diff --git a/.changeset/real-jeans-shout.md b/.changeset/real-jeans-shout.md new file mode 100644 index 000000000..271742ebd --- /dev/null +++ b/.changeset/real-jeans-shout.md @@ -0,0 +1,5 @@ +--- +"myst-transforms": patch +--- + +Allow for {subEnumerator} to be a reference target template. diff --git a/.changeset/rich-frogs-try.md b/.changeset/rich-frogs-try.md new file mode 100644 index 000000000..7c34617c7 --- /dev/null +++ b/.changeset/rich-frogs-try.md @@ -0,0 +1,5 @@ +--- +"tex-to-myst": patch +--- + +Add subref, subfigure, and landscape functions for tex diff --git a/.changeset/tender-crabs-push.md b/.changeset/tender-crabs-push.md new file mode 100644 index 000000000..57814f8b8 --- /dev/null +++ b/.changeset/tender-crabs-push.md @@ -0,0 +1,5 @@ +--- +"myst-spec-ext": patch +--- + +Allow the figure container to have an extensible kind diff --git a/docs/figures.md b/docs/figures.md index 5bfd44234..ebecd002f 100644 --- a/docs/figures.md +++ b/docs/figures.md @@ -90,6 +90,8 @@ Some pictures of fruit and beaches! See [](#my-figure-fruit) for the fruit, and [](#my-figure) to reference both subfigures. ``` +By default, when referring to subfigures, the `{number}` that is used includes the parent enumerator (that is: `1a` rather than just `a`). To specifically use the sub-enumerator only, you can use the syntax `{subEnumerator}` in your text link which will be replaced with the sub-enumerator (that is: `a` rather than `1a`). + ## Supported Image Formats MyST supports many images formats including `.png`, `.jpg`, `.gif`, `.tiff`, `.svg`, `.pdf`, and `.eps`. diff --git a/packages/myst-spec-ext/src/types.ts b/packages/myst-spec-ext/src/types.ts index fe81ecd9b..d14c97024 100644 --- a/packages/myst-spec-ext/src/types.ts +++ b/packages/myst-spec-ext/src/types.ts @@ -217,7 +217,7 @@ export type Include = { }; export type Container = Omit & { - kind?: 'figure' | 'table' | 'quote' | 'code'; + kind?: 'figure' | 'table' | 'quote' | 'code' | string; source?: Dependency; subcontainer?: boolean; noSubcontainers?: boolean; diff --git a/packages/myst-transforms/src/enumerate.ts b/packages/myst-transforms/src/enumerate.ts index 45a58d950..014c8dc4c 100644 --- a/packages/myst-transforms/src/enumerate.ts +++ b/packages/myst-transforms/src/enumerate.ts @@ -128,14 +128,17 @@ function fillReferenceEnumerators( 'label' | 'identifier' | 'children' | 'template' | 'enumerator' > & { type: string }, template: string, - enumerator?: string | number, + target?: TargetNodes, title?: string | PhrasingContent[], ) { const noNodeChildren = !node.children?.length; if (noNodeChildren) { setTextAsChild(node, template); } - const num = enumerator != null ? String(enumerator) : UNKNOWN_REFERENCE_ENUMERATOR; + const num = + target?.enumerator != null + ? `${target.parentEnumerator ?? ''}${target.enumerator}` + : UNKNOWN_REFERENCE_ENUMERATOR; if (!node.template) node.template = template; if (num && num !== UNKNOWN_REFERENCE_ENUMERATOR) node.enumerator = num; const used = { @@ -148,6 +151,10 @@ function fillReferenceEnumerators( used.s = true; return num; }, + '{subEnumerator}': () => { + used.number = true; + return target?.enumerator ?? UNKNOWN_REFERENCE_ENUMERATOR; + }, '{number}': () => { used.number = true; return num; @@ -456,11 +463,11 @@ export class ReferenceState implements IReferenceStateResolver { this.vfile, node, headingTemplate, - target.node.enumerator, + target.node, copyNode(target.node as Heading).children as PhrasingContent[], ); } else if (target.kind === TargetKind.equation) { - fillReferenceEnumerators(this.vfile, node, '(%s)', target.node.enumerator); + fillReferenceEnumerators(this.vfile, node, '(%s)', target.node); } else { // By default look into the caption or admonition title if it exists const caption = @@ -480,13 +487,7 @@ export class ReferenceState implements IReferenceStateResolver { const template = target.node.enumerator ? getDefaultNumberedReferenceLabel(target.kind) : getDefaultNamedReferenceLabel(target.kind, !!title); - fillReferenceEnumerators( - this.vfile, - node, - template, - `${target.node.parentEnumerator ?? ''}${target.node.enumerator}`, - title, - ); + fillReferenceEnumerators(this.vfile, node, template, target.node, title); } node.resolved = true; // The identifier may have changed in the lookup, but unlikely @@ -587,8 +588,9 @@ export const enumerateTargetsPlugin: Plugin<[StateOptions], GenericParent, Gener enumerateTargetsTransform(tree, opts); }; -function getCaptionLabel(kind?: string, subcontainer?: boolean) { - if (subcontainer) return `(%s)`; +function getCaptionLabel(kind?: Container['kind'], subcontainer?: boolean) { + if (subcontainer && (kind === 'equation' || kind === 'subequation')) return `(%s)`; + if (subcontainer) return `({subEnumerator})`; if (!kind) return 'FigureĀ %s:'; const template = getDefaultNumberedReferenceLabel(kind); return `${template}:`; @@ -610,8 +612,8 @@ export function addContainerCaptionNumbersTransform( containers .filter((container: Container) => container.enumerator) .forEach((container: Container) => { - const enumerator = opts.state.getTarget(container.identifier)?.node.enumerator; - if (!enumerator) return; + const target = opts.state.getTarget(container.identifier)?.node; + if (!target?.enumerator) return; // Only look for direct caption children let para = select( 'paragraph', @@ -629,13 +631,13 @@ export function addContainerCaptionNumbersTransform( label: container.label, identifier: container.identifier, html_id: (container as any).html_id, - enumerator, + enumerator: target.enumerator, }; fillReferenceEnumerators( file, captionNumber, getCaptionLabel(container.kind, container.subcontainer), - enumerator, + target, ); // The caption number is in the paragraph, it needs a link to the figure container // This is a bit awkward, but necessary for (efficient) rendering diff --git a/packages/tex-to-myst/src/figures.ts b/packages/tex-to-myst/src/figures.ts index 64c5a44c5..8bc3c430d 100644 --- a/packages/tex-to-myst/src/figures.ts +++ b/packages/tex-to-myst/src/figures.ts @@ -33,7 +33,11 @@ const FIGURE_HANDLERS: Record = { state.closeNode(); }, env_subfigure(node, state) { + state.closeParagraph(); + state.openNode('container', { kind: 'figure' }); state.renderChildren(node); + state.closeParagraph(); + state.closeNode(); }, env_centering(node, state) { centering(node, state); diff --git a/packages/tex-to-myst/src/misc.ts b/packages/tex-to-myst/src/misc.ts index 3ff80de9d..d8d8e704f 100644 --- a/packages/tex-to-myst/src/misc.ts +++ b/packages/tex-to-myst/src/misc.ts @@ -54,6 +54,13 @@ export const MISC_HANDLERS: Record = { macro_and(node, state) { state.data.andCallback?.(); }, + env_landscape(node, state) { + state.closeParagraph(); + state.openBlock({ landscape: true }); + state.renderChildren(node); + state.closeParagraph(); + state.closeBlock(); + }, macro_noindent: pass, macro_acknowledgments: pass, macro_def: pass, diff --git a/packages/tex-to-myst/src/refs.ts b/packages/tex-to-myst/src/refs.ts index 525e6fe43..3faaaecdd 100644 --- a/packages/tex-to-myst/src/refs.ts +++ b/packages/tex-to-myst/src/refs.ts @@ -30,6 +30,11 @@ export const REF_HANDLERS: Record = { const label = texToText(getArguments(node, 'group')); state.pushNode(u('crossReference', { label }, [u('text', '%s')])); }, + macro_subref(node, state) { + state.openParagraph(); + const label = texToText(getArguments(node, 'group')); + state.pushNode(u('crossReference', { label }, [u('text', '{subEnumerator}')])); + }, macro_nameref(node, state) { state.openParagraph(); const label = texToText(getArguments(node, 'group')); diff --git a/packages/tex-to-myst/src/tex.ts b/packages/tex-to-myst/src/tex.ts index 8dd0205be..e2737a2fd 100644 --- a/packages/tex-to-myst/src/tex.ts +++ b/packages/tex-to-myst/src/tex.ts @@ -63,6 +63,7 @@ const macros: Record = { primarypubs: 2, eqref: 1, nameref: 1, + subref: 1, textsubscript: 1, textsuperscript: 1, adjustwidth: 2, diff --git a/packages/tex-to-myst/tests/figures.yml b/packages/tex-to-myst/tests/figures.yml index f6fd21752..2e85610c6 100644 --- a/packages/tex-to-myst/tests/figures.yml +++ b/packages/tex-to-myst/tests/figures.yml @@ -101,10 +101,15 @@ cases: - title: subfigure tex: |- \begin{figure} + \begin{subfigure}{0.49\textwidth} + \includegraphics[width=\textwidth]{figures/one.jpg} + \caption{This is Fig 1a} + \end{subfigure} \begin{subfigure}[b]{0.49\textwidth} \centering - \includegraphics[width=\textwidth]{figures/one.jpg} + \includegraphics[width=\textwidth]{figures/two.jpg} \end{subfigure} + \caption{This is the full figure caption.} \end{figure} tree: type: root @@ -112,9 +117,29 @@ cases: - type: container kind: figure children: - - type: image - url: figures/one.jpg - align: center + - type: container + kind: figure + children: + - type: image + url: figures/one.jpg + - type: caption + children: + - type: paragraph + children: + - type: text + value: This is Fig 1a + - type: container + kind: figure + align: center + children: + - type: image + url: figures/two.jpg + - type: caption + children: + - type: paragraph + children: + - type: text + value: This is the full figure caption. - title: minipage tex: |- \begin{figure}