Skip to content

v2.5.0 — the deck PowerPoint opens is the deck LibreOffice rendered

Choose a tag to compare

@photonn photonn released this 25 Aug 12:18
· 12 commits to main since this release

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:varyColors is 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 = True inserts a bare <c:legend/> — no c:overlay, so PowerPoint
    lays the legend over the plot area, and no c:legendPos, which meant add_chart's
    legend_position argument was accepted and then silently discarded. It works now.
  • c:autoTitleDeleted is 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.