为什么字符`0x62A`使用"Calibri"字体打印,而字符`0x660E`不是?

3
Calibri字体中没有字符0x062A0x660E,但是第一个字符使用其他字体打印。然而,字符0x660E显示为无效字符代码。为什么TextOut()不替换字体以打印这个字符,就像它用字符代码0x062A做的那样?
如果我用Arial字体替换Calibri字体,结果是一样的。
此外,我想引起你的注意,可以在这里找到这句话:http://msdn.microsoft.com/en-us/goglobal/bb688134.aspx:“Windows核心字体(Times New Roman,Courier New,Arial,Microsoft Sans Serif和Tahoma)包含拉丁文,希伯来文,阿拉伯文,希腊文和西里尔文,但不包含东亚文字。它们链接到包含东亚文字的字体。”好吧,我已经尝试了所有这些字体的代码,结果完全相同:字符0x660E被渲染为无效。
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
    static HFONT s_hFont;   

    switch( message )
    {
        case WM_CREATE:
        {
            LOGFONT lf;
            memset(&lf, 0, sizeof(LOGFONT));
            lf.lfHeight = -MulDiv(20, 96, 72);
            lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
            wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Calibri");

            if( !(s_hFont = CreateFontIndirect(&lf)) ) return -1;
        }
        break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            s_hFont = (HFONT)SelectObject(ps.hdc, s_hFont);

            wchar_t wchar1 = 0x062A;                //  Arabic character
            TextOut(ps.hdc, 10, 10, &wchar1, 1);

            wchar_t wchar2 = 0x660E;                //  Japanese character
            TextOut(ps.hdc, 10, 50, &wchar2, 1);

            s_hFont = (HFONT)SelectObject(ps.hdc, s_hFont);
            EndPaint(hwnd, &ps);
        }
        break;


        case WM_DESTROY:
        DeleteObject(s_hFont);
        PostQuitMessage(0);
        break;

        default:

        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0L ;
}

输出结果的截屏

在此输入图片描述


每种字体都有自己表示字符集映射到的字符的方式,大多数情况下是 UTF-8。如果显示为无效,则该字体可能没有该符号的字符风格。 - Alex W
2
也许备用字体有 0x062A 的字形,但没有 0x660E 的字形。 - Benjamin Lindley
那么问题是TextOut()的字体回退如何工作,它使用哪些字体以及如何查询和更改该字体列表。 - bames53
@bames53 这基本上就是我想要理解的内容。 - WaldB
0x062A 是“阿拉伯字母泰”,0x660E 是CJK(中日韩)表意文字。UnicodeData.txt 表明从 0x4E000x9FCC 的范围都是CJK表意文字;它没有提供比这更多的信息。一个字体没有为该范围内的所有20,941个字符提供表示形式并不奇怪。 - Keith Thompson
显示剩余2条评论
2个回答

3
我怀疑你不会得到很好的答案,因为字体链接并没有被很好地记录,而且没有人真正关心它(我将解释原因)。Michael Kaplan在这个主题上写了一系列简短的文章:Font substitution and linking parts 1, 23。还有一个MSDN文章。正如Michael Kaplan所指出的,字体链接取决于当前系统语言环境,并且也可以在任何系统上进行编辑。您不能依赖它。
因此,任何关注多语言文本处理的应用程序都将使用MLangUniscribe或其他库来确保良好的结果。
大多数应用程序不需要这种程度的国际支持。通常处理用户自己的脚本就足够了,而Windows大部分已经为您完成了这个任务。例如,我的日本用户想输入日语文本;他们并不在意乌尔都语在他们的电脑上是否可用。对于巴基斯坦的用户也是如此。
总之,如果您不关心支持用户脚本以外的内容,请使用Windows默认设置。如果您关心,请使用文本输出库。不要依赖字体链接。

谢谢您的回复。我正在阅读您提到的文章,一旦我完成阅读,我会尽快回来。 - WaldB
考虑到引用的MSDN文章中的以下句子:“如果在您的设备上启用了字体链接,则可以通过枚举注册表键HKEY_LOCAL_MACHINE-\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink的子键来检查映射到基本字体的链接字体的映射。”(续) - WaldB
我不知道如何验证我的计算机是否启用了字体链接。但似乎是这样,因为阿拉伯字符(不包含在Calibri字体中)可以在我的代码中打印出来。(续) - WaldB
通过查看SystemLink注册表键,我发现Tahoma字体与以下字体相关联:MS Gothic、MS UI Gothic、MingLiU、PMingLiU等等...而且所有这些字体都包含字符0x660E。但是当我使用Tahoma字体时,它并没有被打印出来。这是什么原因? - WaldB

0
Windows 核心字体(Times New Roman,Courier New,Arial,Microsoft Sans Serif 和 Tahoma)包含拉丁文、希伯来文、阿拉伯文、希腊文和西里尔文字符,但不包含东亚文字。它们链接到具有CJK支持的字体。

实际上,它们确实链接到具有CJK支持的字体,但这并不意味着您已安装了这些字体。您没有指定Windows的哪个版本。在旧版Windows(例如XP和可能的Vista)中,如果安装了“西方”版本,则除非显式要求Windows安装必要的字体和表格,否则无法获得良好的CJK字体支持。
有一个控制面板应用程序(区域设置?)可以让您指示需要更完整的国际字符支持。它将需要您的安装媒体,并安装更多的字体和更新与这些额外字符相关的表。它甚至可能会更新字体链接注册表键。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接