Technical Article

Автоматизиране на предпечатни проверки на PDF в Delphi с HotPDF

Файлът се отваря безпроблемно на вашия компютър. Acrobat го визуализира, предпечатният преглед изглежда правилен, всяка страница е на мястото си. След това той отива в печатницата или в архивната система, която приема вашата месечна партида, и се връща отхвърлен: RGB изображения в CMYK задача, липса на ключ /Trapped, изходно намерение (output intent), което не съответства на печатната машина. Никой не е могъл да види нищо нередно в документа. Проблемът е несъответствие с определен профил, който е бил тестван някъде, където вие не сте присъствали. Предпечатна проверка (preflight) е терминът за този тест в печатната индустрия, а същинският въпрос е къде трябва да се намира той, когато PDF файловете се генерират от собствения ви код в Delphi, а не от компютъра на дизайнер.

HotPDF не предлага функция за предпечатна проверка, която можете да извикате. Компонентът съдържа прозорец за предпечатен отчет в своето графично демо (GUI demo), но зад него няма приложен програмен интерфейс (API), който услуга или скрипт за компилиране могат да задействат. Ако твърдим обратното, това би ви изпратило да търсите метод, който не съществува. Това звучи като липса, докато не забележите, че за файловете, които генерирате сами, извикването на валидатор върху собствените ви резултати така или иначе е грешен подход. Вие вече контролирате всяко свойство, което валидаторът би проверил. Полезното разделение е да направите генератора неспособен да създаде грешен файл, а след това да го докажете с инструмент, който не е написан от вас.

Защо проверявате собствените си файлове по различен начин

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

Има и причина, свързана с достоверността, поради която проверката трябва да бъде външна. Библиотека, която одобрява собствените си резултати, сама оценява изпита си. Когато архивната система на клиент или RIP процесорът на печатница отхвърли вашия файл, твърдението „нашияÑ?компонент казва, че всичко е наредâ€?няма никаква тежест. Решението на veraPDF или Acrobat обаче има тежест, тъй като другата страна използва същите инструменти.

Направете съответствието настройка, а не списък с задачи

Слоят за превенция е просто конфигурация. Задайте PDFACompliance или PDFXCompliance преди BeginDoc и HotPDF ще приложи съответните правила за целия процес на генериране: он ще вгради шрифтовете, ще следи използването на DeviceRGB и DeviceCMYK спрямо декларираното от вас изходно намерение и ще откаже функции, които профилът забранява. Противоречията се появяват при EndDoc, където защитите за съответствие задействат изключения, вместо тихомълком да изпратят файл, който ще се срине по-нататък по веригата. След като файлът бъде записан, същите свойства показват какво действително е било приложено, което е най-важният факт за лога на вашия процес:

// After EndDoc: record the enforced profiles with the run metadata
if Pdf.PDFACompliance <> '' then
  Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
  Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);

Поставете тези флагове на същия ред в лога, където са контролната сума (hash) на входящите данни и версията на HotPDF. В деня, в който валидаторът и вашият генератор се разминат за някой файл, този ред ще ви каже кой шаблон го е създал и коя версия на библиотеката е била заредена, а спорът, който иначе би ви отнел цял следобед, се решава с едно търсене с grep. Изходните намерения, ICC профилите и таговете, които стоят зад тези флагове, са подробно описани в ръководството за PDF/A, PDF/X и PDF/UA изход с HotPDF.

Бърза първа проверка за файлове, които не сте генерирали

Не всеки процес на обработка е чисто генеративен. Клиентите качват PDF файлове, скенерите ги записват в папка, партньорите ги прикачват към имейли. Преминаването на всеки един от тези файлове през пълен структурен валидатор губи време на опашката за файлове, които дори няма да се отворят. Direct File API на HotPDF чете достатъчно от структурата на файла, за да отговори на въпроса „товÐ?изобщо използваем PDF ли еâ€? без да зарежда цялото дърво на обектите, което го прави отлично средство за бързо филтриране при грешка (fail-fast):

function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
  Handle, Pages: Integer;
begin
  Result := False;
  Handle := Pdf.DAOpenFileReadOnly(FileName, '');
  if Handle <= 0 then
    Exit;  // structurally unreadable: quarantine, do not validate
  try
    Pages := Pdf.DAGetPageCount(Handle);
    Result := Pages > 0;
  finally
    Pdf.DACloseFile(Handle);
  end;
end;

Два факта за този API определят как да го обвиете в кода си. Прекият път с икономия на памет важи само за нешифрирани входящи данни. Предайте парола на DAOpenFileReadOnly и той тихомълком ще премине към пълен анализ, така че файл, за който знаете, че е шифриран, трябва да бъде преминал през DecryptFile до чисто работно копие преди филтрирането. Освен това извикването на DAGetPageCount няма смисъл при манипулатор (handle), който не е отворен успешно, така че проверката на манипулатора остава строга и неположителен резултат означава отхвърляне, а не повторен опит. Повече за тези модели на работа можете да прочетете в статията за Direct File API за големи PDF работни процеси.

veraPDF, интегриран като част от процеса по компилиране

За всеки файл, за който твърдите, че отговаря на PDF/A или PDF/UA, veraPDF е подходящият валидатор за интеграция. Той работи без графичен интерфейс (headless), приема партиди файлове, генерира XML или JSON и посочва всяка грешка чрез съответната ISO клауза. Така например грешка в правилото спрямо ISO 19005-1 клауза 6.2.2 ви насочва директно към конкретна настройка на генератора, вместо да ви оставя да гадаете. Управлението му от Delphi е стандартен контрол на процеси:

function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
  Cmd: string;
  SI: TStartupInfo;
  PI: TProcessInformation;
begin
  Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
    [PdfFile, ReportFile]);
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
      CREATE_NO_WINDOW, nil, nil, SI, PI) then
    RaiseLastOSError;
  try
    WaitForSingleObject(PI.hProcess, 120000);  // bound the wait per file
    GetExitCodeProcess(PI.hProcess, Result);
  finally
    CloseHandle(PI.hThread);
    CloseHandle(PI.hProcess);
  end;
end;

Този лимит от време (timeout) си плаща цената. Повреден файл може да блокира всеки парсер в състояние, от което той никога няма да излезе, а безкрайното изчакване в процес от опашката ще забави и останалите задачи. Ограничете времето за изчакване, задайте собствен код за грешка при прекъсване на времето и оставете файла настрана за ръчна проверка от човек. Когато анализирате резултата, четете XML за идентификатори на правила, а не за текст, четим от хора. Идентификаторите на правилата не се променят при актуализации на валидатора; формулировката на съобщенията се променя, а стабилният код е нещо, по което инженерът по поддръжката може да търси в стари тикети.

Начинът, по който изпълнявате партидата, има същото значение като това дали всеки файл преминава проверката успешно. Стартирайте по един процес на файл, а не един на партида, така че повреден входящ файл да ви коства само неговото време за изчакване и нищо друго. Ограничете броя на процесите на валидиране до броя на процесорните ядра, тъй като изграждането на XML отчета натоварва процесора, а твърде многото процеси само ще забавят работата. Поставете и горен лимит за размера на входящите файлове, тъй като сканирана книга с размер два гигабайта ще блокира опашката, независимо колко търпелив е парсерът. Нищо от това не е предпечатна проверка в строгия смисъл на думата: това е разликата между система, която издържа на месечното натоварване, и такава, която бива изключена още първата нощ, когато спре работния процес в 2 часа сутринта.

PDF/X е мястото, където този подход има ограничения. veraPDF не го валидира, така че работещата проверка все още е Preflight в Acrobat с профила по ISO 15930, посочен от вашата печатница. Acrobat изисква намеса от човек, което означава вземане на проби (sampling) вместо пълно покритие: първият файл от нов шаблон плюс малка случайна извадка от всяка партида, докато автоматизираната проверка обработва всичко, което може да се мине без човешка намеса. Ръчна проверка на извадки, която наистина се извършва, е по-добра от пълно автоматизиране, което остава наполовина завършено завинаги.

Отчет, който все още ще ви трябва след година

Защитата с предпечатна проверка носи ползи два пъти. Първо, когато спре повреден файл на входа, и втори път много по-късно, когато някой попита защо конкретен файл е бил допуснат. Този втори момент е този, който трябва да определи формата на отчета, тъй като оскъдният отчет ще ви остави без доказателства. За всеки проверен файл запазвайте контролната сума (hash) на входящите данни, флаговете за съответствие на генератора и версията на библиотеката от споменатия по-горе ред в лога, името и версията на валидатора, профила, спрямо който е бил проверен, информацията за успех или неуспех и идентификаторите на неуспешните правила с номерата на страниците, когато валидаторът ги предоставя. Съхранявайте този отчет до файла, който описва. Ако го поставите в отделна система, тя вероятно ще бъде спряна от експлоатация преди архива, който документира.

Изключенията също трябва да бъдат документирани. Когато клиент настоява да изпрати файл, който проверката отхвърля, решението не е да смекчите правилата за всички. Запишете кой е одобрил този файл, на какви основания и до коя дата, след което прикачете това разрешение (waiver) към неговия отчет. Разрешение с име и срок е решение, за което някой носи отговорност. Проверка, коментирана в кода като „временнаâ€? е просто инцидент, който очаква своята дата.

Още един навик се отплаща многократно: когато даден файл се провали, копирайте го в папка за регресионни тестове, преди някой да го е променил. Почти всеки проблем с предпечатна проверка, който си струва да бъде отстраняван, води началото си от един конкретен входящ файл, а екипите, които пазят тези файлове, отстраняват повторната поява за час, вместо да чакат проблемът да се появи отново в производствена среда. Свойствата за съответствие и Direct File API, показани тук, са част от компонента HotPDF за Delphi и C++Builder, чиято документация описва подробно всяко извикване.