I'VE GOT THE BYTE ON MY SIDE

57005 or alive

Better TeX math typesetting in Hugo

Aug 7, 2016 math blog web 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: {{< tex "\sum_{n=1}^{\infty} 2^{-n} = 1" >}}.

Display mode math looks like
    {{< tex display="\int \frac{1}{x} dx = \ln |x|" >}}

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\) .

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


Shortcode and supporting markup

Here’s the definition of the shortcode:

<!-- Hugo TeX shortcode
     usage:
         inline eqn:       {{< tex "eqn here" >}}
         display mode eqn: {{< tex display="eqn here" >}}
-->
<span class="jsonly">
    {{ if .IsNamedParams }} <!-- display mode, wrap eqn with $$ $$-->
        $${{ .Get "display" }}$$
    {{ else }}              <!-- inline mode, wrap eqn with \(  \)-->
        \({{ .Get 0 }}\)
    {{ end }}
</span>
<noscript>
    {{ if .IsNamedParams }} <!-- display mode -->
        <div style="text-align:center;">
            <img src="https://latex.codecogs.com/gif.latex?{{ .Get "display" }}" title="{{ .Get "display" }}" />
        </div>
    {{ else }}              <!-- inline mode -->
        <img style="display:inline;vertical-align:middle;" src="https://latex.codecogs.com/gif.latex?\inline&space;{{ .Get 0 }}" title="{{ .Get 0 }}" />
    {{ end }}
</noscript>

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:

<!-- top of page -->
{{ if .GetParam "hasMath"}}
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/contrib/auto-render.min.js"></script>
{{ end }}


<!-- bottom of page -->
{{ if .GetParam "hasMath"}}
  <script>renderMathInElement(document.body);</script>
{{ 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 <noscript> 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: .

Display mode math looks like


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:

  <noscript>
    <style>.jsonly { display: none }</style>
  </noscript>

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:

Here’s a snippet of PowerShell that does the trick:

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

# delete jsonly spans completely
$content = $content -replace '&lt;span class=&#34;jsonly&#34;&gt;\s*.+?\s*&lt;/span&gt;',''

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

[io.file]::WriteAllText('index.xml', $content)

What’s left should look fairly decent in most RSS readers.