Пользовательские шрифты: контракт расширения FontRegistry
Краткий обзор
Заголовок раздела «Краткий обзор»FontRegistryInterface определяет контракт уровня процесса для регистрации и поиска шрифтов. Регистрируйте шрифты по пути к файлу, из каталога или из необработанных двоичных данных, а затем блокируйте реестр, чтобы рабочие процессы в продакшене не могли изменять его.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Реестр шрифтов — это синглтон, который живёт дольше любого отдельного экземпляра Document. Он хранит только чистые данные PHP, без дескрипторов ресурсов или объектов расширений, поэтому его можно совместно использовать между запросами в долгоживущем рабочем процессе.
Используйте один из трёх способов регистрации:
- Из файла.
register()разбирает файл.ttf,.otf,.ttcили.pfbи возвращает метаданные. Для TrueType Collection передайте индекс вложенного шрифта. - Из каталога.
addFontDirectory()добавляет путь поиска, который движок сканирует при разрешении семейства по имени. - Из двоичных данных.
registerFromBinary()разбирает необработанные байты TrueType или OpenType. Используйте этот способ для моста@font-face, когда шрифты поступают из идентификатора ресурса (URI)data:или удалённого источника.
Чтобы снизить задержку первого запроса, вызовите warmup() при запуске рабочего процесса и заранее разберите пакет шрифтов. Затем вызовите lock(). После lock() каждый метод изменения выбрасывает LogicException: register(), addFontDirectory(), warmup(), registerBase14() и registerFromBinary(). Методы поиска остаются доступными: get(), has(), all() и getSearchDirectories(). Эта блокировка защищает рабочие процессы в продакшене: ни один запрос не сможет изменить общий набор шрифтов.
В большинстве случаев вам не нужно реализовывать FontRegistryInterface самостоятельно. Движок предоставляет реализацию, а вы её вызываете. Реализуйте его только тогда, когда нужна собственная стратегия разрешения шрифтов, например на основе хранилища с контентной адресацией. В обоих случаях границей остаётся контракт.
Поверхность API
Заголовок раздела «Поверхность API»NextPDF\Contracts\FontRegistryInterface (стабильный, начиная с 1.7.0):
| Метод | Возвращает | Назначение |
|---|---|---|
register(string $fontFile, string $alias, int $fontIndex) | FontInfo | Разобрать и зарегистрировать файл шрифта. Выбрасывает исключение, если реестр заблокирован или файл невозможно разобрать. |
registerFromBinary(string $fontData, string $alias) | FontInfo | Зарегистрировать шрифт из необработанных байтов TrueType или OpenType. |
registerBase14(string $key, FontInfo $font) | void | Зарегистрировать готовый стандартный шрифт Base 14. |
addFontDirectory(string $directory) | void | Добавить каталог поиска шрифтов. |
warmup(array $fontFiles) | void | Заранее разобрать пакет шрифтов при запуске рабочего процесса. |
lock() | void | Заморозить реестр, чтобы предотвратить дальнейшие изменения. |
isLocked() | bool | Сообщить, заблокирован ли реестр. |
get(string $family, string $style) | FontInfo | null | Найти шрифт по семейству и начертанию. |
has(string $key) | bool | Проверить, существует ли ключ регистрации. |
all() | array<string, FontInfo> | Вернуть все зарегистрированные шрифты. |
getSearchDirectories() | list<string> | Вернуть каталоги поиска в порядке обхода. |
memoryUsage() | MemoryReport | Сообщить о текущем использовании памяти реестром. |
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;
/** @var FontRegistryInterface $fonts */$info = $fonts->register('/srv/fonts/Inter-Regular.ttf', 'Inter');
if (!$fonts->has('inter')) { throw new RuntimeException('Inter failed to register');}Пример кода — продакшен
Заголовок раздела «Пример кода — продакшен»При запуске рабочего процесса прогрейте набор шрифтов, заблокируйте реестр и отслеживайте каждую загрузку для учёта лицензий. В примере используются только публичные типы.
<?php
declare(strict_types=1);
use NextPDF\Contracts\FontRegistryInterface;use NextPDF\Event\Content\FontLoadedEvent;use NextPDF\Event\EventDispatcher;use NextPDF\Event\ListenerProvider;use Psr\Log\LoggerInterface;
final class FontWarmup{ /** @param list<string> $fontFiles */ public function __construct( private readonly FontRegistryInterface $fonts, private readonly LoggerInterface $logger, private readonly array $fontFiles, ) {}
public function boot(): EventDispatcher { $listeners = new ListenerProvider(); $listeners->addListener( FontLoadedEvent::class, function (FontLoadedEvent $event): void { $this->logger->info('font.loaded', [ 'family' => $event->family, 'style' => $event->style, 'type' => $event->fontType->name, ]); }, );
if (!$this->fonts->isLocked()) { $this->fonts->warmup($this->fontFiles); $this->fonts->lock(); }
return new EventDispatcher($listeners); }}Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Заблокированный реестр. После
lock()любое изменение выбрасываетLogicException. ПроверяйтеisLocked()перед условным прогревом в повторно используемом рабочем процессе. - Двоичная регистрация не кэшируется по ключу.
registerFromBinary()записывает данные во временный файл и разбирает его. Используйте возвращённыйFontInfoкак дескриптор. - Индекс TrueType Collection (TTC). Для TrueType Collection третий аргумент метода
register()выбирает вложенный шрифт. Значение по умолчанию0выбирает первое начертание. - Разрешение семейства.
get()возвращаетnullдля неизвестной пары семейства и начертания. Никогда не рассчитывайте на ненулевой результат.
Производительность
Заголовок раздела «Производительность»warmup() переносит затраты на разбор с первого запроса на запуск рабочего процесса. Методы реестра работают с чистыми данными PHP, а поиск выполняется как чтение из карты за постоянное время. Вызовите memoryUsage(), чтобы сопоставить резидентный набор шрифтов рабочего процесса с вашим бюджетом памяти.
Замечания по безопасности
Заголовок раздела «Замечания по безопасности»Зарегистрированный шрифт может быть встроен в содержимое формата PDF (Portable Document Format). Проверяйте происхождение шрифта перед регистрацией. Не регистрируйте двоичные данные, контролируемые злоумышленником, без проверки размера и формата. Используйте хук FontLoadedEvent, чтобы обеспечивать соблюдение лицензий на шрифты и фиксировать, какие начертания встраивает документ.
Соответствие
Заголовок раздела «Соответствие»Нормативные требования к подписанию или архивированию не применяются. Встраивание шрифтов и создание подмножеств соответствуют модели шрифтов PDF 2.0. За это соответствие отвечает внутренний механизм создания подмножеств, а не этот контракт.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»NextPDF Enterprise добавляет аттестацию лицензий на шрифты и аудируемую политику создания подмножеств поверх того же FontRegistryInterface. Ваш код регистрации работает одинаково во всех редакциях, потому что границей служит контракт.
См. также
Заголовок раздела «См. также»- Обзор разработки расширений
- Триггеры действий и слушатели событий
- Пользовательский макет и перехват текста
- Правила стабильности SPI
Связанные контракты и модули
Заголовок раздела «Связанные контракты и модули»- Справочник по модулю шрифтов — реализация реестра, разбор и внутренние механизмы создания подмножеств.
- Справочник по контрактам типографики — каталожная запись для
FontRegistryInterface. - Триггеры действий и слушатели событий —
FontLoadedEventи диспетчер. - Пользовательский макет и перехват текста — родственный контракт стратегии на этапе отрисовки.
- Правила стабильности SPI — гарантия интерфейса, стоящая за
FontRegistryInterface.
В глоссарии определены термины font registry, image registry и event listener; канонические определения см. в опубликованном глоссарии.