ConvertPdfDocumentColours reads the active PDF revision, converts original component operands and image samples through an IPdfColourTransform, and appends a coherent revision to a separate output stream
It supports text and vector fill/stroke operators in DeviceGray, DeviceRGB, DeviceCMYK and ICCBased spaces, inherited page resources, named default color-space aliases, nested Form XObjects and raw or general-filter images with 8-bit or 16-bit components
Content arrays share one operand parser and graphics state stack with a space between streams; q/Q, color-space defaults and component counts remain tied to their original source spaces throughout conversion
CMYK operands come from the PDF content and image streams, never rendered RGB getters; provide DeviceCMYKProfile for unprofiled DeviceCMYK sources
Source ICCBased profiles are resolved from their original resource scope; only an absent or default /Range [0 1 ...] is supported, while image /Decode arrays are applied before conversion and removed from the converted image
Images retain PDF big-endian order for 16-bit samples; exact byte geometry and target expansion are checked before sample allocation, and no stride or padding bytes are invented
PreserveAllSpotColours defaults to true; otherwise populate PreservedSpotColours with decoded PDF colorant names, and a Separation or DeviceN space is preserved only when every listed colorant is selected
Preserved spot operators, tint functions and resources remain intact; unselected spots require a dedicated conversion backend and are reported as unsupported
Image stencil masks and referenced mask data remain intact; color-key masks, explicit masks and soft-mask matte components require separate policies
Unsupported boundaries include DCT/JPX images without an original-component decoder, 1/2/4-bit images and their row padding, nondefault ICC ranges, Indexed/Cal/Lab/Pattern spaces, inline images, shading functions, Type3 glyph programs, annotation dictionary colors, form default-appearance instructions, XFA color content, transparency groups, non-normal blending, overprint and graphics transfer functions or rendering intents
Forms with their own resources must establish their color space before bare sc/scn operands; shared forms or images whose inherited source context differs are reported as unsupported, and cached failures propagate to every invoking scope
FailOnUnsupported defaults to true and preserves the destination on failure; setting it to false allows independent supported updates, sets Complete to false and never writes a new OutputIntent for a partial conversion
A partial result can retain an existing OutputIntent from its source; OutputIntentWritten specifically reports whether this operation installed a new target declaration
A complete result embeds the supplied target ICC profile and writes an OutputIntent with the requested identifier; this does not certify PDF/X, PDF/A or another conformance level
Source and destination must be distinct streams; the source position is restored, preparation failures leave the destination unchanged, and cancellation preserves EPdfOperationCancelled
Publishing the prepared result replaces the destination through its stream API; an exception while truncating or writing the destination can leave partial output, so callers needing atomic file publication should write to a temporary file and rename it after success
Encryption and existing signatures require a separately prepared unencrypted, unsigned source; incremental color conversion does not preserve signature validity
Defaults allow 64 MiB per decoded content/image stream, 512 MiB for output staging, 10,000 issue reports and depth 64, in addition to the active-object reader budgets
The output budget conservatively includes source bytes, staged replacement objects and reserved xref/trailer overhead before accumulating each update; the complete trailer length is included before serialization, and a bounded output stream rejects writes beyond the limit before growing its logical size
This bounds logical output staging rather than all process memory or the memory allocator's capacity rounding, and the final serialized size is checked again before publication
Real RGB, CMYK and ICCBased image conversion, text/vector conversion, nested forms, Separation and DeviceN preservation, OutputIntent, compressed object streams and PDF save/reopen/render are verified with Delphi and FPC on Win32 and Win64
ProviderOptions := TPdfLittleCmsOptions.Default; Provider := CreatePdfLittleCmsColourTransform(LibraryPath, ProviderOptions); Options := TPdfDocumentColourOptions.Default; Options.DeviceCMYKProfile := SourceCmykProfile; TargetProfile := CreatePdfLittleCmsGrayProfile(LibraryPath); if not ConvertPdfDocumentColours(Source, Destination, pcsDeviceGray, TargetProfile, Provider, Options, Report, CancellationToken) then raise Exception.Create(Report.ErrorText);
type
TPdfSpotColourNames = array of AnsiString;
TPdfDocumentColourOptions = record
ReadOptions: TPdfActiveObjectReadOptions;
MaxDecodedStreamBytes: Integer;
MaxOutputBytes: Int64;
MaxIssues: Integer;
MaxDepth: Integer;
FailOnUnsupported: Boolean;
PreserveAllSpotColours: Boolean;
PreservedSpotColours: TPdfSpotColourNames;
DeviceGrayProfile, DeviceRGBProfile, DeviceCMYKProfile: TBytes;
OutputIntentIdentifier: string;
class function Default: TPdfDocumentColourOptions; static;
end;
TPdfDocumentColourIssue = record
ObjectNumber: Integer;
Reason: string;
end;
TPdfDocumentColourIssues = array of TPdfDocumentColourIssue;
TPdfDocumentColourReport = record
Succeeded: Boolean;
Complete: Boolean;
ContentStreamsConverted, FormsConverted, ImagesConverted: Integer;
ColourOperatorsConverted, PreservedSpotSelections, PreservedMasks: Integer;
UnsupportedCount: Integer;
OutputIntentWritten: Boolean;
Issues: TPdfDocumentColourIssues;
ErrorText: string;
end;
function ConvertPdfDocumentColours(Source, Destination: TStream;
TargetSpace: TPdfColourSpace; const TargetProfile: TBytes;
Transform: IPdfColourTransform; const Options: TPdfDocumentColourOptions;
out Report: TPdfDocumentColourReport;
CancellationToken: IPdfCancellationToken = nil): Boolean;