Technical Article

PDF/A и PDF/UA проверка в Delphi с PDFlibPas

Стандартите PDF/A и PDF/UA отговарят на два коренно различни въпроса, а разглеждането им като обща отметка за достъпност и архивиране води до изпращането на некоректни файлове в архива под предлог, че са съвместими. PDF/A определя дали даден файл ще се изобразява коректно след двадесет години. PDF/UA определя дали асистентските технологии могат да го прочетат днес. Даден документ може да премине едната проверка и напълно да се провали на другата, затова надеждното заключение изисква изпълнението и на двете проверки преди записването на файла, а не след като системата се довери на етикета в метаданните. Този идентификатор е просто самодекларация: нищо във формата не изисква той да бъде верен и приложение, което записва "PDF/A-1b" в XMP метаданните без реална проверка, генерира файл, който изглежда съвместим само за четците, които се доверяват сляпо на етикета. losLab PDF Library (PDFlibPas) решава този проблем за Delphi и C++Builder, като вгражда двата валидатора директно в библиотеката, което позволява изпълнението на проверките в рамките на процеса без външни услуги.

Два стандарта, които отхвърлят файлове по различни причини

ISO 19005 (PDF/A) е договор за възпроизвеждане: съвместим файл трябва да се изобразява идентично след десетилетия на софтуер, който никога не е имал връзка със системата, която го е създала, затова правилата ограничават външните зависимости: задължително вграждане на шрифтове, цветове, обвързани с ICC OutputIntent или дефинирани в независимо цветово пространство, забрана на шифриране в PDF/A-1, липса на JavaScript и съответствие на XMP метаданните с речника на документа. Стандартът ISO 14289 (PDF/UA) е договор за семантика: асистентските технологии трябва да могат да обходят документа и да извлекат смисъл, което се намира в различен слой: пълно дърво на структурата, алтернативен текст за изображения, заглавие на документа за показване, последователност на заглавията без пропуснати нива и логически връзки в таблиците.

Тъй като двата стандарта контролират различни нива, най-проблемни са документите, които се намират между тях. Файл с перфектно архивиране може да бъде напълно безполезен за екранен четец, а документ с отлично структурирани тагове може да препраща към шрифт, който няма да съществува след десет години. Публикациите в публичния сектор са типичен пример за изискване на двата стандарта едновременно, като процесите не могат да ги обединят в една обща фаза. Резултатите се изпращат до различни екипи: липсата на вградени шрифтове е проблем в кода за генериране на PDF, докато липсващият алтернативен текст е отговорност на авторите на съдържанието, а смесен отчет просто ще бъде препратен на два различни адреса.

Коя версия на PDF/A избирате е толкова важно, колкото и самото съвместимост. PDF/A-1 е базиран на PDF 1.4 и отхвърля прозрачност и JPEG2000, които съвременните системи използват редовно. PDF/A-2 (ISO 19005-2, базиран на ISO 32000-1) поддържа и двете и е разумният избор по подразбиране за нови архиви. PDF/A-3 отива по-далеч и позволява вграждане на всякакви файлове, на което разчитат форматите за електронни фактури. Екип, който през 2026 г. все още изисква съвместимост с PDF/A-1b, вероятно следва правила, написани преди петнадесет години, като промяната на изискванията към по-нова версия често е по-евтина от премахването на прозрачността от графиките на системата.

Структурирани констатации при приемане

Входната точка в основния API е CheckFileCompliance, с тест 1 за PDF/A и 2 за PDF/UA. Тя връща дескриптор на списък с низове, съдържащ отделни констатации на всеки ред, което е удобен формат за автоматизирана обработка:

function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;

var

  ListId, I: Integer;

begin

  ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0);  // 1 = PDF/A

  if ListId = 0 then

  begin

    // 0 means "no findings" OR "file unreadable" -- disambiguate before passing

    Result := Pdf.LastErrorCode = 0;

    Exit;

  end;

  for I := 0 to Pdf.GetStringListCount(ListId) - 1 do

    LogFinding(FileName, Pdf.GetStringListItem(ListId, I));

  Pdf.ReleaseStringList(ListId);

  Result := False;

end;

Две подробности определят дали процесът може да работи без човешка намеса. Първата е върната стойност, която означава две коренно различни неща: CheckFileCompliance връща 0 при пълна съвместимост, но и когато файлът изобщо не може да бъде отворен, тъй като в кода празният резултат съвпада с нула. Автоматичен процес, който тълкува 0 като успех, ще пропусне повредени файлове в архива, затова направете разграничение чрез LastErrorCode, преди да се доверите на нулевия резултат. Втората подробност е моментът на проверка: модулът работи с четец за споделен достъп вместо с пълния обектен модел, като отваря файла за споделено четене без извикване на LoadFromFile. Това позволява обработка на много големи файлове без натоварване на паметта, но се проваля, ако друг процес все още записва във файла (например по време на качване). Изпълнявайте проверката след завършване на трансфера.

Стрийминг архитектурата дава предимство при голямо натоварване: всяка проверка отваря файла само за четене, което позволява паралелен одит в различни нишки или процеси с отделни инстанции на TPDFlib без конфликти за достъп. Ресурсът, който изисква внимание, е самият дескриптор: всеки ненулев резултат от CheckFileCompliance остава заделен в паметта до извикване на ReleaseStringList, като пропускането на това извикване води до бавно изтичане на памет в дълго работещи процеси.

Отчети за потребители и сравнения за процесите на компилация

Списъкът с констатации е подходящ за автоматизирани системи, но е неизползваем за изпращане по имейл. Методът CreatePreflightReport представя анализа в четим текстов формат, CreatePreflightReportEx добавя избор на изходен формат, а SavePreflightReport записва отчета на диска за пакетиране с документа. Често договорите за архивиране изискват този отчет как задължителен съпътстващ елемент, а не само за вътрешни цели.

Функцията от тази фамилия, която е изключително полезна, е ComparePreflightReports. Съвместимостта е обект на регресия, подобно на всяка друга логика: промяна в шаблоните, нов лицензиран шрифт или актуализация на библиотека могат да доведат до нови несъответствия без предупреждение. Поддържайте еталонни отчети (golden reports) за представителни документи под контрол на версиите, обновявайте ги след промени и използвайте ComparePreflightReports за изчисляване на разликите. Липсата на разлики потвърждава качеството на версията, докато неочаквано несъответствие спира компилацията, което спестява време и средства преди одита.

Генериране на файлове, които преминават проверка при първо стартиране

Предварителната проверка е незаменима за външни файлове. За документи, генерирани от ваш код, търсенето на несъответствия след запис и последващото им коригиране е бавен подход. PDFlibPas поддържа режими за съвместимост по време на самото генериране, като можете да активирате и двата едновременно за един документ:

var

  Pdf: TPDFlib;

  Diag: WideString;

begin

  Pdf := TPDFlib.Create;

  try

    Pdf.NewDocument;

    Pdf.SetPDFAMode(1);

    Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');

    Pdf.SetPDFUAMode('en-US');

    Pdf.SetInformation(1, 'Quarterly Statement');  // /Title: required for PDF/UA

    // ... draw tagged content here ...

    Diag := Pdf.GetPDFUADiagnostics;

    if Diag <> '' then

      Writeln('fix before shipping: ', Diag);

    Pdf.SaveToFile('statement.pdf');

    // the preflight that counts runs on the saved file:

    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));

  finally

    Pdf.Free;

  end;

end;

Капанът се проявява при записването: голяма част от корекциите за съвместимост се извършват по време на сериализацията на документа, а не при включване на режима (например флагове за печат на анотации, записване на AFRelationship за вградени файлове в PDF/A-3, подреждане на табове и описания на полета за PDF/UA). Документът в паметта се различава структурно от този на диска, затова валиден резултат дава само проверката на записания файл. Валидирайте самия файл statement.pdf, вместо да предполагате съвместимост по състоянието на обекта в паметта, тъй като проверяваните байтове не съответстват на тези в крайния файл.

Фактурирането, което съдържа структуриран XML файл до визуализацията на документа, следва ZUGFeRD и Factur-X спецификациите, базирани на PDF/A-3. За тези документи трябва да дефинирате връзката изрично чрез SetPDFA3DefaultAFRelationship, тъй като ISO 19005-3 изисква всеки вграден файл да декларира ролята си спрямо документа. Ако я оставите недефинирана, вградената XML ще бъде отчетена като файл без изяснено предназначение, което валидаторът ще маркира като проблем.

Независими валидатори: veraPDF и Acrobat

Създателят на документа не трябва да бъде единствен съдия за своето качество. Проверките в PDFlibPas дават бързи резултати в рамките на процеса, но крайният архив трябва да бъде проверен от валидатор, разработен от външен екип. veraPDF е еталонният инструмент за PDF/A съвместимост и софтуерът, изискван от повечето архивни институции, така че той трябва да бъде ваша цел. Префлайт профилите на Acrobat са полезен арбитър, когато veraPDF и вградената проверка се разминават. Записвайте името на валидатора и неговата версия до всеки отчет: твърдението, че файлът е преминал успешно през veraPDF, не означава нищо без номера на неговата версия, тъй като инструментът затяга изискванията си при всяко издание.

Валидаторите понякога се разминават в тълкуването на спецификациите на стандартите, при което правилното решение не е изборът на по-удобния инструмент. Намалете обема на файла до минимален тест, който възпроизвежда различието, и го сравнете с оригиналния текст на стандарта. Това обикновено разкрива две неща: софтуерна грешка в самия инструмент, която си струва да докладвате, или правило от стандарта, което вашият екип е тълкувал неправилно и трябва да опише в техническите бележки, за да се избегнят бъдещи спорове.

Шифрираните файлове се отхвърлят бързо. И двата валидатора приемат парола, но PDF/A-1 файл с речник за шифриране е несъвместим по подразбиране, тъй като ISO 19005-1 забранява шифрирането, така че защитен файл се отхвърля веднага преди дълбокия анализ. Спецификата на достъпа при шифриране е описана в статията за одит на PDF шифриране и права.

Несъответствията с PDF/UA почти винаги се дължат на начина на изграждане на структурата на дървото, като техниките за маркиране са разгледани в създаване на структура на Tagged PDF в Delphi. Архивите, които изискват цифрово подписване, трябва да комбинират тази фаза с процеса на PAdES подписване и валидиране. Пълната документация за API е достъпна на продуктовата страница на PDFlibPas за Delphi.