Tipografi: registri fon, subsetting, CMap, encoding, BiDi
Sekilas pandang
Bagian berjudul “Sekilas pandang”Modul tipografi mengubah berkas fon dan string Unicode menjadi byte yang dibutuhkan oleh content stream Portable Document Format (PDF). Modul ini menangani penguraian fon, registri sepanjang masa hidup proses, subsetting glif, keluaran CMap ToUnicode, strategi encoding yang sadar cmap, dan mesin dwiarah Unicode.
Instalasi
Bagian berjudul “Instalasi”composer require nextpdf/core:^3Tinjauan konseptual
Bagian berjudul “Tinjauan konseptual”FontRegistry menyimpan fon selama masa hidup proses dan mengimplementasikan FontRegistryInterface. Registri menguraikan berkas TrueType, OpenType, TrueType Collection (TTC), atau Type 1 (Printer Font Binary (PFB) dan Adobe Font Metrics (AFM)) satu kali, lalu mengembalikan FontInfo yang tak dapat diubah. Gunakan registri ini untuk worker yang berjalan lama: panaskan kumpulan fon saat boot, lalu panggil lock(). Setelah itu, registri menolak setiap mutasi, sementara pencarian tetap melayani lalu lintas. Registri hanya menyimpan data PHP murni: metadata yang telah diuraikan dan byte fon mentah. Sebuah worker pool dapat berbagi satu instans. registerFromBinary() menerima byte fon mentah, yang digunakan oleh jembatan @font-face HyperText Markup Language (HTML) untuk fon yang diambil dari sumber jarak jauh atau data URI.
Mesin menyematkan dan melakukan subsetting pada setiap fon yang digunakannya. Program fon yang disematkan ikut masuk ke PDF, sehingga dokumen dirender konsisten di setiap penampil dan tidak bergantung pada fon sistem yang terpasang — ISO 32000-2 §9. Sebuah subset hanya membawa glif yang dirujuk dokumen, yang penting untuk konten Tionghoa, Jepang, dan Korea (CJK) atau konten yang kaya Unicode — ISO 32000-2 §9. FontSubsetter menguraikan direktori tabel asli, mengekstrak cmap, menyelesaikan dependensi glif komposit sebagai penutupan transitif, dan membangun ulang tabel head, hhea, maxp, cmap, loca, glyf, dan hmtx. Subsetter mempertahankan penomoran pengidentifikasi glif asli dan mengisi nol slot yang tidak terpakai, sehingga CIDToGIDMap bertipe /Identity tetap valid. Subsetter mengembalikan fon asli tanpa perubahan ketika subset akan menghemat kurang dari sepuluh persen, sehingga pekerjaan yang tidak sepadan dapat dihindari. CffSubsetter melakukan operasi yang sama untuk fon OpenType yang membawa tabel outline Compact Font Format.
Emisi teks memiliki tiga lapisan penerjemahan: titik kode Unicode, kode karakter di content stream, dan pengidentifikasi glif di dalam fon. Modul menjaga jalur ini tetap eksplisit. FontInfo::encodeText() adalah fasadnya; FontEncodingStrategyResolver mendistribusikan strategi per fon. Fon TrueType atau OpenType tersemat dengan cmap Unicode dialihkan ke TrueTypeCmapStrategy, yang menghasilkan stream heksadesimal Identity-H dua-byte. Inilah bentuk yang dibutuhkan oleh fon Type 0 dengan CMap Identity-H dan turunan CIDFontType2 (ISO 32000-2 §9.7.4; digest chunk retrieval-augmented generation (RAG) yang cocok dikembalikan terpotong oleh batas lisensi, dicatat dalam _downgraded-claims-o3.md). Semua fon lainnya — fon standar Base 14, Type 1 PFB dan AFM — dialihkan ke Base14EncodingStrategy, yang menghasilkan literal string WinAnsi satu-byte. Stream tersebut mencakup seluruh repertoar WinAnsiEncoding (Windows code page 1252) — Latin beraksen, tanda Euro, serta tanda baca tipografis umum. Titik kode di luar repertoar itu digugurkan dari stream satu-byte dan menjalani fallback fon per-cluster ketika sebuah fon yang mencakupinya didaftarkan (ISO 32000-2 Annex D.2). Resolver mencakup seluruh ruang nilai FontInfo; tidak ada jalur yang dapat bernilai null. ToUnicodeCMapBuilder membangun resource /ToUnicode yang memungkinkan pembaca memulihkan Unicode asli dari fon Identity-H. Builder menerapkan penggabungan bfrange secara greedy dan batas blok 100 entri.
BidiEngine adalah layanan batas untuk Unicode Bidirectional Algorithm, yang didefinisikan oleh Unicode Standard Annex #9 (UAX #9), Unicode 16. Saat dukungan isolate dimatikan, ia mendelegasikan ke resolver lawas sehingga pemanggil yang ada melihat perilaku yang sama. Saat dukungan isolate dihidupkan, ia menjalankan pipeline yang sadar isolate: stack isolate eksplisit dengan kedalaman maksimum 125, lintasan tipe lemah, lintasan tipe netral termasuk penyelesaian pasangan tanda kurung, serta lintasan tingkat implisit dan penyusunan ulang baris. Cakupan glif CJK untuk fon kandidat adalah diagnostik terpisah: CjkFontValidator mengambil sampel blok Unicode yang dibutuhkan per skrip dan melaporkan persentase cakupan.
Permukaan API
Bagian berjudul “Permukaan API”| Tipe | Jenis | Anggota utama | Stabilitas | Sejak |
|---|---|---|---|---|
FontRegistry | final class | register(), registerType1(), registerFromBinary(), registerFromDirectory(), get(), has(), all(), warmup(), lock(), isLocked(), memoryUsage() | stabil | 1.7.0 |
FontInfo | final readonly class | $family, $type, $widths, $unicodeMap, $cmapForward, getKey(), encodeText() | stabil | 1.0.0 |
FontSubsetter | final class | subset(string, array<int>, int): string | stabil | 1.0.0 |
CffSubsetter | final class | Subsetting outline OpenType/CFF | stabil | 1.0.0 |
FontEncodingStrategyResolver | final class | resolve(FontInfo): FontEncodingStrategy | stabil | 2.7.0 |
ToUnicodeCMapBuilder | final class | buildFromRun(), buildFromMap(), encodeUnicodeUtf16Be() | stabil | 2.7.0 |
BidiEngine | final class | Penyelesaian UAX #9 yang sadar-isolate | stabil | 3.1.0 |
CjkFontValidator | final class | validateCoverage(), detectScript(), isCjkCodepoint() | stabil | 1.0.0 |
FontInfo bersifat tak dapat diubah: tanda tangan konstruktor dan properti publiknya dibekukan. Strategi encoding adalah fungsi murni dari (FontInfo, UTF-8 text): masukan yang sama mengembalikan EncodedGlyphRun yang sama pada setiap pemanggilan.
Contoh kode — Mulai cepat
Bagian berjudul “Contoh kode — Mulai cepat”<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Typography\Encoding\EncodingMode;use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();$cjkFont = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
$encoded = $cjkFont->encodeText('PDF 2.0 引擎 — 使用 CMap 編碼');
// An embedded CJK TrueType face resolves to the two-byte Identity-H path.assert($encoded->mode === EncodingMode::TwoByteCid);register() menguraikan fon satu kali dan mengembalikan FontInfo yang tak dapat diubah. encodeText() dialihkan melalui resolver dan mengembalikan EncodedGlyphRun berisi byte stream, operand string PDF, lebar advance per glif, dan peta pengidentifikasi glif (GID)-ke-Unicode yang dikonsumsi oleh CMap /ToUnicode.
Contoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Exception\NextPdfException;use NextPDF\Typography\FontRegistry;use Psr\Log\LoggerInterface;
final readonly class FontBootstrap{ public function __construct( private FontRegistry $registry, private LoggerInterface $logger, ) {}
/** * Warm a font set at worker boot, then lock the registry for the * lifetime of the process. * * @param list<string> $fontFiles Absolute paths to font files. */ public function boot(array $fontFiles): void { try { $this->registry->warmup($fontFiles); $this->registry->lock(); } catch (NextPdfException $e) { $this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e; }
$report = $this->registry->memoryUsage(); $this->logger->info('Font cache primed', [ 'fonts' => $report->entryCount, 'bytes' => $report->currentBytes, ]); }}warmup() diikuti oleh lock() adalah urutan boot worker. Setelah lock(), setiap mutasi melempar pengecualian, sementara pencarian tetap melayani lalu lintas. memoryUsage() mengembalikan MemoryReport, sehingga worker dapat melacak cache fon terhadap anggarannya.
Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- Ketika registri terkunci, ia menolak
register(),registerFromBinary(),addFontDirectory(), danwarmup(). Panaskan dan kunci saat boot; jangan pernah mendaftarkan fon selama penanganan permintaan. FontSubsetter::subset()mengembalikan byte asli tanpa perubahan ketika penghematan akan berada di bawah sepuluh persen atau ketika sebuah tabel esensial hilang. Fon yang dikembalikan sama dengan masukan adalah jalur tanpa-keuntungan yang terdokumentasi, bukan kegagalan.- Subsetter mempertahankan penomoran pengidentifikasi glif asli dan mengisi nol glif yang tidak terpakai. Ini menjaga
CIDToGIDMap /Identitytetap valid; jangan berasumsi pengidentifikasi glif dinomori ulang ke dalam rentang yang berurutan. registerFromBinary()menulis byte ke berkas sementara untuk penguraian, lalu menghapus berkas berekstensi maupun berkas dasartempnam()dalam blokfinally. Data fon yang tidak tepercaya merupakan permukaan serangan penguraian; bendung sebelum mencapai parser (lihat Catatan keamanan).BidiEnginemendelegasikan secara verbatim ke resolver lawas ketika dukungan isolate dimatikan. Karakter pemformatan isolate kemudian diteruskan sebagai netral batas. Hidupkan dukungan isolate melalui kebijakan konformansi untuk mendapatkan perilaku UAX #9 secara penuh.CjkFontValidatormengambil sampel titik kode pada interval tertentu alih-alih menguji setiap titik kode, sehingga angka cakupannya merupakan estimasi yang memadai secara statistik, bukan hitungan menyeluruh.
Kinerja
Bagian berjudul “Kinerja”Penguraian fon mendominasi penggunaan pertama; registri mengamortisasi biaya itu menjadi satu kali per proses. Setelah warmup, get() dan has() adalah pencarian peta O(1). Biaya subsetting berskala mengikuti jumlah glif yang digunakan dokumen, bukan seluruh tabel glif fon. Itulah sebabnya subsetting meningkatkan ukuran sekaligus kecepatan untuk konten CJK: subsetter menangani fon dengan 20,000-plus glif melalui pencarian biner, buffer yang dialokasikan di muka, dan operasi string secara massal. Penyelesaian glif komposit dibatasi; prosesnya berhenti pada 100 iterasi penutupan untuk bertahan terhadap referensi komponen melingkar. Parser cmap Format 12 membatasi jumlah grup dan entri untuk membatasi penggunaan memori pada masukan fon berbahaya. performance_budget sebesar 1500 ms wall dan 64 MB puncak mencakup warmup fon biasa ditambah perenderan dokumen.
Catatan keamanan
Bagian berjudul “Catatan keamanan”Dua permukaan memiliki bobot keamanan. Yang pertama adalah masukan fon. register() dan registerFromBinary() menguraikan byte sembarang. registerFromBinary() mewujudkannya sebagai berkas sementara. Batas menolak stream wrapper dan byte null dalam path. Data fon yang tidak tepercaya harus melewati kebijakan sumber daya eksternal yang membatasi ukuran berkas dan jumlah glif sebelum mencapai parser. Pembaca biner subsetter memeriksa batas setiap offset. Parser cmap membatasi jumlah grup, entri, dan tabel (numGroups > 31000 dan batas entri sebesar 200,000 pada Format 12) sehingga fon yang dibuat secara jahat tidak dapat memaksa alokasi tak terbatas. Permukaan kedua adalah pemulihan teks: ToUnicodeCMapBuilder memvalidasi bahwa setiap kode karakter berada di dalam codespace 16-bit dan setiap nilai Unicode adalah skalar yang valid. Ia menolak separuh surrogate, sehingga peta yang cacat tidak dapat menghasilkan resource ekstraksi yang rusak. Perlakukan setiap fon atau teks yang dipasok dari luar sebagai tidak tepercaya.
Konformansi
Bagian berjudul “Konformansi”| Klaim | Standar | Klausa | Bukti |
|---|---|---|---|
| Setiap fon yang digunakan oleh dokumen disematkan sehingga dokumen dirender tanpa bergantung pada fon sistem. | ISO 32000-2 | §9 | |
| Fon yang disematkan di-subset menjadi glif yang dirujuk dokumen. | ISO 32000-2 | §9 | |
Wajah TrueType CJK yang disematkan dipancarkan sebagai fon Type 0 dengan CMap Identity-H dan turunan CIDFontType2. | ISO 32000-2 | §9.7.4 | Digest RAG terpotong oleh batas lisensi; prefiks 7a5258772f508e3b, lihat _downgraded-claims-o3.md |
Dua klausa pertama berbentuk parafrase dan dipinkan-digest. Digest RAG penuh untuk klausa ketiga tidak dikembalikan (pemotongan batas lisensi); ADR-013 dan tinjauan pengembang cmap-encoder mengukuhkannya, dan klaim ini dicatat sebagai diturunkan. NextPDF tidak mereproduksi teks normatif. Konformansi PDF/A-4 dan PDF/UA-2 untuk konten CJK dibendung pada subsetting sisi penulis dan perkabelan /ToUnicode yang dilacak di sana.
Konteks komersial
Bagian berjudul “Konteks komersial”Paket fitur OpenType komersial dan rantai font-fallback premium dibangun di atas lapisan registri dan encoding Core. Modul tipografi Core menyematkan, melakukan subsetting, dan mengodekan setiap fon tanpa lisensi; paket berbayar menambahkan penyelesaian fallback yang dikurasi. Tautan konversi sengaja dihilangkan: halaman ini adalah dokumentasi, bukan jalur penjualan.
Lihat juga
Bagian berjudul “Lihat juga”- Font: registri TrueType, OpenType, dan CID — tipe nilai fon, penyematan, dan fallback.
- Teks: shaping, pemenggalan, BiDi — penanganan run dan shaping yang mengonsumsi glif yang telah dikodekan.
- Contracts / Typography — kontrak
FontRegistryInterfacedan pra-pemroses teks. - Mesin rendereran HTML — jembatan
@font-faceyang memanggilregisterFromBinary().