Recently, I received comps for a project in which the text appeared to wrap gracefully around an isolated (against a solid background) image, as if it were laid out in a prepress tool like InDesign. Rather than wildly waving the comp in the designers’ faces, tears streaming, convincing them by my incontrovertible pathos to drop the feature, I took some extra time to see if it was possible to do that with decent performance and arbitrary text, given AS3′s new features. Some of the bugs I ran into made me cry anyway, but I think I finally got it figured out.
You may want to open it up on its home page here, especially if you are getting crossdomain errors from viewing on Mims’ flava of the blog. To see how this is accomplished, read on past the cut.
The approach I took works on any TextField and any DisplayObject, even if they are in totally different display lists. It only requires one call to a static method, WrapTextUtility.wrapText(textfield, displayobject). The amount of padding between the text and the object can be specified. It works on text areas with line spacing and/or letter spacing set.
My solution also has some limitations. Right now, HTML text is this system’s Achilles’ heel, and a feature I plan on adding when I get some time. It only wraps text around an object on the right side.
But how does it work? Turn on the overlay by cicking inside the movie and hitting space to follow along. (If you get an error or aren’t seeing shapes, please open up it up by itself here).
General Algorithm
- Before we start the main loop, check to see if the image and text field are intersecting at all. If not, you have nothing to do and can quit right off the bat.
- If there’s some sort of intersection, repeat the following steps for every line until you’re done with lines of text or you’ve made it to the bottom of the image, whichever comes first.
- Skip all the lines until the current line is below the top of the image.
- Take a slice of the image, the entire width of the image and the height of the current line. The slice should line up perfectly with the top and the bottom of the current line of text. For this I use from the descender up to the baseline, up through the ascender. Here, the new method
TextField.getLineMetrics()provides what we need handily. - Write that slice into its own little slice BitmapData. You can do this by passing a clipping rectangle to
BitmapData.draw(). In the overlay, this area appears as a grey rectangle. - Find the extent of the area of that slice which contains non-transparent (or non-background-color) pixels. In other words, find out where there is foreground in the image. This can be accomplished with
BitmapData.getColorBoundsRect(). This method returns a rectangle, which I’ve drawn in green in the demonstration. - Find the left edge of the rectangle. As the left edge of all the non-transparent pixels in the current slice, it is also the furthest you could write from the left without hitting something in that line.
- Subtract off a padding distance from the left of the rectangle and you have your line extent for that line. I have drawn this as a red line over the baseline of the text.
- Find the position in the source text that’s going to be drawn over this edge. This will be the last possible letter that can exist in the line. A new method to AS3,
TextField.getCharIndexAtPoint(), makes this simple as pie, but limits us to non-HTML textfields. - Move backwards in the string until you can find a suitable breaking point. In English we can wrap a line at a hyphen or any whitespace. I constructed a regular expression for these possibilities and stepped backwards until I hit one of them. If you hit the beginning of the line, no text can fit on that line and you just have to try the next one. If this were truly accurate, it would also have the ability to insert hyphens at the proper places. Unfortunately, this requires some sort of dictionary, and it’s just more trouble than it’s worth.
- Once you have a breaking point that’s not in the middle of a word somewhere, splice in a newline there and move on to the next line. Make sure that your picture of the next line is based on the text after you wrapped the previous line, as that will change what letters are located where!
- If you get to the bottom of the image or the bottom of the text, you can stop right away.
And that’s the long and the short of it! It behaves fairly well, even though I’m blindly trusting the performance of built-in TextField methods.
Oh Baby Download It!
Update: I’m really sorry about being so lazy about this but finally, here’s the source.
WrapTextUtility View Source | Download (.as, 3k)
Pingback: dispatchEvent » Blood, Sweat, Tears, but Mostly Cupcakes