stet 0.2.1 — PDF/X CMYK rendering correctness
[0.2.1] — 2026-05-09
Patch release focused on PDF/X CMYK rendering correctness against the
Ghent PDF Output Suite (GWG)
test corpus. Fixes a family of bugs where ICCBased / Lab / DeviceN
fills, images, and transparency groups didn't round-trip through the
document's /OutputIntents profile correctly, producing visible
"X" markers in calibration swatches that should render uniform.
This is an additive, non-breaking release. Downstream code does
not need to change. New public API on stet-graphics::IccCache and
a new rendering_intent: u8 field on stet-graphics::ImageParams
are documented below.
Highlights
- GWG 13.3 — ICCBased RGB paints with
/OP trueno longer route
into the custom-spot overprint path; per PDF 1.7 §11.7.4.5 they
paint as if/OPwere false. - GWG 16.1 — per-intent PDF/X proofing chain (
source A2B → PCS → OI B2A → CMYK) is built for every registered ICCBased RGB
profile, threaded throughop_ri/ ExtGState/RI. - GWG 16.4 — transparency groups with no
/CS(inherit) now
resolve correctly to the parent's CMYK compositing space when
the parent is a/CS DeviceCMYKgroup. - GWG 17.2 — ICCBased images now go through the proofing chain
viaconvert_image_8bit_with_intent(was bypassing the
OutputIntent roundtrip and rendering via direct source→sRGB). - GWG 22.1 — Lab fills populate
DeviceColor::native_cmykvia
a directLab → PCS → OI B2A → CMYKchain (matches Adobe ACE),
and the OutputIntent install path pre-warms the sRGB→CMYK
reverse transform so the parallel CMYK buffer never falls back
to the PLRM(1-r, 1-g, 1-b, 0)formula. - WASM viewer —
open_pdfnow applies the document's
OutputIntent before storing the cached state, so PDF/X documents
render in the browser the same way they do in the CLI.
Added — public API (additive, non-breaking)
stet-graphics:
IccCache::convert_to_oi_cmyk(hash, components, intent)— run an
RGB ICC color through the proofing chain at the given intent and
return the intermediate OutputIntent CMYK.IccCache::convert_lab_to_oi_cmyk(l, a, b, intent)— direct
Lab → OI CMYKvia the OI's per-intent B2A LUT.IccCache::convert_image_8bit_with_intent(hash, samples, pixel_count, intent)— bulk image conversion with explicit
rendering intent.IccCache::convert_color_with_intentand
convert_color_readonly_with_intent— per-intent single-color
conversion.IccCache::prepare_lab_to_oi_cmyk()— pre-build per-intent
Lab→OI samplers; pair withprepare_reverse_cmyk().IccCache::intent_from_pdf_byte(b: u8)— map PDF rendering-intent
bytes (0..3) toIccRenderingIntent.pub use moxcms::RenderingIntent as IccRenderingIntent.pub struct LabToCmykSampler(inicc::perceptual) with
pub fn sample_pdf_lab(l, a, b).- New field
ImageParams::rendering_intent: u8. Default is0
(Perceptual). Per the documented "be a reader, not a writer"
policy for param structs (CLAUDE.md), this is additive and not
treated as a SemVer break.
stet-pdf-reader:
PdfDocument::apply_output_intent_as_default_cmyk()now also
pre-warms the sRGB→CMYK reverse and per-intent Lab→OI samplers
in addition to its previous behaviour. No signature change.- Image XObjects with
/Intentnow propagate the per-image
rendering intent intoImageParams.rendering_intent, overriding
the gstate/RIper ISO 32000 §11.3.4.
stet-render:
build_icc_cache_for_listnow also pre-warms the per-intent
Lab→OI samplers when proofing is enabled.
Fixed
- DeviceGray painted in a PDF/X DeviceCMYK page group now routes
through the K plate (matches DeviceCMYK 0/0/0/(1−g) byte-for-byte). - DeviceN images with a non-CMYK alternate space go through the
overprint path so process plates aren't disturbed. - Paired
/OP true /op trueExtGStates are now treated as a
"strict overprint" signal (matches Adobe Illustrator's emit). - The custom-spot overprint dispatch and the parallel CMYK buffer's
is_custom_spotheuristic both now require
process_cmyk.is_some()so proofing-chain ICCBased RGB stays out.
Verification
- 11 GWG calibration tests pass: 13.0 / 13.3 / 16.1 / 16.4 / 17.2 /
17.3 / 22.1 / 23.0 / 1.0 / 3.0 / 12.0 / 8.0.1. cargo test --workspace --releaseclean.cargo fmt --all -- --checkclean. No new clippy errors.wasm-pack build --target web --release crates/stet-wasmclean.
Crates published at 0.2.1
stet, stet-cli, stet-fonts, stet-graphics, stet-core,
stet-ops, stet-engine, stet-render, stet-viewer,
stet-pdf-reader, stet-pdf. The vendored stet-tiny-skia /
stet-tiny-skia-path forks remain at 0.11.4. stet-wasm is
excluded from crates.io and bumped to 0.1.1 independently.
Full changelog: v0.2.0...v0.2.1