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
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""lxml custom element class for CT_GraphicalObjectFrame XML element."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, cast
|
|
6
|
+
|
|
7
|
+
from pptx2.oxml import parse_xml
|
|
8
|
+
from pptx2.oxml.chart.chart import CT_Chart
|
|
9
|
+
from pptx2.oxml.ns import nsdecls
|
|
10
|
+
from pptx2.oxml.shapes.shared import BaseShapeElement
|
|
11
|
+
from pptx2.oxml.simpletypes import XsdBoolean, XsdString
|
|
12
|
+
from pptx2.oxml.table import CT_Table
|
|
13
|
+
from pptx2.oxml.xmlchemy import (
|
|
14
|
+
BaseOxmlElement,
|
|
15
|
+
OneAndOnlyOne,
|
|
16
|
+
OptionalAttribute,
|
|
17
|
+
RequiredAttribute,
|
|
18
|
+
ZeroOrOne,
|
|
19
|
+
)
|
|
20
|
+
from pptx2.spec import (
|
|
21
|
+
GRAPHIC_DATA_URI_CHART,
|
|
22
|
+
GRAPHIC_DATA_URI_OLEOBJ,
|
|
23
|
+
GRAPHIC_DATA_URI_TABLE,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from pptx2.oxml.shapes.shared import (
|
|
28
|
+
CT_ApplicationNonVisualDrawingProps,
|
|
29
|
+
CT_NonVisualDrawingProps,
|
|
30
|
+
CT_Transform2D,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CT_GraphicalObject(BaseOxmlElement):
|
|
35
|
+
"""`a:graphic` element.
|
|
36
|
+
|
|
37
|
+
The container for the reference to or definition of the framed graphical object (table, chart,
|
|
38
|
+
etc.).
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
graphicData: CT_GraphicalObjectData = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
|
|
42
|
+
"a:graphicData"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def chart(self) -> CT_Chart | None:
|
|
47
|
+
"""The `c:chart` grandchild element, or |None| if not present."""
|
|
48
|
+
return self.graphicData.chart
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CT_GraphicalObjectData(BaseShapeElement):
|
|
52
|
+
"""`p:graphicData` element.
|
|
53
|
+
|
|
54
|
+
The direct container for a table, a chart, or another graphical object.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
chart: CT_Chart | None = ZeroOrOne("c:chart") # pyright: ignore[reportAssignmentType]
|
|
58
|
+
tbl: CT_Table | None = ZeroOrOne("a:tbl") # pyright: ignore[reportAssignmentType]
|
|
59
|
+
uri: str = RequiredAttribute("uri", XsdString) # pyright: ignore[reportAssignmentType]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def blob_rId(self) -> str | None:
|
|
63
|
+
"""Optional `r:id` attribute value of `p:oleObj` descendent element.
|
|
64
|
+
|
|
65
|
+
This value is `None` when this `p:graphicData` element does not enclose an OLE object.
|
|
66
|
+
This value could also be `None` if an enclosed OLE object does not specify this attribute
|
|
67
|
+
(it is specified optional in the schema) but so far, all OLE objects we've encountered
|
|
68
|
+
specify this value.
|
|
69
|
+
"""
|
|
70
|
+
return None if self._oleObj is None else self._oleObj.rId
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_embedded_ole_obj(self) -> bool | None:
|
|
74
|
+
"""Optional boolean indicating an embedded OLE object.
|
|
75
|
+
|
|
76
|
+
Returns `None` when this `p:graphicData` element does not enclose an OLE object. `True`
|
|
77
|
+
indicates an embedded OLE object and `False` indicates a linked OLE object.
|
|
78
|
+
"""
|
|
79
|
+
return None if self._oleObj is None else self._oleObj.is_embedded
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def progId(self) -> str | None:
|
|
83
|
+
"""Optional str value of "progId" attribute of `p:oleObj` descendent.
|
|
84
|
+
|
|
85
|
+
This value identifies the "type" of the embedded object in terms of the application used
|
|
86
|
+
to open it.
|
|
87
|
+
|
|
88
|
+
This value is `None` when this `p:graphicData` element does not enclose an OLE object.
|
|
89
|
+
This could also be `None` if an enclosed OLE object does not specify this attribute (it is
|
|
90
|
+
specified optional in the schema) but so far, all OLE objects we've encountered specify
|
|
91
|
+
this value.
|
|
92
|
+
"""
|
|
93
|
+
return None if self._oleObj is None else self._oleObj.progId
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def showAsIcon(self) -> bool | None:
|
|
97
|
+
"""Optional value of "showAsIcon" attribute value of `p:oleObj` descendent.
|
|
98
|
+
|
|
99
|
+
This value is `None` when this `p:graphicData` element does not enclose an OLE object. It
|
|
100
|
+
is False when the `showAsIcon` attribute is omitted on the `p:oleObj` element.
|
|
101
|
+
"""
|
|
102
|
+
return None if self._oleObj is None else self._oleObj.showAsIcon
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def _oleObj(self) -> CT_OleObject | None:
|
|
106
|
+
"""Optional `p:oleObj` element contained in this `p:graphicData' element.
|
|
107
|
+
|
|
108
|
+
Returns `None` when this graphic-data element does not enclose an OLE object. Note that
|
|
109
|
+
this returns the last `p:oleObj` element found. There can be more than one `p:oleObj`
|
|
110
|
+
element because an `mc.AlternateContent` element may appear as the child of
|
|
111
|
+
`p:graphicData` and that alternate-content subtree can contain multiple compatibility
|
|
112
|
+
choices. The last one should suit best for reading purposes because it contains the lowest
|
|
113
|
+
common denominator.
|
|
114
|
+
"""
|
|
115
|
+
oleObjs = cast("list[CT_OleObject]", self.xpath(".//p:oleObj"))
|
|
116
|
+
return oleObjs[-1] if oleObjs else None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class CT_GraphicalObjectFrame(BaseShapeElement):
|
|
120
|
+
"""`p:graphicFrame` element.
|
|
121
|
+
|
|
122
|
+
A container for a table, a chart, or another graphical object.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
nvGraphicFramePr: CT_GraphicalObjectFrameNonVisual = ( # pyright: ignore[reportAssignmentType]
|
|
126
|
+
OneAndOnlyOne("p:nvGraphicFramePr")
|
|
127
|
+
)
|
|
128
|
+
xfrm: CT_Transform2D = OneAndOnlyOne("p:xfrm") # pyright: ignore
|
|
129
|
+
graphic: CT_GraphicalObject = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
|
|
130
|
+
"a:graphic"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def chart(self) -> CT_Chart | None:
|
|
135
|
+
"""The `c:chart` great-grandchild element, or |None| if not present."""
|
|
136
|
+
return self.graphic.chart
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def chart_rId(self) -> str | None:
|
|
140
|
+
"""The `rId` attribute of the `c:chart` great-grandchild element.
|
|
141
|
+
|
|
142
|
+
|None| if not present.
|
|
143
|
+
"""
|
|
144
|
+
chart = self.chart
|
|
145
|
+
if chart is None:
|
|
146
|
+
return None
|
|
147
|
+
return chart.rId
|
|
148
|
+
|
|
149
|
+
def get_or_add_xfrm(self) -> CT_Transform2D:
|
|
150
|
+
"""Return the required `p:xfrm` child element.
|
|
151
|
+
|
|
152
|
+
Overrides version on BaseShapeElement.
|
|
153
|
+
"""
|
|
154
|
+
return self.xfrm
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def graphicData(self) -> CT_GraphicalObjectData:
|
|
158
|
+
"""`a:graphicData` grandchild of this graphic-frame element."""
|
|
159
|
+
return self.graphic.graphicData
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def graphicData_uri(self) -> str:
|
|
163
|
+
"""str value of `uri` attribute of `a:graphicData` grandchild."""
|
|
164
|
+
return self.graphic.graphicData.uri
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def has_oleobj(self) -> bool:
|
|
168
|
+
"""`True` for graphicFrame containing an OLE object, `False` otherwise."""
|
|
169
|
+
return self.graphicData.uri == GRAPHIC_DATA_URI_OLEOBJ
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def is_embedded_ole_obj(self) -> bool | None:
|
|
173
|
+
"""Optional boolean indicating an embedded OLE object.
|
|
174
|
+
|
|
175
|
+
Returns `None` when this `p:graphicFrame` element does not enclose an OLE object. `True`
|
|
176
|
+
indicates an embedded OLE object and `False` indicates a linked OLE object.
|
|
177
|
+
"""
|
|
178
|
+
return self.graphicData.is_embedded_ole_obj
|
|
179
|
+
|
|
180
|
+
@classmethod
|
|
181
|
+
def new_chart_graphicFrame(
|
|
182
|
+
cls, id_: int, name: str, rId: str, x: int, y: int, cx: int, cy: int
|
|
183
|
+
) -> CT_GraphicalObjectFrame:
|
|
184
|
+
"""Return a `p:graphicFrame` element tree populated with a chart element."""
|
|
185
|
+
graphicFrame = CT_GraphicalObjectFrame.new_graphicFrame(id_, name, x, y, cx, cy)
|
|
186
|
+
graphicData = graphicFrame.graphic.graphicData
|
|
187
|
+
graphicData.uri = GRAPHIC_DATA_URI_CHART
|
|
188
|
+
graphicData.append(CT_Chart.new_chart(rId))
|
|
189
|
+
return graphicFrame
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def new_graphicFrame(
|
|
193
|
+
cls, id_: int, name: str, x: int, y: int, cx: int, cy: int
|
|
194
|
+
) -> CT_GraphicalObjectFrame:
|
|
195
|
+
"""Return a new `p:graphicFrame` element tree suitable for containing a table or chart.
|
|
196
|
+
|
|
197
|
+
Note that a graphicFrame element is not a valid shape until it contains a graphical object
|
|
198
|
+
such as a table.
|
|
199
|
+
"""
|
|
200
|
+
return cast(
|
|
201
|
+
CT_GraphicalObjectFrame,
|
|
202
|
+
parse_xml(
|
|
203
|
+
f"<p:graphicFrame {nsdecls('a', 'p')}>\n"
|
|
204
|
+
f" <p:nvGraphicFramePr>\n"
|
|
205
|
+
f' <p:cNvPr id="{id_}" name="{name}"/>\n'
|
|
206
|
+
f" <p:cNvGraphicFramePr>\n"
|
|
207
|
+
f' <a:graphicFrameLocks noGrp="1"/>\n'
|
|
208
|
+
f" </p:cNvGraphicFramePr>\n"
|
|
209
|
+
f" <p:nvPr/>\n"
|
|
210
|
+
f" </p:nvGraphicFramePr>\n"
|
|
211
|
+
f" <p:xfrm>\n"
|
|
212
|
+
f' <a:off x="{x}" y="{y}"/>\n'
|
|
213
|
+
f' <a:ext cx="{cx}" cy="{cy}"/>\n'
|
|
214
|
+
f" </p:xfrm>\n"
|
|
215
|
+
f" <a:graphic>\n"
|
|
216
|
+
f" <a:graphicData/>\n"
|
|
217
|
+
f" </a:graphic>\n"
|
|
218
|
+
f"</p:graphicFrame>"
|
|
219
|
+
),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
@classmethod
|
|
223
|
+
def new_ole_object_graphicFrame(
|
|
224
|
+
cls,
|
|
225
|
+
id_: int,
|
|
226
|
+
name: str,
|
|
227
|
+
ole_object_rId: str,
|
|
228
|
+
progId: str,
|
|
229
|
+
icon_rId: str,
|
|
230
|
+
x: int,
|
|
231
|
+
y: int,
|
|
232
|
+
cx: int,
|
|
233
|
+
cy: int,
|
|
234
|
+
imgW: int,
|
|
235
|
+
imgH: int,
|
|
236
|
+
pic_id: int,
|
|
237
|
+
) -> CT_GraphicalObjectFrame:
|
|
238
|
+
"""Return newly-created `p:graphicFrame` for embedded OLE-object.
|
|
239
|
+
|
|
240
|
+
`ole_object_rId` identifies the relationship to the OLE-object part.
|
|
241
|
+
|
|
242
|
+
`progId` is a str identifying the object-type in terms of the application (program) used
|
|
243
|
+
to open it. This becomes an attribute of the same name in the `p:oleObj` element.
|
|
244
|
+
|
|
245
|
+
`icon_rId` identifies the relationship to an image part used to display the OLE-object as
|
|
246
|
+
an icon (vs. a preview).
|
|
247
|
+
|
|
248
|
+
`pic_id` is the shape id for the inner "show-as-icon" ``p:pic``. It must be unique within
|
|
249
|
+
the slide: two OLE objects sharing the hardcoded ``id="0"`` used previously collide on a
|
|
250
|
+
duplicate shape id, which PowerPoint reports as a file needing repair.
|
|
251
|
+
"""
|
|
252
|
+
return cast(
|
|
253
|
+
CT_GraphicalObjectFrame,
|
|
254
|
+
parse_xml(
|
|
255
|
+
f"<p:graphicFrame {nsdecls('a', 'p', 'r')}>\n"
|
|
256
|
+
f" <p:nvGraphicFramePr>\n"
|
|
257
|
+
f' <p:cNvPr id="{id_}" name="{name}"/>\n'
|
|
258
|
+
f" <p:cNvGraphicFramePr>\n"
|
|
259
|
+
f' <a:graphicFrameLocks noGrp="1"/>\n'
|
|
260
|
+
f" </p:cNvGraphicFramePr>\n"
|
|
261
|
+
f" <p:nvPr/>\n"
|
|
262
|
+
f" </p:nvGraphicFramePr>\n"
|
|
263
|
+
f" <p:xfrm>\n"
|
|
264
|
+
f' <a:off x="{x}" y="{y}"/>\n'
|
|
265
|
+
f' <a:ext cx="{cx}" cy="{cy}"/>\n'
|
|
266
|
+
f" </p:xfrm>\n"
|
|
267
|
+
f" <a:graphic>\n"
|
|
268
|
+
f" <a:graphicData"
|
|
269
|
+
f' uri="http://schemas.openxmlformats.org/presentationml/2006/ole">\n'
|
|
270
|
+
f' <p:oleObj showAsIcon="1"'
|
|
271
|
+
f' r:id="{ole_object_rId}"'
|
|
272
|
+
f' imgW="{imgW}"'
|
|
273
|
+
f' imgH="{imgH}"'
|
|
274
|
+
f' progId="{progId}">\n'
|
|
275
|
+
f" <p:embed/>\n"
|
|
276
|
+
f" <p:pic>\n"
|
|
277
|
+
f" <p:nvPicPr>\n"
|
|
278
|
+
f' <p:cNvPr id="{pic_id}" name=""/>\n'
|
|
279
|
+
f" <p:cNvPicPr/>\n"
|
|
280
|
+
f" <p:nvPr/>\n"
|
|
281
|
+
f" </p:nvPicPr>\n"
|
|
282
|
+
f" <p:blipFill>\n"
|
|
283
|
+
f' <a:blip r:embed="{icon_rId}"/>\n'
|
|
284
|
+
f" <a:stretch>\n"
|
|
285
|
+
f" <a:fillRect/>\n"
|
|
286
|
+
f" </a:stretch>\n"
|
|
287
|
+
f" </p:blipFill>\n"
|
|
288
|
+
f" <p:spPr>\n"
|
|
289
|
+
f" <a:xfrm>\n"
|
|
290
|
+
f' <a:off x="{x}" y="{y}"/>\n'
|
|
291
|
+
f' <a:ext cx="{cx}" cy="{cy}"/>\n'
|
|
292
|
+
f" </a:xfrm>\n"
|
|
293
|
+
f' <a:prstGeom prst="rect">\n'
|
|
294
|
+
f" <a:avLst/>\n"
|
|
295
|
+
f" </a:prstGeom>\n"
|
|
296
|
+
f" </p:spPr>\n"
|
|
297
|
+
f" </p:pic>\n"
|
|
298
|
+
f" </p:oleObj>\n"
|
|
299
|
+
f" </a:graphicData>\n"
|
|
300
|
+
f" </a:graphic>\n"
|
|
301
|
+
f"</p:graphicFrame>"
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
def new_table_graphicFrame(
|
|
307
|
+
cls, id_: int, name: str, rows: int, cols: int, x: int, y: int, cx: int, cy: int
|
|
308
|
+
) -> CT_GraphicalObjectFrame:
|
|
309
|
+
"""Return a `p:graphicFrame` element tree populated with a table element."""
|
|
310
|
+
graphicFrame = cls.new_graphicFrame(id_, name, x, y, cx, cy)
|
|
311
|
+
graphicFrame.graphic.graphicData.uri = GRAPHIC_DATA_URI_TABLE
|
|
312
|
+
graphicFrame.graphic.graphicData.append(CT_Table.new_tbl(rows, cols, cx, cy))
|
|
313
|
+
return graphicFrame
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class CT_GraphicalObjectFrameNonVisual(BaseOxmlElement):
|
|
317
|
+
"""`p:nvGraphicFramePr` element.
|
|
318
|
+
|
|
319
|
+
This contains the non-visual properties of a graphic frame, such as name, id, etc.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
cNvPr: CT_NonVisualDrawingProps = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
|
|
323
|
+
"p:cNvPr"
|
|
324
|
+
)
|
|
325
|
+
nvPr: CT_ApplicationNonVisualDrawingProps = ( # pyright: ignore[reportAssignmentType]
|
|
326
|
+
OneAndOnlyOne("p:nvPr")
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class CT_OleObject(BaseOxmlElement):
|
|
331
|
+
"""`p:oleObj` element, container for an OLE object (e.g. Excel file).
|
|
332
|
+
|
|
333
|
+
An OLE object can be either linked or embedded (hence the name).
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
progId: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
|
|
337
|
+
"progId", XsdString
|
|
338
|
+
)
|
|
339
|
+
rId: str | None = OptionalAttribute("r:id", XsdString) # pyright: ignore[reportAssignmentType]
|
|
340
|
+
showAsIcon: bool = OptionalAttribute( # pyright: ignore[reportAssignmentType]
|
|
341
|
+
"showAsIcon", XsdBoolean, default=False
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def is_embedded(self) -> bool:
|
|
346
|
+
"""True when this OLE object is embedded, False when it is linked."""
|
|
347
|
+
return len(self.xpath("./p:embed")) > 0
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""lxml custom element classes for shape-tree-related XML elements."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Callable, Iterator
|
|
6
|
+
|
|
7
|
+
from pptx2.dml.fill import CT_GradientFillProperties
|
|
8
|
+
from pptx2.enum.shapes import MSO_CONNECTOR_TYPE
|
|
9
|
+
from pptx2.oxml import parse_xml
|
|
10
|
+
from pptx2.oxml.ns import nsdecls, qn
|
|
11
|
+
from pptx2.oxml.shapes.autoshape import CT_Shape
|
|
12
|
+
from pptx2.oxml.shapes.connector import CT_Connector
|
|
13
|
+
from pptx2.oxml.shapes.graphfrm import CT_GraphicalObjectFrame
|
|
14
|
+
from pptx2.oxml.shapes.picture import CT_Picture
|
|
15
|
+
from pptx2.oxml.shapes.shared import BaseShapeElement
|
|
16
|
+
from pptx2.oxml.xmlchemy import (
|
|
17
|
+
BaseOxmlElement,
|
|
18
|
+
Choice,
|
|
19
|
+
OneAndOnlyOne,
|
|
20
|
+
ZeroOrOne,
|
|
21
|
+
ZeroOrOneChoice,
|
|
22
|
+
)
|
|
23
|
+
from pptx2.util import Emu
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from pptx2.enum.shapes import PP_PLACEHOLDER
|
|
27
|
+
from pptx2.oxml.shapes import ShapeElement
|
|
28
|
+
from pptx2.oxml.shapes.shared import CT_Transform2D
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CT_GroupShape(BaseShapeElement):
|
|
32
|
+
"""Used for shape tree (`p:spTree`) as well as the group shape (`p:grpSp`) elements."""
|
|
33
|
+
|
|
34
|
+
nvGrpSpPr: CT_GroupShapeNonVisual = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
|
|
35
|
+
"p:nvGrpSpPr"
|
|
36
|
+
)
|
|
37
|
+
grpSpPr: CT_GroupShapeProperties = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
|
|
38
|
+
"p:grpSpPr"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
_shape_tags = (
|
|
42
|
+
qn("p:sp"),
|
|
43
|
+
qn("p:grpSp"),
|
|
44
|
+
qn("p:graphicFrame"),
|
|
45
|
+
qn("p:cxnSp"),
|
|
46
|
+
qn("p:pic"),
|
|
47
|
+
qn("p:contentPart"),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def add_autoshape(
|
|
51
|
+
self, id_: int, name: str, prst: str, x: int, y: int, cx: int, cy: int
|
|
52
|
+
) -> CT_Shape:
|
|
53
|
+
"""Return new `p:sp` appended to the group/shapetree with specified attributes."""
|
|
54
|
+
sp = CT_Shape.new_autoshape_sp(id_, name, prst, x, y, cx, cy)
|
|
55
|
+
self.insert_element_before(sp, "p:extLst")
|
|
56
|
+
return sp
|
|
57
|
+
|
|
58
|
+
def add_cxnSp(
|
|
59
|
+
self,
|
|
60
|
+
id_: int,
|
|
61
|
+
name: str,
|
|
62
|
+
type_member: MSO_CONNECTOR_TYPE,
|
|
63
|
+
x: int,
|
|
64
|
+
y: int,
|
|
65
|
+
cx: int,
|
|
66
|
+
cy: int,
|
|
67
|
+
flipH: bool,
|
|
68
|
+
flipV: bool,
|
|
69
|
+
) -> CT_Connector:
|
|
70
|
+
"""Return new `p:cxnSp` appended to the group/shapetree with the specified attribues."""
|
|
71
|
+
prst = MSO_CONNECTOR_TYPE.to_xml(type_member)
|
|
72
|
+
cxnSp = CT_Connector.new_cxnSp(id_, name, prst, x, y, cx, cy, flipH, flipV)
|
|
73
|
+
self.insert_element_before(cxnSp, "p:extLst")
|
|
74
|
+
return cxnSp
|
|
75
|
+
|
|
76
|
+
def add_freeform_sp(self, x: int, y: int, cx: int, cy: int) -> CT_Shape:
|
|
77
|
+
"""Append a new freeform `p:sp` with specified position and size."""
|
|
78
|
+
shape_id = self._next_shape_id
|
|
79
|
+
name = "Freeform %d" % (shape_id - 1,)
|
|
80
|
+
sp = CT_Shape.new_freeform_sp(shape_id, name, x, y, cx, cy)
|
|
81
|
+
self.insert_element_before(sp, "p:extLst")
|
|
82
|
+
return sp
|
|
83
|
+
|
|
84
|
+
def add_grpSp(self) -> CT_GroupShape:
|
|
85
|
+
"""Return `p:grpSp` element newly appended to this shape tree.
|
|
86
|
+
|
|
87
|
+
The element contains no sub-shapes, is positioned at (0, 0), and has
|
|
88
|
+
width and height of zero.
|
|
89
|
+
"""
|
|
90
|
+
shape_id = self._next_shape_id
|
|
91
|
+
name = "Group %d" % (shape_id - 1,)
|
|
92
|
+
grpSp = CT_GroupShape.new_grpSp(shape_id, name)
|
|
93
|
+
self.insert_element_before(grpSp, "p:extLst")
|
|
94
|
+
return grpSp
|
|
95
|
+
|
|
96
|
+
def add_pic(
|
|
97
|
+
self, id_: int, name: str, desc: str, rId: str, x: int, y: int, cx: int, cy: int
|
|
98
|
+
) -> CT_Picture:
|
|
99
|
+
"""Append a `p:pic` shape to the group/shapetree having properties as specified in call."""
|
|
100
|
+
pic = CT_Picture.new_pic(id_, name, desc, rId, x, y, cx, cy)
|
|
101
|
+
self.insert_element_before(pic, "p:extLst")
|
|
102
|
+
return pic
|
|
103
|
+
|
|
104
|
+
def add_placeholder(
|
|
105
|
+
self, id_: int, name: str, ph_type: PP_PLACEHOLDER, orient: str, sz: str, idx: int
|
|
106
|
+
) -> CT_Shape:
|
|
107
|
+
"""Append a newly-created placeholder `p:sp` shape having the specified properties."""
|
|
108
|
+
sp = CT_Shape.new_placeholder_sp(id_, name, ph_type, orient, sz, idx)
|
|
109
|
+
self.insert_element_before(sp, "p:extLst")
|
|
110
|
+
return sp
|
|
111
|
+
|
|
112
|
+
def add_table(
|
|
113
|
+
self, id_: int, name: str, rows: int, cols: int, x: int, y: int, cx: int, cy: int
|
|
114
|
+
) -> CT_GraphicalObjectFrame:
|
|
115
|
+
"""Append a `p:graphicFrame` shape containing a table as specified in call."""
|
|
116
|
+
graphicFrame = CT_GraphicalObjectFrame.new_table_graphicFrame(
|
|
117
|
+
id_, name, rows, cols, x, y, cx, cy
|
|
118
|
+
)
|
|
119
|
+
self.insert_element_before(graphicFrame, "p:extLst")
|
|
120
|
+
return graphicFrame
|
|
121
|
+
|
|
122
|
+
def add_textbox(self, id_: int, name: str, x: int, y: int, cx: int, cy: int) -> CT_Shape:
|
|
123
|
+
"""Append a newly-created textbox `p:sp` shape having the specified position and size."""
|
|
124
|
+
sp = CT_Shape.new_textbox_sp(id_, name, x, y, cx, cy)
|
|
125
|
+
self.insert_element_before(sp, "p:extLst")
|
|
126
|
+
return sp
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def chExt(self):
|
|
130
|
+
"""Descendent `p:grpSpPr/a:xfrm/a:chExt` element."""
|
|
131
|
+
return self.grpSpPr.get_or_add_xfrm().get_or_add_chExt()
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def chOff(self):
|
|
135
|
+
"""Descendent `p:grpSpPr/a:xfrm/a:chOff` element."""
|
|
136
|
+
return self.grpSpPr.get_or_add_xfrm().get_or_add_chOff()
|
|
137
|
+
|
|
138
|
+
def get_or_add_xfrm(self) -> CT_Transform2D:
|
|
139
|
+
"""Return the `a:xfrm` grandchild element, newly-added if not present."""
|
|
140
|
+
return self.grpSpPr.get_or_add_xfrm()
|
|
141
|
+
|
|
142
|
+
def iter_ph_elms(self):
|
|
143
|
+
"""Generate each placeholder shape child element in document order."""
|
|
144
|
+
for e in self.iter_shape_elms():
|
|
145
|
+
if e.has_ph_elm:
|
|
146
|
+
yield e
|
|
147
|
+
|
|
148
|
+
def iter_shape_elms(self) -> Iterator[ShapeElement]:
|
|
149
|
+
"""Generate each child of this `p:spTree` element that corresponds to a shape.
|
|
150
|
+
|
|
151
|
+
Items appear in XML document order.
|
|
152
|
+
"""
|
|
153
|
+
for elm in self.iterchildren():
|
|
154
|
+
if elm.tag in self._shape_tags:
|
|
155
|
+
yield elm
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def max_shape_id(self) -> int:
|
|
159
|
+
"""Maximum int value assigned as @id in this slide.
|
|
160
|
+
|
|
161
|
+
This is generally a shape-id, but ids can be assigned to other
|
|
162
|
+
objects so we just check all @id values anywhere in the document
|
|
163
|
+
(XML id-values have document scope).
|
|
164
|
+
|
|
165
|
+
In practice, its minimum value is 1 because the spTree element itself
|
|
166
|
+
is always assigned id="1".
|
|
167
|
+
|
|
168
|
+
First access scans the document; the result is then cached on the
|
|
169
|
+
document-root element and kept current as ids are allocated via
|
|
170
|
+
:meth:`allocate_shape_id`.
|
|
171
|
+
"""
|
|
172
|
+
return self._ensure_id_cache()
|
|
173
|
+
|
|
174
|
+
def allocate_shape_id(self) -> int:
|
|
175
|
+
"""Reserve and return the next unused id for a new shape.
|
|
176
|
+
|
|
177
|
+
Caches the running maximum on the document-root element so consecutive
|
|
178
|
+
adds are O(1) instead of triggering a fresh ``//@id`` scan each call
|
|
179
|
+
(which is what produced the historical O(N²) behavior over a slide).
|
|
180
|
+
Callers that mutate the document outside the shape-tree API can call
|
|
181
|
+
:meth:`invalidate_shape_id_cache` to force a re-scan.
|
|
182
|
+
"""
|
|
183
|
+
cursor = self._ensure_id_cache() + 1
|
|
184
|
+
self._set_id_cache(cursor)
|
|
185
|
+
return cursor
|
|
186
|
+
|
|
187
|
+
def invalidate_shape_id_cache(self) -> None:
|
|
188
|
+
"""Drop the cached max-shape-id; next access will re-scan ``//@id``."""
|
|
189
|
+
root = self.getroottree().getroot()
|
|
190
|
+
if hasattr(root, "_pptx_shape_id_cursor"):
|
|
191
|
+
delattr(root, "_pptx_shape_id_cursor")
|
|
192
|
+
|
|
193
|
+
def _ensure_id_cache(self) -> int:
|
|
194
|
+
"""Return the cached max shape id, populating it on first access."""
|
|
195
|
+
root = self.getroottree().getroot()
|
|
196
|
+
cursor = getattr(root, "_pptx_shape_id_cursor", None)
|
|
197
|
+
if cursor is None:
|
|
198
|
+
used_ids = (int(s) for s in self.xpath("//@id") if s.isdigit())
|
|
199
|
+
cursor = max(used_ids, default=0)
|
|
200
|
+
self._set_id_cache(cursor)
|
|
201
|
+
return cursor
|
|
202
|
+
|
|
203
|
+
def _set_id_cache(self, value: int) -> None:
|
|
204
|
+
self.getroottree().getroot()._pptx_shape_id_cursor = value
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def new_grpSp(cls, id_: int, name: str) -> CT_GroupShape:
|
|
208
|
+
"""Return new "loose" `p:grpSp` element having `id_` and `name`."""
|
|
209
|
+
xml = (
|
|
210
|
+
"<p:grpSp %s>\n"
|
|
211
|
+
" <p:nvGrpSpPr>\n"
|
|
212
|
+
' <p:cNvPr id="%%d" name="%%s"/>\n'
|
|
213
|
+
" <p:cNvGrpSpPr/>\n"
|
|
214
|
+
" <p:nvPr/>\n"
|
|
215
|
+
" </p:nvGrpSpPr>\n"
|
|
216
|
+
" <p:grpSpPr>\n"
|
|
217
|
+
" <a:xfrm>\n"
|
|
218
|
+
' <a:off x="0" y="0"/>\n'
|
|
219
|
+
' <a:ext cx="0" cy="0"/>\n'
|
|
220
|
+
' <a:chOff x="0" y="0"/>\n'
|
|
221
|
+
' <a:chExt cx="0" cy="0"/>\n'
|
|
222
|
+
" </a:xfrm>\n"
|
|
223
|
+
" </p:grpSpPr>\n"
|
|
224
|
+
"</p:grpSp>" % nsdecls("a", "p", "r")
|
|
225
|
+
) % (id_, name)
|
|
226
|
+
grpSp = parse_xml(xml)
|
|
227
|
+
return grpSp
|
|
228
|
+
|
|
229
|
+
def recalculate_extents(self) -> None:
|
|
230
|
+
"""Adjust x, y, cx, and cy to incorporate all contained shapes.
|
|
231
|
+
|
|
232
|
+
This would typically be called when a contained shape is added,
|
|
233
|
+
removed, or its position or size updated.
|
|
234
|
+
|
|
235
|
+
This method is recursive "upwards" since a change in a group shape
|
|
236
|
+
can change the position and size of its containing group.
|
|
237
|
+
"""
|
|
238
|
+
if not self.tag == qn("p:grpSp"):
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
x, y, cx, cy = self._child_extents
|
|
242
|
+
|
|
243
|
+
self.chOff.x = self.x = x
|
|
244
|
+
self.chOff.y = self.y = y
|
|
245
|
+
self.chExt.cx = self.cx = cx
|
|
246
|
+
self.chExt.cy = self.cy = cy
|
|
247
|
+
self.getparent().recalculate_extents()
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def xfrm(self) -> CT_Transform2D | None:
|
|
251
|
+
"""The `a:xfrm` grandchild element or |None| if not found."""
|
|
252
|
+
return self.grpSpPr.xfrm
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def _child_extents(self) -> tuple[int, int, int, int]:
|
|
256
|
+
"""(x, y, cx, cy) tuple representing net position and size.
|
|
257
|
+
|
|
258
|
+
The values are formed as a composite of the contained child shapes.
|
|
259
|
+
"""
|
|
260
|
+
child_shape_elms = list(self.iter_shape_elms())
|
|
261
|
+
|
|
262
|
+
if not child_shape_elms:
|
|
263
|
+
return Emu(0), Emu(0), Emu(0), Emu(0)
|
|
264
|
+
|
|
265
|
+
min_x = min([xSp.x for xSp in child_shape_elms])
|
|
266
|
+
min_y = min([xSp.y for xSp in child_shape_elms])
|
|
267
|
+
max_x = max([(xSp.x + xSp.cx) for xSp in child_shape_elms])
|
|
268
|
+
max_y = max([(xSp.y + xSp.cy) for xSp in child_shape_elms])
|
|
269
|
+
|
|
270
|
+
x = min_x
|
|
271
|
+
y = min_y
|
|
272
|
+
cx = max_x - min_x
|
|
273
|
+
cy = max_y - min_y
|
|
274
|
+
|
|
275
|
+
return x, y, cx, cy
|
|
276
|
+
|
|
277
|
+
@property
|
|
278
|
+
def _next_shape_id(self) -> int:
|
|
279
|
+
"""Return unique shape id suitable for use with a new shape element.
|
|
280
|
+
|
|
281
|
+
Equivalent to :meth:`allocate_shape_id`; preserved for OXML-internal
|
|
282
|
+
callers (`add_freeform_sp`, `add_grpSp`).
|
|
283
|
+
"""
|
|
284
|
+
return self.allocate_shape_id()
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class CT_GroupShapeNonVisual(BaseShapeElement):
|
|
288
|
+
"""`p:nvGrpSpPr` element."""
|
|
289
|
+
|
|
290
|
+
cNvPr = OneAndOnlyOne("p:cNvPr")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class CT_GroupShapeProperties(BaseOxmlElement):
|
|
294
|
+
"""p:grpSpPr element"""
|
|
295
|
+
|
|
296
|
+
get_or_add_xfrm: Callable[[], CT_Transform2D]
|
|
297
|
+
|
|
298
|
+
_tag_seq = (
|
|
299
|
+
"a:xfrm",
|
|
300
|
+
"a:noFill",
|
|
301
|
+
"a:solidFill",
|
|
302
|
+
"a:gradFill",
|
|
303
|
+
"a:blipFill",
|
|
304
|
+
"a:pattFill",
|
|
305
|
+
"a:grpFill",
|
|
306
|
+
"a:effectLst",
|
|
307
|
+
"a:effectDag",
|
|
308
|
+
"a:scene3d",
|
|
309
|
+
"a:extLst",
|
|
310
|
+
)
|
|
311
|
+
xfrm: CT_Transform2D | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
|
|
312
|
+
"a:xfrm", successors=_tag_seq[1:]
|
|
313
|
+
)
|
|
314
|
+
eg_fillProperties = ZeroOrOneChoice(
|
|
315
|
+
(
|
|
316
|
+
Choice("a:noFill"),
|
|
317
|
+
Choice("a:solidFill"),
|
|
318
|
+
Choice("a:gradFill"),
|
|
319
|
+
Choice("a:blipFill"),
|
|
320
|
+
Choice("a:pattFill"),
|
|
321
|
+
Choice("a:grpFill"),
|
|
322
|
+
),
|
|
323
|
+
successors=_tag_seq[7:],
|
|
324
|
+
)
|
|
325
|
+
effectLst = ZeroOrOne("a:effectLst", successors=_tag_seq[8:])
|
|
326
|
+
del _tag_seq
|
|
327
|
+
|
|
328
|
+
def _new_gradFill(self):
|
|
329
|
+
return CT_GradientFillProperties.new_gradFill()
|