- 开发工具
【免费下载链接】fonttools
A library to manipulate font files from Python.
在 fontTools 中,prep表是 TrueType 字体内用于存放“控制值程序”(Control Value Program, CVP)的 OpenType 表,其内容是 TrueType 字节码,可在全字体范围修改控制值表(CVT)并可能在任何字形处理之前被执行。本文以官方 API 文档 Doc/source/ttLib/tables/_p_r_e_p.rst 为核心,结合 Lib/fontTools/ttLib/tables/_p_r_e_p.py 及其父类、ttProgram汇编器/反汇编器、ttx命令行与 subset 工具的源码,完整讲解table__p_r_e_p类如何解编译字节码、如何在 Python API 中读写汇编、以及 TTX XML 的<assembly>/<bytecode>输出格式。
1. prep 表是什么:文档定义的语义
官方文档页 Doc/source/ttLib/tables/_p_r_e_p.rst 本身很短,只声明了两点事实:
prep表是一个 OpenType 表;- 通过
automodule指令展开fontTools.ttLib.tables._p_r_e_p模块的成员文档,即由类 docstring 生成 API 参考。
类 docstring(Lib/fontTools/ttLib/tables/_p_r_e_p.py)给出了 prep 表的准确定义:
The
preptable contains TrueType instructions that can make font-wide alterations to the Control Value Table. It may potentially be executed before any glyph is processed.
即:prep 表保存 TrueType 指令,能够对整个字体的 Control Value Table(cvt表)做修改;它可以在处理任何字形之前被执行。这与fpgm(Font Program)表形成分工——fpgm的 docstring 说明它“typically contains function definitions that are used by font instructions”,且“only executed one time, when the font is first used”(见 Lib/fontTools/ttLib/tables/_f_p_g_m.py)。简言之:fpgm定义并初始化函数,prep执行字形的公共准备逻辑,两者都是 TrueType 字节码程序。
文档目录中该页位于 Doc/source/ttLib/tables.rst 索引下(tables/_p_r_e_p条目),与所有其他表模块的 API 文档并列。
2. 实现结构:一个“空子类”如何复用 fpgm 的全部能力
Lib/fontTools/ttLib/tables/_p_r_e_p.py 全文只有 16 行:
from fontTools import ttLib superclass = ttLib.getTableClass("fpgm") class table__p_r_e_p(superclass): """Control Value Program table The ``prep`` table contains TrueType instructions that can makee font-wide alterations to the Control Value Table. It may potentially be executed before any glyph is processed. """ pass几个值得注意的实现细节:
- 类名与模块名由表标签派生:表标签
prep是四个合法字符,但 Python 标识符不能以数字或保留形式冲突,fontTools 的表模块用“每字符前后加下划线”的命名规则得到模块_p_r_e_p.py、类table__p_r_e_p。模块注册在由 MetaTools/buildTableList.py 自动生成的 Lib/fontTools/ttLib/tables/init.py(第 90 行from . import _p_r_e_p,文件头注明 “DON'T EDIT! This file is generated”)。 ttLib.getTableClass("fpgm")动态取父类:getTableClass定义在 Lib/fontTools/ttLib/ttFont.py,它先查自定义表注册表,再通过getTableModule+tagToIdentifier找到_f_p_g_m模块的table__f_p_g_m类。也就是说 prep 类没有重复任何代码,decompile/compile/toXML/fromXML/__bool__全部继承自table__f_p_g_m。- 类型注解层面:
ttFont.py中为常见表标签提供了Literal重载,例如def __getitem__(self, tag: Literal["prep"]) -> _p_r_e_p.table__p_r_e_p与对应的get("prep")重载,因此静态类型检查能直接知道font["prep"]的返回类型。 - 测试覆盖登记:MetaTools/check_table_coverage.py 中
"_p_r_e_p": {"test_file"},说明 prep 表通过通用 TTX 数据文件(Tests/ttx/data/下的TestTTF.ttx等)间接获得往返测试覆盖。
3. 继承的核心 API:decompile / compile / toXML / fromXML
由于 prep 完全复用 Lib/fontTools/ttLib/tables/_f_p_g_m.py 中table__f_p_g_m的实现,四个核心方法的行为如下(这是文档页:members: :undoc-members:展开出来的完整成员集合):
decompile(字节码 → 对象):
def decompile(self, data, ttFont): program = ttProgram.Program() program.fromBytecode(data) self.program = programcompile(对象 → 字节码,写回字体文件时调用):
def compile(self, ttFont): if hasattr(self, "program"): return self.program.getBytecode() return b""toXML / fromXML(TTX 序列化往返):
def toXML(self, writer, ttFont): self.program.toXML(writer, ttFont) def fromXML(self, name, attrs, content, ttFont): program = ttProgram.Program() program.fromXML(name, attrs, content, ttFont) self.program = program__bool__(空表判定,源码自带 doctest):program不存在或字节码/汇编为空时为False,只有当program存在且非空时才是True。这个语义被 ttx、subset 等流程用来判断一张表是否“有内容”。
关键点:decompile产出的self.program是ttProgram.Program实例,prep 表的一切能力(反汇编、汇编、XML 输出)都由这个对象承担。
4. 字节码与汇编的双向转换:ttProgram.Program
prep 表操作的实体是Program类,定义在 Lib/fontTools/ttLib/tables/ttProgram.py。其文档字符串即“Assembler/disassembler for TrueType bytecode programs”,对外暴露四个入口:
| 方法 | 作用 |
|---|---|
fromBytecode(bytecode) | 保存原始字节码,清除缓存的汇编 |
fromAssembly(assembly) | 接收list[str]或按行分割的字符串,清除缓存的字节码 |
getBytecode() | 若只有汇编则先_assemble(),返回bytes |
getAssembly(preserve=True) | 若只有字节码则先_disassemble(),返回每行一条指令的list[str] |
指令集由同文件中的streamInstructions(NPUSHB/NPUSHW/PUSHB/PUSHW)与普通指令表instructions(约 100 条,SVTCA、SVTCA[0]、SCVTCI、RCVT、WCVTF、FDEF/ENDF等)描述,每条给出操作码、助记符、参数位宽、栈弹出/压入数量。装配逻辑(_assemble,ttProgram.py#L326 起)基于正则逐 token 解析:
- 普通指令写作
MNEMONIC[参数位],如SVTCA[0](参数位宽 1 位,取值0表示 Y 轴); - 参数位数量必须与指令声明一致,否则抛出
tt_instructions_error("Incorrect number of argument bits ..."); - 遇到规范之外的操作码,反汇编器输出
INSTRxx形式(xx为操作码十六进制),装配时再原样写回——这保证了非标准/私有指令也不会丢失字节码(ttProgram.py#L345-L348); - 语法错误统一抛
tt_instructions_error,例如Syntax error in TT program。
一个最小汇编示例(来自 Tests/ttLib/tables/ttProgram_test.py):
ASSEMBLY = [ "PUSH[ ]", "0 4 3", "INSTCTRL[ ]", "POP[ ]", ]注意汇编是“每行一个 token 组”的列表,PUSH[ ]之后的行是它的操作数,/* 注释 */会被解析器跳过。
5. 在 Python 中读写 prep 程序(实战示例)
基于上述 API,对真实 TTF 读取、修改并回写 prep 表的典型流程如下(数据文件可用仓库测试数据 Tests/ttLib/data/TestTTF-Regular.ttx 对应的 TTF):
from fontTools.ttLib import TTFont font = TTFont("TestTTF-Regular.ttf") prep = font["prep"] # 触发 decompile:字节码 -> ttProgram.Program program = prep.program # 1) 查看反汇编结果 for line in program.getAssembly(): print(line) # 例如: SVTCA[0] /* SetFPVectorToAxis */ # 2) 查看原始字节码 print(program.getBytecode().hex()) # 3) 用汇编重建程序(修改 CVT 缩放示例:写入 SCVTCI 截断值等) program.fromAssembly([ "SVTCA[0]", # SetFPVectorToAxis: 将功能向量设为 Y 轴 "PUSH[ ]", "32", # 32 = 1/2 em(CVT 单位下的截断) "SCVTCI[ ]", # SetCVTCutIn ]) # 4) 保存:compile() 会被自动调用,程序重新汇编成字节码写入 'prep' 表 font.save("out.ttf")要点说明:
font["prep"]是惰性加载:首次访问才调用decompile,self.program才存在;fromAssembly会删除缓存的字节码,getBytecode()时才重新汇编,因此“改汇编再保存”是安全往返;- 若
prep表在字体中不存在,font.get("prep")返回None;新建空表可直接font["prep"] = ttLib.newTable("prep")(newTable见 ttFont.py#L1512-L1515); - 空表行为:
bool(table__p_r_e_p())为False,装入空Program仍为False,仅当字节码/汇编非空才为True(继承自 fpgm 的 doctest,见 Lib/fontTools/ttLib/tables/_f_p_g_m.py#L35-L53)。
6. ttx 命令行与 TTX XML 格式
ttx工具是 prep 表最直观的检视入口,选项处理在 Lib/fontTools/ttx.py:
# 只导出 prep 表为 TTX ttx -t prep font.ttf # 不反汇编指令:输出原始字节码十六进制而非汇编 ttx -t prep -i font.ttfdisassembleInstructions默认为True(ttx.py#L139),-i选项将其置为False(ttx.py#L196-L197);- 该标志传入
TTFont(disassembleInstructions=...)(ttx.py#L309),最终被Program.toXML读取(ttProgram.py#L249-L315):反汇编成功则写<assembly>块;若反汇编抛异常,则记录错误并退而写<bytecode>十六进制块;不反汇编时直接写<bytecode>。
真实输出示例,来自仓库测试数据 Tests/ttx/data/TestTTF.ttx:
<fpgm> <assembly> SVTCA[0] /* SetFPVectorToAxis */ </assembly> </fpgm> <prep> <assembly> SVTCA[0] /* SetFPVectorToAxis */ </assembly> </prep> <cvt> <cv index="0" value="0"/> </cvt>fromXML一侧对称地支持两种写法:<assembly>内容用strjoin合并后走fromAssembly并立即汇编校验(ttProgram.py#L317-L324),<bytecode>内容则按十六进制读回。因此手工编辑 TTX 中的汇编、再ttx out.ttf回编是修改 prep 程序的标准工作流,Snippets/subset-fpgm.py 就展示了getAssembly()→ 解析FDEF/ENDF→fromAssembly()→getBytecode()校验可编译性的完整技巧,同样适用于 prep。
7. 与 fpgm/cvt 的协作关系及 subset 工具行为
- 与 fpgm 的关系:prep 程序可以
CALL由 fpgm 中的FDEF定义的全局函数;fpgm 只执行一次做初始化,prep 则在处理字形前执行,两者共同构成 TrueType 的“全字体级”提示环境,与逐字形的glyf指令相区分。 - 与 cvt 表的关系:prep 的“font-wide alterations to the Control Value Table”主要通过
RCVT/WCVTF/WCVTP/SCVTCI等指令(见 ttProgram.py 指令表)作用于cvt表,从而间接影响后续所有字形指令对网格的处理。 - subset 工具:Lib/fontTools/subset/init.py 中,
prep同时出现在两个默认列表:_no_subset_tables_default(与cvt、fpgm一起)——默认原样保留,不做子集化,因为它本来就是全字体级表;_hinting_tables_default = ["cvt", "cvar", "fpgm", "prep", "hdmx", "VDMX"]——当用户传入--no-hinting时,prep会被整体丢弃(可用--hinting-tables-=保留个别表,帮助文本见 subset/init.py#L317-L326)。
也就是说,在 fontTools 生态里 prep 的定位是“随字体整体迁移、可按需随 hinting 一起剥离”的表。
8. 测试与验证入口
围绕 prep 表可参考的仓库测试:
- Tests/ttLib/tables/ttProgram_test.py:直接测试
Program的__bool__、fromBytecode/fromAssembly、装配/反汇编与toXML/fromXML往返,其中用TestFont(disassembleInstructions = True)模拟字体对象; - Tests/ttx/data/TestTTF.ttx 与 Tests/ttLib/data/TestTTF-Regular.ttx:包含
<prep>段的完整 TTX 往返样本,是 prep 表 decompile→toXML→fromXML→compile 闭环的回归依据; - Tests/ttx/ttx_test.py:验证
ttx默认反汇编、-i关闭反汇编的选项行为(disassembleInstructions断言); - MetaTools/check_table_coverage.py:CI 层面登记
_p_r_e_p的测试覆盖来源。
9. 小结
table__p_r_e_p是 fontTools 对prep(Control Value Program)表的包装:一个继承自table__f_p_g_m的空子类,语义由 docstring 定义——存放可在全字体范围修改 CVT、可能先于任何字形执行的 TrueType 指令。- 其全部能力来自
ttProgram.Program:fromBytecode/getBytecode/fromAssembly/getAssembly四向转换,INSTRxx兜底保证未知指令不丢码,参数位错误与语法错误抛tt_instructions_error。 - 实用路径有三条:Python API 中
font["prep"].program直接改装配;ttx -t prep(加-i控制反汇编)检视/编辑 TTX 中的<assembly>;subset 工具默认保留prep,--no-hinting时作为 hinting 表之一移除。 - 适用前提:以上均针对 TrueType 轮廓字体(
cvt/fpgm/prep属 TTF 字节码体系);文档页本身只是 API 参考入口,深入细节以 Lib/fontTools/ttLib/tables/_p_r_e_p.py、Lib/fontTools/ttLib/tables/_f_p_g_m.py 与 Lib/fontTools/ttLib/tables/ttProgram.py 的源码为准。
- 开发工具
【免费下载链接】fonttools
A library to manipulate font files from Python.
相关推荐
fontTools 中 cvt(Control Value Table)表全解析:TrueType 控制值表的读写、TTX 序列化与关联表实战
fontTools 中 cvt(Control Value Table)表全解析:TrueType 控制值表的读写、TTX 序列化与关联表实战 cvt (Con
开发工具FontTools 中的 Graphite Feat 表:特性数据的解析、序列化与 TTX 往返
FontTools 中的 Graphite Feat 表:特性数据的解析、序列化与 TTX 往返 本篇文章聚焦 fontTools 的 ttLib 子系统中针对
开发工具FontTools 中的 VDMX 表:垂直设备度量表的二进制格式解析与 TTX 往返
FontTools 中的 VDMX 表:垂直设备度量表的二进制格式解析与 TTX 往返 VDMX(Vertical Device Metrics) 是 Open
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考