Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-fonts-4] @font-family src: should accept a specifier which lists font requirements #633

Closed
litherum opened this issue Oct 21, 2016 · 17 comments

Comments

@litherum
Copy link
Contributor

litherum commented Oct 21, 2016

When discussing the format() specifier with regards to font variations, we came across diverging behavior between different browsers.

There are two possible mechanisms (that I know of) for fallback:

@font-face {
    ...
    src: url("file-novariations.otf") format("opentype");
    src: url("file-variations.otf") format("opentype", "variations");
}

and

@font-face {
    ...
    src: url("file-variations.otf") format("opentype", "variations"), url("file-novariations.otf") format("opentype");
}

Currently, with either of the above examples, Edge and Firefox would download the variation font even when though those browsers don't (currently) understand variations.

The spec states:

The format hint contains a comma-separated list of format strings that denote well-known font formats. Conformant user agents must skip downloading a font resource if the format hints indicate only unsupported or unknown font formats.

This seems to imply that the format() identifier will be used if any of the arguments are known. Instead, in order to support modern and future font behaviors, web authors should be able to list the requirements that the browser needs to understand in order to use a font.

This could either be changing how format() works, adding a new specifier, or something completely different.

@litherum litherum changed the title [css-fonts-4] Extra unknown arguments to format() specifier should forbid font downloads/uses [css-fonts-4] @font-family src: should accept a specifier which lists font requirements Oct 21, 2016
@tabatkins tabatkins added the css-fonts-4 Current Work label Oct 21, 2016
@svgeesus
Copy link
Contributor

svgeesus commented Oct 21, 2016

So something like
src: url("file-variations.otf") format("opentype") requires("variations");

@svgeesus
Copy link
Contributor

Yes (same for "COLR"), if the stylesheet author didn't want to just use the default fallback.

@tabatkins
Copy link
Member

This seems to imply that the format() identifier will be used if any of the arguments are known.

This is because the purpose of multiple strings in format() is to supply multiple (usually OS-specific) names for the same format (yay real-world compat), not list multiple features that the font requires support for.

Rather than minting a new function, as Chris suggests, we could just expand the grammar for format(), like:

format() = format( [ <format-string> | requires <feature-name> ]# )

src: url("file-variations.otf") format("opentype", requires variations);

@svgeesus
Copy link
Contributor

I prefer the new function, rather than overloading the format specifier. The font/* top level type includes a field for the css @font-face format string, fone for each or each media type. (Indeed, the whole reason we have a format string is because there were no MIME types for fonts, and adding them was strongly resisted at the time). Overloading it with the requires stuff is awkward, a separate function seems cleaner.

@liamquin
Copy link

On Fri, 2016-10-21 at 14:45 -0700, Tab Atkins Jr. wrote:

 
Rather than minting a new function, as Chris suggests, we could just
expand the grammar for format(),

Would a function approach play more nicely with media queries?

@PeterCon
Copy link

What about defining new values that add additional specificity: "opentype-variation", "opentype-colr", etc.?

@jfkthame
Copy link
Contributor

What about defining new values that add additional specificity: "opentype-variation", "opentype-colr", etc.?

The "etc." there worries me.... the number of features/capabilities/requirements that we might want to expose (e.g. variations, COLR glyphs, SVG glyphs, CBDT glyphs, etc) combined with the variety of packaging formats (opentype, woff, woff2, ...) leads to a lot of possible values if we define a unique new value for each combination.

One possible approach, I suppose, would be to define a microsyntax used within the format hint string, e.g. splitting it into tokens on comma or some such character (even hyphen would be possible, I guess, though IMO comma reads better):

src: url("file-colr-var.otf") format("opentype,colr,variations"),
     url("file-svg-var.otf") format("opentype,svg,variations"),
     url("file-var.otf") format("opentype,variations"),
     url("file-colr.otf") format("opentype,colr"),
     url("file-svg.otf") format("opentype,svg"),
     url("file-fallback.otf") format("opentype");

Existing UAs will drop sources with the new-style format hints, avoiding the problem with the format("opentype", "variations") version.

The UA would split the format hint into fragments and use the source only if it supports all the individual requirements listed in its format.

@PeterCon
Copy link

The "etc." there worries me.... leads to a lot of possible values if we define a unique new value for each combination.

Your concern with combinatorial multiplication is valid. I like your alternative.

@behdad
Copy link

behdad commented Nov 1, 2016

The UA would split the format hint into fragments and use the source only if it supports all the individual requirements listed in its format.

I had something like this in mind as well. Ie. more like BCP 47 language tags that need to be broken down and understood. Also, the current option in format() that multiple values can be passed can be used to pass the grandfathered existing keywords for older clients. Eg. format("ot-cff", "opentype"). Where opentype is the old tag and "ot-cff" new one.

@AmeliaBR
Copy link
Contributor

AmeliaBR commented Oct 24, 2017

I was thinking of this issue this week, and thinking that it would be best to build on the OpenType concept of named "tables" for each feature.

So, if a browser should only download a font if it knows what to do with a CPAL (color palette) table, say that. If a browser should only download the font if it knows how to use an FVAR (font variations) table, say that. If the browser should download the font if it knows how to use both a CPAL table and an FVAR table, say that.

This therefore avoids any new debates on naming things, about what a "color" font is or isn't and so on, while keeping it easily open-ended to new features added to OpenType.

As for the syntax, I think it would be best to follow the MIME type with parameters format, which is currently used to specify charsets for text-based files, and codecs for video and audio package formats (see the MIME-sniff spec, or RFC 2046 for the full spec).

I know the format() strings aren't proper MIME type strings to start with (because they don't include the initial font/ part), but might as well follow it as best we can from there on.

So something like:

@font-face {
  font-family: heading-font;
  src: url(fancy-font.woff2) format("woff2;tables=CPAL,FVAR"),
        url(fallback-font.woff2) format("woff2"),
        url(fallback-fallback-font.woff) format("woff"),
        url(how-old-is-your-browser-font.ttf) format("ttf");
}

Of course, you wouldn't need to specify every OpenType table in the format string; only the essential ones that you want the browser to consider as a feature support test.

(Edited to update the link to the RFC to the source spec.)

@AmeliaBR
Copy link
Contributor

Addendum:

It looks like MIME type parameters are supposed to be part of a MIME type registration, and font/otf already has an "outline" parameter to distinguish TTF, CFP, and SVG outlines. But you'd need to contact someone named @svgeesus to get parameters added to the WOFF & WOFF2 registrations.

@svgeesus
Copy link
Contributor

So, if a browser should only download a font if it knows what to do with a CPAL (color palette) table, say that.
Suppose an implementation can handle COLR+CPAL but not SVG +CPAL? OK in that case there is still a single table name that can be used, but sometimes it really is the case of certain combinations of tables or certain parts of tables (OS/2 springs to mind) or certain versions of tables (the many versions of CMAP for example).

I know the format() strings aren't proper MIME type strings to start with (because they don't include the initial font/ part),

I initially planned to use MIME types there, but given significant resistance to the idea of a font/* top level type we went with the 'format' string as a temporary workaround. Temporary meaning two decades or so.

It looks like MIME type parameters are supposed to be part of a MIME type registration,

Yes in general, and yes for the specifics of the font/* top level type. See the registration procedure in RFC 8081.

However, putting files on a web server is significantly complicated if parameters need to be set; and then we also need to deal with legacy and mis-configured servers. Part of the goal of RFC 8081 was to align specifications with actual widely deployed practice.

In my experience, content negotiation of that sort is easier and more likely to work if client driven. So I would rather see a new features() string in the stylesheet than a complex parameter-based addendum to Media Types.

@font-face {
  font-family: heading-font;
  src: url(fancy-font.woff2) format("woff2") features("CPAL,FVAR"),
        url(fallback-font.woff2) format("woff2"),
        url(fallback-fallback-font.woff) format("woff"),
        url(how-old-is-your-browser-font.ttf) format("ttf");
}

@AmeliaBR
Copy link
Contributor

I would rather see a new features() string in the stylesheet

That definitely looks nicer to read. And my main comment was about re-using OpenType table names, anyway, the rest was mostly musings.

One limitation, though: by adding a separate features parameter, instead of overloading format(), wouldn't we break the syntax for existing browsers? So you'd actually need to write (for the near-future, anyway):

@font-face {
  font-family: heading-font;
          /* fallback stack for browsers that don't know features() */
  src: url(fallback-font.woff2) format("woff2"), 
        url(fallback-fallback-font.woff) format("woff"),
        url(how-old-is-your-browser-font.ttf) format("ttf");
  src: url(fancy-font.woff2) format("woff2") features("CPAL,FVAR"),
           /* fallback for browsers that understand features(), but don't support these features */
        url(fallback-font.woff2) format("woff2"); /* assume woff2 support in this case */
}

Which is sub-optimal, but no worse than oodles of other similar fallback redundancies in modern stylesheets.

@litherum
Copy link
Contributor Author

litherum commented Mar 14, 2018

The "etc." there worries me.... leads to a lot of possible values if we define a unique new value for each combination.

Your concern with combinatorial multiplication is valid. I like your alternative.

We are now facing this with font features, font variations, and color fonts. The current system of making longer and longer hyphenated format keywords won't work in the long term.

"woff2;tables=CPAL,FVAR"

I don't think we want more microsyntaxes in CSS. They wreak havoc on object models.

If we chose format("woff2") features("CPAL,FVAR"), wouldn't old browsers then disregard the entire src descriptor? This is probably not what we want. Therefore, we should go with the format("opentype", requires variations) proposal.

This is becoming increasingly important, and we should pick something and add it to the spec.

@svgeesus
Copy link
Contributor

svgeesus commented Apr 3, 2018

If we chose format("woff2") features("CPAL,FVAR"), wouldn't old browsers then disregard the entire src descriptor? This is probably not what we want.

Wait, wouldn't they disregard up to the comma? And if so, isn't that exactly what we want? If old browsers (that don't understand features() ) are also old browsers (that don't understand variations) then this prevents old browsers from attempting to use the variable font. They go on to the fallback font.

So my example means they would reject fancy-font and use fallback-font instead.

Therefore, we should go with the format("opentype", requires variations) proposal.

But without the comma, right?

@w3c w3c deleted a comment from css-meeting-bot Apr 11, 2018
@css-meeting-bot
Copy link
Member

The Working Group just discussed Fonts l4 super-format(), and agreed to the following resolutions:

  • RESOLVED: Parse the value of src throwing out invalid parts like media queries and not like selectors. aka You split on the commas and throw out the pieces you don't understand not the whole thing.
  • RESOLVED: use this: format(<string> [supports (list from spec)#]?)
The full IRC log of that discussion <dael> Topic: Fonts l4 super-format()
<dael> github: https://github.com//issues/633
<dael> myles: There has been much movement in typography industry in the last several years. There are many features fonts can opt into now. One of the tenants of this new world where fonts can have technology inside is browsers that don't support a technology should not trigger downloading a font with that inside.
<dael> myles: In @font-face rules in the past this was done with the format function with the source description.
<dael> myles: Inside the format function it accepts a string. now that we have 3 optional technology we prob shouldn't generate a bunch of strings with tokens. There should be a way for an @font-face to associate a URL with a set of requirements where only if browser fits requirements they should use this.
<dael> myles: That's the issue. We don't have an opinion as to which proposal to do this should be picked. There's one we seem to be zeroing in on in the issue
<dael> ChrisL: We have the format specifier and we don't want the combinatorial thing where we add more things and it explodes. So wexpand the format specifier to that it has a string of what it requires. So exising browser can reject that font. That seems to be where the thread is ending.
<dael> myles: Proposal is do we extend format to extend this.
<dael> Vlad: Hypothetical. Open type font that supports regular + color glyphs + variations. If you can't use color the font provides you the downgrade.
<ChrisL> so for example `format("opentype", requires variations, requires color)`
<dael> myles: Yes. All of these technologies are created such that they are opt in and the browser can still use the font file. Tht's great for backwards compat but these new tech can add significant space in the font file.
<dael> myles: In the case of color glyphs it's a lot. It would be great if browsers didn't have to download more data then needed.
<dael> florian: Is this also avoid the case od a browser choosing to download a variable font when they don't suppor that instead of downloading other fonts.
<dael> astearns: I thought that was correct. Browsers that don't support variable fonts will not download a font with that tag. DOesn't matter if the font in the declarion supports variables, but it's a tag for only if the browser supports variables.
<dael> florian: So fold variable into the rest.
<dael> myles: yes.
<dael> fremy: WE shipped variables and color fonts so if we add this our existing impl will not recognize the new required flags because it doesn't think we know the font. Would have better to think of this before.
<dael> ChrisL: That is why the thread started in 2016.
<dael> myles: We've got the same problem.
<dael> myles: There is a proposal. Is there feedback on it?
<dael> fantasai: Which proposal?
<dael> ChrisL: It's in IRC
<dael> ChrisL: So for that example: `format("opentype", requires variations, requires color)` you have to support both.
<dael> fantasai: ameliaBR suggested using the names of the font
<dael> myles: One feature can have many tables so you need one representitive table
<dael> fantasai: We do that for @supports anyone
<astearns> s/names of the/names of the tables of the
<dael> ChrisL: For variable fonts there's one table for every table that exists so you ave to list a ton
<dael> fantasai: You use one as a proxy for the rest. You use @supports display:grid and I assume the rest
<dael> Jason: Font tables aren't something that users commonly know.
<dael> Jason: I think the concernw ould be that people who know about font format technology might know the tables exist but most font users don't. If we're going to pick a table we might as well pick a keyword as the thing which is more understandable to users of fonts.
<dael> ChrisL: Think so, yes.
<dael> myles: There are 4 popular color font formats and they're all competing. Some browsers support some. Edge does all. It kinda matters.
<jpamental> Just chiming in that the Jason mentioned above is me
<dael> myles: That's a situation where the table matters. Variations is different. I think keywords is a good pick so you can say I support variations.
<dael> eae: You don't want to download the font 4 times that has the same variation.
<dael> ChrisL: It's all open type.
<dael> fantasai: There's a few situations where you need more then does it support.
<dael> Vlad: Can you desc an example where this functionality is needed?
<dael> myles: I don't want to download font files....
<dael> Vlad: You're a user and you want color glyphs or nothing at all. Is that the case?
<dael> astearns: Author intent is I was the display color glyphs in this font if I'm in a browser that supports it. If I'm in a browser that doesn't support it I want to download only the smaller font so I get it faster.
<dael> ChrisL: These are subsetted fonts. You've got one that does svg one that does spix.
<dael> myles: It's up the the author but yes.
<dael> Rick: Same hting happens with variable fonts. I could have a bold weight and a light weight and I want to sort them out.
<dael> astearns: I prefer the keywords. Since there are 4 color font flavors do we need for keywords
<dael> many: yes
<dael> myles: And there will be variations and features
<dael> TabAtkins: If a color wins we'll add color as an alis to it.
<dael> fantasai: You might want references to open type tables for the future. Let's say there's an open type table where support is necessary to use this crazy script.
<dael> fantasai: WE can have keywords for the most common things, but might also want to support the more specific cases.
<TabAtkins> `requires variations, requires table "YOLO"
<TabAtkins> `requires variations, requires table "YOLO"
<astearns> `supports`: keyword | "XXXX"
<TabAtkins> `requires variations, requires table "YOLO"`
<dael> astearns: You mean ^?
<dael> astearns: Arbitrary 4 letter strings which you can put for supportsand you check if browser does anything?
<dael> myles: We can have a feature keyed off a specific table but we shouldn't have that many variations. We can have both.
<dael> ChrisL: should use it for open type features?
<dael> fantasai: Interesting point.
<dael> astearns: Since this issue has been open for years can we do keywords for now and extend later as we find use cases? Particularly if there's a language thing with shaping tables where if a table to load a font in some browsers is the useful thing.
<dael> fantasai: One of the reasons you don' want to defer is if you add it later the brwosers will throw out hte entire declaration So you either define a subset and say we may extend in the future.
<dael> ChrisL: It's important to not throw out the whole declaration.
<dael> fremy: Can't we use string and say we split by slash?
<dael> myles: A micro syntax? It's not good of OMs.
<dael> ChrisL: You said it was backwards compat if you use slash?
<dael> myles: Browsers would look at the format scring and parse.
<dael> fantasai: Micro syntax is more complicated if we extend.
<dael> fremy: If we do this it still works in previous browsers. They'll still parse it. If we require something we'll remove all the fonts in src because if you do something inside the string we don't know.
<dael> TabAtkins: Download source twice. Source for simpliest and second for requires.
<dael> fantasai: Shouldn't we use supports to be consistant?
<dael> myles: browser-support font-files
<dael> fremy: Can't you check if the browser supports font varients?
<dael> myles: It doesn't work for color fonts because the css support for palettes is across types of formats
<dael> TabAtkins: Should we be careful with how we define to add more features. If we say it supports custom ident.
<dael> myles: Syntax isn't a | b | c it's just custom ident?
<dael> TabAtkins: WE'll say these are defined custom idents and if you see one you don't know you don't support it.
<dael> fantasai: I think more general and the values are comma sep but if you don't understand any part of it it throws that part out. It's a change to parsing that opens the whole relmn of possibilities in the future.
<dael> TabAtkins: Yeah. Only poss. backwards compat is if people add nonsense in their source and expect it not to download this will jsut htrow away the nonsense.
<dael> astearns: Does that exist?
<dael> TabAtkins: No.
<dael> fantasai: I'd say we resolve on that.
<dael> fantasai: Prop: The values of the src descriptor, the entire declaration is not thrown out if any comma separated section is invalid
<dael> myles: Parsing is chop it into commas, run parse on each piece, and if that piece doesn't parse throw it out.
<dael> fantasai: Like MQ not like selectors.
<dael> TabAtkins: The grammar would have to be like custom properties, that's less good.
<dael> astearns: Objections to not throwing out a source descriptor if a comma separated piece does not parse
<dael> fremy: Inside a function we can extend, right?
<dael> fantasai: This is broader.
<TabAtkins> TabAtkins: So I'll need to put together some prose to handle that in the grammar.
<dael> RESOLVED: Parse the value of src throwing out invalid parts like media queries and not like selectors. aka You split on the commas and throw out the pieces you don't understand not the whole thing.
<fantasai> Like Media Queries, not like Selectors
<TabAtkins> (unfortunately) not like Selectors
<dael> fantasai: Now people can impl that while we figure out the rest.
<astearns> (fortunately) not like (unfortunate) Selectors
<dael> myles: Now we have that we don't need the string thing. We can just use keywords.
<dael> fantasai: That's a different problem. If you just have keywords that are predefined.
<dael> myles: got it
<myles> format("opentype", [requires custom-ident]#)
<fantasai> s/predefined/predefined, then you can only query predefined things/
<dael> myles: Proposal #2 is ^
<myles> format(DOMString, [requires custom-ident]#)
<dael> astearns: Extending source descriptor to a comma sep list of requirest custom ident
<dael> TabAtkins: not requires
<dael> ChrisL: When I type that out I'll have commas?
<dael> myles: Yes.
<dael> TabAtkins: No requires is no commas.
<fantasai> format(<string>, [requires <custom-ident>#)
<dael> fantasai: This is what you meant ^
<fantasai> format(<string>, [requires <custom-ident>]#)
<ChrisL> almost
<dael> myles: You missed the square bracket
<dael> TabAtkins: With the resolution we don't need the custom-ident business.
<dael> ChrisL: So I can put inherit and stuff in there?
<dael> myles: It's all inside the one format string...I guess that's okay.
<dael> TabAtkins: With the resolution if we mint a brand new requires keyword tomorrow and someone uses it for browsers that don't know it that chunk of the source will be thrown out.
<dael> astearns: If you put in "requires color-something"...oh!
<dael> myles: it goes on to the next source.
<dael> astearns: You break up things between commas which reduces to the things you know.
<ChrisL> so there is an implicit AND, on the requires
<dael> TabAtkins: Source splits on commas and only checks individual pieces
<dael> astearns: Got it.
<dael> myles: I think we're okay.
<dael> fantasai: Do you need requires word?
<Joel> Hi there!I'm Joël from Fontself, I'm really interested about OpenType-SVG / Color fonts
<Joel> My colleagues Mohamed and Franz will be at TypoLabs
<dael> astearns: I like an indication of what this is about. I liked supports better.
<dael> TabAtkins: Open type is written as a string. We can't restrict it because it's an extern source. That's why tables as a string. If we define it's an ident.
<dael> myles: Authors won't understand.
<Joel> Their flight was cancelled at the last minute, they will be in Berlin tonight ;-)
<dael> myles: I think I'm with astearns that having a keyword would be helpful.
<dael> fantasai: Put with many things you have to write it many times.
<dael> astearns: Ties in the supports.
<dael> fantasai: prefer if you don't have to repeat it.
<dael> myles: can you write the grammar so that we don't have to repeat?
<fantasai> format(<string> supports <custom-ident>#)
<dael> fantasai: There ^
<fantasai> format(<string> [supports <custom-ident>#]?)
<dael> myles: Looks like supports is required, though.
<dael> fantasai: there ^
<dael> myles: And a comma
<dael> fantasai: Getting rid of the comma
<dael> TabAtkins: If we're removing supports no commas.
<dael> myles: Okay.
<dael> [everyone argues about commas]
<astearns> (which is a recurring theme)
<dael> ChrisL: The longest github thread ever was about using commas in the color function.
<myles> astearns: proposal: format(<string> [supports <custom-ident>#]?)
<dael> astearns: We have a proposed syntax. Anything more to change/fix or shall we resolve?
<TabAtkins> (It's not gonna be <custom-ident> now.)
<dael> TabAtkins: Custom-ident can be a normal grammar thing.
<dael> ChrisL: So we give an explicit list and if we extend in the future that's fine.
<dael> fantasai: Yes.
<Joel> Btw we think a @supports for color fonts will be needed, especially at the beginning. ie: If you want to use a color font only if it's supported and style it differently.
<dael> astearns: Objections to format(<string> [supports (list from spec)#]?)
<astearns> Joel: that's what we just resolved on
<dael> RESOLVED: use this: format(<string> [supports (list from spec)#]?)
<fantasai> format(<string> [supports <feature-name>#]?)
<Joel> Great!
<fantasai> where <feature-name> is a keyword
<dael> astearns: Someone on irc mentioned @support for color fonts will be needed. Do we have an @support?
<astearns> Joel: sorry, I was wrong. we'll still need to figure it out
<Joel> Ok :)
<astearns> we'll file an issue
<dael> myles: No.I think that should be an issue and we should discuss it.
<dael> fantasai: For @supports you can use the same function as is here.
<dael> myles: WE should do use case research

@fantasai
Copy link
Collaborator

fantasai commented Jun 27, 2018

Issues filed for the 'src' parsing resolution:

Testcase: https://bug1471736.bmoattachments.org/attachment.cgi?id=8988334

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants