* #775: STYLEREF fields in headers and footers show each page's result
Running headers/footers with STYLEREF (e.g. the current chapter title) are
evaluated per page at layout time (ECMA-376 §17.16.5.59): the page's first
paragraph of the style (\l: last), else the nearest before, else after.
Style names/ids, heading levels (STYLEREF 1), character styles, and the
\n \r \w \s \t \p switches. Body STYLEREF fields update with the other
fields. STYLEREF added to Insert › Quick Parts › Field with its options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #775: STYLEREF error text in our own words
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Review fixes for #882: translate the STYLEREF Field dialog strings
Adds the style picker label, the six STYLEREF switch labels and the
STYLEREF description to all 18 interface catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* STYLEREF: reuse the style index while the body and styles are unchanged
Headers with STYLEREF rebuilt and hashed the body's style index on every
layout pass (echelon's follow-up note on #882). The layout cache now keeps
the index with the body blocks it came from (shared, so an edited block is
a different pointer) and the styles/numbering hash, and rebuilds it only
when either changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* vi: STYLEREF field strings
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>
* test(engine): drop a needless drop() that clippy 1.97 rejects (drop_non_drop)
* Comments: a click inside a selected balloon moves the caret in its text (#1013)
- The canvas under the markup area also "contains" the pointer, so a press on the balloon counted as a click on the page and left it
- Presses over a balloon card now belong to the card, in both the deselect check and the document click in mouse()
- Test: press, drag and release inside the selected balloon; a click on the page still leaves it
* Comments: an option to show only the selected text's comment (#1055)
- Track Changes Options › Comments: "In the margin" (default, as in Word) or "Only for the selected text"
- The second mode takes comments out of the margin column; the comment under the caret, or the clicked one, floats beside the page, over its edge in a narrow window
- The floating card has its own foreground layer, so presses on it never reach the canvas
- review.trackingOptions takes commentDisplay: margin|selected
- comment_range_at_caret: the comment whose range holds the caret
- Strings in all catalogs; tests for the option, the helper and the card's clicks
* vi: comment display options
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: merge settings.xml into the original instead of regenerating it (#1034)
A save regenerated word/settings.xml from the model, so every setting the
model doesn't hold was dropped: compatibility options (lost in 18 of 18
real-world Word files), rsids, document variables, proofing state and the
colour scheme mapping.
The reader now keeps the original part when it holds such settings, and
the writer merges into it: the original root and every unowned child stay
byte for byte; owned children follow the model in CT_Settings order; in
w:compat only compatibilityMode is replaced. The privacy options are the
model's since #1062, so they are owned too. Kept children that point at a
relationship (a mail-merge data source) are dropped. Anything unexpected
falls back to the generated part. Files WordCraft wrote itself keep
nothing extra.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: carry unmodelled parts and keep comment ids, style defaults, latent styles, list ids and app props (#1034)
Builds on the settings.xml merge.
- Parts the package or main document relate to that WordCraft neither
models nor writes (webSettings, stylesWithEffects, people, add-in task
panes, the thumbnail, future parts) are carried verbatim with their
relationships and content types; relationship types the writer
produces, and any the body refers to by id, are not (HANDLED_RELS).
- commentsIds / commentsExtensible: durable ids and UTC dates are read
into the model and regenerated against the written paragraph ids.
- w:default on localized default styles, w:latentStyles, lists'
nsid/tmpl/multiLevelType, and app.xml Template/Company/Manager/
HyperlinkBase round-trip; counts are not carried, and Company/Manager
are dropped when the file asks to remove personal information.
- Comment reference marks use the document's "annotation reference"
style id instead of a hard-coded CommentReference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: przemek-arch <263159043+przemek-arch@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
File › Options › Proofing gets a Dictionaries folder: a folder of Hunspell
.aff/.dic files searched before the system locations, saved with the settings
and applied at start-up. An app started from Finder or the Dock doesn't see
WORDCRAFT_DICTIONARIES, so this is how it finds a dictionary kept in a folder
of the user's own. Changing it reloads the Polish and Portuguese dictionaries
and checks the text again. The pane lists the dictionary file each language
found, or that none was found, so missing spelling marks are explained.
Agents set it with file.options {"dictionariesFolder": path | "auto"}; a
missing folder or a relative path is refused and changes nothing.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Clippy 1.96: simplify boolean guards, drop a needless drop()
Rust 1.96's clippy flags nonminimal_bool in find_pattern, table_mouse and
table_resize, and drop_non_drop in a ribbon test; `cargo xtask ci` failed at
clippy on main. Same logic, rewritten as clippy suggests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UI: mini toolbar steps aside for the ribbon
The mini toolbar covered the ribbon's drop-downs (the font size list opened
under it). A press outside the document and the bar (the ribbon, a pane, the
rulers, the status bar) now puts the bar away until the selection changes or
the next press in the document; presses on popups (the bar's own lists, an
open ribbon list over the page) neither hide nor restore it. The bar also
moves from the Tooltip layer to Middle, so every popup and menu draws over
it, as its comment always said.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UI: mini toolbar scrolls under the ribbon, not over it
With text selected, scrolling the selection up toward the ribbon carried the
mini toolbar along on top of the rulers and ribbon. The bar is now clipped to
the document area: it slides under the rulers and ribbon (or the status bar)
with the text and goes once none of it shows. Its area is constrained to the
document for hit testing too, so the hidden part no longer swallows clicks
and drags meant for the ruler or the ribbon underneath.
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>
* feat(ui): implement Ctrl+drag to copy objects on canvas
* feat(ui): implement Ctrl+drag to copy objects on canvas and update translations for "Copy to"
* vi: Copy to
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Down from the start of the line above a table (or Up from the start of the
paragraph after it) skipped the whole table: `DocLayout::vertical` took the
nearest line covering the caret's x, and a cell's lines start a cell margin in
from the table's edge, so the paragraph beyond the table won. Lines between
the caret's paragraph and that line in document order now come first: the
caret goes into the nearest such line's table row, to the cell under it or the
horizontally nearest. Another cell of the caret's own row never counts, so Down
in a table still goes to the cell below. Pinned in `tests_typing.rs` and
`docs/typing-parity.md`.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Reject All (and Accept All of a whole deleted paragraph) joined the next
paragraph into the emptied one, so the next paragraph took the removed
paragraph's properties: a § heading after a rejected list item became a
list item and later § numbers shifted. An emptied paragraph is now removed
whole, as Word does. Found while round-tripping a Word-saved contract.
Co-authored-by: przemek-arch <263159043+przemek-arch@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M10: enable document renaming and account settings
* no-mistakes(review): Allow case-only document renames without overwriting files
* no-mistakes(review): Keep unsaved rename as session file name, not Title
* no-mistakes(review): Keep AutoSave and Recent across renames; Compare clears name
* no-mistakes(document): Document file.rename write-root rule; dedupe rename gap note
* vi: Rename Document strings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #916: Cross-reference opens a dialog
The ribbon's Cross-reference button ran insert.crossReference with no
target, which only returns the target list, so nothing happened. It now
opens a Cross-reference dialog: reference type (heading, bookmark,
equation, figure, table), what to insert (text, number, page,
above/below) and the item, ending in insert.crossReference. Scripts and
agents still get the target list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Cross-reference dialog strings in the audited catalogs (ru, hu, tr, bg)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: Cross-reference dialog strings in pl, it and vi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: justinjohn0306 <babyjjbabyjj549@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Tables: AutoFit in a nested table fits within its cell (#971)
A double-click on a nested table's column border let the table widen up to
the page's right margin, far past the cell around it. It now widens only up
to the right edge of that cell's text (a right-to-left table's start margin
on the right); the columns right of the border still keep their widths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: a selection through a table shades its rows' whole cells (#971)
A selection from text into or across a table takes whole rows (#722), but
only the letters in the cells were highlighted. Every row it takes from start
to end now shades all its cells (an empty one too), as selected cells do, and
the letters in them aren't shaded twice (`table_mouse::selection_rects`, used
by the canvas). A selection that starts and ends in one table keeps its text
highlight, so selecting all the text of a cell doesn't look like selecting the
cell; a nested table in a shaded row shades with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Typing: Shift+Right at a cell's end selects the cell first (#971)
Shift+Right out of the cell the selection is in selected that cell and the
next one at once. As in Word, the first step out of the cell now takes the
cell alone (its end-of-cell mark) and the next press goes on into the next
cell; in an empty cell the first press selects it. Shift+Left in
right-to-left text, the forward step there, does the same. Pinned in
`tests_typing.rs` and `docs/typing-parity.md`; progress docs for all three
table fixes of #971.
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>
#1527's engine test needs a system Turkish Hunspell dictionary; on
machines without one it failed the workspace tests.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* proof: Turkish spelling, suggestions, grammar and hyphenation
* Turkish: dictionary tests skip when no Turkish Hunspell dictionary is installed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ODT: read and write tracked changes (gap 19)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
# ROADMAP.md
# crates/engine/src/tests.rs
# crates/formats/src/model.rs
# crates/formats/src/odt.rs
# docs/file-format-parity.md
# docs/gaps.md
* ODT tracked changes: deleted paragraph marks don't take the body's insertion; one region per deletion
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>
Lift equation color from run-level OMML to CharProps during read,
push it back to runs during write. Keep source OMML until the color
changes to preserve control properties the model can't hold.
* feat-ink-to-shape-arrows: WIP
* Draw: Ink to Shape recognises arrows (gap 12)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ink to Shape arrows: hand-drawn shafts, more tests, docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ink to Shape arrows: the tip is the first near-farthest point
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Ink to Shape arrows: step to the real tip on dense strokes
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>
- Table autofit measured every cell paragraph at an unbounded width through
the paragraph cache, so a long table held two layouts per cell. The cache
now keeps only the measured narrowest/widest widths (`LayoutCache::measures`,
same key and eviction as the layouts).
- The cache key formatted each cell's table-style text formatting with Debug
for every cell paragraph on every pass; the hash is now memoised per pass.
- Update Fields added the hidden ListEntry style even without a generated
list, which changed the layout environment and dropped the whole cache.
The style is now added only when a list is rebuilt.
Co-authored-by: dfgodvan <180286157+dfgodvan@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #1381: WIP
* #1381: HTML import: bold <th>, cell text-align, region paper for HTML/MD/TXT
Header cells are bold unless their own CSS says otherwise. text-align on a
td/th, or inherited from its tr/row group/table, aligns the cell's
paragraphs; a paragraph's own alignment wins, and a table's align attribute
(which places the table) no longer counts. Files that give no page size
(HTML, Markdown, plain text) open on the region's paper, as a new blank
document does: file.open uses the session's region, wordcraft-cli reads the
system region the way the app does (Region::from_locale_vars, shared with
ui-egui). Tables size to that page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1381: the CLI uses the system region everywhere; help mentions WORDCRAFT_REGION
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>
* Ctrl+A stays on this page, and in a table grows cell, then row, then table, then the document (#1424)
Fixes#1424
* Table: Option::map for the outer-cell selection (clippy::manual_map on Rust 1.99)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Update Table took an entry's text from the heading's plain text, which has
no list label, so headings numbered through a multilevel list (1, 1.1)
lost their numbers in the TOC. The entries now walk the body's list
counters and put the label and the level's separator (tab, space or
nothing) before the text, as Word writes it.
Co-authored-by: dfgodvan <180286157+dfgodvan@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Every cell in a row gets the row's largest top and bottom cell margin, so
a cell without its own margins lines up with its neighbours.
- An exact row height covers the top border, top margin and text; the
bottom margin comes on top. Dragging a row border, or drawing a line
through an exact row, accounts for it.
- A row split across pages must keep its cells' bottom margin, border and
last paragraph's space after on the first page, and widow/orphan control
applies to the paragraphs in its cells. A row whose text fits but whose
margin doesn't no longer moves to the next page whole.
- Each cell of a split row goes on at the top of the continuation (one
cell's widow line no longer pushes its neighbours down), and the
continuation is as tall as the cell needing the most room, and at least
the rest of the row (a minimum row height holds over the pages).
- A nested table's cell the page bottom cuts through goes on from the cut
with what is left of it, and the text after it stays below it.
- A row of an exact height never splits: it moves to the next page whole.
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* #980: full screen on F11 and View › Window › Full Screen
view.fullScreen toggles OS-level full screen (ViewportCommand::Fullscreen),
on F11 and ⌃⌘F (macOS); Escape leaves it when nothing else takes Escape.
The window's own full screen changes (WM, macOS button) keep the flag in
step. Disabled on the web (the browser's own F11). Next Field moves from
F11 to Word's other keys, Alt+F1 / Alt+Shift+F1 (Shift+F11 kept).
Ribbon button with a drawn icon, keytip, translations in all catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #980: Escape collapses a selection before leaving full screen; F11 only off macOS
In full screen, Escape left it even when it had a selection to collapse
or a drag to cancel; it now leaves only when nothing else takes Escape.
The shortcut read Ctrl+Ctrl+F on Windows and Linux: macOS gets ⌃⌘F and
F11, others F11. Docs: Alt+F1 may be taken by GNOME/KDE; keyboard
counts re-measured from the registry (152 commands, 172 combinations).
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>
* #961: Alt+numpad decimal character codes (ANSI, OEM 437/850, Unicode)
Hold Alt, type a decimal code, release Alt: the character is typed at the
caret through the new text.typeCode command. A leading 0 means
Windows-1252, no leading 0 the OEM code page (437, or 850 for Western
European locales), above 255 the Unicode character. Not on macOS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #961: Alt codes follow the system code pages; ignore auto-repeat, drop on blur
text.typeCode takes optional ansi (874, 1250-1258) and oem (437, 737, 850,
852, 857, 866) code pages; OEM 852/866/737/857 tables added from the public
mapping files. On Windows the shell passes the system locale's pages and, for
codes up to 255, lets the character Windows composes replace ours (above 255
ours stays and Windows' byte-reduced text isn't typed). Auto-repeat of a held
digit adds nothing, and losing focus drops a half-typed code.
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>
* #960: double-click the margin to create and edit a header or footer
Double-clicking the top or bottom margin in Print Layout now edits the
header/footer that page shows, creating it (first page, even page or
default slot) when the page has none. insert.editHeader/editFooter take
{"page"}; insert.closeHeader takes away a created one left empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #960: making a header by double-click is an undoable, protected edit
insert.editHeader/editFooter stay pure and hand creation to the new
mutating insert.createHeader/createFooter; closeHeader removes empty new
ones through insert.removeEmptyHeaders. An empty new header is never
saved, one an undo brings back goes on the next close, and a new header
goes in the first section (linked to later ones, as in Word).
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>
* Engine: the Word 2003 format check reads through io, so commands still never touch files themselves
is_word_2003_file (#1081) opened the file with std::fs, which the file-access guard test
(commands_do_not_touch_the_file_system_themselves) forbids outside io.rs; main failed it.
A new io::read_file_head reads the first bytes inside the read root.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UI: a chrome click counts the page as focused at once, so the next keystroke isn't dropped; the ribbon-focus test passes again
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>
* #1122: Read Aloud starts PowerShell without a console window
On Windows the speech program is PowerShell, a console program: each sentence it spoke flashed a console window over the document. It now starts with CREATE_NO_WINDOW.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1122: the console-window test checks the flags Read Aloud passes
windows_speech builds the PowerShell program, arguments and creation flags
that speak() turns into its Command; std can't read creation flags back from
a Command, so the test checks that value (CREATE_NO_WINDOW) and the arguments.
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>
* #816: copied equations reach other programs as MathML
A copy or cut whose selection holds an equation now also puts HTML on the
system clipboard, with each equation as Presentation MathML (wrapped in
<semantics> with the linear format as an application/x-unicodemath
annotation), so Word, LibreOffice, Google Docs and browsers paste a
formatted equation instead of `y=2x_s`.
- formats: new mathml module, a writer from the equation tree (runs,
fractions, scripts and pre-scripts, radicals, n-ary operators, delimiters,
functions, limits, accents, bars, border boxes, group characters, equation
arrays, matrices, phantoms) and a bounded reader (our annotation, else the
presentation elements, else a TeX annotation). HTML export writes
equations as MathML; HTML import (open and paste) reads <math> back.
- engine: edit.copy/edit.cut (and column copy) return `html` and keep
Session::clipboard_html when the fragment holds an equation.
- ui-egui: Services::set_clipboard_html; copy sites put HTML with the text
through it, falling back to egui's text copy.
- wordcraft: arboard set().html() with the text alternative; on Wayland
without data-control the text-only path is kept.
OMML on the clipboard (Word's "Office Open XML") needs custom clipboard
formats arboard can't set; left as a follow-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #816: honest clipboard docs; MathML reader fixes (token cap, function names, column vectors)
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>
* #884: a dragged shape anchors in the column it lands in, so that column's text wraps around it
arrange.bounds (the canvas drag) re-anchored a moved object to the paragraph
under its top edge but ignored x: on a multi-column page it took the last
paragraph starting above that y in any column, usually one in the last column.
Layout places a float when it lays out its anchor, so text in an earlier column
(already laid out) ran through a shape dropped there. anchor_paragraph now picks
the paragraph in the column under the object's centre (the ink placement uses
the first stroke point).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #884: a dropped object finds its column from the section's column geometry
Columns came from the distinct left edges of the page's text, so a
paragraph shifted by mirrored indents on an even page made a column of its
own and the drop anchored to the wrong paragraph. The columns now come from
the section's column boxes on the page. Notes in the layout parity doc that
lines a paragraph continues into the next column don't wrap yet.
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>
A press on a selected balloon's Delete, Reply or Resolve button counted
as a click on the page and closed the balloon before the button saw its
click. Presses inside a balloon no longer leave it.
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>
* #1062: honour Remove personal information / Remove date and time on save
w:removePersonalInformation and w:removeDateAndTime (ECMA-376 §17.15.1.69-70)
are read into Settings, written back in CT_Settings order, and honoured by the
DOCX writer: comment and revision authors saved as "Author" (initials "A"), no
dc:creator / cp:lastModifiedBy, no w:date on comments and tracked changes. The
open document keeps its real values. New file.privacyOptions command sets both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #1062: reading privacy options has no side effects; File › Options › Save check boxes
file.privacy reads the settings (a pure command: no undo step, no unsaved changes);
file.privacyOptions only changes them and refuses an empty call. File › Options › Save gets
a "Privacy for this document" section with the two check boxes, translated in every catalog.
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>
* Text boxes: VML text boxes keep their internal margins, fit to their text and alignment
A VML text box's inset ("l,t,r,b", ECMA-376 Part 4 §19.1.2.22) was ignored,
so its text sat Word's default 7.2 pt in and 3.6 pt down even where the file
says 0,0,0,0, which is how letterheads and form headers place address blocks
exactly. It's now read like DrawingML's lIns/tIns/rIns/bIns: each value left
out keeps the default, a bare number is in EMUs, o:insetmode="auto" leaves
the defaults. mso-fit-shape-to-text (VML's "Resize shape to fit text") is read
as auto-fit, like a:spAutoFit, and the shape's v-text-anchor as the text's
vertical alignment. In a group, a member's margins go with that member's
text.
The reader feeds layout, so the editor, PDF and page images all change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drawings: hidden shapes and pictures are kept but not shown
A drawing marked hidden (VML visibility:hidden, floating or inline;
DrawingML wp:docPr/@hidden, or a group member's cNvPr/@hidden) was drawn,
took room and wrapped text. Word for Windows shows it in no view; it's now
kept and saved hidden, but left out of layout: not placed, no wrap area, no
room in its line, nothing drawn, its text box's notes not placed. Exports to
other formats and copies leave it out, the accessibility checker no longer
asks for its alt text, and the selection pane says it's hidden.
Word for Mac draws hidden VML shapes; Windows follows the markup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Naxvog <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
file.exportPng (and `wordcraft-cli render`) rendered with the on-screen
display options, which dim headers and footers while the body is edited,
so exported pages showed grey headers; the PDF export and Word's exports
don't. Page images now render with dimming off.
Co-authored-by: przemek-arch <263159043+przemek-arch@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Italian proofing: it-IT spelling, suggestions and grammar (#951)
* Italian proofing: Review Language menu and editing language
* Italian proofing: interface test for editing language
* Italian proofing: find the Swiss it_CH dictionary too
* Italian proofing: TeX hyphenation patterns (MIT option)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Write WordprocessingML 2003 by rewriting the DOCX writer's package into the 2003 vocabulary; file.save/saveAs take format wordml2003, File > Export lists the format, CLI convert --format wordml2003, a plain save over a 2003 file keeps it.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>