MathJaxとKaTeXによるSSRを試す
はじめに
Webサイトで形式で記述された数式をレンダリングする際に用いるライブラリとしてMathJaxやKaTeXが挙げられるが、そのレンダリング部分を可能な限りサーバサイドでやらせてみたい。基本的な方針としては、JavaScriptなしで数式を表示できるかつ、消費するリソースは最低限になるようにしたい(別に今どきそんなのに耐えられない貧弱な機器があるとは思えないがロマンがある)。あと、何ならネットに接続していないとしても数式が表示できるようなものを利用したい。ただし、今回はMathMLについては検討対象から除外する。
基本的にWebサイト上でJavaScriptなしで数式をレンダリングすることを考えると、以下の方法が挙げられるだろう。
HTML+CSSによるレンダリング
ラスタ画像の埋め込み
ベクタ画像の埋め込み
これらのうち、利用する場合の選択肢として入るのは、1つ目か3つ目の方法だろう。今回の目的と照らし合わせて考えると、1つ目の方法は通常WOFF(Web Open Font Format)が必要になり、3つ目の方法はディスプレイのサイズ変更に関する文字サイズの変更であったり、インライン画像として挿入するのに難があるだろうとパッと考え付く。
結論としてはMathJaxでSVGを出力するかKaTeXでHTMLを出力するかの2択となるが、それは単にSVGの分かフォントファイルの分のどちらを取るかといった具合である。ただ、SVGの特性上、長いインライン数式だと文字の折り返しに難があるため、そこの部分については扱いに注意する必要がある。
MathJax
MathJaxによる数式の出力形式には、HTML、SVGおよびMathMLの3つが存在する。どうやら、MathJaxのバージョン3からはネイティブでNode.jsが利用できるらしいため、Node.jsでレンダリングを行う(バージョン3の利用は初めて)。
というわけで、早速MathJaxを導入する。
パッケージのバージョンについては以下のとおりである。
Node.js上でのMathJaxの使い方については、以下に多くのサンプルが公開されているため、それを参考にして使い方を以下に示す。
単純な数式のレンダリング
特に何も考えずにレンダリングをしたい場合、SVGで数式を出力する場合は以下のようになる。
HTMLで出力する場合はCSSもセットで必要になるため、以下のように追加でCSSを出力する分も記載する必要がある。コメントにも記載してあるが、MathJaxにより生成されるCSSはコンテキストに依存するため、入力した数式によってCSSの内容も異なることに注意する必要がある(もちろん部分的には基本的な部分は一致する)。
HTML上に記述された数式のレンダリング
具体的にHTML上で記述された数式に対してレンダリングを行う方法について示す。基本的な方法は上記で述べた方法と同一であるが、typeset
を用いることで従来のでみられるようなマークアップが可能である。以下にそのコードを示す。なお、この例は数式をHTMLへ変換するものであるが、容易にSVGを出力するコードに変換可能である。
単純なテキストファイルの変換であればこの変換でよいのだが、実際の変換元はHTMLファイルなどといったマークアップ言語により構造化されたテキストを想定しているため、無作為に数式としてマークアップされてしまうのは困る。そのため、実際に利用する場合は適当にパースしながら変換をかける必要がある。
今回はHTMLファイル中に記載された数式をパースすることを目的とするため、Node.jsでDOM操作が利用が可能なJSDOMを導入する。
パッケージのバージョンについては以下のとおりである。
というわけで、JSDOMを用いてHTMLをパースしながら数式を変換して出力するコードを示す。上で示したコードはHTMLへ変換するものであったため、今回はSVGに変換する場合を示す。実際に用途を考えると、各数式にaria-label
属性を持たせるべきであるため、どちらにしてもDOMの解析は必要だろう。
実際に数式の変換元となるサンプルのHTMLファイルを以下に示す。このHTMLでは簡単なフォントサイズを変換するロジックを組み込んである。このHTMLを変換して動作を見てみるとわかることではあるが、SVGで出力してもフォントサイズは自動的にスケーリングしてくれるため、外部のフォントや動的に生成されるCSSが不要なSVGの利用だけでいい気もする(少なくともバージョン2の頃はできなかったはず)。
ただ、手元のスマートフォンで確認をするとかなり大きくSVGによる数式がレンダリングされる事象が発生した(Qiitaの数式付きの記事をスマホで見るとなんか数式が大きく見えるものと同じ事象)。現状確認できている発生する環境はChromium系(Android)である。おそらくであるが、MathJaxで出力する数式のサイズがex
を基本としているのに対して、異常が発生する環境ではex
におけるスケールの方法が誤っているのだと思われる(パッと見ではex
がem
としてスケールされている?)。そのため、ex
からem
の単位系に変換すれば正常に表示できるだろうということであるが、それぞれのサイズの比は環境依存であるため、完璧な変換は不可である。しかしながら、MathJaxの以下のドキュメントによれば、デフォルトのサイズ比は0.5を採用しているように見えるため、0.5でスケーリングするというのが妥当であろう。
上記で述べたようなスケーリングを適用した場合のコードの該当部分は以下のとおりである。これにより、若干数式のSVGのスケールが変化するが、全然問題ない範囲であり(なんなら変換前の数式が若干大きく見える問題が解消して見やすいぐらいである)、問題が生じたい環境でも正常に表示される。
KaTeX
KaTeXによる数式の出力形式はHTMLとMathMLの2つである。KaTeXもNode.js上で動作するため早速導入をする。
パッケージのバージョンについては以下のとおりである。
単純な数式のレンダリング
特に何も考えずにレンダリングをしたい場合、以下のように出力することができる。MathJaxと比較してできることがシンプルな分、かなり簡潔に記述することができる。
CSSについてはMathJaxのように動的に生成されるものではなく、静的なものが与えられている。具体的にはnode_modules/katex/dist
配下に存在するCSS(例えばkate.css
)をリンクすればいい。ただし、CSSをセルフホストするとなると、フォントファイルもセルフホストする必要があるため、個人で利用する分にはCSSはCDNに設置するのが無難と思われる(もちろん、CSSに記載されたフォントファイルのパスを変更してもいいだろう)。
HTML上に記述された数式のレンダリング
具体的にHTML上で記述された数式に対してレンダリングを行う方法について示す。基本的な方法は上記で述べた方法と同一であり、やり方もMathJaxの場合とほとんど変わらない。以下にその実装を示す。本来であればkatex.render
を使いたいところであったが、どうやらdocument
が見つからないとエラーが出るっぽいため、しかたなくkatex.renderToString
を利用している(回避手段もあるが、使わなくても真っ当にできる手段があるがあるならばそちらを利用した方がいいだろう)。
実際に数式の変換元となるサンプルのHTMLファイルを以下に示す。このHTMLはCSSを手動でリンクしていることを除き、MathJaxで示したものと同一である。
おわりに
今回はMathJaxとKaTeXの双方を利用してみたが、フォントファイルのロードを考えると、単一のHTMLファイルで完結するという意味でMathJaxでSVGを出力する場合の方が個人的には好みではある。実際に利用する場合はMathJaxでSVGを出力するかKaTeXでHTMLを出力するかの2択となるが、それは単にSVGの分かフォントファイルの分のどちらを取るかといった具合である。致命的な速度差もないと思うので、使いたい方を使えばいいだろう。ただ、MathJaxでSVGを出力する場合は、SVGの特性上、長いインライン数式だと文字の折り返しに難があるという欠点があるため、そこには注意する必要がある。
正直なところ、MathMLがネイティブでLaTeXライクな数式のレンダリングをサポートするのが一番簡潔だと思うのだが、そんな未来は今のところ来そうにない。CSS組版という概念もあるのだし、そろそろ来てもいいと思う。