Skip to content

Commit

Permalink
Merge pull request #317 from executablebooks/feat/refactor-image-tran…
Browse files Browse the repository at this point in the history
…sform

🎆 Refactor image conversion transform for extensibility and code deduplication
  • Loading branch information
rowanc1 authored Mar 21, 2023
2 parents 45ecdf8 + e6f6d98 commit fd4a9b2
Show file tree
Hide file tree
Showing 22 changed files with 1,218 additions and 266 deletions.
5 changes: 5 additions & 0 deletions .changeset/large-suits-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-cli': patch
---

Refactor image conversion transform for extensibility and code deduplication
5 changes: 5 additions & 0 deletions .changeset/tame-moons-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-cli': patch
---

Resolve images with wildcard extensions to existing images
80 changes: 80 additions & 0 deletions docs/figures.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,83 @@ Relaxing at the beach 🏝 🌊 😎
```{note}
You may also embed [notebook cell outputs as images or figures](#targeting-cells).
```

## Supported Image Formats

MyST supports many images formats including `.png`, `.jpg`, `.gif`, `.tiff`, `.svg`, `.pdf`, and `.eps`.
Many of these image formats are easily supported for HTML themes including `.png`, `.jpg` and `.gif`. However, the raster image formats can be further optimized to [improve site performance](./accessibility-and-performance.md), MyST translates these to the modern `.webp` format while the site is building. The original file-format is also included your site, with a `srcset` and fallback for older web-browsers.

`````{tab-set}
````{tab-item} PNG
:::{figure} ./images/myst-image.png
:width: 50%
`.png` is natively supported in all exports. The image is converted to `.webp` for web-browsers.
:::
````
````{tab-item} JPG
:::{figure} ./images/myst-image.jpg
:width: 50%
`.jpg` or `.jpeg` is natively supported in all exports. The image is converted to `.webp` for web-browsers.
:::
````
````{tab-item} GIF
:::{figure} ./images/myst-image.gif
:width: 50%
`.gif` is supported web-browsers and Microsoft Word, the first frame is extracted for $\LaTeX$ and PDF exports. The image is converted to `.webp` for web-browsers.
:::
````
````{tab-item} TIFF
:::{figure} ./images/myst-image.tiff
:width: 50%
`.tiff` is not supported by most web-browsers, and is converted to `.png`. Microsoft Word, $\LaTeX$ and PDF exports can work with these `.png` images, which are also converted to `.webp` for web-browsers.
:::
````
````{tab-item} SVG
:::{figure} ./images/myst-image.svg
:width: 50%
`.svg` is supported by web-browsers and is not further optimized or rasterized. When exporting to $\LaTeX$ and PDF the images are translated to `.pdf` using `inkscape` or as a fallback to `.png` using `imagemagick`. Microsoft Word requires the `.png` export.
:::
````
````{tab-item} PDF
:::{figure} ./images/myst-image.pdf
:width: 50%
A `.pdf` image is not supported by web-browsers or Microsoft Word. The images are translated to `.png` using `imagemagick`. $\LaTeX$ and PDF use the `.pdf` image directly.
:::
````
````{tab-item} EPS
:::{figure} ./images/myst-image.eps
:width: 50%
An `.eps` image is not supported by web-browsers or Microsoft Word. The images are translated to `.png` using `imagemagick`. $\LaTeX$ and PDF use the `.eps` image directly.
:::
````
`````

### Image Translations

There are formats that are not supported by web-browsers but are common in scientific writing like `.tiff`, `.pdf` and `.eps` for site builds, these are converted to `.svg` or `.png` as appropriate and available. For export to $\LaTeX$, PDF or Microsoft Word, the files are converted to an appropriate format that the export can handle (e.g. $\LaTeX$ can work directly with `.pdf` images). For animated images, `.gif`, the first frame is extracted for static exports.

:::{tip} Installing Image Converters
:class: dropdown
The image translations and optimizations requires you to have the following packages installed:

- [imagemagik](https://imagemagick.org/) for conversion between raster formats
- [inkscape](https://inkscape.org/) for conversion between some vector formats
- [webp](https://developers.google.com/speed/webp) for image optimizations

:::

### Multiple Images

If you have manually converted your images or have different images for different formats, use an asterisk (`*`) as the extension. All images matching the provided pattern will be found and the best image out of the available candidates will be used for the export:

```text
![](./images/myst-image.*)
```

For example, when exporting to $\LaTeX$ the best format is a `.pdf` if it is available; in a web export, a `.webp` or `.svg` will be chosen before a `.png`. In all cases, if an appropriate format is not available the image will be translated.
310 changes: 310 additions & 0 deletions docs/images/myst-image.ai

Large diffs are not rendered by default.

427 changes: 427 additions & 0 deletions docs/images/myst-image.eps

Large diffs are not rendered by default.

Binary file added docs/images/myst-image.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/myst-image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/myst-image.pdf
Binary file not shown.
Binary file added docs/images/myst-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions docs/images/myst-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/myst-image.tiff
Binary file not shown.
3 changes: 2 additions & 1 deletion packages/myst-cli/src/build/docx/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { findCurrentProjectAndLoad } from '../../config';
import { getExportListFromRawFrontmatter, getRawFrontmatterFromFile } from '../../frontmatter';
import { loadProjectFromDisk } from '../../project';
import type { ISession } from '../../session/types';
import { ImageExtensions } from '../../transforms/types';
import type { RendererData } from '../../transforms/types';
import { createTempFolder, logMessagesFromVFile } from '../../utils';
import type { ExportOptions, ExportWithOutput } from '../types';
Expand All @@ -27,7 +28,7 @@ import { createFooter } from './footers';
import { createArticleTitle, createReferenceTitle } from './titles';
import { TemplateKind } from 'myst-common';

const DOCX_IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];
const DOCX_IMAGE_EXTENSIONS = [ImageExtensions.png, ImageExtensions.jpg, ImageExtensions.jpeg];

export async function collectWordExportOptions(
session: ISession,
Expand Down
8 changes: 7 additions & 1 deletion packages/myst-cli/src/build/tex/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getExportListFromRawFrontmatter, getRawFrontmatterFromFile } from '../.
import { loadProjectFromDisk } from '../../project';
import { castSession } from '../../session';
import type { ISession } from '../../session/types';
import { ImageExtensions } from '../../transforms';
import { createTempFolder, logMessagesFromVFile } from '../../utils';
import type { ExportWithOutput, ExportOptions } from '../types';
import {
Expand All @@ -31,7 +32,12 @@ import {
} from '../utils';

export const DEFAULT_BIB_FILENAME = 'main.bib';
const TEX_IMAGE_EXTENSIONS = ['.pdf', '.png', '.jpg', '.jpeg'];
const TEX_IMAGE_EXTENSIONS = [
ImageExtensions.pdf,
ImageExtensions.png,
ImageExtensions.jpg,
ImageExtensions.jpeg,
];

export function mdastToTex(
session: ISession,
Expand Down
3 changes: 2 additions & 1 deletion packages/myst-cli/src/build/utils/getSingleFileContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
transformMdast,
processProject,
} from '../../process';
import type { ImageExtensions } from '../../transforms';
import { reduceOutputs } from '../../transforms';

export async function getSingleFileContent(
Expand All @@ -22,7 +23,7 @@ export async function getSingleFileContent(
}: {
projectPath?: string;
imageAltOutputFolder?: string;
imageExtensions?: string[];
imageExtensions?: ImageExtensions[];
extraLinkTransformers?: LinkTransformer[];
},
) {
Expand Down
4 changes: 3 additions & 1 deletion packages/myst-cli/src/process/mdast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type { ISession } from '../session/types';
import { castSession } from '../session';
import type { RendererData } from '../transforms/types';
import { KINDS } from '../transforms/types';
import type { ImageExtensions } from '../transforms';
import {
checkLinksTransform,
embedDirective,
Expand Down Expand Up @@ -81,7 +82,7 @@ export async function transformMdast(
projectSlug?: string;
pageSlug?: string;
imageAltOutputFolder?: string;
imageExtensions?: string[];
imageExtensions?: ImageExtensions[];
watchMode?: boolean;
extraTransforms?: TransformFn[];
minifyMaxCharacters?: number;
Expand Down Expand Up @@ -166,6 +167,7 @@ export async function transformMdast(
.run(mdast, vfile);
await transformImages(session, mdast, file, imageWriteFolder, {
altOutputFolder: imageAltOutputFolder,
imageExtensions,
});
// Must happen after transformImages
await transformImageFormats(session, mdast, file, imageWriteFolder, {
Expand Down
15 changes: 11 additions & 4 deletions packages/myst-cli/src/process/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@ import { filterPages, loadProjectFromDisk } from '../project';
import { castSession } from '../session';
import type { ISession } from '../session/types';
import { watch, selectors } from '../store';
import { transformWebp } from '../transforms';
import { ImageExtensions, transformWebp } from '../transforms';
import { combineProjectCitationRenderers } from './citations';
import { loadIntersphinx } from './intersphinx';
import type { PageReferenceStates, TransformFn } from './mdast';
import { postProcessMdast, transformMdast } from './mdast';

const WEB_IMAGE_EXTENSIONS = ['.webp', '.svg', '.gif', '.png', '.jpg', '.jpeg'];
const WEB_IMAGE_EXTENSIONS = [
ImageExtensions.webp,
ImageExtensions.svg,
ImageExtensions.gif,
ImageExtensions.png,
ImageExtensions.jpg,
ImageExtensions.jpeg,
];

type ProcessOptions = {
watchMode?: boolean;
Expand All @@ -36,7 +43,7 @@ type ProcessOptions = {
checkLinks?: boolean;
imageWriteFolder?: string;
imageAltOutputFolder?: string;
imageExtensions?: string[];
imageExtensions?: ImageExtensions[];
extraLinkTransformers?: LinkTransformer[];
extraTransforms?: TransformFn[];
defaultTemplate?: string;
Expand Down Expand Up @@ -276,7 +283,7 @@ export async function processProject(

const usedImageExtensions = imageExtensions ?? WEB_IMAGE_EXTENSIONS;
const extraTransforms: TransformFn[] = [];
if (usedImageExtensions.includes('.webp')) {
if (usedImageExtensions.includes(ImageExtensions.webp)) {
extraTransforms.push(transformWebp);
}
if (opts?.extraTransforms) {
Expand Down
21 changes: 21 additions & 0 deletions packages/myst-cli/src/transforms/images.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getConversionFns } from './images';

describe('getConversionFns', () => {
it('unknown extension returns empty list', async () => {
expect(getConversionFns('.bad', ['.png' as any])).toEqual([]);
});
it('known extension with no validExts returns empty list', async () => {
expect(getConversionFns('.svg', [])).toEqual([]);
});
it('known extension with one validExt returns one item', async () => {
expect(getConversionFns('.svg', ['.png' as any]).length).toEqual(1);
});
it('known extension with multiple validExts returns multiple items', async () => {
expect(getConversionFns('.svg', ['.png', '.pdf', '.jpg'] as any[]).length).toEqual(2);
});
it('order of functions depends on order of validExts', async () => {
expect(getConversionFns('.svg', ['.png', '.pdf'] as any[])).toEqual(
getConversionFns('.svg', ['.pdf', '.png'] as any[]).reverse(),
);
});
});
Loading

0 comments on commit fd4a9b2

Please sign in to comment.