Daiji256

超高速な数式表示(KaTeXより高速!)

公開
更新

現在はHugoで作成しておらず、ここで説明される数式表示方法にはしていません。そのため、紹介方法の結果を表示することができません。

はじめに

ここではHugo用の説明をしますが、他の静的サイトジェネレータ(Hexo、Jekyllなど)でも使える内容だと思います。静的サイトジェネレータ向けであり、動的なサイトにはオススメしません。

静的サイトジェネレータはその名の通り、静的なサイトを生成します。WordPressのようにユーザがサイトにアクセスするたびに、ページを作成する動的なものと比べ、読み込み速度などのパフォーマンスが高いメリットがあります。

科学技術に関する内容の記事を書く場合、サイト上に数式を表示したくなります。数式表示にはMathJaxやKaTeXがよく利用されています1。これらはユーザがサイトにアクセスすると、LaTeX記述の数式をJavaScriptを用いてブラウザ上で表示させます。この処理のせいで、サイトにアクセスした直後少しの間、数式がちゃんと表示されません。

当たり前ですが、数式の見た目は何度読み込んでも同じです。せっかくHugoを使っているのに、これでは何となく嫌な感じです。最初からHugoでHTMLを生成するときに数式を埋め込んでしまえば、ユーザがアクセスするたびに、MathJaxやKaTeXの処理をしなくていいです。

こんなこといいな、できたらいいな…

できます!

目標

このコードのようにLaTeX数式コードをSVGへ変換し挿入するShortcodeを作成することを目標にします。作り方なんて興味ないから使い方教えろって人は使い方を見てください。

{{< tex2svg >}}E=mc^2{{< /tex2svg >}}
<svg> ... </svg>

いざ作成

アイデアはtex2svgと同じです。個人的に使いやすくするようにカスタマイズします。

LaTeXからSVGに変換

LaTeXをSVGに変換するツールは色々あります。しかし、Hugoから外部コマンドは実行できないので、簡単ではなさそうです。

HugoはgetJSONでインターネット上のJSONを取得できます。LaTeXをSVGの情報を含むJSONを返すAPIを探したところ、tex2svgとCODECOGSが見つかりました。

インライン表示が綺麗に見えないと嫌なので、tex2svgを利用します。

tex2svgはhttps://tex2svg.herokuapp.com/?+q=E=m^2+&inline=trueのように叩きます。q=に表示したい数式のLaTeXコードを書きます。&inline=trueを書くとインライン表示になります。また、ディスプレイ表示のときclass=tex、インライン表示のときclass=i-texにしてくれます。

APIは次のようなJSONを返します。svgにvertical-alignなどのパラメータもすでに含まれているので、svgだけを利用します。

{
  "speakText": "equation",
  "svg": "<svg ...",
  "width": "...ex",
  "height": "....ex",
  "style": "vertical-align: ...ex;"
}

Shortcodes

getJSONはURLがstr1str2str3のとき"str1" "str2" "str3"のように分割して指定できます。(querify "q" .Inner)は{{< tex2svg >}}と{{< /tex2svg >}}の間のテキストをq=%5Cfrac%7B1%7D%7Bx%7Dみたいにします。インライン表示するときは&inline=trueを加えます。svgには\nなどのコードを含むので、それをsafeHTMLでいい感じにして表示させます。

{{- $json := getJSON "https://tex2svg.herokuapp.com/" "?" (querify "q" .Inner) "&inline=true" -}}
{{- $json.svg | safeHTML -}}

Shortcodeの引数を指定して表示方法の選択ができたほうが便利です。{{- if eq (.Get 0) "display" -}}で引数にdisplayの有無に応じて、<span>と<div>を分岐し、&inline=trueを適宜付けます。具体例は次節の使い方で紹介します。

使い方

この節では使い方だけを説明します。コピペで簡単に利用できるので、「中身とか知らねーけど使わせろ」って人はここだけ見てください。

shortcodesに次のコードをtex2svg.html2として保存してください。

{{- if eq (.Get 0) "display" -}}
<div class="displaystyle">
  {{- $json := getJSON "https://tex2svg.herokuapp.com/" "?" (querify "q" (replace .Inner "\\bm" "\\boldsymbol")) -}}
  {{- $json.svg | safeHTML -}}
</div>
{{- else -}}
<span class="inline">
  {{- $json := getJSON "https://tex2svg.herokuapp.com/" "?" (querify "q" (replace .Inner "\\bm" "\\boldsymbol")) "&inline=true" -}}
  {{- $json.svg | safeHTML -}}
</span>
{{- end -}}

次のようにMarkdownで記事を書いてみてください。

ディスプレイ表示の数式は
{{< tex2svg display >}}
  (f \ast g)(t)=\int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, \mathrm{d}\tau
{{< /tex2svg >}}
となる。

インライン表示の数式は
{{< hgblock display >}}< tex2svg >}}
  (f \ast g)(t)=\int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, \mathrm{d}\tau
{{< /tex2svg >}}
となる。

次のような表示(出力)が確認できます。


ディスプレイ表示の数式は

(f∗g)(t)=∫−∞∞f(τ)g(t−τ)dτ

となる。

インライン表示の数式は

(f∗g)(t)=∫−∞∞f(τ)g(t−τ)dτ

となる。


もちろんalign環境も使えます。

f(x)=x+1g(x)=x2h(x)=1x

インライン表示のときは<span class="inline"> ... </span>で囲み、ディスプレイ表示のときは<div class="display"> ... </div>で囲みます。CSSで設定することで、表示方法を簡単に設定できます。

おまけ

カスタマイズ

ディスプレイ表示を中央へ

ディスプレイ表示の時はTeXみたいに中央に置きたい場合はCSSで左右にマージンを与えます。

.tex {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

フォントサイズを変更したい

ちょっと本文より大きくて嫌だな~と思ったので、フォントサイズを0.9倍させました。

.tex {
  font-size: 0.9em;
}
.i-tex {
  font-size: 0.9em;
}

\bmを使いたい

MathJaxはデフォルトでは\bmを使えないので、代わりに\boldsymbolで太字にします。\bmを\boldsymbolに置換すればもっと便利です。

replace .Inner "\\bm" "\\boldsymbol"

テキスト選択したい

単なる画像としてのSVGなので、数式をコピペできません。SVG内に見えないテキストを用意してコピペできるようにしたいと思います。replaceを使って</svg>を<text> ... </text></svg>に置換して、無理やりテキスト情報を入れます。CSSテキストのスタイルを指定します。色を無色rgba(0,0,0,0)にします。SVG内のフォントサイズはちょっとアレですごく小さいみたいなので、適当に超大きくなるように設定します。

{{- (replace $json.svg "</svg>" (printf "<text x=\"0\" y=\"0\" class=\"eq-text\">%s</text></svg>" $.Inner)) | safeHTML -}}
.eq-text {
  fill: rgba(0, 0, 0, 0);
  font-size: 100ex;
}

tex2svg

MathJaxにはパッケージを追加したり、フォントを変更する機能があります。tex2svgではデフォルト状態になっています。MathJaxの設定をやりたい場合は自分でAPIを用意してください。ソースコードはtex2svgを参考にすれば良いと思います。ローカルでHugoを使う場合はlocalhost:3000にAPIをたてたとしたら、https://tex2svg.herokuapp.com/をhttp://localhost:3030/に置きかえらば良いです。僕はGitHub Actionsでhugoしているので、tex2svgを直接利用しています3。

文献

  1. MathJax
  2. KaTeX
  3. tex2svg
  4. CODECOGS
  5. 超高速な数式表示(KaTeXより高速!)

脚注

  1. KaTeXはMathJaxより早いです。このブログではKaTeXを利用していました。 ↩

  2. 好きな名前で構いません。僕は短いほうが楽なのでeq.htmlにしています。 ↩

  3. まあ、自分でHerokuとかRepl.itとかでAPIを公開すればいいんだけどね… ↩