Search code examples
windowsencodingfontsgdi

How to automatically choose most suitable font for different language?


I need to get the most suitable font for different language. So I could draw different language's text without using GDI text out API such as TextOut.

Actually, the api TextOut does it.

    HFONT hFont = NULL;
    LOGFONT lg = {0};
    lg.lfHeight = 14;
    lg.lfWeight = FW_NORMAL;
    wcscpy_s(lg.lfFaceName, LF_FACESIZE ,L"Arial");
    hFont = CreateFontIndirect(&lg);
    SelectObject(hdc,hFont);
    TextOut(hdc, 0, 50, L"abc我爱", 5);

Because the Arial font does not support Chinese, TextOut should not be able to draw the Chinese '我爱'. But it does and choose a suitable font, a normal font rather than some kind of art font, for it.

How could I simulate what TextOut does, or is there any other way to figure out the best suitable font for one language under Windows?


Solution

  • Yes, it is possible to do this. Here is the slight modified code snippet from chrome project,

    // Callback to |EnumEnhMetaFile()| to intercept font creation.
    int CALLBACK MetaFileEnumProc(HDC hdc,
                                  HANDLETABLE* table,
                                  CONST ENHMETARECORD* record,
                                  int table_entries,
                                  LPARAM log_font)
    {
        if (record->iType == EMR_EXTCREATEFONTINDIRECTW) {
            const EMREXTCREATEFONTINDIRECTW* create_font_record =
                reinterpret_cast<const EMREXTCREATEFONTINDIRECTW*>(record);
            *reinterpret_cast<LOGFONT*>(log_font) = create_font_record->elfw.elfLogFont;
        }
        return 1;
    }
    
    // Finds a fallback font to use to render the specified |text| with respect to
    // an initial |font|. Returns the resulting font via out param |result|. Returns
    // |true| if a fallback font was found.
    // Adapted from WebKit's |FontCache::GetFontDataForCharacters()|.
    // TODO(asvitkine): This should be moved to font_fallback_win.cc.
    bool ChooseFallbackFont(HDC hdc,
                            HFONT font,
                            const wchar_t* text,
                            int text_length,
                            LOGFONT* result)
    {
        // Use a meta file to intercept the fallback font chosen by Uniscribe.
        HDC meta_file_dc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
        if (!meta_file_dc)
            return false;
    
        if (font)
            SelectObject(meta_file_dc, font);
    
        SCRIPT_STRING_ANALYSIS script_analysis;
        HRESULT hresult =
            ScriptStringAnalyse(meta_file_dc, text, text_length, 0, -1,
            SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
            0, NULL, NULL, NULL, NULL, NULL, &script_analysis);
    
        if (SUCCEEDED(hresult)) {
            hresult = ScriptStringOut(script_analysis, 0, 0, 0, NULL, 0, 0, FALSE);
            ScriptStringFree(&script_analysis);
        }
    
        bool found_fallback = false;
        HENHMETAFILE meta_file = CloseEnhMetaFile(meta_file_dc);
        if (SUCCEEDED(hresult)) {
            LOGFONT log_font;
            log_font.lfFaceName[0] = 0;
            EnumEnhMetaFile(0, meta_file, MetaFileEnumProc, &log_font, NULL);
            if (log_font.lfFaceName[0]) {
                *result = log_font;
                found_fallback = true;
            }
        }
        DeleteEnhMetaFile(meta_file);
    
        return found_fallback;
    }
    

    Sample code:

                    std::wstring arabicStr = L"ششش";
            hdc = BeginPaint(hWnd, &ps);
            LOGFONT logFont = {0};
            wcscpy_s(logFont.lfFaceName, L"微软雅黑");
            HFONT hFont = CreateFontIndirect(&logFont);
            ChooseFallbackFont(hdc, hFont, arabicStr.c_str(), arabicStr.length(), &logFont);
            ATLTRACE(logFont.lfFaceName);
            HFONT hFontNew = CreateFontIndirect(&logFont);
            HFONT hFontOld = (HFONT)SelectObject(hdc, hFontNew);
            wchar_t glyphs[10] = {0}; 
            GCP_RESULTS gcpRet = {0};
            gcpRet.lStructSize = sizeof(gcpRet);
            gcpRet.lpGlyphs = glyphs;
            gcpRet.nGlyphs = 10;
            ATLASSERT(GetCharacterPlacement(hdc, arabicStr.c_str(), arabicStr.length(), GCP_MAXEXTENT, &gcpRet, 
                GCP_DISPLAYZWG | GCP_GLYPHSHAPE | GCP_REORDER ));
            RECT rcClient;
            GetClientRect(hWnd, &rcClient);
            ExtTextOut(hdc, 200, 200, ETO_GLYPH_INDEX | ETO_RTLREADING, &rcClient, gcpRet.lpGlyphs, gcpRet.nGlyphs, NULL);
            SelectObject(hdc, hFontOld);
            DeleteObject(hFontNew);
            DeleteObject(hFont);
            EndPaint(hWnd, &ps);
    

    微软雅黑 is a font which does not support arabic, so we need a fallback font for the arabic. In my box, GDI automatically choose Microsoft Sans Serif for the arabic.