As I’m piecing together a very comprehensive solution for using custom typefaces online, one of the crucial aspects is determining what browsers support @font-face.
My requirements for this detection were:
No browser userAgent sniffing
No extra HTTP request required
The test must be synchronous, no race conditions or HTTP requests
/*! * isFontFaceSupported - v0.9 - 12/19/2009 * http://paulirish.com/2009/font-face-feature-detection/ * * Copyright (c) 2009 Paul Irish * MIT license */varisFontFaceSupported=(function(){varfontret,fontfaceCheckDelay=100;// IE supports EOT and has had EOT support since IE 5.// This is a proprietary standard (ATOW) and thus this off-spec,// proprietary test for it is acceptable.if(!(!/*@cc_on@if(@_jscript_version>=5)!@end@*/0))fontret=true;else{// Create variables for dedicated @font-face testvardoc=document,docElement=doc.documentElement,st=doc.createElement('style'),spn=doc.createElement('span'),wid,nwid,body=doc.body,callback,isCallbackCalled;// The following is a font, only containing the - character. Thanks Ethan Dunham.st.textContent="@font-face{font-family:testfont;src:url(data:font/opentype;base64,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)}";doc.getElementsByTagName('head')[0].appendChild(st);spn.setAttribute('style','font:99px _,serif;position:absolute;visibility:hidden');if(!body){body=docElement.appendChild(doc.createElement('fontface'));}// the data-uri'd font only has the - characterspn.innerHTML='-------';spn.id='fonttest';body.appendChild(spn);wid=spn.offsetWidth;spn.style.font='99px testfont,_,serif';// needed for the CSSFontFaceRule false positives (ff3, chrome, op9)fontret=wid!==spn.offsetWidth;vardelayedCheck=function(){if(isCallbackCalled)return;fontret=wid!==spn.offsetWidth;callback&&(isCallbackCalled=true)&&callback(fontret);}addEventListener('load',delayedCheck,false);setTimeout(delayedCheck,fontfaceCheckDelay);}functionret(){returnfontret||wid!==spn.offsetWidth;};// allow for a callbackret.ready=function(fn){(isCallbackCalled||fontret)?fn(fontret):(callback=fn);}returnret;})();
isFontFaceSupported()// will return a boolean indicating support// you can also use with a callback,// it will be called 100ms later which is adaquate for Gecko and Webkit to properly use the data-uri'd font.isFontFaceSupported.ready(function(bool){// bool is a boolean that indicates support});
Sorry. :(
You’ll spot the IE conditional compilation in there. I don’t like it either, but I’m unaware of any other workable approach (that doesn’t pull in an .eot) to test for @font-face support. If you have an idea, please share it!
On the approach
I first use the Web Font Optimizer to subset a truetype font to only contain the period (.) character (2.2k file!), then send it through a data URI converter, then chuck it into a style tag. I test the width of a span of text without the custom font, and then again with the custom font. If the values are different, we can assume @font-face is supported and works.
With a trip through the YUI Compressor, the script is 3.5k3.1k. If you have any ideas on bringing that figure down, I’d love to hear ‘em.
Great! Any disadvantages?
Yeah there’s one big one. Both Gecko and Webkit load in a data-uri font asynchronously, so the test may give a false negative if you call isFontFaceSupported() immediately afterwards:
1234
<script src="isFontFaceSupported.min.js"></script><script> if (isFontFaceSupported()) ... // this may report a false negative.
// that's why we have the isFontFaceSupported.ready() callback mechanism
I’m not terribly happy with this asynchronous delay, so I’ve written an alternative that uses browser userAgent sniffing. This practice is not recommended and is not terribly future-proof, but it’s the only synchronous solution available.
/*! * isFontFaceSupported - Sniff variant - v0.9 - 12/19/2009 * http://paulirish.com/2009/font-face-feature-detection/ * * Copyright (c) 2009 Paul Irish * MIT license *//* Browser sniffing is bad. You should use feature detection. Sadly the only feature detect for @font-face is asynchronous. So for those that *need* a synchronous solution, here is a sniff-based result:*/varisFontFaceSupported=function(){varua=navigator.userAgent,parsed;if(/*@cc_on@if(@_jscript_version>=5)!@end@*/0)returntrue;if(parsed=ua.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/))returnparsed[1]>='4.0.249.4';if((parsed=ua.match(/Safari\/(\d+\.\d+)/))&&!/iPhone/.test(ua))returnparsed[1]>='525.13';if(/Opera/.test({}.toString.call(window.opera)))returnopera.version()>='10.00';if(parsed=ua.match(/rv:(\d+\.\d+\.\d+)[^b].*Gecko\//))returnparsed[1]>='1.9.1';returnfalse;}
2009.09.24: updated the code, and threw it all on github. callback style makes its debut here. This code matches the exact same implementation that’s in Modernizr 1.0
2009.12.18: Added a useragent sniffing alternative for those who want reliable synchronous detection.
2009.12.19: New, smaller font file (Thanks Ethan Dunham). The file is now 15% smaller. Script does not remove the extra element it adds to the DOM now, as to assure more accurate results.
2010.11.02: I now recommend a different technique.. It’s from Diego Perini.. The code is below: