On macOS, Cmd+Left/Right were bound to Word Left/Right (same as Option+arrows).
Bind them to Line Start/End there, as in Word for Mac and every Mac text field;
Option+arrows still move by word. Windows/Linux keys are unchanged.
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>
* fix(ui): draw Arabic and other non-Latin comment text in balloons
Comment balloons and the Comments pane draw comment text and author
names with the interface faces (Inter, Source Sans, CJK), which have no
Arabic, Hebrew, Thai and so on: every such letter was a box. The app now
borrows a document fallback face that covers the comments, as the window
caption does (#840), checked once per document revision.
egui also lays bidi runs and words out left to right, so a right-to-left
comment read backwards. Read-only balloons now wrap the text by word
widths, put each row in display order (Unicode bidi runs, words of a
right-to-left run reversed, brackets mirrored) and set right-to-left
paragraphs against the right edge. Text without right-to-left letters is
laid out exactly as before. The editable fields (the selected balloon and
the Comments pane) keep logical order.
Also adds my contributor entry (sync_github_name).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* One contributor entry each
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Mohamed Youssef <ms.rightsteps@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
part that inherits its lines sends it, and the picker only shows No Border checked for it.
- CellProps::valign is Option<VAlign>: a cell's own Top (Align Top, w:vAlign="top") beats a
table style part's vertical alignment, and round-trips through .docx.
- tableStyle edit rejects Part fields at the top level without "part".
- "font": null or "" makes a part's font inherited; clearing the dialog's font box sends it.
- Document ui.dialog's optional "part" in docs/control-protocol.md.
Co-authored-by: Dmitrii Khlapov <19991457+dkhlapov@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #714: table style editor formats every table style part
Modify/New Table Style: "Apply formatting to" any of the 13 ECMA-376
§17.7.6 parts (whole table, header/total row, first/last column,
odd/even row and column bands, four corner cells), each with font,
size, bold, italic, underline, colour, fill, borders (line style,
width, colour; all/none/outside/inside/each edge) and the 9-way cell
alignment, over a live preview.
- doc: TablePart, PartFormat, PartSet; TableStyleParts::part/set_part
(new parts and per-part jc/vAlign in `regions`)
- layout: generic part resolution incl. band2, corner cells, alignment
- docx: every tblStylePr type read/written with pPr jc and tcPr vAlign
- engine: table.newStyle/modifyStyle take ECMA part names, `part`,
`parts`, per-edge `borders`, `underline`, `align`/`vAlign`/`cellAlign`
- ui: dialog moved to dialogs_table_style.rs; ui.dialog takes `part`
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 #887: Bulgarian strings for the table style parts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #714: Vietnamese strings for the table style parts (main's new vi catalog)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* hr: match the catalog's word for banded
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>
* Tables: preserve row table border exceptions
* Tests: shorten the row border regression name
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Same pattern as Portuguese (#797) and Polish (#346): the cs_CZ
dictionary is read from the system at runtime (e.g. Debian/Ubuntu
hunspell-cs), nothing is bundled. Suggestions use the dictionary's own
MAP/REP/TRY/KEY; grammar stays generic (repeated word, spacing), with
digit ratios like "3 : 1" exempt. The Czech interface now writes new
documents (and opened .txt/.md) in cs-CZ.
Engine fixes the Czech dictionary needs, also used by pl/pt:
- PFX/SFX header lines may carry a trailing comment (cs_CZ has
"SFX y Y 80 #…"); rule lines are left literal;
- NOSUGGEST stems are accepted when typed but never offered;
- KEY is parsed for neighbour-key suggestions.
Tests use a hand-written synthetic Czech dictionary only; an
end-to-end engine test mirrors the Portuguese one.
Co-authored-by: Losminek <301412321+Losminek@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Ruler tab stops: corner selector, click to set, drag to move, drag off to clear
The ruler's markers were read-only (double-click opened the Tabs dialog,
click sets (Left, Center, Right, Decimal, Bar; right-click cycles back),
clicking the ruler sets one on the half-division grid (Alt places
freely), dragging moves it with a dashed guide down the page and a
position tooltip, and dragging it off the ruler clears it. Markers draw
per kind, mirrored for right-to-left paragraphs. Each gesture runs
para.tabs once on release, so it is one undo step and inherited-stop
clearing keeps working. Double-click still opens the dialog.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Progress docs: ruler tab stops (ui-parity Rulers row and revision history, ROADMAP progress log)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Tabs dialog: OK applies what the fields show; double-click a stop to edit its position
Selecting a stop, changing its alignment or leader and pressing OK now
applies the change without Set, and a new position typed while a stop
is selected (the dialog preselects the first) is set on OK, so adding a
stop never requires leaving the dialog for the ruler. Double-clicking a
stop in the list edits its position in place: Enter or clicking away
commits (the interface unit by default, any unit spelled out, either
decimal separator), Escape cancels, and the commit moves the stop in
one clear + set, keeping its alignment and leader. Clear still wins
over a re-typed cleared position, and junk input changes nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Tabs dialog: New button, delete from the list, and the list fills its frame
New stages a fresh stop - deselects, proposes the next free multiple of
the default interval after the last stop (skipping cleared positions),
resets alignment and leader, and focuses the position field - and Set
or OK adds it. Every row gets a small x that clears its stop, and the
Delete key clears the selected one when nothing is being typed; the
selection and an in-progress inline edit follow when a row above them
goes. The list is a fixed 150 pt wide and the rows and scrollbar fill
it, so labels stay left-aligned, the x sits at the frame's edge clear
of the scrollbar, and the scrollbar docks at the edge instead of
floating mid-list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Tabs dialog: the stop list is an inline-edit table
Each row edits its stop in place: a unit-aware position field (rows
resort into position order once the edit ends, never mid-typing; two
stops landing on one position merge, the edited row winning), an
alignment dropdown and a leader dropdown (the closed box shows just the
fill), and the x that clears the row. The double-click editor and
move_stop go: the table subsumes them. The selected row keeps an accent
outline, its edits sync the fields below, and the frame widens to fit
the three columns.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Tabs dialog: the table is the whole editor
New adds a row straight into the table at the next free multiple of
the default interval, selected and ready to edit in place, and the
position field, the Alignment and Leader radio groups and the Set and
Clear buttons go: every one of their jobs is a table cell or the row's
x. What stays is New, Clear All, the default interval, the cleared
list and OK/Cancel, with OK applying the staged diff as one para.tabs
call. The staged-field methods (set, clear, on_ok) go with them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Review fixes: safe ruler gestures, one tab tolerance, dialog cleanups
Ruler gestures are primary-button only, a click on an indent marker
places nothing (the markers absorb their clicks), Escape cancels a drag
like a table-border drag (#217), and a drag is stamped with its
paragraph: the caret leaving it mid-drag, or a release the ruler never
saw, abandons the gesture instead of applying it elsewhere or late. The
dragged marker no longer double-draws on its first frame. One
TAB_POS_EPS in the doc layer replaces four hardcoded 0.5s across the
engine, the dialog and the ruler. The dialog's normalize is a no-op
unless a position edit happened, its combos share the ruler's KINDS and
one LEADERS table, dead code and a stale doc comment go, and the
ui-parity revision row moves to the table's top. Regression tests cover
each gesture rule.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Selecting one cell (Table Layout > Select > Select Cell, or a click along
its left edge) and dragging its left or right border moved the border in
the whole column: one selected cell wasn't told apart from text selected
inside it. `ResizeTarget::limit_to_selection` now takes the selected cells
from `Session::cell_range` (#637) when they're in the dragged table, so one
selected cell's border moves in its row only, as in Word, like the border
of cells selected across (#720). Text selected within a cell still moves
the whole column.
Tests: engine `a_selected_cells_border_moves_only_in_its_row`; kittest
`a_selected_cells_border_drags_in_its_row_only` (a click along the cell's
edge, then a real drag). Progress docs: ui-parity, ROADMAP.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A field begin marker flagged w:dirty by a producer that couldn't compute
its result (pandoc-style TOC placeholders) lost the flag on save, so Word
no longer refreshed the field on open. Mirror w:fldLock end to end:
FieldState, InlineObject::Field/FieldStart, the reader (fldChar and
fldSimple) and the writer (buffered, range and TOC-spanning begins).
Update Field/Update Table clear it on the fields they compute, like
Word's F9; untouched fields keep it. Round-trip and engine regression
tests, incl. a pin for #878 (imported TOC placeholder replaced in place,
fixed earlier by #617).
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* i18n: add Croatian (hr) interface translation
Croatian interface catalog (2,505 entries: every key of the reference catalog, built-in style
names, equation gallery, print dialog), registered as `hr` ("Hrvatski"). `hr`, `hr-HR` and
`hr_HR.UTF-8` system locales pick it automatically.
- Count-neutral wording (`Riječi: 21`), since Croatian numerals take three noun forms.
- New `template` catalog context: the "Letter" template can be called "Pismo" while the "Letter"
paper size keeps its name. Other languages fall back to their plain entry, so nothing changes.
- Locale, saved-preference, catalog-coverage and bundled-font tests; `hr` added to the
interface-audit languages; Polish's coverage test lists `hr` with it/hu/bg.
- Written from the meaning of the English labels (clean room, no Word localisation consulted),
proofread by a second model and reviewed by a native speaker.
- ATTRIBUTION row, contributors entry for the author, docs and roadmap.
Croatian spelling and grammar proofing is separate (#1114).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* ATTRIBUTION: name the models and the human review for the Croatian catalog
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* i18n(hr): native-speaker corrections to math terms and messages
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* i18n(hr): envelope names without their size
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs: Croatian entry count
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* i18n: add Swedish (sv) interface translation
A 2,504-entry Swedish catalog: every key of the reference catalog, the equation gallery's
labels, everything the interface audit finds, and 44 newer strings no catalog had yet (the
native print settings among them). Written from the meaning of the English labels in ordinary
Swedish computing vocabulary; no other product's localisation and no other catalog was used.
An AI-assisted draft whose terminology and running app were reviewed by a native speaker.
Counts use count-neutral wording ("21 ord").
- sv, sv-SE, sv_SE.UTF-8 and sv-FI select it automatically; "Svenska" in Options and
ui.language with {"value":"sv"}.
- The interface audit checks Swedish the way it checks Russian, Hungarian, Turkish and
Bulgarian.
- Tests: locales and saved preference, catalog coverage and key translations, the search
hint's example, count wording, and the bundled Inter/JetBrains Mono faces covering å ä ö é.
- native_print: the narrow-window test pins English, since it finds controls by their English
labels (it failed on a Swedish machine once those labels had a translation).
- The Hungarian locale test used sv-SE as an unsupported language; it now uses fi-FI.
- ATTRIBUTION, README, ROADMAP, docs/localization-parity.md and a new
docs/localization-sv.md; a contributors/people.toml entry for @holmbom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Swedish rows for the cross-reference dialog
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>
* M8: keep long Web and Draft pages sharp with raster tiles
* M8: document Web Layout blur fix with before-and-after screenshots
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: read VML freeforms (v:shape with a path) as freeform shapes
Legacy VML shapes with their own `path` (ECMA-376 Part 4), such as rule
lines in headers, were dropped on open and lost on save. They are now
read as freeforms: m/t/l/r/c/v/x/e commands in the coordsize/coordorigin
space, nf/ns, v:fill and v:stroke opacity; arcs drawn straight, formula
references read as 0. Saved as DrawingML custom geometry.
Tests: header rule read and round-tripped; hostile paths and coordinate
spaces stay finite and never panic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: VML freeforms whose path uses formulas aren't drawn
Formulas (@n, from v:formulas) aren't evaluated; reading them as 0 drew stray lines. Parity row states the fill-opacity limit (none at 0, otherwise opaque, as for DrawingML fills).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* DOCX: VML freeforms reuse the watermark's vml_fraction
The watermark reader (#1328, now on main) brought its own vml_fraction;
the freeform reader had an identical copy, which no longer compiles after
the merge. Keep one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
While landing community PRs, a few rows in gaps.md, layout-parity.md and
file-format-parity.md ended up twice (an older and a newer version).
Each pair is now one row: the newer text, plus what only the older one
said (section note options #385; allowSpaceOfSameStyleInTable).
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Clicks on ribbon chrome that isn't a command button (Styles gallery tiles, tab
names, empty ribbon space, View check boxes, status-bar items) left the page
without keyboard focus. The click was caught at the end of the frame, after the
page had been drawn: want_focus was only picked up next frame and the page's
focused flag, which gates the keys, not recomputed until then either, so the
keystroke right after the click went nowhere.
Claim the keyboard in the same frame a ribbon command button does, using the
page's widget id from the frame just drawn.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word for Windows (checked by hand): Shift+Backspace erases like
Backspace, Shift+Delete cuts the selection, and Shift+Delete with nothing
selected does nothing. On Windows egui-winit already turns Shift+Delete
into a Cut event, so WordCraft matched. On macOS and Linux it arrives as
the Delete key with Shift, no shortcut was bound to it, and it did
nothing, even with text selected.
The canvas now runs Cut for Shift+Delete when there is a selection,
through the same clipboard path as Mod+X, and does nothing otherwise.
Shift+Backspace already erased (#707).
Tests: Shift+Backspace erases, Shift+Delete without a selection changes
nothing, Shift+Delete on a selection cuts it to the clipboard (fails
without the change); the key check accepts Shift alone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
The active ribbon tab was saved to ui.json and restored verbatim at startup,
so relaunching resumed whichever tab was last used (Review, Help, Draw).
Word always opens on Home. apply_prefs now forces DEFAULT_TAB on load.
Going File and back to the editor still returns to the tab that was showing,
contextual tabs included; tab is still written to ui.json, so the UiState JSON
contract (MCP/control) is unchanged. resolve_tab uses the same DEFAULT_TAB so
the startup default and the contextual-tab fallback can't drift.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* RTF import: Mac OS Roman text (`\mac`, `\fcharset77`) (#1653)
A Macintosh RTF document (`{\rtf1\mac …}`) had its `\'xx` bytes read as
Windows-1252: LibreOffice's fdo85889-mac.rtf showed "ñòó" instead of
"ÒÚÛ". Mac OS Roman (code page 10000, with the euro sign at 0xDB) joins
the single-byte code pages, and the reader uses it for `\mac` documents,
fonts with the Mac charset (`\fcharset77`) and `\cpg` / `\ansicpg` 10000.
Tests: the code page table (accents, euro, quotes) and two RTF cases, the
issue's file and a Mac-charset font in a Windows document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* RTF import: DOS code pages (`\pc`, `\pca`, OEM `\cpg`) (#1653)
With the OEM tables from #1146 now on main, the RTF reader decodes DOS
text too: `\pc` documents as code page 437, `\pca` as 850, a font's
`\cpg` 437/737/850/852/857/866 and `\fcharset254` (PC 437).
`encoding::code_page_byte` decodes a byte of document text in any code
page RTF names (Windows, OEM or Mac OS Roman; ASCII below 0x80 stays
ASCII), and `is_byte_code_page` says which those are.
`SINGLE_BYTE_CODE_PAGES` stays Windows-only, so `text.typeCode`'s `ansi`
pages are unchanged.
A `\cpg` / `\ansicpg` number out of the u16 range is now ignored; before,
`pv as u16` truncated it to another code page (65973 read as 437).
Tests: document text in DOS, Mac and Windows pages; `\pc`, `\pca` with a
`\cpg437` font, and an out-of-range `\ansicpg`.
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>
* M12: preserve document properties when importing legacy DOC and DOT
M12: preserve document properties when importing legacy DOC and DOT
Integrate current upstream while retaining import behavior and embedded-font loading.
* docbin: as_chunks for fixed-size records (clippy::chunks_exact_to_as_chunks on Rust 1.99); rustfmt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: LabDennis <341377186+LabDennis@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Envelopes: the size list was drawn as English text (`#10 4.13 × 9.5 in`)
with a decimal point in every region. The label is now a catalog template
(`{name} {height} × {width} in` / `mm`) filled with the region's decimal
separator; ru, hu, tr, bg, pl and it carry it. Regression test included.
- Italian: the 18 strings the interface audit looks up that it.tsv lacked
(Reading Mode bar, Go To, command search, status and command messages);
`Privacy Settings` no longer says "current".
- The audit's catalog-completeness check covers Italian too.
- docs/localization-parity.md: status line, Italian note, revision history.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
w:asciiTheme="minorBidi" (and majorBidi, and w:cstheme) read as the
theme's Latin font, so a template's titles came out in Calibri where
Word draws Arial. Measured in Word: the slot names the theme's a:cs
font, and an empty a:cs names nothing, so the font inherited from the
style applies.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
WordCraft chose a substitute for a font that isn't installed from its
name alone, so an unknown brand font always came out sans. Word reads the
font table instead: measured with the same missing font, it draws it in
Cambria when w:family is roman and in Calibri when it is swiss or auto.
A report whose font table calls its brand font roman got a sans footer
where Word draws serif.
The font table's roman fonts are read into the settings, and layout looks
a missing one up as Cambria (Caladea where Cambria isn't installed). The
other classes keep the existing substitutes, which already start with
Calibri's metric clone.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
space after and the second's space before, as Word does by default. Word
adds them instead when the document's compatibility options include
w:doNotUseHTMLParagraphAutoSpacing. Measured in Word with five paragraphs
of 12 pt before and after: 24 pt between them with the option, 12 pt
without, the same in compatibility modes 14 and 15. A 60-page report with
the option lost 6 pt per paragraph, about 60 pt on its cover.
The option is read, laid out in the body and in boxes, and saved back.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A header or footer a section inherits (Same as Previous) keeps the
percentage widths of its tables relative to the section that defines it.
Measured in Word: a 100%-wide footer table defined by a portrait section
stays 451 pt wide, centred, on landscape pages that inherit it, and spans
the landscape width only when that section names the footer itself.
Paragraph alignment in the same footer follows the landscape page.
Found on a report whose landscape calculation pages drew their footer
table full width where Word draws it at the portrait width.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Layout: an empty section mark stays on a full page
An empty paragraph that carries a section break, when the next section
starts on a new page, needs no room of its own in Word. Measured: 50 lines
filling a page, then that paragraph, then the next section makes two pages
in Word; WordCraft made three, the middle one blank. A plain empty
paragraph in the same place still moves on, in both.
Found after a landscape drawing page in a 60-page report.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Layout: drop an unneeded mut in the section mark test
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>
* Tables: a row with less room than its minimum height moves on whole
Measured in Word with about 49 pt left on a page: a table row of eight
lines splits there, but the same row with an at-least height of 63.6 pt
goes to the next page whole, and so does one holding a nested table.
Without the height both split, as before. WordCraft split them all, so a
report's title row (a logo beside a small nested table) started at the
foot of its cover page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: preserve splitting for oversized minimum-height rows
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Tables: text running past an exact-height row is cut off
A row with an exact height (w:trHeight w:hRule="exact") keeps that
height whatever its cells hold. Word cuts the text that doesn't fit at
the cell's edge; WordCraft drew it over the rows below, so a certificate's
identification block came out as overlapping lines.
The cell is marked as clipped in layout (Placed::Cell clip), and display
draws its lines inside that cell's clip, on screen and in PDF. Hit
testing is unchanged: the lines stay where they are in the layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tables: keep row clipping compatible with Rust 1.90
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A heading whose text opens with a page break had its list number drawn on
the line holding the break, so the number stood alone at the bottom of one
page and the heading text started the next. Word draws the number with the
text. The label now goes on the first line after a leading break.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word writes the next heading's TOC bookmark start into the paragraph that
ends with a page break. The line after the break then held that marker,
so it wasn't the empty mark kept with the break (#932): it went onto the
next page with the paragraph's spacing and pushed the heading 17 pt down.
Lines holding only zero-width markers (InlineObject::is_marker) now count
as empty there.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
A paragraph holding only a page break keeps its mark on the page before
(#932), but its space after still went onto the new page, so a heading
with page break before no longer stood at the top and broke again: a
blank page. The space after now stays with the mark. A next-page section
break carried by such a paragraph also started a second page; the empty
page the break started is now used instead.
Found on a 60-page inspection report: two blank pages Word doesn't have.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
DrawingML's rtTriangle was read as an isosceles triangle, so a cover
page's white corner cut-outs pointed up into a peak instead of cutting a
V into the picture above them. It is now its own shape kind, drawn with
the right angle at the bottom left, wrapped and glued along that outline,
and saved back as rtTriangle. The .doc reader maps msosptRightTriangle (6)
to it too.
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>
* fix: DOCのDIB画像変換でバイト上限を守る
* Drop the duplicate read_file_head left by the merge with main
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: haranishi <223786623+haranishi@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Italian proofing: po', qual è, È and un/un' grammar rules; more elisions in spelling
- grammar (works without a dictionary): un pò / un po -> un po'; qual'è -> qual è;
E' -> È; un'altro -> un altro and un altra -> un'altra (also nessun, ciascun,
qualcun, alcun), gender from the ending with masculine -ista/-ma/-cida... left alone;
more emphatic doubles allowed (pian piano, poco poco, così così...)
- spelling: more elisions (dov'è, cos'è, anch'io, senz'altro, nessun'altra, n'ha...),
truncations po'/mo'/be' accepted, pò and qual'è left to grammar (no double marks)
- tests: rule tests, grammar proptests, Italian suggestion proptest, It in mixed-span proptest
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Contributors: credit lupant by their GitHub profile name
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>
Save got focus only when nothing else had it, but the page keeps focus while typing and claims Tab and the arrow keys, so the prompt opened without a focused button and the keys moved the caret behind it. Save now takes the focus whenever none of the prompt's buttons has it.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
w:pgNumType w:start="0" was read but laid out as 1: the page counter
was set to one less than the restart and could not go below zero. A
report numbering its cover 0 read "Page 2" on the page Word calls 1.
The restart is now the next page's number itself.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Layout: a table's percentage width past 100% is kept
Word keeps a preferred width over 100% of the text column (w:tblW
w:type="pct" above 5000). A cover-page footer table at 131% with the
left margin as a negative indent spans the page edge to edge in Word;
WordCraft capped it at 100%, so it ended three-quarters of the way across.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Contributors: add Fin Warman
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>