Иногда приходится копнуть глубже

Когда-то делала посимвольную подсветку текста поверх Quill (либа для создания текстового редактора) и столкнулась с багом: маркеры под отдельными буквами съезжали. Оказалось, шрифт объединял f и i в одну лигатуру, а моя подсветка рассчитывалась на две отдельные буквы. Пришлось разбираться, как определить границу между ними, если на экране это один общий рисунок.

Например, в EB Garamond есть такая лигатура fi. Это отдельный глиф в шрифте, а не два изображения, которые браузер придвинул друг к другу.

Подстановка происходит на этапе shaping. Шейпер, например HarfBuzz, получает исходный текст, применяет правила шрифта и возвращает глифы с их позициями. При этом сохраняет соответствие: вот этот глиф получился из вот этого участка строки. Поэтому после объединения f и i информация о двух исходных буквах никуда не исчезает.

А чтобы их разделить, в OpenType предусмотрены специальные данные: внутри лигатуры можно указать позиции курсора. Для fi — одну внутреннюю границу, для ffi — две. Они хранятся в таблице GDEF. То есть автор шрифта может описать не только внешний вид слитных букв, но и места, по которым их можно разделять при взаимодействии с текстом. Если этих данных нет или движок их не использует, границы приходится рассчитывать самому движку. Простое деление ширины пополам не всегда удачно: части лигатуры могут занимать разное место, поэтому точность положения курсора и подсветки зависит ещё и от реализации текстового движка.

Я в итоге убрала собственный расчёт ширины букв, и для каждого нужного участка текста создавала DOM Range и получала его прямоугольники через getClientRects(). И уже по ним размещала маркеры поверх редактора.

#ks_фронтенд

Иногда приходится копнуть глубже
Когда-то делала посимвольную подсветку текста поверх Quill (либа для создания текстового редактора) и столкнулась с багом: маркеры под отдельными буквами съезжали | Сетка — социальная сеть от hh.ru