MathML で数式を表示する
Posted on 日 14 4月 2019 in 運用
reStructuredText は数式表示に対応している.ただデフォルトのレンダリング表示がいまいちだったので,ちょっといじった.その備忘録.
math ディレクティブと docutils の設定
reStructuredText には math ディレクティブというものが標準で搭載されていて [1] , TeX の記法を使って以下のように数式が埋め込める:
.. math:: \forall a, b \in \mathbb{N}.\, a \leq b \implies \sqrt{a} \leq \sqrt{b}
これは特に何もしなくても docutils でコンパイルして HTML にしてくれる.なので, reStructuredText を使っている場合は簡単な数式なら Pelican のブログ記事で普通に使える.これ自体は素晴らしいんだが,出される HTML は数学記号を Unicode 表現にして変数をイタリックにしただけで,空白制御とかはあまり考慮されておらず,フォントもあまり良くない.なので,もうちょっと何とか出来ないかということで,色々調べた.
docutils の出力オプションとして math_output というオプションがあり色々弄れるらしい.ここに指定できる出力の仕方は,以下の 4 通り:
- HTML
- デフォルトの出力方法. i タグや sup タグなどを駆使して簡単な数式レンダリングを行う.カスタムCSSファイルを引数として指定すると, CSS が必要な時だけ埋め込んでくれる.
- MathJax
- MathJax を使ってレンダリングを行う.引数に MathJax のライブラリファイルの指定が必須.
- MathML
- MathML を使ってレンダリングを行う.引数にコマンドを指定すると,そのコマンドでコンパイルしてくれる.
- LaTeX
- 数式表現をそのまま埋め込む.
で,今回は気になったので MathML を使ってみた.引数にコマンドを指定しない場合標準のコンパイラで MathML に変換してくれる.なお,標準のは \ldotp が使えなかったりちょっと不便なところがあるが, latexml などを使うと使える記号をもうちょっと増やせるみたい.今回は,妥協してフルに LaTeX 使いたかったら PDF で埋め込めばええやろという気持ちで,標準のをそのまま使うことにした.
pelicanconf.py に次の設定を足すだけで, MathML が使えるようになる:
1 2 3 4 | DOCUTILS_SETTINGS = { ... + 'math_output': 'MathML', } |
MathML のフォールバック
ところで, MathML を使う場合気になるのが,ブラウザの対応状況だ.というわけで, サポート状況 を覗きに行ってみると,見事に壊滅状態にあってサポートしてるの Firefox / Safari ぐらいやんってなった. Edge は現状サポート予定はなくて, Chrome は提案は出てるもののやっぱりサポート予定はないっぽい.
なので, MathML のフォールバックを行うことを考えたい. MathJax が MathML での入力に対応してるので導入してみたのだが,これが webpack とかなり相性が悪い構成になっているっぽい.本家にも イシュー が立っているわけだが,現状の構成でとても対応できるとは思えない.で,色々探っていたら,次期 MathJax v3 では TypeScript を使って書いていていい感じにモジュール構成を取り入れているため, webpack にももちろん対応できるという話が上がっていた.で,まだ正式リリースじゃなくてベータ版なんだけど,使ってみるかってなった.どうせレンダリング失敗しても, MathML 見れる環境なら問題ないはずだし.
まず, npm で mathjax3 をインストールする:
npm install -S mathjax3
で, webpack.config.js に以下の設定を足す:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | resolve: { alias: { ... + mathjax3$: 'mathjax3/mathjax3/mathjax.js', + mathjax3: 'mathjax3/mathjax3', } } ... plugins: [ ... + // to disable asyncLoad() + new webpack.NormalModuleReplacementPlugin( + /AsyncLoad\.js/, + (resource) => { + if (resource.context.endsWith('mathjax3/util')) { + resource.request = resource.request.replace(/AsyncLoad/,"AsyncLoad-disabled"); + } + } + ) ], |
最初の設定はいいとして,2番目の設定についてだけど, webpack はコード解析して依存関係を解決してるんだけどその解析が MathJax v3 の AsyncLoad モジュールで死ぬという問題があるらしい.で, webpack 使ってバンドルする場合そもそも非同期 import いらないねって話があって, MathJax v3 では webpack 向けに AsyncLoad-disabled.js というモジュールを提供することにしたらしい [2] .で, webpack を使う場合は AsyncLoad モジュールを AsyncLoad-disabled モジュールに置換することでその問題を解決できる.それをやるのが上のコード.
後は, JavaScript で,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import { MathJax } from 'mathjax3'; import { MathML } from 'mathjax3/input/mathml'; import { CHTML } from 'mathjax3/output/chtml'; import { browserAdaptor } from 'mathjax3/adaptors/browserAdaptor'; import { RegisterHTMLHandler } from 'mathjax3/handlers/html'; RegisterHTMLHandler(browserAdaptor()); const MathJaxDocument = MathJax.document(document, { InputJax: new MathML(), OutputJax: new CHTML({ fontURL: 'https://cdn.rawgit.com/mathjax/mathjax-v3/3.0.0-beta.3/mathjax2/css', }), }); export function loadMathJaxDocument() { return MathJaxDocument .findMath() .compile() .getMetrics() .typeset() .updateDocument() ; } |
みたいなんを書いて, DOM のロード後のタイミングで loadMathJaxDocument 関数を呼び出せば良い. MathJax v3 は結構モジュールの導入のおかげで治安が良くなってて良い.以下が最終的なレンダリング結果になる:
レンダリングがちゃんと出来ていなかったら,ぜひ使ってるブラウザを教えて欲しい.修正するかは分からないけど.
まとめ
ブログの数式表示を改善した. MathJax v3 がまだベータ版なので,ちょっとまめに更新は確認していきたい.
後ちょっとレンダリングが遅めなのが気になるがまあしゃーないね. MathML 標準対応してほちい.
[1] | http://docutils.sourceforge.net/docs/ref/rst/directives.html#math |
[2] | https://github.com/mathjax/mathjax-v3/issues/88 |