2

I'm trying to get the size of the text string using GetTextExtentPoint32. I read the documentation many times and do some research and as far as I know, the below code should give me the correct width and height of the text.

vFontFamily = "Segoe UI"; //font family
vFontSize = 26; //font size

HDC hdc = GetDC(hwnd);
HFONT hFont = CreateFont(
     -MulDiv(vFontSize, GetDeviceCaps(hdc, LOGPIXELSY), 72), //calculate the actual cHeight.
      0, 0, 0, // normal orientation
      FW_NORMAL,   // normal weight--e.g., bold would be FW_BOLD
      false, false, false, // not italic, underlined or strike out
      DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, // select only outline (not bitmap) fonts
      CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH | FF_SWISS, vFontFamily);

SIZE size;
HFONT oldfont = (HFONT)SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, L"This is Text", wcslen(L"This is Text"), &size);

width = size.cx; //get width
height = size.cy; //get height

SelectObject(hdc, oldfont); //don't forget to select the old.
DeleteObject(hFont); //always delete the object after creating it.
ReleaseDC(hwnd, hdc); //alway reelase dc after using.

Unfortunately, it doesn't give me the correct size. The width has too much for at least 10% and height is too much for at least 5%. To be exact, the result of width in the text of This is Text is 228 and the height is 62 whereas the more accurate is somewhat near to 190 x 55 I think.

The text is painted using GDI+.

case WM_PAINT: {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    Graphics g(hdc);
    
    FontFamily  theFontFamily(vFontFamily);
    Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);
    SolidBrush  brush(Color(255, R, G, B));
    PointF      pointF(0.0f, 0.0f);

    TextRenderingHint hint = g.GetTextRenderingHint(); // Get the text rendering hint.
    g.SetTextRenderingHint(TextRenderingHintAntiAlias); // Set the text rendering hint to TextRenderingHintAntiAlias. 
    g.DrawString(L"This is Text", strlen("This is Text"), &font, pointF, &brush);

    EndPaint(hwnd, &ps);
    return TRUE;
}

I'm thinking maybe it's somehow related to how I drew the text because, in the paint message, I used Font instead of HFONT. But that doesn't make any sense, right? As long as I set the correct font family and font size before GetTextExtentPoint32 then it should give me the exact width and height of the text. What do you think?

Papilion
  • 87
  • 2
  • 10
  • 3
    presumably measuring the text the same way as you draw it will give better results? e.g. https://learn.microsoft.com/en-us/windows/win32/api/gdiplusgraphics/nf-gdiplusgraphics-graphics-measurestring(constwchar_int_constfont_constpointf__conststringformat_rectf) – Alan Birtles Mar 25 '21 at 09:27
  • There are slight differences between GetTextExtentPoint32, DrawText, GDI+ and DriectWrite functions. Larger discrepancies (>2%) are usually caused by using different fonts. In your case it could be difference between negative size (excluding internal leading) and positive size (including internal leading) for classic GDI font. Try with `-` removed before `MulDiv`. – Daniel Sęk Mar 26 '21 at 12:39

1 Answers1

4

You are using GDI to measure your text while you are using GDI+ to draw it.

GDI+ is an improvement on GDI and there are differences between the two when you use it to render text, etc. Another thing is the DPI Awareness, when measuring text, you'll need to make sure to handle the scaling too. Your method will have larger result compare to the actual width and height when you drew it using GDI+ and if you don't handle the DPI properly on your measurement.

Try using Graphics::MeasureString and see if you get the expected width and height. Do it the same way how you draw your string just like what AlanBirtles said in the comment.

For example,

HDC hdc = GetDC(hwnd);                                                      //get dc of your handle.
Graphics graphics(hdc);                                                     //setup graphics.
FontFamily  theFontFamily(vFontFamily);                                     //setup your font family.
Font        font(&theFontFamily, vFontSize, FontStyleRegular, UnitPixel);   //create the font the same way how you do it on your paint message.
PointF      pointF(0.0f, 0.0f);                                             //use PointF instead of RectF since thats how you paint it.

RectF boundRect;                                                            //setup boundRect to get the width and height.
graphics.MeasureString(text, -1, &font, pointF, &boundRect);                //Measure the text of the string
//or
//graphics.MeasureString(L"This is Text", strlen("This is Text"), pointF, &boundRect);

width = boundRect.Width;                                                    //get the width of text from boundRect.
height = boundRect.Height;                                                  //get the height of text from boundRect.

DeleteObject(&font);                                                        //delete the font.
ReleaseDC(LabelHandle, hdc);                                                //always reelase dc after using.

If you need more explanation, the following might be help you.

How to automatically set the width and height of static control base on its content?

GDI+ font size difference under different processes on same machine

DPI-awareness is really needed?

How to draw a GDI + text independent of DPI

https://stackoverflow.com/questions/4551224/what-is-the-difference-between-gdi-and-gdi#:~:text=GDI%2B%20is%20object%20oriented%2C%20and,in%20some%20ways%20GDI%20usage.&text=GDI%2B%20is%20an%20improvement%20on,and%20more%20image%20format%20support.

Polar
  • 3,327
  • 4
  • 42
  • 77