Found while testing crates/formats/src/odt.rs:1480 (finish_para calls p.trim()); crates/formats/src/model.rs:188 (Para::trim)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #1230: Insert Caption on pictures and shapes; Page Setup header and footer options
- Right-clicking a picture, shape, text box or chart offers Insert Caption…, as in Word.
- references.caption with a picture or shape selected puts the caption next to the
paragraph holding it, in the same cell or text box (it used to go after the whole table).
- The Caption dialog proposes Figure for a picture selected in a table, not Table.
- Page Setup: Headers and footers check boxes (Different Odd & Even Pages, Different First
Page), applied on OK only if changed, in one undo step; a title page can go unnumbered.
- Tests: engine caption_for_an_object_in_a_cell_stays_in_the_cell; kittest
right_click_on_a_picture_offers_insert_caption and
page_setup_different_first_page_leaves_the_title_page_unnumbered.
- Docs: ui-parity, target-app-parity, ROADMAP progress log.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
* ui-parity: keep Insert Caption on the context-menu row
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
- Math straight in the body or a cell (m:oMathPara/m:oMath without w:p,
allowed by EG_BlockLevelElts) gets its own paragraph instead of vanishing.
- mc:AlternateContent Requires="m" picks the equation, not its fallback.
- Inside an equation, runs in w:hyperlink, w:fldSimple, w:dir, w:bdo and
mc:AlternateContent are kept (EG_PContentMath); depth stays bounded.
- w:sym, w:noBreakHyphen and Symbol-font text in math runs become Unicode.
- Layout: n-ary operators, delimiters, accents and group characters the math
font lacks use a fallback font instead of a missing-glyph box.
- Coverage test reads every ECMA-376 §22.1 element and checks no argument's
text is dropped; tests for body-level math, Requires="m", symbols, hostile
nesting and the font fallback. Parity docs and progress log updated.
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2601: Drop Cap Options dialog
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gaps: keep the Drop Cap Options note on the dialog-depth row
* i18n: translate the two Drop Cap disabled reasons in the audited catalogs
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Hover hints give the command's own reason, cover split-button arrows, menu buttons and the font/size/outline boxes, and show on keyboard focus.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2551: dragging the scrollbar names the page at the top of the window
While the canvas's vertical scrollbar is dragged, a tip beside the pointer
says which page is at the top of the window, in the status bar's words
("Page 7 of 10"), so a drag through a long document shows where it will land.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2551: the scrollbar tip names the page whose visible band is at the top, also with Hide White Space
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
No Color removed the highlight from the selection, but the Highlight
split button kept the colour picked before, so the next click on it
highlighted again. As in Word, No Color now becomes the button's choice
and the next click removes highlighting. The ribbon and the mini toolbar
share one highlight menu (the ribbon's copy is gone); No Color is
translated in the mini toolbar and no longer shows Ctrl+Alt+H beside it
in the ribbon.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word draws no red squiggle under the word being typed. It appears when the word ends or the
caret leaves it by a key or a click. The session records the word after a text command that
edits the caret's paragraph, with apostrophes and hyphens counted as the spelling check counts
them, and drops it once the caret is anywhere else. The layout leaves that one range out of the
spelling squiggles, and a caret move without an edit lays out only that paragraph again. F7 and
the Spelling pane still report the word, and grammar marks are unchanged.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(formats): support quoted and non-latin charset declarations in HTML decode
* html: clippy (question_mark, collapsible_if) after merging main
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* SmartArt: typing with the graphic selected types into an item
Typing with a SmartArt graphic selected replaced the whole graphic with
the typed text. Now the text goes at the end of the current item (the
first before any is picked) and the Text Pane opens, as in Word. The
typing is one undo step.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* SmartArt: double-click or the frame button opens the Text Pane
A double-click on a SmartArt graphic opens its Text Pane. While a
graphic is selected, a Text Pane button sits on the left edge of its
frame, as in Word, and shows or hides the pane.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* SmartArt: the Text Pane closes with the graphic's selection
It opened by itself on the next graphic selected, because the pane flag
outlived the selection. The frame button hides while the pane is open.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2138: save writes the chosen file when its folder refuses a temporary file (macOS privacy protection)
On macOS a Save-panel pick inside a protected folder (Desktop, Documents…) grants
access to that file only, so the sibling .wordcraft-save-*.tmp was likely refused
with EPERM. On PermissionDenied creating the temporary file, write the destination
directly; a remaining permission error on macOS says where to allow access.
Untested on a Mac: the fallback path is pinned on Unix with a read-only folder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #2138: a failed save in a folder refusing the temporary file keeps the original
Review (FredApps): the in-place fallback truncated the destination before
writing, so a failure partway through lost the previous document.
The fallback now keeps the existing file's bytes in memory (up to 512 MB)
before writing it in place and writes them back if the save fails, or
removes a new file it created. If that write-back fails too, the error
says the file may be incomplete. A file over the limit isn't written in
place; the error suggests Save As.
Tests: the reviewer's partial-write test (read-only folder), a new file
removed on failure, and the size limit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
* #2138: keep the original untouched when no safe replacement is possible
When the folder refuses the sibling temporary file, an existing destination
is no longer overwritten in place (no in-memory rollback): it stays
byte-identical and the error says to use Save As to another folder or allow
access to the folder (macOS: Privacy & Security > Files and Folders). A
destination that doesn't exist yet is created exclusively and removed again
if the write fails.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Picture Styles, Picture Border and Artistic Effects now draw again from the
bitmap under them (kept per drawn picture in Session::picture_looks), so
Thick Frame then Simple Frame is only a simple frame, a border replaces the
border and keeps the style, a style replaces the border, and an effect
replaces the effect ("none" removes it), as in Word. Transparency was already
absolute (#1175); the test pins it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Each item shows a small drawn outline of what it inserts (rectangle,
oval, triangle, star, elbow connector...). The icons are drawn in code in
icons.rs as shape.<kind>; widgets::icon_menu_item draws the item.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(docx): support self-closing w:compat in settings merge
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(formats): support relative font size values in HTML font tags
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* fix(doc): strip negative sign when formatting numbers rounded to zero
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* gap: nested ink WIP
* gap 2: native ink nested in groups and drawing canvases survives DOCX saves
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: review round 1: ungrouping keeps nested ink; no orphan parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: Impact cell matches the restated impact
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: only Word ink pins a group against Ungroup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: alternate ink must hold an a:graphic to be written as standalone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap: nested ink WIP
* gap 2: native ink nested in groups and drawing canvases survives DOCX saves
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: review round 1: ungrouping keeps nested ink; no orphan parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 2: Impact cell matches the restated impact
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: only Word ink pins a group against Ungroup
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 2: alternate ink must hold an a:graphic to be written as standalone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Ruler numbers now thin out as the page shrinks on screen: a number
every 1, 2, 4, 8... units, whichever keeps them at least 30 px apart,
and the small ticks drop out below 4 px, with the text at its usual
size. A number gives way to an indent, tab or column marker sitting on
it, and the margins get ticks only. Both rulers use the caret page's
rect and scale in every layout, including One Page and Multiple Pages,
and follow the caret to another page. Tests cover the density rule, the
marks, the marker rule for indents, tab stops (set or dragged) and
table columns and rows, and the multi-page geometry.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The Quick Access Toolbar's menu ends with Hide Quick Access Toolbar, as Word's does. Show Quick Access Toolbar brings it back from the ribbon's right-click menu and from a check box on Customize Ribbon's Quick Access Toolbar page. The setting is kept between runs, and keytips skip the toolbar's digit badges while it is hidden. The UI command ui.quickAccessToolbar drives it from scripts and MCP. Regenerates docs/parity-checklist.md, which was behind the catalog. Adds my contributor entry.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* gap: RTF formatting revisions WIP
* gap 19: RTF formatting and paragraph-mark revisions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 1: list kept on Reject; old formatting normalised; section marks; hostile borders
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 2: \oldcprops holds only the changed properties, as Word writes it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: old formatting in styled paragraphs uses the style's size and font
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: RTF import keeps main's heading and code normalisation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: file-format-parity status line keeps main's history
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: a paragraph that was a list item gets its old list back on Reject
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: a numbered item made bulleted keeps its numbered list for Reject
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* gap 19: format matrix names the tracked section mark
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* rtf test: Writer has the section fields main added; rustfmt
---------
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Legacy form fields are drawn: check boxes, and drop-downs' selected entry
Legacy form fields keep their w:ffData since the last round, but were
not drawn: a FORMCHECKBOX has an empty result, so a form of check
boxes came out with none, and a FORMDROPDOWN showed its saved result
rather than its selected entry.
- wordcraft-doc: `legacy_form` reads what layout needs from the kept
w:ffData (kind, name, check box state and size, drop-down entries and
selection, text field length); `InlineObject::legacy_form()`.
- wordcraft-layout: a check box takes the text line's height of room
and is drawn as Word draws it; a drop-down shows its selected entry
(w:result, else w:default, else the first).
Measured on Word's PDF export with a synthetic document (Calibri):
text size | Word square | WordCraft
8 pt | 7.44 | 7.45
10 pt | 9.96 | 9.89
12 pt | 12.36 | 12.33
20 pt | 22.08 | 22.09
w:size 15 | 16.08 | 15.99
The square sits 1.16 pt inside the line height on every side (room on
the line within 0.2 pt of Word's), has a 0.72 pt border and, checked,
an X from corner to corner in 0.48 pt lines; w:checked beats
w:default. docs/file-format-parity.md is updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Draw::Shape takes the dash list main added
---------
Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A line's first item stays on it even when it is wider than the line;
markers before it don't count. A floating object's anchor (a zero-size
object cluster) did count, so a picture a little wider than the line,
after an anchor, went to a second line and left the anchor an empty
line of its own above it. Word keeps the picture on the first line.
Anchors are now ignored like markers when deciding whether the line
already has content.
Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Inserting a second shape removed the first: after an insert the new
shape stays selected, and `insert.shape` replaced the selection, so each
shape replaced the one before. Word draws a new shape beside whatever is
selected and never deletes it (checked by hand in Word for Windows: a
second shape is drawn over the first, which stays).
The new shape now goes after the selection, which is left alone, and
becomes the selection. Selected text stays too. A picture still replaces
a selected picture, which is what Word does as well (checked in Word for
Windows), so `insert.picture` is unchanged.
Tests: three shapes inserted in a row are all kept, which fails with the
old code (1 of 3 kept); a shape inserted over selected text keeps the
text and is selected itself; a picture inserted over a selected picture
replaces it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>