Use advanced typography with local fonts Stay organized with collections Save and categorize content based on your preferences.
Learn how the Local Font Access API allows you to access the user's locally installed fonts and obtain low-level details about them
Success: The Local Font Access API was part of thecapabilities project and is now available from Chrome 103 on desktop.Web safe fonts
If you have been doing web development long enough, you may remember the so-calledweb safe fonts.These fonts are known to be available on nearly all instances of the most used operating systems(namely Windows, macOS, the most common Linux distributions, Android, and iOS). In the early 2000s,Microsoft even spearheaded aninitiativecalledTrueType core fonts for the Web that provided these fonts for free download with theobjective that"whenever you visit a Web site that specifies them, you'll see pages exactly as thesite designer intended". Yes, this included sites set inComic Sans MS. Here is aclassic web safe font stack (with the ultimate fallback of whateversans-serif
font) might look like this:
body{font-family:Helvetica,Arial,sans-serif;}
Web fonts
The days where web safe fonts really mattered are long gone. Today, we haveweb fonts, some of which areevenvariable fonts that we can tweak further by changing the values for thevarious exposed axes. You can use web fonts by declaring an@font-face
block at the start of the CSS,which specifies the font file(s) to download:
@font-face{font-family:'FlamboyantSansSerif';src:url('flamboyant.woff2');}
After this, you can then use the custom web font by specifying thefont-family
, as normal:
body{font-family:'FlamboyantSansSerif';}
Local fonts as fingerprint vector
Most web fonts come from, well, the web. An interesting fact, though, is that thesrc
property in the@font-face
declaration, apart from theurl()
function, also accepts alocal()
function. This allows custom fonts to be loaded (surprise!) locally. If the user happens to haveFlamboyantSansSerif installed on their operating system, the local copy will be used rather thanit being downloaded:
@font-face{font-family:'FlamboyantSansSerif';src:local('FlamboyantSansSerif'),url('flamboyant.woff2');}
This approach provides a nice fallback mechanism that potentially saves bandwidth. On the Internet,unfortunately, we cannot have nice things. The problem with thelocal()
function is that it can beabused for browser fingerprinting. Turns out, the list of fonts a user has installed can be prettyidentifying. A lot of companies have their own corporate fonts that are installed on employees'laptops. For example, Google has a corporate font calledGoogle Sans.

An attacker can try to determine what company someone works for by testing for the existence of alarge number of known corporate fonts likeGoogle Sans. The attacker would attempt rendering textset in these fonts on a canvas and measure the glyphs. If the glyphs match the known shape of thecorporate font, the attacker has a hit. If the glyphs do not match, the attacker knows that adefault replacement font was used since the corporate font was not installed. For full details onthis and other browser fingerprinting attacks, read thesurvey paper byLaperdixet al.
Company fonts apart, even just the list of installed fonts can be identifying. The situation withthis attack vector has become so bad that recently the WebKit teamdecidedto"only include [in the list available fonts] web fonts and fonts that come with the operatingsystem, but not locally user-installed fonts". (And here I am, with an article on granting accessto local fonts.)
The Local Font Access API
The beginning of this article may have put you in a negative mood. Can we really not have nicethings? Fret not. We think we can, and maybeeverything is not hopeless.But first, let me answer a question that you might be asking yourself.
Why do we need the Local Font Access API when there are web fonts?
Professional-quality design and graphics tools have historically been difficult to deliver on theweb. One stumbling block has been an inability to access and use the full variety of professionallyconstructed and hinted fonts that designers have locally installed. Web fonts enable some publishinguse-cases, but fail to enable programmatic access to the vector glyph shapes and font tables used byrasterizers to render the glyph outlines. There is likewise no way to access a web font's binarydata.
- Design tools need access to font bytes to do their own OpenType layout implementation and allowdesign tools to hook in at lower levels, for actions such as performing vector filters ortransforms on the glyph shapes.
- 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 notprovide.
- Some fonts may not be licensed for delivery over the web. For example, Linotype has a license forsome fonts that only includesdesktop use.
The Local Font Access API is an attempt at solving these challenges. It consists of two parts:
- Afont enumeration API, which allows users to grant access to the full set of available systemfonts.
- From each enumeration result, the ability to request low-level (byte-oriented)SFNT containeraccess that includes the full font data.
Browser support
How to use the Local Font Access API
Feature detection
To check if the Local Font Access API is supported, use:
if('queryLocalFonts'inwindow){// The Local Font Access API is supported}
Enumerating local fonts
To obtain a list of the locally installed fonts, you need to callwindow.queryLocalFonts()
. Thefirst time, this will trigger a permission prompt, which the user can approve or deny. If the userapproves their local fonts to be queried, the browser will return an array with fonts datathat you can loop over. Each font is represented as aFontData
object with the propertiesfamily
(for example,"Comic Sans MS"
),fullName
(for example,"Comic Sans MS"
),postscriptName
(forexample,"ComicSansMS"
), andstyle
(for example,"Regular"
).
// Query for all available fonts and log metadata.try{constavailableFonts=awaitwindow.queryLocalFonts();for(constfontDataofavailableFonts){console.log(fontData.postscriptName);console.log(fontData.fullName);console.log(fontData.family);console.log(fontData.style);}}catch(err){console.error(err.name,err.message);}
If you are only interested in a subset of fonts, you can also filter them based on the PostScriptnames by adding apostscriptNames
parameter.
constavailableFonts=awaitwindow.queryLocalFonts({postscriptNames:['Verdana','Verdana-Bold','Verdana-Italic'],});
Accessing SFNT data
FullSFNT access is available via theblob()
method of theFontData
object. SFNT is a font file format which can contain other fonts, such as PostScript,TrueType, OpenType, Web Open Font Format (WOFF) fonts and others.
try{constavailableFonts=awaitwindow.queryLocalFonts({postscriptNames:['ComicSansMS'],});for(constfontDataofavailableFonts){// `blob()` returns a Blob containing valid and complete// SFNT-wrapped font data.constsfnt=awaitfontData.blob();// 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-fontconstsfntVersion=awaitsfnt.slice(0,4).text();letoutlineFormat='UNKNOWN';switch(sfntVersion){case'\x00\x01\x00\x00':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);}
Demo
You can see the Local Font Access API in action in thedemo below. Be sure to also check out thesource code. The demoshowcases a custom element called<font-select>
thatimplements a local font picker.
Privacy considerations
The"local-fonts"
permission appears to provide a highly fingerprintable surface. However,browsers are free to return anything they like. For example, anonymity-focused browsers may chooseto only provide a set of default fonts built into the browser. Similarly, browsers are not requiredto provide table data exactly as it appears on disk.
Wherever possible, the Local Font Access API is designed to only expose exactly the informationneeded to enable the mentioned use cases. System APIs may produce a list of installed fonts not in arandom or a sorted order, but in the order of font installation. Returning exactly the list ofinstalled fonts given by such a system API can expose additional data that may be used forfingerprinting, and use cases we want to enable are not assisted by retaining this ordering. As aresult, this API requires that the returned data be sorted before being returned.
Security and permissions
The Chrome team has designed and implemented the Local Font Access API using the core principlesdefined inControlling Access to Powerful Web Platform Features, including usercontrol, transparency, and ergonomics.
User control
Access to a user's fonts is fully under their control and will not be allowed unless the"local-fonts"
permission, as listed in thepermission registry, is granted.
Transparency
Whether a site has been granted access to the user's local fonts will be visible in thesite information sheet.
Permission persistence
The"local-fonts"
permission will be persisted between page reloads. It can be revoked via thesite information 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 about the API that does not work like you expected? Or are there missing methodsor properties that you need to implement your idea? Have a question or comment on the securitymodel? File a spec issue on the correspondingGitHub repo, or add your thoughts to anexisting issue.
Report a problem with the implementation
Did you find a bug with Chrome's implementation? Or is the implementation different from the spec?File a bug atnew.crbug.com. Be sure to include as much detail as you can,simple instructions for reproducing, and enterBlink>Storage>FontAccess
in theComponents box.
Show support for the API
Are you planning to use the Local Font Access API? Your public support helps the Chrome team toprioritize features and shows other browser vendors how critical it is to support them.
Send a tweet to@ChromiumDev using the hashtag#LocalFontAccess
and letus know where and how you're using it.
Helpful links
- Explainer
- Spec draft
- Chromium bug for font enumeration
- Chromium bug for font table access
- ChromeStatus entry
- GitHub repo
- TAG review
- Mozilla standards position
Acknowledgements
The Local Font Access API spec was edited byEmil A. Eklund,Alex Russell,Joshua Bell, andOlivier Yiptong. This article was reviewed byJoe Medley,Dominik Röttsches, andOlivier Yiptong. Hero image byBrett Jordan onUnsplash.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2020-08-24 UTC.