veraPDF поднимает флаг ISO 19005-1 6.2.3.3 на файле, который вообще не рисует RGB, называет голый re f некалиброванным или валит страницу 2 при проходящей идентичной странице 1. PDF/A судит эти правила по тому, что рисует каждая страница, поэтому валидатор PDF/A компонента PDFium для Delphi прослеживает каждую страницу сквозь формы, паттерны, глифы и аннотации, прежде чем выносить вердикт по цвету и прозрачности
Что на самом деле требуют 6.2.3.3 и 6.2.4.3
ISO 19005-1 6.2.3.3 упаковывает три независимых правила в один абзац. Соответствующий файл может использовать DeviceRGB или DeviceCMYK, но не оба сразу; если используется какое-либо некалиброванное цветовое пространство, файл должен содержать PDF/A-1 OutputIntent; а DeviceRGB допустим только с намерением, чей профиль RGB, DeviceCMYK — только с CMYK. DeviceGray вне этого списка, потому что пункт дальше поясняет конвертацию серого под любым из намерений. Само намерение необязательно: 6.2.2 говорит, что файл может его указать, и засчитывает только запись OutputIntents с /S /GTS_PDFA1 и корректным потоком ICC-профиля в /DestOutputProfile. Поэтому проверяющий, требующий намерение от каждого файла, отвергнет соответствующие документы, которые никогда не рисуют цветом устройства. Part 1 не упоминает DefaultRGB или DefaultCMYK, но цвет, переключённый через них, рендерится в пространстве на базе CIE (ISO 32000-1 §8.6.5.6), что удовлетворяет требованию независимости от устройства из 6.2.3.1; тестовый набор veraPDF для 6.2.3.3 считает его калиброванным, и PDFium Component читает пункт так же. Запрет смешивания стоит особняком: файл PDF/A-1, рисующий и RGB, и CMYK, валится, даже когда записи Default калибруют оба. ISO 19005-2 6.2.4.3 снимает этот запрет и калибрует каждое пространство само по себе, как показывает правая колонка ниже, поэтому PDFium Component гонит один скан для каждой части и применяет проверку смешивания только к заявлениям PDF/A-1
| Нарисованное пространство устройства | PDF/A-1 (ISO 19005-1 6.2.3.3) | PDF/A-2 и позже (ISO 19005-2 6.2.4.3) |
|---|---|---|
| DeviceRGB | DefaultRGB в области видимости или RGB-намерение; не рядом с DeviceCMYK | DefaultRGB в области видимости или RGB OutputIntent |
| DeviceCMYK | DefaultCMYK в области видимости или CMYK-намерение; не рядом с DeviceRGB | DefaultCMYK в области видимости или CMYK OutputIntent |
| DeviceGray | DefaultGray в области видимости или любой PDF/A-1 OutputIntent | DefaultGray в области видимости или любой PDF/A OutputIntent |
Что считается использованием цветового пространства?
Использовать цветовое пространство — значит рисовать им по пути, который ридер отрендерит. Объявить его недостаточно. Набор veraPDF доказывает это проходящим файлом 6.2.3.3: именованный Form XObject выставляет CMYK через K и k, сидит в словаре ресурсов страницы, никогда не упоминается из содержимого страницы, а OutputIntent файла — RGB. Поиск имён цветов устройства по всему файлу валит этот файл и пропускает тот, что рисует только DeviceRGB под CMYK-намерением, где никакие два имени не конфликтуют. PDFium Component вместо этого обходит граф отрисовки, построенный на его побайтовом preflight-сканере PDF/A. С каждой страницы обход считает цвет, нарисованный:
- потоками содержимого страницы и каждым Form XObject, который они вызывают через
Do, включая вложенные формы - паттерном, выбранным через
scnилиSCN, читаемым с собственнымиResourcesпаттерна CharProcsшрифта Type 3, выбранного черезTf- XObject-изображениями и инлайн-изображениями — по цветовому пространству, которое называет их собственный словарь
- appearance-потоками аннотаций, рисуемыми поверх страницы; когда
/AP /Nвиджета — словарь состояний, считается каждое состояние, поскольку переключение поля рисует то, чего не выбрал/AS
Более тонкая половина достижимости — цвет, который содержимое никогда не называет. q 0 0 10 10 re f Q не содержит ни одного цветового оператора. Он всё равно рисует. Начальное цветовое пространство состояния графики — DeviceGray (ISO 32000-1 Table 52), поэтому заливка — чёрный DeviceGray, некалиброванный любой частью, пока не существуют PDF/A OutputIntent или DefaultGray. Поэтому скан отслеживает, выбрал ли что-нибудь цвет до сих пор: rg, k, g и их варианты для обводки выбирают пространство устройства, cs и CS выбирают то, что называют, а оператор заливки, обводки или текста, достигнутый до любого выбора, записывает DeviceGray. /CS0 cs 1 0 0 /P0 scn уже выбрал пространство, так что этот scn — не серый
Инлайн-изображения тащат своё цветовое пространство и сырые отсчёты прямо в поток содержимого, и сканеру приходится внимательно читать и то, и другое. /CS может быть сокращением (G, RGB, CMYK, I) или именем ресурса, скажем разрешающегося в [/Indexed /DeviceRGB 255 13 0 R], где считается база; а байты между ID и EI — произвольные двоичные, поэтому EI завершает изображение только между пробельными символами, иначе rg внутри сжатых отсчётов становится фантомным оператором
% page 1: DefaultRGB в области видимости, и XObject, который никто не вызывает
4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R
/Resources << /ColorSpace << /DefaultRGB 9 0 R >>
/XObject << /Fm0 6 0 R >> >> >>
endobj
% page 2: тот же поток содержимого, без подсловаря ColorSpace
7 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /Resources << >> >>
endobj
5 0 obj << /Length 46 >> stream
1 0 0 rg 72 72 200 100 re f
q 0 0 10 10 re f Q
endstream
endobj
6 0 obj << /Type /XObject /Subtype /Form /BBox [0 0 50 50] /Length 24 >> stream
0 0 0 1 k 0 0 50 50 re f
endstream
endobj
9 0 obj [/ICCBased 10 0 R]
endobj
Каждый симптом из вступления вытекает из этого фрагмента. /Fm0 никогда не встречает Do, так что его k нерелевантен. Голый re f на обеих страницах — DeviceGray без ничего, что могло бы его калибровать. rg на странице 1 покрывается DefaultRGB из ресурсов, которыми страница 1 рисует; страница 2 гонит тот же поток против ресурсов без отображения, поэтому те же байты там некалиброваны. Ожидается для заявления PDF/A-1b: pvaiMissingOutputIntent с pvaiMissingIccProfile, вызванные RGB страницы 2 и серым обеих страниц. Форма не вносит вклада; вызовите её — и её CMYK добавит сверху запрет смешивания PDF/A-1
Пространства цвета Default принадлежат словарю ресурсов, который рисует
Отображения Default ищутся в подсловаре ColorSpace текущего словаря ресурсов (ISO 32000-1 §8.6.5.6), что делает их ровно настолько же локальными, насколько локальны сами ресурсы; у документа нет глобального дефолта. У XObject-изображения нет собственных Resources, поэтому изображение, чей словарь говорит /ColorSpace /DeviceCMYK, калибруется DefaultCMYK той страницы или формы, которая его рисует, и одно и то же изображение, нарисованное из двух мест, может быть калиброванным в одном и некалиброванным в другом. Form XObject, мозаичные паттерны и глифовые процедуры Type 3 несут собственные Resources, поэтому DefaultCMYK страницы внутрь них не доходит, а отображение из их собственного словаря — доходит. PDFium Component ограничивает отображения тем же способом. Вердикт возвращается записью TPdfAValidationResult: обнаруженная Conformance плюс набор Issues. Этого достаточно, чтобы разобрать папку
uses
System.SysUtils, System.Classes, System.TypInfo, FPdfPdfa;
procedure ReportColorFindings(const FileName: string; Log: TStrings);
const
ColorRules: TPdfAValidationIssues = [pvaiMissingOutputIntent,
pvaiMissingIccProfile, pvaiMixedDeviceColorSpaces, pvaiBlendSpaceUndefined];
var
Source: TFileStream;
Res: TPdfAValidationResult;
Found: TPdfAValidationIssues;
Issue: TPdfAValidationIssue;
begin
Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Source); // читает с позиции 0
finally
Source.Free;
end;
Log.Add(ExtractFileName(FileName) + ': ' +
GetEnumName(TypeInfo(TPdfAConformance), Ord(Res.Conformance)));
Found := Res.Issues * ColorRules;
for Issue in Found do
case Issue of
pvaiMissingOutputIntent:
Log.Add(' reachable uncalibrated device color, no PDF/A OutputIntent');
pvaiMissingIccProfile:
Log.Add(' no OutputIntent with a DestOutputProfile ICC stream');
pvaiMixedDeviceColorSpaces:
Log.Add(' RGB/CMYK use disagrees with the intent, or PDF/A-1 paints both');
pvaiBlendSpaceUndefined:
Log.Add(' transparent page without a blending color space');
end;
if Found = [] then
Log.Add(' device color and blending rules satisfied');
end;
pvaiMissingOutputIntent и pvaiMissingIccProfile появляются вместе, когда обход достигает некалиброванного цвета устройства, а PDF/A OutputIntent не существует. pvaiMissingIccProfile в одиночку означает, что намерение есть, но профиля не несёт, — 6.2.2 такое намерением PDF/A не считает. pvaiMixedDeviceColorSpaces покрывает и запрет смешивания PDF/A-1, и несовпадение по компонентам: некалиброванный DeviceRGB под четырёхкомпонентным намерением или DeviceCMYK под трёхкомпонентным. Набор называет правило, а не страницу, поэтому в длинном или склеенном документе сравнивайте, какие страницы объявляют записи Default, а какие нет
Когда прозрачной странице нужно собственное пространство смешивания?
Прозрачной странице собственное цветовое пространство смешивания нужно, только когда у документа нет PDF/A OutputIntent. ISO 19005-2 6.2.10 (6.2.9 в PDF/A-4) делает намерение пространством смешивания по умолчанию в смысле ISO 32000-1 §11.3.4; без намерения каждая страница, содержащая прозрачность, должна нести Group, чей словарь атрибутов (ISO 32000-1 Table 147) включает CS. Тот CS тоже подчиняется 6.2.4, поэтому группа страницы из /DeviceCMYK под sRGB-намерением докладывается как отсутствующая, и PDFium Component поднимает pvaiBlendSpaceUndefined для обоих случаев; CalRGB или групповое пространство ICCBased такой зависимости не имеет. PDF/A-1 до этого вопроса не добирается, потому что ISO 19005-1 6.4 исключает прозрачность целиком
Трудная часть — формулировка содержит прозрачность. Приложение A ISO 19005-2 нормативно и проверяет от страницы наружу; подсчёт ключей /SMask или значений /ca по всему файлу отвечает на другой вопрос. Побайтовый поиск сталкивается и с прочим синтаксисом прозрачности: /S /Trans, запрещённое действие перехода, служит префиксом каждой группе /S /Transparency, а /Screen и запрещённый подтип аннотации, и стандартный режим смешивания, поэтому имя ничего не значит, пока не спарено со своим ключом. Следуя приложению A, PDFium Component считает страницу прозрачной, когда от неё достижимо любое из перечисленного:
- ExtGState, который содержимое выбирает через
gs, со словарём мягкой маски, сcaилиCAниже 1 или с режимом смешивания помимоNormal; объявленный, но ни разу не выбранный, не считается - Form XObject, вызываемый через
Do, чей/Groupимеет/S /Transparencyили чьё собственное содержимое отвечает одному из этих условий; мозаичный паттерн судится как форма - XObject-изображение с потоком
SMaskили значениемSMaskInDataвыше 0 - глифовое описание шрифта Type 3, которым страница показывает текст, судится как форма
- appearance-поток аннотации, включая каждое состояние виджета, и режим смешивания на самом словаре аннотации
Группы ещё и замыкаются на правило цвета устройства, и вот здесь корпус нас поправил. Набор veraPDF для 6.2.4.3 содержит файлы PDF/A-4 без всякого OutputIntent, которые рисуют RGB устройства и должны проходить, потому что /Group каждой страницы называет калиброванный CS. Мы считали, что калибровать могут только изолированные группы, ведь не изолированная группа композитится в свой фон, — для групп страниц это оказалось неверно: Table 147 замечает, что страница, наложенная прямо на носитель вывода, фактически изолирована независимо от I, поэтому её CS чтится. На Form XObject или appearance аннотации флаг уже решает; в наборе есть проходящий и валящийся файлы, различающиеся только /I true. DeviceGray калибровки от группы не берёт, потому что 6.2.4.3 требует DefaultGray или намерения, а группе, чей CS — пространство устройства, нужна калибровка самой. Поэтому PDFium Component позволяет группе страницы калибровать, что бы ни говорил её /I, позволяет форме или группе appearance калибровать только в изоляции, держит серый вне групповой калибровки и всё равно проверяет пространство устройства, которое группа называет
Один OutputIntent вместо группы на каждой странице
Если архив пишет ваш собственный код, начните с PDF/A OutputIntent на уровне документа. Одна запись каталога калибрует DeviceGray везде, покрывает соответствующее пространство устройства на каждой странице и служит пространством смешивания по умолчанию для каждой прозрачной страницы. Группы страниц и записи Default ограничены каждый своим словарём ресурсов, и страница, которую поздний merge добавит без них, — та самая, что валится. PDF 2.0 добавляет OutputIntents в объект страницы (ISO 32000-2 §7.7.3.3 и §14.11.5); PDFium Component принимает такое намерение уровня страницы для PDF/A-4, когда в каталоге его нет, тогда как части с 1 по 3, построенные на PDF 1.4–1.7, смотрят только в каталог. Для RGB-содержимого SaveAsPdfAToStream добавляет намерение в каталог, построенное на встроенном sRGB ICC-профиле писателя PDF/A, а валидация вывода показывает, было ли содержимое действительно только RGB
uses
System.SysUtils, System.Classes, PDFium, FPdfPdfa;
function ArchiveWithSrgbIntent(const InFile, OutFile: string;
out Remaining: TPdfAValidationIssues): Boolean;
var
Pdf: TPdf;
Opts: TPdfASaveOptions;
Output: TMemoryStream;
Check: TPdfAValidationResult;
begin
Result := False;
Remaining := [];
Pdf := TPdf.Create(nil);
Output := TMemoryStream.Create;
try
Pdf.FileName := InFile;
Pdf.Active := True;
if not Pdf.Active then
Exit; // файл не открылся
Opts := TPdfASaveOptions.Default;
Opts.Conformance := pac2b; // PDF/A-1 вовсе запретил бы прозрачность
// IccProfileData остаётся пустым: используется встроенный профиль sRGB IEC61966-2.1
if not Pdf.SaveAsPdfAToStream(Output, Opts) then
Exit;
Check := ValidatePdfACompliance(Output);
Remaining := Check.Issues;
// Ожидается для содержимого только из RGB: pvaiMissingOutputIntent и
// pvaiBlendSpaceUndefined исчезают. pvaiMixedDeviceColorSpaces теперь значит
// достижимый путь рисует DeviceCMYK, который не калибрует никакой DefaultCMYK
if Check.IsCompliant then
begin
Output.SaveToFile(OutFile);
Result := True;
end;
finally
Output.Free;
Pdf.Free;
end;
end;
pvaiMixedDeviceColorSpaces, переживший sRGB-намерение, — это обход, сообщающий DeviceCMYK на достижимом пути, и такому документу нужно отображение DefaultCMYK на рисующих его страницах или выходной CMYK-профиль, прежде чем PDFium Component примет его как PDF/A-2b