很早之前,我们团队对接的设计同学推行了一个字体规范,凡是金额价格的数字表达,需要使用他们制作的某个自定义字体,该字体仅包含数字和英文等字符。
但该字体在后续的大量使用中发现在 iOS 上存在基于 baseline 无法对齐的问题,该问题在 Web 和 Native 场景都有同学反馈。

这个问题困扰大家久矣,目前大家的解法通常是 baseline 对齐之后,再将数字设置成绝对定位人肉再微调。整体解决方案比较 Hack。

我们也曾尝试找设计同学,他们反馈这个数字字体他们已经不维护了,要解决的话只能新搞字体成本比较高。鉴于你们不清楚是否是字体产生的这个问题,且你们现在不是有解决方案吗,那就保持现状就好。
这个回复虽然让人有点无语,但确实比较现实。痛定思痛,我想着求人不如求己。虽然我不会字体设计不清楚原因,但我不会不代表 AI 不会呀!于是在 AI 老师的带领下,我的字体求知之路开始了。
怎么就对不齐了?
在此之前我们整体感觉就是这个数字字体偏上所以导致对不齐,且这个问题只在 iOS 中存在,Android 是没有问题的。所以我直接给 AI 下了指令,帮我分析字体的上下空白边距,的确发现上下边距是不一致的,下边距是比较高的。我让其帮我调整字体上下边距一致给我输出调整后的文件。最终 iOS 上使用该修改版确实能达到对齐的效果,但是在 Android 上整体就偏下了。

到这我就有点不知道怎么办了。直到有同学给了一张图,告知字形轮廓整体偏上,我顺势对比了下 Android 发现是正常的之后。于是我换了个思路,让 AI 分析下为什么 iOS 上数字会偏上,Android 是正常的。
这个时候 AI 开始发现真相了,他给了好几个关键字 OS/2 sTypo, hhea, usWin 等。告知我是因为这个字体的 hhea 和 OS/2 sTypo 配置不一致导致的。

OK 我暂时先不管这些东西是什么,先让 AI 按照它发现的问题给我一份修复版的文件看看效果。最终测试下来发现确实返回的字体偏上以及居中的问题都好一些,可以看到数字相对于左侧的标签的是居中对齐了。

字体格式
在了解问题的原因之前,我们得先补习一些印刷和字体相关的知识。我们都知道活字印刷是通过预先对铅字块进行排版然后像盖章一样印刷。后续进入计算机时代之后,出现了照排技术,可以简单理解就是通过将一个一个字模在相纸上光照显影的形式替换了铅字块进行排版,最终将排版后的相纸进行印刷。再后来就是现在的数码印刷时代,所有的排版都是在电脑端完成,打印机通过感知排版后的效果逐行逐帧打印。
这里就会涉及到一个字怎么在电脑上存储表达的。我们现在大多数的字体都是轮廓字体。电脑就像是一支“笔”,字体文件中记录了这支笔从哪里开始起笔绘制整体字形的轮廓所有需要的数据。现代字体格式基本上都是这个逻辑,字体格式的本质不是艺术的存储,而是字体在电脑上可编程化的参数表达。
至于为什么有这么多字体格式,这就涉及到 Adobe, Apple 和 Microsoft 三家巨头公司的爱恨情仇了。
PostScript
PostScript 由 Adobe 在 1984 年推出,是桌面出版领域的革命性技术,使用贝塞尔曲线描述字形轮廓,能用少量数据勾勒出平滑复杂的曲线,支持无限缩放不失真,成为高端印刷和出版行业的标准。它本质是命令型编程语言,通过栈式虚拟机执行图形指令,为后续字体格式奠定了矢量轮廓的核心逻辑 。
早期 Apple 一直做的是硬件设备,乔布斯比较看好未来数字艺术这块的市场。所以当时和 Adobe 进行了合作,在 Apple 的打印机设备上支持了 PostScript 字体。
TrueType
但 PostScript 字体有个比较头疼的问题是太编程化了,你让字体设计师去直接写就很痛苦。所以最好是能有个软件来设计这个字体再转成 PostScript 字体文件比较好。所以当时 Apple 希望 Adobe 能把 PostScript 授权给自己,让他们在 Mac 上去做对应的字体软件。
这在现在看起来是个非常正常的思路,但是在当时闭源的世界大家都是守着自己的垄断软件故步自封。Adobe 担心授权给电脑之后被破解了他们的垄断地位就没有了。我们站在未来的视角看过去就会发现这个决策是非常可笑的。但 Adobe 确实做了这个决定,并在当时为他们的失策付出了惨痛代价。
Apple 在被拒之后痛定思痛,于是自己研发了 TrueType 字体规范,并和 Microsoft 合作进行推广。它的核心目的就是旨在打破 Adobe 在 PostScript 领域的垄断。TrueType 的最大优势是 Apple 吸取了 PostScript 的教训,它选择了公开 TrueType 技术规范。同时它集合了 Windows 和 Mac 未来两大电脑操作系统的支持,所以跨平台兼容性非常好。同时在两家公司的支持下,它保证了电脑屏幕显示和打印输出的一致性。
OpenType
虽然 Apple 公开了 TrueType 规范本身,但是基于 TrueType 还有很多衍生的技术把持在 Apple 手上。当 Microsoft 由于捆绑 TrueType 赚的盆满钵满的时候,流出羡慕泪水的 Apple 拒绝了 Microsoft 想要更多 TrueType 字体技术的授权的申请,期望通过复刻 Microsoft 通过在自家 Mac 上捆绑来实现类似的商业道路。
无独有偶,Microsoft 在被拒之后痛定思痛,转手就和 Adobe 合作开发了新的字体格式 OpenType。俗话说的好,商场如战场,敌人的敌人就是朋友嘛!
OpenType 字体整体沿用了 TrueType 的 sfnt容器,并对其进行了扩展。正因如此,可承载 PostScript CFF/CFF2 或 TrueType glyf 轮廓。在此基础上,OpenType 新增了连字、上下文替换、多语言支持、可变字体轴(2016年 OpenType 1.8加入支持)等高级排版特性,彻底扩展了字体的表现力,如今已成为跨平台字体的事实标准,支持从桌面到 Web 全场景的排版需求。
最终 Apple 在 Mac OS X 慢慢加入了对 OpenType 的系统支持,这场字体的商战慢慢才告一 段落。
字体度量
我知道你们很着急,但请先别太着急。知道字体度量之前,我们还需要知道一些字体的基础知识。

字体设计中,我们会定义一个 em 空间,作为字体的基础区域。基于 Baseline(也就是坐标原点)之上会定义一个上升空间 Ascender,之下会定义一个下降空间 Descender。对选定的一组字体纵向度量,推荐的 baseline-to-baseline distance 通常按以下方式计算:
行高(Line Heigt)= Ascender - Descender + LineGap
其中定义 Ascender, Descender 和 LineGap 的配置就叫 Vertical Metrics(纵向度量)。当然我这里列举的只是和本文相关的几个参数,纵向度量配置还有一些其他的配置项我就不一一列举了。
在 TrueType 格式中,使用 hhea 字段来描述相关的配置:
- hhea.ascender
- hhea.descender
- hhea.lineGap
后续 Windows 在引入 TrueType 的支持的时候,额外增加了 usWin* 字段来描述最大裁剪区域,包含:
- usWinAscent
- usWinDescent
再后来到 OpenType 标准规范引入,为了保持兼容性,它并没有废弃 hhea 字段描述,而是额外新增了 sTypo* 字段描述度量配置。它包含:
- sTypoAscender
- sTypoDescender
- sTypoLineGap
同时 OpenType 还增加了 USE_TYPO_METRICS 标志,当其值为 1 时优先会读取 sTypo* 度量配置。但不同的操作系统有不同的实现。前文咱们已经知道,Apple 创建了 TrueType 字体格式。作为 TrueType 的亲爹,咱们必须得高优支持 hhea 度量配置呀!

诚如 AI 所说,我们用字体软件打开字体配置一看,typoDescender 和 hheaDescender 的值明显不一样,且 hheaDescender 的绝对值明显要比 typoDescender 要大。这就解释了为什么 Android 和 iOS 上的表现为什么会不一样,且为什么 iOS 上体感会更靠上。因为 hheaDescender 代表的基线以下的下降高度,下降高度越大,居中对齐时字形就会看起来靠上。
我们使用 AI 将 hheaDescender 的配置改成和 typoDescender 的配置一致之后,最终双端的表现就拉齐一致了。

怎么还对不齐?
我们修改 hhea 配置和 sTypo 一致之后发现,iOS 的表现确实好了不少,但是还是和右侧的元字有轻微的不对齐。这里就涉及到苹方字体中西文和中文字形的设计策略了。AI 告诉我苹方的几个字在本文测试的字体版本、字号和渲染设备上,截图中“元”的可见轮廓中心比数字低约 1px。所以即使按照 baseline 对齐,由于中文更靠下所以在视觉上仍然不会对齐。


而 Android 使用的系统中文 fallback 通常不是苹方,而是设备对应的 Noto Sans CJK/厂商字体.字形轮廓、ideographic baseline、字体边距,以及系统的文本测量方式都可能不同。
所以这个问题已经不是修改字体整体配置能解决的了,它确实需要字形去做微调来适配。由于字形的位移适配是统一的,也会影响原本正常渲染的设备。为了保证影响最小化,AI 给我的建议是下移30个单元,实际移动距离不足 1px 在不同的系统上可能会由于渲染的取舍最终抹平差异。

最终按照 AI 给的建议修改观察双端的效果,基本能比较符合预期了。

后记
后续为了不影响历史的场景,我将修改后的字体单独整理发布了个新的字体,后续碰到问题的场景再按需替换。现在我们再整体脱水总结下:
- 该字体的
hhea垂直度量配置下降空间要比上升空间大,导致 iOS 中基于该配置渲染的字形垂直居中后依旧整体偏上; - 历史原因,字体文件中存在多套度量配置。而 Android 等非 iOS 设备中在
USE_TYPO_METRICS=1条件下会优先读取的是sTypo度量配置,所以 Android 上渲染和 iOS 有差异是正常的; - iOS 中苹方字体设计上整体会偏基线靠下,导致基线对齐后视觉还是不对齐。最终通过微调字形下移实现对齐
相关文章:

