Chromium 83 中适用于 macOS system-ui 字体的更多可变字体选项

Catalina 为 macOS 带来了新的统一可变系统字体。

Dominik Röttsches
Dominik Röttsches

CSS 字体模块级别 4 规范的 “system-ui”部分定义了一个 system-ui 字体关键字,使开发者能够在自己的网站和应用中直接使用内置的、经过涡轮优化的、本地化的、超高质量的、无需下载的默认操作系统字体。

body {
  font-family: system-ui;
}

这种排版选择相当于说“使用此用户当前语言区域的默认系统字体”。

在 macOS 上,system-ui 字体是 San Francisco,这是一种经过设计团队审核、测试且最近升级的字体!首先,我们将介绍 Catalina 中令人兴奋的全新可变字体功能,然后介绍几个 bug 以及 Chromium 工程师如何解决这些 bug

本文假定您已熟悉可变字体。如果不是,请参阅网页上的可变字体简介和下面的视频。

浏览器兼容性

在撰写本文时,system-ui 已获得 Chromium(自 56 版起)、Edge(自 79 版起)、Safari(自 11 版起)和 Firefox(自 43 版起)的支持,但需要使用 -apple-system 关键字。如需了解最新信息,请参阅我可以使用可变字体吗?

新功能

自 Chromium 83 起,网页开发者现在可以使用 Catalina 为系统字体带来的新功能。system-ui 字体现在具有更多可变设置:光学尺寸调整和 2 种独特的粗细调整:

Mojave
h1 {
  font-family: system-ui;
  font-weight: 700;
  font-variation-settings:
    'wght' 750
  ;
}
Catalina
h1 {
  font-family: system-ui;
  font-weight: 700;
  font-variation-settings:
    'wght' 750,
    'opsz' 20,
    'GRAD' 400,
    'YAXS' 400
  ;
}

在 Mojave 上,system-ui 是一种只有 wght 设置的可变字体。而 Catalina 上的 system-ui 是一种具有 wghtopszGRADYAXS 设置的变量字体。

在我看来,这似乎是一些不错的渐进式增强设计机会!如果您愿意,可以深入了解系统字体的细微之处。

wght

接受介于 0900 之间的字体粗细,并同等地应用于所有字符。

/* 0-900 */
font-variation-settings: 'wght' 750;

opsz

光学调整与字距调整或字母间距调整类似,但间距是由人眼而非数学计算来确定的。19 或更低的值适用于文本和正文间距,而 20 或更高的值适用于显示标题和正文的间距。

/* 19 or 20 */
font-variation-settings: 'opsz' 20;

GRAD

与字重类似,但不会影响水平间距。它接受介于 4001000 之间的值。

/* 400-1000 */
font-variation-settings: 'GRAD' 500;

YAXS

垂直拉伸字形。它接受介于 4001000 之间的值。

/* 400-1000 */
font-variation-settings: 'YAXS' 500;

合并选项

只需几行 CSS,我们就可以将字体设置调整为所选的粗体,或尝试其他有趣的组合:

font-weight: 700;
font-weight: bold;
font-variation-settings: 'wght' 750, 'YAXS' 600, 'GRAD' 500, 'opsz' 20;

这样一来,macOS 上的 Chromium 用户就能看到您升级后的自定义 750 字重以及其他一些有趣的小调整了 👍

macOS 10.15 为其系统字体添加了新功能,并且在 macOS 10.15 中,Chromium bug 跟踪器中记录了一个棘手的 system-ui bug。I wonder if they are related!?

附录:system-ui 回归

这个故事从另一个 bug 开始:#1005969。有人举报了 macOS 10.15,因为 system-ui 字体间距看起来很窄且拥挤。

Facebook 群组页面上两个段落的比较。左侧是 Chrome,右侧是 Safari,Chrome 的间距略微紧凑
左侧为 Chrome(跟踪更紧密),右侧为 Safari(光学间距更合理)

背景

您是否曾注意到,在 macOS 10.14 上,当段落或标题的字号增大或减小时,它们会“突然”变成另一种字体?

在 Mojave (macOS 10.14) 上,system-ui 字体会根据目标字号在两种字体之间切换。当文本大小小于 20px 时,macOS 使用“旧金山文本”。当文字大小为 20px 或更大时,macOS 使用“旧金山显示”字体。光学尺寸是静态构建到两个单独的字体中的。

Catalina (macOS 10.15) 随附了新的统一可变字体 San Francisco。无需再管理“文字”和“展示”。它还获得了前面介绍的新变体设置 opsz

h1 {
  font-variation-settings: 'opsz' 20;
}

遗憾的是,新 Catalina 字体的默认 opsz 值是 20,而 Chromium 工程师并未准备好将 opsz 应用于系统字体。这导致较小的尺寸显示过窄。

为了解决这个问题,Chromium 需要正确地将 opsz 应用到系统字体。这促使 Issue #1005969 得到修复。胜利!还是说……?

尚未完成

问题就出在这里:Chromium 应用了 opsz,但看起来仍然不太对劲。Mac 上的系统字体具有一个名为 trak 的额外字体表,用于调整水平间距。在修复此问题时,Chromium 工程师注意到,在 macOS 上,从 CTFontRef 对象检索水平指标时,trak 指标已纳入指标结果中。Chromium 的塑形库 HarfBuzz 需要尚未纳入 trak 值的指标。

以列表形式显示 system-ui 及其所有字重和变体。其中一半未应用任何权重差异。
左侧:粗体粗细应用于字体大小为 19 及以下的文本。右侧:字号为 20 及以上的字体会失去粗体样式

在内部,Skia(图形库,而非同名字体)同时使用 CoreGraphics 中的 CGFontRef 类和 CoreText 中的 CTFontRef 类。由于需要在这些对象之间进行必要的内部转换(用于保持向后兼容性并访问这两个类上所需的 API),Skia 在某些情况下会丢失粗细信息,导致粗体字体无法正常显示。此问题已在问题 1057654 中跟踪。

Skia 仍需支持 macOS 10.11,因为 Chromium 仍支持该版本。在 10.11 中,“San Francisco Text”和“San Francisco Display”字体甚至都不是可变字体。而是为每个可用的字重提供了一系列单独的字体。在某个时间点,它们的字形 ID 变得不同步。因此,如果 Skia 使用“San Francisco Text”进行文字排版(将文字转换为可绘制的字形),那么如果使用“San Francisco Display”进行绘制,就会变成乱码,反之亦然。即使 Skia 只是请求了其他大小,macOS 也可能会切换到另一个。应该可以始终使用一种字体并对其进行缩放(使用矩阵放大,而不是请求更大的尺寸),但 CoreText 存在一个问题,即它不会放大 sbix(彩色表情符号)字形(只会缩小)。情况比这更复杂一些。CoreText 似乎会在应用矩阵后限制垂直范围,这似乎与它无法以 45 度角绘制表情符号有关。无论如何,如果您希望表情符号以大尺寸显示,则需要复制字体以获得大尺寸版本。

因此,为了在内部创建不同大小的 CTFont 对象,同时确保使用相同的底层字体数据,Chromium 从 CTFont 中提取了 CGFont,然后从 CGFont 创建了一个新的 CTFontCGFont 对象与大小无关,神奇的切换发生在 CoreText 级别)。在 10.154 之前,此功能一直正常运行。在 10.15 中,此往返过程最终丢失了太多信息,导致了权重问题。Flutter 注意到了权重问题,并针对调整大小问题提出了替代修复方案,即直接从原始 CTFont 创建新的 CTFont,同时使用 CoreText 中一个旧的但未记录在案的属性直接控制光栅大小。这样可确保在 10.11 上正常运行,并修复其他问题(例如将光学尺寸明确设置为默认值)。

不过,这样可以保留字体中更多的 CoreText“魔力”。其中一个似乎是,它仍然以某种方式调整字形推进,而不仅仅是 trak 表(Chromium 已经尝试通过另一个未记录的属性来抑制该表的应用)。

CGFont 不会执行任何此类“魔法”,因此 Chromium 或许可以从 CTFont 中获取 CGFont,并仅使用它来获取进展?遗憾的是,这行不通,因为已知 CoreText 也会以其他方式干扰字体。例如,它会使小表情符号比您实际请求的稍大一些(稍微放大)。CGFont 不知道这一点,因此您最终会得到彼此靠得太近的基于 sbix 的表情符号,因为您是以一种尺寸进行测量的,但 CoreText 会将它们绘制得更大一些。Chromium 确实需要 CTFont 方面的改进,但它希望在不进行跟踪的情况下实现这些改进,最好也不要进行任何其他乱七八糟的操作。

由于修复间距问题需要一组相互关联的 Blink 和 Skia 修复,因此 Chromium 工程师无法“仅通过还原”来解决问题。Chromium 工程师还尝试使用其他 build 标志来更改 Skia 中与字体相关的代码库,这修复了粗体字体问题,但导致间距问题出现回归。

修复

最终,Chromium 当然希望同时修复这两项问题。Chromium 现在会使用 HarfBuzz 内置的字体 OpenType 字体指标函数,直接从系统字体字体表中的二进制数据检索水平指标。使用此功能时,如果字体具有 trak 表(表情符号字体除外),Chromium 会绕过 CoreText 和 Skia。

以列表形式显示 system-ui 及其所有字重和变体。之前无法正常运行的半屏现在看起来很不错。

与此同时,我们仍在跟踪 Skia 问题 #10123,以期在 Skia 中完全修复此问题,并重新使用 Skia 从中检索系统字体指标,而不是通过 HarfBuzz 进行当前修复。