| Circle Word Wrap | Lab Report |
Purpose
This lab report describes how to word wrap a string of text inside a
circle.
Materials and Equipment
Software Requirements
Windows 95/98
Delphi 3/4 (to recompile)
CircleWordWrap.EXEHardware Requirements
VGA display
Procedure
- Use the Diameter TSpinEdit control to change the size of the circle.
- Use the Font Height TSpinEdit control to change the size of the selected font.
- Use the Font TComboBox to change the selected font.
- Use the Show Circle and Show Boxes TCheckBox controls to display or not display the circle and the text boxes.
- Use the TMemo control to change the text that is wrapped within the circle.
Discussion
If you want to wrap text inside a rectangular area, the DrawText
or DrawTextEx Windows API calls can be used. (These
API calls are described in The Tomes of
Delphi 3: Win32 Graphical API on pp. 486-495. Examples are also
included there.) But DrawText won't help much
inside a circular area.
Let's assume that the font name and size are already specified. Actually, instead of setting the Font.Size in points, we are more concerned with the font height in pixels, so the Font.Height property is used to specify the height of the characters. Given the Font.Height, the Font.Size is defined automatically by Delphi as
Font.Size = -Font.Height * 72 / Font.PixelsPerInch
Let's make the following assumptions for this example (like shown on the screen above):
Font.Height = 20 pixels
Diameter = 240 pixels
(xMiddle, yMiddle) = (120, 120) = center of the circle
If we assume that at least ˝ of a line is to be left blank at the top and bottom of the circle, the number of rows that can be fit into the circle is:
LineCount = Diameter / Height - 1
For the given assumptions
LineCount = 240/20 - 1 = 11
These 11 lines will require 11*20 = 220 pixels. The remaining space, 240 - 220 = 20 pixels, will be divided between the top and bottom of the circle. The top of the first text line will be y = 10 pixels from the top of the circle.
The Canvas.TextOut methods assumes the starting point of a text box is at the upper left.
y = 10 + (Line-1)Font.Height
x = SQRT[ Radius˛ + (y- yMiddle)˛ ]
based on the equation for a circle: x˛ + y˛ = R˛
Max Text Width = 2x [pixels]
| Text Line | y | x | Max Text Width |
| 1 | 10 | 48 | 96 |
| 2 | 30 | 79 | 158 |
| 3 | 50 | 97 | 194 |
| 4 | 70 | 109 | 218 |
| 5 | 90 | 116 | 232 |
| 6 | 110 | 120 | 240 |
| 7 | 130 | 120 | 240 |
| 8 | 150 | 116 | 232 |
| 9 | 170 | 109 | 218 |
| 10 | 190 | 97 | 194 |
| 11 | 210 | 79 | 158 |
Each text line must fit into the text box with coordinates (xMiddle-x, y) at the upper left and (xMiddle+x, y+Font.Height) at the lower right.
An arbitrary string of characters with a specified font will not fit into a specified width. A function GetStringThatFits was written to fetch a string of a specified pixel width. Assuming the string contains words, these words are parsed from the string until adding another word would exceed the specified width. In the event that the input string does not contain words (that is, the input string doesn't contain any spaces), the function GetStringThatFits then finds the number of characters that will fit into the specified pixel width.
When the lines are broken into words, there is usually some white space on the left and right if the string is centered around the x = xMiddle line. The x coordinate for the centered text is xMiddle -TextWidth(s) / 2.
With more lines of text, and with a smaller font, the text wrapped inside the circle will itself appear to be a circle:
![]() |
![]() |
The Screen.Fonts variable contains a list of all the fonts that can be displayed on the screen. These fonts are stored in the ComboBoxFont in the FormCreate method:
ComboBoxFont.Items := Screen.Fonts;
When the font is changed the UpdateDisplay method is called, just like when any of the controls change any of the parameters of what is to be wrapped inside the circle.
Inside the UpdateDisplay method, all of the lines of text from the TMemo are stored in a string using the Memo.Text property. When this assignment is made, each line from the Memo is terminated with a carriage return ($0D) and a line feed ($0A). A WHILE loop is used to replace the line termination characters with a single blank by using the Delete and Insert string functions. This string is then parsed using the function GetStringThatFits, which is described above.
Thanks to Josef Garvi for suggesting this project in his 23 Apr 99 borland.public.delphi.graphics UseNet Post. See improvements suggested by Josef Garvi.
Conclusions
Enclosing a given text string exactly in a circle may be difficult,
especially if word wrap is included. One approach might be to search a two
dimensional space, Circle Radius by Font Size, to find a combination in which the text
would fit exactly. Perhaps fixing the font size, and then varying the circle radius
would be a good approach.
Jacques Oberto's Wrapping Text Around a Circle Project |
![]() |
Walter Schick's Circular Text Project |
| Walter Schick's Spiral of Rotated Text Project |
Keywords
Word Wrap, Circle, GetTextThatFits function, Canvas.Ellipse, Font.Height,
Canvas.TextHeight, Canvas.TextWidth, Canvas.TextOut, Insert/Delete string functions,
TMemo.Text
Download
Delphi 3/4 Source and EXE (138 KB): CircleWordWrap.ZIP
Updated 14 Sep 2004
since 25 Apr 1999