Skip to main content

M29 — Print production

State: to do — Depends on: M19, M20, M22, M23, M25 — Conformance preserved or its loss reported, per ADR 17; profiles in the conformance satellite and findings apart from diagnostics, per ADR 36; a dependency-free core and satellites, per ADR 9; output versions per ADR 40, which PDF/X-4 may ask to amend; Skia only in .Html and .Rendering, per ADR 43; profiles a stylesheet names fetched only through the resolver, per ADR 38; a lossy change to an image only by name, per ADR 42

Goal​

Hand a print shop what it expects: invoices, statements and reports generated as PDF/X-4 with their bleed, marks and output intent; whole batch runs as PDF/VT with one document part per record and the shared artwork written once; colors converted through a managed, deterministic color-management engine; CMYK and spot colors and overprint written from CSS; and a verdict on the PDF/X and PDF/VT files the library receives, checked against an independent referee chosen before anything else is built.

The business case is the outsourced print run: thousands of invoices, statements or notices sent to an éditique provider who prints, folds, inserts and posts them. Such providers ask for PDF/X, and for large runs PDF/VT, so that their raster image processor can color-manage every page against the press and cache what repeats. M12's batch generation already produces the records and keeps their boundaries; M20 made the conformance engine public; M22 evaluates every color space; M25 renders through a color-conversion seam. This milestone makes the file one they accept, and gives the rest of the library the color management each of those milestones deferred here.

The failures it exists to prevent are the ones a print provider's preflight reports on the morning of the run. A page without a /TrimBox, or with a /TrimBox and an /ArtBox together. An RGB logo under a CMYK output intent, with no default color space to say what the RGB meant. A /Trapped left Unknown. A creation date read from the server's clock, so that a re-run is never byte-identical. A spot color written as Pantone 186 C where the press expects PANTONE 186 C, which prints as a plate nobody loaded. A logo embedded ten thousand times in a ten-thousand-record run. A color engine whose last bit differs between the Windows workstation that proofed the job and the Linux server that produced it. A supplier's CMYK leaflet merged into an RGB volume and converted by nobody. A batch whose record boundaries the inserter cannot find, so that one customer's statement is folded into the next one's envelope.

Scope​

In:

  • the referee and the version, first: the maintainer decides at the start of the milestone, and nothing before, what independently checks a PDF/X-4 and a PDF/VT file — veraPDF validates PDF/A, PDF/UA and WTPDF only, and no open-source PDF/X or PDF/VT validator is known —, with what runs in CI and what the maintainer runs, and whether ISO 15930-7 caps PDF/X-4 at PDF 1.6, and so whether ADR 40 needs a 1.6 output for generated documents; slice 1 records both as ADRs at the next free numbers;
  • page geometry from CSS Paged Media 3: @page bleed and marks written as /MediaBox, /BleedBox and /TrimBox; crop and registration marks drawn outside the bleed box in the registration color; the page's rendering clipped at the bleed edge rather than the page box;
  • output intents for print: one GTS_PDFX intent with its embedded output profile (PDF/X-4) or a reference to it (PDF/X-4p), and, in a file that also claims PDF/A, the same profile object under both intents;
  • PDF/X-4 and PDF/X-4p generation in the core, from M08's builder and from M12's engine: a constraint table as data, consulted by the writer, the fonts, the content builder, the annotations and the assembly, as M20's part-1 table is; the dual claims PDF/A-2b, 2u, 3b and 3u with PDF/X-4, Factur-X included;
  • a managed color-management engine in a satellite of its own: ICC v2 and v4 profiles read under hostile-input bounds; transforms between them at the four rendering intents, with black-point compensation; device links precomputed and interpolated in integer arithmetic, so that the same profile and input give the same bytes on every platform; a transform cache; the implementation of M25's color-conversion seam and of the core's transform seam;
  • color from CSS: device-cmyk() written as DeviceCMYK; CSS Color 5's @color-profile and color() in a named profile's space; spot colors and overprint through -adc- vendor extensions, written as Separation and DeviceN spaces with their alternates and as graphics-state overprint; sRGB content under a CMYK intent either kept managed through a default color space or converted early, by option;
  • document color conversion in the core, through the transform seam: device and ICC-based color in content, images, shadings, patterns and appearances converted to a target profile, spot colorants kept unless the caller asks, JPEG and JPEG 2000 images converted only under M23's named lossy step; used to reconcile output intents on merge (M06, M20), by M09's stamps and by M21's remedies, all of which deferred color conversion here;
  • PDF/VT-1 on M12.6's batch generation — the document part hierarchy, one leaf per record, record metadata, shared XObjects with their reuse hints —, and PDF/VT-2 over PDF/X-4p (the output profile referenced, one file); a read model of a received document's parts;
  • the PDF/X and PDF/VT profiles in AdCodicem.Pdf.Conformance: PDF/X-4 and 4p, PDF/X-1a and PDF/X-3 (their 2001 to 2003 editions) validated, never generated; PDF/VT-1 and 2 validated; the claim reader extended to PDF/X and PDF/VT identifications;
  • color-managed rendering through M25's seam: ICC-based color by its profile, soft proofing against the output intent, and — through a plate device in AdCodicem.Pdf.Rendering — separation previews and overprint simulation, which M25 left here;
  • the command-line tool's print options and verbs.

Out, explicitly:

  • mixed raster content compression — an open question of the roadmap, reopened by color scans too large for a portal cap after M23's recompression; nothing here needs it;
  • trapping, OPI and halftone authoring — TrapNet annotations, OPI dictionaries, HT and HTO — read, preserved through every operation, reported by the profile where a part forbids them, never written;
  • transparency flattening — it needs rasterization into the document, which nothing plans; PDF/X-4 keeps transparency live and does not ask for it, and so PDF/X-1a and PDF/X-3 generation are not planned either (they forbid transparency); the library validates them;
  • PDF/X-5 (g, pg and n: external graphical content, n-colorant intents), PDF/VT-2 over PDF/X-5, PDF/VT-2s (streamed MIME packages) — until a print provider asks;
  • PDF/X-6 and PDF/VT-3, the PDF 2.0 parts — until a print provider asks, as M28 left PDF/X-6 here;
  • imposition beyond M09's N-up, JDF job tickets, color bars and press control strips — not planned;
  • converting a received document to PDF/X — not planned: the profile reports, and color conversion repairs color only; M21 converts to PDF/A;
  • color libraries (Pantone and its kin) — never shipped, their data being licensed: the caller names each spot and gives its alternate; ICC named color profiles are read as unsupported and reported;
  • iccMAX (ICC version 5) profiles, spectral processing, black-preserving and ink-limiting transforms, and profile creation — not planned; a v5 profile is refused with a diagnostic;
  • converting a JPEG's color without re-encoding it — impossible; only M23's named ReencodeJpeg step does it, reported;
  • heuristics on received files — guessing a missing output intent, renaming a spot a caller misspelled — never.

Design​

Where it lives​

PartWhereWhy
PdfXOptions, PdfVtOptions, page geometry, output intents, the PDF/X constraint table, the document-part writer, PdfDocumentPartsCore, beside M14's and M20's conformance targetsThe writer, the builder, the fonts, the stamps and the assembly enforce them; no dependency is needed
The ICC header and tag-table readerCore, internal (M20's)Already bounded and total; the conformance rules and the writer read headers, never transform
IPdfColorTransformProvider, PdfColorConversionCore, Graphics/Conversion edits content through M19's pipeline, which is internal to the core; the color science arrives through the seam, so the core still converts no color of its own (M10's rule)
The color-management engine — IccProfile, IccTransform, intents, black-point compensation, the device-link tables, the cache, the implementations of both seamsNew satellite, AdCodicem.Pdf.ColorManagement — the AdCodicem. prefix is reserved on nuget.org (ADR 24), and the spelling follows the project's convention for identifiersADR 9: a large engine and its reference data in a package a caller adds knowingly; docs/architecture.md already names it without an identifier
device-cmyk(), @color-profile, color(), the -adc- spot and overprint extensionsAdCodicem.Pdf.HtmlCSS is the engine's; the property table gains the rows
The PDF/X and PDF/VT profiles and their rule catalogAdCodicem.Pdf.ConformanceADR 36, on M20's public rule API
The plate device and soft proofingAdCodicem.Pdf.RenderingADR 43: Skia draws there and nowhere else
The referee harnesstests/AdCodicem.Pdf.TestSupportShared by this milestone's integration tests and any later one that claims PDF/X
Print options and verbsAdCodicem.Pdf.ToolThe tool ships every satellite

The color satellite depends on the core alone, has no native code and declares IsAotCompatible from its first commit; it runs in M23's WebAssembly host, where its determinism is tested. It is a new package, so #42 (an API baseline per package) applies to it.

M25's seam is in the core. M25 declares IPdfColorConverter in the core's Graphics/, beside M22's evaluation, so that the color satellite implements it without depending on SkiaSharp.

Page geometry​

  • bleed and marks (CSS Paged Media 3): marks: none | [crop || cross]; bleed: auto | <length>, where auto computes to 6pt when marks has crop and to zero otherwise. M12 parsed both and reported them; M29 writes them.
  • The boxes. The page box is the /TrimBox; the /BleedBox extends it by the bleed on every side; the /MediaBox extends the bleed box by a slug wide enough for the marks when there are marks, and equals the bleed box otherwise. No /ArtBox is written beside a /TrimBox, and no /CropBox is written, so that viewers show the media. page-orientation rotates all of them with /Rotate, as M12 writes it. Under PDF/X, a page without bleed still gets a /TrimBox equal to its /MediaBox.
  • Painting into the bleed. The page's background covers the bleed area, and the page's rendering is clipped at the bleed edge, not the page box: a band that touches the page edge in the source bleeds off it on paper. Layout is unchanged; only the clip moves.
  • The marks. Crop marks at each corner of the trim, offset outside the bleed so that no mark enters the printed area; registration crosses centered on each side in the slug. Drawn in the registration color — a Separation space whose colorant is /All, so that they print on every plate —, as content marked as a page artifact — the kind ISO 32000 gives to production aids extraneous to the document, cut marks and color bars among them —, so that M13's tagging, M15's extraction and PDF/UA leave them aside. Their lengths, offsets and line width are constants recorded by slice 2.
  • The trim, bleed and art boxes are not inherited: ISO 32000 defaults each to the crop box, which is inherited and defaults to the media box (CLAUDE.md, Known traps). The writer puts every box on each page dictionary, and the profile resolves a default through the crop box's inherited resolution — but a default is not a box, and PDF/X asks for the trim or art box on the page (to verify the wording).

Output intents and identification​

PdfXOptions Part (X4, X4p); Condition — identifier (e.g. "FOGRA39", "CGATS TR 001"), registry name
(http://www.color.org when the condition is registered), info; Profile — the output profile's
bytes (X-4) or a reference to it (X-4p); Trapped (False by default, or True); RgbContent
(ManagedByDefaultSpace, the default; ConvertEarly, which needs a transform provider); Policy
(Refuse, the default; Convert, with a provider; RemoveClaim)
PdfXProfileReference for X-4p: the profile's name, URLs, checksum, ICC version and colorant table, as ISO 15930-7
names them (the keys to verify against the text)
  • One GTS_PDFX intent, with /OutputConditionIdentifier, /RegistryName and /Info from the options, and /DestOutputProfile (X-4) or the profile reference (X-4p). The profile must be an output profile (prtr) in gray, RGB or CMYK; anything else is refused when the options are built, by the core's ICC header reader.
  • With a PDF/A claim — PDF/A-2b, 2u, 3b or 3u, and so Factur-X — the GTS_PDFA1 intent is written beside it, both pointing at one profile object, as PDF/A-2 requires of every intent's profile (M20); the XMP carries both identifications, and pdfxid is declared as an extension schema where PDF/A does not predefine it (to verify against ISO 19005-2's list of predefined schemas).
  • Identification: pdfxid:GTS_PDFXVersion in the XMP (M14's model), which is how ISO 15930-7 identifies conformance; pdf:Trapped and the document information dictionary's /Trapped agree, True or False, never Unknown; /Title, /CreationDate and /ModDate present, with their XMP counterparts; the trailer's /ID (M03).
  • Dates are the caller's. PDF/X requires a creation and a modification date; the library never reads the clock (invariant 6), so a PDF/X target without both is refused when the options are built.
  • The version. ISO 15930-7 specifies the use of PDF version 1.6, as ISO's abstract says, and published summaries read that as a file of version 1.6 at most — "PDF/X-4 files are regular PDF 1.6 files" (prepressure.com) — (to verify against the text). ADR 40's writer writes 1.7 or 2.0 for a document it generates and never lowers a received document's version (M03). The maintainer decides against the text at the start of the milestone; if the cap holds, slice 1 records an amendment of ADR 40 at the next free number — a Pdf16 output for generated documents only, whose features the writer knows, the version table's rows for PDF 1.7 features refusing under it — and slice 5 implements it. A received document keeps its version, and a PDF/X-4 target on one that declares 1.7 is refused.

The PDF/X-4 constraints​

PdfXConstraintSet (internal), one per part, is data, like M20's PdfAConstraintSet, and each writer consults the set of the target or of the document's claim. The rows below are the ones this specification can state from published summaries; slice 5 writes each from ISO 15930-7:2010 with its clause, and a row marked to verify is kept, amended or dropped by that reading.

AreaRequirementWho keeps itWhen it cannot be met
Identification, versionpdfxid:GTS_PDFXVersion; the version cap above (to verify)M14's XMP model, M03's writerRefused when the options are built
Output intentOne GTS_PDFX intent, an embedded output profile, or a reference under X-4pAboveRefused
MetadataTitle, creation and modification dates, Trapped true or false, /IDAboveRefused
Page boxesA /MediaBox, and a /TrimBox or an /ArtBox but never both; bleed box within the media box and containing the trim boxPage geometry; M06's page copy, M09's N-upA copied page with both boxes: the /ArtBox dropped and reported, or refused under Refuse
EncryptionNoneM16's writerRefused
FontsEvery font embedded (to verify: whether a font used only in render mode 3 is exempt, as in PDF/A)M08, M12A received part with a font not embedded: a conflict
Device colorDevice color only in the output intent's family, unless a default color space says what it meansThe content builder, M12's color, M09's stamps, M10's codesUnder Refuse, a conflict; under Convert, converted through the provider; RGB content under a CMYK intent follows RgbContent
Other colorICCBased, Lab, Separation and DeviceN with their alternatesColor from CSS, below—
TransparencyAllowed, and kept live; the page group's blending space in the intent's family (to verify: what the part requires of /CS)M12's page groups, M09's opacity—
Optional contentAllowed, under constraints on its configurations (to verify)M11A conflict
InteractivityNo JavaScript, no actions other than navigation, no interactive form fields (to verify: the exact list), no multimediaM11, M16, M17A conflict: M17's fields are refused under a PDF/X target
AnnotationsWithin the bleed box, only printer's marks and trap networks may print (to verify)M11, M12's links (no appearance, not printed)A conflict
Images16-bit samples and JPEG 2000 allowed; no /Alternates, no OPI (to verify)M07, M12.5, M22A conflict
Graphics stateNo transfer function; halftones and PostScript XObjects restricted (to verify)The content builderA conflict

A conflict follows PdfXOptions.Policy, as PdfConformancePolicy does for PDF/A (M09): Refuse throws PdfConformanceException naming the part and the clause, before a byte is written; Convert converts device color through the provider and reports each conversion; RemoveClaim writes without the identification and reports pdfx.claim-removed at ConformanceLoss (invariant 7).

The color-management satellite​

IccProfile Parse(ReadOnlySpan<byte>, IccParseOptions) -> an immutable profile, or a report of why not:
header (version, class, color space, PCS, rendering intent, illuminant, profile ID), tags by
signature, each read on demand and bounded
IccRenderingIntent Perceptual, RelativeColorimetric, Saturation, AbsoluteColorimetric
IccTransform Create(source, destination, intent, blackPointCompensation) -> immutable, thread-safe;
Convert rows of 8- or 16-bit samples, or components in [0, 1]
IccTransformCache bounded by count; keyed by both profiles' SHA-256, the intent and the compensation
ColorManagement the entry point: Transforms(options) -> IPdfColorTransformProvider (core's seam), and
Converter(options) -> IPdfColorConverter (M25's seam)
  • Profiles: versions 2 (ICC.1:2001-04) and 4 (ICC.1:2010, which ISO 15076-1:2010 adopts); the input, display, output, color-space and abstract classes, and device links. Tag types: curv, para, XYZ , sf32, mft1 and mft2 (the v2 LUTs), mAB and mBA (the v4 LUTs, with their A, M and B curves, matrix and CLUT), chad, mluc, desc and text. A named-color profile or an iccMAX profile is refused with color.profile-unsupported.
  • Pipelines are built from a profile's tags for the intent: matrix and TRC for display profiles; A-to-B and B-to-A tables for the rest, falling back to the perceptual table (A2B0, B2A0) when the intent's is absent, as the ICC specification says (to verify per profile class); the PCS joined in XYZ or Lab; v2's and v4's 16-bit Lab encodings differ (L* = 100 is FF00h in v2's legacy encoding and FFFFh in v4's), and a transform between a v2 and a v4 profile converts between them; absolute colorimetric through the media white point, and v4's chromatic adaptation through chad.
  • Black-point compensation as ISO 18619:2015 specifies it: for the relative colorimetric intent, and for the others only where the standard extends it (to verify — v2 perceptual tables, whose black point is not defined, are where implementations differ).
  • Device links, precomputed. A transform is evaluated once on a grid — 33 points per input for three inputs, 17 for four, constants recorded by slice 3 — through the floating-point pipeline in a fixed order of operations, and stored as 16-bit values; conversion interpolates the grid in integer arithmetic, tetrahedrally for three inputs and tetrahedrally in C, M and Y then linearly in K for four. The hot loop is a table read and integer arithmetic, 0 B allocated per row.
  • Deterministic everywhere. The floating-point pipeline uses IEEE basic operations only, in a fixed order, with no fused multiply-add unless written, and M22's own power and cube-root routines rather than Math.Pow, whose last bit the platform's C library chooses (M22's trap). The same profile and input give the same bytes on x64 and ARM64, on Linux, Windows and macOS, and in M23's WebAssembly host — tested on each.
  • Accuracy is measured against two independent implementations: LittleCMS (MIT; transicc for colors, tificc for images) and the ICC's own reference implementation, DemoIccMAX, renamed iccDEV in 2025 (iccApplyNamedCmm, iccDumpProfile; its license read before use). The tolerance, in ΔE2000, is fixed by slice 3 per intent and recorded; where the two referees disagree — black-point compensation and v2 Lab encoding are known places to look —, the manifest of profiles records which reading is ours and why.
  • The profile is hostile input (invariant 4): the tag count is at most what the profile's length can hold (twelve bytes a tag after the header); each tag's offset and size lie within the profile; tags may share data, as the ICC specification allows, but none is followed twice; a CLUT's size — grid points to the power of inputs, times outputs, times the precision — is computed in 64 bits and checked against the tag's size before anything is allocated; a curve of 0 entries is the identity and of 1 entry a gamma; a para function outside types 0 to 4 is refused. A profile that fails is color.profile-invalid, and the color it describes goes through its /Alternate, as M22 does.

Color from CSS​

  • device-cmyk() under a target whose intent is CMYK is written as DeviceCMYK, exactly; otherwise it is converted through the profile an @color-profile device-cmyk rule names, when there is one and a provider is given, and by CSS Color 5's naive formula otherwise, as M12 does, reported.
  • @color-profile (CSS Color 5): src loaded through ADR 38's resolver — deny-by-default, bounded, cached per document —, rendering-intent honored; color(--name c1 c2 …) written in an ICCBased space on that profile, one object per profile per document. A profile the resolver refuses or the parser rejects is css.color-profile-unavailable, and the color falls back as CSS Color 5 says.
  • sRGB content under a CMYK intent — every color M12 resolves, every RGB image — follows RgbContent: ManagedByDefaultSpace writes /DefaultRGB as ICCBased on M14's sRGB profile in each page's resources, so that rg still means sRGB and the press converts it (late binding); ConvertEarly converts every color and RGB image to the intent through the provider at the options' intent, so that the file holds CMYK only. RGB images already tagged with a profile (a PNG's iCCP, a JPEG's APP2, which M07 and M12 read) keep it.
  • Spot colors: a vendor at-rule declares a colorant and its alternate, and a vendor function uses it wherever a color is allowed — text, borders, backgrounds, SVG's fill and stroke, gradient stops —, with a tint. The syntax is fixed by slice 4 and entered in the property table under M12's -adc- prefix and its ADR; Prince's @prince-color rule and prince-color() function are the prior art it is measured against. Written as a Separation space: the colorant's name exactly as given, encoded as a name (a space as #20), never normalized; the alternate in DeviceCMYK or Lab; a type 2 tint transform from zero to the alternate. One space object per colorant per document. Two declarations of one name with different alternates keep the first (print.spot-conflict).
  • Mixtures — a gradient between two spots, or between a spot and a process color — are written as DeviceN with the NChannel subtype, its /Colorants and /Process attributes, and a tint transform sampled as a type 0 function.
  • Overprint: a vendor wrapper around a color sets /OP and /op in the graphics state for the fill or the stroke, with /OPM 1 under a CMYK intent, so that a zero component leaves the plate beneath untouched. Syntax fixed by slice 4; Prince's overprint keyword is the prior art. Knockout groups are not written.
  • Transparency under a CMYK intent: page and form groups blend in the intent's family (/CS /DeviceCMYK), so that a multiply happens where the press will see it, not in RGB (to verify against the part's clause on groups).
  • Versions: Separation needs PDF 1.2, DeviceN 1.3, the NChannel attributes 1.6 — rows in M03's version table, under the cap above when it is confirmed.

Document color conversion​

IPdfColorTransformProvider (core, public) Create(source space, target profile, intent, compensation) ->
IPdfColorTransform, which converts rows of components; implemented by the satellite
PdfColorConversion (core) Convert(document, PdfColorConversionOptions, provider, progress, token)
-> the report: what was converted, left, approximated or refused, object by object
PdfColorConversionOptions target profile and family; intent; compensation; Scope (DeviceOnly — the default —,
DeviceAndIcc, AllButSpots); Spots (Keep — the default — or ConvertToProcess);
ImageSteps (M23's lossy steps named for JPEG and JPEG 2000 images)
  • Content through M19's content-editing pipeline: g, G, rg, RG, k, K, sc, SC, scn, SCN, cs, CS and inline images, in page content, form XObjects (each converted once, however many pages draw it), colored tiling patterns, Type 3 glyph procedures that set color (d0; a d1 glyph takes the current color), and annotation appearance streams.
  • Images through M22's rows: Flate, LZW, RunLength, CCITT and uncompressed images converted from their samples and re-encoded losslessly (Flate with a predictor); an Indexed image by converting its palette, exactly; JPEG and JPEG 2000 only when M23's ReencodeJpeg is named — a lossy step, reported per image —, and otherwise left and covered by a default color space where one can say what they mean, or reported (color.image-left).
  • Shadings: a type 2 function between two device colors has its end points converted; any other function is resampled into a type 0 function of a fixed size, and the largest error of the resampling, in ΔE2000, is reported (color.approximated).
  • Spots stay spots. A Separation or DeviceN colorant prints on its own plate; conversion re-expresses its alternate in the target family only when the alternate's family differs, and converts the colorant itself only under ConvertToProcess. /All and /None are never converted.
  • Group blending spaces: a page or form group's /CS in a device family other than the target's becomes the target's family, so that blending happens where the press will see it (the trap below); an ICCBased /CS follows the scope, as content does.
  • Not converted: soft-mask groups — a luminosity mask is a mask, not a color on paper —; the page group's backdrop; color in /Metadata. Default color spaces that no longer apply are removed.
  • Consumers: M20's merge (PdfAssemblyOptions.Conformance) gains ConvertParts, which converts a part whose profile's family differs from the target's rather than covering it with a default space; M09's stamps gain PdfConformancePolicy.Convert for "an RGB color on a CMYK intent", which M09 refuses today; M21's remedy table gains the row "device color converted to the output intent's family, with a provider, when the caller prefers it to a default color space"; the tool's color convert.

PDF/VT​

PdfVtOptions Part (VT1, VT2 over X-4p); the level names (NodeNameList, e.g. Job, Record); the record level;
a record's metadata builder — strings, numbers, booleans, dates and nested dictionaries, no
streams —; the XObject reuse hints; the PDF/X options it builds on
PdfDocumentParts (core, read model) Open(document) -> the part tree: levels, records, each record's page range
and metadata; iterative, each node read once, a cycle cut and reported
  • On M12.6's batch, in the one-document mode: each record starts a page group of its own already (its counters, a bookmark, a named destination); M29 adds the document part hierarchy: the catalog's /DPartRoot, its /DPartRootNode, /RecordLevel and /NodeNameList; DPart nodes with /Parent and /DParts — an array of arrays of children, as ISO 32000-2 defines it —; one leaf per record with /Start pointing at its first page, /End at its last when the record has more than one, and /DPM holding its metadata; and each page's /DPart pointing at its leaf. ISO 32000-2 §14.12, Document parts, defines the structure and these keys (read in its text), and ISO 16612-2 brought it into PDF 1.6-based files.
  • Forward-only: a leaf's number is reserved when its record starts, its pages point at it, and it is written when the record ends; leaves are grouped under intermediate nodes of at most a constant number of children, so that no /DParts array grows with the run; the root is written last. Memory: one reference per child of the open node at each level.
  • Record metadata: the caller's values per record — a customer number, a postal code for sorting, a page count for the inserter — written in /DPM under the caller's names; ISO 16612-2 recommends a vocabulary derived from CIP4's (to verify), which the documentation shows and the options do not impose. A value outside the closed vocabulary is dropped and reported (vt.dpm-value-dropped).
  • What repeats is written once. M12.6's caches already make a letterhead or a logo one XObject for the whole run. PDF/VT adds hints a raster image processor uses to cache it rendered: a scope hint and an encapsulation hint (to verify: the key names, GTS_Scope and GTS_Encapsulated, and their values), written only for XObjects that the layout proves independent of the state they are drawn in — no pattern, no inherited soft mask, no blending with the backdrop.
  • Identification: pdfvtid:GTS_PDFVTVersion and pdfvtid:GTS_PDFVTModDate in the XMP, beside PDF/X-4's (PDF/VT-1 is a PDF/X-4 file); PDF/VT-2 over X-4p references the output profile instead of embedding it, in one file.
  • Budget: the batch keeps M12's throughput and allocation budget within an overhead this slice records.

The conformance profiles​

  • Levels: PdfConformanceLevel gains PdfX1a2001, PdfX1a2003, PdfX32002, PdfX32003, PdfX4, PdfX4p, PdfVT1 and PdfVT2. The claim reader (core) reads pdfxid:GTS_PDFXVersion from the XMP, the information dictionary's /GTS_PDFXVersion and /GTS_PDFXConformance that the older parts use, and pdfvtid:GTS_PDFVTVersion, reporting what the parts do not define as claim.malformed, as M20 does.
  • Profiles: pdf-x-4, pdf-x-4p, pdf-x-1a, pdf-x-3, pdf-vt-1, pdf-vt-2, on M20's public API. Families: pdfx-file, pdfx-metadata, pdfx-page-box, pdfx-color, pdfx-font, pdfx-graphics, pdfx-image, pdfx-annotation, pdfx-action, pdfx-optional-content, pdfvt-part, pdfvt-metadata, pdfvt-xobject. One identifier per requirement whatever the part, as M20's catalog does; each rule's references give the part and the clause.
  • Sourcing. The ISO 15930 and 16612 texts are paid publications: each rule is written in our own words from the text the maintainer reads, with its clause; the referee is a checklist of what it checks, never the source.
  • Verdicts: PDF/X has no human condition, so a verdict is Conforms, DoesNotConform or Indeterminate (a rule that could not run, as M20 defines it).

Color-managed rendering (M25's seam)​

  • The converter: ICCBased color by its profile; Lab exactly; device color through the document's output intent profile when it has one, so that a CMYK file is shown as the press will print it — soft proofing —, at relative colorimetric with compensation by default, absolute on request (paper white simulated); through M22's device formulas otherwise, as M25 does. Called per color set and per image row, never per pixel through an interface (M25's rule).
  • The plate device, in AdCodicem.Pdf.Rendering: M15's interpreter drives a device that keeps one gray raster per colorant the page uses — the process colorants of the intent and each spot —, a band at a time as M25 renders. Each paint operation writes its tint into the plates its color space reaches, knocking out the others unless the graphics state overprints, where /OPM 1 leaves a plate untouched for a zero component. Skia draws coverage masks; plates are combined in integer arithmetic.
  • Outputs: separation previews — the plates, as grayscale images named by colorant —, and overprint simulation — the plates composed through the intent's profile to sRGB, spots through their alternates.
  • Referees: Ghostscript's tiffsep device, which writes one grayscale plate per colorant with the composite; MuPDF with color management on (mutool draw without -N) for soft proofs; Ghostscript's and MuPDF's overprint simulation options (to verify their names and behavior in the pinned versions).

Bounds, classified (invariant 12, ADR 34)​

BoundClassWhy
An embedded ICC profile's lengthThe existing guard MaxDecodedStreamLengthA profile is a stream's decoded data
A profile a stylesheet namesThe resolver's resource-size bound (ADR 38, M12)The template's input, not the document's
Tag count, tag offsets and sizes, CLUT sizesSized by the profile's bytes, computed in 64 bitsA profile cannot describe more data than it holds
CLUT inputs and grid pointsInternal constants: the ranges the ICC's field widths allow — grid points in one byte, the input count per tag type (to verify)Field widths the specification defines; no valid profile exceeds them
Our device-link gridsInternal constants (33 and 17 points per input)Independent of the file
The transform cacheAn option (count), not a guardReaching it costs a rebuild of a transform, never data
DeviceN colorantsSized by the array the file holds, under MaxObjectLengthEach colorant is a name the parser read
The document part treeWalked iteratively, each node once, cycles cutA tree is no larger than the objects in the file
Plates per pageOne per colorant the page's color spaces name, a band at a timeBounded by the color spaces the file holds and M25's band budget

Diagnostics​

In PdfDiagnosticCodes, disjoint from rule identifiers (ADR 36):

CodeSeverityMeaning
color.profile-invalidWarningAn ICC profile the parser could not use; the space falls back to its alternate
color.profile-unsupportedWarningA named-color or iccMAX profile
color.approximatedInformationA shading's function resampled, or a color converted without a profile; the largest error
color.image-leftInformationAn image whose conversion needs a named lossy step; left, and covered by a default space where possible
css.color-profile-unavailableWarningAn @color-profile the resolver refused or the parser rejected
print.spot-conflictWarningOne colorant name declared with two alternates; the first kept
print.artbox-droppedWarningA copied page carried both a trim box and an art box; the art box dropped under PDF/X
pdfx.content-convertedInformationDevice color converted to the intent under Policy.Convert
pdfx.claim-removedConformanceLossA conflict written under RemoveClaim; the identification is gone
vt.dpm-value-droppedWarningA record's metadata value outside the closed vocabulary
vt.part-tree-cycleWarningA received document part tree that reaches itself; cut where it does

Referees​

All in containers (ADR 27), pinned by digest, their versions recorded in docs/status.md:

RefereeConfirms
The referee chosen in slice 1That every PDF/X-4, 4p and PDF/VT file generated conforms, and the verdict on every received print file
veraPDF — the PDF/A flavors; and its feature report with a PDF/X policy of our own (partial)The PDF/A claim of every dual file; the facts of PDF/X files as an independent parser extracts them, judged by our policy — a regression check, not validation
qpdf, pikepdfEvery output sound; boxes and their nesting, output intents and their profile object, color spaces, the part tree walked independently, XObjects counted
ExifToolThe XMP identifications, pdf:Trapped, the extension schemas
Ghostscripttiffsep plates for spot colors and overprint; its overprint simulation
MuPDFColor-managed renderings for soft proofs; -N for M22's device formulas
LittleCMS (transicc, tificc) and iccDEV (iccApplyNamedCmm, iccDumpProfile)Color values per intent and compensation; image conversions; parsed tags
M14's Factur-X refereeThat a Factur-X invoice made PDF/X-4 is still a valid Factur-X

The command-line tool​

adpdf html2pdf IN.html -o OUT.pdf --pdf-x 4|4p --output-profile P.icc --condition ID [--registry URL] [--bleed 3mm] [--marks crop,cross] [--trapped true|false] [--rgb managed|convert]; html2pdf --template T.html --records R.jsonl --one-document --pdf-vt 1|2 [--record-level NAME]; validate FILE --profile pdf-x-4|pdf-x-1a|pdf-x-3|pdf-vt-1 (M20's verb); color convert FILE --profile P.icc [--intent relative] [--bpc] [--scope device|icc] [--spots keep|process] -o OUT; color inspect P.icc; parts FILE [--json], the records of a PDF/VT file; render FILE --soft-proof | --separations (M25's verb). Exit codes are M06's; the AOT binary produces what the API produces.

Slices​

Each slice ends on a green commit, with its codes and rules documented, its benchmark, if it has one, in benchmarks/budgets.json (M23), and its measurements in docs/status.md.

  1. The referee and the version. Records the maintainer's two decisions, taken at the start of the milestone and not before: the referee's ADR, and whether ISO 15930-7 caps PDF/X-4 at 1.6 — if it does, the ADR amending ADR 40 for a Pdf16 output of generated documents. The referee's ADR weighs: a commercial preflight — callas pdfToolbox, sold as a command-line and server product (its Linux edition and its license terms for CI to verify), or Enfocus PitStop — under a license the project can use in CI; the same, or Acrobat's Preflight, run by the maintainer, its reports committed; the published test suites of the Ghent Workgroup and of the Altona suite as a corpus for our profile, their terms read; and the partial checks open tools allow (the rows of Referees above). The expected outcome, to be confirmed or overturned by the ADR: the partial checks run on every output in CI; the full verdict comes from a commercial preflight, run in CI if a license allows it, and otherwise by the maintainer with its reports committed under tests/corpus/referee/, each naming the SHA-256 of the file it judged — invariant 6 makes that binding exact, and a CI test fails when a generated output's hash has no current report, so that a changed output cannot pass on an old verdict. Delivers the harness for whatever is chosen, and the first reports on the corpus's print files. The manifest's print use case joins the schema's enum here, and the corpus's print files take it. Proved by the harness on two hand-made fixtures, one sound and one breaking five requirements, each verdict as expected; the binding test failing when one byte of an output changes; the version decision cited with its clause of ISO 15930-7. Leaves geometry.
  2. Page geometry, output intents and claims. Delivers bleed and marks written, the boxes, the bleed clip, the marks in the registration color as page artifacts, PdfXOptions with the intent, identification, Trapped and the dates, the shared profile under dual claims, the claim reader extended. Proved by unit tests (box nesting at every page-orientation; bleed: auto with and without crop marks; marks never inside the bleed box; a target without dates refused); integration: pikepdf's boxes and intents, ExifTool's identifications, MuPDF's rendering of the media box showing the marks outside the trim. Leaves color.
  3. The color-management satellite. Delivers the package, IccProfile, the pipelines, the four intents, compensation, the device-link tables, the cache, both seams, ColorTransformBenchmarks. Proved by unit tests per tag type, per encoding and per hostile case; FsCheck — any input converted through a profile and its inverse at relative colorimetric returns within the recorded tolerance on matrix profiles —; integration: ΔE2000 against LittleCMS and iccDEV on the reference profiles, per intent, with and without compensation; iccDumpProfile's tag lists equal ours on every profile the corpus embeds; the determinism test on x64 and ARM64, Linux, Windows and macOS, and in M23's WebAssembly host. Leaves CSS.
  4. Color from CSS. Delivers device-cmyk() written as such, @color-profile through the resolver, color(), the spot and overprint extensions with their property-table rows, Separation, DeviceN and /All, RgbContent, the blending space. Proved by unit tests (a name with a space, a tint of zero, a gradient between two spots, an overprinted black text on a spot background); integration: Ghostscript's tiffsep on the print source gives one plate per process colorant and per spot, named exactly, with the overprinted text present on the plate beneath; pikepdf's color spaces. Leaves PDF/X.
  5. PDF/X-4 and PDF/X-4p generation. Delivers PdfXConstraintSet read from ISO 15930-7, the policy, the dual claims, X-4p's reference, and — if slice 1 confirmed the version cap — the Pdf16 output its ADR decided. Proved by unit tests per constraint alone, each refused, converted and removed as the policy says; integration: the referee on the reference documents and the print source; veraPDF on the dual files; M14's Factur-X referee on the invoice. Leaves the profile.
  6. The PDF/X profiles. Delivers the levels, the four PDF/X profiles and their catalog with clauses. Proved by a triggering, a silent and a borderline fixture per rule; the corpus's print files against the referee's committed or live verdicts, every disagreement fixed or recorded with its reason; our generated files reported conforming. Leaves conversion.
  7. Document color conversion. Delivers IPdfColorTransformProvider, PdfColorConversion, its report, the merge's ConvertParts, M09's Convert, M21's remedy row, color convert. Proved by unit tests per operator family, per image filter, per shading type, for a colored and an uncolored pattern, a d0 and a d1 glyph, a page group's blending space, a luminosity mask left alone, a spot kept and a spot converted; integration: tificc on each converted image within the tolerance; Ghostscript's plates before and after (spots unchanged); veraPDF on the converted EU regulation; MuPDF's soft proofs before and after within the recorded ΔE. Leaves PDF/VT.
  8. PDF/VT-1 on the batch. Delivers PdfVtOptions, the part tree written forward-only, metadata, the reuse hints, the identification, PdfDocumentParts, parts. Proved by unit tests (one record; a record of one page; ten thousand records under intermediate nodes; a metadata value refused); integration: pikepdf's walk of the tree gives our records and page ranges; one logo object referenced by every page; the referee on the batch; the batch within its budget. Leaves the PDF/VT profile.
  9. PDF/VT-2 and the PDF/VT profiles. Delivers VT-2 over X-4p, pdf-vt-1 and pdf-vt-2. Proved by fixtures per rule; received PDF/VT files, not in the corpus (below), against the referee; our batches conforming. Leaves rendering.
  10. Color-managed rendering. Delivers the converter behind M25's seam, soft proofing, render --soft-proof. Proved by the reference documents, the EU regulation and the print source rendered within the threshold of MuPDF's color-managed rendering; RenderBenchmarks rows with and without management. Leaves plates.
  11. Separations and overprint simulation. Delivers the plate device, previews, simulation, render --separations. Proved by unit tests (knockout, overprint with /OPM 0 and 1, /All, a DeviceN of three spots); integration: plates equal Ghostscript's tiffsep within the recorded threshold on every print file, the composites equal its and MuPDF's overprint simulation within it. Leaves the whole.
  12. The whole. Delivers the tool's options and verbs, PrintBatchBenchmarks, the fuzz targets for the ICC parser and the part-tree reader in M23's campaign, the documentation. Proved by every acceptance row, and CorpusToolTests.

Tests required​

Unit — tests/AdCodicem.Pdf.Tests, a folder per satellite as M10 placed its satellite's — the HTML and rendering satellites' in the test project M12.1's first slice gives the satellites that carry native assets, if it gives them one:

  • Geometry: every combination of bleed, marks and page-orientation; boxes on pages, never on nodes; a copied page with both trim and art boxes under each policy.
  • Intents and claims: X-4 and X-4p objects; the shared profile under dual claims; each identification read and written; a malformed claim; dates required.
  • Constraints: each row alone, under Refuse, Convert and RemoveClaim; the version cap if confirmed.
  • ICC: each tag type and pipeline; v2 and v4 Lab encodings; each intent; compensation on and off; absolute colorimetric with a non-D50 media white; a device link; a named-color profile refused.
  • Hostile ICC: a tag count of 2³² − 1, a tag past the profile's end, two tags overlapping, a CLUT declaring 15 inputs of 255 points over 200 bytes, a para of type 9, a curve of 2³¹ entries, a profile whose declared size disagrees with its stream, a truncated header — each rejected with color.profile-invalid, within its time and allocation budget, nothing allocated from a declared size unchecked.
  • Determinism: every transform's output bytes identical across two runs, two cultures, and the platforms CI runs.
  • CSS: device-cmyk() under each intent family; @color-profile refused by the resolver; each spot and overprint form; a name with non-ASCII letters; two declarations of one name.
  • Conversion: each operator family; each image filter and bit depth; each shading type; colored and uncolored patterns; d0 and d1; annotation appearances; soft masks untouched; spots kept and converted; JPEG left without the named step and converted with it.
  • PDF/VT: tree shapes; forward-only reservation with a non-seekable output; metadata vocabulary; reuse hints only on proven-independent XObjects; the read model on a tree with a cycle, a leaf without pages, pages without a leaf.
  • Profiles: a triggering, a silent and a borderline fixture per rule; every catalog entry with its references.
  • Plates: knockout; overprint under both modes; /All and /None; bands of one row.
  • Fuzzing: the ICC parser and the part-tree reader join M23's campaign, seeded with every profile the corpus embeds and the hostile set; a finding becomes a regression test first.

Integration — tests/AdCodicem.Pdf.IntegrationTests, every referee in a container (ADR 27), as Referees lists them: the chosen referee on every PDF/X and PDF/VT file generated and every received print file; veraPDF on every dual claim; qpdf --check on every output; pikepdf, ExifTool, Ghostscript, MuPDF, LittleCMS and iccDEV on what each confirms; M14's Factur-X referee on the PDF/X-4 invoice.

Acceptance conditions​

"The reference documents" are M12's renderings of tests/corpus/sources/invoice-fr.html, report-fr.html and contract-fr.html. "The print source" is a statement with a full-bleed band, a spot-color logo, CMYK brand colors and crop marks — not in the corpus (below). "The batch" is M12's thousand invoices from invoice-fr.html and a seeded record set. "The test output profile" is Adobe's U.S. Web Coated (SWOP) v2, a version 2 CMYK output profile, read at test time from the output intent of the committed vendor/eu-publications/distiller10-eu-consolidated-regulation-2015.pdf, never copied out of it; a version 4 profile and a FOGRA-characterized one are not in the corpus (below). "The referee" is the one slice 1's ADR chooses; where it runs outside CI, a row passes on its committed report bound to the output's hash. Remote rows close only on a green Remote corpus run, recorded in status.md.

DocumentsBehaviorVerified by
The reference documents and the print source, generated as PDF/X-4 and as PDF/X-4p on the test output profileThe referee reports no error; pikepdf reads nested boxes and one GTS_PDFX intent with its profile or reference; ExifTool reads the identification and Trapped; qpdf is silentCorpusPrintTests.Generated_pdfx_documents_pass_the_referee (new)
M14's Factur-X invoice, generated as PDF/A-3b and PDF/X-4 togetherveraPDF reports no error under 3b; the referee none under PDF/X-4; both intents point at one profile object; M14's Factur-X referee accepts the invoiceCorpusPrintTests.A_facturx_invoice_is_also_pdfx_4 (new)
The batch, generated as PDF/VT-1 and as PDF/VT-2 over PDF/X-4pThe referee reports no error; pikepdf's walk of the part tree finds one leaf per record with its pages and metadata; the logo is one object referenced by every page; throughput and allocation within M12's batch budget plus the recorded overheadCorpusPrintTests.Generated_pdfvt_batches_pass_the_referee (new)
remote/ocrmypdf/photoshop-cc2015-pdfx3-cmyk.pdf (a PDF/X-3:2002 claim); vendor/eu-publications/distiller10-eu-consolidated-regulation-2015.pdf (a SWOP intent under a PDF/A-1a claim); vendor/us-federal/illustrator-irs-pub1-english.pdf (a trim box and an art box together, PANTONE 301 C); third-party PDF/X-4 and PDF/VT files — not in the corpus (below)Our profiles give the referee's verdict on the level each claims or is asked about; every check the referee fails maps to one of our rules or a recorded reasonCorpusPrintTests.The_pdfx_profiles_agree_with_the_referee (new)
vendor/us-federal/illustrator-irs-pub1-english.pdf (a Separation and an overprinting graphics state); remote, remote/pdf-association/indesign-cs6-pdfua1-brochure.pdf (overprint, multiply) and remote/opf-format-corpus/acrobat9-portfolio-signed-3d.pdf (DeviceN with a type 4 tint transform); the print sourceThrough M03's rewrite, M06's merge, M09's stamp, M23's lossless optimization and M29's conversion with spots kept: every colorant name byte-identical, and Ghostscript's tiffsep plates before and after within the recorded thresholdCorpusPrintTests.Spot_colors_and_overprint_survive_every_operation (new)
The test output profile; the sRGB profile M14 embeds; the ICC's published sRGB profiles and the iccDEV test profiles — not in the corpus (below)Every transform within the recorded ΔE2000 of LittleCMS and of iccDEV, per intent, with and without compensation; the same bytes on every platform CI runs, WebAssembly includedCorpusColorTests.Transforms_match_the_reference_implementations (new)
Every ICC profile embedded in a committed document — output intents, ICCBased spaces, DefaultCMYK —, and in the remote ones nightlyParsed with the tag list iccDumpProfile reads, or rejected with color.profile-invalid; never an untyped exceptionCorpusColorTests.Every_embedded_profile_parses_or_is_reported (new)
vendor/eu-publications/distiller10-eu-consolidated-regulation-2015.pdf merged with documents/archival/libreoffice-report-pdfa2b.pdf (an sRGB intent under PDF/A-2b) and documents/invoice/chromium-invoice-fr.pdf, under a PDF/X-4 target on the test output profile, with and without ConvertPartsOne intent in the output; the RGB parts converted, or covered by a default space; the referee accepts; pdftotext's text unchanged; without a provider, the loss reported rather than a claim writtenCorpusPrintTests.Merged_output_intents_are_reconciled_or_the_loss_reported (new)
vendor/eu-publications/distiller10-eu-consolidated-regulation-2015.pdf (sRGB ICCBased content in page groups that blend in DeviceRGB, under a CMYK intent: veraPDF 1.30 fails its PDF/A-1a claim on 6.2.3.3 test 1, three checks, and on 6.4 test 3, a transparency group on each page)Converted to its own intent under the default DeviceOnly scope: the page groups blend in DeviceCMYK, veraPDF's 6.2.3.3 failure disappears and its 6.4 failure stays — conversion does not remove transparency groups, which part 1 forbids —, nothing else in its report changing; the sRGB content, which part 1 admits under any intent, converted only under DeviceAndIcc; MuPDF's soft proofs before and after within the recorded ΔE2000CorpusPrintTests.Device_color_is_converted_to_the_output_intent (new)
The reference documents, the print source, the EU regulation; remote, the Photoshop PDF/X-3 pageOur color-managed rendering within the recorded threshold of MuPDF's with color management onCorpusRenderingTests.Color_managed_renderings_agree_with_mupdf (new)
The Illustrator IRS publication, the print source; remote, the InDesign brochure and the Acrobat 9 portfolioOur plates equal Ghostscript's tiffsep plates by name and within the recorded threshold; overprint simulation within it of Ghostscript's and MuPDF'sCorpusRenderingTests.Separations_and_overprint_agree_with_ghostscript (new)
Every profile the corpus embeds, and the hostile profiles — not in the corpus (below) — as seedsThe nightly campaign over the ICC parser and the part-tree reader finds no untyped exception, hang or unbounded allocation over fourteen consecutive nights before the milestone closesFuzzingTests.Parsing_a_mutated_icc_profile_either_works_or_reports (new), the Fuzzing workflow's record
Every print output aboveByte-identical across two runs, two cultures, and the platforms CI runsCorpusPrintTests.Print_output_is_deterministic_everywhere (new)
The same operations through the toolThe AOT binary's print options and verbs produce the API's bytesCorpusToolTests.Print_verbs_match_the_api (new)

Corpus​

What the corpus holds​

  • PDF/X: one claim, remote — Photoshop CC 2015's PDF/X-3:2002 page with a SWOP CMYK intent, a DefaultCMYK, one profile embedded twice, CMYK JPEGs with /ColorTransform, an unused overprinting graphics state and an art box (pdfx-3-claim, cmyk-output-intent, default-cmyk-iccbased, duplicate-icc-profile, artbox).
  • CMYK output intents: Distiller 10's EU regulation, committed, a PDF/A-1a claim veraPDF rejects, blending its sRGB content in DeviceRGB page groups under a SWOP v2 intent (cmyk-output-intent) — which also gives the test output profile.
  • Other intents: sRGB under many PDF/A and Factur-X files; AdobeRGB (Ghostscript's PDF/A-1b, remote); PDF/E intents (the BOE gazette and the AbleDocs chapter, remote); a custom intent (iText 9, remote).
  • Spots and overprint: Illustrator CS6's IRS publication, committed, with PANTONE 301 C, overprint, and trim, bleed and art boxes together (separation-color-space, overprint-extgstate, trimbox-bleedbox-artbox); remote, InDesign CS6's brochure (overprint-extgstate, blend-mode-multiply) and the Acrobat 9 portfolio's DeviceN (devicen-nchannel, type4-function).
  • Page boxes: DILA's FOP notice (bleedbox-trimbox), the IRS publication, the Photoshop page (artbox).
  • ICC-based color: ICCBased images in 16 committed documents, CalRGB and CalGray, CMYK JPEGs (remote).
  • Batch material: M12's template and seeded record set, generated at test time.

What it lacks​

NeedWhyPriorityLikely source
PDF/X-4 files from InDesign, Acrobat or a RIP vendor, with a preflight reportThe PDF/X-4 profile needs third-party files to agree with; the corpus's only claim is PDF/X-31A public source: the Ghent Workgroup's and the Altona suites, terms read first, remote unless redistributable; a contribution from a print provider (W21)
PDF/VT files from variable-data toolsThe PDF/VT profile and PdfDocumentParts need real part trees, not only our own1A public source (vendors' published samples, the Ghent Workgroup's PDF/VT material if any, terms read); a contribution from an éditique provider (W21)
The referee's verdict on every print file of the corpus, recorded in the manifestExpectations come from the independent tool, as conformanceValid does for veraPDF; claimsConformance admits PDF/A and PDF/UA only1Generated here by the referee's harness, or by the maintainer's committed reports, into M20's list of claims — whose pattern admits PDF/X and PDF/VT — with this referee's verdict beside veraPDF's; slice 1 records it with its first reports
The print source: a statement or invoice with a full-bleed band, a spot-color logo, CMYK brand colors, crop marks, and its approved renderingEvery generated-print row needs one document that exercises bleed, spots and marks together1Generated here: tests/corpus/sources/statement-fr.html, with its Chromium rendering beside it for the visual referee as M12's sources have
Reference ICC profiles we may commit: the ICC's sRGB v2 and v4, a v4 CMYK output profile, a FOGRA-characterized one, a gray and a Lab profile, a device link, a named-color profileTransforms must be measured on more than one v2 CMYK profile read out of a document; the refused classes need a file1A public source (W19): the ICC's published profiles and iccDEV's test profiles, terms read; ECI's profiles (terms to verify); generated here: ArgyllCMS's colprof over published characterization data (terms to verify); each under tests/corpus/sources/third-party/ with its license and SOURCE
Hostile ICC profiles, one per bound aboveThe parser's tests and the fuzzing seeds1Generated here: hand-written in the test support, recorded
Documents with several spots, a spot DeviceN, overprint with /OPM 0 and 1, a CMYK-blended groupThe plates and the profiles meet each case in one file at most2Generated here: ReportLab's separation colors and overprint (to verify in the pinned version), Ghostscript's pdfwrite
PDF/X-1a and PDF/X-3 files from other producersTwo older profiles judged on one Photoshop page2Generated here: Ghostscript's pdfwrite with its PDF/X option (to verify which parts it writes); a public source
An invoice or statement run as a print provider receives itThe use case the milestone exists for2A contribution, anonymized (W21)

Traps​

  • The referee may not be free, and may not run in CI. A milestone whose acceptance cannot run is not specified; the maintainer settles it at the start, slice 1 records it before anything is built, and a committed report is bound to the bytes it judged, or it proves nothing about the next build.
  • A committed report goes stale with the runtime: Flate is deterministic per runtime (M03's trap), so a servicing release that changes the deflater changes every output's hash and asks for new reports.
  • The parts differ more than their names suggest. PDF/X-4 keeps transparency live, which X-1a and X-3 forbid; X-4 allows RGB and Lab when managed, X-1a allows CMYK and spots only; X-1a and X-3 identify themselves in the information dictionary, X-4 in the XMP.
  • /TrimBox and /ArtBox together are forbidden, and the corpus's Illustrator file has both, each equal to or inside its media box: a copy under PDF/X must choose.
  • A default is not a box. A trim box absent from a page defaults to its crop box, itself inherited from the page tree: every reader sees one, and a preflight still reports it missing.
  • Device color means nothing without an intent, and an RGB rg under a CMYK intent means nothing at all unless a default space says what it meant.
  • Blending happens in the group's space. A multiply computed in RGB and printed in CMYK is not the multiply the designer saw.
  • Spot names are exact. Case, spaces and their encoding in a name decide which plate a color lands on; a name is never normalized, and /All and /None are not spots.
  • Overprint changes meaning with the family: it has no effect in RGB, and /OPM 1 makes a zero CMYK component transparent. Converting color can change what overprints.
  • A luminosity soft mask is not color on paper: converting its group changes the mask.
  • v2 and v4 encode Lab differently, and a transform between the two versions that ignores it shifts every lightness by a fraction.
  • Black-point compensation is where implementations disagree; say which reading is ours.
  • Floating point is deterministic only if written to be: a fixed order, no implicit fused multiply-add, no Math.Pow (M22's trap).
  • An ICC profile is hostile input: tag tables, counts, offsets and CLUT sizes are bounded by the bytes present (invariant 4).
  • A JPEG cannot change color without a generation; conversion leaves it unless a lossy step is named.
  • PDF/X needs dates, and the library has no clock: a target without them is refused, never completed with "now".
  • The version cap: if ISO 15930-7 limits the header to 1.6, a writer that knows only 1.7 and 2.0 writes files every preflight rejects.
  • A dual PDF/A and PDF/X file needs one profile object under both intents and both identifications in one XMP packet, with the extension schemas PDF/A requires for what it does not predefine.
  • A ten-thousand-record run must not hold ten thousand of anything: part-tree arrays are bounded by intermediate nodes, and the shared artwork is one object.
  • Reuse hints are promises to the RIP: an XObject marked independent of its context that is not will print wrongly on every record after the first.

Documentation​

  • docs/website/docs/guides/print-production.md (new) — PDF/X-4 and 4p, PDF/VT, output intents, bleed and marks, dual claims with PDF/A and Factur-X, what the referee is and how a report is bound to a file.
  • docs/website/docs/concepts/color.md (new) — color spaces, output intents, the color-management satellite, rendering intents and compensation, spots and overprint, conversion and what it leaves, soft proofs and separations.
  • docs/website/docs/reference/css-support.md — bleed, marks, device-cmyk(), @color-profile, color(), the spot and overprint extensions.
  • docs/website/docs/concepts/validation.md, docs/website/docs/reference/validation.md and the generated rule reference — the PDF/X and PDF/VT profiles and their families.
  • docs/website/docs/reference/diagnostics.md — the codes above; docs/website/docs/reference/tool/ — the options and verbs.
  • docs/website/docs/introduction.md and docs/features/features.json — the print entry brought to its state.
  • docs/architecture.md — the color satellite's identifier, contents and dependencies; the core's transform seam; the plate device in .Rendering.
  • docs/corpus.md, the manifest schema and tests/corpus/README.md — PDF/X and PDF/VT claims and the referee's verdicts; docs/corpus-sources.md — the profiles' and suites' terms.
  • The ADRs: the referee, and the amendment of ADR 40 if the version cap holds — both slice 1's.
  • docs/status.md — the tolerances, the thresholds, the referee's versions, the benchmarks.

Exit criteria​

  • The referee is chosen and the version cap settled by the maintainer at the start, both recorded as ADRs by slice 1, and the referee's harness runs as its ADR decides.
  • PDF/X-4 and 4p generation, the dual claims with PDF/A and Factur-X, and PDF/VT-1 and 2 on the batch exist, each accepted by the referee.
  • The color-management satellite converts at every intent within the recorded tolerance of LittleCMS and iccDEV, byte-identical on every platform CI runs; M25's seam and the core's are implemented.
  • Spot colors, overprint and CMYK are written from CSS, and survive every operation of the library.
  • Document color conversion works, and M06, M09, M20 and M21 use it where they deferred to this milestone.
  • The PDF/X and PDF/VT profiles agree with the referee on every print file of the corpus, or each disagreement is recorded with its reason.
  • Soft proofs, separation previews and overprint simulation agree with MuPDF and Ghostscript within the recorded thresholds.
  • The priority-1 gaps above are filled; each remaining gap is recorded in docs/corpus-contributions.md.
  • The acceptance conditions above pass on the corpus, in CI — or through the referee's committed reports bound to the outputs' hashes, as the ADR decides —, with no document skipped, and the remote rows on a green Remote corpus run recorded in status.md.
  • Unit tests cover each behavior, its degenerate cases and its hostile ones; the FsCheck property holds; the ICC parser and the part-tree reader have run fourteen consecutive nights in the fuzzing campaign with no open finding.
  • Integration tests run the referee, veraPDF, qpdf, pikepdf, ExifTool, Ghostscript, MuPDF, LittleCMS, iccDEV and M14's Factur-X referee, each in a container.
  • ColorTransformBenchmarks and PrintBatchBenchmarks run with MemoryDiagnoser, their rows in benchmarks/budgets.json; docs/status.md records the measurements.
  • The print options and verbs ship in the dotnet tool and the AOT binary, documented.
  • The documentation site publishes the pages listed above, and features.json matches what exists.
  • Every page of Documentation is written in its Diátaxis section, one mode per page (ADR 47).