Componer texto CJK con codificación basada en cmap
De un vistazo
Sección titulada «De un vistazo»Esta receta registra una tipografía CJK TrueType y después codifica texto en chino tradicional mediante la fachada basada en cmap FontInfo::encodeText(). La fachada produce un flujo de bytes Identity-H con CID de dos bytes. La receta sigue examples/35-cjk-cmap-demo.php. Conviene leer la nota de alcance que aparece más abajo antes de basarse en ella.
Alcance y estado (léelo primero)
Sección titulada «Alcance y estado (léelo primero)»La arquitectura de codificación de texto basada en cmap se entrega por fases (ADR-013). La fase 1 ya está disponible: la fachada FontInfo::encodeText() y la estrategia de codificación basada en cmap están integradas y son accesibles desde el espacio de usuario. La fase 2 está en curso: consiste en enrutar el renderer y el escritor a través de la fachada. Las fases 3 y 4 están pendientes: la emisión, por tipografía, de /ToUnicode, /CIDSystemInfo, /Encoding y /CIDToGIDMap, y el resolvedor de tipografías sustitutas, todavía no están integrados en el escritor.
Conviene planificar teniendo en cuenta estas consecuencias:
- Esta receta demuestra la fachada de codificación, no un modo de escritura vertical listo para usar. La superficie actual del documento no tiene una API pública de modo de escritura, es decir, no hay
setWritingModeni settervertical-rl. - El ejemplo base es, según su propio encabezado, una prueba de humo de integración, no un fixture de conformidad. La validación de PDF/UA-2 y PDF/A-4 fallará con la salida producida de esta forma hasta que se entreguen las fases 3 y 4. No afirmar que la salida de esta ruta sea conforme. La conformidad la decide un validador, que todavía no aprobará esta salida.
- La infraestructura de métricas de escritura vertical existe, pero es interna. Se compone del objeto de valor
CjkVerticalMetricsy de los emisores/W2y/DW2. NextPDF no la expone como una llamada de espacio de usuario para «escribir en vertical», y el escritor todavía no emite esos diccionarios.
Instalación
Sección titulada «Instalación»composer require nextpdf/core:^3La restricción corresponde al paquete nextpdf/core. El ejemplo se ejecuta en PHP 8.4. El fixture de prueba Noto Sans TC incluido mantiene la receta autocontenida.
Visión conceptual
Sección titulada «Visión conceptual»ISO 32000-2 modela la emisión de texto en tres capas: punto de código Unicode, código de carácter e identificador de glifo. Para una tipografía CJK TrueType, el motor usa una tipografía compuesta Type 0 con codificación Identity-H. Con esta codificación, la cadena mostrada es una serie de pares de bytes que indexan el CIDFont (ISO 32000-2).
FontRegistry::register() analiza la tipografía. Después, FontInfo::encodeText($unicodeText) resuelve una estrategia de codificación a través de FontEncodingStrategyResolver. Para una tipografía CJK TrueType registrada, despacha a TrueTypeCmapStrategy. El EncodedGlyphRun devuelto contiene el flujo de bytes Identity-H, el operando de cadena PDF, los anchos de avance por glifo, los puntos de código usados y el mapa GID→Unicode. La creación de subconjuntos CJK consume los puntos de código usados según ADR-008. Un futuro flujo /ToUnicode usará el mapa GID→Unicode. El modo seleccionado es EncodingMode::TwoByteCid.
Dos estructuras de CIDFont definen la escritura vertical en PDF. La primera es el arreglo de métricas verticales por glifo /W2 (ISO 32000-2). La segunda son las métricas verticales predeterminadas /DW2 (ISO 32000-2). NextPDF tiene el objeto de valor y los emisores para ambas, a través de CjkVerticalMetrics::toW2Array(), toW2RangeArray() y toDw2Array(). Son internos y el escritor todavía no los emite. Consultar la nota de alcance.
Superficie de la API
Sección titulada «Superficie de la API»FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.FontInfo::encodeText(string $unicodeText): EncodedGlyphRun—NextPDF\Typography\FontInfo. La fachada de la fase 1.EncodedGlyphRun—NextPDF\Typography\Encoding\EncodedGlyphRun(byteStream,pdfStringOperand,mode,advanceWidths,toUnicodeMap,usedCodepoints,glyphCount()).EncodingMode—NextPDF\Typography\Encoding\EncodingMode(SingleByte,TwoByteCid).CjkVerticalMetrics—NextPDF\Typography\CjkVerticalMetrics. Objeto de valor interno de métricas verticales. Se documenta por transparencia, no como una ruta de escritura del espacio de usuario.
La tabla completa de PHPDoc se genera a partir del código fuente.
Ejemplo de código — Inicio rápido
Sección titulada «Ejemplo de código — Inicio rápido»<?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";Ejemplo de código — Producción
Sección titulada «Ejemplo de código — Producción»Este ejemplo es autocontenido y puede ejecutarse con el arnés. Sigue examples/35-cjk-cmap-demo.php. Primero registra el fixture Noto Sans TC incluido. A continuación confirma que la fachada basada en cmap es accesible. Luego renderiza a través de DocumentFactory para usar el registro poblado.
<?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 esperado:
Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)Casos límite y trampas
Sección titulada «Casos límite y trampas»- No es un fixture de conformidad. Según el encabezado del propio ejemplo base, esta salida es una prueba de humo de integración. Las comprobaciones de PDF/UA-2 y PDF/A-4 fallan con ella hasta que se entreguen las fases 3 y 4. No debe registrarse como un golden de conformidad.
- No hay API de modo de escritura. Ninguna llamada pública activa la escritura vertical, lo que incluiría
vertical-rlyvertical-lr. Los emisores/W2y/DW2existen internamente. No están expuestos y todavía no se escriben en el diccionario de la tipografía. - Propiedad del registro.
Document::createStandalone()construye su propio registro. UsarDocumentFactorypara que el documento lea el registro que se pobló con la tipografía CJK. - Ruta del flujo de bytes final. Hasta que finalice la fase 2, el flujo de contenido visible todavía pasa por la ruta de texto heredada. La parte probada y accesible actualmente es el paso previo de codificación: la búsqueda directa en el cmap más el flujo de bytes Identity-H.
- Costo de la creación de subconjuntos CJK. Las tipografías CJK grandes se crean como subconjuntos a través de un subproceso aislado. Ese subproceso dispone de un mecanismo alternativo nativo de PHP y un tiempo de espera de dos segundos (ADR-008).
Rendimiento
Sección titulada «Rendimiento»encodeText() realiza una única pasada de búsqueda directa en el cmap sobre la entrada. Es lineal respecto al número de puntos de código, O(n). El presupuesto es wall_ms: 2000, peak_mb: 128. Este presupuesto es el más alto de este conjunto porque las tipografías CJK son grandes, y la creación de sus subconjuntos es el costo dominante. ADR-008 aísla ese trabajo para que no pueda bloquear al código llamador.
Notas de seguridad
Sección titulada «Notas de seguridad»Un archivo de tipografía CJK es una entrada binaria no confiable. El analizador rechaza rutas con envoltorios de flujo y los bytes nulos. La creación de subconjuntos CJK se ejecuta en un subproceso aislado sin estado heredado (ADR-008). Validar la procedencia de la tipografía en las tipografías suministradas por el usuario final. El contenido de texto CJK se renderiza, no se interpreta.
Conformidad
Sección titulada «Conformidad»| Declaración | Especificación | Cláusula | reference_id |
|---|---|---|---|
| Para una tipografía Type 0 Identity-H/Identity-V, la cadena mostrada son pares de bytes que indexan el CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x49.p90 | |
| El arreglo W2 proporciona las métricas de escritura vertical por glifo y solo se aplica a los CIDFonts usados para escritura vertical. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p23 | |
| El arreglo DW2 proporciona las métricas de escritura vertical predeterminadas de un CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p22 |
Esta receta muestra que la fachada de codificación CJK basada en cmap es accesible desde el espacio de usuario (fase 1). No afirma que el archivo producido tenga salida de escritura vertical ni conformidad con PDF/UA-2 / PDF/A-4. La emisión en el lado del escritor de /ToUnicode y de las métricas verticales (fases 3 y 4) está pendiente, y actualmente un validador no aprobaría esta salida.
Contexto comercial
Sección titulada «Contexto comercial»No aplica.