【備忘録】PuppeteerでHTMLを300dpi相当のPNGに変換する

はじめに

資料作成やレポートの図表をHTMLで作って、そのままPNG画像として書き出したいことがある。

ブラウザのスクリーンショット機能では解像度が足りないし、スクリーンショットツールでは再現性がない。

そこで Puppeteer(Node.js でChrome/Chromiumを自動操縦するライブラリ)を使って、ローカルのHTMLファイルを高解像度のPNGに変換するスクリプトを作った。

環境・前提

  • macOS
  • Node.js インストール済み
  • インターネット接続不要(ローカルHTMLを変換)

セットアップ

まずプロジェクトフォルダを作り、Puppeteerをインストールする。

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

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

スクリプト(screenshot.js)

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

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

// HTMLファイルを読み込む(ローカルファイル) const htmlPath = ‘file://’ + __dirname + ‘/input.html’; await page.goto(htmlPath, { waitUntil: ‘networkidle0’ });

// ページサイズ(印刷用300dpiで横10cm x 縦7cmに相当) // 10cm = 1181px(10 * 300 / 2.54) const widthPx = 1181; const heightPx = 827;

await page.setViewport({ width: widthPx, height: heightPx });

// PNG画像を出力 await page.screenshot({ path: ‘output.png’, clip: { x: 0, y: 0, width: widthPx, height: heightPx }, });

await browser.close();

console.log(‘PNG出力完了: output.png’); })();

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

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

  // HTMLファイルを読み込む(ローカルファイル)
  const htmlPath = 'file://' + __dirname + '/input.html';
  await page.goto(htmlPath, { waitUntil: 'networkidle0' });

  // ページサイズ(印刷用300dpiで横10cm x 縦7cmに相当)
  // 10cm = 1181px(10 * 300 / 2.54)
  const widthPx = 1181;
  const heightPx = 827;

  await page.setViewport({ width: widthPx, height: heightPx });

  // PNG画像を出力
  await page.screenshot({
    path: 'output.png',
    clip: { x: 0, y: 0, width: widthPx, height: heightPx },
  });

  await browser.close();

  console.log('PNG出力完了: output.png');
})();

300dpiの考え方

Puppeteerはピクセル単位でキャプチャするため、dpiという概念は直接ない。

印刷物で「300dpi」とは「1インチあたり300ピクセル」なので、以下の換算式でピクセル数を決める。

px = cm × 300 / 2.54

px = cm × 300 / 2.54
サイズ計算式ピクセル数
横 10cm10 × 300 / 2.541181px
縦 7cm7 × 300 / 2.54827px

用途に合わせてスクリプト内の `widthPx` / `heightPx` を変更すればよい。

入力HTMLファイル(input.html)の例

同じディレクトリに `input.html` を置く。SVGや複雑なCSSも問題なくレンダリングされる。

こんにちは世界

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>body { margin: 0; }</style>
</head>
<body>
  <div style="width:100%; height:100%; display:flex; align-items:center; justify-content:center;">
    <h1>こんにちは世界</h1>
  </div>
</body>
</html>

実行

node screenshot.js

node screenshot.js

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

ポイントまとめ

  • `waitUntil: ‘networkidle0’` を指定することで、外部リソースの読み込み完了後にキャプチャできる
  • `clip` オプションで指定サイズにトリミングされるため、ブラウザのスクロール領域が大きくても余白が入らない
  • SVGやCSSアニメーションを含む複雑なHTMLも、Chromeがレンダリングするのでそのまま出力できる
  • ローカルファイルのみで動作するためオフライン環境でも使える

ファイル構成

html-to-png/
├── screenshot.js # 変換スクリプト
├── input.html # 変換したいHTMLを置く
├── output.png # 生成されるPNG
└── package.json

← ITQ Lab トップに戻る