Hello,
I have been creating a load of snapshot unit tests for MathJax output (using the node scripts) as I now use some custom extensions.
I noticed when using fontCache: 'local' the SVG font caching ids have a side effect, for example $a$:
<svg style="vertical-align: -0.025ex;" xmlns="http://www.w3.org/2000/svg" width="1.197ex" height="1.025ex" role="img" focusable="false" viewBox="0 -442 529 453" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="MJX-1-NCM-I-1D44E" d="M498 144C498 153 493 158 482 158C474 158 468 151 465 137C445 58 421 18 394 18C377 18 368 32 368 60C368 73 372 97 381 132L438 357C443 376 445 387 445 392C445 412 434 422 412 422C391 422 377 410 370 387C349 424 319 442 281 442C216 442 159 409 109 343C63 281 40 217 40 150C40 63 91-11 175-11C218-11 260 12 300 58C311 20 345-11 392-11C461-11 482 70 498 144M341 374C350 353 355 339 355 330C355 326 354 321 353 314L304 122C301 111 294 99 285 87C248 41 212 18 177 18C138 18 118 48 118 107C118 131 124 167 136 215C157 300 187 357 224 388C244 405 263 413 282 413C309 413 329 400 341 374Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)">
<g data-mml-node="math" data-latex="a">
<g data-mml-node="mi" data-latex="a">
<use data-c="1D44E" xlink:href="#MJX-1-NCM-I-1D44E"></use>
</g>
</g>
</g>
</svg>
Where the id generated is: MJX-<count / localID>-NCM-I-1D44E. Adding a new test in the middle of existing tests then leads to subsequent tests failing.
Where path id is equal to: FontCache.ts#L73-L78.
I tried the fontCache: 'none' option which sidesteps the issue, but will result in a much larger output in a lot of cases.
I tried the localID: null option but it's not having the desired effect here (still increments localID).
This leads to 2 questions:
1. If I was to go down the route of using fontCache: 'global', how would I access the global cache using node?
I find that using the fontCache: 'global' option leads to:
const htmlNode = htmlDoc.convert(mml)
TypeError: Cannot read properties of null (reading 'children')
❯ LiteAdaptor.append node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/adaptors/liteAdaptor.ts:360:9
❯ FontCache.cachePath node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/FontCache.ts:81:23
❯ SvgTextNode.useNode node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrapper.ts:710:40
❯ SvgTextNode.charNode node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrapper.ts:689:13
❯ SvgTextNode.placeChar node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrapper.ts:640:41
❯ SvgTextNode.toSVG node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrappers/TextNode.ts:171:20
❯ SvgMn.addChildren node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrapper.ts:194:12
❯ SvgMn.toSVG node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrapper.ts:148:9
❯ SvgMrow.addChildren node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrappers/mrow.ts:238:14
❯ SvgMrow.toSVG node_modules/.pnpm/@mathjax+src@4.1.1/node_modules/@mathjax/src/ts/output/svg/Wrappers/mrow.ts:156:11
2. Pure MathJax?
Is there an intended approach for MathJax output with no side effects? My project parses LaTeX documents and manages state— numbered equations, labels, tags, macro expansion, etc. in any maths before it reaches MathJax. I'd ideally like to create a "pure" function where MathJax is just responsible for converting LaTeX math-mode strings to SVG with no side effects. I have tried the following approach (where everything is initialised and evaluated at the same time):
function texToMml(mathMode: string) {
const adaptor = liteAdaptor();
const visitor = new SerializedMmlVisitor();
RegisterHTMLHandler(adaptor);
const tex = new TeX({ packages: ['base'] });
const mmlDoc = mathjax.document('', {
InputJax: tex,
});
const mmlNode = mmlDoc.convert(mathMode, { end: STATE.CONVERT });
return visitor.visitTree(mmlNode)
}
function mmlToSvg(mathMode: string) {
const adaptor = liteAdaptor();
RegisterHTMLHandler(adaptor);
const htmlDoc = mathjax.document('', {
InputJax: new MathML(),
});
const NewcmFont = new MathJaxNewcmFont();
htmlDoc.outputJax = new SVG({ fontData: NewcmFont });
htmlDoc.outputJax.setAdaptor(htmlDoc.adaptor);
const htmlNode = htmlDoc.convert(mml);
const svg = htmlNode.children[0];
return adaptor.outerHTML(svg);
}
but it seems to hold onto state, with the same side effects as:
const adaptor = liteAdaptor();
const visitor = new SerializedMmlVisitor();
RegisterHTMLHandler(adaptor);
const tex = new TeX({ packages: ['base'] });
const mmlDoc = mathjax.document('', {
InputJax: tex,
});
function texToMml(mml: string) {
const mmlNode = mmlDoc.convert(mathMode, { end: STATE.CONVERT });
return visitor.visitTree(mmlNode)
}
const adaptor2 = liteAdaptor();
RegisterHTMLHandler(adaptor2);
const htmlDoc = mathjax.document('', {
InputJax: new MathML(),
});
const NewcmFont = new MathJaxNewcmFont();
htmlDoc.outputJax = new SVG({ fontData: NewcmFont });
htmlDoc.outputJax.setAdaptor(htmlDoc.adaptor);
function mmlToSvg(mml: string) {
const htmlNode = htmlDoc.convert(mml);
const svg = htmlNode.children[0];
return adaptor2.outerHTML(svg);
}
Many thanks.
Hello,
I have been creating a load of snapshot unit tests for MathJax output (using the node scripts) as I now use some custom extensions.
I noticed when using
fontCache: 'local'the SVG font caching ids have a side effect, for example$a$:Where the id generated is:
MJX-<count / localID>-NCM-I-1D44E. Adding a new test in the middle of existing tests then leads to subsequent tests failing.Where
path idis equal to: FontCache.ts#L73-L78.I tried the
fontCache: 'none'option which sidesteps the issue, but will result in a much larger output in a lot of cases.I tried the
localID: nulloption but it's not having the desired effect here (still incrementslocalID).This leads to 2 questions:
1. If I was to go down the route of using
fontCache: 'global', how would I access the global cache using node?I find that using the
fontCache: 'global'option leads to:2. Pure MathJax?
Is there an intended approach for MathJax output with no side effects? My project parses LaTeX documents and manages state— numbered equations, labels, tags, macro expansion, etc. in any maths before it reaches MathJax. I'd ideally like to create a "pure" function where MathJax is just responsible for converting LaTeX math-mode strings to SVG with no side effects. I have tried the following approach (where everything is initialised and evaluated at the same time):
but it seems to hold onto state, with the same side effects as:
Many thanks.