From: Josef Garvi To: Earl F. Glynn <> Subject: Re: Wrapping Text Inside a Circle Date: Thursday, May 06, 1999 1:44 AM Hi Earl, Sure, if you would like to integrate my code into your article, you're welcome to it. Since you give proper credits, and you're not using your web site for commercial purposes, I can only be happy if any of my code can be of use to someone else. Personally I think it's a good idea you have to create a site that is limited to solving graphics puzzles, that way one knows a good place to look for good tips on the issue, and concentrating on one subject helps your site maintain better quality and make it more easy to find relevant information on. I've pasted my code at the end of the mail here. Please note that I recieved the last function GetCircleDiameterNeededForText from Wayne Vinson the other, and I've only adapted it to suite the rest of my code. If you want to integrate it too I think you should ask him if it's ok first. The main difference between my code and yours then lies in the CutString function which also returns hyphenated words when the need arises. Of course, this hyphenation does not follow any particular grammatical rules, but I think it makes a good improvement for me at least. Please let me know if you make any improvements to your article! Happy coding! Josef {***************************************************************************** Returns the next word in a string. Parameters: - Text: The text to retrieve a word from. Return Value: The first word in the string. NOTE: Move to StringExtra unit instead??? *****************************************************************************} function GetNextWord(Text: String): String; var NextWord: String; i: Integer; FoundGoodChar: Boolean; begin { Issues to consider: We should stop as soon as we see a character considered a word break. Stop if we reach the end of the file. Do not stop when we only have wordbreak characters (i.e. there are two spaces after each other). Result should only be blank when there actually is no word in the file. } if Text = '' then begin Result := ''; Exit; end; i := 0; NextWord := ''; FoundGoodChar := False; repeat Inc(i); NextWord := NextWord + Text[i]; if not (Text[i] in WordBreaks) then FoundGoodChar := True; until ( (i >= Length(Text)) or ((Text[i + 1] in WordBreaks) and (FoundGoodChar)) ); Result := NextWord; end; {***************************************************************************** Cuts the given string at a new word when the given width is reached. The cut piece is returned by the function, and is deleted from the original string. Parameters: - Canvas: The canvas on which the text is to be drawn (used to calculate width) - Text: The text to cut. - MaxWidth: The maximum width of the cut out text. Return Value: The text that has been cut out from the string. *****************************************************************************} function CutString(Canvas: TCanvas; var Text: String; MaxWidth: Integer): String; var UseStr, TestStr, HyphStr: String; NextWord: String; i: Integer; HyphenWidth: Integer; Stop: Boolean; begin UseStr := ''; NextWord := ''; Text := Trim(Text); Stop := False; // Concatenate new words repeat UseStr := UseStr + NextWord; Delete(Text, 1, Length(NextWord)); // Remove the word we have picked out from the text NextWord := GetNextWord(Text); // If we took a word that's followed by a hard return then we should break at this line i := 1; while ( i < Length(Text) ) and ( Text[i] in WordBreaks ) do begin if Text[i] = #13 then begin Stop := True; Break; end; Inc(i); end; if Stop then Break; // If we have a carriage return, then exit loop until (Canvas.TextWidth( UseStr + NextWord ) > MaxWidth ) or ( NextWord = '' ); { Hyphenate the word if it is too long to be written } if UseStr = '' then begin HyphenWidth := Canvas.TextWidth( '-' ); HyphStr := ''; TestStr := ''; i := 0; while Canvas.TextWidth( TestStr ) < MaxWidth - HyphenWidth do begin HyphStr := TestStr; i := i + 1; TestStr := Trim(TestStr + Text[i]); end; if HyphStr <> '' then UseStr := HyphStr + '-'; Delete(Text, 1, Length(HyphStr)); end; Result := (UseStr); end; {***************************************************************************** Prints text in a circular shape. Parameters: - TextToPrint: The text to be printed in the circle. - Canvas: The canvas where the text should be drawn. - Center: The centre of the circle in which the text should be drawn. - Diameter: The diameter of the circle in which the text should be drawn. - Options: A few options that modify the way the text is printed. Return Value: - The number of characters that actually were printed in the circle. If the circle is big enough to hold the full string, then this value will be equal to Length(TextToPrint). If not, it will indicate how many characters of the string there was room to print. If nothing was printed then it will return 0. *****************************************************************************} function PrintTextInCircle(TextToPrint: String; Canvas: TCanvas; Center: TPoint; Diameter: Integer; Options: TTextInCircleOptions): Integer; var LineHeight: Integer; NumLines: Integer; y, x, h: Integer; LineNum: Integer; Radius: Integer; Top: Integer; tmp: String; StartLength: Integer; begin StartLength := Length(TextToPrint); Radius := Diameter div 2; LineHeight := Canvas.TextHeight('x'); NumLines := (Diameter div LineHeight) - 1; Top := Center.Y - Radius; if tcoCenterVertical in Options then Y := (diameter - NumLines * LineHeight) div 2 else Y := LineHeight div 2; LineNum := 0; while (TextToPrint <> '') and (LineNum < NumLines) do begin Inc(LineNum); if Y < Radius then h := Y - Radius else h := Y - Radius + LineHeight; x := Trunc(Sqrt(Radius * Radius - h * h)); tmp := CutString(Canvas, TextToPrint, x * 2); if not (tcoTestOnly in Options) then Canvas.TextOut(Center.x - (Canvas.TextWidth(tmp) div 2), Top + Y, tmp); Y := Y + LineHeight; end; Result := StartLength - Length(TextToPrint); end; {***************************************************************************** Calculates how large a circle must be to contain the given amount text. Parameters: - TextToPrint: The text to be printed in the circle. - Canvas: The canvas where the text should be drawn. Return Value: - The diameter of the circle required for it to hold all the given text. NOTE: This function is borrowed from Wayne Vinson *****************************************************************************} function GetCircleDiameterNeededForText(TextToPrint: String; Canvas: TCanvas): Integer; { determine size of smallest circle for text..uses 2 levels of circle size search for speed .. this may not really be necessary } var //s : string; x, TextLength: Integer; diam,dInc : integer; FirstRound : boolean; Center: TPoint; begin Center := Point(0,0); diam := 10; //start with circle of diam 10 (min circle size) dInc := 50;//large initial diameter increment size FirstRound := true; TextLength := Length(TextToPrint); { iterate through the FillCircleWithText routine until all text in TMemo is used..in first round we use large circle diameter incrrement.. then we switch to smaller increment } repeat x := PrintTextInCircle(TextToPrint, Canvas, Center, diam, [tcoTestOnly]); if FirstRound and (x = TextLength) then //no more text begin FirstRound := false; x := 0; //search will stop if s = '' //return to previous diameter used and continue using small increment diam := diam - dInc - 1; dInc := 1;//small diameter increment end; inc(diam,dInc); until (x = TextLength);//no more text so we are done Result := diam; end; -- Josef Garvi Eden Foundation, Skreav. 45B, S-311 72 Falkenberg, Sweden Tel: +46 346-53157, Fax: +46 346-53171 E-Mail: josef@eden-foundation.org WWW: http://www.eden-foundation.org/