Перейти к содержимому
getnextpdf.com

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 его никогда не включает.

СимволПараметрыПоведение по умолчаниюВозвращаетБросает или падает сПримечания
C2paManifestEmbedderSPI встраивания/извлечения только байтов; без I/O; без синтеза заявленийЗамороженный, вендоронезависимый интерфейс шва.
C2paManifestEmbedder::embed()string $pdfBytes, ManifestStore $storeВстраивает $store->toBytes() в объявленное профилем место; пустой Store МОЖЕТ пройти round-trip как no-opstring новые байты PDFC2paException при любом сбое встраивания (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() вживую; строгое сравнение со строкой 1boolНе бросаетОтсутствующая переменная или любое другое значение, включая 0, true, yes, означает «выключено».
ExperimentalC2paEmbedder::__construct()нетОтказоустойчивая проверка гейта во время конструированияExperimentalC2paEmbedderLogicException, когда Feature::PREVIEW_C2PA_DRAFT выключенТихого запасного пути не существует.
ExperimentalC2paEmbedder::buildManifestStore()string $sourceBytes, string $producer (непустая)Строит Store в форме черновика, привязывая $sourceBytes через SHA-256ManifestStore\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): 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
  • Разделение на два слоя. Стабильный шов (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 Signature c2cs опущен, а вывод не подписан, это НЕ жёсткая привязка 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() публичен, поэтому собранный вручную экземпляр может нести произвольные булевы значения. Такой экземпляр — лишь объект-значение; он не меняет никакого поведения.

Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов вне области рассмотрения.