跳转到内容
getnextpdf.com

Pro 版本稳定性: 实验性

C2PA 预览 — 深度参考

本页是 NextPDF Pro 中 C2PA(Content Credentials)预览面的契约级参考。它涵盖 NextPDF\Pro\Compliance\C2pa 中的五个公开符号:C2paManifestEmbedder SPI、ManifestStore 值对象、JumbfBoxParserC2paCapabilityStatus 描述符,以及受开关控制的 Experimental\ExperimentalC2paEmbedder。它同时记录 Feature::PREVIEW_C2PA_DRAFT 开关及其环境变量 NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT

该面为实验性(experimental),并被拆分为两层。稳定接缝 —— ManifestStoreC2paManifestEmbedderJumbfBoxParser —— 始终可达,并在两个方向上承载 Manifest Store 字节。草案清单的合成(synthesis)仅存在于 ExperimentalC2paEmbedder 中,且默认关闭。C2PA-PDF profile 尚未由工作组定稿;合成出的线格式被固定(pin)到某个草案提交。这里不作任何合规性声明,没有验证路径,启用预览开关也无法凭空创建其中任何一者。面向任务的视图见能力页

此能力随 NextPDF Pronextpdf/pro)发布,并在 Pro 级许可证信封(envelope)下激活。没有该授权的部署不会加载此能力的类。对比版本并获取许可证

许可证作为整体激活 Pro 合规面。其中的 C2PA 面无论许可证级别如何都保持为预览。草案合成还额外需要本页所记录的进程级开关;仅凭 Pro 许可证永远不会启用它。

符号参数默认行为返回抛出或失败于备注
C2paManifestEmbedder仅字节的嵌入/提取 SPI;无 I/O;不合成 claim冻结的、厂商中立的接缝接口。
C2paManifestEmbedder::embed()string $pdfBytesManifestStore $store在 profile 声明的位置嵌入 $store->toBytes();空 Store 可以(MAY)作为空操作往返string 新的 PDF 字节任何嵌入失败时抛出 C2paException(Store 过大、PDF 无效、profile 位置冲突)实现绝不修改或保留输入字节。
C2paManifestEmbedder::extract()string $pdfBytes廉价的检测探针;无 Store 的情形几乎不分配内存?ManifestStore(未命中返回 null当存在 Store 但违反某项加固不变量时,抛出 C2paException 子类非 null 的 Store 已通过 JumbfBoxParser 加固。
ManifestStore::fromBoxes()array $boxeslist<JumbfBox>包装一份经解析器校验的有序 box 列表self自身不抛出;手工构造 JumbfBox 会强制执行相同的加固构造函数为私有;box 顺序对往返相等性起决定作用。
ManifestStore::empty()零个根 box 的 Storeself不抛出空 Store 的 toBytes() 为空字符串。
ManifestStore::isEmpty()检测是否为零个根 boxbool不抛出
ManifestStore::toBytes()拼接根 box 的序列化结果string不抛出这段字节序列正是嵌入器所写入的内容。
ManifestStore::size()toBytes() 的字节长度int(>= 0)不抛出
JumbfBoxParser::__construct()三个可选的上限覆盖生产上限:每个 box 64 MiB、总计 128 MiB、每个 superbox 4096 个子级JumbfBoxParser不抛出深度上限固定为 MAX_DEPTH(8),不可经构造函数调整。
JumbfBoxParser::parse()string $bytes校验并物化根 box;空输入产生 []list<JumbfBox>JumbfBombExceptionJumbfCycleDetectedExceptionJumbfDepthExceededExceptionMalformedJumbfException无状态;绝不返回部分图;在同一实例上的并发调用是安全的。
C2paCapabilityStatus::__construct()六个具名的 readonly 字段构建一个任意的描述符实例C2paCapabilityStatus不抛出current() 是规范的构造入口。
C2paCapabilityStatus::current()实时读取开关;硬编码 claim 布尔值C2paCapabilityStatus不抛出generallyAvailableconformanceClaimed 始终为 false
C2paCapabilityStatus::summary()一行状态文本string不抛出措辞上不携带任何 GA 或合规性声明。
Feature字符串支撑的枚举,1 个 case单个 case PREVIEW_C2PA_DRAFT;常量 ENV_PREVIEW_C2PA_DRAFT枚举 case访问 case 时不发生任何抛出作用域内的稳定性开关;与许可证授权相区分。
Feature::isEnabled()实时读取 getenv();与字符串 1 严格比较bool不抛出变量缺失或为任何其他值(包括 0trueyes)均为关闭。
ExperimentalC2paEmbedder::__construct()构造时进行失败即关闭(fail-closed)的开关检查ExperimentalC2paEmbedderFeature::PREVIEW_C2PA_DRAFT 关闭时抛出 LogicException不存在静默回退。
ExperimentalC2paEmbedder::buildManifestStore()string $sourceBytesstring $producer(非空)构建一个草案形态的 Store,通过 SHA-256 绑定 $sourceBytesManifestStore载荷编码失败时抛出 \JsonException;box 构造抛出 C2paException 子类省略 c2cs Claim Signature box;其输出在构造上即为未签名。
interface C2paManifestEmbedder
public function embed(string $pdfBytes, ManifestStore $store): string;
public function extract(string $pdfBytes): ?ManifestStore;
final readonly class ManifestStore
public static function fromBoxes(array $boxes): self
public static function empty(): self
public function isEmpty(): bool
public function toBytes(): string
public function size(): int
final class JumbfBoxParser
public const int MAX_DEPTH = 8;
public const int MAX_PER_BOX_BYTES = 64 * 1024 * 1024;
public const int MAX_TOTAL_BYTES = 128 * 1024 * 1024;
public const int MAX_CHILDREN_PER_SUPERBOX = 4096;
public const array SUPERBOX_TBOXES = ['jumb', 'c2pa', 'c2ma', 'c2as', 'c2cl', 'c2cs', 'c2vc'];
public function __construct(
private readonly int $maxPerBoxBytes = self::MAX_PER_BOX_BYTES,
private readonly int $maxTotalBytes = self::MAX_TOTAL_BYTES,
private readonly int $maxChildrenPerSuperbox = self::MAX_CHILDREN_PER_SUPERBOX,
)
public function parse(string $bytes): array
final readonly class C2paCapabilityStatus
public const string MATURITY_PREVIEW_DRAFT = 'preview-draft';
public function __construct(
public bool $previewEnabled,
public bool $generallyAvailable,
public bool $conformanceClaimed,
public string $maturity,
public string $specPin,
public string $envGate,
)
public static function current(): self
public function summary(): string
enum Feature: string
case PREVIEW_C2PA_DRAFT = 'preview_c2pa_draft';
public const string ENV_PREVIEW_C2PA_DRAFT = 'NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT';
public function isEnabled(): bool
final class ExperimentalC2paEmbedder
public const string SPEC_PIN_SHA = '4e2afed8f3ace20d41317e2e386c9340d2959d55';
public const string SPEC_PIN_DATE = '2026-04-26';
public function __construct()
public function buildManifestStore(string $sourceBytes, string $producer): ManifestStore
  • 两层拆分。 稳定接缝(ManifestStoreC2paManifestEmbedderJumbfBoxParser)始终可达。草案合成仅存在于 NextPDF\Pro\Compliance\C2pa\Experimental\ExperimentalC2paEmbedder 中,位于默认关闭的开关之后。提取与字节承载从不需要该开关;合成则始终需要。
  • 接缝不变量。 C2paManifestEmbedder 契约仅涉及字节:没有内存中的 PDF 对象跨越接缝,实现不执行任何网络或文件系统 I/O,且接缝自身从不组装 claim 断言。extract() 以返回 null 来表示不存在;它从不因不存在而抛出。
  • Store 语义。 ManifestStore 是一个不可变的、由根 JumbfBox 实例构成的有序列表,遵循 C2PA 2.1 §11.1.1 的 Manifest Store 模型:一个 JUMBF 容器聚合一个或多个 manifest,可按 URI 寻址。它不暴露任何 claim 级访问器。box 顺序被保留,且对往返相等性起决定作用。
  • 加固上限。 JumbfBoxParser 无条件拒绝任何超出上限的输入:单个 box 超过 64 MiB、累计 store 超过 128 MiB、嵌套深于 8 层,或某个 superbox 中子级超过 4096 个。没有任何策略开关可以禁用这些上限。对于内存受限的进程,可经构造函数注入更紧的上限。
  • 结构性拒绝。 解析器还以失败即关闭的方式拒绝:LBox = 0(BMFF 直至 EOF)、LBox = 1(XLBox 64 位长度)、小于 8 字节头部的 LBox、截断超出剩余输入、位于可打印 ASCII(0x20–0x7E)之外的 TBox 字节、偏移量重入(环)、以及 superbox 载荷的非精确子级铺排。它绝不返回部分构造的图。
  • Superbox 路由。 SUPERBOX_TBOXES 中的 TBox 值会作为子级序列递归解析;其他每个 TBox 都是携带不透明载荷的叶子。出于解析器安全考虑,cbor 被刻意视为叶子;上游层在需要时再解析其载荷。
  • 进程级开关。 Feature::PREVIEW_C2PA_DRAFT 默认关闭。仅当 NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT 恰好等于字符串 1 时,isEnabled() 才返回 true。每次调用都实时读取;不做任何记忆化。
  • 失败即关闭的构造。 当开关关闭时,new ExperimentalC2paEmbedder() 抛出 LogicException。消息会指明该开关、环境变量以及固定的草案 SHA 与日期。调用方无法意外抵达草案合成。
  • 合成形态。 buildManifestStore() 发出一个 c2pa superbox,内含一个 c2ma manifest,后者持有一个 c2as 断言存储(一条 c2pa.hash.data 断言)与一个 c2cl claim。该断言记录对 $sourceBytes 的一条 SHA-256 哈希断言;由于省略了 c2cs Claim Signature box 且输出未签名,这不是 C2PA 硬绑定(hard binding),也不是溯源裁定 —— 它只是遵循 §9.1 所描述的结构形态。Description-box 载荷携带一个类型 UUID、切换位 0x03 以及一个以 null 结尾的 UTF-8 标签,遵循 C2PA 2.1 §11.1.4.1.1–11.1.4.1.2。
  • 无 Claim Signature。 c2cs box —— 按 C2PA 2.1 §11.1.4.4 是一个标签为 c2pa.signature、由单个 CBOR 内容 box 构成的盒子 —— 被刻意从合成出的 Store 中省略。其输出在构造上即为未签名。这是被判定为在工作组冻结前最可能发生漂移的 profile 区域。
  • 草案固定,无 BC 保证。 合成出的线格式被固定到 c2pa-org/specificationsSPEC_PIN_SHA4e2afed8…,日期 2026-04-26)。它可能在不另行通知的情况下更改,且不带任何向后兼容保证。
  • 诚实不变量。 C2paCapabilityStatus::current()generallyAvailableconformanceClaimed 硬编码为 false。没有任何配置或环境开关能翻转其中任一布尔值。只有 previewEnabled 反映开关状态;maturity 是不作声明的令牌 preview-draft
  • 将开关变量设为 0trueyeson 或空字符串,都会让开关保持关闭。只有恰好为字符串 1 才会启用它。
  • putenv() 的更改会在下一次 isEnabled() 调用时生效,因为读取是实时的。进程运行中途切换的开关会被立即观察到。
  • extract() 区分两种结果:无 Store 时返回 null(廉价、无异常),以及存在 Store 但为敌意或畸形时抛出 C2paException 子类。不存在从不是错误;存在加上畸形则始终是错误。
  • JumbfBoxParser::parse('') 返回空列表。一个存在但为空的 ManifestStore 会往返为自身;接缝不会将其塌陷为 null
  • 嵌入一个空 Store 可以(MAY)原样返回输入。接缝契约允许这一空操作,但不强制要求。
  • 手工构造的 JumbfBox 图在构造时会运行相同的加固:TBox 长度与 ASCII 检查、深度上限、子级深度不变量、载荷与子级互斥规则,以及单 box 大小上限。手工构造的炸弹在构造时失败,而非在嵌入时。
  • 每个解析器异常都携带结构化字段 —— capKind/observed/capoffsetkind —— 因此遥测无需刮取消息字符串。所有子类都扩展自 C2paException(其本身是 RuntimeException),后者是总括性的捕获类型。
  • 解析器 docblock 禁止静默吞掉这些异常;使用方应将其浮现,或有意图地重映射它们。
  • buildManifestStore()JSON_THROW_ON_ERROR 编码 JSON 载荷;一个不是有效 UTF-8 的 $producer 字符串会在任何 box 被构建之前以 \JsonException 失败。
  • 一个格式良好的 extract() 结果只是一条结构性陈述。此面上任何位置都没有 claim 校验、没有签名验证、也没有信任评估。识别不是溯源裁定。
  • 此面不处理任何签名密钥、证书或 COSE 结构。唯一的密码学操作是受开关控制的合成路径内的一次 SHA-256 内容哈希。
声明标准条款
Manifest 序列化进一个持有多个 manifest、可按 URI 寻址的 JUMBF store。C2PA 2.1§11.1.1 (p63.b)
Description-box 标签是以 null 结尾的 UTF-8,含排除范围;所有 Description box 都定义了切换位。C2PA 2.1§11.1.4.1.1–11.1.4.1.2 (p63.a)
Claim Signature box 的标签为 c2pa.signature、类型为 c2cs,持有单个 CBOR 内容 box。C2PA 2.1§11.1.4.4 (p63.c)
硬绑定以密码学方式将 manifest 与其资产相绑并暴露修改 —— 预览的未签名哈希断言满足此标准。C2PA 2.1§9.1 (p57)

以上条款均为转述。NextPDF 不复制规范正文。NextPDF 未持有任何认证,也不授予任何认证。 上述陈述是关于 box 布局、标签与绑定的结构对齐陈述 —— 它们不是合规测试结果、不是第三方证明,也不是 C2PA 或 ISO 合规声明。C2PA-PDF profile 尚未定稿;合成出的线格式跟踪一个固定的草案提交。C2paCapabilityStatus 在代码中固化了这一姿态:generallyAvailableconformanceClaimed 在任何配置下都为 false。此面的输出不是可验证的 Content Credential,且 NextPDF 中不存在验证路径。

  • 解析器实现的 JUMBF box 文法(4 字节大端 LBox、4 字节 ASCII TBox、载荷;superbox 嵌套子 box)遵循 ISO 19566-5;该标准不在所引语料范围内,因此解析器行为基于产品源码确立,而非某条规范引用。

  • 在生产中保持开关关闭。草案合成不增加任何持久能力;发出的字节是临时的,一旦稳定的适配器发布,就应重新嵌入。

  • 针对你的流水线所期望的草案提交断言 ExperimentalC2paEmbedder::SPEC_PIN_SHA。在 CI 中运行 composer c2pa:draft-status(fresh 退出 0,soft-warn 退出 1,hard-fail 退出 2)以检测固定值的陈旧。

  • 在工具或 UI 中呈现 C2PA 状态时,将 C2paCapabilityStatus::current() 视为唯一可信来源。不要手工复述其布尔值;summary() 可安全用于日志与状态端点。

  • 在消费 extract()parse() 时,将 C2paException 作为总括类型捕获。使用四个子类的结构化字段将它们映射到彼此不同的遥测计数器。

  • 对于内存受限的验证器进程,经 JumbfBoxParser 构造函数注入更紧的上限;默认值是宽松的生产上限。

  • C2paCapabilityStatus::__construct() 是公开的,因此手工构造的实例可以携带任意布尔值。这样的实例只是一个值对象;它不改变任何行为。

本页仅记录外部可观测的行为与受支持的公开 API 面。内部命名空间路径、辅助类、机制表、runbook 文件名以及工单前缀均不在范围内。