Graphics
CircleA.gif (989 bytes) Circle Word Wrap Lab Report
Example of Wrapping Text from a Memo Box Inside a Circle
ScreenCircleWordWrap.jpg (65025 bytes)

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.EXE

Hardware Requirements
VGA display

Procedure

  1. Double click on the CircleWordWrap.EXE icon to start the program.
  2. Change any of the controls on the left of the display:

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:

CircleWrap1.jpg (30055 bytes) CircleWrap2.jpg (22793 bytes)

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.


Related: 

Jacques Oberto's Wrapping Text Around a Circle Project

ScreenCircularText.gif (2444 bytes)

 

Walter Schick's Circular Text Project

WalterSchick_CircText.JPG (22063 bytes)

 

Walter Schick's Spiral of Rotated Text Project

WalterSchick_RotatedText.jpg (13301 bytes)


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 26 Feb 2005


since 25 Apr 1999