Files

85 lines
2.8 KiB
JavaScript

#!/usr/bin/env node
/**
* render_math.js — Batch LaTeX-math renderer for the IHUFT MkDocs build.
*
* Reads a JSON array of math expressions from stdin and writes a JSON array
* of rendered HTML strings to stdout. Each input item has the shape:
*
* { "tex": "<LaTeX source>", "display": <bool> }
*
* Each output item has the shape:
*
* { "ok": true, "html": "<rendered HTML>" } // success
* { "ok": false, "error": "<KaTeX error msg>" } // failure
*
* Invoked by hooks/math_prerender.py. All math in a page is rendered in a
* single subprocess call so that the per-page overhead of spawning Node is
* amortised across all equations on the page.
*
* KaTeX produces both an HTML representation (for visual rendering) and an
* MathML representation that includes the original LaTeX source in an
* `<annotation encoding="application/x-tex">` element. This means the LaTeX
* source travels with the rendered math and can be recovered by users for
* copy-and-paste, without any extra plumbing.
*/
const fs = require('fs');
const path = require('path');
// Resolve KaTeX from the local node_tools tree regardless of cwd.
const KATEX_PATH = path.resolve(__dirname, '..', 'node_tools', 'node_modules', 'katex');
const katex = require(KATEX_PATH);
// Common macros the IHUFT proposal relies on but that KaTeX does not bundle
// by default. Extend this object as new macros appear in the source.
const MACROS = {
// None at present. KaTeX already supports \tag, \boxed, \aligned,
// \begin{aligned}, \operatorname, \mathrm, \mathfrak, \mathcal,
// \mathbb, \mathbf, \longleftrightarrow, \longrightarrow, \Vert,
// \hookrightarrow, \blacksquare, etc., out of the box.
};
function renderItem(item) {
try {
const html = katex.renderToString(item.tex, {
displayMode: !!item.display,
output: 'htmlAndMathml',
throwOnError: false,
strict: 'ignore',
trust: true,
macros: MACROS,
});
return { ok: true, html: html };
} catch (err) {
return { ok: false, error: String(err && err.message || err) };
}
}
function main() {
let raw;
try {
raw = fs.readFileSync(0, 'utf-8');
} catch (err) {
console.error('render_math.js: failed to read stdin:', err.message);
process.exit(2);
}
let items;
try {
items = JSON.parse(raw);
} catch (err) {
console.error('render_math.js: invalid JSON on stdin:', err.message);
process.exit(2);
}
if (!Array.isArray(items)) {
console.error('render_math.js: expected a JSON array of math items');
process.exit(2);
}
const results = items.map(renderItem);
process.stdout.write(JSON.stringify(results));
}
main();