Hugo で数式を静的(ビルド時)にレンダリング
このブログでもそうでしたが、Hugo で数式を表示するには、ブラウザで KaTeX か MathJax を使ってレンダリングする構成が一般的でした。
Hugo v0.132.0 から、KaTeX 数式をビルド時に静的にレンダリングする関数 transform.ToMath
が追加され、これにより、WASM で動作する KaTeX を使って数式をレンダリングしたものを出力することができ、ブラウザ実行時の JavaScript ライブラリの読み込みが不要になりました。レンダリング結果が最初から入っているので、表示ラグも(ほぼ)発生しません1。
このブログで使用している、拙作テーマ「名古屋」v2.0〜 でもこの機能を導入しているので、その設定を紹介します。
構成
以前は以下のように、ブラウザで数式をレンダリングするための JavaScript を読み込む必要がありました。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" onload="renderMathInElement(document.body);"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
{ left: "\\(", right: "\\)", display: false },
{ left: "\\[", right: "\\]", display: true },
],
throwOnError: false,
});
});
</script>
上記 3 つの script
は削除し、代わりに Passthrough テンプレートを用意することで、Hugo のテンプレートエンジンに数式の処理をさせます。
必要なものは 3 つです。
- Passthrough テンプレート
- CSS(これまでも使っていたやつ)
- サイト設定で Passthrough を有効にする
Passthrough テンプレート
最初に、Passthrough の中身を処理するためのテンプレートを用意します。
例えば、Passthrough のブロックデリミタとして $$
を有効にしていた場合、Markdown 内で $$
〜 $$
で囲んだ部分が .Inner
で渡ってきます2。
{{ $options := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") "throwOnError" true }}
{{ transform.ToMath .Inner $options }}
よって、それを transform.ToMath
に渡してやることで数式をレンダリングできます。
$options
は辞書型で KaTeX のレンダリングオプションを表します。
KaTeX のオプションがそのまま使えるのが嬉しいですね。
不正な数式に対しては、ビルドを失敗させてほしいので、throwOnError
を true
にしています。
CSS
MathML のみの出力で構成する(output
を mathml
と設定する)と真に KaTeX 用の追加 CSS が不要になりますが、MathML の表示はブラウザによっては非対応なので、htmlAndMathml
で構成し、KaTeX の CSS を読み込むことが実質的には必要になります3。
<link href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css" rel="stylesheet">
この CSS は、静的レンダリング以前に使っていたものと同一です。
サイト設定
あとは、サイト設定で Passthrough を有効にすると、ユーザーが指定したデリミタ内の数式を、先のテンプレートによって出力することができます。
[markup]
[markup.goldmark]
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['$$', '$$'], ['\[', '\]']]
inline = [['$', '$'], ['\(', '\)']]
デリミタはユーザーが指定したものしか有効にならず、また、ユーザーが Passthrough を有効にしない限り意図せぬ特殊文字による想定外の動作を防げるという点が、テーマ制作者的にも気に入っています。
デメリット
強いて言えば、JavaScript を止めたので、後から動的生成される数式テキストはレンダリングされなくなります。また、Markdown 外の数式テキスト(HTML テンプレート内など)は自動ではレンダリングされないです(transform.ToMath
を呼び出す必要があります)。
まとめ
- ビルド時に数式をレンダリングしておくことで数式表示が速くなる
- Hugo の WASM 連携機能強化で、今後 KaTeX に限らずいろいろできることが増えるかもしれない