Reputation: 11059
I want to render math in a page with latex mode in node.js
. I have looked at MathJaX
and KaTeX
.
I render my page with
router.get('/math', function (req, res) {
res.render('math');
});
so how can I make sure the math on this page is rendered as math?
I can use
const katex = require('katex');
const math = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", { displayMode: true });
and then set variables in the template with
router.get('/math', function (req, res) {
res.render('math', { math: math });
});
but I would rather want to write all the math directly in the template instead of setting each variable specifically in the javascript code.
I am getting the html from the template with
router.get('/math', function (req, res) {
res.render('math', function (err, html) {
html = html.replace(/\$\$(.*?)\$\$/g, function (outer, inner) {
return katex.renderToString(inner, { displayMode: true });
});
res.send(html);
});
});
is it a good way to do it or can I omit calling res.render()
before using res.send()
?
When I use
html = html.replace(/\$\$(.*?)\$\$/g, function (outer, inner) {
return katex.renderToString(inner, { displayMode: true });
}).replace(/\$(.*?)\$/g, function (outer, inner) {
return katex.renderToString(inner);
});
the server fails and I get the error ParseError: KaTeX parse error: Expected 'EOF', got '$' at position 1: $_
Upvotes: 3
Views: 2786
Reputation: 519
/\\\((.*?)\\\)/g
does not work in many cases, as the following happens:
Multiple formulas are recognized as one, resulting in errors.
To prevent this, use
/\\\(((.)*?(?=\\\)))?\\\)/
Example
html = html.replace(/\\\(((.)*?(?=\\\)))?\\\)/g, function(a, b) {
return katex.renderToString(b, { displayMode: false });
}).replace(/\\\[((.)*?(?=\\\]))?\\\]/g, function(a, b) {
return katex.renderToString(b, { displayMode: true });
}) ...
However, there are still caveats, for instance things not being rendered properly. An alternative approach could be https://joa.sh/posts/2015-09-14-prerender-mathjax.html (this is the next one I am going to try ...)
Upvotes: 1
Reputation: 60968
The KaTeX core doesn't care about where the text input comes from. Identifying TeX source snippets is not part of its objective. There is a contributed extension called auto-render which is maintained as part of the KaTeX code base. It will identify TeX input within a page, and replace it with KaTeX-rendered HTML. But it operates client-side on the DOM-tree, not server-side on the HTML markup text.
So I suggest you roll your own code here. I guess that you shouldn't need any DOM parsing. Instead I'd try to come up with some suitable regular expressions to describe math blocks, and then replace these by their renderToString
analogon. Something like
html = html.replace(/\$\$(.*?)\$\$/g, function(outer, inner) {
return katex.renderToString(inner, { displayMode: true });
}).replace(/\\\[(.*?)\\\]/g, function(outer, innner) {
return katex.renderToString(inner, { displayMode: true });
}).replace(/\\\((.*?)\\\)/g, function(outer, innner) {
return katex.renderToString(inner, { displayMode: false });
});
Depending on your use case, you may want to apply this substitution to your input template, to the arguments you provide to your template, or to the result you get from rendering the template. In all three cases you should try to get your hands on the relevant portion of HTML text as a single string at some point. Which in some cases might involve buffering a stream-based template output. Since you didn't say what frameworks you are using for templates and app server, I can't provide more detail on this.
Note that the above gives TeX a higher priority than HTML: Input like $$a<p>b$$
are interpreted as TeX input a < p > b
. This is in contrast to client-side rendering (as by the auto-renderer), where the above would be treated as two paragraphs, neither of them containing a complete TeX input fragment, and where to achieve a < p > b
rendering one would have to encode the <
as <
. If you control all your input, giving TeX priority is likely what you want. If you accept user-provided input, though, then this behavior might cause surprises with some content-sanitizing procedures, or perhaps also Wiki markup formatting code. So if you intend to do anything along those lines, make sure you know what behavior you want, and make your clients aware of it.
If you want to aim for even higher compatibility with TeX, you could try supporting additional top-level environments. For example, you could include
html = html.replace(/\\begin\{align\*\}(.*?)\\end\{align\*\}/g, function(outer, inner) {
return katex.renderToString("\\begin{aligned}" + inner + "\\end{aligned}", { displayMode: true });
})
using the fact that the aligned
environment has been implemented while the align*
environment has not.
In response to your edit:
The combination of res.render
with a callback and res.send
inside that callback looks good to me. You could avoid calling res.render
if you called the template rendering yourself instead, but it's up to you to decide whether that is desirable.
The cause of the error message is hard to decide without knowing the input. It seems as though you might have some input starting with a double $$
but ending only with a single $
, so that the first regexp fails to match and the second includes an additional $
in its matched string.
Upvotes: 3