字体文件子集化(Font Subsetting)是指从一个完整的字体文件中提取出仅包含特定字符集的子集,以减小字体文件的大小。这在网页设计、移动应用开发和嵌入式系统中非常有用,可以显著减少加载时间和资源占用。
1. 字体子集化的基本原理
字体子集化的核心步骤包括:
-
确定需要的字符集:根据文本内容提取出需要使用的字符。
-
从原始字体文件中提取字形数据:仅保留与目标字符相关的字形数据。
-
生成新的字体文件:将提取的字形数据打包成一个新的字体文件。
2. 字体文件的基本结构
字体文件通常由以下几个部分组成:
2.1. 文件头(Header)
文件头包含字体的基本信息,如字体版本、表数量、校验和等。
-
主要字段:
-
sfntVersion
:字体格式标识(如\x00\x01\x00\x00
表示 TrueType)。 -
numTables
:字体中包含的表的数量。 -
searchRange
、entrySelector
、rangeShift
:用于快速查找表的元数据。
-
2.2. 表目录(Table Directory)
表目录列出了字体文件中所有表的名称、偏移量和长度。
-
每个表目录项包含:
-
tag
:表的名称(如head
、glyf
等)。 -
checksum
:表的校验和。 -
offset
:表在文件中的偏移量。 -
length
:表的长度。
-
2.3. 表数据(Tables)
字体文件的核心数据存储在多个表中。以下是一些重要的表:
3. 主要表(Tables)介绍
3.1. head
表(字体头表)
-
作用:存储字体的全局信息。
-
重要字段:
-
unitsPerEm
:每个 EM 单位的坐标数。 -
xMin
、yMin
、xMax
、yMax
:字体的全局边界框。 -
indexToLocFormat
:字形位置数据的格式(0 表示短格式,1 表示长格式)。
-
3.2. hhea
表(水平头表)
-
作用:存储水平排版的相关信息。
-
重要字段:
-
ascent
、descent
:字体的上升和下降值。 -
lineGap
:行间距。 -
numberOfHMetrics
:水平度量值的数量。
-
33. maxp
表(最大需求表)
-
作用:存储字体的最大值信息。
-
重要字段:
-
numGlyphs
:字体中包含的字形数量。
-
3.4. cmap
表(字符映射表)
-
作用:将字符代码映射到字形索引。
-
重要字段:
-
多个子表,支持不同的字符编码格式(如 Unicode、ASCII 等)。
-
3.5. glyf
表(字形数据表)
-
作用:存储字形的轮廓数据。
-
重要字段:
-
每个字形的轮廓数据(包括点坐标、标志等)。
-
复合字形(Composite Glyphs)的引用信息。
-
3.6. loca
表(字形位置表)
-
作用:存储每个字形在
glyf
表中的偏移量。 -
重要字段:
-
根据
head
表中的indexToLocFormat
字段,使用短格式或长格式存储偏移量。
-
3.7. hmtx
表(水平度量表)
-
作用:存储每个字形的水平度量值。
-
重要字段:
-
advanceWidth
:字形的水平步进宽度。 -
leftSideBearing
:字形的左侧间距。
-
3.8. name
表(命名表)
-
作用:存储字体的元数据,如字体名称、版本、作者等。
-
重要字段:
-
多个字符串记录,支持多种语言和平台。
-
4 子集化步骤
在 TTF 字体文件中,数据量较大的表主要包括 loca
、glyf
和 hmtx
。对这些表进行紧凑化处理,可以显著减小字体文件的大小。
例如,一个包含 1 万个字符的字体文件,其 loca
表需要存储 1 万个字符的字形轮廓地址。如果对该字体进行子集化,仅保留 10 个字符,那么 loca
表就只需存储这 10 个字符的轮廓地址,文件大小会按比例大幅缩减。
glyf
表用于存储每个字符的轮廓数据,通常占用较大空间。在子集化过程中,只需保留所需字符的轮廓数据即可。
从原理上讲,子集化字体文件并不复杂。然而,字体文件中的各个表之间存在紧密的关联。一旦处理不当,很容易导致字体文件失效。因此,自行编写一个字体子集化程序难度较大。
由于工作需要,我必须处理字体子集化问题,但未能找到合适的开源代码。于是,我查阅了大量资料,参考了多种源码,最终自己编写了一个字体子集化程序。该程序具有以下优势:
-
纯 C# 开发:完全使用 C# 编写,不依赖任何第三方库,支持跨平台运行。
-
性能优化:在子集化过程中,采用了多种优化策略,每秒可处理数千次字体子集化操作。