fix(docx): paint a table the way it was styled - #532
Open
DemchaAV wants to merge 2 commits into
Open
Conversation
DocumentTableStyle carries a fill and a stroke, and neither reached the file: a zebra body, a header band and a ruled grid all exported on Word's defaults. The fill maps to w:shd and the stroke to w:tcBorders, and the cascade that already resolved a cell's text style now resolves every field on its own — so a table-wide rule survives a row that only overrides the fill. A merged cell is painted on every position it covers: a w:vMerge continuation draws its own shading, and leaving it unpainted would stripe the region. A stroke of no width is this codebase's way of saying "no border", and the fixed-layout handler reads the same predicate as draw-nothing. Writing nothing would leave the cell on the grid Word puts on a table by default, so a deliberately borderless design — MintEditorial uses exactly this — would export ruled. The cell now says it has none. A table that says nothing about borders still keeps Word's default: the look belongs to Word where the document did not claim it. What a fill loses is its opacity. w:shd is opaque, and blending it needs a background this backend does not resolve. The Word companion example styles its table, so the feature ships with a render behind it. Both of its previews move — the DOCX for the new markup, the PDF because the fixed-layout backend paints a style it was never given before.
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.
Why
DocumentTableStylecarries afillColorand astroke, andwriteTableread neither. Azebra body, a header band and a ruled grid all exported on Word's defaults — which is to say
with no fill, and with whatever borders POI puts on a table it creates. Word owns both
(
w:shd,w:tcBorders), so nothing here is an approximation of a thing Word cannot do.What changed
w:shd, stroke tow:tcBorders— four edges,w:szin eighths of a pointrounded up to at least one so a hairline the author asked for stays a line.
resolveCellTextStylebecameresolveCellValue(node, placement, field), so the table → column → row → cell order nowapplies to each property on its own: a table-wide rule survives a row that only overrides the
fill. That matches how
TableLayoutSupport.buildStylesGridmerges for the fixed-layout path.w:vMergecontinuation draws itsown shading, so leaving it unpainted would stripe the region.
DocumentStroke.of(colour, 0)is this codebase's "noborder" —
PdfTableRowFragmentRenderHandlerreads the same predicate as draw-nothing, andthe shipped
MintEditorialCV preset uses it. Writing nothing left the cell on the grid Wordputs on a table by default, so a deliberately borderless design exported ruled. The cell now
emits
STBorder.NIL. A table that says nothing about borders still keeps Word's default:the look belongs to Word where the document did not claim it.
What a fill loses is its opacity.
w:shdis opaque, and blending it needs a background thisbackend does not resolve — Word owning the flow is the whole premise of the semantic export.
The example carries the feature. The catalogue had no DOCX example with a styled table, so
this would have shipped with no render behind it —
WordExportExample's table now takes aheader band and a table-wide rule. Both of its committed previews move: the DOCX for the new
markup, and the PDF because the fixed-layout backend paints a style it was never given before.
That second one is the drift guard doing exactly what it was widened for last week.
Verification
./mvnw -B -ntp clean verify -pl :graph-compose-core,…,:graph-compose-coverage -am→BUILD SUCCESS;
./mvnw -B -ntp test -f examples/pom.xml→ BUILD SUCCESS, 73 tests, andno preview drift once both were refreshed.
New
DocxTablePaintTest, 6 tests: a fill reachingw:shdwhile an unpainted cell keepsWord's default; a stroke becoming four
SINGLEedges at the rightw:szand colour; thecascade resolving fill and stroke from different levels at once; a merged cell painted on both
its positions; a hairline that rounds to one eighth rather than to nothing; and a zero-width
stroke emitting
NIL.Reverting
DocxSemanticBackend.javatodevelopfails all six.The emitted
CT_TcPrwas checked against the schema by unzipping a document that carries aspan and paint on one cell: the order comes out
w:gridSpan → w:vMerge → w:tcBorders → w:shd, theCT_TcPrBasesequence, because XmlBeans inserts each child at its schema positionrather than in call order.
Documentation
docs/architecture/backend-capability-matrix.mdandrender-docx/README.mdname what thepaint now carries and what it drops. The root
README.mdDOCX row said fill and border paintwere dropped; that line is the one this series keeps having to correct, and it is corrected
again here.
Not covered
Borders on a
w:vMergecontinuation are written on all four edges, so a merged regionformally carries interior rules. Word is expected to suppress them; I could not pin that in a
test, so it is named rather than claimed.
Lane: shared-engine — a render backend. No public API change.
Closes #528.