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)
17 Comments »
RSS feed for comments on this post. TrackBack URI
Hot dog illustration © Mark Hicks.
Comment by Roger Braunstein — April 1, 2007 #
I LOVE IT!
Comment by p* — April 9, 2007 #
This cant possibly be the “solution”. This is just a dirty, smelly, nasty hackjob…
Comment by brad — July 12, 2007 #
Nuh uh!
Comment by Roger Braunstein — July 12, 2007 #
Have you considered using “sandbag divs” to make this work for HTML text fields? Or can I get a copy of your code so I can play with that idea? :-)
It may be easier to build from scratch, since I know of no way to get text extents for HTML text, so one would just pick an arbitrary splice height and build the sandbags based on that… but your concepts and code with BitmapData.getColorBoundsRect() make this automagically feasible in a way that can’t easily be done in HTML.
Comment by Bibek Sahu — August 28, 2007 #
@Bibek, Flash’s HTML rendering capability just isn’t there for it to be able to flow around correctly placed divs. It’s a great approach for HTML, but it does require someone to make the sandbags, this solution dynamically wraps around anything!
Comment by Roger Braunstein — August 28, 2007 #
[…] site also uses a bit of the dynamic text wrapping and object serialization techniques I posted about […]
Pingback by dispatchEvent » Blood, Sweat, Tears, but Mostly Cupcakes — September 6, 2007 #
Any chance you are willing to share the code? Looks like a very effective solution. I would hate to reinvent the wheel when you’ve already done a great job.
Comment by Stephan — March 6, 2008 #
Hi Roger,
seeing how this article is 1 year old, have you updated the algorithm.
I developed a system with a very similar algorithm that can handle multiple images with rotated text and images. (and alot faster). You’re on the right track though.
I can’t release the code, since it was a commercial product, but I can show a swf if anyone’s interested.
Comment by Denis Smirnov — April 8, 2008 #
Oh man, yeah, I meant to release this source a loooong time ago. I have been keeping busy (go figure) and kept forgetting about it. But yeah, I’d love to see other solutions! Post a link!
Comment by Roger Braunstein — April 8, 2008 #
Dennis,
Would love to take a look at the system that you have developed…
Is the component available for purchase?
Cheers,
Don
Comment by Don — April 8, 2008 #
Thanks for posting the source Roger.
Denis- Id certainly be interested in seeing you implementation in action & if you can give any hints as to what techniques to explore that would be greatly appreciated!
Comment by Sam — April 9, 2008 #
Component is not available for purchase as of yet, demo is coming up in a few days, waiting for Rob to clean it up a bit, and we’re adding some features to it so stay tuned.
Comment by Denis Smirnov — April 16, 2008 #
Here’s the demo I promised :) enjoy
Comment by Denis Smirnov — April 23, 2008 #
Link would’ve been nice :)
http://www.renegadedigital.com/experiments/wrapperDemo.html
Comment by Denis Smirnov — April 23, 2008 #
Hello !
I saw your demo last week, and I would try to do the same by myself.
Here is the result : http://www.meme-pas-peur.com/tom/bmpInTxt/test_BmpInTxt.html
Please, tell me what you think about :)
++
Comment by tlecoz — May 2, 2008 #
Nice one Tom!!!!
Sweet, I should even put together a gallery of all your solutions. It makes me happy to see you writing in with your own implementations!
Comment by Roger Braunstein — May 3, 2008 #