名古屋出身ソフトウェアエンジニアのブログ

Hugo で数式を静的(ビルド時)にレンダリング

公開:
更新:

このブログでもそうでしたが、Hugo で数式を表示するには、ブラウザで KaTeX か MathJax を使ってレンダリングする構成が一般的でした。

Hugo v0.132.0 から、KaTeX 数式をビルド時に静的にレンダリングする関数 transform.ToMath が追加され、これにより、WASM で動作する KaTeX を使って数式をレンダリングしたものを出力することができ、ブラウザ実行時の JavaScript ライブラリの読み込みが不要になりました。レンダリング結果が最初から入っているので、表示ラグも(ほぼ)発生しません1

transform.ToMath
Renders mathematical equations and expressions written in the LaTeX markup language.

このブログで使用している、拙作テーマ「名古屋」v2.0〜 でもこの機能を導入しているので、その設定を紹介します。

GitHub - curegit/nagoya: レスポンシブ対応、日本語特化 Hugo ブログテーマ
レスポンシブ対応、日本語特化 Hugo ブログテーマ. Contribute to curegit/nagoya development by creating an account on GitHub.

構成

以前は以下のように、ブラウザで数式をレンダリングするための 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 つです。

  1. Passthrough テンプレート
  2. CSS(これまでも使っていたやつ)
  3. サイト設定で Passthrough を有効にする

Passthrough テンプレート

Passthrough render hooks
Create a passthrough render hook to override the rendering of text snippets captured by the Goldmark Passthrough extension.

最初に、Passthrough の中身を処理するためのテンプレートを用意します。

例えば、Passthrough のブロックデリミタとして $$ を有効にしていた場合、Markdown 内で $$$$ で囲んだ部分が .Inner で渡ってきます2

{{ $options := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") "throwOnError" true }}
{{ transform.ToMath .Inner $options }}

よって、それを transform.ToMath に渡してやることで数式をレンダリングできます。

$options は辞書型で KaTeX のレンダリングオプションを表します。 KaTeX のオプションがそのまま使えるのが嬉しいですね。

不正な数式に対しては、ビルドを失敗させてほしいので、throwOnErrortrue にしています。

CSS

MathML のみの出力で構成する(outputmathml と設定する)と真に 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 に限らずいろいろできることが増えるかもしれない

  1. 外部フォントを使うことによるラグが多少あるように見える。 ↩︎

  2. ちなみに、デリミタ内で Markdown の特殊文字をエスケープする必要はありません。 ↩︎

  3. この CSS は CDN でなくローカル配置でも良い。フォントファイルも一緒に配置することを忘れずに。 ↩︎