4

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

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

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

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

我还注意到,一些 Windows 应用程序也使用符号字体(符号、Wingdings、Webdings 等)错误地显示此类字符,例如记事本、Beyond Compare 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 应用程序?

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

4

2 回答 2

5

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

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

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

1. 又快又脏

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

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

2.快速清洁

使用特殊的 Unicode 范围F0私人使用区域)而不是 ASCII 字符代码。它由 Wingdings 支持:

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

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

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 > 127 的“不受支持”的虚拟值。

于 2014-01-29T17:04:58.137 回答
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

我假设如果您ExtTextOutWETO_GLYPH_INDEX标志一起使用,Windows 根本不会尝试解释这些值,而只是将它们视为字体的字形索引。但这种假设是错误的。

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

于 2014-01-27T18:57:46.843 回答