AARON SMITH’S CODE ENDEAVOR

Font Libraries with Guttershark

Another key thing when working with Flash is fonts. It can be somewhat intimidating as the documentation for using font library symbols doesn’t make anything obvious. It takes a bit of reading and messing around to get it right.

If you do some snooping around on google, there are quite a few articles that outline the basics of using embedded fonts, or font libraries. I’ll take you beyond what the basics are, and show you how to integrate font libraries with guttershark.

First things first, let’s look at the model file for loading fonts:

<model>
	<assets>
		<asset libraryName="fonts" src="fonts.swf" preload="true" />
	</assets>
	<fonts>
		<font libraryName="LucidaGrandeBold" />
		<font libraryName="LucidaGrandeRegular" />
	</fonts>
</model>

This model file defines an asset to load, which is a swf that contains our fonts. And it declares two fonts that need to be registered with Flash’s Font.registerFont method.

Now let’s look at the first pass of a document controller:

package
{
 
	import flash.text.TextFormat;
	import flash.text.TextField;
	import flash.events.Event;
	import net.guttershark.control.PreloadController;
	import net.guttershark.control.DocumentController;
 
	public class Main extends DocumentController
	{
 
		override protected function flashvarsForStandalone():Object
		{
			return {model:"model.xml"};
		}
 
		override protected function setupComplete():void
		{
			pc=new PreloadController();
			pc.addItems(ml.getAssetsForPreload());
			pc.addEventListener(Event.COMPLETE,onComplete);
			pc.start();
		}
 
		private function onComplete(e:Event):void
		{
			//this registers every font that is declared in model XML.
			ml.registerFonts();
		}
	}
}

This document controller set’s up the bare minimum to get fonts loaded in and registered. From there, we’re ready to start using embedded fonts on text fields with text formats, or stylesheets.

Before we do that, let’s take a look at some some key methods that guttershark has for getting and setting stylesheets or text formats, and i’ll explain what they do internally.

Two key methods:

 - Model#getTextFormatById()
 - Model#getStyleSheetById()

The most important thing that these two methods do is automatically resolve fonts to their “proper name”. A proper name is the name of a font instance - the “fontName” property of a Font instance.

Here’s an addition to the model for a textformat:

...
<textformats>
		<textformat id="labelText" font="LucidaGrandeBold" size="14" />
</textformats>
...

In this example, the “font=LucidaGrandeBold” indicates that the font to use is the font “LucidaGrandeBold” which in turn is the “export for actionscript” name in the fonts swf. Internally, the getTextFormatById method retrieves an instance of this font object, and assigns the “font” property of a textformat with “font.fontName”.

Here’s some psuedo code to show what this method does:

function getTextFormatById(id:String):TextFormat
{
    finalTextFormat = new TextFormat();
    tfNode = getXMLNodeByID;
    fontName = tfNode.@font;
    fontClass = getFontFromLibrary(fontName);
    Font.registerFont(fontClass);
    fontInstance = fontClass();
    finaltextFormat.font = fontInstance.fontName; //KEY
    //apply other node attribtues to textFormat (bold, size, etc)
    return finalTextFormat;
}

Simple right? Now let’s make an addition in the model for stylesheets:

...
<stylesheets>
		<stylesheet id="example">
			<![CDATA[
				.body{
					font:LucidaGrandeRegular;
					fontSize:10;
				}
			]]>
		</stylesheet>
</stylesheets>
...

Gutteshark supports a custom css property “font”, which indicates to guttershark that it should look up that font, and get the right “proper name” for that font, and use it as the “fontFamily” property.

Here’s some psuedo code for this method:

function getStyleSheetById(id:String):StyleSheet
{
    styleSheet = getStyleSheetNodeFromXML;
    styleSheet = CSS.parseCSS(styleSheet.toString());
    styleNames = styleSheet.styleNames;
    var finalFontFamily:String;
    l = styleNames.length;
    i = 0;
    for(i;i<l;i++)
    {
        style = styleSheet.getStyle(styleNames[i])
        for(key in style)
        {
            if(key=="font")
            {
                fontClass = AssetManager.gi().getClass(style[key]);
                Font.registerFont(fontClass);
                fontInstance = fontClass();
                finalFontFamily = fontInstance.fontName;
                delete style[key];
            }
        }
        if(finalFontFamily) style['fontFamily'] = finalFontFamily; //KEY
        styleSheet.setStyle(styleNames[i],style); //update final style
    }
    return styleSheet;
}

Fairly simple right?

The benefit of all this is that you no longer need to write exhaustive code for every text field, and with the help of some shortcuts, we can setup up embedded text fields into a few lines of code.

Here’s an example of the work required for setting up text formats for each text field, without the help of guttershark:

var fc:FontClass = AssetManager.gi().getClass("MyFontLibraryName");
Font.registerFont(fc);
var fi:Font = fc();
 
var tf:TextFormat = new TextFormat();
tf.font = fi.fontName;
//set other textFormat properties
 
var tfield:TextField = myTFOnTheStage;
tfield.embedFonts = true;
tfield.defaultTextFormat = tf;
tfield.text = "Hello World";

Here’s an example with guttershark’s help:

var tf:TextFormat = ml.getTextFormatById("example");
var tfield:TextField = myTextFieldOnTheStage;
utils.text.textFormat(tfield,tf);
tfield.text = "Hello World";

Much cleaner.

Ok, so let’s put some of this together with a real example. Here’s the final model xml file:

<?xml version="1.0" encoding="utf-8"?>
<model>
 
	<assets>
		<asset libraryName="fonts" src="fonts.swf" preload="true" />
	</assets>
 
	<fonts>
		<font libraryName="LucidaGrandeBold" />
		<font libraryName="LucidaGrandeRegular" />
	</fonts>
 
	<textformats>
		<textformat id="labelText" font="LucidaGrandeBold" size="14" />
		<textformat id="lucidaGBold10" font="LucidaGrandeBold" size="10" bold="true" />
		<textformat id="lucidaGBold12" font="LucidaGrandeBold" size="12" bold="true" />
		<textformat id="lucidaGRegular10" font="LucidaGrandeRegular" size="10" />
		<textformat id="lucidaGRegular12" font="LucidaGrandeRegular" size="12" />
	</textformats>
 
	<stylesheets>
		<stylesheet id="example">
			<![CDATA[
				.body{
					font:LucidaGrandeRegular;
					fontSize:10;
				}
			]]>
		</stylesheet>
 
		<stylesheet id="example2">
			<![CDATA[
				.body{
					font:LucidaGrandeRegular;
					fontSize:10;
				}
				.bold{
					font:LucidaGrandeBold;
					fontWeight:bold;
					fontSize:14;
					color:#FF0066;
				}
			]]>
		</stylesheet>
	</stylesheets>
</model>

And here’s the final document controller:

package
{
 
	import flash.text.TextFormat;
	import flash.text.TextField;
	import flash.events.Event;
	import net.guttershark.control.PreloadController;
	import net.guttershark.control.DocumentController;
 
	public class Main extends DocumentController
	{
 
		//bunch of fields on stage..
		public var stylesheetsLabel:TextField;
		public var textFormatsLabel:TextField;
		public var txf1:TextField;
		public var txf2:TextField;
		public var txf3:TextField;
		public var txf4:TextField;
		public var txf5:TextField;
		public var txf6:TextField;
		public var txf7:TextField;
		public var ss1:TextField;
		public var ss2:TextField;
		public var ss3:TextField;
		public var ss4:TextField;
		public var ss5:TextField;
		public var ss6:TextField;
		public var ss7:TextField;
 
		override protected function flashvarsForStandalone():Object
		{
			return {model:"model.xml"};
		}
 
		override protected function setupComplete():void
		{
			pc=new PreloadController();
			pc.addItems(ml.getAssetsForPreload());
			pc.addEventListener(Event.COMPLETE,onComplete);
			pc.start();
		}
 
		private function onComplete(e:Event):void
		{
			//this registers every font that is declared in XML.
			ml.registerFonts();
 
			//set up the labels.
			var labelTF:TextFormat = ml.getTextFormatById("labelText");
			stylesheetsLabel.embedFonts=true;
			stylesheetsLabel.setTextFormat(labelTF);
			textFormatsLabel.embedFonts=true;
			textFormatsLabel.setTextFormat(labelTF);
 
 
			//TEXTFIELDS
 
			txf1.embedFonts=true;
			txf1.setTextFormat(ml.getTextFormatById("lucidaGBold10"));
 
			txf2.embedFonts=true;
			txf2.setTextFormat(ml.getTextFormatById("lucidaGBold12"));
 
			//use shortcuts for other textformats.
			utils.text.textFormat(txf3,ml.getTextFormatById("lucidaGRegular10"));
			utils.text.textFormat(txf4,ml.getTextFormatById("lucidaGRegular12"));
 
			//another shortcut type.
			utils.setters.textFormat(ml.getTextFormatById("lucidaGRegular10"),txf5,txf6,txf7);
 
 
			//STYLESHEETS
 
			ss1.embedFonts=true;
			ss1.styleSheet=ml.getStyleSheetById("example");
			ss1.htmlText="<span class=\"body\">"+ss1.text+"</span>";
 
			ss2.embedFonts=true;
			ss2.styleSheet=ml.getStyleSheetById("example2");
			ss2.htmlText="<span class=\"body\">"+ss2.text+"<span class=\"bold\">. and this is bold.</span></span>";
 
			//stylsheet shortcuts
			utils.text.styleSheet(ss3,ml.getStyleSheetById("example"));
			ss3.htmlText="<span class=\"body\">"+ss3.text+"</span>";
 
			utils.text.styleSheet(ss4,ml.getStyleSheetById("example"));
			ss4.htmlText="<span class=\"body\">"+ss4.text+"</span>";
 
			//another shortcut type
			utils.setters.stylesheet(ml.getStyleSheetById("example"),ss5,ss6,ss7);
			ss5.htmlText="<span class=\"body\">"+ss5.text+"</span>";
			ss6.htmlText="<span class=\"body\">"+ss6.text+"</span>";
			ss7.htmlText="<span class=\"body\">"+ss7.text+"</span>";
		}
	}
}

This example uses some other great shortcuts for preparing text fields for stylesheets, or text formats:

- utils.text.textFormat
- utils.text.textWithTextFormat
- utils.text.textWithTextFormatFromLocaleString
- utils.text.textWithFormattingFromXML
- utils.text.styleSheet
- utils.setters.textFormat
- utils.setters.styleSheet

I’m not going to explain all of those, I’d recommend reading the source for those methods. As well as reading the source for the two model methods (getTextFormatById, and getStyleSheetById).

I put this example in the guttershark repository for all. Pull down the latest version, and checkout “examples/other/font_libs/ex1″. You can get the latest version in git.

enjoy.

  

3 Comments so far

  1. Adam October 7th, 2009 2:17 pm

    Hi Aaron, first of all, great library! It’s helped save quite a bit of time on my latest project (great pair with PureMVC BTW!). I had a quick question regarding this getTextFormatById method. How might I embed fonts properly/declare text formats without using the model.xml file? Instead of relying on this file I’d like to declare essentially the same things in a command within puremvc dedicated to set up guttershark’s model (I have my own models for most other data in the application )

    I’ll poke around some more when I get back to my workspace, but any help would be much appreciated!

  2. admin October 7th, 2009 8:32 pm

    Hey Adam, it sounds like you’re trying to duplicate how the model creates text formats for you? If that’s the case, the place to start is reading the source code of the getTextFormatById method. It’s actually pretty straight forward after you read through it.

    And if you absolutely had to you could update some of the variables on the model that store references to the different pieces of xml. So you could change the protected variable that references the text formats to public, then in your command just set that variable to be a chunk of xml. As long as that xml is formatted the way that the GS model expects it, it will be fine.

    Anyway, hope that helps.

  3. Dan October 23rd, 2009 9:13 am

    Currently the model’s reading of text format nodes converts all numbers to Ints. They should be converted to Numbers to allow for decimals. This around line 690 of model.as should fix it:

    if(n.attribute(”indent”)!=undefined) tf.indent=Number(n.@indent);
    if(n.attribute(”italic”)!=undefined) tf.italic=utils.string.toBoolean(n.@italic);
    if(n.attribute(”kerning”)!=undefined) tf.kerning=utils.string.toBoolean(n.@kerning);
    if(n.attribute(”leading”)!=undefined) tf.leading=Number(n.@leading);
    if(n.attribute(”leftMargin”)!=undefined) tf.leftMargin=Number(n.@leftMargin);
    if(n.attribute(”letterSpacing”)!=undefined) tf.letterSpacing=Number(n.@letterSpacing);
    if(n.attribute(”rightMargin”)!=undefined) tf.rightMargin=Number(n.@rightMargin);
    if(n.attribute(”size”)!=undefined) tf.size=Number(n.@size);
    if(n.attribute(”underline”)!=undefined) tf.underline=utils.string.toBoolean(n.@underline);

Leave a reply