ARTICLE
Adding LaTeX Rendering to Our Website, Part 2
From Hugo in Action by Atishay Jain
This article is all about adding LaTeX rendering to a static website built with Hugo.
Take 40% off Hugo in Action by entering fccjain into the discount code box at checkout at manning.com.
For the intro to this article, check out part 1.
Deploying to Heroku
In case you aren’t using Netlify, you need to select a vendor to host the APIs. Many cloud vendors support both the PAAS and the FAAS models and we’re free to decide what approach to take to deploy our website. Because Netlify uses the FAAS solution, we demonstrate a PAAS solution. Heroku is used as the PAAS platform, though we use little of its features and therefore the code should work in any PAAS platform we choose.
Heroku has integrations with both the Node.js ecosystem and GitHub making the deployment job for us easy. Heroku also provides continuous integration and deployment for our code without us having to write anything in our GitHub actions which makes our job easier. Once configured, we need to push the code to GitHub to get it live within Heroku.
- Once you sign up into Heroku, you’ll land into https://dashboard.heroku.com/apps where you can click on New > Create new app.
- Next, we need to give the app a name. App names are unique in Heroku. latex2svg has been reserved for this article.
- We can now decide the mode of integration. Direct GitHub integration is the easiest and we use that for our use case. To connect to GitHub, you can go to Deploy > Deployment method > GitHub and then click on Connect to GitHub
- After providing credentials, we can search for repositories that we want to connect to Heroku, select the correct repository and then click connect.
- Before triggering the deployment, we need to go to the Settings tab and in the Buildpacks section select node.js. Because we have both
go.sum
andpackage.json
, Heroku can get confused.
We need the node.js Buildpack for our APIs.
- In the settings there’s also a config vars section where we can define our environment variables. We need to define LATEX2SVG_PASSWORD for getting a password to restrict access.
- Once the build settings are setup we can trigger the deployment from Deploy > Manual Deploy. We should first do a manual deploy for verification that everything is correct before switching on automatic.
After the code goes live, we can call https://<endpoint>.herokuapp.com/latex2svg?tex=%5Cfrac%7Ba%7D%7Bb%7D&password=<password>
to get the same response that we got previously when we ran locally.
We can troubleshoot any issues looking at the application logs which are provided in the More menu on the top right where we can click on View logs
Monorepo vs separate repos for APIs and Markup
We decided to use the repository that was used for the website for the API code as well. This has a clear disadvantage of pushing the needless markup code to Heroku and rebuilding the Heroku based APIs every time a markup-based document has changes. If this turns into a big problem, we can choose to have a separate branch for the API or a separate repository. We can also change our integration to happen via GitHub actions instead of the direct Heroku import where we can build the smartness to recognize whether the API has changed. We can move to manual deploys if the APIs aren’t changing frequently.
The choice of a mono-repo has nothing to do with Hugo or the Jamstack. It’s a matter of personal preference. Netlify works a lot better with one repository and managing changes across APIs and their invocation in the core template code may be easier if using a monorepo.
Creating shortcode to render LaTeX
With a functional API, the major work needed to get to rendering LaTeX on the website is done. We need to invoke it and then render the result. Now we need to create a shortcode to call the API and display the result in the Hugo based website.
Although we use the APIs at compile time, if we want to use them at runtime, the steps to setup the APIs are exactly the same. Instead of calling the API via Hugo, we use the fetch function.
In the shortcode, we take the LaTeX code as the inner content of the shortcode and then call the latex2svg API to get the SVG version of this LaTeX document that we can convert to a resource using resources.FromString
or render inline as an SVG document. We’re rendering the SVG inline for our use case.
Although we can use the site.baseURL
in the endpoint in case we use Netlify, it doesn’t work for compile time API access when the website has never been pushed live before. This causes the need to have a deployment twice – once to get the function up and the second time to call it. Unless calling from JavaScript, it is advisable to have a fixed URL for the API present in the config.
The other parameters of the API can be converted into arguments for the shortcode. We use the default values as supplied in the official MathJax documentation at http://docs.mathjax.org/en/latest/web/typeset.html.
The corresponding file is also present in the resources (https://github.com/hugoinaction/hugoinaction/tree/ch11-resources/4)
{{/* layouts/shortcodes/latex.html */}}
{{/* layouts/shortcodes/latex.html */}}
{{/* defaults taken from http://docs.mathjax.org/en/latest/web/typeset.html
{{/* a Boolean specifying whether the math is in display-mode or inline mode */
{{ $display := true }} ❶
{{ with .Get "display" }}
{{ $display = . }}
{{ end }}
{{/* a number giving the number of pixels in an em for the surrounding font. */
{{ $em := 8 }}
{{ with .Get "em" }}
{{ $em = . }}
{{ end }}
{{/* a number giving the number of pixels in an em for the surrounding font. */
{{ $ex := 16 }}
{{ with .Get "ex" }}
{{ $ex = . }}
{{ end}}
{{/* a number giving the width of the container, in pixels. */}}
{{ $containerWidth := (mul 80 $ex) }}
{{ with .Get "containerWidth" }}
{{ $containerWidth = . }}
{{ end }}
{{/* a number giving the line-breaking width in em units. Default is a very large number (100000), so effectively no line breaking. */}}
{{ $lineWidth := 100000 }}
{{ with .Get "lineWidth" }}
{{ $lineWidth = . }}
{{ end }}
{{/* a number giving a scaling factor to apply to the resulting conversion. Default is 1. */}}
{{ $scale := 1 }}
{{ with .Get "scale" }}
{{ $scale = . }}
{{ end }}
{{ if (and $.Site.Params.Latex2Svg (getenv "LATEX2SVG_PASSWORD") ) }} ❷
{{ $json := getJSON $.Site.Params.Latex2Svg "?" (querify "tex" .Inner) "&password="
(getenv "LATEX2SVG_PASSWORD") "&display=" $display "&em=" $em "&ex=" $ex
"&containerWidth=" $containerWidth "&lineWidth=" $lineWidth "&scale=" $scale}}
{{ with $json.data }}
{{. | safeHTML}} ❸
{{ end }}
{{ end }}
❶ Setup all parameters with meaningful defaults
❷ Disallow calling the server if there’s no LATEX2SVG_PASSWORD or the endpoint isn’t defined.
❸ Embed the data without escaping the SVG content inline.
Next, we need to enter the Latex2Svg endpoint in the website config, which depends on whether you used Heroku or Netlify for deployment.
# config/_default/params.yaml
Latex2Svg: https://<endpoint>.herokuapp.com/latex2svg
# or
Latex2Svg: https://<endpoint>/.netlify/functions/latex2svg
If we don’t want to call into the cloud service, we can use the http://localhost:3000/latex2svg as the URL inside of config/development/params.yaml. We can also disable the call during development by setting this to blank.
Adding some LaTex in our website
With all the hard work done to get a shortcode to render latex, the next step is to write some LaTeX in our website. In our blog,we’ve a page on triangles.
Let’s add the formula for the area of a triangle in there:
<!-- content/blog/tropical triangles/index.md -->
{{< latex>}}\text{Area} = \frac{b \times h}{2}{{</latex>}}
Let’s do the same in circle:
content/blog/community/circle.md –>
{{< latex>}}\text{Area} = \pi r²{{</latex>}}
We can also do complex mathematical equations like the mathematical definition of a Bézier curve.
{{< latex>}}
<!-- content/blog/community/curve.md -->
\mathbf {B} (t)=\sum _{i=0}^{n}{n \choose i}(1-t)^{n-i}t^{i}\mathbf {P} _{i}
{{</ latex>}}
We can do inline LaTeX as well.
<!-- content/blog/community/circle.md -->
The area of a circle is {{<latex display="false">}} \pi r^2 {{</latex>}}
Code Checkpoint. Live at https://ch11–2.hugoinaction.com. Source code at https://github.com/hugoinaction/hugoinaction/tree/ch11-2
That’s all for this article. Thanks for reading.