Title: Draw paragraphs one line at a time in C#
My next few posts show how to draw paragraphs on a Graphics object in various ways. The examples draw on PictureBox controls, but it's important to remember that they work with any Graphics object including the one that you are given by a PrintDocument component's PrintPage event handler. That means these routines are useful for drawing text on a printout.
This example shows how to draw paragraphs by breaking text into lines and drawing the lines individually. This isn't terribly useful by itself, but a later post will use this ability to let you draw text that is fully justified (spaced so it reaches the left and right margins).
The following code shows the DrawParagraph method that the program uses to draw paragraphs line-by-line.
// Draw a paragraph by lines inside the Rectangle.
// Return a RectangleF representing any unused
// space in the original RectangleF.
private RectangleF DrawParagraph(Graphics gr, RectangleF rect,
Font font, Brush brush, string text, float line_spacing,
float indent, float extra_paragraph_spacing)
{
// Get the coordinates for the first line.
float x = rect.Left + indent;
float y = rect.Top;
// Break the text into words.
string[] words = text.Split(' ');
int start_word = 0;
// Repeat until we run out of text or room.
for (; ; )
{
// See how many words will fit.
// Start with just the next word.
string line = words[start_word];
// Add more words until the line won't fit.
int end_word = start_word + 1;
while (end_word < words.Length)
{
// See if the next word fits.
string test_line = line + " " + words[end_word];
SizeF line_size = gr.MeasureString(test_line, font);
if (line_size.Width > rect.Width)
{
// The line is too wide. Don't use the last word.
end_word--;
break;
}
else
{
// The word fits. Save the test line.
line = test_line;
}
// Try the next word.
end_word++;
}
// Draw this line.
DrawLine(gr, line, font, brush, x, y, rect.Width);
// Move down to draw the next line.
y += font.Height * line_spacing;
// Make sure there's room for another line.
if (y + font.Height > rect.Height) break;
// Start the next line at the next word.
start_word = end_word + 1;
if (start_word >= words.Length) break;
// Don't indent subsequent lines.
x = rect.Left;
}
// Add a gap after the paragraph.
y += font.Height * extra_paragraph_spacing;
// Return a RectangleF representing any unused
// space in the original RectangleF.
float height = rect.Bottom - y;
if (height < 0) height = 0;
return new RectangleF(rect.X, y, rect.Width, height);
}
The method starts by setting variables x and y to the location where the paragraph's first line of text should be drawn. It then splits the paragraph into words.
Next, the code enters a loop that it repeats until it has finished drawing the text or until it runs out of room. Within this loop, the code enters another loop where it tries adding more words to a test string. It keeps adding words until the test string doesn't fit within the allowed drawing area or it runs out of words.
After it has figured out how many words will fit, the code calls the DrawLine method (described shortly) to draw them.
After it finishes drawing all of the words, the method returns the part of the drawing area rectangle that has not yet been used. This is useful if the program needs to draw other paragraphs after this one.
The following code shows the DrawLine method.
// Draw a line of text.
private void DrawLine(Graphics gr, string line, Font font,
Brush brush, float x, float y, float width)
{
gr.DrawString(line, font, brush, x, y);
}
This method simply draws a line of text at an indicated location.
My next few posts will modify this method to do something more interesting.
Download the example to experiment with it and to see additional details.
|