PDF/E-1 — архівний профіль для інженерних документів, і PDFlibPas реалізує його як режим авторства, який ви вмикаєте через SetPDFEMode, плюс обмежений preflight, що читає потоки вмісту оператор за оператором. Це не PDF/A з іншою наклейкою: у профілю власний простір імен ідентифікації, власна вимога метаданих життєвого циклу і одне правило, яке робить валідацію вмісту суворішою, ніж у будь-якому архівному профілі, який ви зустрічали
Інженерні результати — ось чому існує цей профіль. Набір креслень, який має бути читабельним і доведено незміненим через двадцять років, з історією ревізій, що переживає, і з кольором, який означає те саме на плотері в іншій будівлі. Ці вимоги дають специфікацію, чиї вимоги сидять переважно за межами вмісту сторінок — у метаданих і керуванні кольором, а це рівно те місце, де генеричний PDF-райтер їх псує
Власна ідентифікація, а не варіація на тему PDF/A
Перше, що треба зробити правильно: ідентифікацію PDF/E-1 не можна продукувати, адаптувавши патерн PDF/A чи PDF/X. Профіль використовує окремий простір імен XMP, http://www.aim.org/pdfe/ns/id/, і значення версії має з'явитися в двох місцях: як запис інформації документа і як властивість XMP із кваліфікацією простору імен. Випуск лише властивості XMP або лише запису інформації дає файл, який несе намір і провалює валідацію
Output intent має таку ж конкретну форму. PDF/E-1 вимагає вбудованого ICC-профілю з ідентифікатором підтипу ISO_PDFE1, і профіль мусить мати кількість компонентів, що відповідає сімейству кольорів пристрою, який документ справді використовує. Саме на останньому пункті реалізації тихо помиляються, бо це означає: intent не можна обрати заздалегідь і потім забути
Чому колір пристрою потребує прочісування всього документа?
Бо кольорові простори ховаються в словниках ресурсів, яких скан на рівні сторінки ніколи не досягає. PDF/E-1 розглядає DeviceRGB і DeviceCMYK як взаємовиключні сімейства для документа, тож валідація профілю означає знання кожного кольорового простору пристрою, який будь-що в файлі використовує. Form XObject має власні ресурси. Так само pattern, і так само зображення. Tiling pattern всередині form XObject всередині сторінки — це три рівні вглиб, і валідатор, який перевіряє лише ресурси верхнього рівня сторінки, пропустить документ, що використовує обидва сімейства
Тому прочісування реєструє кольорові простори, обходячи сторінки, форми, зображення та патерни як один прохід, і лише потім вирішує, чи документ когерентний і чи відповідає output intent. Тим самим міркуванням слідує архітектура preflight загалом: частковий обхід дає хибні проходження, а хибне проходження перевірки відповідності гірше за відсутність перевірки, бо воно записується як доказ
var
Lib: TPDFlib;
Diag: WideString;
begin
Lib := TPDFlib.Create(nil);
try
Lib.LoadFromFile('assembly-drawings.pdf');
if Lib.SetPDFEMode(1) = 0 then
raise Exception.Create('PDF/E author mode was refused');
// Режим авторства тримає метадані життєвого циклу в актуальному стані
// при кожному збереженні. Спитайте, чи пройде документ власні ворота
if not Lib.PDFEReadyForSave then
begin
Diag := Lib.GetPDFEDiagnostics;
Writeln('PDF/E blockers: ', Diag);
Exit;
end;
Lib.SaveToFile('assembly-drawings-pdfe.pdf');
finally
Lib.Free;
end;
end;
Метадані життєвого циклу — зобов'язання на кожне збереження
PDF/E-1 просить більше, ніж ідентифікатор документа. Мінімальний набір включає ідентифікатор документа медіаменеджменту, ідентифікатор версії, клас рендерингу, час створення, час модифікації, час метаданих і заголовок. Це словник відстеження ревізій, і він існує тому, що інженерний результат очікує перевидань, а не одноразового написання
Наслідок для реалізації: ці поля не можна встановлювати при створенні документа. Якщо час модифікації записано, коли ви увімкнули режим, а документ потім редаговано, знімок XMP і фактичний стан документа роз'їхалися, і валідатор, який їх порівнює, повідомить неузгодженість, якої ніхто не хотів. Тому режим авторства синхронізує поля безпосередньо перед кожним збереженням, тож метадані описують байти, які зараз будуть записані, а не ті, що існували в момент увімкнення режиму
Це загальний принцип для метаданих відповідності, і його варто сформулювати окремо від PDF/E: похідні метадані живуть на шляху збереження, а не на шляху редагування. Будь-яке поле, обчислене зі стану документа, має бути перераховане в момент, коли стан заморожується, інакше це кеш без інвалідації
Правило, яке робить валідацію вмісту суворою
PDF/E-1 не дозволяє операторам секції сумісності поглинати невідомий вміст. У звичайному PDF BX і EX беруть у дужки регіон, у якому споживач мусить ігнорувати оператори, які не розпізнає, — це аварійний вихід, що дозволяє продуценту випускати новіші конструкції, не ламаючи старіших читачів. Під PDF/E-1 цей вихід зачинено, тож будь-який оператор, який preflight не розпізнає, повідомляється безумовно, незалежно від того, чи сидить він у секції сумісності
Ефект на валідатора значний. Він не може пропускати регіони, яких не розуміє, а отже парсер операндів має справді розібрати кожен оператор у кожному потоці вмісту. Ось тут і з'являються межі. Обхід обмежено 128 рівнями вкладення, мільйоном об'єктів і 64 МіБ вмісту, і ці ліміти — не тюнінг продуктивності. Вороже чи просто побите файл може подати граф об'єктів із циклами або глибиною вкладення, яка перетворює рекурсивний валідатор на переповнення стека, і ліміти — це те, що не дає проходу валідації стати вектором denial-of-service. Та сама оборонна постава описана в безпечному розборі недовірених PDF
// Окреме тестування відповідності для файлу, який ви не продукували,
// без завантаження його в примірник документа
var
Issues: TStringList;
Stream: TFileStream;
I: Integer;
begin
Issues := TStringList.Create;
Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
try
if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
for I := 0 to Issues.Count - 1 do
Writeln('PDF/E: ', Issues[I]);
finally
Stream.Free;
Issues.Free;
end;
end;
Що ворота збереження лагодять, а що відхиляють
Ворота ділять роботу на два етапи, і сам цей поділ — придатна дизайнерська ідея. Спершу вони нормалізують те, що безпечно ремонтоване: прапорці друку анотацій, прапорці no-zoom і no-rotate на текстових анотаціях та прапорець генерації вигляду в словнику форми. Це налаштування з одним правильним значенням за профілем і без жодного інформаційного вмісту, тож мовчазне виправлення — правильно, а відмова через них була б педантизмом
Потім вони перевіряють обмеження, які не ремонтуються без зміни того, що документ означає: версія, ідентифікація, шифрування, output intent, когерентність кольору пристрою і присутність динамічного вмісту форм. Документ, що провалює будь-яке з них, відхиляється, бо вигадати output intent чи обрати сімейство кольорів за автора — це продукувати файл, який пройде валідацію і спотворить вміст
Прочитати діагностику назад через GetPDFEDiagnostics перед збереженням — і відмова перетворюється на придатний до дій список, а не на невдалу операцію. У пакетному конвеєрі викликайте його на кожному документі, логуйте блокери по файлах і скеровуйте відмови в чергу, яку дивиться людина. Це куди корисніше за збереження, що кидає виключення, бо блокери зазвичай кластеризуються: сорок документів, які провалилися через той самий відсутній output intent, — це один фікс, а не сорок
Вибір між архівними профілями
PDF/E-1 — правильна ціль, коли результат — інженерна документація з життєвим циклом ревізій, і особливо коли когерентність кольору пристрою важить, бо вивід іде на плотери та широкосформатні принтери. PDF/A — правильна ціль, коли мета — довгострокова читабельність документів загалом, і це профіль із найширшою підтримкою валідаторів. Вони не взаємозамінні, і документ може задовольняти одному та провалювати інший
Якщо ви обираєте, стартуйте з того, хто валідує файл на далекому кінці. Інструменти валідації PDF/A всюдисущі, і відповідний preflight у PDFlibPas описано в preflight PDF/A і PDF/UA. Валідація PDF/E спеціалізованіша і зазвичай є договірною вимогою, а не дефолтом. Коли наявний архів треба привести до профілю, під який він ніколи не писався, патерн — шлях ремонту метаданих із конвертації в PDF/A з ремонтом метаданих, і та сама форма застосовується тут: ідентифікуй, відремонтуй безпечне, відхили решту зі списком
Режим авторства, обмежений preflight вмісту й окрема перевірка відповідності постачаються з бібліотекою PDFlibPas для Delphi, тож документ можна продукувати під профілем і незалежно верифікувати потім окремим кодовим шляхом — і це єдина схема, якому варто довіряти для заяви про відповідність