WeasyPrint完全ガイド|HTML・CSSから高品質なPDFを生成する方法
WeasyPrintは、HTMLとCSSからPDFを生成できるPython製のPDF生成ツールです。請求書、見積書、帳票、レポート、チケット、契約書、管理画面のエクスポート機能など、Webアプリケーションで「見た目を整えたPDF」を自動生成したい場面でよく使われます。公式ドキュメントでは、WeasyPrintはHTMLとCSSをPDFへ出力できるビジュアルレンダリングエンジンであり、印刷向けWeb標準をサポートすることを目指していると説明されています。
WeasyPrintの大きな特徴は、PDFを直接座標で描画するのではなく、HTMLテンプレートとCSSでレイアウトを作れる点です。Web開発者にとってなじみのあるHTML、CSS、Jinja2、Djangoテンプレート、Flaskテンプレートなどを活用できるため、帳票デザインとバックエンド実装を分離しやすくなります。
本記事では、WeasyPrintの基本、インストール方法、PythonコードでのPDF生成、CSSによるページ制御、ヘッダー・フッター、画像、表、日本語対応、Django・Flask・FastAPIとの連携、wkhtmltopdfやReportLabとの違い、パフォーマンス最適化、セキュリティ、ベストプラクティスまで詳しく解説します。
1. WeasyPrintとは
WeasyPrintとは、PythonからHTMLとCSSを読み込み、PDFとして出力できるライブラリです。Webページをそのまま印刷するというより、印刷向けに設計されたHTMLテンプレートをPDFへ変換する用途に向いています。特に、A4帳票、請求書、業務レポート、管理画面からのPDFエクスポートなど、サーバーサイドで安定したPDFを作りたい場合に便利です。
公式ドキュメントでは、WeasyPrintはWebKitやGeckoのようなフルブラウザエンジンではなく、ページネーション向けに設計されたPython製CSSレイアウトエンジンを持つと説明されています。つまり、ブラウザと完全に同じレンダリングを再現するツールではなく、印刷・PDF生成に特化したHTML/CSSレンダラーとして理解するのが正確です。
1.1 WeasyPrintの概要
WeasyPrintは、HTML文書、CSSスタイル、画像、フォントなどを読み込み、PDFファイルを生成します。開発者はPythonコードからHTML(...).write_pdf(...)を呼ぶだけでPDFを出力できます。公式ドキュメントでも、Python APIの基本例としてfrom weasyprint import HTMLを使い、HTML(...).write_pdf(...)でPDFを生成する方法が示されています。
| 項目 | 内容 |
|---|---|
| ツール名 | WeasyPrint |
| 主な用途 | HTML・CSSからPDFを生成 |
| 開発言語 | Python |
| 入力 | HTML、CSS、画像、フォント |
| 出力 | |
| 得意分野 | 帳票、請求書、レポート、印刷向けPDF |
| ライセンス | BSD 3-Clause License |
1.2 WeasyPrintの仕組み
WeasyPrintは、HTMLを解析し、CSSを適用し、ページ単位のレイアウトを計算してPDFを生成します。通常のWebブラウザは画面表示を前提にしていますが、WeasyPrintは紙面やPDFのようなページメディアを前提にレイアウトします。そのため、@page、余白、ページ番号、改ページ制御、ヘッダー・フッターのような印刷向け機能と相性が良いです。
処理の流れは、HTMLテンプレートを準備し、CSSでPDF向けのスタイルを定義し、PythonからWeasyPrintを呼び出してPDFを書き出す形になります。DjangoやFlaskでは、通常のHTMLテンプレートをレンダリングしてから、そのHTML文字列をWeasyPrintへ渡す構成がよく使われます。
1.3 WeasyPrintが選ばれる理由
WeasyPrintが選ばれる理由は、HTML/CSSベースでPDFを作れるため、Web開発の知識をそのまま活かしやすいからです。ReportLabのように座標を細かく指定してPDFを組み立てる方法より、テンプレートエンジンとの相性が良く、デザイン修正もしやすいです。
また、wkhtmltopdfのようにブラウザエンジンへ依存する方式とは異なり、WeasyPrintは印刷向けCSSに重点を置いています。公式ドキュメントでは、CSS Paged Mediaの@page、ページ余白ボックス、ページカウンター、ページサイズなどの機能が利用できると説明されています。
1.4 対応している技術
WeasyPrintはHTMLとCSSを入力として利用します。CSSについては、すべてのブラウザ向け機能を完全に再現するわけではありませんが、印刷向けPDF生成に必要な機能を多く備えています。公式ドキュメントでは、@font-face、CSS Paged Media、ページカウンター、ページサイズ、名前付きページ、CSS変数などの対応が説明されています。
| 技術 | 対応の考え方 |
|---|---|
| HTML | PDF化する文書構造として利用 |
| CSS | レイアウト・余白・フォント・色・表組みに利用 |
| CSS Paged Media | ページサイズ、余白、ヘッダー、フッター、ページ番号に有効 |
| 画像 | レポート・請求書・ロゴ・グラフに利用 |
| SVG | 図版やアイコンに利用可能 |
| Python API | WebアプリケーションからPDF生成を実行 |
1.5 主な利用シーン
WeasyPrintは、WebアプリケーションからPDFを出力する場面に向いています。たとえば、管理画面で請求書を作成してPDFダウンロードする、月次レポートを自動生成する、契約書テンプレートにユーザーデータを差し込んでPDF化する、帳票システムでA4印刷向けPDFを作るといった用途です。
特に、HTMLテンプレートを使って帳票を作りたい場合に便利です。デザイナーやフロントエンド開発者がHTML/CSSで見た目を調整し、バックエンド開発者がPythonでデータを差し込むという分業もしやすくなります。
2. WeasyPrintの主な特徴
WeasyPrintの特徴は、HTMLとCSSを使ってPDFを作れること、Pythonアプリケーションと連携しやすいこと、印刷向けCSSに対応していることです。WebアプリケーションでPDFを生成する場合、データ差し込み、テンプレート管理、デザイン調整、ダウンロードレスポンスの生成が必要になりますが、WeasyPrintはこの流れに自然に組み込めます。
一方で、WeasyPrintはブラウザそのものではありません。JavaScriptで動的に描画される画面をそのままPDF化する用途や、ブラウザと完全一致の見た目を求める用途には注意が必要です。
2.1 HTMLからPDFを生成できる
WeasyPrintの中心機能は、HTMLからPDFを生成することです。HTMLファイル、URL、HTML文字列、ファイルオブジェクトなどを入力として扱えます。公式ドキュメントでは、ファイル名、絶対URL、読み取り可能なファイルオブジェクト、文字列をHTMLやCSSへ渡せると説明されています。
基本的なコードは次のようになります。
from weasyprint import HTMLHTML("invoice.html").write_pdf("invoice.pdf")
このコードでは、invoice.htmlを読み込み、invoice.pdfとして保存します。HTMLとCSSを分けて管理すれば、テンプレートとデザインを整理しやすくなります。
2.2 CSSによる柔軟なデザイン
WeasyPrintでは、CSSを使ってPDFの見た目を調整できます。フォント、色、余白、表、ボーダー、背景、ページサイズ、改ページ、ページ番号などをCSSで管理できます。特に@pageを使うことで、A4サイズ、余白、ヘッダー、フッターなどを印刷向けに設計できます。
@page { size: A4; margin: 20mm;}body { font-family: sans-serif; font-size: 12px;}
Web画面向けCSSとPDF向けCSSは考え方が異なるため、PDF専用のCSSファイルを用意するのが実務ではおすすめです。
2.3 オープンソースで利用可能
WeasyPrintはオープンソースソフトウェアで、BSD 3-Clause Licenseのもとで公開されています。GitHub上のライセンスにもBSD 3-Clause Licenseと記載されています。
業務システムで使う場合でも、ライセンス条件を確認したうえで導入しやすい点がメリットです。ただし、商用利用や再配布の扱いはプロジェクトの法務・ライセンス確認ルールに従って判断する必要があります。
2.4 Pythonとの高い親和性
WeasyPrintはPythonライブラリとして使えるため、Django、Flask、FastAPI、Celery、Jinja2などと組み合わせやすいです。HTMLテンプレートにデータを差し込み、その結果をWeasyPrintへ渡してPDF化する構成が自然に作れます。
from weasyprint import HTML, CSShtml = HTML(string="<h1>Monthly Report</h1><p>Total: 100</p>")css = CSS(string="h1 { color: #333; }")html.write_pdf("report.pdf", stylesheets=[css])
Pythonで帳票データを組み立て、HTMLテンプレートへ渡し、PDFとして出力する流れは、業務アプリケーションと相性が良いです。
2.5 印刷向け機能への対応
WeasyPrintは、ページメディア向けのCSSに強いことが特徴です。公式ドキュメントでは、CSS Paged Media Module Level 3の機能として、@pageルール、左右ページ・先頭ページ・空白ページセレクタ、ページ余白ボックス、ページカウンター、ページサイズなどが利用できると説明されています。
| 機能 | PDF生成での用途 |
|---|---|
@page | 用紙サイズ・余白設定 |
| ページカウンター | ページ番号表示 |
| page margin boxes | ヘッダー・フッター |
| named pages | 特定ページだけレイアウト変更 |
| 改ページ制御 | 表や章の途中分断を防ぐ |
3. WeasyPrintの動作環境
WeasyPrintを導入する前に、対応OS、Pythonバージョン、依存ライブラリを確認する必要があります。特に、フォント描画やHTML/CSSレンダリングに関係するライブラリが必要になるため、OSによってセットアップ手順が異なります。
公式ドキュメントの安定版では、WeasyPrintはPython 3.10以上、Pango 1.44以上などに依存すると説明されています。 本番環境では、Pythonだけでなく、Pango、フォント、画像ライブラリ、OSパッケージまで含めて検証することが重要です。
3.1 対応OS
WeasyPrintはLinux、macOS、Windowsで利用できます。ただし、インストール方法や必要な依存ライブラリはOSによって異なります。Linuxではディストリビューションのパッケージマネージャーやpipを使い、macOSではHomebrew、Windowsでは実行ファイルまたはMSYS2を使う方法が公式ドキュメントで説明されています。
| OS | 導入方法の例 |
|---|---|
| Linux | パッケージマネージャー、pip、virtualenv |
| macOS | Homebrew、pip、virtualenv |
| Windows | 公式実行ファイル、Python + MSYS2 |
| Docker | 依存関係込みのイメージを利用 |
| WSL | Linuxと同様の方法で導入可能 |
3.2 必要な依存ライブラリ
WeasyPrintはPythonだけで完結する単純なライブラリではなく、文字描画やCSS処理、画像処理のために複数の依存ライブラリを利用します。公式ドキュメントでは、Pango、pydyf、CFFI、tinyhtml5、tinycss2、cssselect2、Pyphen、Pillow、fontToolsなどが依存関係として挙げられています。
| 依存要素 | 主な役割 |
|---|---|
| Python | WeasyPrint本体の実行 |
| Pango | テキストレイアウト・フォント処理 |
| Pillow | 画像処理 |
| tinycss2 | CSS解析 |
| tinyhtml5 | HTML解析 |
| fontTools | フォント関連処理 |
3.3 Pythonバージョン要件
現在の安定版ドキュメントでは、WeasyPrintはPython 3.10以上を対象としています。 そのため、古いPython環境を使っているサーバーでは、Pythonのアップデートや仮想環境の準備が必要になる場合があります。
本番環境では、OS標準のPythonを直接使うより、venvやDockerを使ってWeasyPrint用の環境を分離するのがおすすめです。これにより、依存関係の衝突を避けやすくなります。
3.4 推奨環境
実務では、Linux + Python仮想環境 + 必要フォント + WeasyPrintという構成が安定しやすいです。帳票生成を大量に行う場合は、Webリクエスト内で直接PDFを生成するより、非同期ジョブとして処理する方が安全です。
| 用途 | 推奨環境 |
|---|---|
| 開発環境 | Python venv + pip |
| Linux本番 | Ubuntu/Debian + venv + OS依存ライブラリ |
| コンテナ運用 | Dockerイメージに依存関係を固定 |
| 大量生成 | Celery/RQなどの非同期ワーカー |
| 日本語PDF | 日本語フォントを明示的に導入 |
3.5 導入前に確認すべきポイント
WeasyPrint導入前には、PDF生成対象のHTMLがJavaScript依存ではないか、日本語フォントが利用できるか、画像パスが正しく解決できるか、帳票のページ数が多すぎないか、サーバー負荷に耐えられるかを確認します。
特に、ユーザーがHTMLやCSSを自由に入力できるサービスでは、セキュリティ対策が必要です。公式ドキュメントでは、信頼できないHTMLやCSSをWeasyPrintで処理すると、高メモリ使用、長時間レンダリング、ローカルファイル漏洩などの問題が起こり得るため、実行ユーザー制限、ファイルシステム・ネットワーク・メモリ制限、コンテナ利用などを推奨しています。
4. WeasyPrintのインストール方法
WeasyPrintのインストール方法はOSによって異なります。pipだけで簡単に入る環境もありますが、Pangoやフォント関連ライブラリが不足しているとエラーが出ることがあります。特にWindowsやmacOSでは、ネイティブライブラリのパス設定が原因でつまずくことがあります。
この章では、pip、Linux、Windows、macOSの導入方法と、よくあるエラーを整理します。
4.1 pipによるインストール
Python仮想環境を作成してからpipでインストールする方法が基本です。Linux環境の公式ドキュメントでも、仮想環境を作成し、pip install weasyprintを実行してからweasyprint --infoで確認する流れが示されています。
python3 -m venv venvsource venv/bin/activatepip install weasyprintweasyprint --info
Pythonコードで使う場合は、次のように確認できます。
from weasyprint import HTMLHTML(string="<h1>Hello WeasyPrint</h1>").write_pdf("hello.pdf")
4.2 Linux環境でのセットアップ
UbuntuやDebianでは、OSパッケージとしてWeasyPrintを導入する方法と、依存ライブラリを入れてからpipで導入する方法があります。公式ドキュメントでは、UbuntuやDebian向けにapt install weasyprintや、pip利用時に必要なPango関連ライブラリが示されています。
sudo apt updatesudo apt install -y python3-pip libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0python3 -m venv venvsource venv/bin/activatepip install weasyprintweasyprint --info
日本語PDFを作る場合は、日本語フォントも入れておくと安全です。
sudo apt install -y fonts-noto-cjk
4.3 Windows環境でのセットアップ
Windowsでは、最新リリースの実行ファイルを使う方法が最も簡単と公式ドキュメントで説明されています。Pythonライブラリとして使う場合は、Pythonを入れたうえで、Pangoと依存関係をMSYS2で導入する手順が案内されています。
Pythonライブラリとして使う場合の流れは次のようになります。
python -m venv venvvenv\Scripts\activate.batpython -m pip install weasyprintpython -m weasyprint --info
DLLが見つからない場合は、MSYS2のmingw64\binを環境変数へ追加する必要があることがあります。公式ドキュメントでは、WindowsやmacOSでライブラリが見つからない場合、DLLやdylibのパス設定が原因になることが多いと説明されています。
4.4 macOS環境でのセットアップ
macOSでは、Homebrewを使ってWeasyPrintを導入する方法が公式ドキュメントで紹介されています。
brew install weasyprintweasyprint --info
pipで管理したい場合は、HomebrewでPangoなどの依存ライブラリを用意し、Python仮想環境にWeasyPrintを入れる方法もあります。
brew install pangopython3 -m venv venvsource venv/bin/activatepip install weasyprint
4.5 インストール時によくあるエラー
WeasyPrintのインストールで多いのは、依存ライブラリ不足、フォント不足、DLLやdylibが見つからない問題です。公式ドキュメントでも、文字が表示されない、四角になる場合はフォントをインストールし、WeasyPrintから利用可能にする必要があると説明されています。
| エラー | 原因 | 対策 |
|---|---|---|
| cannot load library | Pangoなどのネイティブライブラリ不足 | OSパッケージを追加 |
| 日本語が□になる | 日本語フォント不足 | Noto CJKなどを導入 |
| 画像が表示されない | パス解決失敗 | base_urlを指定 |
| CSSが反映されない | CSSファイル参照失敗 | 絶対パスまたはbase_url確認 |
| WindowsでDLLエラー | DLLパス未設定 | MSYS2パスや環境変数を確認 |
5. HTMLからPDFを生成する方法
WeasyPrintでPDFを生成する基本は、HTMLを用意してwrite_pdf()を呼ぶことです。HTMLファイル、HTML文字列、URL、テンプレートエンジンで生成したHTMLなど、さまざまな入力方法があります。
実務では、HTMLテンプレートにデータを差し込み、CSSでPDF向けのレイアウトを定義し、PDFファイルとして保存するか、HTTPレスポンスとして返す構成が一般的です。
5.1 基本的なPDF生成
最も簡単な例は、HTML文字列からPDFを作る方法です。
from weasyprint import HTMLhtml = """<!doctype html><html><head> <meta charset="utf-8"> <title>Sample PDF</title></head><body> <h1>WeasyPrint Sample</h1> <p>HTMLからPDFを生成しています。</p></body></html>"""HTML(string=html).write_pdf("sample.pdf")
このコードでは、HTML文字列を直接WeasyPrintへ渡してPDFを生成しています。小さなテストや動作確認にはこの方法が便利です。
5.2 HTMLファイルから出力する方法
HTMLファイルを用意してPDF化する場合は、ファイル名を指定します。
from weasyprint import HTMLHTML(filename="templates/invoice.html").write_pdf("output/invoice.pdf")
画像やCSSを相対パスで参照する場合は、base_urlを指定すると安定します。
from weasyprint import HTMLHTML( filename="templates/invoice.html", base_url=".").write_pdf("output/invoice.pdf")
5.3 文字列から出力する方法
DjangoやFlaskでは、テンプレートをレンダリングした結果のHTML文字列をWeasyPrintへ渡すことが多いです。
from weasyprint import HTMLrendered_html = """<h1>請求書</h1><p>請求先:株式会社サンプル</p><p>金額:100,000円</p>"""pdf_bytes = HTML(string=rendered_html).write_pdf()
write_pdf()に保存先を渡さない場合、PDFのバイト列がメモリ上に返されます。公式ドキュメントでも、write_pdf()は引数なしならバイト列を返し、ファイル名や書き込み可能なファイルオブジェクトを渡すとそこへ書き込むと説明されています。
5.4 PDF保存先の指定
PDFをファイルとして保存する場合は、出力先パスを指定します。
from pathlib import Pathfrom weasyprint import HTMLoutput_dir = Path("pdf")output_dir.mkdir(exist_ok=True)HTML(string="<h1>Report</h1>").write_pdf(output_dir / "report.pdf")
実務では、ファイル名の衝突を避けるため、注文番号、請求書番号、UUID、日付などを組み合わせたファイル名を使うと管理しやすくなります。
5.5 日本語対応のポイント
日本語PDFでは、文字化けや豆腐文字を防ぐため、サーバーに日本語フォントをインストールし、CSSで明示的に指定することが重要です。
body { font-family: "Noto Sans CJK JP", "Noto Sans JP", sans-serif;}
より確実にしたい場合は、@font-faceでフォントファイルを指定します。WeasyPrintで@font-faceを使う場合、公式ドキュメントではFontConfigurationを作成し、HTMLとCSSのPDF出力に渡す例が示されています。
6. CSSによるPDFデザイン
WeasyPrintでは、PDFの見た目をCSSで設計できます。ただし、画面向けCSSと印刷向けCSSでは考え方が異なります。Web画面ではレスポンシブ対応やスクロール表示を前提にしますが、PDFではページサイズ、余白、改ページ、印刷時の読みやすさを前提にします。
PDF向けCSSでは、A4サイズ、余白、ページ番号、ヘッダー・フッター、表の改ページ制御、フォント埋め込みなどを重視します。
6.1 CSSの適用方法
CSSは、HTML内のstyleタグ、外部CSSファイル、PythonコードのCSSオブジェクトで適用できます。
from weasyprint import HTML, CSShtml = HTML(filename="invoice.html", base_url=".")css = CSS(filename="invoice.css")html.write_pdf("invoice.pdf", stylesheets=[css])
テンプレートを複数使う場合は、共通CSSと帳票別CSSを分けると管理しやすくなります。
6.2 フォント設定
PDFでは、フォントが非常に重要です。サーバーに存在しないフォントを指定しても正しく表示されない場合があります。日本語PDFでは、Noto Sans CJK JPやNoto Serif CJK JPなどを導入し、CSSで明示するのが安全です。
@font-face { font-family: "NotoJP"; src: url("fonts/NotoSansCJKjp-Regular.otf");}body { font-family: "NotoJP", sans-serif;}
Python側では次のようにFontConfigurationを使います。
from weasyprint import HTML, CSSfrom weasyprint.text.fonts import FontConfigurationfont_config = FontConfiguration()html = HTML(filename="invoice.html", base_url=".")css = CSS(filename="invoice.css", font_config=font_config)html.write_pdf( "invoice.pdf", stylesheets=[css], font_config=font_config)
6.3 レイアウト調整
PDFレイアウトでは、横幅を固定し、印刷時に崩れない構造を作ることが重要です。Web画面のように可変幅や複雑なレスポンシブグリッドを使うより、A4幅に合わせた固定的なレイアウトの方が安定します。
.container { width: 170mm; margin: 0 auto;}.summary { display: block; margin-top: 16px;}
Flexboxも簡単な用途では使えますが、公式ドキュメントではFlexboxは単純なケースでは動作するものの深くテストされていないと説明されています。複雑なPDF帳票では、表やブロックレイアウトを中心に設計する方が安定しやすいです。
6.4 印刷スタイルの作成
印刷向けCSSでは、@pageと@media printを使います。
@page { size: A4; margin: 18mm 15mm;}@media print { .no-print { display: none; }}
管理画面では表示したいがPDFには不要なボタンやナビゲーションは、.no-printのようなクラスで非表示にします。
6.5 レスポンシブデザインとの違い
Web画面では、スマートフォン、タブレット、PCに合わせてレイアウトを変えます。一方、PDFではA4やレターサイズなど固定された紙面を前提にします。そのため、PDFテンプレートではレスポンシブよりも、印刷時の再現性を重視します。
| 観点 | Web画面 | |
|---|---|---|
| 表示領域 | デバイスごとに変化 | 用紙サイズで固定 |
| スクロール | 前提になる | ページ分割される |
| レイアウト | レスポンシブ重視 | 改ページ重視 |
| ナビゲーション | 必要 | 通常不要 |
| CSS設計 | 画面向け | 印刷向け |
7. ページ設定をカスタマイズする方法
PDFでは、ページサイズ、余白、改ページ、向き、ページ番号を適切に設定する必要があります。WeasyPrintはCSS Paged Mediaに対応しているため、これらの設定をCSSで記述できます。公式ドキュメントでは、CSS Paged Mediaによりページサイズ、向き、余白、ヘッダー・フッター、ページカウンターなどを扱えると説明されています。
帳票や請求書では、1ページ目と2ページ目以降でレイアウトを変えたい場合もあります。その場合は、:firstや名前付きページを活用します。
7.1 用紙サイズの設定
A4サイズを指定する例です。
@page { size: A4; margin: 20mm;}
横向きにする場合は次のように書きます。
@page { size: A4 landscape; margin: 15mm;}
7.2 余白の設定
余白は、上下左右を個別に指定できます。
@page { margin-top: 25mm; margin-right: 15mm; margin-bottom: 20mm; margin-left: 15mm;}
ヘッダーやフッターを置く場合は、余白を大きめに確保します。本文とヘッダー・フッターが重ならないように設計することが重要です。
7.3 改ページ制御
章や表が途中で不自然に分断されないように、改ページ制御を行います。
.section { break-inside: avoid;}.page-break { break-before: page;}tr { break-inside: avoid;}
請求書の明細行がページをまたぐ場合は、表ヘッダーを繰り返す、行の分断を避ける、明細数が多い場合のレイアウトを確認する必要があります。
7.4 縦向き・横向き設定
通常の請求書や契約書は縦向き、一覧表や分析レポートは横向きが適している場合があります。
@page report-wide { size: A4 landscape; margin: 12mm;}.wide-page { page: report-wide;}
名前付きページを使うと、特定の章だけ横向きにするような設計も可能です。
7.5 ページ番号の表示
ページ番号は、CSSのカウンターを使って表示できます。
@page { @bottom-center { content: counter(page) " / " counter(pages); font-size: 10px; color: #666; }}
このように設定すると、各ページの下部に「現在ページ / 総ページ数」を表示できます。帳票や報告書では、ページ番号があると印刷後の管理がしやすくなります。
8. ヘッダーとフッターの実装
帳票PDFでは、ヘッダーとフッターが重要です。会社名、ロゴ、帳票タイトル、発行日、ページ番号、問い合わせ先などを各ページに表示することで、印刷物としての品質が上がります。
WeasyPrintでは、@pageのmargin boxを使って簡単なヘッダー・フッターを設定できます。より複雑なHTMLヘッダーを扱いたい場合は、running elementsを利用する方法もあります。公式ドキュメントでは、CSS Generated Content for Paged Mediaの機能として、ページ余白へHTMLボックスを置くrunning elementsが説明されています。
8.1 ヘッダー設定
シンプルなヘッダーは、@top-centerなどで指定します。
@page { margin: 20mm 15mm; @top-center { content: "Monthly Report"; font-size: 10px; color: #666; }}
会社名や帳票名を入れる場合は、短いテキストならこの方法で十分です。
8.2 フッター設定
フッターには、会社情報、注意書き、ページ番号などを表示できます。
@page { @bottom-center { content: "© Sample Company"; font-size: 9px; color: #999; }}
契約書や請求書では、フッターに住所や登録番号を入れるケースもあります。ただし、長い文章をmargin boxに入れすぎると見づらくなるため、簡潔にまとめるのが基本です。
8.3 ページ番号の表示
ページ番号は、帳票や報告書で非常に重要です。
@page { @bottom-right { content: "Page " counter(page) " of " counter(pages); font-size: 9px; }}
日本語表記にする場合は次のようにします。
@page { @bottom-right { content: counter(page) "ページ / 全" counter(pages) "ページ"; font-size: 9px; }}
8.4 会社情報の表示
請求書では、ヘッダーに会社名やロゴ、フッターに住所や連絡先を入れると実務的です。
@page { @top-left { content: "株式会社サンプル"; font-size: 10px; } @bottom-left { content: "東京都千代田区1-1-1 / [email protected]"; font-size: 8px; color: #777; }}
より複雑な会社情報は、本文の上部にHTMLとして配置し、ページごとの固定情報はフッターに置くのが扱いやすいです。
8.5 共通レイアウトの管理
複数の帳票で共通のヘッダー・フッターを使う場合は、CSSとHTMLテンプレートを分離します。DjangoやFlaskでは、ベーステンプレートを作り、請求書、見積書、レポートなどで継承する設計が便利です。
| 管理対象 | おすすめの管理方法 |
|---|---|
| 会社ロゴ | 共通テンプレート |
| フォント | 共通CSS |
| ページ余白 | 共通CSS |
| 帳票ごとのタイトル | 個別テンプレート |
| 明細表 | 個別コンポーネント |
| フッター | 共通CSSまたは共通HTML |
9. 画像とグラフの表示
PDFでは、ロゴ、印影、商品画像、グラフ、QRコードなどの画像を扱うことがよくあります。WeasyPrintはHTML内のimgタグやCSS背景画像を解釈できますが、画像パスの解決には注意が必要です。
特にWebアプリケーション内でPDFを生成する場合、相対パスの画像が見つからない問題が発生しやすいです。base_urlを正しく指定する、または絶対パスやURLを使うことで安定します。
9.1 画像の埋め込み
HTMLでは通常通りimgタグを使います。
<img src="static/logo.png" class="logo" alt="Company Logo">
Python側ではbase_urlを指定します。
from weasyprint import HTMLHTML( filename="invoice.html", base_url=".").write_pdf("invoice.pdf")
DjangoやFlaskでは、静的ファイルのパス解決を考慮する必要があります。公式ドキュメントでは、Flask-WeasyPrintやDjango-WeasyPrintがカスタムURL fetcherを使い、静的ファイルやメディアファイルをネットワーク経由ではなくファイルシステムから扱う仕組みを持つと説明されています。
9.2 SVGの利用
SVGは、ロゴ、アイコン、図版、グラフに向いています。ベクター形式なので、PDFでも拡大縮小に強いです。
<img src="static/chart.svg" alt="Sales Chart">
ただし、ユーザーがアップロードしたSVGをそのままPDF化する場合はセキュリティに注意が必要です。公式ドキュメントでは、信頼できないSVGにもHTMLやCSSと同様のセキュリティ注意が必要で、SVGレンダリングにもURL fetcherが使われると説明されています。
9.3 グラフ画像の出力
Matplotlibなどでグラフ画像を生成し、それをPDFに埋め込むこともできます。
import matplotlib.pyplot as pltlabels = ["Jan", "Feb", "Mar"]values = [100, 140, 120]plt.figure()plt.bar(labels, values)plt.savefig("static/sales.png", dpi=150, bbox_inches="tight")plt.close()
HTML側では次のように読み込みます。
<img src="static/sales.png" class="chart" alt="Sales Chart">
9.4 解像度の最適化
PDFに埋め込む画像は、解像度が低すぎると印刷時に荒くなり、高すぎるとPDFサイズが大きくなります。帳票やレポートでは、ロゴやグラフは適切なサイズにリサイズしてから使うのが基本です。
| 画像用途 | 推奨方針 |
|---|---|
| ロゴ | SVGまたは高解像度PNG |
| グラフ | 150〜200dpi程度から検証 |
| 写真 | 必要サイズへリサイズ |
| QRコード | PNGまたはSVG |
| 背景画像 | PDFサイズ肥大化に注意 |
9.5 パフォーマンスへの影響
画像が多いPDFは、生成時間とメモリ使用量が増えます。大量の画像を含む帳票では、画像サイズを圧縮し、必要なものだけを読み込む設計が重要です。
WeasyPrint公式ドキュメントには画像のキャッシュと最適化に関するユースケースもあり、大量生成時には画像処理の負荷を意識する必要があります。
10. テーブルを美しく表示する方法
帳票PDFでは、テーブルが非常によく使われます。請求明細、売上レポート、在庫一覧、ユーザー一覧、契約条件など、多くの情報は表形式で整理されます。WeasyPrintではHTMLのtableを使って表を表現できます。
ただし、PDFではページをまたぐ可能性があるため、Web画面のテーブルとは違った注意点があります。ヘッダーの繰り返し、行の分断防止、セル幅、文字折り返し、明細が多い場合のページ制御が重要です。
10.1 基本的な表組み
<table class="items"> <thead> <tr> <th>品目</th> <th>数量</th> <th>単価</th> <th>金額</th> </tr> </thead> <tbody> <tr> <td>Web制作費</td> <td>1</td> <td>100,000円</td> <td>100,000円</td> </tr> </tbody></table>
.items { width: 100%; border-collapse: collapse; font-size: 11px;}.items th,.items td { border: 1px solid #ccc; padding: 6px 8px;}.items th { background: #f2f2f2;}
10.2 セル結合
合計欄や備考欄ではcolspanを使うことがあります。
<tr> <td colspan="3" class="total-label">合計</td> <td class="total-value">100,000円</td></tr>
.total-label { text-align: right; font-weight: bold;}.total-value { text-align: right; font-weight: bold;}
請求書では、数量、単価、金額を右寄せにすると読みやすくなります。
10.3 改ページ対策
表がページをまたぐ場合は、ヘッダーを繰り返す、行の途中分断を避ける、合計欄を分かりやすくすることが重要です。
thead { display: table-header-group;}tfoot { display: table-footer-group;}tr { break-inside: avoid;}
theadをtable-header-groupにすると、ページをまたいだ際にヘッダーが繰り返されやすくなります。
10.4 請求書形式への応用
請求書では、上部に請求先・発行元・請求番号・発行日を置き、中央に明細表、下部に小計・税額・合計を配置します。
| エリア | 内容 |
|---|---|
| ヘッダー | 請求書タイトル、請求番号、発行日 |
| 宛先 | 請求先会社名、担当者名 |
| 発行元 | 自社名、住所、登録番号 |
| 明細表 | 品目、数量、単価、金額 |
| 合計欄 | 小計、消費税、合計 |
| 備考 | 支払期限、振込先 |
10.5 大量データ表示の工夫
大量データを1つのPDFに入れる場合、PDF生成時間が長くなり、ファイルサイズも大きくなります。必要に応じて、月別・カテゴリ別にPDFを分割する、CSVエクスポートとPDFを使い分ける、グラフや要約を先頭に置くなどの工夫が必要です。
11. 日本語PDFを作成する方法
日本語PDFを作る場合、最も重要なのはフォント設定です。HTMLがUTF-8で書かれていても、サーバーに日本語フォントがなければ文字化けや四角表示になることがあります。公式ドキュメントでも、生成PDFに文字が描画されない、または四角になる場合は、フォントをインストールしてWeasyPrintから利用可能にする必要があると説明されています。
日本語帳票では、明朝体、ゴシック体、太字、数字の視認性、印刷時の読みやすさを確認することが重要です。
11.1 日本語フォントの設定
Linuxでは、Noto CJKフォントを導入すると扱いやすいです。
sudo apt install -y fonts-noto-cjk
CSSでは次のように指定します。
body { font-family: "Noto Sans CJK JP", "Noto Sans JP", sans-serif;}
帳票では、本文はゴシック、契約書や正式文書は明朝系を使うこともあります。
11.2 文字化け対策
HTMLにはUTF-8を明示します。
<meta charset="utf-8">
ファイル自体もUTF-8で保存します。また、Pythonコードで文字列を扱う場合も、ファイル読み込み時のエンコーディングを明示すると安全です。
from pathlib import Pathfrom weasyprint import HTMLhtml = Path("invoice.html").read_text(encoding="utf-8")HTML(string=html, base_url=".").write_pdf("invoice.pdf")
11.3 PDF埋め込みフォント
配布先の環境に依存しないPDFを作るには、フォントを埋め込むことが重要です。@font-faceでフォントファイルを参照し、FontConfigurationを使うと管理しやすくなります。公式ドキュメントでも、@font-faceを使う場合にFontConfigurationを作成してHTMLとCSSへ渡す例が示されています。
from weasyprint import HTML, CSSfrom weasyprint.text.fonts import FontConfigurationfont_config = FontConfiguration()css = CSS( string=""" @font-face { font-family: NotoJP; src: url("fonts/NotoSansCJKjp-Regular.otf"); } body { font-family: NotoJP; } """, font_config=font_config)HTML(string="<p>日本語PDFのテスト</p>", base_url=".").write_pdf( "japanese.pdf", stylesheets=[css], font_config=font_config)
11.4 多言語対応
日本語、英語、中国語、韓国語などを同じPDFで扱う場合は、CJK対応フォントを選ぶと安定します。ただし、フォントファイルが大きい場合、PDFサイズが増える可能性があります。
| 言語 | フォント方針 |
|---|---|
| 日本語 | Noto Sans CJK JP |
| 中国語 | Noto Sans CJK SC / TC |
| 韓国語 | Noto Sans CJK KR |
| 多言語混在 | Noto Sans CJK系を検討 |
| 欧文中心 | Noto Sans / Noto Serif |
11.5 印刷品質の向上
日本語PDFでは、行間、文字サイズ、余白、禁則処理、表内の折り返しを確認します。特に帳票では、数字や日付が読みやすいことが重要です。
印刷前提なら、画面確認だけでなく、実際にPDFビューアで開き、A4印刷してレイアウトを確認します。
12. DjangoでWeasyPrintを利用する方法
Djangoでは、テンプレートエンジンでHTMLを生成し、そのHTMLをWeasyPrintへ渡してPDF化できます。請求書、領収書、管理レポート、契約書など、DBのデータをテンプレートへ差し込む帳票出力と相性が良いです。
WeasyPrint公式ドキュメントでも、Django-WeasyPrintがカスタムURL fetcherを使い、静的ファイルやメディアファイルをファイルシステムから扱う仕組みを持つと紹介されています。
12.1 Djangoとの連携概要
Djangoでの基本的な流れは、ビューでデータを取得し、テンプレートをHTML文字列としてレンダリングし、WeasyPrintでPDFを生成し、HttpResponseとして返す形です。
| 手順 | 内容 |
|---|---|
| 1 | DBから帳票データを取得 |
| 2 | Djangoテンプレートをレンダリング |
| 3 | HTML文字列をWeasyPrintへ渡す |
| 4 | PDFバイト列を生成 |
| 5 | HTTPレスポンスとして返す |
12.2 テンプレート活用
Djangoテンプレート例です。
<!doctype html><html><head> <meta charset="utf-8"> <link rel="stylesheet" href="invoice.css"></head><body> <h1>請求書</h1> <p>請求先:{{ customer.name }}</p> <table> {% for item in items %} <tr> <td>{{ item.name }}</td> <td>{{ item.quantity }}</td> <td>{{ item.amount }}円</td> </tr> {% endfor %} </table></body></html>
12.3 PDFレスポンス生成
Djangoビューの例です。
from django.http import HttpResponsefrom django.template.loader import render_to_stringfrom weasyprint import HTMLdef invoice_pdf(request, invoice_id): invoice = get_invoice(invoice_id) html = render_to_string("invoice.html", {"invoice": invoice}) pdf = HTML( string=html, base_url=request.build_absolute_uri("/") ).write_pdf() response = HttpResponse(pdf, content_type="application/pdf") response["Content-Disposition"] = f'attachment; filename="invoice-{invoice_id}.pdf"' return response
base_urlを指定することで、CSSや画像などの相対パスを解決しやすくなります。
12.4 帳票出力システム構築
Djangoで帳票システムを作る場合は、PDF生成処理をビューに直接書きすぎず、サービス層やユーティリティ関数に分離すると保守しやすくなります。
from weasyprint import HTMLdef generate_pdf_from_template(html: str, base_url: str) -> bytes: return HTML(string=html, base_url=base_url).write_pdf()
複数帳票で共通化する場合、テンプレート名、出力ファイル名、CSS、データ取得処理を整理します。
12.5 運用時の注意点
PDF生成はCPUとメモリを使う処理です。大量生成や長い帳票では、リクエスト中に同期処理するとタイムアウトする可能性があります。Celeryなどの非同期ワーカーへ分離し、生成後にダウンロードURLを通知する構成も検討します。
13. Flask・FastAPIでの活用方法
FlaskやFastAPIでもWeasyPrintは利用できます。FlaskではJinja2テンプレートとの相性が良く、FastAPIではAPIレスポンスとしてPDFを返す設計がしやすいです。
Flaskについては、Flask-WeasyPrintという連携ライブラリがあり、公式ドキュメントではflask_weasyprint.HTML()やCSS()を使うことで、Flaskのリクエストコンテキスト内でURL解決を効率化できると説明されています。
13.1 Flaskとの連携
FlaskでPDFを生成する基本例です。
from flask import Flask, render_template, make_responsefrom weasyprint import HTMLapp = Flask(__name__)@app.route("/invoice/<int:invoice_id>.pdf")def invoice_pdf(invoice_id): invoice = {"id": invoice_id, "customer": "株式会社サンプル", "total": 100000} html = render_template("invoice.html", invoice=invoice) pdf = HTML(string=html, base_url=".").write_pdf() response = make_response(pdf) response.headers["Content-Type"] = "application/pdf" response.headers["Content-Disposition"] = f'attachment; filename="invoice-{invoice_id}.pdf"' return response
13.2 FastAPIとの連携
FastAPIでは、Responseを使ってPDFバイト列を返します。
from fastapi import FastAPIfrom fastapi.responses import Responsefrom jinja2 import Environment, FileSystemLoaderfrom weasyprint import HTMLapp = FastAPI()env = Environment(loader=FileSystemLoader("templates"))@app.get("/reports/{report_id}.pdf")def report_pdf(report_id: int): template = env.get_template("report.html") html = template.render(report_id=report_id, title="月次レポート") pdf = HTML(string=html, base_url=".").write_pdf() return Response( content=pdf, media_type="application/pdf", headers={ "Content-Disposition": f'attachment; filename="report-{report_id}.pdf"' } )
13.3 API経由でのPDF生成
API経由でPDFを生成する場合、リクエストごとに即時生成するのか、非同期ジョブで生成するのかを決めます。小さなPDFなら即時生成でも問題ありませんが、大量ページや画像が多いPDFでは非同期化が安全です。
| 方式 | 向いているケース |
|---|---|
| 即時生成 | 1〜数ページの軽いPDF |
| 非同期生成 | 大量ページ、画像多め、帳票一括出力 |
| 事前生成 | 頻繁にダウンロードされる固定PDF |
| キャッシュ | 同じPDFを何度も配布する場合 |
13.4 ダウンロード機能の実装
ブラウザで開かせたい場合はinline、ダウンロードさせたい場合はattachmentを指定します。
headers = { "Content-Disposition": 'inline; filename="preview.pdf"'}
headers = { "Content-Disposition": 'attachment; filename="download.pdf"'}
13.5 実践的な利用例
FlaskやFastAPIでは、APIから請求書、領収書、レポート、受講証明書、チケット、管理画面のエクスポートPDFを生成できます。Jinja2でテンプレートを管理すれば、データ差し込みとデザイン調整を分離できます。
14. 請求書・帳票システムへの活用
WeasyPrintは、請求書や帳票システムに非常に向いています。HTMLテンプレートに顧客名、請求番号、明細、金額、税額、振込先などを差し込み、CSSでA4帳票として整形できます。
帳票システムでは、見た目の美しさだけでなく、データの正確性、再発行、バージョン管理、PDF保存、監査ログなども重要になります。
14.1 請求書PDFの作成
請求書PDFでは、基本情報、請求先、発行元、明細、合計、備考を分かりやすく配置します。
<h1>請求書</h1><section class="meta"> <p>請求書番号:INV-2026-001</p> <p>発行日:2026年6月10日</p></section><section class="customer"> <p>株式会社サンプル 御中</p></section><table class="items"> <thead> <tr> <th>品目</th> <th>数量</th> <th>単価</th> <th>金額</th> </tr> </thead> <tbody> <tr> <td>システム開発費</td> <td>1</td> <td>100,000円</td> <td>100,000円</td> </tr> </tbody></table>
14.2 見積書の出力
見積書では、請求書と似たレイアウトを使えますが、有効期限、納期、支払条件、備考欄を明確に表示する必要があります。テンプレートを共通化し、帳票種別ごとにタイトルと項目を切り替える設計が便利です。
14.3 レポート生成
月次レポートや分析レポートでは、表、グラフ、要約、コメントを組み合わせます。グラフは画像として生成し、HTMLに埋め込むと扱いやすいです。
14.4 契約書作成
契約書では、ページ番号、章番号、署名欄、改ページ制御が重要です。条項の途中でページが分断されると読みにくくなるため、break-inside: avoidなどを活用します。
14.5 業務システムでの活用
業務システムでは、PDF生成を次のような流れで設計します。
| 機能 | 内容 |
|---|---|
| テンプレート管理 | 帳票ごとのHTML/CSSを管理 |
| データ差し込み | DBデータをテンプレートへ渡す |
| PDF生成 | WeasyPrintで出力 |
| 保存 | ストレージやS3へ保存 |
| 再発行 | 生成済みPDFまたは同一データから再生成 |
| 監査 | 誰がいつ生成したかを記録 |
15. WeasyPrintとwkhtmltopdfの比較
WeasyPrintとwkhtmltopdfは、どちらもHTMLからPDFを生成するツールとして知られています。ただし、仕組みが異なります。WeasyPrintはPython製のHTML/CSSレンダリングエンジンで、印刷向けCSSに強い設計です。wkhtmltopdfは、WebKit系のレンダリングを利用するHTML to PDFツールとして広く使われてきました。
どちらを選ぶべきかは、既存HTMLの再現性を重視するか、印刷向けCSSとPython連携を重視するかで変わります。
15.1 仕組みの違い
| 項目 | WeasyPrint | wkhtmltopdf |
|---|---|---|
| 基本思想 | HTML/CSSを印刷向けにPDF化 | Webページをブラウザ風にPDF化 |
| 実装 | Python中心 | WebKit系レンダリング |
| Python連携 | 非常にしやすい | 外部コマンド実行が多い |
| 印刷CSS | 強い | 構成次第 |
| JavaScript | 基本的に期待しない | 一定の実行に対応する構成もある |
15.2 CSS対応状況
WeasyPrintはCSS Paged Mediaや@pageに強く、ページ番号、ヘッダー・フッター、ページ余白などのPDF帳票向け機能を扱いやすいです。公式ドキュメントでも、CSS Paged Mediaの機能としてページ余白ボックスやページカウンターが利用できると説明されています。
一方、画面表示用の複雑なJavaScript UIをそのままPDFにしたい場合は、ブラウザベースのPDF生成ツールを検討することもあります。
15.3 パフォーマンス比較
パフォーマンスはHTMLの複雑さ、画像量、ページ数、フォント、実行環境によって変わります。WeasyPrintは長い文書や複雑なHTMLでは時間がかかることがあり、公式ドキュメントでも長い文書や特殊に作られたHTMLは長時間レンダリングや高CPU・高メモリ使用につながる可能性があると説明されています。
大量生成では、どちらのツールでも非同期化、キャッシュ、テンプレート簡素化、画像最適化が必要です。
15.4 導入のしやすさ
PythonアプリケーションではWeasyPrintの方が自然に組み込みやすいです。pip install weasyprintで導入し、Python APIから直接PDFを生成できます。ただし、OS依存ライブラリの準備は必要です。
wkhtmltopdfは外部バイナリの導入や実行パス管理が必要になることが多く、コンテナ環境ではバイナリ込みのイメージ管理が必要です。
15.5 どちらを選ぶべきか
| 要件 | おすすめ |
|---|---|
| Pythonアプリに自然に組み込みたい | WeasyPrint |
| 請求書・帳票・レポート中心 | WeasyPrint |
| CSS Paged Mediaを使いたい | WeasyPrint |
| 既存Webページの画面再現を重視 | wkhtmltopdfまたはブラウザ系 |
| JavaScript描画が必須 | Playwright/Puppeteer系も検討 |
16. WeasyPrintとReportLabの比較
WeasyPrintとReportLabは、どちらもPythonでPDFを作るための選択肢ですが、設計思想が大きく異なります。WeasyPrintはHTML/CSSからPDFを作るツールで、ReportLabはPythonコードでPDFを直接生成するライブラリです。
HTMLテンプレートで帳票を作りたい場合はWeasyPrint、PDF内部を細かく制御したい場合はReportLabが向いています。
16.1 設計思想の違い
| 項目 | WeasyPrint | ReportLab |
|---|---|
| 作り方 | HTML/CSSから生成 | Pythonで直接描画 |
| レイアウト | CSS中心 | 座標・オブジェクト中心 |
| デザイナー連携 | しやすい | 難しめ |
| 細かいPDF制御 | やや制限あり | 強い |
| 帳票テンプレート | 作りやすい | コード量が増えやすい |
16.2 開発効率の比較
HTML/CSSに慣れているチームでは、WeasyPrintの方が開発効率が高くなりやすいです。テンプレートを編集すれば見た目を調整できるため、デザイン変更に対応しやすいです。
ReportLabは、座標指定やPDFオブジェクトを直接扱うため、細かい制御はできますが、複雑な帳票ではコード量が増えやすくなります。
16.3 レイアウト自由度の比較
WeasyPrintはCSSでレイアウトしますが、ブラウザと完全同一ではありません。対応していないCSS機能もあります。公式ドキュメントでは、たとえばbox-shadowは未サポートと説明されています。
ReportLabはPDFを直接組み立てるため、座標や描画を細かく制御できます。特殊な帳票やバーコード、複雑なPDF加工が必要な場合はReportLabが向くことがあります。
16.4 メンテナンス性の比較
帳票の文言や見た目が頻繁に変わる場合、WeasyPrintはHTML/CSSで管理できるため保守しやすいです。ReportLabでは、デザイン変更がPythonコード変更になりやすく、非エンジニアが扱いにくい場合があります。
16.5 適した利用ケース
| ケース | 適した選択 |
|---|---|
| 請求書・見積書 | WeasyPrint |
| HTMLテンプレート活用 | WeasyPrint |
| Webアプリ連携 | WeasyPrint |
| PDFを座標単位で制御 | ReportLab |
| 特殊なPDF生成 | ReportLab |
| 帳票デザインをCSSで管理 | WeasyPrint |
17. パフォーマンス最適化
WeasyPrintは便利ですが、PDF生成は軽い処理ではありません。HTML解析、CSS適用、レイアウト計算、画像処理、フォント処理、PDF書き出しが発生するため、ページ数や画像量が増えるとCPUとメモリを消費します。
公式ドキュメントでも、長い文書や特殊なHTMLは長時間レンダリングや高メモリ使用につながる可能性があると説明されています。 本番環境では、PDF生成を通常のWebリクエスト処理から分離する設計も重要です。
17.1 PDF生成速度の改善
PDF生成速度を改善するには、HTML構造をシンプルにし、不要なCSSを減らし、画像を最適化します。画面用CSSをそのまま使うのではなく、PDF専用CSSを用意することで、レンダリング負荷を下げやすくなります。
| 改善策 | 効果 |
|---|---|
| PDF専用CSSを作る | 不要なスタイルを減らす |
| 画像を圧縮する | 読み込みとPDFサイズを削減 |
| 複雑なレイアウトを避ける | レンダリングを安定化 |
| フォント数を減らす | フォント処理を軽減 |
| 非同期生成 | Webレスポンスの遅延を防ぐ |
17.2 メモリ使用量の削減
大容量PDFでは、画像、表、ページ数がメモリ使用量に影響します。大量データを1つのPDFに詰め込むより、分割出力やCSV併用を検討します。
また、同じロゴやCSSを何度も読み込む場合は、生成処理を共通化し、不要なリソース参照を減らします。
17.3 大容量PDF対策
大量ページのPDFを生成する場合は、同期レスポンスではなく、ジョブキューで処理する方が安全です。生成完了後にメール通知やダウンロードリンクを提供する設計にすると、タイムアウトを避けやすくなります。
# 疑似コードdef enqueue_pdf_job(user_id, report_id): job_id = create_job(user_id, report_id) pdf_worker.delay(job_id) return job_id
17.4 キャッシュ活用
同じPDFを何度も生成する必要がない場合は、生成済みPDFを保存して再利用します。請求書や契約書のように内容が確定した文書は、生成時点のPDFを保存しておくと再発行が簡単です。
| 文書タイプ | キャッシュ方針 |
|---|---|
| 請求書 | 確定後PDF保存 |
| レポート | 条件が同じなら再利用 |
| 契約書 | 署名前・署名後で保存 |
| プレビュー | 短期キャッシュ |
| 大量帳票 | 非同期生成 + 保存 |
17.5 サーバー負荷軽減
PDF生成はCPU負荷が高くなることがあるため、WebサーバーとPDF生成ワーカーを分ける構成が有効です。Dockerやジョブキューを使い、メモリ制限やタイムアウトを設定すると安全です。
信頼できないHTMLやCSSを扱う場合は、公式ドキュメントが推奨するように、実行ユーザー制限、メモリ制限、レンダリング時間制限、サンドボックス化、カスタムURL fetcherなどを検討します。
18. よくあるエラーと対処法
WeasyPrintでよくある問題は、フォント、画像、CSS、依存関係、パス解決です。特に日本語PDFではフォント問題が多く、Webアプリケーション連携では画像やCSSの相対パス問題が起こりやすいです。
エラー解決の基本は、まずweasyprint --infoで環境を確認し、最小HTMLでPDF生成できるか試し、次にCSSや画像を段階的に追加して原因を切り分けることです。
18.1 フォントエラー
日本語が四角になる、文字が表示されない、太字が効かない場合は、フォント設定を確認します。公式ドキュメントでも、文字が描画されない場合や四角になる場合はフォントをインストールしてWeasyPrintから利用可能にする必要があると説明されています。
対策は、日本語フォントのインストール、CSSのfont-family確認、@font-face利用、FontConfiguration利用です。
18.2 画像表示エラー
画像が表示されない場合は、相対パスが解決できていない可能性があります。base_urlを指定するか、絶対パスや適切なURLを使います。
HTML(string=html, base_url="/app").write_pdf("out.pdf")
Webアプリケーション内の静的ファイルを扱う場合は、Flask-WeasyPrintやDjango-WeasyPrintのような連携ライブラリも検討できます。公式ドキュメントでは、これらがカスタムURL fetcherを使って静的ファイルやメディアファイルを扱うと説明されています。
18.3 CSS反映エラー
CSSが反映されない場合は、CSSファイルのパス、base_url、CSS構文、未対応プロパティを確認します。WeasyPrintは多くのCSSに対応していますが、ブラウザと完全同一ではありません。公式ドキュメントでは、たとえばbox-shadowが未対応であることや、Flexboxは単純なケースでは動くが深くテストされていないことが説明されています。
18.4 PDF生成失敗
PDF生成に失敗する場合は、HTML構文、CSS構文、外部リソース、フォント、画像、依存ライブラリを順番に確認します。まずは次のような最小コードで動作確認します。
from weasyprint import HTMLHTML(string="<h1>Test</h1>").write_pdf("test.pdf")
これが成功するなら、問題はテンプレート、CSS、画像、フォントのどれかにある可能性が高いです。
18.5 依存関係エラー
依存関係エラーでは、PangoやDLL、dylibが見つからないケースが多いです。公式ドキュメントでは、WindowsやmacOSでcannot load libraryのようなエラーが出る場合、WeasyPrintが必要なライブラリを見つけられていないことを意味すると説明されています。
| 問題 | 対処 |
|---|---|
| DLLエラー | WindowsでMSYS2パスを確認 |
| dylibエラー | macOSでライブラリパス確認 |
| Pango不足 | OSパッケージ追加 |
| フォント不足 | 日本語フォント追加 |
| 画像不足 | base_url確認 |
19. WeasyPrint導入時のベストプラクティス
WeasyPrintを実務導入する場合は、単にPDFを生成できればよいわけではありません。テンプレート設計、フォント管理、セキュリティ、非同期処理、保存方針、再発行、ログ、監視まで含めて設計する必要があります。
特に業務システムでは、PDFは証跡や顧客提出物になることがあります。そのため、再現性、バージョン管理、生成日時、生成者、元データとの紐づけが重要です。
19.1 テンプレート設計
テンプレートは、共通部分と個別部分を分けます。ヘッダー、フッター、フォント、ページ設定は共通CSSへまとめ、請求書、見積書、レポートなどの内容部分は個別テンプレートにします。
templates/ pdf/ base.html invoice.html estimate.html report.htmlstatic/ pdf/ base.css invoice.css
19.2 フォント管理
フォントはOS依存にしすぎず、必要に応じてプロジェクト内で管理します。ただし、フォントファイルのライセンスには注意が必要です。日本語帳票では、開発環境と本番環境で同じフォントを使うことで、レイアウト差を減らせます。
19.3 セキュリティ対策
信頼できないHTMLやCSSをWeasyPrintに渡す場合は危険があります。公式ドキュメントでは、信頼できないHTML/CSSにより、高メモリ使用、長時間レンダリング、ローカルファイル漏洩などのセキュリティ問題が発生し得ると説明されています。
| 対策 | 内容 |
|---|---|
| rootで実行しない | 権限を最小化 |
| コンテナ化 | ファイル・ネットワークアクセスを制限 |
| タイムアウト | 長時間レンダリングを防ぐ |
| メモリ制限 | 異常なPDF生成を防ぐ |
| URL fetcher制限 | 外部URLやfile URLを制御 |
| 入力サニタイズ | HTML/CSSを検証 |
19.4 保守性向上
PDFテンプレートは、アプリケーションコードと分離して管理します。CSSの命名規則、ページ設定、フォント、共通部品を整理しておくと、帳票が増えても保守しやすくなります。
また、生成結果のPDFをスクリーンショットやハッシュで回帰テストする仕組みを作ると、CSS変更による帳票崩れを早期に発見できます。
19.5 本番環境運用
本番では、PDF生成のログ、処理時間、失敗率、メモリ使用量を監視します。生成に時間がかかる帳票は非同期化し、PDF生成ワーカーの数を制御します。
import loggingimport timefrom weasyprint import HTMLlogger = logging.getLogger(__name__)def generate_pdf(html: str, output_path: str): start = time.time() HTML(string=html, base_url=".").write_pdf(output_path) elapsed = time.time() - start logger.info("PDF generated: %s seconds", elapsed)
20. WeasyPrintを選ぶべきケース
WeasyPrintは、HTML/CSSテンプレートを使ってサーバーサイドPDFを生成したい場合に向いています。特にPythonアプリケーション、Django、Flask、FastAPI、Jinja2、業務帳票、請求書、レポート出力と相性が良いです。
一方で、JavaScriptで描画されたWebページをそのままPDF化したい場合や、PDF内部を座標単位で完全制御したい場合は、別のツールが向いていることがあります。
20.1 業務システムの場合
業務システムでは、請求書、見積書、発注書、納品書、報告書などの帳票出力が必要になります。WeasyPrintはHTMLテンプレートを使って帳票を作れるため、業務システムに向いています。
20.2 Webアプリケーションの場合
Webアプリケーションでは、管理画面からPDFをダウンロードする機能がよくあります。WeasyPrintを使えば、画面とは別にPDF専用テンプレートを作成し、ユーザーデータを差し込んで出力できます。
20.3 帳票システムの場合
帳票システムでは、ページ番号、ヘッダー、フッター、明細表、改ページ制御が重要です。WeasyPrintはCSS Paged Mediaに対応しているため、帳票レイアウトをCSSで管理できます。
20.4 SaaSサービスの場合
SaaSでは、顧客ごとに請求書、利用レポート、契約書、ダッシュボードPDFを出力することがあります。WeasyPrintはテンプレートを使い回しやすいため、顧客別データを差し込むPDF生成に向いています。
20.5 他ライブラリとの選定基準
| 要件 | 選択肢 |
|---|---|
| HTML/CSSで帳票を作りたい | WeasyPrint |
| Pythonアプリに自然に組み込みたい | WeasyPrint |
| JavaScript画面をそのままPDF化したい | Playwright / Puppeteer系 |
| PDFを座標単位で描画したい | ReportLab |
| 既存wkhtmltopdf資産がある | wkhtmltopdf継続も検討 |
| 大量・高負荷PDF生成 | 非同期化 + キャッシュ設計が必要 |
おわりに
WeasyPrintは、PythonアプリケーションからHTMLとCSSを使って高品質なPDFを生成できる便利なツールです。HTMLテンプレートとCSSで帳票を設計できるため、Django、Flask、FastAPI、Jinja2を使ったWebアプリケーションと相性が良く、請求書、見積書、レポート、契約書、チケット、管理画面のPDFエクスポートなどに活用できます。
特に、@page、ページ番号、ヘッダー、フッター、改ページ制御、日本語フォント設定を適切に使うことで、実務に耐える帳票PDFを作成できます。一方で、WeasyPrintはフルブラウザエンジンではないため、JavaScriptで描画される画面をそのままPDF化する用途には向きません。印刷向けに設計したHTML/CSSをPDF化するツールとして使うのが基本です。
本番環境で利用する場合は、フォント管理、画像パス、CSS対応状況、依存ライブラリ、セキュリティ、非同期処理、キャッシュ、ログ監視まで含めて設計する必要があります。信頼できないHTMLやCSSを扱う場合は、公式ドキュメントが示すように、メモリ制限、時間制限、実行権限制限、サンドボックス化、URL fetcher制御を行うことが重要です。
Pythonで帳票PDFを自動生成したい場合、WeasyPrintは非常に有力な選択肢です。HTML/CSSでデザインを管理したい、DjangoやFlaskと自然に連携したい、請求書やレポートをPDF化したいという要件があるなら、まず検討すべきPDF生成ライブラリの一つです。
FAQ
WeasyPrintはオープンソースで、BSD 3-Clause Licenseのもとで公開されています。公式ドキュメントでもBSDライセンスのフリーソフトウェアであると説明され、GitHub上のLICENSEにもBSD 3-Clause Licenseと記載されています。
日本語PDFは作成できます。ただし、日本語フォントがサーバーにインストールされていて、CSSで適切に指定されている必要があります。文字が四角になる場合は、フォント不足やフォント参照ミスを確認します。公式ドキュメントでも、文字が描画されない場合や四角になる場合はフォントを利用可能にする必要があると説明されています。
WeasyPrintはPython製のHTML/CSSレンダリングエンジンで、印刷向けCSSやPython連携に強いツールです。wkhtmltopdfはブラウザエンジン系のHTML to PDFツールとして使われてきました。帳票や請求書のようにHTML/CSSテンプレートから安定したPDFを作りたい場合はWeasyPrintが向いています。
利用できます。DjangoテンプレートをHTML文字列としてレンダリングし、そのHTMLをWeasyPrintに渡してPDF化できます。公式ドキュメントでも、Django-WeasyPrintがWeasyPrintとの連携にカスタムURL fetcherを利用する例として紹介されています。
追加できます。CSSの@pageとcounter(page)、counter(pages)を使うことで、ページ番号や総ページ数を表示できます。WeasyPrintはCSS Paged Mediaのページカウンターやページ余白ボックスに対応しています。
@page { @bottom-center { content: counter(page) " / " counter(pages); }}
EN
JP
KR