2

我需要为不同的语言获得最合适的字体。所以我可以在不使用 GDI 文本输出 API(如 TextOut)的情况下绘制不同语言的文本。

实际上,api TextOut 做到了。

    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);

因为Arial字体不支持中文,所以TextOut应该不能画出中文的“我爱”。但它确实为它选择了一种合适的字体,一种普通的字体,而不是某种艺术字体。

我如何模拟 TextOut 的功能,或者有没有其他方法可以在 Windows 下找出最适合一种语言的字体?

4

2 回答 2

2

是的,可以这样做。这是来自 chrome 项目的稍微修改的代码片段,

// 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;
}

示例代码:

                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);

微软雅黑是一种不支持阿拉伯语的字体,所以我们需要一个用于阿拉伯语的备用字体。在我的框中,GDI 自动选择Microsoft Sans Serif阿拉伯语。

于 2013-06-14T09:12:01.423 回答
2

我不得不这样做。这是可能的,但工作量很大。

Windows 实际上使用了多种方案:Uniscribe,使用字体回退,它依赖于哪些字体覆盖哪些脚本的内部知识。更高级别的 API,如 GDI,使用字体链接,它依赖于隐藏在注册表中的字体列表。

我正在扩展的代码依赖于 Uniscribe 将文本分解为相同脚本的运行。然后它检查选定的字体,看它是否可以覆盖给定运行中的所有字形。

如果不能,则检查注册表中的字体链接字段。如果这些不存在,或者如果没有首选字体列表,它会枚举所有字体以查找覆盖所需脚本的候选字体(缓存位掩码,编码为遇到的每种字体覆盖哪些脚本)。

然后根据运行的实际覆盖范围(仍然缺少多少字形)对这些候选人进行评分(排名)。从排名靠前的候选中,它选择了一种与首选字体“最相似”的字体。我认为那部分是基于字体的 PANOSE 编号,但已经有一段时间了,所以我记不太清了。可能只是一些启发式方法查看了字体粗细和类似参数。

还有一个 hack 试图最小化字体切换的数量,理论上最好使用一种覆盖整个文本的备用字体而不是多次切换字体。我记得,这解决了一些问题,即 CJK 字符串会从中文字体中选择一些字形,而从日文字体中选择其他字形。对于本地读者来说,如果可能的话,最好将整个字符串放在一种字体中,即使首选字体是另一种字体。

于 2017-04-12T20:04:57.797 回答