Win32 Font Study load font from external file / get font metrix / draw text in custom byte array

Change the font, measure the text string area and draw the text

Hint: The font name ‘Script’ was shipped with Window after Windows 8. But you can always check if certain font installed in Control Panel -> Font. It won’t break the program if you ask a non-existed font. It just gives you a system default font.

SIZE wsize; // get text metric

// not necessary if you just want to draw text using hdc
HDC vhdc = CreateCompatibleDC(hdc);

// Check the installed font using EnumFontFamilies API 
// or from "control panel -> font"
HFONT hFont = CreateFont(
    cHeight, cWidth, cEscapement, cOrientation, cWeight,
    bItalic, bUnderline, bStrikeOut, DEFAULT_CHARSET,
    OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, VARIABLE_PITCH,
    TEXT("Script"));

// store the original font and change to new font
HFONT hOldFont = (HFONT)SelectObject(vhdc, hFont);
SetTextColor(vhdc, RGB(0, 255, 0)); // text color
SetBkMode(vhdc, TRANSPARENT); // trans mode

// wsize.cx / wsize.cy is text string's width and length
GetTextExtentPoint(vhdc, TEXT("1. HELLO WORLD"), 14, &wsize);

// draw to vhdc
TextOut(vhdc, 10, 0, L"1. HELLO WORLD", 14);

// switch font back and release the created resource
SelectObject(vhdc, hOldFont);
DeleteObject(hFont);
DeleteObject(vhdc);

Draw text in another bitmap

In many case, you don’t want to directly draw the text to your main bitmap. For example, you might want to calculate the opacity for the overlap, or add other special effect. Here is how to draw text string in any memory buffer:

std::vector<BYTE> buffer;

// precalculate the text string width and height using GetTextExtentPoint
// to allocate the enough bitmap space
buffer.resize( height * width * 4 ); // 32 bits pixel

HDC vhdc = CreateCompatibleDC(hdc);
HBITMAP hbmp = CreateCompatibleBitmap(hdc, width, height);
BITMAPINFO bmpi = { {sizeof(BITMAPINFOHEADER),width,height,1,32,BI_RGB,0,0,0,0,0},{0,0,0,0} };
SelectObject(vhdc, hbmp);
SetBkMode(vhdc, TRANSPARENT);

// draw bitmap
TextOut(vhdc, 0, 0, L"HELLO WORLD", 11);

// get raw data
GetDIBits(vhdc, hbmp, 0, height, buffer.data(), &bmpi, BI_RGB);

// release the created resource
DeleteObject(hbmp);
DeleteObject(vhdc);

Use the local font instead of installed font

Since we use Visual Studio, want might want to add the font file into project resource for the packing. Assume we create a custom resource group named “Binary” and add a font file into it with ID – IDR_FONT001. Don’t forget to include the resource header file if you get a IDR_FONT001 has not been defined error.

// install font in memory
HFONT hMyFont;
HINSTANCE hInstance = ::GetModuleHandle(nullptr);
HRSRC  hFntRes = FindResource(hInstance, MAKEINTRESOURCE(IDR_BINARY1), L"Binary");
if (hFntRes) 
{
    HGLOBAL hFntMem = LoadResource(hInstance, hFntRes);
    if (hFntMem != nullptr) 
    {
        void* FntData = LockResource(hFntMem);
        DWORD nFonts = 0;
        hMyFont = AddFontMemResourceEx(FntData, len, nullptr, &nFonts);
    }
}

// uninstall the font
RemoveFontMemResourceEx(hMyFont);

Something notable, hMyFont is for release the resource. You can use it to draw, but you might want to create another font with different size and configuration. Check the font file to get the font name. One font file could includes multiple fonts.

Some people on Stackoverflow asks how to avoid the conflict of installed font name and the ‘virtual’ font name programmatically. The most straight forward answer is to change the font file header after load font raw data into memory.