diff --git a/.eleventy.js b/.eleventy.js index 2c381874e..b5717efb2 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -202,6 +202,11 @@ module.exports = function (eleventyConfig) { )}`; return res } + if (token.info === "tikz") { + const code = token.content.trim(); + const b64 = Buffer.from(code, "utf8").toString("base64"); // Encode into Base64 to avoid issues with special characters in TeX code + return `
${b64}
`; + } // Other languages return origFenceRule(tokens, idx, options, env, slf); @@ -501,6 +506,81 @@ module.exports = function (eleventyConfig) { return str && parsed.innerHTML; }); + eleventyConfig.addTransform("render-tikzjax", (() => { + // Lazy loading to save resources. + const tex2svg = require("node-tikzjax").default; + + // Serialize renderings. + // Running node-tikzjax instances concurrently is problematic. See: https://github.com/prinsss/node-tikzjax/blob/main/README.md + let tikzQueue = Promise.resolve(); + + // https://github.com/artisticat1/obsidian-tikzjax/blob/main/main.ts + function tidyTikzSource(src) { + if (!src) return src; + return src + .replaceAll(" ", "") // Remove non-breaking space characters, otherwise we get errors + .replace(/\u00A0/g, "") + .replace(/\r\n/g, "\n") // Normalize line endings & Split into lines + .split("\n") + .map((line) => line.trim()) // Trim whitespace that is inserted when pasting in code, otherwise TikZJax complains + .filter((line) => line.length > 0) // Remove empty lines + .join("\n"); + } + + return async function (content, outputPath) { + if (!outputPath || !outputPath.endsWith(".html")) return content; + if (!content || !content.includes('class="block-language-tikz"')) return content; + + const root = parse(content); + const blocks = root.querySelectorAll("div.block-language-tikz"); + if (!blocks.length) return content; + + for (const block of blocks) { + const srcB64 = block.text || ""; + const texSource = tidyTikzSource(Buffer.from(srcB64, "base64").toString("utf8")); + try { + const run = tikzQueue + .catch(() => {}) + .then(() => + tex2svg(texSource, { + // SvgOptions + embedFontCss: true, // Whether to embed the font CSS file in the SVG. + // TeXOptions + showConsole: false, // Print log of TeX engine to console. + texPackages: {}, // Additional TeX packages to load. e.g. texPackages: { pgfplots: '', amsmath: 'intlimits' }, + tikzLibraries: '', // Additional TikZ libraries to load. e.g. tikzLibraries: 'arrows.meta,calc' + }) + ); + tikzQueue = run.catch(() => {}); + const svg = await run; + + const svgElement = parse(svg) + .querySelector("svg"); + if (svgElement) { + // Zooming would result in other TikZ diagrams being overlapped or partly invisible. + svgElement.setAttribute("style", "margin:auto; display:block;"); // e.g. oleeskild/obsidian-digital-garden#667 + block.replaceWith(svgElement); + } + } catch (e) { + console.warn("\n[TikZJax] render failed at:", outputPath); + console.warn("[TikZJax] TeX source (first 400 chars):\n", texSource.slice(0, 400)); + console.warn("[TikZJax] Warn:", e); + // Escape texSource to be interpreted as HTML properly. + // https://stackoverflow.com/a/7382028 + const texSource_escaped = texSource + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); + block.replaceWith(`
TikZ render failed. See build log.\n\n${(texSource_escaped)}
`); + } + } + + return root.toString(); + }; + })()); + eleventyConfig.addTransform("htmlMinifier", async (content, outputPath) => { if ( (process.env.NODE_ENV === "production" || process.env.ELEVENTY_ENV === "prod") && diff --git a/package-lock.json b/package-lock.json index 995776466..2a604ae94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "markdown-it-mathjax3": "^4.3.1", "markdown-it-plantuml": "^1.4.1", "markdown-it-task-checkbox": "^1.0.6", + "node-tikzjax": "^1.0.5", "npm-run-all": "^4.1.5", "rimraf": "^4.4.1" }, diff --git a/package.json b/package.json index 584bbd56d..119d2e6fc 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "markdown-it-mathjax3": "^4.3.1", "markdown-it-plantuml": "^1.4.1", "markdown-it-task-checkbox": "^1.0.6", + "node-tikzjax": "^1.0.5", "npm-run-all": "^4.1.5", "rimraf": "^4.4.1" }