- 开发工具
【免费下载链接】fonttools
A library to manipulate font files from Python.
本文以 fontTools 的MATH表(Mathematical Typesetting,数学排版表)官方文档页为切入点,完整梳理该 OpenType 表在 fontTools 中的加载、反编译与编译机制,逐层解读MathConstants、MathGlyphInfo、MathVariants三大子表的字段定义,并给出通过otlLib.builder.buildMathTable()以纯 Python 字典构建 MATH 表的完整参数说明与可复现示例,帮助字体开发/调试人员在 fontTools 生态中读写和验证数学字体(如 XITS Math 类字体)的 MATH 表数据。
MATH 表是什么
MATH是一张 OpenType 表,用于存放"将字形排印进数学公式与表达式"所需的各类度量与结构信息,例如上下标位置、分数分数线厚度、根号粗细、括号的可伸缩变体等。在 fontTools 文档体系中,它对应的文档页是 Doc/source/ttLib/tables/M_A_T_H_.rst,该页通过 Sphinx 的automodule指令直接渲染 table_M_A_T_H_ 类的文档字符串:
# Lib/fontTools/ttLib/tables/M_A_T_H_.py from .otBase import BaseTTXConverter class table_M_A_T_H_(BaseTTXConverter): """Mathematical Typesetting table The ``MATH`` table contains a variety of information needed to typeset glyphs in mathematical formulas and expressions. See also https://learn.microsoft.com/en-us/typography/opentype/spec/math """ pass从源码结构看,table_M_A_T_H_类本身没有任何方法体——它的全部行为都继承自BaseTTXConverter。这是 fontTools 处理 OpenType 表(GSUB、GPOS、MATH 等)的典型模式:表类只是"标签名 + 文档"的壳,真正的二进制读写由基类完成。
fontTools 如何加载和编译 MATH 表
MATH表被注册在字体表模块列表中,见 Lib/fontTools/ttLib/ttFont.py,并且TTFont为"MATH"标签提供了类型重载(Lib/fontTools/ttLib/ttFont.py):
def __getitem__(self, tag: Literal["MATH"]) -> M_A_T_H_.table_M_A_T_H_: ...这意味着font["MATH"]在类型检查器眼中会精确返回table_M_A_T_H_实例。
实际的二进制转换由 BaseTTXConverter 实现,其decompile/compile两个方法的调用链是:
- 反编译(首次访问
font["MATH"]时惰性触发):decompile(data, font)创建OTTableReader,然后按表标签从fontTools.ttLib.tables.otTables动态取到对应的表类(getattr(otTables, self.tableTag)),调用该类的decompile(reader, font)把二进制还原成 Python 对象树; - 编译(保存字体时自动触发):
compile(font)创建OTTableWriter,把对象树逐层写入,最终生成二进制。对 GSUB/GPOS 还包含 harfbuzz 重打包与偏移溢出重试状态机,而MATH走的是纯 fontTools 序列化路径(RepackerState.PURE_FT)。
otTables中所有的 MATH 相关类并非手写,而是由 otData.py 中的声明式字段规范在导入时动态生成。这正是理解 MATH 表结构的最佳入口:每个字段都带有类型和取自 OpenType 规范的描述文本。
MATH 表的三级结构与字段详解
顶层 MATH 记录
otData.py 定义了顶层四个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
Version | uint32 | 表版本,当前固定为0x00010000(1.0) |
MathConstants | Offset | 指向MathConstants子表 |
MathGlyphInfo | Offset | 指向MathGlyphInfo子表 |
MathVariants | Offset | 指向MathVariants子表 |
其中大量字段基于通用的MathValueRecord结构,即"一个设计单位数值 + 一个可选的 device table 偏移":
# otData.py: MathValueRecord ("int16", "Value", ...) # X 或 Y 方向的设计单位数值 ("Offset", "DeviceTable", ...) # 可 NULL,建议 device table format 1MathConstants:公式排版的"全局常量"
otData.py 中的MathConstants包含约 50 个字段,可归为几组语义:
- 缩放:
ScriptPercentScaleDown(上/下标级别 1 的缩放百分比,建议 80%)、ScriptScriptPercentScaleDown(级别 2,建议 60%); - 行距与轴:
MathLeading(数学公式间的行距留白)、AxisHeight(公式轴线高度); - 重音:
AccentBaseHeight(无需抬升重音的基字最大高度,建议为 x-height)、FlattenedAccentBaseHeight(无需压平重音的基字最大高度,建议为大写高度); - 上/下标:
SubscriptShiftDown、SubscriptTopMax、SuperscriptShiftUp、SuperscriptBottomMin、SubSuperscriptGapMin等约 10 个常量,规范中给出了具体建议值(如SubscriptTopMax建议 4/5 x-height); - 堆栈/分数:
StackTopShiftUp、FractionNumeratorShiftUp、FractionRuleThickness(分数线粗细)、SkewedFractionHorizontalGap等; - 根号:
RadicalRuleThickness、RadicalDegreeBottomRaisePercent(根指数底部抬升比例,建议 60%)、RadicalKernAfterDegree(负值,规范原文即如此)等。
除两个百分比字段(ScriptPercentScaleDown、ScriptScriptPercentScaleDown为 int16)和RadicalDegreeBottomRaisePercent(uint16)外,其余全部是MathValueRecord。
MathGlyphInfo:逐字形的数学信息
MathGlyphInfo 内含四个可空偏移:
MathItalicsCorrectionInfo:Coverage表 +ItalicsCorrectionCount+MathValueRecord数组,为每个覆盖字形提供斜体修正值(italics correction);MathTopAccentAttachment:TopAccentCoverage+ 逐字形重音附着点高度;ExtendedShapeCoverage:扩展形状(extended shape)字形覆盖表。规范说明:当 box 的左右两侧字形为扩展形状时,垂直定位应使用墨迹盒(ink box)而非MathConstants中的默认值;MathKernInfo:数学字距信息,见下文。
otData.py 中MathKernInfoRecord为每个字形的四个角(TopRight、TopLeft、BottomRight、BottomLeft)各留一个可空MathKern偏移;MathKern本身是分段函数式的两级数组:CorrectionHeight(按高度排序的修正高度点)与KernValue(对应字距值,负值表示将字形彼此拉近),首值覆盖不大于第一个高度的所有情形,末值覆盖大于最后一个高度的所有情形。这套结构让上下标字距能随基字高度连续插值。
MathVariants:可伸缩字形与组装
MathVariants 支持两种"生长"方式:
- 变体字形:
VertGlyphCoverage/HorizGlyphCoverage加计数,再为每个字形指向MathGlyphConstruction表;后者包含可选的GlyphAssembly偏移和一组MathGlyphVariantRecord(VariantGlyph+AdvanceMeasurement,即该方向上的全前进值); - 字形组装:
GlyphAssembly由ItalicsCorrection+PartCount+GlyphPartRecord数组构成,部件按"从左到右、从下到上"排列。GlyphPartRecord 字段为:glyph、StartConnectorLength、EndConnectorLength、FullAdvance,以及PartFlags——目前仅使用一个位:0x0001fExtender(表示该部件可被跳过或重复,0xFFFE为保留位)。
此外MathVariants.MinConnectorOverlap定义连接部件在组装时的最小重叠量(设计单位)。
TTX 文本形态
MATH 表可以像其他 OpenType 表一样导出为 TTX(XML)文本。仓库中带真实 MATH 数据的样本是 Tests/subset/data/TestMATH-Regular.ttx(一份 XITS Math 字体的完整 TTX 转储,<tableVersion value="0x00010000"/>),可直接对照上文结构验证字段命名。查看任意字体中 MATH 表的文本形态,可运行 ttx 工具:
ttx -t MATH font.otf导出后即可在编辑器中查看/核对MathConstants、MathGlyphInfo等节点。
用 buildMathTable() 编程式构建 MATH 表
fontTools 提供了纯 Python 的构建入口 buildMathTable(),签名如下:
builder.buildMathTable( ttFont, constants=None, # dict:常量名 -> 数值 italicsCorrections=None, # dict:字形名 -> 斜体修正值 topAccentAttachments=None,# dict:字形名 -> 重音附着点高度 extendedShapes=None, # set:扩展形状字形 mathKerns=None, # dict:字形名 -> 四角字距 minConnectorOverlap=0, # 连接部件最小重叠 vertGlyphVariants=None, # dict:字形名 -> [(变体字形, 前进高度), ...] horizGlyphVariants=None, # dict:字形名 -> [(变体字形, 前进宽度), ...] vertGlyphAssembly=None, # dict:字形名 -> ([部件], 斜体修正) horizGlyphAssembly=None, # dict:字形名 -> ([部件], 斜体修正) )各参数在源码 docstring 中有完整说明(Lib/fontTools/otlLib/builder.py),要点如下:
constants的键使用 MATH 规范中的常量名(首字母大写,如"ScriptPercentScaleDown"),值为数字;未给出的常量在_buildMathConstants()中按constants.get(conv.name, 0)填 0;mathKerns的值是嵌套字典,键为角名('TopRight'、'TopLeft'、'BottomRight'、'BottomLeft'),值为两个列表的元组:修正高度列表与字距值列表,且要求len(correctionHeights) == len(kernValues) - 1(源码中的断言保证分段函数合法性);vertGlyphAssembly/horizGlyphAssembly的部件是五元组:(glyph_name, flags, start_connector_length, end_connector_length, full_advance);- 所有"期望数值"的参数接受 int 或 float,float 会被
otRound舍入为整数。
实现逻辑简洁清晰:函数创建ot.MATH()对象,设Version = 0x00010000并populateDefaults(),然后依次调用_buildMathConstants、_buildMathGlyphInfo、_buildMathVariants三个辅助函数(Lib/fontTools/otlLib/builder.py)。三者都遵循"入参为空则返回 None、对应偏移置 NULL"的策略——例如只给了italicsCorrections时,MathConstants和MathVariants均为空,只有MathGlyphInfo被填充。
最小可运行示例
import fontTools.ttLib.ttLib as ttLib from fontTools import otlLib ttFont = ttLib.TTFont() ttFont.setGlyphOrder(["A", "B", "parenleft", "parenleft.size1"]) otlLib.builder.buildMathTable( ttFont, constants={ "ScriptPercentScaleDown": 70, "ScriptScriptPercentScaleDown": 50, "DelimitedSubFormulaMinHeight": 24, "DisplayOperatorMinHeight": 60, "AxisHeight": 262, "FractionRuleThickness": 66, }, italicsCorrections={"A": 100, "B": 300}, topAccentAttachments={"A": 300}, extendedShapes={"parenleft"}, minConnectorOverlap=20, vertGlyphVariants={ "parenleft": [("parenleft", 700), ("parenleft.size1", 1000)], }, ) mathTable = ttFont["MATH"].table assert mathTable.Version == 0x00010000 assert mathTable.MathConstants.AxisHeight.Value == 262 print(mathTable.MathGlyphInfo.MathItalicsCorrectionInfo.Coverage.glyphs)该示例的入参形态直接取自源码 docstring 中给出的官方示例(Lib/fontTools/otlLib/builder.py)。
测试用例:构建、合并与回环验证
fontTools 的测试套件为 MATH 表提供了三层可验证依据:
- 构建正确性:Tests/otlLib/builder_test.py 中,
test_buildMathTable_empty验证空参调用后MathConstants/MathGlyphInfo/MathVariants均为None而Version == 0x00010000;test_buildMathTable_constants传入 58 个常量的字典并逐一断言写入值;test_buildMathTable_italicsCorrection等逐项验证各字形字典与Coverage.glyphs的顺序一致性(coverage 顺序即数组顺序); - 字体合并:Tests/merge/merge_test.py 的
test_merge_MATH用FontBuilder+buildMathTable合成三个含 MATH 表的 TTF 再走fontTools.merge流程,断言合并后 coverage 并集(如italics.Coverage.glyphs == ["a", "b"])、MathKernInfo记录数与MathVariants中变体序列均正确,说明 MATH 表已接入 fontTools 的字体合并管线; - 子集化数据:Tests/subset/data/TestMATH-Regular.ttx 作为 subset 模块的数据集,保证含 MATH 表的真实字体在 TTX 回环(round trip)路径上被持续覆盖。
小结
在 fontTools 中处理 MATH 表可以归纳为三条主线:
- 读:
font["MATH"]触发BaseTTXConverter.decompile(),由 otData.py 声明式字段规范动态驱动反编译,得到可直接按属性访问的对象树; - 写:
font.save()或compile()时自动回写二进制;新增/重建则用otlLib.builder.buildMathTable(),以普通 Python 字典表达全部数学度量; - 验证:结合 TTX 导出(
ttx -t MATH font.otf)与 Tests/otlLib/builder_test.py 的断言模式,可以逐字段核对常量、coverage 顺序与变体记录是否符合 OpenType MATH 表规范。
- 开发工具
【免费下载链接】fonttools
A library to manipulate font files from Python.
相关推荐
fontTools otlLib 实战指南:用 Python 构建 GSUB/GPOS/GDEF/STAT/MATH 布局表
fontTools otlLib 实战指南:用 Python 构建 GSUB/GPOS/GDEF/STAT/MATH 布局表 本篇技术指南围绕 fontTool
开发工具fontTools 中 GDEF(Glyph Definition)表的完整解析:结构、读写、构建与实战指南
fontTools 中 GDEF(Glyph Definition)表的完整解析:结构、读写、构建与实战指南 导读 GDEF(Glyph Definition)
开发工具FontTools cvar 表全解析:CVT 变体数据结构的编译、解译与工程实践
FontTools cvar 表全解析:CVT 变体数据结构的编译、解译与工程实践 导读 本文聚焦 FontTools 中 OpenType cvar (Con
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考