getUsedFontFaces() should provide font weights as well
Categories
(Core :: Layout: Text and Fonts, defect)
Tracking
()
People
(Reporter: harth, Unassigned)
References
(Blocks 1 open bug)
Details
Attachments
(2 files)
Comment 1•11 years ago
|
||
Comment 2•11 years ago
|
||
| Reporter | ||
Comment 3•11 years ago
|
||
Updated•3 years ago
|
Comment 4•11 months ago
|
||
We are still working around this topic. I think we should stop piling up workarounds on font-face to style conversions.
As Nicolas highlighted on phabricator, the main challenge is about having many @font-face using the same font name. See the following test page:
data:text/html,<meta charset=utf8><style>@font-face {font-family: "Amstelvar VF";src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")format("woff2-variations");font-weight: 100;font-style: normal;}@font-face {font-family: "Amstelvar VF";src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")format("woff2-variations");font-weight: 900;font-style: oblique;}p {font-family:"Amstelvar VF";font-size: 4rem;margin: 10px;} p.big {font-style: oblique;}</style><p>Hello</p><p class="big">World</p>
@font-face {
font-family: "Amstelvar VF";
src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2") format("woff2-variations");
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: "Amstelvar VF";
src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2") format("woff2-variations");
font-weight: 900;
font-style: oblique;
}
It isn't clear to me how src: local() should be used?
It sounds like we at least have to involve new @font face rules with unique names to be used by devtools previewer code.
Comment 5•11 months ago
|
||
Jonathan, I'm not sure I follow exactly your idea around src: local. Would you have some time to explain how we should be using it (see previous comment)?
Comment 6•11 months ago
|
||
Comment 4 (and the linked phabricator comment) seems to be about webfonts, whereas the original issue here (comment 0, comment 1) was about system-installed fonts (i.e. those simply matched from a font-family name, or from prefs/fallback, not webfont resources). So there are a couple of different scenarios possible, I think. To avoid confusion, can you clarify exactly what you're needing to do? Which case(s) are at issue here?
src:local(...) would provide a way to access a specific installed font (without depending on font-weight/font-style/font-stretch to select the right face from a family), which I think was the original issue here. Suppose getUsedFontFaces() tells you one of the fonts used was "Helvetica Neue Medium Italic". You could select that font (e.g. in canvas) with something like ctx.font = "500 italic 20px 'Helvetica Neue'", but that depends on knowing the appropriate weight number to pick the face you want, which may be tricky.
So instead, if you do something like @font-face { font-family: "HelvNeueMedIta"; src: local("Helvetica Neue Medium Italic"); }, then the family name "HelvNeueMedIta" will refer to a single-face font family that only contains the desired face, so you can use it as ctx.font = 20px HelvNeueMedIta and not worry exactly what its weight value was.
An alternative to generating @font-face rules would be the CSS Font Loading API: so something like
f = new FontFace("HNMI", "local('Helvetica Neue Medium Italic')");
document.fonts.add(f);
ctx.font = "20px HNMI";
should also work to use a specific face, identified by the face name as returned by getUsedFontFaces().
But this is all installed system font stuff; if there's a question here about webfonts, that's different (src:local() will by definition not access those).
Comment 7•11 months ago
|
||
Thanks a lot for your prompt and helpful response!
I'm actually discovering all the complexity of fonts and didn't realize there was a difference between installed system fonts and webfonts!
But the goal is the same: how to correctly preview any arbirary font-face.
This is all about this DevTools logic, called from there, which tries to translate the font face's CSSFamilyName, font-style and font-weight to canvas's font and fillStyle.
I tend to think our current approach is a dead end full of regressions. (bug 1105572, bug 1945809, bug 1953135, bug 1952153 and today bug 1954905).
From there I have two very different questions (feel free to pick only one of the two)
-
let's ignore our current implementation.
How would you try to achieve rendering previews for any arbitraryfont-face, assuming you retrieved them from getUsedFontFaces. Do not restrict yourself to render it in canvas. Any privileged JS way to render a font. -
let's improve our canvas approach.
I thought that we could simplify our current canvas based approach by only referring to the font-family name. But may be I misunderstand the whole complexity of fonts. Nicolas investigated this in phabricator D242117 and highlighted the challenge of the many fonts using the same font-family name (comment 4 example). I then thought that we could introduce some intermediate font, similarly to yoursrc: localtrick to apply the default font style and weight... but it isn't clear how to do such thing with same-name fonts.
Otherwise Nicolas original quest on D242117 was to parse and interpret font-facefont-face'sfont-styleandfont-weightin order to translate them into the canvasfontattribute. But I feel that's imperfect (the mean in weight for example) and hard to maintain.
Comment 8•11 months ago
|
||
There are a couple of different cases to consider, because the faces that getUsedFontFaces() returns may be either system-installed fonts or webfonts defined via @font-face.
If it's a webfont, I think it should work pretty well to use the CSSFamilyName from the returned face (which IIRC will just be a single family name, it won't be a font-family list or a CSS generic or whatever -- so the stuff ), together with the font-weight and -style (and -stretch, for completeness, though it's much less commonly used) to construct a font shorthand to use in the canvas. That seems to be working OK in some simple tests, afaics. The one "gotcha" is that the rule might use ranges for the attributes, which are not valid in the font shorthand; I see that's what was recently reported in bug 1954905.
But if it's an installed font (so there's no associated @font-face rule or webfont resource), then it may have been requested in CSS by a font-family name and associated attributes (font-style, font-weight, font-stretch), or it might not be mentioned in the CSS at all if it's a prefs or fallback font. But we don't necessarily know exactly what attributes to use to ensure we get the right face. (And this is the case where currently, devtools just shows the same "regular" preview for all the various faces.) But we can directly target the resulting face by constructing a new @font-face rule that uses src:local() with the face (not family!) name from the returned face's name attribute. That should allow the correct face to be previewed.
Given that understanding, I think both the webfont and installed-font cases ought to work reasonably well. I've played around a bit with a strawman patch which I will post here for information, but you'll no doubt want to do some tidying up at the very least -- I really don't know my way around JS and front-end code.
One thing to note is that in the case of variable fonts, the same font resource may be providing multiple styles on the page: e.g. the same resource might be rendering as "light", "regular", and "bold" weights. I think that in such a case, we may only get one entry in the getUsedFontFaces() list (because it really is a single resource!), so what the preview shows will not match all the content using that font, but it should be a representative example of what the resource looks like.
Comment 9•11 months ago
|
||
Comment 10•11 months ago
•
|
||
The patch above seems to work ok for me (only tested on macOS for now). I don't much like its approach of inserting a temporary <style> element into the doc, but when I tried to use the CSS Font Loading API instead (new FontFace(....)), this didn't seem to be available to whatever context we're in here. (Obviously, I have very little idea what I'm messing with....)
Anyhow, I hope this helps indicate a workable way forward! Please feel free to take over the patch if it's useful, or to steal ideas from it and do a better implementation.
Comment 11•11 months ago
|
||
Thanks again for your prompt response and your code suggestion!
Your patch is following the same pattern as Nicolas bug 1954905 for the font-weight: Manually transform the ranges string into a mid-range value. That was the particular bit I was willing to avoid. Is this a mandatory operation, or it is required because of <canvas>'s font shorthand limitation? Would we also have to do that if we were using another way to render the text?
Comment 12•11 months ago
|
||
Well.... if the font resource has a range for the font-weight (or font-style/font-stretch) descriptor, that's because it is a variable font. It may be getting used with many different values of the font-weight property (maybe it's even being animated so it's constantly changing!), yet it's just a single font resource.
The issue with this for devtools is that it has to decide what to use as a representative sample for the preview, given that the resource has variable appearance. You could use the weight from an element that's using that font, but if it's in use for several elements with different weight properties, how would you pick which one to use? So aiming for the middle of the value range seemed like a reasonable heuristic.
No matter how you're rendering the text -- even if it were an HTML element rather than canvas drawing -- you'd need to decide somehow what font-weight (etc) properties to apply, whether they're specified via the font shorthand or as individual properties.
Another option -- perhaps a better choice, actually -- would be to call getVariationAxes() on the InspectorFontFace, search the array of variation axes for the axis with the tag wght, and use its defaultValue for rendering the preview. Though you'd still need to have some kind of fallback code path for the case when no matching axis is found. (Although it'd be fairly pointless, there's nothing to stop an author giving a range descriptor for a font resource that is not actually a variable font!)
Comment 13•11 months ago
|
||
(In reply to Jonathan Kew [:jfkthame] from comment #12)
Another option -- perhaps a better choice, actually -- would be to call
getVariationAxes()on theInspectorFontFace, search the array of variation axes for the axis with the tagwght, and use itsdefaultValuefor rendering the preview.
...though if the @font-face descriptor has a limited range that does not include the default from the actual font, it should be clamped to the nearest end of the range, as that will constrain what values the browser actually uses.
So if getVariationAxes() returns a wght axis with a range of [100..900] and a defaultValue of 400, but the @font-face rule had the descriptor font-weight: 700 900, this resource will only be used at those bold weights, and it would be better to use 700 for the preview, rather than the resource's default of 400.
To sum up, then, I think my preferred suggestion is to use the defaultValue of the relevant axis, if present, constrained to the range given by the descriptor. Yeah, it's not a simple answer but that's because font resources are not a simple thing!
(And note that if the descriptor gives a single value, then life is simple: just use that. But if it is absent altogether then it is implicitly the full range, so querying the resource for its default is the best option.)
Comment 14•11 months ago
|
||
(In reply to Jonathan Kew [:jfkthame] from comment #12)
The issue with this for devtools is that it has to decide what to use as a representative sample for the preview, given that the resource has variable appearance.
It may actually be good to discuss a bit about that.
You could use the weight from an element that's using that font, but if it's in use for several elements with different weight properties, how would you pick which one to use?
Trying to lookup with actually used weight in some existing DOM Element sounds interesting but quite complex!
Not only complex to implement, but also to explain as this wouldn't be a list of available font faces anymore.
So aiming for the middle of the value range seemed like a reasonable heuristic.
I have some doubt about the middle?
I would personally assume that the preview would match the one when using the font-face with no additional css/font settings.
When using the font-face without any font-weight value, we seem to be matching the low value in the range, is that consistant?
Here is a test page:
data:text/html,<meta charset=utf8><style>@font-face {font-family: "Amstelvar VF";src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")format("woff2-variations");font-weight: 500 900;}p {font-family:"Amstelvar VF";font-size: 4rem;margin: 10px;} p.middle {font-weight: 700;} p.high {font-weight: 900;}</style><p>Default</p><p class="middle">Middle</p><p class="high">High</p>
The issue with picking the middle is we don't surface that middle weight value in the font panel, and if I want, as a web dev, reproduce that rendering, I'll have a to understand that devtools picked a middle value for font-weight.
Would that sound reasonable to render the preview with default style/weight?
We would still benefit from your patch, to use intermediate font-face dedicated to devtools previews code to distinguish same-family-name fonts.
i.e. such usecase:
data:text/html,<meta charset=utf8><style>@font-face {font-family: "Amstelvar VF";src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")format("woff2-variations");font-weight: 100;font-style: normal;}@font-face {font-family: "Amstelvar VF";src: url("https://mdn.github.io/shared-assets/fonts/variable-fonts/AmstelvarAlpha-VF.woff2")format("woff2-variations");font-weight: 900;font-style: oblique;}p {font-family:"Amstelvar VF";font-size: 4rem;margin: 10px;} p.big {font-style: oblique;}</style><p>Hello</p><p class="big">World</p>
Comment 15•11 months ago
|
||
(In reply to Alexandre Poirot [:ochameau] from comment #14)
Would that sound reasonable to render the preview with default style/weight?
Yeah, that's what I was aiming for with the suggestion to use getVariationAxes() and look up the defaultValue of the axis. (Only needed if the associated descriptor has a range, or isn't present in the rule.)
Comment 16•11 months ago
|
||
I've updated the strawman patch here to use defaultValue instead of calculating a midpoint; see what you think about this.
Comment 17•11 months ago
|
||
Comment 18•11 months ago
|
||
Sorry if I wasn't being clear enough, but here is what I had in mind doing in order to get the default behavior of each font.
Really keep something the simplest possible and use your pattern of instantiating font-face for devtools to do that.
This prevents having to dig into variation axes default value as well as parse/interpret any string (weight & style ones).
Do I miss some particular usecase ?
Comment 19•11 months ago
|
||
I left a couple of comments in phabricator: in short, I think there are various cases (descriptors with ranges; descriptors not present in the rule) that will not work with that patch as it stands.
Comment 20•11 months ago
•
|
||
Jonathan, we're getting into an issue on OSX.
On data:text/html,<meta charset=utf8><style>body { font-family: system-ui; }</style>Hello , we're getting the following object InspectorFontFace { fromFontGroup: true, fromLanguagePrefs: false, fromSystemFallback: false, name: "System Font", CSSFamilyName: ".SF NS", CSSGeneric: "", ranges: [], rule: null, srcIndex: -1, URI: "" }
and so with your suggestion, we're trying to add a font with a local("System Font") src, which does trigger a network error.
(can be reproduced with new FontFace("MyFont", 'local("System Font")').load()).
Do you know if that's an issue with the suggested solution, or with the InspectorFontFace#name property, or in the platform (unlikely I guess, I'm getting a similar error in Chrome)
Comment 21•11 months ago
|
||
Ah, I missed https://phabricator.services.mozilla.com/D244720#8454908 , sorry
Description
•