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] src: local() font unique name matching ambiguous & restricts matched locale #3177

Open
drott opened this issue Sep 27, 2018 · 24 comments

Comments

@drott
Copy link
Collaborator

drott commented Sep 27, 2018

For a

@font-face {
    src: local(<fontname>)
}

definition, the rules in the specification how <fontname> is to be matched seem ambiguous and unnecessarily restrictive with regards to the locale against which <fontname> is matched.

From https://drafts.csswg.org/css-fonts/#descdef-src

For OpenType and TrueType fonts, this string is used to match only the Postscript name or the full font name in the name table of locally available fonts. Which type of name is used varies by platform and font, so authors should include both of these names to assure proper matching across platforms. Platform substitutions for a given font name must not be used.

As far as I can tell, Firefox implements matching against full font name as well as postscript name on all platforms. This is also what I have so far implemented on all Chrome platforms. I suggest we standardize that UAs must match against both to clarify matching expectations and remove redundancy in specified src: local() values (compare the example in the spec suggesting to place multiple local() values).

[...]

Just as a @font-face rule specifies the characteristics of a single font within a family, the unique name used with local() specifies a single font, not an entire font family. Defined in terms of OpenType font data, the Postscript name is found in the font's name table, in the name record with nameID = 6 (see [OPENTYPE] for more details). The Postscript name is the commonly used key for all fonts on OSX and for Postscript CFF fonts under Windows. The full font name (nameID = 4) is used as a unique key for fonts with TrueType glyphs on Windows.

[...]

For OpenType fonts with multiple localizations of the full font name, the US English version is used (language ID = 0x409 for Windows and language ID = 0 for Macintosh) or the first localization when a US English full font name is not available (the OpenType specification recommends that all fonts minimally include US English names). User agents that also match other full font names, e.g. matching the Dutch name when the current system locale is set to Dutch, are considered non-conformant. This is done not to prefer English but to avoid matching inconsistencies across font versions and OS localizations, since font style names (e.g. "Bold") are frequently localized into many languages and the set of localizations available varies widely across platform and font version. User agents that match a concatenation of family name (nameID = 1) with style name (nameID = 2) are considered non-conformant.

I do not understand the reasoning for preferring US English to avoid "matching inconsistencies".

What would be an example of such a matching inconsistency? I believe a matching inconsistency or ambiguity would occur if matching locales widely would create ambiguities in which font to choose. IMO, it is very unlikely that a full font name or postscript name in one locale accidentally collides with a different font's full font name or postscript name in a different locale.

Hence I argue we should more broadly match all locales that exist in the name table for postscript name as well as full font name:

  1. It increases chances of success of local matching for the user/page author. (In my opinion without causing matching inconsistencies)
  2. It improves consistency within the specification itself, compare section 5.1 Localized Name Matching where font family matching is actually required to take all localizations into account.
  3. From an implementation point of view, I don't see any obstacles. This mostly means enumerating through the name table, filtering by name record id, and just not dropping non-English localizations. The definition of "...or the first localization if English is not available" is very arbitrary: We can improve upon that by matching all. As an additional data point, OS level implementations such as IDWriteFontSet::GetMatchingFonts as well as FontConfig's pattern filtering functions currently do no restrict the search by locale.
@drott
Copy link
Collaborator Author

drott commented Sep 27, 2018

CC @behdad

@svgeesus
Copy link
Contributor

Good points. I believe (and this text is also in Fonts 3) that

User agents that also match other full font names, e.g. matching the Dutch name when the current system locale is set to Dutch

actually is trying to say

User agents that also match other full font names, e.g. matching the Dutch name only when the current system locale is set to Dutch

which would indeed be an inconsistency (the it works on my machine problem). Your proposed solution (matching all locales) would not produce inconsistency.

@svgeesus
Copy link
Contributor

The definition of "...or the first localization if English is not available" is very arbitrary

It might also change as a font is revised, which would produce inconsistencies if new localizations are inserted rather than strictly appended.

@litherum
Copy link
Contributor

I believe the "inconsistencies" mentioned are just supposed to ensure that font matching doesn't change when you modify your system language or move between countries.

@litherum
Copy link
Contributor

This proposal seems to have 2 pieces:

  1. Matching based on both font name and PostScript name
  2. Not preferring English over other locales

Both of those are good for the Web platform.

I don't know how to implement these on top of CoreText. It exposes the PostScript name, the family name, the display name, and a few other names. Right now, WebKit just matches on PostScript name and family name (along with some other stuff that I don't think is spec-compliant).

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed Fonts, and agreed to the following:

  • RESOLVED: local() matches against both full font name and postscript name
  • RESOLVED: Investigate whether it's practical to implement multi-locale name matching
The full IRC log of that discussion <fantasai> Topic: Fonts
<fantasai> drott: I raised this issue
<astearns> github: https://github.com//issues/3177
<fantasai> drott: It's about the local() value of font-face
<fantasai> drott: Used for uniquely identifying font on the local system
<fantasai> drott: Identifies one font
<fantasai> drott: Current description that it can only match the ??? name and the ??? name
<fantasai> drott: First want to expand it to ???
<fantasai> drott: We want the authors to have more success in actually matching the font
<myles_> s/???/Arial Bold Condensed/
<fantasai> drott: Currently spec asks authors to add both names to the src descriptor
<fantasai> drott: So current proposal is to match both, help authors have more success in matching thefont
<fantasai> drott: Second part of the proposal is about which locale/language to match against
<fantasai> drott: Currently the spec says only match the US-en version first
<fantasai> drott: If this one cannot be found, then try to match the first one encoded in the font
<fantasai> drott: I find this aspect of the font rather arbitrary, and don't think we should be doing this
<fantasai> drott: I'm not so worried about conflicts here
<fantasai> drott: If any questions, be happy to answer those
<fantasai> heycam: Do we have any requirements on matching non-local font names?
<fantasai> drott: If you don't use the local() value, you do family style matching
<fantasai> heycam: But in terms of what the family name is matched against?
<fantasai> drott: family name matches against the family name
<fantasai> drott: Fonts have three name entries
<fantasai> drott: There's family name
<fantasai> drott: and then full font name
<fantasai> drott: and postcript name
<fantasai> drott: In regular font-family matching, you match that against the family prart
<fantasai> heycam: Is it true that some implementations match the postcript name for font-family?
<fantasai> drott: I believe so
<fantasai> drott: But the matching is not so sharp for family
<fantasai> drott: You leave it to the matching algorithm to find the closest font to what you specified
<fantasai> drott: But for local() you have to match exactly, that's the intent
<fantasai> myles_: I think both of these are good for the web platform, I just dont' know how to actually implement them.
<fantasai> myles_: If someone knows how, that'd be great, happy to do it.
<fantasai> drott: Maybe don't specify as a MUST
<fantasai> drott: But I think we should remove the current specification that says english first, otherwise physically-first entry in the name table which seems pretty random
<fantasai> drott: I think Firefox implements this on all platforms
<fantasai> drott: Windows has DirectWrite API
<fantasai> drott: Fixing in Chromium, I also did this on LInux and Android so far
<fantasai> drott: Maybe not with CoreText, but iI think we can find a language that would allow implementations to do this
<fantasai> astearns: Is your suggestion to keep English first, but allow other matching, or drop any mention of English?
<fantasai> drott: I would prefer to match any
<fantasai> astearns: Do you have any particular spec language or just looking for agreement to go in this direction?
<fantasai> drott: don't have spec language ready, but agreement to go in that direction would be OK
<fantasai> astearns: so proposed resolution is to loosen font name atching requirements and allow more matching than is curretnly allowed
<fantasai> drott: Specifically to match both font names, and secondly to match against all languages
<fantasai> dbaron: Allow or require?
<fantasai> astearns: For the first part, full name and postcript name, would that be allowed or required?
<fantasai> drott: Required
<fantasai> drott: But for second part, locale-matching, would allow. not sure there's consensus to require
<fantasai> dbaron: My concern is that we'll end up in situations where impls do diferent things
<fantasai> dbaron: I'm more comfortable going with a requirement either way than to do MAY
<fantasai> dbaron: What we'll end up with there is someone haveing a web-compat problem and have to go fix it
<fantasai> dbaron: If that impl can't implement it, then we have a bigger problem
<fantasai> myles_: Agreed
<fantasai> myles_: Part of this issue for me is relying on API that isn't clear what it's doing
<fantasai> drott: I see your point
<fantasai> drott: I would prefer a requirement as well
<fantasai> drott: Would be hopeful we can spec that
<fantasai> drott: Otherwise just widening allowances makes sense to me
<fantasai> dbaron: Maybe give Myles a chance to see what CoreText can do?
<fantasai> dbaron: We should care what platform APIs offer when we spec these things
<fantasai> myles_: Is CoreText the only API that's concerning here?
<fantasai> drott: I looked at FontConfig and DirectWrite and on Android looked at APIs
<fantasai> drott: On fontconfig and directwrite it's no problem
<dbaron> I don't know... jfkthame would probably be the person to ask for Mozilla expertise
<fantasai> drott: On Android I implemented an indexing method, so possible but might require own indexing
<fantasai> myles_: I would like to not parse font files myself
<fantasai> astearns: So I guess the proposed resolution is to find better/more ways to match fonts
<fantasai> astearns: and try to make requirements a MUST
<fantasai> astearns: but we don't have spec language for this
<gregwhitworth> fantasai: I agree with dbaron we need to see if myles_ can implement it
<fantasai> drott: Can we split the resolution? Can we match full name + postscript name
<fantasai> myles_: it's OK
<fantasai> astearns: So proposed to match on full font name and postscript name (MUST)
<fantasai> astearns: Any objections?
<fantasai> RESOLVED: local() matches against both full font name and postscript name
<fantasai> RESOLVED: Investigate whether it's practical to implement multi-locale name matching
<fantasai> astearns: Might also want to ask the i18nwg if they have any input on this
<heycam> fantasai: to what extent are there fonts that have one localization plus english, and someone else has a different localization plus english?
<heycam> fantasai: let's say you have a font that's released in Japan and France, and the French version has English and French names, not Japanese names
<heycam> ... and the Japanese versoin has Japanese name and English name, but not a French name
<heycam> ... I think it was there to handle situations like this
<heycam> ... if you force the author to use the English name it could work in more place
<heycam> ... it's possible this is not why it's there
<heycam> myles_: I've never heard of a font author doing that. usually they just make a font in a particular set of languages
<heycam> fantasai: just want to check if this has historically happened in the past

@litherum
Copy link
Contributor

I'd like to wait to do these edits until I can figure out if it's possible to implement.

@svgeesus
Copy link
Contributor

It would be good if other imlementors also report back their implementability findings here on this issue

@drott
Copy link
Collaborator Author

drott commented Oct 29, 2018

  • Windows: As mentioned in the original issue report Microsoft's DirectWrite API for font matching allows specifiying a locale, or when leaving it out, matches all locales, details here IDWriteFontSet::GetMatchingFonts, so for the implementation on Windows 10 I do not see any restrictions. For older Windows versions, IDWriteFont::GetInformationalStrings returns a IDWriteLocalizedStrings pointer which allows access to all localized forms of the full font name and postscript names, which allows building a lookup structure to match against all localized name variants.
  • Linux / ChromeOS: Fontconfig PS name and full font name matching is also not restricted by locale.
  • Android: For Chrome on Android, we implemented our own indexing mechanism which can also match all locales, since it reads information directly from the font files.

Perhaps @jfkthame can comment for FF.

@svgeesus
Copy link
Contributor

Thanks @drott, useful!

@jfkthame
Copy link
Contributor

IIRC, Firefox currently builds its own psname and fullname indexes (of English names only, or first available locale if no English present) for each font face, on all platforms except macOS, where it relies on CGFontCreateWithFontName, which helpfully matches on both psname and fullname.

(Unfortunately, https://developer.apple.com/documentation/coregraphics/1396330-cgfontcreatewithfontname doesn't specify how localized names are handled. A brief test seems to suggest that it does not match all locales.)

I guess we could collect all localized names on the platforms where we build our own indexes, rather than just the English (or first) name, but I'm not particularly keen; ISTM that it adds very little value.

I think the potential "inconsistency" the current spec text is trying to avoid is that the collection of localized names in a font may well vary over time/versions, and/or between locales where the font is made available, while still being "the same font" in the sense that src:local would be expected to match it. (Noting that src:local is appropriate when a particular typeface is wanted, but the exact version of the resource is not important.) Virtually all font resources include English names, so these can be matched reliably, but if src:local is allowed to match all localized names, this introduces the risk that an author may write src:local("Arial Fett"), but on a user's system the equivalent font provides only "Arial Gras" as a localized alternative and so it is missed.

Declaring that src:local consistently matches against English names (and only those) is intended to minimize such issues.

@yisibl
Copy link
Contributor

yisibl commented Nov 3, 2023

@drott @litherum

Off-topic: does font-family in Chrome and Safari still match fonts by Postscript name (Name ID 6) now (not in @font-face)? See: https://bugs.chromium.org/p/chromium/issues/detail?id=641861#c19

Are there any drawbacks to using PostScript name matching fonts? It seems like sometimes it would be easier to match the font the user wants.

data:text/html,<div style="font-family:Times-BoldItalic, sans-serif; font-size: 36px;">should be sans serif

image

@jfkthame
Copy link
Contributor

jfkthame commented Nov 3, 2023

Matching by PostScript name directly in font-family, as in

font-family: Times-BoldItalic

is fundamentally in conflict with the CSS model of font properties. "Times-BoldItalic" is not a font family, it's a single face.

@yisibl
Copy link
Contributor

yisibl commented Nov 3, 2023

is fundamentally in conflict with the CSS model of font properties. "Times-BoldItalic" is not a font family, it's a single face.

@jfkthame Do you mean that the use of PostScript name conflicts with font-style and font-weight?

@jfkthame
Copy link
Contributor

jfkthame commented Nov 3, 2023

And font-stretch, yes.

Would you expect

font-family: Times-Italic;
font-weight: bold;

to result in the browser using Times-BoldItalic? Or what?

@yisibl
Copy link
Contributor

yisibl commented Nov 3, 2023

This does get more complicated, and there's currently no way to see which font the actual fallback goes to in Chrome DevTools, need to fix this issue: https://bugs.chromium.org/p/chromium/issues/detail?id=1365275.

@svgeesus
Copy link
Contributor

svgeesus commented Nov 3, 2023

There are basically two ways to go when specifying a font:

  1. Fully specify a face to be used on each run of text
  2. Specify a bunch of independent font properties and then combine them to find a matching face

Option 1 is verbose, unmaintainable, and fundamentally incompatible with a tree-structured document, so CSS went for option 2 very early.

Relevant early discussions (1996)

@yisibl
Copy link
Contributor

yisibl commented Nov 3, 2023

@jfkthame Also, does Firefox match <family-name> by looking for ID 16 in the font name table and then ID 1? Can you provide a link to this code? Any other documentation on the matching process would be great, thanks so much!

@jfkthame
Copy link
Contributor

jfkthame commented Nov 3, 2023

FWIW, Chrome on macOS doesn't appear to match on PSnames in font-family, at least for me....

data:text/html,<div style="font-size:30px; font-family: Times-BoldItalic, sans-serif;">hello world

gives me sans-serif, not Times.

Currently I'm only seeing Times-BoldItalic (incorrectly) used by Safari with that example.

Regarding family names, from the code at https://searchfox.org/mozilla-central/rev/8acfbe4ba09b46b91c862dc2fbc064d4fc1bac9a/gfx/thebes/gfxFontUtils.cpp#1698 it looks like Firefox checks for both the Typographic Family (ID=16) and legacy Family (ID=1) names; if they're different it will include both in its table of available font families to match.

(With the aim of more consistent behavior across platforms, Firefox doesn't rely on OS APIs to look up fonts; instead, it queries the OS for the available fonts but builds an internal table of families and faces, so that it can apply the font matching algorithm itself rather than depending on what an opaque API may return for a given query.)

@yisibl
Copy link
Contributor

yisibl commented Nov 6, 2023

@jfkthame Strange, the PostScript name works for me(Chrome 121.0.6102.0, macOS 12.3).
image

Regarding family names, from the code at https://searchfox.org/mozilla-central/rev/8acfbe4ba09b46b91c862dc2fbc064d4fc1bac9a/gfx/thebes/gfxFontUtils.cpp#1698 it looks like Firefox checks for both the Typographic Family (ID=16) and legacy Family (ID=1) names; if they're different it will include both in its table of available font families to match.

Thanks, your information is very informative.

@jfkthame
Copy link
Contributor

jfkthame commented Nov 6, 2023

@jfkthame Strange, the PostScript name works for me(Chrome 121.0.6102.0, macOS 12.3).

Strange indeed. But I'm on macOS 14.1 ... maybe some API behavior has changed that's affecting it?

image

But on Safari, the psname is still used.

@litherum
Copy link
Contributor

litherum commented Nov 6, 2023

FWIW, Chrome on macOS doesn't appear to match on PSnames

I believe @flackr was the one who implemented this in Chrome. (at least, he discussed implementing this in Chrome with me a few times)

@yisibl
Copy link
Contributor

yisibl commented Nov 7, 2023

@litherum Thanks, I see @flackr once submitted a PR web-platform-tests/wpt#17004 for WPT.

@flackr
Copy link
Contributor

flackr commented Nov 7, 2023

FWIW Chrome still fails this test on mac. I added the test after doing an investigation which determined that Android did not in fact match PS names on font-family per https://drafts.csswg.org/css-fonts/#font-families . This seems different than what's being discussed here where src: local(<name>) seems to say that PS names should be matched.

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

7 participants