使い倒し研究所
業務自動化

Pythonで日本語PDFを作る|フォントファイルを用意しなくていい方法

PR本記事にはプロモーション(アフィリエイト広告)が含まれます。

配布用の特典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を作るときの要点です。

配布物をスクリプトで作れるようにしておくと、内容の修正が一瞬で反映されます。 デザインツールで作ったものは、直すたびに人間の作業が発生します。

関連:Google Apps Scriptで実際にハマった5つの落とし穴Codex CLIで画像を27枚まとめて生成した全手順

この記事を書いた人
カズト(ETERNAL d.c.t)

ETERNAL d.c.t 代表。Web制作・生成AI導入支援・ライバー事務所運営を手がける。Claude Code / Codex CLI / n8n / Google Apps Script を実務で毎日使い、自社サイト・学習アプリ・業務自動化を内製している。このサイトでは、実際に自分が金を払って使ったツールだけを扱う。