Archive for March, 2010

Flex and Embedded Fonts

Flex tries to provide you with the most flexible way of using embedded fonts in your applications. If your application consists of just one SWF and you embed fonts in that SWF, everything should just work, but once there are multiple SWFs involved, while it should work most of the time, if it doesn’t, you may need the following information to figure out why. With Flex 4 defaulting to using RSLs, your default configuration now involves multiple SWFs and many more of you are suddenly using multiple-SWF applications because each RSL counts as a SWF.
The principle rule is that there are two places that the Flash Player will look to find an embedded font to use to display text in a TextField or TextLine. It will look in the global table where fonts are registered via Font.registerFont, and it will look in the SWF whose code instantiated the TextField or TextLine.
Font.registerFont has a couple of limitations. One is that, if two SWFs try to register the same font, the last one in wins. That is normally ok until you start embedding font subsets (only certain characters in the font) which you might want to do when optimizing for SWF size, especially with large Asian fonts. If SWF A has only upper case characters and SWF B has only lower case characters, for example, then SWF A won’t be able to display its characters if SWF B loads later and registers its font of lower case characters.
The second limitation is that there is no Font.unregisterFont call so registering a Font pins the SWF with the font in memory.
For those reasons, Flex never tries to call Font.registerFont. Instead it has an infrastructure that tries to make sure that all TextFields and TextLines are created by code in the SWF that contains the embedded font. We use the term “SWF context” to mean the SWF whose code created the TextField or TextLine. And because a TextField or TextLine can have only one SWF context, it means that all fonts displayed in a TextField or TextLine must be embedded in the same SWF. If you must mix, you will have to register the font and deal with the fact that the SWFs containing the fonts may never unload.
Making sure the right code instantiates TextField is a bit trickier than it first seems. You can’t just put calls to “new TextField” or “TextBlock.createTextLine()” in a single factory class and link that class into every SWF. The ApplicationDomain rules may cause the class in a SWF to be ignored (see the modules presentation on the blog for details).
For example, suppose I create a TextLineFactory class and link it into SWF A and SWF B. If SWF A loads SWF B, when B calls TextLineFactory, it will be calling the one in SWF A and thus the TextLine will be created in a different SWF context than B expected and the embedded fonts won’t show up. Or suppose the TextLineFactory class is in an RSL. Then it is not in SWF A or SWF B so those SWFs won’t be able to display text using fonts embedded in those SWFs.
In order to solve this, Flex adds code to a uniquely named class in every SWF it generates for RSLs and Applications and Modules. That way there are no ApplicationDomain class collisions. Flex does that by adding that code to the main class of every SWF and names it after the SWF. For example, if you have A.SWF with mx:Application, Flex wraps the mx:Application with a SystemManager subclass called something like A_SystemManager and adds factory APIs to that subclass.
For TextField, Flex uses the ISystemManager.create() method. For TextLine, Flex uses the ISWFContext.callInContext API. If you are writing your own text-based components, it would be a good idea to subclass the Flex components so you can inherit the embedded font infrastructure that makes those calls and also figures out the next hard part: given an embedded font, which SWF do you reference to call these APIs? Again, because of ApplicationDomain rules, any class you write may be running in a different SWF context from the caller.
Flex tracks all fonts it embeds in SWFs and each SWF registers its embedded fonts in the EmbeddedFontRegistry on load. (Yes, we do it in a way that allows the SWF to unload). When a Flex text component is about to display text, it looks up the fontFamily, fontStyle and fontWeight and sees if there is an embedded font in the registry that matches, biasing the search to the current SWF so that colliding subsets will work. The EmbeddedFontRegistry returns the best SWF context for that font and the component uses that SWF context to create the TextField or TextLines. That means that even simple components like Label will destroy and create a new children if the font styles change since the style change could require the use of a different SWF context.
How can Flex always know which SWF is the current SWF given the ApplicationDomain rules? This is done in Flex by conditionally propagating the moduleFactory property to child components. All top-level components in a SWF have their moduleFactory set to the SWF’s IModuleFactory and propagate it to children unless the child already has a moduleFactory set.
Note that even though SystemManager is an IModuleFactory, not all ISystemManagers are IModuleFactory like AIR’s WindowedSystemManager so code should not assume that you can determine your SWF context from the systemManager property.
There are lots of implications from having this infrastructure. If you can’t control the code that creates the TextLine or TextField because the instances are created via mx.core.IFactory (itemRenderers and Chart visuals), you may need to use ContextualClassFactory or code up something similar. It also means you can’t cache a single TextField for measuring text; you may have to cache a TextField in each SWF context. It means that SystemManager.isFontEmbedded and Font.enumerateFonts may not know about every font that has been loaded and return inaccurate results.
And, it turns out that other font-related APIs must also be called in the correct SWF context. If you call ElementFormat.getFontMetrics(), you must also call that via ISWFContext if you are using embedded fonts in a multi-SWF configuration. Getting this wrong an result in really subtle bugs like having the position of strikethrough and underline being off by a pixel or two, or having baselinePosition be off by a pixel or two.
The Flex team has put a lot of work into making all of this transparent to developers, so hopefully you won’t ever need to know all of this, but folks definitely get tripped up by this now and then. And now, if you do, I hope you can use the information in this post to solve your problem.