Articolo tecnico

Il colore device PDF/A si giudica da cosa dipinge la pagina

veraPDF segnala ISO 19005-1 6.2.3.3 su un file che non disegna mai RGB, chiama non calibrato un re f nudo, o boccia la pagina 2 mentre una pagina 1 identica passa. Il PDF/A giudica quelle regole da ciò che ogni pagina dipinge, così il validatore PDF/A di PDFium Component per Delphi traccia ogni pagina dentro form, pattern, glifi e annotazioni prima di pronunciarsi su colore e trasparenza

Che cosa richiedono davvero 6.2.3.3 e 6.2.4.3

ISO 19005-1 6.2.3.3 impacchetta tre regole indipendenti in un solo paragrafo. Un file conforme può usare DeviceRGB o DeviceCMYK ma non deve usare entrambi; se viene usato qualsiasi spazio di colore non calibrato, il file deve contenere un OutputIntent PDF/A-1; e DeviceRGB è permesso solo con un intent il cui profilo è RGB, DeviceCMYK solo con uno CMYK. DeviceGray resta fuori da quella lista perché la clausola prosegue spiegando la conversione del grigio sotto entrambi gli intent. L'intent in sé è opzionale: 6.2.2 dice che un file può specificarne uno, e conta solo una voce OutputIntents con /S /GTS_PDFA1 e uno stream di profilo ICC valido in /DestOutputProfile. Così un controllo che esige un intent da ogni file boccia documenti conformi che non dipingono mai colore device. La Parte 1 non menziona DefaultRGB o DefaultCMYK, eppure un colore passato attraverso di loro viene disegnato in uno spazio basato su CIE (ISO 32000-1 §8.6.5.6), il che soddisfa la richiesta di indipendenza dal device di 6.2.3.1; la suite di test veraPDF per 6.2.3.3 lo tratta come calibrato, e PDFium Component legge la clausola allo stesso modo. Il divieto di miscelazione sta a parte: un file PDF/A-1 che dipinge sia RGB sia CMYK fallisce anche quando voci Default calibrano entrambi. ISO 19005-2 6.2.4.3 lascia cadere quel divieto e calibra ogni spazio per conto suo, come mostra la colonna di destra qui sotto, così PDFium Component esegue una scansione per ogni parte e applica il controllo di miscelazione alle sole dichiarazioni PDF/A-1

Spazio device dipintoPDF/A-1 (ISO 19005-1 6.2.3.3)PDF/A-2 e successivi (ISO 19005-2 6.2.4.3)
DeviceRGBDefaultRGB in ambito o un intent RGB; non insieme a DeviceCMYKDefaultRGB in ambito, o un OutputIntent RGB
DeviceCMYKDefaultCMYK in ambito o un intent CMYK; non insieme a DeviceRGBDefaultCMYK in ambito, o un OutputIntent CMYK
DeviceGrayDefaultGray in ambito, o qualsiasi OutputIntent PDF/A-1DefaultGray in ambito, o qualsiasi OutputIntent PDF/A

Che cosa conta come usare uno spazio di colore?

Usare uno spazio di colore significa dipingere con esso su un percorso che un lettore disegnerà. Dichiararlo non basta. La suite veraPDF lo dimostra con un file di pass per 6.2.3.3: un Form XObject nominato imposta CMYK con K e k, sta nel dizionario delle risorse della pagina, non viene mai referenziato dal contenuto di pagina, e l'OutputIntent del file è RGB. Una ricerca su tutto il file dei nomi di colore device boccia quel file, e non vede uno che dipinge solo DeviceRGB sotto un intent CMYK, dove nessun paio di nomi va in conflitto. PDFium Component attraversa invece il grafo di disegno, costruito sul suo scanner di preflight PDF/A a livello di byte. Da ogni pagina, la scansione conta il colore dipinto da:

  • gli stream di contenuto di pagina, e ogni Form XObject che invocano con Do, form annidate incluse
  • un pattern selezionato con scn o SCN, letto con le Resources proprie del pattern
  • i CharProcs di un font Type 3 selezionato con Tf
  • image XObject e immagini inline, per lo spazio di colore che il loro dizionario nomina
  • gli appearance stream delle annotazioni, dipinti sopra la pagina; quando /AP /N di un widget è un dizionario di stati, ogni stato conta, perché commutare il campo dipinge quello che /AS non aveva selezionato

La metà più sottile della raggiungibilità è il colore che il contenuto non nomina mai. q 0 0 10 10 re f Q non contiene alcun operatore di colore. Dipinge comunque. Lo spazio di colore iniziale dello stato grafico è DeviceGray (ISO 32000-1 Table 52), così il riempimento è nero DeviceGray, non calibrato sotto ogni parte a meno che esista un OutputIntent PDF/A o un DefaultGray. La scansione quindi tiene traccia di se qualcosa abbia già scelto un colore: rg, k, g e le loro forme di stroke selezionano uno spazio device, cs e CS selezionano ciò che nominano, e un operatore di riempimento, stroke o testo raggiunto prima di qualsiasi scelta registra DeviceGray. /CS0 cs 1 0 0 /P0 scn ha già scelto uno spazio, così quello scn non è grigio

Il percorso di pittura raggiungibile che PDFium Component attraversa in Delphi prima di pronunciarsi sul colore device PDF/A: form, pattern, glifi Type 3, immagini e appearance stream delle annotazioni
dichiarare uno spazio di colore non basta — la scansione conta ciò che ogni pagina dipinge, incluso il grigio device che un re f nudo raccoglie dallo stato iniziale

Le immagini inline portano il loro spazio di colore e i loro campioni grezzi dentro lo stream di contenuto stesso, e uno scanner deve leggere entrambi con attenzione. La /CS può essere un'abbreviazione (G, RGB, CMYK, I) o un nome di risorsa come uno che risolve a [/Indexed /DeviceRGB 255 13 0 R], la cui base conta; e i byte tra ID ed EI sono binari arbitrari, così EI chiude l'immagine solo tra spazi bianchi, oppure un rg dentro campioni compressi diventa un operatore fantasma

% pagina 1: DefaultRGB in ambito, e un XObject che nessuno invoca
4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R
           /Resources << /ColorSpace << /DefaultRGB 9 0 R >>
                         /XObject << /Fm0 6 0 R >> >> >>
endobj
% pagina 2: lo stesso content stream, nessun sottodizionario ColorSpace
7 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /Resources << >> >>
endobj
5 0 obj << /Length 46 >> stream
1 0 0 rg 72 72 200 100 re f
q 0 0 10 10 re f Q
endstream
endobj
6 0 obj << /Type /XObject /Subtype /Form /BBox [0 0 50 50] /Length 24 >> stream
0 0 0 1 k 0 0 50 50 re f
endstream
endobj
9 0 obj [/ICCBased 10 0 R]
endobj

Ogni sintomo dell'apertura cade fuori da questo frammento. /Fm0 non incontra mai un Do, così il suo k è irrilevante. Il re f nudo su entrambe le pagine è DeviceGray senza nulla che lo calibri. Il rg della pagina 1 è coperto dal DefaultRGB nelle risorse con cui la pagina 1 dipinge; la pagina 2 esegue lo stream identico contro risorse che non hanno la mappatura, così gli stessi byte sono lì non calibrati. Atteso per una dichiarazione PDF/A-1b: pvaiMissingOutputIntent con pvaiMissingIccProfile, guidati dal RGB della pagina 2 e dal grigio di entrambe le pagine. La form non contribuisce nulla; invocata, il suo CMYK aggiungerebbe sopra il divieto di miscelazione PDF/A-1

Gli spazi di colore Default appartengono al dizionario delle risorse che dipinge

Le mappature Default vengono cercate nel sottodizionario ColorSpace del dizionario delle risorse corrente (ISO 32000-1 §8.6.5.6), il che le rende esattamente locali quanto le risorse stesse; un documento non ha alcun default globale. Un image XObject non ha Resources proprie, così un'immagine il cui dizionario dice /ColorSpace /DeviceCMYK viene calibrata dal DefaultCMYK della pagina o della form che la disegna, e la stessa immagine disegnata da due posti può essere calibrata in uno e nell'altro no. Form XObject, pattern di tiling e procedure di glifi Type 3 portano le proprie Resources, così il DefaultCMYK di una pagina non arriva dentro di loro, mentre una mappatura nel loro dizionario sì. PDFium Component delimita le mappature allo stesso modo. Il verdetto torna come record TPdfAValidationResult: la Conformance rilevata più un insieme Issues. Basta per fare triage su una cartella

uses
  System.SysUtils, System.Classes, System.TypInfo, FPdfPdfa;

procedure ReportColorFindings(const FileName: string; Log: TStrings);
const
  ColorRules: TPdfAValidationIssues = [pvaiMissingOutputIntent,
    pvaiMissingIccProfile, pvaiMixedDeviceColorSpaces, pvaiBlendSpaceUndefined];
var
  Source: TFileStream;
  Res: TPdfAValidationResult;
  Found: TPdfAValidationIssues;
  Issue: TPdfAValidationIssue;
begin
  Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfACompliance(Source);   // legge dalla posizione 0
  finally
    Source.Free;
  end;
  Log.Add(ExtractFileName(FileName) + ': ' +
    GetEnumName(TypeInfo(TPdfAConformance), Ord(Res.Conformance)));
  Found := Res.Issues * ColorRules;
  for Issue in Found do
    case Issue of
      pvaiMissingOutputIntent:
        Log.Add('  reachable uncalibrated device color, no PDF/A OutputIntent');
      pvaiMissingIccProfile:
        Log.Add('  no OutputIntent with a DestOutputProfile ICC stream');
      pvaiMixedDeviceColorSpaces:
        Log.Add('  RGB/CMYK use disagrees with the intent, or PDF/A-1 paints both');
      pvaiBlendSpaceUndefined:
        Log.Add('  transparent page without a blending color space');
    end;
  if Found = [] then
    Log.Add('  device color and blending rules satisfied');
end;

pvaiMissingOutputIntent e pvaiMissingIccProfile appaiono insieme quando la scansione raggiunge colore device non calibrato e non esiste alcun OutputIntent PDF/A. pvaiMissingIccProfile da solo significa che un intent esiste ma non porta profilo, cosa che 6.2.2 non accetta come intent PDF/A. pvaiMixedDeviceColorSpaces copre sia il divieto di miscelazione PDF/A-1 sia una discrepanza di componenti: DeviceRGB non calibrato sotto un intent a quattro componenti, o DeviceCMYK sotto uno a tre componenti. L'insieme nomina la regola e non la pagina, così in un documento lungo o unito confrontate quali pagine dichiarano voci Default e quali no

Una immagine DeviceCMYK disegnata da due dizionari di risorse in Delphi: calibrata da DefaultCMYK su una pagina e non calibrata su un'altra, come riferisce PDFium Component
gli spazi di colore Default appartengono al dizionario delle risorse che dipinge, così la stessa immagine può essere calibrata dove la disegna una pagina e non calibrata dove lo fa una form

Quando una pagina trasparente ha bisogno del suo spazio di fusione?

Una pagina trasparente ha bisogno del suo spazio di colore di fusione solo quando il documento non ha un OutputIntent PDF/A. ISO 19005-2 6.2.10 (6.2.9 in PDF/A-4) fa dell'intent lo spazio di colore di fusione predefinito nel senso di ISO 32000-1 §11.3.4; senza intent, ogni pagina che contiene trasparenza deve portare un Group il cui dizionario di attributi (ISO 32000-1 Table 147) includa CS. Quella CS è vincolata anche a 6.2.4, così un gruppo di pagina /DeviceCMYK sotto un intent sRGB viene riferito come uno mancante, e PDFium Component solleva pvaiBlendSpaceUndefined per entrambi; CalRGB o uno spazio di gruppo ICCBased non hanno questa dipendenza. Il PDF/A-1 non arriva a questa domanda, perché ISO 19005-1 6.4 esclude del tutto la trasparenza

La parte difficile è la frase contiene trasparenza. L'Allegato A di ISO 19005-2 è normativo e prova dalla pagina verso fuori; un conteggio su tutto il file delle chiavi /SMask o dei valori /ca risponde a un'altra domanda. Le ricerche di byte collidono anche con la sintassi di trasparenza altrove: /S /Trans, l'azione di transizione vietata, fa da prefisso a ogni gruppo /S /Transparency, e /Screen è sia un sottotipo di annotazione vietato sia una modalità di fusione standard, così un nome non significa nulla finché non è abbinato alla sua chiave. Seguendo l'Allegato A, PDFium Component tratta una pagina come trasparente quando una di queste viene raggiunta da essa:

  • un ExtGState che il contenuto seleziona con gs che ha un dizionario di soft mask, una ca o CA sotto 1, o una modalità di fusione diversa da Normal; uno dichiarato ma mai selezionato non conta
  • un Form XObject invocato con Do il cui /Group ha /S /Transparency, o il cui contenuto proprio soddisfa una di queste condizioni; un pattern di tiling viene giudicato come una form
  • un image XObject con uno stream SMask o un valore SMaskInData sopra 0
  • una descrizione di glifo di un font Type 3 con cui la pagina mostra testo, giudicata come una form
  • un appearance stream di annotazione, ogni stato di widget incluso, e una modalità di fusione sul dizionario di annotazione stesso

I gruppi rientrano anche nella regola del colore device, e qui il corpus ci ha corretti. La suite veraPDF per 6.2.4.3 contiene file PDF/A-4 senza alcun OutputIntent che dipingono RGB device e sono attesi in pass, perché il /Group di ogni pagina nomina una CS calibrata. Assumevamo che solo i gruppi isolati potessero calibrare, dato che un gruppo non isolato si compone nel suo sfondo, e per i gruppi di pagina era sbagliato: Table 147 nota che una pagina imposta direttamente sul supporto di output è di fatto isolata a prescindere da I, così la sua CS viene rispettata. Su un appearance di Form XObject o di annotazione il flag lo decide davvero; la suite ha un file di pass e uno di fail che differiscono solo per /I true. DeviceGray non prende alcuna calibrazione da un gruppo, perché 6.2.4.3 chiede DefaultGray o un intent, e un gruppo la cui CS è uno spazio device ha bisogno di essere calibrato a sua volta. PDFium Component quindi lascia che un gruppo di pagina calibri a prescindere da ciò che dice il suo /I, lascia che un gruppo di form o di appearance calibri solo quando isolato, tiene il grigio fuori dalla calibrazione dei gruppi, e controlla comunque uno spazio device che un gruppo nomina

Matrice PDF/A per Delphi: quando una pagina trasparente ha bisogno della sua CS di fusione e come PDFium Component giudica la calibrazione dei gruppi di pagina, form e appearance
un output intent è lo spazio di fusione predefinito, e un gruppo di pagina calibra il colore device a prescindere da ciò che dice il suo flag di isolamento mentre un gruppo di form ha bisogno di /I true

Un OutputIntent invece di un gruppo su ogni pagina

Se il vostro codice scrive l'archivio, partite con un OutputIntent PDF/A a livello di documento. Una voce di catalogo calibra DeviceGray ovunque, copre lo spazio device corrispondente su ogni pagina, ed è lo spazio di fusione predefinito per ogni pagina trasparente. I gruppi di pagina e le voci Default hanno l'ambito di un dizionario di risorse ciascuno, e la pagina che una fusione successiva aggiunge senza di loro è quella che fallisce. PDF 2.0 aggiunge OutputIntents all'oggetto pagina (ISO 32000-2 §7.7.3.3 e §14.11.5); PDFium Component accetta quell'intent a livello di pagina per PDF/A-4 quando il catalogo non ne ha, mentre le parti 1 a 3, costruite su PDF 1.4 a 1.7, guardano solo il catalogo. Per contenuto RGB, SaveAsPdfAToStream aggiunge un intent di catalogo costruito sul profilo ICC sRGB incorporato dello scrittore PDF/A, e validare l'output mostra se il contenuto era davvero solo RGB

uses
  System.SysUtils, System.Classes, PDFium, FPdfPdfa;

function ArchiveWithSrgbIntent(const InFile, OutFile: string;
  out Remaining: TPdfAValidationIssues): Boolean;
var
  Pdf: TPdf;
  Opts: TPdfASaveOptions;
  Output: TMemoryStream;
  Check: TPdfAValidationResult;
begin
  Result := False;
  Remaining := [];
  Pdf := TPdf.Create(nil);
  Output := TMemoryStream.Create;
  try
    Pdf.FileName := InFile;
    Pdf.Active := True;
    if not Pdf.Active then
      Exit;                           // il file non poteva essere aperto
    Opts := TPdfASaveOptions.Default;
    Opts.Conformance := pac2b;        // PDF/A-1 vieterebbe la trasparenza del tutto
    // IccProfileData resta vuoto: viene usato il profilo sRGB IEC61966-2.1 interno
    if not Pdf.SaveAsPdfAToStream(Output, Opts) then
      Exit;
    Check := ValidatePdfACompliance(Output);
    Remaining := Check.Issues;
    // Atteso per contenuto solo RGB: pvaiMissingOutputIntent e
    // pvaiBlendSpaceUndefined sono spariti. pvaiMixedDeviceColorSpaces ora indica
    // che un percorso raggiungibile dipinge DeviceCMYK che nessun DefaultCMYK calibra.
    if Check.IsCompliant then
    begin
      Output.SaveToFile(OutFile);
      Result := True;
    end;
  finally
    Output.Free;
    Pdf.Free;
  end;
end;

Una pvaiMixedDeviceColorSpaces che sopravvive all'intent sRGB è la scansione che riferisce DeviceCMYK su un percorso raggiungibile, e quel documento ha bisogno di una mappatura DefaultCMYK sulle pagine che lo dipingono, o di un profilo di output CMYK, prima che PDFium Component lo accetti come PDF/A-2b