crates/ui-egui/src/backstage.rs: t03's bidi_display on the Recent names over t09's borrowed name/path. people.toml: union. Tests: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
docs/gaps.md row 4: one row (t06 had kept a second, older copy of it); t05's #1840 and t06's #2964 clauses both in, main's #2963 clause kept. docs/recovery.md: t06's retry sentence with t01's base64 size text. Test files: both tracks' tests kept whole.
crates/pdf/src/lib.rs: t01's vector_text kept before t05's watermark (box area signature). Test files and canvas.rs tests: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
crates/docx/tests/fixtures.rs: union, keeps main's shape-gradient test and the PR's band-size tests.
ROADMAP.md, docs/file-format-parity.md: main's status lines with this change added to the newest; log rows from both sides in date order.
crates/docx/src/read/story.rs: main's vml_opacity with the PR's box size.
crates/pdf/src/tests.rs: both sides' tests kept; the glyph_frames helper both #3155 and #3156 add stays once.
ROADMAP.md: status line once (this change first, then main's); progress-log rows union in date order
docs/file-format-parity.md: status line once; VML row merged word by word (main's #1500 fill text plus the PR's signature-line text)
crates/doc/src/numbering.rs: kept #3417's per-(num, level) seen map and its single-level override; the override now sets that level's LevelFlags { started: false, first: Some(start) } from #2343 instead of start - 1 (the reviewed integration fix for #2343).
ROADMAP.md: main's status line kept; both progress-log lines added.
docs/target-app-parity.md: main's status line and Tables row kept, #1596 sentence added to the row; log row added.
* #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>
Preserve w:lang/@w:eastAsia when reading and writing DOCX documents.
Previously, only w:val and w:bidi were handled, causing w:eastAsia to be
dropped on save.
Fixes#3457
Word reads a table style without w:tblStyleRowBandSize / w:tblStyleColBandSize
as bands of zero and leaves its banded rows and columns unshaded (MS-OE376
§2.7.5.7, MS-OI29500 §17.7.6.5); ECMA-376 says one, which is what we did. Our
writer only wrote a size a style set, and our built-in styles set none, so a
table in Grid Table 4 saved by WordCraft had shaded rows here and none in Word.
- An unset or zero band size now means no bands, as in Word. PartSet::of_cell
takes row and column band sizes; column bands get a size of their own
(col_band_size).
- Our banded built-in styles and new table styles set both sizes to one, and
the writer writes them (and zero), so Word bands them too.
- The reader reads both sizes, zero included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A num's startOverride for a level applies when that level of that num is first used, and only
there: applying all of them on the num's first use reset the shared counters' other levels and
renumbered every following chapter.
Found while testing crates/doc/src/props.rs:49-56 (Rgb::parse)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/doc/src/encoding.rs:82 (CP1255 table, entry for byte 0xCA)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With Different First Page on, a section's default header was listed and reached by Next
before its first-page header, so Next went to page 2 before page 1. A section's header and
footer versions now go in the order of the first page each one shows on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A first-page or even-page header or footer is shown only while Different First Page or
Different Odd & Even is on, and a text box linked from another shows the first box's text
instead of its own. Comments in those stories were still listed and reached by Next and
Previous, and typing then went into text nobody sees. shown_stories now picks each
section's versions the way layout does and leaves out linked boxes' own text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`\ansicpg932`/`936`/`949`/`950`, a font's `\fcharset128`/`129`/`134`/`136`,
`\cpg932` and the OEM charset 255 were read one byte at a time, so Japanese,
Korean and Chinese text came out as mojibake. The reader now holds the 8-bit
text of a multibyte code page and decodes the run with encoding_rs (SHIFT_JIS,
GBK, EUC_KR/UHC, BIG5): a lead byte and its trail byte decode together, escaped
(`\'hh`) or raw. A code page switch (`\f`, `\fcharset`, `\cpg`, `\ansicpg`), a
group end and the end of the document flush what is pending; invalid sequences
read as U+FFFD, as Word writes them, and never lose the rest of the text. The
pending buffer is capped at 4096 bytes.
Single-byte code pages, `\uN?` escapes and their `\ucN` skips, the font table
and every existing test are unchanged. Tests cover Shift-JIS (escaped, raw and
through a `\fcharset128` font), CP949/CP936/CP950, a mixed Japanese/ASCII/
Cyrillic document whose code-page switch must flush, `\uN?` alongside
multibyte text, an invalid sequence and a Windows-1252 regression.
Word's text watermark is a VML WordArt shape (`PowerPlusWaterMarkObject`)
whose `v:textpath fitshape` stretches the text over the shape's box (its
`style` width and height), centred on the margins and, when diagonal,
turned by `rotation:315`. WordCraft ignored the box: it drew the text
70 % of the page diagonal wide (half of it on screen), centred on the
page and along the page diagonal (55° on A4, not 45°). A "BROUILLON"
watermark in a 515 x 104 pt box came out about 1.4 times as large as in
Word.
- doc: `Watermark::size`, the shape's box (`None`: Word's default, half
the page diagonal across, as high as the text's length makes it);
`Watermark::frame` gives the box for a page, shared by the writer and
the renderers.
- docx: the box is read from the shape's style and written back; a box
equal to the default for the document's page reads as `None`, so a
watermark made by Design > Watermark keeps following the page.
- pdf, render: the text's advance spans the box's width, its em the
box's height, the em's foot (the font's win descent below the
baseline) on the box's bottom; the box centred on the margins,
turned 45° counterclockwise when diagonal.
Checked against Word for Microsoft 365 (ink bounds, pt, left top right
bottom; fonts here: Liberation Sans for Arial and Calibri, Liberation
Serif for Times New Roman):
BROUILLON, Calibri, 515.4 x 104.1, 315°:
Word 93.6 222.7 489.1 619.2; before 60.5 110.4 535.7 732.5;
after 90.7 214.1 495.8 618.7
SPECIMEN, Arial, 400 x 100, 315°:
Word 136.3 261.6 455.0 580.3; before 66.7 108.5 539.0 727.7;
after 137.8 255.4 454.6 573.1
HXH, Arial, 300 x 100, flat: Word 158.9 378.2 436.3 449.8;
before 105.1 353.8 490.1 489.6; after 158.9 380.2 435.8 449.3
HXH, Times New Roman, 300 x 100, flat: Word 149.8 383.0 445.0 449.3;
after 151.2 383.5 443.5 449.3
COPIE, Calibri, 300 x 80, flat: Word 153.1 387.8 441.1 440.2;
before 96.0 373.9 500.2 469.0; after 152.2 387.4 443.5 444.5
Tests: a watermark with a box of its own is written and read back with
it, Word's default box reads back as none; in the PDF, the text of a
300 x 80 pt watermark is 80 pt high, stretched across, starting at the
box's left edge on the margins' centre line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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>
* Fix Paragraph::sentence_at at the first character of a sentence
Found while testing crates/doc/src/para.rs:1397-1427
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* sentence_at: a terminator followed directly by text doesn't end the sentence at the caret
"Hello.World" at offset 6 gave an empty (6,6) range; only treat the caret as just past a
sentence end when the character at the caret is a space.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Word stacks floating objects by wp:anchor/@relativeHeight, not by the
order of their anchors in the paragraph. The reader ignored the
attribute, so a panel anchored after the text boxes it frames, but
lower in the stack, was drawn over them and hid their text.
- wordcraft-doc: Float gets a `z` (0 = none, drawn in document order).
- wordcraft-docx: it is read from relativeHeight and written back as
read; an object without one gets the next order above, as before.
- wordcraft-layout: place_objects collects each object's items with
its order and emits them lowest first (stable, so objects without an
order keep the paragraph's).
Four round-trip tests compare whole floats after a save; saving now
gives objects without an order one, so those tests compare without it
(`unstacked`). The order itself is covered by its own round-trip test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document::paths_between listed every paragraph of the story (every cell
paragraph of every table) and then kept those between the two paths, so each
selection_rects call cost the whole document. The canvas calls it every frame
for each Find reading highlight (up to 5,000), each content control frame,
Read Aloud and the hyperlink under the pointer. It now walks only the
blocks, rows and cells between the two paths: for a one-paragraph range in a
document of 26,000 paragraphs (four 1,500-row tables), 1.0 ms -> 2.6 us.
Co-authored-by: dfgodvan <180286157+dfgodvan@users.noreply.github.com>
Review of the first commit found, and ui_shot runs confirmed, three bugs in it:
- Next Comment moved the caret into a removed header or a deleted
footnote's text: a story nothing shows, so typing went nowhere visible.
- Next visited headers and footers by part id (creation order), so a
footer made first came before the header.
- Comments in footnotes were claimed but untested.
Document::shown_stories lists the stories a reader sees, in reading order:
the body with the notes and text boxes it shows, then the headers and
footers the sections use with theirs. It reuses the reachable walk that
counted_stories uses. comment_list scans those stories (and returns at
once when the document has no comments: the balloons ask every frame), and
nav_comment orders by a story's place in that list instead of by Pos.
tools::all_stories is private again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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>
* fix(doc): strip negative sign when formatting numbers rounded to zero
* Drop the duplicate divyanshu2074 entry in people.toml (already on main)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Legacy form fields are drawn: check boxes, and drop-downs' selected entry
Legacy form fields keep their w:ffData since the last round, but were
not drawn: a FORMCHECKBOX has an empty result, so a form of check
boxes came out with none, and a FORMDROPDOWN showed its saved result
rather than its selected entry.
- wordcraft-doc: `legacy_form` reads what layout needs from the kept
w:ffData (kind, name, check box state and size, drop-down entries and
selection, text field length); `InlineObject::legacy_form()`.
- wordcraft-layout: a check box takes the text line's height of room
and is drawn as Word draws it; a drop-down shows its selected entry
(w:result, else w:default, else the first).
Measured on Word's PDF export with a synthetic document (Calibri):
text size | Word square | WordCraft
8 pt | 7.44 | 7.45
10 pt | 9.96 | 9.89
12 pt | 12.36 | 12.33
20 pt | 22.08 | 22.09
w:size 15 | 16.08 | 15.99
The square sits 1.16 pt inside the line height on every side (room on
the line within 0.2 pt of Word's), has a 0.72 pt border and, checked,
an X from corner to corner in 0.48 pt lines; w:checked beats
w:default. docs/file-format-parity.md is updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Draw::Shape takes the dash list main added
---------
Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>