【備忘録】PuppeteerとMathJaxでLaTeX数式をPNGに変換する

はじめに

資料やブログ記事にきれいな数式画像を載せたいとき、LaTeXをそのまま使えない環境では一手間かかる。

MathJax(ブラウザでLaTeXをレンダリングするJSライブラリ)と Puppeteer を組み合わせることで、コマンド一発でLaTeX数式を高解像度のPNGに書き出せるスクリプトを作った。

環境・前提

  • macOS
  • Node.js インストール済み
  • インターネット接続必要(MathJaxをCDNから取得するため)

セットアップ

mkdir mathjax-to-png cd mathjax-to-png npm init -y npm install puppeteer

mkdir mathjax-to-png
cd mathjax-to-png
npm init -y
npm install puppeteer

スクリプト(render_mathjax.js)

const puppeteer = require(‘puppeteer’); const fs = require(‘fs’);

const math = process.argv[2] || ‘\\frac{a}{b}’;

(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage();

await page.setViewport({ width: 4000, height: 600, deviceScaleFactor: 3 // 3倍スケーリング ≈ 300dpi相当 });

await page.setContent(`

\\(${math}\\)
`);

// MathJaxの描画完了を待つ await page.waitForFunction(() => { return document.body.getAttribute(‘data-mathjax-ready’) === ‘true’; });

const mathElement = await page.$(‘#math’); await mathElement.screenshot({ path: ‘output.png’ });

await browser.close(); })();

const puppeteer = require('puppeteer');
const fs = require('fs');

const math = process.argv[2] || '\\frac{a}{b}';

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setViewport({
    width: 4000,
    height: 600,
    deviceScaleFactor: 3 // 3倍スケーリング ≈ 300dpi相当
  });

  await page.setContent(`
    <html>
      <head>
        <script>
          window.MathJax = {
            tex: { inlineMath: [['$', '$'], ['\\\\(', '\\\\)']] },
            svg: { fontCache: 'global' }
          };
        </script>
        <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
        <style>
          body { margin: 0; padding: 40px; }
          #math { font-size: 80px; }
        </style>
      </head>
      <body>
        <div id="math">\\(${math}\\)</div>
        <script>
          MathJax.startup.promise.then(() => {
            document.body.setAttribute('data-mathjax-ready', 'true');
          });
        </script>
      </body>
    </html>
  `);

  // MathJaxの描画完了を待つ
  await page.waitForFunction(() => {
    return document.body.getAttribute('data-mathjax-ready') === 'true';
  });

  const mathElement = await page.$('#math');
  await mathElement.screenshot({ path: 'output.png' });

  await browser.close();
})();

実行方法

コマンドライン引数にLaTeX形式の数式を渡す。

node render_mathjax.js ‘数式(LaTeX)’

node render_mathjax.js '数式(LaTeX)'

引数を省略すると `\frac{a}{b}` がデフォルトで使われる。

node render_mathjax.js

node render_mathjax.js

実行例

分数

node render_mathjax.js ‘\frac{a}{b}‘

node render_mathjax.js '\frac{a}{b}'

二次方程式の解の公式

node render_mathjax.js ‘x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}‘

node render_mathjax.js 'x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}'

総和(シグマ)

node render_mathjax.js ‘\sum_{i=1}^{n} x_i’

node render_mathjax.js '\sum_{i=1}^{n} x_i'

積分

node render_mathjax.js ‘\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}’

node render_mathjax.js '\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}'

実行すると同じディレクトリに `output.png` が生成される。

ポイントまとめ

  • 数式要素だけをトリミング:
    page.$(‘#math’) で取得した要素に対して .screenshot() を呼ぶことで、余白なく数式だけを切り出せる
  • deviceScaleFactor: 3 で実質300dpi相当の高解像度出力になる
  • MathJaxの描画完了待ち:
    waitForFunctiondata-mathjax-ready 属性が付くまで待機することで、レンダリング前にスクリーンショットが走る問題を防いでいる
  • フォントサイズ調整:
    #math { font-size: 80px; } の値を変えると数式の大きさを調整できる

シェルでのエスケープに注意

バックスラッシュ `\` を含むLaTeX文字列はシングルクォート `’` で囲むと安全。

# OK node render_mathjax.js ‘\frac{a}{b}‘

NG(バックスラッシュが消える)

node render_mathjax.js “\frac{a}{b}“

# OK
node render_mathjax.js '\frac{a}{b}'

# NG(バックスラッシュが消える)
node render_mathjax.js "\frac{a}{b}"

ファイル構成

mathjax-to-png/
├── render_mathjax.js # 変換スクリプト
├── output.png # 生成されるPNG
└── package.json

← ITQ Lab トップに戻る