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

More font metadata (weight, stretch, italic, unlocalized names) #61

Closed
poiru opened this issue Nov 18, 2020 · 17 comments
Closed

More font metadata (weight, stretch, italic, unlocalized names) #61

poiru opened this issue Nov 18, 2020 · 17 comments
Labels
api-shape Would affect the API shape (IDL) enhancement New feature or request

Comments

@poiru
Copy link

poiru commented Nov 18, 2020

The current font metadata fields are not sufficient for Figma. In addition to the existing fields, we also need:

  • weight (0 - 1000 inclusive, like CSS font-weight)
  • stretch (50% - 200% inclusive, like CSS font-stretch)
  • italic boolean
  • style name
  • unlocalized names

Further details below!

weight/stretch/italic

We have a font picker that looks like this:

font-picker

When you select the style (Italic in the example above), we show you a dropdown like this:

style-picker

As you can see, the dropdown is categorized and sorted by a combination of the font weight, stretch, and whether it is italic or not.

It would be ideal if the font API could provide this metadata given that they are already available from the OS font libraries. Otherwise, we would have to manually load the blob of each font, parse them manually or with Freetype, cache the info somewhere, and also worry about cache invalidation in case e.g. a new version of the font is installed.

Loading and parsing all fonts just to display our font picker would be prohibitively expensive. It is not uncommon for designers to have 1000s of fonts installed on their system.

Exposing the existing OS font library metrics would make this a lot simpler for us and allow us to only load and parse fonts that are actually in use.

style name

In the previous screenshots, you can also see that in our font picker, you first choose the family, and the separately choose the style. We could extract it from the fullName field, but that's pretty hacky. Additionally, fonts are weird and font designers do all sorts of weird things so there is no guarantee that fullName even has the style name.

unlocalized names

We need the unlocalized family/style name because we persist it in our file format and need it to be consistent across operating systems and different languages. Our existing methods for local font access provide the unlocalized name so this is also important from an interop perspective.

@sicking
Copy link

sicking commented Nov 23, 2020

One thing that's a bit tricky is that extracting font metadata from a font file is not straight forward. As I understand it, the main problem is that metadata in the font tables is often lacking or wrong. And so different OSs have taken to implementing different heuristics for how to combine the data in the font tables with the font/face/style name in order to determine the "correct" metadata. Here is how windows did it at least some time back.

Ideally we'd have some flexibility in implementing similar heuristics ourselves. We've talked about doing that in order to get consistency across different OSs and different ways of loading fonts.

One potential solution is that Chrome supplies the values as interpreted by the OS or browser. But in addition also provides a font file checksum which would enable us to implement our own caching+cache invalidation

@sicking
Copy link

sicking commented Jan 13, 2021

Updating here after some further dicussions. In order for Figma to display a font picker, we need to quickly load metadata on all fonts. There are two ways we can accomplish that:

Use the metadata provided by the API

With this approach we rely strictly on the data provided by the API. Here we would need the following information

  • Unlocalized font family
  • Unlocalized style name
  • Font weight
  • Font stretch
  • Italic boolean

In all cases we need the information as computed by the OS. I.e. only relying on the data in the font file is insufficient since font files often contain invalid data, and so OSes contain a lot of heuristics to calculate the "real" values.

Note that we don't need the font weight or font stretch in any sort of standardized form. It's completely fine to pass through the values as provided directly by the OS. Figma can do the work of translating the OS values into CSS values. Though getting CSS values is fine too.

The reason we need the unlocalized font family and style name is that these are the values we use as font identity within Figma. So these are the values that we'd use when deciding which font to actually load.

Calculcate metadata ourselves

We could load the font file and extract the metadata ourselves. This would have the advantage that we can be consistent across all platforms for a given font file, and we could extract additional metadata in the future as needs arise (things like variable fonts or oblique styles)

This would only require the following information

  • Some form of identifier
  • Font file name
  • Some combination of hash, filesize, last modified date, etc that we could use to do cache invalidation
  • Maybe access to the API inside workers

This would allow us to extract all information from all font files and then cache it in indexedDB or similar. Then we'd just need to load the information from indexedDB in order to display a font picker.

This solution has the advantage that we need less from the API and be more future proof. But has the downside that we need to do significantly more work on our end so would likely significantly delay when we could adopt the API. But ultimately this is a direction we want to go, just a question of when.

Update notifications

On a separate note, it would also be great to get some sort of callback when fonts are installed/removed/updated. I.e. when the list of fonts change. This wouldn't be a blocker for adoption for us since it's not something we currently do.

But it's something we have received feedback about from users. I.e. in some native applications there is no need to restart the application in order to get updated fonts. Currently they have to reload the Figma tab which is a negative experience for them.

@poiru
Copy link
Author

poiru commented Jan 14, 2021

From the code perpective, for unlocalized font/style name on macOS, you can use:

CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute);

For update notifications on Windows, you could monitor the following:

  • WM_FONTCHANGE
  • RegNotifyChangeKeyValue for changes to Software\Microsoft\Windows NT\CurrentVersion\Fonts under both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER (the latter is for Windows 10 build 1809, which added per-user font installation)

Let me know if you'd like to see our code for the other metadata (e.g. weight/stretch/italic).

@oyiptong
Copy link
Collaborator

RE: style

Just to make sure, on Windows, this is expected to be the DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, correct?

@poiru
Copy link
Author

poiru commented Jan 22, 2021

@oyiptong On Windows, for the family we check DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES first and fallback to DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES. Likewise for the style we check DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES first and fallback to DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES. The preferred family/style is a better fit for cross platform compatibility (the Win32 names are restricted to conform with GDI limitations). From MSDN:

Indicates the string containing the family name preferred by the designer. This enables font designers to group more than four fonts in a single family without losing compatibility with GDI. This name is typically only present if it differs from the GDI-compatible family name.

We also always try to get the en-us string, it should not be localized.

@oyiptong
Copy link
Collaborator

Thanks, that's very helpful!

@oyiptong
Copy link
Collaborator

oyiptong commented Feb 3, 2021

@poiru is italic as a boolean useful if it appears in style?

@poiru
Copy link
Author

poiru commented Feb 3, 2021

@oyiptong Yes! Font designers can name the style arbitrarily (e.g. Italic, Cursive, Incline, Oblique) so we check for DWRITE_FONT_STYLE_ITALIC/DWRITE_FONT_STYLE_OBLIQUE on Windows and for a non-zero kCTFontSlantTrait on macOS to determine whether the font is italic.

@sicking
Copy link

sicking commented Feb 19, 2021

In our last conversation there was a request to describe the use cases for having font weight and other style metadata in the enumeration API. We have two use cases that require this.

First off our font picker groups and sorts fonts by various font properties, such as font weight, italics and density. This can look like the below screenshot. This is a dropdown lets the user chose which style variant of the Inter font to use. The fonts are ordered by weight and grouped by italics. We'd like to have this metadata available very quickly, so loading all the fonts in a given family once the user chooses that family would likely be too slow.

Screen Shot 2021-02-18 at 2 47 42 PM

The second use case is to implement the Cmd+B action (i.e. the user making text bold). Exactly which font style to apply can depend on the list of available styles in a text's family.

For example if the user has selected some weight 400 text and the available weights are 400, 700 and 900, we'd probably want to apply the font weight 700. However if the list of available weights are 200 and 400, then we'd probably want to "turn bold off" by applying the weight 200.

Being able to figure out which font style to apply without having to load all the fonts in the family requires access to style metadata during enumeration.

@inexorabletash
Copy link
Member

API sketch:

partial interface FontMetadata {
    float weight;
    float stretch;
    bool italic;
};
  • weight is a floating point value corresponding to the numeric keyword values of the CSS font-weight property, e.g. 100.0 for extra-light, 400.0 for regular, 900.0 for black. Exact values should not be expected as some operating systems / fonts may support additional values e.g. 375 for book.
  • stretch is a floating point value corresponding to the numeric percentages of the CSS font-stretch property, e.g. 0.5 (50%) for ultra-condensed, 1.0 (100%) for normal, 2.0 (200%) for ultra-expanded
  • italic is true for fonts that identify as italic or oblique, or have a positive slant. Since operating systems and fonts haven't converged e.g. on a fractional slant value or enumeration (roman/italic/oblique), this is simplified to a boolean and future expansion here is anticipated.

How's that look?

@poiru
Copy link
Author

poiru commented Apr 21, 2021

Looks great!

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 27, 2021
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 28, 2021
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
@inexorabletash inexorabletash added api-shape Would affect the API shape (IDL) enhancement New feature or request labels Apr 30, 2021
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 30, 2021
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837071
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#878088}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Apr 30, 2021
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837071
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#878088}
@inexorabletash
Copy link
Member

FYI, the change to add italic, stretch and weight has landed in Chrome, should be in an upcoming Canary.

blueboxd pushed a commit to blueboxd/chromium-legacy that referenced this issue May 1, 2021
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837071
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#878088}
@poiru
Copy link
Author

poiru commented May 2, 2021

@inexorabletash Great! It seems like the origin trial is no longer active – do you plan to do another trial?

Also, on Canary, navigator.fonts.query prompts the user to select the fonts on every single call even when "Enable persistent access to the Font Access API" is enabled. Is this expected?

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue May 8, 2021
…data properties, a=testonly

Automatic update from web-platform-tests
Font Access API: Add additional FontMetadata properties

Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837071
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#878088}

--

wpt-commits: 2452eb50ff6af61b817963bb6f4cb7de967f2ab1
wpt-pr: 28675
@inexorabletash
Copy link
Member

The implementation in Chrome (OT and flag) is paused while we explore the permission issues, so the API is a bit rough at the moment. Hopefully more news soon.

@inexorabletash
Copy link
Member

FYI that the state of the API implementation hasn't changed; still exploring the permission issues, so it has rough edges. Again, hopefully more news soon.

@inexorabletash
Copy link
Member

We heard from Figma that italic/stretch/weight are not important to surface in the API directly, since fonts will be parsed to extract other data anyway. Curious if other potential users of the API consider these properties high priority.

inexorabletash added a commit that referenced this issue Dec 23, 2021
Per latest feedback from the developers that requested the properties, these may not be necessary. Dropping.

See issue #61 for details.
pwnall pushed a commit that referenced this issue Jan 18, 2022
* Remove FontMetadata italic/stretch/weight properties

Per latest feedback from the developers that requested the properties, these may not be necessary. Dropping.

See issue #61 for details.

* Remove unneeded link default
@inexorabletash
Copy link
Member

Closing for now

mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
Per discussion on the proposal[1], exposing additional font
properties during enumeration would greatly help consumers
of the building font selection UX. Properties such as italic
or oblique vs. roman, font weight (regular, bold, ...), and
font stretch/width (condensed, normal, expanded, ...) help
group fonts.

These properties are exposed by the OS-specific font APIs
(CoreText, FontConfig, DirectWrite) and are straightforward
to expose via the API, without requiring API users to load
and parse every font manually.

[1] WICG/local-font-access#61

Change-Id: I4d4efd927bf03d68d69c010098b4e18f1a0266bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2837071
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Commit-Queue: Joshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#878088}
NOKEYCHECK=True
GitOrigin-RevId: 332823ac672a0e9e6e4c1127a647f20672ca3c95
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-shape Would affect the API shape (IDL) enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants