Я пытаюсь отобразить символы Unicode из шрифта Wingdings (это шрифт Unicode TrueType, поддерживающий только символьную кодировку).
Он правильно отображается в моей системе Win7 / 64 с использованием соответствующих региональных настроек ОС:
Но если я переключаю язык системы на русский, символы Юникода с кодами> 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
шрифты вне зависимости от локали системы ОС?
Основной причиной проблемы является то, что шрифт 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.
Вот что я думаю происходит:
Шрифт Wingdings не имеет отображений Unicode (таблица cmap?). (Вы можете увидеть это, используя charmap.exe: Character set
выпадающий контроль недоступен.)
Я думаю, что для шрифтов без сопоставления Unicode Windows предполагает, что это зависит от параметра «Язык для приложений, не поддерживающих Юникод».
Когда это английский, Windows (вероятно) использует кодовую страницу 1252, и все значения отображаются на себя.
Когда это русский язык, Windows (вероятно) использует кодовую страницу 1251, а затем пытается переназначить их.
'\x81'
значение в кодовой странице 1251 отображается на U+0403
, который явно не существует в шрифте, так что вы получите коробку. Точно так же, '\xFC'
карты для U+044C
,
Я предположил, что если вы использовали ExtTextOutW
с ETO_GLYPH_INDEX
Отметим, что Windows вообще не будет пытаться интерпретировать значения и просто обрабатывать их как глиф-индексы в шрифте. Но это предположение неверно.
Тем не менее, есть еще один флаг под названием ETO_IGNORELANGUAGE
, который зарезервирован, но, эмпирически, похоже, решил проблему.