Dynamic Text Wrapping in ActionScript 3

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.

If you see this message, you need to install Flash Player 9.

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

  1. 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.
  2. 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.
  3. Skip all the lines until the current line is below the top of the image.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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.
  11. 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!
  12. 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.

Creative Commons License file WrapTextUtility View Source | Download (.as, 3k)

This entry was posted in AS3, Flash. Bookmark the permalink.

29 Responses to Dynamic Text Wrapping in ActionScript 3

  1. Hot dog illustration © Mark Hicks.

  2. p* says:

    I LOVE IT!

  3. brad says:

    This cant possibly be the “solution”. This is just a dirty, smelly, nasty hackjob…

  4. Bibek Sahu says:

    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.

  5. @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!

  6. Pingback: dispatchEvent » Blood, Sweat, Tears, but Mostly Cupcakes

  7. Stephan says:

    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.

  8. 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.

  9. 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!

  10. Don says:

    Dennis,
    Would love to take a look at the system that you have developed…
    Is the component available for purchase?
    Cheers,
    Don

  11. Sam says:

    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!

  12. 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.

  13. Here’s the demo I promised :) enjoy

  14. tlecoz says:

    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 :)

    ++

  15. 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!

  16. Are you guys aware of the undocumented methods of TextField as described on Flex Non Docs?
    http://nondocs.blogspot.com/2007/05/flashtexttextfieldcopyrichtext.html

  17. flash 10 dude says:

    um. how about if you’re flash player is greater then version 9?

    “If you see this message, you need to install Flash Player 9.”

    ^ message I’m seeing where I’m assuming some cool flash player example *should be*

    love and kips.

  18. Sorry about the embedding thing. Upgraded SWFobject without upgrading the embed code in my blog posts. Should be fine now.

  19. Carl Leiner says:

    This is great except I can get the text to recover (redraw) when I move my object away. A hint? (or code)

    Thanks

  20. Carl Leiner says:

    Oop i Cant’t get the text to cover
    Thnx

  21. Carl Leiner says:

    I think I got it -refill the textfield with the string??\\Thanks again

  22. MUX says:

    great idea btw. seems ideal for what im doing.
    i’ll have a play with your source cheers.
    thanx for sharing.

  23. Sony says:

    Hi,\n\n Great solution. is it possible to view code?\n\n-Regards,\nSony

  24. Frank says:

    Very nice work! – Thanks for sharing!

  25. zhoukai says:

    Very good!
    Thanks for sharing!

  26. adrian says:

    Would you be able to reproduce this feature for flex mobile ? And if so what would you use instead of the TextField, which doesn’t work with AIR 3 for mobile.

    Thx

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 

Spam Protection by WP-SpamFree