Skip to content

feat(pptx): complete the fragment matrix — images, barcodes, paths, transforms#411

Merged
DemchaAV merged 1 commit into
developfrom
feature/pptx-vectors
Jul 17, 2026
Merged

feat(pptx): complete the fragment matrix — images, barcodes, paths, transforms#411
DemchaAV merged 1 commit into
developfrom
feature/pptx-vectors

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

The fixed PPTX backend rendered text, tables, and basic vector shapes, but images, barcodes, polygons, free paths, gradients, and transform markers still threw UnsupportedNodeCapabilityException — the last gaps between "renders some documents" and "renders any resolved LayoutGraph".

What changed

  • Drawing-surface indirection: handlers now draw on PptxRenderEnvironment.surface(page) — the innermost open transform group or the page's slide. Transform begin/end markers become containment: the begin marker opens an XSLFGroupShape whose interior anchor is the fragment box while the exterior anchor scales about the centre and carries the clockwise rotation — PPTX's equivalent of the PDF backend's ambient graphics-state matrix, and it applies to every fragment kind between the markers because they all draw through the surface.
  • Images (PptxImageFragmentRenderHandler): the PDF fit modes — STRETCH fills the box, CONTAIN letter-boxes the centered bitmap, COVER crops through the picture's source rectangle (DrawingML's native crop). Source dimensions decode from the real bitmap with a per-fingerprint cache: POI's header sniffing mis-reports some images and would break the aspect math the PDF backend computes from decoded pixels.
  • Barcodes (PptxBarcodeFragmentRenderHandler): a byte-mirror of the PDF handler's ZXing raster — same writers, hints, oversampling, and pixel mapping — so both formats carry the same bitmap.
  • Polygons and free paths as freeform geometry (PptxInlineGeometry reuse), with the engine's y-up normalization flipped into slide space.
  • Gradients (PptxGradientFill): linear gradients stamp native gradFill (engine's counter-clockwise angle converts to DrawingML's clockwise sixtieths — 90° becomes 270°); gradient strokes use ln/gradFill natively, clearing the noFill POI writes for a null line color (PowerPoint would otherwise honour noFill and drop the stroke). Radial centres and explicit linear axes approximate with a one-time note. The rectangle shape handler now stamps real gradients instead of the primary-color fallback.
  • Clip markers degrade to a one-time warning — DrawingML has no graphics-state clipping; exact clips belong to the PDF backend or raster-slide mode. Anchor and bookmark markers record their destinations for the navigation pass. With these, every payload the layout compiler emits has a handler; the unsupported-payload diagnostic is pinned with a synthetic unknown payload through the public render path.

Verification

./mvnw -B -ntp clean verify (full 10-module reactor gate) → BUILD SUCCESS; render-pptx suite 34 → 39 tests.

  • PptxVectorFragmentsTest — fit-mode anchors and the COVER crop against the captured graph; barcode picture on the fragment box; synthetic polygon and gradient-path freeforms asserting the raw CT: stop count, the 90°→270°×60000 angle conversion, ln/gradFill present with no leftover noFill and the exact EMU width; transform groups: rotation, interior anchor = fragment box, exterior scaled about the unchanged centre.
  • PptxVectorParityDemoTest — the reviewable pair + PNGs under render-pptx/target/visual-tests/pptx-parity/vector-fragments/: images and the QR are visually identical; the rotated badge shows the documented clip degradation (PDF clips the layer to the container outline, PPTX renders it unclipped with the warning).

Notes

  • Numeric dash arrays still map to the dashed preset (documented since the shapes slice).
  • Group nesting relies on the compiler's same-page begin/end balance; an unmatched end now fails with a clear IllegalStateException instead of an empty-stack error.

Lane: canonical (document.backend.fixed.pptx) — backend surface; engine, layout, and the PDF paint path untouched.

…ransforms

Every payload the layout compiler emits now has a PPTX handler. Images
render with the PDF fit modes — STRETCH fills the box, CONTAIN
letter-boxes the centered bitmap, COVER crops through the picture's
source rectangle (DrawingML's native crop, no clipping needed) — with
source dimensions decoded from the real bitmap, because POI's header
sniffing mis-reports some images and would break the aspect math the PDF
backend computes from decoded pixels. Barcodes reuse the PDF handler's
ZXing raster byte for byte. Polygons and free paths render as freeform
geometry with the engine's y-up normalization flipped into slide space;
gradient fills and gradient strokes stamp native DrawingML gradFill
(linear angles convert from the engine's counter-clockwise convention to
DrawingML's clockwise sixtieths; radial centres and explicit axes
approximate with a one-time note), and a gradient stroke clears the
noFill POI writes for a null line color — PowerPoint would otherwise
honour noFill and drop the stroke entirely.

Transform markers become containment: handlers draw on
PptxRenderEnvironment.surface(page) — the innermost open group or the
slide — and the begin marker opens a group whose interior anchor is the
fragment box while the exterior anchor scales about the centre and
carries the clockwise rotation, PPTX's equivalent of the PDF backend's
graphics-state matrix. Clip markers degrade to a one-time warning
(DrawingML has no graphics-state clipping; exact clips belong to the PDF
backend or raster-slide mode). Anchor and bookmark markers record their
destinations for the navigation pass.

Tests (39 in render-pptx): fit-mode anchors and the COVER crop against
the captured graph, barcode anchor, synthetic polygon and gradient-path
freeforms asserting the CT gradFill (stop count, 90°→270° angle
conversion, stroke gradFill present with no leftover noFill and the EMU
width), and transform groups (rotation, interior/exterior anchors,
centre pivot); a vector parity demo writes the PDF/PPTX pair and PNGs.
Full 10-module gate green.
@DemchaAV
DemchaAV merged commit d44092e into develop Jul 17, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the feature/pptx-vectors branch July 17, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant