69

修复跨平台 Webfonts 渲染不一致的问题 | 木匣子

 4 years ago
source link: https://blog.mutoo.im/2019/07/webfonts-inconsistency-cross-platform/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Issue

最近开发的几个 Web 项目都遇到了同样的问题,在 Windows 上与设计稿 pixel-perfect 的网页,在 macOS 或 iPhone 上测试的时候却出现字体偏移现象,如下图所示:

font-rendereing-os-different-os

与这个 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≈2116∗1.33≈21。但里面的字体图元却渲染得不一样,即使把 line-height 设置成 1,也无法解决这一问题,在 macOS 中渲染出来的字体仍然偏上。这一问题对 CSS 来说已经超纲了。虽然我不是特别强迫症,但是这样的问题却非常吸引我,非解决不可。

于是我做了一些研究,找到了一篇非常硬核的文章:Deep dive CSS: font metrics, line-height and vertical-align,文中作者介绍了字体是如何被渲染到网页上,以及字体文件中的各种参数是如何影响渲染。

文章中提到了一个跨平台的开源软件:Font Forge。我们可以用它来直接编辑字体文件,让我们有机会深入字体文件,一探究竟。

Solution

首先我们要找到对应的字体文件。字体是设计师指定的,由客户在字体商店购买授权后发给我们的。为了方便在项目中使用,我将其 @font-face 重新编排,只要根据 font-family, font-weightfont-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 面板中可以看到如下参数:

General Font Info

其中 Em Size: 1000 表示其字体的设计尺寸为 1000 单位。Ascent: 766 是一个辅助线高度,表示在基线上 766 单位,设计字体时顶部不要超过它,(例如带注音的字母ÉÅÑ)。Descent: 234 是另一个辅助线高度,表示在基线下 234 单位,设计设计时底部不要超过它,(例如有尾部的字母gjpqy)。如果对 em 这个单位有兴趣的话,可以阅读一下这篇文章

以上三个参数是供字体设计师在设计的时候使用,并不影响渲染,甚至在设计字体的时候,对某些非常张扬的字母,还可以不遵守这些辅助线。所以设计师在设计完字体的时候,还要另外设置另外的在渲染时使用的参数。这些参数在另一个页面:OS/2 > Metrics

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

URDIN-windows


在 Windows 上修复前字体使用的是 Win * 基线,整体偏下。line-height: 1 时溢出 span 框;
修复后使用 Typo * 基线,与 macOS 表现一致。

macOS

URDIN-macos


在 macOS 上修复前后使用的都是 HHea * 基线,没有区别。
与修复后的 Windows 示例一致。

但是如果要将带注音的字母ÉÅÑ也包括进去,则 Typo * 以及 HHea * 的 Ascent 就需要再往上调整了。(不过我们这个项目无须考虑这个问题。)


在研究的过程中,我还发现有一些在线工具可以帮你修复类似的问题,例如 Font Squirrel Generator。但是这些工具会将商业字体加入黑名单来避免不必要的版权纠纷。如果使用的是开源的字体,没有版权限制的话,到是可以试试。通常开源字体有社区维护,被广大用户使用。经过反馈和修改,几轮迭代后质量反而更好,例如思源系列


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK