* #2852: personal templates in File › New, Save as Word Template
File › New lists the .dotx/.dotm files in the user templates folder under
Personal (a subfolder per category, search covers them); a click makes an
untitled document attached to the template. Save As › Save as Word Template
starts in that folder (<settings>/Templates, made on first use; File ›
Options › Advanced › File locations › User templates overrides it).
Engine: file.listTemplates (bounded, network folders refused, read root
applies), file.newFromTemplate sets the attached template, file.options
userTemplatesFolder, file.saveTemplate keeps a .dotm name. UI:
ui.saveAsTemplate; Save dialog requests carry a start folder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #2852: review — a personal template asks about unsaved changes; network templates folder refused before it's looked at; New lists the folder once per visit with Refresh; .dotx/.dotm labels alike
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #2617: centre equation-array rows without &
A row with no alignment mark sat in the first column and was right-aligned
to it, widening that column and pushing the aligned rows right (Word's
"First Property of Radicals" case equation). Word centres such a row in the
array; it no longer widens the aligned columns.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #2617: review — test a lone eq-array row wider than the aligned columns
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1281: English dialects in proofing — en-GB, en-CA, en-AU… flag each other's spellings (#1045)
English was one proofing language: en-GB, en-CA and en-AU all proofed like
US English, and US documents accepted British spellings. Now ProofLang has
three Englishes, picked by the tag's region:
- En (en-US, other regions, no language): British spellings flagged
(colour → color).
- EnGb (en-GB, en-AU, en-NZ, en-IE, en-ZA, en-IN…): American spellings
flagged (color → colour, center → centre, analyze → analyse); -ize and
-ise both right (Oxford spelling).
- EnCa (en-CA): British -our/-re/-ence, American -ize, tire, program.
Data: assets/spelling/en-variants.txt, our own ~280 American/British
pairs with a policy per English (MIT OR Apache-2.0, ATTRIBUTION.md row).
Inflections by rule (crates/proof/src/en_variants.rs). A dialect's form
is right when the shared list knows it or its other spelling, so
"realisation" is right in en-GB. No system dictionary needed.
Suggestions put the dialect's own form first. Review › Language lists
English (Australia) and (Canada); the Language dialog adds India,
Ireland, New Zealand and South Africa (names in every catalog, not
native-reviewed). An English interface on an en-GB/en-CA/en-AU… region
writes new documents in that English. Options › Language shows the
document's language instead of a fixed "English (United States)".
Tests: per-dialect accept/flag/suggest, tag mapping, no-panic proptests,
engine end to end through review.language, DOCX w:lang round trip,
region-based editing language, language menu.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
* #1281: review — pin the #40 words (judgement, defence, licence, programme, catalogue) in en-GB and en-US
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1281: review — harness picks every proofing language in Review › Language and checks it is ticked
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1281: review — pin more British spellings in en-GB (goitre, sceptre, enrol, fulfil, instalment, skilful, wilful, encyclopaedia…)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1678: Focus fills the screen; Full Screen View is a bindable command
View › Focus goes full screen with the document on black and a slim bar
whose "⋯" drops the ribbon over the page. Full Screen View
(view.fullScreenView, no default key; bind one in Customize Keyboard)
shows the pages alone with the search box above them; the two turn each
other off. Full Screen (view.fullScreen, F11, #980) keeps its whole-window
behaviour. Escape leaves Focus or Full Screen View, then Full Screen, once
nothing nearer takes it; the system leaving full screen ends all three.
Ctrl+F1 collapses the ribbon (not on macOS). Immersive Reader no longer
turns Focus on. The page widget keeps a fixed id so it keeps focus when
the rulers hide.
Fixes#1678
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1678: one Focus path after the rebase; pin the dropped ribbon staying down on a ribbon click
main's Focus (#1017) consumed Escape in WordApp::ui before
leave_full_screen, so Escape would leave Focus even when it should first
collapse a selection or close a popup. That handler is removed: Focus,
Full Screen View and Full Screen are left through leave_full_screen
only. main's focus_hides_the_ribbon_and_escape_brings_it_back still
pins the panes coming back. Full Screen View and Show the ribbon added to
the hr, ko and sv catalogs, which main gained since.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: Show the ribbon is a named button; a bound Ctrl+F1 wins; Esc in Read Mode leaves a dialog's Escape alone
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: parity checklist regenerated after the rebase (Full Screen View counted)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: harness tests for the one Escape path and Ctrl+F1 through the app
In Focus with text selected on the page, the first Escape collapses the
selection and keeps Focus; the second leaves Focus (fails if the old
focus_mode Escape handler in WordApp::ui comes back). Ctrl+F1 sent as a
key event collapses the ribbon when unbound, and runs the user's command
when bound in the keymap, leaving the ribbon alone (not on macOS).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: review — a click on the dropped Focus ribbon gives the page the keyboard; only Ctrl+F1 yields to a binding
The ribbon dropped over the page in Focus lies over the canvas viewport and
in a Middle-order area, so refocus_after_chrome_click skipped clicks on it
and typing went nowhere until the page was clicked (which also put the
ribbon away). Clicks on the focus_ribbon layer now count as chrome. The
Focus test asserts the page has the keyboard after a ribbon click and that
typing reaches it with the ribbon still down.
dispatch checks the keymap ahead of an interface shortcut only for Ctrl+F1;
Alt+Q and Cmd+Option+R keep their place ahead of user bindings, as before
#1678 (test: a_bound_alt_q_still_opens_command_search).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — Ctrl+F1 (⌘⌥R on macOS) collapses or expands the ribbon; a shortcut of your own on Ctrl+F1 wins
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — ui-parity's Word column gives Word for Mac's ribbon shortcut (⌘⌥R)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — Focus's dropped ribbon is a command, ui.focusRibbon; "⋯" and a click on the page run it
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — the Focus UI test turns Focus on from the status bar's Focus button
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1697: legacy menu keys (Alt, V, U and 17 more Word 2003 sequences)
After Alt, a letter no tab keytip or Search uses starts a legacy menu
sequence, as Word for Windows still takes them (observed there): Alt, V, U
turns on Full Screen View; also views, ruler, zoom, Select All, Find /
Replace / Go To, Symbol, Picture, Font, Paragraph, Word Count and Insert
Table. Hidden as in Word; a hint under the tab row names the keys so far.
A letter that leads nowhere is ignored, Esc cancels, Alt goes back to the
tab badges. Tests keep the first letters off every ribbon letter and check
each target exists.
Fixes#1697
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1697: legacy menu keys stay off on macOS, F10's badges included
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1697: review — the legacy hint shows without a ribbon; Alt, V, U through the app; not on macOS in the docs
With no ribbon showing (Focus, Full Screen View) no tab rects are recorded, so
Alt, V's hint didn't show; it now hangs under the slim bar at the top of the
window. A harness test drives Alt, V, U through the whole app and checks no
letter is typed. The parity row and progress log say the sequences are not on
macOS.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1500: shape fills keep gradients and transparency (model, DOCX read/write)
A shape's, text box's and canvas's fill is now a `Fill` (solid with
transparency, or a gradient kept as the file states it) instead of one
opaque colour, so gradients and transparency survive opening and saving.
- doc: `fill::Fill`/`Gradient` with sanitizing for hostile input; `.json`
keeps writing opaque solid fills as `[r,g,b]` and reads both forms;
`ShapeExtra::outline_fill` keeps an outline gradient.
- docx read: `a:solidFill` with `a:alpha`, `a:gradFill` (stops, `a:lin`,
`a:path`/`fillToRect`, `tileRect`, `rotWithShape`, `flip`), theme fill
styles through `a:fillRef` 1-999/1001+ with `phClr`, `a:grpFill`,
`satMod`, outline gradients, VML `v:fill` gradients and opacity, canvas
backgrounds.
- docx write: `a:solidFill` + `a:alpha`, `a:gradFill` as read.
- engine/docbin/layout: call sites; drawing still uses the main colour
(next commit draws gradients).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: draw shape gradients and transparency (layout, render, PDF)
- layout: WordArt's paint becomes a shared `display::Paint` (solid,
linear, radial); `paint_for` lays a fill over the shape's frame
(linear at its angle, `scaled` honoured; path gradients circular from
their focus to the farthest corner). Shapes, freeforms and canvas
backgrounds use it.
- render: one `set_paint` for shapes, paths and WordArt; radial
gradients; soft edges fade the real fill.
- pdf: axial and radial shadings with per-stop opacity.
- Review fixes: nested groups without a fill pass theirs on to
`a:grpFill`, text-box contents don't take a group's fill, `phClr`
transparency only on `phClr` colours, VML focus ends consistent and
opacity clamped, `rotWithShape`/`scaled` written only when set,
Shape Outline clears an outline gradient, shared stop sanitizing.
- docs: file-format, layout parity, gaps #1, roadmap progress log.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: shape.fill sets transparency and gradients on every selected shape
- shape.fill (now cmd/shape_fill.rs) takes {"color", "transparency"?} or {"gradient": {stops,
type, angle, focus, rotateWithShape}}; a colour alone keeps the transparency; values are
sanitized; applies to every selected shape, text box and canvas as one undo step.
- Formatting (shape.fill, shape.effects) keeps a Shift+click multi-selection, as in Word.
- view.formatShapePane toggles the Format Shape pane.
- Session::undo_mark/merge_undo_since: a pane drag that applies as it goes undoes in one step.
- Helpers for the pane: params(fill), stop add/remove/move, directions, variations and
presets (variation and preset colours provisional until compared with Word).
- Catalog: Shape Fill Gradient, Format Shape Pane.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: Shape Fill › Gradient and the Format Shape pane
- Shape Fill › Gradient: No Gradient, Light and Dark Variations of the shape's colour, More
Gradients… (opens the pane).
- Format Shape pane (right): No fill / Solid fill (colour, transparency) / Gradient fill
(presets, type, direction, angle, stops bar with add/remove/drag and arrow keys, per-stop
colour, position, transparency, rotate with shape). Drags preview live, one undo step.
- Right-click › Format Shape… on shapes, text boxes and canvases.
- Gradient swatches have accessible names; new labels translated (own wording, clean room).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: preset gradients are our own design (clean room)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: Gradient variations, starting gradient and directions as Word 365 does them
Observed in Word 365 (behaviour and values only):
- Shape Fill › Gradient: a No Gradient swatch, then 13 Light and 13 Dark Variations in a
grid five wide, in Word's order; light stops tinted 66/44.5/23.5% with saturation ×1.6,
dark shaded 30/67.5/100% with ×1.15; linear ones scaled; corners with their tile rectangle.
- Format Shape › Gradient fill starts from the theme's first accent (four stops, 0/74/83/100%).
- Corner directions in the pane get the same tile rectangle; Add/Remove sit by the stops bar.
- shape.fill carries "scaled" and "tile"; docx exports color_modified (lumMod, lumOff, tint,
shade, satMod) so variations use the reader's own colour maths.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: review fixes for the shape fill ribbon controls
- Gradient variations are built from the colour the shown variation came from, so picking
variations in turn, or No Gradient, doesn't drift the colour.
- A drag's undo mark is forgotten while the Format Shape pane is closed (a stale mark could
merge later undo steps).
- Dragging on the stops bar away from a handle no longer moves the selected stop; the arrow
keys are taken by the bar (no focus moves or shape nudges).
- Direction and Type changes keep a linear gradient's "scaled" and an imported tile
rectangle; only a corner direction sets its tile.
- Colour modifiers (lumMod, lumOff, tint, shade, satMod) live on Rgb in the doc crate, shared
by the DOCX reader and the gradient builders.
- Previews build their stop list once per swatch, not per vertex.
- Translations for the pane's hint, direction and preset names.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: adapt to main: caret contrast samples gradient fills; upstream tests use Fill/Paint
- Paint::at gives a paint's colour and opacity at a point; the caret's background sampling
(#299) uses it for shape fills, now gradients or transparent, and the Format Shape
previews use it too.
- Upstream tests (#886 shapes survive saving, render) build fills as Fill/Paint.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1500: adapt to main after the rebase: VML presets, rounded corners, new catalogs
- vml_fill takes the shape's shapetype: a VML preset shape (#1377) takes its fill, opacity and
gradient from its shapetype when it states none of its own, as it already took its colour.
- Rounded-rectangle corners (#1476) and the gradient paints meet in render, PDF and the caret's
background sampling; ShapeExtra keeps both `corner` and `outline_fill`.
- Tests that landed on main since build fills as Fill/Paint (VML presets, cover pages, VML
freeforms, path arrowheads, PDF and render shapes).
- The 40 Shape Fill / Format Shape strings in the hr, ko, sv and vi catalogs, which main
gained since.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: docs: shape gradients and transparency, Shape Fill › Gradient and the Format Shape pane
Re-applied on main's docs after the rebase: progress log, file-format, layout and UI parity
rows, gaps #1, status lines and revision history; parity checklist regenerated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: a theme picture fill falls back on the reference colour; VML freeform opacity note
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: adapt to main after the second rebase: VML shapetype tests compare Fill; parity checklist regenerated
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: one VML fraction parser; a preset takes its gradient from its shapetype
Review notes: story.rs's watermark/freeform opacity reuses shape_style's vml_fraction (clamped at the callers) instead of a second copy; a test pins a VML preset whose v:fill gradient comes from its v:shapetype, and the 65536ths form of a fraction.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: adapt to main after the third rebase: the shape duplicate preview test fills with Fill
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1500: review — harness test: right-click a shape, Format Shape… opens the pane and keeps the shape selected
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1500: review — theme keeps every fill style a fillRef can reach (999 per list), still bounded
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1678: Focus fills the screen; Full Screen View is a bindable command
View › Focus goes full screen with the document on black and a slim bar
whose "⋯" drops the ribbon over the page. Full Screen View
(view.fullScreenView, no default key; bind one in Customize Keyboard)
shows the pages alone with the search box above them; the two turn each
other off. Full Screen (view.fullScreen, F11, #980) keeps its whole-window
behaviour. Escape leaves Focus or Full Screen View, then Full Screen, once
nothing nearer takes it; the system leaving full screen ends all three.
Ctrl+F1 collapses the ribbon (not on macOS). Immersive Reader no longer
turns Focus on. The page widget keeps a fixed id so it keeps focus when
the rulers hide.
Fixes#1678
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1678: one Focus path after the rebase; pin the dropped ribbon staying down on a ribbon click
main's Focus (#1017) consumed Escape in WordApp::ui before
leave_full_screen, so Escape would leave Focus even when it should first
collapse a selection or close a popup. That handler is removed: Focus,
Full Screen View and Full Screen are left through leave_full_screen
only. main's focus_hides_the_ribbon_and_escape_brings_it_back still
pins the panes coming back. Full Screen View and Show the ribbon added to
the hr, ko and sv catalogs, which main gained since.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: Show the ribbon is a named button; a bound Ctrl+F1 wins; Esc in Read Mode leaves a dialog's Escape alone
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: parity checklist regenerated after the rebase (Full Screen View counted)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: harness tests for the one Escape path and Ctrl+F1 through the app
In Focus with text selected on the page, the first Escape collapses the
selection and keeps Focus; the second leaves Focus (fails if the old
focus_mode Escape handler in WordApp::ui comes back). Ctrl+F1 sent as a
key event collapses the ribbon when unbound, and runs the user's command
when bound in the keymap, leaving the ribbon alone (not on macOS).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1678: review — a click on the dropped Focus ribbon gives the page the keyboard; only Ctrl+F1 yields to a binding
The ribbon dropped over the page in Focus lies over the canvas viewport and
in a Middle-order area, so refocus_after_chrome_click skipped clicks on it
and typing went nowhere until the page was clicked (which also put the
ribbon away). Clicks on the focus_ribbon layer now count as chrome. The
Focus test asserts the page has the keyboard after a ribbon click and that
typing reaches it with the ribbon still down.
dispatch checks the keymap ahead of an interface shortcut only for Ctrl+F1;
Alt+Q and Cmd+Option+R keep their place ahead of user bindings, as before
#1678 (test: a_bound_alt_q_still_opens_command_search).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — Ctrl+F1 (⌘⌥R on macOS) collapses or expands the ribbon; a shortcut of your own on Ctrl+F1 wins
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — ui-parity's Word column gives Word for Mac's ribbon shortcut (⌘⌥R)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — Focus's dropped ribbon is a command, ui.focusRibbon; "⋯" and a click on the page run it
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1678: review — the Focus UI test turns Focus on from the status bar's Focus button
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #2048: text above a float's anchor wraps around it
A floating object joined the page's wrap areas only when layout reached
its anchor paragraph, so paragraphs above the anchor ran behind a picture
placed higher up the page (fixed on the page, or moved up from its
paragraph). Layout now notes such floats and lays the pass out again
(at most 3 times) with their wrap areas known from the page's start.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #2048: early-float wrap settles when the anchor moves to the next page
Review follow-up. Rewrap passes add to the wrap areas pages start with
instead of replacing them, and a pass is done when every float found
above its anchor sits inside the room its page started with. A float
whose wrap pushes its own anchor onto the next page has no fixed point;
the first page keeps the room and the float goes with its anchor, so
the passes no longer swap between the two pages. The last allowed pass
moves any float still not covered into its seeded room, or down to its
paragraph's top, so the returned layout never has text under it.
Tests: the reviewer's page-boundary case, and a last pass with nothing
seeded keeping floats clear of earlier text.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW
* #2048: review — the early-float page-boundary case is pinned for 4 to 18 paragraphs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #2048: review — a column break into another width keeps the floats where the first piece drew them
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1740: triple borders draw as three lines on screen and in PDF
Triple borders (tables, paragraphs, the ### AutoFormat line) were drawn as
double lines. Layout now emits a Triple stroke; the renderer and the PDF
exporter draw three thin lines across the line's direction, so vertical
table borders get three side-by-side lines too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1740: rebase onto main — triple border strokes use the line's f64 ends
Main now converts the line ends to f64 once and draws through a shared
offset-line closure; the triple stroke uses both instead of dereferencing
the shadowed values.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
* #1740: review — PDF triple-border test checks the three strokes are distinct and 3 widths apart
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #1746: the text pointer shows on white paper on Windows
Windows' inverting I-beam pointer stays white over the GPU-drawn page, so
it vanished on white paper (#1746, #1417, #1126). On Windows the canvas now
hides the system pointer wherever it would be the I-beam and draws one in
its place: black on light paper, white on dark (as the caret, #299), edged
in the other colour. Other systems keep their own I-beam.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #1746: the drawn I-beam snaps to physical pixels, so it stays white on the dark page at 125%/150%
Review note on #2230: the 1 pt fill inside a 3 pt edge was snapped to
half a point, so at fractional scaling the white stem was anti-aliased
into the black edge and read grey. Fill and edge are now whole pixels
wide and lie on pixel edges.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1746: the drawn I-beam takes the canvas colour between pages and on cut margins
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1746: tests pin whole-pixel strokes at 125%/150% and the canvas colour off the page
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #1746: review — hovering the page leaves the selection alone (test)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
* #2103: double-clicking a later page's footer opens it without scrolling up
The double-click's first press in the footer band moved the caret to the
nearest body text and the caret-follow scrolled the view up to it, away
from the footer being double-clicked. A press in the header/footer band
from the body now leaves the caret and the view alone, as in Word; the
release still opens the footer on the clicked page.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* #2103: the footer test checks the caret and the view at the press too
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kKPnx8r1vcYumjyL3kcuz
* #2103: review — footer test checks the clicked page's exact footer story, with a header present
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WEhZXSxzS8orX7f9J3qpr
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: aiongg <aiongtaigi@gmail.com>
The Recover page forced a black fill and white text whatever the theme. It now uses the same fills and text colours as the other Backstage pages.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Fix#1974: the caret and clicks land between the letters of a ligature
The glyph's advance is shared evenly among its letters (mirrored in
right-to-left text); ligature letters keep their character spacing.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Fix#1974: a long word never wraps inside a ligature
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Fix#1974: a hyphenation point never splits a ligature
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1974: ligature tests use a bundled font that draws fi and ffi as ligatures
They assumed the installed default-font stand-in draws ligatures; Liberation Sans (Linux) draws
none, so they failed there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
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>
* gap 19: RTF import compares heading and code run formatting with the file's stylesheet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* gap 19: review round 1: a style chain with formatting words is complete; unnumbered entries are Normal
Co-Authored-By: Claude Opus 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>
* Content controls: typed text takes the control's formatting, in any language
Text typed in place of a content control's placeholder takes the
control's own run properties (w:sdtPr/w:rPr, ECMA-376 §17.5.2.27), as
in Word: in a protected form filled in Word for Mac, all 17 entered
values carry the style their control's rPr names. WordCraft kept that
rPr only as XML to write back and gave typed text the placeholder's
formatting minus the placeholder style.
It also knew the placeholder style only by its English id,
PlaceholderText. German Word writes Platzhaltertext (the name stays
"Placeholder Text"), so in forms from German Word every entry stayed
grey, on screen and in the saved file, and restoring a placeholder
added a second Placeholder Text style. The style is now found by its
built-in name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Content controls: typing drops a showing placeholder's own style
A control without run properties that shows its placeholder: text typed
over it drops the placeholder run's character style whatever its id
and name. Word localizes only the placeholder style's id, but another
program may localize its name too, and the name lookup then misses it.
Restoring a placeholder and new controls keep the name lookup.
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>
* Restrict Editing: forms protection keeps a form's content controls
With Filling in forms enforced (w:edit="forms"), Word greys out the
Developer tab's control buttons and Properties, and a field's shortcut
menu offers no Remove Content Control: only the fields' content can be
filled in. WordCraft refused these only outside the fillable fields, so
with the caret in one it removed the control (its content left as
plain, no longer fillable text), inserted new controls inside it, and
renamed it; only lock changes were refused.
Under forms protection, inserting any content control, Properties
(every setting) and Remove Content Control are now refused before they
run. Typing, Toggle Check Box, Choose List Item and Set Date still fill
the fields. Other protection modes are unchanged.
Observed in Word for Mac 16.113 on a protected real-world form
(72 content controls: rich text, check boxes, date pickers); the same
form filled in WordCraft opens in Word still protected, with its values,
ticks and dates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Restrict Editing: parity row states what forms protection refuses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Restrict Editing: correct the comment on content-control property edits in forms mode
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>
The pane drew each heading with `Button::selectable(..).truncate()`, so a heading wider than the
pane ended in an ellipsis and its text had to be read from the hover tooltip. Word wraps the row
instead.
`nav_heading` now wraps: the button takes `Button::wrap()` and a minimum size of the available width
and one row, so the label continues on further lines inside the pane, the row grows with the text,
and the pane still keeps its width. The trailing grow atom still keeps the text flush left under its
parent (#1199). The hover tooltip goes with the ellipsis: nothing is hidden now, and a word wider
than the pane is broken by the wrap instead of overflowing.
The navigation test asserts the wrap instead of the ellipsis. For a heading well past the width and
for a single word wider than the pane, the label has more than one line, is not elided, stays inside
the pane, does not widen it, and takes a taller row than a one-line heading in the same pane.
`docs/ui-parity.md` and `ROADMAP.md` record the new behaviour.
Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
* #2853: Word XML, web pages and MHTML saved as .doc open by what they hold
A Word 2003 XML Document named `.doc` went to the Word 97-2003 reader, which refused it ("Invalid
CFB file"), and the window showed a blank page. Word XML (2003 or Flat OPC), a web page and a
Single File Web Page (MHTML) under a `.doc`, `.docx` or `.rtf` name now open by what they hold,
as in Word. MHTML is read for the first time (`.mht`/`.mhtml` too): the MIME parts are split and
decoded, the page is imported as HTML in its charset and its pictures load from the other parts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2853: leave MHTML to #1104, keep Word XML and web pages saved as .doc
#1104 adds an MHT reader (`wordcraft_formats::mht`), so this branch drops its own MHTML reader and
the `.mht`/`.mhtml` extensions instead of adding a second one. A Word XML Document or a web page
under a `.doc`, `.docx` or `.rtf` name still opens by what it holds (the #2853 fix).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2853: a web page's signature needs its tag name to end (`<htmlish>` is other XML)
CodeRabbit: `starts_with("<html")` took XML with a root like `<htmlish>` under a `.doc` name for a
web page, though XML that isn't a Word document stays an error. `<html` and `<!DOCTYPE html` now
need white space, `>` or `/` after them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #2853: rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Layout: a word after a slash wraps whole, like Word (#2963)
UAX #14 (LB13) forbids a break between a space and a slash, and the break
after a slash is dropped on purpose (Word keeps "and/or" whole). Together
"caption /word" had no break opportunity at all, so a line too short for
both filled letter by letter and split the word. Add the break before a
slash that follows whitespace and starts a word.
Co-Authored-By: Claude <noreply@anthropic.com>
* Layout: no break before a slash after a no-break space (#2963)
The break before a slash word took any whitespace, U+00A0, U+2007 and
U+202F included, so "Nr. /12" could wrap after the no-break space.
Only a breakable space counts now. The parity checklist lists the rule
on its own row as untested against Word.
Co-Authored-By: Claude <noreply@anthropic.com>
* Layout: test the slash word in a fixed-width table cell (#2963)
The report came from a table cell, where the cell's own width is the
line width.
Co-Authored-By: Claude <noreply@anthropic.com>
* Docs: gaps and typing parity for the slash-word break (#2963)
Doc wording taken from #2994 (maxm11), with the test names of this PR.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude (ThinkSystem) <claude@thinksystem.local>
Co-authored-by: Claude <noreply@anthropic.com>
* #662: ribbon groups shrink to fit the window (compact, then one drop-down)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1285: collapsed ribbon groups stay open while used; Styles gallery trims before it folds
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Ribbon collapse: accessible name for folded groups, Tables/Links icons (#662)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Ribbon: Esc in a collapsed-group popup doesn't leave full screen; tab-strip test ignores same-named ribbon buttons (#1285)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Ribbon tab-strip test: pick the tab, not a same-named folded group (#662)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Ribbon: a folded group's open state is keyed by its place in the row, not just its title (#1285)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* rustfmt: collapsed_group signature (#1285)
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 Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* File (Backstage): an icon beside each side-menu item (#1714)
The side menu (Home, New, Open, Info, Save, Save As, Print, Export,
Close, Options) was text only. Each item now has an 18 pt icon in the
label colour, greyed out with it when no document is open. Existing
icons where they fit (house, new page, open folder, info, save, printer,
close), and three new ones drawn in code: Save As (the save disk with a
pencil), Export (a page with an arrow leaving it) and Options (a gear).
Original artwork, no asset files.
Test: every item names its own icon, and each one is a drawn icon, not
the letter-tile fallback an unknown name gets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: one Backstage row with both the Close (#1712) and side-menu icon (#1714) notes
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>
Went through the whole Italian catalog. Fixed the ones that were wrong (Mailings is Lettere in Word, Redo is Ripristina, cursor commands say riga) and made some terms consistent (input penna, Disponi testo, Ombreggiatura).
Also marked Italian as reviewed in the localization doc and added myself to people.toml.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #1348: WIP
* #1348: keep comment drafts per document, keep them on a refused edit, write them before save/close/new/open
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1348: a refused comment draft can be dropped; fields read-only where comments can't change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1348: ui-parity: one Task panes and Context menus row after the rebase
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Docs after rebasing on main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1348: keep the window open while a web save is unconfirmed; split the roadmap row by operation
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1348: pending comment drafts count as unsaved changes for the browser; a save error isn't hidden by a draft refusal
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* #1348: an agent can't replace the document over a pending comment draft without force
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>
* #778: .xls and legacy-encoding recipient lists; filter and sort recipients
- Recipient lists: legacy Excel 97-2003 .xls (BIFF8 reader on the cfb crate already in the
tree); Excel 5.0/95 and password-protected .xls refused with a clear message; a CSV named
.xls reads as text, an HTML one gets an error. .xls added to the picker filters (desktop,
web, built-in browser) and drop handling.
- Text lists (and plain-text import) detect legacy encodings when not UTF-8/UTF-16: GBK/GB18030,
Big5, Shift_JIS, EUC-KR, Windows code pages (encoding_rs + chardetng, MIT/Apache); an
`encoding` param overrides.
- mailings.filterRecipients / mailings.sortRecipients: field, comparison (equal, notEqual,
less, greater, lessOrEqual, greaterOrEqual, blank, notBlank, contains, notContains), value,
And/Or joins; sort keys. Preview, Finish & Merge and labels use only the matching records
in sort order. Edit Recipient List edits every record and keeps the filter.
- Recipient List dialog: Filter and Sort section and a merge-order column; catalog rows;
strings in every catalog; docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Hungarian and French strings for this change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #885: total order for recipient sort, Bulgarian strings
- compare_cells now ranks numbers (compared numerically) before text (compared
case-insensitively), so it is a total order; a column mixing 9, 10 and 1a
could make slice::sort_by panic. Regression test sorts mixed columns.
- Bulgarian catalog: the 24 Filter and Sort strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: placeholder (work in progress)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: keep the filter on a failed Edit Recipients; cap .xls/.xlsx cell text; dialog drops rules on unknown fields and caches the merge order
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1287: rustfmt
Co-Authored-By: Claude Opus 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>
* References: Word sources file import in Manage Sources (#2640)
Manage Sources gains Import… and references.sources an "import": path
parameter: it reads a b:Sources document (Word's own Sources.xml) into the
document's sources with the same reader as a document's own bibliography
part. A tag already here is left alone, an untagged entry is matched by
title and author or gets a tag like a new source, the whole import is one
undo step, and a file that isn't b:Sources is refused. Desktop only; the
web build hides the button.
The docx reader now shares the b:Sources parser (crates/docx/src/
bibliography.rs) with the import, with the same dedupe and 10,000-source
cap; the hostile-input cap test still passes.
Tests: docx unit tests (names, kinds, accessed/DOI fallbacks, repeated
tags, empty entries, wrong root), engine import test (add, undo, second
import, untagged matching, refused file), ui tests (the dialog button asks
for a "sources" file; finish_pick imports), wordcraft-cli end to end
(import, cite, bibliography, save, reopen; the package's customXml
carries the sources). The button's label is in every interface catalogue.
ROADMAP progress log and docs/roadmap.md, gaps.md, file-format-parity.md
and target-app-parity.md get their rows.
* References: review fixes for the sources import (#2640)
- read_sources validates the root namespace instead of the local name alone:
an un-namespaced or unrelated <Sources> is refused, and both bibliography
namespaces read — OOXML's and Word's own 2004/10 (the VBA Source.XML
format), the latter mapped onto the canonical b prefix with parse_ns.
Tests: the 2004/10 namespace is accepted; a no-namespace or unrelated
root is refused.
- The import loop builds the tag and (title, author-or-editor) sets once and
keeps a per-stem counter, with the stem rule shared with auto_tag
(auto_tag_base). Importing thousands of untagged sources no longer costs
O(n^3). Test: untagged sources sharing a stem get Riv21, Riv211, Src, and
a second import adds none.
- docs/target-app-parity.md and docs/file-format-parity.md no longer claim
bibliography sources aren't saved in DOCX: #169 and #532 are closed and
the round trip exists (#790).
* References: doc comments for the sources import helpers (#2640)
Hoist the untagged-source matching helper (by_author) to module level and
document sources, auto_tag_base and sources_ui, so the functions this PR
touches meet CodeRabbit's docstring-coverage pre-merge check.
* References: re-apply the sources import's progress notes (#2640)
The rebase onto current main took upstream's copies of ROADMAP.md,
docs/file-format-parity.md and docs/gaps.md, because the rows this PR
adds conflicted with upstream's own appends. Re-apply them on the current
text: the progress log and revision rows, the bibliography row's
source-format note, and #15's evidence, with each status line's change
note. The other two documents (docs/roadmap.md, docs/target-app-parity.md)
merged cleanly and keep their rows.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2573: Picture Format › Size takes sizes in any unit, pixels too, and opens a Size dialog
The picture size options were too limited (#2573, and #1901's request to enter pixels without
deforming the picture): the ribbon's W/H fields were points only, rejected "300 px", resized on
every keystroke (one undo step per digit), and there was no way to scale from the original
size, lock or unlock the aspect ratio, or reset the size.
- Picture Format › Size: W/H show the interface unit (Options › Advanced) and take a size typed
in any unit, `px` included; it applies on Enter as one undo step, and the other side follows
so the picture keeps its proportions. A drag still applies live.
- The Size group's launcher (and `picture.size` run without a size) opens a Size dialog: height
and width, scale of the original size or of the size it opened with, Lock aspect ratio,
Relative to original picture size, the original size with its pixels, and Reset.
- `picture.size {"original": true, "scale"?}` sizes from the picture's original size (its pixels
at 96 ppi, read from the image adjustments started from, less what is cropped). A size past
4000 pt that keeps the proportions now shrinks as a whole instead of flattening.
- New strings translated in every interface catalog.
The crop tool already exists (Picture Format › Size › Crop) and is being extended in #2027 and
#2440; options cut off in a narrow window are what #708 and #1293 (ribbon groups that shrink to
fit) address.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* contributors: credit @matteo-andreuzza by their GitHub profile name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* #2573: Size keeps the proportions at the 1–4000 pt limits and follows the selection
Review follow-ups (CodeRabbit on #2889):
- Size dialog Reset on a photo larger than 4000 pt (a 6000 px wide one is 4500 pt) set it to
the original size, which `picture.size` then clamped side by side: the picture was flattened.
Reset now shrinks both sides by one factor.
- Locked, a side typed past the limit clamped the other on its own (15×30 pt with width 4000
became 4000×4000). Both sides now scale by one factor.
- `fit_size` (engine) only shrank: a proportional size under 1 pt (scale 1% of 50×200 pt) became
1×2 pt. It now picks the factor nearest 1 that brings both sides within 1–4000 pt, falling
back to side-by-side clamping only for shapes too long and thin for any; the dialog uses it.
- Dialogs aren't modal: picking another picture while the Size dialog was open made OK size it
with the first one's numbers. The form now follows the selection (starts over on the new
object, closes when nothing is left to size).
- Doc comments on the Size form's methods.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* #2573: Size dialog reads the picture again after a crop, change or resize
Review follow-ups (CodeRabbit on #2889):
- The open Size form only compared the selected object's position. A crop from the ribbon,
Change Picture or a handle drag while the dialog is open left Reset and OK working from the
old crop and size. The form now remembers the picture, crop and size it was read from (bit for
bit, without comparing whole objects that may hold large embedded data) and reads them again
when any changes.
- An egui harness test drives the dialog end to end: launcher, a height typed in pixels, nothing
applied before OK, OK sizes in proportion as one undo step and closes the dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* #2573: size fields never resize a picture just by showing it
Review follow-up (CodeRabbit on #2889): egui's DragValue clamps a value already outside its
range when it is drawn and reports that as a change. The Size fields act on changes, so:
- Picture Format's W/H resized a picture smaller than 1 pt (a 1×1 px spacer is 0.75 pt) or
larger than 4000 pt as soon as the tab was shown, an edit and an undo step nobody asked for.
The code before this PR had the same problem with its 4–2000 pt range.
- The Size dialog's scale fields silently changed a picture enlarged past 10000% (a small icon
made large), and its lengths a size outside 1–4000 pt, which OK then applied.
The range now bounds only what is typed or dragged (`clamp_existing_to_range(false)`). Both
regression tests fail without it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* #2573: Shape Format and Chart Design size fields leave existing sizes alone too
Review follow-up (CodeRabbit on #2889, outside the diff): the same egui clamp as in d56a105.
Shape Format's W/H (4–2000 pt) and Chart Design's (36–2000 pt) clamped a size outside their
range as soon as the tab was shown and resized the object unasked. The new Size dialog makes this
easier to reach, since it takes shapes up to 4000 pt, but a thin line under 4 pt was already cut.
Both now show such a size as it is and act only on a new value. Shape Format's range follows
`picture.size` (1–4000 pt), so dragging a 3000 pt shape no longer snaps it back to 2000.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
* #2573: Esc cancels a size typed in Picture Format; a typed size applies when the field is left
Review follow-up (CodeRabbit on #2889, outside the diff) asked to apply typed sizes on Enter only.
That is not done: a field keeps what was typed when it loses focus, as text fields do, and
otherwise typing a height in the Size dialog and clicking OK would throw it away. A harness test
now pins that path with a real click (press, then release frames later).
Checking it turned up a real bug: Esc did not cancel in the ribbon's W/H fields. egui 0.36 keeps
a DragValue's typed text after Esc and applies it the next frame, when the field still counts as
having just lost focus but Esc is no longer pressed. The fields now remember when Esc ended the
typing and drop that late value. (In the dialog Esc cancels the whole dialog, so nothing applies
there either; both are tested.)
Docs and comments now say "applied once typed (Enter, Tab or a click elsewhere; Esc cancels)"
instead of "applied on Enter".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZArmeAT48gF84aKj7Zx4C
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Proofing: say when a dictionary is installed but unreadable, with file and reason (#2830)
* proof test: use SHIFT_JIS as the unsupported encoding (ISO 8859-1 is decoded since #2904)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #2108: File › Print scales pages to another paper size
Scale to paper size on File › Print (Word: Pages per sheet › Scale to
paper size): No Scaling or any Layout › Size paper (Letter, Legal, A4, …).
Every page is scaled to the paper and centred, the paper turned to the
page's orientation, in the preview, the print job and Save these pages
as PDF. Native jobs also ask the spooler for that media. The document's
page size is unchanged. New `paper` param on ui.print/ui.printSubmit and
file.printPdf; unknown papers are errors.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* pdf: bookmark destinations from #2364 use the scaled Fit too
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Keep the ODT paragraph 'Keep with next' option on import and export (#2813)
* file-format-parity: keep with next on the ODT row
---------
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>
* #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>