1

As far as I can tell, when you use TextRenderer.MeasureText to find the width of a character, in my case a fixed width CourierNew character, I get strange results when the font size has a decimal component, eg 11.3 rather than 11.0 or 12.0.

Eg

Dim font As Font = New Font("Courier New", emSize:=11.3) Dim width As Double = TextRenderer.MeasureText(New String("0"c, 1000), font).Width / 1000 

(I'm averaging the length of a long string to account for padding.)

Results

emSize = 11.0 -> width = 9.008 emSize = 12.0 -> width = 10.008 emSize = 11.3 -> width = 10.008 emSize = 11.7 -> width = 10.008 

Am I doing something wrong or is this expected behavior?

1 Answer 1

2

The only thing that the values returned by TextRenderer.MeasureText() are relevant for, is for knowing the text size when the text is rendered by TextRenderer.DrawText(). So, the question really is: do the sizes returned by MeasureText() match what happens when the DrawText() method is called. And indeed, they do:

sample text, drawn four different sizes, with and without anti-aliasing, with TextRenderer and with Graphics.DrawString

The above is the output from a simple program (code below) that renders a sample string of ten digits, in the four different font sizes you are asking about, four different ways. The first two sets are rendered using TextRenderer.DrawText(), while the second two sets are rendered using Graphics.DrawString().

Note that when using TextRenderer, three of the four sizes render exactly the same. So, of course when you measure the text, the measured width is also going to be exactly the same.

For each of the two rendering methods used, the rendering is actually done twice, once with the default settings for the Graphics object, and then again with Graphics.TextRenderingHint set to AntiAlias. It is interesting to note that, at least on my computer, while having AntiAlias set makes the TextRenderer output look better, when using the Graphics.DrawString() method it makes it look worse (in fact, it looks a lot like the TextRenderer output when the setting is the default value of SystemDefault).

More importantly though, note that while TextRenderer does not scale with fractional font sizes, Graphics.DrawString() does. These two rendering methods are fundamentally different. If you want fractional font sizes, you need to use a rendering method that supports that. And TextRenderer isn't that.


Code example:
In a brand new Windows Form project in Visual Studio, you can add these members to the Form1 class, to produce the output above:

private readonly IReadOnlyList<float> _emSizes = new[] { 11.0f, 11.3f, 11.7f, 12.0f }; private const string _text = "0123456789"; protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); int y = _DrawTextExamples(e.Graphics, _text, 0, _emSizes) + 10; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; y = _DrawTextExamples(e.Graphics, _text, y, _emSizes) + 10; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; y = _DrawTextExamplesWithDrawString(e.Graphics, _text, y, _emSizes) + 10; e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; y = _DrawTextExamplesWithDrawString(e.Graphics, _text, y, _emSizes) + 10; } private int _DrawTextExamples(Graphics g, string text, int y, IReadOnlyList<float> emSizes) { Point pt = new Point(0, y); foreach (float emSize in emSizes) { using (Font font = new Font("Courier New", emSize)) { Size size = TextRenderer.MeasureText(g, text, font); TextRenderer.DrawText(g, text, font, pt, Color.Black); pt.X += size.Width + 20; TextRenderer.DrawText(g, $"emSize: {emSize}, width: {size.Width}", font, pt, Color.Black); pt = new Point(0, pt.Y + size.Height); } } return pt.Y; } private int _DrawTextExamplesWithDrawString(Graphics g, string text, int y, IReadOnlyList<float> emSizes) { PointF pt = new Point(0, y); foreach (float emSize in emSizes) { using (Font font = new Font("Courier New", emSize)) { SizeF size = g.MeasureString(text, font); g.DrawString(text, font, Brushes.Black, pt); pt.X += size.Width + 20; g.DrawString($"emSize: {emSize}, width: {size.Width}", font, Brushes.Black, pt); pt = new PointF(0, pt.Y + size.Height); } } return (int)pt.Y + 1; } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.