Runtime Font Embedding in AS3. There is no need to embed the entire fontset anymore
by Carlos Pinho, June 19th, 2007
  • Share
  • Share

Scott Morgan Wrote:

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.

package {

     import flash.display.Sprite;
public class _Arial extends Sprite {

  [Embed(source='C:/WINDOWS/Fonts/ARIAL.TTF', fontName='_Arial', unicodeRange='U+0020-U+002F,U+0030-

U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E')]          public static var _Arial:Class;

     }

}

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.

package{
import flash.display.Loader;

  import flash.display.Sprite;

  import flash.events.Event;

  import flash.net.URLRequest;

  import flash.text.*;     public class FontLoader extends Sprite {


public function FontLoader() {

  loadFont("_Arial.swf");

  }          private function loadFont(url:String):void {

               var loader:Loader = new Loader();

               loader.contentLoaderInfo.addEventListener(Event.COMPLETE, fontLoaded);

               loader.load(new URLRequest(url));

          }


private function fontLoaded(event:Event):void {

  var FontLibrary:Class = event.target.applicationDomain.getDefinition("_Arial") as Class;

  Font.registerFont(FontLibrary._Arial);

  drawText();

  }          public function drawText():void {

               var tf:TextField = new TextField();

               tf.defaultTextFormat = new TextFormat("_Arial", 16, 0);

               tf.embedFonts = true;

               tf.antiAliasType = AntiAliasType.ADVANCED;

               tf.autoSize = TextFieldAutoSize.LEFT;

               tf.border = true;

               tf.text = "Scott was herenScott was here toonblah scott...:;*&^% ";

               tf.rotation = 15;

               addChild(tf);

          }


}}

And there you have it. Run time embeded fonts. The key lines to look at here are

var FontLibrary:Class = event.target.applicationDomain.getDefinition("_Arial") as Class;.

The getDefinition method returns a reference to the _Arial class loaded in through the swf (event.target). The next line:

Font.registerFont(FontLibrary._Arial);

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.

public function FontLoader() {

     var langManager:LanguageManager = LanguageManager.getInstance();

     var langID:String = langManager.getLanguageCode(); //returns jp

     loadFont(langID + "_Arial.swf");

}

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.

Brgds,
CP

Copy and Paste the code below
Email and IM
Websites
Forums
Get This

8 Responses to “Runtime Font Embedding in AS3. There is no need to embed the entire fontset anymore”

  1. Emre Selen Says:

    Hi there…
    I recieve this;

    TypeError: Error #2007: Parameter font must be non-null.
    at flash.text::Font$/registerFont()
    at FontLoader/::fontLoaded()

    when I compile the code. I thin I’m doin smt wrong… The _Arial class return “null” in this situation.

  2. alan macdonald Says:

    what was the problem? you didn’t have someone’s previous stuff given to you that you could call your own without even changing numbers back to actual hex?

    lol jokes – i’m just an ass.

    don’t forget about working over networks too, that can be a b!tch, be sure to disable network cache if doing such as well – had a guy at the office changing file all morning, with no changes being reflected, and delete aso wasnt doin nothing either (of course, since cache)

    muahaha, we meet again – thanks for the read, good stuff :)

  3. alan macdonald Says:

    package
    {
    import flash.util.describeType;
    import flash.display.MovieClip;
    import flash.display.TextField;
    import flash.text.TextFormat;
    import flash.text.AntiAliasType;
    //
    public class Test extends MovieClip
    {
    [Embed(source="C:\WINDOWS\Fonts\somefont.ttf", fontFamily="foo")];
    public var bar:String;=
    public function Test()
    {
    var format:TextFormat=new TextFormat;
    format.font=”foo”;
    format.color=0xFFFFFF;
    format.size=130;
    var label:TextField=new TextField;
    label.embedFonts=true;
    label.autoSize=TextFieldAutoSize.LEFT;
    label.antiAliasType=AntiAliasType.ADVANCED;
    label.defaultTextFormat=format;
    label.text=”Scott Morgan Rocks!”;
    addChild(label);
    }
    }
    }

    works fine in flex

  4. gioLemon Says:

    Sorry…. maby im just tooooooo stupid. But where you explain how you actually crete the swf with the font??? _Arial.swf

    by using fl cs3 … or by using flex… havent known taht flex could create swf’s without the framework.

    Thanks alot
    mo

  5. Cosmin Varlan Says:

    The 1st code that has the line
    public class _Arial extends Sprite
    in it is creating the _Arial.swf

  6. Nishant Says:

    Hey,

    I get “VerifyError: Error #1014: Class IFlexAsset could not be found” just as soon as the font file created by the above method finishes loading :-(

    Any ideas?

  7. Marlin Says:

    Hello:
    I was hoping that some one can help me out. I am currently working on Flash Banner Ads, and so far has been very frustrating. I am not a flash expert nor very familiarize with action script. But I have manage to defend my self with the help of tutorials.

    Here is my problem, I have to create banner ads in flash that meets the requirements of 30ks of file size, and I have done anything possible to keep mi file optimize as much possible by limiting my usage of high res images, using web-safe colors only as well as using simple animations, so far my file size is 28k which is good but when I tested my file the fonts that I am using are not displaying correctly because I am using device fonts to keep my file size down, I I embed the fonts the file size increases like crazy, and i don’t want to compromise my quality of design by allowing the fonts to look like the system wants.

    So my question is Can I use this method to create a dynamic runtime shared font library with the fonts I want and uploaded to our server and have all my SWF banners pulled the fonts from that file that lies on our server to keep my file size down?

    I guess instead of using this:
    package {

    import flash.display.Sprite;
    public class _Arial extends Sprite {

    [Embed(source='C:/WINDOWS/Fonts/ARIAL.TTF', fontName='_Arial', unicodeRange='U+0020-U+002F,U+0030-

    U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E')] public static var _Arial:Class;

    }

    }

    Can It be:
    package {

    import flash.display.Sprite;
    public class _Arial extends Sprite {

    [Embed(source='http://www.rezhub.com/portals/Fonts/ARIAL.TTF', fontName='_Arial', unicodeRange='U+0020-U+002F,U+0030-

    U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E')] public static var _Arial:Class;

    }

    }

    Please Help!!!!!!!!

  8. Daniel Bunte Says:

    hi there,
    you might also be interested in embedding fonts, bitmaps, binaryData, etc. without using the flex framework at all.
    see: http://danielbunte.de/2011/07/24/embed-bitmaps-fonts-binarydata-etc-without-using-the-flex-framework-at-all/

    best regards,
    daniel

Let leave a Comments for this post.