3、字体文件格式:TrueType(TTF)、OpenType(OTF)、Web字体(WOFF/WOFF2)、位图字体

字体文件格式,说白了就是字形的「容器」。不同的容器,装东西的方式、压缩率、支持的特性天差地别。我刚开始做渲染时,以为字体就是一堆点阵图,后来被 TTF 的二次贝塞尔曲线狠狠教育了一顿。

今天咱们就把主流的几种格式掰开揉碎,看看它们到底怎么存字形,各自适合什么场景。

3.1 TrueType(TTF)—— 桌面时代的基石

TrueType 是苹果和微软在 90 年代联合推出的。它的核心是用二次贝塞尔曲线描述字形轮廓。为什么用二次?因为计算简单,渲染快,当时的 CPU 扛得住。

TTF 文件结构分几大块:

  • 表目录(Table Directory):索引所有数据表的位置
  • glyf 表:存储每个字形的轮廓点坐标
  • cmap 表:字符编码到字形索引的映射
  • head 表:全局信息,比如单位 per em、字体方向
  • hmtx / vmtx 表:水平/垂直度量信息

我个人习惯用 ttx 工具把 TTF 转成 XML 来查看内部结构。你试试这个命令:

# 将 TTF 转成可读的 XML
ttx -o SourceHanSansSC.xml SourceHanSansSC-Regular.ttf

# 查看 cmap 表
ttx -t cmap -o cmap.xml SourceHanSansSC-Regular.ttf

打开 XML 你会看到一堆 <TTGlyph> 节点,里面是 on 和 off 曲线点。嗯,这里要注意:on 点表示曲线上的点,off 点是控制点。二次贝塞尔每两个 off 点之间隐含一个 on 点,这是 TTF 特有的隐式约定。

避坑指南:我曾经在解析 TTF 时,以为所有轮廓点都是显式存储的。结果发现某些字形渲染出来缺角,排查了两天才意识到是隐式 on 点没补上。解析时一定要处理「两个 off 点之间自动插入 on 点」的规则。

3.2 OpenType(OTF)—— 更强大的容器

OpenType 是 Adobe 和微软在 TTF 基础上扩展的格式。它支持两种轮廓描述方式:

  • CFF(Compact Font Format):用三次贝塞尔曲线,由 Adobe 主导
  • TrueType 轮廓:兼容 TTF 的二次贝塞尔

三次贝塞尔比二次多一个控制点,曲线更平滑,文件更小。你想想看,同样的字形,CFF 版本通常比 TTF 版本小 30%-50%。

OTF 最大的亮点是高级排版特性:

特性 说明 典型应用
连字(Ligature) 将多个字符替换为单个字形 fi → fi, fl → fl
上下文替换 根据前后字符自动替换字形 阿拉伯语首/中/尾形
花体变体 同一字符提供多种风格 书法字体中的 swash
旧式数字 数字的上伸/下伸风格 正文排版更自然

这些特性通过 GSUB 表和 GPOS 表实现。GSUB 管替换,GPOS 管位置微调。我建议你打开一个支持连字的 OTF 字体,用 hb-shape 工具看看替换过程:

# 查看 "fidelity" 的连字替换过程
hb-shape --font-file=MyFont.otf "fidelity"
# 输出类似:f_i.liga|d|e|l|i|t|y

看到 f_i.liga 了吗?那就是连字替换后的字形名。

关键区别:TTF 和 OTF 的文件扩展名有时会混淆。真正的 OTF(带 CFF 轮廓)内部是 PostScript 曲线,而「.otf」扩展名也可能被用于纯 TrueType 轮廓的字体。判断方法很简单:用 otfinfo 查看 --format,如果返回 CFF 就是真 OTF。

3.3 Web 字体(WOFF / WOFF2)—— 为网络而生

WOFF 本质上是 TTF/OTF 的压缩包。它加了一个 40 字节的文件头,包含元数据(版权、版本等),然后用 zlib 压缩。WOFF2 更进一步,用 Brotli 压缩,并针对字形轮廓做了自定义预处理。

我记得第一次对比 WOFF 和 WOFF2 的压缩率时,结果让我很惊讶:

格式 压缩算法 典型压缩率(相对 TTF) 解压速度
WOFF zlib (deflate) 60%-70% 快
WOFF2 Brotli + 字形预处理 40%-50% 中等

WOFF2 的预处理包括:

  • 轮廓点坐标变换:将绝对坐标转为差值,减少数值范围
  • 重复指令合并:相同 hinting 指令只存一次
  • 表级重排:按访问频率重新排序表,提升解压效率

实际项目中,我建议你:

  • 中文网站优先用 WOFF2,因为中文字体动辄几 MB,压缩收益巨大
  • WOFF 作为降级方案,兼容 IE11 等老浏览器
  • 不要直接引用 TTF/OTF 做 Web 字体,加载慢且浪费带宽
注意:WOFF2 的 Brotli 压缩在服务端需要额外配置。Nginx 默认不带 Brotli 模块,你得自己编译或安装第三方模块。我曾经因为忘了配,导致 WOFF2 文件被当成普通二进制流传输,白白浪费了压缩效果。

3.4 位图字体—— 像素级的精确控制

位图字体就是把每个字形存成固定大小的像素矩阵。没有轮廓,没有曲线,只有 0 和 1。常见格式有 BDF、PCF、FNT。

为什么现在还有人用位图字体?三个场景:

  • 嵌入式系统:LCD 屏幕、LED 点阵屏,不需要缩放
  • 终端/控制台:等宽位图字体渲染快,无锯齿
  • 极小字号:8px 以下,矢量字体的 hinting 很难做好,位图反而清晰

BDF(Bitmap Distribution Format)是纯文本格式,可读性最好。一个字符的定义长这样:

STARTCHAR A
ENCODING 65
SWIDTH 500 0
DWIDTH 8 0
BBX 8 10 0 -2
BITMAP
00
7C
82
82
82
FE
82
82
82
00
ENDCHAR

每行十六进制数代表一行的像素。比如 7C 展开成二进制是 01111100,正好 8 个像素。

PCF(Portable Compiled Format)是 BDF 的二进制编译版,加载更快。FNT 是 Windows 的老格式,现在基本只在内核驱动里见到。

我的经验:如果你在做嵌入式 UI,建议用 BDF 开发调试,发布时转成 PCF。我曾经在 STM32 上直接加载 BDF,解析速度慢到界面卡顿。换成 PCF 后,加载时间从 200ms 降到 15ms。

3.5 格式对比与选型建议

为了让你一目了然,我把四种格式的核心差异整理成一张表:

维度 TTF OTF (CFF) WOFF2 位图
轮廓类型 二次贝塞尔 三次贝塞尔 同源字体 无
文件大小 大 中 小 极小(单尺寸)
缩放质量 依赖 hinting 较好 同源字体 差(需多尺寸)
高级排版 有限 丰富 同源字体 无
适用场景 桌面/文档 专业排版 Web 加载 嵌入式/终端

选型时我的建议很简单:

  • 做 Web 项目,无脑选 WOFF2,降级到 WOFF
  • 做桌面软件,用 OTF(CFF),文件小且曲线平滑
  • 做嵌入式,用位图字体,但要准备 3-5 个常用尺寸
  • 做字体设计,从 TTF 开始,工具链最成熟

3.6 格式内部结构图

下面这张 SVG 图展示了四种字体格式的内部结构对比,帮你建立整体认知:

字体文件格式内部结构对比 TrueType (TTF) Table Directory glyf(二次贝塞尔点) cmap(编码映射) head / hmtx / vmtx OpenType (OTF) CFF(三次贝塞尔) GSUB(替换规则) GPOS(位置微调) cmap / head / hmtx WOFF2 Brotli 压缩头 坐标差值变换 重复指令合并 表级重排 位图字体 BDF(文本格式) PCF(二进制编译) FNT(Windows 旧) 像素矩阵 扩展 压缩 TTF/OTF 是矢量轮廓,WOFF2 是压缩传输格式,位图字体是像素矩阵 选型关键:Web 用 WOFF2,桌面用 OTF,嵌入式用位图 桌面/文档 → TTF 专业排版 → OTF Web 加载 → WOFF2 嵌入式 → 位图 核心原则:根据目标平台和渲染需求,选择最合适的容器格式 没有万能格式,只有最适合场景的格式

这张图把四种格式的核心组件和适用场景串起来了。你可以看到,TTF 和 OTF 是「母体」,WOFF2 是「压缩包」,位图字体是「独立分支」。理解了这个关系,后面学渲染管线时就不会迷路。