Flash + Fonts: getCharBoundaries Tips, adding End Signs

by troy on September 30, 2008

One of the coolest things about Flash9 in my opinion is the increased power of the TextField, we are already seeing applications like Buzzword only really possible now, and Flash 10 promises even more with enhanced - exotic even ligatures and system / device font antialiasing.

If ligatures aren't turning your typographic socks,  there are several great examples of how to use it to do practical things live search and highlighting of text, using the magic of getCharBoundaries.

One neat thing you can do with this is End Signs. You know those small little playful graphics used typically in magazines, at the end of a story to indicate no more hunting through advertisements for the next section. It's a great way to brand the experience but is rarely in web except for magazines like the New Yorker.

  As mentioned in previous articles we are consuming XHTML +CSS to convert into rich headers, and MovieClip dingbats at the end of a section. In implementing the later this eve I ran into some quirks. The goal is to find the last character of HTML formatted text and place the graphic on the same line a character or so to the right. Note that just putting it on the bounding box border of a TextField wouldn't work as there might only be a single word on that line.

The magic is getCharBoundaries getting the last character is assumed to have a period, so makes this easy:

	var idx : int = (myTextField.text as String).lastIndexOf(".");
	var bnds : Rectangle = myTextField.getCharBoundaries(idx);

Tells you the position of the character, in this case a period at the very end of our section.

Tip1: Wait a Frame
When all the auto-goodness are turned on, e.g. wordWrap, autoKern, antiAliasType, on the TextField, it takes a frame for Flash to report back accurate positioning. This took me some time to figure out as the layout routines using it would report back values, it wasn't until testing them again in response to clicking that I realized that they weren't *quite* the right ones.

The easiest way to do this for me was to set a setTimeOut(setupDingBat, 1000/24); to fire about a frame later.

Tip2: Add the position of the TextField as an Offset
Unlike DisplayObject.getBounds(scope). it doesn't really tell you where that bounds actually belongs in the parent, but is relative to the textfield itself so you have to add the position of the textfield's x and y values as an offset.

	graphics.clear();  
	graphics.beginFill(0xFF0000, .9);
	graphics.drawRect(myTextField.x + (bnds.x ), myTextField.y + (bnds.y), Math.abs(bnds.width), bnds.height);
	graphics.endFill();

Tip3: Beware Negatives
NOTE the weird Math.abs on the bnds.width, was returning a negative width, making the graphic placed the opposite from where it was supposed to.

Tip4: Get Creative
It's very easy to create a static array with multiple linkageId's of symbols to attach, e.g. we have a bunch of bugs.

try {
					// checking if the imageHeader exists in the library
					//cycle through them
					var mcStyle : String = blockEnds.shift();
					blockEnds.push(mcStyle);
					//	trace("put end sign " + mcStyle);
					//trace("==================================================");
					var ClassReference : Class = getDefinitionByName(mcStyle) as Class;
					blockEndSymbol = new ClassReference();
					addChild(blockEndSymbol);
 
					// positioning
					blockEndSymbol.x = field.x + (bnds.x ) +Math.abs(bnds.width*2);
					blockEndSymbol.y =field.y + (bnds.y);
 
				} 
                catch(e : Error) {
					trace("ERROR in TextPageBlock " + e.message);
				}

{ 3 comments… read them below or add one }

Rostislav Siryk July 29, 2009 at 11:57 am

Very good. You’ve just reopened my mind to new text methods in FP.

Regarding Tip1: Wait a Frame, I want to add, just for the record: there is a nice solution for Flex Developers, a callLater() method ( http://livedocs.adobe.com/flex/3/html/help.html?content=layoutperformance_12.html ), which does the same job as setTimeout. It forces another method to be executed on next screen refresh, not immediately.

I wonder why callLater in not in the core AS3. Maybe a feature request for this method worth to be filed :)

Ksenia December 23, 2009 at 9:13 am

Thanks, Troy, very interesting. I just wanted to add a couple of points.

First, I guess in the first example:

var idx : int = (myTextField.text as String).lastIndexOf(“.”);
var bnds : Rectangle = field.getCharBoundaries(idx);

you meant the TextField to be named ‘field’ in both cases.The ‘field’ and ‘myTextField’ both refer to the same TextField or are they different things?

And second, instead of assuming that the last character in the string will be a period, while it may be an exclamation sign or a question mark, why don’t we just use the ‘length’ property of the String? If the the text in he TextField has trailing spaces, then we can use the trim() function.
Then your example will look like this:

field.text = StringUtil.trim(field.text);
var idx : int = field.text.length-1;
var bnds : Rectangle = field.getCharBoundaries(idx);

Cheers!

troy December 23, 2009 at 11:11 am

Hi Ksenia!

Thanks I corrected the article you were right on the field/myTextField comment.

Your approach for lenght might work, in my case there was something about white space, p, br, that was throwing things off if I used that, and it turned out that all the docs ended in periods so my hack worked fine :)

Leave a Comment

 

{ 1 trackback }

Previous post:

Next post: