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를 문서화합니다.
이 표면은 experimental 이며 두 계층으로 나뉩니다. 안정적인 심 — ManifestStore, C2paManifestEmbedder, JumbfBoxParser — 은 항상 도달 가능하며 Manifest Store 바이트를 양방향으로 운반합니다. 드래프트 매니페스트 합성 은 오직 ExperimentalC2paEmbedder에만 존재하며 기본 비활성화 되어 있습니다. C2PA-PDF 프로파일은 워킹 그룹에 의해 아직 확정되지 않았습니다. 합성된 와이어 포맷은 특정 드래프트 커밋에 고정되어 있습니다. 적합성 주장은 이루어지지 않으며, 검증 경로가 없고, 프리뷰 플래그를 켜도 둘 중 어느 것도 생성할 수 없습니다. 작업 지향적인 관점은 역량 페이지에 있습니다.
가용성 및 라이선싱
섹션 제목: “가용성 및 라이선싱”이 역량은 NextPDF Pro(nextpdf/pro)에 포함되어 출하되며 Pro 등급 라이선스 엔벨로프로 활성화됩니다. 해당 자격이 없는 배포는 이 역량의 클래스를 로드하지 않습니다. 에디션을 비교하고 라이선스를 받으세요.
라이선스는 Pro 컴플라이언스 표면 전체를 활성화합니다. 그 안의 C2PA 표면은 라이선스 등급과 무관하게 프리뷰로 남습니다. 드래프트 합성은 추가로 여기에 문서화된 프로세스 게이트를 요구합니다. Pro 라이선스만으로는 결코 그것을 활성화하지 못합니다.
공개 API 표면
섹션 제목: “공개 API 표면”| 심볼 | 매개변수 | 기본 동작 | 반환 | 던지거나 실패하는 경우 | 비고 |
|---|---|---|---|---|---|
C2paManifestEmbedder | — | 바이트 전용 임베드/추출 SPI; I/O 없음; 클레임 합성 없음 | — | — | 동결된 벤더 중립 심 인터페이스. |
C2paManifestEmbedder::embed() | string $pdfBytes, ManifestStore $store | 프로파일이 선언한 위치에 $store->toBytes()를 임베드; 비어 있는 Store는 no-op로 왕복(round-trip)될 수 있음(MAY) | string 새로운 PDF 바이트 | 임베드 실패 시 C2paException(과도하게 큰 Store, 유효하지 않은 PDF, 프로파일 위치 충돌) | 구현체는 입력 바이트를 변형하거나 보관하지 않습니다. |
C2paManifestEmbedder::extract() | string $pdfBytes | 저렴한 탐지 프로브; Store가 없는 경우 거의 아무것도 할당하지 않음 | ?ManifestStore(누락 시 null) | Store가 존재하지만 강화 불변식을 위반할 때 C2paException 서브클래스 | non-null Store는 이미 JumbfBoxParser 강화를 통과했습니다. |
ManifestStore::fromBoxes() | array $boxes(list<JumbfBox>) | 파서가 검증한 순서 있는 박스 목록을 래핑 | self | 스스로 던지지 않음; 손으로 만든 JumbfBox 생성은 동일한 강화를 강제함 | 생성자는 private이며, 왕복 동등성을 위해 박스 순서가 핵심(load-bearing)입니다. |
ManifestStore::empty() | 없음 | 루트 박스가 0개인 Store | self | 던지지 않음 | 비어 있는 Store의 toBytes()는 빈 문자열입니다. |
ManifestStore::isEmpty() | 없음 | 루트 박스가 0개인지 검사 | bool | 던지지 않음 | — |
ManifestStore::toBytes() | 없음 | 루트 박스 직렬화들을 연결 | string | 던지지 않음 | 이 바이트 시퀀스가 임베더가 기록하는 것입니다. |
ManifestStore::size() | 없음 | toBytes()의 바이트 길이 | int(>= 0) | 던지지 않음 | — |
JumbfBoxParser::__construct() | 세 개의 선택적 캡 재정의 | 프로덕션 캡: 박스당 64 MiB, 전체 128 MiB, 슈퍼박스당 자식 4096개 | JumbfBoxParser | 던지지 않음 | 깊이 캡은 MAX_DEPTH(8)로 고정되며 생성자로 조정할 수 없습니다. |
JumbfBoxParser::parse() | string $bytes | 루트 박스를 검증하고 구체화; 빈 입력은 []를 산출 | list<JumbfBox> | JumbfBombException, JumbfCycleDetectedException, JumbfDepthExceededException, MalformedJumbfException | 무상태(stateless); 부분 그래프를 결코 반환하지 않음; 한 인스턴스에 대한 동시 호출은 안전함. |
C2paCapabilityStatus::__construct() | 이름 있는 readonly 필드 여섯 개 | 임의의 디스크립터 인스턴스를 생성 | C2paCapabilityStatus | 던지지 않음 | current()가 정규(canonical) 생성자입니다. |
C2paCapabilityStatus::current() | 없음 | 게이트를 실시간으로 읽음; 클레임 불리언을 하드코딩 | C2paCapabilityStatus | 던지지 않음 | generallyAvailable와 conformanceClaimed는 항상 false입니다. |
C2paCapabilityStatus::summary() | 없음 | 한 줄짜리 상태 텍스트 | string | 던지지 않음 | GA나 적합성 주장을 담지 않도록 표현됨. |
Feature | 문자열 기반 enum, 케이스 1개 | 단일 케이스 PREVIEW_C2PA_DRAFT; 상수 ENV_PREVIEW_C2PA_DRAFT | enum 케이스 | 케이스 접근 시 아무것도 던지지 않음 | 범위가 한정된 안정성 게이트; 라이선스 자격과는 별개. |
Feature::isEnabled() | 없음 | getenv()를 실시간으로 읽음; 문자열 1과 엄격 비교 | bool | 던지지 않음 | 변수가 없거나 0, true, yes를 포함한 다른 어떤 값이든 off입니다. |
ExperimentalC2paEmbedder::__construct() | 없음 | 생성 시점에 fail-closed 게이트 검사 | ExperimentalC2paEmbedder | Feature::PREVIEW_C2PA_DRAFT가 off일 때 LogicException | 조용한 폴백은 존재하지 않습니다. |
ExperimentalC2paEmbedder::buildManifestStore() | string $sourceBytes, string $producer(non-empty) | SHA-256으로 $sourceBytes를 바인딩한 드래프트 형태의 Store를 생성 | ManifestStore | 페이로드 인코딩 실패 시 \JsonException; 박스 생성에서 C2paException 서브클래스 | c2cs Claim Signature 박스를 생략함; 출력은 구성상(by construction) 서명되지 않음. |
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도 수행하지 않으며, 심은 결코 스스로 클레임 어서션을 조립하지 않습니다.extract()는 부재를 알리기 위해null을 반환하며, 부재에 대해 결코 던지지 않습니다. - Store 의미론.
ManifestStore는 C2PA 2.1 §11.1.1의 Manifest Store 모델에 따른, 루트JumbfBox인스턴스들의 불변 순서 목록입니다: 하나 이상의 매니페스트를 집약하고 URI로 주소 지정 가능한 하나의 JUMBF 컨테이너입니다. 클레임 수준 접근자는 노출하지 않습니다. 박스 순서는 보존되며 왕복 동등성을 위해 핵심(load-bearing)입니다. - 강화 캡.
JumbfBoxParser는 어떤 캡이든 초과하는 입력을 무조건 거부합니다: 박스당 크기 64 MiB 초과, 누적 store 128 MiB 초과, 8단계보다 깊은 중첩, 또는 한 슈퍼박스에 4096개를 초과하는 자식. 어떤 정책 플래그도 이 캡을 비활성화하지 못합니다. 메모리가 제한된 프로세스를 위해 더 엄격한 캡을 생성자로 주입할 수 있습니다. - 구조적 거부. 파서는 또한 다음을 fail-closed로 거부합니다:
LBox = 0(BMFF의 EOF까지),LBox = 1(XLBox 64비트 길이), 8바이트 헤더보다 작은LBox, 남은 입력을 넘어가는 절단, 인쇄 가능 ASCII(0x20–0x7E) 밖의 TBox 바이트, 오프셋 재진입(사이클), 그리고 슈퍼박스 페이로드의 비정확한(non-exact) 자식 타일링. 부분적으로 구성된 그래프를 결코 반환하지 않습니다. - 슈퍼박스 라우팅.
SUPERBOX_TBOXES에 속하는 TBox 값은 자식 시퀀스로 재귀적으로 파싱되며, 그 밖의 모든 TBox는 불투명 페이로드를 가진 리프(leaf)입니다.cbor는 파서 안전을 위해 의도적으로 리프로 취급되며, 필요할 때 상위 계층이 그 페이로드를 다시 파싱합니다. - 프로세스 게이트.
Feature::PREVIEW_C2PA_DRAFT는 기본적으로 off입니다.isEnabled()는NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT가 정확히 문자열1과 같을 때에만true를 반환합니다. 읽기는 매 호출마다 실시간이며, 아무것도 메모이즈되지 않습니다. - Fail-closed 생성. 게이트가 off인 동안
new ExperimentalC2paEmbedder()는LogicException을 던집니다. 메시지는 플래그, 환경 변수, 그리고 고정된 드래프트 SHA와 날짜를 명시합니다. 호출자는 우발적으로 드래프트 합성에 도달할 수 없습니다. - 합성 형태.
buildManifestStore()는 하나의c2ma매니페스트를 담은c2pa슈퍼박스를 방출하며, 이 매니페스트는c2as어서션 스토어(하나의c2pa.hash.data어서션)와c2cl클레임을 보유합니다. 이 어서션은$sourceBytes에 대한 SHA-256 해시 어서션을 기록합니다.c2csClaim Signature 박스가 생략되고 출력이 서명되지 않으므로, 이것은 C2PA 하드 바인딩이나 출처(provenance) 판정이 아닙니다 — §9.1이 기술하는 구조적 형태만 따를 뿐입니다. Description 박스 페이로드는 타입 UUID, 토글0x03, 그리고 null 종료 UTF-8 레이블을 운반하며, 이는 C2PA 2.1 §11.1.4.1.1–11.1.4.1.2를 따릅니다. - Claim Signature 없음.
c2cs박스 — C2PA 2.1 §11.1.4.4에 따라c2pa.signature로 레이블된 단일 CBOR 콘텐츠 박스 — 는 합성된 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, 또는 빈 문자열로 설정하면 게이트는 off 상태로 남습니다. 오직 정확한 문자열1만 그것을 활성화합니다. putenv()변경은 읽기가 실시간이므로 다음isEnabled()호출에서 효력을 발휘합니다. 프로세스 도중에 토글된 게이트는 즉시 관찰됩니다.extract()는 두 결과를 구분합니다: Store가 없을 때의null(저렴하고 예외 없음), 그리고 Store가 존재하지만 적대적이거나 잘못된 형식일 때 던져지는C2paException서브클래스입니다. 부재는 결코 오류가 아니며, 존재와 잘못된 형식은 항상 오류입니다.JumbfBoxParser::parse('')는 빈 목록을 반환합니다. 비어 있지만 존재하는ManifestStore는 자기 자신으로 왕복하며, 심은 그것을null로 축소하지 않습니다.- 비어 있는 Store를 임베드하면 입력을 변경 없이 반환할 수 있습니다(MAY). 심 계약은 이 no-op를 허용하지만 의무화하지는 않습니다.
- 손으로 만든
JumbfBox그래프는 생성 시점에 동일한 강화를 실행합니다: TBox 길이 및 ASCII 검사, 깊이 캡, 자식 깊이 불변식, 페이로드-또는-자식 배타성 규칙, 그리고 박스당 크기 캡입니다. 손으로 만든 폭탄은 임베드 시점이 아니라 생성 시점에 실패합니다. - 모든 파서 예외는 구조화된 필드 —
capKind/observed/cap,offset, 또는kind— 를 운반하므로 텔레메트리가 메시지 문자열을 스크레이핑하지 않습니다. 모든 서브클래스는C2paException(그 자체가RuntimeException)을 확장하며, 이것이 우산 캐치 타입입니다. - 파서 docblock은 이 예외들을 조용히 삼키는 것을 금지합니다. 소비자는 그것들을 표면화하거나 의도를 가지고 재매핑합니다.
buildManifestStore()는 JSON 페이로드를JSON_THROW_ON_ERROR로 인코딩합니다. 유효한 UTF-8이 아닌$producer문자열은 어떤 박스가 만들어지기 전에\JsonException으로 실패합니다.- 잘 형성된
extract()결과는 구조적 진술일 뿐입니다. 이 표면 어디에도 클레임 검증, 서명 검증, 또는 신뢰 평가가 없습니다. 인식(recognition)은 출처 판정이 아닙니다. - 이 표면은 어떤 서명 키, 인증서, 또는 COSE 구조도 처리하지 않습니다. 유일한 암호화 연산은 게이트가 걸린 합성 경로 내부의 SHA-256 콘텐츠 해시입니다.
적합성
섹션 제목: “적합성”| 주장 | 표준 | 절 |
|---|---|---|
| 매니페스트는 여러 매니페스트를 담고 URI로 주소 지정 가능한 하나의 JUMBF store로 직렬화된다. | C2PA 2.1 | §11.1.1 (p63.b) |
| Description 박스 레이블은 제외 범위가 있는 null 종료 UTF-8이며, 토글은 모든 Description 박스에 대해 정의된다. | C2PA 2.1 | §11.1.4.1.1–11.1.4.1.2 (p63.a) |
Claim Signature 박스는 c2pa.signature로 레이블되고, c2cs로 타입 지정되며, 단일 CBOR 콘텐츠 박스를 보유한다. | C2PA 2.1 | §11.1.4.4 (p63.c) |
| 하드 바인딩은 매니페스트를 그 에셋에 암호학적으로 묶고 변경을 노출한다 — 프리뷰의 서명되지 않은 해시 어서션은 이 기준을 충족하지 못한다(NOT). | C2PA 2.1 | §9.1 (p57) |
모든 절은 의역되었습니다. NextPDF는 규범적 텍스트를 재현하지 않습니다. NextPDF는 어떤 인증도 보유하지 않으며 어떤 인증도 부여하지 않습니다. 위의 진술들은 박스 레이아웃, 레이블, 바인딩에 관한 구조적 정렬(structural-alignment) 진술입니다 — 적합성 테스트 결과도, 제3자 증명도, C2PA나 ISO 적합성 주장도 아닙니다. C2PA-PDF 프로파일은 확정되지 않았습니다. 합성된 와이어 포맷은 고정된 드래프트 커밋을 추적합니다. C2paCapabilityStatus는 이 자세를 코드로 인코딩합니다: generallyAvailable와 conformanceClaimed는 모든 구성에서 false입니다. 이 표면의 출력은 검증 가능한 Content Credential이 아니며, NextPDF에는 검증 경로가 존재하지 않습니다.
개발 노트
섹션 제목: “개발 노트”-
파서가 구현하는 JUMBF 박스 문법(4바이트 빅엔디언 LBox, 4바이트 ASCII TBox, 페이로드; 슈퍼박스는 자식 박스를 중첩)은 ISO 19566-5를 따릅니다. 그 표준은 인용된 코퍼스 밖에 있으므로, 파서 동작은 스펙 인용이 아니라 제품 소스에서 근거합니다.
-
프로덕션에서는 게이트를 off로 유지하세요. 드래프트 합성은 지속적인 역량을 더하지 않습니다. 방출된 바이트는 일시적이며, 안정적인 어댑터가 출하되면 다시 임베드되어야 합니다.
-
파이프라인이 기대하는 드래프트 커밋에 대해
ExperimentalC2paEmbedder::SPEC_PIN_SHA를 어설션하세요. 고정 노후화(pin staleness)를 감지하려면 CI에서composer c2pa:draft-status를 실행하세요(신선하면 exit 0, 소프트 경고 1, 하드 실패 2). -
툴링이나 UI에서 C2PA 상태를 표면화할 때는
C2paCapabilityStatus::current()를 유일한 진실 공급원으로 취급하세요. 그 불리언들을 손으로 다시 진술하지 마세요.summary()는 로그와 상태 엔드포인트에 안전합니다. -
extract()나parse()를 소비할 때는 우산 타입으로C2paException을 캐치하세요. 네 개의 서브클래스를 그들의 구조화된 필드를 사용해 별개의 텔레메트리 카운터로 매핑하세요. -
메모리가 제한된 검증기 프로세스를 위해
JumbfBoxParser생성자를 통해 더 엄격한 캡을 주입하세요. 기본값은 넉넉한 프로덕션 캡입니다. -
C2paCapabilityStatus::__construct()는 public이므로, 손으로 만든 인스턴스는 임의의 불리언을 운반할 수 있습니다. 그런 인스턴스는 값 객체일 뿐이며, 어떤 동작도 바꾸지 않습니다.
함께 보기
섹션 제목: “함께 보기”- C2PA 프리뷰 역량 상태 — 역량 페이지
- 보안 — 심화 레퍼런스 (Pro)
- 컴플라이언스 — 심화 레퍼런스 (Pro)
- 포스트 양자 서명 프리뷰 — 심화 레퍼런스 (Enterprise)
- 보안 / 서명 (Core)
발행 경계
섹션 제목: “발행 경계”이 페이지는 외부에서 관찰 가능한 동작과 지원되는 공개 API 표면만 문서화합니다. 내부 네임스페이스 경로, 헬퍼 클래스, 메커니즘 테이블, 런북 파일명, 티켓 접두사는 범위 밖입니다.