跳转到内容
getnextpdf.com

Pro 版本

Diff

NextPDF\Pro\Diff 比较两份 PDF 文档并报告变更内容。快速路径产出一份按页对齐的文本差异;结构化路径在此之上增加图像和元数据变更检测,并将结果格式化为 JSON 或 HTML。

此功能随 NextPDF Pronextpdf/pro)一同发布,并通过 Pro 级授权信封激活。不具备该授权的部署不会加载此功能的类。比较各版本并获取授权

没有任何运行时能力标志对 Diff 类进行门控;只要安装了 Pro 包,它们即存在。

Terminal window
composer require nextpdf/pro:^3

PdfDiffer::compare() 从每份文档中逐页提取文本,将其拆分为行,并对每个页面对运行一次 Myers 行差异算法,产出新增、删除和修改的区域。文本提取解析 ISO 32000-2:2020 §9.4 的文本显示运算符(TjTJ')。

StructuredDiffer 在此之上构建:它将文本区域归组为段落级变更,比较嵌入图像,比较元数据,并产出一个带聚合摘要的 StructuredDiffResultDiffFormatter 将该结果序列化为 JSON 字符串或 HTML 报告片段。

当安装了可选的 Artisan PDF 读取器时,文本提取会使用它以获得逐页精确的内容;否则会用一种有界的字节级回退方式直接扫描内容流。

差异比较器比较的是提取出的文本和结构,而非渲染后的像素。结构化比较具有确定性、成本低廉,并能对应到审阅者所关心的编辑性变更。而像素差异反而会把抗锯齿和字体微调噪声当作内容标记出来。由于 PDF 存储的是字形和定位信息,而非可直接阅读的字符,因此每次比较都会先从内容流中重建文本。正是这一提取步骤,才使得 Artisan 读取器能提升精度,使得有界的 FlateDecode 回退以覆盖率换取安全性,也使得扫描页面几乎无法比对出差异。页面对齐保持基于索引,以确保可预测性,因此插入一页会呈现为清晰的下游位移。

设计背景:为何 PDF 中的文本并非真正的文本

  • 输入。 源 PDF 和目标 PDF 的原始字节。不以 %PDF 开头的缓冲区会抛出 InvalidArgumentException
  • 输出(快速路径)。 DiffResult,包含 addedremovedmodified 区域列表,以及 isIdentical()hasDifferences()totalChanges()
  • 输出(结构化路径)。 StructuredDiffResult,包含段落差异、图像差异、元数据变更,以及一个 DiffSummary
  • 报告输出。 DiffFormatter 发出一个 JSON 字符串或一个 HTML 片段。它不产出可视化的并排红线对照 PDF。
  • 资源边界。 解压后的内容流大小设有上限,以防解压炸弹;字节级扫描器避免在精心构造的输入上发生灾难性的正则回溯。
  • 确定性。 对于相同的输入,差异区域和格式化后的输出是稳定的。
类型种类关键成员
NextPDF\Pro\Diff\PdfDifferfinal classstatic compare(string $sourcePdf, string $targetPdf): DiffResult, static compareTexts(array $sourcePages, array $targetPages): DiffResult, static extractText(string $contentStream): string
NextPDF\Pro\Diff\StructuredDifferfinal class__construct(?ImageDiffer $imageDiffer = null, ?MetadataDiffer $metadataDiffer = null), compare(string $sourcePdf, string $targetPdf): StructuredDiffResult
NextPDF\Pro\Diff\DiffFormatterfinal classtoJson(StructuredDiffResult $result): string, toHtml(StructuredDiffResult $result): string
NextPDF\Pro\Diff\DiffResultfinal readonly classarray $added, array $removed, array $modified, isIdentical(): bool, hasDifferences(): bool, totalChanges(): int
NextPDF\Pro\Diff\StructuredDiffResultfinal readonly classtext diff, paragraphs, images, metadata changes, summary
NextPDF\Pro\Diff\DiffTypeenumAdded, Removed, Modified, Unchanged
<?php
declare(strict_types=1);
use NextPDF\Pro\Diff\PdfDiffer;
$diff = PdfDiffer::compare(
file_get_contents('v1.pdf'),
file_get_contents('v2.pdf'),
);
if ($diff->hasDifferences()) {
echo $diff->totalChanges(), " text changes detected\n";
}
<?php
declare(strict_types=1);
use NextPDF\Pro\Diff\DiffFormatter;
use NextPDF\Pro\Diff\StructuredDiffer;
function reviewReport(string $oldPdf, string $newPdf): string
{
$result = (new StructuredDiffer())->compare($oldPdf, $newPdf);
// JSON for machine consumption; toHtml() for a review UI fragment.
return (new DiffFormatter())->toJson($result);
}
  • 差异按索引逐页对齐。在前面插入一页会令其后所有页面发生位移,并报告大量下游变更 —— 这对于按索引对齐的比较是预期行为。
  • 图像比较检测新增、删除和修改的嵌入图像;它不是感知层面的可视差异,也不会对页面进行像素渲染。
  • 扫描件(仅含图像)PDF 几乎不产出或不产出文本差异,因为没有执行 OCR。
  • 没有可选的 Artisan 读取器时,提取会使用有界回退方式;高度压缩的文档可能产出较少的文本覆盖。

文本提取随文档字节呈线性;对于相似文档,Myers 差异接近线性,在最坏情况下每个页面对呈二次方。解压上限约束了内存。参见 performance_budget

字节级回退使用基于 strpos 的扫描而非无界正则,以避免在精心构造的 PDF 上发生灾难性回溯,并对解压输出设限。差异比较不执行嵌入脚本。参见 Core 安全模型。

主张规范条款状态
解析 Tj 文本运算符以进行提取ISO 32000-2:2020 §9.4已验证(单元测试套件)
解析 TJ 数组文本运算符以进行提取ISO 32000-2:2020 §9.4已验证(单元测试套件)
可视化并排红线对照 PDF 输出不支持(仅 JSON/HTML)

文档比较没有 Core 等价物。可选的 Artisan 读取器在安装后可提高提取精度,但并非必需。

这是一个内容变更检测器。它不是取证级差异分析器,也不产出证据性或篡改归因报告;这些事项不在本模块的范围之内。

本页仅记录外部可观察的行为以及受支持的公共 API 接口。内部命名空间路径、辅助类、机制表、运行手册文件名和工单前缀均不在范围之内。