veraPDF markiert ISO 19005-1 6.2.3.3 an einer Datei, die nie RGB zeichnet, stuft ein nacktes re f als unkalibriert ein oder lässt Seite 2 scheitern, während eine identische Seite 1 besteht. PDF/A beurteilt diese Regeln danach, was jede Seite malt, also verfolgt der PDFium Component PDF/A-Validator für Delphi jede Seite bis in Forms, Patterns, Glyphen und Annotationen, bevor er über Farbe und Transparenz urteilt
Was 6.2.3.3 und 6.2.4.3 tatsächlich verlangen
ISO 19005-1 6.2.3.3 packt drei unabhängige Regeln in einen Absatz. Eine konforme Datei darf DeviceRGB oder DeviceCMYK benutzen, aber nicht beide; wird irgendein unkalibrierter Farbraum benutzt, soll die Datei einen PDF/A-1-OutputIntent enthalten; und DeviceRGB ist nur mit einem Intent erlaubt, dessen Profil RGB ist, DeviceCMYK nur mit einem CMYK-Intent. DeviceGray fehlt in dieser Liste, weil die Klausel gleich weiter erklärt, wie Grau unter beiden Intents konvertiert wird. Der Intent selbst ist optional: 6.2.2 sagt, eine Datei darf einen angeben, und zählt nur einen OutputIntents-Eintrag mit /S /GTS_PDFA1 und einem gültigen ICC-Profil-Stream in /DestOutputProfile. Ein Checker, der von jeder Datei einen Intent verlangt, lehnt also konforme Dokumente ab, die nie Gerätefarbe malen. Part 1 erwähnt DefaultRGB oder DefaultCMYK nicht, doch eine Farbe, die über sie umgemappt wird, wird in einem CIE-basierten Raum gerendert (ISO 32000-1 §8.6.5.6), was die Geräteunabhängigkeits-Forderung von 6.2.3.1 erfüllt; die veraPDF-Testsuite für 6.2.3.3 behandelt das als kalibriert, und PDFium Component liest die Klausel genauso. Das Mischverbot steht separat: eine PDF/A-1-Datei, die sowohl RGB als auch CMYK malt, scheitert, selbst wenn Default-Einträge beide kalibrieren. ISO 19005-2 6.2.4.3 fällt dieses Verbot und kalibriert jeden Raum zu seinen eigenen Bedingungen, wie die rechte Spalte unten zeigt, also führt PDFium Component einen Scan pro Teil aus und wendet die Mischprüfung allein auf PDF/A-1-Claims an
| Gemalter Device-Space | PDF/A-1 (ISO 19005-1 6.2.3.3) | PDF/A-2 und später (ISO 19005-2 6.2.4.3) |
|---|---|---|
| DeviceRGB | DefaultRGB im Scope oder ein RGB-Intent; nicht neben DeviceCMYK | DefaultRGB im Scope oder ein RGB-OutputIntent |
| DeviceCMYK | DefaultCMYK im Scope oder ein CMYK-Intent; nicht neben DeviceRGB | DefaultCMYK im Scope oder ein CMYK-OutputIntent |
| DeviceGray | DefaultGray im Scope oder irgendein PDF/A-1-OutputIntent | DefaultGray im Scope oder irgendein PDF/A-OutputIntent |
Was zählt als Benutzen eines Farbraums?
Einen Farbraum zu benutzen heißt, mit ihm auf einem Pfad zu malen, den ein Reader rendert. Ihn zu deklarieren reicht nicht. Die veraPDF-Suite macht den Punkt an einer 6.2.3.3-Bestehens-Datei fest: ein benanntes Form XObject setzt CMYK mit K und k, sitzt im Ressourcen-Dictionary der Seite, wird nie vom Seiteninhalt referenziert, und der OutputIntent der Datei ist RGB. Eine dateiweite Suche nach Gerätefarb-Namen lässt diese Datei scheitern und verpasst eine, die nur DeviceRGB unter einem CMYK-Intent malt, wo keine zwei Namen kollidieren. PDFium Component geht stattdessen den Zeichnungsgraphen entlang, gebaut auf ihrem Byte-Level-PDF/A-Preflight-Scanner. Von jeder Seite aus zählt der Gang Farbe, gemalt von:
- den Seiten-Content-Streams und jedem Form XObject, das sie mit
Doaufrufen, verschachtelte Forms eingeschlossen - einem Pattern, gewählt mit
scnoderSCN, gelesen mit den eigenenResourcesdes Patterns - den
CharProcseines Type-3-Fonts, gewählt mitTf - Bild-XObjects und Inline-Bildern, nach dem Farbraum, den ihre eigenen Dictionarys nennen
- Annotations-Appearance-Streams, obenauf auf die Seite gemalt; ist das
/AP /Neines Widgets ein State-Dictionary, zählt jeder State, denn das Umlegen des Feldes malt den, den/ASnicht gewählt hat
Die subtilere Hälfte der Erreichbarkeit ist Farbe, die der Inhalt nie nennt. q 0 0 10 10 re f Q enthält keinen Farboperator. Es malt trotzdem. Der anfängliche Farbraum des Grafikzustands ist DeviceGray (ISO 32000-1 Tabelle 52), also ist die Füllung DeviceGray-Schwarz, unkalibriert unter jedem Teil, solange kein PDF/A-OutputIntent oder DefaultGray existiert. Der Scan verfolgt deshalb, ob schon etwas eine Farbe gewählt hat: rg, k, g und ihre Stroking-Formen wählen einen Device-Space, cs und CS wählen, was auch immer sie nennen, und ein Füll-, Stroke- oder Textoperator, der vor jeder Wahl erreicht wird, verbucht DeviceGray. /CS0 cs 1 0 0 /P0 scn hat schon einen Raum gewählt, also ist dieses scn nicht grau
Inline-Bilder bringen ihren Farbraum und ihre rohen Samples selbst in den Content-Stream, und ein Scanner muss beides sorgfältig lesen. Das /CS kann eine Abkürzung sein (G, RGB, CMYK, I) oder ein Ressourcenname wie einer, der zu [/Indexed /DeviceRGB 255 13 0 R] auflöst, dessen Basis zählt; und die Bytes zwischen ID und EI sind beliebiges Binär, also beendet EI das Bild nur zwischen Whitespace, sonst wird ein rg in komprimierten Samples zum Phantom-Operator
% Seite 1: DefaultRGB im Scope, und ein XObject, das nichts aufruft
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
% Seite 2: derselbe Content-Stream, kein 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
Jedes Symptom aus der Einleitung fällt aus diesem Fragment heraus. /Fm0 trifft nie auf ein Do, also ist sein k irrelevant. Das nackte re f auf beiden Seiten ist DeviceGray ohne etwas, der es kalibriert. Das rg auf Seite 1 ist vom DefaultRGB in den Ressourcen abgedeckt, mit denen Seite 1 malt; Seite 2 läuft denselben Stream gegen Ressourcen, denen das Mapping fehlt, also sind dieselben Bytes dort unkalibriert. Erwartet für eine PDF/A-1b-Claim: pvaiMissingOutputIntent mit pvaiMissingIccProfile, getrieben vom RGB von Seite 2 und dem Grau beider Seiten. Das Form trägt nichts bei; aufgerufen, würde sein CMYK das PDF/A-1-Mischverbot obendrauf legen
Default-Farbräume gehören zum Ressourcen-Dictionary, das malt
Die Default-Mappings werden im ColorSpace-Subdictionary des aktuellen Ressourcen-Dictionarys nachgeschlagen (ISO 32000-1 §8.6.5.6), was sie genau so lokal macht wie die Ressourcen selbst; ein Dokument hat keinen globalen Default. Ein Bild-XObject hat keine eigenen Resources, also wird ein Bild, dessen Dictionary /ColorSpace /DeviceCMYK sagt, vom DefaultCMYK derjenigen Seite oder desjenigen Forms kalibriert, die es zeichnen, und dasselbe Bild, von zwei Stellen gezeichnet, kann an der einen kalibriert sein und an der anderen nicht. Form XObjects, Tiling-Patterns und Type-3-Glyph-Prozeduren tragen ihre eigenen Resources, also reicht das DefaultCMYK einer Seite nicht in sie hinein, während ein Mapping in ihrem eigenen Dictionary es tut. PDFium Component skopiert die Mappings auf dieselbe Weise. Das Urteil kommt als TPdfAValidationResult-Record zurück: die erkannte Conformance plus eine Issues-Menge. Das genügt, um einen Ordner zu triagieren
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); // liest ab Position 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 und pvaiMissingIccProfile erscheinen zusammen, wenn der Gang unkalibrierte Gerätefarbe erreicht und kein PDF/A-OutputIntent existiert. pvaiMissingIccProfile allein heißt, ein Intent existiert, aber trägt kein Profil, was 6.2.2 nicht als PDF/A-Intent akzeptiert. pvaiMixedDeviceColorSpaces deckt sowohl das PDF/A-1-Mischverbot als auch eine Komponenten-Diskrepanz ab: unkalibriertes DeviceRGB unter einem Vier-Komponenten-Intent oder DeviceCMYK unter einem Drei-Komponenten-Intent. Die Menge benennt die Regel statt der Seite, also vergleichen Sie in einem langen oder zusammengeführten Dokument, welche Seiten Default-Einträge deklarieren und welche nicht
Wann braucht eine transparente Seite ihren eigenen Blending-Raum?
Eine transparente Seite braucht nur dann ihren eigenen Blending-Farbraum, wenn das Dokument keinen PDF/A-OutputIntent hat. ISO 19005-2 6.2.10 (6.2.9 in PDF/A-4) macht den Intent zum Default-Blending-Farbraum im Sinne von ISO 32000-1 §11.3.4; ohne Intent soll jede Seite, die Transparenz enthält, eine Group tragen, deren Attributs-Dictionary (ISO 32000-1 Tabelle 147) CS einschließt. Dieses CS hält man auch an 6.2.4 fest, also wird eine Seitengruppe von /DeviceCMYK unter einem sRGB-Intent wie eine fehlende gemeldet, und PDFium Component wirft für beide pvaiBlendSpaceUndefined; CalRGB oder ein ICCBased-Gruppenraum hat keine solche Abhängigkeit. PDF/A-1 kommt an diese Frage gar nicht heran, denn ISO 19005-1 6.4 schließt Transparenz völlig aus
Der harte Teil ist die Wendung contains transparency. Annex A von ISO 19005-2 ist normativ und prüft von der Seite nach außen; eine dateiweite Zählung von /SMask-Schlüsseln oder /ca-Werten beantwortet eine andere Frage. Bytesuchen kollidieren auch anderswo mit Transparenz-Syntax: /S /Trans, die verbotene Übergangsaktion, geht jedem /S /Transparency-Gruppeneintrag voraus, und /Screen ist sowohl ein verbotener Annotation-Subtyp als auch ein Standard-Blend-Mode, also bedeutet ein Name nichts, bis er mit seinem Schlüssel gepaart ist. Annex A folgend behandelt PDFium Component eine Seite als transparent, wenn irgendeins davon von ihr aus erreicht wird:
- ein ExtGState, das der Inhalt mit
gswählt, das ein Soft-Mask-Dictionary, eincaoderCAunter 1 oder einen Blend-Mode außerNormalhat; eines, nur deklariert und nie gewählt, zählt nicht - ein Form XObject, aufgerufen mit
Do, dessen/Group/S /Transparencyhat, oder dessen eigener Inhalt eine dieser Bedingungen erfüllt; ein Tiling-Pattern wird wie ein Form beurteilt - ein Bild-XObject mit einem
SMask-Stream oder einemSMaskInData-Wert über 0 - eine Glyph-Beschreibung eines Type-3-Fonts, mit dem die Seite Text zeigt, beurteilt wie ein Form
- ein Annotations-Appearance-Stream, jeder Widget-State eingeschlossen, und ein Blend-Mode auf dem Annotations-Dictionary selbst
Gruppen fließen auch zurück in die Gerätefarben-Regel, und hier hat uns der Korpus eines Besseren belehrt. Die veraPDF-Suite für 6.2.4.3 hält PDF/A-4-Dateien ohne irgendeinen OutputIntent, die Geräte-RGB malen und bestehen sollen, weil die /Group jeder Seite einen kalibrierten CS nennt. Wir nahmen an, nur isolierte Gruppen könnten kalibrieren, da eine nicht-isolierte Gruppe in ihren Hintergrund komponiert, und das war für Seitengruppen falsch: Tabelle 147 hält fest, dass eine Seite, die direkt auf dem Ausgabemedium aufliegt, unabhängig von I faktisch isoliert ist, also wird ihr CS geehrt. Auf einem Form XObject oder einer Annotations-Appearance entscheidet das Flag doch; die Suite hat eine Bestehens- und eine Scheiter-Datei, die sich nur in /I true unterscheiden. DeviceGray nimmt keine Kalibrierung aus einer Gruppe, denn 6.2.4.3 verlangt DefaultGray oder einen Intent, und eine Gruppe, deren CS ein Device-Space ist, braucht selbst Kalibrierung. PDFium Component lässt also eine Seitengruppe kalibrieren, was auch immer ihr /I sagt, lässt eine Form- oder Appearance-Gruppe nur dann kalibrieren, wenn sie isoliert ist, hält Grau außerhalb der Gruppenkalibrierung und prüft trotzdem einen Device-Space, den eine Gruppe nennt
Ein OutputIntent statt einer Gruppe auf jeder Seite
Schreibt Ihr eigener Code das Archiv, fangen Sie mit einem PDF/A-OutputIntent auf Dokument-Ebene an. Ein Katalogeintrag kalibriert DeviceGray überall, deckt den passenden Device-Space auf jeder Seite ab und ist der Default-Blending-Raum für jede transparente Seite. Seitengruppen und Default-Einträge sind je auf ein Ressourcen-Dictionary skopiert, und die Seite, die ein späteres Merge ohne sie hinzufügt, ist die, die scheitert. PDF 2.0 fügt dem Seitenobjekt OutputIntents hinzu (ISO 32000-2 §7.7.3.3 und §14.11.5); PDFium Component akzeptiert diesen Intent auf Seitenebene für PDF/A-4, wenn der Katalog keinen hat, während die Teile 1 bis 3, gebaut auf PDF 1.4 bis 1.7, nur in den Katalog schauen. Für RGB-Inhalt fügt SaveAsPdfAToStream einen Katalog-Intent hinzu, gebaut auf dem eingebetteten sRGB-ICC-Profil des PDF/A-Writers, und die Validierung der Ausgabe zeigt, ob der Inhalt wirklich nur RGB war
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; // die Datei ließ sich nicht öffnen
Opts := TPdfASaveOptions.Default;
Opts.Conformance := pac2b; // PDF/A-1 würde die Transparenz rundheraus verbieten
// IccProfileData bleibt leer: das eingebaute sRGB-IEC61966-2.1-Profil wird benutzt
if not Pdf.SaveAsPdfAToStream(Output, Opts) then
Exit;
Check := ValidatePdfACompliance(Output);
Remaining := Check.Issues;
// Erwartet für reinen RGB-Inhalt: pvaiMissingOutputIntent und
// pvaiBlendSpaceUndefined sind weg. pvaiMixedDeviceColorSpaces heißt jetzt,
// dass ein erreichbarer Pfad DeviceCMYK malt, das kein DefaultCMYK kalibriert.
if Check.IsCompliant then
begin
Output.SaveToFile(OutFile);
Result := True;
end;
finally
Output.Free;
Pdf.Free;
end;
end;
Ein pvaiMixedDeviceColorSpaces, das den sRGB-Intent überlebt, ist der Gang, der DeviceCMYK auf einem erreichbaren Pfad meldet, und dieses Dokument braucht ein DefaultCMYK-Mapping auf den Seiten, die es malen, oder ein CMYK-Ausgabeprofil, bevor PDFium Component es als PDF/A-2b akzeptiert