Pro 版本穩定性: 實驗性
C2PA 預覽 — 深入參考
本頁是 NextPDF Pro 中 C2PA(Content Credentials)預覽介面的合約層級參考。它涵蓋 NextPDF\Pro\Compliance\C2pa 中的五個公開符號:C2paManifestEmbedder SPI、ManifestStore 值物件、JumbfBoxParser、C2paCapabilityStatus 描述子,以及受閘門控制的 Experimental\ExperimentalC2paEmbedder。它也記載了 Feature::PREVIEW_C2PA_DRAFT 閘門及其環境變數 NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT。
此介面屬於實驗性質,並分為兩層。穩定接縫——ManifestStore、C2paManifestEmbedder、JumbfBoxParser——始終可達,並雙向承載 Manifest Store 位元組。草案 manifest 合成僅存在於 ExperimentalC2paEmbedder,且預設關閉。C2PA-PDF 設定檔尚未由工作組定案;合成後的線路格式被釘選至某個草案提交。它不主張任何符合性,沒有驗證路徑,啟用預覽旗標也無法創造出兩者中的任何一個。以任務為導向的說明位於能力頁面。
可用性與授權
標題為「可用性與授權」的區段此能力隨 NextPDF Pro(nextpdf/pro)出貨,並以 Pro 等級的授權封套啟用。缺少該權利的部署不會載入此能力的類別。比較各版本並取得授權。
授權會整體啟用 Pro 符合性介面。無論授權等級為何,其內部的 C2PA 介面始終維持預覽狀態。草案合成另需本頁所記載的行程閘門;單憑 Pro 授權永遠不會啟用它。
公開 API 介面
標題為「公開 API 介面」的區段| 符號 | 參數 | 預設行為 | 回傳 | 拋出或失敗於 | 備註 |
|---|---|---|---|---|---|
C2paManifestEmbedder | — | 僅位元組的嵌入/擷取 SPI;無 I/O;不合成 claim | — | — | 凍結、供應商中立的接縫介面。 |
C2paManifestEmbedder::embed() | string $pdfBytes、ManifestStore $store | 在設定檔宣告的位置嵌入 $store->toBytes();空的 Store 可以以無操作方式往返 | string 新的 PDF 位元組 | 任何嵌入失敗時拋出 C2paException(Store 過大、PDF 無效、設定檔位置衝突) | 實作絕不變更或保留輸入位元組。 |
C2paManifestEmbedder::extract() | string $pdfBytes | 廉價的偵測探針;無 Store 的情形幾乎不配置記憶體 | ?ManifestStore(未命中回傳 null) | 存在 Store 但違反強化不變量時拋出 C2paException 子類別 | 非 null 的 Store 已通過 JumbfBoxParser 強化。 |
ManifestStore::fromBoxes() | array $boxes(list<JumbfBox>) | 包裝一份經剖析器驗證的有序 box 清單 | self | 本身不拋出;手工建構 JumbfBox 會執行相同的強化 | 建構子為私有;box 順序對往返相等性具有承載作用。 |
ManifestStore::empty() | 無 | 具有零個根 box 的 Store | self | 不拋出 | 空 Store 的 toBytes() 為空字串。 |
ManifestStore::isEmpty() | 無 | 檢測是否為零個根 box | bool | 不拋出 | — |
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> | JumbfBombException、JumbfCycleDetectedException、JumbfDepthExceededException、MalformedJumbfException | 無狀態;絕不回傳部分圖;對單一實例的並行呼叫是安全的。 |
C2paCapabilityStatus::__construct() | 六個具名的 readonly 欄位 | 建立一個任意的描述子實例 | C2paCapabilityStatus | 不拋出 | current() 是規範建構子。 |
C2paCapabilityStatus::current() | 無 | 即時讀取閘門;將 claim 布林值硬編碼 | C2paCapabilityStatus | 不拋出 | generallyAvailable 與 conformanceClaimed 永遠為 false。 |
C2paCapabilityStatus::summary() | 無 | 單行狀態文字 | string | 不拋出 | 措辭不帶任何 GA 或符合性主張。 |
Feature | 字串支撐的列舉,1 個 case | 單一 case PREVIEW_C2PA_DRAFT;常數 ENV_PREVIEW_C2PA_DRAFT | 列舉 case | 存取 case 時不拋出任何東西 | 範疇化的穩定性閘門;有別於授權權利。 |
Feature::isEnabled() | 無 | 即時讀取 getenv();與字串 1 嚴格比較 | bool | 不拋出 | 變數不存在或為任何其他值(包含 0、true、yes)皆為關閉。 |
ExperimentalC2paEmbedder::__construct() | 無 | 建構時的失敗即關閉閘門檢查 | ExperimentalC2paEmbedder | 當 Feature::PREVIEW_C2PA_DRAFT 關閉時拋出 LogicException | 不存在任何無聲退回。 |
ExperimentalC2paEmbedder::buildManifestStore() | string $sourceBytes、string $producer(非空) | 建立一個草案形狀的 Store,透過 SHA-256 綁定 $sourceBytes | ManifestStore | payload 編碼失敗時拋出 \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): selfpublic static function empty(): selfpublic function isEmpty(): boolpublic function toBytes(): stringpublic function size(): intfinal 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): arrayfinal 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(): selfpublic function summary(): stringenum Feature: string
case PREVIEW_C2PA_DRAFT = 'preview_c2pa_draft';
public const string ENV_PREVIEW_C2PA_DRAFT = 'NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT';
public function isEnabled(): boolfinal 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行為合約
標題為「行為合約」的區段- 兩層切分。 穩定接縫(
ManifestStore、C2paManifestEmbedder、JumbfBoxParser)始終可達。草案合成僅存在於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 payload 的非精確子項排布。它絕不回傳部分建構的圖。 - Superbox 路由。 位於
SUPERBOX_TBOXES中的 TBox 值會遞迴剖析為子序列;其他所有 TBox 都是帶有不透明 payload 的葉節點。cbor刻意被當作葉節點以維護剖析器安全;上游各層在需要時再重新剖析其 payload。 - 行程閘門。
Feature::PREVIEW_C2PA_DRAFT預設關閉。唯有當NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT精確等於字串1時,isEnabled()才回傳true。每次呼叫都是即時讀取;不做任何記憶化。 - 失敗即關閉的建構。 當閘門關閉時,
new ExperimentalC2paEmbedder()拋出LogicException。訊息會指名該旗標、環境變數,以及釘選的草案 SHA 與日期。呼叫者無法意外抵達草案合成。 - 合成形狀。
buildManifestStore()產出一個c2pasuperbox,其中含有一個c2mamanifest,該 manifest 持有一個c2as主張儲存區(一個c2pa.hash.data主張)與一個c2clclaim。該主張記錄一個針對$sourceBytes的 SHA-256 雜湊主張;由於c2csClaim Signature box 被省略且輸出未簽署,這不是 C2PA 硬綁定,也不是溯源裁決——它僅僅依循 §9.1 所描述的結構形狀。Description box 的 payload 依 C2PA 2.1 §11.1.4.1.1–11.1.4.1.2,帶有一個型別 UUID、切換位元0x03,以及一個以 null 結尾的 UTF-8 標籤。 - 無 Claim Signature。
c2csbox——依 C2PA 2.1 §11.1.4.4 是一個標籤為c2pa.signature的單一 CBOR content box——被刻意從合成的 Store 中省略。輸出依建構即為未簽署。這是被判斷在工作組凍結前最可能漂移的設定檔區域。 - 草案釘選,無 BC 保證。 合成後的線路格式被釘選至
c2pa-org/specifications的SPEC_PIN_SHA(4e2afed8…,日期2026-04-26)。它可能在不預告的情況下變更,且不帶任何向後相容保證。 - 誠實不變量。
C2paCapabilityStatus::current()將generallyAvailable與conformanceClaimed硬編碼為false。沒有任何組態或環境旗標能翻轉任一布林值。只有previewEnabled反映閘門;maturity是不主張任何東西的權杖preview-draft。
邊界情形與失敗模式
標題為「邊界情形與失敗模式」的區段- 將閘門變數設為
0、true、yes、on或空字串,都會使閘門維持關閉。唯有精確的字串1才能啟用它。 putenv()的變更會在下一次isEnabled()呼叫時生效,因為讀取是即時的。行程中途切換的閘門會被立即觀察到。extract()區分兩種結果:無 Store 存在時回傳null(廉價、無例外),以及存在 Store 但具敵意或格式錯誤時拋出C2paException子類別。不存在永遠不是錯誤;存在加上格式錯誤則永遠是錯誤。JumbfBoxParser::parse('')回傳空清單。一個空但存在的ManifestStore會往返回自身;接縫不會將它塌縮為null。- 嵌入一個空的 Store 可以原封不動地回傳輸入。接縫合約允許此無操作,但不強制。
- 手工建構的
JumbfBox圖在建構時執行相同的強化:TBox 長度與 ASCII 檢查、深度上限、子項深度不變量、payload 或 children 互斥規則,以及單一 box 大小上限。手工建構的炸彈會在建構時失敗,而非在嵌入時。 - 每個剖析器例外都攜帶結構化欄位——
capKind/observed/cap、offset或kind——因此遙測不需刮取訊息字串。所有子類別都擴充C2paException(其本身是RuntimeException),也就是那個總括的攔截型別。 - 剖析器 docblock 禁止無聲吞掉這些例外;消費者應將它們浮現出來,或帶著意圖重新映射它們。
buildManifestStore()以JSON_THROW_ON_ERROR編碼 JSON payload;一個非有效 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 content box。 | C2PA 2.1 | §11.1.4.4 (p63.c) |
| 硬綁定在密碼學上將 manifest 綁定至其資產並揭露被修改——預覽的未簽署雜湊主張不達此門檻。 | C2PA 2.1 | §9.1 (p57) |
所有條款皆為改寫轉述。NextPDF 不重製規範性文字。NextPDF 不持有任何認證,也不授予任何認證。 上述陳述是關於 box 佈局、標籤與綁定的結構對齊陳述——它們不是符合性測試結果、不是第三方認證,也不是 C2PA 或 ISO 符合性主張。C2PA-PDF 設定檔尚未定案;合成後的線路格式追蹤一個釘選的草案提交。C2paCapabilityStatus 在程式碼中編碼此姿態:generallyAvailable 與 conformanceClaimed 在每一種組態中都是 false。此介面的輸出不是可驗證的 Content Credential,且 NextPDF 中不存在任何驗證路徑。
開發備註
標題為「開發備註」的區段-
剖析器所實作的 JUMBF box 文法(4 位元組大端序 LBox、4 位元組 ASCII TBox、payload;superbox 巢狀包含子 box)依循 ISO 19566-5;該標準不在所引用的語料範圍內,因此剖析器行為是以產品原始碼為基礎,而非引用規範。
-
在生產環境中保持閘門關閉。草案合成不增添任何持久能力;產出的位元組是暫時性的,應在穩定的轉接器出貨後重新嵌入。
-
將
ExperimentalC2paEmbedder::SPEC_PIN_SHA對照你的管線所預期的草案提交進行斷言。在 CI 中執行composer c2pa:draft-status(新鮮時退出 0、軟警告 1、硬失敗 2)以偵測釘選過時。 -
在工具或 UI 中呈現 C2PA 狀態時,將
C2paCapabilityStatus::current()視為唯一真實來源。不要手動重述它的布林值;summary()對日誌與狀態端點是安全的。 -
消費
extract()或parse()時,以C2paException作為總括型別來攔截。使用四個子類別的結構化欄位,將它們映射到各自不同的遙測計數器。 -
針對記憶體受限的驗證器行程,透過
JumbfBoxParser建構子注入更緊的上限;預設值是寬裕的生產上限。 -
C2paCapabilityStatus::__construct()是公開的,因此手工建構的實例可以攜帶任意布林值。這樣的實例僅是一個值物件;它不改變任何行為。
另請參閱
標題為「另請參閱」的區段發佈邊界
標題為「發佈邊界」的區段本頁僅記載外部可觀察的行為與受支援的公開 API 介面。內部命名空間路徑、輔助類別、機制表格、runbook 檔名,以及工單前綴皆不在範圍內。