Files
wordcraft/crates/docx/src
Hamza Naveed c2b97f1180 DOCX: read every VML shape in a w:pict, and a v:group's members (#1662)
* DOCX: read every VML shape in a `w:pict`, and a `v:group`'s members (#1643)

`read_vml` took `pict.els().find(..)`, the first `v:*` child that is not a
`v:shapetype`, and read only that one. So a `w:pict` holding several shapes
kept the first and dropped the rest, and a `v:group` was read as a single
shape -- or, with no text box in it, as nothing at all -- losing every member.
Both are gone after a save, since saving writes the model back as DrawingML.

Read all of them instead:

- `read_vml_all` walks every `v:*` child of a `w:pict` and emits one object
  each; `read_vml` keeps returning the first for the two places that show a
  single drawing (an OLE object's picture, a `mc:Fallback` preview).
- a `v:group` becomes an `InlineObject::Group`, its members mapped from the
  space `coordsize`/`coordorigin` give it onto the group's own box, nested
  groups flattened into it -- what `group_members` already does for a
  DrawingML `wpg:wgp`, so the model, the renderer and the writer need no
  change. Nesting is bounded at `MAX_VML_GROUP_DEPTH`, members at
  `MAX_GROUP_CHILDREN`.
- `read_vml_shape` is the old per-shape body, taking the group's scale so a
  member's `width`/`height` (and a `v:line`'s `from`/`to`) come through in
  the group's coordinates.

`boxes()` in the text-box tests now asks for `text_boxes()` rather than
`text_box()`, which only reports an object that is itself a box and so could
not see one inside a group; its assertions are unchanged.

* DOCX: cover a `v:group` holding several text boxes (#1643)

@KNUFFIXX reported the same loss from the other end: an inline `v:group` with
two `_x0000_t202` boxes side by side came through a convert as box A only, in
both a tall-B and a too-short-B variant, against Word reading both.

A text box is the one member a group used to keep -- `vml_textbox` recurses to
the first box that has content -- so this case looked half-working rather than
broken, and it is worth pinning separately from the shape cases. On `main` the
group collapses to a single `Shape { kind: TextBox, w: 400, h: 100 }` wearing
the group's own size and holding box A's story; here both boxes read, keep
their own stories and positions, and survive a save.

Built in code from the reported fixture rather than committing the .docx.

* DOCX: cover the shapetype lookup for a group's member (#1643)

Word writes each `v:shapetype` once, at its first use, as a child of the
`w:pict` -- so a group's member refers to one that is its parent's sibling,
not its own. Reading all shapes means `read_vml_shape` is now called for a
member as well as for a top-level shape, and both need the picture to find it,
which is why `pict` is threaded through the group walk.

Two tests: a member with no geometry and no colours of its own, taking its
diamond from the shapetype's `o:spt` and its fill from its `fillcolor`, and the
same for a shape outside a group, which is the case the lookup was written for.
2026-10-18 04:23:34 -04:00
..