Skip to content

Commit

Permalink
🎆 Add webp --> png conversion for typst/pdf/word (#1598)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanc1 authored Oct 22, 2024
1 parent 7547b1d commit 02a8c3a
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-mayflies-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"myst-cli": patch
---

Add webp-->png conversion
26 changes: 24 additions & 2 deletions packages/myst-cli/src/transforms/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ type ConversionOpts = {
file: string;
inkscapeAvailable: boolean;
imagemagickAvailable: boolean;
dwebpAvailable: boolean;
};

type ConversionFn = (
Expand Down Expand Up @@ -334,6 +335,22 @@ async function gifToPng(
return null;
}

/**
* webp -> png using dwebp
*/
async function webpToPng(
session: ISession,
source: string,
writeFolder: string,
opts: ConversionOpts,
) {
const { dwebpAvailable } = opts;
if (dwebpAvailable) {
return imagemagick.convertWebpToPng(session, source, writeFolder);
}
return null;
}

/**
* These are all the available image conversion functions
*
Expand All @@ -353,6 +370,9 @@ const conversionFnLookup: Record<string, Record<string, ConversionFn>> = {
[ImageExtensions.gif]: {
[ImageExtensions.png]: gifToPng,
},
[ImageExtensions.webp]: {
[ImageExtensions.png]: webpToPng,
},
[ImageExtensions.eps]: {
// Currently the inkscape CLI has a bug which prevents EPS conversions;
// once that is fixed, we may uncomment the rest of this section to
Expand Down Expand Up @@ -418,8 +438,9 @@ export async function transformImageFormats(
});
if (Object.keys(invalidImages).length === 0) return;

const inkscapeAvailable = !!inkscape.isInkscapeAvailable();
const imagemagickAvailable = !!imagemagick.isImageMagickAvailable();
const inkscapeAvailable = inkscape.isInkscapeAvailable();
const imagemagickAvailable = imagemagick.isImageMagickAvailable();
const dwebpAvailable = imagemagick.isDwebpAvailable();

/**
* convert runs the input conversion functions on the image
Expand All @@ -437,6 +458,7 @@ export async function transformImageFormats(
file,
inkscapeAvailable,
imagemagickAvailable,
dwebpAvailable,
});
}
}
Expand Down
96 changes: 70 additions & 26 deletions packages/myst-cli/src/utils/imagemagick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import { RuleId } from 'myst-common';
import type { ISession } from '../session/types.js';
import { addWarningForFile } from './addWarningForFile.js';

function magickCommandAvailable() {
return which.sync('magick', { nothrow: true });
function magickCommandAvailable(): boolean {
return !!which.sync('magick', { nothrow: true });
}

function convertCommandAvailable() {
return which.sync('convert', { nothrow: true });
function convertCommandAvailable(): boolean {
return !!which.sync('convert', { nothrow: true });
}

export function isImageMagickAvailable() {
Expand All @@ -26,12 +26,16 @@ export function imageMagickCommand() {
return 'magick';
}

export function isWebpAvailable() {
return which.sync('cwebp', { nothrow: true });
export function isWebpAvailable(): boolean {
return !!which.sync('cwebp', { nothrow: true });
}

export function isGif2webpAvailable() {
return which.sync('gif2webp', { nothrow: true });
export function isDwebpAvailable(): boolean {
return !!which.sync('dwebp', { nothrow: true });
}

export function isGif2webpAvailable(): boolean {
return !!which.sync('gif2webp', { nothrow: true });
}

const LARGE_IMAGE = 1024 * 1024 * 1.5;
Expand Down Expand Up @@ -66,24 +70,64 @@ export async function extractFirstFrameOfGif(session: ISession, gif: string, wri
const png = path.join(writeFolder, pngFile);
if (fs.existsSync(png)) {
session.log.debug(`Cached file found for extracted GIF: ${gif}`);
} else {
const executable = `${imageMagickCommand()} -density 600 -colorspace RGB ${gif}[0] ${png}`;
session.log.debug(`Executing: ${executable}`);
const exec = makeExecutable(executable, session.log);
try {
await exec();
} catch (err) {
addWarningForFile(
session,
gif,
`Could not extract an image from gif: ${gif} - ${err}`,
'error',
{
ruleId: RuleId.imageFormatConverts,
},
);
return null;
}
return pngFile;
}
const executable = `${imageMagickCommand()} -density 600 -colorspace RGB ${gif}[0] ${png}`;
session.log.debug(`Executing: ${executable}`);
const exec = makeExecutable(executable, session.log);
try {
await exec();
} catch (err) {
addWarningForFile(
session,
gif,
`Could not extract an image from gif: ${gif} - ${err}`,
'error',
{
ruleId: RuleId.imageFormatConverts,
},
);
return null;
}
return pngFile;
}

/**
* webp -> png using dwebp
*/
export async function convertWebpToPng(session: ISession, source: string, writeFolder: string) {
if (!fs.existsSync(source)) return null;
const { name, ext } = path.parse(source);
if (ext !== '.webp') return null;
const pngFile = `${name}.png`;
const png = path.join(writeFolder, pngFile);
if (fs.existsSync(png)) {
session.log.debug(`Cached file found for converted PNG: ${source}`);
return pngFile;
}
const debugLogger = {
// We cannot destructure the logger here, bunyan complains
debug(...args: any[]) {
session.log.debug(...args);
},
error(...args: any[]) {
session.log.debug(...args);
},
};
const exec = makeExecutable(`dwebp "${source}" -o "${png}"`, debugLogger);
try {
await exec();
} catch (err) {
addWarningForFile(
session,
source,
`Could not extract a PNG from WEBP: ${source} - ${err}`,
'error',
{
ruleId: RuleId.imageFormatConverts,
},
);
return null;
}
return pngFile;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/myst-cli/src/utils/inkscape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ function createInkscpapeLogger(session: ISession): LoggerDE {
return logger;
}

export function isInkscapeAvailable() {
return which.sync('inkscape', { nothrow: true });
export function isInkscapeAvailable(): boolean {
return !!which.sync('inkscape', { nothrow: true });
}

export async function convert(
Expand Down

0 comments on commit 02a8c3a

Please sign in to comment.