w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.46k stars 658 forks source link

unicode-range and falling off the end of the font-family list #7449

Open litherum opened 2 years ago

litherum commented 2 years ago

Consider content like this:

@font-face {
    font-family: MyCoolWebFont;
    src: url("someFontThatActuallySupportsTheLetterE.ttf");
    unicode-range: something that doesn't contain the letter e;
}
...
<div style="font-family: 'MyCoolWebFont';">e</div>

The relevant section of the spec is this: https://drafts.csswg.org/css-fonts-4/#cluster-matching

  1. For each family in the font list, a face is chosen using the style selection rules defined in the previous section. ....
  2. If no font was found in the font list in step 1:
    1. If c1 is a variation selector, ...
    2. Otherwise, the user agent may optionally use system font fallback ...
  3. If no font is found in step 2, use the matching sequence from step 1 to determine the longest sequence that is completely supported by a font ...

Step 1 clearly shouldn't select any fonts. The only one listed in the font-family list has a unicode-range that doesn't include the character being rendered. Okay.

Step 2.1 is clearly irrelevant.

Step 2.2 uses the term "system font" which is clearly wrong. "System font" is defined in section 10.4 as "the font which is used by the system-ui generic font family name."

Is step 2.2 allowed to use someFontThatActuallySupportsTheLetterE.ttf? It supports the letter e, and we fell off the end of the fallback list, where we're ostensibly allowed to use any font at this point.

When browsers have fallen off the end of the fallback list and they're trying to find some font to use, it's fairly common practice for browsers to use the first font in the font-family list as a hint, to try to find a used font that's as "close" to the hint font as possible. If the hint font itself supports the target character, then it seems not totally unreasonable to just use the hint font itself to render the character.

Options

There are probably 3 options here:

  1. Say normatively that a @font-face with unicode-range must never, in any circumstances, be used to render a character outside of its unicode-range character set.
  2. Say normatively that step 2 must only be allowed to return an installed font.
  3. Add no restrictions; once the browser falls off the end of the font-family list, all bets are off, it can use any font accessible to the document, including web fonts.

I'm not super sure, but it might be true that options 1 and 2 are behaviorally identical to each other.

litherum commented 2 years ago

cc @svgeesus @drott @jfkthame

dbaron commented 2 years ago

My intuition for what unicode-range is supposed to do matches what you describe in option 1 (which may be equivalent to option 2). I don't think that counts for much, though.

On the other hand, one real compatibility concern might be that if some implementations do (3), developers might observe an implementation doing (3) and start depending on it, i.e., start expecting browsers to use the font for characters outside of its declared unicode-range. So it's possible that option (3) might be problematic if it were allowed but not required. I think this is particularly an issue when the fonts involved have glyphs that don't, um, correspond to what Unicode says should be shown for the codepoints in question.

litherum commented 2 years ago

WebKit is capable of doing option 3 since 2017 (in at least some situations, but maybe all situations? Not sure). I opened this issue because I'm not sure that's desirable. (I totally understand your intuition, @dbaron.)

jfkthame commented 2 years ago

The relevant section of the spec is this: https://drafts.csswg.org/css-fonts-4/#cluster-matching

Is this section really relevant to this example? The steps quoted are to be used "[f]or a given cluster containing a base character, b and a sequence of combining characters c1, c2…", and are focused on the question of whether the entire cluster can be rendered from a single font; but here your example is about a single [base] character, not a cluster.

Hence, I think rule 7 of §5.2 would have applied, which calls for "the user agent [to perform] a[n] installed font fallback procedure". While exactly how that works is explicitly left up to the UA, it seems obvious that it is intended to exclude web fonts and instead rely on whatever "installed fonts" are available.

jfkthame commented 2 years ago

FWIW, I would agree with the intuition that a web font should never be used to render a character that is excluded from its unicode-range. In effect, the unicode-range descriptor creates a subsetted font that only supports the listed characters. (It may not in fact perform a subsetting operation on the resource, but for font matching purposes it behaves like one.)

svgeesus commented 2 years ago

I agree with @jfkthame and @dbaron that we don't want a fragile situation where the unicode-range is sometimes ignored, and that gets depended on.

Consider some old font which has ₠ where it should have €, we would want a unicode-range that excludes U+20AC Euro sign (because that font has the wrong glyph) to always be honoured, right?

And (to get back to installed vs. webfonts) that includes an @font-face with unicode-range where the src uses local().

litherum commented 2 years ago

(Related: https://github.com/w3c/csswg-drafts/pull/7643)

svgeesus commented 2 years ago
  1. Say normatively that a @font-face with unicode-range must never, in any circumstances, be used to render a character outside of its unicode-range character set.
  2. Say normatively that step 2 must only be allowed to return an installed font.

Now that https://github.com/w3c/csswg-drafts/pull/7643 has landed we have done 2.

I would prefer that we also do 1. Any downsides to adding that?