remark-mathでMarkdownに数式機能(TeX)を追加

Category: Tech
2024-12-22
thumbnail

このブログはMarkdownで記述されている、いわゆるJAMstackなサイトで、パーサーにはunified(remark)を採用しています。unifiedは標準で主要なMarkdown文法に対応しておりブログに使うには十分です。
しかし、このブログは技術ブログ、となると数式は必須なのですが、Texを$で囲う記法を試したところ、どうやら対応してないようでそのまま表示されてしまいました。

remark-math

remark-mathというunifiedプラグインを使用してTeX記法での数式に対応しました。
https://github.com/remarkjs/remark-math

この辺りは詳しくないのですが、ブラウザで数式を表示するエンジンとしてMathJaxとKaTeXが主に使われているようで、remark-mathはその2つに対応しています。
KaTeXの方が軽量らしいのでそちらを採用することに(サーバーサイドレンダリングには無関係かも)

使い方は他のプラグイン同様にインストールしてuse()で追加するのですが、2つ追加する必要があります。
一つ目はremark-mathで、これを適用するとTeX記法の部分がlang=mathのコードブロック(```で囲うやつ)に変換されます。
そして、二つ目のrehype-katex(MathJaxの場合はrehype-mathjax)がこれを数式に変換します。
そのため以下のような記法でも数式に変換することができます。

```math

E = mc^2

```

サンプルコード

npm install remark remark-rehype rehype-stringify

npm install remark-math
npm install rehype-katex    # KaTeXの場合
npm install rehype-mathjax  # MathJaxの場合(コードもいい感じに置き換えてね)
import { remark } from 'remark'
import remarkmath from 'remark-math'
import remarkRehype from 'remark-rehype'
import rehypeKatex from 'rehype-katex'
import rehypeStringify from 'rehype-stringify'

let markdown = "$E=mc^2$"

let html = remark()
    .use(remarkmath)
    .use(remarkRehype)
    .use(rehypeKatex)
    .use(rehypeStringify)
    .processSync(markdown)

console.log(String(html))

実行結果

<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mo>=</mo><mi>m</mi><msup><mi>c</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">E=mc^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.05764em;">E</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord mathnormal">m</span><span class="mord"><span class="mord mathnormal">c</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></p>

実行するとhtmlが出てくるのですが、そのまま.htmlで保存してブラウザで開くと、ダブってるし片方は数式になっていません。
これだけではダメで、KaTeXでは追加のCSSを適用する必要があるのです。(MathJaxではCSSが一緒に出力されるので不要)
なので以下のlinkタグを追加して再度開くと

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" rel="stylesheet">

数式が正しく表示されている
数式として表示されました✨

オプション(MathMLで出力)

KaTeXはhtml(+css)とMathMLという形式の出力に対応しています。
MathMLとは簡単にいうとSVGの数式版のようなもので、数式を記述するためのxmlマークアップです。
SVGと同様にwebブラウザで表示が可能ですが、最近までブラウザサポートが微妙だったため、MDNではNEWLY AVAILABLEとなっています。https://developer.mozilla.org/ja/docs/Web/MathML

KaTeXでは、htmlのみを出力、MathMLのみを出力、htmlを表示してMathMLをアクセシビリティのために不可視で付ける、の3種類が選択可能で、3つ目がデフォルトになっています。(ちなみにcssをつけないとダブるのはこのためです。)
自分はcssを付けたくなかったためMathMLにしました。そのため、以下のようにオプションを設定しています。

.use(rehypeKatex, {output:'mathml'})

そのほかのオプションはKaTeXのサイトで確認できます。
https://katex.org/docs/options.html

このブログのMarkdown周りのコードはここです。
https://git.moris.day/moris/moris-blog/src/branch/main/src/lib/components/Markdown.svelte

おまけの数式

二次方程式 解の公式

x=b±b24ac2ax = \frac{-b\pm \sqrt{b^2-4ac}}{2a}

二乗の和

k=1nk2=16n(n+1)(2n+1)\sum_{k=1}^nk^2=\frac{1}{6}n(n+1)(2n+1)

シュレディンガー方程式

tψ(x,t)={22m+V(x)}ψ(x,t)\hbar\frac{\partial}{\partial t}\psi(x,t) = \left\{-\frac{\hbar^2}{2m}+V(x)\right\}\psi(x,t)