为什么非Unicode应用程序系统区域设置使符号字符集的Unicode字体显示不正确?

Ros*_*ost 4 c++ unicode winapi fonts non-unicode

我正在尝试显示Wingdings字体的Unicode字符(它的Unicode TrueType字体仅支持符号字符集).它使用相应的区域操作系统设置在我的Win7/64系统上正确显示:

  • 格式:俄语
  • 地点:俄罗斯
  • 系统区域设置(非Unicode应用程序的AKA语言):英语

但是,如果我将系统区域设置切换为俄语,则代码> 127的Unicode字符显示不正确(替换为框).

我的应用程序是在Visual Studio中使用Unicode Charset创建的,它只调用Unicode Windows API函数.

另外我注意到有几个Windows应用程序也会错误地使用符号字体(符号,Wingdings,Webdings等)显示这些字符,例如Notepad,Beyond Compare 3.但是WordPad和MS Office应用程序不会受到影响.

这是最小的代码片段(为简洁起见,跳过了资源清理):

LOGFONTW lf = { 0 };
lf.lfCharSet = SYMBOL_CHARSET;
lf.lfHeight = 50;
wcscpy_s(lf.lfFaceName, L"Wingdings");

HFONT f = CreateFontIndirectW(&lf);

SelectObject(hdc, f);

// First two chars displayed OK, 3rd and 4th aren't (replaced with boxes) if
// Non-Unicode apps language is NOT English.
TextOutW(hdc, 10, 10, L"\x7d\x7e\x81\xfc");
Run Code Online (Sandbox Code Playgroud)

所以问题是:为什么地狱非Unicode应用程序语言设置会影响Unicode应用程序?

什么是显示SYMBOL_CHARSET字体而不依赖于OS系统区域设置的正确(也是最简单)方法?

Ros*_*ost 5

问题的根本原因是Wingdings字体实际上是非Unicode字体.它部分支持Unicode,因此仍然可以正确显示某些符号.请参阅@Adrian McCarthy的答案,详细了解它是如何在幕后工作的.

另请参阅此处的更多信息:http://www.fileformat.info/info/unicode/font/wingdings 和此处:http://www.alanwood.net/demos/wingdings.html

那么我们可以做些什么来避免这些问题呢?我找到了几种方法:

1.快速而肮脏

回退到ANSI版本的API,如@ user1793036建议:

TextOutA(hdc, 10, 10, "\x7d\x7e\x81\xfc"); // Displayed correctly!
Run Code Online (Sandbox Code Playgroud)

2.快速清洁

使用特殊的Unicode范围F0(专用区域)而不是ASCII字符代码.它受到Wingdings的支持:

TextOutW(hdc, 10, 10, L"\xf07d\xf07e\xf081\xf0fc"); // Displayed correctly!
Run Code Online (Sandbox Code Playgroud)

要探索字体实际支持哪些Unicode符号,可以使用某些字体查看器,例如dp4 Font Viewer

3.缓慢干净,但通用

但是,如果您不知道必须显示哪些字符以及实际使用哪种字体该怎么办?这是最通用的解决方案 - 通过字形绘制文本以避免任何不需要的翻译:

void TextOutByGlyphs(HDC hdc, int x, int y, const CStringW& text)
{
   CStringW glyphs;

   GCP_RESULTSW gcpRes = {0};
   gcpRes.lStructSize = sizeof(GCP_RESULTS);
   gcpRes.lpGlyphs = glyphs.GetBuffer(text.GetLength());
   gcpRes.nGlyphs = text.GetLength();

   const DWORD flags = GetFontLanguageInfo(hdc) & FLI_MASK;
   GetCharacterPlacementW(hdc, text.GetString(), text.GetLength(), 0,
     &gcpRes, flags);
   glyphs.ReleaseBuffer(gcpRes.nGlyphs);

   ExtTextOutW(hdc, x, y, ETO_GLYPH_INDEX, NULL, glyphs.GetString(),
      glyphs.GetLength(), NULL);
}

TextOutByGlyphs(hdc, 10, 10, L"\x7d\x7e\x81\xfc"); // Displayed correctly!
Run Code Online (Sandbox Code Playgroud)

注意GetCharacterPlacementW()功能用法.由于某些未知原因,类似函数GetGlyphIndicesW()无法返回字符> 127的"不支持"虚拟值.

  • 做得很好.这将在未来帮助很多人. (2认同)