Last active
August 6, 2020 18:49
-
-
Save dpvc/2cf897746549c0f92ceff40ca6a0d034 to your computer and use it in GitHub Desktop.
MathJax filter for sag's produce with TikZ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env node | |
/************************************************************************* | |
* | |
* tikz-filter | |
* | |
* Uses MathJax v3 to convert math to svg within TikZ svg output. | |
* | |
* ---------------------------------------------------------------------- | |
* | |
* Copyright (c) 2020 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
// | |
// Load the packages needed for MathJax | |
// | |
require('mathjax-full/js/util/asyncLoad/node.js'); | |
const {mathjax} = require('mathjax-full/js/mathjax.js'); | |
const {TeX} = require('mathjax-full/js/input/tex.js'); | |
const {SVG} = require('mathjax-full/js/output/svg.js'); | |
const {RegisterHTMLHandler} = require('mathjax-full/js/handlers/html.js'); | |
const {liteAdaptor} = require('mathjax-full/js/adaptors/liteAdaptor.js'); | |
const {AllPackages} = require('mathjax-full/js/input/tex/AllPackages.js'); | |
// | |
// Get the command-line arguments | |
// | |
var argv = require('yargs') | |
.demand(0).strict() | |
.usage('$0 [options] infile.svg > outfile.svg') | |
.options({ | |
fontPaths: { | |
boolean: true, | |
default: false, | |
describe: 'use svg paths not cached paths' | |
}, | |
em: { | |
default: 16, | |
describe: 'em-size in pixels' | |
}, | |
packages: { | |
default: AllPackages.sort().join(', '), | |
describe: 'the packages to use, e.g. "base, ams"' | |
} | |
}) | |
.argv; | |
// | |
// Read the SVG file | |
// | |
const svgfile = require('fs').readFileSync(argv._[0], 'utf8'); | |
// | |
// Create DOM adaptor and register it for HTML documents | |
// | |
const adaptor = liteAdaptor({fontSize: argv.em}); | |
const handler = RegisterHTMLHandler(adaptor); | |
// | |
// Patch MathJax 3.0.5 SVG bug: | |
// | |
if (mathjax.version === '3.0.5') { | |
const {SVGWrapper} = require('mathjax-full/js/output/svg/Wrapper.js'); | |
const CommonWrapper = SVGWrapper.prototype.__proto__; | |
SVGWrapper.prototype.unicodeChars = function (text, variant) { | |
if (!variant) variant = this.variant || 'normal'; | |
return CommonWrapper.unicodeChars.call(this, text, variant); | |
} | |
} | |
// | |
// Create an HTML document using the svg file | |
// | |
const html = mathjax.document(svgfile, { | |
InputJax: new TeX({packages: argv.packages.split(/\s*,\s*/)}), | |
OutputJax: new SVG({fontCache: (argv.fontPaths ? 'none' : 'local')}) | |
}); | |
// | |
// Process the SVG diagram | |
// | |
async function processSVG() { | |
const adaptor = html.adaptor; | |
// | |
// For each element with class PTX-math... | |
// | |
for (const ptx of adaptor.getElements(['.PTX-math'], html.document)) { | |
// | |
// Get the LaTeX and typeset it using MathJax | |
// | |
const math = adaptor.getAttribute(ptx, 'data-text'); | |
const mjx = await mathjax.handleRetriesFor(() => html.convert(math)); | |
const svg = adaptor.firstChild(mjx); | |
// | |
// Transfer the x, y, width, height from the bounding box rectangle | |
// to the typeset math to scale and position the math | |
// | |
const rect = adaptor.firstChild(ptx); | |
for (const {name, value} of adaptor.allAttributes(rect)) { | |
adaptor.setAttribute(svg, name, value); | |
} | |
// | |
// Remove unneeded attributes | |
// | |
adaptor.removeAttribute(svg, 'style'); | |
adaptor.removeAttribute(svg, 'role'); | |
adaptor.removeAttribute(svg, 'focusable'); | |
// | |
// Replace the <g> with the typeset math | |
// | |
adaptor.replace(svg, ptx); | |
} | |
} | |
// | |
// Process the SVG and output the result | |
// | |
processSVG() | |
.then(() => console.log(adaptor.innerHTML(adaptor.body(html.document)))) | |
.catch((err) => console.error(err)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env node | |
/************************************************************************* | |
* | |
* tikz-html | |
* | |
* Uses MathJax v3 to convert math to svg and braille titles | |
* within TikZ svg output, with checkboxes to control which is shown. | |
* | |
* ---------------------------------------------------------------------- | |
* | |
* Copyright (c) 2020 The MathJax Consortium | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
// | |
// Load the packages needed for MathJax | |
// | |
require('mathjax-full/js/util/asyncLoad/node.js'); | |
const {mathjax} = require('mathjax-full/js/mathjax.js'); | |
const {TeX} = require('mathjax-full/js/input/tex.js'); | |
const {SVG} = require('mathjax-full/js/output/svg.js'); | |
const {RegisterHTMLHandler} = require('mathjax-full/js/handlers/html.js'); | |
const {liteAdaptor} = require('mathjax-full/js/adaptors/liteAdaptor.js'); | |
const {STATE} = require('mathjax-full/js/core/MathItem.js'); | |
const {AllPackages} = require('mathjax-full/js/input/tex/AllPackages.js'); | |
// | |
// Get the command-line arguments | |
// | |
var argv = require('yargs') | |
.demand(0).strict() | |
.usage('$0 [options] infile.svg > outfile.svg') | |
.options({ | |
braille: { | |
boolean: true, | |
default: true, | |
describe: 'produce braille output' | |
}, | |
svg: { | |
boolean: true, | |
default: true, | |
describe: 'produce svg output' | |
}, | |
fontPaths: { | |
boolean: true, | |
default: false, | |
describe: 'use svg paths not cached paths' | |
}, | |
em: { | |
default: 16, | |
describe: 'em-size in pixels' | |
}, | |
packages: { | |
default: AllPackages.sort().join(', '), | |
describe: 'the packages to use, e.g. "base, ams"' | |
} | |
}) | |
.argv; | |
// | |
// Load SRE if needed for speech or braille | |
// | |
const {sreReady} = (argv.braille ? require('mathjax-full/js/a11y/sre.js') : {sreReady: Promise.resolve()}); | |
// | |
// Read the SVG file | |
// | |
const svgfile = require('fs').readFileSync(argv._[0], 'utf8'); | |
// | |
// Create DOM adaptor and register it for HTML documents | |
// | |
const adaptor = liteAdaptor({fontSize: argv.em}); | |
const handler = RegisterHTMLHandler(adaptor); | |
// | |
// Patch MathJax 3.0.5 SVG bug: | |
// | |
if (mathjax.version === '3.0.5') { | |
const {SVGWrapper} = require('mathjax-full/js/output/svg/Wrapper.js'); | |
const CommonWrapper = SVGWrapper.prototype.__proto__; | |
SVGWrapper.prototype.unicodeChars = function (text, variant) { | |
if (!variant) variant = this.variant || 'normal'; | |
return CommonWrapper.unicodeChars.call(this, text, variant); | |
} | |
} | |
// | |
// Create a MathML serializer | |
// | |
const {SerializedMmlVisitor} = require('mathjax-full/js/core/MmlTree/SerializedMmlVisitor.js'); | |
const visitor = new SerializedMmlVisitor(); | |
const toMathML = (node => visitor.visitTree(node, html)); | |
// | |
// Create an HTML document using the svg file | |
// | |
const html = mathjax.document(svgfile, { | |
InputJax: new TeX({packages: argv.packages.split(/\s*,\s*/)}), | |
OutputJax: new SVG({fontCache: (argv.fontPaths ? 'none' : 'local')}) | |
}); | |
// | |
// Function for adding SVG output for labels | |
// | |
async function addSVG(ptx, math, rect) { | |
if (!argv.svg) return; | |
// | |
// Typeset to SVG | |
// | |
const mjx = await mathjax.handleRetriesFor(() => html.convert(math)); | |
const svg = adaptor.firstChild(mjx); | |
// | |
// Transfer the x, y, width, height from the bounding box rectangle | |
// to the typeset math to scale and position the math | |
// | |
for (const {name, value} of adaptor.allAttributes(rect)) { | |
adaptor.setAttribute(svg, name, value); | |
} | |
// | |
// Remove unneeded attributes | |
// | |
adaptor.removeAttribute(svg, 'style'); | |
adaptor.removeAttribute(svg, 'role'); | |
adaptor.removeAttribute(svg, 'focusable'); | |
// | |
// Replace the <rect> with the typeset math | |
// | |
adaptor.append(ptx, svg); | |
} | |
// | |
// Function for adding braille output for labels | |
// | |
async function addBraille(ptx, math, rect) { | |
if (!argv.braille) return; | |
// | |
// Add the braille title | |
// | |
const mml = await mathjax.handleRetriesFor(() => html.convert(math, {end: STATE.COMPILED})); | |
const braille = SRE.toSpeech(toMathML(mml)); | |
const g = adaptor.node('g', {class: 'ptx-braille'}, [adaptor.node('text', {}, [adaptor.text(braille)])]); | |
// | |
// Move the braile into place | |
// | |
const x = parseFloat(adaptor.getAttribute(rect, 'x')); | |
const y = parseFloat(adaptor.getAttribute(rect, 'y')) + 6; | |
adaptor.setAttribute(g, 'transform', `translate(${x}, ${y}) scale(.6)`); | |
adaptor.append(ptx, g); | |
} | |
// | |
// Process the SVG diagram | |
// | |
async function processSVG() { | |
// | |
// For each element with class PTX-math or PTX-text... | |
// | |
for (const ptx of adaptor.getElements(['.PTX-math', '.PTX-text'], html.document)) { | |
// | |
// Mark the container as containing MathJax output (for CSS selection of alternatives) | |
// | |
adaptor.addClass(ptx, 'PTX-MJX'); | |
// | |
// Get the text or LaTeX and typeset it using MathJax | |
// | |
let math = adaptor.textContent(adaptor.remove(adaptor.firstChild(ptx))); | |
if (adaptor.hasClass(ptx, 'PTX-text')) { | |
math = '\\hbox{' + math + '}'; | |
} | |
// | |
// Get the bounding box rectangle | |
// | |
const rect = adaptor.remove(adaptor.firstChild(ptx)); | |
// | |
// Add various output formats | |
// | |
await addSVG(ptx, math, rect); | |
await addBraille(ptx, math, rect); | |
} | |
} | |
// | |
// Process the SVG and output the result | |
// | |
(async function () { | |
if (argv.braille) { | |
SRE.setupEngine({modality: 'braille', locale: 'nemeth'}); | |
await sreReady(); | |
} | |
await processSVG(); | |
console.log([ | |
'<!DOCTYPE html>', | |
'<head>', | |
'<meta charset="utf-8" />', | |
'<title>Sample SVG with MathJax output</title>', | |
'<style>', | |
' body.no-svg .PTX-MJX > svg {display: none}', | |
' body.no-braille .PTX-MJX > g.ptx-braille {display: none}', | |
'</style>', | |
'</head>', | |
`<body class="no-${argv.svg ? 'braille' : 'svg'}">`, | |
'<label>Show:', | |
'<select onchange="document.body.className = this.value">', | |
argv.svg ? '<option value="no-braille">SVG</option>' : '', | |
argv.braille ? '<option value="no-svg">Braille</option>' : '', | |
'</select>', | |
'<br>', | |
adaptor.innerHTML(adaptor.body(html.document)), | |
'</body>', | |
'</html>' | |
].join('\n')); | |
})().catch((err) => console.error(err)); |
I've added tikz-html
that produces an html file containing an svg that has multiple versions of the math (svg and braille in this case) with a selector that allows you to show one or the other.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This filter converts an SVG file produces from dvisvgm from a TikZ diagram to one that has the math properly typeset by MathJax.