文字:塑形接縫、CJK 與文字段處理
文字模組是塑形接縫:它以小型介面把 UTF-8 文字段轉換成已定位的字形;主機具備真正的 OpenType 後端時就使用該後端,否則退回確定性的備援路徑;同時也提供註冊表,用來登記特定 script 的塑形器。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段ShaperInterface 是文字排版管線與 OpenType 塑形引擎之間的接縫。它刻意維持最小化:只有一個 shape() 方法,接收一個 ShaperInput 並回傳一個 ShapingResult。回傳類型是唯一對使用端可見的輸出——實作不得洩漏塑形引擎的內部細節,而具型別的回傳值會在結構上強制這一點。ShapingResult 帶有 GlyphRun 記錄清單、回傳的原始文字、script 與方向,以及一個 shaperImpl 標記,用來指出這個結果是由哪個後端產生。
後端選擇是顯式的,且會如實反映能力。ShaperFactory 會執行一次能力探測:若主機具備可運作的 HarfBuzz 繫結,create() 就回傳以 HarfBuzz 為後端的塑形器;否則回傳 NullShaper。NullShaper 是一條直通式的備援路徑。它會為每個 Unicode 碼位輸出一個合成字形,並將寬進量與偏移量都設為零。它會標記結果,讓可觀測性能偵測到這次備援。它把寬進量的 resolve(解析)留給字型度量模組處理。這是一條有明文記載的降級路徑,而非完整塑形:替換、連字、標記定位與情境字形都需要真正的後端才能做到。wouldUseRealShaper() 是診斷用的判斷式。正式環境的程式碼應改為根據結果上的 shaperImpl 標記來分支。
特定 script 的塑形是一個 SPI,而非內建實作。ScriptShaperRegistry 是一個 PSR-11 風格的註冊表,會依 ISO 15924 script 標記解析出 MongolianShaperInterface 或 TibetanShaperInterface。註冊表以不分大小寫的方式儲存鍵值,並把 script 代碼的可接受性判斷交由單一可信來源決定。註冊表與 script 塑形器介面構成一份凍結的合約,因此擴充功能可以在不更動呼叫端的情況下,註冊一個 Phase-12 的供應者。引擎本身只提供這道接縫,複雜 script 的供應者則由使用端自行提供。
CJK 文字段處理位於排版流程的編碼接縫上。嵌入的 CJK TrueType 字面會以 Type 0 字型輸出,搭配 Identity-H CMap 與一個 CIDFontType2 後代字型——ISO 32000-2 §9.7.4(RAG 摘要因授權上限被截斷;記錄於 _downgraded-claims-o3.md)。當 TrueType 程式被嵌入時,Type 2 CIDFont 會透過 CIDToGIDMap 項目,把字元識別碼對映到字形的 Index(索引)——ISO 32000-2 §9(摘要由 B1 合約頁面釘選)。子集化工具會精確保留原始的字形編號,因此 /CIDToGIDMap /Identity 對子集而言仍然有效。CjkFontValidator 是診斷工具,會在選用某個候選字型之前,檢查它是否涵蓋某個 script 所需的 Unicode 區塊。
API 介面
標題為「API 介面」的區段| 類型 | 種類 | 主要成員 | 穩定性 | 起始版本 |
|---|---|---|---|---|
ShaperInterface | 介面(interface) | shape(ShaperInput): ShapingResult | 穩定 | 3.2.0 |
ShaperFactory | final class | default()、create()、wouldUseRealShaper() | 穩定 | 3.2.0 |
NullShaper | final readonly class | 直通式的備援塑形器 | 穩定 | 3.2.0 |
ShapingResult | final readonly class | $glyphRuns、$originalText、$script、$direction、$shaperImpl | 穩定 | 3.2.0 |
ScriptShaperRegistry | final class | registerMongolian()、getMongolian()、hasMongolian(),以及對應的 Tibetan 版本 | 穩定 | 3.1.0 |
CjkFontValidator | final class | validateCoverage()、detectScript()、isCjkCodepoint() | 穩定 | 1.0.0 |
無論是 register*、get* 與 has* 形式,或是 ScriptShaperRegistry 與各 script 塑形器介面,都構成一份凍結的合約。依設計,ShapingResult 是塑形器唯一對使用端可見的輸出。
程式碼範例——快速上手
標題為「程式碼範例——快速上手」的區段<?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() 會接上正式環境的能力探測。create() 會在工廠的生命週期內記憶化所選的後端。能力狀態的真實答案,來自 wouldUseRealShaper() 以及每個結果上的 shaperImpl 標記。
程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段<?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(); }}註冊表是複雜 script 供應者的整合點。引擎提供這道接縫以及凍結的存取器形式。Mongolian 與 Tibetan 的實作則由使用端提供。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段NullShaper的結果,其寬進量為零、偏移量為零。不要直接把這些位置餵給文字排版——請改從字型度量模組解析寬進量,並透過shaperImpl標記偵測這次備援。- 空輸入會產生一個空的
glyphRuns清單,而非一個空的文字段。使用端的迭代程式碼不需要特別處理長度為零的文字段。 ScriptShaperRegistry並未直接實作Psr\Container\ContainerInterface,因此具型別的存取器才能在靜態分析下保有其收窄後的回傳類型。請使用getMongolian()與getTibetan(),而非泛用的get()。- script 標記會以正規的 ISO 15924 alpha-4 值來比對,且以不分大小寫的方式儲存。傳入
Mong或Tibt。查找時大小寫無關緊要。 - CJK 擴充 B 區字元位於 Unicode 第 2 平面,會迫使子集中產生一個 cmap Format 12 子表。編碼路徑會處理這種情況。不要假設 CJK 的全部內容都落在基本多文種平面內。
能力探測在每個 ShaperFactory 實例上只執行一次,且後端會被記憶化,因此重複呼叫 create() 幾乎不耗成本。NullShaper 的成本與輸入文字段的碼位數呈線性關係,且不涉及 I/O。ScriptShaperRegistry 的解析是一次常數時間的鍵值查找。CjkFontValidator 會以固定間隔抽樣碼位,而非逐一測試每一個,因此即使面對 20,000 個字形的 CJK 字型,涵蓋率檢查仍然便宜。1500 毫秒掛鐘時間與 64 MB 尖峰用量的 performance_budget 足以涵蓋一次典型的執行。真正塑形時的主要成本來自 OpenType 後端本身;當備援啟用時,這部分不屬於本流程的成本範圍。
安全性注意事項
標題為「安全性注意事項」的區段塑形接縫接收一個 UTF-8 字串。NullShaper 會以盡力切分的方式容忍格式錯誤的 UTF-8,而不會拋出例外,因為這個備援路徑既有的合約本來就是「不做真正的塑形」。呼叫端已預期會得到品質較低的輸出。位元組偏移的群集合約採用以位元組為導向的長度,這對多位元組輸入而言是正確的,也避免了偏差一個碼位的群集對映缺陷。真正的後端(若存在)是第三方原生函式庫。請把它的輸入視為不可信,並在上游限制文字段的長度。script 塑形器註冊表儲存的是使用端提供的供應者——這些實作的信任邊界屬於使用端,而非引擎。
符合性
標題為「符合性」的區段| 宣稱 | 標準 | 條款 | 證據 |
|---|---|---|---|
嵌入的 CJK TrueType 字面會以 Type 0 字型輸出,搭配 Identity-H CMap 與一個 CIDFontType2 後代字型。 | ISO 32000-2 | §9.7.4 | 摘要因授權上限而被截斷,此處僅保留前綴 7a5258772f508e3b,詳見 _downgraded-claims-o3.md |
嵌入的 Type 2 CIDFont 會透過 CIDToGIDMap 把字元識別碼對映到字形索引。 | ISO 32000-2 | §9 |
兩項條款皆已改寫。第二項以摘要釘選(沿用自 B1 合約頁面),第一項則由 ADR-013 與 cmap 編碼器的開發者總覽佐證。NextPDF 不重製規範性文字。塑形後端與 PDF 符合性無關。此處的符合性宣稱針對的是編碼接縫所產生的 CJK 字型字典輸出,在 ADR-013 與 cmap 編碼器的開發者總覽中有進一步說明。
商業情境
標題為「商業情境」的區段一套進階的文字前處理管線與擷取服務,建立在 Core 的塑形接縫與文字段處理值類型之上。Core 文字模組會提供這道接縫、備援路徑與 script 塑形器註冊表,且不需要授權。省略轉換連結是刻意的。
另請參閱
標題為「另請參閱」的區段- Typography:註冊表、子集化、CMap、編碼、BiDi ——編碼接縫與 BiDi 引擎。
- Font:值類型、嵌入、備援 ——塑形器輸入所參照的
FontInfo。 - Contracts / Typography ——位於塑形上游的文字前處理器合約。