Lewati ke konten

Contracts / Typography

Domain tipografi mendefinisikan kontrak untuk registri fon dan prapemrosesan teks: FontRegistryInterface, TextPreprocessorInterface, beserta objek nilai TextPreprocessResult dan TextSegment yang imutabel. Semuanya stable.

Terminal window
composer require nextpdf/core:^3

FontRegistryInterface adalah penyimpanan fon yang hidup sepanjang proses berjalan. Registri ini mendaftarkan fon TrueType, OpenType, TrueType Collection (TTC), atau Printer Font Binary (PFB), lalu mengembalikan metadata FontInfo yang telah diurai. Karena registri hidup lebih lama daripada dokumen individual, satu worker hanya perlu mengurai setiap fon sekali. Anda dapat melakukan warmup untuk sekumpulan fon saat boot, lalu mengunci registri agar lalu lintas produksi tidak dapat memutasinya. Registri yang terkunci melempar LogicException pada register(), addFontDirectory(), atau warmup(); operasi pencarian tetap tersedia. Registri ini juga menerima byte fon mentah melalui registerFromBinary(). Jembatan @font-face menggunakan metode ini untuk mendaftarkan fon yang diambil dari sumber jarak jauh atau data URI (uniform resource identifier). Registri hanya menyimpan data PHP murni, tanpa resource handle, sehingga dapat dibagikan ke seluruh worker pool.

Engine menyematkan dan melakukan subsetting pada setiap fon yang digunakannya. Program fon yang tersemat dibawa di dalam berkas Portable Document Format (PDF), sehingga dokumen dirender dengan hasil yang sama persis di penampil mana pun, terlepas dari fon sistem yang terpasang — ISO 32000-2 §9. Subset fon hanya memuat glif yang benar-benar dirujuk oleh dokumen. Ini terutama penting untuk konten Tionghoa, Jepang, dan Korea (CJK) atau konten lain yang kaya Unicode — ISO 32000-2 §9. Kontrak registri mengekspos metadata terurai yang digunakan pada tahap subsetting dan penyematan.

TextPreprocessorInterface mencegat teks sebelum teks masuk ke tata letak glif, subsetting fon, peta karakter ToUnicode (CMap), dan pohon struktur. Penempatan ini adalah properti keamanannya: praprosesor yang menyunting konten menghapus konten itu sebelum dapat mencapai content stream, subset fon, atau metadata. Kontrak ini membawa dua invarian. Praprosesor tidak boleh memasukkan karakter yang memengaruhi tata letak, dan harus mempertahankan urutan baca logis; tanggung jawabnya adalah substitusi konten, bukan tata letak. Hasilnya adalah TextPreprocessResult imutabel dengan daftar nilai TextSegment yang terurut. Sebuah segmen dapat berupa pass-through atau disunting. Untuk segmen yang disunting, teks tampilannya bergantung pada mode penyamaran: kosong untuk persegi panjang kotak hitam, tanda bintang sepanjang teks asli, atau label tetap. originalCharCount pada segmen adalah petunjuk pengukuran yang tidak dapat dibalik, dan hanya digunakan untuk menentukan ukuran persegi panjang penyuntingan. Petunjuk ini tidak boleh sekali-kali digunakan untuk merekonstruksi konten asli.

TipeJenisAnggota utamaStabilitasSejak
FontRegistryInterfaceinterfaceregister(), get(), has(), all(), addFontDirectory(), warmup(), lock(), isLocked(), registerBase14(), registerFromBinary(), memoryUsage()stable1.7.0
TextPreprocessorInterfaceinterfaceprocess(string): TextPreprocessResultstable1.9.0
TextPreprocessResultfinal readonly class$segments, hasRedactions(), getDisplayText()stable1.9.0
TextSegmentfinal readonly class$displayText, $isRedacted, $originalCharCount, $fillColorstable1.9.0

TextPreprocessResult dan TextSegment membekukan tanda tangan konstruktor beserta properti publiknya; metode baru boleh ditambahkan, tetapi properti tidak boleh berubah.

examples/04-text-and-fonts.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Bold heading', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Body text rendered with a registered font.');
$doc->save(__DIR__ . '/output/04-text-and-fonts.pdf');

setFont() meresolusikan keluarga fon melalui FontRegistryInterface. Dokumen standalone menggunakan registri privat. Di dalam worker, bagikan satu registri; lihat halaman document.

examples/contracts/typography-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class FontWarmupService
{
public function __construct(
private FontRegistryInterface $fonts,
private TextPreprocessorInterface $preprocessor,
private LoggerInterface $logger,
) {}
/**
* Warm a font set at boot, then lock the registry.
*
* @param list<string> $fontFiles Absolute paths to font files.
*/
public function boot(array $fontFiles): void
{
try {
$this->fonts->warmup($fontFiles);
$this->fonts->lock();
} catch (NextPdfException $e) {
$this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e;
}
}
public function redact(string $text): string
{
$result = $this->preprocessor->process($text);
return $result->hasRedactions()
? $result->getDisplayText()
: $text;
}
}

warmup() yang diikuti lock() adalah urutan boot untuk worker. Setelah lock(), setiap mutasi akan melempar eksepsi. Operasi pencarian tetap melayani lalu lintas.

  • Registri yang terkunci menolak setiap metode mutasi. Lakukan warmup lalu kunci registri saat boot; jangan pernah memanggil register() selama penanganan permintaan.
  • registerFromBinary() menulis byte fon ke berkas sementara sebelum mengurainya. Data fon yang tidak tepercaya menjadi permukaan serangan bagi parser — saring melalui ExternalResourcePolicyInterface (lihat halaman security-policy).
  • TextPreprocessor tidak boleh menambahkan jeda baris, carriage return, atau tab. Karakter tersebut mengubah tata letak dan melanggar invarian pertama kontrak ini.
  • TextSegment::$originalCharCount hanyalah petunjuk lebar. Menggunakannya untuk menyimpulkan konten asli menggagalkan penyuntingan dan melanggar invarian ketiga kontrak ini.
  • TextPreprocessResult::getDisplayText() sengaja mengembalikan string kosong untuk segmen kotak hitam. Jangan menganggap segmen kosong sebagai kegagalan prapemrosesan.

Penguraian fon mendominasi penggunaan pertama; registri mengamortisasi biaya itu menjadi satu kali per proses. Setelah warmup, get() dan has() adalah pencarian map O(1). memoryUsage() mengembalikan MemoryReport sehingga worker dapat melacak cache fon terhadap anggarannya. Prapemrosesan teks berjalan linear terhadap panjang masukan. Daftar segmen menambahkan overhead terbatas yang sebanding dengan jumlah kecocokan penyuntingan. performance_budget sebesar 1500 ms wall dan puncak 64 MB mencakup warmup untuk set fon tipikal plus rendering dokumen. Biaya subsetting berskala menurut jumlah glif yang benar-benar digunakan, bukan menurut tabel glif lengkap fon. Karena itu, subsetting mengurangi ukuran keluaran dan biaya rendering untuk konten CJK.

Domain tipografi memiliki dua permukaan yang relevan secara keamanan. Yang pertama adalah masukan fon: registerFromBinary() mengurai byte sembarang. Data fon yang tidak tepercaya harus melewati ExternalResourcePolicyInterface, yang membatasi ukuran berkas dan jumlah glif sebelum data mencapai parser. Yang kedua adalah penyuntingan: TextPreprocessorInterface berjalan sebelum tata letak glif, subsetting fon, ToUnicode CMap, dan pohon struktur, sehingga konten yang disunting tidak pernah masuk ke artefak yang dirender. Penyuntingan dengan overlay pada tahap paint membocorkan teks asli di content stream dan subset. Penempatan kontrak ini mencegah kelas cacat tersebut. Petunjuk pengukuran pada segmen sengaja dibuat tidak dapat dibalik. Perlakukan setiap fon atau teks yang dipasok dari luar sebagai tidak tepercaya.

KlaimStandarKlausaBukti
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 oleh dokumen.ISO 32000-2§9

Kedua klausa diparafrasekan. NextPDF tidak mereproduksi teks normatif. PDF/A-4 mewajibkan penyematan untuk setiap fon. Kesesuaian ini didokumentasikan pada halaman extraction dan accessibility.