PDA

View Full Version : In search of a more Effecient Word Wrapping function...



savage
13-05-2005, 07:47 PM
Hi All,
I found a bug in my TrueTypeFont wrapper class, which relates to
wrapping text. I fixed the bug and it works adequately, but I was
wondering if anyone else on here has written a word wrapping function
and what algorithm they used.

My function looks like this...
function DrawText( aText : WideString; aWidth, aHeight : Integer ) :
PSDL_Surface; overload;

As you can see, I pass a string to the function and a width and height,
in pixels. The function goes off and and wraps the text so that it fits
within the width and height and returns a new SDL surface of the
specified size, with all the text drawn on it.

The algorithm I decided to use, basically starts from the end of the
string and works backwards until it find a space. It then sees if the
string from the beginning up to that space position fits into the
Width/height provided. If it does I store that into an array and proceed
to parse the rest of the text. In code it looks like this...

strChopped := aText; // Where aText is the original string
i := Length( strChopped );
while ( i <> 0 ) do
begin
if ( string( strChopped[ i ] ) <> ' ' ) and ( Integer( string( strChopped[ i ] ) ) <> 13 ) then
dec( i )
else
begin
dec( i );
if TTF_SizeUTF8( FFont, PChar( string( Copy( strChopped, 0, i ) ) ), textw, texth ) = 0 then
begin
if ( textw < aWidth )
and ( texth < aHeight ) then
begin
SetLength( strlist, Length( strlist ) + 1 );
strlist[ Length( strlist ) - 1 ] := Copy( strChopped, 0, i );
strChopped := Copy( strChopped, i + 2, Length( strChopped ) - ( i - 1 ) );
i := Length( strChopped );
if TTF_SizeUTF8( FFont, PChar( string( strChopped ) ), textw, texth ) = 0 then
begin
if ( textw < aWidth )
and ( texth < aHeight ) then
begin
SetLength( strlist, Length( strlist ) + 1 );
strlist[ Length( strlist ) - 1 ] := Copy( strChopped, 0, i );
strChopped := Copy( strChopped, i + 2, Length( strChopped ) - ( i - 1 ) );
i := Length( strChopped );
end;
end;
end;
end;
end;
end;

The TTF_Size* functions just return the supplied text's width and height.

savage
16-05-2005, 11:56 AM
No takers on providing a more effeceint solution?

Andreaz
31-08-2005, 05:08 PM
I know it was a long time ago that you wrote this request, but didn't notice until now, atleast i wrote a textwrapper using a TCanvas and i guess you could use that. It's quite fast, more or less O(N) where N is the length of the text. TextOut is called once per line and textWidth once per word. No costly copy, Length etc as everything is managed on a char to char basis.

It works opposite to yours, starts from the begining and extracts one word from the text. Each word is then added to the line if the line + the word fits within the maximum width.




Procedure DrawText&#40;Canvas&#58; TCanvas; Text&#58; String; Width, Height&#58; Integer&#41;;
var Char&#58; PChar;
var Last&#58; PChar;
var Seperator&#58; System.Char;

var Line&#58; String;
var Word&#58; String;
var X, Y &#58; Integer;

var SpaceWidth&#58; Integer;
var LineHeight&#58; Integer;
begin
Char&#58;=@Text&#91;1&#93;;
Line&#58;='';
Word&#58;='';

SpaceWidth&#58;= Canvas.TextWidth &#40;' '&#41;;
LineHeight&#58;= Canvas.TextHeight&#40;' '&#41;;

X&#58;=0;
Y&#58;=0;
while Char^ <> #0 do begin
// Start of new line
while Char^ <> #0 do begin
// Save the current position
Last&#58;=Char;
// Reset the word
Word&#58;='';
// Get a new word
while not &#40;Char^ in &#91;#0, #32, #13, ',', '.'&#93;&#41; do begin
Word&#58;=Word + Char^;
Inc&#40;Char&#41;;
end;
Seperator&#58;=Char^;

// Add the word's width to the line
Inc&#40;X, Canvas.TextWidth&#40;Word&#41;&#41;;
// Check if the word fits within the width
If X >= Width then begin
// Skip the last word.
Char&#58;=Last;
// Print the text
Canvas.TextOut&#40;0,Y, Line&#41;;
// Reset the line
Line&#58;='';
// Reset the line's width
X&#58;=0;
// Increase our vertical position
Y&#58;=Y + LineHeight;
// Check if the next line will reach the vertical limit
IF Y + LineHeight >= Height then Exit;
// Exit the line loop
Break;
end;
// Special case if we reached the end of the text
IF Char^ = #0 then begin
Canvas.TextOut&#40;0,Y, Line + Word&#41;;
Exit;
end;
// Add the word and a space to the line
Line&#58;=Line + Word + Seperator;
// Add the space's width
Inc&#40;X, SpaceWidth&#41;;
// Skip the space
Inc&#40;Char&#41;;
end;
end;
Canvas.TextOut&#40;0,Y, Line&#41;;
end;


Edit: Typos