The conflict was in links.rs, where main's atomic relink work (b050343a) and
this branch's path gate both changed `update`. Kept main's mechanism (build the
replacement with relinked_asset, then insert it) and this branch's gate (a link
craft-docpath refuses is missing and never read).
relinked_asset now takes the path to read and the link to record separately.
Previously they were one argument, safe only because update passed the raw stored
link; passing the resolved path to the old signature would have rewritten every
relative link to an absolute one on each refresh.
Also passes None for the document in main's embedded-status test, where the asset
has no link so the document is irrelevant.
Resolve 12 conflicted files. Where both sides changed the same code:
- format: keep main's bounded-read load_zip (MAX_ENTRIES, read_entry) and
this branch's u64 version reporting, so a version above u32::MAX is named
as written rather than clamped. Both sides had fixed the same truncation.
- render: main's natural_size supersedes image_natural_size (it already
reads an EPS bounding box and also handles PDF pages); callers in table
and data merge now use it.
- images: keep the DSC-aware header scan and (atend) trailer lookup, take
main's classic-Mac line endings, and carry its overflow guard into
parse_box. eps_proxy draws main's simple rectangles before falling back
to the placeholder, still over a retained EPS source.
- engine/file: take main's placement shape (add_asset, crop box geometry,
place cursor, finite_rect, object effects) and keep this branch's
validate_placement in both branches; edit() works on a clone and commits
only on Ok, so validating inside it stays all-or-nothing.
- engine/links: keep building the replacement before edit() so a file that
cannot be read leaves the document untouched, and move main's crop_pdf
refresh into relinked_asset, which both relink and update go through.
- engine/export: EPS preview warnings join the font-embedding warnings
rather than shadowing them.
- idml: an EPS element keeps ActualPpi (its pixels are a preview raster);
main's PDF exclusion is unchanged.
Cargo.lock regenerated. Tests and doc strings from both sides kept.
Graphic literals (cli perf, doc assets/item, engine datamerge/export/file/links/mathexpr/media/table/sample, epub, format, pdf, pptx, render, tools, ui-egui properties, and tests): name the branch's clip and wrap (#561, #381) and the PR's fill.
crates/doc/src/item.rs: Graphic keeps wrap and gains fill.
crates/idml/src/import.rs: read the image swatch, then the branch's styled FrameFittingOption lookup; the graphic keeps its wrap.
crates/idml/src/tests/assets.rs: keep both new tests.
crates/render/src/lib.rs: keep #362's crop-box sizing of placed PDFs and colour the resulting pixmap.
crates/render/tests/colorize.rs, crates/epub/tests/grouped_reading_order.rs, crates/compose/src/tests.rs: literals name the branch's Asset and Graphic fields.
crates/pdf/src/export.rs: the soft mask interpolates by the standard's raster mode (the branch has no per-exporter interpolate switch).
docs/gaps.md, docs/target-app-parity.md: status lines list both sides' changes; gap text keeps the clipping-path items and adds the image swatch.
An EPS that came in with a document (InDesign embeds its QR codes as
EPS) drew nothing: only File › Place converted EPS to a proxy. Now
`decode_rgba` draws any EPS through `eps_proxy` and `natural_size` measures
it by its bounding box in points, so screen, PNG and PDF show it while the
document keeps the EPS itself.
`eps_proxy` also draws PostScript that only fills rectangles in solid
colours (setcmykcolor, setrgbcolor, setgray, rectfill and procedures that
stand for them, as InDesign's QR codes are written), at 8 px per point so
modules stay crisp. Anything else keeps the preview or placeholder. Input is
bounded: tokens, rectangles, painted area and procedure definitions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- designcraft-color: ICC profiles (built-in RGB working spaces, user .icc files) go through
craft-cms. The default CMYK working space is craft-cms's Coated FOGRA39; CMYK swatches show
relative colorimetric with the settings' black point compensation, as placed images do.
Generic CMYK and Device CMYK stay selectable; names of the profiles FOGRA39 stands in for
resolve to it; ColorSettings::with_known_profiles replaces unavailable saved names with the
defaults (used when the UI restores saved settings). Lab, gamut error and proofing of ICC
CMYK spaces use craft-cms transforms. The Generic CMYK output profile is encoded by craft-cms.
- designcraft-images: Profile, Transform, TransformOptions, Intent and Builtin are craft-cms's;
its black point compensation replaces the module's sRGB table. Untrusted profiles are still
ignored with a log line.
- designcraft-pdf: the PDF/X output intent is the working CMYK space's profile: the FOGRA39
built-in with identifier FOGRA39, user profiles as loaded, Generic CMYK otherwise.
- moxcms is no longer a direct dependency.
Placed rasters are converted to display sRGB once, at decode time, in one
module (designcraft-images colour):
- embedded ICC profiles in JPEG, PNG, TIFF, WebP and PSD (RGB and grey) are
applied; sRGB-like profiles leave the pixels exactly as decoded
- CMYK JPEGs (incl. YCCK, Adobe-inverted values), CMYK TIFFs and CMYK PSD
composites go through their embedded CMYK profile, else the working CMYK
space, with the colour settings' intent and black-point compensation
- oversized (> 16 MiB), unreadable or mismatched profiles are ignored with a
log line
The render image cache is rebuilt when the colour settings change. PDF/A
exports of CMYK TIFFs get the same managed RGB as the screen.
Conflict: crates/fonts/src/fontdb.rs: FontFace, CatalogEntry and ScannedFace keep next's aliases and kind plus the PR's postscript (both catalog paths fill it); next's family_kind and has_family, plus the PR's ScopedFonts::postscript_face
Conflict: crates/fonts/src/tests_sysfonts.rs: both tests
Conflict: crates/images/src/lib.rs: next's case-insensitive query (#404) with the PR's PostScript lookups, using next's spelled() in place of the PR's has_family helper; both tests
A font-family that names no family but matches a face's PostScript name
(as Illustrator's SVG export writes them, e.g. 'MyriadPro-Regular') now
sets the text in that face, from the bundled, loaded or installed fonts.
The name picks the face, so font-weight and font-style don't change it.
Family names and the generic fallbacks resolve as before.
Part of #430
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DesignCraft matched an SVG font-family case-insensitively when importing
the installed faces, but the final fontdb query compares names exactly,
so "DeJaVu SaNs" loaded DejaVu Sans and then set the text in the serif
fallback (Source Serif 4) without a word. Text frames already accept any
case. The SVG font selector now runs usvg default query with each named
family spelled the way the font database spells it; family order,
generics, weight, stretch and style are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BSB7fMQUccqFQekksDGWpo
Rust 1.98 adds the clippy lint chunks_exact_to_as_chunks. It flags two
loops in the PSD decoder, so `cargo xtask ci` stops at the clippy step on
unmodified main for anyone on current stable.
Both loops now use as_chunks / as_chunks_mut (stable since 1.88, below the
workspace rust-version 1.90). The chunks are arrays, so the try_from that
could never fail goes away too. Decoded pixels are unchanged; the existing
PSD tests cover both paths.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Header dimensions are untrusted: a tiny file claiming a ~460 MiB composite
allocated it before finding the data missing. ImageData::holds verifies
raw planes fit in the data, or that RLE row counts each reach a full row
and sum to no more than the data, before the RGBA buffer is allocated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decode the merged composite of .psd/.psb files in designcraft-images
(public file format spec) instead of the psd crate, which mapped
channels by count: grayscale with extra alpha/spot channels rendered
yellow, blue or invisible, and RGB + one spot channel was read as RGBA.
Channels now map by colour mode (Bitmap, Grayscale, Duotone, Indexed,
RGB, CMYK via the uncalibrated CMYK formula, Lab; 8 and 16 bit; raw and
PackBits). The first extra channel is the alpha only when the layer
count is negative (also via Lr16/Lr32), un-matted from white; other
extra channels are ignored. Damaged or oversized files return None
without panicking. Drops the psd dependency and its panic paths.
A CMYK TIFF went into every PDF, PDF/X-4 included, as a DeviceRGB image:
load_image sent anything that wasn't JPEG, PNG, GIF or WebP through
designcraft_images::decode_rgba, and `image` decodes CMYK TIFFs to RGB. 100%
K came out as RGB black, which the press separates as four-colour black
(#35).
designcraft-images now reads a CMYK TIFF's ink values with the tiff crate
(the version `image` already uses): 8- or 16-bit, interleaved, opaque or
with unassociated alpha. PDF export writes them through krilla's CustomImage
as DeviceCMYK, as it does a CMYK JPEG, and leaves out the TIFF's own ICC
profile so the numbers go to the output intent's press unchanged. PDF/A
exports stay RGB. A CMYK TIFF this can't read (planar, premultiplied alpha,
another ink set, over 256 MiB) is still converted, now with a warning. The
screen keeps decoding to RGB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shippori Mincho (8.7 MB) and its licence leave assets/fonts. Japanese fonts now
come from storytold/craft-fonts when built with CRAFT_FONTS_DIR (never a
Cargo.toml dependency): crates/fonts/build.rs embeds its manifest as
designcraft_fonts::CRAFT_FONTS. Native builds embed all four fonts; wasm32
embeds BIZ UDPGothic Regular only (web size budget).
- UI (egui): the Jpan faces at the end of every family, BIZ UDPGothic first
(Bold first in the semibold family).
- Documents: FontDb loads them after the bundled fonts, Mincho first, so they
are the Japanese fallback ahead of system fonts; SVG text gets them too.
- Vertical alternates test runs on every craft-fonts face and skips when
CRAFT_FONTS is empty; new UI no-tofu test and without-craft-fonts tests.
- FreeBSD CI tests with craft-fonts; release.yml builds every platform with
CRAFT_FONTS_DIR + CRAFT_FONTS_REQUIRED=1 at a pinned commit, packages carry
OFL-<family>.txt; NOTICE, ASSETS.md, AGENTS.md/CLAUDE.md, README, ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An .xlsx cell reference far from the others no longer allocates the whole
range before the cell cap (billions of cells, out of memory), and long
column names can't overflow. IDML import doesn't trust zip part sizes for
up-front allocation and adds header/footer counts saturating. Tagged Text
and IDML export use let-else; the font fallback has a compiled-in last
resort. These crates deny the panicking lints.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>