Title: Center text above or below a line segment in C#
My post Draw text on a line segment in C# shows how to draw text above or below a line segment, but that technique does not work very well if you want to center text over the segment. That post was really intended to let you draw characters one at a time over a sequence of small segments to draw curved text.
This example shows how to center text above or below a line segment. The technique is actually a lot easier than the technique used by the previous post.
The approach is to draw the text centered in a rectangle and then move that rectangle into position so it is centered above or below the line segment. The left side of the picture on the right shows how the text is drawn above the origin in the X-Y plane. The result is translated so the origin moves to the center of the line segment as shown on the right side of the picture.
The most interesting part of the example is the following DrawTextOverSegment method, which draws the text.
// Draw text centered above or below the line segment p1 --> p2.
private void DrawTextOverSegment(Graphics gr,
Brush brush, Font font, string text,
PointF p1, PointF p2,
bool text_above_segment)
{
// Save the Graphics object's state.
GraphicsState state = gr.Save();
// Get the segment's angle.
float dx = p2.X - p1.X;
float dy = p2.Y - p1.Y;
float angle = (float)(180 * Math.Atan2(dy, dx) / Math.PI);
// Find the center point.
float cx = (p2.X + p1.X) / 2;
float cy = (p2.Y + p1.Y) / 2;
// Translate and rotate the origin
// to the center of the segment.
gr.RotateTransform(angle, MatrixOrder.Append);
gr.TranslateTransform(cx, cy, MatrixOrder.Append);
// Get the string's dimensions.
SizeF size = gr.MeasureString(text, font);
// Make a rectangle to contain the text.
float y = 0;
if (text_above_segment) y = -size.Height;
RectangleF rect = new RectangleF(
-size.Width / 2, y,
size.Width, size.Height);
// Draw the text centered in the rectangle.
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
gr.DrawString(text, font, brush, rect, sf);
}
gr.Restore(state);
}
The method starts by calling the Graphics object's Save method to save the object's current state. We will later restore that stater to remove the rotation and transformation that the method adds to the Graphics object.
Next, the code calculates the X and Y differences between the segment's two end points and uses them to calculate the segment's angle. It also calculates the segment's midpoint.
The method then adds a rotation to transform anything that it draws so it is rotated by the same amount as the line segment. It follows that with a translation transformation to move the origin to the segment's center point.
Next, the method must figure out what rectangle to use when drawing the text. It starts by measuring the size of the text. It then checks its text_above_segment parameter to determine whether the text should be drawn above or below the line segment. Depending on the parameter's value, the code defines a rectangle that lies above or below the origin and centered horizontally.
After that setup, the method simply draws the text centered inside the rectangle. The rotation and translation transformations rotate the text and move it so it is centered over or below the line segment.
The method finishes by calling the Graphics object's Restore method so the calling code doesn't need to worry about the transformations messing up future graphics.
Download the example to experiment with it and to see additional details.
|