Impostare testo CJK con codifica cmap-aware
In sintesi
Sezione intitolata “In sintesi”Questa ricetta registra un font TrueType CJK e codifica testo in cinese tradizionale tramite la facade cmap-aware FontInfo::encodeText(). La facade produce un flusso di byte CID Identity-H a due byte. La ricetta segue examples/35-cjk-cmap-demo.php. Prima di farvi affidamento, leggere la nota sull’ambito riportata di seguito.
Ambito e stato (leggere prima)
Sezione intitolata “Ambito e stato (leggere prima)”L’architettura cmap-aware per la codifica del testo viene rilasciata in fasi (ADR-013). La Fase 1 è stata integrata: la facade FontInfo::encodeText() e la strategia di codifica cmap-aware sono collegate e raggiungibili da userland. La Fase 2 è in corso: instrada il renderer e il writer attraverso la facade. Le Fasi 3 e 4 sono in sospeso: l’emissione di /ToUnicode, /CIDSystemInfo, /Encoding e /CIDToGIDMap per i font, e il resolver dei font sostitutivi, non sono ancora collegati al writer.
Pianificare considerando queste conseguenze:
- Questa ricetta illustra la facade di codifica, non una modalità di scrittura verticale pronta all’uso. Allo stato attuale la superficie del documento non espone alcuna API pubblica per la modalità di scrittura: non esistono né
setWritingModené un settervertical-rl. - L’esempio di base è, come dichiara esplicitamente la sua intestazione, uno smoke test di integrazione, non una fixture di conformità. La validazione PDF/UA-2 e PDF/A-4 regredirà sull’output prodotto in questo modo finché non saranno integrate le Fasi 3 e 4. Non affermare che l’output prodotto da questo percorso sia conforme. La conformità deve essere stabilita da un checker e, allo stato attuale, non convaliderebbe questo output.
- L’infrastruttura delle metriche per la scrittura verticale esiste ma è interna. Comprende il value object
CjkVerticalMetricse gli emitter/W2e/DW2. NextPDF non la espone come chiamata userland per la «scrittura verticale» e il writer non emette ancora i relativi dizionari.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Il vincolo fa riferimento al pacchetto nextpdf/core. L’esempio viene eseguito su PHP 8.4. Una fixture di test Noto Sans TC inclusa nel pacchetto rende la ricetta autonoma.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”ISO 32000-2 modella l’emissione del testo su tre livelli: codepoint Unicode, codice di carattere e ID del glifo. Per un font TrueType CJK, il motore utilizza un font composito Type 0 con codifica Identity-H. Con questa codifica, la stringa visualizzata è composta da coppie di byte che indicizzano il CIDFont (ISO 32000-2).
FontRegistry::register() analizza il font. FontInfo::encodeText($unicodeText) risolve quindi una strategia di codifica tramite FontEncodingStrategyResolver. Per un font TrueType CJK registrato, delega a TrueTypeCmapStrategy. L’EncodedGlyphRun restituito contiene il flusso di byte Identity-H, l’operando di stringa PDF, le larghezze di avanzamento per glifo, i codepoint utilizzati e la mappa GID→Unicode. Il subsetting CJK usa i codepoint utilizzati secondo ADR-008. Un futuro flusso /ToUnicode utilizzerà la mappa GID→Unicode. La modalità selezionata è EncodingMode::TwoByteCid.
Due strutture CIDFont definiscono la scrittura verticale in PDF. La prima è l’array di metriche verticali per glifo /W2 (ISO 32000-2). La seconda è il set di metriche verticali predefinite /DW2 (ISO 32000-2). NextPDF dispone del value object e degli emitter per entrambe le strutture, tramite CjkVerticalMetrics::toW2Array(), toW2RangeArray() e toDw2Array(). Sono interni e il writer non li emette ancora. Vedere la nota sull’ambito.
Superficie API
Sezione intitolata “Superficie API”FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.FontInfo::encodeText(string $unicodeText): EncodedGlyphRun—NextPDF\Typography\FontInfo. La facade della 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. Value object interno per le metriche verticali. È documentato per trasparenza, non come percorso di scrittura da userland.
La tabella PHPDoc completa è generata dal codice sorgente.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”<?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";Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”Questo esempio è autonomo ed eseguibile nell’harness. Rispecchia examples/35-cjk-cmap-demo.php. Innanzitutto registrare la fixture Noto Sans TC inclusa. Quindi verificare che la facade cmap-aware sia raggiungibile. Infine eseguire il rendering tramite DocumentFactory, così da utilizzare il registry popolato.
<?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 previsto:
Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)Casi limite e insidie
Sezione intitolata “Casi limite e insidie”- Non è una fixture di conformità. Come indica l’intestazione dell’esempio di base, questo output è uno smoke test di integrazione. I controlli PDF/UA-2 e PDF/A-4 regrediscono su questo output finché non saranno integrate le Fasi 3 e 4. Non registrarlo come golden di conformità.
- Nessuna API per la modalità di scrittura. Nessuna chiamata pubblica abilita la scrittura verticale, che coprirebbe
vertical-rlevertical-lr. Gli emitter/W2e/DW2esistono internamente. Non sono esposti e non sono ancora scritti nel dizionario dei font. - Proprietà del registry.
Document::createStandalone()crea il proprio registry. UsareDocumentFactoryaffinché il documento legga il registry popolato con il tipo di carattere CJK. - Percorso finale del flusso di byte. Finché la Fase 2 non sarà conclusa, il flusso di contenuto visibile passa ancora attraverso il percorso di testo legacy. La parte oggi comprovata e raggiungibile è la fase di codifica a monte, cioè la ricerca diretta della cmap più il flusso di byte Identity-H.
- Costo del subsetting CJK. I tipi di carattere CJK di grandi dimensioni vengono sottoposti a subsetting tramite un sottoprocesso isolato. Tale sottoprocesso dispone di un fallback nativo PHP e di un timeout di due secondi (ADR-008).
Prestazioni
Sezione intitolata “Prestazioni”encodeText() esegue un singolo passaggio di ricerca diretta nella cmap sull’input. È lineare rispetto al numero di codepoint, O(n). Il budget è wall_ms: 2000, peak_mb: 128. Questo budget è il più alto dell’insieme perché i font CJK sono di grandi dimensioni e il loro subsetting rappresenta il costo dominante. ADR-008 isola tale operazione in modo che non possa bloccare il chiamante.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”Un file di font CJK è un input binario non attendibile. Il parser rifiuta i percorsi stream-wrapper e i byte null. Il subsetting CJK viene eseguito in un sottoprocesso isolato senza stato ereditato (ADR-008). Convalidare la provenienza dei font forniti dagli utenti finali. Il contenuto di testo CJK viene sottoposto a rendering, non interpretato.
Conformità
Sezione intitolata “Conformità”| Dichiarazione | Specifica | Clausola | reference_id |
|---|---|---|---|
| Per un font Type 0 Identity-H/Identity-V, la stringa visualizzata è composta da coppie di byte che indicizzano il CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x49.p90 | |
| L’array W2 fornisce le metriche di scrittura verticale per glifo e si applica solo ai CIDFont utilizzati per la scrittura verticale. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p23 | |
| L’array DW2 fornisce le metriche di scrittura verticale predefinite per un CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p22 |
Questa ricetta dimostra che la facade di codifica CJK cmap-aware è raggiungibile da userland (Fase 1). Non rivendica, per il file prodotto, né output di scrittura verticale né conformità PDF/UA-2 / PDF/A-4. L’emissione lato writer di /ToUnicode e delle metriche verticali (Fasi 3 e 4) è in sospeso e, allo stato attuale, un checker non convaliderebbe questo output.
Contesto commerciale
Sezione intitolata “Contesto commerciale”Non applicabile.