Draw text on a line segment in C#

[draw text on a line segment]

This example shows how you can draw text on a line segment. The key is the DrawTextOnSegment method shown in the following code. This method can draw text on a line segment or below a line segment.

// Draw some text along a line segment.
// Leave char_num pointing to the next character to be drawn.
// Leave start_point holding the last point used.
private void DrawTextOnSegment(Graphics gr, Brush brush,
    Font font, string txt, ref int first_ch,
    ref PointF start_point, PointF end_point,
    bool text_above_segment)
{
    float dx = end_point.X - start_point.X;
    float dy = end_point.Y - start_point.Y;
    float dist = (float)Math.Sqrt(dx * dx + dy * dy);
    dx /= dist;
    dy /= dist;

    // See how many characters will fit.
    int last_ch = first_ch;
    while (last_ch < txt.Length)
    {
        string test_string =
            txt.Substring(first_ch, last_ch - first_ch + 1);
        if (gr.MeasureString(test_string, font).Width > dist)
        {
            // This is one too many characters.
            last_ch--;
            break;
        }
        last_ch++;
    }
    if (last_ch < first_ch) return;
    if (last_ch >= txt.Length) last_ch = txt.Length - 1;
    string chars_that_fit =
        txt.Substring(first_ch, last_ch - first_ch + 1);

    // Rotate and translate to position the characters.
    GraphicsState state = gr.Save();
    if (text_above_segment)
    {
        gr.TranslateTransform(0,
            -gr.MeasureString(chars_that_fit, font).Height,
            MatrixOrder.Append);
    }
    float angle = (float)(180 * Math.Atan2(dy, dx) / Math.PI);
    gr.RotateTransform(angle, MatrixOrder.Append);
    gr.TranslateTransform(start_point.X, start_point.Y,
        MatrixOrder.Append);

    // Draw the characters that fit.
    gr.DrawString(chars_that_fit, font, brush, 0, 0);

    // Restore the saved state.
    gr.Restore(state);

    // Update first_ch and start_point.
    first_ch = last_ch + 1;
    float text_width =
        gr.MeasureString(chars_that_fit, font).Width;
    start_point = new PointF(
        start_point.X + dx * text_width,
        start_point.Y + dy * text_width);
}

The code starts by getting a unit-length vector <dx, dy> pointing in the direction of the line segment. It then enters a loop making longer and longer strings until it finds one that won’t fit on the line segment. It then removes the last character it added to get the longest substring that will fit.

If the longest substring is empty, the method returns without drawing anything. If every character fits, the code sets the last character to be the final one in the string.

Next the code saves the Graphics object’s state so the code can undo the transformations that follow.

If the method should draw the text above the line segment, the code adds a translation to the Graphics object to move the text up by the text’s height.

The code then calculates the angle of the line segment and adds a rotation transformation to rotate the text to lie along the segment. It then adds a final translation to move the text to the segment’s starting position.

Finally the code draws the text at the origin. The transformations rotate and position the text appropriately.

The code then cleans up. It restores the Graphics object’s original state, sets first_ch to indicate the first character that didn’t fit, and sets start_point to the last position drawn by the text.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, drawing, fonts, graphics, transformations and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *