Info21 писал(а):
Но нетривиально то, что тут нужен выход на таблицы kerning pairs в шрифтах.
Ничего сложного в парах кернинга нет.
Немного теории (для понимания)Кернинг — подрезка литер при т.н. высокой печати, когда слова составлялись из отдельных букв и была необходимость изменять «
полуапроши» для удобства чтения и равномерного набора. Понятие «кернинговая пара» происходит от того, что значение кернинга (как правило, отрицательное), задается для пары символов (или глифов), например A-V, A-W, L-v и т.д. Кернинг задается для разных кеглей (размер шрифта измеряется в пунктах и называется кегль шрифта) отдельно.
Рядом с кернингом стоит упомянуть о существовании
лигатур.
При цифровом наборе понятие осталось.
Далее речь пойдет только о Windows.
Информация о кернинге, равно как и величина полуапрошей, содержится в файле шрифта и доступна с помощью вызова соответствующих функций. В терминах WinAPI полуапроши и ширина глифов называются ABC-метриками.
Для простого графического вывода информация о кернинге не используется . Например, TextOut и ExtTextOut (последняя применяется в ББ) в большинстве случаев информацию о кернинге не используют. Берется только ширина каждого символа и на основе этой ширины строится строка, выводимая на контекст устройства.
Вывод текста в ББПримерно так и организован вывод текста в ББ. Создается таблица шрифтов (кэш шрифтов), для каждого шрифта в кэше создается массив, в который записывается ширина каждого используемого символа и затем эта информация используется для подготовки текста к выводу. Подробности в HostPorts.DrawString и HostPorts.DrawSString.
Я понимаю, почему Оминк приняли такое решение. ББ разрабатывался для использования в Windows 3.1, затем в 95/98. Вариантов вывода текста было, по сути, только два — простой или по отдельным символам (глифам). Второй хоть и визуально дает более привлекательный результат, но гораздо более трудоемок.
Использование кернингаДля вывода текста с использованием правильного (т.е. заданного разработчиком шрифта) кернинга нужно обращаться к таблицам кернинга. Это можно сделать явно с помощью функции GetKerningPairs, которая возвращает массив пар кернинга для выбранного в контексте устройства шрифта. Проблема в том, что для использования информации о кернинге для вывода в этом случае придется использовать посимвольный (а точнее поглифовый) вывод, что является весьма нетривиальной задачей.
В Windows 2000 появилась функция, которая сразу организует «правильный» вывод текста. Точнее не вывод, а подготовку к выводу. Функция эта — GetCharacterPlacement. Она получает на вход указатель на структуру данных, в которой есть указатели на несколько массивов. В этих массивах после вызова функции содержится следующая информация:
- выводимая строка в измененном виде (см. ниже);
- порядок следования глифов;
- начальные координаты каждого глифа;
- координаты каретки возле каждого глифа;
- список глифов.
После вызова функции указанная структура содержит информацию о порядке следования глифов, которые соответствуют символам входной строки. Глифы располагаются с учетом замены символов лигатурами, с учетом метрик шрифта и с учетом информации о кернинге. Кроме того, учитывается еще и хинтовка шрифтов (т.е. используются квадратичные или кубические сплайны). Все это регулируется флагами на входе в функцию GetCharacterPlacement.
Сам вывод осуществляется функцией ExtTextOut с использованием параметра ETO_GLYPH_INDEX.
Функция GetCharacterPlacement может работать с разными языками, в том числе и с RTL-написанием, и с арабской вязью, где несколько символов могут быть заменены одним/несколькими глифами. Именно для этой цели в структуре, упомянутой выше содержится выводимая строка. Она, после вызова, может отличаться от входной довольно значительно.
Что очень важно, функция работает быстро.
Изменения в ББЧтобы использовать описанный механизм в ББ придется пересмотреть весь вывод текста. При определении позиции вставки в ББ широко используется функция GetCharWidth32W, определяющую ширину символа вместе с метриками. Придется пересмотреть этот механизм и определять ширину символов и потенциальную позицию каретки с помощью предварительно кэшированной информации, возвращенной фукнцией GetCharacterPlacement. Кроме того, придется рассмотреть вопрос о стыковке view и текста, стыковке текста разного начертания и др.
Но, в очередной раз следует отдать должное Оминк, все очень неплохо локализовано и изменения будут в считанных местах. HostPorts я уже назвал.
Да, важное замечание: GetCharacterPlacement ожидает на вход строку в UNICODE, не в UTF-8!
PS. Если интересно, могу выложить проект (на Delphi), в котором я экспериментировал с функциями GetCharacterPlacement и ExtTextOut. Предваряя вопрос, почему не ББ, скажу, что в Delphi заведомо работающие прототипы функций, мне не хотелось еще и их (прототипы) отлаживать.
PPS. Для совсем уж экзотических ситуаций, вроде тайского языка или китайского языка с вертикальным следованием текста, нужно использовать
Uniscribe.