お久しぶりです, 先月の Qiita の炎上騒ぎで自ブログを作成しようかと思い, zola を使ってみて悪くなかったんですが, 数式の扱いが不満だったので Rust で静的サイトジェネレータを自作していました. ですが完成する前に公開したい記事が溜まってきたので Qiita も使うことにします. 自作のジェネレータもおおよそ形にはなってきたので近々移行するかもしれません. 自ブログなら技術記事でない数学/物理の記事も書けますし.
それで, 静的サイトジェネレータで使うため1に LaTeX 数式を MathML に変換する処理を Rust で実装したんですが, pure Rust なのだからブラウザでも動くはずと思い, 試しにブラウザで LaTeX を MathML に変換するアプリを作成してみました. もともと純粋な技術実証目的だったのですが, よく考えたらこれはこれで有用なツールだし, 必要な人もいるのではないかなと思い, 記事にまとめてみます.
ツールについて
成果物はこちら: https://osanshouo.github.io/latex2mathml-web/index.html
見たらわかると思いますが, テキストボックスに LaTeX を入力して Convert to MathML! ボタンを押すと変換処理が走って下のボックスに MathML が表示されます.
数式の表示はもちろん MathML を用いているので, Firefox と Safari では表示されるはずですが, Chrome では正しく表示されません (MathML への変換自体は WebAssembly に対応するすべてのブラウザでできます). 早ければこの夏には Chromium に MathML が実装されると聞きましたが, どうなるんでしょうね, 待ち遠しいです.
なお, すべての処理はブラウザで完結しているため, サーバーに入力内容を送信するといったことは起こりません. いったんロードしておけばオフラインでも使えるんじゃないでしょうか.
技術的詳細
LaTeX to MathML
「[Rust] 『Go言語でつくるインタプリタ』Rustで読了」という記事で書いたように, 2 月に Rust でインタプリタの実装をしていました. その経験のおかげで LaTeX の構文解析はすんなり実装できました. 技術的には前の記事で書いたものとほぼ同じで, 例えば \frac{ 1 }{ 2 }
だったら子をふたつ持つノード, というように入力から構文木を生成し, それを XML にフォーマットします. Monkey との違いは MathML を出力する際に attribute が必要だったりするくらいです. 詳細はソースコードを見てください.
ただ, 複雑な添え字 (上下添え字が入り乱れるものや, ${}_n C_k$ みたいなもの) は <mmultiscript>
を使うべきなんですが, 差し当たりそれは諦めてシンプルに <msub>
と <msup>
で実装したのであまり美しくないかもしれません. これに関しては LaTeX を機械的に MathML に置き換えるという部分に若干無理があるような… あと案外プライム ($x'$ のような) のお行儀が悪くて面倒でした.
WebAssembly で使う
この部分も『[Rust] WebAssemblyを用いたウェブフロントエンド開発入門』という記事で説明した通りですね. 自作の latex2mathml クレートに依存する Rust プロジェクトを作成し, wasm-pack でビルドして GitHub Pages で公開するだけで完成です.
なお変換に失敗したときの処理をさぼっていて, エラーが発生すると何事も起こりません. エラーメッセージが表示されるように改良しなければ…
感想
上で書いたように, 過去に自分で Qiita の記事にまとめた経験が生きた感じで, 基本的に大きな問題なく完成しました. それにしても誰が使うのかわからない謎の LaTeX コマンド (\leftrightsquigarrow
↭ とか \boxbox
⧈ とか \beth
ℶ とか \from
⌢ とか) をひたすら定義する部分は単なる修行でしたが…
行列などで \begin{pmatrix} ... \end{pmatrix}
となっている部分のパースだけは汚くなってしまったのが気になりますが, done is better than perfect ということで許してください. 通常使われるたいていの記法には対応しているはずです (していなければ issue を立てて欲しいです). Feynman スラッシュにも対応しています.
-
本当は
cargo doc
で生成される HTML に数式 (MathML) を埋め込むことが本来の目的です. 既に必要な機能は実装が終わっているんですけど, どういう形式で公開すべきか迷ってて… 普通にcargo install
できたら嬉しいですかね? docs.rs で数式を使おうと思うなら本家rustdoc
にパッチを送るべきなんでしょうが, それは大変すぎるし, どうしたものか… ↩