Утилита UTF8 Range Checker

Современный web – удивительная вещь. С одной стороны, есть большое количество стандартизованных API для CSS и JavaScript. С другой – до сих пор нет единого стандарта на набор шрифтов.

Каждая ОС будет поставляться со своими шрифтами. Из-за этого один и тот же сайт на двух разных устройствах может выглядеть по-разному.

Решение достаточно простое – взять отдельный шрифт и использовать именно его. Нужно только добавить font-face в CSS-стиль, указав Regular версию шрифта.

Как пример, можно взять шрифт JetBrains Mono, я использую на сайте именно его. Базовый шрифт будет весить около 90К. Достаточно много, но приемлемо.

К сожалению, этого недостаточно. Дополнительную проблему создаёт разница в движках рендеринга шрифтов. Firefox куда более плавно делает шрифт “жирным”. Основанный на Chromium-браузеры изменяют толщину куда более резко, ступенчато.

В результате Chrome и Firefox рендерят большую часть страницы нормально, но те места, где есть выделенный жирно текст, они рендерят совсем по-разному.

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

Один из плюсов font-face в CSS – возможность указать unicode-range. Эта опция подскажет браузеру, какой шрифт нужно загрузить для какого интервала Unicode символов. Если font-face содержит интервал, символы которого не встречаются на странице, то это font-face не будет загружен.

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

Распилить символы из исходных woff2 на логические группы Unicode можно с помощью утилиты pyftsubset. Вот пример для группы символов Basic Latin.

$ pyftsubset JetBrainsMono-Regular.woff2 \
    --unicodes='U+0000-007F' \
    --layout-features='' \
    --flavor='woff2' \
    --output-file=JetBrainsMono-Basic-Latin-Regular.woff2

В результате получим файл всего в 8К. Описать в CSS полученный шрифт можно так:

@font-face {
    font-family: 'JetBrains Mono';
    src: local('JetBrains Mono'), url('JetBrainsMono-Basic-Latin-Regular.woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-007F;
}

Конечно, для каждого полученного из pyftsubset шрифта в формате woff2 нужно сделать соответствующий блок font-face.

Перечислять таким образом все логические блоки не слишком удобно. Во-первых, это сделает объём CSS намного больше. Во-вторых, это просто неудобная ручная работа.

Теперь встаёт самый сложный вопрос. Как можно понять, какие именно символы используются на ваших страницах? Ведь, если у браузера не будет ни одного подходящего unicode-range для символа, то он не сможет отобразить символ “правильно”. Да, что-то будет отражено, но как – вопрос открытый.

Представим, что я думал, что мне не нужен греческий сабсет, но по глупости и наивности вставил формулу. Она содержит греческие символы. Как мне валидировать набор символов на страницах сайта? Как понять, что где-то будет неопределённость в рендеринге?

Я не нашёл простого и удобного инструмента, поэтому написал простенькую утилиту [UTF8 Range Checker] для решения этой проблемы.

Смысл программы предельно просто. На вход в виде аргументов можно передать несколько интервалов Unicode символов, которые Вы уже описали в font-face. UTF-8 текст для проверки передаётся как STDIN.

Отмечу отдельно – программа рассчитана только на UTF-8!

В результате работы программа ответит, все ли UTF-8 символы в STDIN входят в указанные интервалы Unicode символов. Если нет и в наличии неучтённые символы, то checker выведет их код и скажет сколько всего символов было вне интервалов.

Проще всего продемонстрировать работу, чем пытаться её объяснить. Проверим, что в файле index.html содержаться только символы базовой латиницы и кириллицы:

$ cat www/tsb99x.ru/index.html \
    | ./utf8-range-checker U+0000-007F U+0400-04FF \
    | sort -u

U+2014
U+00A9
total code points outside of specified ranges: 2

Наглядно видим, сколько всего символов есть вне интервалов и их коды, супер!

Естественно, программу можно использовать для скриптинга, т.к. она возвращает релевантные статус коды. Я подключил её в свой Makefile и проверяю все страницы сайта перед публикацией, дабы не получить артефакты рендеринга у браузеров.

Есть ещё один вариант использования. Чтобы узнать коды всех символов, которые использованы в файле, можно запустить программу без указания интервалов.

Подводя итоги работы по “распиливанию” шрифтов, могу сказать, что результат меня приятно порадовал. В прошлом, для просмотра этой страницы, нужно было загрузить минимум 180К только шрифтов. Теперь нужно загрузить всего 74К. И это не только шрифты, но и CSS, HTML и SVG изображения. Успех!

Данные о лицензии, моих флагах компиляции, вариантах использования и граничных условиях можно найти в репозитории на GitHub.

На этом всё, надеюсь такая программа может Вам пригодиться!

Журнал изменений

1 января 2026 г.: перенёс исходники на GitHub.

Если Вы хотите обсудить содержание заметки, задать вопросы или предложить изменения, то со мной можно связаться в Telegram