Technical Article

Automatizácia kontrol PDF preflight v Delphi pomocou HotPDF

Súbor sa na vašom počítači otvorí bez problémov. Acrobat ho zobrazí správne, náhľad tlače vyzerá v poriadku a nechýba žiadna stránka. Potom sa však dostane do tlačiarne alebo archívneho systému, ktorý spracováva vaše mesačné dávky, a vráti sa ako zamietnutý: RGB obrázky v CMYK úlohe, chýbajúci kľúč /Trapped alebo zámer výstupu (output intent), ktorý nezodpovedá tlačovému stroju. Na prvý pohľad nebolo s dokumentom nič v neporiadku. Problém bol v nesúlade s profilom a tento profil bol overovaný niekde mimo vášho dosahu. Preflight je pred tlačový názov pre túto kontrolu a skutočnou otázkou zostáva, kam patrí, ak PDF súbory pochádzajú z vášho vlastného kódu v Delphi a nie z pracovnej plochy dizajnéra.

HotPDF neposkytuje žiadnu preflight funkciu, ktorú by ste mohli jednoducho zavolať. Komponent síce obsahuje okno preflight správy vo svojom GUI deme, ale neexistuje za ním žiadne API, ktoré by mohol spustiť služobný alebo zostavovací skript. Predstierať opak by vás len priviedlo k hľadaniu metódy, ktorá neexistuje. Môže to vyzerať ako nedostatok, kým si neuvedomíte, že pre súbory, ktoré si sami generujete, je volanie validátora na vlastnom výstupe nesprávnym prístupom. Všetky vlastnosti, ktoré by validátor kontroloval, už máte pod kontrolou. Efektívnym riešením je navrhnúť generátor tak, aby nedokázal vytvoriť nesprávny súbor, a následne to dokázať nástrojom, ktorý ste sami nenapísali.

Prečo vlastný výstup kontrolujete inak

Tradičný preflight predpokladá, že ide o cudzí súbor. Vytvoril ho nejaký dizajnér, iná aplikácia alebo neznámy reťazec úprav a vy ho kontrolujete, pretože netušíte, čo je vo vnútri. Dokument vytvorený vaším kódom však nie je cudzí. Vkladanie písiem, farebný priestor, zámer výstupu, blok metadát: váš program o tom všetkom rozhodol len niekoľko milisekúnd pred zápisom súboru na disk. Kontrolovať ho dodatočne a zisťovať rozhodnutia, ktoré ste práve urobili, je zbytočná práca. Efektívnejším krokom je obmedziť tieto možnosti tak, aby nekompatibilný súbor vôbec nemohol vzniknúť.

Existuje aj dôvod dôveryhodnosti, prečo ponechať overovanie na externé nástroje. Knižnica, ktorá schvaľuje svoj vlastný výstup, v podstate známkuje vlastnú skúšku. Keď archívny systém zákazníka alebo RIP tlačiarne odmietne váš súbor, argument typu „náš komponent hovorí, že je v poriadku“ nemá žiadnu váhu. Verdikt od veraPDF alebo Acrobat áno, pretože druhá strana používa tie isté nástroje.

Nastavte súlad ako parameter, nie ako zoznam úloh

Preventívna vrstva je iba otázkou konfigurácie. Nastavte PDFACompliance alebo PDFXCompliance pred zavolaním BeginDoc a HotPDF uplatní príslušné pravidlá pre celý proces generovania: vloží písma, sleduje použitie DeviceRGB a DeviceCMYK vzhľadom na deklarovaný zámer výstupu a zakáže funkcie, ktoré daný profil nepovoľuje. Prípadné rozpory sa prejavia pri EndDoc, kedy sa aktivujú kontrolné mechanizmy, namiesto toho, aby sa ticho odoslal súbor, ktorý neskôr zlyhá. Po uložení súboru tie isté vlastnosti načítajú to, čo bolo skutočne vynútené, čo je kľúčový údaj pre váš protokol o spracovaní:

// 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);

Umiestnite tieto príznaky na rovnaký riadok protokolu (logu) ako hash vstupných dát a verziu HotPDF. V deň, keď sa validátor a váš generátor nezhodnú na súbore, tento riadok vám povie, ktorá šablóna ho vytvorila a ktorá verzia knižnice bola načítaná. Diskusia, ktorá by inak zabrala celé popoludnie, sa tak zúži na jednoduchý grep. Zámery výstupu, ICC profily a tagovanie, ktoré stoja za týmito príznakmi, sú podrobne popísané v príručke pre PDF/A, PDF/X a PDF/UA výstup s HotPDF.

Jednoduchá prvá kontrola pre súbory, ktoré ste sami negenerovali

Nie každý proces spracovania je čisto generatívny. Zákazníci nahrávajú PDF súbory, skenery ich ukladajú do priečinka, partneri ich posielajú ako prílohy e-mailov. Spúšťanie úplnej štrukturálnej validácie pre každý takýto súbor plytvá časom frontu na súbory, ktoré sa ani nedajú otvoriť. Rozhranie Direct File API komponentu HotPDF dokáže načítať dostatočnú časť štruktúry súboru na to, aby odpovedalo na otázku, či ide vôbec o použiteľné PDF, a to bez načítania celého stromu objektov, čo z neho robí ideálny nástroj na rýchle zachytenie chýb:

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;

Spôsob, akým toto API obalíte, závisí od dvoch skutočností. Skratka s priamou pamäťou platí len pre nezašifrovaný vstup; ak odovzdáte funkcii DAOpenFileReadOnly heslo, ticho prejde na úplnú analýzu, takže súbor, o ktorom viete, že je zašifrovaný, by mal pred triážou prejsť cez DecryptFile do nezašifrovanej pracovnej kópie. A DAGetPageCount nemá žiadny význam pre handle, ktorý sa neotvoril správne, takže kontrola handle musí zostať prísna a nekladný výsledok znamená zamietnutie, nie nový pokus. Viac o týchto vzoroch nájdete v článku o Direct File API pre spracovanie veľkých PDF súborov.

veraPDF, spúšťaný ako súčasť zostavenia

Pre všetko, čo deklarujete ako PDF/A alebo PDF/UA, je veraPDF tým správnym validátorom na integráciu. Beží v bezoknovom režime (headless), spracováva dávky, generuje XML alebo JSON a označuje každé zlyhanie podľa príslušného bodu normy ISO. Zlyhanie pravidla voči ISO 19005-1, bodu 6.2.2 tak ukazuje priamo na nastavenie generátora, namiesto toho, aby ste museli hádať. Jeho riadenie z Delphi je záležitosťou bežnej kontroly procesov:

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;

Tento časový limit (timeout) má svoje opodstatnenie. Poškodený súbor môže dostať akýkoľvek analyzátor do stavu, z ktorého sa už nedostane, a nekonečné čakanie vo fronte na pozadí by zbrzdilo celú frontu. Obmedzte čakanie, priraďte časovému limitu vlastný kód zlyhania a odložte súbor na manuálnu kontrolu. Pri čítaní výsledku analyzujte XML kvôli identifikátorom pravidiel, nie kvôli textu pre ľudí. ID pravidiel prežijú aj aktualizácie validátora, zatiaľ čo formulácie správ sa môžu zmeniť. Stabilný kód je niečo, podľa čoho môže technik podpory vyhľadávať v starších požiadavkách.

Spôsob spúšťania dávky závisí rovnako od toho, či každý súbor prejde úspešne. Použite jeden proces na jeden súbor, nie na celú dávku, aby vás problematický vstup stál iba časový limit tohto jedného súboru. Obmedzte počet procesov validátora na počet jadier procesora, pretože zostavenie správy XML je náročné na CPU a nadmerné prideľovanie zdrojov by systém len spomalilo. Taktiež nastavte maximálnu veľkosť súboru na vstupe, pretože dvojgigabajtová naskenovaná kniha zahltí front bez ohlladu na trpezlivosť analyzátora. Nič z toho nie je preflight v prísnom zmysle slova. Je to rozdiel medzi kontrolným bodom, ktorý zvládne nápor na konci mesiaca, a tým, ktorý sa vypne hneď prvú noc, keď o druhej ráno zastaví celé spracovanie.

Pri PDF/X však toto riešenie naráža na limity. veraPDF ho nevaliduje, takže funkčnou kontrolou zostáva Acrobat Preflight s profilom ISO 15930, ktorý špecifikovala vaša tlačiareň. Acrobat však vyžaduje zásah človeka, čo znamená skôr výberové overovanie než úplné pokrytie: prvý súbor z novej šablóny a malý náhodný výber z každej dávky, zatiaľ čo automatizovaný proces spracuje všetko, čo sa dá vyriešiť bez asistencie. Výberová kontrola, ktorá reálne funguje, je lepšia ako kompletná automatizácia, ktorá zostane navždy nedokončená.

Správa, ktorú oceníte aj o rok

Kontrola preflight sa oplatí dvakrát. Prvýkrát, keď zastaví chybný súbor hneď na začiatku, a druhýkrát oveľa neskôr, keď sa niekto opýta, prečo bol konkrétny súbor schválený. Práve tento druhý moment by mal určovať formát správy, pretože stručná správa vám vtedy nepomôže. Pre každý kontrolovaný súbor ukladajte hash vstupu, príznaky súladu generátora a verziu knižnice z vyššie uvedeného riadku logu, názov a verziu validátora, profil, voči ktorému bol súbor kontrolovaný, výsledok kontroly a ID zlyhaných pravidiel spolu s číslami strán, ak ich validátor poskytuje. Uložte túto správu vedľa súboru, ktorý popisuje. Ak ju uložíte do samostatného systému, tento systém bude pravdepodobne vyradený z prevádzky skôr ako samotný archív.

Výnimky je tiež potrebné zaznamenať. Keď zákazník trvá na odoslaní súboru, ktorý kontrola neodporúča, riešením nie je zmiernenie pravidiel pre všetkých. Zaznamenajte, kto tento súbor schválil, z akých dôvodov a do akého dátumu, a potom priložte túto výnimku k správe. Výnimka s menom a dátumom expirácie je rozhodnutie, za ktoré niekto nesie zodpovednosť. Kontrola dočasne vypnutá v kóde je len incidentom čakajúcim na svoju príležitosť.

Ešte jeden zvyk sa rýchlo vráti: keď súbor zlyhá, skopírujte ho do pomenovaného priečinka regresných testov skôr, než na ňom niekto vykoná zmeny. Takmer každý problém s preflightom, ktorý stojí za ladenie, sa dá vystopovať k jednému konkrétnemu vstupu. Tímy, ktoré si tieto vstupy uchovávajú, vyriešia opakovaný problém za hodinu, namiesto toho, aby čakali, kým sa znova objaví v produkcii. Vlastnosti súladu a Direct File API zobrazené v tomto článku sú súčasťou komponentu HotPDF Component pre Delphi a C++Builder, ktorého dokumentácia podrobne popisuje každé volanie.