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:523 — frame_anchor_type returns
AnchorType::as_char unconditionally, with a standing
// TODO 'wp:anchor' is floating, not as_char.
ooxml_text_document.cpp:529, :533 — frame_x / frame_y return
std::nullopt.
ooxml_text_document.cpp:554 — frame_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
- 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.
- 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.
- Default a text-document table cell to
vertical-align:top.
- 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.
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 theircell. ODR: no borders at all, and the numbers (
33,25,53,75) sitvertically centred against the bullet list beside them instead of at the top of
the cell.
Not a
.docThe reporter's mail does not name the format, and the guess was
.doc. It isnot:
oldms/textis scoped to visible text plus direct character formattingand explicitly has no tables, frames or images
(
src/odr/internal/oldms/text/AGENTS.md). A.docwould have rendered neitherthe logo nor the table structure, and both are present. The file therefore went
through
ooxml/text(.docx) — orodf, but the reporter is a Word user andthe
odfpaths below are the ones that already work.Cause 1 — a floating drawing renders inline
ooxml/textreports every drawing as anchoredas_charand supplies noposition and no graphic style:
ooxml_text_document.cpp:523—frame_anchor_typereturnsAnchorType::as_charunconditionally, with a standing// TODO 'wp:anchor' is floating, not as_char.ooxml_text_document.cpp:529,:533—frame_x/frame_yreturnstd::nullopt.ooxml_text_document.cpp:554—frame_stylereturns{}, so notext_wrapand nohorizontal_position.The parser does distinguish the two cases (
ooxml_text_parser.cpp:223-224maps both
wp:anchorandwp:inlineto a group), and the html rendereralready implements the whole anchor/wrap matrix — float, absolute placement,
shape-outside(html/document_style.cpp:476-530). It is only the docxadapter that starves it.
odffeeds the same renderer properly(
odf_document.cpp:815readstext:anchor-type,odf_style.cpp:541readsstyle:wrap), which is why this is docx-only.Effect: a
wp:anchordrawing is laid out at the character position of itsanchor 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:tcBordersresolve_table_cell_style_reads borders fromw:tcPr/w:tcBordersonly(
ooxml_text_style.cpp:145-161). Nothing anywhere readsw:tblPr/w:tblBorders, andresolve_table_style_(
ooxml_text_style.cpp:109) reads nothing butw:tblW.The public
TableStylehas no border field at all (src/odr/style.hpp:165),so even the existing
w:tblStylecascade (ooxml_text_style.cpp:353) hasnowhere 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.
odfisunaffected: ODF stores borders per cell style (
odf_style.cpp:488-504).ooxml/text/README.mdcurrently lists "cell vertical alignment, borders" asdone 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 defaultvertical-alignfor document tables —frontend.cpp:86sets one for.odr-sheetcells only. Withw:vAlignabsent the property is unset, so thebrowser default
middleapplies, whereas Word (and ODF) default a cell totop. That matches
33,25sitting beside the second line of its neighbourcell instead of the first.
Suggested work
wp:anchoras floating inooxml/text: realAnchorType,frame_x/frame_yfromwp:positionH/wp:positionV, and aGraphicStylewithtext_wrapfromwp:wrapSquare/wp:wrapTopAndBottom/wp:wrapNone.The renderer needs no change.
TableStyle, readw:tblPr/w:tblBorders, and let thew:tblStylecascade carry them; have a cell fall back to the table'sborders where
w:tcBordersis silent.w:tblStylePr(banding, first row)stays out of scope here — it is already tracked in the README.
vertical-align:top.ooxml/text/README.mdonce 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.