メインコンテンツに移動

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
得意分野帳票、請求書、レポート、印刷向け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変数などの対応が説明されています。

技術対応の考え方
HTMLPDF化する文書構造として利用
CSSレイアウト・余白・フォント・色・表組みに利用
CSS Paged Mediaページサイズ、余白、ヘッダー、フッター、ページ番号に有効
画像レポート・請求書・ロゴ・グラフに利用
SVG図版やアイコンに利用可能
Python APIWebアプリケーションから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、読み取り可能なファイルオブジェクト、文字列をHTMLCSSへ渡せると説明されています。

基本的なコードは次のようになります。

 

from weasyprint import HTML

HTML("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, CSS

html = 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
macOSHomebrew、pip、virtualenv
Windows公式実行ファイル、Python + MSYS2
Docker依存関係込みのイメージを利用
WSLLinuxと同様の方法で導入可能

3.2 必要な依存ライブラリ

WeasyPrintはPythonだけで完結する単純なライブラリではなく、文字描画やCSS処理、画像処理のために複数の依存ライブラリを利用します。公式ドキュメントでは、Pango、pydyf、CFFI、tinyhtml5、tinycss2、cssselect2、Pyphen、Pillow、fontToolsなどが依存関係として挙げられています。

依存要素主な役割
PythonWeasyPrint本体の実行
Pangoテキストレイアウト・フォント処理
Pillow画像処理
tinycss2CSS解析
tinyhtml5HTML解析
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 venv
source venv/bin/activate
pip install weasyprint
weasyprint --info

 

Pythonコードで使う場合は、次のように確認できます。

 

from weasyprint import HTML

HTML(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 update
sudo apt install -y python3-pip libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0

python3 -m venv venv
source venv/bin/activate
pip install weasyprint
weasyprint --info

 

日本語PDFを作る場合は、日本語フォントも入れておくと安全です。

 

sudo apt install -y fonts-noto-cjk

 

4.3 Windows環境でのセットアップ

Windowsでは、最新リリースの実行ファイルを使う方法が最も簡単と公式ドキュメントで説明されています。Pythonライブラリとして使う場合は、Pythonを入れたうえで、Pangoと依存関係をMSYS2で導入する手順が案内されています。

Pythonライブラリとして使う場合の流れは次のようになります。

 

python -m venv venv
venv\Scripts\activate.bat
python -m pip install weasyprint
python -m weasyprint --info

 

DLLが見つからない場合は、MSYS2のmingw64\binを環境変数へ追加する必要があることがあります。公式ドキュメントでは、WindowsやmacOSでライブラリが見つからない場合、DLLやdylibのパス設定が原因になることが多いと説明されています。

4.4 macOS環境でのセットアップ

macOSでは、Homebrewを使ってWeasyPrintを導入する方法が公式ドキュメントで紹介されています。

 

brew install weasyprint
weasyprint --info

 

pipで管理したい場合は、HomebrewでPangoなどの依存ライブラリを用意し、Python仮想環境にWeasyPrintを入れる方法もあります。

 

brew install pango
python3 -m venv venv
source venv/bin/activate
pip install weasyprint

 

4.5 インストール時によくあるエラー

WeasyPrintのインストールで多いのは、依存ライブラリ不足、フォント不足、DLLやdylibが見つからない問題です。公式ドキュメントでも、文字が表示されない、四角になる場合はフォントをインストールし、WeasyPrintから利用可能にする必要があると説明されています。

エラー原因対策
cannot load libraryPangoなどのネイティブライブラリ不足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 HTML

html = """
<!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 HTML

HTML(filename="templates/invoice.html").write_pdf("output/invoice.pdf")

 

画像やCSSを相対パスで参照する場合は、base_urlを指定すると安定します。

 

from weasyprint import HTML

HTML(
    filename="templates/invoice.html",
    base_url="."
).write_pdf("output/invoice.pdf")

 

5.3 文字列から出力する方法

DjangoやFlaskでは、テンプレートをレンダリングした結果のHTML文字列をWeasyPrintへ渡すことが多いです。

 

from weasyprint import HTML

rendered_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 Path
from weasyprint import HTML

output_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, CSS

html = 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, CSS
from weasyprint.text.fonts import FontConfiguration

font_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画面PDF
表示領域デバイスごとに変化用紙サイズで固定
スクロール前提になるページ分割される
レイアウトレスポンシブ重視改ページ重視
ナビゲーション必要通常不要
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 HTML

HTML(
    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 plt

labels = ["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;
}

 

theadtable-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 Path
from weasyprint import HTML

html = 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, CSS
from weasyprint.text.fonts import FontConfiguration

font_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として返す形です。

手順内容
1DBから帳票データを取得
2Djangoテンプレートをレンダリング
3HTML文字列をWeasyPrintへ渡す
4PDFバイト列を生成
5HTTPレスポンスとして返す

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 HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML

def 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 HTML

def 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_response
from weasyprint import HTML

app = 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 FastAPI
from fastapi.responses import Response
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML

app = 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 仕組みの違い

項目WeasyPrintwkhtmltopdf
基本思想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 HTML

HTML(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.html

static/
  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 logging
import time
from weasyprint import HTML

logger = 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

Q
WeasyPrintは無料で利用できますか?
A

WeasyPrintはオープンソースで、BSD 3-Clause Licenseのもとで公開されています。公式ドキュメントでもBSDライセンスのフリーソフトウェアであると説明され、GitHub上のLICENSEにもBSD 3-Clause Licenseと記載されています。

Q
WeasyPrintは日本語に対応していますか?
A

日本語PDFは作成できます。ただし、日本語フォントがサーバーにインストールされていて、CSSで適切に指定されている必要があります。文字が四角になる場合は、フォント不足やフォント参照ミスを確認します。公式ドキュメントでも、文字が描画されない場合や四角になる場合はフォントを利用可能にする必要があると説明されています。

Q
WeasyPrintとwkhtmltopdfの違いは何ですか?
A

WeasyPrintはPython製のHTML/CSSレンダリングエンジンで、印刷向けCSSやPython連携に強いツールです。wkhtmltopdfはブラウザエンジン系のHTML to PDFツールとして使われてきました。帳票や請求書のようにHTML/CSSテンプレートから安定したPDFを作りたい場合はWeasyPrintが向いています。

Q
DjangoでWeasyPrintを利用できますか?
A

利用できます。DjangoテンプレートをHTML文字列としてレンダリングし、そのHTMLをWeasyPrintに渡してPDF化できます。公式ドキュメントでも、Django-WeasyPrintがWeasyPrintとの連携にカスタムURL fetcherを利用する例として紹介されています。

 

Q
PDFにページ番号を追加できますか?
A

追加できます。CSSの@pagecounter(page)counter(pages)を使うことで、ページ番号や総ページ数を表示できます。WeasyPrintはCSS Paged Mediaのページカウンターやページ余白ボックスに対応しています。

 

@page {
  @bottom-center {
    content: counter(page) " / " counter(pages);
  }
}

LINE Chat