Better TeX math typesetting in Hugo

Better TeX math typesetting in Hugo

There is a page in the Hugo documentation that describes how to use MathJax to embed nicely-typeset mathematics in one’s Hugo-generated site.

For my own site, I took this as a starting point and made a few improvements. Here’s how I do the math typesetting in this blog.

Authoring

I author my posts in Markdown, where the bulk of the content is plain text. When I want to include specially-typeset mathematical expressions, I use a custom shortcode to convert TeX-style equation definition into a snippet of HTML that looks nice when rendered in the browser.

So when I author a post, I’ll write something like this:

Here’s sum inline math: {{}}.

Display mode math looks like
{{}}
Hugo will process this, apply my shortcode, and generate HTML that ultimately renders like this:

Here’s sum inline math: \sum_{n=1}^{\infty} 2^{-n} = 1∑
​n=1
​∞
​​ 2
​−n
​​ =1 .

Display mode math looks like
\int \frac{1}{x} dx = \ln |x|∫
​x

​1
​​ dx=ln∣x∣
Shortcode and supporting markup

Here’s the definition of the shortcode:

<!– Hugo TeX shortcode
usage:
inline eqn: {{}}
display mode eqn: {{}}
–>

{{ if .IsNamedParams }}
$${{ .Get “display” }}$$
{{ else }}
\({{ .Get 0 }}\)
{{ end }}

{{ if .IsNamedParams }}

{{ else }}

{{ end }}

This takes care of inserting the required content and tags for each particular expression, but that alone isn’t enough. Some additional Javascript and CSS are required in the header and footer of the page, as well.

In my Hugo page template I conditionally include this extra markup, which will pull down the CSS and Javascript libraries that do the real typesetting work when someone loads the page:


{{ if .GetParam “hasMath”}}

https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js
https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/contrib/auto-render.min.js
{{ end }}


{{ if .GetParam “hasMath”}}
renderMathInElement(document.body);
{{ end }}
I simply add hasMath: true to the front matter of any post that has equations, and everything is taken care of. If a post doesn’t contain any mathematics, no superfluous scripts or CSS are included.

As you can see above, I am using KaTeX, a MathJax competitor from Khan Academy, as my backend typesetting library. KaTeX renders noticeably faster than MathJax, but has a more limited feature set. Given the meager sophistication of the equations appearing in this blog, I don’t need the more exotic features anyway, so KaTeX a pretty clear win.

Handling noscript

I’ve also taken care to include a block for each equation, which will activate when Javascript is disabled.

In such a case, KaTeX or MathJax won’t work, so I fall back to using flat images. Images don’t render as crisply, and won’t resize or zoom quite as well as proper HTML + CSS, but they will do in a pinch.

There are a few free cloud services available for this, which dynamically generate math typesetting images. You just embed the equation you want in the URL query parameters, and the server will create the image on the fly.

Google Charts is one such service. I’m currently using CodeCogs, as I find that its images look better than Google’s, and they support both inline and display-mode image rendering.

With Javascript disabled, the earlier example will render like this:

Here’s sum inline math: \sum_{n=1}^{\infty} 2^{-n} = 1.

Display mode math looks like

\int \frac{1}{x} dx = \ln |x|
To prevent the Javascript-only equation markup from appearing “raw” next to the images, I include a small bit of CSS in every page to hide such elements:

.jsonly { display: none }

RSS readers

Some people consume my blog through an RSS reader, rather than visiting my site directly. It would be nice if equations came through in a reasonable way for for these people, too.

Javascript is not exposed to RSS at all, so KaTeX and MathJax are out the window, and I am limited to HTML and inline CSS for formatting. Thankfully, this is exactly how the noscript fallback operates, so most of the work is already done.

After running Hugo to generate all of my site’s HTML and RSS content, I run a post-processing script to fix up the RSS XML slightly. The script does 2 things:

Removes the jsonly spans entirely, since they aren’t useful for RSS and will be rendered in their ugly raw form.
Removes the noscript opening and closing tags, leaving just the inner image content. Some RSS readers will not render content inside of noscript tags, so it’s important to remove these.
Here’s a snippet of PowerShell that does the trick:

$content = [io.file]::ReadAllText(‘index.xml’)

# delete jsonly spans completely
$content = $content -replace ‘<span class="jsonly">\s*.+?\s*</span>’,”

# remove tags, leaving just the inner content
$content = $content -replace ‘<noscript>\s*((?:.|\s)+?)\s*</noscript>’, ‘$1’

[io.file]::WriteAllText(‘index.xml’, $content)
What’s left should look fairly decent in most RSS readers.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s