Font Table Access API

A Collection of Interesting Ideas,

Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google Inc.)
(Google Inc.)
(Google Inc.)

Abstract

This specification documents web browser support for allowing users to grant web sites access to the raw table data of fonts, allowing for more detailed custom text rendering.

logo

1. Introduction

This specification describes a font table access API which provides low-level (byte-oriented) access to the various OpenType tables of both local and remotely-loaded fonts.

While the web has its origins as a text-focused medium and user agents provide very high quality typography support, they have limitations that impact some classes of web-based applications:

This API provide high-end tools access to the same underlying data tables that browser layout and rasterization engines use for drawing text. Examples include the OpenType [OPENTYPE] glyf table for glyph vector data, the GPOS table for glyph placement, and the GSUB table for ligatures and other glyph substitution. This information is necessary for these tools in order to guarantee both platform-independence of the resulting output (by embedding vector descriptions rather than codepoints) and to enable font-based art (treating fonts as the basis for manipulated shapes).

2. Goals

The API should:

3. Examples

This section is non-normative.

The API allows script to request the internal tables of fonts.

4. Concepts

4.1. Font Representation

A font representation is an OpenType [OPENTYPE] definition of a font. Even if the font was originally described in another file format, it is assumed that if it is supported by a user agent then an OpenType representation can be derived for it. This includes True Type [TrueType], Web Open Font Format 1.0 [WOFF] and Web Open Font Format 2.0 [WOFF2] files.

Every FontFace has a corresponding font representation; many FontFace may share the same font representation.

A font representation has a table list, a list of font tables.

4.2. Font Table

A font table is an OpenType [OPENTYPE] table.

A font table has a tag, which is a ByteString of length 4, derived from the Tag of the table record.

A font table has data bytes, which is a byte sequence corresponding to the table data.

Table types within the original font file that are not supported by the user agent should be elided from the font representation's table list as defined by this specification. Tables with subtables not supported by the user agent should be transcoded to elide those subtables.

Note: For example, if a user agent does not support the EBDT table, it will not appear in a font representation's table list. If a user agent does not support the platform 1, encoding 0, format 6 subtable of the cmap table, the font table with the cmap tag will not include it.

5. API

5.1. FontFace additions

await map = fontFace . getTables()
await map = fontFace . getTables(tableNames)

Request the internal tables of fontFace. The result map is a Map where the keys are matching table names and the values are ArrayBuffers with the table binary data. If tableNames is not specified, all tables are returned.

This method is only usable for local fonts, and will throw an exception if called for a web font.

[Exposed=(Window,Worker)]
partial interface FontFace {
  [SecureContext] Promise<Map> getTables(optional sequence<ByteString> tableNames);
};

The getTables(tableNames) method, when invoked, must run these steps:

  1. Let promise be a new promise.

  2. Otherwise, if either of this's [[Urls]] or [[Data]] slots are not null, then reject promise with a TypeError.

  3. Otherwise, if tableNames was given but is empty, then reject promise with a TypeError.

  4. Otherwise, run these steps in parallel:

    1. Let permission be the result of requesting permission to use "local-fonts".

    2. If permission is not "granted", then reject promise with a "NotAllowedError" DOMException, and abort these steps.

    3. Let map be a new Map.

    4. Let font be this's font representation.

    5. For each table of font’s table list:

      1. Let tag be table’s tag.

      2. If tableNames was given and does not contain tag, then continue.

      3. Let data be a new ArrayBuffer containing table’s data bytes.

      4. Append the Record { [[Key]]: tag, [[Value]]: data } to map.[[MapData]].

    6. Resolve promise with map.

  5. Return promise.

Table order? If returned in requested order, what if the tableNames argument is omitted? (Observable by iterating the map.)

6. Internationalization considerations

Document internationalization considerations

7. Accessibility considerations

There are no known accessibility impacts of this feature.

8. Security considerations

Malicious font files containing incorrect length and offset values have been used as attack vectors. User agents should validate and sanitize fonts, e.g. ensuring that length and offset values are correct.

While user agents defend against this for their own use of fonts, sanitizing data before presenting it to script via this API helps web applications avoid similar problems.

Tables unknown to a user agent cannot be sanitized, and should therefore not be present in the font representation.

Note: Chromium’s OTS defines the tables and subtables which are supported in that browser engine, implicitly defining the allow-list for font tables.

9. Privacy considerations

9.1. Fingerprinting

Local system fonts exposed by user agents to script include:

This provides several "bits of entropy" to distinguish users.

User agents could mitigate this in certain cases (e.g. when the permission is denied, or in Private Browsing / "incognito" mode) by providing an enumeration of a fixed set of fonts provided with the user agent, or by transcoding to reduce the data provided by individual tables or subtables within a font.

9.2. Identification

Users from a particular organization could have specific fonts installed. Employees of "Example Co." could all have an "Example Corporate Typeface" installed by their system administrator, which would allow distinguishing users of a site as employees.

There are services which create fonts based on handwriting samples. If these fonts are given names including personally identifiable information (e.g. "Alice’s Handwriting Font"), then personally identifiable information would be made available. This may not be apparent to users if the information is included as properties within the font, not just the font name.

10. Acknowledgements

We’d like to acknowledge the contributions of:

Special thanks (again!) to Tab Atkins, Jr. for creating and maintaining Bikeshed, the specification authoring tool used to create this document.

And thanks to Chase Phillips, Dominik Röttsches, and Igor Kopylov for suggestions, reviews, and other feedback.

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-FONT-LOADING-3]
Tab Atkins Jr.. CSS Font Loading Module Level 3. 22 May 2014. WD. URL: https://www.w3.org/TR/css-font-loading-3/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[OPENTYPE]
OpenType specification. URL: http://www.microsoft.com/typography/otspec/default.htm
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres; Jeffrey Yasskin. Permissions. 25 September 2017. WD. URL: https://www.w3.org/TR/permissions/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[WebIDL]
Boris Zbarsky. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

Informative References

[TrueType]
TrueType™ Reference Manual. URL: https://developer.apple.com/fonts/TrueType-Reference-Manual/
[WOFF]
Jonathan Kew; Tal Leming; Erik van Blokland. WOFF File Format 1.0. 13 December 2012. REC. URL: https://www.w3.org/TR/WOFF/
[WOFF2]
Vladimir Levantovsky; Raph Levien. WOFF File Format 2.0. 1 March 2018. REC. URL: https://www.w3.org/TR/WOFF2/

IDL Index

[Exposed=(Window,Worker)]
partial interface FontFace {
  [SecureContext] Promise<Map> getTables(optional sequence<ByteString> tableNames);
};

Issues Index

Table order? If returned in requested order, what if the tableNames argument is omitted? (Observable by iterating the map.)
Document internationalization considerations