自定义布局引擎与布局阶段的文本拦截
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(扩展点);各自的标准定义请参阅已发布的术语表。