Articolo tecnico

Automatizzare controlli PDF preflight in Delphi con HotPDF

Il run mensile degli estratti sembrava corretto su ogni schermo di sviluppo, quindi è stato spedito. Il service di stampa ha restituito l'intero batch due giorni dopo: immagini RGB in un job CMYK e nessuna dichiarazione /Trapped. Il costo non era la ristampa; erano due giorni di una scadenza regolatoria. "Preflight" è il termine di prestampa per intercettare esattamente questa classe di problemi prima che i file escano dall'edificio, e la domanda ingegneristica interessante per un team Delphi è dove debba stare quel controllo quando i PDF sono prodotti dal proprio codice con una libreria come HotPDF invece che dagli strumenti desktop di un designer.

La prevenzione batte l'ispezione quando possiedi il generatore

Il preflight classico presume un file esterno di qualità ignota e lo ispeziona a posteriori. Quando la tua applicazione genera il documento, quell'architettura è al contrario: ogni proprietà che l'ispettore controllerebbe, embedding dei font, uso degli spazi colore, output intent, metadati, è stata decisa dal tuo codice pochi millisecondi prima. Il fallimento preflight più economico è quello reso impossibile in fase di generazione.

Conviene essere precisi su ciò che HotPDF offre e non offre qui. Il componente include una finestra di preflight report come parte della sua applicazione demo GUI, ma non esiste un'API preflight programmatica da chiamare da un servizio o da uno script di build. È meno una lacuna di quanto sembri, perché per documenti generati il pattern robusto ha comunque due metà indipendenti: vincolare il generatore affinché non possa emettere strutture non conformi, poi verificare l'output con un validatore che non mantieni tu. Una libreria che valida il proprio output si corregge il compito da sola; uno strumento esterno fornisce evidenza accettabile per cliente o auditor.

Il lato generazione: rendere la compliance una configurazione, non un item di review

Le proprietà standard di HotPDF sono il livello di prevenzione. Quando PDFACompliance o PDFXCompliance sono impostate prima di BeginDoc, il componente applica le regole corrispondenti durante la generazione, incorporando i font, tracciando l'uso di DeviceRGB e DeviceCMYK rispetto all'output intent dichiarato e rifiutando funzionalità vietate dal profilo. Dopo il salvataggio, quelle stesse proprietà registrano ciò che è stato imposto, esattamente ciò che serve al log della pipeline:

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

Scrivi questi flag, l'hash dei dati di input e la versione di HotPDF nella stessa riga di log. Quando un validatore più tardi non concorda con il generatore, quella riga ti dice quale revisione template e quale versione libreria hanno prodotto il file contestato, e una disciplina di log in una riga sostituisce un pomeriggio di ipotesi forensi. La configurazione completa alla base di questi flag, output intent, profili ICC e tagging, è trattata nella nostra guida all'output PDF/A, PDF/X e PDF/UA con HotPDF.

Fai triage dei file in ingresso prima dei controlli costosi

Molte pipeline non sono puramente generative: i clienti caricano PDF, gli scanner li depositano, i partner li inviano via email. Eseguire una validazione strutturale completa su ogni file in ingresso spreca tempo coda su input che non sono nemmeno apribili. La Direct File API di HotPDF legge la struttura file senza caricare l'intero albero degli oggetti, quindi è un primo gate economico:

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;

Due comportamenti di questa API modellano la logica circostante. DAOpenFileReadOnly resta a memoria piatta solo per input non cifrati: se passi una password, ripiega internamente su un parse completo, quindi instrada i file noti come cifrati attraverso DecryptFile per produrre prima una copia di lavoro in chiaro. Inoltre DAGetPageCount è valido solo su un handle proveniente da un'apertura riuscita, quindi mantieni rigoroso il controllo dell'handle invece di assumere un valore positivo. Altri pattern per questa API sono nell'articolo Direct File API per workflow PDF di grandi dimensioni.

Il lato verifica: veraPDF come step di build

Per dichiarazioni PDF/A e PDF/UA, veraPDF è il validatore da collegare alla pipeline: gira headless, elabora batch, emette XML o JSON machine-readable e riporta ogni fallimento per numero di clausola ISO, quindi un finding come un rule failure contro ISO 19005-1 clausola 6.2.2 si mappa direttamente a un'impostazione nota del generatore. Invocarlo da Delphi è normale controllo di processo:

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;

Il timeout non è decorazione. Un file malformato può mandare qualsiasi parser in territorio patologico, e un'attesa senza limiti dentro un worker di coda porta giù l'intera coda. Limita l'attesa, tratta un timeout come fallimento con un proprio codice e metti l'input in quarantena per ispezione umana. Analizza l'XML cercando gli identificatori delle regole invece di fare scraping dei messaggi leggibili dagli umani; gli ID regola sono stabili tra release del validatore, il wording dei messaggi no, e i codici stabili sono ciò che il supporto può cercare nei ticket passati.

Il comportamento batch merita la stessa cura della correttezza single-file. Esegui il validatore come un processo per file invece che un processo per batch, così un input patologico costa il timeout di quel file e non dell'intero batch; limita i processi validatore concorrenti al numero di core, perché la generazione dei report XML è CPU-bound; imponi un tetto di dimensione file all'intake, perché un mostro scansionato da 2 GB dominerà la coda per quanto il parser sia ben comportato. Nulla di questo è logica preflight, ma è la differenza tra un gate che sopravvive al volume di fine mese e uno che viene disattivato la prima volta che blocca la pipeline alle 2 di notte.

PDF/X è il vuoto in questa storia: veraPDF non lo copre, e il controllo pratico resta Acrobat Preflight con il profilo ISO 15930 corrispondente. Acrobat è interattivo, quindi usalo per campionamento, primo articolo di un nuovo template e una piccola estrazione casuale per batch, mentre il gate automatizzato copre ciò che può essere automatizzato. Un controllo manuale a campione che avviene davvero batte un'automazione completa teorica che nessuno ha finito di costruire.

Che cosa deve contenere il report per valere la pena conservarlo

Un gate preflight produce valore due volte: quando blocca un file difettoso e mesi dopo quando qualcuno chiede perché un file sia stato accettato. Il secondo uso è quello che detta il formato del report. Conserva, per ogni file controllato: hash dell'input, flag di compliance e versione del generatore dalla riga di log sopra, nome e versione del validatore, profilo controllato, esito pass o fail, ed elenco degli ID regola falliti con numeri pagina quando il validatore li fornisce. Conserva il report accanto all'artefatto che descrive, non in un sistema separato che verrà dismesso prima dell'archivio.

Anche le deviazioni accettate richiedono documentazione. Quando un cliente insiste per spedire un file che il gate non gradisce, registra chi lo ha approvato, perché e fino a quando, e collega quel waiver al report invece di indebolire globalmente la regola. Un waiver con owner e scadenza è un'eccezione gestita; un controllo commentato è un incidente futuro.

I fallimenti meritano un altro passaggio: copia il file fallente in una cartella di regressione nominata. Ogni incidente preflight che abbiamo aiutato a debuggare alla fine si è ridotto a un input riproducibile, e i team che hanno conservato quegli input hanno corretto regressioni in ore invece di riscoprirle in produzione.

FAQ

HotPDF può validare programmaticamente un PDF arbitrario di terze parti?

No. Il preflight report nel prodotto è una funzionalità demo GUI, non un'API chiamabile. Il pattern di automazione supportato è enforcement lato generazione tramite proprietà di compliance più un validatore esterno come veraPDF per il verdetto formale.

veraPDF basta per lavori di stampa?

Copre PDF/A e PDF/UA. Per master di stampa PDF/X, esegui Acrobat Preflight con il profilo specificato dalla tipografia e conferma che l'output intent corrisponda alla caratterizzazione di macchina attesa.

Che cosa deve far fallire la build: solo errori o anche warning?

Blocca sui rule failure del profilo di cui dichiari la conformità, e registra i warning con monitoraggio dei trend. Promuovere ogni warning a blocker addestra le persone ad aggirare il gate, peggio che non averlo.

Riferimento prodotto

Le proprietà di compliance e la Direct File API usate in questa pipeline appartengono a HotPDF Component per Delphi e C++Builder; la sua documentazione descrive per intero ogni chiamata mostrata qui.