Commit Graph
196 Commits
Author SHA1 Message Date
skylar taylor-barrick af2803a546 #3279: Keep protected session author fixed in CLI and Options 2026-10-22 20:52:44 -06:00
Mat Strangeandopencode ff36e73ecb Docs: the session author, not the user name
"the user name can't change while the document is protected" was the
wrong term twice over: it is the session author — the name new tracked
changes and comments are recorded under, and the one a named exception
is matched against — and it is `file.setAuthor` that refuses to change
it. The CLI's `--author` names the session directly, so it works on a
protected document.

The parity row, the two status lines and the two progress rows now say
that.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 23:51:19 +01:00
Mat Strangeandopencode 5151809e11 Merge origin/main into fix/protection-file-commands
Resolves the conflicts with the changes that landed since the branch was cut:

- crates/engine/src/cmd/protect.rs: the forms-mode refusals stay behind
  upstream's `greyed_out()` (so the ribbon still greys the Developer tab's
  control buttons out), and the refusal of the document's properties and its
  privacy options is added on top of it.
- ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: both sides' entries
  are kept; the status lines carry the merge's date and this change at the
  front.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-22 23:08:09 +01:00
aiongg 3f46d9c65f Integration: status lines list each change once
Combining the tracks' status lines repeated a few entries (#2852, #1500, #2617).
2026-10-22 01:54:49 +00:00
aiongg 5dc68481ed Integration: track t09
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.
2026-10-22 01:48:25 +00:00
aiongg e1e258e4d9 Integration: track t08
contributors/people.toml: union. crates/layout/src/tests.rs: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
2026-10-22 01:47:56 +00:00
aiongg 3581f80251 Integration: track t07
# Conflicts:
#	ROADMAP.md
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/layout-parity.md
2026-10-22 01:47:46 +00:00
aiongg 04b3881e3e Integration: track t06
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.
2026-10-22 01:47:34 +00:00
aiongg 0d3b8eb0cb Integration: track t04
crates/formats/src/odt.rs: t01's Body::new/take_blocks kept; take_blocks finishes tables with t04's TableB::finish (column widths). Test files: both tracks' tests kept whole. Docs: status lines combined, rows unioned or word-merged.
2026-10-22 01:46:17 +00:00
aiongg 245b5bd8d0 Integration: track t02
Docs: status lines combined (t02's entries first); log rows unioned; file-format-parity .rtf row from t02 (#1653, duplicate .rtf row dropped as t02 did), .odt row word-merged.
2026-10-22 01:44:01 +00:00
aiongg ef00b55870 Merge PR #3429: #3415: banded rows shade the same rows in the reference app and WordCraft (table style band sizes)
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.
2026-10-22 01:25:02 +00:00
aiongg 92b36ceae0 Merge PR #2557: #1681: HTML export and import keep furigana (ruby)
docs/file-format-parity.md: main's status line with this change added; both progress-log rows, this one first.
docs/localization-parity.md: main's status line with this change added.
2026-10-22 01:23:40 +00:00
aiongg 9cd24539bd Merge PR #2549: #1653: RTF import decodes the East Asian multibyte code pages (932/936/949/950)
docs/file-format-parity.md: main's status line with this change added.
2026-10-22 01:23:23 +00:00
aiongg a05c650a16 Merge PR #3391: ODT: table column widths are read, so a resized column survives a save
crates/formats/src/odt.rs: StyleInfo keeps both cell_valign (#2416) and column_width.
ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: main's status lines kept; the PR's log rows added.
2026-10-22 01:23:19 +00:00
aiongg 2dc0e4adf7 Merge PR #3440: A document with many content controls saves in the time they take
ROADMAP.md: status line once (this change first, then main's); progress-log entries and history rows union
docs/file-format-parity.md: status line once; history rows union in date order
docs/gaps.md: status line once (this change first, then main's)
2026-10-22 01:23:17 +00:00
aiongg 5b19acb4ec Merge PR #3193: ODT: read an embedded equation's own MathML instead of its undecodable metafile preview
ROADMAP.md: progress log carries both sides.
docs/file-format-parity.md: ODT row keeps #873's headers and footers and adds #3193's formula objects.
2026-10-22 01:23:17 +00:00
aiongg 57205e310a Merge PR #2392: PDF export: lam-alef drawn as two glyphs at the same x extracts in reading order (#2310)
ROADMAP.md: main's status lines, with this change added to the newest.
docs/file-format-parity.md: main's status line with this change added; PDF export row keeps main's outline note and adds the lam-alef note.
2026-10-22 01:23:07 +00:00
aiongg d64ce28903 Merge PR #3204: DOCX: majorBidi/minorBidi theme fonts follow the theme font language
docs/file-format-parity.md: status line once (this change first, then main's); history rows union
2026-10-22 01:23:01 +00:00
aiongg 1c757ece6b Merge PR #3199: Signature lines stay signable after saving a .docx
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)
2026-10-22 01:22:51 +00:00
aiongg b0fb2b9386 Merge PR #2101: File: recovery copies and .json files store pictures as base64 (#586 follow-up)
ROADMAP.md: status line, progress log and revision history carry both sides.
docs/file-format-parity.md: status line and change log carry both sides.
2026-10-22 01:22:47 +00:00
aiongg 3f52369269 Merge PR #2453: DOCX: preserve text boxes containing pictures
ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: main's status lines kept; the PR's log rows added.
2026-10-22 01:22:24 +00:00
aiongg 544647318d Merge PR #873: ODT: read headers and footers from the master page (#863)
crates/formats/src/odt.rs: Body::new from #873 with main's xml_space_end; parse_tracked returns main's document font as 4th element and #873's page parts as 5th (the reviewed integration fix for #873).
docs/file-format-parity.md: status line, ODT row and change log carry both sides.
2026-10-22 01:22:10 +00:00
aiongg 1cb405ea48 Merge PR #3269: #3003: Save to a NAS share mounted by GVFS keeps working when it refuses a mode change
ROADMAP.md,docs/file-format-parity.md: status lines keep main's with the PR's item added; tables and lists union.
2026-10-22 01:21:41 +00:00
aiongg cb8d365923 Merge PR #2673: Fonts: Times New Roman keeps Liberation Serif under the roman-font rule (#1613)
docs/file-format-parity.md: one status line naming this change first, then main's
2026-10-22 01:21:32 +00:00
aiongg d1182d5f8d Merge PR #2477: DOCX: HYPERLINK fields read as links, so TOCs from the reference app's web version keep their tabs and page fields (#2174)
ROADMAP.md: main's status lines; both progress-log and revision rows kept.
docs/file-format-parity.md: main's status line and rows; the PR's HYPERLINK sentence added to both of main's Fields rows; revision rows unioned.
2026-10-22 01:21:08 +00:00
aiongg 467ad8e279 Merge PR #2460: DOCX: a text frame in the paragraph defaults frames nothing (#2360)
ROADMAP.md, docs/file-format-parity.md: status lines keep main's with the PR's item added; log rows union.
2026-10-22 01:20:37 +00:00
aiongg d21c8825c2 Merge PR #2429: DOCX: keep the reference app's Table of Contents content control around a TOC the writer doesn't rebuild 2026-10-22 01:19:47 +00:00
aiongg 211daa5862 Merge PR #1847: engine: reject malformed comment IDs before editing or caret fallback 2026-10-22 01:19:42 +00:00
6b01aa967f Shape fills: gradients and transparency (#1500) (#1605)
* #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>
2026-10-21 19:12:26 -04:00
Midasdf 1678d93f54 #1681: ruby review follow-ups — tracked ODT ruby, HTML start space, docs
- ODT export reads the tracked insertion/deletion from a Ruby inline's formatting
  too (Item::marks and the paragraph's tracked check): an inserted or deleted
  phonetic guide keeps its change range around the base text instead of being
  written as untracked text.
- HTML export computes the paragraph-start state before the ruby's formatting tags,
  so a leading space in the base text becomes &nbsp; instead of a space HTML can
  collapse.
- docs/file-format-parity.md: restore main's format rows (the PR had replaced them
  with stale copies that lost ODT keep-with-next #2813, TXT #2853 and the HTML
  blockquote/MathML details) and add the ruby sentence to the retained .htm/.html
  row; restore the rolled-back header change list.
- docs/localization-parity.md: restore the header change list; date the new
  revision-history rows 2026-10-21.
- Tests: html_ruby_at_the_paragraph_start_keeps_a_leading_space and
  an_inserted_ruby_keeps_its_tracked_insertion.

Validation: cargo fmt --all -- --check; cargo clippy -p wordcraft-formats
--all-targets -- -D warnings; cargo test -p wordcraft-formats -> 204 passed,
0 failed (doctests 0).
2026-10-21 22:41:05 +09:00
Midasdf cc724045dd #1653: review follow-ups — BOM, streamed runs, consolidated docs
- Create the East Asian decoder without BOM handling: the code page is named by
  \ansicpg, \cpg or \fcharset, so a GBK/Big5/UHC run that starts EF BB BF is text,
  not a UTF-8 BOM that switches the rest of the document to UTF-8 (#2549 review).
- Decode raw 8-bit runs through the streaming decoder in one pass instead of one
  byte at a time: ASCII body text in a Japanese/Chinese/Korean document and picture
  hex skip the per-byte path, and a single \'hh byte decodes into a stack buffer
  (no String allocation per byte). Picture hex no longer goes through the code page
  decoder at all.
- Decode loop: decode_to_string writes only into spare capacity, so drain the stack
  buffer and keep decoding until the run is consumed (the old one-call version could
  truncate a run longer than the reserved capacity).
- docs/file-format-parity.md: keep main's format rows (the PR had replaced them with
  stale copies that lost ODT keep-with-next #2813, TXT #2853 and the HTML blockquote
  and MathML details), merge the East Asian sentence into the one RTF row — which also
  removes the duplicate RTF row main carries and restores "export writes every
  section" and the tracked paragraph-mark wording — restore the rolled-back header
  change list, and date the new revision row 2026-10-21.

Validation: cargo fmt --all -- --check; cargo clippy -p wordcraft-formats -p wordcraft-doc
--all-targets -- -D warnings; cargo test -p wordcraft-formats -p wordcraft-doc
→ 200 + 174 passed, 0 failed (doctests 0).
2026-10-21 22:38:07 +09:00
Mat Strangeandopencode 3944664035 Record the content-control save fix in the progress documents
The repository's convention is that landed work updates the gap document, the
parity document and the progress log, with each touched document's status line
and revision history bumped. This does that for the fix in the commit before:

- docs/file-format-parity.md: a section says what saving many content
  controls costs, with the measurement before and after (32,000 controls:
  3.6 s -> 0.1 s), and names the tests.
- docs/gaps.md: gap 20 (performance on large real-world documents) is
  narrowed by it.
- ROADMAP.md: the progress log and its revision history gain the entry.

Fixes #3437.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-21 13:12:40 +01:00
Nghiathan13andClaude Opus 5.5 67d21b0a68 #3415: banded rows shade the same rows in Word and WordCraft
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>
2026-10-21 18:21:49 +07:00
Marco Arnulfo 07a9880c14 DOCX: keep Word's Table of Contents content control around a TOC the writer doesn't rebuild (fixes #2427) 2026-10-21 10:24:17 +02:00
FaabyyandClaude 7b19cd02d2 ODT: note table column widths in the progress docs
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-21 06:42:25 +00:00
Fin Warman e8b41d157f M2.1: retain textbox parents and picture-fill fallback 2026-10-20 22:41:28 +01:00
Mex bf9571d7e1 Merge branch 'main' into fix/reject-invalid-comment-ids 2026-10-20 22:41:14 +02:00
Mat Strangeandopencode 3a15b19e7f Docs: the progress docs' status lines carry today's date
Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 17:20:53 +01:00
Jayesh Prajapati fdc325fa89 #3003: Save to a NAS share mounted by GVFS keeps working when it refuses a mode change
A replaced document's temporary file takes the original's Unix mode before
it replaces it. A GVFS SMB mount (smb:// in the Linux file manager) refuses
every mode change with ENOTSUP, so every save of an existing file there
failed and the close prompt's Save never closed. A folder that keeps no
Unix modes has no mode to keep, so the save goes on; any other failure to
set the mode still stops it and keeps the original.
2026-10-20 19:24:45 +05:30
Mat Strangeandopencode 376a517cb0 Docs: the Restrict Editing gaps that are closed
Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:32:40 +01:00
Mat Strangeandopencode bcd751a588 Restrict Editing: a file command that changes the document is checked too
`always_allowed` waved through every `file.*` id, so on a protected document
the file commands that do change it went unchecked: the privacy options and
the properties wrote document settings, and the inspector's removals deleted
content. The privacy options' own comment already said they were "refused on
a protected document"; that wasn't true.

The allow-list now names the file commands that don't change the open
document, and the ones that do are checked like any other command. Reading
the document stays allowed: the inspector without removals, the privacy
settings, the info, and saving a version snapshot.

Restoring a stored version replaces the whole document, and a snapshot's own
protection is stale — restoring one lifted the protection and dropped its
password. The live document's protection, password and formatting limit now
survive a restore.

Co-Authored-By: opencode <noreply@opencode.ai>
2026-10-20 14:31:41 +01:00
midasdf a7f888055a #1681: HTML export and import keep furigana (ruby)
`InlineObject::Ruby` is written as `<ruby>base<rt>reading</rt></ruby>`, in the
base text's run formatting, and `<ruby>`/`<rb>`/`<rt>` are read back into one
ruby object, so docx/doc → HTML → doc keeps the annotation. `<rp>` fallback
parentheses and the tags are not text; unclosed or half-marked ruby (no `<rt>`,
empty `<rt>`, no base) is tolerated and keeps the text it held, with a block
boundary closing the element as a browser does. HTML states no alignment, size or
raise, so import uses the Phonetic Guide's defaults (centred, half the base size,
raised clear of the base text; the run's size, or 10.5 pt when none is stated).

The flow model gains `Inline::Ruby`; formats without ruby (RTF, ODT, Markdown,
LaTeX) write its base text as before. Tests: `html::tests::ruby_*` and
`tests::html_ruby_round_trip`. Docs: file-format-parity (HTML row) and
localization-parity (Japanese row).
2026-10-20 13:35:52 +09:00
midasdf 2d645dba03 #1653: RTF import decodes the East Asian multibyte code pages
`\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.
2026-10-20 13:35:52 +09:00
Dominik Strebel 1fa2533a52 DOCX: majorBidi/minorBidi theme fonts follow the theme font language 2026-10-20 01:19:14 +02:00
Joey 156817d6ba Word signature lines stay signable after saving a .docx 2026-10-20 01:10:22 +02:00
Matteo Andreuzza 59d9beaeec 3099: Read embedded ODT equations from their MathML object 2026-10-20 00:56:59 +02:00
Магда М.Г. bb4c579a4d Merge remote-tracking branch 'up/main' into up-fonts-roman-known-substitutes
# Conflicts:
#	crates/fonts/src/word.rs
#	crates/layout/src/para.rs
#	docs/file-format-parity.md
2026-10-19 22:46:07 +03:00
4e2568e2da RTF import: heading and code formatting compared with the file's own stylesheet (gap 19 follow-up) (#2236)
* 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>
2026-10-19 12:57:16 -04:00
b8debb3050 Content controls: typed text takes the control's formatting, in any language (#3116)
* 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>
2026-10-19 12:53:44 -04:00
f1b4be8a04 Restrict Editing: forms protection keeps a form's content controls (#3112)
* 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>
2026-10-19 12:49:05 -04:00