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

この記事は静的サイトジェネレータ向けの内容ですもちろん動的なサイトでも使えますがおすすめしません

はじめに

このブログは静的サイトジェネレータのHugoで作成していますここではHugo用の説明をしますが他の静的サイトジェネレータHexoJekyllなどでも使える内容だと思います

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

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

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

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

できます!

目標

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

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

いざ作成

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

LaTeXからSVGに変換

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

HugogetJSONでインターネット上のJSONを取得することができますLaTeXSVGの情報を含むJSONを返すAPIを探したところtex2svgCODECOGSが見つかりました

  • tex2svgMathJaxによって数式を生成しますHerokuAPIが公開されていてのレスポンスは少し遅いです
  • CODECOGS昔からある有名な奴ですSVG以外にPNGGMPとかも生成できますベースラインが無くインラインで表示すると微妙になる

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

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

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

1
2
3
4
5
6
7
{
  "speakText": "equation",
  "svg": "<svg ...",
  "width":"...ex",
  "height":"....ex",
  "style": "vertical-align: ...ex;"
}

Shortcodes

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

1
2
{{- $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として保存してください

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{{- 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で記事を書いて見てください

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ディスプレイ表示の数式は
{{< tex2svg display >}}
  (f \ast g)(t)=\int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, \mathrm{d}\tau
{{< /tex2svg >}}
となる

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

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


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

(f \ast g)(t)=\int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, \mathrm{d}\tau(f \ast g)(t)=\int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, \mathrm{d}\tau

となる

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


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

\begin{align} f(x) &= x+1\\ g(x) &= x^2\\ h(x) &= \frac{1}{\sqrt{x}} \end{align}\begin{align} f(x) &= x+1\\ g(x) &= x^2\\ h(x) &= \frac{1}{\sqrt{x}} \end{align}

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

おまけ

カスタマイズ

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

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

1
2
3
4
5
.tex {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

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

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

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

\bmを使いたい

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

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

テキスト選択したい

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

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

tex2svg

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

文献

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

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

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

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