Утилита 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.