Skip to content

docx: a floating drawing renders inline, and a table's borders are dropped unless they sit on the cell #803

Description

@andiwand

A user reported two rendering defects in the Android reader, with before/after
screenshots of the same document rendered by MS Word and by OpenDocumentReader
(a German business letterhead + invoice). No source document was supplied, so
the diagnosis below is read off the code, not off a reproduction.

What the screenshots show

1. Letterhead. Word: a name and a company line as two stacked lines, with a
partner logo placed below them. ODR: the logo is rendered between the two
lines and at a different horizontal position, pushing the second line below it.

2. Invoice table. Word: a bordered two-column table (Anz. /
Bezeichnung), horizontal rules between rows, numbers top-aligned in their
cell. ODR: no borders at all, and the numbers (33,25, 53,75) sit
vertically centred against the bullet list beside them instead of at the top of
the cell.

Not a .doc

The reporter's mail does not name the format, and the guess was .doc. It is
not: oldms/text is scoped to visible text plus direct character formatting
and explicitly has no tables, frames or images
(src/odr/internal/oldms/text/AGENTS.md). A .doc would have rendered neither
the logo nor the table structure, and both are present. The file therefore went
through ooxml/text (.docx) — or odf, but the reporter is a Word user and
the odf paths below are the ones that already work.

Cause 1 — a floating drawing renders inline

ooxml/text reports every drawing as anchored as_char and supplies no
position and no graphic style:

  • ooxml_text_document.cpp:523frame_anchor_type returns
    AnchorType::as_char unconditionally, with a standing
    // TODO 'wp:anchor' is floating, not as_char.
  • ooxml_text_document.cpp:529, :533frame_x / frame_y return
    std::nullopt.
  • ooxml_text_document.cpp:554frame_style returns {}, so no
    text_wrap and no horizontal_position.

The parser does distinguish the two cases (ooxml_text_parser.cpp:223-224
maps both wp:anchor and wp:inline to a group), and the html renderer
already implements the whole anchor/wrap matrix — float, absolute placement,
shape-outside (html/document_style.cpp:476-530). It is only the docx
adapter that starves it. odf feeds the same renderer properly
(odf_document.cpp:815 reads text:anchor-type, odf_style.cpp:541 reads
style:wrap), which is why this is docx-only.

Effect: a wp:anchor drawing is laid out at the character position of its
anchor rather than at its own offset — exactly the "logo lands between the two
text lines" in the screenshot.

Cause 2 — table borders only come from w:tcBorders

resolve_table_cell_style_ reads borders from w:tcPr/w:tcBorders only
(ooxml_text_style.cpp:145-161). Nothing anywhere reads
w:tblPr/w:tblBorders, and resolve_table_style_
(ooxml_text_style.cpp:109) reads nothing but w:tblW.

The public TableStyle has no border field at all (src/odr/style.hpp:165),
so even the existing w:tblStyle cascade (ooxml_text_style.cpp:353) has
nowhere to put a table style's borders — it carries paragraph and text
properties down and drops the rest.

Word writes an invoice table's rules at table level, or in a referenced table
style, far more often than per cell. Those tables render borderless. odf is
unaffected: ODF stores borders per cell style (odf_style.cpp:488-504).

ooxml/text/README.md currently lists "cell vertical alignment, borders" as
done under tables, which reads as more coverage than exists.

Cause 3 (likely, same screenshot) — cell vertical align defaults to middle

A text-document cell emits a <td> carrying only its own style
(html/document_element.cpp:531), and no stylesheet sets a default
vertical-align for document tables — frontend.cpp:86 sets one for
.odr-sheet cells only. With w:vAlign absent the property is unset, so the
browser default middle applies, whereas Word (and ODF) default a cell to
top. That matches 33,25 sitting beside the second line of its neighbour
cell instead of the first.

Suggested work

  1. Read wp:anchor as floating in ooxml/text: real AnchorType, frame_x /
    frame_y from wp:positionH / wp:positionV, and a GraphicStyle with
    text_wrap from wp:wrapSquare / wp:wrapTopAndBottom / wp:wrapNone.
    The renderer needs no change.
  2. Add borders to TableStyle, read w:tblPr/w:tblBorders, and let the
    w:tblStyle cascade carry them; have a cell fall back to the table's
    borders where w:tcBorders is silent. w:tblStylePr (banding, first row)
    stays out of scope here — it is already tracked in the README.
  3. Default a text-document table cell to vertical-align:top.
  4. Correct the table row in ooxml/text/README.md once 2 lands.

A reduced fixture per cause would be worth adding — inline docx XML in the test
suite rather than a fixture file, per the module's existing tests.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions