Results 1 to 3 of 3

Thread: In search of a more Effecient Word Wrapping function...

  1. #1

    In search of a more Effecient Word Wrapping function...

    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...
    [pascal]
    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;[/pascal]

    The TTF_Size* functions just return the supplied text's width and height.
    <br /><br />There are a lot of people who are dead while they are still alive. I want to be alive until the day I die.<br />-= Paulo Coelho =-

  2. #2

    In search of a more Effecient Word Wrapping function...

    No takers on providing a more effeceint solution?
    <br /><br />There are a lot of people who are dead while they are still alive. I want to be alive until the day I die.<br />-= Paulo Coelho =-

  3. #3

    In search of a more Effecient Word Wrapping function...

    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.


    Code:
    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
    Amnoxx

    Oh, and this code appears to be an approximate replacement for return(random() & 0x01);

    Phoenix Wiki
    http://www.phoenixlib.net/

    Phoenix Forum
    http://www.pascalgamedevelopment.com/viewforum.php?f=71

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •