Почему системный язык приложений не-Unicode делает шрифты Unicode с символьной кодировкой неправильно отображаемой?

Я пытаюсь отобразить символы Unicode из шрифта Wingdings (это шрифт Unicode TrueType, поддерживающий только символьную кодировку).
Он правильно отображается в моей системе Win7 / 64 с использованием соответствующих региональных настроек ОС:

  • Форматы: русский
  • Расположение: Россия
  • Язык системы (язык AKA для приложений, не поддерживающих Юникод): английский

Но если я переключаю язык системы на русский, символы Юникода с кодами> 127 отображаются неправильно (заменяются полями).

Мое приложение создано как использующее Unicode Charset в Visual Studio, оно вызывает только функции Unicode Windows API.

Также я отметил, что некоторые приложения Windows также неправильно отображают такие символы с помощью символьных шрифтов (Symbol, 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");

Итак, вопрос в том, почему адские языковые настройки не-Unicode-приложений влияют на Unicode-приложения?

И какой правильный (и самый простой) способ отображения SYMBOL_CHARSET шрифты вне зависимости от локали системы ОС?

4

Решение

Основной причиной проблемы является то, что шрифт 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!

2. Быстрый & чистый

Используйте специальный диапазон Unicode F0 (Частная зона использования) вместо кодов символов 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() не будет работать, возвращая «неподдерживаемые» фиктивные значения для символов> 127.

5

Другие решения

Вот что я думаю происходит:

  1. Шрифт Wingdings не имеет отображений Unicode (таблица cmap?). (Вы можете увидеть это, используя charmap.exe: Character set выпадающий контроль недоступен.)

  2. Я думаю, что для шрифтов без сопоставления Unicode Windows предполагает, что это зависит от параметра «Язык для приложений, не поддерживающих Юникод».

  3. Когда это английский, Windows (вероятно) использует кодовую страницу 1252, и все значения отображаются на себя.

  4. Когда это русский язык, Windows (вероятно) использует кодовую страницу 1251, а затем пытается переназначить их.

  5. '\x81' значение в кодовой странице 1251 отображается на U+0403, который явно не существует в шрифте, так что вы получите коробку. Точно так же, '\xFC' карты для U+044C,

Я предположил, что если вы использовали ExtTextOutW с ETO_GLYPH_INDEX Отметим, что Windows вообще не будет пытаться интерпретировать значения и просто обрабатывать их как глиф-индексы в шрифте. Но это предположение неверно.

Тем не менее, есть еще один флаг под названием ETO_IGNORELANGUAGE, который зарезервирован, но, эмпирически, похоже, решил проблему.

4

По вопросам рекламы [email protected]