Zum Inhalt springen

CJK-Text mit cmap-bewusster Kodierung setzen

Dieses Recipe registriert eine CJK-TrueType-Schrift und kodiert anschließend traditionellen chinesischen Text über die cmap-bewusste FontInfo::encodeText()-Fassade. Die Fassade erzeugt einen Identity-H-Bytestrom aus Zwei-Byte-CIDs. Das Recipe folgt examples/35-cjk-cmap-demo.php. Lesen Sie den Geltungsbereichshinweis weiter unten, bevor Sie sich darauf verlassen.

Die cmap-bewusste Textkodierungs-Architektur wird phasenweise eingeführt (ADR-013). Phase 1 ist verfügbar: Die FontInfo::encodeText()-Fassade und die cmap-bewusste Kodierungsstrategie sind angebunden und aus dem Userland erreichbar. Phase 2 ist in Arbeit: Sie führt den Renderer und den Writer über die Fassade. Phasen 3 und 4 stehen aus: Die schriftspezifische Ausgabe von /ToUnicode, /CIDSystemInfo, /Encoding und /CIDToGIDMap sowie der Ersatzschrift-Resolver sind im Writer noch nicht angebunden.

Planen Sie mit diesen Konsequenzen:

  • Dieses Recipe demonstriert die Kodierungsfassade, keinen schlüsselfertigen Modus für vertikales Schreiben. Die Dokumentoberfläche hat derzeit keine öffentliche Writing-Mode-API, das heißt, es gibt kein setWritingMode und keinen vertical-rl-Setter.
  • Das zugrunde liegende Beispiel ist laut eigenem Header ein Integrations-Smoke-Test, keine Konformitäts-Fixture. Die PDF/UA-2- und PDF/A-4-Validierung schlägt für auf diese Weise erzeugte Ausgaben fehl, bis die Phasen 3 und 4 vorliegen. Behaupten Sie nicht, dass Ausgaben aus diesem Pfad konform sind. Über die Konformität entscheidet ein Checker, und er lässt diese Ausgabe noch nicht durch.
  • Die Metrik-Infrastruktur für vertikales Schreiben existiert, ist aber intern. Sie besteht aus dem Value Object CjkVerticalMetrics sowie den Emittern für /W2 und /DW2. NextPDF stellt sie nicht als Userland-Aufruf „vertikal schreiben“ zur Verfügung, und der Writer gibt ihre Dictionaries noch nicht aus.
Terminal-Fenster
composer require nextpdf/core:^3

Die Version-Constraint passt zum Paket nextpdf/core. Das Beispiel läuft auf PHP 8.4. Eine mitgelieferte Noto-Sans-TC-Test-Fixture sorgt dafür, dass das Recipe in sich geschlossen bleibt.

ISO 32000-2 modelliert die Textausgabe in drei Schichten: Unicode-Codepoint, Character Code und Glyph-ID. Für eine CJK-TrueType-Schrift verwendet die Engine eine zusammengesetzte Type-0-Schrift mit Identity-H-Kodierung. Bei dieser Kodierung besteht der angezeigte String aus Byte-Paaren, die in die CIDFont indexieren (ISO 32000-2).

FontRegistry::register() parst die Schrift. FontInfo::encodeText($unicodeText) ermittelt anschließend über FontEncodingStrategyResolver eine Kodierungsstrategie. Für eine registrierte TrueType-CJK-Schrift delegiert er an TrueTypeCmapStrategy. Der zurückgegebene EncodedGlyphRun enthält den Identity-H-Bytestrom, den PDF-String-Operanden, die Vorschubbreiten pro Glyph, die verwendeten Codepoints und die GID→Unicode-Map. Das CJK-Subsetting verarbeitet die verwendeten Codepoints gemäß ADR-008. Ein künftiger /ToUnicode-Stream wird die GID→Unicode-Map nutzen. Der gewählte Modus ist EncodingMode::TwoByteCid.

Zwei CIDFont-Strukturen definieren das vertikale Schreiben in PDF. Die erste ist das /W2-Array mit den Vertikalmetriken pro Glyph (ISO 32000-2). Die zweite sind die /DW2-Standard-Vertikalmetriken (ISO 32000-2). NextPDF enthält das Value Object und die Emitter für beides, über CjkVerticalMetrics::toW2Array(), toW2RangeArray() und toDw2Array(). Sie sind intern, und der Writer gibt sie noch nicht aus. Siehe den Geltungsbereichshinweis.

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry.
  • FontInfo::encodeText(string $unicodeText): EncodedGlyphRunNextPDF\Typography\FontInfo. Die Phase-1-Fassade.
  • EncodedGlyphRunNextPDF\Typography\Encoding\EncodedGlyphRun (byteStream, pdfStringOperand, mode, advanceWidths, toUnicodeMap, usedCodepoints, glyphCount()).
  • EncodingModeNextPDF\Typography\Encoding\EncodingMode (SingleByte, TwoByteCid).
  • CjkVerticalMetricsNextPDF\Typography\CjkVerticalMetrics. Internes Value Object für Vertikalmetriken. Es ist zur Transparenz dokumentiert, nicht als Userland-Schreibpfad.

Die vollständige PHPDoc-Tabelle wird aus dem Quellcode generiert.

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

Dieses Beispiel ist in sich geschlossen und über den Harness ausführbar. Es spiegelt examples/35-cjk-cmap-demo.php wider. Registrieren Sie zuerst die mitgelieferte Noto-Sans-TC-Fixture. Bestätigen Sie anschließend, dass die cmap-bewusste Fassade erreichbar ist. Rendern Sie danach über DocumentFactory, damit die befüllte Registry verwendet wird.

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

Erwartete STDOUT:

Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)
  • Keine Konformitäts-Fixture. Laut dem eigenen Header des zugrunde liegenden Beispiels ist diese Ausgabe ein Integrations-Smoke-Test. Die PDF/UA-2- und PDF/A-4-Prüfungen schlagen dafür fehl, bis die Phasen 3 und 4 vorliegen. Registrieren Sie sie nicht als Konformitäts-Golden.
  • Keine Writing-Mode-API. Es gibt keinen öffentlichen Aufruf, der zum vertikalen Schreiben wechselt und vertical-rl sowie vertical-lr abdecken würde. Die Emitter für /W2 und /DW2 existieren intern. Sie sind nicht nach außen verfügbar gemacht und noch nicht in das Font-Dictionary geschrieben.
  • Registry-Eigentümerschaft. Document::createStandalone() baut seine eigene Registry. Verwenden Sie DocumentFactory, damit das Dokument die Registry liest, die Sie mit der CJK-Schrift befüllt haben.
  • Endgültiger Bytestrom-Pfad. Bis Phase 2 abgeschlossen ist, läuft der sichtbare Content-Stream weiterhin über den Legacy-Textpfad. Der heute nachweisbar erreichbare Teil ist der vorgelagerte Kodierungsschritt, also das cmap-Forward-Lookup plus der Identity-H-Bytestrom.
  • Kosten des CJK-Subsettings. Große CJK-Schriften werden über einen isolierten Subprozess subgesetzt. Dieser Subprozess hat einen PHP-nativen Fallback und ein Timeout von zwei Sekunden (ADR-008).

encodeText() führt einen einzigen cmap-Forward-Lookup-Durchlauf über die Eingabe aus. Er ist linear zur Anzahl der Codepoints, O(n). Das Budget ist wall_ms: 2000, peak_mb: 128. Dieses Budget ist das höchste in dieser Gruppe, weil CJK-Schriften groß sind und ihr Subsetting der dominierende Kostenfaktor ist. ADR-008 isoliert diese Arbeit, damit sie den Aufrufer nicht blockieren kann.

Eine CJK-Schriftdatei ist nicht vertrauenswürdige binäre Eingabe. Der Parser weist Stream-Wrapper-Pfade und Null-Bytes zurück. Das CJK-Subsetting läuft in einem isolierten Subprozess ohne vererbten State (ADR-008). Prüfen Sie die Provenienz (Herkunft) der Schrift bei Schriften, die vom Endnutzer bereitgestellt werden. CJK-Textinhalt wird gerendert, nicht interpretiert.

AussageSpecAbschnittreference_id
Bei einer Identity-H/Identity-V-Type-0-Schrift besteht der angezeigte String aus Byte-Paaren, die in die CIDFont indexieren.ISO 32000-2iso32000_2_sec9#x1.x49.p90
Das W2-Array liefert Metriken für vertikales Schreiben pro Glyph und gilt nur für CIDFonts, die für vertikales Schreiben verwendet werden.ISO 32000-2iso32000_2_sec9#x1.x44.p23
Das DW2-Array liefert die Standardmetriken für vertikales Schreiben bei einer CIDFont.ISO 32000-2iso32000_2_sec9#x1.x44.p22

Dieses Recipe zeigt, dass die cmap-bewusste CJK-Kodierungsfassade aus dem Userland erreichbar ist (Phase 1). Es behauptet nicht, dass die erzeugte Datei vertikales Schreiben ausgibt oder PDF/UA-2- / PDF/A-4-Konformität erreicht. Die Writer-seitige Ausgabe von /ToUnicode und der Vertikalmetriken (Phasen 3 und 4) steht aus, und ein Checker würde diese Ausgabe heute nicht durchlassen.

Nicht zutreffend.