跳转到内容

契约 / 提取

提取领域提供用于读取和验证 PDF,并将其内容转换为结构化数据的各项契约。这些契约包括检查器、合规性验证器、PDF/A 管理器、导入对象契约、嵌入与向量 Index(索引)契约,以及电子发票验证器子命名空间。

Terminal window
composer require nextpdf/core:^3

InspectorInterface 会读取原始 PDF。它会返回结构化的 InspectResult,其中列出文档中的对象。任何需要读取非本引擎生成的 PDF 的工具,都应使用该接口。

ExternalComplianceValidatorInterface 会桥接到外部检查工具,例如 veraPDF。该检查工具会测试 PDF/A 与 PDF/UA。未配置任何检查工具时,null 实现会返回「不可用」结果。没有 veraPDF 的站点仍可正常运行。ProfileValidatorInterface 会依据部署配置文件检查执行环境,查看必要和建议的扩展,并返回带类型信息的判定结果。

PdfAManagerInterface 会在 PDF/A 文档写入过程中保持其符合规格。它会阻止 JavaScript、JavaScript 表单动作以及内置加密。PDF/A 禁止这三者。它还会检查每个字体是否已嵌入,设置符合规格的元数据,并在目录(catalog)之前先写入所需对象。实际类随 Pro 版提供。Core 会通过 class_exists() 找到它,并将其转换为该契约。开源引擎因此不包含任何付费依赖。

有两个契约涵盖导入对象:ImportedFormObjectInterfaceEmbeddedPdfObjectInterface。它们为从既有 PDF 读取的对象提供带类型的访问方式,随后引擎即可将这些对象重新嵌入。无损路径会保留原始字典字节。对于来自对象流的对象,后备路径会提供已解析的字典数组。每个重新嵌入的对象都是一个 PDF 间接对象,并由对象编号与世代编号共同命名——ISO 32000-2 §7.3.10。

嵌入契约服务于搜索工作。EmbeddingServiceInterface 会将文本转换为密集向量,并报告模型大小与名称。调用方会在运行时进行调整。Pro 版运行 CPU 模型。Enterprise 版运行 GPU 模型。VectorIndexInterface 会构建最近邻索引并对其进行搜索。它是供核心使用的小型进程内索引。更大规模的搜索位于仅限 Enterprise 使用的契约中。

EInvoice 这个分组提供跨版本层级的电子发票检查器。ValidatorInterface 会对 CII 或 UBL 内容执行预检。SchematronRunnerInterface 会执行商业规则检查。ValidationResult 会收集发现项与规则违规。检查器必须通过结果报告来拒绝不良输入,而不是抛出异常。它还必须防范 DOCTYPE 和过大的内容。

类型种类主要成员稳定性起始版本
InspectorInterfaceinterfaceinspect(string, InspectConfig): InspectResult实验性2.2.0
ExternalComplianceValidatorInterfaceinterfacevalidate(string, ComplianceFlavour), isAvailable()实验性2.4.0
ProfileValidatorInterfaceinterfacevalidate(DeploymentProfile): DeploymentProfileResult实验性2.4.0
PdfAManagerInterfaceinterfacevalidateNoJavaScript(), validateFont(), validateNoEncryption(), applyOutputProfile(), writeRequiredObjects()稳定1.10.0
ImportedFormObjectInterfaceinterfacegetWidth(), getHeight(), getEmbeddedObjects(), getResourcesDict(), getMediaBox(), getContentStream()稳定1.8.0
EmbeddedPdfObjectInterfaceinterfacegetRawDictionaryBytes(), getRawStreamData(), getDictionary()稳定1.8.0
EmbeddingServiceInterfaceinterfaceembed(), batchEmbed(), getDimension(), getModelName()实验性2.1.0
VectorIndexInterfaceinterfacebuild(), search(), delete(), count()实验性2.1.0
EInvoice\ValidatorInterfaceinterfacevalidate(string, ValidatorContext): ValidationResult实验性5.1.0
EInvoice\ValidationResultfinal readonly class$isValid, getErrors(), getWarnings(), fail()实验性5.1.0

EInvoice 命名空间还发布了 SchematronRunnerInterfaceProfileInterfaceValidationFindingRuleViolation,以及 ProfileTypeRuleSeverityValidationFindingLevel 枚举。

examples/contracts/extraction-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\InspectorInterface;
use NextPDF\Inspect\InspectConfig;
/**
* Inspect a PDF and report its object count.
*
* @param InspectorInterface $inspector A configured inspector.
* @param string $pdfData Raw PDF bytes.
*/
function describe(InspectorInterface $inspector, string $pdfData): \NextPDF\Inspect\InspectResult
{
return $inspector->inspect($pdfData, new InspectConfig());
}

此函数依赖该契约。任何检查器实现都能满足它。

examples/contracts/extraction-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\EInvoice\ValidatorInterface;
use NextPDF\Contracts\EInvoice\ValidatorContext;
use NextPDF\Contracts\ExternalComplianceValidatorInterface;
use NextPDF\ValueObjects\ComplianceFlavour;
use Psr\Log\LoggerInterface;
final readonly class InvoiceConformanceService
{
public function __construct(
private ValidatorInterface $invoiceValidator,
private ExternalComplianceValidatorInterface $pdfaValidator,
private LoggerInterface $logger,
) {}
/**
* Validate the invoice XML, then the PDF/A-3 carrier.
*
* @param string $xml The CII or UBL invoice payload.
* @param string $pdfPath Absolute path to the PDF/A-3 carrier.
*/
public function validate(string $xml, string $pdfPath, ValidatorContext $ctx): bool
{
$result = $this->invoiceValidator->validate($xml, $ctx);
if (!$result->isValid) {
$this->logger->warning('Invoice XML invalid', [
'errors' => \count($result->getErrors()),
]);
return false;
}
if (!$this->pdfaValidator->isAvailable()) {
$this->logger->info('PDF/A validator unavailable; skipping carrier check.');
return true;
}
$carrier = $this->pdfaValidator->validate($pdfPath, ComplianceFlavour::PdfA3b);
return $carrier->isConformant();
}
}

此服务会明确处理验证器不可用的情况,而不会假设验证器一定存在。

  • EInvoice\ValidatorInterface::validate() 对格式错误的输入会返回失败的 ValidationResult。它不会因为格式正确性违规而抛出异常。请检查 $isValid,不要为了这种情况用 try/catch 包住该调用。
  • ExternalComplianceValidatorInterface::isAvailable() 必须在依赖判定结果之前先检查。null 实现会返回「不可用」。将其视为「不符合规范」会产生伪阴性。
  • EmbeddedPdfObjectInterface::getRawDictionaryBytes() 对于来自对象流的对象会返回 null。请改用 getDictionary() 作为后备。不要假设原始字节一定存在。
  • EmbeddingServiceInterface::getDimension() 会因版本层级而异。配置固定宽度向量的代码必须在运行时读取维度,而不是写死。
  • VectorIndexInterface::build() 要求向量列表与 id 列表长度相等且维度一致。不一致会引发 InvalidArgumentException。请在构建之前先验证。

检查与验证的成本会随文件大小和对象数量增加。1500 毫秒墙钟时间与 64 MB 峰值内存的 performance_budget 覆盖单个中等规模文件。外部 veraPDF 调用会额外产生验证器自身的进程处理时间。该处理时间不计入引擎预算,应在请求路径之外执行。嵌入成本会随文本长度增加,批处理远比在循环中逐条处理更便宜,在 GPU 模型上尤其如此。请优先使用 batchEmbed()。对于进程内索引,向量搜索相对于索引大小是次线性的。再现性配置文件为 structural。验证报告会记录时间戳和环境指纹。两次执行在这些字段上会有差异,而符合规范的判定结果会保持相同。

提取会读取非本引擎创建的文件,因此每笔输入都不可信。检查器与电子发票验证器都会解析外部提供的字节。电子发票验证器必须在解析之前拦截带有 DOCTYPE、过大以及包含禁用控制字符的内容,以防范 XML 外部实体与 billion-laughs 攻击。导入对象的重新嵌入会从外部 PDF 复制字节。恶意来源对象可能携带有害内容,因此重新嵌入会保留字节而不执行它们。PDF/A 强制机制会移除 JavaScript 与动作。PDF/A 管理器会拒绝 JavaScript 与加密,因为两者在该配置文件中均被禁止,且两者在长期保存的归档文件中都是滥用途径。请始终将受检查的内容、导入对象与发票 XML 视为恶意输入。

主张标准条款佐证
PDF/A-4 禁止 JavaScript 与 JavaScript 表单动作;PDF/A 管理器会拒绝两者。ISO 19005-4§6.7.1按条款引用(不在语料库中)
每个重新内嵌的对象都是以对象编号与世代识别的 PDF 间接对象。ISO 32000-2§7.3.10

ISO 19005-4 按条款引用。它不在可验证的引用语料库中,因此不会记录 reference_id。ISO 32000-2 的间接对象主张已固定在词汇表中。两者均为改写。引擎不会复现任何规范性文字。

Core 定义并冻结提取契约。PdfAManagerInterfaceEmbeddingServiceInterfaceVectorIndexInterface 背后的生产环境代码随 Pro 与 Enterprise 版提供,包含 CPU 与 GPU 嵌入模型,以及完整的 PDF/A 强制路径。Core 会在运行时通过 class_exists() resolve(解析)这些类。开源引擎因此不包含任何商业依赖,且 API 在升级时不会变动。