WICG / local-font-access

Web API for enumerating fonts on the local system
https://wicg.github.io/local-font-access
Apache License 2.0
75 stars 16 forks source link

Review #48

Open jakearchibald opened 3 years ago

jakearchibald commented 3 years ago

Small things in the explainer:

System font engines (and browser stacks) may display certain glyphs differently. These differences are necessary, in general, to create fidelity with the underlying OS (so web content doesn't "look wrong"). These differences reduce consistency for applications that span across multiple platforms, e.g. when pixel-accurate layout and rendering is required.

To me, this reads like a disadvantage of local fonts, and an advantage of web fonts. As in, if you want your files to look the same once shared, you shouldn't rely on system fonts.


Developers may have legacy font stacks for their applications that they are bringing to the web. To use these stacks, they usually require direct access to font data, something web fonts do not provide.

This point seems a bit weak hand-wavy. Is there a more concrete example?


A font enumeration API, which allows users to grant access to the full set of available system fonts.

The level of access isn't clear here. Maybe:

A font enumeration API, which allows users to grant access to the full set of available system font metadata.


glyf table for glyph vector data, the GPOS table for glyph placement, and the GSUB table for ligatures and other glyph substitution

Nit: Only "glyf" is linked to its definition. Maybe link to GPOS and GSUB too?


Ensure UAs are free to return anything they like. If a browser implementation prefers, they may choose to only provide a set of default fonts built into the browser.

Enable access to all browser-allowed font tables (may vary per browser)

These two sound bad on the surface as they encourage breaking interoperability. Maybe detail why these are desirable?


  • Logging of likely-available fonts to improve server-side font rule generation.
  • Scripts to generate style rules based on "similar" local fonts, perhaps saving a download

I don't really understand these. Given that the process may involve a permission prompt, this sounds pretty intrusive vs just specifying a long list of fonts in font-family.


const fonts_iterator = navigator.fonts.query();

Nit: In JavaScript names are generally camelcased. Same goes for font_select in the next example.

I also agree with @domenic that this isn't an iterator, so maybe call it systemFonts?


option.value = metadata.fullName;

You don't need this line. If no value is provided, it defaults to the text.


document.body.appendChild(font_select);

Kinda nitty, but this code produces a useless select if permission isn't granted. Maybe move the <select> creation/append until after permission is granted?


option.setAttribute("postscriptName", metadata.postscriptName);

We shouldn't encourage the use of non-standard attribute names.

Instead, either use a data attribute:

option.dataset.postscriptName = metadata.postscriptName;

Or create associated data:

const optionToMetadata = new WeakMap();
// ...
optionToMetadata.set(option, metadata);

const sfntVersion = (new TextDecoder).decode(
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    await sfnt.slice(0, 4).arrayBuffer());

Some of the formatting here is a little unusual. I'd go with:

const sfntVersion = new TextDecoder().decode(
  // Slice out only the bytes we need: the first 4 bytes are the SFNT
  // version info.
  // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
  await sfnt.slice(0, 4).arrayBuffer()
);

If in doubt, pass code through https://prettier.io/playground/. Oh actually, it could just be:

// Slice out only the bytes we need: the first 4 bytes are the SFNT
// version info.
// Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
const sfntVersion = await sfnt.slice(0, 4).text();

FontFaceSource is specified in the CSS 3 Font Loading draft. At first glance, this is the most appropriate interface from which to hang something like the proposed query() method. It is, however, a synchronous iterator.

FontFaceSource isn't an iterator, but FontFaceSet is.

The FontFaceSet is exposed in pages as document.fonts, which doesn't seem like a good fit for this feature as it's of the system, not of the document.


Necessarily, user agents and operating systems already provide this functionality, so requiring web applications to include their own copies leads to additional download and memory cost. In some cases, this may be required by the web application to ensure identical behavior across browsers, but in other cases exposing some of these libraries directly to script as additional web APIs could be beneficial.

"ensure identical behavior across browsers" - this seems to clash with the earlier claim "UAs are not required to provide table data exactly as it appears on disk".

Also, it isn't clear what the conclusion is for this section. Is it being considered in future, or dismissed?


It isn't clear to me from reading this if the API presented is exhaustive. Eg, is there anything else hanging off navigator.fonts or is it just query?

It might be worth adding an MDN-like description of the API, aimed at developers.


Styling with Local Fonts

It isn't clear to me which bit of metadata I'd use in CSS's font-family.

My assumption would be metadata.family, since that's closest in naming to font-family, but the example suggests otherwise, as that's the one bit of metadata that isn't used.


Can fonts contain direct personal information that might be exposed here? Eg, name of the user in some kind of licencing information?


Former editors:

  • Emil A. Eklund

😒

Nice callout at the end, thanks for adding that.


I'll create issues for things that might need further discussion.

jakearchibald commented 3 years ago

In terms of other issues:

Before OT:

Stuff which has a major impact on API design:

Lower priority stuff:

oyiptong commented 3 years ago

To me, this reads like a disadvantage of local fonts, and an advantage of web fonts. As in, if you want your files to look the same once shared, you shouldn't rely on system fonts.

It occurs for WebFonts as well.

The main issue with the web stack is that it's meant for rendering content and part of its tasks is to make the content feel "natural" in the deployment platform. On Windows, ClearType might be applied to a font, for instance, to make all fonts look the same based on the operating system settings.

When consistency is important, in e.g. a photo editor, it is important not to apply such modifications, but rather, to let the application handle the rendering (and modification of) the raw font data, so it reflects the designer's intent, on any platform and on any medium, including screens, printed, etc.

Do you think I should reference the different use-cases and ClearType directly as an example in that bullet point?

Developers may have legacy font stacks for their applications that they are bringing to the web. To use these stacks, they usually require direct access to font data, something web fonts do not provide.

This point seems a bit weak hand-wavy. Is there a more concrete example?

WebFonts do not provide a raw data API. While WebFonts does not have an API like blob(), one could read the bytes and interpret in script, e.g. using something like opentype.js.

I suppose that it's not that WebFonts does not provide the data, it's that the web font rendering stack is meant for a different purpose than this API. There is no WebFont enumeration API for local fonts, for instance, and second, there is no aforementioned "raw data" API.

Both of those capabilities are needed to be used for a class of currently non-web applications.

The level of access isn't clear here. Maybe:

A font enumeration API, which allows users to grant access to the full set of available system font metadata. πŸ‘

glyf table for glyph vector data, the GPOS table for glyph placement, and the GSUB table for ligatures and other glyph substitution

Nit: Only "glyf" is linked to its definition. Maybe link to GPOS and GSUB too? πŸ‘

Ensure UAs are free to return anything they like. If a browser implementation prefers, they may choose to only provide a set of default fonts built into the browser.

Enable access to all browser-allowed font tables (may vary per browser)

These two sound bad on the surface as they encourage breaking interoperability. Maybe detail why these are desirable?

They are detailed in the Privacy and Security Considerations. WDYT of something like:

 * Ensure UAs are free to return anything they like. If a browser implementation prefers, for privacy and security, they may choose to only provide a set of default fonts built into the browser.
  • Logging of likely-available fonts to improve server-side font rule generation.
  • Scripts to generate style rules based on "similar" local fonts, perhaps saving a download

I don't really understand these. Given that the process may involve a permission prompt, this sounds pretty intrusive vs just specifying a long list of fonts in font-family.

The idea is not to render the fonts. A long list of fonts in font-family is not precise enough. For example, instead of showing "Arial", one could want to present "Arial MT Bold", "Helvetica Bold", etc. Not all fonts have "Bold" as an available style.

The context that's missing is that the kinds of applications the enumeration API enables is for the creation of font-based content. I suppose "user-generated content" is not descriptive enough.

Would a preamble before the bullet list explaining creation vs consumption be good enough context?

const fonts_iterator = navigator.fonts.query();

Nit: In JavaScript names are generally camelcased. Same goes for font_select in the next example. πŸ‘

option.value = metadata.fullName;

You don't need this line. If no value is provided, it defaults to the text. I changed the example in #47 It is actually preferable to use postscriptName as value, because according to the spec, it is in ASCII.

It is used as a unique identifier of sorts. I'll find some way to explain that.

document.body.appendChild(font_select);

Kinda nitty, but this code produces a useless select if permission isn't granted. Maybe move the <select> creation/append until after permission is granted? πŸ‘

option.setAttribute("postscriptName", metadata.postscriptName);

We shouldn't encourage the use of non-standard attribute names. πŸ‘

If in doubt, pass code through https://prettier.io/playground/. Oh actually, it could just be:

// Slice out only the bytes we need: the first 4 bytes are the SFNT
// version info.
// Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
const sfntVersion = await sfnt.slice(0, 4).text();

πŸ‘ Thanks! Much more concise and legible!

FontFaceSource isn't an iterator, but FontFaceSet is. πŸ‘

The FontFaceSet is exposed in pages as document.fonts, which doesn't seem like a good fit for this feature as it's of the system, not of the document. πŸ‘ That's a good point. I'll add it to the explanations.

"ensure identical behavior across browsers" - this seems to clash with the earlier claim "UAs are not required to provide table data exactly as it appears on disk".

For WebFonts, Chrome and Firefox uses the OTS library to filter some tables out from font files for security purposes. Those tables in theory should not affect what's rendered, but should prevent code from a foreign origin embedded in font files to break the font rendering code.

While we did consider it before, in the case of local fonts, Chrome has decided to return the data verbatim from disk. If the system fonts have been compromised, that means that the system has already been compromised. However, Perhaps we should let UA's make the call on their own? It's a non-normative option.

Also, it isn't clear what the conclusion is for this section. Is it being considered in future, or dismissed?

We're not considering them as part of this effort yet. That said, there could be shaping/metrics data we could expose as additional metadata, perhaps saving the cost on developers loading e.g. HarfBuzz or fontconfig.

I'll add a sentence with this explanation.

It isn't clear to me from reading this if the API presented is exhaustive. Eg, is there anything else hanging off navigator.fonts or is it just query? For now just query. In the future, we might expose other things. Top of mind right now for me:

It might be worth adding an MDN-like description of the API, aimed at developers.

Styling with Local Fonts

It isn't clear to me which bit of metadata I'd use in CSS's font-family. Ah. Yes. One would use @font-face src: local option, with either fullName or postscriptName. Description here

I'll re-work the example.

My assumption would be metadata.family, since that's closest in naming to font-family, but the example suggests otherwise, as that's the one bit of metadata that isn't used.

Your assumption is correct. And yes, that metadata isn't used in this example. One could use it to group fonts together.

Can fonts contain direct personal information that might be exposed here? Eg, name of the user in some kind of licencing information?

The fonts can contain licensing information. And these can be identifying, but it won't be the name of the user. It would be the name of the author of the font. That said, these are required table entries as defined by the OpenType spec under ID 13.