math-expression-support-in-my-blog

ブログの数式対応

KaTeX を使って数式(LaTeX 記法)の表示に対応させました。テキストで $E = mc^2$ と書くと次のように表示されます:$E = mc^2$

$$ で囲むことでブロック表示も可能です。例えば次のコードは

$$
E = mc^2
$$

このような表示になります。 $$ E = mc^2 $$

KaTeX の auto-render 拡張を有効にすると、文書の中に書かれた数式を KaTeX が自動的に探し出し、数式として描画してくれます。非常にお手軽。HTML の <head></head> の中に CSS や JavaScript の読み込みタグをいくつか記載するだけでよく、ブログへの設置は拍子抜けするほど簡単でした。

KaTeX の機能リスト を見ると \begin{align} などがあり、いくつかは当ブログでも使えるようにしています。例えば次のコードは

$$
\begin{align}
f_H &= \frac{1}{2\pi R_3C_3} \\
    &\approx 15.92\, \mathrm{Hz}
\end{align}
$$

$$ \begin{align} f_H &= \frac{1}{2\pi R_3C_3} \\ &\approx 15.92\, \mathrm{Hz} \end{align} $$

という感じに、イコール記号の位置を揃えて表示されます。

数式表示ライブラリの選定

最初は MathJax を使おうと思ったのですが、次のような理由から KaTeX を使うことにしました。

  • KaTeX の方が描画が速い(らしい)
  • elchika が KaTeX を使っている

前者の理由は分かりやすいので特に説明はいらないと思います。描画が速ければ速いほどユーザ体験は向上します。

後者の理由ですが、元々筆者は elchika に電子工作系の記事をアップロードしており、記事データの互換性を保ちたいです。elchika にアップロードしている過去記事を当ブログに持ってきたり、あるいは当ブログ向けに書いた記事を elchika にも掲載する、などの相互運用性を高めたいと思っています。

elchika は KaTeX を使っているのですが、シンプルな数式環境 $…$$$…$$ 以外(\begin{align} など)は有効化されていないようです。したがって、当ブログの記事を elchika に持っていくと数式が壊れる可能性はあります。逆方向なら大丈夫なのですが。記事データの互換性を高めるには、必要な箇所以外はシンプルな数式だけで構成した方がよさそうですね。どうしてもそれらの機能を使う場合は、elchika にアップロードする際に数式を画像化するといった工夫が必要だろうと思います。今後の課題。

数式描画に潜むバグ

ブログの表示を注意深く確認したら、数式描画が盛大にバグっていることが分かりました。align の効果により本来は縦に 2 行の表示になるべきところが、1 行になってしまっています。さらに細かく見ると、小さな空白を出力するための \, という命令が、空白ではなくカンマ「,」として表示されてしまっていることも分かります。

2行になるはずの数式が横1行になってしまった
バグがある数式描画処理
等号の位置が揃って表示される2行の数式
本来はこのように表示されて欲しい

HTML ソースを表示してみるとバグの要因が推測できるでしょうか。数式の部分を抜粋してみます。

<pre><code>$$
\begin{align}
f_H &amp;= \frac{1}{2\pi R_3C_3} \\
    &amp;\approx 15.92\, \mathrm{Hz}
\end{align}
$$
</code></pre>
<p>$$ \begin{align} f_H &amp;= \frac{1}{2\pi R_3C_3} \ &amp;\approx 15.92, \mathrm{Hz} \end{align} $$ という感じに、イコール記号の位置を揃えて表示されます。</p>

ソースコード引用ブロック <pre><code> で囲まれた範囲について、「&」が「&」になるなどの HTML エスケープ処理が行われています。これはまあ妥当な処理でしょう。スクリーンショットを見ても問題無くソースコードが表示されています。

問題はその後の <p>$$ から始まる数式部分です。「&」が「&」になるのは先ほどと同じです。この置換はどうやら問題無いようで、KaTeX は「&」でも「&」でもちゃんと認識してくれるようです。ダメなのは数式 1 行目の末尾にある 2 つのバックスラッシュが 1 つになってしまっていることと、15.92\, のバックスラッシュが消えて 15.92, になってしまっていることです。

検証の結果、使用している Markdown パーサ(pulldown_cmark)がこれらの処理をやっていました。バックスラッシュによるエスケープは Markdown の解析に必要な機能ですので、当然といえば当然です。パーサに非はありません。数式を埋め込もうとした筆者の責任です。

解決するための選択肢は 3 つほどあるでしょうか。

  1. pulldown_cmark の出力結果から正しい数式コードを再構成する
  2. pulldown_cmark を改造して数式コードを無変更で通過させる
  3. 数式部分は pulldown_cmark を迂回して Markdown ファイルから生のデータを読む

1 が最もストレートな解決法というか、今のブログエンジンの内部構造を大きく変えずにできる改造です。しかし実際にやってみたところ、ちょっと無理があることが分かりました。具体的にはバックスラッシュによるカンマのエスケープにまつわる部分です。pulldown_cmark が出力した , という文字を見たときに、これがバックスラッシュエスケープされた結果なのか、元々カンマだけが書かれていたのか、判別できません。

pulldown_cmark は、解析された結果を Markdown 文書の範囲(Markdown ファイル先頭からの文字の位置の組)とともに出力することができます。その情報を使えば , の前に何か文字があったが読み飛ばされたのか、元々カンマ単体で存在したのかが分かるはずです。カンマの前に読み飛ばされた文字があることが分かったら、Markdown 文書のその位置を参照し、バックスラッシュかどうかを見るわけです。このようにすれば 1 の手法でもいけるかもしれません。

ただ、Markdown 文書を参照するということをやるのであれば、何もバックスラッシュの確認だけをするのではなく、数式全体を Markdown 文書から無加工で持ってくるということが出来るわけです。つまり手法 1 ができるのであれば手法 3 も可能で、そちらの実装の方がすっきりすると思います。

今からバグを修正しようと思っています。上記の数式がきちんと 2 行に表示されていて、カンマが表示されなくなっていればバグ修正が完了した証です。しばらくお待ちください。


作成:2022-10-06 10:42:13

最終更新:2022-10-07 05:03:49