Catalina 为 macOS 带来了新的统一可变系统字体。
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 种独特的粗细调整:
h1 { font-family: system-ui; font-weight: 700; font-variation-settings: 'wght' 750 ; }
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
是一种具有 wght
、opsz
、GRAD
和 YAXS
设置的变量字体。
在我看来,这似乎是一些不错的渐进式增强设计机会!如果您愿意,可以深入了解系统字体的细微之处。
wght
接受介于 0
和 900
之间的字体粗细,并同等地应用于所有字符。
/* 0-900 */
font-variation-settings: 'wght' 750;
opsz
光学调整与字距调整或字母间距调整类似,但间距是由人眼而非数学计算来确定的。19
或更低的值适用于文本和正文间距,而 20
或更高的值适用于显示标题和正文的间距。
/* 19 or 20 */
font-variation-settings: 'opsz' 20;
GRAD
与字重类似,但不会影响水平间距。它接受介于 400
和 1000
之间的值。
/* 400-1000 */
font-variation-settings: 'GRAD' 500;
YAXS
垂直拉伸字形。它接受介于 400
和 1000
之间的值。
/* 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
字体间距看起来很窄且拥挤。

背景
您是否曾注意到,在 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
值的指标。

在内部,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
创建了一个新的 CTFont
(CGFont
对象与大小无关,神奇的切换发生在 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。

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