CSS 字体加载模块第 3 级

W3C 工作草案,

更多关于此文档的详细信息
此版本:
https://www.w3.org/TR/2023/WD-css-font-loading-3-20230406/
最新发布版本:
https://www.w3.org/TR/css-font-loading/
编辑草案:
https://drafts.csswg.org/css-font-loading/
以往版本:
历史记录:
https://www.w3.org/standards/history/css-font-loading-3
反馈:
CSSWG 问题 仓库
规范中的内嵌问题
编辑:
Tab Atkins Jr. (Google)
前编辑:
(Mozilla)
建议修改此规范:
GitHub 编辑器

摘要

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

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

本文档的状态

本节描述了本文档在出版时的状态。 当前 W3C 发布物的列表 和此技术报告的最新修订版 可在 W3C 技术报告索引 https://www.w3.org/TR/ 中找到。

本文档由 CSS 工作组 作为工作草案发布, 使用推荐标准流程。 作为工作草案发布并不意味着获得 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 中的字体, 允许通过 FontFace 接口轻松创建字体, 并通过脚本加载(通过 document.fonts)。 它还提供了方法来跟踪单个字体的加载状态, 或整个页面上所有字体的加载状态。

该规范中的几项使用了普通的 ES 对象来定义行为, 例如内部使用 Promises 的各种操作, 以及 FontFaceSet 内部使用 Set。 我认为此处的意图是这些对象 (及其原型链)应当是原生的, 不受作者操作的影响。 这是个好意图吗? 如果是,我该如何在规范中表示这一点?

1.1.

本规范使用 Promise, 这些在 ECMAScript 6 中定义。 MDN 提供了一些 关于 Promises 的不错的教程材料

1.2. 任务源

每当本规范排队一个任务时, 它会将任务排到“字体加载”任务源上。

2. FontFace 接口

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

typedef (ArrayBuffer  ArrayBufferView) BinaryDatadictionary 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  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;
};

需要明确所有提及“文档”的内容,以清楚指代的是哪个文档, 因为对象可能会在文档之间移动。

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

这些属性表示字体的相应方面, 正如在 CSS @font-face 规则中定义的描述符一样。 它们的解析与对应的 @font-face 描述符相同。 它们用于字体匹配算法中, 但除此之外没有任何影响。

例如,具有 FontFacestyle 值为 "italic" 的对象 表示 一个斜体字体; 它并不会使 该字体变为斜体。

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

在设置时,根据 CSS 语法解析字符串, 如果不匹配语法, 则抛出 SyntaxError 异常; 否则,将该属性设置为解析值的序列化结果。

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

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

它们在支持这些功能的字体中打开或关闭特定的功能。 与之前的属性不同, 这些属性实际上会影响字体本身。

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

在设置时,根据 CSS 语法解析字符串, 如果不匹配语法, 则抛出 SyntaxError 异常; 否则,将该属性设置为解析值的序列化结果。

status, 类型为 FontFaceLoadStatus, 只读

此属性反映字体的当前状态。 对于新创建的 FontFace, 它必须是 "unloaded"。

它可以通过作者显式请求加载字体(例如通过 load() 方法)或通过用户代理的隐式操作(例如用户代理检测到需要该字体来绘制一些文本)而更改。

loaded, 类型为 Promise<FontFace>, 只读

此属性反映字体的 [[FontStatusPromise]]

所有 FontFace 对象都包含一个内部的 [[FontStatusPromise]] 槽, 用于跟踪字体的状态。 它最初为 pending, 当字体成功加载并解析,或遇到错误时,它会完成或被拒绝。

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

2.1. 构造器

FontFace 可以通过指向字体文件的 URL 或包含字体二进制表示的 ArrayBuffer (或 ArrayBufferView) 来构造。

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

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

    解析 family 参数, 以及 descriptors 参数的成员, 根据 CSS @font-face 规则的相应描述符的语法解析它们。 如果 source 参数是 CSSOMString, 则根据 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 faceFontFaceSet

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

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

    异步尝试将其数据解析为字体。 完成后, 成功与否, 将任务排队以同步执行以下步骤:

    1. 如果加载成功,font face 现在表示已解析的字体; 使用 font face 完成 font face[[FontStatusPromise]], 并将其 status 属性设置为 "loaded"。

      对于每个包含 font faceFontFaceSet

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

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

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

      对于每个包含 font faceFontFaceSet

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

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

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

2.2. load() 方法

load() 方法会强制基于 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]] 槽的值, 按照 [CSS-FONTS-3] 中定义的方式尝试加载字体, 就像它是 @font-face 规则的 src 描述符的值一样。
  5. 当加载操作完成时, 无论成功与否, 将任务排队以同步执行以下步骤:
    1. 如果加载失败, 使用名为 "NetworkError" 的 DOMException 拒绝 font face[[FontStatusPromise]] 并将 font facestatus 属性设置为 "error"。

      对于每个包含 font faceFontFaceSet

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

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

    2. 否则,font face 现在表示已加载的字体; 使用 font face 完成 font face[[FontStatusPromise]], 并将 font facestatus 属性设置为 "loaded"。

      对于每个包含 font faceFontFaceSet

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

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

用户代理可以自行启动字体加载, 只要它们确定某个字体是呈现页面上某些内容所必需的。 当这种情况发生时, 它们必须表现得好像它们调用了相应的 FontFaceload() 方法,如本文所述。

注意: 某些用户代理使用 "字体缓存", 以避免在同一页面或同一来源的多个页面上多次下载相同的字体。 多个 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 连接的。

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

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

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

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

2.4. 字体信息的发现

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

[Exposed=(Window,Worker)]
interface FontFaceFeatures {
  /* CSS 工作组仍在讨论这里的内容 */
};

[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();

  // 加载状态更改的事件
  attribute EventHandler onloading;
  attribute EventHandler onloadingdone;
  attribute EventHandler onloadingerror;

  // 检查并启动加载(如果合适)
  // 并在所有加载完成时实现 promise
  Promise<sequence<FontFace>> load(CSSOMString font, optional CSSOMString text = " ");

  // 返回字体列表中的所有字体是否已加载
  // (如果不可用则不启动加载)
  boolean check(CSSOMString font, optional CSSOMString text = " ");

  // 字体加载和布局操作完成时的异步通知
  readonly attribute Promise<FontFaceSet> ready;

  // 加载状态,“loading”表示一个或多个字体正在加载,否则为“loaded”
  readonly attribute FontFaceSetLoadStatus status;
};
ready, 类型为 Promise<FontFaceSet>, 只读

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

参见 § 3.4 ready 属性 了解此 Promise 的更多详细信息及其使用。

FontFaceSet(initialFaces)

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

迭代顺序

在迭代时, 所有 CSS 连接的 FontFace 对象必须首先出现, 以其连接的 @font-face 规则的文档顺序, 后面跟着未 CSS 连接的 FontFace 对象, 以插入顺序排列。

set entries

如果 FontFaceSet字体来源, 则其 set entries 会按照在 § 4.2 与 CSS 的 @font-face 规则交互 中指定的方式进行初始化。

否则,其 set entries 最初是空的。

add(font)

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

  1. 如果 font 已存在于 FontFaceSetset entries 中, 则立即跳到该算法的最后一步。

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

  3. font 参数添加到 FontFaceSetset entries 中。

  4. 如果 fontstatus 属性为“loading”:

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

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

  5. 返回 FontFaceSet

delete(font)

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

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

  2. deleted 设为从 FontFaceSetset entries 中移除 font 的结果。

  3. 如果 font 存在于 FontFaceSet[[LoadedFonts]], 或 [[FailedFonts]] 列表中, 则将其移除。

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

  5. 返回 deleted

clear()

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

  1. FontFaceSetset entries 中移除所有非 CSS 连接的 项, 以及 [[LoadedFonts]] 列表和 [[FailedFonts]] 列表中的所有项。

  2. 如果 FontFaceSet[[LoadingFonts]] 列表不为空, 则将所有项移除, 然后 将 FontFaceSet 切换为 loaded 状态

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

因为字体系列仅在使用时才加载, 内容有时需要了解字体加载的时机。 作者可以使用此处定义的事件和方法来更好地控制依赖于特定字体可用性的操作。

FontFaceSet 如果符合以下任一条件,则视为 依赖环境

注意: 一旦 FontFaceSet 停止 依赖环境, 只要没有其他更改文档的内容, 作者可以依赖测量时大小/位置是“正确的”。 如果上述条件无法完全捕获此保证, 则需要对其进行修改以实现该保证。

3.1. 事件

字体加载事件使得响应整个文档的字体加载行为变得更加简单,而不必对每个字体进行单独监听。 loading 事件在文档开始加载字体时触发, 而 loadingdoneloadingerror 事件在文档完成加载字体时触发,分别包含成功加载的字体和加载失败的字体。

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

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

要在 FontFaceSet target 上触发名为 e字体加载事件,并可选带有 font faces,意味着使用符合以下条件的 FontFaceSetLoadEvent 接口触发一个简单事件名为 e

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

当被要求为给定的 FontFaceSet将 FontFaceSet 切换为 loading时,用户代理必须执行以下步骤:

  1. font face set 为给定的 FontFaceSet
  2. font face setstatus 属性设置为“loading”。
  3. 如果 font face set[[ReadyPromise]] 槽当前持有一个已实现的 promise,则替换为一个新的挂起的 promise。
  4. 排队任务以在 font face set触发一个字体加载事件,名为 loading

当被要求为给定的 FontFaceSet将 FontFaceSet 切换为 loaded时,用户代理必须执行以下步骤:

  1. font face set 为给定的 FontFaceSet

  2. 如果 font face set 依赖环境,则将其标记为依赖环境,并退出此算法。

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

  4. font face set 完成 font face set[[ReadyPromise]] 属性的值。

  5. 排队任务以同步执行以下步骤:

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

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

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

    4. 触发一个字体加载事件,名为 loadingdone,在 font face set 上并带有 loaded fonts

    5. 如果 font face setfailed fonts 不为空,触发一个字体加载事件,名为 loadingerror,在 font face set 上并带有 failed fonts

每当 FontFaceSet依赖环境变为不再依赖环境时,用户代理必须执行以下步骤:

  1. 如果 FontFaceSet 依赖环境,并且其 [[LoadingFonts]] 列表为空,将 FontFaceSet 切换为 loaded

  2. 如果 FontFaceSet 依赖环境,取消标记它。

如果请求从 FontFaceSet source找到匹配的字体,给定字体字符串 font,可选一些示例文本 text,并可选一个 允许系统字体 标志,执行以下步骤:

  1. 解析 font,使用 font 属性的 CSS 值语法。 如果出现语法错误, 返回语法错误。

    如果解析后的值是 CSS-wide 关键字, 返回语法错误。

    将所有相对长度对对应属性的初始值进行绝对化。 (例如,相对字体权重如 bolder 是相对于初始值 normal 来评估的。)

  2. 如果未显式提供 text, 让它是一个包含单个空格字符(U+0020 SPACE)的字符串。
  3. font family list 为从 font 解析出的字体系列列表, 并让 font style 为从 font 解析出的其他字体样式属性。
  4. available font facessource 中的可用字体。 如果指定了 允许系统字体 标志, 则将所有系统字体添加到 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() 方法

load() 方法属于 FontFaceSet,用于确定给定字体列表中的所有字体是否已加载并可用。如果有任何字体是可下载字体且尚未加载,用户代理将启动这些字体的加载。它返回一个 Promise,当所有字体加载并可以使用时会被满足,如果任何字体加载失败,则会被拒绝。

load( font, text ) 方法被调用时,执行以下步骤:

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

3.3. check() 方法

check() 方法属于 FontFaceSet,用于确定是否可以“安全地”渲染一些提供的文本与特定的字体列表,这样可以防止“字体切换”在之后发生。如果给定的文本/字体组合能在不尝试使用任何未加载或正在加载的字体的情况下渲染,此方法将返回 true;否则,返回 false。

在此方法的行为中应注意两个特殊情况,因为它们不明显:

check( font, text) 方法被调用时,执行以下步骤:

  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 中所有字体的 status 属性为“loaded”或是系统字体,则返回 true。否则,返回 false

3.4. ready 属性

由于加载的字体数量取决于为给定文本使用的字体数量,在某些情况下是否需要加载字体可能不清楚。ready 属性包含一个 Promise,当文档完成加载字体时会被解决,这为作者提供了一种避免在检查可能受字体加载影响的内容之前跟踪哪些字体已加载的方法。

注意:作者应注意,给定的 ready promise 仅会被满足一次,但在它满足之后可能会加载更多字体。这类似于监听 loadingdone 事件,但传递给 ready promise 的回调总是会被调用,即使没有字体加载,因为所涉及的字体已经加载。它是同步代码与字体加载的一种简单而方便的方式,无需跟踪需要哪些字体以及它们何时加载。

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

注意:请注意,此 ready 属性返回的 Promise 只会被满足,不会被拒绝,这与 FontFaceload() 方法返回的 Promise 不同。

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

当用户代理自动运行 [CSS-FONTS-3] 中的字体匹配算法时,它匹配的字体集必须严格为文档的 字体源中的字体集合,以及任何本地字体。

当用户代理需要加载某个字体时,必须通过调用相应 load() 方法加载 FontFace 对象。

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

当字体被添加到 FontFaceSet 中时,字体即为可用。向样式表添加一个新的 @font-face 规则也会向 FontFaceSet 添加一个新的 FontFace 对象到 Document 对象中。

向样式表添加新的 @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 意味着字体将永远不会被加载,文本将以默认的衬线字体显示:

var f = new FontFace("newfont", "url(newtest.woff)", {});

/* 新的 {{FontFace}} 未添加到 {{FontFaceSet}},
   因此 'font-family' 属性无法看到它,
   将使用衬线字体 */
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;

任何可以以某种方式使用字体的文档、工作者或其他上下文都必须包含 FontFaceSource 混入。上下文的 fonts 属性的值是它的 字体源,除非另有定义,它提供了所有用于字体相关操作的字体。引用“字体源”的操作必须解释为引用相关上下文中发生操作的 字体源

对于在这些上下文中发生的任何与字体相关的操作,字体源中的 FontFace 对象是它的 可用字体

4.1. Worker 中的 FontFaceSources

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

注意:可以像往常一样构造 FontFace 对象并将其添加到字体源中,这会影响 Worker 内的 CSS 字体匹配(例如,将文本绘制到 OffscreenCanvas)。

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

文档的 字体源集合条目必须初始填充所有来自文档或影子根 CSS 样式表中的 CSS @font-face 规则的 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";
});
在画布中绘制带有可下载字体的文本,显式启动字体下载并在完成后进行绘制:
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() {
  // 内容现在可以使用可下载字体的度量来测量
}

function doEditing() {
  // 内容/布局操作可能会导致额外的字体加载
  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 事件才会触发。

“unused” 字体没有被加载,但没有文本使用它,因此用户代理甚至没有尝试加载它。它不会干扰 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",例如:

这是一个说明性示例。

说明性注释以“注”一词开头,并与规范文本分开,使用 class="note",例如:

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

建议是规范性的部分,样式强调以引起特别注意,并使用 <strong class="advisement"> 与其他规范文本分开,例如:用户代理必须提供可访问的替代方案。

一致性类别

对本规范的符合性定义了三个符合性类别:

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

如果样式表中使用的语法符合该模块中定义的通用 CSS 语法以及每个特性定义的各自语法,则该样式表符合本规范。

如果渲染器除了按适当的规范定义解释样式表外,还支持本规范定义的所有特性,通过正确解析并相应地渲染文档,则该渲染器符合本规范。然而,由于设备的限制,用户代理无法正确渲染文档,并不意味着该用户代理不符合规范。(例如,用户代理不要求在单色显示器上渲染颜色。)

如果创作工具编写的样式表在语法上符合通用 CSS 语法以及该模块中每个特性的各自语法,并满足该模块描述的样式表的所有其他一致性要求,则该创作工具符合本规范。

部分实现

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

不稳定和专有功能的实现

为了避免与未来稳定的 CSS 特性发生冲突,CSSWG 推荐遵循最佳实践来实现不稳定特性和专有扩展到 CSS。

非实验性实现

一旦规范进入候选推荐阶段,就可以进行非实验性实现,并且实现者应发布任何 CR 级别特性的无前缀实现,前提是他们能够证明该实现符合规范。

为了建立和保持 CSS 在各个实现之间的互操作性,CSS 工作组请求非实验性的 CSS 渲染器在发布任何 CSS 特性的无前缀实现之前,向 W3C 提交实施报告(如有必要,还包括用于该实施报告的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组进行审核和修正。

有关提交测试用例和实施报告的更多信息,请访问 CSS 工作组的网站 https://www.w3.org/Style/CSS/Test/。如有问题,请联系public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性参考

[CSS-FONT-LOADING-3]
Tab Atkins Jr.,CSS 字体加载模块第 3 级,2014年5月22日,工作草案。URL:https://www.w3.org/TR/css-font-loading-3/
[CSS-FONTS-3]
John Daggett;Myles Maxfield;Chris Lilley,CSS 字体模块第 3 级,2018年9月20日,推荐标准。URL:https://www.w3.org/TR/css-fonts-3/
[CSS-FONTS-4]
John Daggett;Myles Maxfield;Chris Lilley,CSS 字体模块第 4 级,2021年12月21日,工作草案。URL:https://www.w3.org/TR/css-fonts-4/
[CSS-FONTS-5]
Myles Maxfield;Chris Lilley,CSS 字体模块第 5 级,2021年12月21日,工作草案。URL:https://www.w3.org/TR/css-fonts-5/
[CSS-SYNTAX-3]
Tab Atkins Jr.;Simon Sapin,CSS 语法模块第 3 级,2021年12月24日,候选推荐标准。URL:https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-4]
Tab Atkins Jr.;Elika Etemad,CSS 值和单位模块第 4 级,2022年10月19日,工作草案。URL:https://www.w3.org/TR/css-values-4/
[CSSOM-1]
Daniel Glazman;Emilio Cobos Álvarez,CSS 对象模型 (CSSOM),2021年8月26日,工作草案。URL:https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren,DOM 标准,现行标准。URL:https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren 等人,HTML 标准,现行标准。URL:https://html.spec.whatwg.org/multipage/
[RFC2119]
S. Bradner,在 RFC 中使用关键字表示需求级别,1997年3月,最佳当前实践。URL:https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen;Timothy Gu,Web IDL 标准,现行标准。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 {
    /* CSS 工作组仍在讨论此处的内容 */
};

[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();

  // 加载状态更改时的事件
  attribute EventHandler onloading;
  attribute EventHandler onloadingdone;
  attribute EventHandler onloadingerror;

  // 检查并在合适时开始加载
  // 当所有加载完成时兑现 promise
  Promise<sequence<FontFace>> load(CSSOMString font, optional CSSOMString text = " ");

  // 返回字体列表中的所有字体是否已加载
  // (如果不可用则不会启动加载)
  boolean check(CSSOMString font, optional CSSOMString text = " ");

  // 异步通知字体加载和布局操作已完成
  readonly attribute Promise<FontFaceSet> ready;

  // 加载状态,"loading" 表示一个或多个字体正在加载,否则为 "loaded"
  readonly attribute FontFaceSetLoadStatus status;
};

interface mixin FontFaceSource {
  readonly attribute FontFaceSet fonts;
};

Document includes FontFaceSource;
WorkerGlobalScope includes FontFaceSource;

问题索引

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