Can calculation of word wrapping be made notably faster? I need to re-wrap a text so that it fits a given width, font and DC. The result needs to be an array of lines. I use the following code:

private struct SplitInfo
{
    public string Word;

    public string SplitChar;

    public override string ToString()
    {
        return Word;
    }
}

private static float GetWidth(Graphics gr, string text, Font font)
{
    SizeF size = gr.MeasureString(text, font, 10000, StringFormat.GenericTypographic);
    return size.Width;
}

public static string[] WrapText(string text, IntPtr hdc, Font font, int textWidthInLoMetric)
{
    // Split words at space (functionality for splitting at multiple different chars omitted for simplicity).
    // This is also why it needs SplitInfo to store the words.
    List&lt;SplitInfo&gt; words = new List&lt;SplitInfo&gt;(text.Split(' ').Select(x =&gt; new SplitInfo { SplitChar = " ", Word = x }));

    StringBuilder resultText = new StringBuilder();
    string currentLine = string.Empty;
    SplitInfo lastWord = new SplitInfo { Word = string.Empty, SplitChar = string.Empty };

    using (Graphics gr = Graphics.FromHdc(hdc))
    {
        while (true)
        {
            string newString = (currentLine + lastWord.SplitChar).TrimStart(' ') + words[0].Word;

            if (currentLine != string.Empty &amp;&amp; GetWidth(gr, newString, font) &gt; textWidthInLoMetric)
            {
                // Word no longer fits in line.
                resultText.Append(currentLine.TrimEnd() + "\n");
                currentLine = string.Empty;
            }
            else
            {
                lastWord = words[0];
                words.RemoveAt(0);
                currentLine = newString;
            }

            if (words.Count == 0)
            {
                resultText.Append(currentLine.TrimEnd() + "\n");
                break;
            }
        }
    }

    return resultText.ToString().TrimEnd().Split('\n');
}


Performance measurement shows that MeasureString is the main time consumer here (~90%). Is there any clever way to reduce the number of MeasureString calls? Other than that, I don't see opportunities to make word wrapping significantly faster.
One possibility is to cache the measured length for each word. I do this, when the line breaks need to be recalculated (i.e. when document needs to be reflowed) frequently, for example because the user edits words or the user resizes the width of the control.

A second possibility is to use different APIs, for example:


A different StringFormat value passed as a parameter
A different method for example TextRenderer.MeasureText instead of Graphics.MeasureString


My code says ...

        //http://msdn.microsoft.com/en-us/magazine/cc163630.aspx#S7 i.e. 
        //"Practical Tips For Boosting The Performance Of Windows Forms Apps" in MSDN says,
        //
        //It's better to use the TextRenderer method overloads that do not get IDeviceContext as an argument.
        //These methods are more efficient because they use a cached screen-compatible memory device context
        //rather than retrieving the native handle for device context from the internal DeviceContext and
        //creating an internal object to wrap it.
        //
        //My profiling shows that the method without the Graphics parameter takes 0.025 msec instead of 0.1 msec.
        //However, the resulting calculation is inaccurate.
        Size size = TextRenderer.MeasureText(
            m_graphics,
            text,
            paintStyle.font,
            proposedSize,
            textFormatFlags);


... and ...

    static TextFormatFlags textFormatFlags =
        TextFormatFlags.NoPrefix |
        TextFormatFlags.NoPadding |
        TextFormatFlags.ExternalLeading |
        TextFormatFlags.Left |
        TextFormatFlags.Bottom;


This code recalculates dozens of pages in a fraction of a second (and because it caches the measured word sizes it only measures all words once, when the document is first loaded).

The corresponding method which I use for drawing the text is:

        TextRenderer.DrawText(
            m_graphics,
            text,
            paintStyle.font,
            drawingPoint,
            color,
            textFormatFlags);