Fișierul se deschide curat pe mașina dumneavoastră. Acrobat îl afișează, previzualizarea de tipărire arată bine, fiecare pagină este prezentă. Apoi ajunge la biroul de tipar, sau în sistemul de arhivare care înghite lotul dumneavoastră lunar, și se întoarce respins: imagini RGB într-o lucrare CMYK, fără cheie /Trapped, un output intent care nu se potrivește cu tiparul. Nimic nu era greșit la document, din ce putea vedea cineva. Era greșit față de un profil, iar profilul a fost verificat undeva unde dumneavoastră nu erați. Preflight este numele din prepress pentru acea verificare, iar întrebarea reală este unde își are locul atunci când PDF-urile ies din propriul dumneavoastră cod Delphi, nu de pe desktopul unui designer
HotPDF nu vă oferă o funcție de preflight de apelat. Componenta poartă o fereastră de raport preflight în demo-ul său GUI, dar nu există niciun API în spatele ei pe care un serviciu sau un script de build să îl poată invoca, iar a pretinde altfel v-ar trimite să căutați o metodă care nu există. Asta sună ca o gaură până observați că, pentru fișierele pe care le generați chiar dumneavoastră, apelarea unui validator asupra propriei ieșiri este oricum forma greșită. Controlați deja fiecare proprietate pe care un validator ar inspecta-o. Separarea utilă este să faceți generatorul incapabil să emită un fișier greșit, apoi să dovediți asta cu un instrument pe care nu l-ați scris dumneavoastră
De ce vă verificați propria ieșire altfel
Preflight-ul tradițional presupune fișierul unui străin. Vreun designer, vreo altă aplicație, vreun lanț necunoscut de editări l-a produs, iar dumneavoastră îl inspectați pentru că nu aveți nicio idee ce se află înăuntru. Un document produs de codul dumneavoastră nu este un străin. Înglobarea fonturilor, spațiul de culoare, output intent-ul, blocul de metadate: programul dumneavoastră a decis toate acestea cu câteva milisecunde înainte ca fișierul să ajungă pe disc. Inspectarea lui ulterioară pentru a descoperi alegeri pe care tocmai le-ați făcut este muncă de umplutură. Mișcarea mai ieftină este să constrângeți acele alegeri astfel încât un fișier neconform să nu existe niciodată pentru a fi prins
Există și un motiv de credibilitate pentru a păstra verificarea externă. O bibliotecă care își binecuvântează propria ieșire își notează propriul examen. Când sistemul de arhivare al unui client sau RIP-ul unei tipografii respinge fișierul dumneavoastră, "componenta noastră spune că e în regulă" nu cântărește nimic. Un verdict de la veraPDF sau Acrobat cântărește, pentru că cealaltă parte rulează aceleași instrumente
Faceți din conformitate o setare, nu un checklist
Stratul de prevenție este doar configurație. Setați PDFACompliance sau PDFXCompliance înainte de BeginDoc, iar HotPDF menține regulile corespunzătoare pentru întreaga trecere de generare: înglobează fonturile, urmărește utilizarea DeviceRGB și DeviceCMYK față de output intent-ul declarat și refuză funcțiile pe care profilul le interzice. Contradicțiile ies la suprafață la EndDoc, unde porțile de conformitate ridică o excepție, în loc să livreze pe tăcute ceva care va eșua în aval. Odată ce fișierul este salvat, aceleași proprietăți citesc înapoi ce s-a aplicat de fapt, ceea ce este exact faptul de care jurnalul fluxului dumneavoastră are cel mai mult nevoie:
// După EndDoc: înregistrează profilurile aplicate împreună cu metadatele rulării
if Pdf.PDFACompliance <> '' then
Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);
Puneți acele flaguri pe aceeași linie de jurnal cu hash-ul datelor de intrare și versiunea HotPDF. În ziua în care un validator și generatorul dumneavoastră nu cad de acord asupra unui fișier, acea linie vă spune ce șablon l-a produs și ce build al bibliotecii era încărcat, iar disputa care altfel ar mânca o după-amiază devine un grep. Output intents, profilurile ICC și taguirea care stau în spatele acestor flaguri sunt detaliate în ghidul pentru ieșirea PDF/A, PDF/X și PDF/UA cu HotPDF
O primă poartă ieftină pentru fișierele pe care nu le-ați generat dumneavoastră
Nu fiecare flux este pur generativ. Clienții încarcă PDF-uri, scanerele le lasă într-un folder, partenerii le atașează la e-mail. Trecerea fiecăruia dintre acestea printr-un validator structural complet irosește timp de coadă pe fișiere care nici măcar nu se vor deschide. Direct File API al HotPDF citește suficient din structura unui fișier pentru a răspunde la "este acesta măcar un PDF utilizabil" fără a încărca întregul arbore de obiecte, ceea ce îl face un loc bun pentru a eșua rapid:
function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
Handle, Pages: Integer;
begin
Result := False;
Handle := Pdf.DAOpenFileReadOnly(FileName, '');
if Handle <= 0 then
Exit; // ilizibil structural: pune în carantină, nu valida
try
Pages := Pdf.DAGetPageCount(Handle);
Result := Pages > 0;
finally
Pdf.DACloseFile(Handle);
end;
end;
Două fapte despre acest API decid cum îl împachetați. Scurtătura de memorie constantă se aplică doar pentru intrări necriptate; dați-i lui DAOpenFileReadOnly o parolă și el coboară pe tăcute la un parsing complet, așa că un fișier despre care știți că este criptat ar trebui să treacă prin DecryptFile într-o copie de lucru simplă înainte de triaj. Iar DAGetPageCount nu înseamnă nimic pe un handle care nu s-a deschis curat, așa că verificarea handle-ului rămâne strictă, iar un rezultat nepozitiv este o respingere, nu o reîncercare. Mai multe dintre aceste modele se găsesc în articolul Direct File API pentru fluxuri de lucru cu PDF-uri mari
veraPDF, rulat ca parte din build
Pentru orice pretindeți a fi PDF/A sau PDF/UA, veraPDF este validatorul de cablat în flux. Rulează headless, primește un lot, emite XML sau JSON și numește fiecare eșec după clauza ISO corespunzătoare, așa că un eșec de regulă față de ISO 19005-1 clauza 6.2.2 indică direct înapoi la o setare a generatorului, în loc să vă lase să ghiciți. Comandarea lui din Delphi este control de proces simplu:
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); // limitează așteptarea per fișier
GetExitCodeProcess(PI.hProcess, Result);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
Acel timeout își merită locul. Un fișier malformat poate duce orice parser într-un colț din care nu mai iese niciodată, iar o așteptare fără limită într-un worker din coadă trage după ea restul cozii. Limitați așteptarea, dați unui timeout propriul cod de eșec și puneți fișierul deoparte pentru un om. Când citiți rezultatul, parsați XML-ul după identificatorii de regulă, nu după textul lizibil pentru oameni. ID-urile de regulă supraviețuiesc actualizărilor de validator; formularea mesajelor nu, iar un cod stabil este ceva după care un inginer de suport poate căuta în tichete vechi
Modul în care rulați lotul contează la fel de mult ca faptul dacă fiecare fișier trece sau nu. Un proces per fișier, nu unul per lot, astfel încât o intrare otrăvitoare vă costă doar timeout-ul acelui fișier și nimic mai mult. Limitați numărul de procese de validator la numărul de nuclee, pentru că generarea raportului XML este limitată de CPU, iar suprasubscrierea doar zvârcolește sistemul. Și puneți un plafon de dimensiune la recepție, pentru că o carte scanată de doi gigaocteți va monopoliza coada indiferent cât de răbdător este parserul. Nimic din toate acestea nu este preflight în sensul strict. Este diferența dintre o poartă care rezistă la volumul de sfârșit de lună și una care este oprită în prima noapte în care blochează fluxul la 2 dimineața
PDF/X este locul unde acest lucru se poticnește. veraPDF nu îl validează, așa că verificarea funcțională rămâne tot Preflight din Acrobat, cu profilul ISO 15930 numit de tipografia dumneavoastră. Acrobat vrea un om, ceea ce înseamnă eșantionare, nu acoperire completă: primul fișier ieșit dintr-un șablon nou, plus o mică tragere aleatorie din fiecare lot, în timp ce poarta automatizată se ocupă de tot ce poate fi gestionat fără un om. O verificare eșantionată care chiar rulează bate o automatizare completă care rămâne pe jumătate terminată pentru totdeauna
Un raport pe care îl veți dori și peste un an
O poartă de preflight se răsplătește de două ori. O dată când oprește un fișier greșit la ușă, și din nou mult mai târziu, când cineva întreabă de ce un anumit fișier a fost lăsat să treacă. Acel al doilea moment este cel care ar trebui să dicteze formatul, pentru că este cel în care un raport subțire vă lasă blocat. Pentru fiecare fișier verificat, păstrați hash-ul de intrare, flagurile de conformitate ale generatorului și versiunea bibliotecii din linia de jurnal de mai sus, numele și versiunea validatorului, profilul față de care a fost verificat, rezultatul trecut sau eșuat și ID-urile regulilor eșuate, cu numerele de pagină ori de câte ori validatorul le oferă. Stocați acel raport alături de fișierul pe care îl descrie. Puneți-l într-un sistem separat și acel sistem va fi dezafectat înaintea arhivei pe care o documentează
Excepțiile trebuie de asemenea scrise. Când un client insistă să livreze un fișier pe care poarta nu îl acceptă, răspunsul nu este să relaxați regula pentru toată lumea. Înregistrați cine a aprobat acest fișier, pe ce motive și până la ce dată, apoi atașați acea derogare la raportul său. O derogare cu un nume și o expirare este o decizie pe care cineva și-o asumă. O verificare comentată "temporar" este un incident care își așteaptă data
Încă un obicei se răsplătește singur: când un fișier eșuează, copiați-l într-un folder de regresie numit înainte ca cineva să-l atingă. Aproape fiecare problemă de preflight care merită depanată se întoarce la o intrare specifică, iar echipele care păstrează acele intrări repară recurența într-o oră, în loc să aștepte să reapară în producție. Proprietățile de conformitate și Direct File API prezentate aici fac parte din HotPDF Delphi Component pentru Delphi și C++Builder, a cărui documentație acoperă fiecare apel în detaliu