Как проверить кириллический шрифт на комбинируемые знаки и NFC/NFD

Один и тот же кириллический текст может прийти в редактор как готовая буква или как основная буква вместе с отдельным диакритическим знаком. Для шрифта это разные последовательности кодов и иногда разные глифы. Проверка NFC и NFD помогает обнаружить пропавшие комбинируемые знаки, неверное расположение меток и различия между приложениями до того, как макет уйдёт в печать или на сайт.
Почему одна буква бывает записана двумя способами
В Unicode текст хранится как последовательность кодовых точек. Некоторые знаки существуют как отдельный символ, другие могут быть представлены сочетанием базовой буквы и комбинируемого знака. Unicode называет такие последовательности канонически эквивалентными, если они обозначают один и тот же абстрактный текст. Визуально они должны вести себя согласованно, но на уровне кода это не обязательно один и тот же набор чисел.
Например, заглавная Й может быть записана одной кодовой точкой U+0419 либо как И U+0418 и комбинируемая кратка U+0306. Аналогично Ё U+0401 имеет каноническое разложение Е U+0415 и комбинируемая диерезис U+0308. А в словарной записи ударение часто вводят отдельной последовательностью: гласная плюс комбинируемое острое ударение U+0301. Это не означает, что все кириллические буквы с надстрочными элементами имеют такие же разложения: проверяйте конкретный знак и его данные Unicode.
Формы NFC и NFD — два способа привести канонически эквивалентный текст к упорядоченному виду. NFD раскладывает некоторые составные символы на основу и знак; NFC сначала выполняет такое разложение, а затем снова объединяет те пары, для которых предусмотрена композиция. Ни одна из форм не заменяет работу над глифами: приложение может сохранить исходную последовательность, а шрифт должен уметь показать поступившие символы и корректно их расположить. Описание алгоритма есть в Unicode Standard Annex #15.
Какие части шрифта участвуют в показе
Для отдельного символа важна карта cmap: она связывает кодовую точку с глифом. Если в шрифте есть Й, но отсутствует U+0306, строка из И плюс кратки может показать пустой прямоугольник, пропустить знак или подставить его из другого шрифта. Для комбинируемого знака недостаточно только нарисовать контур: ему нужно назначить корректную категорию и подготовить позиционирование относительно базовой буквы.
В OpenType позицию метки обычно задают через GPOS, в частности через привязку mark-to-base: у знака и базового глифа есть соответствующие опорные точки. Для нескольких меток может понадобиться и привязка mark-to-mark. Спецификация OpenType GPOS описывает эти механизмы. Таблица cmap задаёт соответствие кодов глифам. Конкретный результат зависит также от движка формирования строк, браузера, редактора и режима набора, поэтому проверять нужно именно целевые приложения, а не только список глифов в редакторе шрифта.
Различайте отсутствие глифа и неправильную позицию. Если острая метка видна отдельно на базовой линии или слишком далеко от гласной, кодовая точка, вероятно, попала в шрифт, но настройка позиционирования или класс знака требует внимания. Если пропадает целая часть последовательности, сначала проверьте карту кодовых точек, выбранный файл шрифта и подстановку. Проверить назначение cmap можно через FontForge или утилиту просмотра таблиц; спецификация OpenType также определяет назначение этой таблицы.
Соберите минимальный тестовый набор
Не начинайте с огромного списка Unicode. Добавьте в контрольную строку только те знаки, которые реально нужны вашему проекту, и для каждого различайте кодовую точку и видимую форму. Для универсального кириллического шрифта полезно отдельно проверить готовые Й и Ё, их разложенные варианты, а также несколько последовательностей с комбинируемым ударением. В двуязычном словаре добавьте реальные слова из редакционного набора: изолированная буква не покажет, как надстрочный знак ведёт себя рядом с высокими строчными или прописными.
- Й / И + U+0306: проверить готовую и разложенную запись одной буквы.
- Ё / Е + U+0308: сравнить высоту, расстояние и ширину строки.
- и + U+0301 и И + U+0301: проверить комбинируемое ударение в нижнем и верхнем регистре.
- а + U+0300 или у + U+0300: добавить только если такие формы есть в реальных текстах.
- гласная + два знака: использовать лишь для поддерживаемого языка и проверить порядок их наложения.
Запишите рядом кодовые точки, например U+0438 U+0301, а не только похожее на экране слово. Это даст коллеге воспроизводимый тест и позволит отличить комбинируемое U+0301 от самостоятельного знака ударения или визуально похожего символа. Названия и свойства кодовых точек сверяйте с официальными таблицами Unicode, например с блоком Combining Diacritical Marks.
Сравните NFC и NFD без догадок
Создайте две версии одной и той же строки из настоящего текста: первую сохраните в NFC, вторую преобразуйте в NFD. Это можно сделать в редакторе, который явно показывает форму нормализации, или небольшой программой. Для воспроизводимой проверки подойдут Python и встроенный модуль unicodedata: `unicodedata.normalize("NFC", text)` и `unicodedata.normalize("NFD", text)`. Не применяйте NFKC или NFKD вместо NFC/NFD без отдельной причины: compatibility-нормализация может менять другие различия символов, которые для верстки и содержания значимы.
Сначала сравните строку в обычном текстовом редакторе, который способен показывать кодовые точки, а затем откройте обе версии рядом в приложении, где будет использоваться шрифт. Не судите только по копированию текста из браузера: при вставке промежуточная программа может нормализовать строку. Если нужен точный контроль, сохраните тест в UTF-8 и отдельно проверьте последовательность кодовых точек скриптом либо инспектором символов.
- Сохраните исходные слова и выпишите их коды до преобразования.
- Получите NFC и NFD из одной исходной строки, не набирайте варианты вручную.
- Откройте оба файла в одном приложении с тем же файлом шрифта и масштабом.
- Сравните не только форму знака, но и межстрочное расстояние, переносы и ширину строки.
- Повторите пробу в браузере или редакторе, который является целевой средой проекта.
Канонически эквивалентные версии не обязаны иметь побайтно одинаковые данные. Задача теста — увидеть согласованное отображение и понять, где именно появляется расхождение. Если после нормализации одна версия стала составной буквой, а другая осталась базовой буквой с меткой, это ожидаемо для некоторых знаков; критично, чтобы обе строки корректно обрабатывались в поддерживаемом сценарии.
Настройте и оцените положение меток
Рисуйте комбинируемый знак как самостоятельный глиф, пригодный к переиспользованию, а не как часть одного-единственного слова. Для метки задайте опорную точку, для базовых кириллических букв — соответствующие точки привязки. Проверьте широкие и узкие буквы, прописные и строчные: одна универсальная координата может хорошо сесть над И, но оказаться слишком далеко от узкой и или столкнуться с соседним элементом в широком слове. Если два знака ставятся один над другим, оцените их порядок и запас пространства до строки выше.
Сравнивайте относительную позицию, а не только абсолютную высоту в файле шрифта. У гласных различаются ширина, наклон и верхний контур; метке нужен стабильный визуальный отступ. Для рукописного или наклонного начертания опорная точка может смещаться относительно вертикальной оси, но не должна нарушать узнаваемость буквы. Не выравнивайте все метки механически по одному x-координатному значению, если форма базовых глифов заметно отличается.
Проверьте, что метка не влияет на ширину строки сама по себе и не превращается в самостоятельный знак с непредусмотренным интервалом. У комбинируемых символов типичен нулевой или специальный advance, однако полагайтесь на фактическое поведение своего файла и рендерера. В паре символ плюс метка ширина обычно должна определяться базовой буквой, а не дополнительным пробелом после неё. Проверяйте слово целиком: отдельная метка может выглядеть правильно, но создавать некрасивый ритм в тексте.
Используйте слова, которые выявляют проблемы
Соберите короткие тестовые слова из содержимого проекта: словарные формы с ударением, учебные примеры произношения, фамилии или топонимы, если они входят в заявленный набор. Включите в них разные базовые буквы, длину слова и соседство с высокими элементами. Оставьте рядом обычную строку без диакритики: она станет визуальным эталоном высоты и интерлиньяжа. Укажите шрифт, его версию, приложение, операционную систему и форму нормализации, чтобы результат можно было повторить после экспорта.
Печатная проба полезна даже для экранного набора, если надстрочные знаки занимают много места: она быстро выявляет столкновение со строкой выше. Для веба проверьте и файл WOFF2, и исходный OTF/TTF. При субсеттинге убедитесь, что в итоговый файл вошли как кодовые точки комбинируемых знаков, так и необходимые глифы и таблицы позиционирования. Проверка исходного файла не гарантирует, что оптимизатор сохранил нужные данные.
Частые ошибки при работе с комбинируемыми знаками
- Проверять только Й и Ё и считать, что любая строка NFD автоматически заработает: у отдельных меток могут отсутствовать глифы или позиционирование.
- Сравнивать вручную набранные слова: клавиатура или приложение могли создать разные последовательности и скрыть причину расхождения.
- Считать любой видимый акцент отдельной буквой либо, наоборот, комбинируемым знаком: сначала запишите кодовую точку.
- Добавлять знак в cmap, но не тестировать положение над разными базовыми глифами.
- Проверять только в редакторе шрифта и не открывать реальную веб-страницу, документ или печатный макет.
- Применять compatibility-нормализацию без необходимости и менять символы за пределами задачи.
Если результат отличается, локализуйте слой проблемы в таком порядке: исходная строка и кодовые точки; форма нормализации; наличие символов в файле; подстановка шрифта; позиционирование в GPOS; версия и поведение приложения. Такая последовательность помогает не перерисовывать знак, когда причина, например, в резервном шрифте или субсете.
Контрольный список перед выпуском
- Зафиксируйте нужные кодовые точки и реальные текстовые примеры.
- Сгенерируйте NFC и NFD из одного источника и подпишите версии.
- Проверьте наличие комбинируемых знаков в cmap и связанное позиционирование.
- Сравните прописные, строчные, узкие, широкие и наклонные базовые буквы.
- Проверьте файл после экспорта в браузере, редакторе и, при необходимости, на печати.
- Сохраните снимок, сведения о версии шрифта и обнаруженные расхождения.
Для собственного проекта можно собрать кириллический набор в Fontgenerator, затем использовать полученный файл как основу теста: отдельно откройте текст с готовыми буквами и с комбинируемыми знаками. Это не заменяет проверку таблиц и рендеринга, зато помогает увидеть реальные контуры в выбранном начертании.
Вопросы и ответы
Нужно ли нормализовать весь текст перед показом?
Не всегда. NFC удобен для единообразного хранения и сравнения строк, но приложение может получать уже существующий текст в другой форме. Если проект принимает пользовательский ввод, тестируйте обе канонически эквивалентные записи и определите нормализацию в своем программном процессе отдельно от поддержки шрифта.
Если Й отображается, значит ли это, что И плюс кратка тоже работает?
Нет. Готовая Й и последовательность И с U+0306 могут проходить разные пути формирования глифов. Проверьте отдельно символ U+0419 и пару U+0418 U+0306; убедитесь, что оба сценария дают ожидаемую форму и положение.
Нужно ли включать все комбинируемые знаки Unicode?
Нет. Состав набора определяют языки, тексты и задачи продукта. Добавьте только знаки, которые действительно нужны; для каждого зафиксируйте кодовую точку и контрольные слова. Большой неподтверждённый набор увеличивает работу по рисунку и тестированию, но не гарантирует поддержку нужного языка.
Почему комбинируемое ударение оказывается на соседней букве?
Причиной может быть отсутствие правильной привязки метки к базе, некорректная связь в движке набора или изменённая последовательность символов. Сверьте коды строки, наличие глифа U+0301, таблицу GPOS и результат в другом целевом приложении. Если ошибка проявляется только в одном месте, сравните его шрифтовую подстановку и настройки рендеринга.
Можно ли проверить это только в FontForge?
FontForge полезен для просмотра карты символов и редактирования глифов, но финальное отображение зависит от приложения и его текстового движка. Используйте FontForge для инспекции, затем проверьте экспортированный файл в браузере, редакторе или верстальной программе, где шрифт будет применяться.
Чем NFC отличается от NFKC?
NFC приводит текст к канонической композиционной форме. NFKC дополнительно использует совместимостные преобразования и может сводить некоторые различающиеся по назначению формы к общему представлению. Для проверки шрифтовой поддержки используйте NFC и NFD, если задача касается канонически эквивалентных строк; выбирайте NFKC только когда это обосновано правилами обработки самого текста.
Итог
Тест комбинируемых знаков состоит из трёх проверок: одинаково ли интерпретируются коды, есть ли все нужные глифы и правильно ли метки привязаны к кириллической основе. Сохраните пару NFC/NFD, реальный набор слов и список приложений, где проба прошла. Так вы превратите проверку диакритики из разового визуального взгляда в повторяемый контроль шрифта.