超高速な数式表示(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
:MathJaxによって数式を生成します。HerokuでAPIが公開されていてのレスポンスは少し遅いです。- CODECOGS:昔からある有名な奴です。SVG以外にPNGやGMPとかも生成できます。ベースラインが無く、インラインで表示すると微妙になる。
インライン表示が綺麗に見えないと嫌なので、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.html
2として保存してください。
{{- 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 >}}
となる。
次のような表示(出力)が確認できます。
ディスプレイ表示の数式は
となる。
インライン表示の数式は
となる。
もちろんalign
環境も使えます。
インライン表示のときは<span class="inline"> ... </span>
で囲み、ディスプレイ表示のときは<div class="display"> ... </div>
で囲みます。CSSで設定することで、表示方法を簡単に設定できます。
おまけ
カスタマイズ
ディスプレイ表示を中央へ
ディスプレイ表示の時はTeXみたいに中央に置きたい場合はCSSで左右にマージンを与えます。
.tex {
display: block;
margin-left: auto;
margin-right: auto;
}
フォントサイズを変更したい
ちょっと本文より大きくて嫌だな~と思ったので、フォントサイズを倍させました。
.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。