Перейти к содержимому
getnextpdf.com

Шрифт: типы значений, встраивание и резервная замена

В NextPDF шрифт описывается неизменяемым объектом-значением FontInfo и типом технологии: по нему движок понимает, как встроить шрифт. Движок встраивает каждый используемый шрифт. Устаревшая ссылка на Base 14 заменяется встроенным в пакет метрически совместимым аналогом.

Окно терминала
composer require nextpdf/core:^3

FontInfo — неизменяемый объект-значение, который содержит всё необходимое движку для встраивания шрифта: семейство и начертание, имя PostScript, флаги дескриптора, метрики, приведённые к em из 1000 единиц, ширины символов, отображение глифов в Unicode, прямое отображение символов (cmap, из Unicode в идентификатор глифа), необработанные байты шрифта, а также, при наличии, оси вариаций, именованные экземпляры, селекторы вариаций, пары кернинга и вертикальные метрики. Класс объявлен как final readonly. Сигнатура его конструктора и публичные свойства зафиксированы, поэтому разобранный шрифт становится стабильным фактом, пригодным для совместного использования. FontInfo::encodeText() — единственный метод с поведением. Он обращается к резолверу кодировки и возвращает EncodedGlyphRun.

FontType перечисляет шрифтовые технологии, которые встраивает движок: TrueType (однобайтовая кодировка), TrueTypeUnicode (многобайтовая кодировка по идентификатору символа (CID) для письменностей с большим набором Unicode-символов), OpenType (контуры в формате Compact Font Format), Type1 (PostScript Type 1, регистрируемый из пары Printer Font Binary (PFB) и Adobe Font Metrics (AFM)) и CidFont0 (CID-шрифт на основе PostScript). Назначенный парсером тип определяет структуру словаря шрифта, который выводит модуль записи.

Чтобы отрисовка не зависела от установленных системных шрифтов, движок встраивает программу шрифта — ISO 32000-2 §9. Программа TrueType встраивается через запись дескриптора шрифта FontFile2 и должна включать таблицы glyf, head, hhea, hmtx, loca и maxp — ISO 32000-2 §9.6.5. Программа OpenType с таблицей контуров в формате Compact Font Format встраивается через FontFile3 — ISO 32000-2 §9.6.5. Модуль создания подмножеств перестраивает именно этот обязательный набор таблиц, поэтому встроенное подмножество остаётся программой, соответствующей требованиям.

Резервная замена покрывает устаревший сценарий Base 14. Base14SubstituteFonts сопоставляет нормализованный ключ Base 14 — helvetica, helveticab, times, courier и остальные — с файлом Liberation Fonts из пакета. Liberation Sans, Serif и Mono метрически совместимы с Helvetica или Arial, Times Roman и Courier. Каждый из них — встроенное начертание TrueType, поэтому он отрисовывает полный латинский репертуар WinAnsiEncoding (Windows-1252), который требует эталон стандартных 14 шрифтов: латиницу с диакритикой, знак евро и распространённую типографскую пунктуацию (ISO 32000-2 Annex D.2). Для Symbol и ZapfDingbats нет метрически совместимой замены со свободной лицензией, поэтому NextPDF намеренно их не подставляет; документ, которому нужен любой из этих шрифтов, должен зарегистрировать встраиваемый шрифт. Резолвер не имеет побочных эффектов: он только сообщает, какому файлу соответствует ключ. Вызывающая сторона по-прежнему отвечает за регистрацию в реестре, сохраняя семантику блокировки и конвейер прогрева.

ТипРазновидностьКлючевые членыСтабильностьС версии
FontInfofinal readonly class$family, $style, $type, $unitsPerEm, $widths, $unicodeMap, $cmapForward, $fileData, $variationAxes, $kernPairs, getKey(), encodeText()стабильно1.0.0
FontTypeenum (string)TrueType, TrueTypeUnicode, OpenType, Type1, CidFont0стабильно1.0.0
Base14SubstituteFontsfinal class (внутренний)нормализованный ключ Base 14 в путь к файлу Liberation из пакетастабильно2.7.0
ShaperFactoryfinal classdefault(), create(), wouldUseRealShaper()стабильно3.2.0
ShapingResultfinal readonly class$glyphRuns, $originalText, $script, $direction, $shaperImplстабильно3.2.0

Base14SubstituteFonts помечен как @internal: только для внутреннего использования фреймворком, без гарантии обратной совместимости этой API-поверхности.

examples/35-cjk-cmap-demo.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Typography\FontRegistry;
use NextPDF\Typography\FontType;
$registry = new FontRegistry();
$font = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
// FontInfo is the immutable parsed fact about the face.
echo $font->family, ' / ', $font->type->value, "\n"; // e.g. "Noto Sans TC / TrueTypeUnicode"
assert($font->type === FontType::TrueTypeUnicode);

Парсер заполняет FontInfo и назначает FontType. Начертание TrueType с символьной картой Unicode становится типом TrueTypeUnicode, который модуль записи выводит как составной шрифт Type 0.

examples/font/base14-fallback.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Typography\Base14SubstituteFonts;
use NextPDF\Typography\FontRegistry;
final readonly class Base14EmbeddingResolver
{
public function __construct(private FontRegistry $registry) {}
/**
* Register an embeddable substitute for a legacy Base 14 key so the
* output document embeds every font (PDF/A-4 and PDF/UA-2 require it).
*/
public function ensureEmbeddable(string $base14Key): void
{
$path = Base14SubstituteFonts::resolve($base14Key);
if ($path === null) {
// Symbol / ZapfDingbats have no permissive substitute — the
// caller must supply its own embeddable font.
throw new \RuntimeException("No bundled substitute for {$base14Key}");
}
if (!$this->registry->has($base14Key)) {
$this->registry->register($path, alias: $base14Key);
}
}
}

Резолвер не имеет побочных эффектов. Регистрация остаётся явной, поэтому контракты блокировки и прогрева реестра соблюдаются. Для Symbol и ZapfDingbats путь намеренно не возвращается.

  • Symbol и ZapfDingbats намеренно не подставляются. Возврат null для этих ключей — задокументированное поведение, а не ошибка из-за отсутствующего шрифта.
  • FontInfo объявлен как final readonly. Относитесь к разобранному шрифту как к значению: не рассчитывайте изменять ширины или метрики на месте; перерегистрируйте, если источник меняется.
  • Шрифту Type 1 нужны и контур PFB, и метрики AFM. FontRegistry::registerType1() принимает эту пару; автоопределение выводит путь AFM из пути PFB по расширению.
  • FontType::TrueType и FontType::TrueTypeUnicode фиксируют различие между однобайтовым и многобайтовым вариантами. Резолвер кодировки использует заполненное прямое отображение символов, а не имя семейства, поэтому начертание Unicode TrueType автоматически направляется по пути Identity-H.
  • Оси вариаций шрифта и именованные экземпляры разбираются в FontInfo при их наличии, но подробный пример для китайского, японского и корейского языков (CJK) намеренно использует статическое начертание, чтобы разобранный FontInfo оставался детерминированным.

Реестр создаёт FontInfo один раз на шрифт в каждом процессе, а затем предоставляет его по ссылке. Основная часть памяти уходит на необработанные байты шрифта. Прогревайте только те шрифты, которые нужны воркеру, и отслеживайте memoryUsage(). Резолвер замены Base 14 выполняет поиск по карте за константное время без операций ввода-вывода (I/O), пока вызывающая сторона не зарегистрирует найденный файл. Параметр performance_budget в 1500 мс реального времени и 64 МБ пикового потребления покрывает прогрев типичного набора шрифтов и отрисовку. До запуска модуля создания подмножеств объём занимаемой памяти каждого шрифта зависит от размера файла шрифта, а не от количества глифов.

FontInfo сам по себе инертен: это разобранные данные без какого-либо поведения, кроме чистого преобразования encodeText(). Поверхность атаки находится раньше в конвейере, на этапе разбора, когда произвольные байты шрифта попадают в парсер TrueType или Type 1. Парсеры проверяют границы каждого двоичного смещения и отклоняют обёртки потоков и нулевые байты в путях. Перед регистрацией недоверенные входные данные шрифта должны пройти проверку политики внешних ресурсов, которая ограничивает размер и количество глифов. Входящие в пакет заменители Liberation — это доверенные ресурсы, поставляемые вместе с пакетом, поэтому путь резервной замены не вносит новых недоверенных входных данных.

УтверждениеСтандартПункт
Каждый шрифт, используемый документом, встроен, поэтому документ отображается без опоры на системные шрифты.ISO 32000-2§9
Программа TrueType встраивается через FontFile2 с таблицами glyf, head, hhea, hmtx, loca, maxp.ISO 32000-2§9.6.5
Программа OpenType (CFF) встраивается через FontFile3.ISO 32000-2§9.6.5

Пункты о FontFile2 и FontFile3 являются перефразированиями; это поведение также отражено в FontSubsetter (который перестраивает именно набор glyf/head/hhea/hmtx/loca/maxp) и в перечислении FontType. NextPDF не воспроизводит нормативный текст. В исходном коде Base14SubstituteFonts ссылается на ISO 32000-2 §9.6.2.2 (обработка стандартных шрифтов Type 1), ISO 14289-2:2024 §8.4.5.5.1 (встраивание шрифтов PDF/UA-2) и ISO 19005-4:2020 §6.3.5 (встраивание шрифтов PDF/A-4). Полное соответствие профилям описано на страницах о доступности и соответствии требованиям.

Коммерческий пакет лицензирования шрифтов и сервис динамического создания подмножеств построены на FontInfo из Core и реестре. Модуль шрифтов Core встраивает, создаёт подмножества и выполняет резервную замену без лицензии. Ссылка на конверсию намеренно отсутствует.