Thursday, October 7, 2010

Locally Caching Google Web Fonts

So recently amid all my hubbub of implementing CSS3 & HTML5 feature sets, I ran into a very annoying problem regarding Google Web Fonts ( Which I love, tremendously ). The problem, in a nutshell, is this: DoubleClick. I have hated this "technology" for as long as it's existed. Not for any paranoid tin-foil-hat-wearing concerns about my cat pictures being cached for all eternity. Rather because it's so painfully resource-intensive. When I'm trying to access web content and I see in my browser status bar, "Waiting for xyz.doubleclick....", a slow seething sort of anger starts to seep into me.



Well, it turns out that since Google now owns DoubleClick and they put it in pretty much everything, "everything" includes end user downloads of Web Fonts, which is otherwise an excellent and extremely game-changing solution for presentation design. As opposed to, say, sifr, which makes me want to run away and hide under Zeldman's seat cushion.



Also, as far as my personal anecdotal testing is concerned, these fonts do _not_ cache well. Maybe the actual font does, but the added HTTP overhead from DoubleClick pretty much negates any actual caching of the font data itself. So, how to fix? Well, pretty simply, really, as it turns out. I'll outline it here.



The current state of affairs:



If you include a Google Web Font in your HTML documents and have any sort of basic thimblefull of knowledge about HTML & CSS relationships, you'll notice that the basic relationship is just like any other CSS include <link> that you might create yourself. The difference is that the end document is not a flat CSS file, but rather "dynamically" calls for a specific font family & possible variant(s) to be pulled into your overall HTML document. Like this:



<link href="'http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic&subset=latin' rel='stylesheet' type='text/css'>



Well, so that's fairly ok, right? Just for kicks, let's go & see what that page contains. It's just a URL after all. Click Here to see it:



http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic&subset=latin



Again, fairly ok. Now, we have a whole new set of URL's, and some basic CSS to encapsulate them. Again, in this example I've selected all 4 variants of PT Sans. You might have more or fewer in your actual usage of a particular font.



Again, copy-paste one of those URL's into a browser tab ( you're using tabs, right? Ok, just checking... ). Ah, now here's NOT a normal text file ala CSS/HTML. It is in fact a ttf, or True Type Font, file. Again if you inspect the URL you can tell that it's being "dynamically" generated.



This is all fine & well and not a huge concern, if you have even some basic access to your web stores, ala FTP.



The Fix:



What I've done can be pretty simply described. I've added a few flairs based on long-term considerations. Basic steps:




  1. Copy-Paste the font ( or font family ) CSS file.

  2. Put the resulting FLAT CSS file at a location & naming convention of your choosing, somewhere within your web app or a centralized known location that you can always get to.

  3. Download each of the .ttf files, rename them according to some sensible convention, inclluding the actual ".ttf" file extension. The mime-type should be included in the binary of the file itself. ( This is a good theory. If any knows how to test it let me know. )

  4. Place those, again, somewhere sensible that you can access. A "ttf/" directory in the directory that you placed that CSS file in step 2 seems pretty darn reasonable to me.

  5. Edit the CSS to reflect the new URL's of those .ttf's instead of Google's "dynamicized" URL's.

  6. Edit the original CSS include to reflect the new location of your flat CSS file.
  7. Enjoy fast download times on Web Fonts.



That's pretty much it.



Some Considerations:



Naming conventions I used were pretty much according to my existing web development conventions. "PTSans.css" ( really complicated, right? ). "ttf/PTSansItalic.ttf" ( super creative, I know. ).



I also placed these on my own company domain, under a /_css/webFonts/ directory. That way I can "locally" store them just once and continually recycle the URL's for them.



If I'm going to use one of these, I'm just going to grab all variants from Google, do all this downloading once, and be done with it, rather than piece-mealing each variant. Small bit of extra time now, saving time later.



I used wget ( best command line tool E V E R ) to pull these to my web server. If you happen to be blessed with shell access, here's an example ( assuming you're cd'd to the directory you want the .ttf's in ):



# wget http://themes.googleusercontent.com/font?kit=F51BEgHuR0tYHxF0bD4vwvesZW2xOQ-xsNqO47m55DA -O PTSansBold.ttf



Hope that helps someone. This post is mostly just to document the fact that DoubleClick latency is EXTREMELY annoying, and that again, from my anecdotal testing definitely affects end-user experience if you're trying to use this overall technique.

2 comments:

  1. Assuming you want several of these (in all the formats, ttf,woff,otf), I just wrote the following shell one-liner. (yes, it is all one long line!)

    You can just specify the font family (the family=xxx bit) and the script will fetch all 3 files, and name them sanely.

    HTH - Richard


    family='Dancing+Script'; for url in $( { for agent in 'Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0' 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1' 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)' ; do curl -A "$agent" -s "http://fonts.googleapis.com/css?family=$family" | grep -oiE 'http://[a-z0-9/._-]+'; done } | sort -u ) ; do extn=${url##*.} ; file=$(echo "$family"| tr +[:upper:] _[:lower:]); echo $url $file.$extn; curl -s "$url" -o "$file.$extn"; done

    ReplyDelete
  2. Thanks Richard, converting to a shell script that takes the desired fonts as arguments. I also needed to adjust the regex slightly.

    #!/bin/sh

    for family in $*; do
    for url in $( {
    for agent in \
    'Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0' \
    'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1' \
    'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)' ;
    do
    curl -A "$agent" -s "http://fonts.googleapis.com/css?family=$family" | \
    grep -oE 'http://[A-Za-z0-9/._-]+'; \
    done } | sort -u ) ;
    do
    extn=${url##*.} ;
    file=$(echo "$family"| tr +[:upper:] _[:lower:]);
    echo $url $file.$extn;
    curl -s "$url" -o "$file.$extn";
    done
    done

    ReplyDelete