Skip to content

fix(pptx): read shapes in reading order (left-to-right within a row) - #2572

Open
Aimery Barrault (aImErYbArRaUlT) wants to merge 1 commit into
microsoft:mainfrom
aImErYbArRaUlT:fix/pptx-reading-order
Open

Aimery Barrault (aImErYbArRaUlT) wants to merge 1 commit into
microsoft:mainfrom
aImErYbArRaUlT:fix/pptx-reading-order

Conversation

@aImErYbArRaUlT

@aImErYbArRaUlT Aimery Barrault (aImErYbArRaUlT) commented Oct 1, 2026 •

Copy link
Copy Markdown

PptxConverter.convert() ordered shapes by (top, left), which sorts by top first and has no notion of "same row". So a two-column body reads its right column before its left, a right-aligned value (a date next to a job title) is read before its label, and a caption beside a figure detaches from it. A few EMU of difference in top is enough to reverse a row.

A _sort_shapes_reading_order() helper groups shapes that substantially overlap vertically into row bands, reads the bands top-to-bottom and the shapes left-to-right within a band. A full-height spanner (a sidebar) beside a grid is lifted onto its horizontal side so the grid stays row-major; a full-width title that dips into the row below leads its band; and a full-width caption sharing a chart's or table's frame is read on the correct side of it. No shape is added or dropped; only the order changes. Applied to a slide's shapes and to group children, and it preserves the None/top-zero handling from #2408.

Evidence

On a ground-truth benchmark (synthetic slides with a known reading order and seeded jitter; mean Spearman vs that order):

layout current (top, left) fix
two-column body 0.88 1.00
same-row label / value 0.90 1.00
grid 0.88 1.00
grid beside a full-height sidebar 0.38 - 0.95 1.00
full-width title over a row 0.58 1.00
newspaper / column-major (out of scope) 0.48 0.67

The fix matches or beats the current order on every archetype, including the out-of-scope newspaper case. Across a 740-slide real-deck corpus (docling, Unstructured, Apache POI) no shape is ever dropped or duplicated, and the order changes only on the slides where the current top-first sort scrambles a row. Full numbers, the blind real-deck render result, and the benchmark script are in #2571.

The change is default-on on that basis; if you would rather gate it behind a kwarg until it has mileage, say so and I will switch it.

Limitation

It does not reconstruct column-major "newspaper" order: when a slide is laid out as several tall columns that each stack several boxes, the fix reads it row by row (left to right across the columns) rather than down one column before starting the next. There it improves on the current sort but does not fully solve the layout (the newspaper row above).

Tests

Added in tests/test_pptx_reading_order.py (two-column, label/value, grid beside a sidebar, group recursion, title-over-row, caption-in-frame, a diagonal staircase, and three None-position units); the ordering tests fail on the old order and pass with the fix. Full suites pass.

Note

This touches the same group-shape block in _pptx_converter.py as #2570, so whichever merges second will need a small rebase. I will rebase this onto main once #2570 lands.

Fixes #2571.

PptxConverter ordered shapes by (top, left), which sorts by top first and
has no notion of "same row", so two-column bodies read right-then-left,
right-aligned values jump ahead of their labels, and captions detach from
figures.

Shapes that overlap vertically are now grouped into row bands, read
top-to-bottom and left-to-right within a band. A full-height spanner
(a sidebar) beside a grid is lifted onto its horizontal side so the grid
stays row-major; a full-width title that dips into the row below leads its
band; and a full-width caption sharing a chart's or table's frame is read
on the correct side of it. No shape is added or dropped; only the order
changes. Applied to a slide's shapes and to group children.
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.

PPTX: same-row shapes are read top-first instead of left-to-right

1 participant