a_missing_link_shown_by_its_preview_is_still_reported stored the link
/definitely/missing dir/photo.jpg in a document without a path. On
Windows that path names no drive, so document_path refuses it as not
absolute and the preflight message ends with "(the path is not
valid)", and the test failed. The test now stores a link under
std::env::temp_dir(), an absolute path with the default
temporary-folder settings on Windows, macOS and Linux, and expects the
message for that path. The preflight code is unchanged.
A placed file whose catalog has /OCProperties null (literal or through a
reference) exported on main but failed after the layer-choice change; treat it
as absent. Like the trailer guard in #1670, a non-null /Prev that can't be read
as an offset ends nothing and is reported as a damaged cross-reference table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export takes the placed PDF's layer choice as file defaults or an explicit
hidden list, where an explicit empty list shows every layer, including one
the file hides by default. Today an empty list still means file defaults;
an explicit all-on state can map straight onto Some(&[]).
The incremental writer now reads each cross-reference section's trailer by
walking the table rather than searching for the keyword, follows the bounded
Prev chain for inherited encryption, and treats a literal null /Encrypt as
absent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
File › File Info… opens a dialog with Document Title, Author,
Description, Keywords, Copyright Notice and Copyright Info URL, and the
document saves them. The new file.info command reads them with {} and
sets the fields it is given; "" or null clears a field, and keywords
are a list or one line split at semicolons and commas. file.info.get
returns the fields without editing the document, so opening the dialog
does not end a Data Merge preview.
Export PDF, PDF/X-4 and Print Booklet write the title, author,
description and keywords into the PDF's document information and XMP.
EPUB export takes the title and author from File Info, and HTML export
takes the title. A title or author given to the export replaces File
Info's, and an empty Document Title leaves the document's name as the
title. Export PDF's dialog no longer sends the document's name as the
PDF title, so the PDF takes its title from File Info; the file name
still comes from the document's name.
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.
Found while testing crates/engine/src/cmd/table.rs:1742
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* 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
A group's transform can move a frame onto another page; the page lookup
used the frame's bounds without its parents' transforms, so the grid took
the wrong page's top margin.
The setting was imported and editable but never read: the composer, the
canvas grid and baseline snapping measured the grid from the page top.
BaselineGrid::origin gives the first line on a page (start, plus the
page's top margin for Top Margin), and all three use it. The composition
cache tracks the setting and the pages' top margins.
Also adds a left-to-right facing-pages guard test for mirrored margins
and the right parent page's guides and running head.
Found while testing crates/epub/src/lib.rs:453-458 (char_css)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/geom/src/path.rs:284-288 (MoveTo branch of PathData::from_bezpath)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* 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>