The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Problem with word wrapping algorithm

I have a word wrapping algorithmn that frequently underestimates the width of a string (in pixels) and therefore the number of lines of text produced.

The problem is that I'm using the tmAveCharWidth of TEXTMETRIC and the strings typically aren't long enough for the "averaging" effect to kick in.

The text output area is fixed width and either fixed or variable height. For fixed height scenarios I need to ensure that the text will fit within the region and for variable height scenarios I need to minimize the footprint.

So padding the results to allow a little leeway isn't an optimal solution.

Before I resort to measuring each individual character width, are there any tips or rules of thumb people have used for word wrapping algorithms that make using the average character width feasible?

-- Thanks
Nick Hebb Send private email
Monday, September 11, 2006
What are you programming in, Nick?

Most of my development is in Delphi and there must be 100 freeware, with source, VCL controls that provide wordwrap code I can look at and use / tweak / learn from should I so desire (subject to licenses, etc of course), including Borland's base VCL code.

Are there any controls / components / snippets out there you can grab to have a look at, pretty sure the whole wordwap wheel has been invented and refined to death enough times such that you won't have to do it all over agin yourself?

Aaron DC Send private email
Monday, September 11, 2006
What's the objection to measuring the string based on each individual character?
Monday, September 11, 2006
I never had good luck with GetTextMetrics() for calculating text length. Did you try DrawText()?
MBJ Send private email
Monday, September 11, 2006
Use GetTextExtentPoint32 for each possible wrap point until it gets too wide.  For "The quick brown fox" you'd call GetTextExtentPoint32 for "The" then "The quick" then "The quick brown" and so on.  You say your strings are short, so the overhead of calling GetTextExtentPoint32 multiple times per string will be negligible, and you'll get perfect results.
Richie Hindle Send private email
Monday, September 11, 2006
You can't rely on the average even for long strings.

That's because, the average is just that, an average.  And there can be variation on both sides.

You have to measure the width of the string yourself.

I seem to remember that GetTextExtent(32?) works, except sometimes, the sometimes including at least when the font is italicized.  Do some experiments drawing on a bitmap, and measuring how long you think the string is compared to how long it really is, and I think that you'll see this.

I also seem to remember, but this is more hazy, the DrawText functions to measure the size of text also have some problems with italics.

You may be able to roughly compensate for the italics using a fudge factor from some experimentation with different fonts/sizes/etc.

Or, if you want to compensate exactly, I think you'd probably have to write your own text measurement function using an off-screen bitmap.
S. Tanna
Monday, September 11, 2006
I'd put my solution into Richie Hindle's general approach.  If it was too slow, I'd create some kind of cache map of

Word/Font/Font-Attributes <---> Width in pixels

So I wouldn't constantly be recalculating the width of the same words just to redraw the screen or something.
S. Tanna
Monday, September 11, 2006
Use the average character width to guess where the break should be, then start using the API functions to fine tune it.
Monday, September 11, 2006
The DrawText API will render text to a rectangular area.  It automatically does word breaking and has various other nifty options.

GetTextExtentPoint32 is an option.  Simply call it for subsequently longer break points.

Using a RichEdit Control is also an option.  The control will format and display the text.  It will also tell you how much of the text will fit in a certain "height".  Look at the EM_FORMATRANGE, EM_DISPLAYBAND messages.

GetCharWidth/GetCharABCWidths is another option.  These measure individual or ranges of characters.
Dave B. Send private email
Monday, September 11, 2006
> The DrawText API will render text to a rectangular area.  It automatically does word breaking and has various other nifty options.

I was referring to the function/mode, where you use it to measure the text without actually drawing it (And there is one in Win32).  IIRC, it doesn't exactly work, same problem as GetTextExtent32
S. Tanna
Monday, September 11, 2006
Thanks for the replies so far. Let me give some context to this problem...

The text wrapping is for flowchart shapes in Excel. I have a COM add-in written in VB6 that automates flowcharting - think of it as a text-to-flowchart automation tool. When a user enters some text, my add-in grabs the text, generates a flowchart symbol, then adds the text to the symbol.

The auto-sizing feature of Excel's autoshapes doesn't work, so I need to set the shape's height based on the length of the text.

Unfortunately, Excel doesn't expose a window handle for the autoshapes (or more specically, the TextFrame within the autoshape), so using traditional text wrapping methods hasn't worked.

Based on the feedback here, I've got several ideas I could try, though. Thanks for all the help so far!
Nick Hebb Send private email
Monday, September 11, 2006

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
Powered by FogBugz