Flash: Runtime Font Sharing Embedding with only Partial Character sets: How To.
While not planning on it, spent most the day figuring out how to implement runtime shared fonts, while only embedding a fraction off the characters. Holy moly was this a minefield. But happily got a simple solution.
Here's the goal, I'm working on the phase 2.x of a website, and we are using several flavors of Myriad Pro to make it look great, plus a few others. However with each flavor weighing in at ~35Kb each we are loading about 200K worth of fonts (bold, italic, bold italic etc), and at present many of the pages have their own copies of the fonts (the typical evil that comes up on projects with no time), so the sight loads far more sluggish than it should. After this and the following are done, the load is down to 54kb, as only the elements needed are loaded once.
In Flash IDE (CS3) and prior selecting partial characters was easy if you only had one swf, just select from the characters in the embed button, in the properties panel. If you have character sets you use often, short cut this by creating custom character sets, with this neat approach, perhaps even with this tool However this is only really good for elements scoped inside the swf with the embedded font, no way to share them between swfs without resorting to traditional runtime shared libraries that have several issues (like bypassing preloaders all together). In AS2.0 there was a nifty hack possible to help,where a embedded font was coerced into sharing by a proxy clip that referenced it as a runtime shared library, at the preloaders convenience, but this (for me) was brittle in having font collisions, e.g. a static textfield on stage could render the approach useless, and I'm pretty sure this doesn't work anymore in AS3. UPDATE. !%!$ AS3 has the exact same issue, flash player appears to load a font in a given swf scope on a first come-first serve only once. Meaning any static or textfield anywhere on teh stage (regardless or not if any characters are embedded) will collide with whatever you load in dynamically later, rendering the dynamically loaded useless,...and any references to it. Meaning your dynamic textfields will break...not show up at all!. A possible workaround is having either the placeholder textfields with a sacrificial font (e.g. sans), or renaming the font in the embed created in a few steps to something like (Myriad Pro ->MyriadPro) and adjusting the css/etc.
For reasons unknown to me, adding a Font symbol to the library (either for use with actioncript, or runtime sharing) embeds the whole font. Needless to say, when on the web, getting every character known to mankind is not going to help your site load fast. This can be the difference of 2kb for only what you need for the preloader, to 2MB for entire international character sets. Also it can grind compile times to a halt, as every publish requires every glyph to be embedded and this is time consuming. Both seem like major oversights on the Flash IDE's design.
So oddly the best way to create fonts is to use the Flex 3.x SDK (download). For this the great and free FlashDevelop (download) will work great (if your on windows). Just follow the installation instructions, setup a new Actionscript 3, Flex 3 Project.
Create a class like the following in the 'src' directory, and in the righthand tree, right click over it and select 'always compile'
-
-
package {
-
-
import flash.display.Sprite;
-
import flash.text.Font;
-
public class MyriadProFontLib extends Sprite {
-
-
[Embed(source='C:/WINDOWS/Fonts/MyriadPro-Regular.otf', fontName='Myriad Pro', unicodeRange='U+0020-U+0020,U+0021-U+0021,U+0022-U+0022,U+0023-U+0023,U+0024-U+0024,U+0025-U+0025,U+0026-U+0026,U+0027-U+0027,U+0028-U+0028,U+0029-U+0029,U+002A-U+002A,U+002B-U+002B,U+002C-U+002C,U+002D-U+002D,U+002E-U+002E,U+002F-U+002F,U+0030-U+0030,U+0031-U+0031,U+0032-U+0032,U+0033-U+0033,U+0034-U+0034,U+0035-U+0035,U+0036-U+0036,U+0037-U+0037,U+0038-U+0038,U+0039-U+0039,U+003A-U+003A,U+003B-U+003B,U+003C-U+003C,U+003D-U+003D,U+003E-U+003E,U+003F-U+003F,U+0040-U+0040,U+0041-U+0041,U+0042-U+0042,U+0043-U+0043,U+0044-U+0044,U+0045-U+0045,U+0046-U+0046,U+0047-U+0047,U+0048-U+0048,U+0049-U+0049,U+004A-U+004A,U+004B-U+004B,U+004C-U+004C,U+004D-U+004D,U+004E-U+004E,U+004F-U+004F,U+0050-U+0050,U+0051-U+0051,U+0052-U+0052,U+0053-U+0053,U+0054-U+0054,U+0055-U+0055,U+0056-U+0056,U+0057-U+0057,U+0058-U+0058,U+0059-U+0059,U+005A-U+005A,U+005B-U+005B,U+005C-U+005C,U+005D-U+005D,U+005E-U+005E,U+005F-U+005F,U+0060-U+0060,U+0061-U+0061,U+0062-U+0062,U+0063-U+0063,U+0064-U+0064,U+0065-U+0065,U+0066-U+0066,U+0067-U+0067,U+0068-U+0068,U+0069-U+0069,U+006A-U+006A,U+006B-U+006B,U+006C-U+006C,U+006D-U+006D,U+006E-U+006E,U+006F-U+006F,U+0070-U+0070,U+0071-U+0071,U+0072-U+0072,U+0073-U+0073,U+0074-U+0074,U+0075-U+0075,U+0076-U+0076,U+0077-U+0077,U+0078-U+0078,U+0079-U+0079,U+007A-U+007A,U+007B-U+007B,U+007C-U+007C,U+007D-U+007D,U+007E-U+007E,U+00A9-U+00A9,U+00AE-U+00AE,U+00B7-U+00B7,U+02C6-U+02C6,U+02DC-U+02DC,U+2013-U+2013,U+2014-U+2014,U+2018-U+2018,U+2019-U+2019,U+201A-U+201A,U+201C-U+201C,U+201D-U+201D,U+201E-U+201E,U+2020-U+2020,U+2021-U+2021,U+2022-U+2022,U+2026-U+2026,U+2030-U+2030,U+2039-U+2039,U+203A-U+203A,U+20AC-U+20AC,U+2122-U+2122')]
-
-
public static var _MyriadPro:Class;
-
-
public function MyriadProFontLib()
-
{
-
super();
-
Font.registerFont(_MyriadPro);
-
}
-
}
-
}
Go to the Top Nav's "Project>Properties" and adjust the output file to something like "bin\FontLibs.swf"
Copy the characters you want as Ascii e.g. (missing white space)
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~©®·ˆ˜–—‘’‚“”„†‡•…‰‹›€™
goto this great unicode converter here
top left 'characters' box, then hit convert
select the output from the Unicode U+hex notation box, and paste it into this app's left side.
Copy the right side and replace the 'unicodeRange=''' in the above class.
Build the file by pressing the green play looking button. If all goes well you should see the console output
(fcsh)
Build succeeded
Done (0)
and a new FontLibs.swf in the bin folder.
You can use the excellent Action Script Viewer 6 to look at the glyphs embedded into the FontLibs.swf. This is very useful for seeing if the unicode is getting you the characters you think you are putting in. I tried several ascii to unicode convertors, and only the one linked above got it right.
Then using the tool of your choice create this class to load up the fontlib and use a dynamic textfield.
-
package {
-
-
import flash.display.Loader;
-
-
import flash.display.Sprite;
-
-
import flash.events.Event;
-
-
import flash.net.URLRequest;
-
-
import flash.text.*;
-
import flash.utils.describeType;
-
-
public class FontLoader extends Sprite {
-
-
public function FontLoader() {
-
-
loadFont("FontLibs.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 {
-
-
drawText();
-
-
}
-
-
public function drawText():void {
-
-
var tf:TextField = new TextField();
-
-
tf.defaultTextFormat = new TextFormat("Myriad Pro", 16, 0);
-
-
tf.embedFonts = true;
-
-
tf.antiAliasType = AntiAliasType.ADVANCED;
-
-
tf.autoSize = TextFieldAutoSize.LEFT;
-
-
tf.border = true;
-
tf.text = "Troy was here\nScott was here too\nblah scott...:;*&^% ";
-
tf.rotation = 15;
-
addChild(tf);
-
-
}
-
-
}
-
-
}
In decreasing order of relevance, these are some of the posts that I read to get to this point.
If characters that are embedded are funky, and your using Flex 2.0, upgrade to Flex SDK 3.x. I was originally using Flex 2.0...very dated at this point.
If your using OTF (Open Type Fonts) like Myriad Pro make sure your using Flex SDK 3.x else you might have issues...flex 2.0 is very dated at this point. Also consider using a tool like the excellent CrossFont if you're trying to use Mac Fonts.
[1] The one that helped me realize the ability to limit the unicode. The more readable one on the yahoo labs, and the authors blog with many comments. The solution he and others presented didn't work for me, and I think the solution here is cleaner.
[2] Flex 3.x Compiler comes with multiple The font transcoder doesn't always work, try another. in particular the (flash.fonts.AFEFontManager);
[3] Runtime Font Sharing For Flash CS3...[Robert's excellent article and followup] [far more verbose] , using the traditional whole-enchilda Font.
[4] Special considerations when using Japanese character sets (a) usin (b)
[5] A must read if you want a grand Q&A of embedded fonts and many quirks, (CSS specific)
[6] More details on mime-type for the font, I think this was only relevant in Flex 2.0 than Flex 3.x.
[7 ] Basics with AS3/Flex without using a library
[ 8 ] More rants on Flash CS3's lack of proper embedding, in particular with pixel fonts..Flash CS3 renames them for you.
Flash: Detecting the end of an animation
Say you have an animated character in a game, that others need to know when they are through playing their animation. Here are a few approaches:
Let's first assume we have a generic object named 'player_mc' that is our animation. We also have a centralized controller script closer to the root, that has a function like:
-
function onAnimationComplete(evt:Event = null):void{
-
//do something
-
currentPlayer = evt.target as MovieClip;
-
currentPlayer.gotoAndStop("still");
-
}
Note that we use evt = null just in case we want to use call the function without passing it an event.
1) Using addFrameScript
-
var init:Boolean;
-
if(!init){
-
/// !init check, this block is only needed when pasting on timeline
-
// to keep it from activating more than once when the movieClip recycles
-
addFrameScript(totalFrames -1, onAnimationComplete);
-
init = true;
-
}
-
-
Just remember if doing that approach, addFrameScript uses a frame number one less that what you see in Flash.
This approach is safer when indexing frame labels prior (e.g. with UIUtil) to map say a frame label of "AnimationFinishedFrame" to the frame number for addFrameScript to use.
-
addFrameScript(com.troyworks.ui.UIUtil.getNumberOfFrameLabel("AnimationFinishedFrame", onAnimationComplete);
-
2) Using Event Bubbling
Just communicate up the display list.
-
player.addEventListener("ANIMATION_COMPLETE", onAnimationComplete);
Then
-
//inside the player on last frame of the player animation
-
-
stop();
-
dispatchEvent(new Event("ANIMATION_COMPLETE", true, true));
For more info see here.
3) Use Singleton/Static or Stage as a message bus.
like 2 but using the stage or some other easy to access to communicate without bubbling.
4) Poll for Progress
This is a bit more work, but get's you the ability to find out more about the progress, eg. showing a progress bar for percentage played.
-
player.addEventListener(Event.ENTER_FRAME, onENTER_FRAME
-
-
function onENTER_FRAME(evt:Event):void{
-
var playermc:MovieClip = evt.target as MovieClip;
-
-
if(playermc.currentFrame == playermc.totalFrames){
-
onAnimationComplete();
-
}
-
}
Hope that helps.
Flash: Fast Prototyping and Sketching UI with FlowControl
While I enjoy the new power of Flash9/AS3.0, one of the things I really dislike about the AS3, is how much extra work to do basic things like setup buttons to do simple timeline control, this is especially hard on designers and animators who don't code. I agree with 5etdemi post 4 years ago, the basics have ironically gotten harder to do.
What wrong with ECMAScript/Actionscript? Well, it is not especially well suited for certain tasks, the worst of which is time-based/timeline-oriented animation and declarative drawing. I mean, it seems pretty ironic that ActionScript is derived from ECMAScript which was primarily oriented at scripting in a text-based environment, HTML and the DOM. Simply put, ActionScript has leaned more and more towards heavy lifting text operations (like RegEx and the new XML implementation) and less towards building programmatically what Flash was originally meant for, animation.
While there are useful utilities like Lee Brimelow's nice event generator utility, I wanted less, as even if being good with actionscript, when you have a few minutes to put together something for (or during!) a client/skype meeting, scripting isn't really an option. Thankfully AS3 has some tricks to help!
( press RIGHT arrow key to advance)
One tennant of Sketch is using the type and instance names to do most the event binding work, largely this is to allow Designers to stay on the timeline, but the same could apply to objects created in actionscript as well. So in the above example there are 2 scenes, and a bunch of empty frames, and a nav control. What's special is the buttons you see are actually just semi-transparent buttons (I call InvisiButtons) that have instance names like 'next', 'prev', (shown in the button names) there is no other script on the timeline for them. That via the FlowControl listening to ADDED event are autowired up to call 'nextFrame()' , 'prevFrame()' etc. These InvisiButtonscould be used as a hotspot over a comped image or basic text and wireframe graphics, allowing low to high fidelity comps to be created.
In this example a ENTER_FRAME event updates the TextField off to the right to show the current scene, frame et, to show where you are.
FlowControl has some other nifty features.
- optional features is the fadein, fadeout effect, with configureable color, so while there is a normal timeline, the fade effect is free and ads a bit of polish IMHO.
- tooltips telling you where the buttons go/do.
- keypress for left/right arrow to go to the next previous frame, escape to goto first frame-useful for slide shows
- XXX_autoBtn. If it sees this pattern that button will generate an XXX event (e.g. sayHi_autoBtn generates a new Event("sayHi"), which anybody is free to listen to.
All these are in the example (as well as all the code for FlowControl) on code.google.
Flow Control can be used 'embedded' as the document level class (or extending from it) or loaded as an external swf, and I'll be demoing that in the future. That latter feature is more targetted to creating highly skinnable games which I'll be covering in future blog posts.
Flash: View Controller binding
When using actionscript to turn a MovieClip , Sprite etc into a component. There are several points that can be used to introspect the children to bind the displayObject, whatever configuration is on stage, with the ActionScript class. Different binding points allow different things, here's the order and
OUTPUT:
- constructor: // displayObject children are already on stage in the displayList but haven' t been drawn yet. the Actionscript class for it is just getting instantiated.
- onFrame1: // actionscript on or associated with frame1 has fired
- activateHandler: //not always called
- onFirstFrameRender: //first time ENTER_FRAME has hit the frame1, meaning it's script has fired, and everything is already visible.
- renderHandler: //only if the stage.invalidate() is called.
-
package {
-
-
import flash.display.MovieClip;
-
-
import flash.display.Sprite;
-
-
import flash.display.StageAlign;
-
-
import flash.display.StageScaleMode;
-
-
import flash.events.Event;
-
-
import flash.text.TextField;
-
-
public class StageExample extends MovieClip {
-
-
public var sizeMe:MovieClip;
-
-
public var output_txt:TextField;
-
-
public var textLog:Array = new Array();
-
-
public function StageExample() {
-
-
super();
-
-
stage.scaleMode = StageScaleMode.NO_SCALE;
-
-
stage.align = StageAlign.TOP_LEFT;
-
-
stage.addEventListener(Event.ACTIVATE, activateHandler);
-
-
stage.addEventListener(Event.RESIZE, resizeHandler);
-
-
addEventListener(Event.ENTER_FRAME, onFirstFrameRender);
-
-
addEventListener(Event.RENDER, renderHandler);
-
-
addToLog("constructor: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
addFrameScript(0, onFrame1);
-
-
stage.invalidate();
-
-
}
-
-
private function onFrame1():void {
-
-
addToLog("onFrame1: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
//resizeHandler(event);
-
-
}
-
-
private function activateHandler(event:Event):void {
-
-
trace("onFirstFrameRender: " + event);
-
-
addToLog("activateHandler: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
//resizeHandler(event);
-
-
}
-
-
private function onFirstFrameRender(event:Event):void {
-
-
trace("onFirstFrameRender: " + event);
-
-
addToLog("onFirstFrameRender: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
// resizeHandler(event);
-
-
removeEventListener(Event.ENTER_FRAME, onFirstFrameRender);
-
-
}
-
-
private function renderHandler(event:Event):void {
-
-
trace("renderHandler: " + event);
-
-
addToLog("renderHandler: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
}
-
-
///////////////////////////////////////////////////////////////////
-
-
private function resizeHandler(event:Event):void {
-
-
trace("resizeHandler: " + event);
-
-
trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);
-
-
addToLog("resize: w " + stage.stageWidth + " h " + stage.stageHeight);
-
-
sizeMe.width = stage.stageWidth;
-
-
sizeMe.height = stage.stageHeight;
-
-
}
-
-
private function addToLog(str:String):void
-
-
{
-
-
textLog.unshift(str);
-
-
output_txt.text = textLog.join("\r");
-
-
}
-
-
}
-
-
}
What is FlashFirst?
Let's define an RIA as a stew of tasty graphics, sounds, business logic, script assets, controls etc. The end product might be a game, might be a traditional website with non-page based navigation, might be serious application like Photoshop or Word. Across all of these, separating the UI elements from the underlying logic leads to easier to maintain applications.
FlashFirst describes the workflow of using Flash CS3 output to ignite the experience, much as in normal 'test movie' or 'publish' outputting say "UI.swf" with the key difference that all the script is in a second swf file (e.g.
script.swf) , which may be built from Flash or Flex. This is the opposite of most Flex workflows where Flex compiling has control and loads up the appropriate graphical assets from Flash.
FlashFirst in it's best incarnation, follows MVC design patterns near perfectly, the User interface is from flash (e.g. UI.swf) the controller loaded up by UI is a separate swf (e.g. controller.swf) with no/minimal User Interface, and the model is often from XML, jpg, CSS, AMF.
The two patterns for FlashFirst
The default way it works is UI.swf->Engine.swf->assets... So in detail user starts up the UI.swf, which pauses itself, loads up the controller Engine.swf into itself, which takes over the preloading of assets, fonts, internationalization, configuration. Once through preloading the engine tells the swf to resume playing, as things are added and removed from stage and via the magic of addFrameScript to well defined contracts in the form of instance names, class types, and framelabels. The UI elements are enhanced with interactivity, possibly based on configuration on stage.
Say in a jigsaw puzzle, you have a MovieClip on stage, with instance name 'game1_jigsawpuzzle_mc', inside that clip on a frame script might be something like
var config:Object = new Object();
config.numPiecesVertical = 4;
config.numPiecesHorizonatl = 4;
Which the listening game engine (e.g. jigsawpuzzle_engine.swf) would be listening for scene requiring it's services (e.g th instance name of the clip "game#_jigsaw") to be loaded, and then using to slice up the image on stage into 16 pieces.
A second case is a Starter.swf loads in the UI and passes it some initial configuration settings that override the defaults, say jump to level2,. So in that case Starter.swf->UI.swf->Engine.swf->assets... This is similar to using FlashVars passed into a swf running inside a webpage.
Why FlashFirst?
The clean separation between UI and Logic, allows for easy maintenance, both in reskinning and feature enhancement. Say you have a game, that you want to be rethemed for every season, one logic, clone the Skin file a few times, adjust the graphics to fit each season. Sometime later it's decided that there is a bug to be fixed in that game, but the UI Files and fonts were lost in the great harddrive crash of 2007, and the primary designer (on a Mac) was killed in a bizarre velcro wall accident. Despite these horrible occurances, provided the contracts are the same, a new engine can be deployed just by dropping in a new file, without finding and republishing the UI. Even better, if the UI is generic enough, new products can be created by different engine features not known at the time of developing the original UI. e.g. for a math game, simple, intermediate, and advanced addition, a new series on multiplication is added.
Adobe is pushing Flex hard, as they should, it's a great ...if heavy handed tool/framework. One of the things that have always appealed to me is Flash's ability to push beyond standard UI and work on high experience quickly, prototyping, new components, and historically Flex starts to break down. Most of the projects I work on, it's really the Flash Designer hitting compile a billion times tweaking the graphics and tweens that is pushing that creative edge, often in constraints that Flex can't squeeze in either size or framework.
Another gripe is many apps produced with Flex look, "Flexy", and while generally superior in UX to RIA's DHTML+Javascript, Flexy doesn't represent the brand and experience that most client and designers want.
Flash is awesome rapid prototyping application. I've creating essentially functional (meaning only the bare essence) mockups and prototypes *while sitting in meetings with the client* given the ability to draw, drag and drop, work on a timeline and integrate photos from whiteboards or video.
The Creative First frequently has designers designing in a vacuum outside of IA and Useability, miss critical things or overshoot on experience resulting in overbudget catastrophies that aren't useable.... like reinventing scrollbars, and creating obnoxious keyholes everywhere, as they haven't mastered fluid layout yet, and in the Flash world it has to be built by hand.
