跳到內容

自訂版面引擎與版面階段文字攔截

NextPDF 並未提供可插拔的版面引擎介面。公開的版面擴充契約是 TextPreprocessorInterface,它會在版面階段接入文字處理。你也能使用內容生命週期事件,觀察版面實際產生的內容。

Terminal window
composer require nextpdf/core:^3

版面管線(pipeline)屬於內部實作。它涵蓋字元版面、字型子集化、ToUnicode CMap 輸出以及結構樹,而 NextPDF 不允許你替換它。穩定的位元組輸出與標記式 PDF 的一致性,都仰賴單一受控的建構流程。

NextPDF 確實對外公開的是版面之前的切入點:TextPreprocessorInterface。實作會取得原始文字並回傳切分後的結果;這會發生在該文字進入字元版面、字型子集化、ToUnicode CMap 或結構樹之前。這是在不動到版面引擎的情況下變更文字內容的受支援做法。

這份契約的原始 PHPDoc 明定一條硬性規則:實作不得改變版面的運作方式。它不得加入會影響版面的字元,例如換行(line feed)、歸位(carriage return)或定位字元(tab),而且必須維持邏輯閱讀順序。前置處理器宣告的是一次性的內容置換;它不做版面決策。請遵守這條規則,否則你會破壞穩定輸出與無障礙性。

若要觀察版面結果,而不是變更版面,請改用 行為觸發器與事件監聽器一節中的內容生命週期事件。ContentRenderedEvent 會在內容繪製到頁面之後觸發。FontLoadedEvent 會針對每個字型家族與樣式各觸發一次。

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,或透過同一份公開契約使用經過驗證的付費版建構。

詞彙表定義了 text preprocessor(文字前置處理器)與 extension point(擴充點);各自的標準定義請參閱已發布的詞彙表。