Screen readers got only the swatch name; the painted marks (colour type and
mode, can't be edited) were not in it. The row is now named "Cyan, Process,
CMYK" or "[Black], Process, CMYK, Cannot be edited" (translated; one new
string). Tests find rows by name with names_swatch. Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A GREP match that is empty (^) was counted but got no paragraph style; the
paragraph style now goes to the paragraphs of every match, empty ones too.
Character formatting still needs characters.
- apply_char_style and apply_para_style listed every paragraph of the story
for each match; Story::paras_touching gives the ones a range reaches, from
the range itself.
Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A style the font lacks counts as a missing font in Summary and Fonts, as
file.package counts it (font_status).
- The dialog remembers its document; Package... and Continue refuse when another
document became active meanwhile, so the package is the one summarised.
- Enter in Address or Instructions makes a new line: the dialog footer no
longer takes it for Continue (dialogs::MULTILINE_FOCUS).
- Instructions.txt starts with the printing instructions, then the report, as
the dialog promises.
Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two unbounded things in the GREP-style path.
`BACKTRACK_LIMIT` bounds one search, not a scan: `find_all` was called with
`usize::MAX` and pays the limit again for every match, so a pattern tuned just
under it cost `200_000 x matches` per paragraph on every relayout. A scan now
stops at `MAX_MATCHES_PER_PARAGRAPH` (4096) -- more styled runs than a
paragraph can usefully carry -- and says which pattern reached it.
The `PATTERNS` thread-local never evicted, so it grew across every pattern a
session ever composed. It now holds at most 64, least recently used dropped
first: a tick counter marks each use, so the entry evicted is the one longest
unused rather than the one inserted earliest. The test checks that the pattern
just used is still there after the cache overflows, which insertion-order
eviction would not give.
Left for a separate change: DC-17 also asks for a per-compose step budget, so
the cost cannot add up across paragraphs. That means threading a budget from
`compose_story` through `shape` into `overlays`, a compose-internal signature
change that wants its own review rather than riding along here.
Merges #1676 (a null table style is no style), and:
- The dialog compares separators as table.convertFromText resolves them, so
"comma" and a typed "," are the same and Number of Columns applies.
- The column count goes to the engine as given: 0 is refused (the dialog stays
open) instead of becoming 1.
- At most 200 columns, table.insert's MAX_COLUMNS, in the engine and the field.
- The help says the words tab, comma and paragraph name those separators.
- A short last row's padding cells take the text's paragraph and character
format (Table::from_strings formats every cell).
Found by CodeRabbit and @skylartaylor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new command note.convertFromText removes the selected story text
and puts it into a new editorial note whose anchor sits where the text
was. It takes an optional author, and Convert Note to Text puts the text
back. DesignCraft refuses a selection in a table cell, or one that holds
a note, footnote, endnote, anchored object, index marker, text anchor,
cross-reference or table, and leaves the story unchanged. The item
follows New Note in Type › Notes, with translations in the catalogs that
already translate Convert Note to Text.
The importers each had their own spelling of "is this a number we can use", or
only checked that it was finite. Measured before the change:
<cSize:1e300> -> Some(1e300) pt tagged text
<cSize:-5> -> Some(-5.0) pt
<cSize:0> -> Some(0.0) pt
w:spacing before=1e300 -> 5e298 pt indent DOCX
DC-12 asks for a shared clamp helper used by every importer, so there are two
in `doc::attrs`, beside the tab limits:
* `imported_type_size` drops a size the engine cannot mean, so the text keeps
the size its style gives it -- which is what RTF and DOCX already did.
* `imported_measure` clamps an indent, spacing or offset to the widest page.
Clamped rather than dropped on purpose: a file that says "indent everything"
still indents as far as the engine goes, where dropping would quietly restore
the style's own indent and hide what the file asked for.
Both are now used by tagged text, DOCX runs and paragraphs, DOCX numbering
levels and RTF -- the last two had the 0.1-1296 pt range as a literal, which is
the duplication that let tagged text drift.
Control: with the helpers back to a finite-only test, both new tests fail, the
DOCX one printing the 5e298 pt spacing in full.
Stale in the issue: its first location, `docx.rs` `w:sz`, is already clamped --
that came with #1574. DC-12's remaining holes are the ones fixed here.
Dialog::s turned JSON null into the string "null", which table.insert refused
as an unknown style. The field is read as text, and null or empty means
[Basic Table]. Found by CodeRabbit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Update, Relink and Relink Folder all reload a linked graphic through `reload`,
which read the file with an unbounded `read`. A document whose link points at a
multi-GB file exhausted memory on Update. How large a linked file is belongs to
whoever wrote the document, not to whoever opens it: the path comes out of the
document.
`reload` now reads through `read_capped` at the limit packaging already applies
to a linked file (`MAX_LINKED_FILE_BYTES`, 256 MB), and a file over it is
refused by name instead of read. `reload_capped` takes the cap so a test can
show both sides without a 256 MB fixture.
Not changed: `file.place` reads a path the same unbounded way, but there the
user picked the file, where a link's path can come from a document someone else
wrote. Whether that read wants the same cap is a call for a maintainer, so it
is reported rather than changed.
Found in the audit of #1596 (DC-15).
The catalogue test refuses a key in both fr.rs and the shared table (CI).
The shared row keeps the French; it matches, or takes fr.rs's text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalogue test refuses a key in both fr.rs and the shared table (CI).
The shared row keeps the French; it matches, or takes fr.rs's text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's insert_pages_bounds.rs and a_page_count_insert_pages_cannot_mean_is_refused
pin that an `after` past the last page appends. This branch had made it an error;
`before` (new here) still has to name a page, and both together are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>