修复跨平台 Webfonts 渲染不一致的问题
¶Issue
最近开发的几个 Web 项目都遇到了同样的问题,在 Windows 上与设计稿 pixel-perfect 的网页,在 macOS 或 iPhone 上测试的时候却出现字体偏移现象,如下图所示:
与这个 span 相关的的 css 大致如下:
.gs-header-menu__mega-toggle span {
font-family: Larsseit;
font-weight: 700;
font-size: 1.6rem; // 16px in this case
line-height: 1.33;
}
仔细观察发现 span 框的大小是完全一样的,其高度为 \(16 * 1.33 \approx 21\)。但里面的字体图元却渲染得不一样,即使把 line-height
设置成 1
,也无法解决这一问题,在 macOS 中渲染出来的字体仍然偏上。这一问题对 CSS 来说已经超纲了。虽然我不是特别强迫症,但是这样的问题却非常吸引我,非解决不可。
于是我做了一些研究,找到了一篇非常硬核的文章:Deep dive CSS: font metrics, line-height and vertical-align,文中作者介绍了字体是如何被渲染到网页上,以及字体文件中的各种参数是如何影响渲染。
文章中提到了一个跨平台的开源软件:Font Forge。我们可以用它来直接编辑字体文件,让我们有机会深入字体文件,一探究竟。
¶Solution
首先我们要找到对应的字体文件。字体是设计师指定的,由客户在字体商店购买授权后发给我们的。为了方便在项目中使用,我将其 @font-face
重新编排,只要根据 font-family
, font-weight
和 font-style
就能找到对应的字体文件:
@font-face {
font-family: 'Larsseit';
font-weight: 700;
font-style: normal;
src: url('#{$fonts-directory-path}/39A063_4_0.eot');
src: url('#{$fonts-directory-path}/39A063_4_0.eot?#iefix') format('embedded-opentype'),
url('#{$fonts-directory-path}/39A063_4_0.woff2') format('woff2'),
url('#{$fonts-directory-path}/39A063_4_0.woff') format('woff'),
url('#{$fonts-directory-path}/39A063_4_0.ttf') format('truetype'),
url('#{$fonts-directory-path}/39A063_4_0.svg#wf') format('svg');
}
商业字体通常提供了多种不同的格式,在兼容不同浏览器的同时作了一些优化。例如 woff2 可以在最新的浏览器上使用,文件小,加载快。其它格式可以参考这个页面:不台平台的 Webfonts 格式支持情况。
客户提供的字体文件多达 60 个(1款字体 * 12个字型 * 5种格式)。由于字体的问题只出现在 macOS 和 iOS 上,所以我们只需要处理 woff2 格式的 12 个字形即可。
针对上面这个字体 39A063_4_0.woff2
我们用 Font Forge 编辑它。在 Element > Font Info... > General
面板中可以看到如下参数:
其中 Em Size: 1000
表示其字体的设计尺寸为 1000 单位。Ascent: 766
是一个辅助线高度,表示在基线上 766 单位,设计字体时顶部不要超过它,(例如带注音的字母ÉÅÑ
)。Descent: 234
是另一个辅助线高度,表示在基线下 234 单位,设计设计时底部不要超过它,(例如有尾部的字母gjpqy
)。如果对 em 这个单位有兴趣的话,可以阅读一下这篇文章。
以上三个参数是供字体设计师在设计的时候使用,并不影响渲染,甚至在设计字体的时候,对某些非常张扬的字母,还可以不遵守这些辅助线。所以设计师在设计完字体的时候,还要另外设置另外的在渲染时使用的参数。这些参数在另一个页面:OS/2 > Metrics
:
对于如何设置这些个参数,业界也没有一个统一的说法。比较主流的实践有这两个,一个来自 Glyphs App,另一个来自 Google Fonts。我作为 Web 前端,自然是站队后者。
以 Google Fonts 的规范为例,需要调整参数如下:
- Win Ascent/Win Descent:将其设置为字形中的最高和高低线,防止 Windows 裁剪字型。
- Really use Typo metrics:启用该选项,引导 Windows 使用
Typo *
来控制字体的基线和行高。 - Typo Ascent/Typo Descent:设置 Ascent 为 Cap Height 与 Ascender Height 中较大的一个。设置 Descent 为 Ascent - EmSize。
- HHead Ascent/HHead Descent:保持与
Typo *
一致,确保 macOS 与 Windows 有相同的渲染效果。
依照这个规范,对比我们现在使用的这个字体。之所以在 Windows 和 macOS 上有不同的渲染效果是因为没有启用 Really use Typo metrics
,导致 Windows 上使用的是 Win *
的基线,而 macOS 使用的是 HHead *
的基线。所以只要将 Really use Typo metrics
钩上,保存并导出字体即可。如果原本字体中的 Ascent/Descent 设置不符合要求,可以自行按上面的标准进行微调。
¶Example
以下是使用这种方法修复 URWDIN 字体的示例:
¶Windows
在 Windows 上修复前字体使用的是 Win *
基线,整体偏下。line-height: 1
时溢出 span 框;
修复后使用 Typo *
基线,与 macOS 表现一致。
¶macOS
在 macOS 上修复前后使用的都是 HHea *
基线,没有区别。
与修复后的 Windows 示例一致。
但是如果要将带注音的字母ÉÅÑ
也包括进去,则 Typo *
以及 HHea *
的 Ascent 就需要再往上调整了。(不过我们这个项目无须考虑这个问题。)
在研究的过程中,我还发现有一些在线工具可以帮你修复类似的问题,例如 Font Squirrel Generator。但是这些工具会将商业字体加入黑名单来避免不必要的版权纠纷。如果使用的是开源的字体,没有版权限制的话,到是可以试试。通常开源字体有社区维护,被广大用户使用。经过反馈和修改,几轮迭代后质量反而更好,例如思源系列。