Компонент PDFium для Delphi валідує готові до друку документи PDF/X за допомогою TPdf.ValidatePdfX, який реалізує перевірку ISO 15930 на двох рівнях: вісім перевірок вмісту на рівні байтів (заборонене стиснення LZW, JavaScript, поля форм, посилання OPI, відсутній TrimBox, невстановлений ключ Trapped тощо) плюс прохід по моделі об'єктів PDFium, який використовує FPDFFont_GetIsEmbedded для перевірки вбудовування шрифтів для кожного текстового об'єкта на кожній сторінці. Результатом є запис TPdfXValidationResult, який вказує на виявлений рівень відповідності та перелічує кожне порушення у вигляді типізованого переліку, тому ваш додаток на Delphi може точно повідомити клієнту, чому файл буде відхилено в друкарні, ще до того, як буде виготовлено друкарські форми
Якщо ви коли-небудь відправляли замовлення до комерційної друкарні та отримували його назад із короткою відмовою («немає TrimBox», «шрифти не вбудовані», «Trapped не встановлено»), ви знаєте ціну пізнього виявлення проблем. PDF/X — це додрукарський аналог PDF/A: якщо архівний стандарт PDF/A гарантує, що документ відображатиметься ідентично через десятиліття, то PDF/X гарантує, що документ буде ідентично розділений на кольори, виведений на плівки та обрізаний на чужому обладнанні завтра вранці. Обидва стандарти мають спільні механізми (ідентифікація XMP, OutputIntents, вбудовані профілі ICC), але відповідають на різні запитання, тому компонент постачає окремі валідатори для кожного — сторона PDF/A описана в валідації PDF/A з компонентом PDFium
Що насправді вимагає ISO 15930 від готового до друку PDF?
Стандарт ISO 15930 існує для того, щоб зробити можливим сліпий обмін (blind exchange): дизайнер передає файл принтеру, з яким він ніколи не спілкувався, і принтер може отримати правильний результат без телефонних дзвінків, листів про відсутні шрифти та пов'язаних зображень, які залишилися на ноутбуці дизайнера. Кожне правило в стандарті служить цій меті. Шрифти повинні бути вбудовані, оскільки не можна припускати, що приймаюче обладнання володіє ними. Зовнішні посилання заборонені, тому що файл повинен бути самодостатнім. Інтерактивні функції заборонені, оскільки фарба на папері не має обробника onclick
Компонент PDFium розпізнає три родини відповідності та повідомляє про них через перелік TPdfXConformance у результаті валідації: pxc1a для PDF/X-1a:2001 (ISO 15930-1, суворий CMYK-плюс-spot базис на PDF 1.3/1.4), pxc3 для PDF/X-3:2002 (ISO 15930-3, який допускає RGB, Lab та кольори під управлінням ICC) та pxc4 для PDF/X-4:2010 (ISO 15930-7, який нарешті дозволяє живу прозорість та шари на базі PDF 1.6). Файл, який взагалі не містить ідентифікації PDF/X, повертається як pxcNone, що саме по собі є корисною відповіддю: документ ніколи не заявляв, що він готовий до друку, і все інше, про що повідомляє валідатор, пояснює, що потрібно зробити, щоб цього досягти
Заборони стають зрозумілими, як тільки ви починаєте думати як постачальник обладнання для растрової обробки (RIP). Фільтр /LZWDecode заборонений у всіх варіантах PDF/X, щоб сумісний споживач ніколи не залежав від фільтра з історією сумісності та ліцензування; Flate виконує ту саму роботу без цих проблем. JavaScript, поля AcroForm та словники додаткових дій /AA заборонені, оскільки файл для друку повинен бути фіксованим описом позначок на папері — все, що може змінити зовнішній вигляд під час відкриття, порушує гарантію того, що на папері вийде саме те, що було погоджено. Заглушки OPI (Open Prepress Interface) заборонені, оскільки за дизайном вони є посиланнями на зображення високої роздільної здатності, що зберігаються десь в іншому місці, а «десь в іншому місці» — це саме те, що забороняє сліпий обмін
Чому друкарні відхиляють PDF-файли без TrimBox?
TrimBox — це готова сторінка, тобто прямокутник, який залишається після роботи гільйотинного ножа. MediaBox, який є у кожної сторінки PDF — це просто аркуш: він включає випуск за обріз (bleed), мітки різу, приціли суміщення та шкали контролю кольору. Програми для спуску смуг позиціонують сторінки на друкованому аркуші за їхніми TrimBox; без нього оператор повинен вгадувати, де насправді закінчується ваша візитна картка, і помилка приведе до зрізання випуску за обріз або залишить білу смугу на одному з країв. Ось чому стандарт ISO 15930 вимагає наявності TrimBox (або ArtBox) на кожній сторінці, і чому ValidatePdfX видає pvxiMissingTrimBox, коли на жодній сторінці документа не знайдено ключа /TrimBox
Ключ /Trapped відповідає на інше виробниче питання. Трепінг (trapping) — це додрукарська техніка незначного перекриття суміжних кольорів, щоб мінімальне неспівпадіння друкарських форм не створювало білих щілин між ними. Друкар повинен знати, чи була вже виконана ця робота: трепінг уже обробленого файлу подвоїть перекриття, а пропуск трепінгу на необробленому файлі загрожує появою видимих щілин. Тому PDF/X вимагає, щоб словник Info явно вказував /Trapped /True або /Trapped /False — відсутність ключа або значення /Unknown змушує людину перевіряти файл вручну, що є саме тим діалогом, який сліпий обмін мав усунути. Компонент позначає це як pvxiTrappedNotSet
Запуск двохетапної валідації за допомогою TPdf.ValidatePdfX
Метод TPdf.ValidatePdfX не приймає аргументів і повертає запис TPdfXValidationResult із трьома елементами: Conformance (виявлений тип PDF/X), Issues (паскалівський набір значень TPdfXValidationIssue) та допоміжний елемент IsCompliant. Внутрішньо він серіалізує завантажений документ у потік пам'яті, запускає інспектор рівня байтів, а потім проходить по моделі об'єктів PDFium для перевірки вбудовування кожного шрифту. Мінімальний додрукарський фільтр виглядає так:
uses PDFium, FPdfPdfx;
procedure CheckPrintReadiness(const FileName: string);
var
Pdf: TPdf;
Res: TPdfXValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Res := Pdf.ValidatePdfX;
Writeln('Detected conformance: ',
Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...
if Res.IsCompliant then
Writeln('PDF/X checks passed')
else
begin
if pvxiMissingTrimBox in Res.Issues then
Writeln('REJECT: no /TrimBox on the pages');
if pvxiTrappedNotSet in Res.Issues then
Writeln('REJECT: /Trapped missing or /Unknown');
if pvxiPdfiumFontNotEmbedded in Res.Issues then
Writeln('REJECT: a page uses a non-embedded font');
if pvxiLzwForbidden in Res.Issues then
Writeln('REJECT: LZWDecode filter present');
end;
finally
Pdf.Free;
end;
end;
Оскільки Issues — це звичайний паскалівський набір, ви можете розділити його так, як цього вимагає ваш робочий процес — вважати структурні проблеми критичними відхиленнями, розглядати pvxiMissingTitle (рекомендація в стандарті, а не жорстка вимога) як попередження, і реєструвати решту. Цей самий тип запису також живить генератор звітів компонента, тому, якщо ви віддаєте перевагу виведенню зручного для читання документа замість розгалуження за переліками, шаблон, описаний у створенні консольної програми для пакетних звітів додрукарської перевірки, застосовується до PDF/X без змін
Що виявляє рівень байтів — і що він пропускає
Рівень байтів — це сканування токенів по структурних байтах документа з очищеними тілами потоків, тому JPEG, який випадково містить послідовність байтів /JavaScript, не може викликати хибне спрацьовування. На додаток до перевірок маркерів (XMP pdfxid:GTS_PDFXVersion, OutputIntent з вбудованим ICC-профілем, трейлер /ID, заборона шифрування), прохід по вмісту додає вісім перевірок, кожна з яких має власне значення переліку:
pvxiLzwForbidden— фільтр/LZWDecodeприсутній у будь-якому місці файлу (заборонений у всіх варіантах PDF/X)pvxiJavaScriptForbidden— дія або дерево імен/JavaScriptприсутніpvxiFormFieldsForbidden— словник/AcroFormабо запис/XFAіснуютьpvxiAdditionalActions— словник додаткових дій/AAприсутнійpvxiEmbeddedFilesForbidden—/EmbeddedFilesабо анотація/FileAttachmentприсутніpvxiOpiForbidden— запис/OPIабо/Alternatesпосилається на змінний вміст зображенняpvxiMissingTrimBox—/TrimBoxне знайдено на жодній сторінціpvxiTrappedNotSet—/Trappedвідсутній або встановлений у значення/Unknown
Сканування байтів виконується швидко і не потребує рушія рендерингу, але воно має приховану сліпу зону щодо шрифтів: на цьому рівні інспектор може застосувати лише грубу евристику — він позначає документ, якщо не знаходить жодної програми вбудованого шрифту взагалі. Файл із дев'ятьма вбудованими шрифтами та одним системним шрифтом виглядатиме нормально для сканування байтів. Саме тому існує другий рівень
Перевірка вбудовування для кожного шрифту через модель об'єктів PDFium
Рівень моделі об'єктів компонента PDFium дає точну відповідь на питання щодо шрифтів. Після проходу на рівні байтів метод TPdf.ValidatePdfX перебирає кожну сторінку, запитує список об'єктів за допомогою FPDFPage_CountObjects і для кожного текстового об'єкта визначає дескриптор шрифту через FPDFTextObj_GetFont та опитує FPDFFont_GetIsEmbedded. Один невбудований шрифт у будь-якому місці документа додає pvxiPdfiumFontNotEmbedded до набору проблем. Обхід переривається на двох рівнях — він припиняє сканування об'єктів на сторінці та припиняє завантаження наступних сторінок, як тільки проблема підтверджується — тому для помилкового каталогу на 300 сторінок вердикт часто виноситься вже на першій сторінці
Дві крайові примітки, які варто знати. По-перше, цей рівень вимагає завантаженої бібліотеки PDFium і потребує збірок, які експортують FPDFFont_GetIsEmbedded; за відсутності експорту перевірка пропускається, а не вважається невдалою, тому стара версія DLL ніколи не призведе до помилкових відхилень. По-друге, перевірка відповідає на запитання «вбудований чи ні» і більше нічого — вона не відрізняє повне вбудовування від створення підмножини (subsetting) і не перевіряє покриття гліфів. Якщо файл не проходить перевірку і вам потрібно дізнатися, який саме шрифт на якій саме сторінці винен, методи переліку, описані в аналізі властивостей шрифтів PDF з PDFium у Delphi, продовжують роботу з того місця, де зупиняється логічне значення валідатора
Валідація потоків без завантаження документа або DLL
Інспектор рівня байтів також доступний як окрема функція ValidatePdfXCompliance(Source: TStream) у модулі FPdfPdfx, і це чистий код Object Pascal без залежностей від PDFium DLL. Це дозволяє розгортати його в місцях, де рушій рендерингу небажаний: легкий фільтр завантаження на веб-сервері, завдання CI, яке перевіряє згенеровані макети, або служба Lazarus на платформі, куди ви б воліли не постачати бінарні файли. Передайте їй будь-який потік із підтримкою пошуку:
uses Classes, FPdfPdfx;
function QuickPdfXGate(const FileName: string): Boolean;
var
Fs: TFileStream;
Res: TPdfXValidationResult;
begin
Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfXCompliance(Fs);
Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
finally
Fs.Free;
end;
end;
Компроміс є очевидним: автономний шлях виконує перевірки маркерів та всі вісім перевірок вмісту, але не перевіряє кожен шрифт на рівні PDFium, тому його вердикт щодо шрифтів спирається на грубу евристику. Розумна архітектура використовує ValidatePdfXCompliance як швидкий перший фільтр і резервує повний метод TPdf.ValidatePdfX для файлів, які його пройшли
Де закінчується цей валідатор і починається повна додрукарська перевірка
Чесність важлива в інструментах додрукарської перевірки (preflight), тому ось межа. Метод ValidatePdfX перевіряє ідентифікаційні маркери, структурні заборони, ключі геометрії сторінки, оголошення Trapped та вбудовування шрифтів аж до окремих текстових об'єктів. Він не вимірює загальне покриття фарбою (TAC), не перевіряє відповідність кожного колірного простору заявленому варіанту (наприклад, правило CMYK-only для X-1a), не звіряє роздільну здатність зображень із лініатурою растру і не оцінює накладання кольорів (overprint) та зведення прозорості — для цього потрібен повноцінний додрукарський рушій із підтримкою управління кольором, і власна документація модуля рекомендує використовувати його для фінальної сертифікації. Що дає цей двохетапний метод, так це 80% структурних проблем, які можна виявити за мілісекунди безпосередньо у вашому коді Delphi замість того, щоб отримати лист відмови з друкарні наступного ранку
Обидва рівні валідації, API додавання маркерів PDF/X для створення сумісного виводу, а також валідатори PDF/A, PDF/UA, PDF/E та PDF/VT, що мають таку ж архітектуру, постачаються в компоненті PDFium Component для Delphi та C++Builder — один компонент від рендерингу до додрукарського контролю