v2.5.0 — the deck PowerPoint opens is the deck LibreOffice rendered
Visual QA renders through LibreOffice. The deck is delivered to PowerPoint. This release
closes three places where those two disagreed — and where, because the disagreement was
invisible to every check we run, the defect shipped.
All three have the same shape: python-pptx leaves a setting implicit, ECMA-376 reads the
omission as true, PowerPoint applies it and LibreOffice does not.
1. Charts no longer render two different ways
A single-series bar chart came out of LibreOffice as one blue series with one legend entry,
and out of PowerPoint with a colour per category, five legend entries, and the legend lying
on top of the bars.
Three omissions, all now written out:
c:varyColorsis absent from python-pptx's bar/column writers. PowerPoint reads the
missing value as true and colours a single-series chart per data point, listing the
categories in the legend instead of the series.chart.has_legend = Trueinserts a bare<c:legend/>— noc:overlay, so PowerPoint
lays the legend over the plot area, and noc:legendPos, which meantadd_chart's
legend_positionargument was accepted and then silently discarded. It works now.c:autoTitleDeletedis absent on an untitled chart, so PowerPoint invents a title from
the series name and LibreOffice shows none.
normalize_chart_defaults runs at the end of add_chart and states all of them, plus
plotVisOnly. add_combo_chart already built explicit XML and needed nothing.
2. Repaired placeholders stay where they were put
visual_repair_slides could move a shape to the top-left corner of the slide — but only in
PowerPoint, and only for placeholders that inherit their geometry from the layout.
Setting one dimension on such a placeholder makes python-pptx create an <a:xfrm> holding
only what was set: no <a:off> at all, or an <a:ext> with a zero extent. That partial
transform stops the inheritance dead. LibreOffice falls back to the layout and draws the
shape correctly, so the QA render looked right; PowerPoint reads it literally.
The reason this survived so long is that it was invisible from every angle: python-pptx
also reports the inherited value for the missing half, so shape.left returned the right
number, and the geometry validator saw a perfectly well-placed shape.
pin_inherited_geometry materializes all four values before the edit, and both move_shape
and resize_shape call it — move had the mirror bug, an a:off with no a:ext.
3. Structural validation catches the ones that slip through
New partial_transform warning: a half-written transform is reported wherever it came from,
with the consequence spelled out per case — a missing offset puts the shape in the corner, a
missing extent makes it vanish — and the fix naming move_shape or resize_shape to match.
This is squarely the axis visual QA cannot see, which is what deck_validation is for.
4. Scatter charts work
add_chart advertised 'scatter' as a valid type while building CategoryChartData for it.
That raises on every call, so the type had never once worked — it just came back as
{"error": "Failed to add chart: …"}.
It now builds XyChartData. A scatter chart has no category axis, so categories carries the
x values and they must be numbers; when they aren't, the tool returns an instruction that
names line_markers as the alternative, rather than a traceback string.
Upgrading: decks exported before this release still carry the old XML. Nothing in the
repair whitelist retrofits a varyColors or a missing a:off — the affected slides have to
be rebuilt. New builds are correct in both renderers.
Charts built after upgrading will look different from ones built before, in PowerPoint:
single-series charts lose their per-category colours, untitled charts lose the auto-title,
and the legend goes where it was asked to go. That is the fix, not a regression.