И так мне понадобилось разобрать данные из файла шрифта woff (Web Open Font Format). Я прям с самого начала скажу, что там чёрт ногу сломит, а разработчики формата это представители инопланетной жизни страдающие от старческого маразма. Отправляемся на https://www.w3.org/TR/WOFF/, читаем, в принципе всё понятно (нет). Если кратко, то это бинарный файл, который имеет заголовок с информацией о таблицах. Таблицы – это некоторые структурированные данные, информация в которых отражает только один технический аспект шрифта. И все эти таблицы в woff заархивированы DEFLATE алгоритмом сжатия. На картинках ниже формат woff файла представлен более подробно.
Я буду рассматривать в этой статье только таблицы cmap и CFF, так как основная информация о символах именно в них.
cmap таблица по сути содержит информацию о том, как перейти от unicode байт к глифам (к начертаниям). По каким-то неведомым причинам видов cmap таблицы существует аж 14 штук, что за балаган? Но мне нужен только 4-ый. Документация просто ужасная, но методом проб и ошибок я пришёл к правильному способу перехода от unicode к глифам. Мне повезло в том, что количество сегментов равнялось количеству символов, и соответственно каждый startCode равнялся коду каждого символа. Но glyphIdArray почему-то был пустой. В итоге я сделал так: к startCode прибавил idDelta, и отсортировал полученные результаты по порядку. Этот порядок строго соответствовал порядку глифов в CFF. Може я сделал не совсем правильно, но иного способа не нашёл. Потому что везде пишут о том, что для этой задачи нужны ещё таблицы glyf и loca, но в woff файлах я этих таблиц не видел.
Перейдём к CFF. Если вы открыли документацию по CFF, то думаю вы поняли, что особо ничего и не понятно. CFF таблица состоит из заголовка, индексов (INDEX) и словарей (DICT). Индексами названы данные, которые структурированы как массив, словарями названы данные, которые структурированы как ключ-значение данные. В CFF присутствуют следующие данные: NameIndex, TopDictIndex, StringIndex, GlobalSubrIndex, Charsets, Encodings, CharStringIndex, PrivateDict, FontDictIndex, LocalSubrIndex. Мне понадобилось разобрать именно CharStringIndex, но так как данные предусматривалось находить по offset’ам, а offset говорит о нахождении только следующего блока, то разбирать пришлось практически всю CFF таблицу, чтобы дойти до CharStringIndex. Для идентификации глифов из CharStringIndex я просто сранивал длину каждого CharString из этого массива, дальше расковыривать формат не требовалось.
Кстати, в процессе поиска информации о форматах нашёл блог одного очень умного азиата, его исходники мне очень помогли в работе, парень проделал огромную работу по разбору шрифтов. В его исходниках всё понятно, написано очень классно, в отличие от мудацких исходников opentype.js, по которым понятно только одно, что автору платили за кривость кода.
В заключении вернусь к вопросу о том, что почему же woff формат такой сложный? Этот формат несёт в себе наследие CFF формата. CFF насколько я понимаю по документации был разработан в 2003 году, и вероятно он тоже в себе таит тайны прошлого. Прошлого, когда был важен каждый байт и важна скорость загрузки информации в память (а самая быстрая загрузка это когда просто копирование последовательности байт в память), видимо поэтому вся информация о шрифтах структурировалась в бинарный вид, вид который хорошо понятен машине, а не человеку – это вам не json смотреть.