一文读懂Font文件

一文读懂Font文件

一、引言

在开始阅读本文之前,推荐先阅读字符(Character)、字形(Glyph)、字体的区别理解基本概念。

如果你对字符与Unicode的相关概念还不理解,推荐阅读字符与编码

前文,我们介绍了字符(Character)、字形(Glyph)、字体的区别,这里我们再来实际分析一个字体文件中到底有什么,这有利于我们后续理解文字排版引擎的工作原理和流程。

macOS上系统字体路径一般为/System/Library/Fonts/,可以看到有文件后缀有ttc和ttf,二者有什么区别呢?

1).ttf (TrueType Font)

ttf表示这是一个单字体文件,每个 .ttf 文件通常只对应一个字体样式(例如 Microsoft YaHei Regular)

2).ttc (TrueType Collection)

ttc表示这是一个字体集合文件,内部可以包含多个 TrueType 字体(多个 .ttf 打包在一起),这些字体通常共享某些表(比如 glyph 轮廓、cmap),减少冗余,提高存储效率,常用于一个 Typeface 的多个变体(Regular, Bold, Italic, Light…)

上面提到了TrueType,与之对应的还有OpenType,二者其实都是字体类型标准,简单理解就是OpenType是TrueType的扩展,OpenType支持更多的特性,比如:连字、RTL、上下标等。

OpenType一般以otf为后缀,但也不能简单的根据文件名后缀区分二者,文件扩展名只是习惯,并不能完全说明内部格式,真正的区别还是要看字体表结构,比如OpenType有GSUB、GPOS、GDEF等扩展表。

下面,我们来真正解析一个字体文件,看里面有什么,可以通过如下命令行将字体解析成XML。

# 对于ttf文件

ttx NewYork.ttf

# ttc文件是个字体集合,需要明确指明要提取哪个index的字体

ttx -y 0 Times.ttc

二、Font文件解析

我们以NewYork.ttf文件为例,如下是NewYork.ttf中的表

2.1 GlyphOrder

...

GlyphOrder定义glyphID与glyphName的映射。

2.2 head

Font Header,存储一些全局信息;关注几个值:

...

1)unitsPerEm

字体表里的数值一般都很大(见后文),其单位并不是像素值,而是 em unit,表示2048 units = 1 em = 设计的字高,比如当字体在屏幕上以 16px 渲染时,1 em = 16px,其他数值可按比例换算

2.3 hhea

Horizontal Typesetting Header,横向排版信息,关注几个值

...

1)ascent & descent

假设字体大小16,unitsPerEm如上为2048,则按比例换算:ascent = 1950/2048 * 16 ≈ 15.2,descent ≈ 494/2048 * 16 ≈ 3.8。

需要注意,OS_2表中也有ascent、descent的定义,这是因为不同平台会读取不同表中的ascent、descent,比如macOS、iOS一般使用hhea中的值,Windows一般使用OS_2表中的usWinAscent、usWinDescent,专业排版软件(如InDesign)一般用OS_2表中的sTypoAscender、sTypoDescender。

Q:对于同一个Font,ascent、descent的值是固定的吗?

这个问题的答案需要加定语,对于同一个Font,在同一个平台上,ascent、descent是固定的。

Q:为什么descent值是负数?

可以理解成规范,TrueType/OpenType的规范里,descent是负数,表示基线(baseline)以下延伸的高度。

2.4 maxp

...

定义字体里 glyph 的数量,以及一些最大值参数。

2.5 OS_2

...

参见Apple文档,关注几个值:

1)ySubscriptXSize & ySubscriptYSize & ySubscriptXOffset & ySubscriptYOffset

下标的大小和偏移

2)ySuperscriptXSize & ySuperscriptYSize & ySuperscriptXOffset & ySuperscriptYOffset

上标的大小和偏移

3)yStrikeoutSize & yStrikeoutPosition

删除线的粗细和垂直位置

4)ulUnicodeRange1 & ulUnicodeRange2 & ulUnicodeRange3 & ulUnicodeRange4

ulUnicodeRange表示该字体支持的Unicode范围,用ulUnicodeRange1 … ulUnicodeRange4 这 4 个 32 位字段来表示,总共 128 个 bit,对应 128 个 Unicode Block,如果某 bit = 1,表示字体支持该区块中的至少一些字符,映射表见:https://learn.microsoft.com/en-us/typography/opentype/spec/os2#ur 。

Windows系统通常用 ulUnicodeRange 来看一个字体是否支持某Unicode;macOS/iOS系统一般用 cmap 表(精确的字符映射),ulUnicodeRange只作为辅助信息;浏览器排版一般直接查 cmap,但 ulUnicodeRange 有时也用于字体 fallback 策略。

5)sTypoAscender & sTypoDescender & usWinAscent & usWinDescent

如前文所述,不同系统会取不同的值作为ascent、descent

2.6 hmtx

...

Horizontal Metrics,记录每个 glyph 的 advance width 和left side bearing。

简单理解排版引擎绘制字形的流程是:将字形放在当前点 + lsb 偏移位置进行绘制,画完后,将光标向右移动 advanceWidth,准备绘制下一个字形。

2.7 cmap

...

...

Character to Glyph Mapping,定义 Unicode code point → glyph ID 的映射,cmap表中能精确的查到该Font支持哪些Unicode。

2.8 glyf

...

Glyph Data,真正的字形轮廓(矢量点、轮廓、控制点);cmap 表负责把 Unicode 字符映射到 glyphID,而 glyf 表告诉渲染系统该 glyph 的具体形状。

2.9 name

© 2017-2024 Apple Inc. All rights reserved.

.New York

Regular

.New York; 20.0d1e1; 2024-05-06

.New York

20.0d1e1

.NewYork-Regular

...

name表中定义的是字体名称、字体家族、PostScript Name、厂商信息等。

nameID对应的含义如下:

nameID

含义

0

Copyright notice

1

Font Family name(字体家族名,比如 New York)

2

Font Subfamily name(字重/样式,比如 Regular、Bold)

3

Unique font identifier(唯一ID,通常包含厂商名+版本号)

4

Full font name(family + subfamily,比如 New York Regular)

5

Version string

6

PostScript name(唯一的、无空格的名字)

这里需要重点介绍下PostScript Name:PostScript Name是字体在一个系统里的唯一标识,是单个字符串,不允许有空格,一般是 FamilyName-StyleName 形式,比如:.NewYork-Regular,Helvetica-Bold,NotoSansCJKsc-Regular等。

在CoreText的API里,一般都要求传PostScript Name,比如:CTFontCreateWithName

2.10 GDEF

Glyph Definition Table,简单理解GDEF表就是是GPOS / GSUB的辅助表,比如GPOS和 GSUB需要知道「哪些字形是 mark、哪些能连接、哪些有变体」等信息,这些元数据就是放在GDEF 表里的。

2.11 GPOS

Glyph Positioning Table,控制字形的相对位置(如kerning、上下标等),比如「A + V」之间要减少间距,或者音标放在元音正上方等。

2.12 GSUB

Glyph Substitution Table,控制字形替换(连字、、阿拉伯文变体、直角引号换弯引号等),比如f + i → fi,'quoteleft' → ‘等。

2.13 HVAR & MVAR & avar & fvar & gvar…

这几个表是用于转换可变字体的,可变字体不在本文范围内,不再详述。

更多精彩内容欢迎关注🌍公众号:非专业程序员Ping

← Previous Post

Next Post →

相关推荐

2014巴西世界杯H组

2014巴西世界杯H组

📅 07-21 👁️ 7724
灵魂守卫乌迪尔多少钱

灵魂守卫乌迪尔多少钱

📅 10-29 👁️ 6762
癔症:一个古老症状的当代境遇