HTML to PDFで実際に保持される要素、測定済み
headless Chromiumでレンダリングされた12個のprint-CSS機能をPDFから読み戻す — ページ区切り、@pageサイズ、繰り返されるテーブルヘッダー、リンク、フォント。そのうち2つは機能せず、1つの一般的な落とし穴はCSS側ではなくAPI側の問題です。
新しい機能: HTML to PDF print CSSの機能別サポートマトリックス。Chromium 148で測定され、各ケースをレンダリングして、結果のPDFのページ数、ページサイズ、リンク注釈、ページごとのテキストを確認 — ドキュメントを読むのではなく。すべての結論は公開されたテストスイートとデータセットから再現可能であり、広く推奨される2つの手法は機能しないことが示されています。
すべてのHTML to PDFサービスは、その出力が忠実であると主張しています。しかし、それが何を意味するのかを公表しているサービスはありません。 そこで、私たちが測定しました。12個のプリントCSS機能を、それぞれヘッドレスChromiumでレンダリングしました。このエンジンはMostlyRenderの背後にあり、ほとんどのHTML to PDFサービスの背後にもあります。その後、結果のPDFから読み直しました。
ここに示されているものは、ドキュメントから引用したものではありません。各結論は、ファイルを検査することで得られました。ページ数、ページサイズ、テキストがどのページに配置されたか、リンク注釈が存在するか、フォントが埋め込まれているかなどです。
# マトリックス
Chromium 148.0.7778.96上で測定しました。2026年9月1日に初回測定を行い、2026年9月27日に再測定しました。両方の実行で同じ12個の結論が得られました。
| 機能 | 動作 | 観察された内容 |
|---|---|---|
break-after: page |
はい | 2ページ、要求通り |
page-break-after: always (従来) |
はい | 2ページ — 古い記法も有効です |
break-inside: avoid |
はい | ブロックが分割されず、ページ2に移動しました |
@page { size: A5 } |
はい | 420×595pt — 正確にA5 |
@page { size: A4 landscape } |
はい | 842×595pt — 横向き |
ページ全体を通じた<thead>の繰り返し |
はい | 3ページ、ページ2にもヘッダーが表示されます |
ランニングヘッダーとしてのposition: fixed |
はい | 2ページ全に表示 |
@pageマージンボックス + counter(page) |
いいえ | 出力にページ番号が一切ありません |
クリック可能なリンクとしての<a href> |
はい | リンク注釈が存在します |
内部#anchorリンク |
はい | リンク先が存在します |
| フォント埋め込み | はい | DejaVuSansMonoが埋め込まれてサブセット化されました |
| ページ区切りを超えるCSSグリッド | いいえ | 8個のセルすべてがページ1に配置; ページ2は空です |
12個中10個が機能します。機能しない2つは、設計する前に知っておく価値があります。
# 失敗する2つ
CSS Paged Mediaのマージンボックスはレンダリングされません。 仕様では、@page { @bottom-center { content: "Page " counter(page) } }が利用可能で、Webからの印刷に関するあらゆるチュートリアルに含まれていますが、Chromiumはこれを無視します。出力にはページ番号がまったく見つかりませんでした。
ページ番号が必要な場合は、PDFを生成するツール側で追加する必要があります。そのため、ページ番号付けを提供するツールは、CSSで記述するよう指示するのではなく、機能として提供しているのです。
CSSグリッドはフラグメント化されません。 2ページに渡る高さの2列グリッドに8個のアイテムをレイアウトしました。8個すべてがページ1にレンダリングされ、ページ2は空のままでした。グリッドは1ページに収まるコンテンツには問題ありませんが、改ページが必要なコンテンツにはブロックフローまたはテーブルを使用してください。
# CSS自体ではない落とし穴
ハーネスを構築している最中に、@page { size: A5 }がサポートされていないと記録しました。これは私たち自身の誤りでした。レンダラーはAPI呼び出しでA4を使用するように指示されており、呼び出し元から渡された用紙形式はドキュメントの@pageサイズを静かに上書きします。 形式を省略し、CSSページサイズを優先すると、A5と横向きはどちらも正確に出力されました。
これは明確に述べる価値があります。@pageがサポートされていないと結論付ける最も一般的な理由は、これがほぼ確実だからです。CSSは問題なく、上流の何かが静かにそれを優先しているだけです。
仕様に矛盾する結果が出てきたため、数値を公表するのではなく、ハーネスを読み直すようにしました。
# 再現方法
ハーネスとこの表の元となったデータセットの両方が公開されています。
- bench-htmltopdf.js はChromiumを起動し、各ケースをレンダリングし、pdf-libとpdf.jsで出力を検査します。
- html-to-pdf.json はこの表の作成に使用した出力で、測定に使用したChromiumバージョンがタイムスタンプされています。
自分で実行するには、ハーネスを空のフォルダに保存して、以下を実行します。
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
1つの制限について述べておく価値があります。フォント検出はPDFの生バイトからフォント名をスキャンするため、肯定的な結果は確実ですが、空の結果は「このメソッドでは見えない」ことを意味するだけです。ハーネスはそのケースを失敗ではなく「結論付けられない」としてレポートします。スキャンで見えないという根拠で「フォントが埋め込まれていない」と公表することは、この記事が存在する理由である、まさにこうした主張を避けるためです。
# なぜ測定したのか
私たちはこのエンジンでHTML to PDFツールとAPIを構築しています。それが何をし、何をしないかを公表することは、それが忠実であると主張することより有用です。この表の数値が間違っていた場合、それを生成したハーネスは上にリンクされているので実行して確認できます。
よくある質問
page-break-afterはまだ機能していますか?それとも break-afterが必要ですか?
どちらも機能します。最新の break-after: page とレガシーの page-break-after: always を個別に測定したところ、Chromium 148 ではどちらも予想通り 2 ページのドキュメントが生成されました。古いテンプレートがレガシーのスペルを出力している場合でも、それがページネーション問題の原因ではありません。
自分の@pageサイズが無視されるのはなぜですか?
ほとんどの場合、呼び出し元がそれをオーバーライドしたからです。レンダリングを駆動する API またはライブラリが独自の用紙フォーマットを渡す場合、それが優先され、@page { size: … } はサイレントに破棄されます。フォーマットが省略され CSS ページサイズが優先される場合、A5 と横向きの両方が正確に要求通りに出力されました。これは「@page サイズが機能しない」という最も一般的な誤った報告であり、この記事用のハーネス構築時に私たちを引っかかりました。
CSSを使ってマージンにページ番号を付けることはできますか?
Chromium ではありません。@page { @bottom-center { content: counter(page) } } は CSS Paged Media の一部で広く文書化されていますが、マージンボックスは単にレンダリングされません。測定したところ、出力のどこにもページ番号がありませんでした。ページ番号はスタイルシートではなく、変換ツールから提供される必要があります。
CSSグリッドレイアウトはページ全体で適切に分割されますか?
いいえ。当社のテストでは、8つのグリッドアイテムはすべてページ1にレンダリングされ、2ページ目は空白のままでした — グリッドは分割されませんでした。ドキュメントがページネーションするのに十分な長さの場合は、通常のブロックフローまたはテーブルで繰り返し部分をレイアウトし、1ページに収まるものにグリッドを保持してください。
ハイパーリンクはPDFで保持されていますか?
はい。外部 <a href> と内部 #anchor の両方が出力ファイルに実際のリンク注釈を生成したため、リンクはクリック可能なままで、生成された目次は引き続き操作可能です。