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.
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
* 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
path.moveAnchors treated a non-list points as absent and fell back to the
id/anchors form, so a request with a malformed points and an id moved that
path. Found by CodeRabbit on #1657.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- table.insert takes tableStyle and applies it in its own edit, so one Insert
Table is one undo, and a style that is gone (or not a name) changes nothing.
The dialog passes it instead of running style.table.apply afterwards.
- The dialog's fields stop at table.insert's limits (500 body rows, 200
columns, 100 header or footer rows; now public constants), so the table made
is the one shown.
Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
selection.cells may start at a covered grid position; the toggle now goes to
the merged cell (its owner at the top left), whose story holds the text.
Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Table › Convert Text to Table… and Convert Table to Text… open dialogs laid
out as InDesign's instead of converting at once with tabs and paragraphs:
- Column and Row Separator: Tab, Comma, Paragraph from the field's menu, or
any characters typed.
- Number of Columns (Convert Text to Table), available when both separators
are the same: the cells fill rows of that many.
- Table Style (Convert Text to Table), applied to the new table in the same
undo step.
table.convertFromText takes rowSeparator, columns and tableStyle, and any
characters as separators; table.convertToText takes both separators
(Table::text_with). Convert Text to Table is available only with text
selected outside a table, as in InDesign. New strings are translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Table menu: Insert Table… (Cmd+Alt+Shift+T), Convert Text to Table,
Convert Table to Text | Cell Options | Insert, Delete, Select | Merge,
Unmerge, Split Horizontally/Vertically, Convert Cell Type, Convert Rows |
Distribute Rows Evenly, Distribute Columns Evenly. The Table panel item left
the menu (it is in Window › Type & Tables, Shift+F9).
- Cmd+Alt+Shift+T opens the Insert Table dialog, as InDesign's; it ran
table.insert with its defaults, so the shortcut and the menu did different
things.
- The Insert Table dialog: InDesign's title, a narrow window, Body Rows and
Columns then Header and Footer Rows as numbers with spinners and
right-aligned labels, and a Table Style applied to the new table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
As in InDesign (Learn Skills Daily / Adobe "How to make a table" lesson):
- With the Type tool in a table cell, Shift+Tab goes to the previous cell.
It went on to the next one: the tool sent a plain Tab whatever the
modifiers.
- Esc in a cell toggles between the text and the cell: the insertion point
or text in a cell selects that cell, and selected cells select the text of
the first one. Esc left the table and selected the frame.
New engine command table.toggleSelection does the toggle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>