PDF 要么符合 PDF/A-3,要么不符合。既然标准答案只有一个,为什么还要对同一个文件跑两个验证器?
原因很现实:PDF/A 规范足够庞大,两个认真实现的验证器仍可能在边界条件上给出不同判断。在审计、归档、电子发票这些场景里,单一引擎显示 “Pass” 更像黄灯,不是可以直接放行的绿灯。
PDF/A 不是一个简单算法
PDF/A 横跨 ISO 19005 的多个版本,PDF/A-1、PDF/A-2、PDF/A-3、PDF/A-4 又各自有 b、a、u、e、f 等不同符合级别。它们还叠加在 PDF 本体规范 ISO 32000 之上。实际要解释的规范文本是数千页级别。
常见分歧通常发生在这些地方:
- PDF/A-2/3 里的透明度:标准允许特定条件下使用,但条件判断并不总是机械清晰。
- ICC 色彩配置:某个 profile 到底是必须还是建议,不同实现可能给出不同结论。
- PDF/A-3 附件元数据:
AFRelationship、/AF引用、XMP 元数据都要对齐,严格程度会影响结果。 - 字体子集:CID 字体、部分嵌入和真实编码之间的边界尤其容易暴露实现差异。
这些不一定是某个验证器的 bug,而是复杂规范由不同团队独立实现后的自然结果。越是受监管行业,越倾向要求多一次独立确认。
参考实现之外,还需要第二意见
veraPDF 是 PDF Association 维护的参考实现,也是 PDF/A 验证里最重要的单一信号。如果 veraPDF 通过,说明文件已经过了最权威的一关。
但“最强单一信号”仍不等于“审计级证据”。银行、医疗档案、政府记录和长期归档系统通常希望看到第二个独立引擎,因为:
- 下游系统可能使用另一个验证器,单引擎通过不代表一定不会被拒收。
- 任何单一实现都可能有 bug,重复跑同一个引擎无法发现它。
- 合规采购里常见的原则是“两份独立确认”,PDF/A 只是继承了这种工作方式。
gPdf 的做法是把 veraPDF 与我们自己的 Rust + WebAssembly 验证引擎放在一起。后者是对同一套规范的独立实现。两个引擎都通过时,证据强度明显高于任意一个单独结果;两个引擎不一致时,也能快速定位需要检查的字段或实现差异。
一个 URL 同时看两份报告
我们把这个流程做成了免费的 gpdf.com/validator/:无需登录,上传文件后同时跑 veraPDF 和 gPdf edge engine,并把两份报告并排返回。
典型用法很直接:
- 准备交付 PDF/A 文件:上传、双引擎通过,把 JSON 报告作为 QA 证据留档。
- 一个通过、一个失败:对比报告,通常能找到 XMP 时间、
/AF引用或附件关系这类细节问题。 - 两个都失败:文件本身不合规,应回到生成源头修复。
- 抽检归档批次:随机抽样上传,把报告 URL 和结果放进审计底稿。
上传的文件只在请求内存中处理。验证在 Cloudflare Workers 上完成,报告生成后文件即被丢弃;无需登录、无需持久化、无需配额。
同一种模式可以泛化
这不只是 PDF/A 的问题。“两个独立确认”的模式也适用于:
- Factur-X / ZUGFeRD 电子发票:gpdf.com/validator/ 会使用 Mustang(mustangproject.org)检查嵌入的 EN 16931 CII XML,并与上面的 PDF/A 检查一起展示。(用 Mustang 验证 ZUGFeRD:哪些会通过、哪些会失败 详细说明了这个流程。)
- TLS 证书:现代 CA log 都会被多个监控系统交叉检查。
- 构建可复现性:同一份源码由两个独立流程重新构建,应该产出字节一致的结果。
合规世界已经这样做了几十年。PDF/A 只是终于跟上了这种工作方式。
TL;DR
单引擎 “Pass” 是黄灯;双引擎 “Pass” 才是绿灯。两个引擎都可以在 validator 免费运行:上传文件,拿到两份报告,然后附到 QA 证据里。如果文件由 gPdf 生成,这个验证器就是 API 兑现合规声明的公开收据。
如果你在使用 gPdf API,E-invoice API reference (§5) 展示了如何直接输出 Factur-X / ZUGFeRD PDF/A-3。validator 再从外部确认结果。两个引擎,一次上传,这就是审计级模式。