Technical Article

PDF/A Device Color and Blending Spaces Follow the Paint Path

veraPDF flags ISO 19005-1 6.2.3.3 on a file that never draws RGB, calls a bare re f uncalibrated, or fails page 2 while an identical page 1 passes. PDF/A judges those rules by what each page paints, so the PDFium Component PDF/A validator for Delphi traces every page into forms, patterns, glyphs and annotations before ruling on color and transparency

What 6.2.3.3 and 6.2.4.3 actually require

ISO 19005-1 6.2.3.3 packs three independent rules into one paragraph. A conforming file may use DeviceRGB or DeviceCMYK but shall not use both; if any uncalibrated color space is used, the file shall contain a PDF/A-1 OutputIntent; and DeviceRGB is allowed only with an intent whose profile is RGB, DeviceCMYK only with a CMYK one. DeviceGray is left out of that list because the clause goes on to explain converting gray under either intent. The intent itself is optional: 6.2.2 says a file may specify one, and only counts an OutputIntents entry with /S /GTS_PDFA1 and a valid ICC profile stream in /DestOutputProfile. So a checker that demands an intent from every file rejects compliant documents that never paint device color. Part 1 does not mention DefaultRGB or DefaultCMYK, yet a color remapped through them is rendered in a CIE-based space (ISO 32000-1 §8.6.5.6), which satisfies the device-independence demand of 6.2.3.1; the veraPDF test suite for 6.2.3.3 treats it as calibrated, and PDFium Component reads the clause the same way. The mixing ban stands apart: a PDF/A-1 file that paints both RGB and CMYK fails even when Default entries calibrate both. ISO 19005-2 6.2.4.3 drops that ban and calibrates each space on its own terms, as the right-hand column below shows, so PDFium Component runs one scan for every part and applies the mixing check to PDF/A-1 claims alone

Device space paintedPDF/A-1 (ISO 19005-1 6.2.3.3)PDF/A-2 and later (ISO 19005-2 6.2.4.3)
DeviceRGBDefaultRGB in scope or an RGB intent; not alongside DeviceCMYKDefaultRGB in scope, or an RGB OutputIntent
DeviceCMYKDefaultCMYK in scope or a CMYK intent; not alongside DeviceRGBDefaultCMYK in scope, or a CMYK OutputIntent
DeviceGrayDefaultGray in scope, or any PDF/A-1 OutputIntentDefaultGray in scope, or any PDF/A OutputIntent

What counts as using a color space?

Using a color space means painting with it on a path a reader will render. Declaring it is not enough. The veraPDF suite makes the point with a 6.2.3.3 pass file: a named Form XObject sets CMYK with K and k, sits in the page's resource dictionary, is never referenced from the page content, and the file's OutputIntent is RGB. A file-wide search for device color names fails that file, and misses one that paints only DeviceRGB under a CMYK intent, where no two names conflict. PDFium Component walks the drawing graph instead, built on its byte-level PDF/A preflight scanner. From each page, the walk counts color painted by:

  • the page content streams, and every Form XObject they invoke with Do, nested forms included
  • a pattern selected with scn or SCN, read with the pattern's own Resources
  • the CharProcs of a Type 3 font selected with Tf
  • image XObjects and inline images, by the color space their own dictionary names
  • annotation appearance streams, painted on top of the page; when a widget's /AP /N is a state dictionary, every state counts, since flipping the field paints the one /AS did not select

The subtler half of reachability is color the content never names. q 0 0 10 10 re f Q contains no color operator. It still paints. The initial color space of the graphics state is DeviceGray (ISO 32000-1 Table 52), so the fill is DeviceGray black, uncalibrated under every part unless a PDF/A OutputIntent or DefaultGray exists. The scan therefore tracks whether anything has chosen a color yet: rg, k, g and their stroking forms select a device space, cs and CS select whatever they name, and a fill, stroke or text operator reached before any choice records DeviceGray. /CS0 cs 1 0 0 /P0 scn has already picked a space, so that scn is not gray

The reachable paint path PDFium Component walks in Delphi before ruling on PDF/A device color: forms, patterns, Type 3 glyphs, images and annotation appearance streams
declaring a color space is not enough — the walk counts what each page paints, including the device gray a bare re f picks up from the initial state

Inline images bring their color space and their raw samples into the content stream itself, and a scanner has to read both carefully. The /CS may be an abbreviation (G, RGB, CMYK, I) or a resource name such as one resolving to [/Indexed /DeviceRGB 255 13 0 R], whose base counts; and the bytes between ID and EI are arbitrary binary, so EI ends the image only between whitespace, or an rg inside compressed samples becomes a phantom operator

% page 1: DefaultRGB in scope, and an XObject that nothing invokes
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
% page 2: the same content stream, no 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

Each symptom from the opening falls out of this fragment. /Fm0 never meets a Do, so its k is irrelevant. The bare re f on both pages is DeviceGray with nothing to calibrate it. The rg on page 1 is covered by the DefaultRGB in the resources page 1 paints with; page 2 runs the identical stream against resources that lack the mapping, so the same bytes are uncalibrated there. Expected for a PDF/A-1b claim: pvaiMissingOutputIntent with pvaiMissingIccProfile, driven by page 2's RGB and both pages' gray. The form contributes nothing; invoked, its CMYK would add the PDF/A-1 mixing ban on top

Default color spaces belong to the resource dictionary that paints

The Default mappings are looked up in the ColorSpace subdictionary of the current resource dictionary (ISO 32000-1 §8.6.5.6), which makes them exactly as local as the resources themselves; a document has no global default. An image XObject has no Resources of its own, so an image whose dictionary says /ColorSpace /DeviceCMYK is calibrated by the DefaultCMYK of whichever page or form draws it, and the same image drawn from two places can be calibrated in one and not the other. Form XObjects, tiling patterns and Type 3 glyph procedures carry their own Resources, so a page's DefaultCMYK does not reach inside them, while a mapping in their own dictionary does. PDFium Component scopes the mappings the same way. The verdict comes back as a TPdfAValidationResult record: the detected Conformance plus an Issues set. That is enough to triage a folder

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);   // reads from 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 and pvaiMissingIccProfile appear together when the walk reaches uncalibrated device color and no PDF/A OutputIntent exists. pvaiMissingIccProfile on its own means an intent exists but carries no profile, which 6.2.2 does not accept as a PDF/A intent. pvaiMixedDeviceColorSpaces covers both the PDF/A-1 mixing ban and a component mismatch: uncalibrated DeviceRGB under a four-component intent, or DeviceCMYK under a three-component one. The set names the rule rather than the page, so in a long or merged document, compare which pages declare Default entries and which do not

One DeviceCMYK image drawn from two resource dictionaries in Delphi: calibrated by DefaultCMYK on one page and uncalibrated on another, as PDFium Component reports
default color spaces belong to the resource dictionary that paints, so the same image can be calibrated where one page draws it and uncalibrated where a form does

When does a transparent page need its own blending space?

A transparent page needs its own blending color space only when the document has no PDF/A OutputIntent. ISO 19005-2 6.2.10 (6.2.9 in PDF/A-4) makes the intent the default blending color space in the sense of ISO 32000-1 §11.3.4; without an intent, every page that contains transparency shall carry a Group whose attributes dictionary (ISO 32000-1 Table 147) includes CS. That CS is also held to 6.2.4, so a page group of /DeviceCMYK under an sRGB intent is reported like a missing one, and PDFium Component raises pvaiBlendSpaceUndefined for both; CalRGB or an ICCBased group space has no such dependency. PDF/A-1 does not reach this question, because ISO 19005-1 6.4 rules out transparency entirely

The hard part is the phrase contains transparency. Annex A of ISO 19005-2 is normative and tests from the page outward; a file-wide count of /SMask keys or /ca values answers another question. Byte searches also collide with transparency syntax elsewhere: /S /Trans, the forbidden transition action, prefixes every /S /Transparency group, and /Screen is both a forbidden annotation subtype and a standard blend mode, so a name means nothing until it is paired with its key. Following Annex A, PDFium Component treats a page as transparent when any of these is reached from it:

  • an ExtGState the content selects with gs that has a soft mask dictionary, a ca or CA below 1, or a blend mode other than Normal; one declared but never selected does not count
  • a Form XObject invoked with Do whose /Group has /S /Transparency, or whose own content meets one of these conditions; a tiling pattern is judged like a form
  • an image XObject with an SMask stream or an SMaskInData value above 0
  • a glyph description of a Type 3 font the page shows text with, judged like a form
  • an annotation appearance stream, every widget state included, and a blend mode on the annotation dictionary itself

Groups also feed back into the device color rule, and here the corpus corrected us. The veraPDF suite for 6.2.4.3 holds PDF/A-4 files without any OutputIntent that paint device RGB and are expected to pass, because each page's /Group names a calibrated CS. We assumed only isolated groups could calibrate, since a non-isolated group composites into its backdrop, and that was wrong for page groups: Table 147 notes that a page imposed directly on the output medium is effectively isolated regardless of I, so its CS is honored. On a Form XObject or annotation appearance the flag does decide it; the suite has a pass and a fail file that differ only in /I true. DeviceGray takes no calibration from a group, because 6.2.4.3 asks for DefaultGray or an intent, and a group whose CS is a device space needs calibrating itself. PDFium Component therefore lets a page group calibrate whatever its /I says, lets a form or appearance group calibrate only when isolated, keeps gray out of group calibration, and still checks a device space that a group names

PDF/A matrix for Delphi: when a transparent page needs its own blending CS and how PDFium Component judges page, form and appearance group calibration
an output intent is the default blending space, and a page group calibrates device color whatever its isolation flag says while a form group needs /I true

One OutputIntent instead of a group on every page

If your own code writes the archive, start with a document-level PDF/A OutputIntent. One catalog entry calibrates DeviceGray everywhere, covers the matching device space on every page, and is the default blending space for every transparent page. Page groups and Default entries are scoped to one resource dictionary each, and the page a later merge adds without them is the one that fails. PDF 2.0 adds OutputIntents to the page object (ISO 32000-2 §7.7.3.3 and §14.11.5); PDFium Component accepts that page-level intent for PDF/A-4 when the catalog has none, while parts 1 to 3, built on PDF 1.4 to 1.7, look only at the catalog. For RGB content, SaveAsPdfAToStream adds a catalog intent built on the PDF/A writer's embedded sRGB ICC profile, and validating the output shows whether the content really was RGB-only

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;                           // the file could not be opened
    Opts := TPdfASaveOptions.Default;
    Opts.Conformance := pac2b;        // PDF/A-1 would forbid the transparency outright
    // IccProfileData stays empty: the built-in sRGB IEC61966-2.1 profile is used
    if not Pdf.SaveAsPdfAToStream(Output, Opts) then
      Exit;
    Check := ValidatePdfACompliance(Output);
    Remaining := Check.Issues;
    // Expected for RGB-only content: pvaiMissingOutputIntent and
    // pvaiBlendSpaceUndefined are gone. pvaiMixedDeviceColorSpaces now means
    // a reachable path paints DeviceCMYK that no DefaultCMYK calibrates.
    if Check.IsCompliant then
    begin
      Output.SaveToFile(OutFile);
      Result := True;
    end;
  finally
    Output.Free;
    Pdf.Free;
  end;
end;

A pvaiMixedDeviceColorSpaces that survives the sRGB intent is the walk reporting DeviceCMYK on a reachable path, and that document needs a DefaultCMYK mapping on the pages that paint it, or a CMYK output profile, before PDFium Component will accept it as PDF/A-2b