Technisch artikel

PDF/A-devicekleur wordt beoordeeld op wat de pagina tekent

veraPDF vlagt ISO 19005-1 6.2.3.3 op een bestand dat nooit RGB tekent, een kale re f ongecalibreerd noemt, of pagina 2 laat zakken terwijl een identieke pagina 1 slaagt. PDF/A beoordeelt die regels op wat elke pagina tekent, dus de PDF/A-validator van PDFium Component voor Delphi volgt elke pagina tot in forms, patterns, glyphs en annotaties voordat hij over kleur en transparantie oordeelt

Wat 6.2.3.3 en 6.2.4.3 werkelijk eisen

ISO 19005-1 6.2.3.3 propt drie onafhankelijke regels in één alinea. Een conform bestand mag DeviceRGB of DeviceCMYK gebruiken maar niet allebei; als een ongecalibreerde kleurruimte wordt gebruikt, moet het bestand een PDF/A-1 OutputIntent bevatten; en DeviceRGB is alleen toegestaan met een intent waarvan het profiel RGB is, DeviceCMYK alleen met een CMYK-variant. DeviceGray ontbreekt op die lijst omdat de clausule daarna uitlegt hoe grijs onder elk van beide intents wordt omgezet. De intent zelf is optioneel: 6.2.2 zegt dat een bestand er één mag opgeven, en telt alleen een OutputIntents-entry met /S /GTS_PDFA1 en een geldige ICC-profielstream in /DestOutputProfile mee. Een checker die van elk bestand een intent eist, wijst dus conforme documenten af die nooit devicekleur tekenen. Part 1 rept niet over DefaultRGB of DefaultCMYK, maar een kleur die erlangs is gemapt wordt gerenderd in een op CIE gebaseerde ruimte (ISO 32000-1 §8.6.5.6), wat voldoet aan de eis van deviceonafhankelijkheid uit 6.2.3.1; de veraPDF-testsuite voor 6.2.3.3 telt dat als gecalibreerd, en PDFium Component leest de clausule op dezelfde manier. Het mixverbod staat apart: een PDF/A-1-bestand dat zowel RGB als CMYK tekent zakt, ook wanneer Default-entries allebei calibreren. ISO 19005-2 6.2.4.3 schaft dat verbod af en calibreert elke ruimte op eigen kracht, zoals de rechterkolom hieronder laat zien, dus PDFium Component draait één scan per part en past de mixcontrole alleen op PDF/A-1-claims toe

Getekende device-ruimtePDF/A-1 (ISO 19005-1 6.2.3.3)PDF/A-2 en later (ISO 19005-2 6.2.4.3)
DeviceRGBDefaultRGB in scope of een RGB-intent; niet naast DeviceCMYKDefaultRGB in scope, of een RGB OutputIntent
DeviceCMYKDefaultCMYK in scope of een CMYK-intent; niet naast DeviceRGBDefaultCMYK in scope, of een CMYK OutputIntent
DeviceGrayDefaultGray in scope, of enige PDF/A-1 OutputIntentDefaultGray in scope, of enige PDF/A OutputIntent

Wat telt als gebruik van een kleurruimte?

Een kleurruimte gebruiken betekent ermee tekenen op een pad dat een reader zal renderen. Het declareren is niet genoeg. De veraPDF-suite maakt dat punt met een 6.2.3.3-slaagbestand: een benoemde Form XObject zet CMYK met K en k, zit in de resourcedictionary van de pagina, wordt nooit vanuit de paginacontent aangeroepen, en de OutputIntent van het bestand is RGB. Een zoektocht door het hele bestand op devicenamen voor kleur laat dat bestand zakken, en mist er één dat uitsluitend DeviceRGB tekent onder een CMYK-intent, waar geen twee namen conflicteren. PDFium Component loopt in plaats daarvan door de tekengraaf, gebouwd op zijn byte-level PDF/A-preflightscanner. Vanaf elke pagina telt de tocht kleur getekend door:

  • de contentstreams van de pagina, en elke Form XObject die ze met Do aanroepen, geneste forms inbegrepen
  • een pattern geselecteerd met scn of SCN, gelezen met de eigen Resources van het pattern
  • de CharProcs van een Type 3-font geselecteerd met Tf
  • image XObjects en inline afbeeldingen, naar de kleurruimte die hun eigen dictionary noemt
  • appearance streams van annotaties, bovenop de pagina getekend; wanneer de /AP /N van een widget een state-dictionary is, telt elke state mee, want het omschakelen van het veld tekent degene die /AS niet selecteerde

De subtielere helft van bereikbaarheid is kleur die de content nooit benoemt. q 0 0 10 10 re f Q bevat geen kleuroperator. Het tekent toch. De initiële kleurruimte van de graphics state is DeviceGray (ISO 32000-1 Table 52), dus de vulling is DeviceGray-zwart, onder elk part ongecalibreerd tenzij er een PDF/A OutputIntent of DefaultGray bestaat. De scan houdt daarom bij of er al iets een kleur heeft gekozen: rg, k, g en hun stroking-vormen selecteren een device-ruimte, cs en CS selecteren wat ze benoemen, en een fill-, stroke- of tekendeoperator die vóór elke keuze wordt bereikt noteert DeviceGray. /CS0 cs 1 0 0 /P0 scn heeft al een ruimte gekozen, dus die scn is niet grijs

Het bereikbare tekelpad dat PDFium Component in Delphi doorloopt voordat het over PDF/A-devicekleur oordeelt: forms, patterns, Type 3-glyphs, afbeeldingen en annotatie-appearance streams
een kleurruimte declareren is niet genoeg — de tocht telt wat elke pagina tekent, inclusief het devicegrijs dat een kale re f uit de initiële staat meepakt

Inline afbeeldingen brengen hun kleurruimte en hun ruwe samples mee in de contentstream zelf, en een scanner moet allebei zorgvuldig lezen. De /CS kan een afkorting zijn (G, RGB, CMYK, I) of een resourcenaam zoals één die uitkomt op [/Indexed /DeviceRGB 255 13 0 R], waarvan de basis meetelt; en de bytes tussen ID en EI zijn willekeurige binaire data, dus EI beëindigt de afbeelding alleen tussen witruimte, anders wordt een rg in gecomprimeerde samples een spookoperator

% pagina 1: DefaultRGB in scope, en een XObject dat niets aanroept
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: dezelfde contentstream, geen ColorSpace-subdictionary
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

Elk symptoom uit de opening valt uit dit fragment te verklaren. /Fm0 ontmoet nooit een Do, dus zijn k is irrelevant. De kale re f op beide pagina's is DeviceGray zonder iets dat hem calibreert. De rg op pagina 1 wordt gedekt door de DefaultRGB in de resources waarmee pagina 1 tekent; pagina 2 draait dezelfde stream tegen resources zonder die mapping, dus dezelfde bytes zijn daar ongecalibreerd. Verwacht bij een PDF/A-1b-claim: pvaiMissingOutputIntent met pvaiMissingIccProfile, aangedreven door de RGB van pagina 2 en het grijs van beide pagina's. De form levert niets; aangeroepen zou zijn CMYK bovenop het PDF/A-1-mixverbod stapelen

Default-kleurruimten horen bij de resourcedictionary die tekent

De Default-mappings worden opgezocht in het ColorSpace-subdictionary van de huidige resourcedictionary (ISO 32000-1 §8.6.5.6), wat ze precies zo lokaal maakt als de resources zelf; een document heeft geen globale default. Een image XObject heeft geen eigen Resources, dus een afbeelding waarvan het dictionary /ColorSpace /DeviceCMYK zegt wordt gecalibreerd door de DefaultCMYK van de pagina of form die hem toevallig tekent, en dezelfde afbeelding vanaf twee plekken getekend kan op de één gecalibreerd zijn en op de ander niet. Form XObjects, tiling patterns en Type 3-glyphprocedures dragen hun eigen Resources, dus de DefaultCMYK van een pagina reikt er niet naar binnen, terwijl een mapping in hun eigen dictionary dat wel doet. PDFium Component scherpt de mappings op dezelfde manier af. Het oordeel komt terug als record TPdfAValidationResult: de gedetecteerde Conformance plus een set Issues. Dat is genoeg om een map te triageren

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);   // leest vanaf positie 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 en pvaiMissingIccProfile verschijnen samen wanneer de tocht ongecalibreerde devicekleur bereikt en er geen PDF/A OutputIntent bestaat. pvaiMissingIccProfile op zichzelf betekent dat er een intent is maar zonder profiel, wat 6.2.2 niet als PDF/A-intent accepteert. pvaiMixedDeviceColorSpaces dekt zowel het PDF/A-1-mixverbod als een componentmismatch: ongecalibreerde DeviceRGB onder een intent met vier componenten, of DeviceCMYK onder een met drie. De set benoemt de regel in plaats van de pagina, dus in een lang of samengevoegd document vergelijkt u welke pagina's Default-entries opgeven en welke niet

Eén DeviceCMYK-afbeelding getekend vanuit twee resourcedictionaries in Delphi: gecalibreerd door DefaultCMYK op de ene pagina en ongecalibreerd op een andere, zoals PDFium Component rapporteert
default-kleurruimten horen bij de resourcedictionary die tekent, dus dezelfde afbeelding kan gecalibreerd zijn waar de ene pagina haar tekent en ongecalibreerd waar een form dat doet

Wanneer heeft een transparante pagina zijn eigen blending-ruimte nodig?

Een transparante pagina heeft alleen dan een eigen blending-kleurruimte nodig wanneer het document geen PDF/A OutputIntent heeft. ISO 19005-2 6.2.10 (6.2.9 in PDF/A-4) maakt de intent de default blending-kleurruimte in de zin van ISO 32000-1 §11.3.4; zonder intent moet elke pagina die transparantie bevat een Group dragen waarvan het attributen-dictionary (ISO 32000-1 Table 147) een CS bevat. Die CS wordt ook afgerekend op 6.2.4, dus een paginagroep met /DeviceCMYK onder een sRGB-intent wordt gemeld alsof hij ontbreekt, en PDFium Component geeft pvaiBlendSpaceUndefined voor allebei; CalRGB of een ICCBased-groepsruimte heeft die afhankelijkheid niet. PDF/A-1 komt bij deze vraag niet, omdat ISO 19005-1 6.4 transparantie volledig uitsluit

Het lastige is de formulering bevat transparantie. Bijlage A van ISO 19005-2 is normatief en toetst vanuit de pagina naar buiten; een telling door het hele bestand van /SMask-sleutels of /ca-waarden beantwoordt een andere vraag. Zoektochten op bytes botsen ook elders op transparantiesyntax: /S /Trans, de verboden transitie-actie, gaat aan elke /S /Transparency-groep vooraf, en /Screen is zowel een verboden annotatiesubtype als een standaard blend mode, dus een naam zegt niets totdat hij met zijn sleutel is gepaard. Volgend Bijlage A behandelt PDFium Component een pagina als transparant wanneer een van deze ervan bereikt wordt:

  • een ExtGState die de content met gs selecteert en die een soft mask-dictionary heeft, een ca of CA onder 1, of een blend mode anders dan Normal; één die alleen gedeclareerd en nooit geselecteerd wordt telt niet mee
  • een Form XObject aangeroepen met Do waarvan /Group op /S /Transparency staat, of waarvan de eigen content aan een van deze voorwaarden voldoet; een tiling pattern wordt als form beoordeeld
  • een image XObject met een SMask-stream of een SMaskInData-waarde boven 0
  • een glyph-beschrijving van een Type 3-font waarmee de pagina tekst toont, beoordeeld als een form
  • een annotatie-appearancestream, elke widgetstate inbegrepen, en een blend mode op het annotatiedictionary zelf

Groepen voeden ook terug in de deviceregel voor kleur, en hier corrigeerde het corpus ons. De veraPDF-suite voor 6.2.4.3 bevat PDF/A-4-bestanden zonder enige OutputIntent die device-RGB tekenen en naar verwachting slagen, omdat de /Group van elke pagina een gecalibreerde CS benoemt. Wij gingen ervan uit dat alleen geïsoleerde groepen konden calibreren, omdat een niet-geïsoleerde groep in zijn achtergrond componeert, en dat was fout voor paginagroepen: Table 147 merkt op dat een pagina die direct op het uitvoermedium wordt afgedrukt effectief geïsoleerd is, ongeacht I, dus zijn CS wordt gerespecteerd. Op een Form XObject of annotatie-appearance beslist de vlag wel; de suite heeft een slaag- en een zakbestand die alleen in /I true verschillen. DeviceGray haalt geen calibratie uit een groep, want 6.2.4.3 vraagt om DefaultGray of een intent, en een groep waarvan de CS een device-ruimte is moet zelf gecalibreerd worden. PDFium Component laat daarom een paginagroep calibreren wat zijn /I ook zegt, laat een form- of appearancegroep alleen calibreren wanneer geïsoleerd, houdt grijs buiten groepscalibratie en toetst toch een device-ruimte die een groep benoemt

PDF/A-matrix voor Delphi: wanneer een transparante pagina zijn eigen blending-CS nodig heeft en hoe PDFium Component de calibratie van paginagroepen, formgroepen en appearancegroepen beoordeelt
een output intent is de default blending-ruimte, en een paginagroep calibreert devicekleur wat zijn isolation-vlag ook zegt terwijl een formgroep /I true nodig heeft

Eén OutputIntent in plaats van een groep op elke pagina

Schrijft uw eigen code het archief, begin dan met een OutputIntent op documentniveau. Eén catalogentry calibreert DeviceGray overal, dekt de overeenkomende device-ruimte op elke pagina en is de default blending-ruimte voor elke transparante pagina. Paginagroepen en Default-entries gelden elk voor één resourcedictionary, en de pagina die een latere merge zonder hen toevoegt is degene die zakt. PDF 2.0 voegt OutputIntents toe aan het paginaobject (ISO 32000-2 §7.7.3.3 en §14.11.5); PDFium Component accepteert die intent op paginaniveau voor PDF/A-4 wanneer de catalog geen heeft, terwijl parts 1 tot 3, gebouwd op PDF 1.4 tot 1.7, alleen naar de catalog kijken. Voor RGB-content voegt SaveAsPdfAToStream een catalogintent toe gebouwd op het ingebedde sRGB ICC-profiel van de PDF/A-writer, en het valideren van de uitvoer laat zien of de content werkelijk alleen RGB was

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;                           // het bestand kon niet worden geopend
    Opts := TPdfASaveOptions.Default;
    Opts.Conformance := pac2b;        // PDF/A-1 zou de transparantie ronduit verbieden
    // IccProfileData blijft leeg: het ingebouwde sRGB IEC61966-2.1-profiel wordt gebruikt
    if not Pdf.SaveAsPdfAToStream(Output, Opts) then
      Exit;
    Check := ValidatePdfACompliance(Output);
    Remaining := Check.Issues;
    // Verwacht bij content met alleen RGB: pvaiMissingOutputIntent en
    // pvaiBlendSpaceUndefined zijn verdwenen. pvaiMixedDeviceColorSpaces betekent
    // nu dat een bereikbaar pad DeviceCMYK tekent dat geen DefaultCMYK calibreert.
    if Check.IsCompliant then
    begin
      Output.SaveToFile(OutFile);
      Result := True;
    end;
  finally
    Output.Free;
    Pdf.Free;
  end;
end;

Een pvaiMixedDeviceColorSpaces die de sRGB-intent overleeft is de tocht die DeviceCMYK meldt op een bereikbaar pad, en dat document heeft een DefaultCMYK-mapping nodig op de pagina's die hem tekenen, of een CMYK-outputprofiel, voordat PDFium Component hem als PDF/A-2b accepteert