Search code examples
c++windowswinapifontsdirectwrite

How to draw text with the default UI font in DirectWrite?


The CreateTextFormat method expects a fontFamilyName parameter. How do I create an IDWriteTextFormat that uses the default UI font?


Solution

  • Please note, that all code here is done without any checks (too many methods here return HRESULT, would blow this example up!).

    For acquiring the system wide font you should use this:

    (This is from another stackoverflow question!)

    NONCLIENTMETRICS ncm;
    ncm.cbSize = sizeof(ncm);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
    HFONT hFont = CreateFontIndirect(&(ncm.lfMessageFont)); //
    

    for paint now use this:

    HDC hdc = BeginPaint(...); //Creates a device context
    SelectObject(hdc, hFont);
    //Your font is now set for the current device context
    //do something
    
    DeleteObject(hFont); //Don't forget to do this at the end!
    

    A bit changed from this question!

    This solution is really raw and in my opinion ugly.

    Alternative solution do get the IDWriteFont (looks ugly but is fine):

    //just the same as above except the hfont, instead use
    NONCLIENTMETRICS ncm;
    
    ncm.cbSize = sizeof(ncm);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
    
    IDWriteFactory *dwriteFactory_;
    DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(IDWriteFactory),
        reinterpret_cast<IUnknown**>(&dwriteFactory_)
    );
    
    IDWriteGdiInterop* gdiInterop = NULL;
    dwriteFactory_->GetGdiInterop(&gdiInterop);
    
    IDWriteFont* sys_font = nullptr;
    gdiInterop->CreateFontFromLOGFONT(&ncm.lfMessageFont, &sys_font); //Now we have it!
    
    //The text format can now be aquired like this
    //We need the font family of our font
    IDWriteFontFamily* family = nullptr;
    sys_font->GetFontFamily(&family);
    
    //Now we have to get the "localized" name of our family
    IDWriteLocalizedStrings* font_family_name = nullptr;
    family->GetFamilyNames(&font_family_name);
    UINT32 index = 0;
    UINT32 length = 0;
    BOOL exists = false;
    font_family_name->FindLocaleName(L"en-us", &index, &exists);
    font_family_name->GetStringLength(index, &length);
    wchar_t* name = new wchar_t[length + 1];
    font_family_name->GetString(index, name, length + 1);
    wprintf(L"%s\n", name);
    
    //Some user defined stuff
    DWRITE_FONT_WEIGHT font_weight = DWRITE_FONT_WEIGHT_BLACK;
    DWRITE_FONT_STYLE font_style = DWRITE_FONT_STYLE_ITALIC;
    DWRITE_FONT_STRETCH font_stretch = DWRITE_FONT_STRETCH_EXPANDED;
    
    IDWriteTextFormat* text_format = nullptr;
    dwriteFactory_->CreateTextFormat(name, nullptr, font_weight, font_style, font_stretch, 10.0, L"en-us", &text_format);
    

    Even without checks, code runs on my computer without any problems and gives me the same result as the first solution (Windows 10, Font family name is Segoe UI).

    Sources: General Microsoft DirectWrite API documentation

    CreateIndirectFont Documentation

    How to enumerate font families, Microsoft documentation