font face’s {{FontFace/status}} is "error",
terminate this algorithm;
otherwise,
complete the rest of these steps asynchronously.
2. If the {{source!argument}} argument was a {{DOMString}},
parse it according to the grammar of the 'src' descriptor of the CSS ''@font-face'' rule.
If it fails to parse correctly,
reject font face's {{[[FontStatusPromise]]}} with a DOMException named "SyntaxError" exception,
queue a task to set font face’s {{FontFace/status}} attribute to "error",
and abort these steps;
otherwise, set font face's internal {{[[Urls]]}} slot to the string.
Note: Note that this means that passing a naked url as the source argument,
like "http://example.com/myFont.woff",
won't work - it needs to be at least wrapped in a ''url()'' function,
like "url(http://example.com/myFont.woff)".
In return for this inconvenience,
you get to specify multiple fallbacks,
specify the type of font each fallback is,
and refer to local fonts easily.
Issue: Need to define the base url,
so relative urls can resolve.
Should it be the url of the document?
Is that correct for workers too,
or should they use their worker url?
Is that always defined?
If the {{source}} argument was a {{BinaryData}},
set font face's internal {{[[Data]]}} slot to the passed argument.
3. If font face's {{[[Data]]}} slot is not null,
queue a task to set font face's {{FontFace/status}} attribute to "loading".
Attempt to parse the data in it as a font.
When this is completed,
successfully or not,
queue a task to run the following steps synchronously:
1. If the load was successful,
font face now represents the parsed font;
fulfill font face's {{[[FontStatusPromise]]}} with font face,
and set its {{FontFace/status}} attribute to "loaded".
2. Otherwise,
reject font face's {{[[FontStatusPromise]]}} with a DOMException named "SyntaxError"
and set font face's {{FontFace/status}} attribute to "error".
Note: Newly constructed FontFace objects are not automatically added
to the FontFaceSet associated with a document
or a context for a worker thread.
This means that while newly constructed fonts can be preloaded,
they cannot actually be used until they are explicitly added to a FontFaceSet.
See the following section for a more complete description of FontFaceSet.
The load() method
The {{FontFace/load()}} method of {{FontFace}}
forces a url-based font face to request its font data and load.
For fonts constructed from binary data,
or fonts that are already loading or loaded,
it does nothing.
When the load() method is called,
execute these steps:
-
Let font face be the {{FontFace}} object on which this method was called.
-
If font face's {{[[Urls]]}} slot is
null,
or its {{FontFace/status}} attribute is anything other than "unloaded",
return font face's {{[[FontStatusPromise]]}}
and abort these steps.
-
Otherwise,
set font face's {{FontFace/status}} attribute to "loading",
return font face's {{[[FontStatusPromise]]}},
and continue executing the rest of this algorithm asynchronously.
-
Using the value of font face's {{[[Urls]]}} slot,
attempt to load a font as defined in [[!CSS3-FONTS]],
as if it was the value of a ''@font-face'' rule's 'src' descriptor.
-
When the load operation completes,
successfully or not,
queue a task to run the following steps synchronously:
-
If the attempt to load fails,
reject font face's {{[[FontStatusPromise]]}} with
a DOMException whose name is "NetworkError"
and set font face's {{FontFace/status}} attribute to "error".
-
Otherwise,
font face now represents the loaded font;
fulfill font face's {{[[FontStatusPromise]]}} with font face
and set font face's {{FontFace/status}} attribute to "loaded".
User agents can initiate font loads on their own,
whenever they determine that a given font face is necessary to render something on the page.
When this happens,
they must act as if they had called the corresponding {{FontFace}}’s {{FontFace/load()}} method described here.
Note: Some UAs utilize a "font cache"
which avoids having to download the same font multiple times
on a page or on multiple pages within the same origin.
Multiple {{FontFace}} objects can be mapped to the same entry in the font cache,
which means that a {{FontFace}} object might start loading unexpectedly,
even if it's not in a {{FontFaceSet}},
because some other {{FontFace}} object pointing to the same font data
(perhaps on a different page entirely!)
has been loaded.
Interaction with CSS’s ''@font-face'' Rule
A CSS ''@font-face'' rule automatically defines a corresponding {{FontFace}} object,
which is automatically placed in the document's font source
when the rule is parsed.
This {{FontFace}} object is CSS-connected.
The {{FontFace}} object corresponding to a ''@font-face'' rule
has its {{FontFace/family}}, {{FontFace/style}}, {{FontFace/weight}}, {{FontFace/stretch}}, {{FontFace/unicodeRange}}, {{FontFace/variant}}, and {{FontFace/featureSettings}} attributes
set to the same value as the corresponding descriptors in the ''@font-face'' rule.
There is a two-way connection between the two:
any change made to a ''@font-face'' descriptor is immediately reflected in the corresponding {{FontFace}} attribute,
and vice versa.
The internal {{[[Urls]]}} slot of the {{FontFace}} object is set to the value of the ''@font-face'' rule's 'src' descriptor,
and reflects any changes made to the 'src' descriptor.
Otherwise, a {{FontFace}} object created by a CSS ''@font-face'' rule is identical to one created manually.
If a ''@font-face'' rule is removed from the document, its corresponding {{FontFace}} object is no longer CSS-connected.
The connection is not restorable by any means
(but adding the ''@font-face'' back to the stylesheet will create a brand new {{FontFace}} object which is CSS-connected).
The FontFaceSet Interface
dictionary FontFaceSetLoadEventInit : EventInit {
sequence<FontFace> fontfaces = [];
};
[Constructor(DOMString type, optional FontFaceSetLoadEventInit eventInitDict),
Exposed=Window,Worker]
interface FontFaceSetLoadEvent : Event {
readonly attribute sequence<FontFace> fontfaces;
};
enum FontFaceSetLoadStatus { "loading", "loaded" };
callback ForEachCallback = void (FontFace font, long index, FontFaceSet self);
[Exposed=Window,Worker,
Constructor(sequence initialFaces)]
interface FontFaceSet : EventTarget {
// Emulate the Set interface, until we can extend Set correctly.
readonly attribute unsigned long size;
void add(FontFace font);
boolean has(FontFace font);
boolean delete(FontFace font);
void clear();
Iterator entries();
Iterator keys();
Iterator values();
void forEach(ForEachCallback cb, optional any thisArg);
FontFace iterator;
// -- 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(DOMString font, optional DOMString text = " ");
// return whether all fonts in the fontlist are loaded
// (does not initiate load if not available)
boolean check(DOMString font, optional DOMString 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
-
This attribute reflects the {{FontFaceSet}}'s {{[[ReadyPromise]]}} slot.
See [[#font-face-set-ready]] for more details on this {{Promise}} and its use.
- size
-
This attribute reflects the
size attribute of the {{FontFaceSet}}'s {{[[ContainedFonts]]}} slot.
- status
-
If there are possibly pending font loads,
the {{FontFaceSet/status}} attribute must have the value
"loading".
Otherwise, it must have the value "loaded".
- FontFaceSet(sequence<{{FontFace}}> initialFaces)
-
The {{FontFaceSet}} constructor, when called,
must construct a new {{Set}} object
by passing its {{initialFaces}} argument
to the {{Set}} constructor,
then assigning that {{Set}} object to the {{FontFaceSet}}'s {{[[ContainedFonts]]}} slot.
- add({{FontFace}} font)
- has({{FontFace}} font)
- delete({{FontFace}} font)
- entries()
- keys()
- values()
- forEach({{ForEachCallback}} cb, optional any thisArg)
-
If {{add()}} or {{delete()}} are called
with an argument that is a CSS-connected {{FontFace}} object,
they must throw an InvalidModificationError exception.
All of these methods call the corresponding method of the {{FontFaceSet}}'s {{[[ContainedFonts]]}} slot
with the same arguments as were passed to them,
and return the value returned by the called method.
- clear()
-
This must remove all non-CSS-connected {{FontFace}} objects
from the {{FontFaceSet}}'s {{[[ContainedFonts]]}} slot.
- iterator behavior
-
The iterator behavior is the iterator behavior of the {{FontFaceSet}}'s {{[[ContainedFonts]]}} slot.
{{FontFaceSet}} objects also have internal
\[[LoadingFonts]],
\[[LoadedFonts]],
and \[[FailedFonts]] slots,
all of which are initialized to the empty list,
a \[[ReadyPromise]] slot,
which is initialized to a fresh pending {{Promise}},
and a \[[ContainedFonts]] slot,
which is initialized by the FontFaceSet constructor to a {{Set}} object
(though see [[#document-font-face-set]] for information on how the Set may be pre-filled
for {{FontFaceSet}} objects created by the user agent).
Because font families are loaded only when they are used,
content sometimes needs to understand when the loading of fonts occurs.
Authors can use the events and methods defined here to allow greater control over actions
that are dependent upon the availability of specific fonts.
There are no pending font loads for a given {{FontFaceSet}} whenever
none of its contained {{FontFace}} objects have a {{FontFace/status}} of "loading".
For {{FontFaceSet}}s that are font sources,
in addition to the above constriant,
the following must all be true
for them to be considered as having no pending font loads:
-
The document has finished loading.
-
There are no pending stylesheet requests.
-
There are no pending layout operations which might cause the user agent to request a font.
If a {{FontFaceSet}} can't be considered to have no pending font loads,
it instead has possibly pending font loads.
Events
Font load events make it easy to respond to the font-loading behavior of the entire document,
rather than having to listen to each font specifically.
The loading event
fires when the document begins loading fonts,
while the loadingdone
and loadingerror events
fire when the document is done loading fonts,
containing the fonts that successfully loaded
or failed to load,
respectively.
The following are the event handlers (and their corresponding event handler event types)
that must be supported by FontFaceSet objects as IDL attributes:
| Event handler
| Event handler event type
|
| {{onloading}}
| {{loading}}
|
| {{onloadingdone}}
| {{loadingdone}}
|
| {{onloadingerror}}
| {{loadingerror}}
|
To fire a font load event named e
at a {{FontFaceSet}} target
with optional font faces
means to
fire a simple event named e
using the {{FontFaceSetLoadEvent}} interface that also meets these conditions:
-
The {{FontFaceSetLoadEvent/fontfaces}} attribute is initialized to the given list of {{FontFace}} objects.
Whenever one or more {{FontFace}} objects within a given {{FontFaceSet}}
change their {{FontFace/status}} attribute to "loading",
the user agent must run the following steps:
-
Let font face set be the given {{FontFaceSet}},
and loading fonts be the {{FontFace}} objects
that have newly switched to "loading" status,
in the same order as they appear in font face set.
-
Set the {{FontFaceSet/status}} attribute of font face set to "loading".
-
If font face set's {{[[LoadingFonts]]}} slot is currently empty,
fire a font load event named {{loading}} at font face set.
-
Append the loading fonts to font face set's {{[[LoadingFonts]]}} slot.
-
If font face set's {{[[ReadyPromise]]}} slot currently holds a fulfilled promise,
replace it with a fresh pending promise.
Whenever one or more available font faces for a given {{FontFaceSet}}
change their {{FontFace/status}} attribute to "loaded" or "error",
the user agent must run the following steps:
-
Let font face set be the given {{FontFaceSet}},
and loaded fonts be the {{FontFace}} objects
that have newly switched to "loaded" or "error" status,
in the same order as they appear in font face set.
-
For each font in the loaded fonts,
if their {{FontFace/status}} attribute is "loaded",
append them to font face set's {{[[LoadedFonts]]}} slot;
if it's "error",
append them to font face set's {{[[FailedFonts]]}} slot.
Whenever a {{FontFaceSet}} goes from having possibly pending font loads
to having no pending font loads,
user agents must run these steps:
-
If this is not the first time the {{FontFaceSet}} has had no pending font loads,
and none of its contained {{FontFace}} objects began loading
since the last time it had no pending font loads,
abort this algorithm.
-
Set font face set's {{FontFaceSet/status}} attribute to "loaded".
-
Fire a font load event named {{loadingdone}} at font face set
with the (possibly empty) contents of font face set's {{[[LoadedFonts]]}} slot.
Reset the {{[[LoadedFonts]]}} slot to an empty list.
-
If font face set's {{[[FailedFonts]]}} slots is non-empty,
fire a font load event named {{loadingerror}} at font face set
with the contents of font face set's {{[[FailedFonts]]}} slot.
Reset the {{[[FailedFonts]]}} slot to an empty list.
-
Fulfill font face set's {{[[ReadyPromise]]}} attribute's value with font face set.
If asked to find the matching font faces
from a FontFaceSet source,
for a given font string font
optionally some sample text text,
and optionally an allow system fonts flag,
run the following steps:
-
Parse font
using the CSS value syntax of the 'font' property.
If a syntax error occurs,
return a syntax error.
-
If text was not explicitly provided,
let it be a string containing a single space character (U+0020 SPACE).
-
Let font family list be the list of font families parsed from font,
and font style be the other font style attributes parsed from font.
-
Let available font faces be the font faces within source.
If the allow system fonts flag is specified,
add all system fonts to available font faces.
-
Let matched font faces initially be an empty list.
-
For each family in font family list,
use the font matching rules to select the font faces from available font faces
that match the font style,
and add them to matched font faces.
The use of the {{FontFace/unicodeRange}} attribute means that this may be more than just a single font face.
-
For each font face in matched font faces,
if its defined 'unicode-range' does not include the codepoint of at least one character in text,
remove it from the list.
-
Return matched font faces.
The load() method
The {{FontFaceSet/load() method of {{FontFaceSet}} will determine whether all fonts in the given font list
have been loaded and are available.
If any fonts are downloadable fonts and have not already been loaded,
the user agent will initiate the load of each of these fonts.
It returns a Promise,
which is fulfilled when all of the fonts are loaded and ready to be used,
or rejected if any font failed to load properly.
When the load(font, text) method is called,
execute these steps:
-
Let font face set be the {{FontFaceSet}} object this method was called on.
Let promise be a newly-created promise object.
-
Return promise.
Complete the rest of these steps asynchronously.
-
Find the matching font faces from font face set
using the {{FontFaceSet/load()/font}} and {{FontFaceSet/load()/text}} arguments passed to the function,
and let font face list be the return value.
If a syntax error was returned,
reject promise with a SyntaxError exception
and terminate these steps.
-
Queue a task to run the following steps synchronously:
-
For all of the font faces in the font face list,
call their {{FontFace/load()}} method.
-
Resolve promise with the result of
waiting for all of the {{[[FontStatusPromise]]}}s of each font face in the font face list, in order.
The check() method
The {{check()}} method of {{FontFaceSet}} will determine whether all fonts in the given font list
have been loaded and are available.
If all fonts are available,
it returns true;
otherwise, it returns false.
When the check(font, text) method is called,
execute these steps:
-
Let font face set be the {{FontFaceSet}} object this method was called on.
-
Find the matching font faces from font face set
using the {{FontFaceSet/check()/font}} and {{FontFaceSet/check()/text}} arguments passed to the function,
and including system fonts,
and let font face list be the return value.
If a syntax error was returned,
throw a SyntaxError exception
and terminate these steps.
-
If the font face list contains no font faces,
return
false.
-
If all fonts in the font face list have a {{FontFace/status}} attribute of "loaded",
or are system fonts,
return
true.
Otherwise, return false.
The ready attribute
Because the number of fonts loaded depends on the how many fonts are used for a given piece of text,
in some cases whether fonts need to be loaded or not may not be known.
The {{FontFaceSet/ready}} attribute contains a {{Promise}} which is resolved when the document is done loading fonts,
which provides a way for authors to avoid having to keep track of which fonts have or haven't been loaded
before examining content which may be affected by loading fonts.
Note: Authors should note that a given ready promise is only fulfilled once,
but further fonts may be loaded after it fulfills.
This is similar to listening for a {{loadingdone}} event to fire,
but the callbacks passed to the {{FontFaceSet/ready}} promise will always get called,
even when no font loads occur because the fonts in question are already loaded.
It's a simple, easy way to synchronize code to font loads
without the need to keep track of what fonts are needed and precisely when they load.
Note: Note that the user agent may need to iterate over multiple font loads before the ready promise is fulfilled.
This can occur with font fallback situations,
where one font in the fontlist is loaded
but doesn't contain a particular glyph
and other fonts in the fontlist need to be loaded.
The ready promise is only fulfilled after layout operations complete
and no additional font loads are necessary.
Note: Note that the Promise returned by this {{FontFaceSet/ready}} attribute is only ever fulfilled,
never rejected,
unlike the Promise returned by the {{FontFace}} {{FontFace/load()}} method.
Interaction with CSS Font Loading and Matching
When the font matching algorithm in [[CSS3-FONTS]] is run automatically by the user-agent,
the set of font faces it matches over must be precisely the set of fonts in the font source for the document,
plus any local font faces.
When a user-agent needs to load a font face,
it must do so by calling the {{FontFace/load()}} method
of the corresponding {{FontFace}} object.
(This means it must run the same algorithm,
not literally call the value currently stored in the load property of the object.)
Fonts are available when they are added to a {{FontFaceSet}}.
Adding a new ''@font-face'' rule to a stylesheet
also adds a new {{FontFace}} to the {{FontFaceSet}} of the {{Document}} object.
Adding a new ''@font-face'' rule:
document.styleSheets[0].insertRule(
"@font-face { font-family: newfont; src: url(newfont.woff); }", 0);
document.body.style.fontFamily = "newfont, serif";
Constructing a new {{FontFace}} object and adding it to
document.fonts:
var f = new FontFace("newfont", "url(newfont.woff)");
document.fonts.add(f);
document.body.style.fontFamily = "newfont, serif";
In both cases, the loading of the font resource “newfont.woff” will be initiated by the layout engine,
just as other ''@font-face'' rule fonts are loaded.
Omitting the addition to
document.fonts means the font would never be loaded
and text would be displayed in the default serif font:
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";
To explicitly preload a font before using it,
authors can defer the addition of a new {{FontFace}} to a {{FontFaceSet}} until the load has completed:
var f = new FontFace("newfont", "url(newfont.woff)", {});
f.load().then(function (loadedFace) {
document.fonts.add(loadedFace);
document.body.style.fontFamily = "newfont, serif";
});
In this case, the font resource “newfont.woff” is first downloaded.
Once the download completes,
the font is added to the document's {{FontFaceSet}},
the body font is changed,
and the layout engine uses the new font resource.
The FontFaceSource interface
[NoInterfaceObject]
interface FontFaceSource {
readonly attribute FontFaceSet fonts;
};
Document implements FontFaceSource;
WorkerGlobalScope implements FontFaceSource;
Any document, workers, or other context which can use fonts in some manner must implement the {{FontFaceSource}} interface.
The value of the context’s {{fonts}} attribute is its font source,
which provides all of the fonts used in font-related operations,
unless defined otherwise.
Operations referring to “the font source” must be interpreted as referring to the font source of the relevant context in which the operation is taking place.
For any font-related operation that takes place within one of these contexts,
the {{FontFace}} objects within the font source are its available font faces.
Worker FontFaceSources
Within a Worker document, the font source is initially empty.
Note: {{FontFace}} objects can be constructed and added to it as normal,
which affects CSS font-matching within the worker
(such as, for example, drawing text into a {{CanvasProxy}}).
Interaction with CSS’s ''@font-face'' Rule
The set entries for a document's font source
must be initially populated with all the CSS-connected {{FontFace}} objects
from all of the CSS ''@font-face'' rules in the document's stylesheets,
in document order.
As ''@font-face'' rules are added or removed from a stylesheet,
or stylesheets containing ''@font-face'' rules are added or removed,
the corresponding CSS-connected {{FontFace}} objects
must be added or removed from the document's font source,
and maintain this ordering.
Note: Authors can still maintain references to a removed {{FontFace}},
even if it's been automatically removed from a font source.
As specified in [[#font-face-css-connection]], though,
the {{FontFace}} is no longer CSS-connected at that point.
All non-CSS-connected {{FontFace}} objects must be sorted after the CSS-connected ones,
in insertion order.
Note: It is expected that a future version of this specification
will define ways of interacting with and querying local fonts as well.
API Examples
To show content only after all font loads complete:
document.fonts.ready().then(function() {
var content = document.getElementById("content");
content.style.visibility = "visible";
});
Drawing text in a canvas with a downloadable font, explicitly
initiating the font download and drawing upon completion:
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);
A rich text editing application may need to measure text elements
after editing operations have taken place. Since style changes may
or may not require additional fonts to be downloaded, or the fonts
may already have been downloaded, the measurement procedures need to
occur after those font loads complete:
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);
}
The {{loadingdone}} event only fires after all font related loads have completed
and text has been laid out without causing additional font loads:
<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>納豆はいかがでしょうか
In this situation, the user agent first downloads “latinserif.woff”
and then tries to use this to draw the Japanese text.
But because no Japanese glyphs are present in that font,
fallback occurs and the font “mincho.woff” is downloaded.
Only after the second font is downloaded and the Japanese text laid out does the {{loadingdone}} event fire.
The "unused" font isn't loaded,
but no text is using it,
so the UA isn't even
trying to load it.
It doesn't interfere with the {{loadingdone}} event.
Changes
Changes from the May 2014 CSS Font Loading Last Call Working Draft:
-
Corrected the async algorithms to use "queue a task" language,
to ensure that side-effect timing is well-defined.
Acknowledgments
Several members of the Google Fonts team provided helpful feedback on font load events,
as did Boris Zbarsky, Jonas Sicking and ms2ger.