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

时间:2014-01-27 14:14:00

标签: c++ winapi unicode fonts non-unicode

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

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

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

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

另外我注意到有几个Windows应用程序也会错误地使用符号字体(符号,Wingdings,Webdings等)显示这样的字符,例如记事本,超越比较3.但写字板和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");

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

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

2 个答案:

答案 0 :(得分:5)

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

另请参阅此处的详细信息:http://www.fileformat.info/info/unicode/font/wingdings 在这里:http://www.alanwood.net/demos/wingdings.html

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

<强> 1。快速&amp;脏

回到ANSI版API,建议 @ user1793036

TextOutA(hdc, 10, 10, "\x7d\x7e\x81\xfc"); // Displayed correctly!

<强> 2。快速&amp;清洁

使用特殊的Unicode范围F0Private Use Area)而不是ASCII字符代码。它得到了Wingdings的支持:

TextOutW(hdc, 10, 10, L"\xf07d\xf07e\xf081\xf0fc"); // Displayed correctly!

为了探索字体实际支持哪些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!

注意GetCharacterPlacementW()函数用法。由于某些未知原因,类似的函数GetGlyphIndicesW()无法为chars&gt;返回'notpported'虚拟值。 127。

答案 1 :(得分:4)

以下是我的想法:

  1. Wingdings字体没有Unicode映射(cmap表?)。 (您可以使用charmap.exe查看此信息:Character set下拉控件显示为灰色。)

  2. 对于没有Unicode映射的字体,我认为Windows假定它依赖于“非Unicode应用程序的语言”设置。

  3. 当这是英文时,Windows(可能)使用代码页1252,所有值都映射到自己。

  4. 当这是俄语时,Windows(可能)使用代码页1251,然后尝试重新映射它们。

  5. 代码页1251中的'\x81'值映射到U+0403,这显然不存在于字体中,因此您会得到一个框。同样,'\xFC'映射到U+044C

  6. 我认为如果你使用带有ExtTextOutW标志的ETO_GLYPH_INDEX,Windows就不会尝试解释这些值,只是将它们视为字体中的字形索引。但这种假设是错误的。

    但是,还有另一个名为ETO_IGNORELANGUAGE的标志,它是保留的,但从经验上看,似乎可以解决问题。