Repository navigation
feat(ooxml,cli): DOCX writer — M3 3.6 - #39
Merged
Merged
Conversation
`mirsam repair report.docx fixed.docx` works, and `repair` stops turning a Word document away as a readable format without a writer. M3 is closed. `word.rs` is WordprocessingML's repair vocabulary — what `rewrite.rs` is for DrawingML and `sheet.rs` for SpreadsheetML — and it is the third vocabulary built on `token.rs` without a Word element name entering it. Every mutation is a scaffold call with a name in it: `w:pPr/w:bidi`, `w:pPr/w:jc`, `w:rPr/w:lang@w:bidi`, `w:rPr/w:rFonts@w:cs`, `w:pPr/w:numPr` and `w:tblPr/w:bidiVisual`, each placed by rank against the schema sequence its parent is, because `CT_PPrBase` and `CT_RPr` are `xsd:sequence` and a correct element in the wrong position is a document Word offers to repair. The writer needs no inherited direction, and that is Word's doing rather than a shortcut. 1.2's hardest correction was that lowering `Start` onto DrawingML's physical `algn` without knowing the paragraph's direction reproduces the defect being repaired — which is why `rewrite::Inherited` exists and why `PptxDocument::apply` re-runs its scanner over every part it touches. `w:jc` is evaluated against the paragraph's own `w:bidi`, so `Start` is written `start` whichever way the paragraph runs and stays the start edge if the direction changes afterwards. The whole inheritance pass is absent from this adapter, not skipped in it. Two refusals, and both are the format's. A physical edge is not something a Word paragraph can state, which is the refusal the conformance suite already records for the reading side; `supports` answers it for the writing side, so `SetAlignment(Left)` is listed in `repairs.skipped` rather than lowered onto whichever relative edge happens to land left today. And a typed bullet becomes a `w:numPr` pointing into the numbering part, so it can only join a list the document already defines — `package.rs` replaces the entries a package holds, and a numbering part that is not there is a part, a content-type override and a relationship, none of which is an edit to the paragraph the finding named. `bullet_list` prefers a definition whose first level draws the marker the author typed, falls back to any bulleted one, and refuses a `w:num` that overrides its abstract at level zero. Three smaller decisions, each a defect if taken the other way. `<w:bidi/>` is on, so a left-to-right repair writes `w:val="0"` and never an empty element: creating one to mean off would turn the repair into the defect it was sent to fix. Filling `@w:cs` removes `@w:cstheme` beside it, because the theme reference is what Word renders and `@w:cs` the value it caches for consumers that do not implement themes — writing into the cache and leaving the reference standing is a repair that reports success and changes nothing a reader sees. And a `w:t` whose text this repair changed is marked `xml:space="preserve"` where the result has an edge of whitespace, because stripping a typed bullet leaves the space behind it and Word would otherwise collapse a space nobody asked to delete; only the runs actually rewritten are marked, so the rest of the paragraph still comes out byte for byte. `token.rs` gained the ability to step over a caller-named set of elements, and Word is why. A `w:txbxContent` nests whole paragraphs inside a run, and each is a unit with its own ordinal, its own text and its own repairs — offsets the domain computed for the outer paragraph index the outer paragraph's runs alone. An `mc:Fallback` is stepped over for the reader's reason: it spells out the same paragraphs as the `mc:Choice` beside it, and a rewriter that counted both would put paragraph 2's repair on the fallback's copy of paragraph 1, a report and a document that had come apart while both still looked right. It also gained `remove_attribute`, the counterpart `set_attribute` needed once a repair had to stop a second attribute contradicting the one being written. The corpus is where it shows, which is what the recorded refusal was for. Both `.docx` reports lost their `refused` line and gained a repair report; `quarterly-review.docx` now carries a `changed_parts` diff naming exactly the tags above, and its `after` audit is empty — every fixable finding in a real Word document cleared. `make validate-fixtures` accepts the repaired copy against the ECMA-376 transitional schemas, and a second `repair` of that output stages nothing and reproduces it byte for byte. The CLI case that proved the refusal moved to `.html`, the format that still gives one, rather than being deleted with the adapter that outgrew it. Not run: "Word opens the repaired document without offering to repair it", the M1 application check in its Word form, cannot run in CI. Schema validity is the strongest claim a machine can make here and is what the acceptance rests on. It is not the same claim.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mirsam repair report.docx fixed.docxworks, andrepairstops turning a Word document away as a readable format without a writer — PLAN §3.6, which closes M3. The third OOXML vocabulary on one token-rewrite scaffold, and the first writer that turned out simpler than the one before it.What lands where
word.rsis WordprocessingML's repair vocabulary — whatrewrite.rsis for DrawingML andsheet.rsfor SpreadsheetML. Not one Word element name enteredtoken.rsto carry it.FixSetDirectionw:pPr/w:bidi, andw:tblPr/w:bidiVisualon a tableSetAlignmentw:pPr/w:jcSetLanguagew:rPr/w:lang@w:bidiSetComplexFontw:rPr/w:rFonts@w:csConvertLiteralBulletw:pPr/w:numPrEach is placed by rank against the schema sequence its parent is.
CT_PPrBaseandCT_RPrarexsd:sequence, and a correct element in the wrong position is a document Word offers to repair.The inheritance pass is absent, not skipped
§1.2's hardest correction was that lowering
Startonto DrawingML's physicalalgnwithout knowing the paragraph's direction reproduces the defect being repaired. That is whyrewrite::Inheritedexists, and whyPptxDocument::applyre-runs its scanner over every part it touches.w:jcis evaluated against the paragraph's ownw:bidi(MS-OE376 Part 4 §2.3.1.13, note b), soStartis writtenstartwhichever way the paragraph runs, and stays the start edge if the direction changes afterwards. There is nothing here to resolve. This adapter has noInheritedtype at all.Two refusals, both the format's
supportsanswers it for the writing side, soSetAlignment(Left)appears inrepairs.skippedrather than being lowered onto whichever relative edge happens to land left today. No Word unit ever produces one, so this will not be seen in practice; it is stated so the writer cannot start inventing it.w:numPrpointing into the numbering part, so it can only join a list the document already defines.package.rsreplaces the entries a package holds, and a numbering part that is not there is a part, a content-type override and a relationship — none of which is an edit to the paragraph the finding named.bullet_listprefers a definition whose first level draws the marker the author typed, falls back to any bulleted one, and refuses aw:numthat overrides its abstract at level zero.Three smaller decisions, each a defect the other way
<w:bidi/>is on. A left-to-right repair writesw:val="0"and never an empty element. Creating one to mean off would turn the repair into the defect it was sent to fix.@w:csremoves@w:cstheme. The theme reference is what Word renders and@w:csthe value it caches for consumers that do not implement themes. Writing into the cache and leaving the reference standing is a repair that reports success and changes nothing a reader sees.xml:space="preserve"on the runs this repair rewrote. Stripping a typed•leaves the space behind it, and Word collapses aw:t's edge whitespace unless the run says otherwise — a space nobody asked to delete. Only runs whose text actually changed are marked, so the rest of the paragraph still comes out byte for byte.What is not this paragraph
token.rsgained the ability to step over a caller-named set of elements, and Word is why.A
w:txbxContentnests whole paragraphs inside a run, and each is a unit with its own ordinal, its own text and its own repairs; an offset the domain computed for the outer paragraph indexes the outer paragraph's runs alone. Anmc:Fallbackis stepped over for the reader's reason — it spells out the same paragraphs as themc:Choicebeside it, and a rewriter that counted both would put paragraph 2's repair on the fallback's copy of paragraph 1: a report and a document that had come apart while both still looked right.It also gained
remove_attribute, the counterpartset_attributeneeded once a repair had to stop a second attribute contradicting the one being written.The corpus, which is what the recorded refusal was for
Both
.docxreports lost theirrefusedline and gained a repair report.quarterly-review.docxnow carries achanged_partsdiff naming exactly the tags above, and itsafteraudit is empty — every fixable finding in a real Word document cleared.make validate-fixturesaccepts the repaired copy against the ECMA-376 transitional schemas, and a secondrepairof that output stages nothing and reproduces it byte for byte.The CLI case that proved the refusal moved to
.html, the format that still gives one, rather than being deleted with the adapter that outgrew it.Checks
make verifyclean — version, fmt, clippy-D warnings, tests, docs, release build.crates/mirsam-ooxml/tests/word.rsadds 38 cases: the rewriter asserted on the whole part, as §1.2's are, plus theDocumentWriterport over a real package.make validate-fixturespasses on all nine corpus packages.Not run: "Word opens the repaired document without offering to repair it" is the M1 application check in its Word form, and it cannot run in CI. Schema validity is the strongest claim a machine can make here and is what the acceptance rests on. It is not the same claim.