Ga naar inhoud

CJK-tekst verwerken met cmap-bewuste codering

Dit recipe registreert een Chinees, Japans en Koreaans (CJK) TrueType-lettertype en codeert daarna traditioneel-Chinese tekst via de cmap-bewuste FontInfo::encodeText()-facade. De facade retourneert een Identity-H tweebyte-CID-bytestream. Het recipe volgt examples/35-cjk-cmap-demo.php. Lees de scope-notitie voordat je erop vertrouwt.

De cmap-bewuste tekstcodeerarchitectuur wordt in fasen uitgeleverd (ADR-013). Fase 1 is opgeleverd: de FontInfo::encodeText()-facade en de cmap-bewuste coderingsstrategie zijn aangesloten en bereikbaar vanuit userland. Fase 2 is in uitvoering: deze routeert de renderer en de writer via de facade. Fase 3 en 4 zijn nog in behandeling: de emissie per lettertype van /ToUnicode, /CIDSystemInfo, /Encoding en /CIDToGIDMap, plus de resolver voor vervangende lettertypen, zijn nog niet aangesloten op de writer.

Houd in je planning rekening met deze gevolgen:

  • Dit recipe demonstreert de codeerfacade, niet een volledige modus voor verticaal schrijven. Het documentoppervlak heeft momenteel geen publieke API voor de schrijfmodus, dus er is geen setWritingMode-aanroep en geen vertical-rl-setter.
  • Het onderliggende voorbeeld is, volgens de eigen kop, een integratie-smoketest, geen conformiteitsfixture. De validatie van PDF/UA-2 en PDF/A-4 zal regresseren voor uitvoer die op deze manier wordt geproduceerd, totdat fase 3 en 4 landen. Ga er niet van uit dat uitvoer via dit pad conform is. Een checker bepaalt conformiteit, en die laat deze uitvoer nog niet door.
  • De infrastructuur voor metingen bij verticaal schrijven bestaat, maar is intern. Deze omvat het waardeobject CjkVerticalMetrics en de emitters voor /W2 en /DW2. NextPDF stelt deze niet beschikbaar als userland-aanroep voor “verticaal schrijven”, en de writer emitteert de bijbehorende dictionaries nog niet.
Terminal window
composer require nextpdf/core:^3

De constraint komt overeen met het pakket nextpdf/core. Het voorbeeld draait op PHP 8.4. Dankzij de meegeleverde Noto Sans TC-testfixture blijft dit recipe op zichzelf staand.

ISO 32000-2 modelleert tekstuitvoer in drie lagen: Unicode-codepunt, tekencode en glief-ID. Voor een CJK TrueType-lettertype gebruikt de engine een samengesteld Type 0-lettertype met Identity-H-codering. Met deze codering gebruikt de getoonde tekenreeks bytepaarindexen in de CIDFont (ISO 32000-2).

FontRegistry::register() parseert het lettertype. FontInfo::encodeText($unicodeText) bepaalt vervolgens een coderingsstrategie via FontEncodingStrategyResolver. Voor een geregistreerd TrueType CJK-lettertype dispatcht deze naar TrueTypeCmapStrategy. De geretourneerde EncodedGlyphRun bevat de Identity-H-bytestream, de PDF-tekenreeksoperand, de advance widths per glief, de gebruikte codepunten en de GID→Unicode-toewijzing. CJK-subsetting gebruikt de codepunten conform ADR-008. Een toekomstige /ToUnicode-stream zal de GID→Unicode-toewijzing gebruiken. De geselecteerde modus is EncodingMode::TwoByteCid.

Twee CIDFont-structuren definiëren verticaal schrijven in PDF. De eerste is de /W2-array met verticale metingen per glief (ISO 32000-2). De tweede is /DW2, de standaard verticale meting (ISO 32000-2). NextPDF levert het waardeobject en de emitters voor beide via CjkVerticalMetrics::toW2Array(), toW2RangeArray() en toDw2Array(). Ze zijn intern, en de writer emitteert ze nog niet. Zie de scope-notitie.

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry.
  • FontInfo::encodeText(string $unicodeText): EncodedGlyphRunNextPDF\Typography\FontInfo. De facade van fase 1.
  • EncodedGlyphRunNextPDF\Typography\Encoding\EncodedGlyphRun (byteStream, pdfStringOperand, mode, advanceWidths, toUnicodeMap, usedCodepoints, glyphCount()).
  • EncodingModeNextPDF\Typography\Encoding\EncodingMode (SingleByte, TwoByteCid).
  • CjkVerticalMetricsNextPDF\Typography\CjkVerticalMetrics. Intern waardeobject voor verticale metingen. Het is gedocumenteerd voor transparantie, niet als userland-schrijfpad.

De volledige PHPDoc-tabel wordt uit de broncode gegenereerd.

<?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 fired
echo $encoded->glyphCount() . " glyph run entries\n";

Dit voorbeeld is op zichzelf staand en uitvoerbaar via de harness. Het weerspiegelt examples/35-cjk-cmap-demo.php. Registreer eerst de meegeleverde Noto Sans TC-fixture. Controleer vervolgens dat de cmap-bewuste facade bereikbaar is. Render daarna via DocumentFactory, zodat het document de registry gebruikt die je hebt gevuld.

<?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";

Verwachte STDOUT:

Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)
  • Geen conformiteitsfixture. Volgens de eigen kop van het onderliggende voorbeeld is deze uitvoer een integratie-smoketest. De controles voor PDF/UA-2 en PDF/A-4 regresseren ervoor totdat fase 3 en 4 landen. Gebruik deze uitvoer niet als conformiteits-golden.
  • Geen API voor de schrijfmodus. Geen publieke aanroep schakelt over op verticaal schrijven, wat vertical-rl en vertical-lr zou dekken. De emitters voor /W2 en /DW2 bestaan intern. Ze zijn niet beschikbaar gesteld en worden nog niet naar de lettertype-dictionary geschreven.
  • Eigenaarschap van de registry. Document::createStandalone() bouwt een eigen registry. Gebruik DocumentFactory, zodat het document de registry leest die je met het CJK-lettertype hebt gevuld.
  • Route van de uiteindelijke bytestream. Totdat fase 2 is afgerond, routeert de zichtbare contentstream nog steeds via het verouderde tekstpad. Het bewezen, bereikbare deel van vandaag is de upstream-coderingsstap: de voorwaartse cmap-lookup plus de Identity-H-bytestream.
  • Kosten van CJK-subsetting. Grote CJK-lettertypen worden gesubset via een geïsoleerd subproces. Dat subproces heeft een PHP-native fallback en een time-out van twee seconden (ADR-008).

encodeText() maakt één voorwaartse cmap-lookup-pass over de invoer. Het is lineair in het aantal codepunten, O(n). Het budget is wall_ms: 2000, peak_mb: 128. Dit budget is het hoogste in deze reeks omdat CJK-lettertypen groot zijn en subsetting de dominante kostenpost is. ADR-008 isoleert dat werk, zodat het de aanroeper niet kan blokkeren.

Een CJK-lettertypebestand is niet-vertrouwde binaire invoer. De parser weigert stream-wrapper-paden en null-bytes. CJK-subsetting draait in een geïsoleerd subproces zonder overgenomen state (ADR-008). Valideer de herkomst van lettertypen die eindgebruikers aanleveren. CJK-tekstinhoud wordt weergegeven, niet geïnterpreteerd.

BeweringSpecClausulereference_id
Voor een Identity-H/Identity-V Type 0-lettertype gebruikt de getoonde tekenreeks bytepaarindexen in de CIDFont.ISO 32000-2iso32000_2_sec9#x1.x49.p90
De W2-array geeft verticale metingen per glief en is alleen van toepassing op CIDFonts die voor verticaal schrijven worden gebruikt.ISO 32000-2iso32000_2_sec9#x1.x44.p23
De DW2-array geeft de standaard verticale metingen voor een CIDFont.ISO 32000-2iso32000_2_sec9#x1.x44.p22

Dit recipe toont aan dat de cmap-bewuste CJK-codeerfacade bereikbaar is vanuit userland (fase 1). Het claimt geen uitvoer voor verticaal schrijven of PDF/UA-2- / PDF/A-4-conformiteit voor het geproduceerde bestand. De emissie aan writer-zijde van /ToUnicode en verticale metingen (fase 3 en 4) is nog in behandeling, en een checker zou deze uitvoer momenteel niet doorlaten.

Niet van toepassing.