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でIssueを提出(推奨)することでお寄せください。タイトルに仕様コード「css-font-loading」を含めてください(例:「[css-font-loading] …コメント要旨…」)。 すべてのIssueおよびコメントはアーカイブされています。 または、(アーカイブあり)パブリックメーリングリスト www-style@w3.orgに送信することもできます。

この文書は、2021年11月2日W3Cプロセス文書に従って管理されています。

この文書は、W3C特許ポリシーのもとで運用されているグループによって作成されました。 W3Cは、グループの成果物に関連して行われた 公開特許開示リストを管理しています。 そのページには、特許の開示方法についても記載されています。 特定の特許について実際に知っている個人は、 それが必須クレームを含むと考える場合、 W3C特許ポリシー第6節に従って情報を開示しなければなりません。

1. はじめに

CSSは、@font-face規則を使って、ウェブからカスタムフォントを著者が読み込むことを可能にします。 スタイルシートを作成する際には簡単ですが、 スクリプトで動的に利用するのはかなり難しくなります。

さらに、CSSではユーザーエージェントが実際にフォントをロードするタイミングを選択できます。 ページ上でフォントフェイスが現在何も使われていない場合、 多くのユーザーエージェントは関連ファイルをダウンロードしません。 つまり、後からそのフォントフェイスを使用すると、 ユーザーエージェントが利用を検知してフォントファイルのダウンロードと解析を開始するまで遅延が発生します。

この仕様は、CSSのフォントフェイスに対するスクリプティングインターフェイスを定義し、 フォントフェイスを簡単に作成(FontFace インターフェイス経由) およびスクリプトからロード(document.fonts経由)できるようにします。 また、個々のフォントやページ全体のフォントのロード状況を追跡するメソッドも提供します。

この仕様書のいくつかの箇所では、通常のESオブジェクトを使って挙動を定義しています。 例えば内部的にPromiseを利用したり、 FontFaceSetがSetを内部で使うなどです。 ここで意図しているのは、これらのオブジェクト(およびそのプロトタイプチェーン)がプリスティンであり、 著者が行ったいかなる操作にも影響されないことだと思っています。 この意図は良いものでしょうか? もしそうなら、仕様書内でどう記載すべきでしょうか?

1.1.

この仕様では、PromiseECMAScript 6で定義)を使用します。 MDNにはPromiseを導入する良いチュートリアル資料があります。

1.2. タスクソース

この仕様がタスクをキューするときは、"font loading"タスクソースにキューします。

2. FontFaceインターフェイス

FontFace インターフェイスは、1つの利用可能なフォントフェイスを表します。 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"の場合、「イタリック体のフォントフェイス」を表しますが、 フォントフェイス自体をイタリック体にするわけではありません。

取得時は、この属性に関連付けられた文字列を返します。

設定時は、対応する記述子の文法に従って文字列をパースします。 文法と一致しない場合は、 SyntaxErrorを投げます。 一致すれば、パースされた値のシリアライズを属性に設定します。

variant, 型: CSSOMString
featureSettings, 型: CSSOMString
variationSettings, 型: CSSOMString
display, 型: CSSOMString
ascentOverride, 型: CSSOMString
descentOverride, 型: CSSOMString
lineGapOverride, 型: CSSOMString

これらの属性も同じ意味を持ち、 CSSの@font-face規則の対応する記述子と同じようにパースされます。

対応するフォントがサポートしていれば、特定の機能をオン・オフします。 前述の属性と違い、 これらの属性はフォントフェイス自体に実際に影響します。

取得時は、この属性に関連付けられた文字列を返します。

設定時は、対応する記述子の文法に従って文字列をパースします。 文法と一致しない場合は、 SyntaxErrorを投げます。 一致すれば、パースされた値のシリアライズを属性に設定します。

status, 型: FontFaceLoadStatus, 読み取り専用

この属性は、フォントフェイスの現在の状態を反映します。 新しく作成したFontFaceでは"unloaded"でなければなりません。

著者が明示的にフォントフェイスのロードを要求した場合(例えば、 load() メソッドやFontFace経由)、 またはユーザーエージェントが画面のテキスト描画に必要だと検知した場合に状態が変化する可能性があります。

loaded, 型: Promise<FontFace>, 読み取り専用

この属性はフォントフェイスの[[FontStatusPromise]]を反映します。

すべてのFontFace オブジェクトは、フォントの状態を追跡する内部スロット[[FontStatusPromise]]を持っています。 初期状態はpendingで、 フォントが正常にロード・解析された場合やエラーが発生した場合にfulfillまたはrejectされます。

すべての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の場合は、 src記述子の文法に従ってパースする。 いずれかが正しくパースできない場合は、 font face[[FontStatusPromise]] を"SyntaxError"という名前のDOMExceptionでrejectし、 font faceの該当属性を空文字列に設定し、 font facestatus 属性を"error"に設定する。 それ以外の場合は、解析された値のシリアライズを各属性に設定する。

    注: source引数に裸のURLを渡しても動作しません(例: "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をloadingに切り替える

      2. font faceFontFaceSet[[LoadingFonts]] リストに追加する。

    非同期的に、内部データをフォントとして解析を試みる。 完了したら(成功・失敗いずれでも)以下の手順を同期的に実行するタスクをキューする:

    1. ロードが成功した場合、font faceは解析されたフォントを表すようになり、 font face[[FontStatusPromise]]font faceでfulfillし、 status 属性を"loaded"に設定する。

      font faceが所属する各FontFaceSetについて:

      1. font faceFontFaceSet[[LoadedFonts]] リストに追加する。

      2. font faceFontFaceSet[[LoadingFonts]] リストから削除する。 そのリストの最後の項目だった場合(空になった場合)はFontFaceSetをloadedに切り替える

    2. それ以外の場合は、 font face[[FontStatusPromise]] を"SyntaxError"という名前のDOMExceptionでrejectし、 status 属性を"error"に設定する。

      font faceが所属する各FontFaceSetについて:

      1. font faceFontFaceSet[[FailedFonts]] リストに追加する。

      2. font faceFontFaceSet[[LoadingFonts]] リストから削除する。 そのリストの最後の項目だった場合(空になった場合)はFontFaceSetをloadedに切り替える

注: 新しく構築された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. ロードに失敗した場合は、 font face[[FontStatusPromise]] を 名前が"NetworkError"のDOMExceptionでrejectし、 font facestatus 属性を"error"に設定する。

      font faceが所属する各FontFaceSetについて:

      1. font faceFontFaceSet[[FailedFonts]] リストに追加する。

      2. font faceFontFaceSet[[LoadingFonts]] リストから削除する。 そのリストの最後の項目だった場合(空になった場合)はFontFaceSetをloadedに切り替える

    2. それ以外の場合は、font faceがロード済みのフォントを表すようになり、 font face[[FontStatusPromise]]font faceでfulfillし、 font facestatus 属性を"loaded"に設定する。

      font faceが所属する各FontFaceSetについて:

      1. font faceFontFaceSet[[LoadedFonts]] リストに追加する。

      2. font faceFontFaceSet[[LoadingFonts]] リストから削除する。 そのリストの最後の項目だった場合(空になった場合)はFontFaceSetをloadedに切り替える

ユーザーエージェントは、ページ上の何かを描画するために特定のフォントフェイスが必要と判断した場合、 自動的にフォントロードを開始できます。 この場合は、ここで説明されている該当FontFaceload() メソッドを呼び出したかのように動作しなければなりません。

注: 一部のUAは「フォントキャッシュ」を利用して、 同じフォントをページ内や同一オリジンの複数ページで何度もダウンロードすることを避けます。 複数のFontFaceオブジェクトが同じフォントキャッシュのエントリにマッピングされる場合があり、 そのためFontFaceオブジェクトが 予期せずロードを開始することがあります。 たとえそれがFontFaceSetに所属していなくても、 他のFontFaceオブジェクトが 同じフォントデータを参照している場合(まったく別のページでも!)ロードが始まることがあります。

2.3. CSSの@font-face規則との連携

CSSの@font-face規則は、該当するFontFaceオブジェクトを自動的に定義し、規則がパースされると同時にドキュメントのfont sourceに自動的に配置されます。 このFontFaceオブジェクトはCSS接続済みです。

@font-face規則に対応するFontFace オブジェクトは、familystyleweightstretchunicodeRangevariantfeatureSettings の属性に、@font-face規則の該当する記述子の値が設定されます。 これらは双方向に連携しており、@font-face記述子に変更があると、即座に対応するFontFace属性に反映され、逆も同様です。

FontFaceが文書間で転送された場合、CSS接続済みでなくなります。

FontFaceオブジェクトの内部[[Urls]]スロットは、@font-face規則のsrc記述子の値が設定され、その記述子に変更があれば反映されます。

それ以外は、CSSの@font-face規則により生成されたFontFaceオブジェクトは、手動で作成したものと同一です。

@font-face規則がドキュメントから削除されると、対応するFontFaceオブジェクトはCSS接続済みでなくなります。 この連携はどの手段でも復元できません(ただし、@font-faceをスタイルシートに再追加すれば、新しいFontFaceオブジェクトが作成され、それはCSS接続済みとなります)。

@font-face規則のsrc記述子が新たな値に変更された場合、 元のCSS接続済みFontFaceオブジェクトはCSS接続済みでなくなる必要があります。 新しいsrcを反映する新しいFontFaceオブジェクトが作成され、CSS接続済みとして@font-faceに連携されます。 (これにより古いFontFaceオブジェクトはfont sourcesから削除、新しいものが追加されます)

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

FontFaceSetfont sourceの場合、 set entries§ 4.2 CSSの@font-face規則との連携に従って初期化されます。

それ以外の場合は、set entriesは初期状態で空です。

add(font)

add() メソッドが呼び出されたとき、以下の手順を実行します:

  1. fontがすでにFontFaceSetset entriesに含まれている場合は、このアルゴリズムの最後の手順に即座に進みます。

  2. fontCSS接続済みの場合、 InvalidModificationError 例外をthrowし、即座にこのアルゴリズムを終了します。

  3. font引数をFontFaceSetset entriesに追加します。

  4. fontstatus 属性が"loading"の場合:

    1. FontFaceSet[[LoadingFonts]] リストが空なら、FontFaceSetをloadingに切り替えます。

    2. fontFontFaceSet[[LoadingFonts]] リストに追加します。

  5. FontFaceSetを返します。

delete(font)

delete() メソッドが呼び出されたとき、以下の手順を実行します:

  1. fontCSS接続済みの場合、 falseを返して即座にこのアルゴリズムを終了します。

  2. deletedに、fontFontFaceSetset entriesから削除した結果を設定します。

  3. fontFontFaceSet[[LoadedFonts]]または[[FailedFonts]] リストに含まれている場合、削除します。

  4. fontFontFaceSet[[LoadingFonts]] リストに含まれている場合、削除します。 そのリストの最後の項目だった場合(空になった場合)はFontFaceSetをloadedに切り替えます。

  5. deletedを返します。

clear()

clear() メソッドが呼び出されたとき、以下の手順を実行します:

  1. CSS接続済み項目をFontFaceSetset entries[[LoadedFonts]]リスト、[[FailedFonts]]リストから削除します。

  2. FontFaceSet[[LoadingFonts]] リストが空でなければ、すべて削除し、その後FontFaceSetをloadedに切り替えます。

FontFaceSetオブジェクトは、内部に[[LoadingFonts]][[LoadedFonts]][[FailedFonts]]スロットを持ち、すべて空リストで初期化されます。 また、[[ReadyPromise]]スロットを持ち、これは新しいpending状態のPromiseで初期化されます。

フォントファミリーは使用されたときだけロードされるため、 コンテンツがフォントのロードタイミングを把握する必要がある場合があります。 著者はここで定義されたイベントやメソッドを使って、 特定フォントの利用可能性に依存するアクションをより細かく制御できます。

FontFaceSetは、次のいずれかが真の場合、環境でpendingです:

注: FontFaceSet環境でpendingでなくなった時点で、以降文書に変化がなければ、著者はサイズや位置の測定値が「正しい」と信頼できます。 上記条件がこの保証を十分に表現できていなければ、修正が必要です。

3.1. イベント

フォントロードイベントを使うことで、ドキュメント全体のフォントロード挙動に簡単に対応でき、個々のフォントごとに監視する必要がなくなります。 loadingイベントは ドキュメントがフォントのロードを開始した際に発火し、 loadingdoneおよびloadingerrorイベントは ドキュメントのフォントロードが完了した際に発火し、それぞれ成功したフォント・失敗したフォントを含みます。

以下はFontFaceSetオブジェクトがIDL属性としてサポートしなければならないイベントハンドラー(および対応するイベントタイプ)です:

イベントハンドラー イベントハンドラーイベントタイプ
onloading loading
onloadingdone loadingdone
onloadingerror loadingerror

eという名前のフォントロードイベントを発火するには、FontFaceSettargetで、オプションとしてfont facesを指定し、以下の条件を満たすFontFaceSetLoadEventインターフェイスを使って単純なイベントを発火することを意味します:

  1. fontfaces 属性は font facesのうちtargetに含まれるFontFaceオブジェクトのみをフィルタリングした結果で初期化します。

指定されたFontFaceSetについてFontFaceSetをloadingに切り替えるよう要求された場合、ユーザーエージェントは以下の手順を実行します:

  1. font face setを指定されたFontFaceSetとする。
  2. font face setstatus 属性を"loading"に設定する。
  3. font face set[[ReadyPromise]] スロットが現在fulfilledなpromiseを保持している場合、新しいpending promiseに置き換える。
  4. font face setloading という名前のフォントロードイベントを発火するタスクをキューする。

指定されたFontFaceSetについてFontFaceSetをloadedに切り替えるよう要求された場合、ユーザーエージェントは以下の手順を実行します:

  1. font face setを指定されたFontFaceSetとする。

  2. font face set環境でpendingの場合、 それを環境でstuckとマークし、このアルゴリズムを終了します。

  3. font face setstatus 属性を"loaded"に設定します。

  4. font face set[[ReadyPromise]] 属性値をfont face setでfulfillします。

  5. 同期的に以下の手順を実行するタスクをキューします:

    1. loaded fontsfont face set[[LoadedFonts]] スロットの内容(空の場合もあり)とする。

    2. failed fontsfont face set[[FailedFonts]] スロットの内容(空の場合もあり)とする。

    3. [[LoadedFonts]][[FailedFonts]] スロットを空リストにリセットする。

    4. font face setloaded fontsを使って、loadingdone という名前のフォントロードイベントを発火する

    5. もしfont face setfailed fontsが空でない場合、failed fontsを使ってloadingerror という名前のフォントロードイベントを発火する

FontFaceSet環境でpendingからpendingでなくなった際、ユーザーエージェントは以下の手順を実行しなければなりません:

  1. もしFontFaceSet環境にスタックされている状態で、 その[[LoadingFonts]] リストが空の場合、FontFaceSetをloaded状態に切り替える

  2. もしFontFaceSet環境にスタックされている状態なら、 その印付けを解除する。

FontFaceSetからマッチするフォントフェイスを探すには、 source(FontFaceSet)、font(フォント文字列)、オプションのサンプルテキストtext、オプションのallow system fontsフラグを指定し、以下の手順を実行します:

  1. fontfontプロパティのCSS値構文でパースする。 構文エラーが発生した場合は、構文エラーを返す。

    パースされた値がCSS全体キーワードの場合は、構文エラーを返す。

    すべての相対長さは、対応するプロパティの初期値に対して絶対化する(例:bolderは初期値normalに対して評価される)。

  2. textが明示的に指定されていなければ、U+0020 SPACEのみからなる1文字の文字列とする。
  3. font family listfontからパースしたフォントファミリーリスト、font styleをその他のスタイル属性とする。
  4. available font facessource内の利用可能なフォントフェイスのリストとする。 allow system fontsフラグが指定されていれば、すべてのシステムフォントも追加する。
  5. matched font facesを空リストで初期化する。
  6. font family list内の各familyに対し、フォントマッチングルールを使ってavailable font facesからfont styleに一致するフォントフェイスを選び、matched font facesに追加する。 unicodeRange 属性の利用により、1つ以上のフォントフェイスが選ばれる場合がある。
  7. matched font facesが空の場合、found facesフラグをfalseに設定する。 そうでなければtrueに設定する。
  8. matched font faces内の各フォントフェイスについて、 定義されたunicode-rangetext内の少なくとも1文字のコードポイントを含まない場合は、リストから削除する。

    注: したがって、textが空文字列なら、すべてのフォントが削除される。

  9. matched font facesfound facesフラグを返す。

3.2. load()メソッド

load() メソッド(FontFaceSet)は、指定されたフォントリスト内のすべてのフォントがロード済みで利用可能かどうかを判定します。 ダウンロード可能なフォントで、まだロードされていないものがあれば、 ユーザーエージェントはそれらのロードを開始します。 このメソッドはPromiseを返し、 全てのフォントがロード・利用可能になった時にfulfilledとなり、 いずれかのフォントのロードに失敗した場合はrejectされます。

load( font, text ) メソッドが呼び出された際、以下の手順を実行します:

  1. font face setをこのメソッドが呼ばれたFontFaceSetオブジェクトとする。 promiseを新しく作成したpromiseオブジェクトとする。
  2. promiseを返す。 残りの手順は非同期で完了する。
  3. font face setから、関数に渡されたfont 引数とtext 引数を使って該当フォントフェイスを検索する。 戻り値をfont face listとし(found facesフラグは無視する)。 もし構文エラーが返された場合は、promiseをSyntaxError例外で拒否し、この手順を終了する。
  4. 同期的に以下の手順を実行するタスクをキューする:
    1. font face list内のすべてのフォントフェイスについて、load() メソッドを呼び出す。
    2. font face list内の各フォントフェイスの[[FontStatusPromise]] を順番に待機し、その結果でpromiseをresolveする。

3.3. check()メソッド

check() メソッド(FontFaceSet)は、指定されたテキストとフォントリストで「安全に」レンダリングできるかどうかを判定します。 つまり、後で「フォントスワップ」が発生しないかどうかです。 指定されたテキスト/フォントの組み合わせが、未ロードまたはロード中のフォントを使おうとせずにレンダリングされる場合はtrueを返し、 そうでなければfalseを返します。

このメソッドの動作には注意すべき特別なケースが2つあります:

check( font, text) メソッドが呼び出されたとき、以下の手順を実行します:

  1. font face setを、このメソッドが呼び出されたFontFaceSet オブジェクトとする。
  2. font 引数とtext 引数を使い、システムフォントも含めてfont face setから一致するフォントフェイスを検索し、 戻り値のフォントフェイスリストをfont face list、戻り値のfound facesフラグをfound facesとする。 構文エラーが返された場合は、SyntaxError例外を投げて、これらの手順を終了する。
  3. font face listが空、またはfont face list内の全てのフォントがstatus 属性が"loaded"であるかシステムフォントの場合は、trueを返す。 それ以外の場合はfalseを返す。

3.4. ready属性

どのフォントがロードされるかは、どのテキストで何フォントが使われるかによって異なるため、場合によってはロードの必要性が分からないことがあります。 ready 属性には、ドキュメントのフォントロードが完了した時にresolveされるPromiseが格納されます。 これにより、著者はどのフォントがロード済みかを個別に管理せずとも、フォントロードに影響されるコンテンツを安全に参照できます。

注: ready promiseは1度だけfulfilledになりますが、その後でもさらにフォントがロードされる可能性があります。 これはloadingdone イベントを監視するのに似ていますが、ready Promiseのコールバックは、対象フォントがすでにロード済みの場合でも必ず呼び出されます。 必要なフォントやロードタイミングを逐一管理せずに、コードをフォントロードに同期させるシンプルな方法です。

注: UAはready promiseがfulfilledされるまで複数回にわたってフォントロードを繰り返す場合があります。 これはフォントフォールバック等で、フォントリスト内の一つがロードされても該当グリフがない場合に他のフォントもロードされるケースです。 ready promiseはレイアウト処理が完了し、追加のフォントロードが不要になった時点でのみfulfilledされます。

注: このready 属性が返すPromiseはrejectされることはなく、必ずfulfilledになります。 これはFontFaceload() メソッドが返すPromiseと異なります。

3.5. CSSフォントのロードとマッチングとの連携

[CSS-FONTS-3]のフォントマッチングアルゴリズムがUAにより自動実行される際、対象となるフォントフェイスの集合は、ドキュメントのfont sourceに含まれるフォントに加え、ローカルフォントフェイスの集合でなければなりません。

UAがフォントフェイスのロードを必要とする場合は、該当FontFaceオブジェクトのload() メソッドを呼び出すことでロードしなければなりません。

(これは同じアルゴリズムを実行するという意味であり、オブジェクトのloadプロパティに現在格納されている値を文字通り呼ぶわけではありません。)

フォントはFontFaceSetに追加されると利用可能になります。 新しい@font-face規則をスタイルシートに追加すると、 新しいFontFaceFontFaceSetに追加されます(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に追加しない場合は、フォントはロードされず、 テキストはデフォルトのserifフォントで表示されます:

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

/* 新しい {{FontFace}} は {{FontFaceSet}} に追加されていないため、
   'font-family'プロパティからは参照できず、serifが使用される */
document.body.style.fontFamily = "newfont, serif";

フォントを使用前に明示的にプリロードする場合、新しいFontFaceFontFaceSetに追加するまでロード完了を待つことができます:

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に追加され、bodyのフォントを変更し、レイアウトエンジンが新しいフォントリソースを使います。

4. FontFaceSourceミックスイン

interface mixin FontFaceSource {
  readonly attribute FontFaceSet fonts;
};

Document includes FontFaceSource;
WorkerGlobalScope includes FontFaceSource;

任意のドキュメント、ワーカー、またはフォントを利用できる他のコンテキストは、FontFaceSourceミックスインを含める必要があります。 コンテキストのfonts属性の値は、そのfont sourceとなり、 特に指定がなければフォント関連操作で使われる全フォントを提供します。 「font source」を参照する操作は、該当コンテキストのfont sourceを指すものとして解釈されます。

これらコンテキスト内で行われるフォント関連操作では、font source内のFontFaceオブジェクトが利用可能なフォントフェイスとなります。

4.1. WorkerのFontFaceSource

Workerドキュメント内では、font sourceは初期状態で空です。

注: FontFaceオブジェクトは通常通り構築して追加でき、 Worker内のCSSフォントマッチング(例えばOffscreenCanvasでテキスト描画等)に影響します。

4.2. CSSの@font-face規則との連携

ドキュメントのfont sourceset entriesは、すべてのCSS@font-face規則から得られるCSS接続済みFontFaceオブジェクトで初期化され、ドキュメント順に並べられます。 @font-face規則やそれを含むスタイルシートの追加/削除に伴い、 対応するCSS接続済みFontFaceオブジェクトもドキュメントのfont sourceから追加・削除され、この順序を保ちます。

手動で追加されたFontFaceオブジェクトは、CSS接続済みのものより後ろに並べられます。

FontFaceSetオブジェクトのadd()メソッドにCSS接続済みFontFaceオブジェクトを渡した場合、オブジェクトがすでにセット内にある場合はno-op、 そうでなければ何もせずInvalidModificationErrorをthrowします。

FontFaceSetオブジェクトのdelete()メソッドにCSS接続済みFontFaceオブジェクトを渡した場合はno-opとして、falseを返します。

注: 削除されたFontFaceへの参照は著者が保持可能ですが、§ 2.3 CSSの@font-face規則との連携にある通り、その時点ではFontFaceCSS接続済みではありません。

注: 今後の仕様ではローカルフォントへの操作やクエリ方法も定義される予定です。

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"); /* 漢字・仮名を含まない */
}
@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>納豆はいかがでしょうか

この場合、UAはまず「latinserif.woff」をダウンロードし、日本語テキストを描画しようとします。 しかしそのフォントに日本語グリフがないため、フォールバックとして「mincho.woff」がダウンロードされます。 2つ目のフォントがダウンロードされ、日本語テキストがレイアウトされた後、loadingdone イベントが発火します。

"unused"フォントはロードされませんが、テキストがそれを利用していないため、UAはロードしようとすらしません。 これはloadingdone イベントには影響しません。

変更点

2014年5月 CSS Font Loading Last Call Working Draftからの変更点:

謝辞

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]

本仕様の例は、「for example」という言葉で始まるか、またはclass="example"のように通常のテキストから区別されています。例:

これは説明用の例です。

情報的な注記は「Note」で始まり、class="note"で通常のテキストから区別されています。例:

Note: これは情報的な注記です。

助言(Advisement)は特別な注意を喚起するための規範セクションであり、<strong class="advisement">で他の規範テキストから区別されます。例: UAはアクセシブルな代替手段を必ず提供しなければなりません。

適合クラス

本仕様への適合は3つの適合クラスで定義されます:

スタイルシート
CSSスタイルシート
レンダラー
UA。スタイルシートの意味を解釈し、それを使う文書をレンダリングします。
オーサリングツール
UA。スタイルシートを作成します。

スタイルシートは、このモジュールで定義された構文を使ったすべての文が、汎用CSS文法および本モジュールで定義された各機能の個別文法に従って有効であれば、本仕様に適合しています。

レンダラーは、スタイルシートを適切な仕様に従って解釈することに加え、本仕様で定義されたすべての機能を正しくパースして文書をレンダリングすれば適合しています。ただし、デバイスの制限によってUAが文書を正しくレンダリングできない場合でも、UAが非適合となるわけではありません。(例:モノクロモニターで色のレンダリングは不要)

オーサリングツールは、汎用CSS文法および本モジュールで定義された各機能の個別文法に従って構文的に正しいスタイルシートを書き、また本モジュールで記述されているスタイルシートのすべての適合要件を満たしていれば、本仕様に適合しています。

部分的な実装

著者が将来互換性のあるパース規則を活用してフォールバック値を指定できるよう、CSSレンダラーは、利用可能なレベルのサポートがないatルール、プロパティ、プロパティ値、キーワード、その他構文構造を無効として(そして適切に無視)扱わなければなりません。特に、UAはサポートされていない値のみを無視し、サポートされている値のみを複数値プロパティ宣言内で適用する、という選択的な無視をしてはならない:いずれかの値が無効(サポートされていない値は無効とみなす)であれば、CSSは宣言全体を無視することを要求します。

不安定および独自機能の実装

将来の安定CSS機能との衝突を避けるため、CSSWGはベストプラクティスに従って不安定機能や独自拡張CSS機能を実装することを推奨します。

非実験的な実装

仕様がCandidate Recommendation段階に達したら、非実験的な実装が可能となり、実装者は仕様通りに正しく実装できるCRレベルの機能について、プレフィックスなしの実装を公開すべきです。

CSS実装間の相互運用性を確立・維持するため、CSSワーキンググループは非実験的CSSレンダラーに対し、プレフィックスなしのCSS機能を公開する前に実装報告書(必要に応じてそのテストケースも)をW3Cに提出することを求めます。提出されたテストケースはCSSワーキンググループによりレビュー・修正される場合があります。

テストケースや実装報告書提出方法については、CSSワーキンググループのWebサイト(https://www.w3.org/Style/CSS/Test/)で案内されています。 質問はpublic-css-testsuite@w3.orgメーリングリストへ。

索引

本仕様で定義された用語

参照によって定義された用語

参考文献

規範参考文献

[CSS-FONT-LOADING-3]
Tab Atkins Jr.. CSSフォントローディングモジュール レベル3. 2014年5月22日. WD. URL: https://www.w3.org/TR/css-font-loading-3/
[CSS-FONTS-3]
John Daggett; Myles Maxfield; Chris Lilley. CSSフォントモジュール レベル3. 2018年9月20日. REC. URL: https://www.w3.org/TR/css-fonts-3/
[CSS-FONTS-4]
John Daggett; Myles Maxfield; Chris Lilley. CSSフォントモジュール レベル4. 2021年12月21日. WD. URL: https://www.w3.org/TR/css-fonts-4/
[CSS-FONTS-5]
Myles Maxfield; Chris Lilley. CSSフォントモジュール レベル5. 2021年12月21日. WD. URL: https://www.w3.org/TR/css-fonts-5/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS構文モジュール レベル3. 2021年12月24日. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS値および単位モジュール レベル4. 2022年10月19日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSSオブジェクトモデル (CSSOM). 2021年8月26日. WD. 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月. Best Current Practice. 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 {
  /* 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オブジェクトを使って挙動を定義しています。 例えば内部的にPromiseを利用したり、 FontFaceSetがSetを内部で使うなどです。 ここで意図しているのは、これらのオブジェクト(およびそのプロトタイプチェーン)がプリスティンであり、 著者が行ったいかなる操作にも影響されないことだと思っています。 この意図は良いものでしょうか? もしそうなら、仕様書内でどう記載すべきでしょうか?
"the document"という表現について、どの文書を指しているのか明確にするようにしてください。 オブジェクトが文書間を移動する可能性があるためです。
基底URLの定義が必要です。 相対URLを解決するために、文書のURLを使うべきでしょうか? Workerの場合はWorkerのURLを使うべきか? 常に定義されていますか?
FontFaceが文書間で転送された場合、CSS接続済みでなくなります。