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

Catalina 为 macOS 引入了全新的统一变量系统字体。

Adam Argyle
Adam Argyle
多米尼克·罗特切斯
Dominik Röttsches

CSS 字体模块级别 4 规范的“system-ui”部分定义了 system-ui 字体关键字,以便开发者直接在其网站和应用中使用内置的、经过增强型优化、本地化的超高品质、无需下载的默认操作系统字体。

body {
  font-family: system-ui;
}

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

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

这篇博文假定您已熟悉可变字体。如果没有,请查看网页版可变字体简介和下面的视频。

浏览器兼容性

在撰写本文时,system-ui 自 Chromium(自 56 起)、Edge(自 79 年起)、Safari(自 11 月起)和 Firefox(自 43 起)支持 -apple-system,但使用 -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 重量和其他一些有趣的调整 👍?

游乐园

点击下方“Glitch”(故障)中的 Remix to Edit(混剪以修改),获取该故障的可编辑副本,然后修改新的 font-variation-settings 选项,看看这会如何影响您的字体。请注意,只有使用 macOS Catalina 设备时,此故障才会起作用。

macOS 10.15 为其系统字体添加了新功能,并在 macOS 10.15 中记录了一个棘手的 system-ui bug,即 Chromium bug 跟踪器。我想知道它们之间是否有关联!?

附录: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 会使用“San Francisco Text”。当文本为 20px 或以上时,macOS 会使用“San Francisco Display”。光学大小调整以静态方式构建为两种单独的字体。

Catalina (macOS 10.15) 面向旧金山推出了一种新的统一可变字体。无需再管理“文字广告”和“展示广告”。它还获得了前面所述的新版本设置“opsz”。

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

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

为了解决此问题,Chromium 需要将 opsz 正确应用于系统字体。这导致了问题 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 检索系统字体指标。