views:

257

answers:

3

I have a c# application that does text rendering, something on par with a simple wysiwyg text editor.

I'm using TextRenderer.DrawText to render the text to the screen and GetTextExtentPoint32 to measure text so I can position different font styles/sizes on the same line.

In Vista this all works fine. In XP however, Arial renders differently, certain characters like 'o' and 'b' take up more width than in Vista. GetTextExtentPoint32 seems to be measuring the string as it would in Vista though, with the smaller widths. The end result is that every now and then a run of text will overlap the text preceding it because the preceding text gets measured as smaller than it actually is on the screen.

Also, my text rendering code mimics ie's text rendering exactly (for simple formatting and english language only) and ie text rendering seems to be consistent between vista and xp - that's how I noticed the change in size of the different characters.

Anyone have any ideas about what's going on?

In short, TextRenderer.DrawText and GetTextExtentPoint32 don't match up in xp for Arial. DrawText seems to draw certain characters larger and/or smaller than it does in Vista but GetTextExtentPoint32 seems to be measuring the text as it would in Vista (which seems to match the text rendering in ie on both xp and vista). Hope that makes sense.

Note: unfortunately TextRenderer.MeasureString isn't fast or accurate enough to meet my requirements. I tried using it and had to rip it out.

A: 

I'm not a C# guy, but I believe the .NET rendering is built on top of GDI+. I'm also pretty sure that GDI+ does its own font rendering which uses unhinted scaling.

GetTextExtentPoint32, on the other hand, is part of GDI. GDI uses sizing hints, which can affect the width of characters depending on the font size. In general GDI text at small sizes will look a little bit getter, but it won't scale linearly.

You have to consistently use one model or the other to get pixel-perfect results.

There may be other factors in play that may make this more obvious on XP than on Vista, but the fundamental problem exists in both. These other factors might include DPI settings, DPI scaling, ClearType or antialiasing settings, font-linking (if you're mixing scripts from other alphabets), font substitution (especially in printing), and possibly even different versions of Arial. I'm not even sure GDI+ uses the same default mapping mode as GDI.

Also see my answer on print preview.

Adrian McCarthy
+1  A: 

Thanks for taking the time to respond Adrian.

My understanding is that TextRenderer.DrawText actually wraps a call to GDI, bypassing GDI+ text rendering completely. That's why I was confused about GetTextExtentPoint32 not jiving with the output.

I think I found the issue though. It turns out if you set Graphics.TextRenderingHint to System.Drawing.Text.TextRenderingHint.ClearTypeGridFit, or possibly other values, it causes some characters in some fonts to increase or decrease in size. This seems to be true more in XP than in Vista. I haven't seen it happen at all in Vista. Anyway, it looks like GetTextExtentPoint32 is either not capable of recognizing the difference or I am not setting some kind of flag when I make the call.

My solution is to just use the system default textrenderinghint settings.

Michael
Looks right. I found some good information here (but he makes some dubious claims as well, so watch out). http://blogs.msdn.com/cjacks/archive/2006/05/11/595525.aspx
Adrian McCarthy
A: 

Actually both TextRenderer's DrawText and MeasureString based on DrawTextEx (and this is User32, not Gdi function). So you can consider using native marshalled calls to this function instead of MeauseString, because it doing some additional calculations (especially if you are using function override without HDC).

Also maybe this post will be helpful for you too.

arbiter