CSS 字体加载模块第 3 级

W3C 工作草案

关于此文档的更多细节
此版本
https://w3org.cn/TR/2023/WD-css-font-loading-3-20230406/
最新发布版本
https://w3org.cn/TR/css-font-loading/
编辑草案
https://drafts.csswg.org/css-font-loading/
历史版本
历史
https://w3org.cn/standards/history/css-font-loading-3
反馈
CSS 工作组问题仓库
文档内联
编辑
Tab Atkins Jr. (Google)
前任编辑
(Mozilla)
建议编辑此规范
GitHub 编辑器

摘要

本 CSS 模块描述了用于动态加载字体资源的事件和接口。

CSS 是一种用于描述结构化文档(如 HTML 和 XML)在屏幕、纸张等介质上渲染方式的语言。

关于本文档

本部分描述了本文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新版本可在 W3C 技术报告索引(https://w3org.cn/TR/)中找到。

本文件由 CSS 工作组推荐标准轨道 (Recommendation track) 下的 工作草案 (Working Draft) 形式发布。以工作草案形式发布并不代表 W3C 及其成员的认可。

这是一份草案文档,可能会随时被其他文档更新、替换或废弃。将其作为非进行中工作引用是不恰当的。

请通过 在 GitHub 上提交议题(推荐)发送反馈,并在标题中包含规范代码“css-font-loading”,例如:“[css-font-loading] ……评论摘要……”。所有议题和评论均已 存档。此外,也可以将反馈发送至(已 存档 的)公共邮件列表 www-style@w3.org

本文档受 2021 年 11 月 2 日版 W3C 流程文档管辖。

本文档由在 W3C 专利政策下运作的组织制作。W3C 维护一份与该组织交付成果相关的 公开专利披露列表;该页面还包含披露专利的说明。任何知悉其认为包含 必要权利要求 的专利的个人,必须按照 W3C 专利政策第 6 节披露该信息。

1. 简介

CSS 允许作者通过 @font-face 规则从网络加载自定义字体。虽然在编写样式表时这很容易使用,但通过脚本动态使用却困难得多。

此外,CSS 允许用户代理选择实际加载字体的时间;如果页面上目前没有任何内容使用该字体,大多数用户代理将不会下载其关联文件。这意味着随后使用该字体时,用户代理在最终察觉到使用并开始下载和解析字体文件时会产生延迟。

本规范定义了 CSS 中字体显示(font faces)的脚本接口,允许轻松创建字体显示(通过 FontFace 接口)并从脚本加载它们(通过 document.fonts)。它还提供了跟踪单个字体或整个页面所有字体加载状态的方法。

本规范中的几处内容使用普通的 ES 对象来定义行为,例如在内部使用 Promise,以及 FontFaceSet 内部使用 Set。我认为这里的意图是这些对象(及其原型链)是原始的,不受作者所做的任何操作的影响。这是一个好的意图吗?如果是,我应该如何在规范中指明这一点?

1.1.

本规范使用 Promise,它们定义在 ECMAScript 6 中。MDN 提供了一些 介绍 Promise 的优秀教程资料

1.2. 任务源

每当本规范将任务排入队列时,它都会将其排入“字体加载”(font loading)任务源。

2. FontFace 接口

FontFace 接口代表单个可用的字体显示。CSS @font-face 规则隐式定义了 FontFace 对象,或者也可以通过 URL 或二进制数据手动构造它们。

typedef (ArrayBuffer or ArrayBufferView) BinaryData;

dictionary FontFaceDescriptors {
  CSSOMString style = "normal";
  CSSOMString weight = "normal";
  CSSOMString stretch = "normal";
  CSSOMString unicodeRange = "U+0-10FFFF";
  CSSOMString variant = "normal";
  CSSOMString featureSettings = "normal";
  CSSOMString variationSettings = "normal";
  CSSOMString display = "auto";
  CSSOMString ascentOverride = "normal";
  CSSOMString descentOverride = "normal";
  CSSOMString lineGapOverride = "normal";
};

enum FontFaceLoadStatus { "unloaded", "loading", "loaded", "error" };

[Exposed=(Window,Worker)]
interface FontFace {
  constructor(CSSOMString family, (CSSOMString or BinaryData) source,
                optional FontFaceDescriptors descriptors = {});
  attribute CSSOMString family;
  attribute CSSOMString style;
  attribute CSSOMString weight;
  attribute CSSOMString stretch;
  attribute CSSOMString unicodeRange;
  attribute CSSOMString variant;
  attribute CSSOMString featureSettings;
  attribute CSSOMString variationSettings;
  attribute CSSOMString display;
  attribute CSSOMString ascentOverride;
  attribute CSSOMString descentOverride;
  attribute CSSOMString lineGapOverride;

  readonly attribute FontFaceLoadStatus status;

  Promise<FontFace> load();
  readonly attribute Promise<FontFace> loaded;
};

澄清所有提到的“文档”(the document),明确引用的是哪个文档,因为对象可以在文档之间移动。

family类型为 CSSOMString
style类型为 CSSOMString
weight类型为 CSSOMString
stretch类型为 CSSOMString
unicodeRange类型为 CSSOMString

这些属性都代表了字体显示的对应方面,如 CSS @font-face 规则中定义的描述符所定义。它们的解析方式与相应的 @font-face 描述符相同。它们被字体匹配算法使用,除此之外没有其他影响。

例如,FontFacestyle"italic",这代表一个斜体字体显示;它并不会该字体显示变为斜体。

获取时,返回与此属性关联的字符串。

设置时,根据相应 @font-face 描述符的语法 解析 字符串。如果与语法不匹配,则抛出 SyntaxError;否则,将该属性设置为解析值的序列化结果。

variant类型为 CSSOMString
featureSettings类型为 CSSOMString
variationSettings类型为 CSSOMString
display类型为 CSSOMString
ascentOverride类型为 CSSOMString
descentOverride类型为 CSSOMString
lineGapOverride类型为 CSSOMString

这些属性具有与 CSS @font-face 规则中相应描述符相同的含义,解析方式也相同。

它们会开启或关闭支持该特性的字体中的特定特性。与前面的属性不同,这些属性实际上会影响字体显示。

获取时,返回与此属性关联的字符串。

设置时,根据相应 @font-face 描述符的语法 解析 字符串。如果与语法不匹配,则抛出 SyntaxError;否则,将该属性设置为解析值的序列化结果。

status类型为 FontFaceLoadStatus,只读

此属性反映了字体显示的当前状态。对于新创建的 FontFace,它必须是 "unloaded"(未加载)。

由于作者明确请求加载字体显示(例如通过 FontFace 上的 load() 方法),或者由于用户代理检测到需要该字体来绘制屏幕上的某些文本,其状态可能会发生隐式改变。

loaded类型为 Promise<FontFace>,只读

此属性反映了字体显示的 [[FontStatusPromise]]

所有 FontFace 对象都包含一个内部 [[FontStatusPromise]] 插槽,用于跟踪字体的状态。它起初处于挂起状态,当字体被成功加载和解析,或发生错误时,它会履行(fulfilled)或拒绝(rejected)。

所有 FontFace 对象还包含内部 [[Urls]][[Data]] 插槽,其中一个为 null,另一个不为 null(非 null 的那个由构造函数根据传入的数据设置)。

2.1. 构造函数

FontFace 可以从指向字体文件的 URL 构建,或者从包含字体二进制表示的 ArrayBuffer(或 ArrayBufferView)构建。

当调用 FontFace(family, source, descriptors) 方法时,执行以下步骤

  1. font face 为一个新的 FontFace 对象。设置 font facestatus 属性为 "unloaded",设置其内部 [[FontStatusPromise]] 插槽为一个新的挂起 Promise 对象。

    根据 CSS @font-face 规则相应描述符的语法,解析 family 参数和 descriptors 参数的成员。如果 source 参数是 CSSOMString,则根据 @font-face 规则的 CSS src 描述符语法解析它。如果其中任何一个解析不正确,则用名为 "SyntaxError" 的 DOMException 拒绝 font face[[FontStatusPromise]],将 font face 的对应属性设置为空字符串,并将 font facestatus 属性设置为 "error"。否则,将 font face 的对应属性设置为解析值的序列化结果。

    注意: 注意这意味着将纯 URL 作为 source 参数传递(例如 "http://example.com/myFont.woff")是无效的——它至少需要包装在 url() 函数中,例如 "url(http://example.com/myFont.woff)"。作为这种不便的补偿,您可以指定多个回退方案、指定每个回退方案的字体类型,并轻松引用本地字体。

    需要定义基本 URL,以便解析相对 URL。它应该是文档的 URL 吗?这对 Worker 也是正确的吗,还是应该使用它们的 Worker URL?那总是定义的吗?

    返回 font face。如果 font facestatus 为 "error",则终止此算法;否则,异步完成其余步骤。

  2. 如果 source 参数是 CSSOMString,则将 font face 的内部 [[Urls]] 插槽设置为该字符串。

    如果 source 参数是 BinaryData,则将 font face 的内部 [[Data]] 插槽设置为传入的参数。

  3. 如果 font face[[Data]] 插槽不为 null,则将一个任务排入队列以同步运行以下步骤

    1. font facestatus 属性设置为 "loading"。

    2. 对于 font face 所在的每个 FontFaceSet

      1. 如果 FontFaceSet[[LoadingFonts]] 列表为空,则 将 FontFaceSet 切换为加载状态

      2. font face 追加到 FontFaceSet[[LoadingFonts]] 列表中。

    异步尝试将内部数据解析为字体。完成后(无论成功与否),将一个任务排入队列以同步运行以下步骤

    1. 如果加载成功,font face 现在代表已解析的字体;履行(fulfill)font face[[FontStatusPromise]] 并返回 font face,并将其 status 属性设置为 "loaded"。

      对于 font face 所在的每个 FontFaceSet

      1. font face 添加到 FontFaceSet[[LoadedFonts]] 列表中。

      2. FontFaceSet[[LoadingFonts]] 列表中删除 font face。如果 font 是该列表中的最后一项(因此列表现在为空),则 将 FontFaceSet 切换为已加载状态

    2. 否则,用名为 "SyntaxError" 的 DOMException 拒绝 font face[[FontStatusPromise]],并将 font facestatus 属性设置为 "error"。

      对于 font face 所在的每个 FontFaceSet

      1. font face 添加到 FontFaceSet[[FailedFonts]] 列表中。

      2. FontFaceSet[[LoadingFonts]] 列表中删除 font face。如果 font 是该列表中的最后一项(因此列表现在为空),则 将 FontFaceSet 切换为已加载状态

注意: 新构造的 FontFace 对象不会自动添加到与文档或 Worker 线程上下文关联的 FontFaceSet 中。这意味着虽然新构造的字体可以预加载,但它们必须显式添加到 FontFaceSet 之后才能实际使用。请参阅下一节以获取对 FontFaceSet 的更完整描述。

2.2. load() 方法

FontFaceload() 方法强制基于 URL 的字体显示请求其字体数据并进行加载。对于从二进制数据构建的字体,或者已经处于加载中或已加载状态的字体,它不执行任何操作。

当调用 load() 方法时,执行这些步骤

  1. font face 为调用此方法的 FontFace 对象。
  2. 如果 font face[[Urls]] 插槽为 null,或者其 status 属性不是 "unloaded",则返回 font face[[FontStatusPromise]] 并中止这些步骤。
  3. 否则,将 font facestatus 属性设置为 "loading",返回 font face[[FontStatusPromise]],并异步继续执行此算法的其余部分。
  4. 使用 font face[[Urls]] 插槽的值,尝试加载字体,如同该值是 @font-face 规则的 src 描述符的值一样,定义见 [CSS-FONTS-3]
  5. 当加载操作完成时(无论成功与否),将一个任务排入队列以同步运行以下步骤
    1. 如果加载尝试失败,用名为 "NetworkError" 的 DOMException 拒绝 font face[[FontStatusPromise]],并将 font facestatus 属性设置为 "error"。

      对于 font face 所在的每个 FontFaceSet

      1. font face 添加到 FontFaceSet[[FailedFonts]] 列表中。

      2. FontFaceSet[[LoadingFonts]] 列表中删除 font face。如果 font 是该列表中的最后一项(因此列表现在为空),则 将 FontFaceSet 切换为已加载状态

    2. 否则,font face 现在代表已加载的字体;履行(fulfill)font face[[FontStatusPromise]] 并返回 font face,并将其 status 属性设置为 "loaded"。

      对于 font face 所在的每个 FontFaceSet

      1. font face 添加到 FontFaceSet[[LoadedFonts]] 列表中。

      2. FontFaceSet[[LoadingFonts]] 列表中删除 font face。如果 font 是该列表中的最后一项(因此列表现在为空),则 将 FontFaceSet 切换为已加载状态

用户代理可以自行发起字体加载,只要它们确定某种给定的字体对于渲染页面上的某些内容是必要的。发生这种情况时,它们必须表现得就像调用了此处描述的相应 FontFaceload() 方法一样。

注意: 一些 UA 使用“字体缓存”,避免在页面上或同一源下的多个页面上多次下载同一个字体。多个 FontFace 对象可以映射到字体缓存中的同一个条目,这意味着即使 FontFace 对象不在 FontFaceSet 中,它也可能意外开始加载,因为指向同一字体数据的其他 FontFace 对象(可能在完全不同的页面上!)已经被加载了。

2.3. 与 CSS 的 @font-face 规则的交互

CSS @font-face 规则会自动定义相应的 FontFace 对象,该对象在规则被解析时会自动放置在文档的 字体源 中。此 FontFace 对象是 CSS 连接的

对应于 @font-face 规则的 FontFace 对象将其 familystyleweightstretchunicodeRangevariantfeatureSettings 属性设置为与 @font-face 规则中相应的描述符相同的值。两者之间存在双向连接:对 @font-face 描述符所做的任何更改都会立即反映在相应的 FontFace 属性中,反之亦然。

当 FontFace 在文档之间转移时,它就不再是 CSS 连接的了。

FontFace 对象的内部 [[Urls]] 插槽被设置为 @font-face 规则的 src 描述符的值,并反映对 src 描述符所做的任何更改。

否则,由 CSS @font-face 规则创建的 FontFace 对象与手动创建的对象相同。

如果从文档中删除了 @font-face 规则,其对应的 FontFace 对象将不再是 CSS 连接的。这种连接无法以任何方式恢复(但将 @font-face 加回到样式表中将创建一个全新的、 CSS 连接的 FontFace 对象)。

如果 @font-face 规则的 src 描述符更改为新值,则原始连接的 FontFace 对象必须停止成为 CSS 连接的。必须创建一个反映其新 src 的新 FontFace,并将其 CSS 连接到 @font-face。(这也将从它们出现的任何 字体源 中删除旧的 FontFace 对象并添加新的。)

2.4. 发现关于字体的信息

FontFace 对象包括关于字体文件内容的各种只读信息。

[Exposed=(Window,Worker)]
interface FontFaceFeatures {
  /* The CSSWG is still discussing what goes in here */
};

[Exposed=(Window,Worker)]
interface FontFaceVariationAxis {
  readonly attribute DOMString name;
  readonly attribute DOMString axisTag;
  readonly attribute double minimumValue;
  readonly attribute double maximumValue;
  readonly attribute double defaultValue;
};

[Exposed=(Window,Worker)]
interface FontFaceVariations {
  readonly setlike<FontFaceVariationAxis>;
};

[Exposed=(Window,Worker)]
interface FontFacePalette {
  iterable<DOMString>;
  readonly attribute unsigned long length;
  getter DOMString (unsigned long index);
  readonly attribute boolean usableWithLightBackground;
  readonly attribute boolean usableWithDarkBackground;
};

[Exposed=(Window,Worker)]
interface FontFacePalettes {
  iterable<FontFacePalette>;
  readonly attribute unsigned long length;
  getter FontFacePalette (unsigned long index);
};

partial interface FontFace {
  readonly attribute FontFaceFeatures features;
  readonly attribute FontFaceVariations variations;
  readonly attribute FontFacePalettes palettes;
};

注意: 此只读数据旨在帮助作者了解 font-feature-settingsfont-variation-settings@font-palette-values 接受哪些值。

3. FontFaceSet 接口

dictionary FontFaceSetLoadEventInit : EventInit {
  sequence<FontFace> fontfaces = [];
};

[Exposed=(Window,Worker)]
interface FontFaceSetLoadEvent : Event {
  constructor(CSSOMString type, optional FontFaceSetLoadEventInit eventInitDict = {});
  [SameObject] readonly attribute FrozenArray<FontFace> fontfaces;
};

enum FontFaceSetLoadStatus { "loading", "loaded" };

[Exposed=(Window,Worker)]
interface FontFaceSet : EventTarget {
  constructor(sequence<FontFace> initialFaces);

  setlike<FontFace>;
  FontFaceSet add(FontFace font);
  boolean delete(FontFace font);
  undefined clear();

  // events for when loading state changes
  attribute EventHandler onloading;
  attribute EventHandler onloadingdone;
  attribute EventHandler onloadingerror;

  // check and start loads if appropriate
  // and fulfill promise when all loads complete
  Promise<sequence<FontFace>> load(CSSOMString font, optional CSSOMString text = " ");

  // return whether all fonts in the fontlist are loaded
  // (does not initiate load if not available)
  boolean check(CSSOMString font, optional CSSOMString text = " ");

  // async notification that font loading and layout operations are done
  readonly attribute Promise<FontFaceSet> ready;

  // loading state, "loading" while one or more fonts loading, "loaded" otherwise
  readonly attribute FontFaceSetLoadStatus status;
};
ready类型为 Promise<FontFaceSet>,只读

此属性反映了 FontFaceSet[[ReadyPromise]] 插槽。

有关此 Promise 及其使用的更多详细信息,请参阅 § 3.4 ready 属性

FontFaceSet(initialFaces)

FontFaceSet 构造函数在被调用时,必须迭代其 initialFaces 参数,并将每个值添加到其 设置条目(set entries)中。

迭代顺序

迭代时,所有 CSS 连接的 FontFace 对象必须排在前面,按其连接的 @font-face 规则的文档顺序排列,随后是非 CSS 连接的 FontFace 对象,按插入顺序排列。

set entries (设置条目)

如果 FontFaceSet 是一个 字体源,则其 设置条目§ 4.2 与 CSS @font-face 规则的交互 中指定的那样进行初始化。

否则,其 设置条目 最初为空。

add(font)

当调用 add() 方法时,执行以下步骤

  1. 如果 font 已经在 FontFaceSet设置条目 中,则立即跳到本算法的最后一步。

  2. 如果 fontCSS 连接的,则抛出 InvalidModificationError 异常并立即退出本算法。

  3. font 参数添加到 FontFaceSet设置条目 中。

  4. 如果 fontstatus 属性为 "loading"

    1. 如果 FontFaceSet[[LoadingFonts]] 列表为空,则 将 FontFaceSet 切换为加载状态

    2. font 追加到 FontFaceSet[[LoadingFonts]] 列表中。

  5. 返回 FontFaceSet

delete(font)

当调用 delete() 方法时,执行以下步骤

  1. 如果 fontCSS 连接的,则返回 false 并立即退出本算法。

  2. deleted 为从 FontFaceSet设置条目 中删除 font 的结果。

  3. 如果 font 存在于 FontFaceSet[[LoadedFonts]][[FailedFonts]] 列表中,则删除它。

  4. 如果 font 存在于 FontFaceSet[[LoadingFonts]] 列表中,则删除它。如果 font 是该列表中的最后一项(因此列表现在为空),则 将 FontFaceSet 切换为已加载状态

  5. 返回 deleted

clear()

当调用 clear() 方法时,执行以下步骤

  1. FontFaceSet设置条目、其 [[LoadedFonts]] 列表及其 [[FailedFonts]] 列表中删除所有非 CSS 连接的 项目。

  2. 如果 FontFaceSet[[LoadingFonts]] 列表非空,则从中删除所有项目,然后 将 FontFaceSet 切换为已加载状态

FontFaceSet 对象还具有内部 [[LoadingFonts]][[LoadedFonts]][[FailedFonts]] 插槽,它们都初始化为空列表,以及一个 [[ReadyPromise]] 插槽,初始化为一个新的挂起 Promise

因为字体系列仅在使用时才会加载,所以内容有时需要了解字体何时发生加载。作者可以使用此处定义的事件和方法,对依赖于特定字体可用性的操作进行更细致的控制。

如果满足以下任一条件,FontFaceSet 即为 挂起于环境(pending on the environment)

注意: 其构想是,一旦 FontFaceSet 不再 挂起于环境,只要没有进一步的因素改变文档,作者在进行测量时就可以依赖事物的大小/位置是“正确”的。如果上述条件未能完全涵盖此保证,则需要对其进行修正。

3.1. 事件

字体加载事件使得响应整个文档的字体加载行为变得容易,而无需专门监听每个字体。loading 事件在文档开始加载字体时触发,而 loadingdoneloadingerror 事件在文档完成字体加载时触发,分别包含成功加载或未能加载的字体。

以下是 `FontFaceSet` 对象作为 IDL 属性必须支持的事件处理程序(及其对应的事件处理程序事件类型)

事件处理器事件处理器事件类型
onloading loading
onloadingdone loadingdone
onloadingerror loadingerror

要在 FontFaceSet target触发字体加载事件(名为 e,带有可选的 font faces)意味着使用 FontFaceSetLoadEvent 接口 触发一个名为 e 的简单事件,该接口也满足以下条件

  1. fontfaces 属性初始化为过滤 font faces 的结果,使其仅包含 target 中包含的 FontFace 对象。

当被要求针对给定的 FontFaceSet 将 FontFaceSet 切换为加载状态 时,用户代理必须运行以下步骤

  1. font face set 为给定的 FontFaceSet
  2. font face setstatus 属性设置为 "loading"。
  3. 如果 font face set[[ReadyPromise]] 插槽当前持有一个已履行(fulfilled)的 Promise,则用一个新的挂起 Promise 替换它。
  4. 将一个任务排入队列,以在 font face set触发字体加载事件(名为 loading)。

当被要求针对给定的 FontFaceSet 将 FontFaceSet 切换为已加载状态 时,用户代理必须运行以下步骤

  1. font face set 为给定的 FontFaceSet

  2. 如果 font face set 挂起于环境,则将其标记为 卡在环境上(stuck on the environment),并退出本算法。

  3. font face setstatus 属性设置为 "loaded"。

  4. 履行(fulfill)font face set[[ReadyPromise]] 属性的值,并返回 font face set

  5. 将一个任务排入队列以同步执行以下步骤

    1. loaded fontsfont face set[[LoadedFonts]] 插槽的内容(可能为空)。

    2. failed fontsfont face set[[FailedFonts]] 插槽的内容(可能为空)。

    3. [[LoadedFonts]][[FailedFonts]] 插槽重置为空列表。

    4. font face set触发字体加载事件(名为 loadingdone,带有 loaded fonts)。

    5. 如果 font face setfailed fonts 非空,则在 font face set触发字体加载事件(名为 loadingerror,带有 failed fonts)。

每当 FontFaceSet挂起于环境 变为 不 挂起于环境 时,用户代理必须运行以下步骤

  1. 如果 FontFaceSet卡在环境上 且其 [[LoadingFonts]] 列表为空,则 将 FontFaceSet 切换为已加载状态

  2. 如果 FontFaceSet卡在环境上,则取消该标记。

若被要求从 FontFaceSet source 查找匹配的字体显示,针对给定的字体字符串 font(可选包含样本文本 text,以及可选的 allow system fonts 标志),运行以下步骤

  1. 使用 font 属性的 CSS 值语法解析 font。如果发生语法错误,则返回语法错误。如果解析出的值是 CSS 宽关键词,则返回语法错误。

    根据相应属性的初始值绝对化所有相对长度。(例如,像 bolder 这样的相对字体粗细是根据初始值 normal 进行评估的。)

  2. 如果未显式提供 text,则令其为包含单个空格字符(U+0020 SPACE)的字符串。
  3. font family list 为从 font 解析出的字体系列列表,font style 为从 font 解析出的其他字体样式属性。
  4. available font facessource 内的 可用字体显示。如果指定了 allow system fonts 标志,则将所有系统字体添加到 available font faces 中。
  5. matched font faces 最初为空列表。
  6. 对于 font family list 中的每个系列,使用字体匹配规则从 available font faces 中选择匹配 font style 的字体显示,并将它们添加到 matched font faces 中。unicodeRange 属性的使用意味着这可能不仅仅是单个字体显示。
  7. 如果 matched font faces 为空,则将 found faces 标志设置为 false。否则,将其设置为 true。
  8. 对于 matched font faces 中的每个字体显示,如果其定义的 unicode-range 不包含 text 中至少一个字符的码点,则将其从列表中删除。注意: 因此,如果 text 为空字符串,每个字体都将被删除。
  9. 返回 matched font facesfound faces 标志。

3.2. load() 方法

FontFaceSetload() 方法将确定给定字体列表中的所有字体是否已加载并可用。如果任何字体是可下载字体且尚未加载,用户代理将发起对这些字体的加载。它返回一个 Promise,当所有字体加载并准备好使用时履行,如果任何字体未能正确加载则拒绝。

当调用 load( fonttext ) 方法时,执行这些步骤

  1. font face set 为调用此方法的 FontFaceSet 对象。令 promise 为一个新创建的 Promise 对象。
  2. 返回 promise。异步完成这些步骤的其余部分。
  3. 使用传递给该函数的 fonttext 参数从 font face set 查找匹配的字体显示,令 font face list 为返回值(忽略 found faces 标志)。如果返回语法错误,则用 SyntaxError 异常拒绝 promise 并终止这些步骤。
  4. 将一个任务排入队列以同步运行以下步骤
  5. 对于 font face list 中的所有字体显示,调用它们的 load() 方法。
  6. 以等待 font face list 中每个字体显示的 [[FontStatusPromise]] 按顺序完成的结果来解析 promise

3.3. check() 方法

FontFaceSetcheck() 方法将确定您是否可以“安全地”用特定的字体列表渲染提供的某些文本,从而确保它以后不会导致“字体交换”(font swap)。如果给定的文本/字体组合在渲染时不会尝试使用任何未加载或正在加载的字体,则此方法返回 true;否则返回 false。

应注意此方法行为中的两个特殊情况,因为它们并不显而易见
  • 如果指定的字体存在,但由于其 unicode-range 不覆盖所提供的文本而排除了所有可能的显示,该方法返回 true,因为文本将使用 UA 的回退字体进行渲染,而不会触发任何字体加载。

  • 同样,如果指定的字体都不存在(例如名称拼写错误),该方法也返回 true,因为使用此字体列表不会触发任何加载;相反,会发生回退。

  • 当调用 check( fonttext) 方法时,执行这些步骤

    1. font face set 为调用此方法的 FontFaceSet 对象。
    2. font face set 查找匹配的字体显示,使用传递给该函数的 fonttext 参数,并包含系统字体;令 font face list 为返回的字体显示列表,found faces 为返回的 found faces 标志。如果返回语法错误,则抛出 SyntaxError 异常并终止这些步骤。
    3. 如果 font face list 为空,或者 font face list 中的所有字体要么具有 "loaded" 的 status 属性,要么是系统字体,则返回 true。否则返回 false

    3.4. ready 属性

    因为加载的字体数量取决于为给定文本片段使用了多少字体,在某些情况下,可能不知道是否需要加载字体。ready 属性包含一个 Promise,它在文档完成加载字体时解析,这为作者提供了一种方法,无需跟踪哪些字体已加载或未加载,即可检查可能受字体加载影响的内容。

    注意: 作者应注意,给定的 ready promise 只会履行一次,但在它履行后可能会加载更多字体。这类似于监听 loadingdone 事件的触发,但传递给 ready promise 的回调将始终被调用,即使因为相关字体已加载而未发生字体加载时也是如此。这是一种简单、轻松的方法来同步代码与字体加载,而无需跟踪需要哪些字体以及它们具体何时加载。

    注意: 注意用户代理可能需要在 ready promise 履行之前迭代多次字体加载。这种情况可能发生在字体回退的情况下,即字体列表中的一个字体已加载但不包含特定字形,而字体列表中的其他字体需要被加载。只有在布局操作完成且无需额外字体加载后,ready promise 才会履行。

    注意: 注意此 ready 属性返回的 Promise 只会被履行,永远不会被拒绝,这与 FontFace load() 方法返回的 Promise 不同。

    3.5. 与 CSS 字体加载和匹配的交互

    当用户代理自动运行 [CSS-FONTS-3] 中的字体匹配算法时,它匹配的字体显示集合必须精确地是文档的 字体源 中的字体集合,加上任何本地字体显示。

    当用户代理需要加载字体显示时,它必须通过调用相应 FontFace 对象的 load() 方法来实现。

    (这意味着它必须运行相同的算法,而不是字面上调用对象 `load` 属性中当前存储的值。)

    字体在添加到 FontFaceSet 时可用。向样式表添加新的 @font-face 规则也会将新的 FontFace 添加到 Document 对象的 FontFaceSet 中。添加新的 @font-face 规则
    document.styleSheets[0].insertRule(
      "@font-face { font-family: newfont; src: url(newfont.woff); }", 0);
    document.body.style.fontFamily = "newfont, serif";
    

    构造一个新的 FontFace 对象并将其添加到 document.fonts

    var f = new FontFace("newfont", "url(newfont.woff)");
    document.fonts.add(f);
    document.body.style.fontFamily = "newfont, serif";
    

    在这两种情况下,字体资源“newfont.woff”的加载都将由布局引擎发起,就像加载其他 @font-face 规则字体一样。

    忽略添加到 document.fonts 意味着字体将永远不会被加载,文本将以默认 serif 字体显示

    var f = new FontFace("newfont", "url(newtest.woff)", {});
    
    /* new {{FontFace}} not added to {{FontFaceSet}},
       so the 'font-family' property can’t see it,
       and serif will be used instead */
    document.body.style.fontFamily = "newfont, serif";
    

    为了在使用前显式预加载字体,作者可以将新 FontFace 添加到 FontFaceSet 的操作推迟到加载完成之后

    var f = new FontFace("newfont", "url(newfont.woff)", {});
    f.load().then(function (loadedFace) {
      document.fonts.add(loadedFace);
      document.body.style.fontFamily = "newfont, serif";
    });
    

    在这种情况下,首先下载字体资源“newfont.woff”。一旦下载完成,字体就会被添加到文档的 FontFaceSet 中,正文字体被更改,布局引擎使用新的字体资源。

    4. FontFaceSource 混合接口

    interface mixin FontFaceSource {
      readonly attribute FontFaceSet fonts;
    };
    
    Document includes FontFaceSource;
    WorkerGlobalScope includes FontFaceSource;
    

    任何可以以某种方式使用字体的文档、Worker 或其他上下文都必须包含 FontFaceSource 混合接口。上下文的 fonts 属性的值就是它的 字体源,除非另有定义,否则它提供了字体相关操作中使用的所有字体。引用“字体源”的操作必须被解释为引用正在进行操作的相关上下文的 字体源

    对于在这些上下文之一内进行的任何字体相关操作,字体源 中的 FontFace 对象就是其 可用字体显示

    4.1. Worker FontFaceSources

    在 Worker 文档中,字体源 最初为空。

    注意: FontFace 对象可以按正常方式构建并添加到其中,这会影响 Worker 内的 CSS 字体匹配(例如,在将文本绘制到 OffscreenCanvas 时)。

    4.2. 与 CSS 的 @font-face 规则交互

    文档的 字体源集合条目 必须最初填充来自 文档或影子根 CSS 样式表 中所有 CSS @font-face 规则的所有 CSS 连接的 FontFace 对象,按文档顺序排列。当 @font-face 规则从样式表中添加或删除,或者包含 @font-face 规则的样式表被添加或删除时,相应的 CSS 连接的 FontFace 对象必须从文档的 字体源 中添加或删除,并保持此顺序。

    任何手动添加的 FontFace 对象必须排序在 CSS 连接的 对象之后

    当使用 CSS 连接的 FontFace 对象调用 FontFaceSet 对象的 add() 方法时,如果该对象已在集合中,则该操作必须为空操作;否则,该操作必须不执行任何操作,并抛出 InvalidModificationError

    当使用 CSS 连接的 FontFace 对象调用 FontFaceSet 对象的 delete() 方法时,该操作必须为空操作,并返回 false

    注意:即使 FontFace 已被自动从 字体源 中移除,作者仍然可以保留对它的引用。然而,正如 § 2.3 与 CSS 的 @font-face 规则交互 中所指出的,此时该 FontFace 不再是 CSS 连接的

    注意:预期本规范的未来版本也将定义与本地字体交互和查询的方法。

    5. API 示例

    仅在所有字体加载完成后显示内容
    document.fonts.ready.then(function() {
      var content = document.getElementById("content");
      content.style.visibility = "visible";
    });
    
    在 canvas 中使用可下载字体绘制文本,明确启动字体下载并在完成后绘制
    function drawStuff() {
      var ctx = document.getElementById("c").getContext("2d");
    
      ctx.fillStyle = "red";
      ctx.font = "50px MyDownloadableFont";
      ctx.fillText("Hello!", 100, 100);
    }
    
    document.fonts.load("50px MyDownloadableFont")
                  .then(drawStuff, handleError);
    
    富文本编辑应用程序可能需要在执行编辑操作后测量文本元素。由于样式更改可能会或可能不会要求下载额外的字体,或者字体可能已经下载,因此测量程序需要在这些字体加载完成后进行
    function measureTextElements() {
      // contents can now be measured using the metrics of
      // the downloadable font(s)
    }
    
    function doEditing() {
      // content/layout operations that may cause additional font loads
      document.fonts.ready.then(measureTextElements);
    }
    
    loadingdone 事件仅在所有字体相关的加载完成文本排版完成而未导致额外字体加载后才会触发
    <style>
    @font-face {
      font-family: latin-serif;
      src: url(latinserif.woff) format("woff"); /* contains no kanji/kana */
    }
    @font-face {
      font-family: jpn-mincho;
      src: url(mincho.woff) format("woff");
    }
    @font-face {
      font-family: unused;
      src: url(unused.woff);
    }
    
    body { font-family: latin-serif, jpn-mincho; }
    </style>
    <p>納豆はいかがでしょうか
    

    在这种情况下,用户代理首先下载“latinserif.woff”,然后尝试使用它来绘制日语文本。但由于该字体中不存在日语字形,因此会发生回退,并下载字体“mincho.woff”。仅在第二种字体下载完成且日语文本排版完成后,才会触发 loadingdone 事件。

    “未使用的”字体不会被加载,因为没有文本正在使用它,所以 UA 甚至不会尝试加载它。它不会干扰 loadingdone 事件。

    变更

    2014 年 5 月 CSS 字体加载最后征求意见工作草案 以来的变更

    致谢

    Google Fonts 团队的几位成员提供了有关字体加载事件的有益反馈,Boris Zbarsky、Jonas Sicking 和 ms2ger 也是如此。

    隐私性考虑

    FontFaceSet 对象会泄露有关用户已安装字体的信息,但方式与现有的 @font-face 规则完全相同;没有泄露新的信息,也没有以任何明显更容易的方式进行泄露。

    安全性考虑

    针对本规范未提出任何安全方面的考虑。

    一致性

    文档约定

    一致性要求通过描述性断言和 RFC 2119 术语相结合来表达。本文档规范性部分中的关键词“MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(必需)、“SHALL”(应)、“SHALL NOT”(不应)、“SHOULD”(推荐)、“SHOULD NOT”(不推荐)、“RECOMMENDED”(建议)、“MAY”(可以)和“OPTIONAL”(可选)应按照 RFC 2119 中的描述进行解释。然而,为了可读性,这些词在本文档中不以全大写形式出现。

    本规范的所有文本均为规范性文本,明确标记为非规范性的部分、示例和注释除外。 [RFC2119]

    本规范中的示例均以“例如”一词引入,或者通过 class="example" 与规范性文本隔开,如下所示

    这是一个说明性示例。

    说明性注释以“Note”一词开头,并使用 class="note" 与规范性文本隔开,如下所示

    注意,这是一个说明性注释。

    建议(Advisements)是规范性章节,旨在引起特别注意,并使用 <strong class="advisement"> 与其他规范性文本区分开来,如下所示: 用户代理必须提供可访问的替代方案。

    一致性类别

    本规范为三类一致性定义了一致性要求。

    样式表
    一份 CSS 样式表
    渲染器
    一种 用户代理 (UA),它解释样式表的语义并渲染使用它们的文档。
    创作工具
    一种 用户代理 (UA),用于编写样式表。

    如果样式表包含的所有使用本模块定义语法的语句,根据通用 CSS 语法及本模块定义的各功能语法均有效,则该样式表符合本规范。

    如果渲染器除了按相应规范解释样式表外,还通过正确解析本规范定义的所有功能并相应地渲染文档来支持这些功能,则该渲染器符合本规范。然而,由于设备限制导致 UA 无法正确渲染文档,并不意味着该 UA 不符合规范。(例如,UA 无需在单色显示器上渲染颜色。)

    如果创作工具编写的样式表根据通用 CSS 语法及本模块中各功能的语法是句法正确的,并符合本模块中描述的所有其他样式表一致性要求,则该创作工具符合本规范。

    部分实现

    为了使作者能够利用前向兼容的解析规则来指定后备值,CSS 渲染器 **必须** 将其无法使用支持级别的任何 @规则、属性、属性值、关键字和其他语法结构视为无效(并 适当忽略)。特别地,用户代理 **不得** 在单一多值属性声明中选择性地忽略不支持的组件值而保留支持的值:如果任何值被视为无效(因为不支持的值必须如此),CSS 要求忽略整个声明。

    不稳定和专有特性的实现

    为了避免与未来稳定的 CSS 功能发生冲突,CSS 工作组建议在实施不稳定功能和私有扩展遵循最佳实践

    非实验性实现

    一旦规范达到候选推荐阶段,非实验性实现即可成为可能,实现者应发布他们能够证明根据规范正确实现的任何 CR 级别特性的无前缀实现。

    为建立并保持 CSS 在不同实现间的互操作性,CSS 工作组请求非实验性的 CSS 渲染器在发布任何 CSS 功能的无前缀实现之前,向 W3C 提交一份实现报告(并在必要时提交用于该实现报告的测试用例)。提交给 W3C 的测试用例需经 CSS 工作组审阅和修正。

    有关提交测试用例和实现报告的详细信息,请访问 CSS 工作组网站 https://w3org.cn/Style/CSS/Test/。问题可发送至 public-css-testsuite@w3.org 邮件列表。

    索引

    本规范定义的术语

    通过引用定义的术语

    引用

    规范性引用

    [CSS-FONT-LOADING-3]
    Tab Atkins Jr.. CSS 字体加载模块第 3 级. 2014年5月22日. WD. URL: https://w3org.cn/TR/css-font-loading-3/
    [CSS-FONTS-3]
    John Daggett; Myles Maxfield; Chris Lilley. CSS 字体模块 Level 3. 2018年9月20日. REC. URL: https://w3org.cn/TR/css-fonts-3/
    [CSS-FONTS-4]
    John Daggett; Myles Maxfield; Chris Lilley. CSS 字体模块第 4 级. 2021 年 12 月 21 日. WD. URL: https://w3org.cn/TR/css-fonts-4/
    [CSS-FONTS-5]
    Myles Maxfield; Chris Lilley. CSS 字体模块 Level 5. 2021 年 12 月 21 日. WD. URL: https://w3org.cn/TR/css-fonts-5/
    [CSS-SYNTAX-3]
    Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021年12月24日. CR. URL: https://w3org.cn/TR/css-syntax-3/
    [CSS-VALUES-4]
    Tab Atkins Jr.; Elika Etemad. CSS 值与单位模块第 4 级. 2022年10月19日. WD. URL: https://w3org.cn/TR/css-values-4/
    [CSSOM-1]
    Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021年8月26日. WD. URL: https://w3org.cn/TR/cssom-1/
    [DOM]
    Anne van Kesteren. DOM 标准. Living Standard. URL: https://dom.spec.whatwg.org/
    [HTML]
    Anne van Kesteren; et al. HTML 标准. Living Standard. URL: https://html.whatwg.cn/multipage/
    [RFC2119]
    S. Bradner. RFC 中用于指示要求级别的关键词. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
    [WEBIDL]
    Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. URL: https://webidl.spec.whatwg.org/

    IDL 索引

    typedef (ArrayBuffer or ArrayBufferView) BinaryData;
    
    dictionary FontFaceDescriptors {
      CSSOMString style = "normal";
      CSSOMString weight = "normal";
      CSSOMString stretch = "normal";
      CSSOMString unicodeRange = "U+0-10FFFF";
      CSSOMString variant = "normal";
      CSSOMString featureSettings = "normal";
      CSSOMString variationSettings = "normal";
      CSSOMString display = "auto";
      CSSOMString ascentOverride = "normal";
      CSSOMString descentOverride = "normal";
      CSSOMString lineGapOverride = "normal";
    };
    
    enum FontFaceLoadStatus { "unloaded", "loading", "loaded", "error" };
    
    [Exposed=(Window,Worker)]
    interface FontFace {
      constructor(CSSOMString family, (CSSOMString or BinaryData) source,
                    optional FontFaceDescriptors descriptors = {});
      attribute CSSOMString family;
      attribute CSSOMString style;
      attribute CSSOMString weight;
      attribute CSSOMString stretch;
      attribute CSSOMString unicodeRange;
      attribute CSSOMString variant;
      attribute CSSOMString featureSettings;
      attribute CSSOMString variationSettings;
      attribute CSSOMString display;
      attribute CSSOMString ascentOverride;
      attribute CSSOMString descentOverride;
      attribute CSSOMString lineGapOverride;
    
      readonly attribute FontFaceLoadStatus status;
    
      Promise<FontFace> load();
      readonly attribute Promise<FontFace> loaded;
    };
    
    [Exposed=(Window,Worker)]
    interface FontFaceFeatures {
      /* The CSSWG is still discussing what goes in here */
    };
    
    [Exposed=(Window,Worker)]
    interface FontFaceVariationAxis {
      readonly attribute DOMString name;
      readonly attribute DOMString axisTag;
      readonly attribute double minimumValue;
      readonly attribute double maximumValue;
      readonly attribute double defaultValue;
    };
    
    [Exposed=(Window,Worker)]
    interface FontFaceVariations {
      readonly setlike<FontFaceVariationAxis>;
    };
    
    [Exposed=(Window,Worker)]
    interface FontFacePalette {
      iterable<DOMString>;
      readonly attribute unsigned long length;
      getter DOMString (unsigned long index);
      readonly attribute boolean usableWithLightBackground;
      readonly attribute boolean usableWithDarkBackground;
    };
    
    [Exposed=(Window,Worker)]
    interface FontFacePalettes {
      iterable<FontFacePalette>;
      readonly attribute unsigned long length;
      getter FontFacePalette (unsigned long index);
    };
    
    partial interface FontFace {
      readonly attribute FontFaceFeatures features;
      readonly attribute FontFaceVariations variations;
      readonly attribute FontFacePalettes palettes;
    };
    
    dictionary FontFaceSetLoadEventInit : EventInit {
      sequence<FontFace> fontfaces = [];
    };
    
    [Exposed=(Window,Worker)]
    interface FontFaceSetLoadEvent : Event {
      constructor(CSSOMString type, optional FontFaceSetLoadEventInit eventInitDict = {});
      [SameObject] readonly attribute FrozenArray<FontFace> fontfaces;
    };
    
    enum FontFaceSetLoadStatus { "loading", "loaded" };
    
    [Exposed=(Window,Worker)]
    interface FontFaceSet : EventTarget {
      constructor(sequence<FontFace> initialFaces);
    
      setlike<FontFace>;
      FontFaceSet add(FontFace font);
      boolean delete(FontFace font);
      undefined clear();
    
      // events for when loading state changes
      attribute EventHandler onloading;
      attribute EventHandler onloadingdone;
      attribute EventHandler onloadingerror;
    
      // check and start loads if appropriate
      // and fulfill promise when all loads complete
      Promise<sequence<FontFace>> load(CSSOMString font, optional CSSOMString text = " ");
    
      // return whether all fonts in the fontlist are loaded
      // (does not initiate load if not available)
      boolean check(CSSOMString font, optional CSSOMString text = " ");
    
      // async notification that font loading and layout operations are done
      readonly attribute Promise<FontFaceSet> ready;
    
      // loading state, "loading" while one or more fonts loading, "loaded" otherwise
      readonly attribute FontFaceSetLoadStatus status;
    };
    
    interface mixin FontFaceSource {
      readonly attribute FontFaceSet fonts;
    };
    
    Document includes FontFaceSource;
    WorkerGlobalScope includes FontFaceSource;
    
    

    问题索引

    本规范中的一些内容使用普通的 ES 对象来定义行为,例如内部使用 Promises 的各种事物,以及内部使用 Set 的 FontFaceSet。我相信这里的意图是这些对象(及其原型链)是原始的,不受作者所做任何事情的影响。这是一个好的意图吗?如果是,我应该如何在规范中指明这一点?
    澄清所有对“文档”的提及,以清楚地说明引用的是哪个文档,因为对象可以在文档之间移动。
    需要定义基础 URL,以便解析相对 URL。应该是文档的 URL 吗?这对 worker 来说正确吗,还是应该使用它们的 worker URL?这总是定义好的吗?
    当 FontFace 在文档之间传输时,它不再是 CSS 连接的。