LINUX.ORG.RU

ttf-parser 0.5 — новая библиотека для работы с TrueType шрифтами

 ,


3

8

ttf-parser — это библиотека для разбора TrueType/OpenType шрифтов. В новой версии появилась полноценная поддержка переменных шрифтов (variable fonts) и C API, вследствие чего я решил прорекламировать её на лоре.

До недавнего времени, если была необходимость работы с TrueType шрифтами, было ровно два варианта: FreeType и stb_truetype. Первый является огромным комбайном, второй поддерживает довольно небольшое количество функций.

ttf-parser находится где-то посредине. Он поддерживает все те же TrueType таблицы (формат TrueType состоит из множества отдельных бинарных таблиц) что и FreeType, но не занимается отрисовкой самих глифов.

При этом, ttf-parser содержит множество других значительных отличий:

  1. ttf-parser написан на Rust без использования unsafe. FreeType и stb_truetype написаны на C.
  2. ttf-parser является единственной безопасной (memory-safe) реализацией. Чтение произвольной памяти невозможно. Во FreeType постоянно исправляют уязвимости, а stb_truetype в принципе не предназначен для чтения произвольных шрифтов.
  3. ttf-parser является единственной thread-safe реализацией. Все методы парсинга константны. Единственным исключением является задание координат для переменных шрифтов, но эта функция reentrant. FreeType в принципе однопоточный. stb_truetype - reentrant (можно использовать отдельные копии в разных потоках, но не одну из множества).
  4. ttf-parser является единственной реализацией не использующей аллокации в «куче». Это позволяет ускорить разбор и избежать проблем при OOM.
  5. Также, почти все арифметические операции и приведение числовых типов проверяются (в том числе статически).
  6. В самом худшем случае библиотека может бросить исключение. При этом в C API исключения будут перехвачены и функция вернёт ошибку, но не упадёт.

И несмотря на все гарантии безопасности, ttf-parser также является и самой быстрой реализацией. Например разбор CFF2 в 3.5 раза быстрее чем в FreeType. Разбор glyf тем временем на 10% медленнее чем в stb_truetype, но это из-за того, что он не поддерживает переменные шрифты, для реализации которых требуется хранить доп. информацию. Больше подробностей в README.

>>> Подробности

★★★★★

Проверено: Satori ()

Безопасность это хорошо, но чтобы нарисовать глиф всё равно придется обращаться к небезопасным stbtt и ft2?

Какие-то планы на отрисовку есть?

a1batross ★★★★★ ()
Ответ на: комментарий от a1batross

Отрисовку пилят люди из RustType. Я тут не при чём и влазить туда пока не планирую.

Если нужно тупо нарисовать глиф - есть raqote. Но мы же хотим нормальный хинтинг и антиалиасинг. А значит нужно ждать пока кто-то соберётся запилить нормальный freetype.

RazrFalcon ★★★★★ ()

How portable is FreeType 2? The FreeType 2 source code is extremely portable for the following reasons:

Everything is written in standard ANSI C.

We are very pedantic to avoid any kinds of compiler warnings. The current source code has been compiled with many compilers without producing a single warning.

The library doesn’t use any static writable data at all, making it an ideal choice on various embedded systems (e.g., it can be run from ROM directly). It is completely thread-safe too.

We have made great efforts to ensure that the library is efficient, compact, and customizable.

ну и про memory-safe тоже враньё. так нагло врать - это так себе, примерно уровень днища, расчёт на лохов.

anonymous ()
Ответ на: комментарий от anonymous

Где-ты это откопал, болезненный? Открываем офф доку и видим:

https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_library

In multi-threaded applications it is easiest to use one FT_Library object per thread. In case this is too cumbersome, a single FT_Library object across threads is possible also, as long as a mutex lock is used around FT_New_Face and FT_Done_Face.

https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_face

An FT_Face object can only be safely used from one thread at a time. Similarly, creation and destruction of FT_Face with the same FT_Library object can only be done from one thread at a time.

RazrFalcon ★★★★★ ()
Последнее исправление: RazrFalcon (всего исправлений: 1)

ttf-parser является единственной безопасной (memory-safe) реализацией.

Каждый раз когда читаю такое, вспоминаю про «Memory Leaks are Memory Safe» и проигрываю

anonymous ()
Ответ на: комментарий от RazrFalcon

Тоесть разожранное днище с безумным количеством кода для реорганизации памяти во имя «неалоцировать»? 70ые какието... Бабушкины коврижки а не ПО

Jetty ★★★★★ ()

Прокольная штука. Особенно радует, что библиотека не пытается сама память выделять, идеально ложится на кастомные аллокаторы. Спасибо, что поделился.

i-rinat ★★★★★ ()
Ответ на: комментарий от RazrFalcon

хотим нормальный хинтинг и антиалиасинг

Эх, хотим, но сам я бы такое не стал писать. :(

stbtt из-за этого очень сильно проигрывает freetype.

Я в целом удовлетворён работой freetype, но честно говоря в код его не лазил и не особо хочется. Работает и хорошо.

a1batross ★★★★★ ()
Ответ на: комментарий от a1batross

Ну меня freetype не устраивает, ибо очень жирный, неудобно использовать из Rust и с многопоточностью боль. А мне оно в первую очередь нужно для resvg, где хинтинг бесполезен.

код его не лазил

А я лазил. Типичный сишный ад и Израиль.

RazrFalcon ★★★★★ ()
Последнее исправление: RazrFalcon (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Авторы не различают thread-safe и reentrant.

Да вроде как различают. Сама библиотека заявлена как thread-safe, в то время как отдельные объекты - нет. Противоречия нет

yoghurt ★★★★★ ()
Ответ на: комментарий от RazrFalcon

ttf-parser не алоццирует в принципе. Шах и мат.

std::vector<char> data((std::istreambuf_iterator<char>(s)),
                        std::istreambuf_iterator<char>());
m_fontData = std::move(data);

m_font = ttfp_create_font(m_fontData.data(), m_fontData.size(), index);

Пропущен const, из-за этого компилятор не сделает оптимизацию. std::move здесь не нужен, это всего лишь каст к &&, если компилятор может сделать мув, то он его сделает сам, надо добавить const и включить оптимизации. О3 или хотя бы О2. В бенчмарке stb на каждый глиф выделяется и освобождается память, бенчмарк меряет не скорость аутлайна, а скорость алокаций. И т.д.

anonymous ()
Ответ на: комментарий от RazrFalcon

О том и речь, для пользователя-не разработчика чего-либо из этого списка, новость бесполезна. Сравнивать с freetype некий промежуточный парсер это смешно.

anonymous ()
Ответ на: комментарий от anonymous

Вы не понимаете. В этом и есть сама суть и сам смысл подобных импульсов. Растофанаты просто не могут, не способны изъясняться честно и написать «данная поделка хороша тем что моя и малинкой пахнет». Ведь так бы не было никаких вопросов и возможно, адекватные люди бы ей заинтересовались.

Растофанатам просто необходимо громко визжать что их произведения всесторонне лучше чем софт здоровых людей. Разумеется, при этом нагло врать. На этом и стоит вся рекламная кампания истероидной секты под названием раст.

anonymous ()
Ответ на: комментарий от RazrFalcon

кто-то соберётся запилить нормальный freetype

А потом захочется поддержки всего разнообразия языков ЮВА и будете ждать, когда кто-то запилит Pango со всеми дополнительными библиотеками :)

Linfan ★★★★★ ()
Ответ на: комментарий от RazrFalcon

То что стб даже с алокацией на каждый глиф оказывается не намного медленнее, а где-то даже быстрее - это уже само по себе довольно впечатляюще. В тестах надо сравнивать какие-то сравнимые вещи.

anonymous ()
Ответ на: комментарий от anonymous

если компилятор может сделать мув, то он его сделает сам

Каким образом он его сможет сделать в данном случае? Никаким.

std::move здесь не нужен

std:: не нужен :D move нужен.

anonymous ()
Ответ на: комментарий от q0tw4

Там в функцию передаётся структура:

typedef struct ttfp_outline_builder {
    void (*move_to)(float x, float y, void *data);
    void (*line_to)(float x, float y, void *data);
    void (*quad_to)(float x1, float y1, float x, float y, void *data);
    void (*curve_to)(float x1, float y1, float x2, float y2, float x, float y, void *data);
    void (*close_path)(void *data);
} ttfp_outline_builder;

а функция, по всей видимости, дёргает эти колбеки в нужной последовательности.

i-rinat ★★★★★ ()
Ответ на: комментарий от anonymous

Это следующий этап. Надо убрать мусор из кода чтобы понять, что там не надо сначала делать копию, а потом ее мувать.

Да, пацаны, С++ - это реально сложно осилить, я понимаю почему есть спрос на язычки попроще. Сам не знаю как так получается, что люди пишут все эти кракозябры вместо нормального кода, что-то здесь не так. Есть подозрение, что он просто взял пример с stackoverflow и несколько раз скопипастил.

anonymous ()

Что оно делает? Просто свойства шрифтов вытаскивает, или может рендерить? Мы тут собирались как раз делать конвертер картинок в портянки из Emoji (обнаруженные решения почему-то гадят ими на страницу с абсолютными координатами, а не формируют текстовую сетку). И как раз на Rust.

mertvoprog ()