Improved way of rendering TeX math in Hugo

2020-11-29

Tags: Hugo Equations KaTex TeX Python

Lincoln Atkinson wrote a post about TeX math typesetting in Hugo (archive) where he described a solution that could do TeX math typesetting with and without Javascript enabled.

I have improved slightly upon his method, creating a simple Python script to download the equation images prior to publication and serving them from the local web server.

Why?

The primary reason was that I wanted a missing noscript equation to be an error at compile time instead of just a dead link. When going to the codecogs website at the time of writing, I see the following in a little box:

Licence Details
Registered use:     CLOUD
Expiration date:    31-12-2020

What does that mean? I have no idea. It might mean that service transistions to a new backend at the start of 2021, or it might mean that the service completely shuts down.

How?

Lincoln created a shortcode1 that inserts an image with src=latex.codecogs.com/gif.latex? followed by the equation to render, ending with title= (the " are removed by Hugo).

Knowing this it is possible to create a script that finds all instances of the codecogs.com sources, downloads the equation as a file and replaces the src= attribute with the local file.

The benefits of this approach is that the noscript equations still work before the script is run, it’s just fetched from codecogs.com instead of locally.

Script

Running the script in the directory that contains the public folder will download equations, place them next to the .html file and replace the src= attribute.

#!/user/bin/env python3
from pathlib import Path
import requests
import urllib

EXTENSION = "svg"
LATEX_URL = f"https://latex.codecogs.com/{EXTENSION}.latex?"
LATEX_INLINE = f"https://latex.codecogs.com/{EXTENSION}.latex?\inline&space;"

for path in Path('public').rglob('*.html'):
    contents = Path(path).read_text();

    while (LATEX_INLINE in contents) or (LATEX_URL in contents):
        latex_url = ""
        if LATEX_INLINE in contents:
            latex_url = LATEX_INLINE
        elif LATEX_URL in contents:
            latex_url = LATEX_URL
        else:
            continue

        print("PATH: " + str(path))

        index = contents.find(latex_url)
        end = contents.find(" title=", index)
        equation = contents[index + len(latex_url):end]
        url = contents[index:end]

        f = requests.get(url).content
        with open(path.parent.joinpath(equation + f".{EXTENSION}"), 'wb') as w:
            w.write(f)

        print(f"\tReplacing {url} with {urllib.parse.quote(equation)}.{EXTENSION}")
        contents = contents.replace(url, urllib.parse.quote(equation) + f".{EXTENSION}")

    with open(path, 'w') as w:
        w.write(contents)

Future work

Ideally the equations would be done entirely at compile time without external dependencies. Currently it does not seem possible to do entirely in HTML, since the math requires some fonts that are not installed by default on most platforms. A LaTeX to SVG tool could be used, but would break the hugo serve tool since it does not seem possible to insert arbitrary commands in that process.


  1. Think of it like a function for Hugo that inserts text and can take parameters. ↩︎