all groups > dotnet drawing api > june 2005 >
You're in the

dotnet drawing api

group:

MeasureString doesn't work with spaces.


MeasureString doesn't work with spaces. Neil Walters
6/23/2005 5:41:18 AM
dotnet drawing api:
If a string contains spaces MeasureString returns unexpected results

The following code

private void button1_Click(object sender, System.EventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
string s = "s";

for (int i = 0; i < 40; i++)
{
SizeF size = g.MeasureString(s, this.Font, 30);
System.Diagnostics.Debug.WriteLine(size.ToString());
s = s.Insert(0, " ");
}
}
}

produces the following output

{Width=9.33317, Height=13.8252}
{Width=12.34098, Height=13.8252}
{Width=15.34879, Height=13.8252}
{Width=18.35661, Height=13.8252}
{Width=21.36442, Height=13.8252}
{Width=24.37223, Height=13.8252}
{Width=27.38004, Height=13.8252}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
{Width=9.32959, Height=26.27539}
Re: MeasureString doesn't work with spaces. Neil Walters
6/23/2005 6:21:25 AM
Allen

When the string exceeds 30 (on the eighth line of debug) I would expect
the width to stay at 27.38004. I would also expect the height to
continue increasing as more lines are needed to accommodate to string.

Neil.
Re: MeasureString doesn't work with spaces. zhaounknown
6/23/2005 7:07:01 AM
Sample code and comments, read below:
/// <summary>
/// Any character has its own internal space. When input, extra ferning
space will be added after the character, which is not same to the space
character.
/// The ferning space width can be computed by measure a string of " ",
Notice, " " contains a space inside, but it measures the width of the ferning
(not clear the reason why, so far)
/// The width of a character can then be computed by widthOf("|")-widthOf(" ")
/// The realy width of a space character can't be computed in the normal way
of measuring " " or " " (which return the same result), it cannot be
computed also by "| " or "| ", which return the same width of "|".
/// It can only be computed by padding the space character before "|", so
the width of space can be computed by widthOf(" |")-widthOf(-"|")
/// Notice, '|' can be replaced by any 26 characters, and the result is
tested to be correct.
/// </summary>
/// <param name="theChar"></param>
/// <param name="theFont"></param>
/// <returns></returns>

private float ComputeWidthOfACharacter(char theChar, Font theFont)
{
//a control to create a graphics object
Button aButtonToCreateGraphics=new Button();
Graphics g=aButtonToCreateGraphics.CreateGraphics();

float trailingSpaceBeforeAndAfterAChar=(g.MeasureString(" ", theFont)).Width;
float theWidthOfACharWithTrailingSpace=(g.MeasureString(theChar+" ",
theFont)).Width;

return theWidthOfACharWithTrailingSpace-trailingSpaceBeforeAndAfterAChar;
}

private float ComputeWidthOfTrailingSpace(Font theFont)
{
//a control to create a graphics object
Button aButtonToCreateGraphics=new Button();
Graphics g=aButtonToCreateGraphics.CreateGraphics();

float trailingSpaceBeforeAndAfterAChar=(g.MeasureString(" ", theFont)).Width;

return trailingSpaceBeforeAndAfterAChar;
}

private float ComputeWidthOfASpaceChar(Font theFont)
{
//a control to create a graphics object
Button aButtonToCreateGraphics=new Button();
Graphics g=aButtonToCreateGraphics.CreateGraphics();

float preSpaceWithACharWidth=(g.MeasureString(" |", theFont)).Width;
float withoutPreSpaceWithACharWidth=(g.MeasureString("|", theFont)).Width;

return preSpaceWithACharWidth-withoutPreSpaceWithACharWidth;
}

[quoted text, click to view]
Re: MeasureString doesn't work with spaces. Neil Walters
6/23/2005 8:23:37 AM
Thanks zhaounknown

I guess with this information I could write my own version of
MeasureString that behaves the way any sane person would expect it too.

I'm still a bit bewildered why MeasureString behaves the way it does.

Thanks.
Re: MeasureString doesn't work with spaces. Ron Allen
6/23/2005 9:06:30 AM
Neil,
This looks like it is behaving the way the documentation specifies.
After the width of the string exceeds 30 it is showing the width needed to
fit the string into 2 lines. It appears that the spaces are very small in
width for this measurement, is this what you are questioning? This probably
has to do with the default StringFormat for the MeasureString call.
You might try inserting somthing like a '.' to see how the behaviour
changes.

Ron Allen
[quoted text, click to view]

Re: MeasureString doesn't work with spaces. Scott McChesney
6/23/2005 10:17:00 AM
Neil -

While I can't explain the situation (perhaps the estimable Mr. Powell can
lend his expertise), I can tell you how to get a little closer to what you
expect. It does involve using a custom StringFormat object, as has been
suggested. The odd thing (at least to me) is the choice of properties to
use.

I found that using a StringFormat object with the "FormatFlags" property set
to "MeasureTrailingSpaces" makes "MeasureString" pay attention to the spaces
(sort of). Obviously, these spaces aren't trailing, so why it works is
beyond me. But what does happen is that, once the string requires more than
one line to fully display, your Size object looks like this:

{Width=30, Height=27.92969}

Obviously, this still isn't quite right, since it's a far cry to assume a
two-line rectangle such as this can contain 39 spaces and an "s". However,
just to make sure that spaces don't measure a lot smaller than I might have
thought, I increased the loop to 59. That didn't change anything, so we
have to assume that "MeasureString" is throwing out what it considers
"extraneous" spaces.

Changing the test so that the spaces are on the end is even worse -
"MeasureString" never gives a size encompassing more than one line, even
with "MeasureTrailingSpaces" turned on. Without that flag, you never get
anything but the rectangle for the single "s" character.

It's been noted in this forum many times that "MeasureString" isn't the most
accurate thing, and even MSDN admits it after a fashion. Heck, they even
tell you that "MeasureString" and "DrawString" don't work the same way,
which would seem to defeat the purpose of the whole exercise. They try to
clear themselves by saying that "DrawString" can draw the string in a
rectangle *narrower* than what "DrawString" returns, depending on the
situation. But the fact that the two routines aren't consistent among
themselves makes this exercise painful if you need something accurate.

MSDN (and folks here in the NG) suggests you use "MeasureCharacterRanges" if
you need a more exact rendering, but that requires that you already have
your bounding rectangle set. Of course, you could always create some
arbitrarily large layout rectangle, and build your actual one from the
regions returned. So long as you don't change resolutions between the
measuring and rendering of the text, you should be in good shape. But it's
certainly a much longer exercise than the simplicity of the "MeasureString"
call...

HTH

- Scott

[quoted text, click to view]

Re: MeasureString doesn't work with spaces. Bob Powell [MVP]
6/23/2005 7:33:43 PM
To obtain an accurate measurement from MeasureString is often difficult and
sometimes impossible.

The most accurate method is to do the following:

#1 Make sure that you base the StringFormat on the GenericTypographic object
but make sure that you use Clone to obtain it. such as StringFormat
sf=(StringFormat)StringFormat.GenericTypographic.Clone();
#2 Ensure that the Graphics object is set to use the
TextRenderingHint.Antialias setting
#3 Use a StringFormat with MeasureTrailingSpaces flag set to true.

Measurestring will now render the best possible results.

If this still isn't accurate enough for you, and it often isn't if you're
doing complex text placement, use the MeasureCharacterRanges method to
obtain the exact character spacing sizes for the individual characters.

Text is fun isn't it?

--
Bob Powell [MVP]
Visual C#, System.Drawing

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.





[quoted text, click to view]

AddThis Social Bookmark Button