PC Review


Reply
Thread Tools Rate Thread

On CreateGraphics().MeasureString

 
 
Dom
Guest
Posts: n/a
 
      14th Oct 2011
For various reasons, I've had to create my own control that allows me
to display text. The control will highlight a word by placing it in a
gray rectangle as the mouse flies over it. To do this, I split the
text into an array of words, and each word carries it's rectangle,
specifying the x, y, width, and height of the word. Each word is
composed of a string of "char-types" -- numeric, alpha, whitespace
(space and tab), or special (comma, semicolon, etc). For example, in
"Doe, John123" there are 5 words:

1. Doe
2. <comma>
3. <whitespace>
4. John
5. 123

The rectangle for each word is obtained through
CreateGraphics().MeasureString which gets passed four arguments:

1. The text
2. A Font object
3. A StringFormat object
4. The Client width.

The StringFormat object is created by "new StringFormat()". I've
tried various flags, but they don't fix the problem.

The problem is that each word has a noticable white space trailing
it. Sometimes it is large, sometimes small, but always there. This
makes the text look strange, especially with a space coming before the
commas.

Any advice?
 
Reply With Quote
 
 
 
 
Dom
Guest
Posts: n/a
 
      15th Oct 2011
On Oct 14, 8:43*pm, Peter Duniho <NpOeStPe...@NnOwSlPiAnMk.com> wrote:
> On 10/14/11 12:45 PM, Dom wrote:
>
>
>
>
>
> > For various reasons, I've had to create my own control that allows me
> > to display text. *The control will highlight a word by placing it in a
> > gray rectangle as the mouse flies over it. *To do this, I split the
> > text into an array of words, and each word carries it's rectangle,
> > specifying the x, y, width, and height of the word. *Each word is
> > composed of a string of "char-types" -- numeric, alpha, whitespace
> > (space and tab), or special (comma, semicolon, etc). *For example, in
> > "Doe, John123" there are 5 words:

>
> > 1. Doe
> > 2.<comma>
> > 3.<whitespace>
> > 4. John
> > 5. 123

>
> > The rectangle for each word is obtained through
> > CreateGraphics().MeasureString which gets passed four arguments:

>
> Don't use "CreateGraphics().MeasureString()". *You need to dispose of
> the Graphics instance when you're done with it, which you can't do if
> you don't put the reference in a variable somewhere.
>
> > 1. The text
> > 2. A Font object
> > 3. A StringFormat object
> > 4. The Client width.

>
> > The StringFormat object is created by "new StringFormat()". *I've
> > tried various flags, but they don't fix the problem.

>
> What "various flags" did you try? *How did you set them?
>
> > The problem is that each word has a noticable white space trailing
> > it. *Sometimes it is large, sometimes small, but always there. *This
> > makes the text look strange, especially with a space coming before the
> > commas.

>
> > Any advice?

>
> Yes. *Post your actual code.
>
> Pete- Hide quoted text -
>
> - Show quoted text -


Thanks for looking into this, Peter. Concerning the flags, I just
used one after the other in the call to StringFormat(). I never tried
to combine the flags.

Here is the code. I've inserted some comments. Something I've just
discovered: The effect depends on the font. The Calibri font works
as it should, but the times new roman font has a whole extra space at
the end of each word.

Begin ----------------------------------------------

private void Sketch(ArrayList Words)
{
int Row = 0;
StringFormat f = new StringFormat();
SizeF s;
PointF p = new Point(m_LeftMargin, m_TopMargin); // This
point is the upper left point for the start of the string.
int ClientWidth = base.Width - m_RightMargin -
m_LeftMargin;

// For each "Word" object, I need the rect to highlight it
foreach (Word w in Words)
{
s = CreateGraphics().MeasureString(w.Text, TextFont,
ClientWidth, f);

// Come here if I need to "wrap around"
if ((p.X + s.Width) > ClientWidth)
{
Row++;
p.Y += TextFont.Height + m_TagGutter +
TagFont.Height + m_LineGutter;
p.X = m_LeftMargin;
}
w.Row = Row;
w.Printable = true; // ((w.Text[0] != ' ' &&
w.Text[0] != '\t') || (p.X != m_LeftMargin)); // White space at the
start of a line is not printable
w.Rect = new RectangleF(p, s);
if (w.Printable) p.X += s.Width;
}
}

End --------------------------------
 
Reply With Quote
 
Jeff Johnson
Guest
Posts: n/a
 
      17th Oct 2011
"Dom" <(E-Mail Removed)> wrote in message
news:ed13a456-6f08-4da0-8910-(E-Mail Removed)...

> The rectangle for each word is obtained through
> CreateGraphics().MeasureString which gets passed four arguments:


> The problem is that each word has a noticable white space trailing
> it. Sometimes it is large, sometimes small, but always there. This
> makes the text look strange, especially with a space coming before the
> commas.


I seem to recall reading that MeasureString() is simply not reliable. I
believe it has to do with GDI+ itself. But it's been quite a while since
I've seen any discussion on the issue, so I could be remembering things
wrong.


 
Reply With Quote
 
Dom
Guest
Posts: n/a
 
      25th Oct 2011
On Oct 17, 11:03*am, "Jeff Johnson" <i....@enough.spam> wrote:
> "Dom" <dolivas...@gmail.com> wrote in message
>
> news:ed13a456-6f08-4da0-8910-(E-Mail Removed)...
>
> > The rectangle for each word is obtained through
> > CreateGraphics().MeasureString which gets passed four arguments:
> > The problem is that each word has a noticable white space trailing
> > it. *Sometimes it is large, sometimes small, but always there. *This
> > makes the text look strange, especially with a space coming before the
> > commas.

>
> I seem to recall reading that MeasureString() is simply not reliable. I
> believe it has to do with GDI+ itself. But it's been quite a while since
> I've seen any discussion on the issue, so I could be remembering things
> wrong.


I now have the answer to this question, and I want to complete this
post in case others have a similar problem.

First, CreateGraphics().MeasureString, and TextRenderer.MeasureText
don't do a good job of measuring a string. After some testing, it
seems like it pads the ending, and spaces are not measured accurately.

Also, CreateGraphics().DrawString does a poor job, since it does not
draw the string at the requested X and Y. It seems to start drawing
at X+?.

This site does a good job of explaining why: http://windowsclient.net/articles/gdiptext.aspx

This site gives two work arounds: http://www.codeproject.com/KB/GDI-pl...urestring.aspx

But the two work arounds still did not give me the precision I
needed. The solution is really remarkably simple. Bypass the
graphics object in CSharp altogether. Go directly to the Windows
API. You need GetTextExtentPoint32, TextOut, and several related
API's, such as BeginPaint, EndPaint, GetDC, etc.

This will give you absolute precision. I can now get the exact letter
that the mouse is hovering over. I can block a string of text at
will, and I can even insert a carat at the right place when the mouse
is clicked. IT doesn't matter what font is used. It is always
accurate. Nothing else is needed.
 
Reply With Quote
 
Dom
Guest
Posts: n/a
 
      31st Oct 2011
On Oct 25, 11:47*am, Dom <dolivas...@gmail.com> wrote:
> On Oct 17, 11:03*am, "Jeff Johnson" <i....@enough.spam> wrote:
>
> > "Dom" <dolivas...@gmail.com> wrote in message

>
> >news:ed13a456-6f08-4da0-8910-(E-Mail Removed)...

>
> > > The rectangle for each word is obtained through
> > > CreateGraphics().MeasureString which gets passed four arguments:
> > > The problem is that each word has a noticable white space trailing
> > > it. *Sometimes it is large, sometimes small, but always there. *This
> > > makes the text look strange, especially with a space coming before the
> > > commas.

>
> > I seem to recall reading that MeasureString() is simply not reliable. I
> > believe it has to do with GDI+ itself. But it's been quite a while since
> > I've seen any discussion on the issue, so I could be remembering things
> > wrong.

>
> I now have the answer to this question, and I want to complete this
> post in case others have a similar problem.
>
> First, CreateGraphics().MeasureString, and TextRenderer.MeasureText
> don't do a good job of measuring a string. *After some testing, it
> seems like it pads the ending, and spaces are not measured accurately.
>
> Also, CreateGraphics().DrawString does a poor job, since it does not
> draw the string at the requested X and Y. *It seems to start drawing
> at X+?.
>
> This site does a good job of explaining why: *http://windowsclient.net/articles/gdiptext.aspx
>
> This site gives two work arounds: *http://www.codeproject.com/KB/GDI-pl...urestring.aspx
>
> But the two work arounds still did not give me the precision I
> needed. *The solution is really remarkably simple. *Bypass the
> graphics object in CSharp altogether. *Go directly to the Windows
> API. *You need GetTextExtentPoint32, TextOut, and several related
> API's, such as BeginPaint, EndPaint, GetDC, etc.
>
> This will give you absolute precision. *I can now get the exact letter
> that the mouse is hovering over. *I can block a string of text at
> will, and I can even insert a carat at the right place when the *mouse
> is clicked. *IT doesn't matter what font is used. *It is always
> accurate. *Nothing else is needed.


Back again. The program with the calls to the API had a nasty memory
leak, and I discovered the hard way that it was happening in
Font.ToHfont(). Apparently, this does more than just return a cached
variable (I should have guessed since it's a method, not a property).
To confirm this I searched the web and found:

"GDI memory leak in Windows Forms"

http://megakemp.wordpress.com/2009/0...windows-forms/

So, do something like cache the HFont in the constructor.
 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off



Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 05:43 AM.