Composer du texte CJK avec un encodage tenant compte de la cmap
Ce recipe enregistre une fonte CJK TrueType, puis encode du texte en chinois traditionnel au moyen de la façade FontInfo::encodeText(), qui tient compte de la cmap. La façade produit un flux d’octets Identity-H fondé sur des CID de deux octets. Ce recipe suit examples/35-cjk-cmap-demo.php. Lis la note de portée ci-dessous avant de t’appuyer dessus.
Portée et statut (à lire en premier)
Section intitulée « Portée et statut (à lire en premier) »L’architecture d’encodage de texte tenant compte de la cmap est livrée par phases (ADR-013). La phase 1 est livrée : la façade FontInfo::encodeText() et la stratégie d’encodage tenant compte de la cmap sont câblées et accessibles depuis l’espace utilisateur. La phase 2 est en cours : elle fait passer le renderer et le writer par la façade. Les phases 3 et 4 sont en attente : l’émission par fonte de /ToUnicode, /CIDSystemInfo, /Encoding et /CIDToGIDMap, ainsi que le résolveur de fonte de substitution, ne sont pas encore câblés dans le writer.
Organise-toi en tenant compte de ces conséquences :
- Ce recipe illustre la façade d’encodage, et non un mode d’écriture verticale prêt à l’emploi. La surface du document n’expose aujourd’hui aucune API publique de mode d’écriture, c’est-à-dire aucun
setWritingModeet aucun settervertical-rl. - L’exemple sous-jacent est, selon son propre en-tête, un test de fumée d’intégration, pas une fixture de conformité. La validation PDF/UA-2 et PDF/A-4 va régresser pour une sortie produite de cette façon, tant que les phases 3 et 4 n’ont pas été livrées. N’affirme pas que la sortie issue de ce chemin est conforme. La conformité relève du vérificateur, et il ne validera pas encore cette sortie.
- L’infrastructure de métriques d’écriture verticale existe, mais elle est interne. Elle se compose de l’objet valeur
CjkVerticalMetricset des émetteurs/W2et/DW2. NextPDF ne l’expose pas comme un appel « écrire verticalement » dans l’espace utilisateur, et le writer n’émet pas encore les dictionnaires correspondants.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3La contrainte correspond au package nextpdf/core. L’exemple s’exécute sous PHP 8.4. Une fixture de test Noto Sans TC fournie permet au recipe de rester autonome.
Aperçu conceptuel
Section intitulée « Aperçu conceptuel »ISO 32000-2 modélise l’émission de texte en trois couches : le point de code Unicode, le code de caractère et l’identifiant de glyphe. Pour une fonte CJK TrueType, le moteur utilise une fonte Type 0 composite avec l’encodage Identity-H. Avec cet encodage, la chaîne affichée est une suite de paires d’octets qui indexent la CIDFont (ISO 32000-2).
FontRegistry::register() analyse la fonte. FontInfo::encodeText($unicodeText) résout ensuite une stratégie d’encodage via FontEncodingStrategyResolver. Pour une fonte CJK TrueType enregistrée, il aiguille vers TrueTypeCmapStrategy. Le EncodedGlyphRun renvoyé contient le flux d’octets Identity-H, l’opérande de chaîne PDF, les largeurs d’avance par glyphe, les points de code utilisés et la table GID→Unicode. Le sous-ensemblage CJK utilise les points de code utilisés selon l’ADR-008. Un futur flux /ToUnicode utilisera la table GID→Unicode. Le mode sélectionné est EncodingMode::TwoByteCid.
Deux structures de CIDFont définissent l’écriture verticale en PDF. La première est le tableau de métriques verticales par glyphe /W2 (ISO 32000-2). La seconde est l’ensemble de métriques verticales par défaut /DW2 (ISO 32000-2). NextPDF possède l’objet valeur et les émetteurs pour les deux, via CjkVerticalMetrics::toW2Array(), toW2RangeArray() et toDw2Array(). Ces éléments restent internes, et le writer ne les émet pas encore. Voir la note de portée.
Surface de l’API
Section intitulée « Surface de l’API »FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.FontInfo::encodeText(string $unicodeText): EncodedGlyphRun—NextPDF\Typography\FontInfo. La façade de la phase 1.EncodedGlyphRun—NextPDF\Typography\Encoding\EncodedGlyphRun(byteStream,pdfStringOperand,mode,advanceWidths,toUnicodeMap,usedCodepoints,glyphCount()).EncodingMode—NextPDF\Typography\Encoding\EncodingMode(SingleByte,TwoByteCid).CjkVerticalMetrics—NextPDF\Typography\CjkVerticalMetrics. Objet valeur interne de métriques verticales. Il est documenté par transparence, pas comme un chemin d’écriture dans l’espace utilisateur.
Le tableau PHPDoc complet est généré depuis le code source.
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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";Exemple de code — Production
Section intitulée « Exemple de code — Production »Cet exemple est autonome et exécutable par le harnais. Il reflète examples/35-cjk-cmap-demo.php. Enregistre d’abord la fixture Noto Sans TC fournie. Puis confirme que la façade tenant compte de la cmap est accessible. Effectue ensuite le rendu via DocumentFactory pour que le registre prérempli soit bien utilisé.
<?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";Sortie STDOUT attendue :
Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)Cas limites et pièges
Section intitulée « Cas limites et pièges »- Pas une fixture de conformité. D’après l’en-tête même de l’exemple sous-jacent, cette sortie est un test de fumée d’intégration. Les contrôles PDF/UA-2 et PDF/A-4 régressent pour elle tant que les phases 3 et 4 n’ont pas été livrées. Ne l’enregistre pas comme golden de conformité.
- Pas d’API de mode d’écriture. Aucun appel public ne bascule vers l’écriture verticale, ce qui couvrirait
vertical-rletvertical-lr. Les émetteurs/W2et/DW2existent en interne. Ils ne sont ni exposés ni encore écrits dans le dictionnaire de fonte. - Propriété du registre.
Document::createStandalone()construit son propre registre. UtiliseDocumentFactorypour que le document utilise le registre que tu as rempli avec la fonte CJK. - Chemin final du flux d’octets. Tant que la phase 2 n’est pas terminée, le flux de contenu visible passe encore par le chemin de texte historique. La partie démontrée et accessible aujourd’hui est l’étape d’encodage en amont, c’est-à-dire la recherche directe dans la cmap et le flux d’octets Identity-H.
- Coût du sous-ensemblage CJK. Les grandes fontes CJK sont sous-ensemblées dans un sous-processus isolé. Ce sous-processus dispose d’un repli natif en PHP et d’un délai d’attente de deux secondes (ADR-008).
Performance
Section intitulée « Performance »encodeText() effectue une seule passe de recherche directe dans la cmap sur l’entrée. Sa complexité est linéaire en nombre de points de code, en O(n). Le budget est wall_ms: 2000, peak_mb: 128. Ce budget est le plus élevé de cet ensemble, car les fontes CJK sont grandes et leur sous-ensemblage est le coût dominant. L’ADR-008 isole ce travail pour qu’il ne puisse pas bloquer l’appelant.
Notes de sécurité
Section intitulée « Notes de sécurité »Un fichier de fonte CJK est une entrée binaire non fiable. L’analyseur rejette les chemins utilisant un wrapper de flux et les octets nuls. Le sous-ensemblage CJK s’exécute dans un sous-processus isolé sans état hérité (ADR-008). Valide la provenance des fontes fournies par l’utilisateur final. Le contenu textuel CJK est rendu, pas interprété.
Conformité
Section intitulée « Conformité »| Énoncé | Spécification | Clause | reference_id |
|---|---|---|---|
| Pour une fonte Type 0 Identity-H/Identity-V, la chaîne affichée est une suite de paires d’octets qui indexent la CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x49.p90 | |
| Le tableau W2 donne les métriques d’écriture verticale par glyphe et ne s’applique qu’aux CIDFonts utilisées pour l’écriture verticale. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p23 | |
| Le tableau DW2 donne les métriques d’écriture verticale par défaut d’une CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p22 |
Ce recipe montre que la façade d’encodage CJK tenant compte de la cmap est accessible depuis l’espace utilisateur (phase 1). Il ne prétend pas que le fichier produit fournit une sortie en écriture verticale ni une conformité PDF/UA-2 / PDF/A-4. L’émission par le writer de /ToUnicode et des métriques verticales (phases 3 et 4) est en attente, et un vérificateur ne validerait pas cette sortie aujourd’hui.
Contexte commercial
Section intitulée « Contexte commercial »Sans objet.