Технічна стаття

Вихідні формати PDF/A, PDF/X та PDF/UA у Delphi: посібник з HotPDF

PDF/A, PDF/X і PDF/UA — це три різні стандарти, що розв'язують три різні проблеми: довгострокове архівування, обмін для друку та доступність. Це не три прапорці на одній формі відповідності, і найпоширеніша помилка — трактувати їх саме так. Файл може бути бездоганним PDF/A і водночас марним для друкарні; ідеальний друкарський майстер-файл може бути нечитабельним для програми зчитування з екрана. Гірше того, усі три стандарти — це обмеження на внутрішню структуру файлу, а не на те, як він виглядає. Документ, що бездоганно відкривається в кожному переглядачі, який у вас є, усе одно може провалити перевірку з першої спроби — і зазвичай так і трапляється

HotPDF, нативна VCL-бібліотека PDF від losLab, трактує відповідність як те, що ви декларуєте ще до появи першої сторінки. Ви встановлюєте властивість відповідності, приєднуєте структури, яких вимагає стандарт, а бібліотека відмовляє конфігураціям, що суперечать профілю, у момент збереження. Це краща модель, ніж генерувати файл і сподіватися, що постпроцесор зможе допасувати його заднім числом, бо більшу частину того, чого вимагають ці стандарти, неможливо додати вже після факту

Три стандарти ISO, три різні обіцянки

PDF/A (ISO 19005) — про час. Він обіцяє, що файл однаково відображатиметься й через десятиліття, тож вимагає повної самодостатності: усі шрифти вбудовані, кожен колір отримує незалежне від пристрою значення через OutputIntent, повні метадані XMP і заборону на все, чия поведінка залежить від середовища. Шифрування й JavaScript виключені, бо ніхто не може гарантувати, що дешифратор чи рушій скриптів існуватимуть у 2050 році

PDF/X (ISO 15930) — про колір на папері. Він існує, щоб дизайнер міг передати файл у друкарню, і жодній зі сторін не довелося б нічого узгоджувати додатково: це означає характеризовані умови друку, обов'язковий ключ /Trapped, визначену геометрію обрізки й випуску за обріз, а в різновиді X-1a — жодної живої прозорості, яку RIP мав би вгадувати. PDF/UA (ISO 14289) — про те, хто може прочитати результат. Технологіям доступності потрібне повне дерево тегів, розумний порядок читання, задекларована мова документа й текстові альтернативи для всього, що не є текстом

Оскільки ці три стандарти тягнуть у різні боки, обирайте керівний стандарт для кожного каналу виводу окремо, замість того щоб гнатися за одним файлом, який задовольнить їх усі. Друкарський майстер-файл лише в CMYK — це якраз те, що не варто давати користувачу програми зчитування з екрана, який ніколи не бачить кольору, а блокування динамічної поведінки в архівному профілі конфліктує з будь-якою інтерактивністю. Генеруйте окремо для кожного каналу з тих самих вихідних даних — і весь конфлікт обходиться стороною

HotPDF генерує файли PDF/A, PDF/X і PDF/UA на кожен вихідний канал з одного документа-джерела в Delphi, кожен стандарт ISO тримає окрему структурну обіцянку
PDF/A, PDF/X і PDF/UA тягнуть у різні боки, тож HotPDF генерує по одному файлу на вихідний канал з того самого джерела

PDF/A: OutputIntent — та частина, про яку всі забувають

Якщо файл PDF/A провалює перевірку, OutputIntent — перше, що варто перевірити. Це та структура, яку генератори пропускають найчастіше, саме тому, що від неї нічого видимого не залежить. ISO 19005 вимагає такий: вбудований ICC-профіль, що точно визначає, що насправді означають кольори пристрою в документі. HotPDF робить цей профіль явним вхідним параметром, а не запізнілою думкою:

var
  Pdf: THotPDF;
  ICC: TFileStream;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archival.pdf';
    Pdf.PDFACompliance := 'B';            // рівень B: візуальна відповідність
    Pdf.Lang := 'en-US';
    Pdf.StandardFontEmulation := False;   // вбудовувати справжні шрифти, без емуляції Base-14
    ICC := TFileStream.Create('sRGB.icc', fmOpenRead);
    try
      Pdf.AddPDFAOutputIntent('sRGB IEC61966-2.1', '', ICC, 3, 'DeviceRGB');
    finally
      ICC.Free;
    end;
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Archival invoice body');
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Кілька деталей вирішують тут успіх чи провал. StandardFontEmulation має бути вимкнено: емульовані шрифти Base-14 не вбудовуються, а вбудовування не підлягає обговоренню за ISO 19005. Шифрування має лишатися вимкненим, тож ніколи не поєднуйте PDFACompliance з ActivateProtection; зашифрований архівний файл — це суперечність, яку валідатор помічає миттєво. Кількість компонентів у AddPDFAOutputIntent повинна відповідати профілю: 3 для RGB-профілю на кшталт sRGB IEC61966-2.1 і 4 для CMYK. HotPDF відстежує використання DeviceRGB і DeviceCMYK щодо задекларованого наміру під час запису, тож випадкова заливка CMYK у документі з наміром RGB перетворюється на повідомлену проблему, а не мовчазну

Варто сказати одну річ про ICC-профіль: ставтеся до нього як до версійованого артефакту розгортання, а не файлу, який хтось колись кинув на сервер збірки. Його байти вбудовуються в кожен документ, що ви генеруєте, тож усічений чи пошкоджений профіль тихо отруює цілу партію, і ви дізнаєтеся про це лише на етапі перевірки. Постачайте його разом з інсталятором, записуйте його контрольну суму в журнал запуску й завантажуйте через шаблон TFileStream, показаний вище, щоб відсутній файл гучно провалювався під час генерації, а не мовчки на воротах архіву

PDF/X для друку: Trapped, CMYK і профіль преси

Друкарські майстер-файли перевертають історію з кольором. Преса хоче характеризований CMYK, а стандарт змушує вас заявити, чи застосовано треппінг, навіть коли чесна відповідь — «поняття не маю». Ключ /Trapped обов'язковий у будь-якому разі:

Pdf.PDFXCompliance := 'X-1a';
Pdf.Trapped := 'Unknown';        // обов'язковий ключ за ISO 15930
ICC := TFileStream.Create('FOGRA39.icc', fmOpenRead);
try
  Pdf.AddPDFXOutputIntent('FOGRA39 (ISO 12647-2:2004)', '', ICC, 4, 'DeviceCMYK');
finally
  ICC.Free;
end;
Pdf.BeginDoc;
// малювати безпечними для CMYK кольорами, без прозорості, без шифрування
Pdf.EndDoc;

Кількість компонентів тепер дорівнює 4 для друкарського профілю CMYK. X-1a також забороняє живу прозорість, тож перевірте будь-який код малювання, що накладає напівпрозорі елементи; те, що переглядач компонує на екрані, — це якраз те, що RIP відмовиться інтерпретувати. Коли ваша друкарня надсилає іншу характеризацію, замініть байти профілю та рядок ідентифікатора, але не чіпайте решту структури

PDF/UA: структура генерується, а не допасовується заднім числом

Доступність — це стандарт, який команди найчастіше намагаються прикрутити наприкінці, і він карає за такий підхід суворіше за інші два. Дерево тегів має відображати порядок, у якому вміст логічно створювався, — а це інформація, якої у вас просто вже немає, щойно файл записано. Встановлення PDFUACompliance вмикає тегований вивід, а API структури прив'язує кожен виклик малювання до його семантичної ролі на льоту:

HotPDF будує дерево тегів PDF/UA наживо під час виконання кожного виклику малювання Delphi, а текст поза BeginTaggedContent і EndTaggedContent залишається невидимим для читачів з екрана
Дерево тегів пишеться, поки ви малюєте, і текст поза парою тегів рендериться чудово, але залишається невидимим для екранних читачів
Pdf.PDFUACompliance := True;     // автоматично вмикає тегований PDF
Pdf.Lang := 'en-US';             // встановіть явно; порожнє значення відкочується до 'en'
Pdf.BeginDoc;

Root := Pdf.AddStructureElement(sstDocument, nil);
H1 := Pdf.EmitTaggedHeading(1, Root, 50, 700, 'Quarterly Report');
Para := Pdf.BeginTaggedContent('P', Root);
Pdf.CurrentPage.TextOut(50, 650, 0, 'Revenue grew in all regions.');
Pdf.EndTaggedContent;

Pdf.EndDoc;

Збій, за яким варто стежити, — це текст, намальований поза будь-якою парою BeginTaggedContent/EndTaggedContent. Він відображається бездоганно й лишається невидимим для програми зчитування з екрана, тож жоден зрячий тестувальник ніколи цього не помітить; помилка потрапляє в реліз і випливає лише тоді, коли на цю прогалину натрапляє справжній користувач технологій доступності. Якщо у ваших шаблонах є власні назви структурних ролей, зіставте їх зі стандартним набором через AddStructRoleMap('MyHead', 'H1'), щоб сумісні читачі розуміли, що вони означають. ISO 14289 також вимагає задекларованої мови. HotPDF відкочується до 'en', коли Lang порожній, але це лише страховка, а не привід залишати справжню мову документа невстановленою

Перевірка: довіряйте валідатору, а не переглядачу

Переглядач, що відкриває ваш файл, нічого не доводить щодо відповідності, тож перевірка має бути частиною шляху релізу з інструментами, які перевіряють структуру, а не рендеринг. Для PDF/A і PDF/UA veraPDF — це еталонний відкритий валідатор; він повідомляє про збої за пунктом ISO, що прямо зіставляється з конфігурацією вище. Для PDF/X практичною перевіркою досі лишаються профілі Preflight в Adobe Acrobat, бо відповідність друкарським вимогам стосується наміру щодо кольору не менше, ніж синтаксису

Генератор виконує свою частину цієї роботи. У момент збереження HotPDF узгоджує прапорці функцій із налаштованою версією PDF, мовчки понижуючи те, що версія не може виразити, наприклад AES-256, що опускається до AES-128 нижче PDF 1.7. Бар'єри відповідності в EndDoc ідуть далі й прямо піднімають виняток на жорстких суперечностях, як-от запит PDFACompliance разом із шифруванням. Жодне з цього не замінює зовнішній валідатор. Вони лише не дають неможливим конфігураціям взагалі до нього дійти

Шлюзи відповідності HotPDF у EndDoc виловлюють неможливі конфігурації до того, як veraPDF звірить PDF/A і PDF/UA, а Acrobat Preflight звірить PDF/X на шляху випуску в Delphi
HotPDF відхиляє суперечливі конфігурації на EndDoc, а незалежні валідатори вирішують справжню відповідність

Одна звичка окупається знову і знову: версіонуйте всю конфігурацію відповідності як єдине ціле. Реліз HotPDF, ревізію шаблону, контрольну суму ICC-профілю, збірку валідатора, що дала добро. Відповідність розповзається в той момент, коли будь-який з цих елементів змінюється незалежно від інших, а найпотворніші аудити — саме ті, де ніхто не може відновити, яка комбінація породила архівний файл п'ятирічної давнини. Один запис конфігурації на партію вирішує це раз і назавжди

Нарешті, запускайте валідатор на реальному продакшн-виводі, ніколи на охайному вручну зібраному зразку. Збої, що справді кусаються, походять із даних, яких ніхто не передбачив: логотип клієнта, що надходить у CMYK, тоді як намір заявляє RGB, правка шаблону, що непомітно протягує невбудований шрифт, новий шлях коду, що малює текст поза деревом тегів. Тримайте по одному завідомо поганому файлу з кожного минулого інциденту як вхідні дані для регресії, і бар'єр відповідності лишатиметься чесним із часом. Для рендерингової сторони цих конвеєрів див. нашу статтю про вивід звітів, шрифти та зображення в HotPDF; про підключення валідаторів у збірку є суміжна стаття про автоматизацію перевірок preflight PDF

Властивості відповідності, output intent і API тегування, використані в цих прикладах, постачаються з HotPDF Delphi Component для Delphi та C++Builder; сторінка продукту містить посилання на повний довідник для кожного показаного тут виклику