【Angular】KaTeX or MathJax で数式を表示する

 JavaScript を使用してブラウザ上に数式を表示する代表的なライブラリに KaTeX と MathJax があります。今回は両方の実装方法を解説していきます。

 KaTeX を使用するのに前回使用した Marked を利用するので、ソースコードは前回の記事のものを流用します。

完成しているコードは下記にあります。

Angular-sample/markdown at main · tsuneken5/Angular-sample
Contribute to tsuneken5/Angular-sample development by creating an account on GitHub.

環境

  • node: 18.20.0
  • Angular CLI:17.0.0
  • Marked:14.1.3
  • marked-katex-extension:5.1.2
  • mathjax-angular:2.2.1

KaTeX

パッケージのインストール

$ npm install marked
$ npm install marked-katex-extension

 ライブラリは Marked を使って簡単に KaTeX が使える marked-katex-extension を使用します。

GitHub - UziTech/marked-katex-extension: MarkedJS extension for katex syntax
MarkedJS extension for katex syntax. Contribute to UziTech/marked-katex-extension development by creating an account on ...

スタイルシートのインポート

@import 'katex/dist/katex.min.css';

html,
body {
  height: 100%;
}

body {
  margin: 0;
  font-family: Roboto, "Helvetica Neue", sans-serif;
}

コンポーネントの生成

$ ng generate component component/page/katex
import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';

import { InputComponent } from '../../share/input/input.component';
import { OutputComponent } from '../../share/output/output.component';

import { marked } from 'marked';
import markedKatex from "marked-katex-extension";

@Component({
  selector: 'app-katex',
  standalone: true,
  imports: [InputComponent, OutputComponent],
  templateUrl: './katex.component.html',
  styleUrl: './katex.component.css'
})
export class KatexComponent {
  public safeHTML!: SafeHtml;
  private readonly KATEX_OPTIONS = {
    throwOnError: false
  };

  constructor(
    private sanitizer: DomSanitizer
  ) {
    marked.use(markedKatex(this.KATEX_OPTIONS));
  }

  public async onParse(message: any) {
    const content = await marked.parse(message);
    this.safeHTML = this.sanitizer.bypassSecurityTrustHtml(content);
  }
}

26行目

 marked.use(markedKatex(this.KATEX_OPTIONS)) と設定するだけで Marked と同じ要領で KaTeX が使えるようになります。

確認

 入力は数式を「$」で囲むとインラインとして、「$$」で囲むとブロックとして出力されます。

This is inline katex: $c = \pm\sqrt{a^2 + b^2}$

$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
$$

MathJax

パッケージのインストール

$ npm install mathjax-angular

 ライブラリは Angular で簡単に MathJax が使用できる mathjax-angular を使用します。変にこだわらなければ楽に数式を出力できます。ただ、今回は変にこだわってるのでかなり苦労しました・・・

GitHub - sajivkumar/mathjax-angular: Mathjax 3 for angular
Mathjax 3 for angular. Contribute to sajivkumar/mathjax-angular development by creating an account on GitHub.

モジュールの生成

$ ng generate module modules/mathjax-root
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { MathjaxModule } from 'mathjax-angular';

const MATHJAX_CONFIG = {
  config: {
    loader: {
      load: ["output/svg", "[tex]/require", "[tex]/ams"]
    },
    tex: {
      inlineMath: [["$", "$"], ["\\(", "\\)"]],
      displayMath: [["$$", "$$"], ["\\[", "\\]"]],
      packages: ["base", "require", "ams"]
    },
  },
  src: "https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/startup.js"
};


@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    MathjaxModule.forRoot(MATHJAX_CONFIG)
  ]
})
export class MathjaxRootModule { }

 MathJax の設定をするためのモジュールを生成します。Angular16以前で app.module.ts があればそっちで記載したほうがいいです。

 公式の GitHub で解説されてる設定に追加して、 displayMath を設定することでブロック単位で数式に変換できるようにしています。

コンポーネントの生成

$ ng generate component component/page/mathjax
import { Component } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { MatCardModule } from '@angular/material/card';

import { InputComponent } from '../input/input.component';
import { OutputComponent } from '../output/output.component';

import { MathjaxRootModule } from '../modules/mathjax-root/mathjax-root.module'

declare global {
  interface Window {
    MathJax: any;
  }
}

@Component({
  selector: 'app-mathjax',
  standalone: true,
  imports: [MatCardModule, InputComponent, OutputComponent, MathjaxRootModule],
  templateUrl: './mathjax.component.html',
  styleUrl: './mathjax.component.css'
})
export class MathjaxComponent {
  public safeHTML!: SafeHtml;
  public content: string = '';

  constructor(
    private sanitizer: DomSanitizer
  ) { }

  public async onParse(message: any) {
    let content = '<div class="math-content">' + message + '</div>';
    this.safeHTML = this.sanitizer.bypassSecurityTrustHtml(content);

    setTimeout(() => {
      const math = document.querySelectorAll('.math-content');
      if (math && window.MathJax) {
        window.MathJax.typesetPromise(math);
      }
    }, 0);
  }
}

 本来であれば HTML に <div [mathjax]="content"></div> と記載しておき、 TypeScript から content に数式を代入すれば簡単に数式を表示することができます。ただ今回は、 KaTeX とアウトプットの方法を変更したくないという変なこだわりにより、変に凝った作りにしています。

9~13行目

 手動で MathJax を操作したいので、ブラウザの Window インターフェースに MathJax プロパティを追加しています。本来であれば不要です。

33~38行目

 safeHTML が DOM に反映されるのを待ってから、 window.MathJax.typesetPromise() で数式をレンダリングします。引数に DOM を渡すとその DOM のみに MathJax のレンダリングが反映されます。引数を渡さない場合は、ページ全体に反映されます。これも本来であれば不要です。

確認

 KaTeX と同じように、数式を「$」で囲むとインラインとして、「$$」で囲むとブロックとして出力されます。

This is inline katex: $c = \pm\sqrt{a^2 + b^2}$

$$
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix}
$$

KaTeX と MathJax の比較

 KaTeX も MathJax もできることは同じですが、何が違うのかというと出力する方式が違います。KaTeX は HTML で出力されて、 MathJax は(設定にもよりますが)svg で出力されます。

 今回は解説のため両方紹介しましたが、 KaTeX の方が高速なので基本的には KaTeX を使えば問題ないです。

コメント

タイトルとURLをコピーしました