Когда PDFXCompliance выставлен в 'X-1a', HotPDF проверяет цвет контента в самом конце работы: EndDoc вызывает THotPDF.EnsurePDFX1aContentColorSpaces, которая отвергает любой контент DeviceRGB, CalRGB или ICCBased RGB на любой странице и поднимает исключение, называющее провинившийся ресурс. Файл никогда не будет записан как PDF/X-1a, который типография завернёт
Последнее — и есть суть. PDF/X-1a с одной затесавшейся RGB-плашкой выглядит на экране безупречно, проходит беглый просмотр и валится на префлайте в типографии за два дня до машинного слота. Поймать это в своей Delphi-сборке, с сообщением, называющим страницу и изображение, куда дешевле, чем узнать от раздосадованного препресс-оператора
Почему PDF/X-1a отвергает RGB-контент?
PDF/X-1a отвергает RGB, потому что формат обещает: каждый цвет уже финален для одного конкретного CMYK-условия печати. ISO 15930-1:2001 §6.2.3 ограничивает цвет контента DeviceCMYK, DeviceGray и плашечными красками — то есть пространствами Separation и DeviceN, чьи альтернативы CMYK или Gray. Встроенный в файл OutputIntent GTS_PDFX называет это условие печати, скажем, FOGRA39 coated offset, и принимающий конвейер вправе слать числа прямиком на пластины без всякого преобразования цвета
RGB рвёт этот договор. Значение RGB описывает цвет относительно дисплея или рабочего пространства, а не рецепт краски, поэтому кто-то ниже по цепочке должен решать, как его сепарировать, — а X-1a существует ровно затем, чтобы отнять это решение у приёмщика. ISO 32000-1 §8.6 группирует цветовые пространства PDF в семейства device, CIE-based и special, и RGB-представители встречаются более чем в одном семействе: DeviceRGB — device-пространство, CalRGB и ICCBased с тремя компонентами — CIE-based, а Indexed или Separation могут прятать RGB-базу или альтернативу в глубине. Проверяющий, ищущий только оператор rg, пропустит большинство из них. Позднейшая редакция PDF/X-1a:2003 (ISO 15930-4) держит то же цветовое правило; учтите, что значение 'X-1a' в HotPDF пишет в XMP-пакет идентификацию PDF/X-1a:2001
Если ваш контент честно RGB и должен им остаться, X-1a — неверная цель. HotPDF ограничивает эту проверку только 'X-1a', поэтому 'X-3' и 'X-4', допускающие ICC-based и калиброванный цвет, не затронуты
Что EnsurePDFX1aContentColorSpaces проверяет фактически
EnsurePDFX1aContentColorSpaces соединяет флаг времени рисования с рекурсивным обходом ресурсов каждой страницы и идёт последним в шлюзе PDF/X. EndDoc сперва обеспечивает общие требования PDF/X: Title задан, Trapped равен 'True', 'False' или 'Unknown', существует OutputIntent GTS_PDFX (EnsurePDFXOutputIntent), и каждая страница имеет TrimBox или ArtBox (EnsurePDFXPageBoxes). И только после их прохождения стартует цветовая проверка
Первый путь — общедокументный флаг. RGB-сеттеры HotPDF, встраивание битмапов и цветовые записи аннотаций фиксируют использование DeviceRGB в момент рисования, покрывая inline-операторы rg и RG, которые никогда не появляются в словаре ресурсов. Второй путь обходит /Resources каждой страницы и осматривает:
- Именованные пространства в
/ColorSpace, включая сокращение inline-изображенийRGBкак синоним DeviceRGB /ColorSpaceкаждого Image XObject- Form XObjects и tiling Patterns, рекурсивно заходя в их собственные вложенные
/Resources - Shading Patterns и записи словаря
/Shadingстраницы, через/ColorSpaceшейдинга - Базовые пространства Indexed, альтернативы Separation и DeviceN и базовые пространства Pattern — на глубину вложения до 8
Отвергаются три вещи: DeviceRGB, CalRGB и пространства ICCBased, чей поток профиля декларирует N = 3. Словари ресурсов, разделённые между Form XObjects и паттернами, посещаются однократно, поэтому взаимоссылающийся набор форм не загонит обход в петлю. Метод публичен на THotPDF, но в нормальном использовании вы его сами не вызываете — за вас это делает EndDoc
Страница X-1a на Delphi, проходящая проверку
Соответствующий HotPDF-документ X-1a требует CMYK OutputIntent, TrimBox на каждой странице и цвета, выставленного только через API CMYK, Gray и плашечных красок. Скелет ниже использует профиль FOGRA39; AddPDFXOutputIntent принимает идентификатор выходного условия, строку информации, поток ICC-профиля, число его компонент и альтернативное пространство
var
Pdf: THotPDF;
Profile: TFileStream;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'brochure-x1a.pdf';
Pdf.Title := 'Spring brochure, press edition';
Pdf.Trapped := 'False';
Pdf.PDFXCompliance := 'X-1a';
Pdf.BeginDoc;
Profile := TFileStream.Create('CoatedFOGRA39.icc', fmOpenRead or fmShareDenyWrite);
try
Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)',
'Coated FOGRA39 offset', Profile, 4, 'DeviceCMYK');
finally
Profile.Free;
end;
Pdf.CurrentPage.SetTrimBox(0, 0, 612, 792); // обязателен на каждой странице
Pdf.CurrentPage.SetCMYKFillColor(0, 0.5, 0.8, 0.1);
Pdf.CurrentPage.Rectangle(72, 600, 468, 120);
Pdf.CurrentPage.Fill;
Pdf.CurrentPage.SetGrayFillColor(0.2); // текст DeviceGray
Pdf.CurrentPage.SetFont('Arial', [], 14);
Pdf.CurrentPage.TextOut(72, 560, 0, 'Spring catalog, press edition');
Pdf.EndDoc; // цветовая проверка идёт здесь
finally
Pdf.Free;
end;
end;
Каждый вызов на базе TColor в этом контексте — ловушка. SetRGBFillColor, SetRGBStrokeColor и SetRGBColor принимают GDI-цвет и эмитят DeviceRGB, поэтому хелпер, написанный годы назад для экранных отчётов, запятнает печатную работу в момент повторного использования. Берите SetCMYKFillColor, SetCMYKStrokeColor или SetCMYKColor с компонентами в диапазоне 0–1 либо Gray-аналоги
Плашечные цвета и CMYK-градиенты
Плашечные краски в X-1a разрешены, пока их альтернатива CMYK или Gray. RegisterSeparation строит пространство Separation и возвращает имя ресурса, а RegisterAxialGradient выбирает color space шейдинга по числу переданных компонент — четыре компоненты означают DeviceCMYK
var
SpotCS, Ramp: AnsiString;
begin
// Плашечная краска с приблизительной CMYK-сборкой в качестве альтернативы
SpotCS := Pdf.RegisterSeparation('Corporate Red', 'DeviceCMYK',
[0, 1, 0.8, 0.05]);
Pdf.CurrentPage.SetFillColorSpace(SpotCS);
Pdf.CurrentPage.SetFillColor([1.0]); // тон 100%
Pdf.CurrentPage.Rectangle(72, 420, 200, 80);
Pdf.CurrentPage.Fill;
// 4 компоненты на точку: шейдинг DeviceCMYK, а не DeviceRGB
Ramp := Pdf.RegisterAxialGradient(72, 300, 540, 300,
[0, 0, 0, 0], [1, 0.6, 0, 0.2]);
Pdf.CurrentPage.SetFillPattern(Ramp);
Pdf.CurrentPage.Rectangle(72, 260, 468, 80);
Pdf.CurrentPage.Fill;
end;
Передайте тому же вызову градиента три компоненты — получите шейдинг DeviceRGB, который обход ресурсов отвергнет от имени шейдинг-паттерна. То же с Separation, зарегистрированным с 'DeviceRGB' в альтернативе: имя плашки выглядит безобидно, но обходит альтернативу и находит под ней RGB. Как такие плашечные краски композитятся, когда вьюер или RIP откатывается к альтернативе, раскрыто в статье о рендеринге Separation и DeviceN плашечных цветов на Delphi
Растровые изображения требуют той же дисциплины. HotPDF прибивает Flate-сжатые битмапы к DeviceRGB, тогда как JPEG, встроенный с icJpeg, держит собственное число компонент: четырёхкомпонентный CMYK JPEG или полутоновой JPEG в порядке, обычный RGB JPEG — нет. Конвертируйте графику в печатный профиль в своём imaging-конвейере, прежде чем она дойдёт до PDF
Почему /DefaultRGB не спасает файл PDF/X-1a?
/DefaultRGB не помогает X-1a, потому что правило о том, что получает машина, а не о том, охарактеризован ли RGB. Для PDF/A HotPDF позволяет RegisterDefaultColorSpace('DefaultRGB', ...) поглотить использование DeviceRGB: как только Default-пространство отображает DeviceRGB на ICC-профиль, контент device-независим, и проверка консистентности PDF/A его принимает. Разработчики, прошедшие тот конвейер, склонны пробовать здесь ту же починку
EnsurePDFX1aContentColorSpaces намеренно игнорирует то отображение. Охарактеризованное значение RGB — всё ещё RGB, а OutputIntent X-1a — CMYK-условие печати, поэтому конвертировать его всё равно пришлось бы кому-то. Если запоминать одну разницу между двумя архивно-печатными профилями — запоминайте эту: PDF/A спрашивает, однозначен ли цвет, PDF/X-1a — является ли он уже краской
Порядок рисования и setup страницы, который не считается
Поскольку проверка идёт на EndDoc по уже написанному контенту, порядок, в котором вы настраивали вещи, безразличен. RGB, нарисованный до назначения PDFXCompliance или до регистрации OutputIntent, всё равно будет пойман. Это важно для кода, строящего страницы в общих рутинах и решающего выходной профиль поздно — иногда по пользовательской настройке, выбранной, когда половина документа уже существует
Pdf.BeginDoc;
// ... OutputIntent и TrimBox как раньше ...
Pdf.CurrentPage.SetRGBFillColor(clRed); // легаси-хелпер, эмитит DeviceRGB
Pdf.CurrentPage.Rectangle(72, 72, 200, 100);
Pdf.CurrentPage.Fill;
Pdf.PDFXCompliance := 'X-1a'; // решено после рисования
Pdf.EndDoc; // всё равно поднимет: DeviceRGB записан
У флага времени рисования есть одна ловушка, которую стоит знать, если строите похожую проверку сами. Каждая новая страница инициализируется чёрным graphics state через SetRGBColor(0), эмитящий пару 0 0 0 rg и 0 0 0 RG, и если бы этот boilerplate настройки страницы позволялся выставлять флаг DeviceRGB, каждый документ выглядел бы запятнанным RGB — и для этой проверки, и для проверки консистентности цвета PDF/A. HotPDF сохраняет и восстанавливает флаг вокруг той инициализации, поэтому считается только цвет, который вы реально нарисовали
Как найти RGB-контент, когда EndDoc поднимает исключение?
Читайте локацию в начале сообщения исключения — она говорит, какой из двух путей нашёл проблему. Сообщение имеет форму PDFXCompliance 'X-1a': <where> uses <what>, затем ссылку на ISO 15930-1 и совет конвертировать в CMYK или Gray либо переключиться на X-3 или X-4. Валидатор поднимает обычный Exception на первом нарушении, поэтому матчите по тексту, а не по классу исключения
document content uses DeviceRGB (rg/RG operators, RGB images or annotation colours recorded during drawing)— сработал флаг времени рисования. Номера страницы нет, потому что флаг общедокументный и проверяется до обхода. Ищите в коде рисования вызовыSetRGB*, Flate-сжатые битмапы, RGB JPEG и аннотационные цветаTColorpage 3 image XObject /Im2 uses DeviceRGB contentилиpage 1 shading Pattern /P1 uses DeviceRGB content— от обхода ресурсов, указывает на точный ключ ресурсаuses an ICCBased RGB colour space (N=3)после локацииColorSpace /...— типичный итог регистрации sRGB-профиля черезRegisterICCProfileс выбором черезSetFillColorSpace, путь, которого флаг времени рисования не видит- Вложенные локации цепляются друг за друга, например
page 2 Form XObject /Fm1 ColorSpace /CS1, так что можно пройти путь от страницы вниз к ресурсу, несущему RGB-пространство
try
Pdf.EndDoc;
except
on E: Exception do
begin
if Pos('X-1a', E.Message) > 0 then
LogPrepressFailure(Pdf.FileName, E.Message); // ваш собственный логгер
raise;
end;
end;
Поскольку сообщается только первое нарушение, починка работы с несколькими RGB-источниками итеративна: чинить, пересобирать, читать следующее сообщение. В батч-работах стоит логировать полное сообщение по файлу и держать короткий список известных виновников — скажем, битмап-логотип или хелпер диаграмм, всё ещё принимающий TColor
Куда вписывается RGB-шлюз в вашем префлайте
EnsurePDFX1aContentColorSpaces — прицельный RGB-шлюз внутри вашей сборки, и он дополняет полный префлайт PDF/X-1a, а не заменяет его. Он покрывает RGB-семейства — через обход ресурсов от /Resources каждой страницы и через флаг времени рисования, который держат цветовые сеттеры HotPDF для inline-операторов rg и RG; вложенные пространства ведутся до глубины 8 как страховка от кривых циклов, и на каждый EndDoc сообщается первое нарушение. Прочие семейства цветов вроде Lab или CalGray — забота полного префлайта, поэтому прогоняйте готовый файл через внешний валидатор как часть сборки, чтобы получить полный вердикт соответствия. Более широкая картина вывода соответствия — в гиде по PDF/A, PDF/X и PDF/UA с HotPDF, а подключение валидатора к unattended-конвейеру — в автоматизации префлайт-проверок PDF на Delphi
HotPDF — нативный VCL PDF-компонент для Delphi и C++Builder с выводом PDF/X, PDF/A и PDF/UA, API CMYK- и плашечных цветов и поддержкой ICC OutputIntent. Детали, лицензирование и пробная загрузка — на странице продукта HotPDF Delphi PDF component