Test if a font is installed via JavaScript

By Luke Smith on May 26, 2009 8:45 AM

There are a few threads available on the web with regard to how to test if a font is installed on a client machine, but I'm not satisfied with any of those that I found. The reason being that they each seem to use a single common font as a baseline measurement and compare the dimensions of some reasonably complex string rendered in the common font against the same string rendered in the test font.

This irks me for a number of reasons:

  1. It assumes the common font is installed
  2. It only confirms the dimensions of the tested font don't coincide with the common font
  3. All of those I saw rerendered the same element over and over, triggering an unnecessary number of reflows

If you want to check if a font is installed, start with a provable test. The system default monospace font should render a string in different dimensions than the system default sans-serif font. But you can test this.

(function () {
var div = document.createElement('div'),
    different = false;

div.innerHTML = '<span style="...;font-family: sans-serif">some string</span>' +
                '<span style="...;font-family: monospace">some string</span>';

document.body.insertBefore(div, document.body.firstChild);

different = div.childNodes[0].offsetWidth != div.childNodes[1].offsetWidth;

document.body.removeChild(div);

alert(different);
})();

Using this tested assertion as a baseline, let the browser tell you if the test font is installed by using the default behavior of font stacks. Create two elements with the same content ("ii" has been sufficient in my tests), each styled with a font-family of the test font followed respectively by the two control fonts that you know to render to different dimensions.

<b style="font: normal 10px/1 'FONT_X', sans-serif !important">ii</b>
<b style="font: normal 10px/1 'FONT_X', monospace !important">ii</b>

The two elements will render to the same dimensions if and only if the the font is installed.

Here's my approach:

function testFont(name) {
    name = name.replace(/['"<>]/g,'');

    var body  = document.body,
        test  = document.createElement('div'),
        installed = false,
        template =
            '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',sans-serif !important">ii</b>'+
            '<b style="display:inline !important; width:auto !important; font:normal 10px/1 \'X\',monospace !important">ii</b>',
        ab;

    if (name) {
        test.innerHTML = template.replace(/X/g, name);

        test.style.cssText = 'position: absolute; visibility: hidden; display: block !important';

        body.insertBefore(test, body.firstChild);

        ab = test.getElementsByTagName('b');

        installed = ab[0].offsetWidth === ab[1].offsetWidth;

        body.removeChild(test);
    }

    return installed;
}

The code above assumes the string "ii" renders to different width in monospace than sans-serif, but the important part is that this can be tested if you don't feel comfortable with that assumption. Additionally, containing two elements in an absolutely positioned div should limit the scope of the two requisite reflows if the browser is smart about it.

If you want to test more fonts, create more template entries in the innerHTML all at once before attaching the div to the DOM, then just loop through the entry pairs. This will preserve the two reflow impact.

Here's a test page.

And here's the code in a GitHub gist. It's likely this is more up to date than the snippet above.

8 Comments

  1. Gravatar

    This is a great improvement!

    Too bad it's still not 100% conclusive since some mobile phone somewhere surely sizes the letter "i" in monospace the same as in sans-serif. But I'll wager you've got something that'll work 99% of the time. Screw that mobile phone vendor and its 6 users. ;-)

    But seriously, very clever thinking.

  2. Gravatar

    The baseline can be tested separately in preparation for the actual test function. I was just too lazy to actually write that part, since I figured readers would understand how to do it if they cared that much :)

    Rather than assume monospace and sans-serif, have the template strings concat in fontA and fontB, then separately in an initialization phase, do a very similar test to establish the proven !== fonts, and set fontA and fontB for runtime reference.

  3. Gravatar

    Love this idea.

    However, I'm getting a false positive for Helvetica on my Windows machine in FF3?

  4. Gravatar

    That's wild! I can reproduce this, and it seems to make no sense unless IE has some built in logic to accept "Helvetica" (it is a popular font, after all) but use a different font in its place.

    This is both fascinating and saddening. I'll have to do some digging into this behavior. Thanks for sharing!

  5. Gravatar

    Confirmed. This snippet from \Windows\system\setup.inf suggests it's not the only one, too:

      win.ini   , FontSubstitutes,                    ,   "Helv=MS Sans Serif"
      win.ini   , FontSubstitutes,                    ,   "Tms Rmn=MS Serif"
      win.ini   , FontSubstitutes, "Courier=Courier New" ,
      win.ini   , FontSubstitutes,                    ,   "Times=Times New Roman"
      win.ini   , FontSubstitutes,                    ,   "Helvetica=Arial"
  6. Gravatar

    I'm very happy to have found something like this. It's awful the system in windows thinks it's necessary to automatically substitute Helvetica to Arial. Even if the fonts are somewhat alike, that's no reason to really substitute this. Even more because Helvetica isn't even Microsoft's. they can ignore their own fonts, but it's pretty unfair to substitute a font created by someone else.

    I'm using this snippet for a, I hope, text-UI-driven site. I hope I manage to really use it to its best. on another site I also use it to check if I should initiate sIFR3, it's quite handy that way!

  7. Gravatar

    Glad you like it! Using this to avoid sIFR is a good fit, and you should be safe from Windows font substitutions, since you're far more likely to be testing for a decorative font.

  8. Gravatar

    Good one.....

Your comment?

HTML is ok

No TrackBacks (http://lucassmith.name/mt/mt-tb.cgi/121)

ls.n

LucasSmith.name

Luke and Liam

I'm Luke. I am a front end engineer at Yahoo! on the YUI team.

Mostly I write about code stuff, but occassionally I'll mix in some real life. You've been warned.

Archives

Tags

Feeds

Subscribe to feed Recent entries

Content licensed under Creative Commons

Code licensed under BSD license

©2005 - 2010 Lucas Smith

Powered by Movable Type