Render shapes held in mc:AlternateContent - #20
Merged
Conversation
Word writes a shape as a compatibility block: the modern DrawingML in mc:Choice and a VML fallback beside it. The run parser did not know mc:AlternateContent, so the whole block fell into the unknown element branch and was kept as raw XML. It survived a write but layout never saw it, so nothing was drawn and no space was reserved. The parser now reads the DrawingML out of mc:Choice as well as keeping the block verbatim. The parsed form is held in a separate alt_drawings field that is never serialised, so the raw copy stays the single source for output and the mc:Fallback is not lost. A test asserts the block comes back exactly once and that the fallback survives. A wps:wsp is modelled as a preset geometry, an optional solid fill and the paragraphs of its text box. Two details are easy to get wrong: - The fill and the outline are both written as a:srgbClr, and the outline sits inside a:ln, so the fill is read from a captured spPr with anything at or below an a:ln skipped. - A picture also carries a pic:spPr, so the presence of shape properties does not make a drawing a shape. An embed id is what makes it a picture and that takes precedence. Without this the anchored image in the file from #3 stopped rendering. Rendering covers what the layout output can already express. A rect with a fill becomes a FilledRect and a line becomes a Line. A shape with a:noFill draws no body, which is correct rather than a gap: Word uses unfilled rectangles as plain text boxes, and two of the five shapes in the #3 file are exactly that. An unrecognised preset draws no body but still draws its text. Text boxes are laid out in the engine, because breaking them into lines needs the font manager. The paginator renders them at the left margin and translates them onto the shape, which keeps justification and indent handling in one place. Closes #11.
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.
Closes #11. With this, every symptom reported in #3 is addressed.
The bug
Word writes a shape as a compatibility block, the modern DrawingML in
mc:Choiceand a VML fallback beside it:The run parser did not know
mc:AlternateContent, so the block fell into the unknown-element branch and was kept as raw XML. It survived a write, but layout never saw it, so nothing was drawn and no space was reserved.Keeping fidelity while making it visible
The block is still captured verbatim. The DrawingML is read out of it separately into an
alt_drawingsfield that is never serialised, so the raw copy remains the single source for output and themc:Fallbackis not lost.That is the main risk in this change, so there is a test asserting the block comes back exactly once and the fallback survives. Checked against the real file from #3 as well:
mc:AlternateContent5 in and 5 out,w:drawing6 and 6,w:pict5 and 5,wps:wsp5 and 5,a:blip1 and 1.Two things that are easy to get wrong
Fill against outline. Both are written as
a:srgbClr. The outline sits insidea:ln, so the fill is read from a capturedspPrwith anything at or below ana:lnskipped. Without that, a shape picks up its border colour as its fill.A picture also has an spPr.
pic:piccarriespic:spPr, so parsed shape properties do not make a drawing a shape. An embed id is what makes it a picture and that takes precedence. I caught this because the anchored image from #3 stopped rendering once shapes were wired up.What renders
Only what the layout output can already express, so no new primitives.
rectwith a fill becomes aFilledRectlineandstraightConnector1become aLinea:noFilldraws no body. That is correct rather than a gap. Word uses unfilled rectangles as plain text boxes, and two of the five shapes in the Lost: images, drawings (shapes), list numbered order, list hierarchy indent #3 file are exactly thatText boxes are laid out in the engine, because breaking them into lines needs the font manager. The paginator renders them at the left margin and translates them onto the shape, which keeps justification and indent handling in one place rather than duplicating it.
Result on the file from #3
All five shapes now appear: the two blue boxes with their labels inside them, and the connector. The anchored image still renders in the right place alongside them, and the list fixes from #16 are unaffected.
Checks
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warningsand the full suite all pass. 354 tests, up from 347.python3 scripts/hash_harness.py --checkpasses with 28 entries matching. As with #19, worth being explicit about why: none of the harness fixtures contains a shape, so the baselines are genuinely untouched. This does change output for any document that has one.One thing to decide
This adds a public field to
CT_R, a public field toCT_Anchor, and two new public types inrdocx-layout.AnchoredDrawing::embed_idalso becomesAnchoredDrawing::content. So it is a breaking change and wants a 0.4.0 rather than a 0.3.x.