自訂版面引擎與版面階段文字攔截
快速概覽
標題為「快速概覽」的區段NextPDF 並未提供可插拔的版面引擎介面。公開的版面擴充契約是 TextPreprocessorInterface,它會在版面階段接入文字處理。你也能使用內容生命週期事件,觀察版面實際產生的內容。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段版面管線(pipeline)屬於內部實作。它涵蓋字元版面、字型子集化、ToUnicode CMap 輸出以及結構樹,而 NextPDF 不允許你替換它。穩定的位元組輸出與標記式 PDF 的一致性,都仰賴單一受控的建構流程。
NextPDF 確實對外公開的是版面之前的切入點:TextPreprocessorInterface。實作會取得原始文字並回傳切分後的結果;這會發生在該文字進入字元版面、字型子集化、ToUnicode CMap 或結構樹之前。這是在不動到版面引擎的情況下變更文字內容的受支援做法。
這份契約的原始 PHPDoc 明定一條硬性規則:實作不得改變版面的運作方式。它不得加入會影響版面的字元,例如換行(line feed)、歸位(carriage return)或定位字元(tab),而且必須維持邏輯閱讀順序。前置處理器宣告的是一次性的內容置換;它不做版面決策。請遵守這條規則,否則你會破壞穩定輸出與無障礙性。
若要觀察版面結果,而不是變更版面,請改用 行為觸發器與事件監聽器一節中的內容生命週期事件。ContentRenderedEvent 會在內容繪製到頁面之後觸發。FontLoadedEvent 會針對每個字型家族與樣式各觸發一次。
API 介面
標題為「API 介面」的區段NextPDF\Contracts\TextPreprocessorInterface(穩定,自 1.9.0 起):
| 方法 | 回傳 | 用途 |
|---|---|---|
process(string $text) | TextPreprocessResult | 在進入 render 管線之前轉換原始文字;回傳附有遮蔽(redaction)中繼資料的分段結果。 |
回傳的 NextPDF\Contracts\TextPreprocessResult 是凍結的值物件。它的建構式簽章與公開屬性保持穩定,不會在次版本或修補版本中變動。未來可能會新增方法。
程式碼範例——快速上手
標題為「程式碼範例——快速上手」的區段這個小型前置處理器會遮蔽一個固定的權杖(token)。它不會加入任何會影響版面的字元,並會維持閱讀順序。
<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;use NextPDF\Contracts\TextPreprocessResult;use NextPDF\Contracts\TextSegment;
final class TokenMaskingPreprocessor implements TextPreprocessorInterface{ public function process(string $text): TextPreprocessResult { $masked = \str_replace('SECRET-TOKEN', '••••••••••••', $text);
return new TextPreprocessResult([ new TextSegment($masked, redacted: $masked !== $text), ]); }}程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段正式環境的前置處理器會將比對規則集中管理。遇到錯誤的樣式時,它會以失敗封閉(fail closed)方式處理,且絕不記錄原始文字。
<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;use NextPDF\Contracts\TextPreprocessResult;use NextPDF\Contracts\TextSegment;use Psr\Log\LoggerInterface;
final class PatternRedactionPreprocessor implements TextPreprocessorInterface{ /** * @param non-empty-string $pattern A valid PCRE pattern for sensitive spans */ public function __construct( private readonly string $pattern, private readonly LoggerInterface $logger, ) {}
public function process(string $text): TextPreprocessResult { $result = \preg_replace($this->pattern, '[REDACTED]', $text);
if ($result === null) { // Fail closed: never emit unredacted text on a pattern error. $this->logger->error('Redaction pattern failed; substituting empty text');
return new TextPreprocessResult([new TextSegment('', redacted: true)]); }
return new TextPreprocessResult([ new TextSegment($result, redacted: $result !== $text), ]); }}邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 不提供版面替換。 沒有任何契約可用來替換盒子版面、斷行或分頁。導入第三方版面引擎的需求,依設計即不在範圍內。
- 規則強制。 在
process()中加入\n、\r或\t,會破壞版面並使穩定輸出失效。引擎信任這條規則;它不會重新檢查你的輸出是否含有會影響版面的字元。 - 閱讀順序。 重排 segment(段落)會破壞標記式 PDF 的閱讀順序與 PDF/UA 一致性。
- 單一職責。 前置處理器宣告的是一次性的內容置換。請改用生命週期事件來觀察,不要透過
process()推送副作用。
process() 位於版面熱路徑上,每個文字 run 都會執行一次。請讓它保持記憶體輕量。請在建構式裡一次編譯好樣式,而非每次呼叫時才編譯。沒有綁定任何監聽器時,內容生命週期事件不會耗費任何成本。
安全性注意事項
標題為「安全性注意事項」的區段TextPreprocessorInterface 是在敏感內容抵達內容串流、字型子集或中繼資料之前將其移除的受支援切入點。因為它在子集化與 ToUnicode CMap 之前執行,被遮蔽的字元永遠不會進入檔案。請將前置處理器的失敗視為失敗封閉(fail-closed),改用空白或遮蔽後的文字,而非輸出原始內容。
一致性
標題為「一致性」的區段本頁不提出任何規範性的簽章或封存主張。閱讀順序規則使這份契約與標記式 PDF 的需求一致。標籤層級的一致性涵蓋在無障礙參考文件中。
商業情境
標題為「商業情境」的區段NextPDF Pro 提供正式環境等級的文字前置處理策略,包括針對常見文件類型調校的 PII 遮蔽。在 Core 中,你可以自行撰寫 TextPreprocessorInterface,或透過同一份公開契約使用經過驗證的付費版建構。
另請參閱
標題為「另請參閱」的區段相關契約與模組
標題為「相關契約與模組」的區段- 排版契約參考——其中列出
TextPreprocessorInterface與TextPreprocessResult。 - 串流契約參考——
experimentalCursorInterface與StreamingWriterInterface契約已有隨引擎一同出貨的實作。 - 行為觸發器與事件監聽器——用來觀察版面輸出的生命週期事件。
- SPI 穩定性規則——
TextPreprocessResult背後對凍結值物件的承諾。 - 擴充開發概觀——完整的公開 SPI 介面。
詞彙表定義了 text preprocessor(文字前置處理器)與 extension point(擴充點);各自的標準定義請參閱已發布的詞彙表。