KaTeXの導入

本Weblogに導入した新しい技術その2はKaTeX. これはWebページ内にあるLaTeXコードを数式へとレンダリング(これを植字"Typeset"と言う)してくれるJavaScriptライブラリ. 今までMathJaxを利用していたのだが,動作が遅く,ロードするリソース量も多かったことから,シンプルで高速なKaTeXへと移行した. 発展途上のライブラリだが,その機能は十分に素晴らしい.

Installation 導入

次のタグをheadタグ内に書き込めばKaTeXの導入(インストレーション)は終わり.
...
<link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css" integrity="sha256-tkzDFSl16wERzhCWg0ge2tON2+D6Qe9iEaJqM4ZGd4E=" crossorigin="anonymous" type="text/css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.js" integrity="sha256-gNVpJCw01Tg4rruvtWJp9vS0rRchXP2YF+U+b2lp8Po=" crossorigin="anonymous" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/contrib/auto-render.min.js" integrity="sha256-ExtbCSBuYA7kq1Pz362ibde9nnsHYPt6JxuxYeZbU+c=" crossorigin="anonymous" type="text/javascript"></script>
</head>
...
Bloggerに導入する場合,テンプレートはHTMLではなくXMLなので,linkタグの終わりに/を入れて閉じる必要がある. このように. <link href="https://cdnjs..." ... rel="stylesheet"/> 後は各種設定を行い,renderMathInElement函数をオンロード時に呼び出せば植字される.

Configuration 設定

次のスクリプトをbodyタグ内の最後に書けば設定は完了. デリミタ(後述)で囲われたLaTeXコードが自動で(オンロードで)植字される.
...
<script>
  renderMathInElement(
    document.body,
    {
      delimiters: [
        {left: "$$", right: "$$", display: true},
        {left: "\\[", right: "\\]", display: true},
        {left: "\\(", right: "\\)", display: false}
      ],
      ignoredTags: [
        "script",
        "noscript",
        "style",
        "textarea",
        "pre",
        "code"
      ]
    }
  );
</script>
</body>
...
函数renderMathInElementの第1引数には植字したいLaTeXコードを含むDOM要素を指定する(上の例ではbodyタグ内すべて). 続く第2引数はオプションになっていて,LaTeXコードを見分けるデリミタ(区切り文字"delimiters")と,植字の対象外にする要素の名前(いわゆるブラックリスト)を渡すことが出来る(ignoredTags). なお,上のオプションはすべてデフォルト値.

delimiters

delimiters: [
  {left: "$$", right: "$$", display: true},
  {left: "\\[", right: "\\]", display: true},
  {left: "\\(", right: "\\)", display: false}
]
上の場合\\( \KaTeX \\)はインラインで $ \KaTeX $ 植字され,$$ \KaTeX $$などは, $$ \KaTeX $$ のように,ディスプレイとして植字される. leftとrightにデリミタを,displayがtrueならばディスプレイ,falseならばインラインで植字される.

ignoredTags

ignoredTags: [
  "script",
  "noscript",
  "style",
  "textarea",
  "pre",
  "code"
]
このリストのタグ内にデリミタがあってもそのLaTeXコードは植字されないようになる. 上はデフォルト値.
私は下のような設定で利用している.
renderMathInElement(
  document.body,
  {
    delimiters: [
      {left: "$", right: "$", display: false},
      {left: "$$", right: "$$", display: true}
    ]
  }
);
本Weblogでは$もしくは$$のデリミタしか用いていないため上の設定とした. またignoredTagsは指定しなければデフォルト値が利用される.

InstantClickとの併用

InstantClickと併用する場合はrenderMathInElementchangeコールバック内に書く.
<script data-no-instant>
  /* Google Analyticsのコード */

  InstantClick.on('change', function () {
    // ga('send', 'pageview', location.pathname + location.search);

    renderMathInElement(
      document.body,
      {
        delimiters: [
          {left: "$", right: "$", display: false},
          {left: "$$", right: "$$", display: true}
        ]
      }
    );
  });

  InstantClick.init();
</script>

デモ

KaTeXのライブデモをご覧いただきたい. $$ e^{i \pi} = -1 $$
$$ e^{i \pi} = -1 $$
Eulerの公式はご覧の通り. $$ \alpha^2 + \beta^2 = \gamma^2 $$
$$ \alpha^2 + \beta^2 = \gamma^2 $$
三平方の定理もきれいに植字されている. $$ \mathcal{L} [ f(t) ] ( s ) = \int_0^\infty \mathrm{e}^{-st} f(t) dt $$
$$ \mathcal{L} [ f(t) ] ( s ) = \int_0^\infty \mathrm{e}^{-st} f(t) dt $$
Laplace変換のように複雑な式もきちんと植字される.
$$ \begin{aligned}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\[1em]   
\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\[0.5em]
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\[1em]
\nabla \cdot \vec{\mathbf{B}} & = 0
\end{aligned} $$
$$ \begin{aligned} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\[1em] \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\[0.5em] \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\[1em] \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned} $$
こちらはMaxwellの方程式. aligned環境を使ってイコールを縦揃えすることも出来る.
$$ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 
1+\frac{e^{-2\pi}}
{1+\frac{e^{-4\pi}}
{1+\frac{e^{-6\pi}}
{1+\frac{e^{-8\pi}}
{1+\cdots}}}} $$
$$ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots}}}} $$
再帰的な分数(連分数)の植字もお手の物.
$$ \delta(t) = \begin{cases}
+\infty, & t = 0 \\
0, & t \neq 0
\end{cases} $$
$$ \delta(t) = \begin{cases} +\infty, & t = 0 \\ 0, & t \neq 0 \end{cases} $$
Diracの$ \delta $函数のような条件式を植字することももちろん出来る.
$$ A_{3,3} = \begin{pmatrix}
a_{11} & a_{12} & a_{13}\\ 
a_{21} & a_{22} & a_{23}\\ 
a_{31} & a_{32} & a_{33}
\end{pmatrix} $$
$$ A_{3,3} = \begin{pmatrix} a_{11} & a_{12} & a_{13}\\ a_{21} & a_{22} & a_{23}\\ a_{31} & a_{32} & a_{33} \end{pmatrix} $$
pmatrixbmatrix環境などで行列や行列式を植字することも可能. と,このように思いつく限りの数式はすべて植字出来そうな印象.

LaTeXコマンドの対応状況

GitHubのWikiページには対応しているLaTeXコマンド一覧がある. Function Support in KaTeX LaTeXコマンドの対応状況については次のページが参考になる. Symbols and Functions in KaTeX

MathJaxとの比較

TL;DR KaTeXは...

  • 植字が高速かつ高品質
  • 他のライブラリに依存しない
  • サーバーサイドでの植字も可能
  • ただしMathJaxと比べると対応するLaTeXコマンドの数が少ない

Webページ中のLaTeXを植字するJavaScriptライブラリといってまず思い浮かぶのはMathJax. 以前は私も利用していた. 機能がとても豊富で植字も美しいのだが,動作が遅く,大量のリソースを必要とし,なによりInstantClickとの相性も悪かった. 下のリンクへ飛んでほしい. KaTeX and MathJax Comparison Demo リンク先のページではまずKaTeXで植字が行われる. そして植字にかかった時間が表示される. 大体70ms(0.07秒)あたりか. "Process with MathJax"ボタンを押すとMathJaxでの植字に切り替わる. 同じく処理時間を見てみよう. おそらく1200ms(1.2秒)ほど掛かっている. KaTeXはMathJaxと比べ,機能を絞っているため植字がとても速い. 更に,KaTeXはMathJaxとは違い,サーバー側で植字させることも可能. Node.jsにより植字済みのHTMLを送ることで,クライアント側での処理を実質0にすることも出来る. また,他のライブラリに依存していない点(スタンドアロンで動作する点)も素晴らしい. Khan/KaTeX: Fast math typesetting for the web | GitHub.com 処理が速いからと言って植字の品質が劣る訳ではない. もちろん発展途上のライブラリなので成熟したMathJaxに比べ細かな点で差異はあるだろうが,十分に実用範囲. 現在も盛んに開発が進んでいるので,これからより一層磨きがかかること必至. 今後の展開が楽しみだ.

おまけ 動的な導入

最初に紹介した導入方法はscriptタグとlinkタグをheadタグ内に書き込む方法だった. しかし,これではWebページのパフォーマンスが若干落ちる. 外部のファイルを読み込む間,レンダリング(Webページの表示)がブロックされてしまうからだ. 私はJavaScriptを用いて,動的にKaTeXを読み込むようにしている. 下のコードはInstantClickとの併用も考えた,Bloggerテンプレート対応の導入例. bodyタグ内の最後に書き込めば,ページの読み込み後にKaTeXを読み込み,その後自動で植字してくれるようになる.
...
<script data-no-instant="true" type="text/javascript">
  //<![CDATA[
  var renderKaTeX = function() {
    if (typeof renderMathInElement === 'undefined') return;
    renderMathInElement(document.body, {
      delimiters: [
        {left: "$$", right: "$$", display: true},
        {left: "$", right: "$", display: false}
      ]
    });
  };

  (function() {
    var alpha = beta = gamma = false;
    var katexLoaded = function () {
      if (this.outerHTML.indexOf('katex.min.js') != -1) alpha = true;
      else if (this.outerHTML.indexOf('auto-render.min.js') != -1) beta = true;
      else if (this.outerHTML.indexOf('katex.min.css') != -1) gamma = true;

      if (alpha && beta && gamma) renderKaTeX();
    }

    var insertElement = function (elm, uri, sri) {
      var e = document.createElement(elm);
      e.setAttribute((elm === 'script') ? 'src' : 'href', uri);
      e.setAttribute('integrity', sri);
      e.setAttribute('crossorigin', 'anonymous');

      if (elm === 'script') e.type = 'text/javascript';
      else e.type = 'text/css', e.rel = 'stylesheet';

      if (window.attachEvent) e.attachEvent('onload', katexLoaded);
      else if (window.addEventListener) e.addEventListener('load', katexLoaded);
      else e.onload = katexLoaded;

      document.head.appendChild(e);
    }

    insertElement('script',
                  'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.js',
                  'sha256-gNVpJCw01Tg4rruvtWJp9vS0rRchXP2YF+U+b2lp8Po=');

    insertElement('script',
                  'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/contrib/auto-render.min.js',
                  'sha256-ExtbCSBuYA7kq1Pz362ibde9nnsHYPt6JxuxYeZbU+c=');

    insertElement('link',
                  'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css',
                  'sha256-tkzDFSl16wERzhCWg0ge2tON2+D6Qe9iEaJqM4ZGd4E=');
  })();

  InstantClick.on('change', function(isInitialLoad) {
    if (!isInitialLoad) {
      renderKaTeX();
    }
  });

  InstantClick.init();
  //]]>
</script>
</body>
...
5234112669561651423 http://www.storange.jp/2017/02/katex.html http://www.storange.jp/2017/02/katex.html KaTeXの導入 2017-02-19T10:24:00+09:00 http://www.storange.jp/2017/02/katex.html Hideyuki Tabata 200 200 72 72