Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/myst-cli/src/transforms/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ type ConversionOpts = {
imagemagickAvailable: boolean;
dwebpAvailable: boolean;
ffmpegAvailable: boolean;
page?: number;
};

type ConversionFn = (
Expand All @@ -270,7 +271,8 @@ function imagemagickConvert(
return async (session: ISession, source: string, writeFolder: string, opts: ConversionOpts) => {
const { imagemagickAvailable } = opts;
if (imagemagickAvailable) {
return imagemagick.convert(from, to, session, source, writeFolder, options);
const optsWithPage = opts.page !== undefined ? { ...options, page: opts.page } : options;
return imagemagick.convert(from, to, session, source, writeFolder, optsWithPage);
}
return null;
};
Expand Down Expand Up @@ -479,13 +481,18 @@ export async function transformImageFormats(
let outputFile: string | null = null;
for (const conversionFn of conversionFns) {
if (!outputFile) {
outputFile = await conversionFn(session, inputFile, writeFolder, {
const conversionOpts: ConversionOpts = {
file,
inkscapeAvailable,
imagemagickAvailable,
dwebpAvailable,
ffmpegAvailable,
});
};
if (image.page !== undefined) {
conversionOpts.page = image.page;
}

outputFile = await conversionFn(session, inputFile, writeFolder, conversionOpts);
}
}
if (outputFile) {
Expand Down
9 changes: 5 additions & 4 deletions packages/myst-cli/src/utils/imagemagick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,24 @@ export async function convert(
session: ISession,
input: string,
writeFolder: string,
options?: { trim?: boolean },
options?: { trim?: boolean; page?: number },
) {
if (!fs.existsSync(input)) return null;
const { name, ext } = path.parse(input);
if (ext !== inputExtension) return null;
const filename = `${name}${outputExtension}`;
const filename = `${name}${options?.page ? '-' + options.page : ''}${outputExtension}`;
const output = path.join(writeFolder, filename);
const inputFormatUpper = inputExtension.slice(1).toUpperCase();
const outputFormatUpper = outputExtension.slice(1).toUpperCase();
if (fs.existsSync(output)) {
session.log.debug(`Cached file found for converted ${inputFormatUpper}: ${input}`);
return filename;
} else {
const executable = `${imageMagickCommand()} -density 600 -colorspace RGB ${input}${
const executable = `${imageMagickCommand()} -density 600 -colorspace RGB ${input}${options?.page ? '[' + options.page + ']' : ''}${
options?.trim ? ' -trim' : ''
} ${output}`;
session.log.debug(`Executing: ${executable}`);

session.log.info(`Executing: ${executable}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
session.log.info(`Executing: ${executable}`);
session.log.debug(`Executing: ${executable}`);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

const exec = makeExecutable(executable, createImagemagikLogger(session));
try {
await exec();
Expand Down
38 changes: 27 additions & 11 deletions packages/tex-to-myst/src/figures.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { GenericNode } from 'myst-common';
import { u } from 'unist-builder';
import type { Handler, ITexParser } from './types.js';
import { getArguments, texToText } from './utils.js';
import { getArguments, extractParams, texToText } from './utils.js';
import { group } from 'console';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed


function renderCaption(node: GenericNode, state: ITexParser) {
state.closeParagraph();
Expand Down Expand Up @@ -45,18 +46,33 @@ const FIGURE_HANDLERS: Record<string, Handler> = {
state.closeParagraph();
const url = texToText(getArguments(node, 'group'));
const args = getArguments(node, 'argument')?.[0]?.content ?? [];
const params = extractParams(args);

// Only support width and page for now
for (const key in params) {
if (key !== 'width' && key !== 'page') {
delete params[key];
}
}

// TODO: better width, placement, etc.
if (
args.length === 4 &&
args[0].content === 'width' &&
args[1].content === '=' &&
Number.isFinite(Number.parseFloat(args[2].content))
) {
const width = `${Math.round(Number.parseFloat(args[2].content) * 100)}%`;
state.pushNode(u('image', { url, width }));
} else {
state.pushNode(u('image', { url }));

// Convert width to percentage if present
if (params.width) {
if (typeof params.width === 'number') {
params.width = `${Math.round(params.width * 100)}%`;
} else {
delete params.width; // If width is a string, we don't know what it is, so we ignore it
}
}
if (params.page) {
if (typeof params.page === 'number') {
params.page = Number(params.page) - 1; // Convert to 0-based for imagemagick
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to round or parse this or have something similar to Number.isFinite(Number.parseFloat(params.page))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right! Unless I'm mistaken, I think the extractParams should already have done the parsing, but I've added the missing Number.isFinite and the rounding.

} else {
delete params.page;
}
}
state.pushNode(u('image', { url: url, ...params }));
},
macro_caption: renderCaption,
macro_captionof: renderCaption,
Expand Down
17 changes: 17 additions & 0 deletions packages/tex-to-myst/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,23 @@ export function getArguments(
);
}

export function extractParams(args: { content: string }[]): Record<string, string | number> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it has potential for wider use across other macros/parameter types. 👍

const params: Record<string, string | number> = {};

for (let i = 0; i < args.length - 2; i++) {
const param = args[i].content;
const equalsSign = args[i + 1].content;
const value = args[i + 2].content;

if (equalsSign === '=' && (Number.isFinite(Number.parseFloat(value)) || value)) {
params[param] = Number.isFinite(Number.parseFloat(value)) ? Number.parseFloat(value) : value;
i += 2; // Skip the processed elements
}
}

return params;
}

export function renderInfoIndex(node: GenericNode, name: string): number {
return node._renderInfo?.namedArguments?.findIndex((a: string) => a === name);
}
Expand Down
34 changes: 34 additions & 0 deletions packages/tex-to-myst/tests/figures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,37 @@ cases:
value: link to notebook
- type: text
value: '.'
- title: includegraphics specifies the page
tex: |-
\begin{figure}[htbp]
\centering
\includegraphics[width=1.0\textwidth,page=3]{figures/my_pic.pdf}
\caption{ This is the caption, \href{computations.ipynb}{link to notebook}.}
\label{fig:picture}
\end{figure}
tree:
type: root
children:
- type: container
kind: figure
identifier: fig:picture
label: fig:picture
align: center
children:
- type: image
url: figures/my_pic.pdf
width: 100%
page: 2 # start from 0
- type: caption
children:
- type: paragraph
children:
- type: text
value: 'This is the caption, '
- type: link
url: computations.ipynb
children:
- type: text
value: link to notebook
- type: text
value: '.'