Thursday, February 28, 2013

Preloading web fonts for use with html CANVAS

The problem:
Your application makes use of html <canvas> to draw some text to a canvas element when the page loads. You've picked out a cool web font to use but find that it isn't loading in time to be drawn onto the canvas. Unlike DOM elements that automatically update to the correct font when it becomes available ( FOUT ), the canvas is simply drawn with the incorrect font.  Refreshing the canvas or page will probably display the correct font because it has finished loading or has been cached.

There are several creative ways around this. The following one has not been extensively tested but is something I want to experiment with in the future.

I found that while using @font-face to declare fonts in CSS, there was no way to know when the resources requested ( font files ) were loaded. The 'onload' will fire because all of the scripts and stylesheets have loaded, but the files the stylesheets requested could still be in transit.

By embedding the font files in the CSS by base64-encoding them, I was able to verify that the stylesheet ( including the font ) was loaded when the onload event happens. Run this within the <head> tags.

        var fileref = document.createElement("link");
        fileref.setAttribute("rel", "stylesheet");
        fileref.setAttribute("type", "text/css");
        fileref.setAttribute("href", "PATH_TO_CSS_FILE");
        document.getElementsByTagName("head")[0].appendChild(fileref);

Here are some considerations:
This approach will increase the size of your font by 30-40%, but will spare a round trip to the server to fetch additional font files.
This will prevent the page from loading until your whole CSS/font file is loaded. This could be unacceptably long if using a large font, multiple fonts, or browsing on a mobile device.

Now here is something I'm working on that is very untested and crazy:

        var filename = "cssGen.php?font="+fontFile;
        var fileref = document.createElement("link");
        fileref.setAttribute("rel", "stylesheet");
        fileref.setAttribute("type", "text/css");
        fileref.setAttribute("href", filename);
        document.getElementsByTagName("head")[0].appendChild(fileref);

In this case the CSS file being added is actually a PHP script posing as CSS.  The name of the font file is passed into the css/php using the 'font' query parameter.  Then inside the css/php script:

***
<?php
    header("Content-type: text/css; charset: UTF-8");
   
    $font = explode(".",$_GET['font']);
    $fontData = file_get_contents("fonts/".$_GET['font']);
    $encoded = base64_encode($fontData);
?>
@font-face {
    font-family: '<?php print $font[0]; ?>';
    src: url(data:application/x-font-woff;charset=utf-8;base64,<?php print $encoded; ?>) format('woff');
    font-weight: normal;
    font-style: normal;
}

#myCanvas {
    border: 1px dashed #999;
}

body {
    font-family: '<?php print $font[0]; ?>';
}

***
The headers are set so the file is interpreted as text/css. The name of the font family is derived from the filename and the font file is base64 encoded on the fly.
Again, not tested much, but fun to play around with.  I thought the whole idea of executing php inside CSS was worth trying anyway.

No comments:

Post a Comment