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}
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.
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] "Neil Walters" wrote: > 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. >
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.
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] "Neil Walters" <neil.walters@boxtelematics.co.uk> wrote in message news:1119530478.439966.293850@g14g2000cwa.googlegroups.com... > 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} >
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] "Neil Walters" <neil.walters@boxtelematics.co.uk> wrote in message news:1119530478.439966.293850@g14g2000cwa.googlegroups.com... > 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} >
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] "Neil Walters" <neil.walters@boxtelematics.co.uk> wrote in message news:1119530478.439966.293850@g14g2000cwa.googlegroups.com... > 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} >
Don't see what you're looking for? Try a search.
|