Встраивание шрифта TrueType и создание его подмножества
Краткий обзор
Заголовок раздела «Краткий обзор»Зарегистрируйте шрифт TrueType, отрисуйте с ним текст и позвольте средству записи встроить только нужное подмножество. В рецепте используется тот же путь обработки содержимого, что и в examples/04-text-and-fonts.php, но с дополнительно зарегистрированным шрифтом TrueType (.ttf).
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Эта зависимость устанавливает пакет nextpdf/core. Пример рассчитан на PHP 8.4, а входящая в комплект тестовая фикстура LiberationSans-Regular.ttf делает рецепт самодостаточным.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»Зарегистрируйте начертание с помощью FontRegistry::register($path, $alias). Реестр анализирует файл и возвращает FontInfo, используя TrueTypeParser для файлов .ttf и .otf. Чтобы выбрать начертание, укажите его псевдоним через setFont($alias, ...). Этот вызов также записывает используемые кодовые точки.
Создание подмножества выполняется автоматически при save(). Средство записи шрифтов PDF собирает используемые кодовые точки и вызывает FontSubsetter::subset() или CffSubsetter для начертаний Compact Font Format (CFF) или OpenType. Если подмножество меньше полной программы, средство записи встраивает именно его. Оно также переписывает BaseFont и FontName, добавляя тег подмножества из шести прописных букв, присоединённый через плюс. Это форма ABCDEF+FontName, которую ISO 32000-2 требует для подмножества шрифта. Средство записи сохраняет встроенную программу TrueType как FontFile2 в дескрипторе шрифта (ISO 32000-2).
Префикс подмножества детерминированно генерируется из имени PostScript, поэтому детерминированная сборка получает стабильный тег. Поэтому профиль воспроизводимости этого рецепта — structural. Структурный профиль нормализует префикс подмножества и трейлер /ID вместо их побайтовой проверки.
Состав API
Заголовок раздела «Состав API»FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.setFont(string $family, string $style = '', float $size = 12.0): static—NextPDF\Core\Concerns\HasTypography; передайте зарегистрированный псевдоним как$family.- Создание подмножества — внутренняя операция средства записи (
NextPDF\Writer\PdfFontWriter->NextPDF\Typography\FontSubsetter). Публичного переключателя для включения или отключения нет: средство записи всегда создаёт подмножество, когда кодовые точки известны и подмножество меньше.
Полная таблица PHPDoc генерируется из исходного кода.
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();$registry->register(__DIR__ . '/MyFont-Regular.ttf', alias: 'MyFont');
$doc = Document::createStandalone();$doc->addPage();$doc->setFont('MyFont', '', 14);$doc->cell(0, 10, 'Rendered with an embedded, subset TrueType face.', newLine: true);
$doc->save(__DIR__ . '/out.pdf');Document::createStandalone() создаёт собственный реестр. Чтобы использовать реестр, который вы заполнили самостоятельно, создайте документ через DocumentFactory, как показано в рабочем примере. Фабрика обеспечивает чтение зарегистрированного вами начертания средством записи.
Пример кода — рабочая версия
Заголовок раздела «Пример кода — рабочая версия»Этот пример самодостаточен и подходит для запуска в тестовой обвязке. Он регистрирует входящий в комплект LiberationSans-Regular.ttf и выполняет отрисовку через DocumentFactory, поэтому используется именно заполненный реестр. Он использует автоматическое создание подмножества при сохранении.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
// A bundled TrueType test fixture keeps this recipe self-contained.// Replace with a font you have the right to embed.$fontPath = __DIR__ . '/../../fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';if (!is_file($fontPath)) { // Fall back to the repository-relative fixture location. $fontPath = dirname(__DIR__, 2) . '/fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';}
$fontRegistry = new FontRegistry();$fontRegistry->register($fontPath, alias: 'LiberationSans');
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();$doc->setTitle('Embedded Subset Font');$doc->addPage();
$doc->setFont('LiberationSans', '', 20);$doc->cell(0, 14, 'Embedded TrueType face', newLine: true);
$doc->setFont('LiberationSans', '', 12);$doc->multiCell(0, 7, 'Only the glyphs used by this document are embedded. ' . 'The writer subsets the font program and rewrites the BaseFont with a ' . 'deterministic six-letter subset prefix, for example ABCDEF+LiberationSans.');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/embed-and-subset-fonts.pdf');
echo "Wrote embed-and-subset-fonts.pdf\n";Ожидаемый STDOUT:
Wrote embed-and-subset-fonts.pdfЧтобы убедиться, что подмножество создано, откройте выходной файл и просмотрите словарь шрифта. В BaseFont указано <TAG>+LiberationSans, а дескриптор содержит FontFile2. Запуск qpdf --check не сообщает о структурных ошибках.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Владение реестром.
Document::createStandalone()создаёт собственный реестр. Шрифт, который вы зарегистрировали в отдельномFontRegistry, ему не виден. ИспользуйтеDocumentFactory, чтобы передать ваш реестр, как это делает рабочий пример. - Права на встраивание. Создание подмножества не меняет условия лицензирования. Встраивайте только те шрифты, на встраивание которых у вас есть лицензия. В некоторых шрифтах заданы биты ограничения встраивания; средство разбора считывает эти биты, но ответственность за соблюдение требований остаётся за вами.
- Путь CFF/OpenType. Для начертаний
.otfи CFF используетсяCffSubsetter, а неFontSubsetter. Поведение и переписывание тега подмножества эквивалентны. Различается только путь выполнения кода. - Нет выигрыша в размере. Иногда подмножество не меньше оригинала. Это может произойти с очень маленькими шрифтами или когда используются все глифы. В этом случае средство записи встраивает исходную программу без тега подмножества. Это правильное поведение, а не сбой.
- Шрифты CJK. Крупные начертания для китайского, японского и корейского языков (CJK) используют многоступенчатую стратегию создания подмножеств согласно ADR-008, с изолированным подпроцессом и резервным вариантом на PHP. Подробности по CJK и текущее состояние конвейера см. в Размещение текста CJK с кодировкой на основе cmap.
Производительность
Заголовок раздела «Производительность»Разбор выполняется за один проход по таблицам шрифта, а затраты на создание подмножества растут с количеством глифов. Латинские начертания, не относящиеся к CJK, обрабатываются внутри процесса в пределах бюджета wall_ms: 1500, peak_mb: 96. Крупные начертания CJK направляются в изолированный подпроцесс с тайм-аутом реального времени в две секунды и резервным вариантом на PHP (ADR-008). Такая маршрутизация означает, что медленное или аварийно завершающееся создание подмножества не может заблокировать вызывающий код.
Примечания по безопасности
Заголовок раздела «Примечания по безопасности»Файл шрифта — это недоверенные двоичные входные данные. Средство разбора отклоняет пути с stream-обёртками и нулевые байты. Подпроцесс создания подмножеств CJK работает без унаследованных соединений с базой данных, файловых дескрипторов или состояния фреймворка. Он безопасно переключается на резервный вариант при сбое или тайм-ауте (ADR-008). Проверяйте происхождение шрифтов, принимаемых от конечных пользователей.
Соответствие требованиям
Заголовок раздела «Соответствие требованиям»| Утверждение | Спецификация | Раздел | reference_id (идентификатор ссылки) |
|---|---|---|---|
| BaseFont/FontName подмножества шрифта содержит префикс подмножества из шести прописных букв, присоединённый через плюс. | ISO 32000-2 | iso32000_2_sec9#x1.x66.p2 (раздел) | |
| Встроенная программа шрифта TrueType хранится как FontFile2 в дескрипторе шрифта. | ISO 32000-2 | iso32000_2_sec9#x1.x65.p15 (раздел) |
Этот рецепт показывает, как NextPDF встраивает начертание TrueType, создаёт его подмножество и формирует соответствующий требованиям префикс подмножества. Он не подтверждает соблюдение условий лицензии на шрифт. Права на встраивание — это ответственность интегратора.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Неприменимо.