* Table formulas: read the Panamanian balboa B/. in numbers
Word's table formula and sort number reader ignores a currency sign or short currency word around a number; the balboa's B/. was not in the list. Add it as a whitespace-delimited sign, as Word reads it: B/. 1,250.50 and 1,250.50 B/. are numbers, while B/.1,250.50 and B/ 1,250.50 are not. Tested in the locale reader and through =SUM(ABOVE); the localization parity doc records the behaviour.
* Table formulas: read the balboa cell in the document's language
The formula test summed a B/. cell through ev, which supplies no document
or language, so a regression in locale selection could pass. Evaluate
=SUM(ABOVE) with a document whose default text is es-PA and a pl-PL
editing language: 1,250.50 is a number under es-PA and not under pl-PL,
so the assertion fails if the formula stops consulting the cell's
language. The converse case reads B/. 1 250,50 from a pl-PL document with
an en-US fallback, which English alone cannot parse.
* docs: close the parenthesis in the localization-parity change note (#3546)
---------
Co-authored-by: Francisco Herrera <14956584+alosslessdev@users.noreply.github.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
* WC6-06: preserve adjustRightInd in DOCX
* WC6-06: honor grid indent defaults and XML order
* Update lockfile for DOCX test dependency
* Format DOCX ordering assertion
* Keep main lockfile for paragraph property PR
* PR 5384: tighten grid indent docs and regression
* PR 5384: match paragraph check formatting
The transports, the message types and the origin checks come from the shared crate (storytold/craft-libs#420); WordCraft keeps the host save and dirty hooks, the saveAs rule, the open size limit and the author.
data-host="page" on the canvas opts a page that loads the module into its own document into the host bridge. The ?host=parent messages travel as non-bubbling CustomEvents on the canvas; the bridge holds a Transport (Parent or Page) with one send, and the open, save, dirty, ready and failure handling is shared. ?host=parent is checked first and unchanged, including no bridge for an unframed page. The hosting guide documents both modes.
* docbin: a field's cached result keeps its formatting per run, not per character (#5477)
The result was a Vec<(char, CharProps)>: 365 bytes per character, 1.44 GB
at peak for a 4 MB file with one field. It is now a String plus a run
list that gains an entry only when the formatting changes (25 MB peak
for the same file). Output is unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docbin: show the objects when the field-result test fails
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
`comment_export_discovery_scales_with_unrelated_markers` timed `notes` on 2,000 and 16,000
markers through /proc/thread-self/schedstat. That count moves only at a scheduler tick or switch,
so a 17 µs baseline read as nothing or a whole tick, and gate failed on linear code (58.68x on
PR #3228's run 41932894607). As #5608 did for the balloon anchors, the test now counts the
characters the walk visits (`wordcraft_doc::work`): 4,010 -> 32,010 steps, 8.0x, every run. A
prefix scan per marker injected into `notes` counts 63.9x and fails it.
`work::steps` becomes public so a crate that walks a paragraph's text itself can count it; outside
tests it is still nothing.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Alt codes type their character in Find and other text boxes (#4403)
Alt+numpad codes worked only on the page. In a text box (Find what,
Replace with, a dialog's field) the digits were typed instead on Linux,
which sends their text with Alt held; on Windows the box got only what
the system composes, a byte for a code above 255.
A pass before the frame is drawn now gives the focused text box the
same codes as the page: the digits' text is dropped, letting go of Alt
types the character the code names in the same code pages, and a
platform's own composed character is not typed twice (up to 255 the
system's replaces ours, as on the page).
* Alt codes in text boxes: letting go of Alt ends the digits' text
The flag that drops a digit's own text stayed set when no text came
for the last digit, so the first text after the release was lost: on
Windows the system's composed character for a code we name no
character for (CodeRabbit), and on Linux whatever followed a frame
whose digits sent no text (CI). A digit's text comes with its key,
before Alt is let go, so the release now clears the flag everywhere.
* docbin: cap paragraph and cell marks, keep the table context only for table marks (#5476)
The walk budget counts characters, not paragraphs, so a file made of
paragraph marks grew memory about 2,000 times faster than its size
(4 MB of marks: 9.4 s, 8.4 GB peak). Marks now draw on one document-wide
cap (2,000,000, all stories together); past it the read returns
DocbinError::Limit. A ParaOut no longer embeds the 296-byte RowInfo of a
table mark: it is an optional box, present only for table marks.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docbin: say what the paragraph cap bounds; name the budget test for what it checks
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docbin: refuse an exhausted paragraph budget before assembling a story (review point)
CodeRabbit pointed out that the header, footnote, endnote and text box walks
assembled their partial paragraph lists before the budget check, a transient
memory peak. They now share story_blocks, which checks first.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docbin: test that the paragraph cap is shared by the body and a footnote (review nitpick)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docbin test: review fixes (assert empty pairs, comment wording)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Content controls: a press on the drop-down list button no longer moves the caret (#5389)
The list button hangs over whatever follows the control. The canvas put the
caret under the pointer first, which left the control, and the list closed in
the frame it opened. A press on the button now skips the caret move.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Content controls: only a press that began on the list button skips the caret move (#5389)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Content controls: a press and release in one frame, and a table tool, no longer hit the button guard (review points)
CodeRabbit pointed out that egui clears press_origin when a press and its
release arrive in the same frame, so the caret still moved off the control and
closed the list; the press is now also read from the frame's events. It also
pointed out that the guard ran before the table pen, so a stroke could not
start over the button; the guard is skipped while a table tool is on.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Content controls: feed the same-frame press and release as one frame in the test
Harness::event runs one frame per event, so the earlier version of the test did
not exercise the single-frame case.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* M6: preserve numbered REF results during field updates
Adapt and validate the patch published in WordCraft #4533, retaining main's bounded numbering renderer and adding relative-context/cache guards.
Co-authored-by: Alyosha-dot-shop <218732667+Alyosha-dot-shop@users.noreply.github.com>
* M6: reuse bookmark lookup for numbered REF updates
* REF: use explicit imports for the workspace clippy gate
* #4609: adapt synthetic fixtures to shared document data
* #4609: checkpoint review fixes and isolated validation notes
* #4609: retain rendered REF labels and isolate relative context by list
---------
Co-authored-by: Alyosha-dot-shop <218732667+Alyosha-dot-shop@users.noreply.github.com>
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
* Mini toolbar: hovering a formatting button previews it on the selection (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Keep mini toolbar stable during alignment preview
* Track live mini toolbar anchor during preview
* Mini toolbar: hovering a font, size, highlight or font colour previews it (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mini toolbar preview: the page holds its scroll position (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mini toolbar: hovering the highlight and font colour buttons previews their colour (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mini toolbar: the font list and colour menus mark the choice in effect (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Skip hover previews in context toolbar
* Pass preview mode in toolbar test
* Font list: a picked family keeps its place until the list next opens (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Font and size lists close when an entry is picked (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mini toolbar: a clicked button holds its preview until the pointer leaves (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Mini toolbar: a button already on does not preview (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Alignment buttons follow the paragraph a selection covers; a toggle previews its undo on return (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Font list: a recent family is marked as chosen in Recently Used Fonts only (#4664)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Fluorine <joseph.anglada92@gmail.com>
* #3614: reuse paragraph-prefix lookups during ink discovery
* Update ink lookup status date (#3614)
---------
Co-authored-by: Martin Ball <martin.ball@ucscaregroup.co.uk>
* Let every credits column be dragged, and refit changed tables
The last column was egui's remainder column, which has no drag handle, so
it could only be resized from the edge before it. It is now an ordinary
column with its own handle.
A table keeps its column widths, so switching to a longer name left the
Name column at its old width and clipped the text. The table now goes
back to the opening fit whenever that fit changes: a longer widest cell
or a resized window. Dragged widths are dropped at that point.
fit_widths could overflow the room when one column hit the minimum,
because the other columns did not take up its share. Columns now give
way in proportion to their width above the minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Refit credits tables on what they measure, not the fitted widths
Comparing the fitted widths missed the case it was meant for. Name is the
only growing column, so a longer name takes its extra width from the
spare room and the fit comes out the same: a dragged Name width stayed,
clipping the longer name.
It could also drop dragged widths by accident. The fit comes from the
available width inside the scroll area, which scrolling can nudge, so
scrolling a widened table could reset it.
The table now compares the natural widths and the clip rect's width,
which scrolling does not move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The page the section before ends on is finished with its own margins and
footnote separators before the starting section's replace them, so the
footnotes pending on it are placed where the page they sit on says.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
* docx: keep a paragraph's text alignment
w:textAlignment (ECMA-376 17.3.1.39) was not read, so a save dropped it. Word sizes a line by it when a
list's bullet is larger than the text: without it, those lines grow and every page after them moves.
ParaProps gains text_alignment, read and carried through styles, and written back after w:jc. Kept for
the file only; the layout does not use it yet.
* contributors: add entropybeater
---------
Co-authored-by: Brian <255903314+entropybeater@users.noreply.github.com>
Spelling as you type now notes that a web or e-mail address after a
no-break, narrow no-break (U+202F), figure (U+2007) or ideographic space
is not marked while Ignore web and file addresses is on, as
address_after_a_wide_space_is_an_internet_token in craft-proof checks.
* doc, docx: keep the compound border lines
The nine thin and thick compound lines (thinThickSmallGap to thinThickThinLargeGap, ECMA-376 17.18.2)
were read as a single line and written back as one, on paragraphs, tables, cells and pages. BorderStyle
gains them; they are written back by name and drawn as a single line of the border's width, as before.
RTF export and import carry them too (the importer read four of them as double).
* contributors: add entropybeater
---------
Co-authored-by: Brian <255903314+entropybeater@users.noreply.github.com>
metafile_size() parsed the whole metafile, decoding every bitmap and building every path, to
read two numbers at picture insert and replace. A new wordcraft_metafile::size reads the frame
from the EMF header or the placeable WMF header, and falls back to the full parse for every
other file (plain WMF, an EMF without a usable frame, a file with no records), so it returns
what parse would. For an EMF with one 8000x5000 1 bpp bitmap the size now costs well under a
microsecond instead of about 280 ms.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
decode_clip matched the bit depth, bounds-checked and (for 16 and 32 bpp) divided per channel
for every pixel. The depth, palette and masks are now turned into a row decoder once per
bitmap: a 256-entry colour table for 1/4/8 bpp, plain channel swaps for 24 bpp and default
32 bpp, and precomputed shifts and scale tables for masked 16/32 bpp. Output is byte for byte
the same; the old per-pixel code stays in the tests as the reference.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
decode_clip only capped width x height at 40 MP. A 32 x 1,250,000 pixel 1 bpp bitmap (about
5 MB in the file) was decoded to 160 MB of RGBA and kept, then dropped by Item::take_pixels
because its side is over MAX_BITMAP_SIDE, while counting against the parse's 256 MB budget.
decode_clip now returns None when the clipped width or height is over the cap, so crops of a
large bitmap that fit still decode.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* Pin that a comment id past u32 is refused, not truncated (#3677)
`validate_comment_id` refuses an `id` that does not fit a `u32`, so the `as
u32` casts behind it can no longer reach another comment -- but nothing pinned
it, and the casts are still there to be reached if the guard moves.
The ids the test sends are the ones that would alias onto real comments if the
truncation came back: 2^32 truncates to 0 (and the first comment is 0), 2^32 +
id to that comment, 4294967301 to 5 as the report describes, and u64::MAX to
u32::MAX. Each of the four commands that take an id is tried with each, then
the comments are checked to be unchanged and unresolved, and a valid id is
accepted afterwards so the guard is shown to be a range check rather than a
refusal.
* Comment ids: the values past u32 go in the existing invalid-id test, refused by the range check
anchors_walk_dense_unrelated_markers_in_linear_work compared CPU times of
a few microseconds, so cache and timer noise could push the ratio past its
24x limit on a linear walk (43.52x in a CI run).
wordcraft-doc now counts, in tests only, the characters its paragraph
object lookups walk (objects_before, object_offsets and
objects_with_offsets), through a work::count helper behind the existing
test-support feature. The test asserts that 8x the markers take at most
12x the steps, as the layout linear-time tests do: the walk takes 2001 ->
16001 steps (8.0x), and the object_offsets + object_at walk it replaced
takes 2003001 -> 128024001 (63.9x).
update_generated now keeps what comes before a wrapped list's field, which can be
another list field; rebuild from the converted field's code instead of the
paragraph's first list field, and let index::rebuild_from take that code.
- update_generated keeps what precedes a wrapped list's field (r4468529243)
- from_doc keeps objects in hidden text when hidden text is written (r4469018820)
- clipped cell lines in a turned frame are drawn once, clipped, turned (r4469018900)
- kept::while_writing works out reply map and page fingerprint once per export (r4468529227)
- odt_charts fixture declares its namespaces (r4468529259)
- guard test for character styles based on a missing parent (r4469018843, not a bug)