はじめに
資料作成やレポートの図表を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
| サイズ | 計算式 | ピクセル数 |
| 横 10cm | 10 × 300 / 2.54 | 1181px |
| 縦 7cm | 7 × 300 / 2.54 | 827px |
用途に合わせてスクリプト内の `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