Commit Graph
2944 Commits
Author SHA1 Message Date
Brandon Thomas 53eeb497c2 Merge pull request #1949 from garrytan/fix/settle-chained-own-story-wraps
compose: settle a story's own anchored wraps by object count, not 4 passes
2026-10-29 21:53:10 -04:00
Brandon Thomas 211619e949 Merge pull request #1935 from Silvertree2010/fix/1883-bug-report-designcraft-error-mes
Open documents whose items share an id
2026-10-29 21:53:07 -04:00
Brandon Thomas 08e8fdf655 Merge pull request #1909 from Hamza0101/fix/unc-test-runs-on-windows
Data merge: the UNC refusal is tested on Windows too (#1601)
2026-10-29 21:52:57 -04:00
Brandon Thomas 3892c8ed1a Merge pull request #1871 from Hamza0101/fix/import-amplification
Import: a shared string and a footnote are not copied per reference (#1584)
2026-10-29 21:52:53 -04:00
Brandon Thomas fd207a0d7c Merge pull request #1998 from Silvertree2010/fix/h3964-tagged-text-import-trims-style-n
Keep leading and trailing spaces in Tagged Text style names
2026-10-29 21:52:46 -04:00
Brandon Thomas f9fabc3334 Merge pull request #1840 from dotCooCoo/fix/print-test-driver-default-color
Print: the Windows print test expects the driver's default color
2026-10-29 21:52:42 -04:00
Brandon Thomas b4463b5b26 Merge pull request #1911 from Hamza0101/fix/merge-image-reads
Data merge: an image named by many records is read once (#1595)
2026-10-29 21:52:38 -04:00
Brandon Thomas 1f20480194 Merge pull request #1948 from Silvertree2010/fix/h3527-placing-a-plain-text-file-keeps
Strip story marker characters from placed plain text files
2026-10-29 21:52:33 -04:00
Brandon Thomas 45369565ad Merge pull request #1872 from Hamza0101/fix/pdf-name-escaping
PDF export: a name escapes every byte a name cannot hold (#1582)
2026-10-29 21:52:30 -04:00
Brandon Thomas e62583dfc4 Merge pull request #1905 from Silvertree2010/fix/h2977-rtf-import-a-short-or-invalid-hh
Fix RTF import swallowing characters after a short \'hh escape
2026-10-29 21:52:27 -04:00
Brandon Thomas c0d1acd2b3 Merge pull request #1568 from Hamza0101/fix/table-insert-size
A table size Create Table cannot mean is refused
2026-10-29 21:52:22 -04:00
Brandon Thomas 352d9c526d Merge pull request #1860 from Silvertree2010/fix/h2768-tagged-text-export-does-not-esca
Escape font family and typeface values in Tagged Text export
2026-10-29 21:52:16 -04:00
Brandon Thomas 61adb2063e Merge pull request #1306 from Hamza0101/reproducible-pdf-export
PDF export: SOURCE_DATE_EPOCH stands in for the clock, so exports repeat
2026-10-29 21:52:12 -04:00
Brandon Thomas 0a05c1631c Merge pull request #1233 from seanth/running-header-order
[fix] Running headers read a page in text order, not by height
2026-10-29 21:52:08 -04:00
Brandon Thomas 4777269ee6 Merge pull request #1976 from hscabral/idml-export-story-preference
IDML export: write the document's default StoryPreference
2026-10-29 21:52:04 -04:00
Brandon Thomas bb210adf99 Merge pull request #1932 from devius/ka-new-strings
i18n(ka): Georgian for keyboard shortcut sets, command forms and labels added since #1573
2026-10-29 21:52:01 -04:00
Brandon Thomas 9d73109012 Merge pull request #1908 from Silvertree2010/fix/h2951-xml-export-writes-control-charac
Drop XML-forbidden control characters in file.exportXml
2026-10-29 21:51:57 -04:00
Brandon Thomas cddf074cc4 Merge pull request #1868 from Hamza0101/fix/table-allocation-cap
Tables: a shape a table cannot hold is clamped, not allocated (#1583)
2026-10-29 21:51:53 -04:00
Brandon Thomas 3e15ebc7b0 Merge pull request #2008 from Silvertree2010/fix/h4052-name-with-color-value-produces-r
Fix "-0" in Name with Color Value for slightly negative RGB
2026-10-29 21:51:49 -04:00
Brandon Thomas f920aa0d93 Merge pull request #1971 from KnottyDyes/squad/tables-clear-footnotes
Compose: tables clear the footnote area
2026-10-29 21:51:45 -04:00
Brandon Thomas 1f04ec6d61 Merge pull request #1915 from Silvertree2010/fix/h3022-idml-export-duplicates-cross-ref
Fix duplicated xref format text before an unclosed '<' in IDML export
2026-10-29 21:51:42 -04:00
Brandon Thomas 2b15bf79a2 Merge pull request #1842 from Silvertree2010/fix/1835-document-not-updated-on-image-re
Repaint the canvas when a placed file is relinked or updated
2026-10-29 21:51:38 -04:00
Brandon ThomasandClaude Opus 5.5 cc47975960 Use clamp for the wrap pass bounds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:43:16 -04:00
Brandon ThomasandClaude Opus 5.5 a9256308e3 Keep at least four wrap passes for a story with few anchored objects
A single wrapping object moved by keep options or vertical justification had four passes to
settle before; one-per-object would have given it two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:39:35 -04:00
Brandon ThomasandClaude Opus 5.5 84801932f2 Renumbering duplicate ids stops at the top of the id range instead of overflowing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:39:23 -04:00
NetterSilverandClaude Haiku 5.5 6c6efe2f7a Fix "-0" in Name with Color Value for slightly negative RGB
Found while testing crates/color/src/swatch.rs:94-101 (value_name)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 15:43:45 +00:00
NetterSilverandClaude Haiku 5.5 e9d7a19994 Keep leading and trailing spaces in Tagged Text style names
Found while testing crates/textimport/src/tagged.rs:~190-205 (the ParaStyle and CharStyle branches of import: `unescape_value(value.trim())`)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-29 11:58:54 +00:00
Hamza 18f2dc2bde A spent cell budget stops reading tables, rather than keeping one each
Keeping a 1 x 1 table for every further `<Table>` was unbounded in the one
direction the budget exists to close. `<Table></Table>` is fifteen bytes, so
the 512 MiB a part may decompress to holds some 36 million of them, and a
`Cell` is 1,360 bytes: tens of gigabytes, with every table inside both caps.
The test I wrote said as much without my noticing -- its invariant was
`reserved <= MAX + tables`, and `tables` had no bound.

`budgeted_size` now returns `None` once the budget is spent and the table is
not read at all, so the cells one import reserves are at most the budget,
exactly. The invariant is now `reserved <= MAX_IMPORT_TABLE_CELLS`, with no
term that grows with the file.

The warnings needed the same treatment, for the same reason: a file of 400,000
tables each claiming one row and two columns got a line about every one of
them. At most 20 are named, then one line saying the rest were the same, and
the file is told once that its tables ran out of cells between them.
2026-10-29 16:43:49 +05:00
Henrique CabralandClaude Opus 5.5 50b5936b78 IDML export: write the document's default StoryPreference
InDesign always writes a document-level StoryPreference in
Resources/Preferences.xml; DesignCraft only wrote one per story. IDML readers
that take the spread reading order from the document's preference, such as
Okapi's IDML filter, failed on DesignCraft's files without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:07:42 -04:00
KnottyDyesandClaude Opus 5.5 ed88f3e4b5 Compose: tables clear the footnote area
Reserve column and spanning footnotes when fitting inline table row groups. Continue rows in the next column or frame when the remaining space is too small, and include cell-local footnotes in measured row height.

Fixes #1901

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:44:34 -06:00
aiongg 855d606325 Show file and message dialogs without blocking the frame (fixes macOS crash) (#1961)
* Show file and message dialogs without blocking the frame

A blocking rfd dialog inside the egui frame runs AppKit's modal loop; a
file dragged from Finder and held over the window then re-enters winit's
handler and aborts the app. Dialogs now go through craft-dialogs: a
command without a path asks with a continuation and returns, the host
shows the dialog at the end of the frame, and the continuation runs with
the answer on a later frame, in the document it was asked for. Commands
given a path stay synchronous. The replace-file question is a message
box from the same queue. Quit and Close wait for a Save As dialog's
answer. clippy.toml disallows rfd::FileDialog and rfd::MessageDialog.

* Raise the open file panel when File > Open is chosen again

* Repin craft-dialogs to the craft-libs PR head

* Queue dialogs asked by different code or documents; hold answers behind newer dialogs

Two asks for the same file dialog were treated as one, so Place for one
document and then Choose Image, or Place again for another document, ran
the first ask's action. An ask is now skipped (and the open panel
raised) only when the same code asked for the same dialog for the same
document. An answer that would replace an egui dialog opened since the
asking (an unsaved-changes question, Preferences) waits until it closes;
Quit counts it as pending.

* Repin craft-dialogs to the craft-libs PR head

* Allow the file dialogs' existence checks in the file guard

* Repin craft-dialogs to storytold/craft-libs main
2026-10-28 17:13:45 -04:00
Claude 94e0e75249 Merge storytold/main (v0.14.0) into running-header-order
The running-header tests move to cmd/variables/tests.rs with upstream's split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CgGsJkVe5GYG9qK6VFdECa
2026-10-28 18:00:57 +00:00
NetterSilverandClaude Haiku 5.5 79b871b94f Strip story marker characters from placed plain text files
Found while testing crates/textimport/src/lib.rs:140 (plain) and crates/engine/src/cmd/place_text.rs:372 (place_text)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 15:18:35 +00:00
Hamza 87b49d9973 PDF export: SOURCE_DATE_EPOCH stands in for the clock, so exports repeat
A PDF export recorded the second it was made in, so exporting one document
twice gave different files although the pages matched -- and the instance id
krilla derives from the bytes followed the timestamp, so nothing about the two
files was comparable.

SOURCE_DATE_EPOCH stands in for the clock when the export is given no date, as
the reproducible-builds specification defines it. A date passed in still wins,
and a value the PDF date format cannot hold -- malformed, negative, or past
the range -- is ignored rather than refusing the export or writing a date no
reader can parse. With the epoch pinned the two files are identical byte for
byte.

Rebased onto the post-split main: export_time and pinned_or live in
export/metadata.rs beside now_unix, which they stand in front of, and the call
site is in export/pipeline.rs. The ROADMAP keeps upstream's 2026-10-17 entry
with this one above it.
2026-10-28 19:09:25 +05:00
Hamza 352609117f A table size Create Table cannot mean is refused
A size that was given but is not one a table can have was corrected in
silence: {"rows": 0} made a table with one row, -1 and 1.5 made the default
four, and 10000 was capped at 500 -- so a script asking for a shape the
command cannot honour got a different table and no indication of it.

A size that is absent still takes the default; a size that is present and
outside the range is refused, naming the key and the range.

Rebased onto the post-split main: `table.insert` moved to
table/structure.rs and the tests to table/sizing.rs. The helper is named
`given` rather than `count`, which that module already imports from
table/target.rs for the repeat count of a row or column insert.
2026-10-28 18:20:13 +05:00
Hamza 96b498ca70 Import: a shared string and a footnote are not copied per reference (#1584)
A footnote reference clones its note and walks it again, and an XLSX shared
string is copied per cell that uses it, so one 1 MB footnote referenced 100k
times meant around 100 GB. The text is kept at the first reference; later ones
get the mark and an empty note, and the report says so. The spreadsheet reader
stops at 64 MB of text and 400,000 collected cells.

Carries both review fixes:

- a footnote id is recorded only once its text has actually been kept, so a
  first reference arriving after the budget was spent no longer counts as
  "done" and tell the next reference the text is at a reference that has none;
- the report and the note budget are both bounded. The duplicate warning names
  the id, so 400 ids referenced twice made 400 warnings, each pushed after a
  Vec::contains scan of all the earlier ones; warnings go through warn() now,
  which dedups through a set and stops at 100 distinct messages. And the budget
  counted only `text`, which holds a table's anchor and not its cells, so 18
  notes of 600 KB of cell text retained 10,800,072 bytes against the 8 MB
  budget; note_bytes() walks the tables and their nested tables.

Rebased onto the post-split main: the implementation stays in docx.rs and the
tests moved to docx/tests.rs, so the test additions went there.
2026-10-28 18:10:41 +05:00
Dylan M. Taylor d04c8b2e58 Refactor ui-egui for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor 5b21472180 Refactor engine for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor 072dd02f73 Refactor render for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor be9b8f360e Refactor idml for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor 0104122f06 Refactor doc for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor e6ff9d5aac Refactor fonts for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor 71a579e8da Refactor compose for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor 07366af1b6 Refactor pdf for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor bcb9546f5d Refactor tools for strict Clippy checks 2026-10-28 11:39:26 +00:00
Dylan M. Taylor f1a5744e8c Refactor textimport for strict Clippy checks 2026-10-28 11:39:25 +00:00
Dylan M. Taylor c2869280a2 Refactor epub for strict Clippy checks 2026-10-28 11:39:25 +00:00
Hamza 1995fb5feb IDML import: the tables in one file are capped between them
Review pointed out that capping each table leaves a file free to hold
many capped tables: three at the per-table limit claim 750,000 cells,
about 1 GB of Cell, with every table inside its own limit. The package
reader's limits count input bytes, which say nothing about how many
cells a row and column count claim.

budgeted_size clamps each table against what the import has left, and
the cells are reserved before Table::new so a cell's own story cannot
spend the budget the table around it is counting on. The budget is
400,000 cells: a Cell carries a whole story and is 1,360 bytes, so that
is about the 512 MiB one part of the file may already decompress to,
and the same count the spreadsheet importer collects at most.

A table is never read as nothing. One row and one column are kept, so
the table stays in the document and still says where its text went, and
a file pays one cell for a table it really does contain -- which its own
size already bounds. The warning says which limit cut the table down.

The arithmetic is tested rather than the allocation: 400,000 cells is
544 MB and three such tables are not a thing to ask CI to allocate. The
sequence test runs 32 claimed sizes, including usize::MAX, and holds the
invariant across all of them.
2026-10-28 16:31:34 +05:00
Hamza b49535624c Tables: a shape a table cannot hold is clamped, not allocated (#1583)
`Table::new` allocates `rows * cols` cells up front, and the counts reach it
from IDML and DOCX, where they are whatever the file claims. Measured here:
`size_of::<Cell>()` is 1,360 bytes, so the reported IDML case -- one `<Table>`
with 5,000 `<Row/>` and 5,000 `<Column/>`, about 1 KB zipped -- asks for 25M
cells, some 34 GB. That allocation does not fail politely: it aborts the
process through `handle_alloc_error`, which no `catch_unwind` can catch, so
every open document's unsaved work goes with it.

A table is clamped to InDesign's own limits, 10,000 rows and 1,000 columns,
and to 250,000 cells whatever its shape -- the row and column limits alone
would still allow 13.6 GB. Columns are clamped first and rows take what is
left, because a table is far more often long than wide.

The IDML importer walks the clamped slices and reports the difference.
Indexing its rows and columns by the file's own counts would be out of
bounds, which is how a 34 GB abort would otherwise have become a panic.

Rebased onto the post-split main: the constants and `clamped_size` live in
`doc`'s `table` module root, `Table::new` in `table/grid.rs`, and the IDML
table reader in `import/table.rs`.
2026-10-28 16:26:13 +05:00
Hamza ff35b3561f Data merge: an extended-length drive path is a local file, not a refusal
Review caught a wrong assertion in my own test. `\\?\C:\x` is the
extended-length spelling of `C:\x`: craft-docpath parses it as
Root::VerbatimDrive, is_absolute, and its own suite classifies it
PathKind::Local. The test asserted it was refused, so it would have
failed on Windows -- the one platform it runs on.

A data file can already name C:\secret\x.png, so refusing the long
spelling would have shut out long paths without shutting anything out.
It is now asserted the other way, and the refusal list keeps a device
path in its place.

Each shape was checked against the pinned craft-docpath through
resolve(), which recognises Windows forms on every platform, so the
verdicts are first-hand and not read off the source: Network for the
extended UNC prefix, Device for \\.\pipe, GLOBALROOT, CON and NUL.png,
and Ok for \\?\C:\secret\x.png.
2026-10-28 15:33:12 +05:00