Detecting System Fonts Without Flash

July 30, 2015

Many web-based text ed­i­tors al­low the user to se­lect cus­tom fonts through a drop down menu. These font menus of­ten in­clude sys­tem fonts. Un­for­tu­nately, there is no web plat­form API for re­triev­ing a list of lo­cally in­stalled fonts, so many of these ap­pli­ca­tions rely on a Flash li­brary to de­tect which fonts are in­stalled on the user’s sys­tem.

Google Docs font menu

Be­sides the re­cent se­cu­rity is­sues, there are sev­eral good rea­sons for not us­ing Flash. Its mar­ket share has dropped sig­nif­i­cantly in the last cou­ple of years. There is also a good chance that some­one vis­it­ing your site will not have Flash in­stalled (pretty much a guar­an­tee on iOS).

So, what should you do when you want to find out which fonts are in­stalled lo­cally? Well, you can ac­tu­ally use plain JavaScript to de­tect if a font is avail­able or not. Here are some of the sys­tem fonts your com­puter claims to sup­port:

The re­sult of de­tect­ing the avail­abil­ity of sev­eral com­mon sys­tem fonts on your com­puter. The ones marked with ✓ are avail­able on your com­puter; the ones marked with ✗ are not.

The ex­am­ple above uses Font­FaceOb­server, a small JavaScript li­brary I wrote for de­tect­ing when a web font loads. Even though Font­FaceOb­server was cre­ated for web fonts, it can also be used to de­tect lo­cally in­stalled fonts.

To check if a font is avail­able you pass its name to the FontFaceObserver con­struc­tor and call the check method. If the re­turned promise is re­solved the font is avail­able lo­cally; if the promise is re­jected the font is not avail­able.

var observer = new FontFaceObserver(‘Georgia’);

observer.check().then(function () {
  // Georgia is available
}, function () {
  // Georgia is not available
});

By do­ing this for each font in your font menu you can warn the user if some of their choices are not avail­able (or look dif­fer­ent).

How does it work?

The idea be­hind Font­FaceOb­server is very sim­ple: cre­ate a span el­e­ment with a known font fam­ily, mea­sure its width, set the tar­get font fam­ily, and mea­sure the width again. If there is a dif­fer­ence in the width, you’ll know that the font has ren­dered and is thus avail­able.

In the ex­am­ple above we use a span el­e­ment with the string BESbwy ren­dered in the generic sans-serif font fam­ily. The string BESbwy is used be­cause it con­tains char­ac­ters that of­ten have dif­fer­ent width in many fonts. The ini­tial width of the span us­ing the sans-serif font fam­ily is 130 pix­els.

We then change the font-family prop­erty of the span to in­clude our tar­get font. The tar­get font is found and the browser re­draws the span. Be­cause the tar­get font’s met­rics (the widths of each in­di­vid­ual char­ac­ter) are dif­fer­ent the span changes its width to 126 pix­els. The dif­fer­ence be­tween the ini­tial width and the tar­get width means that the font is avail­able.

This ap­proach has a prob­lem though. What would hap­pen if the font we’re try­ing to de­tect is the same as the generic sans-serif font fam­ily? In this case the span would stay the same width and we would­n’t be able to de­tect the font. To work around this prob­lem Font­FaceOb­server uses three spans, one for each of the three generic font fam­i­lies: sans-serif, serif, and monospace.

In this ex­am­ple the font we’re try­ing to de­tect is the same as the generic serif font fam­ily. If we had used a sin­gle span with we would­n’t be able to de­tect this font be­cause the ini­tial and tar­get width are the same for the serif span. Be­cause the other two spans have dif­fer­ent ini­tial widths we can safely as­sume the font has ren­dered and is avail­able for use.

Can I use it?

So how ac­cu­rate is it? It works well on plat­forms that are truth­ful about which fonts are in­stalled. Un­for­tu­nately, some plat­forms lie. For ex­am­ple, An­droid only ships with three fonts: Droid Sans, Droid Serif, and Droid Sans Mono­space (or in more re­cent ver­sions of An­droid: Ro­boto and Noto). Its graph­ics sub­sys­tem maps com­mon font fam­ily names to lo­cally in­stalled fonts. This means that from the point of view of the browser those fonts ac­tu­ally ex­ist, even though they are in fact aliases to the same fonts. While this is dis­ap­point­ing, you could ar­gue that those aliases were cho­sen be­cause they are ac­cept­able sub­sti­tutes (well, let’s hope so).

Fontfamily.io

Font aliases are less com­mon on other plat­forms, but still ex­ist. Zach Leather­man’s font­fam­ily.io is a great tool to find out which fonts are avail­able on which plat­forms. It’ll also tell you if a font is aliased or not.

So, is this a re­li­able way to de­tect lo­cally in­stalled fonts? It re­ally de­pends on your goals. I would­n’t rec­om­mend us­ing this to dis­able items in a font menu. How­ever, it can be a help­ful tool to pop up a warn­ing when a “web safe” font is­n’t in­stalled lo­cally. In the end, if your web ap­pli­ca­tion re­lies on the pres­ence of a font, it is best to use web fonts in­stead of lo­cal fonts. You can be cer­tain that web fonts will be avail­able no mat­ter the plat­form.

Credits

Remy Sharp ad­vo­cates a sim­i­lar tech­nique in his blog post on “How to de­tect if a font is in­stalled”. His method re­lies on Comic Sans as a way to de­tect the ex­is­tence of a lo­cally in­stalled font. Un­for­tu­nately this is un­re­li­able on plat­forms that do not have Comic Sans (of which there are many).

Lalit Pa­tel has im­ple­mented a sim­i­lar so­lu­tion which uses the generic mono­space font fam­ily. While this is bet­ter than us­ing Comic Sans it can re­port false neg­a­tives for sys­tem fonts and met­ric com­pat­i­ble fonts.

Fi­nally, Zach Leather­man uses the same tech­nique for de­tect­ing lo­cally in­stalled fonts for font­fam­ily.io.