この数式表示を実際に利用した 僕のブログ を確認してもらえると嬉しいです。
この記事は静的サイトジェネレータ向けの内容です。もちろん動的なサイトでも使えますが,おすすめしません。
はじめに
僕のブログは静的サイトジェネレータの 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>
いざ作成
アイデアは文献 4 の tex2svg (GitHub) と同じです。個人的に使いやすくするようにカスタマイズします。
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 >}}
となる。
インライン表示の数式は
{{< 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$ となる。
もちろん 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 で左右にマージンを与えます。
.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 (GitHub) を参考にすれば良いと思います。ローカルで HUGO を使う場合は localhost:3000
に API をたてたとしたら,https://tex2svg.herokuapp.com/
を http://localhost:3030/
に置きかえらば良いです。僕は GitHub Actions で HUGO しているので,tex2svg を直接利用しています3。