(二)文字排版与渲染:数字字体的发展历程

42 阅读33分钟

今天在电脑上输入一个 A,我们通常可以自由改变字号,再把它显示在屏幕上或打印到纸面。早期数字设备处理这个 A 的方式却各不相同:有的屏幕只能在有限网格中点亮像素,绘图仪需要让笔尖沿着路线移动;再往前,实体排印直接把字形刻在一枚字模表面。它们最终都能呈现一个 A,保存和形成这个形状的方法却并不相同。

当文字从实体字模进入计算机,改变的也不只是记录形状的材料。过去由字模、字盒、排字者和输出设备共同完成的形状保存、寻找、排列和再现,也要被拆成机器能够读取的数据与外部处理过程。数字字体的发展,正是在不同设备条件和系统需求下,持续回答两个相连的问题:一枚字形应该怎样保存,一整套字体又怎样进入软件和输出系统。

这里的 A 首先仍是一个字符。上一篇《(一)文字排版与渲染:从编码模型角度理解字符编码》详细解释了这层文本身份怎样进入计算机:码点 U+0041 可以指向拉丁大写字母 A,却没有说明它应该画成什么形状,也没有规定这个形状默认占多宽。字符与最终画面之间还需要一份能够被系统反复读取的字体实现,为后续处理提供具体字形及其使用信息。

本文先从字符与字形的区别开始,说明一份字体资源需要提供什么;再回到实体排印,追踪这些职责怎样进入数字系统,并逐步形成今天的字体技术与共同规范。

一、字符与字体实现

1.1 字符与字形

刚才那个 A 可以拆成两个问题。第一个问题是“这段文字里写的是什么”,第二个问题是“这一次把它画成什么样”。切换字体时,第一个答案保持不变,第二个答案发生了变化。

文本系统用字符(character) 表示第一层身份。字符是被识别、存储和交换的抽象信息单位;字符 A 本身不会包含具体文字在显示时线条的粗细、曲线或衬线这种视觉信息元素。真正显示或印刷时采用的具体视觉形式,叫作字形(glyph)。Unicode 负责说明字符怎样被解释,却不规定它必须画成什么样;具体形状要由字体和后续文字处理共同确定。Unicode 17.0 对 character 与 glyph 的说明

换一种字体时,同一个字符 A 可以由另一种造型的字形呈现,这是最容易看见字符和字形并非同一回事的情况。但字符与字形怎样对应,并不只取决于选择了哪款字体:即使字体不变,它们也可能形成两种不同的对应关系:

  • 字符序列 f、i 在满足条件时可以共同使用一枚连字字形,此时是两个字符组合对应一个字形。
  • 某些带附加符号的字符也可能由主体和附加部分等多枚字形共同呈现,此时是一个字符由多个字形组合形成。

也就是说,一枚字形可以对应一个或多个字符,一个字符也可能需要一枚或多枚字形。Unicode 17.0 · Characters, Not Glyphs

字符告诉系统当前文字是什么,字形是这一次呈现要采用的视觉形式。字符编码把第一层信息交给计算机以后,后续还要回答:到哪里找相应的字形、它的形状怎样记录、它们会如何排布,排完以后怎样继续。接下来就要看所选字体为这些工作提供了什么。

1.2 字体实现与字体资源

继续看计算机显示这个 A 时需要做什么。程序先把输入识别为字符 A,但这一步只能确定文本身份。要显示出所选字体的样子,后续还要完成几项不同的工作:

  • 通过字符映射,根据 A 的码点 U+0041,找到当前字体里应该读取的字形。
  • 读取这枚字形的字形描述,取得构成可见形状的数据。
  • 读取相应的基础空间数据,知道排完这枚字形以后,下一个字形默认从哪里开始。
  • 遇到 f、i 这样的特定组合时,还可能根据字体提供的排版数据改用连字,或者调整字形的位置。

这些工作彼此关联,却分别需要不同的数据支持。

把完成这些工作所需的字符映射、字形描述、基础空间和排版数据组织在一起,形成系统能够单独识别、选择和读取的一套确切数据,就是我们所说的字体资源(font resource)。当我们在界面里选定一款字体时,系统最终必须找到这样一套可用数据,后续处理才有明确依据。

这也解释了为什么字形描述不能代表全部字体资源:它只回答某枚字形的形状怎样被记录;系统从哪里找到这枚字形、放好以后怎样继续,都要由其他数据回答。至于字体名称、具体样式、字体资源和磁盘文件之间怎样对应,下一篇再展开。本篇先顺着这套数据向前追溯,看看这些工作最初怎样依附于实体字模和人工排印,又怎样一步步转交给数字系统。

二、实体字模与数字字形

2.1 字体职责的载体变化

假设排字者要印出一个 A。排字者先从字盒中找到表面带有 A 字形的字模,把它放进当前文字序列,再由印刷设备把字模表面的形状转移到纸面。在这套过程中:

  • 可见形状留在字模表面;
  • 字身为排列提供物理占位;
  • 字盒的位置和排字者的经验帮助人寻找需要的字模;
  • 印刷设备则完成最后的再现。
金属字模的实物形态、单枚字模结构,以及从字盒查找、并排组合到压印纸面的过程转存失败,建议直接上传图片文件

到了数字系统中,A 不再对应一枚可以从字盒里取出、放进文字序列的实体字模。计算机面对的是表示这个字符的码点 U+0041,却仍要完成查找形状、安排位置和交给设备输出这些工作。它需要一个机器能够读取的映射,才能找到当前字体中的 A 字形;需要字形描述,才能取得具体形状;还需要基础空间数据,才能知道排完这枚字形以后从哪里继续。至于怎样把这些数据变成屏幕像素、打印点或机械运动,则要由字体之外的输出系统接手。

计算机并没有省略这些工作,只是换了完成方式:

需要完成的工作实体排印中的载体或执行者数字系统中的对应方式
找到所需字形字盒的位置和排字者的经验字符映射
保存可见形状字模表面字形数据
提供排列占位字身基础空间数据
形成最终输出印刷设备字体之外的排版、绘制与输出系统

字体资源保存查找字形、取得形状和安排基础空间所需的信息,排版、绘制和真实设备再接手后续处理。

字体资源不一定用同一种数据保存字形。早期屏幕只能点亮一个个像素,系统可以直接记录哪些像素要亮;绘图仪通过移动笔尖来画字,更需要记录笔尖要走的路线。如果同一套字形还要适应不同尺寸和设备,就不能只保存某个像素尺寸下的结果。

2.2 点阵字形

假设 A 只有十几个像素高,设计者会预先决定哪些网格单元应该亮起。显示时,系统只需把这组结果放到目标位置,不必先恢复一条连续曲线,再临时计算它会覆盖哪些单元。

这种表示就是点阵字形(bitmap glyph)。一枚字形由目标网格中的许多单元共同组成,每个单元可以记录是否着色、覆盖多少,或者使用什么颜色。系统读出的不是一条可以任意放大的边界,而是这枚字形在某个网格中的既定结果。

这种直接性也带来了尺寸依赖。设计者可以分别制作 12 px 和 16 px 的 A:12 px 版本也许需要省去细小转折,16 px 版本则有空间保留更多细节。把 12 px 的格子简单放大,不会自动得到设计过的 16 px 结果;直接缩小 16 px 版本,也可能让关键笔画挤在一起或消失。同一字体为某个目标尺寸准备的一整组点阵字形,通常称为一个尺寸版本(strike)。

点阵字形分别保存 12 px 与 16 px 尺寸下已经确定的网格

单枚点阵字形看起来像一张由网格单元组成的小图片,但这里的“小图片”只是直观说法。实际保存的,是字体资源内部属于当前字形的一段网格数据;它会和字形编号、适用尺寸、基础空间等信息一起被组织和读取,而不是作为一个独立的图片文件存在。

程序借助同一资源中的字符映射找到相应字形,并在多个尺寸版本中选择适合当前目标的那一组。点阵只说明单枚字形怎样保存,映射和排列仍由资源中的其他数据支持。

早期系统曾用不同的文件形式保存和加载点阵字体:

这些格式如今都属于遗留格式,已经不是现代通用排版的主流选择,但在旧系统和少数特定场景中仍可能出现。

今天仍有一些场景直接使用点阵字体,例如终端、嵌入式设备、复古界面,以及需要精确控制某个小尺寸结果的设计。这些场景有一个共同条件:目标网格已经确定,而且预先控制好每个网格单元的结果,比运行时重新计算更重要。

2.3 笔画字形

不过,并非所有设备都靠逐格点亮来形成文字。绘图仪或雕刻设备的成像部件会沿着路径移动;对这类设备来说,直接保存“工具要沿哪里走”更合适。

想象一台绘图仪正在画 A。笔尖先移动到第一条斜线的起点,落笔后沿路径上行;到达顶端以后改变方向,再画出另一条斜线;需要画横杠时,笔尖先抬起,移动到新的起点,再次落笔。设备真正需要知道的,是笔尖经过哪里,以及什么时候移动、落笔和抬起。

如果字体直接保存这组中心路径和动作顺序,就得到一枚笔画字形(stroke glyph)。一条路径可以形成一笔,多条路径可以共同构成整枚字形。这里保存的是成像部件要经过的连续路线,而不是某个像素尺寸下已经完成的结果,因此笔画字形也属于矢量表示。

同一条中心路线最终形成多粗的线、什么样的端点和边缘,还取决于输出时采用的工具或描边规则。换一支更粗的笔,A 的线条会变粗;刀具的形状不同,雕刻边缘也会变化;软件描边还可以改变端点和连接处。也就是说,中心路径只规定“沿哪里走”,最终填充区域的宽度和边缘并没有完整写进字形数据。

同一组笔画中心路径经过不同工具宽度、端点与连接规则后形成不同结果

AutoCAD 中的 SHP/SHX 保存的就是笔画字体。SHP 是可以用文本编辑器编写的 ASCII 描述文件,其中用向量长度、方向和特殊动作记录字形;它还可以被编译成 AutoCAD 实际加载的 SHX 字体文件。Autodesk 对 SHP/SHX shape description 的说明

2.4 轮廓字形

笔画字形与轮廓字形都可以用连续路径描述,也都能够缩放;两者的区别不在这里,而在于路径表示什么。笔画路径通常表示字形骨架或设备运动轨迹,轮廓路径则直接界定最后需要填充的区域。传统排版字形中的粗细变化、衬线和内孔,需要后一种边界才能被完整确定。

以大写 O 为例,如果只保存一条环形中心线,这条线只能说明工具沿哪里走,不能确定笔画有多粗,也不能准确给出外边缘和内孔的位置。要让字形数据完整保存这些形状,同时适应多种尺寸,字体需要记录外侧和内侧两条边界。

这种表示称为轮廓字形(outline glyph)。对于 O,外侧边界圈出字形的最外缘,内侧边界圈出不应填充的孔洞;两条边界共同确定最终可见的圆环。系统不需要从一条中心线猜测笔画宽度,因为内外边界已经写进字形数据。

轮廓字形用外轮廓与内轮廓确定 O 的填充区域,并由轮廓段组成闭合轮廓

这些边界不是作为一张图片保存的,而是由直线和 Bézier 曲线逐段连接而成。Bézier 曲线用端点和控制点描述弯曲形状,不同字体技术可以采用二次或三次曲线。各部分继续按下面的层次组成完整字形:

  1. 边界中的每一小段直线或曲线,称为轮廓段(segment)。
  2. 多个轮廓段首尾连接并回到起点,形成一条闭合轮廓(contour)。
  3. 一枚字形中的全部闭合轮廓合在一起,构成它的字形轮廓(glyph outline)。FreeType 对 segment、contour 与 glyph outline 的说明

这些点和曲线记录的是不依赖某个固定像素网格的边界。字号改变时,系统可以把它们的坐标按同一比例放大或缩小,得到当前大小的边界,再把边界围出的填充区域转换成屏幕像素或打印点。因此,同一组轮廓可以服务多个尺寸,不需要像点阵字形那样为每个目标尺寸另存一套完整网格。

与只保存中心路径的笔画字形相比,轮廓字形也已经把设计中的笔画宽度和边缘写进字体数据,不需要在输出时再由工具或描边规则补足。这两点使轮廓逐渐成为桌面和 Web 通用排版的主要路线。

不过,可缩放只说明同一组边界能够按比例变大或变小,不保证每个小字号都会自动清晰。连续曲线最终仍要落到有限网格,字体提供的提示信息、引擎执行的网格适配和具体栅格化方式,都会影响小尺寸结果。

现在可以把三种表示放回同一个比较坐标:

字形表示单枚字形保存什么生成可见结果时还依赖什么
点阵某个尺寸下各网格单元的着色结果目标网格与必要的缩放策略
笔画中心路径或设备运动轨迹工具宽度、端点、连接与设备动作
轮廓最终填充区域的封闭边界缩放、提示/网格适配与栅格化

点阵保存已经落在目标网格中的结果,笔画保存成像部件经过的中心路径,轮廓保存最终填充区域的封闭边界。三条路线产生于不同设备动作和使用条件,可以长期并存,并不是必须依次经历的三个时代。

三、PostScript 与 Type 1

轮廓字形让单枚字形能够跨尺寸保存,但还没有说明一整套轮廓字体怎样被具体系统装入和调用。在 PostScript 页面输出环境中,页面程序会按名称选择字体,解释器需要装入相应的字体资源、找到所需字形并执行绘制。PostScript 提供这套运行环境,Type 1 则规定具体的轮廓字体怎样进入其中。

3.1 PostScript 页面环境

PostScript 是一种可执行的页面描述语言。排版软件生成页面程序代码,用指令说明页面上要绘制的文字、路径和图像;PostScript 解释器则用于执行这些指令,然后再由光栅图像处理器(raster image processor,RIP)按照当前设备的分辨率生成打印点或预览像素。页面程序描述的是内容,而不是某台设备预先排好的像素,因此同一份页面描述可以交给不同的输出设备。Adobe 对 PostScript 的历史与职责说明

对数字字体来说,关键发生在页面程序要求绘制文字时。程序先按名称选择某款字体,再提交需要绘制的文本;解释器必须找到并装入相应的字体资源,从中取得所需字形及相关信息,才能继续执行页面指令。字体由此成为 PostScript 页面环境会调用的一类资源,而不再只是一组孤立的轮廓。

PostScript 为字体提供了被装入和调用的位置,但一款具体的轮廓字体还需要按照解释器能够理解的方式,组织名称、字符与字形的对应、空间信息和单枚字形的绘制数据。Type 1 字体程序就是这套环境中的一种具体实现。

页面程序和某款 Type 1 字体程序分别进入 PostScript 解释器,解释器完成绘制后再由 RIP 生成设备网格

3.2 Type 1 字体程序

页面要调用某款 Type 1 字体,解释器首先需要装入这款字体自己的 Type 1 字体程序(Type 1 font program)。Type 1 是 PostScript 字体系统规定的一种专用字体类型。每款具体的 Type 1 字体都有自己的一份字体程序,其中保存着这款字体自己的名称、字符映射、基础空间、提示信息和字形绘制指令;它不是所有 Type 1 字体共同使用的一段通用程序。

整份 Type 1 字体程序本身就是一种受严格约束的 PostScript 语言程序,不存在另外包在外面的“PostScript 接入程序”。Adobe Type 1 Font Format

可以用 Web 中的 Canvas 绘图过程把两套环境里的角色对应起来:

Web 中的 Canvas 绘图环境PostScript 页面输出环境共同承担的职责
调用 Canvas API 的 JavaScript 代码PostScript 页面程序发出绘制文字、路径和图像的指令
Canvas API 提供的绘图方法PostScript 语言中的绘图指令让程序说明要画什么、画在哪里
浏览器已经装入并注册的字体资源Type 1 字体程序运行后注册到 PostScript 环境中的字体资源提供可以按名称选择的字体及其字形数据
JavaScript 引擎PostScript 解释器中负责语言执行的部分读懂并执行程序代码
浏览器的图形系统与栅格器PostScript 解释器中的图形处理机制与 RIP把绘图指令和字形转换成屏幕像素或设备输出

具体使用一份 Type 1 字体,可以分成“装入字体”和“绘制字形”两个阶段:

装入字体:解释器执行这款字体自己的 Type 1 字体程序
        → 程序生成一份属于这款字体的字体字典
        → 把“字体名称 → 字体字典”注册到 PostScript 的字体资源表中

绘制字形:页面按名称选择字体并提交字符 A
        → 字体资源表按名称找到这款字体的字体字典
        → 字体字典通过字符映射找到相应字形的 CharString
        → 解释器中的 Type 1 机制执行 CharString,得到字形轮廓并处理提示
        → RIP 把结果转换成打印点或屏幕像素
Type 1 字体程序先形成并注册字体字典,页面随后按名称找到字体字典和 A 的 CharString

这里涉及两个需要区分的对象:

  • 字体字典(font dictionary) 代表当前这一款字体,组织着字体名称、字符与字形的对应、基础空间、提示参数、可复用子程序,以及每枚字形的绘制数据。它不是收纳多款字体的总表。
  • PostScript 环境中的字体资源表(FontDirectory) 负责保存“字体名称 → 字体字典”的对应关系。

所谓注册,就是把当前字体的这组对应关系加入字体资源表,让页面以后能够按名称找到它;这不等同于今天操作系统里的永久安装。Adobe 对字体程序注册过程的说明

Type 1 CharString 是 Type 1 字体中用来描述单枚字形的一段紧凑指令序列。它依次给出这枚字形的基础空间、轮廓绘制动作和作用于这枚字形的提示信息,也可以调用字体中预先定义的子程序。字体引擎执行这些指令后,便能构造出相应的字形轮廓。Adobe Type 1 Font Format

例如,一小段 CharString 解密并解码后可能写成 100 0 rmoveto 0 500 rlineto,意思是先相对移动,再画一条相对直线。它在形式上类似 SVG 的 <path d="m 100 0 l 0 500">;这里只是把编码还原成了可读形式,字体中的实际 CharString 是经过紧凑编码并加密的数据。

这些指令由 PostScript 解释器内部的 Type 1 机制执行;后来 Adobe Type Manager 一类专用 Type 1 引擎也可以完成同样的工作。

Type 1 字体程序作为文件保存时,常见的两种形式是 PFA 和 PFB。两者的差别主要在保存方式:

文件形式保存方式
PFA以 ASCII 文本保存整份程序,其中加密内容写成十六进制字符
PFB把同一程序分成 ASCII 段和二进制段,用二进制保存加密内容,因此占用的空间更少

在当时需要通过 7-bit ASCII 数据流下载字体的环境中,系统读取 PFB 后,会在传输时把二进制段转换为十六进制文本。Adobe 对 PostScript 字体程序存储与传输形式的说明

PFA 与 PFB 保存的是同一种 Type 1 字体程序,只是文件的存放与传输形式不同,并不是两种新的轮廓技术。如今它们主要出现在遗留字体与旧版输出流程中,已经不是现代通用字体的主要交付形式;Adobe 也已从 2023 年起在新版主要创作软件中停止支持 Type 1 字体。Adobe 对 Type 1 字体停止支持的说明

PostScript 体系中还有 Type 3 字体。Type 1 使用专门的 CharString 描述字形轮廓和提示信息,再由 Type 1 引擎解释;Type 3 则允许每枚字形直接使用一般的 PostScript 绘图过程,可以组合填充、描边或图像,但不采用 Type 1 的 CharString 和专用提示机制。

四、TrueType 与 OpenType

4.1 TrueType 字体技术

Type 1 已经让高质量、可缩放的轮廓字体进入数字出版,但它原本就是 PostScript 技术体系中的字体类型。Apple、Microsoft 等系统厂商若想在自己的平台上使用 Type 1,还需要提供 PostScript 环境、Adobe Type Manager,或另一套兼容的 Type 1 引擎,才能装入字体并生成屏幕或打印结果。

在当时,字体格式、解释和栅格化的核心机制主要由 Adobe 掌握;系统厂商即使增加了 Type 1 支持,也难以像管理自己的系统技术那样,自主决定字体怎样进入操作系统、怎样适配低分辨率屏幕,以及以后怎样扩展整套字体能力。

Apple 因此开发了 TrueType,把一套由自己定义的可缩放字体格式和栅格器直接纳入操作系统。字体安装以后,各类应用可以通过系统调用它,屏幕和打印也可以由平台自己的字体引擎处理。Apple 由此既能自行改进低分辨率屏幕上的字形处理,也能避开采用其他字体技术时需要支付的逐字体授权费用。Microsoft 面对相近的需求,后来也从 Apple 获得了 TrueType 的授权。Microsoft 对 TrueType 产生背景与提示技术的说明

在这套技术中,职责分在字体资源和平台引擎两侧:

  • TrueType 字体资源保存字符映射、字形轮廓、基础空间和提示等数据。
  • 操作系统或字体库中的字体引擎负责读取这些数据,把轮廓缩放到当前尺寸,再执行字体中的提示指令,完成网格适配和栅格化。

字体提供数据和指令,平台提供真正运行它们的引擎,于是字体的安装、调用和轮廓处理可以由操作系统统一管理,不再依赖 PostScript 或专用 Type 1 机制。Apple 对 TrueType font engine 工作过程的说明

TrueType 采用一种称为 sfnt 的结构来组织和管理数据。字体资源开头先放置一份数据表目录,说明当前资源实际包含哪些表,以及每张表位于什么位置。解析器读到目录以后,便能按当前任务直接找到需要的数据,而不必把整份资源从头到尾当作一种连续内容解释。Apple TrueType Reference Manual 对 sfnt 与 table directory 的说明

也就是说,一份 TrueType 字体资源以 sfnt 作为总体组织结构,cmap、loca、glyf 和 hmtx 等数据表则是这份资源内部的具体组成部分。我们可以用字符 A 来分别解释每个表的作用:

  • cmap 表让系统从 U+0041 找到当前资源中的字形编号(glyph ID)。
  • loca 表根据字形编号,记录这枚字形的轮廓描述在 glyf 表中的起始偏移量,也就是这段数据从表内的第几个字节开始;下一个字形编号对应的起始偏移量,则界定了这段数据在哪里结束。
  • glyf 表保存这枚字形的轮廓描述。
  • hmtx 表提供基础横向空间。
  • 字体名称和其他信息也有各自的数据表。

sfnt 负责把这些表组织在一起并让解析器找到它们,但它本身既不是某一种轮廓,也不是执行字体的引擎。

glyf 表中的简单字形使用有序轮廓点、直线和二次 Bézier 曲线表达形状,复合字形还可以引用并变换其他字形。字体中也可以带有提示指令。

外部 TrueType 引擎读取轮廓和指令,按当前尺寸调整它们与设备网格的关系,最后生成适合屏幕或打印的结果。Apple 对 TrueType font engine 工作过程的说明

TrueType 的 sfnt 表结构通过 cmap、loca、glyf 和 hmtx 提供数据,由外部字体引擎缩放、网格适配并栅格化

因此,TrueType 的变化不只是把 Type 1 使用的三次 Bézier 曲线换成二次曲线。它建立了一种新的职责分配:字体以结构化数据为主,并可以携带受限的提示程序;平台一侧则拥有完整的解析、缩放和成像能力。

4.2 OpenType 字体规范

TrueType 已经让操作系统能够直接管理结构化、可缩放的字体资源,但在许多文字与排版场景中,要把一段文字正确排出来,基础字符映射和默认占位还不够,还需要进一步处理高级排版。本文所说的高级排版,主要指基础字符映射和默认占位之后的两类能力:字形替换与字形定位。

这两类能力可以分别通过下面两个例子来理解:

  • 字形替换:输入 f、i 后,cmap 可以先找到各自的基础字形;如果当前字体提供连字,书写系统、语言和功能设置也允许使用,排版时便可以把它们替换成一枚 fi 连字字形。
  • 字形定位:在 T 后面输入 o 时,两枚字形可以保持不变,只让 o 向左靠近一些,避免中间留下过大的空隙;附加符号也可能需要相对于主体重新放置。这里改变的是字形的位置,而不是采用哪枚字形。

原始 TrueType 规范已经规定了字符映射、轮廓和基础空间等基本数据怎样组织,却没有同时建立一套跨平台通用的字形替换与定位机制。sfnt 允许增加新的数据表,因此不同平台可以在它的基础上定义自己的表和处理机制。Apple 沿着 GX/AAT 发展自己的替换和定位机制,Microsoft 则发展出 TrueType Open。

两条路线采用的数据结构和处理方式并不相同。字体生产方即使按照其中一套机制写入了连字、上下文换形或定位数据,但如果换个平台的文字引擎,就可能失效。当时真正缺少的不是容纳新数据的空间,而是一套可由不同平台共同遵守的高级排版规范。Microsoft 对 TrueType GX 与 TrueType Open 的说明

除了高级排版机制没有统一,大量 Type 1 字体仍在使用,PostScript 风格轮廓与 TrueType 轮廓也分别处在不同的资源和引擎体系中。字体厂商若想同时服务不同平台和两条轮廓路线,往往需要准备不同的字体版本;应用与系统厂商也要分别支持相应的格式和处理机制。

Microsoft 与 Adobe 因此在 TrueType Open 的基础上共同发展 OpenType。它延续并扩展 sfnt,一方面为跨平台的高级排版数据及其处理建立共同约定,另一方面把 TrueType 与 PostScript 两条轮廓路线纳入同一种字体资源框架。Microsoft 对 OpenType 产生背景与目标的说明 · OpenType 1.9.1 Overview

从技术对象来看,OpenType 是一整套字体规范。这套规范同时约定字体资源和消费端两侧的职责:

  • 在字体资源一侧,它规定数据表、字段、关联和约束,让生产方知道字符映射、名称、基础空间、字形描述和排版数据应该怎样存储。
  • 在消费端一侧,它规定这些数据表示什么,以及外部系统应该怎样进行映射、字形替换和定位等低层处理。

其中关于高级排版部分,OpenType 使用 GSUB 表保存字形替换规则,使用 GPOS 表保存字形定位数据。字体资源提供可以采用的规则和参数,外部文字塑形引擎再结合当前字符序列、书写系统、语言和功能设置,决定是否以及怎样执行它们。OpenType GSUB 规范 · OpenType GPOS 规范

这套约定不会自己读取文字并完成整个塑形过程,也不是一套现成的平台引擎。真正执行解析、塑形、缩放和栅格化的,仍是操作系统、应用或字体库中的外部组件。OpenType 1.9.1 对规范与外部处理边界的说明

OpenType 中 GSUB 改变实际采用的字形序列,GPOS 保留字形身份并调整位置

OpenType 解决的另一项整合问题,是让 TrueType 与 PostScript 风格这两条轮廓路线进入同一个资源框架。一份 OpenType 资源可以采用其中任意一条路线:

  • 用 glyf 和 loca 表保存 TrueType 轮廓。
  • 用 CFF 或 CFF2 表保存 PostScript 风格轮廓。

字符映射、名称、基础空间和其他公共数据则由外层的其他数据表统一组织。OpenType Font File 对字体数据与轮廓路线的说明 因此,OpenType 位于具体的字形表示和轮廓编码之上:它没有发明新的轮廓,而是规定不同轮廓和其他字体数据怎样组成一份共同资源。

CFF(Compact Font Format) 是一种用于保存 PostScript 风格字形描述的紧凑格式,CFF2 则是为 OpenType 资源重新设计的后续版本。两者的适用位置与内部职责并不相同:OpenType CFF2 对 CFF 与 CFF2 边界的说明

格式可以在哪里使用自身承担的职责
CFF可以独立构成字体,也可以放进 OpenType 资源为了能够独立工作,自身还带有字符集、编码、名称和宽度等信息;放进 OpenType 后,部分职责会与外层公共数据表重复
CFF2只在 OpenType 内部使用把公共职责交给 OpenType 外层,自己集中保存 PostScript 风格的字形描述、提示和变化数据

这样再看 Type 1、TrueType 与 OpenType,它们就不再是三个孤立的格式名称。Type 1 让具体轮廓字体以受约束的程序进入 PostScript 输出环境;TrueType 让平台通过结构化数据和外部字体引擎直接管理字体;OpenType 则把公共数据、两条主要轮廓路线,以及字形替换与定位的处理约定放进共同框架。三次变化持续处理的是同一个问题:一套轮廓字体怎样进入更广泛的系统,并与消费它的环境明确分工。

五、总结与边界

5.1 数字字体的发展历程

现在回到开篇中那个分别出现在实体字模、早期数字设备和现代系统里的字符 A。它的文本身份没有改变,但保存和形成字形的方法发生了变化。实体排印用字模表面保存形状,由字盒和排字者负责寻找与排列,字身提供物理占位,印刷设备完成输出;计算机接手以后,一部分职责变成字体资源中的字符映射、字形描述和基础空间等数据,另一部分则交给字体之外的排版与输出系统。

字形数据具体保存什么,也会受到设备工作方式的影响。早期屏幕在有限网格中显示文字,可以直接保存点阵结果;绘图仪让笔尖沿路线移动,可以保存中心路径;同一设计需要适应多种尺寸和输出设备时,则适合保存最终填充区域的封闭轮廓。这三种路线并非依次淘汰的三个时代,而是对不同条件作出的数据选择。轮廓因为能够先缩放、再生成当前设备结果,逐渐成为现代通用排版的主要路线。

轮廓解决了单枚字形怎样保存,却没有自动解决一整套字体怎样进入软件和输出系统。Type 1 让具体轮廓字体以受约束的程序接入 PostScript 页面环境;TrueType 把字体组织成平台可以直接管理的结构化数据,并把完整执行能力放在外部字体引擎一侧;OpenType 再让不同轮廓路线、公共数据和排版规则遵守共同的跨平台约定。

今天的数字字体因此不只是“许多字形图片”,也不等于某一种 Bézier 曲线或某一个格式名称。它是一份能够被系统识别和读取的字体资源。资源内部保存可复用的字形和相关数据,外部系统则结合当前文字、字号和设备条件继续完成选择、排列、缩放与成像。开篇中那个可以改变字号并交给不同设备输出的 A,正是这套数据与外部系统共同工作的结果。

数字字体的发展先形成点阵、笔画与轮廓三种并行字形表示,再沿现代通用轮廓路线进入 Type 1、TrueType 与 OpenType

5.2 当前边界与下一篇

本文追踪了实体排印中的职责怎样进入数字系统,并解释了现代字体资源为什么形成。至于一份具体字体资源包含哪些数据、这些数据怎样组织在一起,将在后续的文章中进一步讲解,本文不再展开。

日常界面中的“一个字体”也可能指向不同对象。字体列表显示的名称、某个已经确定的具体样式、系统实际读取的字体资源,以及磁盘上承载数据的文件,彼此相关,却不一定一一对应。一份可变资源还可以形成多个具体使用状态,一个字体集合文件也可以承载多份资源。

下一篇将从这个日常说法继续向内拆解:先分清设计与选择对象,再看它们怎样由静态或可变资源实现,随后说明 OpenType 资源内部怎样组织数据,最后落到独立文件和字体集合。到那时,我们不仅知道数字字体为什么会成为一套资源,也能在下载、安装和选择字体时,准确指出手中的“一个字体”究竟指向哪一层。