Fork me on GitHub

Font.js

by Mike "Pomax" Kamermans - @TheRealPomax on Twitter, Pomax on numerous IRC networks.

This library adds a Font object to the collection of predefined objects available to you when doing JS coding for the web. You are probably already familiar with new Image() for loading images through the browser; this object does something similar for fonts.

Font.js can be used for both remote fonts (from URL) or system fonts (by font family name), which allows you to use the Font object as a way to detect whether a specific system font is installed at the client, for implementing typesetting fallbacks and failsafes.

Download Font.js directly, or head over to github for the code project page

The API

constructor
var font = new Font();
load event handler
font.onload = function() { /* your code here */ };
error event handler
font.onerror = function(error_message) { /* your code here */ };
name assignment
font.fontFamily = "name goes here";
font loading
remote font: font.src = "http://your.font.location/here.ttf";
system font: font.src = font.fontFamily;
This will kick off font loading and will make the font available on-page. note: this "src=fontFamily" approach works for any font that is known by name, rather than by filename, so this doesn't just cover system fonts, but also fonts that have been loaded from @font-face outside of Font.js, such as Google, Typekit and Font Squirrel webfonts.
DOM removal
document.head.removeChild(font.toStyleNode());
This only applies to fonts loaded from a remote resource. System fonts do not have an associated style node.
DOM (re)insertion
document.head.appendChild(font.toStyleNode());
This is only required if you removed the font from the page, as the font is added to the DOM for use on-page during font loading already.

Font Metrics

Note that system font files cannot be inspected, so they do not have an associated font.metrics object. Instead, font.metrics is simply the boolean value "false".

font.metrics.quadsize
The font-indicated number of units per em
font.metrics.leading
The font-indicated line height, in font units (this vaue is, often, useless)
font.metrics.ascent
The maximum ascent for this font, as a ratio of the fontsize
font.metrics.decent
The maximum descent for this font, as a ratio of the fontsize
font.metrics.weightclass
The font-indicated weight class

Text Metrics

font.measureText(string, size)
Compute the metrics for a particular string, with this font applied at the specific font size in pixels
font.measureText(...).width
the width of the string in pixels, using this font at the specified font size
font.measureText(...).fontsize
the specified font size
font.measureText(...).height
the height of the string. This may differ from the font size
font.measureText(...).leading
the actual line spacing for this font based on ten lines.
font.measureText(...).ascent
the ascent above the baseline for this string
font.measureText(...).descent
the descent below the baseline for this string
font.measureText(...).bounds
An object {xmin:<num>, ymin:<num>, xmax:<num>, ymax:<num>} indicating the string's bounding box.

How does it work?

We introduce a JavaScript object called Font with a property "src" that, when set, triggers an AJAX retrieval of the font you indicated should be downloaded. When the download is complete, rather than handing control over to the browser, the code first examines the font file: is it a real font? Is it truetype or opentype? And what are the encoded metrics values? Once it has determined these things (and no errors have occurred because the font is bad), the package tells the browser to set up a text paragraph, to be typeset with this font.

In order to verify that the text is indeed typeset with the correct font, rather than with a fallback font, the code generates a tiny fallback font that encodes a single character, being a character that the loaded font contains (retrieved from the font's character map). This fallback font doesn't actually encode the character as a letter, but as a zero-width unit, so as long as the real font isn't quite ready for typesetting yet, the browser will use this zero-width fallback font for the text paragraph, and its width will be zero pixels.

The code then polls the paragraph width every 50 milliseconds, and when it sees a real width, rather than a zero width, it knows the browser is now done loading the font into memory and getting all the data it needs to style text with it. When this is the case, the onload() function is called, and whatever code you wrote for handling font.onload() will be triggered.

Font.js is compatible with all browsers that support <canvas> and Object.defineProperty - This includes all versions of Firefox, Chrome, Opera, IE and Safari that were 'current' (Firefox 9, Chrome 16, Opera 11.6, IE9, Safari 5.1) at the time Font.js was released.

Font.js will not work on IE8 or below due to the lack of Object.defineProperty - I recommend using the solution outlined in http://ie6update.com/ for websites that are not corporate intranet sites, because as a home user you have no excuse not to have upgraded to Internet Explorer 9 yet, or simply not using Internet Explorer if you're still using Windows XP. If you have friends or family that still use IE8 or below: intervene.

Demonstrators for TrueType and Type2 fonts

Let's try this with the A.C.M.E. Secret Agent font by BLAMBOT:

code used:

(press button to load font and show code)

Let's also try this for a (slightly smaller) OTF font instead: Diavlo, by exljbris Font Foundry.

code used:

(press button to load font and show code)

Of course, if you want to use one of the older "web safe" system fonts, you can use those too. Let's try "Verdana":

code used:

(press button to load font and show code)

Comments and feedback

If you have any questions or remarks about Font.js, feel free to leave a comment here. If you have a bug report, please use the github issue tracker instead.

Comments

Leave a comment