Składaj tekst CJK z użyciem kodowania uwzględniającego cmap
W skrócie
Dział zatytułowany „W skrócie”Ten przepis rejestruje krój TrueType obsługujący języki chiński, japoński i koreański (CJK), a następnie koduje tekst w języku chińskim tradycyjnym przez fasadę FontInfo::encodeText() uwzględniającą cmap. Fasada zwraca strumień bajtów Identity-H z dwubajtowymi CID. Przepis wzoruje się na examples/35-cjk-cmap-demo.php. Zanim oprzesz na nim pracę, przeczytaj uwagę o zakresie.
Zakres i status (przeczytaj najpierw)
Dział zatytułowany „Zakres i status (przeczytaj najpierw)”Architektura kodowania tekstu uwzględniająca cmap jest wdrażana etapami (ADR-013). Etap 1 jest gotowy: fasada FontInfo::encodeText() oraz strategia kodowania uwzględniająca cmap są podłączone i dostępne z poziomu kodu użytkownika. Etap 2 jest w toku: obejmuje skierowanie renderer i writer przez fasadę. Etapy 3 i 4 są w przygotowaniu: emisja dla poszczególnych czcionek /ToUnicode, /CIDSystemInfo, /Encoding oraz /CIDToGIDMap, a także resolver czcionek zastępczych, nie są jeszcze podłączone do writera.
Planuj z uwzględnieniem tych konsekwencji:
- Ten przepis demonstruje fasadę kodowania, a nie kompletny tryb pisma pionowego. Powierzchnia dokumentu nie udostępnia dziś publicznego API trybu pisma, więc nie ma wywołania
setWritingModeani setteravertical-rl. - Towarzyszący przykład jest, jak wskazuje jego własny nagłówek, integracyjnym testem dymnym, a nie fiksturą zgodności. Walidacja PDF/UA-2 i PDF/A-4 ulegnie regresji dla danych wyjściowych utworzonych w ten sposób, dopóki etapy 3 i 4 nie zostaną wdrożone. Nie deklaruj zgodności danych wyjściowych z tej ścieżki. O zgodności decyduje walidator, który obecnie nie zatwierdzi tych danych wyjściowych.
- Infrastruktura metryk pisma pionowego istnieje, ale jest wewnętrzna. Obejmuje obiekt wartości
CjkVerticalMetricsoraz emitery/W2i/DW2. NextPDF nie udostępnia jej jako wywołania „pisz pionowo” na poziomie kodu użytkownika, a writer nie emituje jeszcze powiązanych słowników.
Instalacja
Dział zatytułowany „Instalacja”composer require nextpdf/core:^3To ograniczenie dotyczy pakietu nextpdf/core. Przykład działa na PHP 8.4. Dzięki dołączonej fiksturze testowej Noto Sans TC przepis jest samowystarczalny.
Przegląd koncepcyjny
Dział zatytułowany „Przegląd koncepcyjny”ISO 32000-2 modeluje emisję tekstu w trzech warstwach: punkt kodowy Unicode, kod znaku oraz identyfikator glifu. Dla kroju TrueType CJK silnik używa złożonej czcionki Type 0 z kodowaniem Identity-H. W tym kodowaniu wyświetlany ciąg składa się z par bajtów indeksujących CIDFont (ISO 32000-2).
FontRegistry::register() parsuje krój. FontInfo::encodeText($unicodeText) następnie wybiera strategię kodowania przez FontEncodingStrategyResolver. Dla zarejestrowanego kroju TrueType CJK przekazuje wywołanie do TrueTypeCmapStrategy. Zwracany EncodedGlyphRun zawiera strumień bajtów Identity-H, operand ciągu PDF, szerokości postępu dla poszczególnych glifów, użyte punkty kodowe oraz mapę GID→Unicode. Tworzenie podzbioru CJK używa punktów kodowych zgodnie z ADR-008. Przyszły strumień /ToUnicode użyje mapy GID→Unicode. Wybranym trybem jest EncodingMode::TwoByteCid.
Pismo pionowe w PDF definiują dwie struktury CIDFont. Pierwszą jest tablica metryk pionowych dla poszczególnych glifów /W2 (ISO 32000-2). Drugą są domyślne metryki pionowe /DW2 (ISO 32000-2). NextPDF dostarcza dla nich obiekt wartości oraz emitery przez CjkVerticalMetrics::toW2Array(), toW2RangeArray() oraz toDw2Array(). Te elementy są wewnętrzne, a writer jeszcze ich nie emituje. Zobacz uwagę o zakresie.
Powierzchnia API
Dział zatytułowany „Powierzchnia API”FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfo—NextPDF\Typography\FontRegistry.FontInfo::encodeText(string $unicodeText): EncodedGlyphRun—NextPDF\Typography\FontInfo. Fasada etapu 1.EncodedGlyphRun—NextPDF\Typography\Encoding\EncodedGlyphRun(byteStream,pdfStringOperand,mode,advanceWidths,toUnicodeMap,usedCodepoints,glyphCount()).EncodingMode—NextPDF\Typography\Encoding\EncodingMode(SingleByte,TwoByteCid).CjkVerticalMetrics—NextPDF\Typography\CjkVerticalMetrics. Wewnętrzny obiekt wartości metryk pionowych. Udokumentowano go dla przejrzystości, nie jako ścieżkę zapisu na poziomie kodu użytkownika.
Pełna tabela PHPDoc jest generowana ze źródła.
Przykład kodu — szybki start
Dział zatytułowany „Przykład kodu — szybki start”<?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";Przykład kodu — produkcja
Dział zatytułowany „Przykład kodu — produkcja”Ten przykład jest samowystarczalny i można go uruchomić w harnessie. Odzwierciedla examples/35-cjk-cmap-demo.php. Najpierw zarejestruj dołączoną fiksturę Noto Sans TC. Potem potwierdź, że fasada uwzględniająca cmap jest dostępna. Na końcu renderuj przez DocumentFactory, aby dokument korzystał z uzupełnionego rejestru.
<?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";Oczekiwany STDOUT:
Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)Przypadki brzegowe i pułapki
Dział zatytułowany „Przypadki brzegowe i pułapki”- To nie jest fikstura zgodności. Jak wskazuje własny nagłówek towarzyszącego przykładu, te dane wyjściowe są integracyjnym testem dymnym. Kontrole PDF/UA-2 i PDF/A-4 ulegają dla nich regresji, dopóki etapy 3 i 4 nie zostaną wdrożone. Nie rejestruj tego przykładu jako wzorca zgodności.
- Brak API trybu pisma. Żadne publiczne wywołanie nie przełącza dokumentu na pismo pionowe obejmujące
vertical-rlivertical-lr. Emitery/W2i/DW2istnieją wewnętrznie. Nie są udostępnione i nie są jeszcze zapisywane do słownika czcionki. - Własność rejestru.
Document::createStandalone()buduje własny rejestr. UżyjDocumentFactory, aby dokument korzystał z rejestru, który wypełniono krojem CJK. - Ścieżka końcowego strumienia bajtów. Dopóki etap 2 nie zostanie zamknięty, widoczny strumień treści nadal przechodzi przez starszą ścieżkę tekstu. Sprawdzoną, dostępną dziś częścią jest wcześniejszy krok kodowania: wyszukiwanie w przód w cmap oraz strumień bajtów Identity-H.
- Koszt tworzenia podzbioru CJK. Duże kroje CJK tworzą podzbiór przez izolowany podproces. Ten podproces ma mechanizm awaryjny natywny dla PHP oraz dwusekundowy limit czasu (ADR-008).
Wydajność
Dział zatytułowany „Wydajność”encodeText() wykonuje pojedyncze przejście wyszukiwania w przód w cmap po danych wejściowych. Jest liniowy względem liczby punktów kodowych, O(n). Budżet wynosi wall_ms: 2000, peak_mb: 128. Ten budżet jest najwyższy w tym zestawie, ponieważ kroje CJK są duże, a tworzenie podzbioru jest dominującym kosztem. ADR-008 izoluje tę operację, aby nie mogła zablokować kodu wywołującego.
Uwagi dotyczące bezpieczeństwa
Dział zatytułowany „Uwagi dotyczące bezpieczeństwa”Plik czcionki CJK jest niezaufanym wejściem binarnym. Parser odrzuca ścieżki ze stream wrapperami oraz bajtami null. Tworzenie podzbioru CJK działa w izolowanym podprocesie bez dziedziczonego stanu (ADR-008). Sprawdzaj pochodzenie krojów dostarczanych przez użytkownika końcowego. Treść tekstowa CJK jest renderowana, a nie interpretowana.
Zgodność
Dział zatytułowany „Zgodność”| Stwierdzenie | Specyfikacja | Klauzula | reference_id |
|---|---|---|---|
| Dla czcionki Type 0 z kodowaniem Identity-H/Identity-V wyświetlany ciąg składa się z par bajtów indeksujących CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x49.p90 | |
| Tablica W2 podaje metryki pisma pionowego dla poszczególnych glifów i dotyczy wyłącznie czcionek CIDFont używanych do pisma pionowego. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p23 | |
| Tablica DW2 podaje domyślne metryki pisma pionowego dla czcionki CIDFont. | ISO 32000-2 | iso32000_2_sec9#x1.x44.p22 |
Ten przepis pokazuje, że fasada kodowania CJK uwzględniająca cmap jest dostępna z poziomu kodu użytkownika (etap 1). Nie twierdzi, że utworzony plik zawiera dane wyjściowe pisma pionowego ani że jest zgodny z PDF/UA-2 / PDF/A-4. Emisja /ToUnicode oraz metryk pionowych po stronie writera (etapy 3 i 4) jest w przygotowaniu, a walidator obecnie nie zatwierdziłby tych danych wyjściowych.
Kontekst komercyjny
Dział zatytułowany „Kontekst komercyjny”Nie dotyczy.