* #2853: Word XML, web pages and MHTML saved as .doc open by what they hold A Word 2003 XML Document named `.doc` went to the Word 97-2003 reader, which refused it ("Invalid CFB file"), and the window showed a blank page. Word XML (2003 or Flat OPC), a web page and a Single File Web Page (MHTML) under a `.doc`, `.docx` or `.rtf` name now open by what they hold, as in Word. MHTML is read for the first time (`.mht`/`.mhtml` too): the MIME parts are split and decoded, the page is imported as HTML in its charset and its pictures load from the other parts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #2853: leave MHTML to #1104, keep Word XML and web pages saved as .doc #1104 adds an MHT reader (`wordcraft_formats::mht`), so this branch drops its own MHTML reader and the `.mht`/`.mhtml` extensions instead of adding a second one. A Word XML Document or a web page under a `.doc`, `.docx` or `.rtf` name still opens by what it holds (the #2853 fix). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #2853: a web page's signature needs its tag name to end (`<htmlish>` is other XML) CodeRabbit: `starts_with("<html")` took XML with a root like `<htmlish>` under a `.doc` name for a web page, though XML that isn't a Word document stays an error. `<html` and `<!DOCTYPE html` now need white space, `>` or `/` after them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * #2853: rustfmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
82 KiB
File-format parity with Microsoft Word
Last reviewed: 2026-10-11 · Last updated: 2026-10-18 · Change: trivial (Word XML and web pages saved as
.docopen by what they hold, #2853; previously trivial: a VML shape in a header cell is always placed on the page, user-checked in Word for Mac, #1775; previously trivial:w:symcharacters keep their symbol font when the text's font changes and are written back asw:sym, #2036; previously trivial: RTF export writes sections (gap 19); previously trivial: ODT export writes sections (gap 19); previously trivial: two lines in one and horizontal in vertical (w:eastAsianLayout) kept on save; previously trivial: PDF outline targets follow leading page breaks; previously trivial: positional tabsw:ptabread and kept on save, #2367; previously trivial: DOCX: mixed right-to-left runs split and markedw:rtlwith their language, #1543; previously trivial:w:doNotTrackMovesround-trips and turns move tracking off; previously trivial: legacy outline, shadow, emboss and imprint text effects drawn, #2534; previously trivial: DOCX glossary documents (building blocks) read, kept and written; previously trivial: cached field results keep their visible run formatting; previously trivial: cached REF results retain hyperlink destinations; previously trivial: CLI conversions can explicitly refresh fields; previously trivial: a copied equation also goes in the clipboard's MathML formats, #1297; previously trivial: a pasted HTML<blockquote>is an indented paragraph, not the Quote style, #1270); previously trivial: every table style part round-trips, #714; previously trivial: VML freeforms read and kept on save; previously trivial: multi-paragraph field instructions no longer add blank lines; previously trivial:flowChartOrdraws its circle and cross spokes, as in Word; previously trivial: RTF: Mac OS Roman and DOS text, #1653; previously trivial: VML text boxes keep their internal margins, fit to their text and vertical alignment; hidden drawings not shown; previously trivial: PDF export draws horizontal and vertical lines inside groups, #1474; previously trivial: symbol-font text exported as Unicode, #776; previously trivial: text-box margins, Resize shape to fit text and freeforms in groups, #743; previously trivial: formatted paste from the system clipboard's HTML and RTF, #687; previously trivial: independent complex-script formatting inheritance and round-trip regression tests; previously trivial: import/export fixes for TXT, RTF, ODT, HTML and LaTeX, #433–#440; previously major: first version; every format Word reads or writes, measured from the readers and writers on origin/main) · Target: Microsoft Word (Microsoft 365) for Mac 16.113.4, plus Word for Windows' extra formats
Every format Word opens or saves, what WordCraft does with it, and how it's tested. Fidelity is the
share of a typical real-world file's content and formatting that survives (read: shows correctly;
write: Word opens it showing the same). Summary in target-app-parity.md;
the work items are in gaps.md.
Dimension: ~60% (estimated), 179–290 h to full (the sum of the rows below). The weighted table gives ~64%; we discount it to ~60% because DOCX fidelity has never been checked on real-world files. DOCX dominates the weight: a Word user sends and receives .docx; everything else is occasional.
Legacy DOC/DOT document properties
The binary Word reader imports SummaryInformation text properties: title, subject, author, keywords, description, last editor and revision. Code-page strings use their declared Windows single-byte encoding, UTF-8 or UTF-16LE; UnicodeString lengths are counted in UTF-16 units. Metadata streams are capped at 2 MiB and individual values at 64 KiB. Invalid optional metadata is skipped without losing the document body. Creation/modification timestamps and DocumentSummaryInformation/custom properties are not imported by this change; DOC/DOT remain read-only formats and encrypted legacy documents remain unsupported.
Tests create OLE property sets in Rust and exercise the public document reader for normal DOCs, templates, accented/Cyrillic/Unicode properties and malformed lengths/offsets. A local LibreOffice-generated DOC and DOT containing an original synthetic document reproduced the missing-title bug before this change and retain their title after it.
Formats
Weights are the share of a Word user's file traffic (estimate). Read/write: ✅ yes, 🟡 partial, ❌ no, — Word doesn't either.
| Format | Weight | Word | WordCraft read | WordCraft write | Fidelity | Hours | How it's tested | Notes |
|---|---|---|---|---|---|---|---|---|
.docx page background picture/texture (v:background/v:fill) |
— | R/W | ✅ | ✅ | ~80% | — | roundtrip.rs::page_background_picture_round_trips, crates/render |
Image part and type/o:title kept; tiled at 96 dpi or stretched; drawn on screen and in PDF; no Fill Effects dialog yet, gradients and patterns are not read |
.docx Word Document (ECMA-376 Transitional) |
70% | R/W | ✅ | ✅ | ~65% | 70–110 | crates/docx/tests (roundtrip 34, fixtures 29, malformed 8, bidi 5, bidi_runs 12, citations 5); synthetic fixtures only |
Word opens our files. Untested on a real-world corpus. Tracked changes inside a field result stay revisions (the field is kept as a range, #1037). Details below |
.docx Strict Open XML |
1% | R/W | ✅ (namespace mapping in xml.rs) |
❌ | ~60% | 2–4 | strict_namespace_and_bad_numbers, strict_line_heights_with_a_unit_are_read_as_point_multiples |
Writing Strict is rare; unit-suffixed measures (13.90pt) read as points (#2329) |
.docm / .dotx / .dotm |
3% | R/W | ✅ | ✅ | ~65% | incl. above | macro_packages.rs |
Macros and signatures kept on save (#172); macros never run |
.doc Word 97-2003 (and .dot) |
8% | R/W | 🟡 | ❌ | ~60% | 25–40 | crates/docbin/src/tests.rs, tests/drawing.rs |
Spec-based reader ([MS-DOC]): text, formatting, styles, sections, headers/footers, tables, lists, notes, fields, bookmarks; inline and floating pictures (PNG, JPEG, DIB, TIFF, EMF, WMF; Macintosh PICT as a sized placeholder) with crop, alt text, position and wrap; basic shapes, lines, groups and text boxes ([MS-ODRAW]). Table cell shading from every SHD record (sprmTDefTableShd, sprmTDefTableShdRaw and their 2nd/3rd ranges, with the Word 97 Shd80 as fallback; a clear pattern shows its background colour); the row-end mark is not a cell, and each table has its own grid and properties (#1380). Horizontally merged rows stored as fewer, wider cells come with #1123. Freeform shapes, z-order and Word 6/95 files are dropped or refused. Password-protected files (XOR obfuscation, RC4, RC4 CryptoAPI) open after a password prompt, from the public specs; not yet tried on a file Word wrote (AES-encrypted ones are refused). No writer. DIB conversion caps BMP bytes (header included) and encoded PNG at the supplied BLIP byte limit, and uses it as the decoder's allocation limit; a BMP fallback is kept if it fits (related #1334 and #1554). |
| PDF export | 8% | W | — | ✅ | ~80% | 6–10 | crates/pdf/src/tests.rs |
krilla: embedded subsets, selectable text incl. field results (#133); Hebrew and Arabic copy and search as whole words (right-to-left glyphs written in visual order, #1383); All Caps and Small Caps text extracts as the capitals shown, as from Word's PDFs, and never changes the case of other text (#1473), links, outline, tagging. Outline destinations follow leading page breaks to the heading text. Fonts it can't subset become outlines (#132). Print Markup (#589): comments in balloons beside each page (scaled to its paper) and tracked changes; HTML/Markdown/text copies end with a comment list. Horizontal and vertical lines inside groups are drawn (#1474). No PDF/A option, no "best for printing/online" choice |
| PDF open (PDF Reflow, Word for Windows) | 1% | R | ❌ | — | 0% | 30–50 | — | Converting a PDF into an editable document. A sibling app (PdfCraft) may supply the parser |
.rtf Rich Text Format |
3% | R/W | 🟡 | 🟡 | ~50% | 10–15 | crates/formats/src/tests.rs |
~1,500 lines: text, character/paragraph formatting, styles, lists, tables, pictures (\shppict, #154), fields, bookmarks. 8-bit text in the document's code page (\ansicpg) or the current font's (\fcharset, \cpg): Cyrillic, Central European, Greek, Turkish, Hebrew, Arabic, Baltic, Vietnamese, Thai (was Windows-1252 only), Mac OS Roman (\mac, \fcharset77) and DOS code pages (\pc 437, \pca 850, \cpg 437/737/850/852/857/866, \fcharset254) (#1653). Double-byte East Asian code pages (Japanese \fcharset128, Chinese, Korean) not decoded yet. Pasted from the system clipboard where the platform offers it (Windows, Wayland) when there is no HTML (#687). Tracked insertions and deletions read and written (\revtbl, \revised/\deleted, authors, \revdttm dates), so Accept/Reject All work after opening or a round trip; tracked paragraph marks (\par inside \revised/\deleted groups, including a \sect inside a deletion) and character and paragraph formatting revisions (\crauth/\crdate with \oldcprops, \prauth/\prdate with \oldpprops) read and written; section and table formatting revisions (\oldsprops, \oldtprops) not yet. Sections on import (#1378): each \sect ends a section with its own page size and orientation, margins, header/footer distance, columns, break type, page-number restart, start and format, title page and headers/footers (those it leaves out link to the previous); export still writes one section. Thin: comments, notes, section export, RTL |
.odt OpenDocument Text |
2% | R/W | 🟡 | 🟡 | ~50% | 8–12 | crates/formats/src/tests.rs, crates/formats/src/odt.rs unit tests, crates/engine/tests/odt_sections.rs |
Styles, lists (#87), tables, pictures, headers/footers, page breaks (#86), keep with next (#2813), page size, orientation and margins (#810), sections: one per master-page change on a paragraph or table (next page, with its own page size, orientation, margins, columns and page-numbering restart) and per columned text:section (continuous, with its columns and separator line) (#1379); export writes them back: a master page and page layout per new-page section (size, orientation, margins, columns, page-numbering restart on the first paragraph or table), a text:section with its columns per continuous section whose columns differ from the page's (one column included), a list cut by a section break goes on numbering. Limits: a continuous section's page-numbering restart isn't written (ODF applies it only with a master page); a continuous section with the page's columns merges into the section before it on reopening; even and odd page starts are written as plain new pages; a tracked deletion of a section break is lost; an empty last section is dropped; tracked insertions and deletions with author and date, inserted and deleted paragraph marks and text inserted then deleted included (ODF change tracking; format changes read as none; pictures and tables inside a tracked deletion are dropped). No footnotes, comments, fields or RTL |
.rtf Rich Text Format |
3% | R/W | 🟡 | 🟡 | ~50% | 10–15 | crates/formats/src/tests.rs |
~1,500 lines: text, character/paragraph formatting, styles, lists, tables, pictures (\shppict, #154), fields, bookmarks. 8-bit text in the document's code page (\ansicpg) or the current font's (\fcharset, \cpg): Cyrillic, Central European, Greek, Turkish, Hebrew, Arabic, Baltic, Vietnamese, Thai (was Windows-1252 only), Mac OS Roman (\mac, \fcharset77) and DOS code pages (\pc 437, \pca 850, \cpg 437/737/850/852/857/866, \fcharset254) (#1653). Double-byte East Asian code pages (Japanese \fcharset128, Chinese, Korean) not decoded yet. Pasted from the system clipboard where the platform offers it (Windows, Wayland) when there is no HTML (#687). Tracked insertions and deletions read and written (\revtbl, \revised/\deleted, authors, \revdttm dates), so Accept/Reject All work after opening or a round trip; paragraph-mark and formatting revisions not yet. Sections on import (#1378): each \sect ends a section with its own page size and orientation, margins, header/footer distance, columns, break type, page-number restart, start and format, title page and headers/footers (those it leaves out link to the previous); export writes every section too (break type, page size and orientation, margins, header/footer distance, columns, page numbering, title page; not headers/footers). Thin: comments, notes, RTL |
.txt Plain text |
2% | R/W | ✅ | ✅ | ~95% | 1–2 | unit tests | Encoding detection. Files with other extensions (.m3u/.m3u8 playlists, logs, config files, no extension) open as text when they are text, Word and RTF files by their signature; binary files are refused (#569). A Word or RTF extension that contradicts the content also opens by signature, as in Word: RTF saved as .doc (hh.ru résumés, accounting exports), a .docx renamed .doc, a Word 97-2003 file renamed .docx; a Word XML Document (Word 2003 XML or Flat OPC) or a web page (<html>, <!DOCTYPE html>) under a .doc, .docx or .rtf name opens as what it is (#2853, tests::word_xml_and_web_pages_saved_as_doc_open_by_content). Word's encoding choice dialog and line-ending options missing; such files save only through Save As |
.htm/.html Web Page and Web Page, Filtered |
1% | R/W | 🟡 | 🟡 | ~60% | 6–10 | crates/formats/src/tests.rs |
Tables (#102), relative pictures embedded (#113), missing </head> tolerated (#154). Header cells (<th>) bold unless their CSS says otherwise; text-align on a cell, or inherited from its row, row group or table, aligns the cell's paragraphs (#1381). HTML gives no page size, so it opens on the region's paper (A4 or Letter), as Markdown and plain text do; @page isn't read (#1381). No Word "Web Page" (with _files folder) round-trip. The same reader takes HTML from the system clipboard: Paste keeps another program's formatting (a Word document, a web page; Windows' "HTML Format" header understood), Merge Formatting keeps its emphasis (#687). A pasted <blockquote> (LibreOffice's paragraph indented on both sides) becomes a Normal paragraph indented by the page's blockquote margins, else 0.5 in each side; opening an .html file keeps it as the Quote style (#1270). Equations are written as MathML (with the linear format as a UnicodeMath annotation) and read back from MathML: our annotation, else the presentation elements, else a TeX annotation (#816). A copy of one equation alone also goes in the clipboard's MathML formats (Windows "MathML" and "MathML Presentation", macOS public.mathml.presentation and public.mathml; W3C MathML 3 §6.2.4), which Microsoft documents Word reading first when it pastes math (#1297; the paste into Word is not yet verified) |
.mht/.mhtml Single File Web Page |
<1% | R/W | ❌ | ❌ | 0% | 3–5 | — | |
.xml Word XML Document (Flat OPC) |
<1% | R/W | ✅ | ✅ | ~65% | — | crates/docx/tests/word_xml.rs, roundtrip.rs (flat_opc_round_trip_equals_docx) |
The DOCX package in one XML file, read and written by the DOCX code (#548); File › Export › Word XML Document. .xml files are told apart by their root element |
.xml Word 2003 XML (WordprocessingML 2003) |
<1% | R/W | 🟡 | 🟡 | ~55% | 1–2 | crates/docx/tests/word_xml.rs |
Opened by rewriting it as a package for the DOCX reader (#548): styles, default fonts, lists, tables, sections, headers/footers, footnotes/endnotes, comments, bookmarks, tracked insertions/deletions, hyperlinks, w:binData pictures, document properties. Not read: formatting changes, w:docPr settings, smart tags. Written (#1081) by rewriting the DOCX writer's package into the 2003 vocabulary (crates/docx/src/wordml2003_write.rs, crates/docx/tests/wordml2003_write.rs): core properties, styles (document defaults folded into Normal), lists, text and paragraph/character formatting, tables, hyperlinks, footnotes/endnotes inline, headers/footers in the section properties, bookmarks, comments and tracked insertions/deletions as aml:annotation, section page setup, and PNG/JPEG/GIF/BMP/EMF/WMF pictures as w:pict with w:binData. Dropped on write: shapes, text boxes, groups, charts, SmartArt, ink, equations, OLE objects, picture crop, rotation and floating position (pictures become inline), content-control wrappers (their content stays), formatting-change tracking, theme colours/fonts, custom XML, macros, even/odd header setting. File › Export › Word 2003 XML Document, file.save/file.saveAs with format: "wordml2003", wordcraft-cli convert … --format wordml2003; a plain save over a 2003 file keeps that format. Never opened in Word yet (written from the published schemas; needs a tester) |
.wps Works 6–9, WordPerfect .wpd (Windows) |
<1% | R | ❌ | — | 0% | 10–20 | — | Windows-only converters; low value |
Encrypted/password-protected .docx/.doc |
1% | R/W | ❌ | ❌ | 0% | 6–10 | — | ECMA-376 Agile Encryption; .doc XOR, RC4 and RC4 CryptoAPI open with a password (docbin crypt.rs, #1079; synthetic tests only, untested against Word-written files; AES .doc refused). Issue #55 |
.xlsx/.xlsm/.xls/.ods as a mail-merge recipient list |
— | R (data source) | ✅ | — | ~90% | 1–3 | crates/formats/src/sheet.rs, cmd/mailings.rs |
Read-only, one sheet's cell values (#334): shared/inline/rich strings, numbers, booleans, cached formula results, dates and times as ISO 8601 (yyyy-mm-dd) from the cell's number format, sparse cells; ODS value types, repeated rows/columns, covered cells. Select Table picks the sheet. Capped (64 MiB per part, 100,000 rows, 1,000 columns, 2 M cells, 32,767 characters a cell, 64 MiB of cell text a sheet so a shared string repeated in every cell can't exhaust memory, #1287). Legacy .xls (Excel 97–2003 BIFF8, #778): SST/LABELSST, LABEL, NUMBER, RK/MULRK, BOOLERR and cached FORMULA results with the same date handling; Excel 5.0/95 and password-protected .xls are refused with a message; a CSV/TSV named .xls reads as text; an HTML page or XML spreadsheet named .xls gets a message saying which it is (#1287). Text lists (.csv/.tsv/.txt) decode UTF-8/UTF-16 by BOM or shape, else the detected legacy encoding (GBK/GB18030, Big5, Shift_JIS, EUC-KR, Windows code pages; encoding param overrides) (#778). Not shown: number formats other than dates (currency, percent, thousands separators); no Access or Outlook sources |
Encrypted/password-protected .docx |
1% | R/W | ✅ | ✅ | ~60% | 2–4 | crates/docx/src/crypt.rs (6 unit tests), crates/engine/src/tests.rs::password_protected_documents_round_trip |
ECMA-376 / [MS-OFFCRYPTO] agile encryption (AES-256-CBC, SHA-512): a protected .docx opens with its password and saves encrypted (#55, #281). Synthetic files only; never checked against Word-made protected files |
Encrypted/password-protected .doc (RC4/XOR) |
<1% | R | ❌ | — | 0% | 4–6 | crates/docbin/src/tests.rs::encrypted_rejected |
Detected and refused (DocbinError::Encrypted, docbin fib). Not decrypted |
.md Markdown |
— | (no) | ✅ | ✅ | — | — | crates/formats/src/tests.rs |
Beyond Word |
.tex LaTeX |
— | — | ✅ | ✅ | — | — | crates/formats/src/tests.rs |
Beyond Word (#11), incl. math |
| Pictures for Insert › Picture, drag-and-drop and paste | — | R | ✅ | — | — | — | insert_picture_takes_a_windows_metafile (engine), paste_picture.rs |
PNG, JPEG, GIF, WebP, BMP, and Windows Metafile .wmf / Enhanced Metafile .emf at their own frame size (#802), drawn by crates/metafile. Not yet: SVG, TIFF, ICO, HEIC |
.png page images |
— | (Save as picture, Mac) | — | ✅ | — | — | io_ext.rs |
|
.json document model |
— | — | ✅ | ✅ | — | — | engine tests | For agents |
DOCX in depth
Measured by grepping crates/docx/src/read and src/write for the OOXML elements (2026-10-10).
| Feature | Read | Write | Notes |
|---|---|---|---|
| Text, runs, paragraph and character properties, styles, numbering, sections | ✅ | ✅ | Including a Styles gallery for files that mark no style w:qFormat (LibreOffice: Word's usual quick styles are added, #1238), complex-script props (w:rtl, w:cs, w:szCs, w:bCs…); runs that mix right-to-left and left-to-right text are split on save where the direction changes, and the right-to-left pieces get w:rtl and a bidi language (w:lang w:bidi, e.g. he-IL), so Word lays Hebrew around Latin names, numbers and e-mail addresses out as WordCraft does and doesn't call it Arabic (#1543, checked in Word for Mac 16.113.4), East Asian fonts kept apart from Latin (#111); independent complex-script formatting inherited through defaults/styles and preserved over repeated saves (complex_script_inheritance.rs), indents in characters (w:firstLineChars, w:hangingChars, w:leftChars, w:rightChars, #534); every ST_NumberFormat value of w:numFmt is kept on save, Chinese/Japanese/Korean, enclosed and full-width formats are drawn, the rest (Hebrew, Arabic, Hindi, Thai…) show decimal (#534). w:beforeLines/w:afterLines not kept (the points are) |
Tables incl. floating (w:tblpPr), table styles and conditional formatting |
✅ | ✅ | Custom table styles round-trip (#256); right-to-left tables (w:bidiVisual) and sections (w:bidi) round-trip (#362) |
| Text, runs, paragraph and character properties, styles, numbering, sections | ✅ | ✅ | Including complex-script props (w:rtl, w:cs, w:szCs, w:bCs…), East Asian fonts kept apart from Latin (#111); independent complex-script formatting inherited through defaults/styles and preserved over repeated saves (complex_script_inheritance.rs), indents in characters (w:firstLineChars, w:hangingChars, w:leftChars, w:rightChars, #534); every ST_NumberFormat value of w:numFmt is kept on save, Chinese/Japanese/Korean, enclosed and full-width formats are drawn, the rest (Hebrew, Arabic, Hindi, Thai…) show decimal (#534). w:beforeLines/w:afterLines not kept (the points are) |
Positional tabs (w:ptab: alignment, margin or indent, leader) |
✅ | ✅ | Laid out at their place whatever the tab stops; one with an alignment or base the spec doesn't define reads as a plain tab (#2367). crates/docx/tests/positional_tabs.rs |
Tables incl. floating (w:tblpPr), table styles and conditional formatting |
✅ | ✅ | Custom table styles round-trip (#256), every w:tblStylePr part (wholeTable, firstRow, lastRow, firstCol, lastCol, band1/2Vert, band1/2Horz, ne/nw/se/swCell) with run formatting, w:jc, cell borders, shading and w:vAlign (#714); a cell's own w:vAlign w:val="top" is kept and beats the style's (#1288); right-to-left tables (w:bidiVisual) and sections (w:bidi) round-trip (#362) |
| Text, runs, paragraph and character properties, styles, numbering, sections | ✅ | ✅ | Including a Styles gallery for files that mark no style w:qFormat (LibreOffice: Word's usual quick styles are added, #1238), complex-script props (w:rtl, w:cs, w:szCs, w:bCs…), East Asian fonts kept apart from Latin (#111); independent complex-script formatting inherited through defaults/styles and preserved over repeated saves (complex_script_inheritance.rs), indents in characters (w:firstLineChars, w:hangingChars, w:leftChars, w:rightChars, #534); every ST_NumberFormat value of w:numFmt is kept on save, Chinese/Japanese/Korean, enclosed and full-width formats are drawn, the rest (Hebrew, Arabic, Hindi, Thai…) show decimal (#534). w:beforeLines/w:afterLines not kept (the points are) |
Tables incl. floating (w:tblpPr), table styles and conditional formatting |
✅ | ✅ | Row table border exceptions (w:tblPrEx/w:tblBorders) round-trip (row_table_borders_round_trip); other row table exceptions are not preserved. Custom table styles round-trip (#256); right-to-left tables (w:bidiVisual) and sections (w:bidi) round-trip (#362) |
| Headers/footers (first, even/odd), page borders, line numbers, gutter, mirror margins | ✅ | ✅ | |
Hyphenation settings (w:autoHyphenation, w:hyphenationZone, w:doNotHyphenateCaps, w:consecutiveHyphenLimit) |
✅ | ✅ | Zone, capitals and limit kept and honoured by layout (#407) |
| Footnotes, endnotes, comments | ✅ | ✅ | The document's own note separators and continuation notice (w:type separator, continuationSeparator, continuationNotice) round-trip as ids -1, 0 and 1 (#352); note options (w:pos, w:numFmt, w:numStart, w:numRestart) round-trip in the settings and in each w:sectPr (#385); commentsExtended written; commentsIds (durable ids) and commentsExtensible (UTC dates) read and regenerated consistently with the written paragraph ids; people.xml carried verbatim |
Tracked insertions and deletions (w:ins/w:del), paragraph-mark revisions |
✅ | ✅ | #125, #244 |
Formatting revisions (w:rPrChange, w:pPrChange, w:sectPrChange, w:tblPrChange, w:trPrChange, w:tcPrChange, w:numberingChange) |
✅ | ✅ | #41: kept on open and save, schema order (change element last); changes inside styles.xml are dropped. RTF reads and writes character and paragraph changes (\oldcprops, \oldpprops, with the formatting the flow model knows, list membership included); as Word writes them, \oldcprops holds only the properties that changed (with \crauth/\crdate inside it) and \oldpprops the full old set from \pard; a run without a direct font or size has its paragraph style's written there (heading, Title, Quote, Code), Normal's as \f0\fs24; a size or bold set by hand on heading/code text isn't kept through RTF (as before), so Reject there gives the style's value; a formatting change on the paragraph mark itself and section, table, row and cell changes are dropped. ODT ignores them |
Move tracking (w:moveFrom/w:moveTo, w:moveFromRangeStart/End, w:moveToRangeStart/End), moved paragraph marks |
✅ | ✅ | #400: both ends linked by the range name; ranges rewritten around each moved stretch (ids renumbered); a move missing an end or a range end opens as a plain deletion/insertion. w:doNotTrackMoves (Track moves off) round-trips; with it a tracked cut and paste records a plain deletion and insertion. ODT/PDF export the result (moved-away text hidden like deletions); RTF writes a move as a plain tracked deletion and insertion |
Fields (w:fldChar, w:fldSimple), TOC, cross-references, ADDIN citations |
✅ | ✅ | Field codes Word supports but we don't evaluate keep their cached result; ASK and SET keep it but show nothing, as in Word (#1765). Imported TOCs preserve actual result boundaries, formatted entries and nested fields without relying on TOC styles. EQ \o (overstrike, enclosed characters) is drawn; enclosed characters are written as Word stores them, their style in the code's run sizes (#297); a cached picture (INCLUDEPICTURE) stays as the content of a range field (#465) |
Fields (w:fldChar, w:fldSimple), TOC, cross-references, ADDIN citations |
✅ | ✅ | Field codes Word supports but we don't evaluate keep their cached result. Textual REF fields with \h export internal PDF links without refreshing cached text; existing enclosing links take precedence. wordcraft-cli convert --update-fields explicitly runs field updates before saving; ordinary conversion preserves caches and locked fields stay unchanged. Instruction-only paragraphs carry hidden marks instead of blank lines; an empty result paragraph keeps its line. Imported TOCs preserve actual result boundaries, formatted entries and nested fields without relying on TOC styles. EQ \o (overstrike, enclosed characters) is drawn; enclosed characters are written as Word stores them, their style in the code's run sizes (#297); a cached picture (INCLUDEPICTURE) stays as the content of a range field (#465) |
Move tracking (w:moveFrom/w:moveTo, w:moveFromRangeStart/End, w:moveToRangeStart/End), moved paragraph marks |
✅ | ✅ | #400: both ends linked by the range name; ranges rewritten around each moved stretch (ids renumbered); a move missing an end or a range end opens as a plain deletion/insertion. ODT/PDF export the result (moved-away text hidden like deletions); RTF writes a move as a plain tracked deletion and insertion |
Content controls (w:sdt) |
✅ | ✅ | Block, inline, row and cell level, nested; title, tag, id, lock, placeholder, temporary, rich/plain text, w14:checkbox, combo box, drop-down list, date, picture, building block gallery, w15 repeating sections; other w:sdtPr children (data binding, appearance) kept verbatim (#381). A TOC's control is rebuilt by the TOC. Replace All respects content-edit and control-deletion locks and refuses a batch before making any edit |
Restrict Editing (w:documentProtection edit/formatting/enforcement and password hash, w:locked styles, w:permStart/w:permEnd exceptions) |
✅ | ✅ | #412: SHA-512 hash with salt and spin count written; Word's legacy w:cryptAlgorithmSid/w:hash form (SHA-1/-256/-384/-512, ECMA-376 Part 4 §14.8.1) is kept and verified; MD2/MD4/MD5, w:algIdExt and other unsupported forms are refused, never unlocked; w:colFirst/w:colLast table-column ranges read as plain ranges; forms mode (w:edit="forms") lets edits inside fillable content controls (w:lock content-editable, innermost wins) run, everything else rolls back; row-wrapped controls and control deletion remain refused; review.restrict reports formFields; Find Next Region covers form fields (the pane button shows for forms documents); under active protection, developer.properties refuses lockEdit/lockDelete changes — as Word blocks property edits until Stop Protection — while other properties (title, tag, placeholder…) stay editable |
Legacy form fields (w:ffData), check boxes, drop-downs |
🟡 | ✅ | The field definition is kept raw and saved back. Check boxes (FORMCHECKBOX) are drawn as Word draws them: a square as tall as the text's line, inset 1.16 pt, 0.72 pt border, an X when checked (w:checked, else w:default), w:size honoured; drop-downs (FORMDROPDOWN) show their selected entry; text fields (FORMTEXT) show their result. Not yet: filling them in, Protect Document's forms mode for legacy fields |
DrawingML pictures inline and anchored (wp:anchor), wrap square/tight/through/top-bottom |
✅ | ✅ | Tight/through read and written with their wp:wrapPolygon (and edited); without one, a shape's outline is written (a picture's rectangle). wrapText (both sides, left, right, largest) read and written for square, tight and through |
Legacy form fields (w:ffData), check boxes, drop-downs |
❌ | ❌ | |
DrawingML pictures inline and anchored (wp:anchor), wrap square/tight/through/top-bottom |
✅ | ✅ | Tight/through read and written with their wp:wrapPolygon (and edited); without one, a shape's outline is written (a picture's rectangle). wrapText (both sides, left, right, largest) read and written for square, tight and through. DrawingML layoutInCell="0" is kept and written back; a VML shape in a header or footer table cell is always treated the same way (user-checked in Word for Mac, whatever o:allowincell says; saved as DrawingML layoutInCell="0"). In a header or footer table cell, such a picture placed relative to the page or margins is placed on the page, so a picture watermark in a header that starts with a table is centred behind the text instead of sitting in the cell (#1775) |
WordArt text effects (w14:textFill, w14:textOutline, w14:shadow, w14:glow, w14:reflection in w:rPr) |
✅ | ✅ | Solid, gradient (linear) and no fill, outline colour/width, shadow, glow, reflection (#416); w14:scene3d/w14:props3d dropped |
Legacy font effects (w:outline, w:shadow, w:emboss, w:imprint in w:rPr) |
✅ | ✅ | Drawn on screen and in PDFs as Word's PDFs show them (#2534): hollow outlined letters; a sharp grey copy down-right (shadow, emboss) or up-left (imprint); WordArt effects on the same run win; tracked insertions and deletions keep them. Not drawn on colour emoji, or on top-to-bottom text with upright East Asian characters. Emboss and imprint are approximations (no highlight edge) |
OpenType ligatures (w14:ligatures in w:rPr: runs, styles, document defaults) |
✅ | ✅ | All 16 values; an unknown value is ignored; nothing is written when the document has none |
DrawingML shapes and text boxes (wps:) |
✅ | ✅ | Preset geometries subset (callouts and flowchart boxes drawn as the nearest shape, #611); flowChartOr draws a circle with horizontal and vertical spokes; rounded rectangles keep their own corner (roundRect adj: read, written back, drawn on screen and in PDF, #1476); fill and outline from the shape or from the theme through its style references (a:fillRef/a:lnRef, #611), scheme, preset, system, scRGB and HSL colours; rotation and flips (a:xfrm rot/flipH/flipV, pictures and groups too, #332), the effect extent covering the rotated bounds; wps:bodyPr vert and anchor, internal margins (lIns/tIns/rIns/bIns) and Resize shape to fit text (a:spAutoFit: the text is never cut, #743), linked text boxes (wps:txbx/@id, wps:linkedTxbx id/seq, #369); text warps (a:prstTxWarp with adjust values, all 41 presets kept; unknown ones dropped), connectors (straightConnector1, bentConnector2–5, curvedConnector2–5 read; 3-segment forms written) with glued ends (wps:cNvCnPr a:stCxn/a:endCxn; the glued shapes keep their drawing ids) and arrowheads (a:headEnd/a:tailEnd, drawn as triangles), including custom-geometry paths (#416). Hidden drawings (wp:docPr/@hidden, a group member's cNvPr/@hidden) are kept and saved but neither drawn nor laid out, nor exported to other formats, as in Word for Windows; .doc and RTF shapes don't read their hidden flag yet |
Freeform shapes (a:custGeom: a:moveTo, a:lnTo, Bézier curves flattened) and WordCraft ink |
✅ | ✅ | Ink is written as a freeform wps:wsp in a wp:anchor (round caps, alpha for highlighter) and recognised again by its drawing name (#307). It is saved as a custom-geometry shape, not as Word's own ink (InkML in w14:contentPart), so Word shows WordCraft ink as a freeform shape it can move and recolour but not erase with its ink eraser; arcs drawn straight |
Group shapes (wpg:), drawing canvas (wpc:) |
✅ | ✅ | Groups (#267) and canvases (#344) with pictures, shapes, freeforms (custom geometry, #743; a path drawn back to its start without a:close is filled, as in Word) and text boxes; nested groups flattened. The canvas schema has no transform, so a turned or flipped canvas keeps its turn in our own wpc:extLst extension (urn:wordcraft:canvas-xfrm; Word shows it unturned). Legacy VML canvases (v:group editas="canvas") aren't read as canvases |
VML (w:pict, v:shape, v:textbox) |
🟡 | 🟡 | Pictures, text boxes, lines (with arrowheads), rectangles and ovals, best effort (#242, #611). Preset shapes (v:shape whose o:spt, own or from its v:shapetype, or Word's shapetype id _x0000_tNN, names a preset; [MS-ODRAW] §2.4.24 numbers) are drawn and saved like the DrawingML preset of the same shape: exactly for rectangle, rounded rectangle, ellipse, diamond, isosceles triangle, five-point star, right arrow, line and straight connector, heart, text box, and the flowchart process, decision and connector; the others as their nearest shape, as for DrawingML (left/up/down arrows as the arrow, right triangle as the triangle, callouts as their box). Picture frames without a picture aren't drawn. Fill, outline and size come from the shape, else its shapetype; text inside keeps the shape around it (#1377). Adjust values (adj) aren't read: presets use WordCraft's proportions. Text boxes keep their internal margins (inset, unless o:insetmode="auto"), Resize shape to fit text (mso-fit-shape-to-text) and vertical alignment (v-text-anchor). Hidden shapes (visibility:hidden, like DrawingML's wp:docPr/@hidden) are kept and saved but neither drawn nor laid out, as in Word for Windows (Word for Mac draws them). Not read: vertical text (layout-flow) Word's text watermark (a v:textpath shape in a header or footer, recognised by Word's PowerPlusWaterMarkObject id or by being centred on the page both ways) becomes the document's watermark: text, font, fill colour, semitransparent (v:fill opacity) and diagonal (any turn); drawn with WordCraft's own size and angle, on every page even when only some sections' headers (or only the first-page header) have it (#1290). Saving writes the watermark back the same way: a v:textpath shape centred on the margins, behind the text, at the start of every header's first paragraph; a section whose pages would show no header gets a header holding only the watermark (default, and first-page or even-page ones when those are on), while sections linked to an earlier header stay linked. Freeforms (v:shape with its own path: moves, lines, Béziers and closes in its coordsize/coordorigin space, nf/ns, v:stroke opacity) are read as freeforms and saved as DrawingML custom geometry; fill opacity: none at 0, otherwise opaque (as for DrawingML fills); arcs are drawn straight; shapes whose path uses formulas (@n) and paths inherited from a v:shapetype aren't read. Other VML is written only for OLE object pictures. Other VML WordArt (Word 2007's: v:textpath in the body, or a header logo) becomes a text box without fill or outline holding its string, in the textpath's font, the shape's fill colour and a size from the shape's height; its WordArt shape (o:spt 136–175) becomes the DrawingML text warp of the same shape (#2875). Its outline, shadow and 3-D effects aren't read |
Alt text of drawings (wp:docPr/cNvPr descr and title, decorative mark adec:decorative from [MS-ODRAWXML]) and tables (w:tblCaption, w:tblDescription) |
✅ | ✅ | Pictures, shapes, text boxes, groups and their members, charts and SmartArt; OLE objects get theirs written into the kept XML (#386) |
Charts (c:chart), SmartArt (dgm) |
✅ | ✅ | Drawn from the chart's cached data and SmartArt's stored drawing (#292), with data labels (c:showVal); a chart of only types not drawn yet (radar, stock, bubble, surface, 3-D, pie of pie) shows a placeholder in its frame with its title and type (#998). Charts made in WordCraft (Insert › Chart, #409) are written as a c:chartSpace part with their data as literals (c:strLit/c:numLit, no embedded workbook, so Word shows them and offers to create a workbook on Edit Data) and read back as editable; a chart part holding anything more (any chart from Word, or one re-saved by Word) stays read-only. Saved back as read, with every part they reach (chart, embedded workbook, colours and style; diagram data, layout, quick style, colours and drawing) and their content types; a moved or resized one keeps its new frame, and a pasted copy gets its own parts (#319). SmartArt made in WordCraft (#497) is written from its model: data (dgm:dataModel points and connections), our own layout, quick-style and colour definitions under the built-in layout identifiers, and a dsp:drawing with every shape placed; it reads back editable only when its data part is exactly what WordCraft writes |
OLE objects (w:object: embedded or linked files, ActiveX) |
🟡 | ✅ | Shown as their picture (VML or DrawingML); saved back whole with the embedding and picture parts, at the size, rotation and flips set in WordCraft (#319, #332). Can't be opened or edited |
Native Word ink (w14:contentPart, InkML) |
🟡 | 🟡 | Drawing-level ink kept opaque with its InkML part, relationship and content type; direct run-level w:contentPart and run-level mc:AlternateContent keep the native branch and VML picture fallback. Direct content parts remain direct through repeated save/reopen cycles, without a native rendering. Drawing-level ink shows the preview or a missing-picture box. Ink members of groups (wpg:wgp, nested groups too) and drawing canvases (wpc:wpc), bare or in mc:AlternateContent with a pic:pic fallback, keep their markup, part, relationship and place among the members; they are written at the member's current position in the group and drawn from their fallback picture. Such a group can't be ungrouped yet (Ungroup is unavailable, so the ink is never left in a form it can't be saved in). Their part is dropped with the group (no orphan part); ink whose part is missing or isn't InkML gives way to its fallback, else is dropped. No native stroke rendering, eraser, conversion or replay |
| 3D models | ❌ | ❌ | Dropped on read, not preserved on save |
Equations (OMML m:oMath) |
✅ | ✅ | #191; manual breaks (m:brk) and the settings' m:mathPr wrapping options (#326). Every ECMA-376 §22.1 element is read and laid out (pinned by read::math::tests::every_omml_element_keeps_its_content); equations straight in the body or a cell (no w:p), behind mc:AlternateContent Requires="m", or with runs inside hyperlinks, simple fields, content controls and tracked insertions inside the equation keep their content; w:sym and Symbol-font text in math become Unicode (#1273). Not yet: Equation Editor 3.0 / MathType OLE objects show their WMF preview only (text in #1994), m:mathFont (Cambria Math is always asked for), m:dispDef/m:defJc/m:lMargin, tracked deletions shown struck through inside equations |
| Themes, font table, settings, compatibility mode | ✅ | ✅ | w:allowSpaceOfSameStyleInTable read and written in schema order; absent/false default off, enabled flag retained in modes 11/12/14/15. Embedded fonts (w:embedRegular, w:embedBold…) lay out and export the text set in them for the session (#1176), subsets Word embeds ("embed only the characters used", a made-up family such as ___WRD_EMBED_SUB_46 inside) found by the font table's name (#2240); the font table, its relationships and the embedded font parts are written back unchanged (#770); attached template (w:attachedTemplate and its external relationship) and w:linkStyles round-trip (#377). w:removePersonalInformation and w:removeDateAndTime round-trip and are honoured on save (#1062): comment and revision authors written as "Author" (initials "A"), no dc:creator/cp:lastModifiedBy, no annotation dates; the open document keeps its values (file.privacyOptions sets them, file.privacy reads them; File › Options › Save › Privacy for this document). The template path comes from the file, so opening never reads it: styles update from it only on an explicit Update Styles. Settings the model doesn't hold (other w:compatSettings, rsids, document variables, proofing state, colour scheme mapping…) survive a save: the writer merges into the file's own settings.xml (below); w:latentStyles, w:default on localized default styles and lists' w:nsid/w:tmpl/w:multiLevelType round-trip |
| Custom XML parts, document properties, bibliography sources | 🟡 | 🟡 | Custom XML stores reached from the package/main document preserve opaque data, properties and relationships; binding evaluation remains absent. Custom properties and bibliography sources round-trip (#532). Word's own bibliography file (b:Sources, Sources.xml) imports into Manage Sources, sharing the reader of a document's own bibliography part (#2640) |
Section text direction (w:textDirection in w:sectPr, §17.6.20) |
✅ | ✅ | tbRl, btLr (and tbRlV/tbLrV, read as tbRl); lrTbV and unknown values read as horizontal (#491) |
Section vertical alignment (w:vAlign in w:sectPr, §17.6.23) |
✅ | ✅ | top, center, bottom and both (Justified, read as Center before #1320); unknown values read as top, top writes nothing (crates/docx/tests/section_valign.rs). ODT/RTF don't carry it |
Ruby (w:ruby: w:rubyPr, w:rt, w:rubyBase) |
✅ | ✅ | #288; base and ruby text with their run formatting, alignment, sizes, raise, language |
Document grid (w:docGrid: type, line pitch, w:charSpace), paragraph w:snapToGrid |
✅ | ✅ | #391; run-level w:snapToGrid not read |
Two lines in one and horizontal in vertical (w:eastAsianLayout, §17.3.2.10) |
✅ | ✅ | Every attribute (w:id, w:combine, w:combineBrackets, w:vert, w:vertCompress) read into the run's formatting and written back after w:lang, in runs and styles; values outside the schema are dropped (crates/docx/tests/east_asian_layout.rs). Not laid out yet: the text shows as ordinary text |
w:fitText |
❌ | ❌ | East Asian layout |
| Glossary document (building blocks) | ✅ | ✅ | w:docPart entries (name, style, gallery, category, types, behaviours, description, guid, body) are read into Document::glossary and offered by Insert › Quick Parts and AutoText; save with "in": "document" stores one in the file. An untouched glossary is written back byte for byte with its own parts (styles, settings, fonts…); an edited one, or one whose parts weren't kept, is written from the model as word/glossary1.xml with copies of the document's styles and numbering. The glossary's own styles, settings and fonts, text boxes inside entries, and the notes and comments of a selection saved into the document don't survive an edit. Tests: crates/docx/tests/glossary.rs |
w:altChunk, sub-documents |
❌ | ❌ | |
| Hostile input | ✅ | — | malformed.rs, capped allocations, never-crash standard |
Document paragraph defaults (w:pPrDefault) |
✅ | ✅ | Omission and explicit empty defaults remain distinct across repeated saves; nonempty defaults and later default-spacing edits are written |
Opening files from the system
Double-clicking a document, Open With, and making WordCraft the default app (#295). The installers register WordCraft for these types but never take over a default; File › Options › General has Make WordCraft the default for Word documents… where the system allows it.
| Platform | Registered types | Default app | Notes |
|---|---|---|---|
| Windows (MSI) | .docx .docm .dotx .dotm .doc .dot .rtf .odt; .md .txt under Open With only |
Options button opens Settings › Default apps on WordCraft's page | One ProgId per type, OpenWithProgids, Capabilities + RegisteredApplications (packaging/windows/wordcraft.wxs). Not yet tried on a real install |
| Linux, BSD (deb, rpm, Flatpak, AppImage) | Word, ODT, RTF, Markdown, LaTeX, HTML, text (MimeType= in the desktop entry, AppStream mediatype) |
Options button runs xdg-mime default for the Word, RTF and ODT types |
Not from inside Flatpak (the sandbox can't change host defaults); a bare AppImage needs desktop integration first |
| macOS | CFBundleDocumentTypes as Alternate handler (Word, ODT, RTF, Markdown, HTML, text) |
Finder › Get Info › Open with › Change All (the Options page says so) | Apple events open the files (#27) |
Paragraph-default preservation
The styles reader records whether w:pPrDefault exists. The writer keeps an omitted
container omitted when its modeled properties are still empty. An explicitly empty
container stays present, and editing document-default spacing writes the new values.
New documents and older serialized style sheets keep their explicit defaults.
crates/docx/tests/fixtures.rs covers omitted docDefaults, omitted paragraph defaults,
empty containers and populated defaults across two save/reopen cycles. This preserves
application-defined defaults without assigning inferred Word spacing values to the model.
Observed in Word for Mac 16.113.3 (26092714): three synthetic paragraph-default
controls (omitted, explicitly empty, populated) and two reduced invoice controls retain
identical text, glyph/image geometry, page sizes and embedded font-program hashes through
two WordCraft save/reopen cycles. All fifteen variants open without repair warnings.
Word PDFs and measurements remain local under ignored plan/word/; they are not fixtures.
These checks cover preservation on Word reopen, not WordCraft pagination parity.
The public OOXML reference
describes omission as leaving paragraph defaults application-defined. Real-world corpus
coverage and WordCraft layout matching remain separate gaps.
Custom XML store preservation
A reduced original bound-control DOCX loses its backing data, item properties and relationship on main, both without editing and after unrelated edits. Main's content-control support (#381) preserves control metadata, including binding attributes, while this custom XML path keeps the backing store through the bounded opaque part graph: payload bytes, content types and related-part IDs survive two edited save/reopen cycles. The public dataBinding contract maps a control to a custom XML data part; its item properties identify the store. Keeping displayed text alone does not preserve that mapping. This change preserves stores without evaluating XPath or synchronizing control edits into XML. Focused original tests cover package/main roots, multiple/shared stores, cycles, external schema references, missing parts, UTF-16 payloads, strict relationship types and reserved filenames. The bibliography names used by #532 are reserved; bibliography already loaded into the source model belongs to its writer. Word-derived files are not fixtures. No readiness estimate rises from these synthetic package checks, and native Word rendering/binding behavior remains untested.
Native save-path guarantees
Native io::save_path_with serializes (and, for password-protected DOCX, encrypts) before creating an exclusively owned sibling temporary file. It writes and synchronizes that file, closes it, then replaces the destination. Failed preparation or replacement keeps the original bytes; cleanup removes only the save's temporary file, on a best-effort basis. Existing <name>.tmp files, directories and links are untouched. A new document gets the default mode (0666 less the umask). When replacing an existing regular file, the Unix temporary file starts with mode 0600 and then takes the existing file's basic permissions. Serialization and format support are unchanged. Native io::write_file (Print to PDF, file.printPdf; Export Page as PNG, file.exportPng) writes through the same path (#1355). If the folder refuses the temporary file with a permission error (macOS privacy protection, a read-only folder; #2138), an existing destination is never written in place: it stays byte for byte as it was, and the error says to use Save As to another folder or to give WordCraft access to the folder (on macOS, System Settings › Privacy & Security › Files and Folders). Only a destination that doesn't exist yet is created directly (exclusively, so nothing already there is opened); a failed direct write removes it again. Unit tests cover a read-only folder on Unix; the macOS privacy case is not yet confirmed on a Mac.
Synthetic CLI cases and engine/tests/safe_save.rs cover occupied names, links, blocked replacement, missing directories, Save As session state, password-protected saves and original-file protection on macOS. Unit tests inject preparation errors and interleave two prepared saves. Independent saves use separate files; the last successful replacement wins. This is not document locking or conflict detection.
Windows/Linux runtime behavior, ACLs and extended metadata, real disk-full conditions, power loss and network filesystems are not verified by these tests. The containing directory is not explicitly synchronized. Cleanup can fail when storage or permissions change, and process termination can leave a temporary file. No Word observation was needed for this filesystem change; it makes no new format-fidelity claim.
What decides beta for formats
- A real-world DOCX corpus opened, rendered and round-tripped, compared page by page with Word (local only: Word output can't be committed; keep a manifest and our own renders).
- Preserve what we can't render: remaining ink forms and unsupported graphics survive a round trip (keep the parts and the run). Charts, SmartArt and OLE objects do (#292, #319); drawing-level and direct run-level native ink now keep their InkML and optional picture fallback.
- Move tracking (
w:moveFrom/w:moveTo) read and written (formatting revisions: #41; content controls: #381). - Encrypted
.docxopens with a password.
Imported TOC result preservation
Original synthetic TOCs without TOC paragraph styles lose their outer result range on main and #538. Main also flattens a nested page field in a single-paragraph result. Imported TOCs now use the existing range-field markers to retain the actual result boundaries, nested page-reference fields, formatting, links and locks through edited saves.
The public complex-field contract defines begin/separate/end delimiters and permits nested fields. Paragraph styles do not define those boundaries. A complete range is converted to the engine's styled TOC representation only when that writer can reproduce its exact extent; partial or custom-styled ranges stay explicit.
Focused original regressions cover single/multiple paragraphs, partial ranges, adjacent styled text, multiple/simple fields, table cells, section breaks, missing ends and bounded nesting. The existing styled TOC write/read path remains supported. Update Table and Update Fields refresh unlocked top-level body TOC ranges in place through the engine's generator; one undo restores the imported range. Locked, nested and table-cell ranges remain preserved without regeneration. Preservation is separate from field evaluation. Native Word reopening and refresh, full schema validation and company documents remain untested. Synthetic checks do not raise readiness estimates. No Word-derived files are fixtures.
Settings preservation
A save used to regenerate settings.xml from the model alone, so every setting the model doesn't
hold was dropped without a warning (on 18 real-world Word documents: compatibility settings in 18
of 18; the privacy options are modelled since #1062). Now the writer merges into the
file's original (crates/docx/src/write/settings_merge.rs): the original root and every child
the model doesn't own stay byte for byte; owned children follow the model, in CT_Settings order;
in w:compat only compatibilityMode is replaced. Kept children that point at a relationship
(a mail-merge data source) are dropped. Files WordCraft wrote itself keep nothing extra. Tests:
crates/docx/tests/settings_merge.rs.
Unmodelled-part preservation
Parts the package or the main document relate to that WordCraft neither models nor writes (web
settings, styles-with-effects, add-in task panes, people, the thumbnail, future parts) are
carried verbatim with their relationships and content types. The glossary document is carried the
same way while its building blocks are as read; once they change it is written from the model. Relationship types the writer
produces, and any the document's markup refers to by id, are not (HANDLED_RELS in
crates/docx/src/read/embed.rs), so nothing is written twice. app.xml keeps Template, Manager,
Company, HyperlinkBase and the link/sharing flags; counts (pages, lines, characters, editing
time) and the producing application's version are not carried, and Manager and Company are
dropped when the file asks to remove personal information on save. Not yet: unknown elements and
attributes inside stories and style or list definitions; a regenerated theme. Tests:
crates/docx/tests/passthrough.rs.
Saving legacy Word imports
Word 97–2003 .doc and .dot remain import-only. A user Save without an explicit destination opens the existing Save As flow suggesting DOCX (#2445), including before Close. The original stays unchanged. Cancelling retains the draft, revision and path; a failed write keeps the draft open, and successful DOCX save/reopen retains the edited text. Uppercase extensions follow the same route. Scripts and explicitly supplied legacy destinations still receive an unsupported-format error and never open a dialog. Late Save As answers are rejected before writing if automation has replaced the document.
Original binary fixtures and actual WordApp/Session file I/O cover Save, toolbar dispatch, a Mod+S input event, cancel, failure, retry, close continuation and repeated DOCX saves. The picker service is controlled in these tests; a native Windows picker and broad legacy-document fidelity remain unqualified.
Cached field result formatting
Imported text fields use the first nonempty cached result run's formatting on their paragraph span, including character styles. Empty results retain begin-marker formatting. Synthetic fixtures cover simple and complex fields, default inheritance and repeated saves. Mixed-format results remain represented by one span; field refresh policy is unchanged.
Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-10-18 | trivial | A Word 2003 XML or Flat OPC document, or a web page, saved under a .doc, .docx or .rtf name opens by what it holds, as in Word, instead of going to the Word 97-2003 reader and showing a blank page (#2853) |
| 2026-10-18 | trivial | w:sym: the symbol's w:font is kept apart from the run font (ECMA-376 §17.3.3.30), so changing the text's font (or Clear Formatting) keeps Wingdings/Symbol characters showing, and they are written back as <w:sym w:font=… w:char=…/>; Insert › Symbol in a symbol font does the same (#2036). A private-use character with no symbol font of its own in a non-symbol run is not guessed |
| 2026-10-18 | trivial | Manage Sources imports Word's Sources.xml (b:Sources) into the document's bibliography sources, sharing the reader of a document's own bibliography part (#2640) |
| 2026-10-14 | trivial | RTF export writes sections: \sectd formatting and \sect per section, round-tripping through the reader (gap 19) |
| 2026-10-14 | trivial | ASK and SET fields keep their result but show nothing (#1765) |
| 2026-10-16 | trivial | Strict OOXML line heights: w:line="13.90pt" under lineRule="auto" is read as Word renders it — through twips (13.90 × 20 = 278; the 1.1583 multiple), not as 240ths of a line — so a Word 2024 strict file opens at Word's line spacing and page count (#2329) |
| 2026-10-15 | trivial | RTF import's heading, Title and Code normalisation is main's again (direct size, bold and code font cleared); only the writer resolves old formatting to the style's values (gap 19) |
| 2026-10-15 | trivial | RTF old formatting in heading, Title, Quote and Code paragraphs is written with the style's size and font, not RTF's 12 pt default; \pard inside \oldcprops no longer drops its words (gap 19) |
| 2026-10-15 | trivial | RTF \oldcprops holds only the properties a tracked change altered, as Word for Mac writes it: read on top of the run's formatting (Reject no longer clears its font and size) with \crauth/\crdate inside the group, and written as the differences (gap 19) |
| 2026-10-15 | trivial | RTF reads and writes tracked paragraph marks (\par, \sect) and character/paragraph formatting revisions, list items keeping their list (\oldcprops, \oldpprops, \crauth/\crdate, \prauth/\prdate); section and table changes remain (gap 19) |
| 2026-10-14 | trivial | ODT export writes sections: each new-page section gets its own page layout and master page (the first stays "Standard"), named by its first paragraph or table with the page-numbering restart; a continuous section with columns is a columned text:section (gap 19) |
| 2026-10-18 | trivial | #2445: pending Save As answers keep their originating document identity, so a replacement document is never written to an old picker destination; real Save-button and focused-editor shortcut controls verified |
| 2026-10-16 | trivial | #2445: interactive Save on import-only .doc/.dot paths uses the existing DOCX Save As flow; cancel and failed writes keep the draft, and Close waits for a successful save. Explicit/script saves still report unsupported legacy output |
| 2026-10-16 | trivial | Fonts Word embeds as subsets are found by the font table's name (OBRBWW+Century Schoolbook), not only the family inside the file, so the text set in them uses them instead of a substitute (#2240) |
| 2026-10-17 | trivial | Paste: an HTML <blockquote> (LibreOffice's both-sides indent) pastes as a Normal paragraph with its margins as indents, not the centred italic Quote style; opening .html unchanged (#1270) |
| 2026-10-15 | trivial | wordcraft-cli convert --update-fields explicitly refreshes unlocked fields before conversion; ordinary conversion keeps cached results. Field refresh can change pagination |
| 2026-10-16 | trivial | Cached textual REF results with \h export links to their bookmarks. Code view and existing enclosing hyperlinks remain unchanged; unresolved bookmarks emit no PDF link. |
| 2026-10-16 | trivial | Cached text fields retain result-run formatting instead of field-begin formatting. |
| 2026-10-17 | trivial | Glossary document (building blocks) read into the model, kept verbatim while unchanged and written from the model once edited; split from the w:altChunk/sub-documents row |
| 2026-10-16 | trivial | Legacy w:outline, w:shadow, w:emboss and w:imprint run effects drawn on screen and in PDFs (#2534) |
| 2026-10-15 | trivial | w:doNotTrackMoves read and written (Track moves off: a tracked cut and paste is a deletion and an insertion) |
| 2026-10-16 | trivial | Positional tabs (w:ptab) keep their alignment, base and leader through DOCX saves instead of becoming plain tabs (#2367) |
| 2026-10-16 | trivial | PDF outline destinations point to heading text after leading page breaks, including selected-page exports |
| 2026-10-16 | trivial | w:eastAsianLayout (two lines in one, horizontal in vertical) is read and written; not laid out yet |
| 2026-10-15 | trivial | A group holding Word ink can't be ungrouped (its ink would be lost on save) |
| 2026-10-15 | trivial | Native ink nested in groups and drawing canvases kept on save, in its place, drawn from its fallback picture |
| 2026-10-18 | trivial | DOCX: every VML shape anchored in a header table cell is placed on the page whatever o:allowincell says (user-checked in Word for Mac, #1775) |
| 2026-10-16 | trivial | DOCX: wp:anchor/@layoutInCell="0" and VML o:allowincell="f" are read and written back (layoutInCell="0"), so a header-cell picture watermark is placed on the page (#1775) |
| 2026-10-16 | trivial | Equations from Word files no longer lose content: body-level m:oMathPara, mc:AlternateContent Requires="m", hyperlinks/simple fields/mc:AlternateContent inside an equation, w:sym and Symbol-font text in math (#1273) |
| 2026-10-13 | trivial | Recipient lists: cell text capped at 64 MiB a sheet (shared-string amplification), an XML spreadsheet named .xls explained (#1287) |
| 2026-10-13 | trivial | A cell's explicit top vertical alignment (w:vAlign w:val="top") is kept on read and write and beats a table style that centres or bottom-aligns (#1288) |
| 2026-10-14 | trivial | Row table border exceptions survive DOCX saves |
| 2026-10-12 | trivial | Fields keep w:dirty through DOCX round trips — on buffered fields and range-field begin markers alike — and computing a result (Update Field, Update Table) clears it like Word's F9, so a generator's placeholder TOC still refreshes when Word opens the file until WordCraft replaces the placeholder (#877) |
| 2026-10-14 | trivial | VML freeforms (v:shape with a path, such as rule lines in headers) read as freeforms and kept on save instead of being dropped |
| 2026-10-14 | trivial | DOCX paragraphs inside complex-field instructions carry a hidden paragraph mark; cached result paragraphs remain visible. |
| 2026-10-14 | trivial | DrawingML flowChartOr preserves its circle and crossing spokes, including when grouped or rotated |
| 2026-10-14 | trivial | minorBidi/majorBidi theme fonts name the theme's complex-script font (a:cs); an empty one names nothing, so the inherited font applies, as measured in Word |
| 2026-10-14 | trivial | A missing font the font table classes as roman is drawn in Cambria, as measured in Word (Calibri for the other classes, as before) |
| 2026-10-14 | minor | Legacy DOC DIB helper honours the supplied byte limit for its BMP header/copy, decoder and PNG output, with a bounded BMP fallback. Generated 1-pixel regression only; resource-exhaustion consequences unmeasured, fidelity/readiness estimates unchanged. Related: #1334 and #1554 |
| 2026-10-14 | trivial | Right triangles (rtTriangle) keep their right angle at the bottom left instead of becoming isosceles triangles, and save back as rtTriangle |
| 2026-10-13 | trivial | Section w:vAlign="both" reads as Justified and writes back as both (#1320) |
| 2026-10-13 | trivial | DOCX: the document's watermark is written as a VML text shape in every header, adding a header where pages had none (#1290) |
| 2026-10-13 | trivial | DOCX: Word's VML text watermark in headers is read as the document's watermark (#1290) |
| 2026-10-13 | trivial | Rounded rectangles keep their own corner radius: roundRect adj read, written back and drawn (#1476) |
| 2026-10-13 | trivial | Replace All checks content-control locks before editing; a refused batch leaves the document and undo history unchanged; existing deletion-lock rules reused, with tracked/untracked regression coverage |
| 2026-10-14 | trivial | VML preset shapes (o:spt, _x0000_tNN shapetypes) drawn like the DrawingML preset of the same shape (the nearest shape when WordCraft has none) and kept on save, e.g. Word's right arrow _x0000_t13 (#1377) |
| 2026-10-14 | trivial | RTF import (#1653): Mac OS Roman (code page 10000) for \mac documents, \fcharset77 fonts and \cpg/\ansicpg 10000, and DOS text: \pc (437), \pca (850), the OEM pages through \cpg and \fcharset254 (was read as Windows-1252); a code page number out of range is ignored instead of truncated to another; East Asian double-byte code pages still to do |
| 2026-10-12 | trivial | VML text boxes keep their internal margins (inset), fit to their text (mso-fit-shape-to-text) and vertical alignment (v-text-anchor); hidden drawings (visibility:hidden, wp:docPr/@hidden) are kept but not shown |
| 2026-10-13 | trivial | Remove personal information / Remove date and time settings kept and honoured on save: anonymous annotation authors, no creator or last-modified-by, no annotation dates (#1062) |
| 2026-10-12 | trivial | Direct run-level w:contentPart InkML keeps its relationship, content type and bytes through DOCX edits and repeated saves; nested group/canvas ink and native rendering remain missing |
| 2026-10-13 | trivial | HTML export writes equations as MathML instead of linear text, and HTML import (open and paste) reads <math> back into equations; a copy holding an equation puts that HTML on the system clipboard: MathML in HTML on the clipboard (Word/LibreOffice paste not yet verified; OMML is #1297) (#816) |
| 2026-10-16 | trivial | A copy of one equation alone also goes in the clipboard's MathML formats ("MathML"/"MathML Presentation" on Windows, public.mathml.presentation/public.mathml on macOS), which Word reads first when it pastes math, instead of OMML, for which no clipboard format is published (#1297; the paste into Word is not yet verified) |
| 2026-10-14 | minor | DOCX save merges settings.xml into the file's original: unmodelled settings (compatibility options, rsids, document variables, proofing state…) survive (#1034) |
| 2026-10-14 | minor | DOCX save carries unmodelled package parts verbatim; comment durable ids and UTC dates, localized w:default, latent styles, list nsid/tmpl/multiLevelType and app.xml template/company round-trip; comment reference marks use the document's own style id (#1034) |
| 2026-10-14 | trivial | PDF export: All Caps and Small Caps text extracts in capitals, as displayed, and no longer turns later capitals in uncapitalised text lowercase (#1473) |
| 2026-10-14 | trivial | ODT tracked changes: text:tracked-changes insertions and deletions open as revisions (author, date, inserted and deleted paragraph marks, insertions nested in deletions) and are written back, a missing date as the save time; text:track-changes maps to Track Changes on/off. Pictures, other objects and tables inside a tracked deletion are dropped |
| 2026-10-14 | trivial | RTF reads and writes tracked insertions and deletions with their authors and dates (gap 19); deleted text no longer opens as normal text |
| 2026-10-14 | trivial | ODT import reads sections: a paragraph naming another master page starts a new-page section with that page's size, orientation, margins and columns, and its style:page-number restart; a columned text:section is a continuous section with its columns (#1379) |
| 2026-10-14 | trivial | PDF export writes right-to-left runs' glyphs in visual order, so Chrome, Edge (PDFium) and poppler extract Hebrew and Arabic as whole words (vowelled words keep their letters in order but may extract with spaces inside), and Ctrl+F finds them (#1383). /ReversedChars (for strict file-order readers) covers plain runs and vowelled runs whose marks split into their own glyphs, not runs with ligatures or mixed directions |
| 2026-10-14 | trivial | HTML: <th> bold, cell text-align (own or inherited) reaches the cell's paragraphs; HTML, Markdown and plain text open on the region's paper, in the app and wordcraft-cli (#1381) |
| 2026-10-13 | trivial | PDF export draws horizontal and vertical lines inside groups (#1474) |
| 2026-10-14 | trivial | RTF import keeps sections: page size and orientation, margins, columns, break type, page numbering and each section's headers and footers (#1378) |
| 2026-10-14 | trivial | .doc tables: cell shading read from every SHD record (sprmTDefTableShd/ShdRaw and their 2nd/3rd ranges for cells 22-62; Word 97 Shd80 clear fills now show their background colour; exact colours win over the palette fallback), the row-end mark no longer becomes an extra empty column, and a table no longer inherits the previous table's grid, borders, alignment or indent (#1380). Horizontally merged rows (a row of fewer, wider cells) come with #1123 |
| 2026-10-12 | trivial | Password-protected DOCX row corrected: agile-encrypted .docx opens and saves since #281 (was listed as unsupported); legacy encrypted .doc (RC4/XOR) split out, still refused |
| 2026-10-12 | trivial | Restrict Editing: forms mode (w:edit="forms") fills content controls — edits inside fillable controls run and everything else rolls back; review.restrict reports formFields; Find Next Region covers form fields; a save/reopen keeps w:edit="forms", the filled value and the password guard; under active protection, content-control lock-flag property changes are refused, as Word blocks property edits until Stop Protection (#1077) |
| 2026-10-12 | trivial | Paste keeps the formatting of HTML and RTF on the system clipboard; Paste Special lists them and pictures (#687, #833) |
| 2026-10-12 | trivial | ODT page size, orientation and margins read back from styles.xml on open, so they survive a save and reopen (#810) |
| 2026-10-12 | trivial | All 13 table style conditional formatting parts round-trip with alignment (#714) |
| 2026-10-13 | trivial | Print to PDF and PNG export replace an existing file only after the new one is fully written, as document saves do (#1355) |
| 2026-10-12 | minor | RTF reads 8-bit text in the document's or the font's code page (\ansicpg, \fcharset, \cpg) instead of always Windows-1252; a .doc/.docx/.rtf whose content is another of these formats opens by signature. Found on a private corpus of real Russian documents: 38 of 44 .doc files were RTF, all now open with their Cyrillic text |
| 2026-10-12 | trivial | w:sym and symbol-font runs keep their stored characters on save; TXT export and copy write their Unicode equivalents (#776) |
| 2026-10-12 | trivial | Insert › Picture, drag-and-drop and paste take Windows Metafiles and Enhanced Metafiles (#802); new row for picture formats |
| 2026-10-11 | trivial | Independent complex-script size, bold and italic inherit correctly through defaults and styles; explicit equal values survive; six synthetic multi-generation round-trip tests, including tracked formatting, added |
| 2026-10-11 | minor | Opening files from the system: file associations and the default-app button (#295) |
| 2026-10-12 | trivial | DOCX: a table of contents' entry links (Hyperlink-styled runs in hyperlinks inside a TOC field) import without the Hyperlink style, as Word draws them; measured in Word for Windows |
| 2026-10-12 | trivial | .doc: custom tab stops read from styles and paragraphs (sprmPChgTabsPapx, sprmPChgTabs, with the alignment and leader of each stop; sprmPChgTabs' operand size fixed), and a section mark ends its section's last paragraph instead of becoming a page break, so the sections (continuous breaks, footers) are kept. Found by comparing EPA templates with Word for Windows; two of them now paginate like Word |
| 2026-10-12 | trivial | DOCX: a style or paragraph w:numPr with a level and no list takes the list of its style chain (Heading 3 based on a numbered Heading 2 numbers 6.4.5, not 6.15), found in NIH's monitoring-plan template against Word for Windows |
| 2026-10-12 | trivial | DOCX: w:hideMark on table cells read and written |
| 2026-10-12 | trivial | DOCX text frames (w:framePr other than drop caps) are read and written back; they were dropped on save |
| 2026-10-11 | trivial | .doc: inline and floating pictures (BLIP store, PNG/JPEG/DIB/TIFF/EMF/WMF), basic shapes, groups and text boxes read from OfficeArt drawings (#551); fidelity ~55% → ~60% |
| 2026-10-12 | trivial | Word 2003 XML Document (WordprocessingML 2003) written (#1081): Export, format: "wordml2003", CLI --format; not yet checked in Word |
| 2026-10-11 | trivial | Word XML Document (Flat OPC) read and written, Word 2003 XML read (#548) |
| 2026-10-11 | trivial | #534: DOCX indents in characters read and written; .doc first-line indents (sprmPDxaLeft1, its two-byte operand, and sprmPDxaLeft1_80), indents in characters (sprmPDxc*) and exact/at-least line spacing (LSPD) read correctly |
| 2026-10-11 | trivial | List and page number formats: all ST_NumberFormat values round-trip; Chinese, Japanese and Korean counting, legal and digit formats, stems/branches, kana, hangul, enclosed and full-width numbers drawn; unknown values are decimal; .doc number format codes mapped (#534) |
| 2026-10-11 | trivial | SmartArt made in WordCraft written as diagram parts (data, layout, quick style, colours, drawing) and read back editable (#497) |
| 2026-10-11 | trivial | Attached template (w:attachedTemplate, external relationship of the settings part) and w:linkStyles read and written in schema order; opening never reads the template (#377) |
| 2026-10-11 | trivial | A bookmark (or comment range) ending after the document's last table keeps its end, in the table's last cell (#464); a field whose cached result is a picture (INCLUDEPICTURE) is kept as a range field around the picture, so the picture survives save; the linked file is never read (#465) |
| 2026-10-11 | trivial | Print Markup: comments exported in PDFs, prints, page images and HTML/Markdown/text copies (#589) |
| 2026-10-11 | trivial | Files with an extension WordCraft doesn't know open by content: text (.m3u/.m3u8 playlists, logs…) through the encoding-detecting text import, misnamed Word/RTF files by signature, binary files refused with an error (#569) |
| 2026-10-11 | trivial | Restrict Editing: Word's legacy password hash (w:cryptAlgorithmSid, w:hash, w:salt, w:cryptSpinCount) verified per ECMA-376 Part 4 §14.8.1; unsupported hash forms refused (#412) |
| 2026-10-11 | trivial | Restrict Editing: w:documentProtection (edit, formatting, password hash), w:locked styles and w:permStart/w:permEnd exceptions read and written (#412) |
| 2026-10-13 | trivial | Word 365 cover page (#743): a position wrapped in mc:AlternateContent (wp14:pctPosVOffset) takes the Fallback's offset; a freeform path drawn back to its start is closed |
| 2026-10-12 | trivial | Word 365 cover page (#743): text-box internal margins and a:spAutoFit read and written; freeforms inside groups keep their custom geometry |
| 2026-10-11 | trivial | DOCX diagrams (#611): shapes and connectors styled by the theme (wps:style) show their outline and fill; preset/system colours; VML lines and rectangles; lines run top-left to bottom-right as in DrawingML |
| 2026-10-14 | trivial | Open DrawingML freeform paths draw a:tailEnd arrowheads from their final segment |
| 2026-10-11 | trivial | WordArt text effects (w14: run properties), text warps (a:prstTxWarp) and connectors with glued ends read and written (#416) |
| 2026-10-11 | trivial | Content controls read and written with their properties and nesting (#381) |
| 2026-10-11 | trivial | Charts made in WordCraft written as chart parts and read back as editable (#409) |
| 2026-10-11 | trivial | Hyphenation zone, words in capitals and consecutive-hyphen limit read and written (#407) |
| 2026-10-11 | trivial | Alt text, titles and the decorative mark for every drawing, table captions and descriptions round-trip (#386) |
| 2026-10-11 | trivial | Document grid (w:docGrid) and paragraph w:snapToGrid read and written (#391) |
| 2026-10-11 | trivial | Move tracking (w:moveFrom/w:moveTo and their ranges) read and written (#400) |
| 2026-10-11 | trivial | DOCX text-box bodyPr vert/anchor and linked text boxes (wps:linkedTxbx) read and written (#369) |
| 2026-10-10 | trivial | Right-to-left tables (w:bidiVisual) read and written (#362) |
| 2026-10-10 | trivial | Drawing canvas (wpc:wpc) read and written (#344); group shapes row brought up to date (#267) |
| 2026-10-11 | trivial | Spreadsheets (.xlsx/.xlsm/.ods) read as mail-merge recipient lists (#334) |
| 2026-10-12 | trivial | Font table (word/fontTable.xml), its relationships and embedded fonts (word/fonts/*) written back unchanged (#770) |
| 2026-10-12 | minor | Preserve opaque custom XML stores and related properties; bindings and control support remain separate |
| 2026-10-11 | trivial | Native saves protect existing temporary-name entries and originals on pre-replacement failure; synthetic macOS evidence, explicit durability/platform limits; readiness unchanged |
| 2026-10-11 | minor | Preserve imported TOC ranges independently of paragraph styles, with explicit evaluation limits |
| 2026-10-11 | trivial | Import/export fixes (#433–#440): TXT opens through the encoding-sniffing reader (Windows-1252, unmarked UTF-16) and saves list labels; RTF \uN in document info, bookmark names and field instructions; ODT common paragraph styles' text formatting and table-cell background colours; HTML borderless tables export without cell borders and white-space:pre keeps edge spaces; LaTeX brace groups scope \centering/\raggedleft/\raggedright |
| 2026-10-11 | trivial | .doc import fixes per [MS-DOC]: piece Prm0/Prm1 decoding (#458), section-break kinds (#459), explicit orientation (#460), negative sprmPIlfo list references (#461), zero-based istdBase style inheritance (#462), font-table entries with alternate names (#463) |
| 2026-10-11 | minor | Native drawing-level ink and its picture fallback survive DOCX edits and repeated saves; rendering and editing InkML remain missing |
| 2026-10-12 | minor | Native JSON now preserves media, opaque parts and embedded source markup/relationships for lossless recovery of the document model. Older JSON still opens; recovery limits. |
| 2026-10-12 | trivial | AllowSpaceOfSameStyleInTable read and written; modes 11/12/14 versus 15 checked with original synthetic documents |
| 2026-10-11 | trivial | Formatting revisions (w:rPrChange, w:pPrChange, table, row, cell, section and numbering changes) read and written (#41) |
| 2026-10-11 | trivial | Omitted w:pPrDefault stays omitted across saves; explicit empty/default properties and later spacing edits remain writable. Synthetic preservation checks only; readiness estimates unchanged |
| 2026-10-10 | trivial | OLE objects turned or flipped in WordCraft keep the turn on save (VML rotation/flip, DrawingML a:xfrm with the rotated effect extent); test counts refreshed |
| 2026-10-11 | trivial | Section text direction read and written (#491) |
| 2026-10-10 | trivial | Charts, SmartArt and OLE objects written back on save with their parts (#319); chart/SmartArt reading (#292) recorded |
| 2026-10-10 | trivial | Footnote and endnote separators and continuation notices read and written (#352) |
| 2026-10-11 | trivial | Enclosed characters (EQ \o\ac) read, drawn and written (#297) |
| 2026-10-11 | trivial | Ruby (w:ruby) read and written (#288) |
| 2026-10-10 | trivial | wrapText on square, tight and through wraps read and written (#354) |
| 2026-10-10 | trivial | Wrap polygons for Tight and Through wrapping read and written (#354) |
| 2026-10-10 | trivial | DrawingML rotation and flips read and written (#332) |
| 2026-10-11 | trivial | Footnote and endnote options round-trip for the document and per section (#385) |
| 2026-10-10 | trivial | Equation breaks and m:mathPr wrapping options round-trip (#326) |
| 2026-10-10 | trivial | Custom table styles round-trip (#256 merged) |
| 2026-10-10 | major | First version: Word's full format list, read/write status, DOCX element coverage from the source, fidelity and hours |
| 2026-10-10 | trivial | Freeform shapes and ink as DrawingML custom geometry, read and written (#307) |
| 2026-10-12 | trivial | Charts of types WordCraft doesn't draw (radar, stock…) show a placeholder with their title and type (#998) |
| 2026-10-13 | trivial | DOCX styles without any w:qFormat (LibreOffice) get Word's usual Styles gallery: matching built-in styles are marked quick and missing quick built-ins added (#1238) |
| 2026-10-12 | trivial | Recipient lists: legacy .xls workbooks and GBK/Big5/Shift_JIS/UTF-16 CSV/TXT (#778); plain-text import detects legacy encodings too |
| 2026-10-15 | trivial | DOCX: runs mixing right-to-left and left-to-right text are split by direction on save; right-to-left pieces get w:rtl and w:lang w:bidi, so Word shows the order WordCraft shows (#1543) |
| 2026-10-16 | trivial | DOCX: OpenType ligatures (w14:ligatures) read and written in runs, styles and document defaults |
| 2026-10-15 | trivial | Legacy form fields are drawn: check boxes as Word's squares (checked with an X), drop-downs with their selected entry |
| 2026-10-17 | trivial | A folder that refuses a temporary file leaves an existing document untouched (the error says to use Save As or allow access to the folder); a new file is created directly (#2138) |
| 2026-10-18 | trivial | Word 2007 WordArt (VML v:textpath in the body or a header logo) shown as a text box with its text, font, colour and warp, not dropped (#2875) |