Simple Text Outlines in Flash

31st January 2008 @ 6:41 am
(This post is part of the series I threatened to write about actionscript 3.)

For the mySociety travel time maps I wanted to have user-defined labels that looked pretty much like the map styles in the OpenStreetMap base maps we were using, like this one:

London SW1P 4DR, from (licensed CC-BY-SA)

It turns out Flash doesn't have a way to draw the outlines of text easily, nor does it have a stroke filter like Photoshop. I tried a few different solutions that didn't work out, including a combinations of ConvolutionFilters (too hard to get right), GlowFilters (too blurry) and stacks of offset BevelFilters (nasty looking edges). Since the only thing in Flash that has the same quality antialiasing as the text does is, well, more text, that's what I ended up using. It looks like this:

Flash Text Outlines

I've included a sample SWF and pulled out the relevant AS3 code (for Flex Builder) in the full post, below.

[kml_flashembed movie="" height="30" width="195" bgcolor="#808080" /]

package {

import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.text.AntiAliasType; import flash.text.TextField; import flash.text.TextFormat;

[SWF(backgroundColor="#808080")] public class TextOutlines extends Sprite { public function TextOutlines() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE;

var tf:TextField = new TextField(); tf.antiAliasType = AntiAliasType.ADVANCED; tf.defaultTextFormat = new TextFormat("Helvetica", 18); tf.textColor = 0xffffff; tf.text = "Sample Outlined Text"; tf.x = 5; tf.y = 5; tf.width = tf.textWidth + 4; tf.height = tf.textHeight + 4; var background:Sprite = new Sprite();

var borderWeight:Number = 2;

for (var i:int = -borderWeight; i <= borderWeight; i++) { for (var j:int = -borderWeight; j <= borderWeight; j++) { if (i == 0 && j == 0) continue; var otf:TextField = clone(tf); otf.selectable = false; otf.textColor = 0x000000; otf.x = i; otf.y = j; background.addChild(otf); } } background.x = tf.x; background.y = tf.y; background.cacheAsBitmap = true; background.mouseEnabled = false; addChild(background); addChild(tf); } public function clone(tf:TextField):TextField { var otf:TextField = new TextField(); for each (var prop:String in ['defaultTextFormat', 'antiAliasType', 'text', 'width', 'height']) { otf[prop] = tf[prop]; } return otf; } } }