18

Can you measure the width of a string more exactly in WIN32 than using the GetTextMetrics function and using tmAveCharWidth*strSize?

Draemon
  • 33,955
  • 16
  • 77
  • 104
Razvi
  • 2,808
  • 5
  • 31
  • 39
  • 3
    You should note that "tmAveCharWidth*strSize" is only sane for fixed width fonts. – Evan Teran Jul 14 '09 at 17:08
  • Even for a decidedly fixed-width font (Iosevka), `tmAveCharWidth` does not match `tmMaxCharWidth` although I am not entirely sure what it would mean in practice nor do I have the code at hand to tell you if, say, the second number is some integer-multiple of the first (suggesting e.g. a grapheme spanning multiple glyph "boxes"). – Armen Michaeli Mar 31 '23 at 12:18

5 Answers5

24

Try using GetTextExtentPoint32. That uses the current font for the given device context to measure the width and height of the rendered string in logical units. For the default mapping mode, MM_TEXT, 1 logical unit is 1 pixel.

However, if you've changed the mapping mode for the current device context, a logical unit may not be the same as a pixel. You can read about the different mapping modes on MSDN. With the mapping mode, you can convert the dimensions returned to you by GetTextExtentPoint32 to pixels.

Nick Meyer
  • 39,212
  • 14
  • 67
  • 75
  • 2
    GetTextExtentPoint32 uses "logical units"; not pixels: http://msdn.microsoft.com/en-us/library/dd144938%28VS.85%29.aspx – user20493 Sep 18 '12 at 19:55
  • 2
    @user good catch. When the mapping mode is MM_TEXT (the default), 1 logical unit = 1 pixel, but that doesn't necessarily have to be true. I'll modify my answer. – Nick Meyer Sep 19 '12 at 18:12
  • And, nowadays, you also have to worry about high DPI scaling if your process is not marked as high-DPI aware. – Adrian McCarthy Mar 30 '17 at 16:20
17

I don't know for certain, but it seems that:

HDC hDC = GetDC(NULL);
RECT r = { 0, 0, 0, 0 };
char str[] = "Whatever";
DrawText(hDC, str, strlen(str), &r, DT_CALCRECT);

might work.

Andreas
  • 5,393
  • 9
  • 44
  • 53
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • Thanks, tried it and shows same width as GetTextExtentPoint32 :). – Razvi Jul 14 '09 at 17:39
  • 5
    This is a much better solution than `GetTextExtentPoint32` since it takes the mapping mode out of equation. One thing the author needs to change is the flags for `DrawText`. Set it to `DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE`. The resulting width can be then calculated as `abs(r.right - r.left);` – c00000fd Jun 09 '13 at 01:44
5

Graphics::MeasureString ?

VOID Example_MeasureString(HDC hdc)
{
   Graphics graphics(hdc);
   // Set up the string.
   WCHAR string[] = L"Measure Text";
   Font font(L"Arial", 16);
   RectF layoutRect(0, 0, 100, 50);
   RectF boundRect;
   // Measure the string.
   graphics.MeasureString(string, 12, &font, layoutRect, &boundRect);
   // Draw a rectangle that represents the size of the string.
   graphics.DrawRectangle(&Pen(Color(255, 0, 0, 0)), boundRect);
}
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • Your method is definitely better than using GetTextExtentPoint32(). – William Chan Oct 31 '15 at 05:24
  • I have a confused thing. what is the layoutRect? I don't understand it – krosshj Aug 02 '16 at 07:44
  • 1
    Instead of `RectF layoutRect(0, 0, 100, 50);` you can use `PointF pointF(0.0f, 0.0f);` for `x` and `y`. See the example from here https://stackoverflow.com/questions/66795957/c-gettextextentpoint32-doesnt-give-the-correct-size/66797699?noredirect=1#comment118098332_66797699 – Polar Mar 26 '21 at 02:53
1

Depending on how you are using this, you can use DrawText with DT_CALCRECT specified and it will (its always done it fairly accurately for me) calculate the size of the required rectangle based on the text/font/etc.

DeusAduro
  • 5,971
  • 5
  • 29
  • 36
0

For Builder C++ first make new TLabel dynamicly and then change font attributes.Set your TLabel as autosize.Then you can get you TLabel width witch represents your string width in pixels.

 int WidthPixels (String font, int size, String text)
 {
    TLabel* label = new TLabel(Form1); // dynamic TLabel
    label->AutoSize = true;
    label->Font->Name = font; // your font
    label->Font->Size = size; // your font size
    label->Caption = text; // your string
    return label->Width;
 }

int width = WidthPixels("Times New Roman", 19 , "Hey");