athena-python-pptx 0.10.0__tar.gz → 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- athena_python_pptx-0.10.0/CLAUDE.md → athena_python_pptx-0.14.0/AGENTS.md +5 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/CHANGELOG.md +377 -0
- athena_python_pptx-0.14.0/CLAUDE.md +5 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/PKG-INFO +2 -2
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/docs/API_PARITY_EXCEPTIONS.md +471 -17
- athena_python_pptx-0.14.0/pptx/__init__.py +348 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/batching.py +336 -19
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/data.py +15 -7
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/client.py +174 -65
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/commands.py +28 -3
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/shapes.py +4 -2
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/errors.py +9 -0
- athena_python_pptx-0.14.0/pptx/kit.py +1329 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/presentation.py +77 -27
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/__init__.py +1624 -306
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/slides.py +1606 -184
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/text/__init__.py +202 -23
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/typing.py +11 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pyproject.toml +1 -1
- athena_python_pptx-0.10.0/pptx/__init__.py +0 -208
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/.gitignore +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/API_PARITY_REPORT.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/DEV-GUIDE.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/PARITY_QUESTIONS.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/PUBLISHING.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/README.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/_athena_extension.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/_citations.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/_ptc.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/_references.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/action.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/__init__.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/axis.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/category.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/chart.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/datalabel.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/legend.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/marker.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/plot.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/point.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/series.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/chart/xlsx.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/chtfmt.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/color.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/effect.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/fill.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/dml/line.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/lang.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/exc.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/media.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/oxml/__init__.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/oxml/ns.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/package.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/__init__.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/_base.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/chart.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/coreprops.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/embeddedpackage.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/image.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/media.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/presentation.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/parts/slide.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/base.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/connector.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/freeform.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/group.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/picture.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/placeholder.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shapes/shapetree.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/shared.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/slide.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/spec.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/table.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/text/fonts.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/text/layout.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/text/text.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/types.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/units.py +0 -0
- {athena_python_pptx-0.10.0 → athena_python_pptx-0.14.0}/pptx/util.py +0 -0
|
@@ -59,6 +59,11 @@ A small number of REST-SDK-specific departures are documented in
|
|
|
59
59
|
- **`_BaseSeriesData.index`** raises `NotImplementedError` (raising
|
|
60
60
|
rather than silently returning 0).
|
|
61
61
|
- **`Package.open()`** raises `UnsupportedFeatureError`.
|
|
62
|
+
- **Axis-title text frames** are one paragraph with one font: runs join
|
|
63
|
+
into one title string in the first run's font, with a one-time
|
|
64
|
+
`UserWarning` when that drops styling.
|
|
65
|
+
- **Table-cell text frames** are one paragraph with one run (bold, italic,
|
|
66
|
+
size, name, color); `font.underline` raises.
|
|
62
67
|
|
|
63
68
|
### Intentionally omitted (REST SDK limitations)
|
|
64
69
|
|
|
@@ -2,6 +2,383 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `athena-python-pptx` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.14.0 — waterfall charts render and export as waterfalls
|
|
6
|
+
|
|
7
|
+
Requires a studio that authors `AddChart2016` waterfalls and accepts the
|
|
8
|
+
`SetWaterfallSubtotals` chart patch (older servers reject the batch).
|
|
9
|
+
|
|
10
|
+
- `add_chart(XL_CHART_TYPE.WATERFALL, ...)` now makes a real waterfall instead
|
|
11
|
+
of a clustered column chart: one series of deltas, bars floating on the
|
|
12
|
+
running total, data labels on. More than one series raises
|
|
13
|
+
`ValidationError`.
|
|
14
|
+
- New Athena extension `chart.plots[0].subtotals = [0, 5]` (also
|
|
15
|
+
`set_subtotals([...])`, a `WaterfallPlot`) marks the bars drawn from zero as
|
|
16
|
+
totals; a total left `None` shows the running total.
|
|
17
|
+
- `chart.chart_type` reads back `XL_CHART_TYPE.WATERFALL`.
|
|
18
|
+
- `chart.plots` on a reopened deck picks the plot class from the snapshot's
|
|
19
|
+
plot type, so `chart.plots[0]` is a `WaterfallPlot` (or `BarPlot`,
|
|
20
|
+
`LinePlot`, ...) instead of the bare base plot.
|
|
21
|
+
- `chart.replace_data()` keeps the plot's other settings (subtotals, gap
|
|
22
|
+
width, data labels) in the local mirror, as the server does; subtotal
|
|
23
|
+
indices past the new points are dropped on both sides.
|
|
24
|
+
- The exported deck carries a stacked-column waterfall (invisible base
|
|
25
|
+
series), so it opens in any PowerPoint version and stays editable; the studio
|
|
26
|
+
preview matches it (centred labels, no connector lines).
|
|
27
|
+
|
|
28
|
+
## 0.14.0 — chart series line and no-fill formatting reach the deck
|
|
29
|
+
|
|
30
|
+
Requires a studio with the `SetSeriesLine` / `SetSeriesFillVisible` /
|
|
31
|
+
`SetPointFillVisible` chart patch ops (older servers reject the batch).
|
|
32
|
+
|
|
33
|
+
- `series.format.line.color.rgb`, `.width`, `.dash_style` and
|
|
34
|
+
`.fill.background()` / `.fill.solid()` now emit `SetSeriesLine`: the plotted
|
|
35
|
+
line of a line / XY / radar series, the outline of a bar / area / pie /
|
|
36
|
+
bubble series. Previously these were local-state-only and a line chart kept
|
|
37
|
+
its palette colours. `dash_style` accepts `MSO_LINE_DASH_STYLE` members,
|
|
38
|
+
snake_case line tokens and `<a:prstDash>` presets, and reads back the enum
|
|
39
|
+
member (or the preset string for `dot` / `sysDashDot` / `sysDashDotDot`,
|
|
40
|
+
which have none); `None` clears width / dash.
|
|
41
|
+
- `series.format.fill.background()` emits `SetSeriesFillVisible` (the invisible
|
|
42
|
+
base series of a stacked-column waterfall now stays invisible); `solid()`
|
|
43
|
+
on a hidden fill, or assigning `fore_color`, shows it again.
|
|
44
|
+
`series.points[i].format.fill.background()` emits `SetPointFillVisible`; the
|
|
45
|
+
hidden state is kept on the series, so `solid()` through a new `format`
|
|
46
|
+
object (or after a refresh) shows the point again.
|
|
47
|
+
- Chart format writes that still have no patch op (axis line / fill, axis-title
|
|
48
|
+
and gridline fills, per-point line, marker hollow fill / line width / dash,
|
|
49
|
+
line `theme_color`, data-label borders) keep their local value and emit a
|
|
50
|
+
one-time `UserWarning`: `"<property> is not supported by the Athena studio
|
|
51
|
+
renderer and was ignored"`. They never raise. Data-label border setters no
|
|
52
|
+
longer send fields the server silently dropped.
|
|
53
|
+
- Gridline `dash_style` accepts `MSO_LINE_DASH_STYLE` members (previously only
|
|
54
|
+
preset strings).
|
|
55
|
+
|
|
56
|
+
## 0.14.0 — axis-title paragraphs and runs, per-cell font family
|
|
57
|
+
|
|
58
|
+
Two python-pptx idioms crashed agent scripts partway through a deck
|
|
59
|
+
(staging session thread_3de488f7), after earlier batches had committed:
|
|
60
|
+
|
|
61
|
+
- `axis.axis_title.text_frame.paragraphs[0].runs` raised `AttributeError`
|
|
62
|
+
(`AxisTitleTextFrame` exposed only `.text`). It is now a python-pptx
|
|
63
|
+
`TextFrame`: `paragraphs` holds one paragraph whose `text`, `runs`,
|
|
64
|
+
`font` and `add_run()` work, and each run's `text` / `font`. Text goes to
|
|
65
|
+
the title text and fonts to the `SetAxisTitle` font fields (size, bold,
|
|
66
|
+
`color.rgb`, name). The patch model is one string and one font, so several
|
|
67
|
+
runs join into one title string drawn in the first run's font over the
|
|
68
|
+
paragraph font; a `UserWarning` is emitted once per axis title when that
|
|
69
|
+
drops a run's styling or when a font property the patch can't carry
|
|
70
|
+
(italic, underline, strike, theme color, …) is set. An existing title's
|
|
71
|
+
text is now read from the deck snapshot.
|
|
72
|
+
- `cell.text_frame.paragraphs[0].runs[0].font.name = "Calibri"` raised
|
|
73
|
+
`UnsupportedFeatureError`. `SetTableCell` now carries `font_family` (wire
|
|
74
|
+
`fontFamily`) and `italic`; `font.name` / `font.italic` set them, the
|
|
75
|
+
studio draws them and the export writes `<a:latin typeface>` / `i="1"` on
|
|
76
|
+
the cell's runs, for SDK-authored and uploaded tables alike.
|
|
77
|
+
`paragraphs[0].font` is the cell's run font. Assigning `None` cannot clear
|
|
78
|
+
a value the session wrote (no inherit marker on the wire) and warns.
|
|
79
|
+
`font.underline` still raises.
|
|
80
|
+
|
|
81
|
+
Needs a PPTX Studio server with `SetTableCell.fontFamily` / `italic`
|
|
82
|
+
(older servers drop the fields without error).
|
|
83
|
+
|
|
84
|
+
## 0.14.0 — `add_run()` on an empty paragraph returns `runs[0]`
|
|
85
|
+
|
|
86
|
+
Fixes the empty run agent-built decks stored ahead of the real text in
|
|
87
|
+
almost every text box (staging `asset_e41e33eb`: 224 of 226), and the
|
|
88
|
+
doubled, default-styled titles and labels that later `runs[0].text = ...`
|
|
89
|
+
edits produced by writing that empty run.
|
|
90
|
+
|
|
91
|
+
- The first `paragraph.add_run()` on a paragraph whose only run is the SDK's
|
|
92
|
+
untouched empty seed returns that run as `runs[0]` instead of appending
|
|
93
|
+
after it, matching python-pptx. This covers new text boxes and autoshapes,
|
|
94
|
+
blank lines from `text_frame.text = ...`, and paragraphs emptied by
|
|
95
|
+
`paragraph.text = ""`. Repeated `add_run()` calls still create distinct
|
|
96
|
+
runs, and a seed run the caller wrote directly is kept.
|
|
97
|
+
- After a text reset the returned run is bare, as in python-pptx: the SDK
|
|
98
|
+
sends `SetRunText` with the new `resetFormatting` flag so the look the
|
|
99
|
+
server's `SetText` kept on that empty run is dropped. The same command
|
|
100
|
+
writes run 0 into snapshot paragraphs stored with no runs (ingested empty
|
|
101
|
+
placeholders), so a style set before the text resolves.
|
|
102
|
+
- Snapshot-loaded paragraphs keep their stored run indices, so decks written
|
|
103
|
+
before this fix keep their stored empty runs. `text_frame.clear()` +
|
|
104
|
+
`add_run()` is unchanged.
|
|
105
|
+
- Release after the pptx-studio server with `SetRunText.resetFormatting` is
|
|
106
|
+
deployed. Older servers ignore the flag, and the run returned after a text
|
|
107
|
+
reset then keeps the replaced text's look.
|
|
108
|
+
- Documented in `docs/API_PARITY_EXCEPTIONS.md`.
|
|
109
|
+
|
|
110
|
+
## 0.14.0 — `validate()` judges text by its ink and flags table rows only when they grow
|
|
111
|
+
|
|
112
|
+
Cuts the false layout warnings an agent had to reason about and dismiss
|
|
113
|
+
(staging session `thread_3de488f7`: collisions for value labels on bubbles,
|
|
114
|
+
captions inside wide title boxes and table cells in rows sized for two
|
|
115
|
+
lines). Same signatures.
|
|
116
|
+
|
|
117
|
+
- **Text is compared by its ink, not its frame.** Each text frame is laid out
|
|
118
|
+
on the glyph-width estimate — greedy word wrap at the inner width minus the
|
|
119
|
+
paragraph indent (the first-line indent on the first line; a bullet moves
|
|
120
|
+
the text at least 1.5em in and an unindented outline level 0.375in, as in
|
|
121
|
+
the studio renderer), per-paragraph size and line spacing — and every line is
|
|
122
|
+
placed by its paragraph's alignment and the frame's vertical anchor
|
|
123
|
+
(autoshapes centre by default, text boxes anchor at the top). Lines are
|
|
124
|
+
widened 10% within their frame so an under-estimate cannot hide a real
|
|
125
|
+
collision; a placeholder's alignment or anchor inherited from its layout
|
|
126
|
+
widens the ink to every place the text could be. Text whose ink sits
|
|
127
|
+
entirely on a shape (a label on a card or bubble, a caption on a picture)
|
|
128
|
+
counts as containment unless that shape is drawn over it with an opaque
|
|
129
|
+
fill; frames that overlap while their texts do not are not reported; and
|
|
130
|
+
text that runs over the edge of a visible shape (fill or outline — plain
|
|
131
|
+
text boxes show no edge) or partly under a later opaque shape gets its own
|
|
132
|
+
message. Hidden text is judged by the share of the *text* under the cover,
|
|
133
|
+
and a cover that carries text of its own still hides what is beneath it.
|
|
134
|
+
Overflowing text now spills out of its frame the way PowerPoint draws it,
|
|
135
|
+
so a paragraph running onto the box below is reported even though the two
|
|
136
|
+
frames never touch; a studio "fits" verdict pulls that ink back inside.
|
|
137
|
+
- **Table cells warn only when the row grows**: wrapped lines x line height
|
|
138
|
+
plus the cell's top/bottom margins taller than the row (every row a vertical
|
|
139
|
+
merge spans) — the case where PowerPoint grows the row, measured at the
|
|
140
|
+
weight and family the snapshot resolved (table-style headers are bold). A single word wider
|
|
141
|
+
than its column (a number that will break mid-word) keeps a warning of its
|
|
142
|
+
own. Both are confirmed with `POST /measure-text-fit` like text overflow,
|
|
143
|
+
within the same 12-call budget.
|
|
144
|
+
- **Readable overflow message**: `needs ~2 lines at 20pt but only ~1 fits
|
|
145
|
+
(~0.67in of text in a 0.40in-tall box)`, `needs ~1 line at 32pt but not
|
|
146
|
+
even one line fits (…)`, paragraph spacing called out when it alone tips
|
|
147
|
+
the box over, `12–40pt` for mixed sizes, and correct plurals.
|
|
148
|
+
- **Measurement sends the real inner box.** `/measure-text-fit` subtracts
|
|
149
|
+
PowerPoint's default insets from the box it gets; `validate()` sent the box
|
|
150
|
+
with the frame margins already removed, so the studio measured a box 0.2in
|
|
151
|
+
narrower and 0.1in shorter than the text's and over-confirmed overflow. The
|
|
152
|
+
box now goes out grown by those insets.
|
|
153
|
+
|
|
154
|
+
## 0.13.1 — in-batch `add_slide()` carries layout placeholders, proxies survive `refresh()`
|
|
155
|
+
|
|
156
|
+
Fixes the `'NoneType' object has no attribute 'text'` failure agents hit
|
|
157
|
+
with the textbook idiom `slide = prs.slides.add_slide(layout);
|
|
158
|
+
slide.shapes.title.text = ...` inside `with prs.batch():`, and the
|
|
159
|
+
follow-on where `prs.refresh()` left the held `slide` orphaned.
|
|
160
|
+
|
|
161
|
+
- A slide added inside a batch now projects its layout's placeholders
|
|
162
|
+
locally, from the snapshot's `themeHierarchy.layouts[].placeholders`
|
|
163
|
+
(new server projection), under the same `__inherited__:<slide id>:<layout
|
|
164
|
+
element id>` ids the server materializes on `AddSlide`. `shapes.title` /
|
|
165
|
+
`placeholders` resolve immediately with no flush or refresh, and writes to
|
|
166
|
+
them are sent in the same request as the `AddSlide`, so batch atomicity is
|
|
167
|
+
preserved. Decks whose snapshot lacks layout placeholder data keep the
|
|
168
|
+
previous behaviour (`title` is `None` until the slide is committed).
|
|
169
|
+
- `CommandBuffer.flush()` records `client_id -> server slide id` for every
|
|
170
|
+
`AddSlide` / `CopySlideFromDeck` it sends, from the response's
|
|
171
|
+
`created.slideIds` (same order). `Slides._update_from_snapshot()` uses
|
|
172
|
+
only those authoritative aliases to re-key the *existing* `Slide` object
|
|
173
|
+
(and its projected placeholder shape ids) to the server id instead of
|
|
174
|
+
dropping it and appending a fresh one. Pending proxies without an alias
|
|
175
|
+
are never paired positionally with unseen snapshot slides, which may
|
|
176
|
+
belong to other collaborators.
|
|
177
|
+
- No public API change; `slide.delete()` remains intentionally absent
|
|
178
|
+
(python-pptx parity) — use `prs.slides.delete(slide)`.
|
|
179
|
+
|
|
180
|
+
## 0.13.0 — `pptx.kit` composition kit
|
|
181
|
+
|
|
182
|
+
- New Athena-only module `pptx.kit` (`from pptx.kit import Kit, Palette,
|
|
183
|
+
Fonts, Region`; also re-exported from `pptx`). `Kit(prs)` composes slides
|
|
184
|
+
from regions derived from the slide size: `header` (small-caps kicker,
|
|
185
|
+
action title auto-fit to a two-line band, footer rule + page number +
|
|
186
|
+
`kit.footer_text`), `footer`, `takeaway` (full-width bar one gap above the
|
|
187
|
+
footer band), `content_region`, `columns`, `grid`, `kpi_row` / `kpi_card`
|
|
188
|
+
(surface card, accent stripe, big auto-fit value, auto-fit label), `card`,
|
|
189
|
+
`text` and `bullets`.
|
|
190
|
+
- Text asked to fit is sized to its box by the studio's `/measure-text-fit`
|
|
191
|
+
layouter (same client pattern as `TextFitter.best_fit_font_size`, result
|
|
192
|
+
floored and cached per kit, one failed probe latches the fallback) or, when
|
|
193
|
+
the studio is unreachable, by a conservative greedy wrap on the
|
|
194
|
+
`_estimate_text_width_emu` glyph table at 1.2 line height. It never grows
|
|
195
|
+
past the requested size; when nothing fits even at the minimum size the kit
|
|
196
|
+
uses the minimum and emits a `RuntimeWarning` naming the text; `fit=False`
|
|
197
|
+
keeps the size as given.
|
|
198
|
+
- Regions are clamped to the slide and the footer band is reserved, so
|
|
199
|
+
nothing lands outside the slide or under the footer; a region with nothing
|
|
200
|
+
left raises instead of silently placing nothing. `content_region` keeps the
|
|
201
|
+
takeaway band free by default so a bar added after the body cannot hide it,
|
|
202
|
+
and the `y` / `h` overrides of `columns` / `grid` / `kpi_row` are clamped to
|
|
203
|
+
the content region. Per-slide state is keyed by the stable `slide.slide_id`.
|
|
204
|
+
- Pure python-pptx public API underneath (`add_shape` / `add_textbox` /
|
|
205
|
+
`fill` / `line` / `text_frame` / `font`), emitted through the
|
|
206
|
+
presentation's command buffer — build each slide inside `with prs.batch():`
|
|
207
|
+
for one round-trip. Reaches agents with the next `athena-python-pptx` bump.
|
|
208
|
+
Documented in `docs/API_PARITY_EXCEPTIONS.md`; demo in
|
|
209
|
+
`examples/kit_demo.py`.
|
|
210
|
+
|
|
211
|
+
## 0.13.0 — validate() finds overflow and hidden text, drops containment noise
|
|
212
|
+
|
|
213
|
+
Reworks `Slide.validate()` around the defects that actually ship in agent
|
|
214
|
+
decks (verified 2026-09-14 against two 8-slide agent builds: 167 and 127
|
|
215
|
+
warnings, ~75% of them a background card behind its own label). Same
|
|
216
|
+
signature; an empty list is once again reachable by a designed slide.
|
|
217
|
+
|
|
218
|
+
- **Containment overlaps are no longer reported** unless both shapes carry
|
|
219
|
+
text (text on text is always reported). Partial overlaps are reported
|
|
220
|
+
only when at least one shape carries text; hairline overlaps thinner
|
|
221
|
+
than a text-frame inset (0.05in) and overlaps between two text-less
|
|
222
|
+
shapes are ignored. Tables count as text-bearing.
|
|
223
|
+
- **New: text overflow for non-table shapes** — per-paragraph font-metric
|
|
224
|
+
estimate (`_estimate_text_width_emu`, run size → paragraph default →
|
|
225
|
+
shape default → 18pt; word wrap on → `ceil(width / inner_width)` lines,
|
|
226
|
+
off → horizontal check; `lines × font × 1.2 × line_spacing` + spacing vs
|
|
227
|
+
box height minus margins). Frames set to auto-fit are skipped. When the
|
|
228
|
+
presentation has a studio client each hit whose paragraphs share one
|
|
229
|
+
font size and weight is confirmed with one `MeasureTextFit` round-trip
|
|
230
|
+
pinned at that size (`fits: true` drops it; capped at 12 per call; every
|
|
231
|
+
network error falls back to the heuristic verdict); mixed-font frames keep
|
|
232
|
+
the heuristic verdict since the server measures at a single font.
|
|
233
|
+
- **New: hidden text (occlusion)** — a text shape at least half covered by
|
|
234
|
+
a later (higher z-order) shape with a solid, non-transparent fill and no
|
|
235
|
+
text: `"text in 'X' is covered by 'Y'"`. Unknown fills (none, gradient,
|
|
236
|
+
pattern, pictures) count as not opaque; any transparency on the proxy or
|
|
237
|
+
a snapshot `fillTransparency` disables the rule for that shape.
|
|
238
|
+
- Warnings are deduplicated, ordered overflow → occlusion → text-on-text →
|
|
239
|
+
other (off-slide, one-text partial overlaps, empty placeholders), capped
|
|
240
|
+
at 15 per slide with a trailing `"… and N more (M containment overlaps
|
|
241
|
+
ignored)"` line. Rotated shapes near 90°/270° use their swapped visual
|
|
242
|
+
bbox in the pairwise rules.
|
|
243
|
+
- `bring_to_front()` / `send_to_back()` / `bring_forward()` /
|
|
244
|
+
`send_backward()` now mirror the move on the local `slide.shapes` order
|
|
245
|
+
(previously only the server saw it), so `validate()` reads the intended
|
|
246
|
+
stack without a refresh.
|
|
247
|
+
- New module-level `pptx.validate_all_touched() -> dict[int, list[str]]`
|
|
248
|
+
(Athena extension) for the executor postamble: `CommandBuffer` records
|
|
249
|
+
the slide indexes / slide ids / shape ids of every command the server
|
|
250
|
+
applied (`CommandBuffer.take_touched()` returns and clears them);
|
|
251
|
+
`validate_all_touched()` resolves them to slides on every live
|
|
252
|
+
`Presentation`, validates only those, and returns the slides with
|
|
253
|
+
warnings. Recorded indexes are shifted for slide insertions, deletions
|
|
254
|
+
and reorders applied later in the run; destination-bearing commands
|
|
255
|
+
(`CloneShape`, `CopySlideFromDeck`, `ReplaceText`) record their
|
|
256
|
+
destination; `SetPresentationSize` and a deck-wide `ReplaceText` validate
|
|
257
|
+
every slide. Commands that cannot change a layout (notes, names,
|
|
258
|
+
citations, deck metadata) never mark a slide; never raises for
|
|
259
|
+
validation reasons.
|
|
260
|
+
|
|
261
|
+
## 0.12.0 — snapshot hydration, ReplaceChartData emission, level dirty-flag, discard_all
|
|
262
|
+
|
|
263
|
+
SDK halves of the issue #30178 Cluster D server work (#30204 / #30211 /
|
|
264
|
+
#30212 / #30213).
|
|
265
|
+
|
|
266
|
+
**⚠️ Server ordering:** `Chart.replace_data` now emits a single
|
|
267
|
+
`ReplaceChartData` patch, which servers without #30211 reject (400) — the
|
|
268
|
+
`presentation-exec` snapshot bump to 0.12.0 must wait for the pptx-studio
|
|
269
|
+
deploy that ships the op.
|
|
270
|
+
|
|
271
|
+
- `Shape.shadow` hydrates from the snapshot's `shadow` bag: a partial edit
|
|
272
|
+
like `shadow.blur_radius = Pt(6)` re-emits the ingested shadow's
|
|
273
|
+
`visible=True` / type / distance / direction / color / transparency
|
|
274
|
+
instead of the un-hydrated defaults (`visible=False` used to DELETE the
|
|
275
|
+
ingested shadow server-side). Hydrated shadows read back
|
|
276
|
+
`inherit == False`.
|
|
277
|
+
- `Shape.click_action` hydrates from the snapshot's `clickAction` bag —
|
|
278
|
+
same deletion-bug class (`click_action.tooltip = ...` used to emit
|
|
279
|
+
`action_type='none'` and delete the ingested link).
|
|
280
|
+
- `Presentation.core_properties` hydrates from the snapshot's
|
|
281
|
+
`coreProperties` (server #30204): setting one property no longer wipes
|
|
282
|
+
the deck's other metadata to `None`. `refresh()` re-hydrates.
|
|
283
|
+
- `Chart.replace_data` emits ONE `ReplaceChartData` patch carrying the
|
|
284
|
+
full categories + series matrix (python-pptx semantics: shrink drops
|
|
285
|
+
trailing points, grow adds them, extra series are removed) instead of
|
|
286
|
+
per-index `UpdateSeries*` / `UpdateCategoryLabel` patches that couldn't
|
|
287
|
+
change the data's shape. The local `chartSpec` mirror still updates,
|
|
288
|
+
preserving server series ids by index. A zero-series `chart_data`
|
|
289
|
+
raises `ValidationError` before touching the local mirror (the op
|
|
290
|
+
requires at least one series; silently returning would desync local
|
|
291
|
+
reads from the server).
|
|
292
|
+
- `run.hyperlink.target_slide = n` no longer rides with `address=''` (the
|
|
293
|
+
clear convention) — exactly one of address / targetSlideIndex is sent,
|
|
294
|
+
aligning with server #30212. The SetText re-emit carries
|
|
295
|
+
`hyperlinkTargetSlideIndex`, and snapshot rich content hydrates it back.
|
|
296
|
+
`SetRunHyperlinkTarget.validate()` rejects setting both.
|
|
297
|
+
- `paragraph.level` is sent only when the paragraph is indented or the
|
|
298
|
+
caller explicitly assigned it (explicit `level = 0` still reaches the
|
|
299
|
+
wire); an untouched level-0 paragraph omits the field so unrelated style
|
|
300
|
+
edits don't stomp the server-side outline level (belt-and-suspenders
|
|
301
|
+
with the server-side lvl=0 canonicalization in #30213).
|
|
302
|
+
- New module-level `pptx.discard_all()` (mirrors athena-python-docx):
|
|
303
|
+
drops all buffered, un-flushed commands across open decks without
|
|
304
|
+
posting and returns the dropped count. The agora executor postamble
|
|
305
|
+
calls it when user code raised, so failed scripts discard their
|
|
306
|
+
half-buffered tail instead of posting it.
|
|
307
|
+
|
|
308
|
+
## 0.11.0 — silent no-op & data-loss fixes (deep-dive remediation)
|
|
309
|
+
|
|
310
|
+
Eliminates a cluster of silent no-ops, read-after-write desyncs, and
|
|
311
|
+
buffer-poisoning bugs found in the 2026-08 presentation-asset deep dive.
|
|
312
|
+
All fixes preserve python-pptx API parity (behavior, not surface, changes).
|
|
313
|
+
|
|
314
|
+
- `paragraph.level = 0` now reaches the wire (was dropped by the None-drop),
|
|
315
|
+
so resetting an indented paragraph works.
|
|
316
|
+
- `font.subscript = False` / `font.superscript = False` now send `baseline: 0`
|
|
317
|
+
(the server's clear signal) instead of omitting the field, so they actually
|
|
318
|
+
un-sub/superscript existing text.
|
|
319
|
+
- A bare `shape.fill.solid()` now emits, matching `gradient()`/`patterned()`.
|
|
320
|
+
- Chart `has_legend` / `has_title` / `chart_title` now update the local
|
|
321
|
+
snapshot on write, so reads after a write return the new value.
|
|
322
|
+
- `rows.insert()` / `columns.insert()` now shift the cached row-height /
|
|
323
|
+
column-width lists, fixing corrupted height/width reads after an insert.
|
|
324
|
+
- `GroupShape.shapes` now resolves child shapes via `childIds` (was hardcoded
|
|
325
|
+
to `[]`).
|
|
326
|
+
- `PicturePlaceholder.insert_picture` converts WebP/SVG/AVIF to PNG (was
|
|
327
|
+
mislabeling them `png`) and honors its `width`/`height` params.
|
|
328
|
+
- `batch()` discards the buffer when its body raises (was committing the
|
|
329
|
+
partial prefix).
|
|
330
|
+
- A server-rejected command in a batch is dropped (not re-queued), ending the
|
|
331
|
+
permanent buffer-poison loop; the rest of the rolled-back chunk is restored.
|
|
332
|
+
- Command chunks carry a stable `txn.id` across retries so an ambiguous-timeout
|
|
333
|
+
resend dedups server-side instead of double-applying.
|
|
334
|
+
- `flush_all(strict=True)` raises `FlushAllError` on a per-buffer flush failure
|
|
335
|
+
(the sandbox executor opts in) instead of silently swallowing it.
|
|
336
|
+
- `Presentation` no longer pins its buffer/client/session via the atexit hook
|
|
337
|
+
(weakref closure); `close()`/`__del__` release the HTTP session.
|
|
338
|
+
- Export 404s fail fast for a deck this client already read (deleted deck),
|
|
339
|
+
instead of burning the ~85s post-upload retry budget.
|
|
340
|
+
- `XyChartData` / `BubbleChartData` `add_series` raise on malformed inline
|
|
341
|
+
points instead of silently dropping them.
|
|
342
|
+
- `mso_shape_to_string` raises on an unmapped autoshape id instead of silently
|
|
343
|
+
authoring a rectangle.
|
|
344
|
+
- `AddMovie` emits `posterBase64` (the key the server reads) and always sends a
|
|
345
|
+
poster (a 1x1 placeholder when none is supplied), so `add_movie`/`add_video`
|
|
346
|
+
no longer fail the whole batch.
|
|
347
|
+
|
|
348
|
+
## 0.10.1 — unnamed-shape parity, browser UA on image fetches, honest SVG errors
|
|
349
|
+
|
|
350
|
+
Fixes the three `execute_presentation_code` failures observed in the
|
|
351
|
+
2026-08-12 production deck-build session (thread_d7eb9dd6).
|
|
352
|
+
|
|
353
|
+
- `Shape.name` now always returns a `str` (python-pptx parity —
|
|
354
|
+
`cNvPr@name` is a schema-required attribute upstream). Elements
|
|
355
|
+
persisted before names were stored (e.g. pictures added by pre-0.10
|
|
356
|
+
SDKs) read back as `""` instead of `None`, so idioms like
|
|
357
|
+
`'Picture' in shape.name` no longer raise `TypeError`.
|
|
358
|
+
- `add_picture(url)` fetches now send browser-equivalent
|
|
359
|
+
`User-Agent`/`Accept` headers. Image hosts and proxies behind bot
|
|
360
|
+
rules (e.g. `wsrv.nl`, Cloudflare-fronted CDNs) 403 the default
|
|
361
|
+
`python-requests/x.y` UA while serving the identical URL to a browser
|
|
362
|
+
UA. The Accept header advertises only formats the deck pipeline
|
|
363
|
+
handles (PNG/JPEG native, WebP via Pillow) so format-negotiating CDNs
|
|
364
|
+
don't switch the body to AVIF or SVG.
|
|
365
|
+
- AVIF is now detected explicitly (any `avif`/`avis` brand in the
|
|
366
|
+
ISO-BMFF `ftyp` box — major or compatible, so `mif1`-major HEIF
|
|
367
|
+
containers are covered) instead of falling through the sniffer as
|
|
368
|
+
`png` — it converts via Pillow where the build supports AVIF, and
|
|
369
|
+
otherwise raises the actionable convert-or-fail error rather than
|
|
370
|
+
shipping undecodable bytes that render as a silent broken-image
|
|
371
|
+
placeholder.
|
|
372
|
+
- `Shapes.by_name("")` / `get_by_name("")` no longer match unnamed
|
|
373
|
+
legacy shapes — `""` is the no-name sentinel, not an addressable
|
|
374
|
+
name.
|
|
375
|
+
- The SVG `add_picture` error no longer recommends `pip install svglib`
|
|
376
|
+
(rasterization also needs the `rlPyCairo` backend and the native cairo
|
|
377
|
+
library, so that advice cannot work in a stock sandbox). WebP and SVG
|
|
378
|
+
now get separate, accurate remediation hints. Pairs with the
|
|
379
|
+
presentation-exec v59 snapshot, which bundles `svglib` + `rlPyCairo`
|
|
380
|
+
so SVG→PNG conversion works in-sandbox.
|
|
381
|
+
|
|
5
382
|
## 0.9.0 — eager validation, table overflow diagnostics, and text caps
|
|
6
383
|
|
|
7
384
|
Publishes the SDK changes already deployed with PPTX Studio after 0.8.1.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration
|
|
5
5
|
Project-URL: Homepage, https://github.com/pptx-studio/python-sdk
|
|
6
6
|
Project-URL: Documentation, https://docs.pptx-studio.com/sdk/python
|