CSSプリントスタイルシート完全ガイド|@page・@media print・PDF出力
HTMLからPDFを生成するとき、「背景色が消える」「改ページが意図しない場所に入る」「テーブルヘッダーが2ページ目以降に表示されない」「@pageのマージンが効かない」といった問題によく直面します。このガイドでは、ChromiumのプリントCSSに関するすべての技法を、コピペできるスニペット付きでまとめます。
FUNBREW PDF・Puppeteer・Playwright のいずれを使う場合も、CSSエンジンは同じChromiumのプリントパイプラインです。ここで紹介するルールはすべてに適用されます。
@pageとページ区切りのCSSに特化した問題についてはHTML to PDF CSSガイドを参照してください。CJKフォントの埋め込みは日本語フォントガイドで解説しています。レイアウト崩れや空白ページのトラブルシューティングはHTML to PDF トラブルシューティングをご覧ください。
2つのCSSコンテキスト:@media print vs @page
どのCSSルールがどこに適用されるかを理解することが最初のステップです。
@media print |
@page |
|
|---|---|---|
| 用途 | スタイルをPDF/印刷コンテキストにスコープ | 用紙の物理的な設定 |
| 制御対象 | フォント・色・表示・レイアウト | サイズ・向き・マージン |
| CSS変数サポート | あり | なし |
| 入れ子関係 | @pageを含められる |
単独または@media printの中 |
| 例 | @media print { .nav { display: none } } |
@page { size: A4; margin: 20mm } |
プリントスタイルシートは以下の順序で構造化するのが推奨パターンです。
/* 1. @media printですべてをスコープ */
@media print {
/* 2. @pageで用紙の物理設定 */
@page {
size: A4 portrait;
margin: 20mm 15mm 25mm 15mm; /* 上 右 下 左 */
}
/* 3. 画面用スタイルを印刷用に上書き */
body {
font-size: 11pt;
color: #000;
}
/* 4. 印刷不要な要素を非表示 */
.no-print,
nav,
.sidebar {
display: none !important;
}
}
@page:サイズ・向き・マージン
定義済み用紙サイズ
@page { size: A4 portrait; } /* 210mm × 297mm 縦 */
@page { size: A4 landscape; } /* 297mm × 210mm 横 */
@page { size: Letter portrait; } /* 8.5in × 11in */
@page { size: Legal; } /* 8.5in × 14in */
@page { size: A3 landscape; } /* 420mm × 297mm */
カスタムサイズ
@page {
size: 210mm 297mm; /* 幅 高さ を明示 */
margin: 20mm;
}
ページごとのマージン制御
@page { margin: 20mm 15mm; } /* 全ページ */
@page :first { margin-top: 40mm; } /* 表紙(1ページ目) */
@page :left { margin-left: 25mm; } /* 偶数ページ(製本余白) */
@page :right { margin-right: 25mm; } /* 奇数ページ(製本余白) */
注意:
@pageルール内ではCSSカスタムプロパティ(var())は機能しません。ハードコードした値を使ってください。
改ページ:break-before・break-after・break-inside
モダンなbreak-*プロパティと、レガシーなpage-break-*の両方を書くのが基本です。
セクション前で強制改ページ
.chapter,
.report-section,
h1 {
break-before: page;
page-break-before: always; /* レガシーフォールバック */
}
ブロック内での改ページを禁止
.card,
.figure,
.keep-together {
break-inside: avoid;
page-break-inside: avoid; /* レガシーフォールバック */
}
表紙ページの後で強制改ページ
.cover {
break-after: page;
page-break-after: always;
height: 100vh; /* 1ページ全体を埋める */
}
Flex・Gridレイアウトの落とし穴
break-inside: avoidをflex/gridの子要素に直接適用すると無視されることがあります。ブロック<div>でラップしてください。
<!-- OK: ブロック要素でラップ -->
<div class="grid-wrapper">
<div class="card-wrapper"> <!-- break-inside: avoid をここに -->
<div class="card">...</div>
</div>
</div>
.card-wrapper {
break-inside: avoid;
page-break-inside: avoid;
}
背景色・背景画像
全要素にグローバル適用
@media print {
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
要素ごとに適用
.colored-header,
.status-badge,
.chart-background {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
背景画像
.banner {
background-image: url('/img/logo.png');
background-size: cover;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
FUNBREW PDFを使う場合は、APIオプションでprintBackground: trueを渡すことでCSSを書かずにグローバルに有効化できます。
{
"options": {
"printBackground": true
}
}
テーブル:ヘッダーの繰り返しと行分割の防止
テーブルヘッダーを全ページで繰り返す
thead {
display: table-header-group;
}
tfoot {
display: table-footer-group;
}
tbody {
display: table-row-group;
}
<table>
<thead>
<tr>
<th>名前</th><th>金額</th><th>日付</th>
</tr>
</thead>
<tbody>
<!-- 複数ページにまたがる行 -->
</tbody>
</table>
行の途中で改ページを防ぐ
tr {
break-inside: avoid;
page-break-inside: avoid;
}
安定したカラム幅のための固定テーブルレイアウト
table {
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
th, td {
word-wrap: break-word;
overflow-wrap: break-word;
}
フォント:読み込みと埋め込み
システムフォントスタック(外部リクエストなし)
body {
font-family: -apple-system, 'Segoe UI', Arial, sans-serif;
}
@font-faceでセルフホスティング(CDN不要)
@font-face {
font-family: 'Noto Sans JP';
src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: block; /* フォント読み込みまで待つ */
}
body {
font-family: 'Noto Sans JP', sans-serif;
}
Base64埋め込みフォント(ネットワーク依存なし)
@font-face {
font-family: 'MyFont';
src: url('data:font/woff2;base64,AAABAA...') format('woff2');
}
Base64でフォントを埋め込むとCDN依存がなくなり、PDF生成で最も安定した方法です。日本語フォント(Noto Sans JP)の埋め込みは日本語フォントガイドで詳しく解説しています。
Google Fonts(CDN経由・ネットワークが必要)
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=block" rel="stylesheet">
FUNBREW PDFを使う場合はwaitForNetworkIdle: trueを追加して、フォント読み込み完了後にPDFを生成します。
{
"options": {
"waitForNetworkIdle": true
}
}
ページ番号とヘッダー・フッター
FUNBREW PDF の headerTemplate / footerTemplate
{
"options": {
"displayHeaderFooter": true,
"headerTemplate": "<div style='font-size:9pt; color:#888; padding:0 15mm; width:100%; display:flex; justify-content:space-between;'><span>月次レポート</span><span class='date'></span></div>",
"footerTemplate": "<div style='font-size:9pt; color:#888; padding:0 15mm; width:100%; text-align:center;'>ページ <span class='pageNumber'></span> / <span class='totalPages'></span></div>",
"margin": { "top": "25mm", "bottom": "20mm" }
}
}
テンプレート文字列で使えるChromium組み込みクラス:
| クラス | 出力 |
|---|---|
<span class='pageNumber'> |
現在のページ番号 |
<span class='totalPages'> |
総ページ数 |
<span class='date'> |
現在の日付 |
<span class='title'> |
ドキュメントのtitle |
<span class='url'> |
ドキュメントのURL |
印刷時の要素非表示
よく非表示にする要素
@media print {
/* ナビゲーションとサイドバー */
nav,
header.site-header,
footer.site-footer,
.sidebar,
.breadcrumb,
.pagination,
/* インタラクティブ要素 */
button,
input,
select,
textarea,
form,
/* ユーティリティクラス */
.no-print,
.screen-only {
display: none !important;
}
}
印刷専用要素を表示
.print-only {
display: none;
}
@media print {
.print-only {
display: block;
}
}
PDFのためのレイアウトパターン
フルブリード表紙ページ
@page :first {
margin: 0;
}
.cover-page {
width: 210mm;
height: 297mm;
padding: 40mm 30mm;
background: #1e3a5f;
color: #fff;
display: flex;
flex-direction: column;
justify-content: flex-end;
break-after: page;
}
2カラムレイアウト(印刷対応)
.two-column {
column-count: 2;
column-gap: 10mm;
column-rule: 1px solid #e2e8f0;
}
/* 見出しをカラムをまたいで分割させない */
h2, h3 {
break-after: avoid;
column-span: none;
}
請求書・データテーブルのレイアウト
.invoice-table {
width: 100%;
border-collapse: collapse;
font-size: 10pt;
}
.invoice-table th {
background: #1e3a5f;
color: #fff;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
padding: 3mm 4mm;
text-align: left;
}
.invoice-table td {
padding: 2.5mm 4mm;
border-bottom: 0.5px solid #e2e8f0;
}
.invoice-table tr:nth-child(even) td {
background: #f8fafc;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
FUNBREW PDF APIとの組み合わせ
上記のCSSを組み合わせたHTMLをAPIに送信します。
import fs from 'fs';
const html = fs.readFileSync('report.html', 'utf-8');
const response = await fetch('https://pdf.funbrew.cloud/api/v1/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.FUNBREW_PDF_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html,
options: {
format: 'A4',
printBackground: true, // 背景色・画像を有効化
waitForNetworkIdle: true, // Webフォント・CDNの読み込みを待つ
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:9pt;padding:0 15mm;width:100%;color:#888;">月次レポート</div>',
footerTemplate: '<div style="font-size:9pt;padding:0 15mm;width:100%;text-align:right;color:#888;">ページ <span class="pageNumber"></span></div>',
margin: { top: '25mm', right: '15mm', bottom: '20mm', left: '15mm' },
},
}),
});
const pdf = Buffer.from(await response.arrayBuffer());
fs.writeFileSync('output.pdf', pdf);
import requests, os
with open('report.html', 'r') as f:
html = f.read()
response = requests.post(
'https://pdf.funbrew.cloud/api/v1/generate',
headers={'Authorization': f'Bearer {os.environ["FUNBREW_PDF_API_KEY"]}'},
json={
'html': html,
'options': {
'format': 'A4',
'printBackground': True,
'waitForNetworkIdle': True,
'displayHeaderFooter': True,
'headerTemplate': '<div style="font-size:9pt;padding:0 15mm;width:100%;color:#888;">月次レポート</div>',
'footerTemplate': '<div style="font-size:9pt;padding:0 15mm;width:100%;text-align:right;color:#888;">ページ <span class="pageNumber"></span></div>',
'margin': {'top': '25mm', 'right': '15mm', 'bottom': '20mm', 'left': '15mm'},
},
},
)
response.raise_for_status()
with open('output.pdf', 'wb') as f:
f.write(response.content)
実際のHTMLをコードを書く前にFUNBREW PDF Playgroundでテストできます。
クイックリファレンス:25のCSSプリントスニペット
| 問題 | 対処 |
|---|---|
| 背景色が消える | print-color-adjust: exact |
| 背景画像が消える | 要素にprint-color-adjust: exact |
| セクション前で改ページ | break-before: page; page-break-before: always |
| ブロック内の改ページ禁止 | break-inside: avoid; page-break-inside: avoid |
| 表紙後で改ページ | break-after: page; page-break-after: always |
| テーブルヘッダーを全ページで繰り返す | thead { display: table-header-group } |
| テーブルフッターを全ページで繰り返す | tfoot { display: table-footer-group } |
| 行を分割させない | tr { break-inside: avoid } |
| カラム幅を固定 | table { table-layout: fixed } |
| カスタム用紙サイズ | @page { size: A4 landscape } |
| カスタムマージン | @page { margin: 20mm 15mm } |
| 表紙のマージン変更 | @page :first { margin-top: 40mm } |
| ナビをPDFで非表示 | @media print { nav { display: none } } |
| フォント確実に読み込む | ローカルWOFF2の@font-face |
| CDN経由フォント | APIオプションにwaitForNetworkIdle: true |
| ページ番号 | footerTemplateで<span class='pageNumber'> |
| 総ページ数 | footerTemplateで<span class='totalPages'> |
| フルブリード表紙 | @page :first { margin: 0 } |
| 2カラムレイアウト | column-count: 2; column-gap: 10mm |
| Flex改ページ修正 | 子要素をブロックdivでラップ |
| 背景をグローバル有効化 | APIオプションにprintBackground: true |
| フォント読み込み待ち | APIオプションにwaitForNetworkIdle: true |
| ヘッダーテンプレート | displayHeaderFooter: true + headerTemplate |
| フッターテンプレート | displayHeaderFooter: true + footerTemplate |
関連ガイド
- HTML to PDF CSSガイド — ChromiumのPDF描画特有のCSSバグを深掘り
- HTML to PDF トラブルシューティング — 空白ページ・レイアウト崩れ・要素消失の対処
- 日本語フォントガイド — Noto Sans JPとCJKフォントの埋め込み
- PDFレポート自動生成ガイド — Chart.js・ヘッダー・cronスケジューリング
- HTML to PDF完全ガイド — APIを初めて使う方向けのエンドツーエンド概要
- APIドキュメント — FUNBREW PDF APIの全オプション仕様
- Playground — 実装前にCSSとHTMLをブラウザでテスト