检查是否显示Unicode字符或豆腐

时间:2017-12-15 22:42:43

标签: c winapi unicode font-linking

我的问题类似于this one,但稍微前进了一步。

在我的Win32程序中,我有一些菜单按钮,其中包含BMP以上的Unicode字符,例如U + 1F5A4(UTF-16代理对0xD83D 0xDDA4)。 在Windows 10中,系统字体 Segoe UI 没有此字形:它会自动替换为字体 Segoe UI Symbol 中的字形并在按钮中正确显示感谢一个叫做字体链接的过程(或字体后退,我还不清楚) 但是在Windows 7中,字体链接带来的字体既没有这个字形也没有,代理对显示为两个空框▯▯。 Windows XP中使用Tahoma字体也一样。

我想避免使用这些替换框,方法是在分配给按钮之前或之后解析文本,并用一些常见的ASCII字符替换丢失的字形。

我尝试了GetGlyphOutlineScriptGetCMapGetFontUnicodeRangesGetGlyphIndices,但他们不支持代理对。
我还尝试了支持代理对的GetCharacterPlacement和Uniscribe ScriptItemize + ScriptShape,但所有这些功能只搜索HDC的基本字体( Segoe UI ),他们不会搜索最终后备字体( Segoe UI符号),这是提供字形的字体。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink它是我看的地方,但我真的认为系统没有将字体链接到那里。

问题是:我怎么知道系统字体链接是否会产生正确的字形或豆腐盒呢?

修改

我发现某种解决方案正在复制this code并添加最后一个GetCharacterPlacement

#include <usp10.h>

wchar_t *checkGlyphExist( HWND hwnd, wchar_t *sUnicode, wchar_t *sLimited ) {

    // Create metafile
    HDC hdc = GetDC( hwnd );
    HDC metaFileDC = CreateEnhMetaFile( hdc, NULL, NULL, NULL );

    // Select menu font
    NONCLIENTMETRICSW ncm;
    ncm.cbSize = sizeof(ncm);
    SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0 );
    HFONT hFont = CreateFontIndirectW( &(ncm.lfMenuFont) );
    SelectObject( metaFileDC, hFont );
    wprintf( L"%s\n", ncm.lfMenuFont.lfFaceName );  // 'Segoe UI' in Win 10 and 7 (ok)
                                                    // 'Tahoma' in Win XP (ok)

    // Use the meta file to intercept the fallback font chosen by Uniscribe
    SCRIPT_STRING_ANALYSIS ssa;
    ScriptStringAnalyse( metaFileDC, sUnicode, wcslen(sUnicode), 0, -1,
                      SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,  
                      0, NULL, NULL, NULL, NULL, NULL, &ssa );
    ScriptStringFree( &ssa );
    HENHMETAFILE metaFile = CloseEnhMetaFile(metaFileDC);
    LOGFONTW logFont = {0};
    EnumEnhMetaFile( 0, metaFile, metaFileEnumProc, &logFont, NULL );
    DeleteEnhMetaFile( metaFile );
    wprintf( L"%s\n", logFont.lfFaceName );
        // 'Segoe UI Symbol' in Win 10 (ok)
        // 'Microsoft Sans Serif' in Win 7 (wrong, should be 'Segoe UI Symbol')
        // 'Tahoma' in Win XP for characters above 0xFFFF (wrong, should be 'Microsoft Sans Serif', I guess)

    // Get glyph indices for the 'sUnicode' string
    hFont = CreateFontIndirectW( &logFont );
    SelectObject( hdc, hFont );
    GCP_RESULTSW infoStr = {0};
    infoStr.lStructSize = sizeof(GCP_RESULTSW);
    wchar_t tempStr[wcslen(sUnicode)];  
    wcscpy( tempStr, sUnicode );
    infoStr.lpGlyphs = tempStr;
    infoStr.nGlyphs = wcslen(tempStr);
    GetCharacterPlacementW( hdc, tempStr, wcslen(tempStr), 0, &infoStr, GCP_GLYPHSHAPE );
    ReleaseDC( hwnd, hdc );

    // Return one string
    if( infoStr.lpGlyphs[0] == 3 || // for Windows 7 and 10
        infoStr.lpGlyphs[0] == 0 )  // for Windows XP
        return sLimited;
    else
        return sUnicode;
}

// Callback function to intercept font creation
int CALLBACK metaFileEnumProc( HDC hdc, HANDLETABLE *table, const ENHMETARECORD *record,
                            int tableEntries, LPARAM logFont ) {
    if( record->iType == EMR_EXTCREATEFONTINDIRECTW ) {
        const EMREXTCREATEFONTINDIRECTW* fontRecord = (const EMREXTCREATEFONTINDIRECTW *)record;
        *(LOGFONTW *)logFont = fontRecord->elfw.elfLogFont;
    }
    return 1;
}

您可以使用checkGlyphExist( hWnd, L"", L"<3" );

进行调用

我在Windows 10和两台虚拟机上进行了测试:Windows 7 Professional,Windows XP SP2 它工作得很好,但是当基本字体中缺少字形时EnumEnhMetaFile检索到的后备字体仍然存在两个问题:

    Windows 7中的
  • 始终是 Microsoft Sans Serif ,但真正的后备字体应为 Segoe UI Symbol
  • Windows XP中的
  • Tahoma 而不是 Microsoft Sans Serif ,但仅针对代理对字符(对于BMP字符是 Microsoft Sans Serif ,是的,我猜是正确的。

有人可以帮我解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

首先,您必须确保在Win7和Win10上使用相同的API。我认为较低级别的gdi32 API不应该支持代理对,而在每个级别上都有较新的DirectWrite。接下来要记住的是字体回退(字体链接是另一回事)数据因发行版而异,并且它不是用户可以访问的内容,并且它不可修改。

首先要检查Win7是否首先在U + 1F5A4上为符号提供字体,但它可能仅在以后的版本中引入。

基本上,如果你使用系统渲染功能,无论是旧的还是更新的,你不应该在大多数时候控制回退,如果它不适合你,通常意味着它不起作用。 DirectWrite允许自定义回退列表,您可以在其中明确地将U + 1F5A4分配给任何支持它的字体,包括可以与应用程序捆绑在一起的自定义字体。

如果您需要更详细的答案,则需要显示一些不适合您的摘录摘录。

答案 1 :(得分:-3)

我认为高和低16位字是为代理对定义的。您应该能够通过检查每个16位字的值范围来识别代理对。

对于高字,它应该在0xd800到0xdbff的范围内 对于低位字,它应该在0xdc00到0xdfff

的范围内

如果有任何两对&#34;字符&#34;符合这个标准,他们是代理对。

有关详细信息,请参阅有关UTF-16的维基百科文章。