reportlab、pdfkit和weasyprint生成pdf时中文显示方框,根本原因是字形缺失而非编码错误;需分别为reportlab显式注册truetype字体并指定字体名,为pdfkit在html中内联base64字体或配置wkhtmltopdf系统字体,为weasyprint确保css font-family精确匹配fontconfig列出的中文字体族名。

reportlab 默认不加载中文字体,pdfkit 依赖的 wkhtmltopdf 渲染引擎缺少中文 fallback 字体栈,weasyprint 的 pango 后端若未配置 CJK 支持也会跳过映射——这三类库在生成 PDF 时,只要没显式注册或内联支持 Unicode BMP 中文区(U+4E00–U+9FFF)的字体,就会把汉字渲染成方框 □ 或空白。
根本不是“编码错了”,而是字形缺失:PDF 是矢量文档,它不存字符语义,只存字形索引 + 字体描述。没字体,就等于没画笔。
reportlab 注册中文字体必须手动做两件事
reportlab 不会自动扫描系统字体,必须:
- 提前用
pdfmetrics.registerFont加载一个本地 TrueType 文件(如simsun.ttc或NotoSansCJKsc-Regular.otf) - 在
ParagraphStyle或canvas.setFont()中显式指定该字体名(不是文件名)
常见错误:
- 把字体文件路径写错,或权限不足导致加载失败(
IOError: cannot open resource) - 注册时用了文件名(
"simsun.ttc"),但设置样式时却用显示名("SimSun"),两者不匹配 - 忘记调用
pdfmetrics.registerFontFamily配置normal/bold/italic映射,导致加粗失效变回默认字体
示例关键片段:
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
<p>pdfmetrics.registerFont(TTFont('SimSun', '/path/to/simsun.ttc'))
pdfmetrics.registerFontFamily('SimSun', normal='SimSun', bold='SimSun')</p>
pdfkit 的乱码本质是 wkhtmltopdf 缺字体
pdfkit 是个封装,真正干活的是系统级二进制 wkhtmltopdf。它用 Qt WebEngine 渲染 HTML,但:
- Ubuntu/Debian 默认不装中文字体包(
fonts-wqy-zenhei或fonts-noto-cjk) - CentOS/RHEL 需手动
fc-cache -fv刷新字体缓存,否则fc-list | grep -i sim查不到 - 即使系统有字体,
wkhtmltopdf也不读/etc/fonts/fonts.conf,得靠--enable-local-file-access+ CSS 内联@font-face才能加载本地 .ttf
最稳做法:
- HTML 中用
@font-face引入 base64 编码的字体数据(避免路径和跨域问题) - 添加
<meta charset="UTF-8">,且 Python 生成 HTML 字符串时确保是str类型(非bytes) - 启动
pdfkit.from_string()时传configuration=pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf'),确认用的是新版(>=0.12.6)
weasyprint 的 font-family 必须精确匹配系统字体名
weasyprint 依赖 pango 解析 CSS,但它查字体靠 fontconfig,不是文件名。执行 fc-list :lang=zh family 才能看到实际可用的字体族名,例如:
-
Noto Sans CJK SC(不是NotoSansCJKsc) -
WenQuanYi Zen Hei(不是WenQuanYiZenHei)
容易踩的坑:
- CSS 里写
font-family: "Microsoft YaHei",但系统没装该字体,fallback 到serif就乱码 - 没加
lang="zh"属性,pango不触发 CJK shaping 逻辑 - 使用
font-feature-settings: "liga"等高级特性,但 Noto CJK 不支持 ligature,反而崩渲染
修复只需一行 CSS:
body { font-family: "Noto Sans CJK SC", sans-serif; }
前提是 fc-list 能列出它。
字体注册不是“做了就行”,而是每种 PDF 生成路径都有独立的字体上下文。同一份中文字体文件,在 reportlab 里注册后,对 pdfkit 完全无效;weasyprint 的 CSS 字体声明也不会影响 fitz 的文本提取。别指望一次配置全局生效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











