Cacao's repaying
メモとか作業ログとか

KaTeXの出力をクライアントサイドでSVGに変換するやつが欲しくて作ろうとしたけど実は必要なかった話

posted on

結論

v3以降の新しいMathJaxのSVG出力機能を使いましょう。以下はこの結論に至るまでの無駄な悪戦苦闘についてのメモです。

きっかけ

去年の1月にD論を書き終わり、LaTeXで重たい文章を書く機会はしばらく無さそうだったので、ローカル環境からTeX Liveをアンインストールすることを検討し始めた。

TeX LiveはCTANで公開されているパッケージを片っ端からローカルにインストールするので(LaTeXのライブラリはイマドキの言語のようにパッケージの依存関係を統一的な方法で明記するようにはなっていないので仕方ないのだろう)、インストールにやたらと時間がかかるしMac OS用環境構築スクリプトのCIが不安定になる原因にもなっていた。

数式の入った文章を書きたいことはもちろんあるが、ちょっとしたものならばNotionで十分で、少し重いものでもOverleafのようなクラウド環境を活用すれば十分だった。唯一困るのが数式をベクター画像化するLaTeXiTのようなツールが使えなくなってしまうこと。プレゼンに数式を入れるときはやはりこの手のツールが使えないと辛い。Webで使える数式を画像化するツールもあるがやはりローカルで完結したものが欲しい。

幸いにもMathJaxKaTeXといったJavaScriptによるTeX数式処理の再実装と呼ぶべきライブラリがあるので、これらとNode.jsやDenoのようなサーバーサイドのJSランタイムを組み合わせて、LaTeX処理系に依存しないLaTeXiT的ツールを作れば良さそうという結論になった。

KaTeXを使いたい

以前Twitterで見かけた、MathJaxは古く行儀の悪いライブラリであるという話が印象に残っていた(ツイートされたのが2017年であることに注意してほしい)。

実際、パフォーマンス面でもMathJaxを使っているページは不安定なことが多かった印象で、これからはKaTeXの時代なのだなと思い込んでいたのである。GitHubがMathJaxを使った数式表示をサポートするという話を見た時も、なぜKaTeXではないのだ?と疑問に思ったものだ。

もしKaTeXにSVG出力する機能があれば話はそれで終わりだったのだが、現状では存在しない。なお、このIssueのおかげでMathJaxにはSVG出力機能があることを知ったのだが、前述の理由でこのときは無視していた。

KaTeXではHTMLとCSSを使って数式のレイアウトを組んでいるので、その出力を画像化するにはブラウザのHTMLレンダリングエンジンが必要に思える。元々はLaTeX処理系に依存するのが嫌だからツールを作ろうという話だったのに、代わりにブラウザに依存するようになったというのはあまり面白く無い。

satoriというすごいツールがあるらしい

そんな感じでどうしようかなぁと悩んでいた時、satoriというHTML+CSSをJS(とwasm)だけでSVGに変換するツールが公開されたことを知った。OG画像をブラウザ非依存で生成できるようにするために開発されたものとのこと。

https://github.com/vercel/satori

動作についてはLINE UITのポッドキャストの説明が分かりやすいが、肝となるのはyogaというレイアウトエンジンを使ってHTML+CSSのレイアウトを計算している点にある。それぞれの要素の位置が計算できれば、後はSVG化したOpenTypeフォントなどを配置していくことでページ全体のSVG画像が作れるという寸法。

https://uit-inside.linecorp.com/episode/135

これを見て、じゃあsatoriとKaTeXを適当に組み合わせれば、目当ての数式SVG化ツールが作れそうだなと思い、本格的に実装を始めてみた。

satoriが対応しているCSSはサブセット

ところが、satori(というかyoga)が実装しているCSSはあくまでもサブセットでdisplayに関してはflexしか対応していない。この背景には、flexさえ対応していればdisplay: blockdisplay: inlineなどの振る舞いは再現できる筈なのでこれで十分、という考えがあるようだ。

一方、KaTeXは複雑な数式を表示するためかdisplay: tableとかを使いまくっているのだ。

https://github.com/KaTeX/KaTeX/blob/main/src/katex.less

というわけで、satoriを使えば何も考えずにSVG画像化が実装できる、というわけにはいかないらしいことが分かった。問題解決のためには、少なくとも一部のレイアウトは自分で計算するしかない。KaTeXで使われているCSSの機能はかなり限定的なので、これも現実的解決手段ではあった。しかし、自分はHTMLやCSSに関してはほぼ素人であり、一部とはいえWeb標準の仕様を読み解いてレイアウト計算を実装するのはかなり厳しそうで、少なくとも空いた時間にちょいちょいやって完成という訳にはいかなくなってしまった。

その後html2canvasというツールもJSによるCSSの再実装のようなことをやっているというのを知り、使えるかもと思ってコードを読んでみたりもしたが、これはクライアントサイドが前提でレイアウトの計算についてはブラウザに任せていることが判明し、結局自分の目的には役に立たないことが分かった。

MathJax再訪

やるならやるでそれなりに気合いが必要と分かったので、他の選択肢についてもう一度ちゃんと調べておこうという気分になった。とりあえずMathJaxとKaTeXの比較について改めて調査していたところ、以下のページがヒットした。

https://groups.google.com/g/mathjax-users/c/aboJLMb50uQ/m/Y77FexF_AwAJ

ここに

With the MathJax rewrite for v3, we updated MathJax's infrastructure to bring the codebase into alignment with modern language features and programming paradigms.  This means that MathJax gets a speed boost over v2 (what was already 5 times faster than the MathJax version to which KaTeX compared itself).  We have also made it possible to do single-file downloads rather than v2's dynamic downloads (though both are possible), and don't do the "chunked" output any longer.  All of these improve the speed with which MathJax's final output is produced.

と書いてあるのを見て、MathJaxがv3になって書き直されていることを知った。詳しく調べていくと、v3になるにあたってコードが完全に書き直されていたことが分かった。同時にNode.jsで動作するようにもなっており、先述した通りSVGを吐く機能もある。

そして、いろいろ探していくと、nodeを使ってTeXをSVGに変換するCLIツールというそのものズバリなサンプルも公開されていたのだった。

https://github.com/mathjax/MathJax-demos-node/blob/master/simple/tex2svg

答えは最初から目の前にあったというわけである。これをベースに自分に使いやすいように軽く改造すれば、十分実用的なものが作れそうという結論になった。

なお、先ほどのmizchi氏のMathJaxをこき下ろすツイートはv3リリース前のものである。

まとめ

先行研究の調査は大切。思い込みを捨て、最新の1次ソースを当たるのも大切。まあ、調べる仮定でWeb技術について勉強になったことも多かったので、良しとしようかと思う。

なお、このブログの数式表示については当分KaTeXのままでいいかなと思っている。ただ、一部で数式の表示が崩れているので、そのうちMathJaxの吐いたSVGを静的に埋め込むように書き直すかも。

← 2022のまとめテキストエディタで式変形する人... →