Questions and Answers on any aspect of .NET. Now closed.
I want to render a long paragraph of text into a System.Windows.Form.Control using a non-fixed-width font.
I don't know how to calculate exactly where to break the pararaph into lines, to implement word-wrap myself.
Failing that I can render it using the Graphics.DrawString method which takes a StringFormat as its last parameter, which does the word-wrap for me automatically.
My problem is that I then want to render some fragment of the text (e.g. the 15th sentence) using a different color:
* I don't know exactly where the fragment begins and ends within the control's rectangle
* Even if I work out where the fragment begins and ends (by using the Graphics.MeasureCharacterRanges method), I don't know how to use Graphics.DrawString to draw an automatically-line-wrapped fragment which begins at some offset from the left-hand edge of its rectangle.
My constraints on the solution are:
* I don't want to use PInvoke (nor the built-in WebBrowser or RichTextBox controls)
* It must be reasonably CPU-efficient
The only solution I can think of involves using the MeasureCharacterRanges method to find which character is at the begining and end of each line that contains the text to be colored (so that I can redraw these lines individually).
Is there any alternative?
http://support.microsoft.com/default.aspx?scid=kb;en-us;307208 introduces the issue. It recommends using Uniscribe (which http://blogs.msdn.com/michkap/archive/2005/12/06/500485.aspx discourages), or using TextRenderingHint.AntiAlias which results in sub-optimal text appearance.
http://wesnerm.blogs.com/net_undocumented/2006/06/text_mess_in_ne.html is a rant which says that MeasureString is slow.
http://www.codeproject.com/csharp/articlefour.asp implements painting each character individually! It uses the Graphics.FromImage method to find the accurate width of each character (which apparently is difficult to do using GDI+), and to find the kerning between each pair of characters: it avoids using the MeasureString method, except in a test case to show how badly it works. It's written in early 2005 and targets .NET v1.1.
http://www.eggheadcafe.com/forumarchives/netframeworknetwindowsforms/nov2005/post24272966.asp says that TextRenderer is based on GDI (as opposed to Graphics which is GDI+), it encapsulates Uniscribe in fact, but it doesn't implement MeasureCharacterRanges functionality.
There's more functionality available in WPF (.NET v3): the FormattedText class mentioned in http://msdn2.microsoft.com/en-us/library/system.windows.media.formattedtext.aspx does just what I want ... but it's .NET v3.0 only.
In summary, I can use the FormattedText class iff .NET v3.0 is available, otherwise I should use one of the other implementations. I don't know:
* Whether the new GDI TextRender.MeasureText function avoids the problems that are associated with the Graphics.MeasureString class
* How the appearance of text drawn using TextRender.DrawText compares with text drawn using Graphics.DrawString
* How assumptions I may make about TextRender.MeasureText may be wrong depending on whether ClearType is enabled in the O/S
If I use TextRender, then its lack of a MeasureCharacterRanges method seems to mean that I'll need to measure each string to work out the line break/word wrap locations myself, to draw each line individually (and thus know where each word is) instead of drawing the whole paragraph.
Oops! Once again I didn't read closely enough. It looks like you DID consider WPF already.
Wednesday, January 10, 2007
By the way, the TextRender.DrawText method which wraps GDI produces uglier output on my screen than the Graphics.DrawString method which wraps GDI+: the width of each character is worse.
This is using size 10 Microsoft Sans Serif, which is a true-type font: I wouldn't have expected a difference in the way each character is rendered, but some characters are a pixel wider in GDI then in GDI+.
So it looks as if the algorithm in the "Article Four" article above (with double-buffering enabled) *is* the best .NET v2 way to produce good-looking text in a editor.
However, drawing individual characters won't work for complicated scripts as explained in http://www.d-type.com/layout/index.htm which need to be drawn as whole strings.
Programs like Notepad (not to mention Word etc.) seem to work OK: I'm guessing they use the GetTextExtentPoint API as mentioned in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/fontext_8f6t.asp (which I suppose isn't wrapped by the .NET framework).
The thing I don't understand is why the TextRender.DrawText method doesn't render e.g. the size 10 Microsoft Sans Serif font in the same was as Notepad does(which I'd guess is GDI) and Graphics.DrawString does (which is GDI+): shouldn't TextRender.DrawText output look the same as Notepad's?
This topic is archived. No further replies will be accepted.Other recent topics
Powered by FogBugz