Skip to main content

Find out how the Local Font Access API allows you to access and get low-level details about the user's locally installed fonts.

The Local Source Access API is part of
capabilities project
and is currently under development. This post will be updated as implementation progresses.

Safe fonts for the web

If you've been doing web development long enough, you may remember the call secure web fonts. These fonts are known to be available on almost every instance of the most widely used operating systems (namely Windows, macOS, the most common Linux distributions, Android, and iOS). In the early 2000s, Microsoft even spearheaded a initiative called Top TrueType Fonts for the Web who provided these fonts for free download with the goal that "Whenever you visit a website that specifies them, you will see the pages exactly as the site designer intended". Yes, this included the sites established in Comic Sans MS. Here's a classic stack of web-safe fonts (with the last resort of
sans-serif

source) might look like this:

body {
font-family : Helvetica , Arial , sans-serif ;
}

Web fonts

The days when secure web fonts really mattered are long gone. Today we have web fonts, some of which are even variable sources that we can modify even more by changing the values of the different exposed axes. You can use web fonts by declaring a
@ font-face

block at the beginning of the CSS, which specifies the font file or files to download:

@ font-face {
font-family : "FlamboyantSansSerif" ;
src : url ( "flamboyant.woff2" ) ;
}

After this you can use the custom web font by specifying the
font-family, as usual:

body {
font-family : "FlamboyantSansSerif" ;
}

Local sources as a fingerprint vector

Most web sources come from, well, from the web. An interesting fact, however, is that the
src

property in the @ font-face statement, apart from the
url ()

function, also accepts a
local()

function. This allows custom fonts to load (surprise!) Locally. If the user has FlamboyantSansSerif installed on your operating system, the local copy will be used instead of downloading:

@ font-face {
font-family : "FlamboyantSansSerif" ;
src : local ( "FlamboyantSansSerif" ) ,
url ( "flamboyant.woff2" ) ;
}

This approach provides a good backup mechanism that potentially saves bandwidth. On the Internet, unfortunately, we cannot have nice things. The problem with the local() The function is that it can be abused for browser fingerprinting. It turns out that the list of fonts that a user has installed can be quite identifiable. Many companies have their own corporate fonts that are installed on employee laptops. For example, Google has a corporate font called Google Sans.

google-sans-8646559

The Google Sans font installed on a Google employee's laptop.

An attacker can try to determine which company someone works for by proving the existence of a large number of corporate sources known as Google Sans. The attacker would attempt to render the text set in these fonts on a canvas and measure the glyphs. If the glyphs match the known shape of the corporate font, the attacker is successful. If the glyphs do not match, the attacker knows that a default replacement font was used since the corporate font was not installed. For full details on this and other browser fingerprint attacks, read the survey paper by Laperdix et al.

Apart from company fonts, even only the list of installed fonts can be identified. The situation with this attack vector has gotten so bad that recently the WebKit team decided to "Includes only [in the list available fonts] web fonts and fonts that come with the operating system, but not locally installed user-installed fonts". (And here I am, with an article on granting access to local sources.)

The local source access API

The beginning of this article may have put you in a bad mood. Can't we really have nice things? Do not worry. We believe that we can, and maybe not everything is desperate. But first, let me answer a question that you may be wondering.

Why do we need the local font access API when there are web fonts?

Historically, professional-quality graphics and design tools have been difficult to offer on the web. One hurdle has been the inability to access and use the full range of suggested and professionally built fonts that designers have installed locally. Web fonts enable some publishing use cases, but do not enable programmatic access to vector glyph shapes and font tables used by rasterizers to render glyph outlines. Also, there is no way to access binary data from a web source.

  • Design tools need access to font bytes to perform their own OpenType design implementation and allow design tools to connect at lower levels, for actions such as performing vector filters or transformations on glyph shapes.
  • Developers can have legacy font stacks for their applications that they are bringing to the web. To use these stacks, they generally require direct access to font data, something that web fonts don't provide.
  • Some fonts may not be licensed for distribution over the web. For example, Linotype has a license for some fonts that only includes desktop use.

The Local Source Access API is an attempt to solve these challenges. It is made up of two parts:

  • A Font Enumeration API, which allows users to grant access to the full set of available system fonts.
  • From each enumeration result, the ability to request a low level (byte-oriented) Access to the SFNT container which includes the complete data of the source.

Actual state

He passed Condition
1. Create an explainer To complete
2. Create initial draft specification In progress
3. Collect feedback and repeat the design In progress
4. Proof of origin Not started
5. Launch Not started

How to use the local font access API

Enabling via chrome: // flags

To experiment locally with the Local Font Access API, enable #font-access flag on chrome://flags.

Feature detection

To check if the local font access API is supported, use:

if ( 'fonts' in navigator ) {
}

Asking permission

Access to a user's local sources is blocked behind the "local-fonts" permission, which you can request with
navigator.permissions.request ().


try {
const status = await navigator . permissions . request ( {
name : 'local-fonts' ,
} ) ;
if ( status . state ! == 'granted' ) {
throw new Error ( 'Permission to access local fonts not granted.' ) ;
}
} catch ( err ) {
if ( err . name ! == 'TypeError' ) {
throw err ;
}
}

Warning:
At this early stage of development of the Local Source Access API, the permission mentioned above is not yet implemented. Meanwhile, a permission message will appear the moment font enumeration starts. This behavior was implemented in crbug.com/1112552.

Enumeration of local sources

Once permission is granted, you can, from the FontManager interface that is exposed in navigator.fonts, call query () to prompt the browser for locally installed fonts. This results in an asynchronous iterator that you can loop through in a
for await ... of

statement. Each font is represented as FontMetadata object with properties
Familia (for example, "Comic Sans MS"), fullName (for example, "Comic Sans MS"), Y postscriptName (for example, "ComicSansMS").


const fonts = navigator . fonts . query ( ) ;
try {
for await ( const metadata of fonts ) {
console . log ( metadata . postscriptName ) ;
console . log ( metadata . fullName ) ;
console . log ( metadata . family ) ;
}
} catch ( err ) {
console . error ( err . name , err . message ) ;
}

Accessing SFNT data

Full SFNT
access is available through the blob () method of FontMetadata object. SFNT is a font file format that can contain other fonts, such as PostScript, TrueType, OpenType, Web Open Font Format (WOFF), and others.

const fonts = navigator . fonts . query ( ) ;
try {
for await ( const metadata of fonts ) {
if ( metadata . family ! == 'Comic Sans MS' ) {
continue ;
}
const sfnt = await metadata . blob ( ) ;

const sfntVersion = ( new TextDecoder ) . Decode (
await sfnt . slice ( 0 , 4 ) . arrayBuffer ( ) ) ;

let outlineFormat = 'UNKNOWN' ;
switch ( sfntVersion ) {
case 'x00x01x00x00' :
case 'true' :
case 'typ1' :
outlineFormat = 'truetype' ;
break ;
case 'OTTO' :
outlineFormat = 'cff' ;
break ;
}
console . log ( 'Outline format:' , outlineFormat ) ;
}
} catch ( err ) {
console . error ( err . name , err . message ) ;
}

Manifestation

You can see the Local Source Access API in action in the
manifestation down. Be sure to also check the
source code. The demo shows a custom element called which implements a local font selector.

Privacy Considerations

the "local-fonts" the permit seems to provide a highly printable surface. However, browsers can return whatever they want. For example, anonymity-focused browsers may choose to provide only a set of default fonts built into the browser. Similarly, browsers are not required to provide table data exactly as it appears on disk.

Whenever possible, the Local Source Access API is designed to expose exactly the information needed to enable the mentioned use cases. System APIs can produce a list of installed fonts not in a random or ordered order, but in the order of font installation. Returning exactly the list of installed fonts provided by said system API can expose additional data that can be used for fingerprinting, and the use cases we want to enable are not supported by retaining this order. As a result, this API requires that the returned data be sorted before it is returned.

Security and permissions

The Chrome team has designed and implemented the Local Font Access API using the basic principles defined in Control access to powerful features of the web platform, including user control, transparency and ergonomics.

User control

Access to a user's sources is entirely under their control and will not be allowed unless the user "local-fonts" permission, as indicated in the
permit registration, It is granted.

Transparency

If a site has been granted access to the user's local sources, it will be visible in the
site information sheet.

Permission persistence

the "local-fonts" the permission will be kept between page reloads. It can be revoked through the Site information bed sheet.

Feedback

The Chrome team wants to hear about your experiences with the Local Font Access API.

Tell us about the API design

Is there something in the API that is not working as expected? Or are you missing any methods or properties you need to implement your idea? Have a question or comment about the security model? File a spec issue in the corresponding GitHub repositoryor add your thoughts to an existing problem.

Report a problem with the deployment

Found a bug with the Chrome implementation? Or is the implementation different from the specification? File a bug in new.crbug.com. Be sure to include as much detail as you can, simple instructions to reproduce, and enter Blink> Fonts at Components box. Failure works great for quick and easy sharing of reps.

Show API support

Thinking of using the Local Font Access API? Your public support helps the Chrome team prioritize features and shows other browser vendors how important it is to support them.

Send a tweet to @Cromodev with the #LocalFontAccess hashtag and let us know where and how you are using it.

Thanks

The local source access API specification was edited by
Emil A. Eklund,
Alex Russell,
Joshua belland
Olivier Yiptong. This article was reviewed by
Joe medley,
Dominik Röttschesand
Olivier Yiptong. Hero image of Brett jordan in Unsplash.

error: Attention: Protected content.