Closed Bug 471915 Opened 16 years ago Closed 12 years ago

feature request: event for when a downloaded font is ready to use

Categories

(Core :: CSS Parsing and Computation, enhancement)

enhancement
Not set
normal

Tracking

()

RESOLVED DUPLICATE of bug 835247

People

(Reporter: blizzard, Unassigned)

Details

Attachments

(1 file)

I'm doing a demo that includes using a downloaded font in a canvas. It works fine except that it uses a fallback font for drawing on the canvas when the page is first loaded. This isn't a problem with text that's driven by the layout engine, since when the font is finished loading the style and layout are automatically re-resolved. But it's a problem if I'm using the text API for canvas. I don't know when to re-draw and/or can't wait to know when it's safe to draw.
Severity: normal → enhancement
OS: Mac OS X → All
Hardware: x86 → All
Version: unspecified → Trunk
Some of our reftests use an onresize hack to work around the lack of such an event (layout/reftests/font-face/resize-detector-iframe.html).
I am so not the right person to do this, but here's a strawman proposal Event: "fontload" Attributes: readonly sequence<string> loaded; readonly sequence<string> pending; The elements of |loaded| and |pending| would be an array of CSS font specification strings, e.g. "12px serif ...". (XXX should these be exactly what the page specified or some normal form?) |loaded| would contain the list of fonts "loaded" (more specifically, renderable by the UA; e.g., canvas.drawText() with the specified font would actually use that font (modulo undefined glyphs)) since the last "fontload" event. |pending| would contain the list of /all/ @font-faces created but not yet renderable, not just the ones since the last fontload event. The target of the fontload events should be the document (body?) containing the CSS or script that created the @font-face. If there are multiple such documents in the same window, a fontload event is delivered to each. For the CSS that must be loaded before "onload" is fired, do we currently wait for all the @font-faces created by that CSS to be renderable too before firing "onload"? I don't know. If we do, then I think those "static" @font-faces, that is those that "onload" would wait for, should be ignored by "fontload". Otherwise "fontload" should be used for all @font-faces. (If @font-faces are "already" loaded upon creation, say because they've been cached, then fontload should still fire but possibly only once, with no elements in the |pending| array.) Thoughts?
(In reply to comment #2) > Event: "fontload" > Attributes: > readonly sequence<string> loaded; > readonly sequence<string> pending; Maybe we should include readonly sequence<string> failed; too for those that couldn't be fetched from the network or didn't pass the sanitizer(s).
What's the use-case driving the need for the loaded and pending attributes?
We are rendering with custom fonts into canvas. We load them via CSS but we can't tell when the loading is done.
OK, but do you need these attributes for that? Or could you just have a boolean "all font loads are done"?
I think we want to fire of font loading and then render the first page when all fonts for the first page are done, and immediately render more once more is loaded.
The only way to trigger font loading right now is to draw using the fonts you need. In your case, that would mean drawing to a canvas. If we have an attribute on the canvas for "font loads pending", and fire an event at the canvas when all the fonts have loaded, would that suffice?
Yes and no. That would work for our canvas backend, but the exact same problem exists for SVG. The proposal above is intended to cover both. We might be rendering multiple pages concurrently that need different subsets of fonts. If we got an event telling us enough fonts were loaded so that we could render pages 1 and 3 but 2, say, then that would make things better.
(In reply to comment #9) > We might be rendering multiple pages concurrently that need different > subsets of fonts. If we got an event telling us enough fonts were loaded so > that we could render pages 1 and 3 but 2, say, then that would make things > better. (Sorry, this was in reply to "What's the use-case driving the need for the loaded and pending attributes?".)
(In reply to comment #9) > That would work for our canvas backend, but the exact same problem exists > for SVG. The proposal above is intended to cover both. Although to be fair, we could add some hackity hacks that used <canvas> to determine loaded fonts for use in SVG documents. But I'd rather not hack unless there's a good reason to.
OK, what if we had a single "all fonts loaded" event that fires either at a <canvas> or at the document (or maybe even just the document), and a separate CSSOM API that lets you ask for a given element, "are there any pending font loads needed by this element"? The problem I'm trying to avoid here is having to return a list of strings and let authors grapple with that. Multiple strings can refer to a given font, and I'm not even sure a single string only refers to one font; in fact I'm pretty sure with scoped stylesheets that would no longer be true. It also doesn't sound particularly easy to use for most authors.
(In reply to comment #12) > OK, what if we had a single "all fonts loaded" event that fires either at a > <canvas> or at the document (or maybe even just the document), and a > separate CSSOM API that lets you ask for a given element, "are there any > pending font loads needed by this element"? If we had a single "all fonts loaded" event that was only fired at the document, I'm not sure how we would solve the problem of determining when we could render pages 1 and 3 to canvas but need to wait longer on page 2. The CSSOM query would be great for SVG. > The problem I'm trying to avoid here is having to return a list of strings > and let authors grapple with that. Multiple strings can refer to a given > font, and I'm not even sure a single string only refers to one font; in fact > I'm pretty sure with scoped stylesheets that would no longer be true. It > also doesn't sound particularly easy to use for most authors. OK, that makes sense. I'm OK with an initial implementation that fires an "all fonts loaded" event to particular canvases. We can implement everything we need for pdf.js on top of that. Using that mechanism for SVG too is a hack, but I guess we could look into adding the CSSOM query when we cross that bridge.
(In reply to comment #13) > Using that mechanism for SVG too is a hack "The mechanism" == "using canvas to determine when it's OK to unhide an SVG document". Firing allfontsloaded at the SVG doc would work great too.
... although it would be much harder since SVG docs can have subdocs and canvases etc., and it might be tricky to specify behavior for all that. Maybe the canvas hack really is the best way to determine readyness of fonts in SVG docs. OK, no sweat.
(In reply to comment #13) > (In reply to comment #12) > > OK, what if we had a single "all fonts loaded" event that fires either at a > > <canvas> or at the document (or maybe even just the document), and a > > separate CSSOM API that lets you ask for a given element, "are there any > > pending font loads needed by this element"? > > If we had a single "all fonts loaded" event that was only fired at the > document, I'm not sure how we would solve the problem of determining when we > could render pages 1 and 3 to canvas but need to wait longer on page 2. The > CSSOM query would be great for SVG. Er, yeah. I guess you would need to get the event "some font loaded" for each font, although we could coalesce them, so it should be "some fonts loaded".
> For the CSS that must be loaded before "onload" is fired, do we currently wait > for all the @font-faces created by that CSS to be renderable too before firing > "onload"? Yes. See bug 477880.
Attached patch WIPSplinter Review
Here's the approach I'm taking. This patch works, but depends on a few gallons of DOM-event goop that I split into another patch. The basic strategy is - watch successful text draw/measure calls to 2d <canvas> contexts -- if draw/measure used a gfxFontGroup that might have selected a not-fully-loaded web font for any glyph, add the CSS descriptor from which that gfxFontGroup was created to a set of "pending font groups" -- register with the enclosing nsPresContext to hear about web-font updates - whenever the PresContext notifies that web-font loaded-ness changed, iterate over the list of pending font groups -- re-resolve them to gfxFontGroup -- check if any font in the gfxFontGroup is still pending -- If not, remove the descriptor from the set of pending fonts -- if there are no more "pending font groups", dispatch MozFontsLoaded to the <canvas> I like this approach because <canvas> is mostly oblivious to the details of font loading; it only needs to know when web-font state might have changed. <canvas> also needs to know almost nothing about the font descriptors it tracks. On the other hand, because <canvas> knows so little about font loading, it's forced to use slow ways to check loaded-ness. I think this is probably fine for the web because the alternatives are much worse. One detail that's not great in this patch is that @font-faces referring to the same name (say "Arial") but at different sizes (say "5px" and "10px") will be treated as separate font groups. IMHO this is a second-class concern. A bigger problem in this patch is that web fonts which fail to load because of a network error, e.g., are not distinguishable to script listeners from normally-loaded fonts. This is kind of a nice property of the patch to nsCanvasRenderingContext2D, but not so great for web authors (potentially). I'm not sure how to get more detailed information about font load vs. error status in nsCanvasRenderingContext2D, but a followup bug might be the right place to figure that out.
Assignee: nobody → jones.chris.g
Attachment #541917 - Flags: feedback?(bzbarsky)
I don't quite follow why this needs to or should be dependent on access via <canvas> related text usage? To me, the logical place for a trigger would be in nsFontFaceLoader::OnStreamComplete: http://mxr.mozilla.org/mozilla-central/source/layout/style/nsFontFaceLoader.cpp#222 Depending upon whether you want events to trigger on font load failures or not, it might make sense to modify gfxUserFontSet::OnLoadComplete to return more than just a boolean as to whether it failed or not. I really don't think this should be canvas-specific at all, nor should the canvas code need to be font-aware at all. Font load events would be interesting for lots of use cases other than just those associated with canvas elements.
(In reply to comment #20) > I don't quite follow why this needs to or should be dependent on access via > <canvas> related text usage? See discussion above. This is intended to be a general feature, but we're spec'ing it for <canvas> because that seems to be the cleanest way that satisfies the use cases. Defining what to do with subdocuments, canvases, hidden/display:none elements, etc. sounds like a tar baby to me. The implementation would be similarly hairier. (FWIW, I wrote the WIP literally yesterday evening, and it's about 90% complete.) > Depending upon whether you want events to trigger on font load failures or > not, it might make sense to modify gfxUserFontSet::OnLoadComplete to return > more than just a boolean as to whether it failed or not. Load failures are an interesting question. The WIP punts on them. We should definitely decide on a behavior for them, possibly in a followup.
From the above discussion I thought we would add some kind of "fontload" event on documents, and provide a fontsLoaded attribute on elements. For non-canvas elements we would check the text frame descendants to see if their 'font-family' refers to fonts with pending loads. For canvas elements, we would keep track of fonts whose loads were pending when we used those fonts to draw text to the canvas, removing fonts from the set when their loads are complete, and fontsLoaded would return true when that set is empty (much like Chris's patch).
OK, that wasn't my understanding :). Spec'ing and implementing this for "everything" sounds like it would increase complexity by maybe an order of magnitude. What use cases does it support that the <canvas> proposal doesn't? I think either way, the canvas bits of this will be special and magical enough to merit their own discussion. Here are some problems that've come to mind - multiple contexts for one canvas. Need to track the union of the contexts, not too controversial I imagine. - canvas1.drawImage(canvas2) ... hmm, *scratches head* - canvas.drawWindow(window) ... ? This API is starting to look like it's on shaky ground. Will do some more thinking.
(In reply to comment #23) > OK, that wasn't my understanding :). > > Spec'ing and implementing this for "everything" sounds like it would > increase complexity by maybe an order of magnitude. What use cases does it > support that the <canvas> proposal doesn't? Non-canvas dynamic font loading, use within a canvas element is just one of many possible uses of this! I'm somewhat puzzled as to why this adds such complexity when the loaded fonts are associated with the document and not individual elements. Seems like this should *reduce* the complexity, not increase it.
Well, I'm not sure what exactly interface you have in mind. I'm just arguing from the perspective that, pages can always draw to a hidden 1x1 canvas to detect font loaded-ness, with the canvas magic we discussed above. That's a spec and implementation with a small surface area, and full generality (unless there's something I'm missing). But I'm not sure the canvas parts are well-defined yet.
I wonder if adding CSS pseudo-classes like :fonts-loaded and :fonts-loading is a reasonable approach. The simplest use for this kind of feature would probably then be |#foo:fonts-loading { visibility: hidden; }|. For my use case, if I got a "fontloaded" event, I could re-run querySelector() on ... some things. The "fontloaded" event could be triggered by |UserFontSetUpdated();|, as comment 20 and 22 suggest. Problem 1 (for my use case) would be how to track font-loadedness reliably without putting something onscreen. Maybe some transparent divs with the right "font" styling? (Assuming we don't optimize their frames away.) Problem 2 would be implementing the pseudo-classes efficiently. It could be done but might be hard. Will think some more.
Just for the pseudoclass bit; haven't really followed the rest: 1) Implementing the matching efficiently should be easy if its supposed to match if _any_ fonts are loading. If we're only talking fonts used in the element, that's harder. 2) For implementing dynamic updates, last I checked we blew away style data completely when fonts load. That includes redoing all selector matching on the page. So there's nothing to be done here.
(In reply to comment #27) > Just for the pseudoclass bit; haven't really followed the rest: > > 1) Implementing the matching efficiently should be easy if its supposed to > match > if _any_ fonts are loading. If we're only talking fonts used in the > element, > that's harder. I would like it to only match only fonts used in the element (and descendents). > 2) For implementing dynamic updates, last I checked we blew away style data > completely when fonts load. That includes redoing all selector matching > on > the page. So there's nothing to be done here. Yep, that's still the case. Will more general machinery efficiently reresolve on element insertion/deletion/move? (Or did you have the "matches if any fonts are loading" semantics in mind.)
> I would like it to only match only fonts used in the element (and descendents). That takes a bit more work, then... > Will more general machinery efficiently reresolve on element > insertion/deletion/move? Nope. That's a harder problem... I'm thinking about it for other reasons, but we don't have a great solution yet.
OK. Maybe the approach of comment 22 minus the canvas part is the best solution at the moment.
(In reply to comment #22) > From the above discussion I thought we would add some kind of "fontload" > event on documents, This part is very easy. > and provide a fontsLoaded attribute on elements. For > non-canvas elements we would check the text frame descendants to see if > their 'font-family' refers to fonts with pending loads. This part is quite easy if you follow the work that Jonathan did for inIDOMUtils::GetUsedFontFaces. > For canvas elements, > we would keep track of fonts whose loads were pending when we used those > fonts to draw text to the canvas, removing fonts from the set when their > loads are complete, and fontsLoaded would return true when that set is empty > (much like Chris's patch). This part is very much like what you described in comment #19, isn't it?
The fonts-loading selector is an interesting idea. Since, as Boris observed, dynamic updates are pretty easy, it could be implemented much like comment #22, but we'd have to try to cache on each frame whether all descendants have fonts loaded or not (in a tristate formed from two state bits, probably). So it's more work but probably a slightly better API than comment #22.
(In reply to comment #31) > (In reply to comment #22) > > From the above discussion I thought we would add some kind of "fontload" > > event on documents, > > This part is very easy. Trivial, even. > > and provide a fontsLoaded attribute on elements. For > > non-canvas elements we would check the text frame descendants to see if > > their 'font-family' refers to fonts with pending loads. > > This part is quite easy if you follow the work that Jonathan did for > inIDOMUtils::GetUsedFontFaces. Thanks for the pointer. The attribute doesn't seem like the right tool for the job to me, but I concede it's probably the best way forward. > > > For canvas elements, > > we would keep track of fonts whose loads were pending when we used those > > fonts to draw text to the canvas, removing fonts from the set when their > > loads are complete, and fontsLoaded would return true when that set is empty > > (much like Chris's patch). > > This part is very much like what you described in comment #19, isn't it? Yes, but see comment 23. If the goal of the API is to mean, "now all fonts your canvas depends on have loaded, go ahead and redraw", then it's not clear to me what canvas should do with drawImage/drawWindow (and pattern fill). I think the options are "be wrong" or "be complex and (potentially) slow". So maybe it's best to not go there.
(In reply to comment #33) > Yes, but see comment 23. If the goal of the API is to mean, "now all fonts > your canvas depends on have loaded, go ahead and redraw", then it's not > clear to me what canvas should do with drawImage/drawWindow (and pattern > fill). I think the options are "be wrong" or "be complex and (potentially) > slow". So maybe it's best to not go there. I think it's totally fine, reasonable and even expected for the attribute/selector to only check fonts that you drew directly to the canvas, and leave it up to Web authors to track indirect dependencies themselves.
Why add a special case for canvas if we have a more general impl that solves the same problem? (Yes, this is the opposite of what I said earlier, but I promise the same motivation underlies both statements, limiting surface area of spec/impl)
An implementation that walks the frame tree looking for text nodes which have fonts that haven't loaded yet won't work for <canvas>. But I know you know that, so I guess I'm misunderstanding your question.
I think we've agreed that we want to implement the attribute or selector for normal DOM elements. So, for my use case, I can create say transparent divs with style="font-family: [web font]" and wait for fontload events and check selectors. (Or whatever.) Given that any possible font-loaded check can be implemented using the DOM elements + attribute/selector, why would we still want to add the special case for canvas? What use case does the special-case for canvas solve that the more general attribute/selector doesn't?
(In reply to comment #37) > I think we've agreed that we want to implement the attribute or selector for > normal DOM elements. So, for my use case, I can create say transparent divs > with style="font-family: [web font]" and wait for fontload events and check > selectors. (Or whatever.) > > Given that any possible font-loaded check can be implemented using the DOM > elements + attribute/selector, why would we still want to add the special > case for canvas? Because your suggestion for authors to work around lack of direct canvas support is a horrible hack? :-)
But I don't mind if you only implement the non-canvas version in a first pass.
I think it would be hard for us to find supporters of the proposed canvas spec. But that's just a guess, we can try pitching it to whatwg or whomever.
(In reply to comment #8) > The only way to trigger font loading right now is to draw using the fonts > you need. I wonder if we should start by addressing this issue. If I want to draw with a downloadable font in canvas, it seems rather hacky to say that I have to first draw something (invisible) using that font specification just in order to trigger the download, and then later do my "real" drawing after I detect that the font is actually available. What would you think of adding API (perhaps to nsIDOMCSSFontFaceRule?) that can be used from script to explicitly trigger font downloading? And maybe that would also be the place to expose properties indicating whether the download is in progress/complete/failed.
That sounds OK to me, although I don't know how to get to an nsIDOMCSSFontFaceRule from script (I'm sure google can tell me). I think that would be orthogonal to "fontload" event and element attribute/selector, but fontload event and nsIDOMCSSFontFaceRule loaded-ness property would work for the pdf.js use cases.
(In reply to comment #41) > I wonder if we should start by addressing this issue. If I want to draw with > a downloadable font in canvas, it seems rather hacky to say that I have to > first draw something (invisible) using that font specification just in order > to trigger the download, and then later do my "real" drawing after I detect > that the font is actually available. Or you could just do the real drawing every time and redraw every time a font has loaded (possibly, redraw when a font has loaded and there are no more pending downloads). It doesn't seem hacky to me, it's probably simpler for authors than manually triggering font downloads and waiting for events to run.
So is my feedback still desired here?
(In reply to comment #41) > What would you think of adding API (perhaps to nsIDOMCSSFontFaceRule?) that > can be used from script to explicitly trigger font downloading? And maybe > that would also be the place to expose properties indicating whether the > download is in progress/complete/failed. The problem with this approach is that it requires explicit knowledge of style mapping, the script needs to figure out the computed weight/style/width combination to figure out which rule to trigger. Not impossible but kind of grotty.
By the way, we really should be bouncing ideas off of the Webkit folks, as I know they're interested in these sorts of capabilities.
Comment on attachment 541917 [details] [diff] [review] WIP This isn't the patch you're looking for.
Attachment #541917 - Flags: feedback?(jfkthame)
Attachment #541917 - Flags: feedback?(bzbarsky)
Can this proposal also be used by bug 468568 comment 2? (Sorry but I don't understand all the details behind web fonts right now...)
To meet the font needs as they exist in Processing.js as well as websites in general, such as a need for good metrics functions and onload events, I wrote https://github.com/Pomax/Font.js a while ago. This has sparked a significant amount of discussion on what browsers should be able to do for fonts, resulting in the list of ideas currently up on https://etherpad.mozilla.org/UAAaHYAWPh After discussing this with David Humphrey and others at Seneca College there is enough animo to see if the more basic functionalities on this list can be implemented in Firefox, but it would be good to get others' opinions as well. This bug appears to have moved the responsibility for most font things to the CSS layer, which I'm going to have to disagree with - things on the CSS side don't exist, as far as JavaScript is concerned for instance, and any work that we do will focus on making as much functionality available in plain sight. As such, it's possible that the ideas we have so far warrants filing a new bug instead, with a note that this bug discussion also exists, but isn't strictly speaking related - which course of action is best? We will be turning the etherpad into a better worked out mozwiki page for collaborative "this is not an official spec but let's work on this to see if we can get to something that makes sense as one", and good ideas are, naturally, welcome.
Firebug would also need this. See http://code.google.com/p/fbug/issues/detail?id=2495. Sebastian
There's currently some back and forth about this on www-style. http://lists.w3.org/Archives/Public/www-style/2012Sep/0139.html Event target for font load events, contained as a property of the document: partial interface Document { readonly attribute FontLoader fontloader; }; [Constructor] interface FontLoader : EventTarget { // -- events for one or more fonts loading and completing [TreatNonCallableAsNull] attribute Function? onloading; [TreatNonCallableAsNull] attribute Function? onloadingdone; // -- events for each individual font load [TreatNonCallableAsNull] attribute Function? onload; [TreatNonCallableAsNull] attribute Function? onerror; // async load void loadFont(DOMString font, optional DOMString text); // notify after font loads complete or no loads needed callback FontsReadyCallback = void (); void notifyWhenFontsReady(FontsReadyCallback fontsReady); // "idle" or "loading" readonly attribute DOMString readyState; };
readyState should be an enum, not a string there. I'm not sure I understand how onload/onerror should work if those are supposed to be per-font-load...
Or is the point that the event would tell you which font loaded?
(In reply to Boris Zbarsky (:bz) from comment #53) > readyState should be an enum, not a string there. Several people have told me the current pattern is to use a string instead (e.g. document.readyState). > I'm not sure I understand how onload/onerror should work if those are > supposed to be per-font-load... One of those fires for each font loaded. So if three fonts are needed for a page, "loading" and "doneloading" each fire once, and "load" or "error" fires three times indicating the specific @font-face rule that loaded/failed.
> Several people have told me the current pattern is to use a string instead WebIDL enums _do_ look like strings in JS. Except strings with a limited set of values allowed, as specified in the enum. That's the current best-practice pattern (as opposed to the old pattern of using integers). You can fake it with a DOMString and comments + prose, but you get all that for free if you just use an IDL enum. So in your case, you'd do: enum FontLoaderReadyState { "idle", "loading" }; and then in the interface: readonly attribute FontLoaderReadyState readyState; or so.
Assignee: jones.chris.g → nobody
Blocks: b2g-v-next
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → DUPLICATE
No longer blocks: b2g-v-next
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: