Настройка текста CJK с кодированием на основе cmap
В этом рецепте вы регистрируете начертание TrueType для китайского, японского и корейского (CJK), затем кодируете текст на традиционном китайском через фасад с поддержкой cmap FontInfo::encodeText(). Фасад возвращает двухбайтовый поток CID-байтов в кодировке Identity-H. Рецепт основан на examples/35-cjk-cmap-demo.php. Прежде чем полагаться на него, прочитайте примечание о границах применимости.
Границы применимости и статус (прочитайте сначала)
Заголовок раздела «Границы применимости и статус (прочитайте сначала)»Архитектура кодирования текста с поддержкой cmap внедряется поэтапно (ADR-013). Этап 1 завершён: фасад FontInfo::encodeText() и стратегия кодирования с поддержкой cmap подключены и доступны из пользовательского кода. Этап 2 в процессе: средство отрисовки и средство записи переводятся на работу через фасад. Этапы 3 и 4 ожидаются: вывод /ToUnicode для каждого шрифта, /CIDSystemInfo, /Encoding и /CIDToGIDMap, а также распознаватель шрифтов-заменителей пока не подключены к средству записи.
Планируйте с учётом следующих последствий:
- Этот рецепт демонстрирует фасад кодирования, а не полноценный режим вертикального письма. В публичном интерфейсе документа сейчас нет API режима письма, поэтому нет ни вызова
setWritingMode, ни сеттераvertical-rl. - Судя по собственному заголовку, лежащий в основе пример является интеграционным дымовым тестом, а не эталоном соответствия. Проверка PDF/UA-2 и PDF/A-4 будет регрессировать для вывода, полученного таким образом, пока не появятся этапы 3 и 4. Не утверждайте, что вывод этого пути соответствует требованиям. Соответствие определяет средство проверки, и этот вывод оно пока не пропустит.
- Инфраструктура метрик вертикального письма существует, но остаётся внутренней. Она включает объект-значение
CjkVerticalMetrics, а также эмиттеры/W2и/DW2. NextPDF не предоставляет её как пользовательский вызов “писать вертикально”, и средство записи пока не выводит соответствующие словари.
Установка
Заголовок раздела «Установка»composer require nextpdf/core:^3Это ограничение относится к пакету nextpdf/core. Пример работает на PHP 8.4. Встроенная тестовая фикстура Noto Sans TC делает рецепт самодостаточным.
Концептуальный обзор
Заголовок раздела «Концептуальный обзор»ISO 32000-2 моделирует вывод текста в трёх слоях: кодовая точка Unicode, код символа и идентификатор глифа. Для начертания TrueType для CJK движок использует составной шрифт Type 0 с кодировкой Identity-H. В этой кодировке показываемая строка использует пары байтов, индексирующие CIDFont (ISO 32000-2).
FontRegistry::register() анализирует начертание. FontInfo::encodeText($unicodeText) затем выбирает стратегию кодирования через FontEncodingStrategyResolver. Для зарегистрированного начертания TrueType для CJK он делегирует обработку TrueTypeCmapStrategy. Возвращаемый EncodedGlyphRun содержит поток байтов Identity-H, операнд строки PDF, ширины продвижения для каждого глифа, использованные кодовые точки и отображение GID→Unicode. При создании подмножества CJK используются кодовые точки согласно ADR-008. Будущий поток /ToUnicode будет строиться на отображении GID→Unicode. Выбранный режим — EncodingMode::TwoByteCid.
В PDF вертикальное письмо определяют две структуры CIDFont. Первая — поглифовый массив вертикальных метрик /W2 (ISO 32000-2). Вторая — вертикальные метрики по умолчанию /DW2 (ISO 32000-2). NextPDF предоставляет объект-значение и эмиттеры для обеих структур через CjkVerticalMetrics::toW2Array(), toW2RangeArray() и toDw2Array(). Они внутренние, и средство записи пока их не выводит. См. примечание о границах применимости.
Поверхность API
Заголовок раздела «Поверхность API»FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.FontInfo::encodeText(string $unicodeText): EncodedGlyphRun—NextPDF\Typography\FontInfo. Фасад этапа 1.EncodedGlyphRun—NextPDF\Typography\Encoding\EncodedGlyphRun(byteStream,pdfStringOperand,mode,advanceWidths,toUnicodeMap,usedCodepoints,glyphCount()).EncodingMode—NextPDF\Typography\Encoding\EncodingMode(SingleByte,TwoByteCid).CjkVerticalMetrics—NextPDF\Typography\CjkVerticalMetrics. Внутренний объект-значение вертикальных метрик. Документирован для прозрачности, а не как пользовательский путь письма.
Полная таблица PHPDoc генерируется из исходного кода.
Пример кода — быстрый старт
Заголовок раздела «Пример кода — быстрый старт»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Typography\Encoding\EncodingMode;use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();$font = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
$encoded = $font->encodeText('PDF 2.0 引擎');
assert($encoded->mode === EncodingMode::TwoByteCid); // cmap-aware branch firedecho $encoded->glyphCount() . " glyph run entries\n";Пример кода — рабочее окружение
Заголовок раздела «Пример кода — рабочее окружение»Этот пример самодостаточен и запускается в тестовом окружении. Он повторяет examples/35-cjk-cmap-demo.php. Сначала зарегистрируйте встроенную фикстуру Noto Sans TC. Затем убедитесь, что фасад с поддержкой cmap доступен. После этого выполните отрисовку через DocumentFactory, чтобы документ использовал подготовленный вами реестр.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\Encoding\EncodingMode;use NextPDF\Typography\FontRegistry;
$cjkFontPath = dirname(__DIR__, 2) . '/fonts/test-fixtures/Noto Sans TC/NotoSansTC-Regular.ttf';if (!is_file($cjkFontPath)) { fwrite(STDERR, "Missing CJK font fixture: {$cjkFontPath}\n"); exit(1);}
$fontRegistry = new FontRegistry();$cjkFont = $fontRegistry->register($cjkFontPath, alias: 'NotoSansTC');
// Phase 1 facade: prove the cmap-aware path is reachable from userland.$cjkSample = 'PDF 2.0 引擎 — 使用 CMap 編碼';$encoded = $cjkFont->encodeText($cjkSample);
if ($encoded->mode !== EncodingMode::TwoByteCid) { fwrite(STDERR, "Expected TwoByteCid (TrueTypeCmapStrategy branch)\n"); exit(2);}
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();$doc->setTitle('NextPDF CJK CMap-Aware Encoding Demo');$doc->setLanguage('zh-Hant');$doc->addPage();
$doc->setFont('helvetica', 'B', 16);$doc->cell(0, 12, 'CJK cmap-aware encoding (Phase 1 facade)', newLine: true);$doc->setFont('helvetica', '', 10);$doc->cell(0, 6, 'Mode: ' . $encoded->mode->name . ' (Identity-H, 2-byte CIDs)', newLine: true);$doc->cell(0, 6, 'Glyphs: ' . $encoded->glyphCount() . ' run entries', newLine: true);$doc->cell(0, 6, 'Bytes: ' . strlen($encoded->byteStream) . ' encoded bytes', newLine: true);$doc->ln(4);
$doc->setFont('NotoSansTC', '', 18);$doc->cell(0, 12, $cjkSample, newLine: true);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/cjk-vertical-writing.pdf');
echo "Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)\n";Ожидаемый STDOUT:
Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)Крайние случаи и подводные камни
Заголовок раздела «Крайние случаи и подводные камни»- Не эталон соответствия. Согласно собственному заголовку лежащего в основе примера, этот вывод является интеграционным дымовым тестом. Проверки PDF/UA-2 и PDF/A-4 для него регрессируют, пока не появятся этапы 3 и 4. Не регистрируйте его как эталон соответствия.
- Нет API режима письма. Нет публичного вызова, который переключал бы вывод в режим вертикального письма с поддержкой
vertical-rlиvertical-lr. Эмиттеры/W2и/DW2существуют внутренне. Они не доступны публично и пока не записываются в словарь шрифта. - Владение реестром.
Document::createStandalone()строит собственный реестр. ИспользуйтеDocumentFactory, чтобы документ использовал реестр, который вы заполнили начертанием CJK. - Путь итогового потока байтов. Пока не завершён этап 2, видимый поток содержимого по-прежнему проходит через устаревший путь обработки текста. На сегодня проверен и доступен только предшествующий шаг кодирования: прямой поиск по cmap плюс поток байтов Identity-H.
- Стоимость создания подмножества CJK. Крупные начертания CJK создают подмножество через изолированный подпроцесс. У этого подпроцесса есть резервная реализация на PHP и двухсекундный тайм-аут (ADR-008).
Производительность
Заголовок раздела «Производительность»encodeText() выполняет один прямой проход поиска по cmap по входным данным. Он линеен по числу кодовых точек, O(n). Бюджет — wall_ms: 2000, peak_mb: 128. Этот бюджет — самый высокий в данном наборе, потому что начертания CJK имеют большой размер, а создание подмножества доминирует в затратах. ADR-008 изолирует эту работу, чтобы она не могла блокировать вызывающий код.
Примечания по безопасности
Заголовок раздела «Примечания по безопасности»Файл шрифта CJK — недоверенные двоичные входные данные. Анализатор отклоняет пути с обёртками потоков и нулевыми байтами. Создание подмножества CJK выполняется в изолированном подпроцессе без унаследованного состояния (ADR-008). Проверяйте происхождение начертаний, предоставленных конечными пользователями. Содержимое текста CJK отрисовывается, а не интерпретируется.
Соответствие
Заголовок раздела «Соответствие»| Утверждение | Спецификация | Пункт | reference_id (идентификатор ссылки) |
|---|---|---|---|
| Для шрифта Type 0 Identity-H/Identity-V показываемая строка — это пары байтов, индексирующие CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x49.p90 (§9 шрифты) | |
| Массив W2 задаёт поглифовые метрики вертикального письма и применяется только к шрифтам CIDFont, используемым для вертикального письма. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p23 (§9 вертикальные метрики) | |
| Массив DW2 задаёт вертикальные метрики по умолчанию для шрифта CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p22 (§9 вертикальные метрики) |
Этот рецепт показывает, что фасад кодирования CJK с поддержкой cmap доступен из пользовательского кода (этап 1). Он не заявляет о выводе вертикального письма или соответствии PDF/UA-2 / PDF/A-4 для полученного файла. Вывод /ToUnicode и вертикальных метрик средством записи (этапы 3 и 4) ожидается, и средство проверки сегодня этот вывод не пропустит.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»Неприменимо.