feat(pptx): table rows — fills, borders, and cell text at graph coordinates#409
Merged
Conversation
…inates Table rows render as positioned rectangles, edge lines, and text frames — never native PowerPoint tables, which re-lay-out their content and cannot hold the engine's exact row geometry. The backend groups contiguous same-table row fragments and paints every cell fill before any border or text, the PDF backend's two-pass discipline, so fills always land beneath strokes. Border lines reuse the PDF handler's half-stroke-width extension on internal horizontal joins (outer table edges stay clamped), a fragment that starts a page forces its TOP border, and row-spanning cells grow downward through their merged rows via the negative yOffset. Cell text mirrors the PDF handler line for line: control-sanitized lines measured with PdfFont.getTextWidth, anchored nine ways inside the padded cell box, drawn through sanitizeForRender into per-line frames seated at baseline − viewer ascent. The frame discipline (wrap/autofit off, zero insets, LEFT align, kern=0) moved into a shared PptxTextFrames helper that the paragraph handler now delegates to — no behavior change there. Tests (32 in render-pptx): PptxTableGeometryTest re-derives fills, border segments, and text frames from the captured LayoutGraph across a page break with a repeated header, pins the resolved rowSpan (negative yOffset), the fills-before-ink paint order per slide, and the page-start TOP-border branch at the handler level with a synthetic fragment; PptxTableParityDemoTest writes the reviewable PDF/PPTX pair plus per-page PNGs under target/visual-tests/pptx-parity/tables/. Full 10-module gate green.
With the standard-14 default the two formats legitimately differ in letterforms (PDF draws Helvetica, PPTX declares metric-compatible Arial); a registered binary family embeds the same TTF into both outputs, so the demo now proves glyph-identical rendering in real viewers as well as geometry identity.
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
The fixed PPTX backend renders text and vector shapes but throws
UnsupportedNodeCapabilityExceptionon table rows — the biggest remaining gap for real documents. Native PowerPoint tables cannot hold the engine's geometry (they re-lay-out their content), so tables need the same positioned-shapes treatment the rest of the backend uses.What changed
PptxTableRowFragmentRenderHandler: eachTableResolvedCellrenders as a positioned fill rectangle, butt-capped border edge lines, and per-line text frames. Border math mirrors the PDF handler exactly: horizontal lines extend half a stroke width past internal joins (adjacent cells' perpendicular borders otherwise leave a corner notch after rasterization) while outer table edges stay clamped; a fragment that starts a page forces its TOP border; row-spanning cells grow downward through merged rows via their negativeyOffset.PptxFixedLayoutBackend.renderGraphgroups contiguous same-path row fragments and callsrenderFillsfor every row before anyrenderBordersAndText— the port ofPdfFixedLayoutBackend.renderTableRowGroup, so cell fills always land beneath strokes across row-span joins.PdfFont.getTextWidth, nine-way anchored inside the padded cell box, drawn throughsanitizeForRenderinto per-line frames seated at baseline − viewer ascent.PptxTextFrames; the paragraph handler now delegates to it — one home for the rules both text paths must agree on, no behavior change (setShapeNameadditionally learned connector shapes, which previously went unnamed).Verification
./mvnw -B -ntp clean verify(full 10-module reactor gate) → BUILD SUCCESS; render-pptx suite grows 29 → 32 tests.PptxTableGeometryTest— re-derives every fill rectangle, border segment (including caps and the page-start TOP), and text frame from the capturedLayoutGraphand matches them against the reopened.pptxacross a page break with a repeated header; pins that the row-spanning cell actually resolved (yOffset < 0asserted), that a page-start fragment exists, and the fills-before-ink paint order per slide. A synthetic-fragment test pins the page-start TOP-border branch at the handler level, independent of what border sides the engine emits.PptxTableParityDemoTest— writes the reviewable pairtables.pdf/tables.pptx+ per-page PNGs underrender-pptx/target/visual-tests/pptx-parity/tables/; the previews match including the zebra fills, the merged "Fixed layout" cell, and the repeated header on page 2.Notes
TableResolvedCell.fillInsetsis unused by both backends today — parity preserved, not a new gap.Lane: canonical (document.backend.fixed.pptx) — backend surface; engine, layout, and the PDF paint path untouched.