如何使用reportlab的TTFont字体回退功能
目录
参考
- reportlab TTFont字体回退实现解析 — 配套实现原理分析(来自 reportlab_enhanced)
- reportlab-enhanced 文档 - TrueType 字体回退
- reportlab-enhanced 文档
- selcarpa/reportlab_enhanced
介绍
reportlab_enhanced 是 reportlab 的 fork 分支,在上游基础上进行字体功能增强。TTFont 字体回退即为其中一项核心改进。
在原生 reportlab 中,TTFont 一直缺少字体回退机制。这导致处理多语言混合文本(如拉丁字母 + 中文、日文等)时,如果主字体缺少某些字符的字形,显示效果会非常糟糕——轻则方块,重则缺失。reportlab_enhanced 通过设置环境变量 REPORTLAB_FONT_FALLBACK=1,为 TTFont 添加了与 Type1 字体同等能力的 fallback 支持。
本文介绍如何使用这一功能。实现原理请参考 reportlab TTFont字体回退实现解析。
启用功能
必须设置环境变量才能启用,默认关闭:
REPORTLAB_FONT_FALLBACK=1 python your_script.py或在脚本开头设置:
import os
os.environ['REPORTLAB_FONT_FALLBACK'] = '1'基础用法:逐行配置
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 注册主字体(缺少某些字形)
latin_font = TTFont('NotoSans', 'NotoSans-Regular.ttf')
# 注册 fallback 字体(包含主字体缺失的字形)
cjk_font = TTFont('NotoSansCJK', 'NotoSansCJK-Regular.ttf')
pdfmetrics.registerFont(latin_font)
pdfmetrics.registerFont(cjk_font)
# 设置 fallback
latin_font.substitutionFonts = [cjk_font]
# 使用
canvas.setFont('NotoSans', 12)
canvas.drawString(100, 700, 'Hello 你好 World') # 中文字符自动使用 NotoSansCJK便利函数:一行搞定
registerFontWithFallback 可以一步完成注册和 fallback 配置:
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 传入 fallback 字体
font = pdfmetrics.registerFontWithFallback(
'NotoSans',
'NotoSans-Regular.ttf',
fallbackFonts=[
TTFont('NotoSansCJK', 'NotoSansCJK-Regular.ttf')
]
)
canvas.setFont('NotoSans', 12)
canvas.drawString(100, 700, 'Hello 你好 World')fallbackFonts 参数支持字体名称字符串或 TTFont 实例:
# 方式1:传入字体名称字符串
font = pdfmetrics.registerFontWithFallback(
'NotoSans', 'NotoSans-Regular.ttf',
fallbackFonts=['NotoSansCJK'] # 需要预先通过 getFont() 注册
)
# 方式2:传入 TTFont 实例
font = pdfmetrics.registerFontWithFallback(
'NotoSans', 'NotoSans-Regular.ttf',
fallbackFonts=[TTFont('NotoSansCJK', 'NotoSansCJK-Regular.ttf')]
)检查字体字形:hasGlyph()
如果需要手动检查某个字符是否在字体中:
font = TTFont('NotoSans', 'NotoSans-Regular.ttf')
font.hasGlyph('A') # True,拉丁字母通常有
font.hasGlyph('你') # False,主字体没有中文字形
font.hasGlyph(0x4F60) # False,Unicode 码点方式
font.hasGlyph(ord('你')) # 同上多级 Fallback
可以配置多个 fallback 字体,按顺序查找:
latin_font = TTFont('NotoSans', 'NotoSans-Regular.ttf')
cjk_font = TTFont('NotoSansCJK', 'NotoSansCJK-Regular.ttf')
emoji_font = TTFont('NotoEmoji', 'NotoEmoji.ttf')
latin_font.substitutionFonts = [cjk_font, emoji_font]
canvas.setFont('NotoSans', 12)
canvas.drawString(100, 700, 'Hello 你好 😄 World') # 中文字符 → NotoSansCJK,表情符号 → NotoEmoji段落文本中的使用
同样支持 <font> 标签:
from reportlab.platypus import Paragraph, SimpleDocTemplate
doc = SimpleDocTemplate('output.pdf')
story = [
Paragraph(
'<font name="NotoSans">Hello 你好 World 世界</font>',
style=ParagraphStyle(fontName='NotoSans', fontSize=14)
)
]
doc.build(story)注意:需要在样式或标签中显式指定字体名称为已配置 fallback 的字体。
注意事项
- 功能来自 reportlab_enhanced — 这是 reportlab_enhanced 对上游 reportlab 的字体增强,默认关闭,需要设置
REPORTLAB_FONT_FALLBACK=1环境变量才生效 - 性能开销 — 每次文本渲染时,若主字体缺失某字符,会依次查询 fallback 链的字形表,文本量较大时有一定性能影响
完整示例
import os
os.environ['REPORTLAB_FONT_FALLBACK'] = '1'
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 注册字体
font = pdfmetrics.registerFontWithFallback(
'NotoSans', 'NotoSans-Regular.ttf',
fallbackFonts=[
TTFont('NotoSansCJK', 'NotoSansCJK-Regular.ttf')
]
)
# 创建 PDF
c = canvas.Canvas('mixed_lang.pdf')
c.setFont('NotoSans', 16)
# 绘制混合文本
c.drawString(100, 800, 'ReportLab: Hello 你好 World 世界')
c.save()生成的 PDF 中,拉丁字符使用 NotoSans,中文字符自动切换到 NotoSansCJK,无需手动分段处理。
本文由 AI 辅助编制