Draw improved text on a curve in C#

[draw improved text]

This example shows how to draw improved text along a curved path. The example Draw text on a curve in C# shows how to draw text along a curved path. Unfortunately the spacing between the letters is pretty mediocre in (at least) two respects. First, the spacing is just generally too big. Second the program doesn’t add room for space characters.

The second problem is caused by the Graphics class’s MeasureString method. That method doesn’t allow room for trailing spaces because they normally don’t take up room when you draw text. For example, that method gives the strings “A” and “A ” the same width because the trailing spaces are invisible.

To draw improved text, this version of the program replaces calls to MeasureString with calls to the following MeasureText method.

// Return the size of some text adding extra room for spaces.
private SizeF MeasureText(Graphics gr, string text, Font font)
    SizeF size = gr.MeasureString(text, font);
    int num_spaces = text.Length - text.TrimEnd().Length;
    size.Width += font.Size * num_spaces;
    return size;

This code calls MeasureString as before. It then calculates the number of whitespace characters at the end of the string and adds the font’s size for each of them before returning the result.

The other thing that this example does to draw improved text is adjust the character spacing in the following code.

float text_width = MeasureText(gr, chars_that_fit, font).Width;
const float kern = 0.8f;
start_point = new PointF(
    start_point.X + dx * text_width * kern,
    start_point.Y + dy * text_width * kern);

This version of the code multiplies the space used by a piece of drawn text by a kerning value, which in this example is set to 0.8. That moves the characters a bit closer together.

In general Kerning is the process of adjusting the spacing between characters. Many fonts use kerning for specific characters or character pairs. For example, a font might move adjacent “ij” characters closer together so the j reaches a bit under the i.

The result is much better than the text drawn by the previous example. As in that version, the result will be best if you use a relatively large font and if the curves don’t change too quickly. You may also need to fiddle with the kerning value to produce the best results for your situation.

Download Example   Follow me on Twitter   RSS feed   Donate

About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in algorithms, drawings, fonts, graphics, transformations and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

2 Responses to Draw improved text on a curve in C#

  1. Tim Gray says:

    Hi Rod,
    In the shape of a partially circularised tetrahedron, I’m attempting to draw text along 12 arcs detailing the relationship of each apex to each of the other three apexes with an end result that no text us upside down and that the text start or finish is closest to its parent. Using the code you have here in varying combinations of start starting the segments from different ends, above or below the line and/or applying to reversed text a reverse rotation to the letters in the DrawTextOnSegment, I can end up with six of the text that look Ok, two that are sort or reverse italic and four that are a complete mess. I suppose, in a nutshell, I am trying to justify the text to either end of the curve and not have to stand on your head to read any…. I hope you understood all that. Would you have any tips, tricks or recommendations that I could use?
    BTW – Maths is not my strong point, nor ever was.

    • RodStephens says:

      To right align, use MeasureText to see how wide the text will be. Subtract that from the curve length to determine where to start drawing on the curve.

      That begs the question, “How do you calculate the curve’s length?” The example flattens the path into small segments so you can just add their lengths. Alternatively you can loop through them backwards from the end subtracting their lengths from the length of the text (as given by MeasureText) until you reach zero. Start the text at that point.

Comments are closed.