结合使用高级排版与本地字体

了解 Local Font Access API 如何帮助您访问用户本地安装的字体,并获取有关这些字体的底层详细信息

网络安全字体

如果您从事 Web 开发的时间足够长,您可能还记得 网络安全字体。 众所周知,这些字体几乎适用于所有最常用的操作系统 (即 Windows、macOS、最常见的 Linux 发行版、Android 和 iOS)。在 21 世纪初 微软甚至率先推出了 倡议 名为适用于网络的 TrueType 核心字体,该字体通过 一个目标:“每当您访问有指定限制的网站时,您所看到的网页就是与 网站设计人员的预期”。是,此包含的网站在 Comic Sans MS。这里有一个 经典的 Web 安全字体堆栈(具有 sans-serif 可能如下所示:

body {
  font-family: Helvetica, Arial, sans-serif;
}

网络字体

网络安全字体真正至关重要的时代已经一去不复返了。目前,我们 网络字体 甚至是可变字体,我们可以通过更改 显示不同的轴您可以通过将 @font-face 代码块, 该参数指定要下载的字体文件:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

然后,您可以通过指定 font-family,像往常一样:

body {
  font-family: 'FlamboyantSansSerif';
}

本地字体作为指纹矢量

大多数网络字体都来自网络。有趣的是, @font-face 中的 src 属性 声明中,除了 url() 函数,也接受 local() 函数。这允许在本地加载自定义字体(令人惊讶!)。如果用户恰好 FlamboyantSansSerif,将使用本地副本,而不是 正在下载:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

这种方法提供了一种很好的后备机制,有可能节省带宽。在互联网上 很遗憾,我们不能有好的方面local() 函数的问题在于, 滥用浏览器数字“指纹”收集事实证明,用户安装的字体列表 识别。很多公司在自己的员工的字体上都安装了自己的公司字体, 笔记本电脑。例如,Google 有一种名为 Google Sans 的企业字体。

<ph type="x-smartling-placeholder">
</ph> 显示 Google Sans 字体预览的 macOS Font Book 应用。 <ph type="x-smartling-placeholder">
</ph> Google 员工笔记本电脑上安装的 Google Sans 字体。

攻击者可以尝试通过测试是否存在 大量已知的企业字体,如 Google Sans。攻击者会尝试渲染文本 并测量字形。如果字形与 企业字体,攻击者就会受到攻击。如果字形不匹配,攻击者就知道 由于未安装企业字体,因此使用了默认替换字体。如需全面了解 以及其他浏览器指纹攻击 调查问卷论文,作者: Laperdix

区分公司字体,即使只有已安装字体的列表也可以识别。以下情况: 这种攻击途径已经变得非常糟糕,以至于最近,WebKit 团队 已决定 [在可用字体列表中]“仅包含 [在可用字体列表中] 网络字体和 而不是用户本地安装的字体”。(我查看了一篇有关授予访问权限的文章 本地字体。)

Local Font Access API

本文开头可能会让你产生负面情绪。我们真的不能有 该怎么办?别担心。我们认为可以,而且可能 但首先,我先回答一个您可能会问自己的问题。

有网络字体时,我们为什么需要 Local Font Access API?

一直以来,我们很难提供专业品质的设计和图形工具, 。但他们遇到的一个阻碍是无法获取和使用各种专业工具, 设计人员在本地安装的构造字体和微调字体。网络字体支持部分发布 这些用例,但无法以编程方式访问 光栅化处理程序来渲染字形轮廓。同样,您也无法访问网络字体的二进制文件 数据。

  • 设计工具需要访问字体字节才能实现自己的 OpenType 布局,并允许 设计工具,以便在较低级别接入,用于执行矢量滤镜或 对字形形状进行转换。
  • 开发者在其应用中引入的旧版字体堆栈可能已经引入到网站中。 要使用这些堆栈,它们通常需要直接访问字体数据,而网页字体不需要 提供。
  • 有些字体可能未获许可,无法在网络上投放。例如,Linotype 拥有 某些字体仅包含桌面应用

Local Font Access API 就是为了解决这些问题。它由两部分组成:

  • 一个字体枚举 API,让用户可以向所有可用系统授予访问权限 字体。
  • 从每个枚举结果中请求低级别(面向字节的)SFNT 容器 访问权限,其中包含完整字体数据。

浏览器支持

浏览器支持

  • Chrome:103。 <ph type="x-smartling-placeholder">
  • Edge:103。 <ph type="x-smartling-placeholder">
  • Firefox:不支持。 <ph type="x-smartling-placeholder">
  • Safari:不支持。 <ph type="x-smartling-placeholder">

来源

如何使用 Local Font Access API

功能检测

如需检查 Local Font Access API 是否受支持,请使用以下命令:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}

枚举本地字体

如需获取本地安装字体的列表,您需要调用 window.queryLocalFonts()。通过 第一次时,这将触发权限提示,用户可以批准或拒绝。如果用户 批准查询其本地字体,浏览器将返回一个包含字体数据的数组 您可以循环播放。每种字体都表示为一个具有 family 属性的 FontData 对象 (例如 "Comic Sans MS")、fullName(例如 "Comic Sans MS")、postscriptName(例如 例如 "ComicSansMS")和 style(例如 "Regular")。

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

如果您只对一部分字体感兴趣,还可以根据 PostScript 添加 postscriptNames 形参,从而创建名称。

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

访问 SFNT 数据

完整的 SFNT 访问权限可通过blob() FontData 对象。SFNT 是一种字体文件格式,可包含其他字体,如 PostScript、 TrueType、OpenType、网络开放字体格式 (WOFF) 字体等。

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

演示

您可以在 演示。请务必查看 源代码。演示 展示了一个名为 <font-select> 的自定义元素,该元素 实现一个本地字体选择器。

隐私注意事项

"local-fonts" 权限似乎提供了一个高度可指纹识别的 surface。不过, 则可以随意返回所需的任何内容例如,注重匿名性的浏览器可能会选择 以便仅提供一组内置于浏览器中的默认字体。同样, 以提供与磁盘上完全相同的表数据。

Local Font Access API 会尽可能仅公开 实现上述用例所需的资源。系统 API 可能会生成不在 随机或按顺序排列,但要按字体安装的顺序排列。返回与 此类系统 API 提供的已安装字体可能会公开其他数据,这些数据可用于 数字“指纹”收集以及我们想要启用的用例无法通过保留此顺序来提供帮助。作为 结果,此 API 要求在返回之前对返回的数据进行排序。

安全与权限

Chrome 团队按照核心原则设计和实施了 Local Font Access API (如控制对强大的 Web 平台功能的访问权限中所述),包括 透明度和人体工学设计。

用户控制

用户对字体的访问权限完全在其控制之下,除非 "local-fonts" 权限,如 权限注册表)授予此权限。

透明度

如果网站已被授予用户本地字体的访问权限, 网站信息表

权限保留

"local-fonts" 权限在页面重新加载后将保持不变。可通过 网站信息工作表。

反馈

Chrome 团队希望了解您使用 Local Font Access API 的体验。

向我们介绍 API 设计

API 是否有什么无法按预期运行?或者是否缺少方法 需要哪些资源或属性来实现您的想法?对安全性有疑问或意见 模型?在相应的 GitHub 代码库中提交规范问题,或将您的想法添加到 现有问题。

报告实现存在的问题

您在 Chrome 的实现过程中是否发现了错误?或者,实现是否与规范不同? 在 new.crbug.com 上提交 bug。请务必提供尽可能多的细节信息 简单的重现说明,并在组件框中输入 Blink>Storage>FontAccessGlitch 非常适用于分享轻松快速的重现问题。

表示对 API 的支持

您打算使用 Local Font Access API 吗?您的公开支持可帮助 Chrome 团队 确定功能的优先级,并向其他浏览器供应商展示支持这些功能的重要性。

使用 # 标签向 @ChromiumDev 发送推文 #LocalFontAccess,然后让 我们都会知道您在何处使用它

致谢

Local Font Access API 规范已由以下人员修改: Emil A.EklundAlex RussellJoshua BellOlivier Yiptong。本文由以下人员审核: Joe Medley Dominik RöttschesOlivier Yiptong。主打图片提供方 Brett Jordan 发布 不启动