Patch 06 records that DeviceN fails export under PDF/A-1 to -3 (its tint transform is
PostScript) and is written under PDF/A-4; patch 08 records the second output intent and
the caller's CMYK destination profile.
The plate helper renders all inks of a page with one render cache, as a separating caller
does, so ICC transforms are built once per page instead of once per plate. The mutated
profile test changes the header fields a reader checks instead of every 16th header byte,
and samples the tag data more sparsely: each profile that still loads costs a fifth of a
second in a debug build.
Process tints become DeviceCMYK, one spot a Separation with its alternate, other mixes a
DeviceN space with process inks first, registration and None the All and None
separations. Lab spot alternates convert through the printing condition, as resolve does.
A mesh gradient took its color space and component count from the first
patch's first corner, so a later corner in another space was truncated or
padded with zeros and written as the wrong color. Mesh corner colors now go
through the same rule as gradient stops (`common_color_space`): process
colors alone are DeviceCMYK, one spot alone is Separation, and mixes are
DeviceN with process colorants first, then spots. Colors that rule can't
unify (RGB with CMYK, registration) fail the document with
`KrillaError::MixedMeshColorSpaces`. The opacity soft mask is unchanged, and
a mesh in one space writes the same bytes as before.
patches/10-mesh-gradients.patch holds #184's change and this fix, so
patches 01 to 10 reproduce src; the ledger section 10 and SOURCE.json follow.
paint::MeshGradient / MeshPatch: Coons patches with a colour and opacity at each corner, written as
one Type 6 (Coons patch mesh) shading through pdf-writer's StreamShading, so viewers and RIPs blend
them smoothly at device resolution instead of an app cutting each patch into flat-colour pieces.
Same path as the other gradients: a shading pattern fill, and corner opacities below 1 as a
luminosity soft mask of the same mesh in gray. 32-bit coordinates over the mesh bounds, 8-bit
components, Flate. Recorded in vendor/krilla/README.craft.md and SOURCE.json.
`chunk_container` wrote `xmpMM:RenditionClass` as `proof` on every PDF krilla
produces. That property says which rendition a file is (XMP Part 2, Media
Management): `default` is the master, `proof` a proof copy. So every export
told a prepress system that a print deliverable was only a proof, and PDF/X
output carries the same claim.
Noticed on storytold/designcraft#1207, where PDF/X-4 output was being checked
against Acrobat's preflight; it is not what fails that check, but it is wrong
and it reaches every app that writes PDF through this tree.
Checked with DesignCraft pointed at this tree: `xmpMM:RenditionClass` reads
`default` for a PDF/X-4 export and for an ordinary one, where both read `proof`
before.
craft-plate-test writes JSON scenes through craft-pdf and checks the written PDF's
structure (CI, under a second) and, locally only, its separated plates through
Ghostscript tiffsep (scripts/plate-check.sh). Apps can run the same scenes through
their own export as a dev-dependency.
Print PDFs written through krilla separated wrongly: transparency groups blended in RGB
(K 50% over C100 separated as C84 M34 Y32 K37 instead of C50 K50), and there was no
overprint, knockout, DeviceN, soft-mask backdrop or print output intent.
Patches in vendor/krilla/patches/, recorded in README.craft.md and SOURCE.json:
- 02 blending color space: GroupColorSpace { Rgb (default, unchanged output), Cmyk }
- 03 overprint: Surface::push_overprint (/op /OP /OPM)
- 04 knockout groups: Surface::push_group(GroupOptions { isolated, knockout })
- 05 soft-mask backdrop and transfer function (/BC /TR)
- 06 DeviceN and NChannel colors for fills and strokes
- 07 DeviceN images and shadings
- 08 print output intent (GTS_PDFX), /Trapped, PDF/X version markers
craft_pdf::write::print::settings(PrintColor) turns them on for print export.
Aliases (a spot printed on another ink's plate), spot to process and all
spots to process, with resolve applying both to a colour. The README
describes the coverage, composite and ink manager API and how the apps use
it for a Separations Preview and an Ink Manager.
Per-ink coverage at a pixel and over a page, TAC per pixel, the pixels and
connected regions over a TAC limit, and an sRGB composite of chosen plates:
process plates through the printing condition, spot plates multiplied over
them by their alternate's appearance. Ported from the data parts of
pdfcraft #2770 separations.rs.
Co-authored-by: Guillem Hermida <66697068+guillehr2@users.noreply.github.com>
The ink model (process and spot inks, InkSet with PDF name lookup,
InkColor, Overprint) and plates(pdf, page, dpi, opts): ink discovery
and one gray film per ink through craft-pdf's raster plate mode, with
process colour converted through craft-cms (output intent, else the
caller's profile or FOGRA39). Plate rendering, ink discovery, caps and
output-intent selection port pdfcraft #2770's separations.rs.
Co-authored-by: Guillem Hermida <66697068+guillehr2@users.noreply.github.com>
Bring storytold/pdfcraft#2974 (source commit
bae69819594a6385a027fec1f67e6b3c36c259b4) onto the vendored
hayro-interpret and hayro. Knockout transparency groups (/K true) knock
out: while one is innermost, fills, strokes, glyph outlines and shadings
are drawn with Compose::Copy, weighted by coverage. An unmasked image's
rectangle and a nested group's shape are cleared before their layer
composites; the nested group's shape comes from an erase pass over its
content. Masked images and stencils composite as before.
Code comments label the change "Knockout patch (pdfcraft #2974)": the
pull request numbers it hayro-interpret patch 39 and hayro patch 19,
which pdfcraft may renumber. The erase pass resolves resources with
Resources::from_scope, as this tree's form drawing does.
crates/pdf/tests/knockout.rs adapts the pull request's test to the
raster path (without its preview case: craft-pdf has no preview mode).
A plate-mode test checks that a knockout group of two 50% squares
separates with only the top square in the overlap. Options documents
that color_replacement also maps plate films. Ledger, NOTICE and the
source manifests are updated.
Co-authored-by: mtlcl410 <2368091+mtlcl410@users.noreply.github.com>
CmykSimulation converts process colour to an output intent's CMYK
through a color::CmykConversion: sRGB colours, and CIE XYZ relative to
the D50 PCS white (color::PCS_WHITE) for Lab and ICCBased colours.
CmykSimulation::new keeps the moxcms conversion from pdfcraft #2770,
which takes XYZ on through its ProPhoto hub; with_conversion takes a
caller's, so a crate can separate with its own colour management.
crates/pdf/tests/plates.rs adapts #2770's plate tests to the raster
path: process, spot, DeviceN, All/None, ICC CMYK, gray and RGB colours,
images, shadings, uncoloured patterns, soft masks, OP/op/OPM, the ink
sink, output-intent simulation with #2770's synthetic ICC profiles, and
a caller's conversion. The ledger and NOTICE name the pull request.
Co-authored-by: Guillem Hermida <66697068+guillehr2@users.noreply.github.com>
Bring the vendored interpreter part of storytold/pdfcraft#2770 (Output
Preview, source commit 6fcf2a4aca29e943e28cb69017ff8f87d00656c6) onto
the ec0a965 snapshot. It applies without conflicts.
InterpreterSettings::plate draws one ink's separation: every paint
draws that ink's tint as a gray film, by the colour space's ink use and
tint. The graphics state reads OP, op and OPM, and an overprinting
paint that doesn't specify the plate's ink leaves the plate as it is
(ISO 32000-2 8.6.7). InterpreterSettings::ink_sink reports the spot
inks paints use, and color::CmykSimulation converts process colour to
an output intent's CMYK profile.
PdfCraft's separations.rs, Output Preview UI and automation are not
part of this port. The ledger in vendor/README.craft-reader.md names the
pull request, author and source commit.
Co-authored-by: Guillem Hermida <66697068+guillehr2@users.noreply.github.com>
Bring vendor/hayro, hayro-interpret, hayro-syntax and hayro-jbig2 to
storytold/pdfcraft ec0a965074f376a5bf610435948e4e5184bf3299 (main,
2026-10-19), from 7437bde. The local patches (per-document decode
limits, incomplete-content diagnostics, Hayro 0.8 resource scopes,
nesting cap, iterative page tree, TJ fill order, transfer rounding,
safety repairs) are carried by a three-way merge without conflicts.
The re-sync includes transparency group /I and /K reading (pdfcraft
#2027), patches 28, 29 and 31-34, and the reader fixes listed in
vendor/README.craft-reader.md. SOURCE.json manifests, NOTICE and the
craft-pdf README name the new source revision.
Skylar's review of 886a0b3:
1. tc/tid go locale by locale: in each locale the context or @id row,
then the plain row, so pt-br's plain row beats pt's context row.
2. pt (and pt-* other than pt-br) uses pt-PT plural rules: 0 is plural.
3. A script subtag (sr-Latn-RS, sr_RS@latin, be_BY@latin) never picks a
code of another script; sr-Cyrl-RS still picks sr.
4. A row defined twice is reported and the first kept, as in the checker.
5. The pseudo locale is debug-only via from_code, get marks too, and
looked_up() lists what was looked up for an app's audit.
6. intl_pluralrules pinned to =7.0.2; plural_form_counts copied from the
checker, plus pt.
7. I18n::new takes the app's offered codes: languages(), from_code,
match_tag and system_lang only return those; others stay fallbacks.
Smaller: en/pseudo/repeated codes are skipped and reported,
system_lang is cached, zh-hant-hk falls back to zh-hant, rtl adds ckb,
dv, ps, sd, ug and yi.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Review fixes (CodeRabbit): a glibc @latin/@cyrillic modifier becomes the
script subtag and only codes with that script are tried. sys-locale
drops the modifier, so on desktop Unix system_lang reads LANGUAGE,
LC_ALL, LC_MESSAGES and LANG raw, in its order. README: tl! qualifies
CURRENT with $crate; get follows the locale chain but never the plain
row or English.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Final review fixes: a European Portuguese locale wins over pt-br when both
are embedded; sr-latn no longer falls back to Cyrillic sr; get() looks up
one context with no plain or English fallback (key-cap names).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F9fM3mv1PUyKpk5rdwUQrW
resolve_absolute_refuses_results_that_are_not_absolute resolved links against a
document at /home/u/docs/a.doc outside its cfg!(windows) branch. On Windows that
path is rooted but has no drive, so it is not absolute, resolve_absolute refuses
the result as documented, and the unwrap panicked. The Windows arm already covers
the same three cases with a drive. Found by the new docpath CI job on its first run.
The per-crate workflows name their crates with -p, so the new crate had no job
and its tests, including the Windows-only colon and stream assertions, never ran
in CI.
A test is not instant and the address field stays editable while it runs,
so its answer can arrive after the address has changed. `set_models` then
recorded the models against whatever the field held by the time it
returned, filing one endpoint's models under another — the mislabelling
`models_fit` exists to prevent, reached the asynchronous way rather than
the synchronous ones already closed.
`set_models` now takes the address the test was started against and
returns whether the models were taken, so an answer that outlived its
address is dropped and the caller can say the result is out of date. The
parameter replaces the old signature rather than joining it: there was no
second legitimate caller, and leaving an address-free version beside it
would have left the racy call the shorter, easier one to reach — against
this module's rule that the rules live once so two screens cannot
disagree.
Also fixes a pre-existing mismatch found while testing this:
`from_connection` recorded the loaded address untrimmed while `prepare`
compares it trimmed, so a stored endpoint with stray whitespace dropped
its models on the first save.
Model provenance across loaded and preset endpoints and repeated endpoint edits, so models from one endpoint are not relabelled as another's, and eligibility resolved in both directions: a model must accept every input the tool sends, and the tool must supply every input the model requires (new ResolveError::InputRequired).
All 18 checks pass on a40245bd, including craft-ai-core fmt, clippy -D warnings, tests and the wasm32 check on Ubuntu, Windows and macOS.
Note for callers: after a tracked endpoint change, use set_models for newly discovered models; direct assignment on fresh custom drafts and unchanged saved endpoints still works. The asynchronous in-flight probe race is separate and not addressed here.
resolve_absolute refuses a result that is not absolute on the running
platform, such as a relative link in a document that has no path yet.
It is the function to call before opening a file.
The eager tuple computed o + 8 even when the first read had failed,
overflowing a 32-bit usize at offsets near u32::MAX (n = 2 with
size = u32::MAX - 20 reaches it: o fits, o + 8 does not). The record is
now bounds-checked in one slice and the fields read inside it, the same
complete-record pattern the profile reader uses elsewhere.