Here is the part three..
The very fact that you are writing applications using Flex proves that you care about user interface. And one of the features of Flex that enables you to provide a better user experience is font embedding. There are many benefits of using embedded fonts but the most important one is that it provides consistency regarding how the application will appear across different client environments and you don’t need to make sure whether the font is pre-installed. But there can also be few disadvantages of using embedded fonts, like when you use embedded font it gets stitched into your SWF which results in increasing the overall size of the SWF.
So lets look at how you can reap the benefits of using embedded fonts while still avoiding the start up time cost due to a larger SWF. The trick for doing this is to use Modules (for embedding fonts) and avoid embedding the fonts in the main SWF. That way you can achieve smaller start up time and fetch the fonts when you need to use them.
Here is an example of an application that displays text using four different fonts:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="650" height="400" verticalAlign="middle" horizontalAlign="center" creationComplete="creationCompleteHandler(event)"> <mx:Script> <![CDATA[ import mx.events.ModuleEvent; import mx.modules.ModuleManager; import mx.modules.IModuleInfo; import mx.events.FlexEvent; import mx.controls.Alert; import mx.validators.Validator; private var fontModuleInfo:IModuleInfo; protected function button_clickHandler(event:MouseEvent):void { if(currentState != 'LoggedIn') { var validators:Array = []; validators[0] = this.v0; validators[1] = this.v1; var inValid:Array = Validator.validateAll(validators); if(inValid.length ==0) { if(tiUserName.text == 'guest' && tiPassword.text =='guest') { currentState = 'LoggedIn'; } else { Alert.show("Invalid username" + " or password."); } } } else { currentState = ''; } } protected function creationCompleteHandler(event:FlexEvent):void { fontModuleInfo = ModuleManager.getModule("FontModule.swf"); fontModuleInfo.addEventListener(ModuleEvent.READY, readyHandler); fontModuleInfo.load(); } private function readyHandler(event:Event):void { fontModuleInfo.factory.create(); } ]]> </mx:Script> <mx:StringValidator source="{tiUserName}" property="text" id="v0" required="true" maxLength="10" trigger="{loginButton}" triggerEvent="click" /> <mx:StringValidator source="{tiPassword}" property="text" id="v1" required="true" maxLength="10" trigger="{loginButton}" triggerEvent="click" /> <mx:states> <mx:State name="LoggedIn"> <mx:RemoveChild target="{loginPanel}"/> <mx:AddChild relativeTo="{loginButton}" position="before"> <mx:Panel id="pnl" title="Labels with different fonts. Embedded fonts can be rotated." paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10"> <mx:Label id="lblVerdana" styleName="myVerdanaDescriptor" rotation="1" text="The quick brown fox jumps over the lazy dog." /> <mx:Label id="lblCandara" styleName="myCandaraDescriptor" rotation="1" text="The quick brown fox jumps over the lazy dog." /> <mx:Label id="lblVrinda" styleName="myVrindaDescriptor" rotation="1" text="The quick brown fox jumps over the lazy dog." /> <mx:Label id="lblMinion" styleName="myMinionDescriptor" rotation="1" text="The quick brown fox jumps over the lazy dog." /> </mx:Panel> </mx:AddChild> <mx:SetProperty target="{loginButton}" name="label" value="Log Out"/> </mx:State> </mx:states> <mx:Panel id="loginPanel" title="Login" borderAlpha="0.15" horizontalScrollPolicy="off" verticalScrollPolicy="off"> <mx:Form id="loginForm" > <mx:FormItem label="Username:"> <mx:TextInput id="tiUserName" /> </mx:FormItem> <mx:FormItem label="Password:"> <mx:TextInput id="tiPassword" displayAsPassword="true" /> </mx:FormItem> </mx:Form> </mx:Panel> <mx:Button label="Login" id="loginButton" click="button_clickHandler(event)"/> </mx:Application>
And here is the module (FontModule.mxml) used to embed fonts:
<?xml version="1.0" encoding="utf-8"?> <mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Style> @font-face { src:url("verdana.TTF"); fontFamily: myVerdana; } .myVerdanaDescriptor { fontFamily: myVerdana; color: red; fontSize: 24; } @font-face { src:url("MinionPro-Regular.otf"); fontFamily: myMinion; } .myMinionDescriptor { fontFamily: myMinion; color: green; fontSize: 24; } @font-face { src:url("Vrinda.ttf"); fontFamily: myVrinda; } .myVrindaDescriptor { fontFamily: myVrinda; color: red; fontSize: 24; } @font-face { src:url("CANDARA.TTF"); fontFamily: myCandara; } .myCandaraDescriptor { fontFamily: myCandara; color: green; fontSize: 24; } </mx:Style> </mx:Module>
Below is the running sample, to log in use “guest” as user name and password and click on the button.
The size benefits:
| Configuration | Swf size | Comments |
|---|---|---|
| If fonts are embedded in main swf | 607 KB | |
| If fonts are fonts embedded in module | 267 KB | The font Module is 354 KB |
| If fonts are fonts embedded in module and main app uses default SDK RSLs. (as is the case with the above example) | 84 KB | This app was compiled with Flex 4 beta build. With a more recent Flex 4 build there should be increased benefits for swf size reduction. |
So we trimmed the SWF by 86% for this sample.
The above approach works if you don’t need embedded fonts right at the application start up. In the above example the font module is loaded after the main app is initialized, as we don’t need to show text using embedded fonts on the log in screen. This way the user experiences a reduced start up time and you can still use the fonts you like
If you app uses multiple fonts and some of them are not required at the application start up, consider embedding fonts in a module.
Thanks for the article.I want to ask,is it possible to unload the modules using button in modules?THanks a lot… ^_^
I don’t believe that there is any readily available function to do this. But you can dispatch an event from the module which the parent app can listen for and then unload the module.
Is it possible to embed fonts in an RSL in Flash Builder 4?
I have tried following this post:
http://flexdevtips.blogspot.com/2009/06/default-stylesheet-in-swc-flex-library.html
but this only seems to work for flex 3.
There is still a significant issue in Flex 4. If an RSL is not loaded by the main app, the modules don’t load it!!!! This is a must if one want dynamic behavior with max reuse, where you have multiple modules that have common functionality that should be built into an RSL, that the main application doesn’t either know or care.
@Matt,
Yes, its is possible. In Flex 4 we added a new Font Manager (CFFFontManager) which support (DefineFont4) CFF font embedding. There is a new attribute called embedAsCFF which is true by default.
Older MX components (like the ones in your example) work with DefineFont3 format. So you should modify your example and add embedAsCFF: false; in the @font-face declaration.
In case you run into any issues please file a bug at http://bugs.adobe.com/flex
@Adrian,
RSLs are specified at compile time.. so the main app will load the RSL that you pass in using (runtime-shared-library-path). If there is a library that the main app doesn’t need but is required for the modules. You can specify that in the compiler arguments for the module. If there are multiple modules, make sure the modules that gets loaded first is compiled against the RSLs..
There’s an eeror in the Flex application, it says:
Error #2046
I think this is the error:
“The loaded file did not have a valid signature”