Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the EXACT pixel height of text rendered in Windows using DrawText

Tags:

c++

windows

gdi

I wish to find the exact height of text rendered in Windows. I have tried both GetTextExtentPoint32 and calling DrawText with the DT_CALCRECT flag and both give the same result.

It seems that the height returned is based on the full cell height, regardless of the actual text to be drawn.

The code below is the WM_PAINT handler for a standard Visual Studio 2013 Win32 project. It creates a (large) font and draws the sample text. The tallest part of the text is 98 pixels, but the value returned by GetTextExtentPoint32 is 131.

I realise that some applications might want the full cell height, but also some applications (like mine) just want the actual height used by the text.

Does anyone know how to find this information?

Yes, I can render to a memory DC and scan down looking for the first non-background coloured pixel – but that is going to be super slow.

Thanks

case WM_PAINT:
{
    hdc = BeginPaint (hWnd, &ps);

    HFONT hfont = CreateFont (-99, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, VARIABLE_PITCH, L"Segoe UI Semibold");
    auto old_hfont = SelectObject (hdc, hfont);

    wchar_t sample_text[] = L"123 Testing 123";
    size_t sample_text_length = wcslen (sample_text);

    SIZE s;
    GetTextExtentPoint32 (hdc, sample_text, sample_text_length, &s);

    RECT r = {10, 10, 10 + s.cx, 10 + s.cy};

    SetBkColor   (hdc, RGB (80, 120, 160));
    SetTextColor (hdc, RGB (220, 220, 220));

    DrawText (hdc, sample_text, sample_text_length, &r, DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_TOP);

    SelectObject (hdc, old_hfont);
    DeleteObject (hfont);

    EndPaint (hWnd, &ps);
    break;
}
like image 254
Anthony Wild Avatar asked Mar 17 '15 13:03

Anthony Wild


2 Answers

Have a look at GetGlyphOutline(GGO_METRICS) The returned GLYPHMETRICS struct should contain all the data you need to compute the extent.

like image 71
Paul Groke Avatar answered Oct 14 '22 09:10

Paul Groke


Your text isn't drawn directly, first he becomes a path which describes the outlines of your geometries/glyphs. A path consists of moves, lines and curves (and a close flag to the prior). Except curves, other path segments are extreme points to the final fill. Converting curves to lines and iterating through all path segments to find the minimum and maximum in horizontal & vertical dimension by the points results in the closest fitting rectangle to your text.

You can convert your text to a path by drawing him with calling BeginPath before and EndPath after. FlattenPath does the curve->line conversion. GetPath provides access to the path points on context. AbortPath finally removes the path from the context.

When you haven't set the background to transparent, the path may be the background around your text and already the first path segments are the background/extents rectangle lines - not what you want.

To simplify this method, you can exclude repeating characters and group characters by 1) below baseline 2) above midline 3) rest. This all together works very fast in comparison to your "look pixels" try.

Other useful sources about text sizes are font metrics (GetTextMetrics) and character widths (GetCharABCWidths)

like image 32
Youka Avatar answered Oct 14 '22 10:10

Youka