Question

I'm trying to display Unicode chars from Wingdings font (it's Unicode TrueType font supporting symbol charset only). It's displayed correctly on my Win7/64 system using corresponding regional OS settings:

  • Formats: Russian
  • Location: Russia
  • System locale (AKA Language for Non-Unicode applications): English

But if I switch System locale to Russian, Unicode characters with codes > 127 are displayed incorrectly (replaced with boxes).

My application is created as using Unicode Charset in Visual Studio, it calls only Unicode Windows API functions.

Also I noted that several Windows apps also display such chars incorrectly with symbol fonts (Symbol, Wingdings, Webdings etc), e.g. Notepad, Beyond Compare 3. But WordPad and MS Office apps aren't affected.

Here is minimal code snippet (resources cleanup skipped for brevity):

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");

So the question is: why the hell Non-Unicode apps language setting affects Unicode apps?

And what is the correct (and most simple) way to display SYMBOL_CHARSET fonts without dependency to OS system locale?

Was it helpful?

Solution

The root cause of the problem is that Wingdings font is actually non-Unicode font. It supports Unicode partially, so some symbols are still displayed correctly. See @Adrian McCarthy's answer for details about how it's probably works under the hood.

Also see more info here: http://www.fileformat.info/info/unicode/font/wingdings and here: http://www.alanwood.net/demos/wingdings.html

So what can we do to avoid such problems? I found several ways:

1. Quick & dirty

Fall back to ANSI version of API, as @user1793036 suggested:

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

2. Quick & clean

Use special Unicode range F0 (Private Use Area) instead of ASCII character codes. It's supported by Wingdings:

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

To explore which Unicode symbols are actually supported by font some font viewer can be used, e.g. dp4 Font Viewer

3. Slow & clean, but generic

But what to do if you don't know which characters you have to display and which font actually will be used? Here is most universal solution - draw text by glyphs to avoid any undesired translations:

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!

Note GetCharacterPlacementW() function usage. For some unknown reason similar function GetGlyphIndicesW() would not work returning 'unsupported' dummy values for chars > 127.

OTHER TIPS

Here's what I think is happening:

  1. The Wingdings font doesn't have Unicode mappings (a cmap table?). (You can see this by using charmap.exe: the Character set drop down control is grayed out.)

  2. For fonts without Unicode mappings, I think Windows assumes that it depends on the "Language for Non-Unicode applications" setting.

  3. When that's English, Windows (probably) uses code page 1252, and all the values map to themselves.

  4. When that's Russian, Windows (probably) uses code page 1251, and then tries to remap them.

  5. The '\x81' value in code page 1251 maps to U+0403, which obviously doesn't exist in the font, so you get a box. Similarly the, '\xFC' maps to U+044C.

I assumed that if you used ExtTextOutW with the ETO_GLYPH_INDEX flag, Windows wouldn't try to interpret the values at all and just treat them as glyph indexes into the font. But that assumption is wrong.

However, there is another flag called ETO_IGNORELANGUAGE, which is reserved, but, empirically, it seems to solve the problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top