背景:wkhtmltopdfとの涙の別れ
それは午前2時のことでした。顧客の請求書発行システムで突然大量のエラーが発生しました。原因は?フロントエンドが新しくFlexboxを採用したものの、古いwkhtmltopdfエンジン(2014年頃のWebKitベース)ではモダンなレイアウトをレンダリングできなかったのです。CSSを「石器時代」のテーブルレイアウトに戻すか、新しいツールを探すかの選択を迫られ、私はWeasyPrintを選びました。
なぜWeasyPrintなのか?Ubuntu 22.04などで依存ライブラリの欠如によるエラーが頻発するpdfkitやwkhtmltopdfとは異なり、WeasyPrintはPythonとの親和性が非常に高いです。CSS3、Flexbox、さらにはGridまでスムーズにサポートしています。特に、実行にChromeのような重いヘッドレスブラウザを必要としないため、サーバーのRAMを300〜500MBほど節約できます。
ただし、これが「即席の」解決策だと思わないでください。本番環境で安定して動作させるには、システムライブラリとの連携を正しく理解する必要があります。
インストール:システムライブラリの壁を越える
最も一般的な間違いは、pip install weasyprintだけを実行することです。そうすると、すぐにOSError: cannot load library 'gobject-2.0'という非常に厄介なエラーに遭遇します。WeasyPrintが動作するには、CairoやPangoといったグラフィックレンダリングエンジンが必要です。
1. Linux (Ubuntu/Debian) へのインストール
サーバーに万全の準備を整えるには、以下のコマンドを実行するだけです:
sudo apt-get update
sudo apt-get install build-essential python3-dev python3-pip python3-setuptools python3-wheel python3-cffi libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
2. Windowsという名の悩み種
WindowsへのWeasyPrintのインストールは、DLLエラーによって悩まされることが多いです。現在最も簡単な方法は、GTK for Windows Runtimeをダウンロードすることです。インストール後、必ずbinディレクトリのパスを環境変数PATHに追加してください。これを行わないと、Pythonが必要な実行ファイルを見つけることができません。
実践:基本コードからプロフェッショナルなPDFへ
実際の実務では、HTMLにCSSを詰め込む(インラインスタイル)のはメンテナンス上の悪夢です。クライアントがブランドカラーを青から赤に変えたいと言い出した時、何百行ものコードを探し回ることになります。最初からこれらを分離しておきましょう。
基本的なレンダリング方法
これは、単純なHTML文字列を変換する最も速い方法です:
from weasyprint import HTML
html_content = """
<h1 style='color: #1a73e8; font-family: sans-serif;'>サービス請求書</h1>
<p>弊社製品をご利用いただき、誠にありがとうございます。</p>
"""
# わずか1行のコードで<a href="https://itfromzero.com/ja/python-vi-ja/pathlib%ef%bc%9apython%e3%81%ae%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%e5%87%a6%e7%90%86%e3%82%b3%e3%83%bc%e3%83%89%e3%82%92os-path%e3%81%8b%e3%82%89%e3%80%8c%e5%88%b7%e6%96%b0%e3%80%8d%e3%81%99%e3%82%8b.html">ファイル出力</a>
HTML(string=html_content).write_pdf("invoice.pdf")
ベトナム語フォント(多言語対応)と高度なCSSの処理
最も頭の痛い問題は、文字が四角(豆腐)になってしまうフォント化けです。WeasyPrintはフォント管理にPangoを使用しているため、font-faceを明確に定義し、そのフォントがシステムにインストールされていることを確認する必要があります。
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
font_config = FontConfiguration()
css = CSS(string="""
@font-face {
font-family: 'Roboto';
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2);
}
body { font-family: 'Roboto', Arial, sans-serif; }
.header { display: flex; justify-content: space-between; border-bottom: 2px solid #eee; }
""")
html = HTML(string='<div class="header"><h1>レポート</h1><span>番号: #123</span></div>')
html.write_pdf('report.pdf', stylesheets=[css], font_config=font_config)
本番環境の最適化:サーバーを停止させないために
PDFのレンダリングは大量のリソースを消費します。複雑なPDFファイルは、数秒間CPU使用率を80〜90%まで急上昇させることがあります。50人が同時にレポート出力をクリックすれば、サーバーは即座にダウンしてしまいます。
- ワーカー(Worker)の使用: リクエスト処理のスレッド内で直接PDFをレンダリングしてはいけません。タスクをCeleryやRedis Queueに投げましょう。ユーザーには「処理中」という通知を表示し、後でメールやS3のリンク経由でファイルをダウンロードさせます。
- スマートな画像管理: WeasyPrintに外部URLから画像を読み込ませる(タイムアウトの原因になります)のではなく、Base64形式やローカルパスを使用してください。これにより、レンダリング時間を5秒から1秒未満に短縮できます。
- 画像のリサイズ: A4サイズのPDFに4K画像を詰め込むのは無駄です。画像を必要なサイズにリサイズしましょう。私の経験では、画像の最適化だけでPDFファイルのサイズを10MBから200KBまで削減できました。
おわりに
WeasyPrintは単なるツールではなく、CSSテーブルの時代に戻りたくない、美しいPDF出力を求める人々にとっての救世主です。初期設定は少し面倒ですが、その安定性とモダンなCSSのサポート能力は、投資する価値が十分にあります。最高の体験を提供するために、ライブラリを揃え、フォントを正しく設定し、常にバックグラウンドタスクで実行することを忘れないでください!

