Articolo tecnico

Documenti tecnici PDF/E-1 in Delphi con PDFlibPas

PDF/E-1 è il profilo di archiviazione per i documenti tecnici, e PDFlibPas lo implementa come una modalità author da attivare con SetPDFEMode più un preflight limitato che legge i content stream operatore per operatore. Il profilo non è un PDF/A con etichetta diversa: ha un namespace di identificazione proprio, un requisito di metadati del ciclo di vita proprio, e una regola che rende la validazione del contenuto più severa di qualsiasi profilo di archiviazione tu abbia mai incontrato

I deliverable dell'ingegneria sono il motivo per cui il profilo esiste. Un corredo di tavole che dovrà essere leggibile e dimostrabilmente invariato tra vent'anni, con uno storico revisioni che sopravvive, e con un colore che significa la stessa cosa sul plotter nell'altro edificio. Quei requisiti producono una specifica le cui richieste stanno per lo più fuori dal contenuto di pagina, nei metadati e nella gestione del colore, che è esattamente dove un generico writer PDF le sbaglia

Un'identificazione propria, non una variante del PDF/A

La prima cosa da azzeccare è che l'identificazione PDF/E-1 non si produce adattando lo schema di PDF/A o PDF/X. Usa un namespace XMP distinto, http://www.aim.org/pdfe/ns/id/, e il valore di versione deve comparire in due posti: come voce nelle informazioni del documento e come proprietà XMP qualificata dal namespace. Emettere solo la proprietà XMP, o solo la voce nelle informazioni, produce un file che porta l'intenzione e fallisce la validazione

L'output intent ha una forma altrettanto specifica. PDF/E-1 richiede un profilo ICC incorporato con l'identificativo di sottotipo ISO_PDFE1, e il profilo deve avere un numero di componenti corrispondente alla famiglia di colori device che il documento usa davvero. È nell'ultima clausola che le implementazioni sbagliano in silenzio, perché significa che l'output intent non può essere scelto all'inizio e poi ignorato

Perché il colore device richiede una scansione dell'intero documento?

Perché i color space si nascondono nei dizionari risorse che una scansione a livello di pagina non raggiunge mai. PDF/E-1 tratta DeviceRGB e DeviceCMYK come famiglie mutuamente esclusive per un documento, quindi validare il profilo significa conoscere ogni color space device usato da qualsiasi cosa nel file. Un form XObject ha le proprie risorse. Anche un pattern, e anche un'immagine. Un tiling pattern dentro un form XObject dentro una pagina è a tre livelli di profondità, e un validatore che controlla solo le risorse di primo livello della pagina farà passare un documento che usa entrambe le famiglie

La scansione quindi registra i color space mentre percorre pagine, form, immagini e pattern come un'unica traversata, e solo dopo decide se il documento è coerente e se l'output intent corrisponde. Lo stesso ragionamento guida in generale l'architettura del preflight: una traversata parziale produce falsi passaggi, e un falso passaggio su un controllo di conformità è peggio di nessun controllo, perché viene registrato come prova

var
  Lib: TPDFlib;
  Diag: WideString;
begin
  Lib := TPDFlib.Create(nil);
  try
    Lib.LoadFromFile('assembly-drawings.pdf');

    if Lib.SetPDFEMode(1) = 0 then
      raise Exception.Create('PDF/E author mode was refused');

    // La modalità author tiene allineati i metadati del ciclo di vita a ogni salvataggio.
    // Chiedi prima di salvare se il documento passerebbe la propria barriera
    if not Lib.PDFEReadyForSave then
    begin
      Diag := Lib.GetPDFEDiagnostics;
      Writeln('PDF/E blockers: ', Diag);
      Exit;
    end;

    Lib.SaveToFile('assembly-drawings-pdfe.pdf');
  finally
    Lib.Free;
  end;
end;

I metadati del ciclo di vita sono un obbligo per ogni salvataggio

PDF/E-1 chiede più di un identificativo di documento. L'insieme minimo comprende l'identificativo documento di media management, un identificativo di versione, una rendition class, il tempo di creazione, il tempo di modifica, il tempo dei metadati e un titolo. È un vocabolario per il tracciamento delle revisioni, ed esiste perché da un deliverable di ingegneria ci si aspettano riemissioni, non una stesura unica

La conseguenza per un'implementazione è che questi campi non si possono impostare alla creazione del documento. Se il tempo di modifica viene scritto quando attivi la modalità e il documento viene poi modificato, lo snapshot XMP e lo stato effettivo del documento divergono, e un validatore che li confronta riporta un'incongruenza che nessuno voleva. La modalità author quindi sincronizza i campi immediatamente prima di ogni salvataggio, così i metadati descrivono i byte che stanno per essere scritti invece dei byte che esistevano quando la modalità è stata attivata

È un principio generale per i metadati di conformità e vale la pena enunciarlo separatamente dal PDF/E: i metadati derivati appartengono al percorso di salvataggio, non a quello di modifica. Qualsiasi campo calcolato dallo stato del documento va ricalcolato nel momento in cui lo stato viene congelato, altrimenti è una cache senza invalidazione

Diagramma PDF/E-1 di PDFlibPas della scansione del colore device sull'intero documento che percorre i dizionari risorse di pagina, form XObject, tiling pattern e immagine raccogliendo le famiglie DeviceRGB e DeviceCMYK prima di giudicare la coerenza, accanto ai campi dei metadati del ciclo di vita che la modalità author risincronizza immediatamente prima di ogni salvataggio affinché lo snapshot XMP corrisponda ai byte in procinto di essere scritti
La coerenza del colore si può giudicare solo dopo che una traversata ha raggiunto ogni dizionario risorse, e i metadati derivati del ciclo di vita vengono ricalcolati nel momento in cui lo stato del documento viene congelato, non quando si attiva la modalità

La regola che rende severa la validazione del contenuto

PDF/E-1 non permette agli operatori di sezione di compatibilità di assorbire contenuto sconosciuto. Nel PDF ordinario, BX e EX delimitano una regione in cui un consumer deve ignorare gli operatori che non riconosce, ed è la via di fuga che permette a un producer di emettere costrutti più recenti senza rompere i reader più vecchi. Sotto PDF/E-1 quella via di fuga è chiusa, quindi qualsiasi operatore che il preflight non riconosce viene riportato incondizionatamente, che stia dentro una sezione di compatibilità o no

L'effetto su un validatore è notevole. Non può saltare le regioni che non capisce, il che significa che il parser degli operandi deve davvero analizzare ogni operatore in ogni content stream. Ed è qui che entrano i limiti. La traversata è limitata a 128 livelli di annidamento, un milione di oggetti e 64 MiB di contenuto, e quei limiti non sono taratura delle prestazioni. Un file ostile o semplicemente corrotto può presentare un grafo di oggetti con cicli o una profondità di annidamento che trasforma un validatore ricorsivo in uno stack overflow, e sono i limiti a impedire che un passaggio di validazione diventi un vettore denial-of-service. La stessa postura difensiva è descritta in parsing sicuro di PDF non fidati

// Validazione standalone di un file che non hai prodotto, senza caricarlo
// in un'istanza di documento
var
  Issues: TStringList;
  Stream: TFileStream;
  I: Integer;
begin
  Issues := TStringList.Create;
  Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
  try
    if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
      for I := 0 to Issues.Count - 1 do
        Writeln('PDF/E: ', Issues[I]);
  finally
    Stream.Free;
    Issues.Free;
  end;
end;

Cosa ripara la barriera di salvataggio e cosa rifiuta

La barriera divide il lavoro in due stadi, e la divisione è di per sé un'idea di design riutilizzabile. Prima normalizza ciò che è riparabile in sicurezza: i flag di stampa delle annotazioni, i flag no-zoom e no-rotate delle annotazioni di testo, e il flag di generazione dell'aspetto sul dizionario del form. Sono impostazioni con un solo valore corretto sotto il profilo e nessun contenuto informativo, quindi correggerle in silenzio è giusto e rifiutare per esse sarebbe pedanteria

Poi controlla i vincoli che non si possono riparare senza cambiare ciò che il documento significa: versione, identificazione, cifratura, output intent, coerenza del colore device e presenza di contenuto di form dinamico. Un documento che ne fallisce anche uno solo viene rifiutato, perché inventare un output intent o scegliere una famiglia di colori per conto dell'autore produrrebbe un file che passa la validazione e falsa il contenuto

Diagramma della barriera di salvataggio PDF/E-1 di PDFlibPas per Delphi con il preflight limitato che scandisce ogni operatore dei content stream sotto limiti di annidamento a 128 livelli, un milione di oggetti e 64 MiB, ripara in silenzio i flag di stampa, zoom e rotazione delle annotazioni, rifiuta versione, identificazione, cifratura, output intent, colore device o contenuto di form dinamico errati, e riporta i bloccanti tramite GetPDFEDiagnostics
La barriera ripara in silenzio solo ciò che non porta informazione, rifiuta ogni vincolo che una riparazione distorcerebbe, e trasforma il rifiuto in una lista di bloccanti tramite GetPDFEDiagnostics prima che un solo byte arrivi su disco

Rileggere la diagnostica tramite GetPDFEDiagnostics prima di salvare trasforma quel rifiuto in una lista su cui agire invece che in un'operazione fallita. In una pipeline batch, chiamala su ogni documento, logga i bloccanti per file, e instrada i fallimenti verso una fila che una persona guarda. È molto più utile di un salvataggio che solleva eccezioni, perché i bloccanti di solito si aggregano: quaranta documenti che falliscono per lo stesso output intent mancante sono una correzione, non quaranta

Scegliere tra i profili di archiviazione

PDF/E-1 è il bersaglio giusto quando il deliverable è documentazione di ingegneria con un ciclo di vita a revisioni, e in particolare quando la coerenza del colore device conta perché l'output va a plotter e stampanti large-format. PDF/A è il bersaglio giusto quando l'obiettivo è la leggibilità a lungo termine dei documenti in generale, ed è il profilo con il supporto di validazione più ampio. I due non sono intercambiabili, e un documento può soddisfare l'uno e fallire l'altro

Diagramma decisionale di PDFlibPas che confronta i profili di archiviazione PDF/E-1 e PDF/A per Delphi: PDF/E-1 per i deliverable di ingegneria con ciclo di vita a revisioni, colore da plotter e validazione contrattuale sotto un namespace XMP proprio con output intent ISO_PDFE1, PDF/A per la leggibilità a lungo termine generale con il supporto di validazione più ampio
Parti da chi valida il file all'estremo ricevente: i profili esigono garanzie diverse di identificazione, metadati e colore, e un documento può soddisfarne uno fallendo l'altro

Se stai scegliendo, parti da chi valida il file all'estremo ricevente. Gli strumenti di validazione PDF/A sono ovunque, e il corrispondente preflight in PDFlibPas è descritto in preflight PDF/A e PDF/UA. La validazione PDF/E è più specializzata e di solito è un requisito contrattuale, non un default. Quando un archivio esistente va portato a un profilo per cui non è mai stato scritto, il percorso di riparazione metadati in conversione a PDF/A con riparazione dei metadati è lo schema da seguire, e qui vale la stessa forma: identifica, ripara ciò che è sicuro, rifiuta il resto con una lista

La modalità author, il preflight limitato del contenuto e la verifica di conformità standalone sono tutti inclusi nella PDFlibPas Delphi PDF library, così un documento può essere prodotto sotto il profilo e verificato in seguito in modo indipendente attraverso un percorso di codice separato, che è l'unica disposizione a cui valga la pena fidarsi per una dichiarazione di conformità