python-pptx2 2.13.0__py3-none-any.whl
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.
- pptx2/__init__.py +152 -0
- pptx2/_color.py +75 -0
- pptx2/_slide_importer.py +597 -0
- pptx2/_svg.py +155 -0
- pptx2/_template_applier.py +292 -0
- pptx2/_textstyle.py +187 -0
- pptx2/accessibility.py +365 -0
- pptx2/action.py +270 -0
- pptx2/animation.py +2237 -0
- pptx2/api.py +49 -0
- pptx2/audit.py +258 -0
- pptx2/chart/__init__.py +0 -0
- pptx2/chart/analytics.py +381 -0
- pptx2/chart/axis.py +543 -0
- pptx2/chart/category.py +200 -0
- pptx2/chart/chart.py +670 -0
- pptx2/chart/data.py +864 -0
- pptx2/chart/datalabel.py +406 -0
- pptx2/chart/legend.py +86 -0
- pptx2/chart/marker.py +70 -0
- pptx2/chart/palettes.py +129 -0
- pptx2/chart/plot.py +462 -0
- pptx2/chart/point.py +101 -0
- pptx2/chart/quick_layouts.py +325 -0
- pptx2/chart/series.py +334 -0
- pptx2/chart/xlsx.py +272 -0
- pptx2/chart/xmlwriter.py +1845 -0
- pptx2/compose/__init__.py +28 -0
- pptx2/compose/from_spec.py +1094 -0
- pptx2/design/__init__.py +8 -0
- pptx2/design/components.py +607 -0
- pptx2/design/figures.py +389 -0
- pptx2/design/layout.py +370 -0
- pptx2/design/recipes.py +1967 -0
- pptx2/design/style.py +209 -0
- pptx2/design/tokens.py +915 -0
- pptx2/diagrams.py +754 -0
- pptx2/dml/__init__.py +0 -0
- pptx2/dml/chtfmt.py +40 -0
- pptx2/dml/color.py +496 -0
- pptx2/dml/effect.py +909 -0
- pptx2/dml/fill.py +691 -0
- pptx2/dml/line.py +287 -0
- pptx2/dml/picture.py +212 -0
- pptx2/dml/three_d.py +381 -0
- pptx2/enum/__init__.py +0 -0
- pptx2/enum/action.py +71 -0
- pptx2/enum/animation.py +31 -0
- pptx2/enum/base.py +218 -0
- pptx2/enum/chart.py +574 -0
- pptx2/enum/dml.py +740 -0
- pptx2/enum/lang.py +685 -0
- pptx2/enum/presentation.py +133 -0
- pptx2/enum/shapes.py +1029 -0
- pptx2/enum/text.py +230 -0
- pptx2/exc.py +42 -0
- pptx2/formats.py +139 -0
- pptx2/geometry.py +420 -0
- pptx2/inherit.py +109 -0
- pptx2/lint.py +2256 -0
- pptx2/math.py +177 -0
- pptx2/media.py +197 -0
- pptx2/opc/__init__.py +0 -0
- pptx2/opc/constants.py +332 -0
- pptx2/opc/oxml.py +188 -0
- pptx2/opc/package.py +762 -0
- pptx2/opc/packuri.py +109 -0
- pptx2/opc/serialized.py +296 -0
- pptx2/opc/shared.py +20 -0
- pptx2/opc/spec.py +45 -0
- pptx2/oxml/__init__.py +555 -0
- pptx2/oxml/action.py +53 -0
- pptx2/oxml/chart/__init__.py +0 -0
- pptx2/oxml/chart/axis.py +337 -0
- pptx2/oxml/chart/chart.py +481 -0
- pptx2/oxml/chart/datalabel.py +253 -0
- pptx2/oxml/chart/legend.py +72 -0
- pptx2/oxml/chart/marker.py +61 -0
- pptx2/oxml/chart/plot.py +365 -0
- pptx2/oxml/chart/series.py +425 -0
- pptx2/oxml/chart/shared.py +220 -0
- pptx2/oxml/coreprops.py +288 -0
- pptx2/oxml/dml/__init__.py +0 -0
- pptx2/oxml/dml/color.py +135 -0
- pptx2/oxml/dml/effect.py +213 -0
- pptx2/oxml/dml/fill.py +316 -0
- pptx2/oxml/dml/line.py +12 -0
- pptx2/oxml/dml/three_d.py +110 -0
- pptx2/oxml/ns.py +135 -0
- pptx2/oxml/presentation.py +313 -0
- pptx2/oxml/shapes/__init__.py +19 -0
- pptx2/oxml/shapes/autoshape.py +467 -0
- pptx2/oxml/shapes/connector.py +107 -0
- pptx2/oxml/shapes/graphfrm.py +347 -0
- pptx2/oxml/shapes/groupshape.py +329 -0
- pptx2/oxml/shapes/picture.py +270 -0
- pptx2/oxml/shapes/shared.py +577 -0
- pptx2/oxml/simpletypes.py +1027 -0
- pptx2/oxml/slide.py +563 -0
- pptx2/oxml/table.py +650 -0
- pptx2/oxml/text.py +815 -0
- pptx2/oxml/theme.py +36 -0
- pptx2/oxml/xmlchemy.py +717 -0
- pptx2/package.py +222 -0
- pptx2/parts/__init__.py +0 -0
- pptx2/parts/chart.py +95 -0
- pptx2/parts/coreprops.py +167 -0
- pptx2/parts/diagram.py +37 -0
- pptx2/parts/embeddedpackage.py +93 -0
- pptx2/parts/image.py +275 -0
- pptx2/parts/media.py +37 -0
- pptx2/parts/presentation.py +136 -0
- pptx2/parts/slide.py +371 -0
- pptx2/presentation.py +408 -0
- pptx2/py.typed +0 -0
- pptx2/render.py +586 -0
- pptx2/section.py +272 -0
- pptx2/shapes/__init__.py +26 -0
- pptx2/shapes/autoshape.py +442 -0
- pptx2/shapes/base.py +1078 -0
- pptx2/shapes/connector.py +297 -0
- pptx2/shapes/freeform.py +337 -0
- pptx2/shapes/graphfrm.py +316 -0
- pptx2/shapes/group.py +264 -0
- pptx2/shapes/picture.py +422 -0
- pptx2/shapes/placeholder.py +468 -0
- pptx2/shapes/shapetree.py +2027 -0
- pptx2/shared.py +82 -0
- pptx2/skill/SKILL.md +450 -0
- pptx2/skill/__init__.py +78 -0
- pptx2/skill/__main__.py +64 -0
- pptx2/skill/references/animations.md +189 -0
- pptx2/skill/references/basics.md +421 -0
- pptx2/skill/references/charts.md +254 -0
- pptx2/skill/references/compose.md +234 -0
- pptx2/skill/references/design.md +366 -0
- pptx2/skill/references/effects.md +249 -0
- pptx2/skill/references/end-to-end-deck.md +231 -0
- pptx2/skill/references/geometry-and-arrows.md +334 -0
- pptx2/skill/references/lint.md +275 -0
- pptx2/skill/references/math.md +86 -0
- pptx2/skill/references/picture-effects.md +129 -0
- pptx2/skill/references/render.md +151 -0
- pptx2/skill/references/smart-art.md +75 -0
- pptx2/skill/references/space-aware-authoring.md +249 -0
- pptx2/skill/references/tables.md +244 -0
- pptx2/skill/references/theme.md +127 -0
- pptx2/skill/references/three-d.md +109 -0
- pptx2/skill/references/transitions.md +100 -0
- pptx2/slide.py +1244 -0
- pptx2/smart_art.py +220 -0
- pptx2/spec.py +633 -0
- pptx2/table.py +1181 -0
- pptx2/table_styles.py +184 -0
- pptx2/templates/default.pptx +0 -0
- pptx2/templates/docx-icon.emf +0 -0
- pptx2/templates/generic-icon.emf +0 -0
- pptx2/templates/notes.xml +23 -0
- pptx2/templates/notesMaster.xml +352 -0
- pptx2/templates/pptx-icon.emf +0 -0
- pptx2/templates/theme.xml +321 -0
- pptx2/templates/xlsx-icon.emf +0 -0
- pptx2/text/__init__.py +0 -0
- pptx2/text/fonts.py +482 -0
- pptx2/text/layout.py +374 -0
- pptx2/text/text.py +1272 -0
- pptx2/theme.py +721 -0
- pptx2/types.py +36 -0
- pptx2/util.py +263 -0
- python_pptx2-2.13.0.dist-info/METADATA +351 -0
- python_pptx2-2.13.0.dist-info/RECORD +175 -0
- python_pptx2-2.13.0.dist-info/WHEEL +5 -0
- python_pptx2-2.13.0.dist-info/entry_points.txt +3 -0
- python_pptx2-2.13.0.dist-info/licenses/LICENSE +22 -0
- python_pptx2-2.13.0.dist-info/top_level.txt +1 -0
pptx2/smart_art.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""SmartArt shape access for text substitution.
|
|
2
|
+
|
|
3
|
+
Exposes :class:`SmartArtCollection` (accessed via ``slide.smart_art``) and
|
|
4
|
+
:class:`SmartArtShape`, whose :meth:`~SmartArtShape.set_text` method rewrites
|
|
5
|
+
the text nodes inside an existing ``diagrams/data#.xml`` part without touching
|
|
6
|
+
layout, style, or colour parts.
|
|
7
|
+
|
|
8
|
+
Scope: text *substitution* in existing SmartArt only. Full SmartArt creation
|
|
9
|
+
is explicitly out of scope.
|
|
10
|
+
|
|
11
|
+
Example::
|
|
12
|
+
|
|
13
|
+
slide = prs.slides[0]
|
|
14
|
+
org_chart = slide.smart_art[0]
|
|
15
|
+
print(org_chart.texts) # ['CEO', 'CTO', 'CFO']
|
|
16
|
+
org_chart.set_text(['Alice', 'Bob', 'Carol'])
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING, Iterator, Sequence
|
|
22
|
+
|
|
23
|
+
from lxml.etree import _Element as LxmlElement # pyright: ignore[reportPrivateUsage]
|
|
24
|
+
|
|
25
|
+
from pptx2.oxml.ns import _nsmap as _GLOBAL_NSMAP # pyright: ignore[reportPrivateUsage]
|
|
26
|
+
from pptx2.oxml.ns import qn
|
|
27
|
+
from pptx2.spec import GRAPHIC_DATA_URI_DIAGRAM
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pptx2.oxml.xmlchemy import BaseOxmlElement
|
|
31
|
+
|
|
32
|
+
from pptx2.parts.slide import SlidePart
|
|
33
|
+
from pptx2.slide import Slide
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Internal helpers
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
# <dgm:pt> types that carry visible text content (all others are structural)
|
|
41
|
+
_NODE_TYPES = frozenset({"node", "asst"})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _xpath(element: LxmlElement, expr: str) -> list[LxmlElement]:
|
|
45
|
+
"""Evaluate *expr* on *element* using the global namespace map.
|
|
46
|
+
|
|
47
|
+
Uses ``lxml.etree._Element.xpath`` directly so that the call always
|
|
48
|
+
receives the full ``namespaces`` keyword argument, even when *element* is
|
|
49
|
+
a ``BaseOxmlElement`` whose ``xpath`` method has an overridden signature.
|
|
50
|
+
"""
|
|
51
|
+
return LxmlElement.xpath(element, expr, namespaces=_GLOBAL_NSMAP) # type: ignore[arg-type]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _node_pts(data_element: LxmlElement) -> list[LxmlElement]:
|
|
55
|
+
"""Return all ``<dgm:pt>`` elements whose ``type`` marks them as content nodes.
|
|
56
|
+
|
|
57
|
+
The returned list is in document order, which corresponds to the visual
|
|
58
|
+
display order in most SmartArt layouts.
|
|
59
|
+
"""
|
|
60
|
+
return _xpath(data_element, "dgm:ptLst/dgm:pt[@type='node' or @type='asst']")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _text_runs(pt: LxmlElement) -> list[LxmlElement]:
|
|
64
|
+
"""Return all ``<a:t>`` leaf elements inside a ``<dgm:pt>/<dgm:t>`` text body."""
|
|
65
|
+
return _xpath(pt, "dgm:t//a:r/a:t")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Public classes
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class SmartArtShape:
|
|
74
|
+
"""Proxy for one SmartArt graphic on a slide.
|
|
75
|
+
|
|
76
|
+
Wraps a ``<p:graphicFrame>`` that encloses a SmartArt diagram and exposes
|
|
77
|
+
the diagram-data part as a mutable text surface.
|
|
78
|
+
|
|
79
|
+
Do not construct directly; obtain instances from :attr:`Slide.smart_art`.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(self, graphic_frame: BaseOxmlElement, slide_part: SlidePart) -> None:
|
|
83
|
+
self._frame = graphic_frame
|
|
84
|
+
self._slide_part = slide_part
|
|
85
|
+
|
|
86
|
+
# ------------------------------------------------------------------
|
|
87
|
+
# Public interface
|
|
88
|
+
# ------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def name(self) -> str:
|
|
92
|
+
"""Internal shape name, e.g. "SmartArt 5"."""
|
|
93
|
+
cNvPr_elms = _xpath(self._frame, ".//p:cNvPr")
|
|
94
|
+
return cNvPr_elms[0].get("name", "") if cNvPr_elms else ""
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def texts(self) -> list[str]:
|
|
98
|
+
"""Ordered list of text strings — one per content node in the diagram data.
|
|
99
|
+
|
|
100
|
+
Each string is the concatenation of all ``<a:t>`` run text inside the
|
|
101
|
+
corresponding ``<dgm:pt>`` element. The order matches the visual
|
|
102
|
+
sequence in most layouts (document order in ``<dgm:ptLst>``).
|
|
103
|
+
"""
|
|
104
|
+
data_el = self._data_element
|
|
105
|
+
result: list[str] = []
|
|
106
|
+
for pt in _node_pts(data_el):
|
|
107
|
+
runs = _text_runs(pt)
|
|
108
|
+
result.append("".join(r.text or "" for r in runs))
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
def set_text(self, values: Sequence[str], *, strict: bool = True) -> None:
|
|
112
|
+
"""Replace the text of each content node with the corresponding entry in *values*.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
values:
|
|
117
|
+
New text strings, one per content node. The order must match
|
|
118
|
+
:attr:`texts`.
|
|
119
|
+
strict:
|
|
120
|
+
When ``True`` (the default), raises :class:`ValueError` if
|
|
121
|
+
``len(values)`` does not equal the number of content nodes.
|
|
122
|
+
When ``False``, extra values are silently ignored and nodes
|
|
123
|
+
without a corresponding value are left unchanged.
|
|
124
|
+
|
|
125
|
+
Raises
|
|
126
|
+
------
|
|
127
|
+
ValueError
|
|
128
|
+
When ``strict=True`` and ``len(values) != len(self.texts)``.
|
|
129
|
+
"""
|
|
130
|
+
data_el = self._data_element
|
|
131
|
+
nodes = _node_pts(data_el)
|
|
132
|
+
|
|
133
|
+
if strict and len(values) != len(nodes):
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"set_text() received {len(values)} value(s) but this SmartArt has "
|
|
136
|
+
f"{len(nodes)} content node(s). Pass strict=False to suppress this check."
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
for i, pt in enumerate(nodes):
|
|
140
|
+
if i >= len(values):
|
|
141
|
+
break
|
|
142
|
+
new_text = str(values[i])
|
|
143
|
+
runs = _text_runs(pt)
|
|
144
|
+
if runs:
|
|
145
|
+
# Overwrite first run; clear the rest.
|
|
146
|
+
runs[0].text = new_text
|
|
147
|
+
for extra_run in runs[1:]:
|
|
148
|
+
extra_run.text = ""
|
|
149
|
+
else:
|
|
150
|
+
# No existing <a:t>; build a minimal <dgm:t>/<a:p>/<a:r>/<a:t> subtree.
|
|
151
|
+
t_body = pt.find(qn("dgm:t"))
|
|
152
|
+
if t_body is None:
|
|
153
|
+
from lxml import etree
|
|
154
|
+
|
|
155
|
+
t_body = etree.SubElement(pt, qn("dgm:t"))
|
|
156
|
+
etree.SubElement(t_body, qn("a:bodyPr"))
|
|
157
|
+
etree.SubElement(t_body, qn("a:lstStyle"))
|
|
158
|
+
from lxml import etree
|
|
159
|
+
|
|
160
|
+
p = etree.SubElement(t_body, qn("a:p"))
|
|
161
|
+
r = etree.SubElement(p, qn("a:r"))
|
|
162
|
+
t = etree.SubElement(r, qn("a:t"))
|
|
163
|
+
t.text = new_text
|
|
164
|
+
|
|
165
|
+
# ------------------------------------------------------------------
|
|
166
|
+
# Internals
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def _data_rId(self) -> str:
|
|
171
|
+
"""rId of the diagram-data relationship from the slide part."""
|
|
172
|
+
# <a:graphicData><dgm:relIds r:dm="rIdN" .../>
|
|
173
|
+
rel_ids = _xpath(self._frame, ".//dgm:relIds")
|
|
174
|
+
if not rel_ids:
|
|
175
|
+
raise ValueError("SmartArt frame has no <dgm:relIds> element.")
|
|
176
|
+
return rel_ids[0].get(qn("r:dm")) # type: ignore[return-value]
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def _data_element(self) -> BaseOxmlElement:
|
|
180
|
+
"""Root element of the diagram-data part for this SmartArt."""
|
|
181
|
+
rId = self._data_rId
|
|
182
|
+
data_part = self._slide_part.related_part(rId)
|
|
183
|
+
return data_part._element # pyright: ignore[reportPrivateUsage]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class SmartArtCollection:
|
|
187
|
+
"""Sequence of :class:`SmartArtShape` objects on a slide.
|
|
188
|
+
|
|
189
|
+
Accessed via :attr:`Slide.smart_art`. Supports indexing and iteration.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def __init__(self, slide: Slide) -> None:
|
|
193
|
+
self._slide = slide
|
|
194
|
+
|
|
195
|
+
def __len__(self) -> int:
|
|
196
|
+
return len(self._frames)
|
|
197
|
+
|
|
198
|
+
def __getitem__(self, idx: int) -> SmartArtShape:
|
|
199
|
+
frames = self._frames
|
|
200
|
+
if idx < 0 or idx >= len(frames):
|
|
201
|
+
raise IndexError(f"SmartArt index {idx} out of range (0–{len(frames) - 1})")
|
|
202
|
+
return SmartArtShape(frames[idx], self._slide.part)
|
|
203
|
+
|
|
204
|
+
def __iter__(self) -> Iterator[SmartArtShape]:
|
|
205
|
+
for frame in self._frames:
|
|
206
|
+
yield SmartArtShape(frame, self._slide.part)
|
|
207
|
+
|
|
208
|
+
def __repr__(self) -> str:
|
|
209
|
+
return f"SmartArtCollection({len(self)} SmartArt shape(s))"
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def _frames(self) -> list[BaseOxmlElement]:
|
|
213
|
+
"""All ``<p:graphicFrame>`` elements on the slide whose URI is the SmartArt URI."""
|
|
214
|
+
spTree = self._slide._element.spTree # pyright: ignore[reportPrivateUsage]
|
|
215
|
+
frames: list[BaseOxmlElement] = []
|
|
216
|
+
for gf in spTree.iter(qn("p:graphicFrame")):
|
|
217
|
+
uri_nodes = _xpath(gf, ".//a:graphicData/@uri")
|
|
218
|
+
if uri_nodes and uri_nodes[0] == GRAPHIC_DATA_URI_DIAGRAM:
|
|
219
|
+
frames.append(gf) # type: ignore[arg-type]
|
|
220
|
+
return frames
|