Main now aborts an export when a stored hidden layer can no longer be
applied (#876). Vector export follows it: a layer hidden in Object Layer
Options that the placed PDF doesn't have, because the file has no layers
or was relinked, fails with an error naming the file instead of exporting
everything visible. The image-path tests for oversized images and
undecodable content export as PDF/A-2b, which still rasterizes placed
PDFs with hidden layers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep source defaults and per-frame hidden layers in plain and PDF/X-4 vector exports, including independent repeated placements, booklet output and book merging. Accept usage-application arrays and resource-only groups without a catalog configuration.
Use file-named warnings and all-ON defaults for recoverable default-configuration problems and unsupported base states. Add readable groups omitted from OCGs without discarding their visibility, combine concurrent recovery warnings, and generate missing names only when no frame override needs name matching. Retain errors for contradictory states, unresolved references and ambiguous name overrides.
Cache prepared variants per asset and hidden-name set while replaying recovery warnings through the existing deduplicating channel. Keep original source bytes when every group is visible and named, so reader-repaired cross-reference tables remain usable without an incremental layer update. When hidden groups or generated names require writing, reject an unusable cross-reference table with a file-named damage error and retain the first placement failure.
Cover visibility, recovery, names, independent placements, repaired cross-reference tables and vector output with 46 optional-content integration tests, plus preparation-cache and shared-buffer unit tests.
* chore: resolve SonarCloud maintainability issues
- Replace lambda closures with method references (S1612)
- Replace wildcard imports with explicit imports (S2208)
- Refactor complex functions with extracted helpers (S3776)
- Replace OR pattern with range pattern in math parser (S9047)
* style: apply rustfmt to warichu helper
* fix(compose): revert slice conversion in tests and remove unused var in warichu
* Group warichu row layout scalars to satisfy S107 parameter limit
* style: satisfy rustfmt in warichu.rs
---------
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
Preserve existing PDF catalog entries and full object generations. Accept literal null trailer entries while failing closed on encrypted/hybrid/ambiguous or unreadable history. Validate visibility variants atomically before document edits, suppress stale unsupported canvas overrides, and preserve conservative content cropping without overcropping empty optional-content stubs.
Local full CI: all 9 gates passed, 3063 tests passed and 6 skipped, all 16 WASM-safe crates checked. Independent qpdf/preservation/Poppler checks passed for17 supported inputs;6 refusal cases stayed refused. All11 additional Prev controls passed. GitHub CI and CodeRabbit passed on00d5fa5c; CodeRabbit confirmed the Prev finding addressed. Author history preserved; no native format changes.
Reject a non-null Prev entry that cannot be read as an offset instead of silently treating it as the end of the guarded trailer chain. Keep absent/null end markers and readable numeric offsets unchanged.
Reproduced CodeRabbit discussion_r4366245854 with an independent probe and a failing native regression before the fix. The synthetic regression covers seven unreadable value types plus absent/null and integer/real history controls.
Validation: all 9 cargo xtask ci gates passed (3063 tests passed, 6 skipped; 16 WASM-safe crates). One unrelated GPU test was labelled leaky by nextest in that run and passed cleanly in an isolated rerun. Independent checks: 17 original supported PDF variants still pass qpdf, logical preservation and Poppler pixels; 6 original refusal cases remain refused. All 11 additional Prev controls pass (7 refused, 4 accepted).
Integrate current main (d8d78851) without rewriting the contributor's commits, retaining craft-libs 9f75d3dc.
Accept literal null trailer entries while rejecting actual or ambiguous encryption/hybrid markers. Structurally inspect bounded xref/trailer history instead of adopting trailer-looking comments. Preflight all selected source variants before editing/undo; preserve reset-to-defaults. Suppress stale unrewritable canvas overrides rather than revealing source content. Keep conservative all-content crop fallback without overcropping layerless/null stubs.
Validation: cargo xtask ci, all 9 gates passed; 3062 tests passed, 6 skipped; all 16 WASM-safe crates checked. 17 supported variants passed independent qpdf, logical preservation and Poppler pixel checks; 6 unsupported/repair-only cases refused. Direct-harness Claude Opus 5 reviewed the exact main-relative delta with no blocking findings.
No native-format changes or unrelated layer features. Synthetic regression fixtures are generated in test code.
* Anchored objects: Relative to Spine mirrors custom positions on right-hand pages
* Anchored objects: page side from the frame's spread position; footnote cache keyed by page side
* PDF: craft-pdf, krilla and hayro from craft-libs main (input bounds and crash fixes)
* PDF: craft-libs 9f75d3dc, with hayro-cmap from its vendored copy
* Links: use resolve_absolute for stored paths
* Links: check for a missing linked file with is_ok_and
* CI: release only with git dependencies from storytold
The winit patch from skylartaylor/winit at its pinned rev is the one other source allowed.
* Links: Relink to Folder only uses a name that stays a file name in the folder
* CI: the git-sources check reads Cargo.lock as TOML, needs it current and refuses config patches
* CI: the git-sources check parses .cargo/config and .cargo/config.toml for source replacement, patches and paths
* Links: rustfmt
* Links: Relink to Folder only takes a file directly in the chosen folder
* Links: Relink to Folder compares folders as the file system resolves them
* fonts: Georgian UI text from craft-fonts' Noto Sans Georgian
font_definitions registers the Geor face and appends it to every family, like
Arabic (and to the Arabic families after the CJK faces); the wasm filter in
crates/fonts/build.rs keeps the face. The installed-font probe for Geor stays
skipped whenever craft-fonts covers the script. Bump craft-fonts-ref to a71c20e
(storytold/craft-fonts#27).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
* Release: build the web version with craft-fonts too (web-craft-fonts)
The shared release workflow builds the web release without CRAFT_FONTS_DIR unless web-craft-fonts
is set, so the web version would still have no Georgian face (CodeRabbit on vectorcraft#2934).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
* test: the Arabic New Document test scrolls to Height like the other fields
With craft-fonts' Noto Sans Arabic (from a71c20e3) the rows above Height are taller, so at
392x271 the field starts partly below the dialog body's clip and the test's unscrolled
visible_label check failed (FreeBSD jobs). It stays reachable: scroll to it as the test
already does for Gutter and the margins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
* CI: same craft-fonts pin as release (a71c20e3), so the gate checks the Georgian face
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>