Pythonで日本語PDFを作る|フォントファイルを用意しなくていい方法
配布用の特典PDFをPythonで生成しています。日本語PDFで最初に詰まるのがフォントですが、外部ファイルを一切用意しない方法がありました。実際に使っているコードで解説します。
結論:CIDフォントを使えばフォントファイルが要らない
Pythonで日本語のPDFを作ろうとすると、まずフォントで詰まります。
多くの解説では、TTFファイルを用意して登録する手順が書かれています。しかしその方法には問題があります。
- フォントファイルを一緒に配布・管理する必要がある
- ライセンスの確認が必要
- 環境が変わるとパスが通らない
CIDフォントを使えば、この全部が不要です。
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
# 外部ファイル不要
pdfmetrics.registerFont(UnicodeCIDFont("HeiseiKakuGo-W5")) # ゴシック
pdfmetrics.registerFont(UnicodeCIDFont("HeiseiMin-W3")) # 明朝
JP = "HeiseiKakuGo-W5"
この3行だけで日本語が出ます。 フォントファイルはどこにも置きません。
何を作っているか
配布用の特典PDFを生成しています。表紙・見出し・箇条書き・表を含む、10ページ程度の資料です。
手作業でデザインツールを使うと、内容を1文字直すたびに書き出しが必要になります。スクリプトにしておくと、テキストを直して再実行するだけです。
実装:スタイルを先に定義する
ReportLabでは、文字のスタイルをあらかじめ定義しておきます。
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER
styles = {
"title": ParagraphStyle("title", fontName=JP, fontSize=24, leading=32,
textColor=NAVY, alignment=TA_CENTER, spaceAfter=6),
"body": ParagraphStyle("body", fontName=JP_MIN, fontSize=11, leading=20,
textColor=NAVY),
}
fontName を毎回指定するのがポイントです。指定を忘れると英字用のフォントが使われ、日本語が文字化けします。
leading は行間です。日本語では文字サイズの1.6〜1.8倍にすると読みやすくなります。英語圏向けの既定値のままだと、日本語では詰まって見えます。
色を定数にまとめる
from reportlab.lib import colors
PINK = colors.HexColor("#E94E8A")
NAVY = colors.HexColor("#2B2D42")
GRAY = colors.HexColor("#6C757D")
色をコードのあちこちに直接書かないのは、あとから調整するためです。ブランドカラーを変えたくなったとき、1か所で済みます。
これは他の自動化でも同じで、あとから調整しそうなものは1か所に寄せるのが原則だと考えています。
中身を組み立てる
ReportLabは、部品をリストに並べて渡すと1つのPDFになります。
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, PageBreak, Table
)
story = []
story.append(Paragraph("タイトル", styles["title"]))
story.append(Spacer(1, 10))
story.append(Paragraph("本文テキスト", styles["body"]))
story.append(PageBreak())
doc = SimpleDocTemplate("output.pdf", pagesize=A4)
doc.build(story)
上から順に流し込まれ、ページに入りきらなければ自動で改ページされます。 レイアウトの座標を計算する必要はありません。
途中で切れてほしくない部分
見出しとその直後の本文が、ページの境目で分断されると読みにくくなります。
from reportlab.platypus import KeepTogether
story.append(KeepTogether([
Paragraph("見出し", styles["h1"]),
Paragraph("この段落は見出しと同じページに置く", styles["body"]),
]))
まとめて置きたいものを KeepTogether で囲むと、分断されなくなります。
つまずきやすい点
文字化けはほぼフォント指定の漏れ
日本語が「■■■」や空白になる場合、そのスタイルで fontName が指定されていないのがほぼ原因です。
表の中のテキストは特に忘れやすい場所です。テーブルのスタイルにも明示的に指定します。
TableStyle([
("FONTNAME", (0, 0), (-1, -1), JP), # ← 忘れやすい
("FONTSIZE", (0, 0), (-1, -1), 10),
])
行間の既定値は日本語に合わない
leading を指定しないと、英語基準の行間になります。日本語では明らかに詰まって見えます。
文字サイズを決めたら、必ず leading もセットで指定してください。
単位はポイント
ReportLabの数値はポイントです。ミリで考えたい場合は変換します。
from reportlab.lib.units import mm
doc = SimpleDocTemplate("out.pdf", pagesize=A4,
leftMargin=20*mm, rightMargin=20*mm)
A4の余白を数値で直接書くと、あとで見て意味が分からなくなります。 20*mm と書いておくほうが読めます。
明朝とゴシックを使い分ける
本文を明朝、見出しをゴシックにすると、印刷物らしい見た目になります。
両方を登録しておいて、スタイルごとに使い分けるのが手軽です。デザインツールを使わなくても、この程度の差で仕上がりが変わります。
よくある質問
PythonでPDFを作ると日本語が文字化けします。
スタイルにfontNameが指定されていない可能性が高いです。特に表の中のテキストは指定を忘れやすい場所です。CIDフォントを登録したうえで、すべてのスタイルとテーブルスタイルにフォント名を明示してください。
日本語フォントのファイルを用意する必要はありますか?
CIDフォントを使えば不要です。UnicodeCIDFontで登録すると、外部のフォントファイルなしで日本語のPDFが生成できます。ファイルの管理やライセンス確認の手間がなくなります。
見出しと本文がページの境目で分断されてしまいます。
KeepTogetherで囲むと、その中身が同じページに収まるように配置されます。見出しとその直後の段落をまとめて囲むと、分断を防げます。
まとめ
Pythonで日本語PDFを作るときの要点です。
- CIDフォントを使えばフォントファイルが要らない
- すべてのスタイルに
fontNameを明示する(表の中も忘れずに) leadingは文字サイズの1.6〜1.8倍にする- 色とサイズは定数にまとめ、あとから調整できるようにする
配布物をスクリプトで作れるようにしておくと、内容の修正が一瞬で反映されます。 デザインツールで作ったものは、直すたびに人間の作業が発生します。
関連:Google Apps Scriptで実際にハマった5つの落とし穴|Codex CLIで画像を27枚まとめて生成した全手順