Hello,
since Prince isn't able to work with MathJax yet I've tried to find a workaround. The basic idea is to have the math rendered with something else to a separate HTML document and then process this document with Prince. I also wanted to use as less external tools/installations as possible and only simple, standard JavaScript, no further frameworks like jQuery etc.
I've found a "solution" which may help other users. You'll need PhantomJS (http://phantomjs.org/) and a local copy of MathJax (https://www.mathjax.org/). The PhantomJS download contains a single executable (at least on the Mac) with no further dependencies. I assume an "installation" like this:
The content of render-math-and-dump.js:
This is a sample setup for MathJax (ConfigMathJax.js). The MathJax.Hub.Config part can be changed, but you'll have to use the MathJax HTML-CSS output variant; for the moment I was unable to get it to work with the SVG output.
The content of test.html:
You can now use:
The resulting PDF is attached. I also attached a rendering from the web (http://www.feynmanlectures.caltech.edu/I_47.html), due to the nature of processing it with the primitive script locally it lacks images and CSS but it shows that perhaps with some modifications rendering math from the web could be made possible.
The script does the following:
I haven't tried it on Windows yet, but I think it will work the same way.
Please note that this isn't meant to be an out-of-the-box solution, I'm sure the script can be made better (I'm not a JS developer). For further information please look at the comments in the scripts.
And please forgive the formatting, the code area in this forum is a bit narrow, so just paste the code in your favorite editor.
since Prince isn't able to work with MathJax yet I've tried to find a workaround. The basic idea is to have the math rendered with something else to a separate HTML document and then process this document with Prince. I also wanted to use as less external tools/installations as possible and only simple, standard JavaScript, no further frameworks like jQuery etc.
I've found a "solution" which may help other users. You'll need PhantomJS (http://phantomjs.org/) and a local copy of MathJax (https://www.mathjax.org/). The PhantomJS download contains a single executable (at least on the Mac) with no further dependencies. I assume an "installation" like this:
myDir/MathJax
myDir/MathJax/MathJax.js
myDir/MathJax/ ... other MathJax files
ConfigMathJax.js
phantomjs
render-math-and-dump.js
test.html
The content of render-math-and-dump.js:
// PhantomJS setup
var webPage = require('webpage');
var page = webPage.create();
var system = require('system');
var waitForMath;
var noLongerExecWaitForMathJax = false;
// Load page
if (system.args.length < 2) {
console.log("Usage: phantomjs render-math-and-dump.js <filename>");
console.log("Renders <filename> and dumps the DOM to the console.");
phantom.exit();
};
page.open(system.args[1], null);
// Alternatives:
// page.open(encodeURI(system.args[1]), null);
// page.open("file://" + encodeURI(system.args[1]), null);
// This also works (to some extent) with web URLs. Be sure to disable
// waitForMath = window.setInterval at the end of this file if you want
// to try this variant of the script.
// page.open(system.args[1], function(status){
// if (status === "success") {
// // In this setup a complete local MathJax installation is present in MathJax/
// page.injectJs("MathJax/MathJax.js");
// page.injectJs("ConfigMathJax.js");
// // Execute the query every 20ms
// waitForMath = window.setInterval(
// waitForMathJax,
// 20
// );
// };
// });
// Query a variable named 'MathJaxFinished' in the document,
// see also ConfigMathJax.js.
var waitForMathJax = function () {
if (noLongerExecWaitForMathJax) {
return;
};
var mjFinished = page.evaluate(function () {
return MathJaxFinished;
});
if (mjFinished) {
// waitForMathJax was called at least twice (?), even after window.clearInterval()
// This flag prevents it (see above).
noLongerExecWaitForMathJax = true;
window.clearInterval(waitForMathJax);
page.evaluate(function() {
// Remove all MathJax related script elements
var MathJaxScriptFound = true;
var elements = document.getElementsByTagName("script");
// Repeating this loop until no more matching scripts are found seems to be necessary
// because removing children from the DOM did not always happen immediately, some
// of them were still written to the output when dumping 'page.content'.
while (MathJaxScriptFound) {
MathJaxScriptFound = false;
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i].hasAttribute("id")) {
var id = elements[i].getAttribute("id");
// Remove script if 'id' starts with 'MathJax-Element'
if (id.indexOf("MathJax-Element") === 0) {
MathJaxScriptFound = true;
elements[i].parentNode.removeChild(elements[i]);
};
} else if (elements[i].hasAttribute("src")) {
var src = elements[i].getAttribute("src");
// Remove script if 'src' ends on 'ConfigMathJax.js', 'MathJax.js' or 'jax.js'
if ((src.indexOf("ConfigMathJax.js", src.length - 16) !== -1)
|| (src.indexOf("MathJax.js", src.length - 10) !== -1)
|| (src.indexOf("jax.js", src.length - 6) !== -1)) {
MathJaxScriptFound = true;
elements[i].parentNode.removeChild(elements[i]);
};
};
};
elements = document.getElementsByTagName("script");
};
// Remove div with ID 'MathJax_Font_Test'
var element = document.getElementById("MathJax_Font_Test");
if (element) {
element.parentNode.removeChild(element);
};
// Avoid empty elements in HTML-Syntax (<meta>, <link>, <br>, <hr>, ...),
// this makes it possible to call Prince in XML mode with the generated
// output from PhantomJS. In mode 'auto' this code can be disabled completely.
// Please also note that for example the resulting <br></br> is not
// compliant with HTML, so you should keep an eye on the output
// generated by Prince. The same is probably true for <meta></meta>,
// <link></link> and the like.
elements = document.documentElement.getElementsByTagName("*");
for (var i = elements.length - 1; i >= 0; i--) {
if (elements[i].childNodes.length == 0) {
elements[i].appendChild(document.createTextNode(""));
};
};
});
console.log(page.content);
phantom.exit();
};
}
// Execute the query every 20ms
waitForMath = window.setInterval(
waitForMathJax,
20
);
This is a sample setup for MathJax (ConfigMathJax.js). The MathJax.Hub.Config part can be changed, but you'll have to use the MathJax HTML-CSS output variant; for the moment I was unable to get it to work with the SVG output.
// A trigger which is queried by the script
// render-math-and-dump.js in PhantomJS
var MathJaxFinished = false;
// A sample MathJax setup
MathJax.Hub.Config({
displayAlign: "left",
displayIndent: "3em",
extensions: ["tex2jax.js"],
// HTML Output
jax: ["input/TeX", "output/HTML-CSS"],
imageFont: null,
messageStyle: "none",
showProcessingMessages: false,
showMathMenu: false,
delayStartupUntil: "onload",
tex2jax: {
ignoreClass: "tex2jax_ignore",
processClass: "math",
inlineMath: [ ["$","$"], ["\\(","\\)"] ],
displayMath: [ ["$$","$$"], ["\\[","\\]"] ],
processEscapes: false
},
TeX: {
extensions: ["AMSmath.js","AMSsymbols.js"],
TagSide: "right",
TagIndent: "1em",
MultLineWidth: "85%",
equationNumbers: {
autoNumber: "AMS"
},
unicode: {
fonts: "STIXGeneral,'Arial Unicode MS'"
}
},
"HTML-CSS": {
scale: 100,
minScaleAdjust: 50,
linebreaks: {
automatic: true
},
// Anchors in formulas in black
styles: {
".MathJax a": {
color: "#000000"
},
".MathJax": {
"vertical-align": "baseline"
}
},
mtextFontInherit: true,
matchFontHeight: true,
availableFonts: ["TeX"],
preferredFont: "TeX",
webFont: "TeX",
imageFont: null
}
});
// This function will be called when MathJax has finished *all* rendering
MathJax.Hub.Queue(
function () {
MathJaxFinished = true;
}
);
The content of test.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="MathJax/MathJax.js"></script>
<script type="text/javascript" src="ConfigMathJax.js"></script>
</head>
<body>
<p>Formula 1:
<span class="math">$$
\begin{equation}
\label{Formula1.Label}
P (\Omega) = \int_{-\infty}^{+\infty} f (x) \: dx = 1
\end{equation}
$$</span></p>
<p>Formula 2:
<span class="math">$$
\begin{equation}
\label{Formula2.Label}
E (X) := \int_{-\infty}^{+\infty} x \: f (x) \; dx
\end{equation}
$$</span></p>
<p>A third one, with references to 1 and 2:
<span class="math">$$
\begin{equation}
\begin{array}{ll}
E (a X + b) & = \int_{-\infty}^{+\infty} (a x - b) \: f(x) \: dx
\\
\\ & =
a \int_{-\infty}^{+\infty} x \: f(x) \: dx + b \int_{-\infty}^{+\infty} f(x) \: dx
\\
\\ & \underset{\eqref{Formula1.Label}, \eqref{Formula2.Label}}{=}
a E(X) + b
\end{array}
\end{equation}
$$</span></p>
<p>And another one:
<span class="math">$$
\begin{equation}
V(X) = \frac{a^2 + b^2 + c^2 - ab - ac - bc}{18}
\end{equation}
$$</span></p>
</body>
</html>
You can now use:
./phantomjs render-math-and-dump.js test.html | prince --javascript -o test.pdf -
The resulting PDF is attached. I also attached a rendering from the web (http://www.feynmanlectures.caltech.edu/I_47.html), due to the nature of processing it with the primitive script locally it lacks images and CSS but it shows that perhaps with some modifications rendering math from the web could be made possible.
The script does the following:
- Load and render the document given on the commandline with PhantomJS (WebKit based).
- Wait for MathJax to finish its work (can take quite long). The result is a DOM with all modifications made by MathJax.
- Remove all MathJax related <script> tags.
- Optionally create non-self-closing tags which enables the use of -i xml with Prince.
- Dump the final DOM to the console.
I haven't tried it on Windows yet, but I think it will work the same way.
Please note that this isn't meant to be an out-of-the-box solution, I'm sure the script can be made better (I'm not a JS developer). For further information please look at the comments in the scripts.
And please forgive the formatting, the code area in this forum is a bit narrow, so just paste the code in your favorite editor.