Kimler Sidebar Menu
Kimler Adventure Pages: Journal Entries
Better @font-face Syntax
I recently published an article about cross-browser font embedding, using the @font-face CSS selector. It turns out that the code I put forth causes a 404 look-up in Internet Explorer. A reader has suggested some superior code, which I put to the test
Paul Irish Sets My Morning Schedule
Funny how a single comment can change the direction of my day!
Paul proposes two concepts - new to me - in his recent article, "Bulletproof Font Face Implementation":
- Internet Explorer tries and fails to download the TTF file (with 2-selector syntax) even though the 2nd @font-face selector includes a "format" declaration.
- He proposes a single @font-face selector, which satisfies all browsers (obviating the need for two selectors), searches the local computer for the font first and eliminates the Internet Explorer "file not found" problem.
Okay ... this is techie, geeky cool and - for sure - not everyone is going to want to read about this, so here is where you should get off the geek train (if you haven't already).
If you're all aboard, heading for geekdom and want to be cool, then read on brave web-font enthusiasts ...
Bringing you up to date
If you haven't heard, since the Jun 30th release of FireFox 3.5 - and for the first time ever on the web - it's possible for cross-browser embedding of fonts. I was one of the first to twig about this and I wrote a *wildly popular* article in early July titled: "xBrowser Fonts" (using fancy embedded fonts, thank you very much)! The article provides a bit of history as well as good resources and a tutorial. You might want to give it a read, if you're new to all this, as today's article is a follow-on.
Today's article is "an exploration", of sorts. Exciting and I'm hoping it pans out, but let's just call it "an exploration", so we don't just run around firing off guns and stuff without checking things.
Because Internet Explorer's support for @font-face is handled differently than the recent CSS3 recommendations, two @font-fact selectors are thought to be necessary. Like this:
|/* for MSIE */|
|/* for Others */|
|src: url(/fonts/lexograph.ttf) format("truetype");|
|/* use the font */|
Having two separate selectors doesn't look as nice as having one, but it just seemed to be the best way to go, at the time. It was my understanding that the reason this worked, was because Internet Explorer couldn't parse the second @font-face selector, because it contained a "format" declaration - it would just ignore it.
Andrea pointed out that Internet Explorer didn't ignore it, but rather, tried to download a very obtuse file instead, which yields a 404 on the server. So, I took a peek at my own server logs this morning, it turns out that Andrea is spot on. Here's a line from our server log, showing what Internet Explorer ends up looking for:
|184.108.40.206 - - [04/Sep/2009:11:42:23 -0400] "GET /fonts/lexograph.ttf)%20format(%22truetype%22 HTTP/1.1" 404 7601 "http://randsco.com/index.php/2009/07/04/p680" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322)"|
Ugly, eh? Well, it's more than ugly, since it's a complete waste of server resources. Looking for a file that we already know isn't there - Bah! It doesn't get much uglier than that. This will not do!
So much for Internet Explorer ignoring that second @font-face selector.
Testing Paul's Bulletproof @font-face Syntax
Paul proposes a single selector that not only fixes the "File not Found" issue in Internet Explorer, but also provides (at least for non-IE browsers) the ability to search locally for the font file, before pestering the server to download it. (Actually, it's the "local" declaration that causes Internet Explorer to lose it's head, which means that it truly ignores that line in the CSS, but it's more fun to claim it's a feature than a necessity!)
Here's the code that Paul's suggested:
|font-family: "some lexograph";|
|src: local('lexograph'), url(/fonts/lexograph-paul1.ttf) format("truetype");|
|/* use the font */|
|font:2em/1.1em "some lexograph",verdana,sans-serif;|
So, let's test it. (I modified the font-family name for some later testing, as I want to see if IE loads locally or not. I also modified the EOT filename and the TTF filename, so they'll be easier to pull from the server logs).
Oh ... first I must see if I have the font loaded locally. Yay ... I don't, but the local font is called "Lexographer", not "lexograph", which will be important to know, later. For now I'm just happy it won't find it locally, because I want to test the downloading.
So I hit the page with IE8 and quickly realize - DOH! - while I changed the EOT and TTF filenames in the CSS, I neglected to change them on the server. Ack ... *has to bump everything up a notch to "2"* now!
I hit the page again with IE8 and notice the fancy fonts are used on the page (it works for IE) and in the server logs, there's only a call for the EOT file (no 404 for the TTF). Yay! So far, so good.
I'll hit the page with FireFox, Safari and Opera (ewe ... is Opera 10 been released yet? YES! ... upgrade time!) HA ... Opera10 was just released on Sept. 1st and does support the CSS3 font embedding! Yay. Okay ... let me hit it first with Opera .... check the server logs ... should be only one EOT hit and one TTF hit on "paul2" files. Hmmm ... "paul2.ttf" isn't found. Let's try under "paul.html". Hmmm ... I see the Opera hit, but only on the HTML page, no other hits. Odd. Let's try again, with "Paul3" this time. Still nothing. Hmmm, makes me think it's loading it from the local machine, but I don't see how that could be. Even though it's now set to "local('lexographer')" that font is not "loaded" locally (and I can't believe it's pulling it from my HDD location). I changed that name to "lexoman", just to be certain. Delete private data, reload the page, the fancy font loaded (but I notice there's a flash of "normal" font - just like FireFox - before the fancy font is replaces, yuck). Look at the logs again ... finally, there's a "paul3.ttf" 200 entry:
|220.127.116.11 - - [04/Sep/2009:13:52:19 -0400] "GET /test/fontFace/lexograph-paul3.ttf HTTP/1.1" 200 77600 "http://randsco.com/test/fontFace/paul.html" "Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00"|
Pauls@font-face bulletproof syntax works!
So Internet Explorer is pulling the EOT (and not looking for the TTF, because it's src first contains a "local()" declaration), as confirmed by the server logs. Additionally, the other browsers (FireFox3.5, winSafari, (presumably macSafari) and now Opera10) all pick up that TTF file. Yay!
Testing the Local() Loaded Fonts
I'm curious about the local setting. I'd like to test the other browsers and make sure that there's not a server call if the font is loaded locally. So I'll rename everything EOT, TTF and HTML to "paul4" and see what flies. (Oh, and load the lexographer font locally). Here goes. I'm expecting to see a hit in the log files for paul4.html, but none for paul4.ttf (or paul4.eot), when I load that page - for the first time - using FireFox. (I'll pick on FireFox for a while).
Odd ... no fancy fonts.
LOL ... it was the paul.html file I edited "and saved", but it was the paul3.html I renamed to paul4.html. Ack! *renames the saved paul.html file to paul5.html*
Disappointing results. Despite having the font locally loaded (not installed mind you, but loaded) and calling it correctly by the name, there's still FireFox still makes a server call for the TTF file:
|18.104.22.168 - - [04/Sep/2009:14:10:24 -0400] "GET /test/fontFace/lexograph-paul5.ttf HTTP/1.1" 200 77600 "http://randsco.com/test/fontFace/paul5.html" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:22.214.171.124) Gecko/20090729 Firefox/3.5.2"|
Maybe I'm doing something wrong? Let me install it, just for grins and look at the font information. Maybe it's got a different name? (The font viewer shows - a weird break after the "Jay" - and that the font is called "Lexographer"). Yay, I'm also not breaking any copyright rules, but I already knew that! Thanks Tom Murphy, for making and letting folks use your font, but what's up with the J?
Think maybe the capital "L" might make a difference? Can't imagine. Maybe installation does? Let's try that first. It's installed. *bumps stuff up to 6* and tries with FireFox again. Ewe ... nope, "installation" doesn't make a difference, but the capital "L" does! (Yuck ... the file name has to be exact and capitalization matters! I feel like I'm in grade school all over again).
Now that things are working finally, I'll bump up to "paul8" (I kinda was busy there for a minute) and see if there's any TTF calls in the server logs (I doubt it, but let's be certain). Yep ... no TTF calls, so the browser is grabbing the local (installed) version. Crap, suppose now I need to test a loaded (not installed) font. Eye Veh! *bumps up to paul9* Yeah, worked fine (it picked up the loaded font, even when I renamed the font files to something that doesn't exist).
Now, for the final, fancy test. Will Internet Explorer pick up the font file locally, if I just specify it when I use it, saving a EOT download (I kinda doubt it, since the @font-face selector comes first). Here's what I mean:
|font-family: "some lexograph";|
|/* even though we know IE will pick up the red line */|
|src: local('Lexographer'), url(/test/fontFace/lexograph-paulx.ttf) format("truetype");|
|/* now use the fonts like any other font */|
|/* will it load the local file instead? */|
|font:2em/1.1em Lexographer,"some lexograph",verdana,sans-serif;|
I went to "paulx" here, cause - hopefully - it's my last test (whew!)
The answer, as I expected, is "No". IE will load the EOT file, even if it's locally loaded and named correctly when you use it, later on.
|126.96.36.199 - - [04/Sep/2009:14:38:56 -0400] "GET /test/fontFace/lexograph-paulx.eot HTTP/1.1" 200 39500 "http://randsco.com/test/fontFace/paulx.html" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322)"|
This concludes today's testing. Hasn't it been fun?
ACK! I forgot all about my font-family test. I set it up at the very beginning and forgot all about it, when I renamed the font-family to "some lexograph", rather than give it its true name, which turns out to be "Lexographer". Let's go back and try a "paulz" case, using that. (Here's what I want to test, to see if IE skips the EOT file GET)
|/* will IE load locally and skip the EOT GET? */|
|src: local('Lexographer'), url(/test/fontFace/lexograph-paulz.ttf) format("truetype");|
|/* now use the fonts like any other font */|
Well, isn't that a kick in the pants. Internet Explorer downloads the EOT file, even though the real font file (named in the font-family declaration) is loaded on the local computer.
|188.8.131.52 - - [04/Sep/2009:15:06:44 -0400] "GET /test/fontFace/lexograph-paulz.eot HTTP/1.1" 200 39500 "http://randsco.com/test/fontFace/paulz.html" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.0.3705; .NET CLR 1.1.4322)"|
In a very round-a-bout way, maybe this makes sense, as the (precious little) information on Embedding Font on the Microsoft Developer Network indicates: "... however, Internet Explorer 4.0 downloads, decompresses, and temporarily installs font objects, even if the real font is present on the user's computer." (source)
As Andrea points out, using two @font-face selectors leads to unnecessary work for the server, as Internet Explorer will look for munged TTF font file. Paul introduced some bullet-proof @font-face syntax that fixes this and affords other browsers the opportunity to load the file locally, before downloading the TTF or OTF font file off the server. My personal testing verifies that Paul's method is indeed superior. Also, there appears to be no point of trying to tell Internet Explorer "look for this font locally", because IE is just plain stubborn and will download the EOT regardless.
My hat is off to both Andrea and Paul! Thanks for turning me on to this superior method and I'll be amending (and crediting) the copy in my xBrowser Fonts article.