Lewati ke konten

Teks: batas shaping, CJK, dan penanganan run

Modul teks menetapkan batas shaping. Modul ini mengekspos antarmuka kecil yang mengubah run 8-bit Unicode Transformation Format (UTF-8) menjadi glyph yang sudah diposisikan, memilih backend OpenType nyata bila tersedia, melakukan fallback secara deterministik bila tidak ada, dan menyediakan registri untuk shaper khusus skrip.

Terminal window
composer require nextpdf/core:^3

ShaperInterface menghubungkan alur tata letak teks dengan mesin shaping OpenType. Antarmuka ini sengaja dibuat kecil: satu metode shape() menerima sebuah ShaperInput dan mengembalikan sebuah ShapingResult. Tipe kembalian tersebut adalah satu-satunya keluaran yang dilihat konsumen. Implementasi tidak boleh membocorkan internal mesin shaping, dan kembalian bertipe ini menegakkan batas tersebut. ShapingResult membawa daftar rekaman GlyphRun, teks sumber yang disertakan kembali, skrip dan arah, serta tag shaperImpl yang mengidentifikasi backend yang menghasilkan hasil tersebut.

Pemilihan backend bersifat eksplisit dan melaporkan kapabilitas tanpa menebak. ShaperFactory menjalankan satu pemeriksaan kapabilitas. Jika host memiliki binding HarfBuzz yang berfungsi, create() mengembalikan shaper berbasis HarfBuzz. Bila tidak, ia mengembalikan NullShaper. NullShaper adalah fallback pass-through. Ia memancarkan satu glyph sintetis per codepoint Unicode, dengan advance nol dan offset nol. Ia menandai hasil agar observabilitas dapat mendeteksi fallback, dan menyerahkan resolusi advance kepada modul metrik fon. Jalur ini merupakan degradasi yang terdokumentasi, bukan shaping penuh. Substitusi, ligatur, pemosisian mark, dan bentuk kontekstual membutuhkan backend nyata. wouldUseRealShaper() adalah predikat diagnostik. Kode produksi sebaiknya menentukan cabang berdasarkan tag shaperImpl milik hasil.

Shaping khusus skrip adalah antarmuka penyedia layanan (SPI), bukan implementasi yang dibundel. ScriptShaperRegistry adalah registri bergaya PHP Standards Recommendation 11 (PSR-11) yang me-resolve sebuah MongolianShaperInterface atau TibetanShaperInterface berdasarkan tag skrip International Organization for Standardization (ISO) 15924. Registri menyimpan kunci tanpa membedakan huruf besar/kecil dan bergantung pada satu sumber kebenaran untuk kelayakan kode skrip. Registri dan antarmuka shaper-skrip merupakan kontrak yang dibekukan, sehingga ekstensi dapat mendaftarkan penyedia Phase-12 tanpa menyentuh situs pemanggilan. Mesin menyediakan batas tersebut. Konsumen menyediakan penyedia skrip kompleks.

Penanganan run bahasa Tionghoa, Jepang, dan Korea (CJK) berada di batas pengkodean tipografi. Sebuah face TrueType CJK yang tertanam dipancarkan sebagai fon Type 0 dengan CMap Identity-H dan descendant CIDFontType2, sebagaimana dibahas dalam ISO 32000-2 §9.7.4 (digest retrieval-augmented generation (RAG) terpotong oleh batas lisensi; tercatat dalam _downgraded-claims-o3.md). Ketika program TrueType tertanam, CIDFont Type 2 memetakan pengidentifikasi karakter ke indeks glyph melalui entri CIDToGIDMap, sebagaimana dibahas dalam ISO 32000-2 §9 (digest yang dipin oleh halaman kontrak B1). Subsetter mempertahankan penomoran glyph asli sehingga /CIDToGIDMap /Identity tetap valid untuk subset. CjkFontValidator memeriksa apakah fon kandidat mencakup blok Unicode yang dibutuhkan skrip sebelum fon tersebut dipilih.

TipeJenisAnggota utamaStabilitasSejak
ShaperInterfaceantarmukashape(ShaperInput): ShapingResultstabil3.2.0
ShaperFactorykelas finaldefault(), create(), wouldUseRealShaper()stabil3.2.0
NullShaperkelas readonly finalshaper fallback pass-throughstabil3.2.0
ShapingResultkelas readonly final$glyphRuns, $originalText, $script, $direction, $shaperImplstabil3.2.0
ScriptShaperRegistrykelas finalregisterMongolian(), getMongolian(), hasMongolian(), dan padanan Tibetnyastabil3.1.0
CjkFontValidatorkelas finalvalidateCoverage(), detectScript(), isCjkCodepoint()stabil1.0.0

Rangkaian metode register*, get*, dan has* dari ScriptShaperRegistry beserta antarmuka shaper-skrip merupakan kontrak yang dibekukan. Secara desain, ShapingResult adalah satu-satunya keluaran shaper yang dapat dilihat konsumen.

examples/text/shaper-factory.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Font\Shaper\ShaperFactory;
use NextPDF\Font\Shaper\ShaperImpl;
$factory = ShaperFactory::default();
$shaper = $factory->create();
// Branch on the result tag, not on the concrete class.
$wouldShape = $factory->wouldUseRealShaper()
? 'HarfBuzz backend available'
: 'NullShaper fallback (degraded — no substitution or positioning)';
echo $wouldShape, "\n";

ShaperFactory::default() menyambungkan pemeriksaan kapabilitas produksi. create() melakukan memoisasi backend yang terpilih selama masa hidup factory. Gunakan wouldUseRealShaper() dan tag shaperImpl pada setiap hasil untuk memeriksa kapabilitas.

examples/text/script-shaper-registry.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Text\Shaping\MongolianShaperInterface;
use NextPDF\Text\Shaping\ScriptShaperRegistry;
final readonly class ComplexScriptBootstrap
{
public function __construct(private ScriptShaperRegistry $registry) {}
/**
* Register a consumer-supplied Mongolian shaper provider at boot so
* the layout pipeline can resolve it by ISO 15924 script tag.
*/
public function register(MongolianShaperInterface $mongolian): void
{
$this->registry->registerMongolian($mongolian);
}
public function hasMongolian(): bool
{
return $this->registry->hasMongolian();
}
}

Registri adalah titik integrasi untuk penyedia skrip kompleks. Mesin menyediakan batas tersebut beserta bentuk accessor yang dibekukan. Konsumen menyediakan implementasi Mongolia dan Tibet.

  • Hasil NullShaper memiliki advance nol dan offset nol. Jangan langsung memasukkan posisi tersebut ke tata letak teks. Resolve advance dari modul metrik fon, dan deteksi fallback melalui tag shaperImpl.
  • Masukan kosong menghasilkan daftar glyphRuns yang kosong, bukan run kosong. Kode iterasi konsumen tidak memerlukan penanganan khusus untuk run dengan panjang nol.
  • ScriptShaperRegistry tidak mengimplementasikan Psr\Container\ContainerInterface secara langsung, sehingga accessor bertipe mempertahankan tipe kembalian yang dipersempit dalam analisis statis. Gunakan getMongolian() dan getTibetan(), bukan get() generik.
  • Tag skrip dicocokkan berdasarkan nilai alpha-4 kanonis ISO 15924 dan disimpan tanpa membedakan huruf besar/kecil. Gunakan Mong atau Tibt. Penggunaan huruf besar/kecil tidak memengaruhi pencarian.
  • Karakter CJK Extension B berada di plane 2 Unicode dan memaksa subtabel cmap Format 12 dalam subset. Jalur pengkodean menangani hal ini. Jangan berasumsi bahwa basic multilingual plane mencakup seluruh teks CJK.

Pemeriksaan kapabilitas berjalan sekali per instance ShaperFactory, dan backend dimemoisasi, sehingga panggilan create() berulang praktis tanpa biaya. NullShaper bersifat linear terhadap jumlah codepoint dalam run masukan dan tidak melakukan input/output (I/O). Resolusi ScriptShaperRegistry adalah pencarian berbasis kunci dengan waktu konstan. CjkFontValidator menyampel codepoint pada interval tertentu alih-alih menguji setiap codepoint, sehingga pemeriksaan cakupan tetap murah bahkan terhadap fon CJK berukuran 20,000 glyph. performance_budget sebesar 1500 ms wall dan 64 MB puncak mencakup run yang umum. Dalam shaping nyata, biaya dominan adalah backend OpenType. Biaya tersebut berada di luar cakupan modul ini ketika fallback aktif.

Batas shaper menerima string UTF-8. NullShaper menoleransi UTF-8 yang cacat dengan memecahnya secara best-effort alih-alih melempar kesalahan, karena kontrak fallback yang terdokumentasi memang sudah “tanpa shaping nyata”. Pemanggil harus siap menerima keluaran berkualitas rendah. Kontrak cluster byte-offset menggunakan panjang berorientasi byte, yang benar untuk masukan multibyte dan menghindari cacat pemetaan cluster yang meleset satu codepoint. Bila ada, backend nyata adalah pustaka native pihak ketiga. Perlakukan masukannya sebagai tidak tepercaya, dan batasi panjang run di hulu. Registri shaper-skrip menyimpan penyedia yang dipasok konsumen. Implementasi tersebut berada di dalam batas kepercayaan konsumen, bukan batas mesin.

KlaimStandarKlausulBukti
Sebuah face TrueType CJK yang tertanam dipancarkan sebagai fon Type 0 dengan CMap Identity-H dan descendant CIDFontType2.ISO 32000-2§9.7.4Digest retrieval-augmented generation (RAG) terpotong oleh batas lisensi; prefiks 7a5258772f508e3b, lihat _downgraded-claims-o3.md
CIDFont Type 2 tertanam memetakan pengidentifikasi karakter ke indeks glyph melalui CIDToGIDMap.ISO 32000-2§9

Kedua klausul diparafrasa. Yang kedua berupa digest yang dipin (digunakan kembali dari halaman kontrak B1), dan yang pertama dikuatkan oleh ADR-013 serta tinjauan pengembang cmap-encoder. NextPDF tidak mereproduksi teks normatif. Backend shaper bersifat independen dari kesesuaian Portable Document Format (PDF). Klaim kesesuaian di sini berkenaan dengan pemancaran kamus fon CJK yang dihasilkan oleh batas pengkodean. ADR-013 dan tinjauan pengembang cmap-encoder mendokumentasikan jalur tersebut secara lebih rinci.

Alur prapemrosesan teks tingkat lanjut dan layanan ekstraksi dibangun di atas batas shaper Core dan tipe nilai penanganan run. Modul teks Core menyediakan batas, fallback, dan registri shaper-skrip tanpa lisensi. Ketiadaan tautan konversi memang disengaja.