Pro редакциястабильность: Экспериментальная
Предпросмотр C2PA — глубокий справочник
Эта страница — справочник контрактного уровня по поверхности предпросмотра C2PA (Content Credentials) в NextPDF Pro. Она охватывает пять публичных символов в NextPDF\Pro\Compliance\C2pa: SPI C2paManifestEmbedder, объект-значение ManifestStore, JumbfBoxParser, дескриптор C2paCapabilityStatus и гейтированный Experimental\ExperimentalC2paEmbedder. Также описан гейт Feature::PREVIEW_C2PA_DRAFT и его переменная окружения NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT.
Поверхность экспериментальная и разделена на два слоя. Стабильный шов — 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 МОЖЕТ пройти round-trip как no-op | string новые байты PDF | C2paException при любом сбое встраивания (Store слишком велик, некорректный PDF, коллизия с местом по профилю) | Реализации никогда не изменяют и не удерживают входные байты. |
C2paManifestEmbedder::extract() | string $pdfBytes | Дешёвый зонд обнаружения; случай без Store почти ничего не выделяет | ?ManifestStore (null при промахе) | Подкласс C2paException, когда Store присутствует, но нарушает инвариант устойчивости | Ненулевой Store уже прошёл устойчивость JumbfBoxParser. |
ManifestStore::fromBoxes() | array $boxes (list<JumbfBox>) | Оборачивает проверенный парсером упорядоченный список боксов | self | Сам не бросает; ручное конструирование JumbfBox применяет ту же устойчивость | Конструктор приватный; порядок боксов важен для равенства при round-trip. |
ManifestStore::empty() | нет | Store с нулём корневых боксов | self | Не бросает | toBytes() пустого Store — пустая строка. |
ManifestStore::isEmpty() | нет | Проверяет отсутствие корневых боксов | 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 | Без состояния; никогда не возвращает частичный граф; параллельные вызовы на одном экземпляре безопасны. |
C2paCapabilityStatus::__construct() | шесть именованных readonly-полей | Строит произвольный экземпляр дескриптора | C2paCapabilityStatus | Не бросает | current() — канонический конструктор. |
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, означает «выключено». |
ExperimentalC2paEmbedder::__construct() | нет | Отказоустойчивая проверка гейта во время конструирования | ExperimentalC2paEmbedder | LogicException, когда Feature::PREVIEW_C2PA_DRAFT выключен | Тихого запасного пути не существует. |
ExperimentalC2paEmbedder::buildManifestStore() | string $sourceBytes, string $producer (непустая) | Строит Store в форме черновика, привязывая $sourceBytes через SHA-256 | ManifestStore | \JsonException при сбое кодирования payload; подклассы C2paException при конструировании боксов | Опускает бокс Claim Signature c2cs; вывод по построению не подписан. |
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— неизменяемый упорядоченный список корневых экземпляровJumbfBox, согласно модели Manifest Store из C2PA 2.1 §11.1.1: один JUMBF-контейнер, агрегирующий один или несколько манифестов, адресуемых по URI. Он не предоставляет аксессоров уровня заявления. Порядок боксов сохраняется и важен для равенства при round-trip. - Лимиты устойчивости.
JumbfBoxParserбезусловно отклоняет входы, превышающие любой лимит: размер бокса свыше 64 MiB, суммарный store свыше 128 MiB, вложенность глубже 8 уровней или более 4096 детей в одном суперБоксе. Никакой флаг политики не отключает эти лимиты. Более строгие лимиты можно внедрить через конструктор для процессов с ограниченной памятью. - Структурное отклонение. Парсер также отклоняет, отказоустойчиво:
LBox = 0(BMFF до EOF),LBox = 1(XLBox 64-битная длина),LBoxменьше 8-байтового заголовка, усечение за пределами оставшегося ввода, байты TBox вне печатного ASCII (0x20–0x7E), повторный вход по смещению (циклы) и неточное покрытие payload суперБокса дочерними боксами. Он никогда не возвращает частично построенный граф. - Маршрутизация суперБоксов. Значения TBox из
SUPERBOX_TBOXESразбираются рекурсивно как последовательности детей; всякий иной TBox — лист с непрозрачным payload.cborнамеренно трактуется как лист ради безопасности парсера; вышестоящие слои повторно разбирают его payload при необходимости. - Процессный гейт.
Feature::PREVIEW_C2PA_DRAFTвыключен по умолчанию.isEnabled()возвращаетtrue, только когдаNEXTPDF_FEATURE_PREVIEW_C2PA_DRAFTв точности равна строке1. Чтение выполняется вживую при каждом вызове; ничего не мемоизируется. - Отказоустойчивое конструирование.
new ExperimentalC2paEmbedder()бросаетLogicException, пока гейт выключен. Сообщение называет флаг, переменную окружения и закреплённые SHA и дату черновика. Вызывающий не может случайно добраться до синтеза черновика. - Форма синтеза.
buildManifestStore()выдаёт суперБоксc2pa, содержащий один манифестc2ma, который хранит хранилище утвержденийc2as(одно утверждениеc2pa.hash.data) и заявлениеc2cl. Утверждение записывает хеш-утверждение SHA-256 над$sourceBytes; поскольку бокс Claim Signaturec2csопущен, а вывод не подписан, это НЕ жёсткая привязка C2PA и не вердикт о происхождении — оно лишь следует структурной форме, описанной в §9.1. Payload’ы 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 единственный CBOR-бокс содержимого с меткойc2pa.signature— намеренно опущен в синтезируемом Store. Вывод по построению не подписан. Это область профиля, признанная наиболее вероятной к дрейфу до заморозки рабочей группой. - Закрепление черновика, без гарантии BC. Синтезируемый формат передачи закреплён за
SPEC_PIN_SHA(4e2afed8…, датировано2026-04-26) репозиторияc2pa-org/specifications. Он может измениться без предупреждения и не несёт гарантии обратной совместимости. - Инвариант честности.
C2paCapabilityStatus::current()жёстко зашиваетgenerallyAvailableиconformanceClaimedвfalse. Никакая конфигурация или флаг окружения не переключает ни один из этих булевых значений. ТолькоpreviewEnabledотражает гейт;maturity— не несущий заявления токенpreview-draft.
Пограничные случаи и режимы отказа
Заголовок раздела «Пограничные случаи и режимы отказа»- Установка переменной гейта в
0,true,yes,onили пустую строку оставляет гейт выключенным. Включает только точная строка1. - Изменения через
putenv()вступают в силу при следующем вызовеisEnabled(), поскольку чтение живое. Гейт, переключённый в середине процесса, наблюдается немедленно. extract()различает два исхода:null, когда Store отсутствует (дёшево, без исключений), и брошенный подклассC2paException, когда Store присутствует, но враждебен или искажён. Отсутствие никогда не ошибка; присутствие плюс искажение — всегда.JumbfBoxParser::parse('')возвращает пустой список. Пустой, но присутствующийManifestStoreпроходит round-trip сам в себя; шов не сворачивает его вnull.- Встраивание пустого Store МОЖЕТ вернуть вход без изменений. Контракт шва допускает этот no-op, но не предписывает его.
- Собранные вручную графы
JumbfBoxпроходят ту же устойчивость во время конструирования: проверки длины и ASCII TBox, лимит глубины, инвариант глубины детей, правило исключительности payload-или-детей и лимит размера бокса. Собранная вручную бомба падает при конструировании, а не при встраивании. - Каждое исключение парсера несёт структурированные поля —
capKind/observed/cap,offsetилиkind— чтобы телеметрия не парсила строки сообщений. Все подклассы наследуютC2paException(сам являющийсяRuntimeException), который и есть зонтичный тип для catch. - Docblock парсера запрещает молчаливое проглатывание этих исключений; потребители всплывают их или переотображают осознанно.
buildManifestStore()кодирует JSON-payload’ы сJSON_THROW_ON_ERROR; строка$producer, не являющаяся валидным UTF-8, падает с\JsonExceptionдо построения любого бокса.- Корректный результат
extract()— лишь структурное утверждение. Нигде на этой поверхности нет валидации заявлений, проверки подписи или оценки доверия. Распознавание — не вердикт о происхождении. - Эта поверхность не обрабатывает ни ключей подписи, ни сертификатов, ни структур COSE. Единственная криптографическая операция — контентный хеш SHA-256 внутри гейтированного пути синтеза.
Соответствие
Заголовок раздела «Соответствие»| Заявление | Стандарт | Пункт |
|---|---|---|
| Манифесты сериализуются в один JUMBF-store, содержащий несколько манифестов, адресуемых по URI. | 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) |
| Жёсткая привязка криптографически связывает манифест с его ассетом и раскрывает изменение — неподписанное хеш-утверждение предпросмотра НЕ достигает этой планки. | C2PA 2.1 | §9.1 (p57) |
Все пункты пересказаны. NextPDF не воспроизводит нормативный текст. NextPDF не имеет никакой сертификации и не предоставляет никакой. Приведённые выше утверждения — это утверждения о структурном соответствии раскладки боксов, меток и привязок — они не являются результатами тестов на соответствие, не сторонними аттестациями и не заявлением о соответствии C2PA или ISO. Профиль C2PA-PDF не финализирован; синтезируемый формат передачи отслеживает закреплённый черновой коммит. C2paCapabilityStatus кодирует эту позицию в коде: generallyAvailable и conformanceClaimed равны false в любой конфигурации. Вывод этой поверхности не является проверяемым Content Credential, и в NextPDF пути проверки не существует.
Заметки по разработке
Заголовок раздела «Заметки по разработке»-
Грамматика JUMBF-боксов, которую реализует парсер (4-байтовый big-endian LBox, 4-байтовый ASCII TBox, payload; суперБоксы вкладывают дочерние боксы), следует ISO 19566-5; этот стандарт вне цитируемого корпуса, поэтому поведение парсера обосновано исходным кодом продукта, а не цитатой спецификации.
-
Держите гейт выключенным в продакшене. Синтез черновика не добавляет долговременной возможности; выдаваемые байты преходящи и должны быть повторно встроены, как только выйдет стабильный адаптер.
-
Проверяйте
ExperimentalC2paEmbedder::SPEC_PIN_SHAпротив чернового коммита, который ожидает ваш конвейер. Запускайтеcomposer c2pa:draft-statusв CI (выход 0 — свежо, 1 — мягкое предупреждение, 2 — жёсткий отказ) для обнаружения устаревания закрепления. -
Считайте
C2paCapabilityStatus::current()единственным источником истины при отображении статуса C2PA в инструментах или UI. Не переизлагайте его булевы значения вручную;summary()безопасен для логов и статус-эндпоинтов. -
Ловите
C2paExceptionкак зонтичный тип при потребленииextract()илиparse(). Отображайте четыре подкласса в отдельные счётчики телеметрии, используя их структурированные поля. -
Внедряйте более строгие лимиты через конструктор
JumbfBoxParserдля процессов-верификаторов с ограниченной памятью; значения по умолчанию — щедрые продакшн-лимиты. -
C2paCapabilityStatus::__construct()публичен, поэтому собранный вручную экземпляр может нести произвольные булевы значения. Такой экземпляр — лишь объект-значение; он не меняет никакого поведения.
Смотрите также
Заголовок раздела «Смотрите также»- Статус возможности предпросмотра C2PA — страница возможности
- Безопасность — глубокий справочник (Pro)
- Соответствие — глубокий справочник (Pro)
- Предпросмотр постквантовой подписи — глубокий справочник (Enterprise)
- Безопасность / Подписание (Core)
Граница публикации
Заголовок раздела «Граница публикации»Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов вне области рассмотрения.