#!/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": "", "display": } * * Each output item has the shape: * * { "ok": true, "html": "" } // success * { "ok": false, "error": "" } // 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 * `` 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();