Runtime Font Embedding in AS 3
by Scott Morgan, Senior Flash Platform Engineer and token Canadian
This article was originally published on Scott Morgan's blog
In an ideal world the Flash Player would allow for dynamic runtime shared font libraries that allow for only a subset of characters to be embeded. In AS2 there were a few ways (hacks) to load font libraries at runtime. Unfortunately none of the AS2 hacks work in Flash CS3 using AS3. There is an answer but it didn't come easy. Read on to learn how to do it and how to get around a compiler bug.
Well, pulled a few more hairs out today. Hard to believe I have any left. I am working on a multi-lingual application that needs embeded fonts. As most of you know, loading every character in a given fontset equates to one big ass swf. For example, if you want to embed a regular and bold Japanese font you're looking at approx. 13 megs, and depending on the quality of the font you could be close to 20 megs. That's a lot of font. In an ideal world the Flash Player would allow for dynamic runtime shared font libraries that allow for only a subset of characters to be embeded.
In AS2 there were a few ways (hacks) to load font libraries at runtime. Unfortunately none of the AS2 hacks work in Flash CS3 using AS3. However, there is an answer. In Flex you can embed fonts at compile time using the [Embed] metadata tag in your Actionscript. And the best part is you can use the unicodeRange attribute to define a subset of characters you want to embed. Below is a class I created that compiles a swf that contains all Latin I characters in the Arial font.
A couple things to point out here. The fontName attribute value can not be the same name as a device font. If I were to change my code to use fontName='Arial' the compiler throws the following warning "the embedded font 'Arial' may shadow a device font of the same name. Use fontName to alias the font to a different name". To get around this I simply added an underscore before the name. From this point on you must reference the _Arial in your TextFormats or CSS. Now if you compile that it will create a swf named _Arial.swf.
Ok, great, now what? Well, now you have to load the font into the application. Here is a sample class that loads in the font and displays some rotated text just to prove that it is embeded.
And there you have it. Run time embeded fonts. The key lines to look at here are
The getDefinition method returns a reference to the _Arial class loaded in through the swf (event.target). The next line:
registers the loaded in _Arial font in the global font list. If you were to trace out the results of Font.enumerateFonts() you will now see _Arial at the top of the list.
Now lets say your site was in a few different languages you may add the following line to the FontLoader constructor to load language specific fonts at runtime.
Instead of loading in _Arial.swf the application will load in jp_Arial.swf. jp_Arial.swf would be another generated font swf like the _Arial example above but this time the unicodeRange would only include the Japanese fonts you need. All you have to do now is create a CSS file and fonts for each language, store the proper language code somewhere within the application and use that language code when loading your CSS files and fonts.
You may not know this but Adobe has supplied us with sample unicodeRanges in the following file "\Applications\Adobe\Flex Builder 2\Flex SDK 2\frameworks\flash-unicode-table.xml". You can either use one of the supplied ranges or create your own. You may only need to embed a few characters, if so just list the unicode values of the characters you want each seperated by a comma.
Sounds pretty straight forward eh? Or is it????
In doing this I ran into a huge bug using Flex Builder to generate the font swfs. When I was experimenting with the unicodeRanges my compiled versions did not contain the proper character ranges that I specified. For example I would define a range that only contained Uppercase characters. In my test font loading app I would only see numbers. Only if I removed the unicodeRange attribute would I see all my characters. This led me to believe that Adobe had documented something that really wasn't part of the compiler. I tried deleting files, I was checking timestamps, nothing. Then I tried to clean my project before compiling (In FlexBuilder select Project > Clean). It worked! The subset of characters I defined in my unicodeRange only loaded into my test app. YAY! Then I tried switching the unicodeRange again. DOH! nothing. Cleaned project again and BINGO!
Lesson learned: Whenever you change your unicodeRange clean your project before you compile or else your change may not be compiled properly.
I haven't tried this in Flex 3 yet to see if the bug has been addressed. I did look at the Flex 3 bug system and didn't see it listed. Either it has been fixed or no one has run into this issue yet. It is pretty obscure I guess.