Drawings carry an angle and horizontal/vertical flips on their placement (Float::rot, flip_h, flip_v), read and written as DrawingML a:xfrm rot, flipH and flipV for pictures, shapes, text boxes and groups; the written effect extent covers the rotated bounds and reading takes that overhang back out. Layout draws them turned about their centre (screen and PDF), square wrap and inline lines keep clear of the rotated bounds, and hit testing follows the turned shape. arrange.rotate now turns the frame (90 degrees, flips) instead of rewriting pixels; arrange.rotation sets an exact angle. The canvas frame has a rotation handle (Shift snaps to 15 degrees) and turned frames resize along their own axes; Picture Format and Shape Format get a Rotation box and a Rotate menu. Text inside a rotated text box stays upright for now. Groups turn and flip as a whole (wpg:grpSpPr a:xfrm): members keep their own turn inside the group's, are carried round its centre, and a group's text boxes move with it. Shape effects are drawn inside the turn; a shadow's direction follows DrawingML rotWithShape (kept on the model, true when absent; Word's presets write 0, so their shadows keep their page direction). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
6.7 KiB
Layout and pagination parity with Microsoft Word
Last reviewed: 2026-10-10 · Last updated: 2026-10-10 · Change: major (first version; line breaking, pagination and object placement checked against Word's behaviour from the layout source on origin/main) · Target: Microsoft Word (Microsoft 365) for Mac 16.113.4
A document only "opens correctly" if its pages break where Word breaks them: the same lines, the
same page count, footnotes and pictures on the same pages. This checklist covers line breaking,
pagination and object placement in crates/layout. Typing behaviour is in
typing-parity.md; the reading and writing of the properties is in
file-format-parity.md.
Dimension: ~55% (estimated), 90–145 h to full (the sum of the rows below). The engine implements most of Word's layout rules, but nothing has been compared page-for-page with Word on real documents, and the default font can't match Word's metrics.
Checklist
✅ matches Word as far as our tests show · 🟡 implemented, differs or untested against Word · ❌ missing
| Feature | State | Evidence / gap | Hours |
|---|---|---|---|
Line breaking (UAX #14 with Word's exceptions: no break after /) |
✅ | layout::tests::no_line_break_right_after_a_slash |
— |
| Justification, Word 2013+ space shrinking (compat mode 15) | ✅ | #109 | — |
| Kashida justification (Arabic) | ❌ | no kashida in crates/layout |
3–5 |
| Distributed / Thai distributed alignment | 🟡 | read; layout untested | 1–2 |
| Default font metrics (Aptos, Calibri, Cambria…) | 🟡 | Carlito/Caladea/Liberation are metric-compatible for Calibri/Cambria/Arial/Times/Courier; Aptos (Word's default since 2024) has no metric-compatible open substitute, so new Word documents wrap differently (typing-parity.md Known gaps). Needs an owner decision (commission or license a metric-matched font) | 6–10 + owner |
| Line spacing (single, 1.5, double, at least, exactly, multiple), spacing before/after, contextual spacing | 🟡 | Word puts multiple-spacing extra below the text and collapses spacing differently (#135, #108 open) | 3–5 |
| Widow/orphan control, keep with next, keep lines together, page break before | ✅ | lib.rs keep_next/keep_lines |
— |
| Tabs (left, center, right, decimal, bar), leaders | 🟡 | Bar tabs untested | 1–2 |
| Hyphenation (automatic, 0.25" zone, soft hyphens, consecutive limit) | 🟡 | English patterns only; other languages hyphenate wrongly or not at all | in localization |
| Drop caps | ✅ | 2026-10-06 | — |
| Columns, column breaks, separators | 🟡 | Columns don't balance at a continuous section break or document end | 4–6 |
| Section breaks (next page, continuous, even, odd), different first page, odd/even headers | ✅ | Section breaks don't show in Draft view (#42) | 1–2 |
| Body top below a tall header, footer pushes body up | ✅ | #138 | — |
| Footnotes at the page bottom, endnotes at the end, note numbering | 🟡 | A long note doesn't continue onto the next page; no continuation separator/notice; per-section restart partial | 4–6 |
| Tables: row heights (at least/exact), rows split across pages, header rows repeat, Can't Split | ✅ | 2026-10-06, #138 | — |
| Tables: autofit to contents/window, fixed widths, Word 2013 edge | 🟡 | #137; #44: AutoFit Contents measures each column's narrowest and widest text and shares the width like Word, once (not live as you type); cell preferred widths and spans still differ | 3–5 |
Floating tables (w:tblpPr) |
🟡 | #137; overlap rules untested | 2–3 |
| Nested tables | 🟡 | render; polish missing | 2–4 |
| Floating pictures/shapes placement (relative to page/margin/column/paragraph/line/character) | ✅ | #136 | — |
| Text wrap: square, top-and-bottom, behind, in front | ✅ | 2026-10-06 | — |
| Text wrap: tight, through (contour, wrap polygon) | ❌ | laid out as square | 6–10 |
| Text boxes, overflow, linked text boxes | 🟡 | #46; linked text boxes missing | 3–5 |
| Rotated objects and text | 🟡 | #332: pictures, shapes, charts and groups drawn turned and flipped about their centre (screen and PDF); square wrap and inline lines keep clear of the rotated bounds; hit testing on the turned shape. Text in a rotated text box stays upright | 2–3 |
| Page borders (from page edge or text), page colour, watermark | ✅ | — | |
| Line numbers (restart per page/section, count by) | ✅ | — | |
| Vertical page alignment | ✅ | — | |
| Gutter, mirror margins, book fold, 2 pages per sheet | 🟡 | gutter and mirror margins; book fold missing | 2–3 |
Document grid (w:docGrid, lines per page, characters per line) |
❌ | East Asian documents paginate differently without it | 4–6 |
Vertical text (tbRl) in sections and text boxes; cell text direction |
🟡 | cell text direction landed (#245); page-level and text-box vertical text missing | 8–12 |
| Ruby / phonetic guide, enclose characters, combined characters | ❌ | 4–6 | |
| Bidirectional paragraphs, mixed-direction lines | 🟡 | #207; RTL sections and tables (w:bidiVisual) missing |
4–6 |
| Equations: display layout, numbering, wrapping long display equations | ✅ | #191; long display equations break at top-level operators (m:brkBin, m:brkBinSub), manual breaks and m:alnAt, continuation lines indented by m:wrapIndent or set right (m:wrapRight) (#326) |
— |
| Hidden text excluded from breaking and hyphenation | ✅ | #173 | — |
| Fields: PAGE, NUMPAGES, SECTIONPAGES, TOC page numbers | ✅ | #52 | — |
Compatibility modes (Word 2003/2007/2010 layout for old files: w:compat options) |
🟡 | mode 15 behaviour; the ~60 legacy compat options are mostly ignored | 10–20 |
| Page-by-page comparison harness against Word | ❌ | Run Word locally on our own synthetic documents, export PDF, compare line breaks; Word output stays under plan/ (never committed) |
8–12 |
Performance
| Measure | WordCraft | Word | Kind |
|---|---|---|---|
| Cold layout, 188-page document | 61 ms | not measured | measured 2026-10-06 (not re-run this pass) |
| Relayout after an edit | 1.4 ms | not measured | measured 2026-10-06 |
| Font picker with many installed fonts | fixed freezes (#233) | instant | issue #31, #59 |
| Large real-world documents (500+ pages, many pictures) | not measured | gap |
Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-10-10 | trivial | Rotated and flipped objects laid out and drawn (#332) |
| 2026-10-10 | trivial | Long display equations wrap across lines (#326) |
| 2026-10-10 | trivial | AutoFit Contents measures the text (#44) |
| 2026-10-10 | major | First version: layout and pagination checklist with evidence from crates/layout, PRs and issues |