Articolo tecnico

Preflight PDF/A e PDF/UA in Delphi con PDF Library for Delphi

PDF/A e PDF/UA rispondono a due domande che non hanno nulla a che fare l'una con l'altra, e trattarle come un'unica casella di accessibilità-e-archiviazione è il modo in cui file rotti raggiungono un archivio indossando un'etichetta di conformità. PDF/A chiede se un file sarà ancora renderizzato fedelmente tra vent'anni. PDF/UA chiede se la tecnologia assistiva può leggerlo oggi. Un documento può passare l'uno a pieni voti e fallire l'altro, quindi l'unico verdetto onesto viene dall'eseguire entrambi, e dall'eseguirli prima che il file sia scritto, non dopo che un sistema a valle si fida dell'identificatore di conformità cotto nei suoi metadati. Quell'identificatore è un'autodichiarazione. Niente nel formato richiede che sia vero, e un'applicazione che scrive "PDF/A-1b" nell'XMP senza validare rispetto allo standard produce un file che sembra conforme a ogni consumatore che legge solo l'etichetta. losLab PDF Library (PDF Library for Delphi) colma quel divario per Delphi e C++Builder costruendo entrambi i validatori nella libreria, così il controllo gira in-process senza nessun servizio esterno da avviare

Due standard che bocchiano i file per ragioni opposte

ISO 19005 (PDF/A) è un contratto di riproduzione. Un file conforme deve renderizzarsi in modo identico decenni da ora su software che non ha mai visto il sistema che lo ha prodotto, quindi le regole attaccano le dipendenze esterne: ogni font incorporato, colore ancorato a un ICC OutputIntent incorporato o espresso in uno spazio indipendente dal dispositivo, nessuna crittografia in PDF/A-1, nessun JavaScript, metadati XMP che concordano con il dizionario informazioni del documento. ISO 14289 (PDF/UA) è invece un contratto semantico. La tecnologia assistiva deve attraversare il documento e venir via con un significato, che vive in uno strato del tutto diverso: un albero di struttura completo, testo alternativo sulle figure, un titolo del documento impostato per la visualizzazione, livelli di intestazione che non saltano, relazioni di intestazione di tabella che sopravvivono una volta che la pagina esce dallo schermo

Poiché i due standard sorvegliano strati diversi, i file che ti mordono sono quelli che siedono tra loro. Un documento perfetto per l'archivio può essere muto per un lettore di schermo. Uno bellamente taggato può referenziare un font desktop che non esisterà tra dieci anni. La pubblicazione del settore pubblico è il solito posto in cui entrambi i requisiti atterranno in una volta, e una pipeline lì non può collassarli in un unico cancello. I riscontri vanno a persone diverse. I font non incorporati sono un difetto nel codice che genera il PDF, mentre il testo alternativo mancante appartiene a chiunque possieda i modelli di contenuto, e un rapporto che mescola i due viene solo inoltrato due volte

Quale parte di PDF/A prendi di mira conta quanto se la raggiungi. PDF/A-1 è congelato su PDF 1.4 e respinge trasparenza e JPEG2000, entrambe cose che l'output di report moderno cerca senza pensare. PDF/A-2 (ISO 19005-2, costruito su ISO 32000-1) accetta entrambi ed è il default sensato per un nuovo archivio. PDF/A-3 va oltre e permette file incorporati di qualsiasi tipo, che è ciò su cui i formati di fatturazione elettronica regolamentati fanno affidamento. Un team che ancora si standardizza su PDF/A-1b nel 2026 di solito porta un requisito che qualcuno ha scritto quindici anni fa, e rinnegociare la parte target è spesso più economico che eliminare la trasparenza da ogni grafico che il sistema emette

Diagramma PDF Library for Delphi che confronta il contratto di riproduzione PDF/A con il contratto di semantica PDF/UA più una matrice passa-fallisce di documenti che soddisfano l'uno e falliscono l'altro
PDF/A garantisce una resa fedele a distanza di decenni mentre PDF/UA garantisce la lettura assistita oggi, e nessun verdetto implica l'altro

Riscontri strutturati al momento dell'ingestione

Il punto di ingresso della flat-API è CheckFileCompliance, con il selettore di test 1 per PDF/A e 2 per PDF/UA. Restituisce un handle di lista di stringhe i cui elementi sono riscontri individuali, uno per riga, che è esattamente la forma che un cancello automatizzato vuole percorrere:

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 vuol dire "nessun rilievo" OPPURE "file illeggibile" -- disambigua prima di dare l'ok
    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;

Due dettagli decidono se questo gira incustodito. Il primo è un valore di ritorno che significa due cose opposte. CheckFileCompliance restituisce 0 quando il file è pienamente conforme e anche quando il file non poteva essere aperto affatto, perché internamente una lista di risultati vuota collassa a 0 in entrambi i casi. Un cancello che legge 0 come un via libera farà passare caricamenti corrotti dritti nell'archivio, quindi disambigua con LastErrorCode prima di fidarti dello zero, come fa il cancello sopra. Il secondo riguarda dove il file è nel suo ciclo di vita. Il controllore gira sul lettore in flusso della libreria invece che sul modello di documento completo, aprendo il file direttamente con condivisione di lettura e non chiamando mai LoadFromFile, che è il motivo per cui può masticare input multi-gigabyte senza costruire un albero di oggetti. Quella stessa apertura in flusso fallisce mentre un altro processo detiene ancora il file in scrittura, e un caricamento in corso è precisamente quello stato. Metti il cancello dopo che il trasferimento è finito

Il design in flusso paga di nuovo sotto carico. Ciascun controllo apre il suo input in sola lettura e lo condivide per la lettura, quindi un audit di corpus scala orizzontalmente tra thread o processi di lavoro con un'istanza TPDFlib per lavoratore e nessuna contesa tra loro. La risorsa che ha bisogno di disciplina è l'handle stesso. Ogni risultato diverso da zero da CheckFileCompliance resta allocato finché non chiami ReleaseStringList, e un cancello di lunga durata che dimentica di rilasciarli non va in crash, sanguina solo memoria lentamente finché qualcuno non va a cercare perché

Report per umani, diff per cancelli di build

Una lista di riscontri è la forma giusta per un cancello e la forma sbagliata per un'email al team dei modelli. CreatePreflightReport renderizza la stessa analisi come prosa leggibile, CreatePreflightReportEx aggiunge un selettore di formato di report, e SavePreflightReport la scrive su disco così il report può viaggiare dentro il pacchetto documento consegnato. Molti contratti di archiviazione fanno di quel report una consegna a sé stante, non solo un artefatto interno

Il membro di questa famiglia che si guadagna il posto in silenzio è ComparePreflightReports. La conformità è una superficie di regressione come qualsiasi altra parte di comportamento. Un ritocco al modello, un font aziendale appena concesso in licenza, o un aggiornamento della libreria possono ciascuno introdurre un riscontro che non c'era nel rilascio precedente, e nessuno di essi si annuncia. Mantieni report dorati per un insieme di documenti rappresentativi sotto controllo versione, rigenerali dopo ogni cambiamento, ed esegui ComparePreflightReports per calcolare il delta. Un diff vuoto è un artefatto di rilascio che vale la pena conservare. Un riscontro a sorpresa fa fallire la build, che è un posto molto più economico per scoprirlo dell'audit

Generare output che passa al primo giro

Il preflight guadagna il suo_keep su file che arrivano da altrove. Per documenti che il tuo codice produce, trovare violazioni dopo la generazione e rattopparle dentro è la via lenta. PDF Library for Delphi porta una modalità lato-generazione per ciascuno standard, e puoi accendere entrambe per lo stesso documento:

Diagramma PDF Library for Delphi dell'ingestione preflight dimensionata con un'istanza della libreria streaming per worker, aperture gate dopo il completamento degli upload, e liste di risultanze rilasciate
I varchi si aprono solo al termine dei trasferimenti, ogni worker fa transitare l'input in sola lettura attraverso un'istanza privata della libreria, e ogni handle restituito deve un rilascio
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: obbligatorio per PDF/UA
    // ... disegna qui il contenuto taggato ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // il preflight che conta gira sul file salvato:
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

La trappola si nasconde al momento del salvataggio. Diverse delle riparazioni di conformità avvengono mentre il documento viene serializzato invece che quando abiliti la modalità: forzare la bandiera di stampa sulle annotazioni, scrivere il AFRelationship di default per i file incorporati PDF/A-3, normalizzare l'ordine di tabulazione e le descrizioni dei campi modulo per PDF/UA. Il documento seduto in memoria non è byte-identico a quello che atterra su disco, quindi l'unico verdetto di preflight che significa qualcosa è quello calcolato dal file salvato. Convalida statement.pdf stesso. Non inferire la conformità dall'oggetto ancora in memoria, perché i byte che staresti giudicando non sono i byte che hai consegnato

Diagramma PDF Library for Delphi delle riparazioni di conformità al salvataggio applicate durante la serializzazione, per questo il preflight va eseguito sul file PDF salvato piuttosto che sul modello in memoria
La serializzazione forza i flag di stampa delle annotazioni, assegna un default ad AFRelationship e normalizza l'ordine di tabulazione, così l'ispezione della memoria giudica byte che nessuno spedisce mai

Gli scenari di fattura che portano un XML leggibile a macchina accanto al documento visivo seguono lo schema ZUGFeRD e Factur-X, che è costruito su PDF/A-3. Quelli dovrebbero impostare la relazione dell'allegato esplicitamente con SetPDFA3DefaultAFRelationship, poiché ISO 19005-3 richiede che ogni file incorporato dichiari il suo ruolo relativo al documento. Lascialo non impostato e l'XML incorporato è solo un blob senza scopo dichiarato, che il validatore nota

Arbitri indipendenti: veraPDF e Acrobat

Un produttore non dovrebbe essere l'unico giudice del proprio output. I controllori di PDF Library for Delphi ti danno verdetti veloci e strutturati in-process, che è ciò che vuoi sul percorso caldo, ma il cancello di rilascio per un batch di archiviazione dovrebbe ancora far passare l'output attraverso un validatore che nessuno nel tuo team ha scritto. veraPDF è l'implementazione di riferimento mantenuta dalla comunità per PDF/A e lo strumento che la maggior parte degli archivi nomina nei loro criteri di accettazione, quindi è quello da eguagliare. I profili di preflight di Acrobat fanno un utile arbitro quando veraPDF e il controllo in-process non sono d'accordo. Registra il nome del validatore e la sua versione accanto a ogni report conservato. Un'affermazione che un file ha superato veraPDF dice pochissimo senza il numero di build che lo ha superato, poiché lo strumento restringe le sue regole tra un rilascio e l'altro

I validator non sono d'accordo ai bordi degli standard, e quando accade la risposta non è scegliere lo strumento che ti piace. Riduci il file a un campione minimo che innesca ancora il disaccordo e leggilo contro il testo dello standard. Un'ora di quello di solito fa emergere una di due cose: un bug dello strumento genuino da segnalare a monte, o una clausola che il tuo team ha letto male e dovrebbe annotare negli appunti di conformità così che la prossima persona non la ridiscuta

L'input crittografato ottiene una scorciatoia. Entrambi i controllori prendono un argomento password, ma un file PDF/A-1 con un dizionario di crittografia è già non conforme, perché ISO 19005-1 vieta la crittografia del tutto, quindi un invio crittografato può essere respinto prima che qualsiasi analisi più profonda sia eseguita. Capire cosa un dizionario di crittografia concede davvero è un compito a sé, trattato in audit della crittografia e dei permessi PDF

I riscontri PDF/UA quasi sempre risalgono a come l'albero di struttura è stato redatto in primo luogo, e le tecniche di tagginazione dietro di esso vivono in costruire alberi di struttura PDF taggati in Delphi. Gli archivi che richiedono anche firme digitali dovrebbero accoppiare questo cancello con il flusso di lavoro in firma e validazione PAdES. Il riferimento completo dell'API di preflight vive sulla pagina del prodotto losLab PDF Library per Delphi