Есть старючая программа (не скажу какая), использующая отчеты Crystal Reports (CR).
Была задача исправить в имеющемся .rpt файле шаблона отчета определенные надписи.
Изначально, версия CR, в которой был написан отчет, известна не была, а пересохраненый отчет в более поздней версии CR уже программой не читался.
Экспериментальным, мать его, путем версия Crystal Reports была найдена – это 8 (а точнее 8.5).
P.S.
Для таких же нубов, как был я, поясняю политику наименования версий CR:
- До версии 10 – наименования версий арабскими цифрами.
- Версия 11 – римской цифрой XI.
- Затем – наименования в виде года выпуска: 2008, 2011, 2013.
Но при открытии отчета в CR 8.5, некоторые надписи на русском языке, у которых установлен шрифт TenseC, отображались квадратиками и вопросиками.
И тут пришлось погрузиться во вторую половину 90-х годов, во времена Windows 9x, и столкнуться с проблемой, до боли знакомой олдфагам, заставшим начало перехода Windows от однобайтных кодировок к Unicode.
Мат. часть
Полезные ссылки:
Языки, шрифты и кодировки как инкарнация Unicode – подробные статьи по этой теме
Кодировка текста ASCII (Windows 1251, CP866, KOI8-R) и Юникод (UTF 8, 16, 32) — как исправить проблему с кракозябрами” – большая подробная статья
Шрифты и Microsoft Windows – познавательная статья о кодировках и шрифтах в ранних версиях Windows (материал использовался для этой статьи)
Windows NT/2000/XP. Настройка. Шрифты – советы по использованию шрифтов на старых Windows
Наши представления о возможностях использования дополнительных символов
в русских шрифтах в MS Windows 95/98 – описание проблемы перехода от однобайтовых кодовых страниц к Unicode
Кодировка. 8-битные и 16-битные приложения – хорошая статья простыми словами о сложных в данный момент понятиях кодирования текста, история кодировок
Суть проблемы со шрифтами TrueType (.ttf) заключается в том, что в некоторых кириллических шрифтах Unicode-индексы символов, изображающих русские буквы, соответствуют латинской кодовой таблице CP1252, а не кириллической CP1251.
На первый взгляд, для нормальной работы шрифтов как в программах, поддерживающих Unicode, так и в не-Unicode программах, необходимо и достаточно:
- разместить символы в кодовом пространстве шрифта в соответствии с требованиями кириллической кодовой таблицы CP1251;
-
дать каждому символу корректный Unicode-индекс согласно CP1251;
- (также) убедиться, что в шрифте присутствует малая буква «ё» с Unicode-индексом 0451, иначе шрифт может быть не распознан как кириллический.
Казалось бы, не-Unicode программам не важно, какой Unicode-индекс имеет русская буква «А», лишь бы она стояла в кодовой позиции 192. А Unicode программам, наоборот, все равно, в какой кодовой позиции стоит буква «А», лишь бы у нее был Unicode-индекс 0410. Размещение же символов в соответствии с предписаниями кодовой таблицы 1251 и обеспечивает нам нахождение символа с Unicode-индексом 0410 в кодовой позиции 192.
Кодовая страница 1251 (условно):
Кодовая позиция (dex) | Unicode-индекс (hex) |
192 | 0x0410 |
Шрифт:
Unicode-индекс (hex) | Символ (глиф) |
0x0410 | А |
Однако при этом обнаруживается неожиданный обратный эффект: русские буквы перестают отображаться в тех программах, с которыми ранее не было никаких проблем, если они не поддерживают работу с Unicode.
Объясняется это следующим. В реализации формата TrueType для MS Windows предусмотрена только линейная таблица кодирования шрифтов cmap, задающая соответствие между Unicode-индексами символов и их изображениями (глифами) в порядке возрастания Unicode-индексов.
Каким же образом осуществляется в Windows доступ к символам национальных алфавитов?
Определения поддерживаемых системой наборов национальных символов (т. е. кодовых страниц) содержатся в ресурсах gdi.exe, а также (для 32-битных Unicode-программ) в файлах национальной поддержки – NLS. Именно здесь и указывается, какому знакоместу из 256 возможных должны соответствовать Unicode-индексы нужных символов. Подобные таблицы соответствий принято называть «сечениями шрифтов».
Драйвер TrueType в GDI способен определить, какие конкретно кодовые страницы реально присутствуют в том или ином шрифте TTF, используя для этого специальный алгоритм.
В правильном Unicode-шрифте для каждого используемого национального набора символов (кроме исходного латинского) должно быть определено логическое сечение шрифта с использованием стандартных суффиксов.
В Unicode-программах такой шрифт будет виден только с исходным именем – никакие логические сечения в шрифтовых меню не появляются. В не-Unicode программах этот же шрифт будет появляться всеми имеющимися его сечениями. И если в Unicode-программах при работе с подобными шрифтами мы выбираем шрифт по его основному имени (например, Arial), то при переходе в не-Unicode программы мы должны постоянно помнить, что в них мы должны использовать шрифт не Arial, но Arial Cyr. Неудобства этого очевидны.
Остается только каким-то образом объяснить системе, что нас интересует кириллическое, а не какое-либо другое сечение шрифта. Для этого программа должна запросить не просто данный шрифт (например, “Arial”), но “Arial + Cyrillic script”. В программах с поддержкой Unicode для этого достаточно просто переключиться в русский регистр с помощью встроенного в Windows переключателя раскладки клавиатуры. На не-Unicode программы это не действует: здесь следует использовать перешедший из Windows 3.1 механизм создания логических шрифтов с использованием стандартных суффиксов шрифтов. Для этого необходимо для каждого Unicode-шрифта:
* при работе под Windows 9x в секции [FontSubstitutes] файла Win.ini записать строки вида:
Font <суффикс>,Charset=Font,Charset
* при работе под Windows NT в реестре по адресу
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes
записать строки вида:
"Font <суффикс>,Charset"="Font,Charset"
Таким образом, для каждого используемого физического шрифта создается несколько логических шрифтов, отличающихся друг от друга суффиксами в имени – для каждого кодовой страницы Charset свой логический шрифт.
Например, если мы хотим использовать кириллическое и греческое сечение шрифта в не-Unicode программах, мы должны создать два логических шрифта следующим образом:
Arial Cyr,204=Arial,204
Arial Greek,161=Arial,161
, где:
161 – это код греческой кодовой страницы 1253 в Windows
204 – это код кириллической кодовой страницы 1251 в Windows
Charsets, или наборы символов
Код |
Код |
Наименование |
Кодовая |
Суффикс |
Win 3.1 |
Win 9x |
---|---|---|---|---|---|---|
0x00 |
000 |
Western (Latin 1) |
|
+ |
+* |
|
0x01 |
001 |
Unknown |
|
|
0 |
0 |
0x02 |
002 |
Symbol |
|
|
+ |
+ |
0x4D |
077 |
Mac |
|
Mac (?) |
0 |
+* |
0x80 |
128 |
Japanese |
CP932 |
SJis |
– |
0 |
0x81 |
129 |
Hangeul |
CP949 |
Hang |
– |
0 |
0x82 |
130 |
Hangeul (Johab) |
CP1361 |
JOHAB |
– |
0 |
0x86 |
134 |
Chinese_GB2312 |
CP936 |
GB2312 |
– |
0 |
0x88 |
136 |
Chinese_BIG5 |
CP950 |
ChiBIG |
– |
0 |
0xA1 |
161 |
Greek |
Greek |
– |
+ |
|
0xA2 |
162 |
Turkish (Latin 5) |
Tur |
+ |
+ |
|
0xA3 |
163 |
Vietnamese |
Viet or Vietnamese |
– |
– |
|
0xB1 |
177 |
Hebrew |
(Hebrew) |
– |
+ |
|
0xB2 |
178 |
Arabic |
(Arabic) |
– |
+ |
|
0xBA |
186 |
Baltic |
Baltic |
+ |
+ |
|
0xCC |
204 |
Cyrillic |
Cyr |
+ |
+ |
|
0xDE |
222 |
Thai |
CP874 |
Thai |
– |
– |
0xEE |
238 |
Central (Eastern) |
CE |
+ |
+ |
|
0xFF |
255 |
OEM/DOS |
|
|
0 |
+* / 0 |
Строго говоря, от нас не требуется давать логическому шрифту имя, совпадающее с именем физического шрифта, да и суффиксы в имени не обязательны. Однако рекомендуется соблюдать это правило по причине следующей недокументированной особенности Word’а 95: при переключении клавиатуры (и тем самым языка) он автоматически переключается на шрифт того же названия, добавляя к нему суффикс, соответствующий выбранному языку. И соответствие это определяется как раз жестко зашитыми в Word 95 допустимыми суффиксами национальных версий шрифтов: <пусто>, Cyr, CE, Baltic, Greek и Tur. Кроме того, таким образом мы обеспечиваем совместимость создаваемых нами документов с Windows 3.1x.
А теперь решение конкретной проблемы
Программа Crystal Reports 8.5, для отображения символов, написанных шрифтом с именем “TenseC” (т.е. без задания суффикса), обращается к латинской кодовой странице шрифта (TenseC,0).
Соответственно, происходит обращение к Unicode-кодам русских символов, которых в этой (латинской) кодовой странице нет.
Поэтому, нужно “перенаправить” программу на кириллическое сечение этого шрифта (TenseC,204).
Добавим в ветку
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes
параметр (строку):
"TenseC, 0=TenseC,204"