ps-python-pptx 1.1.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.
Files changed (117) hide show
  1. pptx/__init__.py +84 -0
  2. pptx/action.py +270 -0
  3. pptx/api.py +49 -0
  4. pptx/chart/__init__.py +0 -0
  5. pptx/chart/axis.py +523 -0
  6. pptx/chart/category.py +200 -0
  7. pptx/chart/chart.py +280 -0
  8. pptx/chart/data.py +864 -0
  9. pptx/chart/datalabel.py +288 -0
  10. pptx/chart/legend.py +79 -0
  11. pptx/chart/marker.py +70 -0
  12. pptx/chart/plot.py +412 -0
  13. pptx/chart/point.py +101 -0
  14. pptx/chart/series.py +258 -0
  15. pptx/chart/xlsx.py +272 -0
  16. pptx/chart/xmlwriter.py +1840 -0
  17. pptx/dml/__init__.py +0 -0
  18. pptx/dml/chtfmt.py +40 -0
  19. pptx/dml/color.py +301 -0
  20. pptx/dml/effect.py +41 -0
  21. pptx/dml/fill.py +468 -0
  22. pptx/dml/line.py +100 -0
  23. pptx/enum/__init__.py +0 -0
  24. pptx/enum/action.py +71 -0
  25. pptx/enum/base.py +175 -0
  26. pptx/enum/chart.py +492 -0
  27. pptx/enum/dml.py +405 -0
  28. pptx/enum/lang.py +685 -0
  29. pptx/enum/shapes.py +1029 -0
  30. pptx/enum/text.py +371 -0
  31. pptx/exc.py +23 -0
  32. pptx/media.py +197 -0
  33. pptx/opc/__init__.py +0 -0
  34. pptx/opc/constants.py +331 -0
  35. pptx/opc/oxml.py +188 -0
  36. pptx/opc/package.py +762 -0
  37. pptx/opc/packuri.py +109 -0
  38. pptx/opc/serialized.py +296 -0
  39. pptx/opc/shared.py +20 -0
  40. pptx/opc/spec.py +44 -0
  41. pptx/oxml/__init__.py +492 -0
  42. pptx/oxml/action.py +53 -0
  43. pptx/oxml/chart/__init__.py +0 -0
  44. pptx/oxml/chart/axis.py +297 -0
  45. pptx/oxml/chart/chart.py +282 -0
  46. pptx/oxml/chart/datalabel.py +252 -0
  47. pptx/oxml/chart/legend.py +72 -0
  48. pptx/oxml/chart/marker.py +61 -0
  49. pptx/oxml/chart/plot.py +345 -0
  50. pptx/oxml/chart/series.py +254 -0
  51. pptx/oxml/chart/shared.py +219 -0
  52. pptx/oxml/coreprops.py +288 -0
  53. pptx/oxml/dml/__init__.py +0 -0
  54. pptx/oxml/dml/color.py +111 -0
  55. pptx/oxml/dml/fill.py +197 -0
  56. pptx/oxml/dml/line.py +12 -0
  57. pptx/oxml/ns.py +129 -0
  58. pptx/oxml/presentation.py +130 -0
  59. pptx/oxml/shapes/__init__.py +19 -0
  60. pptx/oxml/shapes/autoshape.py +455 -0
  61. pptx/oxml/shapes/connector.py +107 -0
  62. pptx/oxml/shapes/graphfrm.py +342 -0
  63. pptx/oxml/shapes/groupshape.py +280 -0
  64. pptx/oxml/shapes/picture.py +270 -0
  65. pptx/oxml/shapes/shared.py +523 -0
  66. pptx/oxml/simpletypes.py +740 -0
  67. pptx/oxml/slide.py +347 -0
  68. pptx/oxml/table.py +588 -0
  69. pptx/oxml/text.py +748 -0
  70. pptx/oxml/theme.py +29 -0
  71. pptx/oxml/xmlchemy.py +717 -0
  72. pptx/package.py +222 -0
  73. pptx/parts/__init__.py +0 -0
  74. pptx/parts/chart.py +95 -0
  75. pptx/parts/coreprops.py +167 -0
  76. pptx/parts/embeddedpackage.py +93 -0
  77. pptx/parts/image.py +275 -0
  78. pptx/parts/media.py +37 -0
  79. pptx/parts/presentation.py +126 -0
  80. pptx/parts/slide.py +297 -0
  81. pptx/parts/theme.py +13 -0
  82. pptx/presentation.py +113 -0
  83. pptx/py.typed +0 -0
  84. pptx/shapes/__init__.py +26 -0
  85. pptx/shapes/autoshape.py +355 -0
  86. pptx/shapes/base.py +293 -0
  87. pptx/shapes/connector.py +297 -0
  88. pptx/shapes/freeform.py +337 -0
  89. pptx/shapes/graphfrm.py +166 -0
  90. pptx/shapes/group.py +69 -0
  91. pptx/shapes/picture.py +203 -0
  92. pptx/shapes/placeholder.py +407 -0
  93. pptx/shapes/shapetree.py +1190 -0
  94. pptx/shared.py +82 -0
  95. pptx/slide.py +577 -0
  96. pptx/spec.py +632 -0
  97. pptx/table.py +520 -0
  98. pptx/templates/default.pptx +0 -0
  99. pptx/templates/docx-icon.emf +0 -0
  100. pptx/templates/generic-icon.emf +0 -0
  101. pptx/templates/notes.xml +23 -0
  102. pptx/templates/notesMaster.xml +352 -0
  103. pptx/templates/pptx-icon.emf +0 -0
  104. pptx/templates/theme.xml +321 -0
  105. pptx/templates/xlsx-icon.emf +0 -0
  106. pptx/text/__init__.py +0 -0
  107. pptx/text/fonts.py +399 -0
  108. pptx/text/layout.py +325 -0
  109. pptx/text/text.py +770 -0
  110. pptx/theme.py +135 -0
  111. pptx/types.py +36 -0
  112. pptx/util.py +284 -0
  113. ps_python_pptx-1.1.0.dist-info/METADATA +75 -0
  114. ps_python_pptx-1.1.0.dist-info/RECORD +117 -0
  115. ps_python_pptx-1.1.0.dist-info/WHEEL +5 -0
  116. ps_python_pptx-1.1.0.dist-info/licenses/LICENSE +20 -0
  117. ps_python_pptx-1.1.0.dist-info/top_level.txt +1 -0
pptx/shared.py ADDED
@@ -0,0 +1,82 @@
1
+ """Objects shared by pptx modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from pptx.opc.package import XmlPart
9
+ from pptx.oxml.xmlchemy import BaseOxmlElement
10
+ from pptx.types import ProvidesPart
11
+
12
+
13
+ class ElementProxy(object):
14
+ """Base class for lxml element proxy classes.
15
+
16
+ An element proxy class is one whose primary responsibilities are fulfilled by manipulating the
17
+ attributes and child elements of an XML element. They are the most common type of class in
18
+ python-pptx other than custom element (oxml) classes.
19
+ """
20
+
21
+ def __init__(self, element: BaseOxmlElement):
22
+ self._element = element
23
+
24
+ def __eq__(self, other: object) -> bool:
25
+ """Return |True| if this proxy object refers to the same oxml element as does *other*.
26
+
27
+ ElementProxy objects are value objects and should maintain no mutable local state.
28
+ Equality for proxy objects is defined as referring to the same XML element, whether or not
29
+ they are the same proxy object instance.
30
+ """
31
+ if not isinstance(other, ElementProxy):
32
+ return False
33
+ return self._element is other._element
34
+
35
+ def __ne__(self, other: object) -> bool:
36
+ if not isinstance(other, ElementProxy):
37
+ return True
38
+ return self._element is not other._element
39
+
40
+ @property
41
+ def element(self):
42
+ """The lxml element proxied by this object."""
43
+ return self._element
44
+
45
+
46
+ class ParentedElementProxy(ElementProxy):
47
+ """Provides access to ancestor objects and part.
48
+
49
+ An ancestor may occasionally be required to provide a service, such as add or drop a
50
+ relationship. Provides the :attr:`_parent` attribute to subclasses and the public
51
+ :attr:`parent` read-only property.
52
+ """
53
+
54
+ def __init__(self, element: BaseOxmlElement, parent: ProvidesPart):
55
+ super(ParentedElementProxy, self).__init__(element)
56
+ self._parent = parent
57
+
58
+ @property
59
+ def parent(self):
60
+ """The ancestor proxy object to this one.
61
+
62
+ For example, the parent of a shape is generally the |SlideShapes| object that contains it.
63
+ """
64
+ return self._parent
65
+
66
+ @property
67
+ def part(self) -> XmlPart:
68
+ """The package part containing this object."""
69
+ return self._parent.part
70
+
71
+
72
+ class PartElementProxy(ElementProxy):
73
+ """Provides common members for proxy-objects that wrap a part's root element, e.g. `p:sld`."""
74
+
75
+ def __init__(self, element: BaseOxmlElement, part: XmlPart):
76
+ super(PartElementProxy, self).__init__(element)
77
+ self._part = part
78
+
79
+ @property
80
+ def part(self) -> XmlPart:
81
+ """The package part containing this object."""
82
+ return self._part
pptx/slide.py ADDED
@@ -0,0 +1,577 @@
1
+ """Slide-related objects, including masters, layouts, and notes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Iterator, cast
6
+
7
+ from pptx.dml.fill import FillFormat
8
+ from pptx.enum.shapes import PP_PLACEHOLDER
9
+ from pptx.opc.constants import RELATIONSHIP_TYPE as RT
10
+ from pptx.oxml.ns import qn
11
+ from pptx.shapes.shapetree import (
12
+ LayoutPlaceholders,
13
+ LayoutShapes,
14
+ MasterPlaceholders,
15
+ MasterShapes,
16
+ NotesSlidePlaceholders,
17
+ NotesSlideShapes,
18
+ SlidePlaceholders,
19
+ SlideShapes,
20
+ )
21
+ from pptx.shared import ElementProxy, ParentedElementProxy, PartElementProxy
22
+ from pptx.text.text import Font
23
+ from pptx.util import lazyproperty
24
+
25
+ if TYPE_CHECKING:
26
+ from pptx.oxml.presentation import CT_SlideIdList, CT_SlideMasterIdList
27
+ from pptx.oxml.slide import (
28
+ CT_CommonSlideData,
29
+ CT_NotesSlide,
30
+ CT_Slide,
31
+ CT_SlideLayoutIdList,
32
+ CT_SlideMaster,
33
+ )
34
+ from pptx.parts.presentation import PresentationPart
35
+ from pptx.parts.slide import SlideLayoutPart, SlideMasterPart, SlidePart
36
+ from pptx.presentation import Presentation
37
+ from pptx.shapes.placeholder import LayoutPlaceholder, MasterPlaceholder
38
+ from pptx.shapes.shapetree import NotesSlidePlaceholder
39
+ from pptx.text.text import TextFrame
40
+
41
+
42
+ class _BaseSlide(PartElementProxy):
43
+ """Base class for slide objects, including masters, layouts and notes."""
44
+
45
+ _element: CT_Slide
46
+
47
+ @lazyproperty
48
+ def background(self) -> _Background:
49
+ """|_Background| object providing slide background properties.
50
+
51
+ This property returns a |_Background| object whether or not the
52
+ slide, master, or layout has an explicitly defined background.
53
+
54
+ The same |_Background| object is returned on every call for the same
55
+ slide object.
56
+ """
57
+ return _Background(self._element.cSld)
58
+
59
+ def set_background_picture(self, image_file) -> None:
60
+ """Use an image path or file-like object as a stretched slide background.
61
+
62
+ Available on slides, masters, and layouts. The image is embedded through
63
+ the owning part and shared with other uses of identical image bytes.
64
+ """
65
+ _, relationship_id = self.part.get_or_add_image_part(image_file)
66
+ self.background.fill._set_picture(relationship_id)
67
+
68
+ @property
69
+ def name(self) -> str:
70
+ """String representing the internal name of this slide.
71
+
72
+ Returns an empty string (`''`) if no name is assigned. Assigning an empty string or |None|
73
+ to this property causes any name to be removed.
74
+ """
75
+ return self._element.cSld.name
76
+
77
+ @name.setter
78
+ def name(self, value: str | None):
79
+ new_value = "" if value is None else value
80
+ self._element.cSld.name = new_value
81
+
82
+
83
+ class _BaseMaster(_BaseSlide):
84
+ """Base class for master objects such as |SlideMaster| and |NotesMaster|.
85
+
86
+ Provides access to placeholders and regular shapes.
87
+ """
88
+
89
+ @lazyproperty
90
+ def placeholders(self) -> MasterPlaceholders:
91
+ """|MasterPlaceholders| collection of placeholder shapes in this master.
92
+
93
+ Sequence sorted in `idx` order.
94
+ """
95
+ return MasterPlaceholders(self._element.spTree, self)
96
+
97
+ @lazyproperty
98
+ def shapes(self):
99
+ """
100
+ Instance of |MasterShapes| containing sequence of shape objects
101
+ appearing on this slide.
102
+ """
103
+ return MasterShapes(self._element.spTree, self)
104
+
105
+
106
+ class NotesMaster(_BaseMaster):
107
+ """Proxy for the notes master XML document.
108
+
109
+ Provides access to shapes, the most commonly used of which are placeholders.
110
+ """
111
+
112
+
113
+ class NotesSlide(_BaseSlide):
114
+ """Notes slide object.
115
+
116
+ Provides access to slide notes placeholder and other shapes on the notes handout
117
+ page.
118
+ """
119
+
120
+ element: CT_NotesSlide # pyright: ignore[reportIncompatibleMethodOverride]
121
+
122
+ def clone_master_placeholders(self, notes_master: NotesMaster) -> None:
123
+ """Selectively add placeholder shape elements from `notes_master`.
124
+
125
+ Selected placeholder shape elements from `notes_master` are added to the shapes
126
+ collection of this notes slide. Z-order of placeholders is preserved. Certain
127
+ placeholders (header, date, footer) are not cloned.
128
+ """
129
+
130
+ def iter_cloneable_placeholders() -> Iterator[MasterPlaceholder]:
131
+ """Generate a reference to each cloneable placeholder in `notes_master`.
132
+
133
+ These are the placeholders that should be cloned to a notes slide when the a new notes
134
+ slide is created.
135
+ """
136
+ cloneable = (
137
+ PP_PLACEHOLDER.SLIDE_IMAGE,
138
+ PP_PLACEHOLDER.BODY,
139
+ PP_PLACEHOLDER.SLIDE_NUMBER,
140
+ )
141
+ for placeholder in notes_master.placeholders:
142
+ if placeholder.element.ph_type in cloneable:
143
+ yield placeholder
144
+
145
+ shapes = self.shapes
146
+ for placeholder in iter_cloneable_placeholders():
147
+ shapes.clone_placeholder(cast("LayoutPlaceholder", placeholder))
148
+
149
+ @property
150
+ def notes_placeholder(self) -> NotesSlidePlaceholder | None:
151
+ """the notes placeholder on this notes slide, the shape that contains the actual notes text.
152
+
153
+ Return |None| if no notes placeholder is present; while this is probably uncommon, it can
154
+ happen if the notes master does not have a body placeholder, or if the notes placeholder
155
+ has been deleted from the notes slide.
156
+ """
157
+ for placeholder in self.placeholders:
158
+ if placeholder.placeholder_format.type == PP_PLACEHOLDER.BODY:
159
+ return placeholder
160
+ return None
161
+
162
+ @property
163
+ def notes_text_frame(self) -> TextFrame | None:
164
+ """The text frame of the notes placeholder on this notes slide.
165
+
166
+ |None| if there is no notes placeholder. This is a shortcut to accommodate the common case
167
+ of simply adding "notes" text to the notes "page".
168
+ """
169
+ notes_placeholder = self.notes_placeholder
170
+ if notes_placeholder is None:
171
+ return None
172
+ return notes_placeholder.text_frame
173
+
174
+ @lazyproperty
175
+ def placeholders(self) -> NotesSlidePlaceholders:
176
+ """Instance of |NotesSlidePlaceholders| for this notes-slide.
177
+
178
+ Contains the sequence of placeholder shapes in this notes slide.
179
+ """
180
+ return NotesSlidePlaceholders(self.element.spTree, self)
181
+
182
+ @lazyproperty
183
+ def shapes(self) -> NotesSlideShapes:
184
+ """Sequence of shape objects appearing on this notes slide."""
185
+ return NotesSlideShapes(self._element.spTree, self)
186
+
187
+
188
+ class Slide(_BaseSlide):
189
+ """Slide object. Provides access to shapes and slide-level properties."""
190
+
191
+ part: SlidePart # pyright: ignore[reportIncompatibleMethodOverride]
192
+
193
+ @property
194
+ def show_master_shapes(self) -> bool | None:
195
+ """Whether master graphics are shown. None uses the file format default (True)."""
196
+ value = self._element.get("showMasterSp")
197
+ return None if value is None else value in ("1", "true")
198
+
199
+ @show_master_shapes.setter
200
+ def show_master_shapes(self, value: bool | None):
201
+ if value is not None and not isinstance(value, bool):
202
+ raise TypeError("show_master_shapes must be bool or None")
203
+ if value is None:
204
+ self._element.attrib.pop("showMasterSp", None)
205
+ else:
206
+ self._element.set("showMasterSp", "1" if value else "0")
207
+
208
+ @property
209
+ def follow_master_background(self):
210
+ """|True| if this slide inherits the slide master background.
211
+
212
+ Assigning |False| causes background inheritance from the master to be
213
+ interrupted; if there is no custom background for this slide,
214
+ a default background is added. If a custom background already exists
215
+ for this slide, assigning |False| has no effect.
216
+
217
+ Assigning |True| causes any custom background for this slide to be
218
+ deleted and inheritance from the master restored.
219
+ """
220
+ return self._element.bg is None
221
+
222
+ @property
223
+ def has_notes_slide(self) -> bool:
224
+ """`True` if this slide has a notes slide, `False` otherwise.
225
+
226
+ A notes slide is created by :attr:`.notes_slide` when one doesn't exist; use this property
227
+ to test for a notes slide without the possible side effect of creating one.
228
+ """
229
+ return self.part.has_notes_slide
230
+
231
+ @property
232
+ def notes_slide(self) -> NotesSlide:
233
+ """The |NotesSlide| instance for this slide.
234
+
235
+ If the slide does not have a notes slide, one is created. The same single instance is
236
+ returned on each call.
237
+ """
238
+ return self.part.notes_slide
239
+
240
+ @lazyproperty
241
+ def placeholders(self) -> SlidePlaceholders:
242
+ """Sequence of placeholder shapes in this slide."""
243
+ return SlidePlaceholders(self._element.spTree, self)
244
+
245
+ @lazyproperty
246
+ def shapes(self) -> SlideShapes:
247
+ """Sequence of shape objects appearing on this slide."""
248
+ return SlideShapes(self._element.spTree, self)
249
+
250
+ @property
251
+ def slide_id(self) -> int:
252
+ """Integer value that uniquely identifies this slide within this presentation.
253
+
254
+ The slide id does not change if the position of this slide in the slide sequence is changed
255
+ by adding, rearranging, or deleting slides.
256
+ """
257
+ return self.part.slide_id
258
+
259
+ @property
260
+ def slide_layout(self) -> SlideLayout:
261
+ """|SlideLayout| object this slide inherits appearance from."""
262
+ return self.part.slide_layout
263
+
264
+
265
+ class Slides(ParentedElementProxy):
266
+ """Sequence of slides belonging to an instance of |Presentation|.
267
+
268
+ Has list semantics for access to individual slides. Supports indexed access, len(), and
269
+ iteration.
270
+ """
271
+
272
+ part: PresentationPart # pyright: ignore[reportIncompatibleMethodOverride]
273
+
274
+ def __init__(self, sldIdLst: CT_SlideIdList, prs: Presentation):
275
+ super(Slides, self).__init__(sldIdLst, prs)
276
+ self._sldIdLst = sldIdLst
277
+
278
+ def __getitem__(self, idx: int) -> Slide:
279
+ """Provide indexed access, (e.g. 'slides[0]')."""
280
+ try:
281
+ sldId = self._sldIdLst.sldId_lst[idx]
282
+ except IndexError:
283
+ raise IndexError("slide index out of range")
284
+ return self.part.related_slide(sldId.rId)
285
+
286
+ def __iter__(self) -> Iterator[Slide]:
287
+ """Support iteration, e.g. `for slide in slides:`."""
288
+ for sldId in self._sldIdLst.sldId_lst:
289
+ yield self.part.related_slide(sldId.rId)
290
+
291
+ def __len__(self) -> int:
292
+ """Support len() built-in function, e.g. `len(slides) == 4`."""
293
+ return len(self._sldIdLst)
294
+
295
+ def clear(self) -> None:
296
+ """Remove all slides while retaining masters, layouts, and presentation settings.
297
+
298
+ Slide parts and their notes/media become unreachable unless another retained
299
+ part references them. Such shared relationships are preserved.
300
+ """
301
+ for slide_id in list(self._sldIdLst):
302
+ self._sldIdLst.remove(slide_id)
303
+ self.part.drop_rel(slide_id.rId)
304
+
305
+ def add_slide(self, slide_layout: SlideLayout) -> Slide:
306
+ """Return a newly added slide that inherits layout from `slide_layout`."""
307
+ rId, slide = self.part.add_slide(slide_layout)
308
+ slide.shapes.clone_layout_placeholders(slide_layout)
309
+ self._sldIdLst.add_sldId(rId)
310
+ return slide
311
+
312
+ def get(self, slide_id: int, default: Slide | None = None) -> Slide | None:
313
+ """Return the slide identified by int `slide_id` in this presentation.
314
+
315
+ Returns `default` if not found.
316
+ """
317
+ slide = self.part.get_slide(slide_id)
318
+ if slide is None:
319
+ return default
320
+ return slide
321
+
322
+ def index(self, slide: Slide) -> int:
323
+ """Map `slide` to its zero-based position in this slide sequence.
324
+
325
+ Raises |ValueError| on *slide* not present.
326
+ """
327
+ for idx, this_slide in enumerate(self):
328
+ if this_slide == slide:
329
+ return idx
330
+ raise ValueError("%s is not in slide collection" % slide)
331
+
332
+
333
+ class SlideLayout(_BaseSlide):
334
+ """Slide layout object.
335
+
336
+ Provides access to placeholders, regular shapes, and slide layout-level properties.
337
+ """
338
+
339
+ part: SlideLayoutPart # pyright: ignore[reportIncompatibleMethodOverride]
340
+
341
+ @property
342
+ def show_master_shapes(self) -> bool | None:
343
+ """Whether master graphics are shown. None uses the file format default (True)."""
344
+ value = self._element.get("showMasterSp")
345
+ return None if value is None else value in ("1", "true")
346
+
347
+ @show_master_shapes.setter
348
+ def show_master_shapes(self, value: bool | None):
349
+ if value is not None and not isinstance(value, bool):
350
+ raise TypeError("show_master_shapes must be bool or None")
351
+ if value is None:
352
+ self._element.attrib.pop("showMasterSp", None)
353
+ else:
354
+ self._element.set("showMasterSp", "1" if value else "0")
355
+
356
+ def iter_cloneable_placeholders(self) -> Iterator[LayoutPlaceholder]:
357
+ """Generate layout-placeholders on this slide-layout that should be cloned to a new slide.
358
+
359
+ Used when creating a new slide from this slide-layout.
360
+ """
361
+ latent_ph_types = (
362
+ PP_PLACEHOLDER.DATE,
363
+ PP_PLACEHOLDER.FOOTER,
364
+ PP_PLACEHOLDER.SLIDE_NUMBER,
365
+ )
366
+ for ph in self.placeholders:
367
+ if ph.element.ph_type not in latent_ph_types:
368
+ yield ph
369
+
370
+ @lazyproperty
371
+ def placeholders(self) -> LayoutPlaceholders:
372
+ """Sequence of placeholder shapes in this slide layout.
373
+
374
+ Placeholders appear in `idx` order.
375
+ """
376
+ return LayoutPlaceholders(self._element.spTree, self)
377
+
378
+ @lazyproperty
379
+ def shapes(self) -> LayoutShapes:
380
+ """Sequence of shapes appearing on this slide layout."""
381
+ return LayoutShapes(self._element.spTree, self)
382
+
383
+ @property
384
+ def slide_master(self) -> SlideMaster:
385
+ """Slide master from which this slide-layout inherits properties."""
386
+ return self.part.slide_master
387
+
388
+ @property
389
+ def used_by_slides(self):
390
+ """Tuple of slide objects based on this slide layout."""
391
+ # ---getting Slides collection requires going around the horn a bit---
392
+ slides = self.part.package.presentation_part.presentation.slides
393
+ return tuple(s for s in slides if s.slide_layout == self)
394
+
395
+
396
+ class SlideLayouts(ParentedElementProxy):
397
+ """Sequence of slide layouts belonging to a slide-master.
398
+
399
+ Supports indexed access, len(), iteration, index() and remove().
400
+ """
401
+
402
+ part: SlideMasterPart # pyright: ignore[reportIncompatibleMethodOverride]
403
+
404
+ def __init__(self, sldLayoutIdLst: CT_SlideLayoutIdList, parent: SlideMaster):
405
+ super(SlideLayouts, self).__init__(sldLayoutIdLst, parent)
406
+ self._sldLayoutIdLst = sldLayoutIdLst
407
+
408
+ def __getitem__(self, idx: int) -> SlideLayout:
409
+ """Provides indexed access, e.g. `slide_layouts[2]`."""
410
+ try:
411
+ sldLayoutId = self._sldLayoutIdLst.sldLayoutId_lst[idx]
412
+ except IndexError:
413
+ raise IndexError("slide layout index out of range")
414
+ return self.part.related_slide_layout(sldLayoutId.rId)
415
+
416
+ def __iter__(self) -> Iterator[SlideLayout]:
417
+ """Generate each |SlideLayout| in the collection, in sequence."""
418
+ for sldLayoutId in self._sldLayoutIdLst.sldLayoutId_lst:
419
+ yield self.part.related_slide_layout(sldLayoutId.rId)
420
+
421
+ def __len__(self) -> int:
422
+ """Support len() built-in function, e.g. `len(slides) == 4`."""
423
+ return len(self._sldLayoutIdLst)
424
+
425
+ def get_by_name(self, name: str, default: SlideLayout | None = None) -> SlideLayout | None:
426
+ """Return SlideLayout object having `name`, or `default` if not found."""
427
+ for slide_layout in self:
428
+ if slide_layout.name == name:
429
+ return slide_layout
430
+ return default
431
+
432
+ def index(self, slide_layout: SlideLayout) -> int:
433
+ """Return zero-based index of `slide_layout` in this collection.
434
+
435
+ Raises `ValueError` if `slide_layout` is not present in this collection.
436
+ """
437
+ for idx, this_layout in enumerate(self):
438
+ if slide_layout == this_layout:
439
+ return idx
440
+ raise ValueError("layout not in this SlideLayouts collection")
441
+
442
+ def remove(self, slide_layout: SlideLayout) -> None:
443
+ """Remove `slide_layout` from the collection.
444
+
445
+ Raises ValueError when `slide_layout` is in use; a slide layout which is the basis for one
446
+ or more slides cannot be removed.
447
+ """
448
+ # ---raise if layout is in use---
449
+ if slide_layout.used_by_slides:
450
+ raise ValueError("cannot remove slide-layout in use by one or more slides")
451
+
452
+ # ---target layout is identified by its index in this collection---
453
+ target_idx = self.index(slide_layout)
454
+
455
+ # --remove layout from p:sldLayoutIds of its master
456
+ # --this stops layout from showing up, but doesn't remove it from package
457
+ target_sldLayoutId = self._sldLayoutIdLst.sldLayoutId_lst[target_idx]
458
+ self._sldLayoutIdLst.remove(target_sldLayoutId)
459
+
460
+ # --drop relationship from master to layout
461
+ # --this removes layout from package, along with everything (only) it refers to,
462
+ # --including images (not used elsewhere) and hyperlinks
463
+ slide_layout.slide_master.part.drop_rel(target_sldLayoutId.rId)
464
+
465
+
466
+ class SlideMaster(_BaseMaster):
467
+ """Slide master object.
468
+
469
+ Provides access to slide layouts. Access to placeholders, regular shapes, and slide master-level
470
+ properties is inherited from |_BaseMaster|.
471
+ """
472
+
473
+ _element: CT_SlideMaster # pyright: ignore[reportIncompatibleVariableOverride]
474
+
475
+ @property
476
+ def theme(self):
477
+ """Editable theme shared by masters referencing the same theme part."""
478
+ return self.part.part_related_by(RT.THEME).theme
479
+
480
+ def text_style_font(self, kind: str, level: int = 0):
481
+ """Explicit font defaults for title, body, or other text at level 0..8.
482
+
483
+ Return None if that style has no explicit font defaults. This does not
484
+ resolve inheritance and does not create missing style elements.
485
+ """
486
+ if kind not in ("title", "body", "other"):
487
+ raise ValueError("kind must be title, body, or other")
488
+ if isinstance(level, bool) or not isinstance(level, int) or not 0 <= level <= 8:
489
+ raise ValueError("level must be an integer from 0 to 8")
490
+ path = "/".join(
491
+ qn(tag)
492
+ for tag in (
493
+ "p:txStyles",
494
+ "p:" + kind + "Style",
495
+ "a:lvl" + str(level + 1) + "pPr",
496
+ "a:defRPr",
497
+ )
498
+ )
499
+ element = self._element.find(path)
500
+ return None if element is None else Font(element)
501
+
502
+ @lazyproperty
503
+ def slide_layouts(self) -> SlideLayouts:
504
+ """|SlideLayouts| object providing access to this slide-master's layouts."""
505
+ return SlideLayouts(self._element.get_or_add_sldLayoutIdLst(), self)
506
+
507
+
508
+ class SlideMasters(ParentedElementProxy):
509
+ """Sequence of |SlideMaster| objects belonging to a presentation.
510
+
511
+ Has list access semantics, supporting indexed access, len(), and iteration.
512
+ """
513
+
514
+ part: PresentationPart # pyright: ignore[reportIncompatibleMethodOverride]
515
+
516
+ def __init__(self, sldMasterIdLst: CT_SlideMasterIdList, parent: Presentation):
517
+ super(SlideMasters, self).__init__(sldMasterIdLst, parent)
518
+ self._sldMasterIdLst = sldMasterIdLst
519
+
520
+ def __getitem__(self, idx: int) -> SlideMaster:
521
+ """Provides indexed access, e.g. `slide_masters[2]`."""
522
+ try:
523
+ sldMasterId = self._sldMasterIdLst.sldMasterId_lst[idx]
524
+ except IndexError:
525
+ raise IndexError("slide master index out of range")
526
+ return self.part.related_slide_master(sldMasterId.rId)
527
+
528
+ def __iter__(self):
529
+ """Generate each |SlideMaster| instance in the collection, in sequence."""
530
+ for smi in self._sldMasterIdLst.sldMasterId_lst:
531
+ yield self.part.related_slide_master(smi.rId)
532
+
533
+ def __len__(self):
534
+ """Support len() built-in function, e.g. `len(slide_masters) == 4`."""
535
+ return len(self._sldMasterIdLst)
536
+
537
+
538
+ class _Background(ElementProxy):
539
+ """Provides access to slide background properties.
540
+
541
+ Note that the presence of this object does not by itself imply an
542
+ explicitly-defined background; a slide with an inherited background still
543
+ has a |_Background| object.
544
+ """
545
+
546
+ def __init__(self, cSld: CT_CommonSlideData):
547
+ super(_Background, self).__init__(cSld)
548
+ self._cSld = cSld
549
+
550
+ @lazyproperty
551
+ def fill(self):
552
+ """|FillFormat| instance for this background.
553
+
554
+ This |FillFormat| object is used to interrogate or specify the fill
555
+ of the slide background.
556
+
557
+ Note that accessing this property is potentially destructive. A slide
558
+ background can also be specified by a background style reference and
559
+ accessing this property will remove that reference, if present, and
560
+ replace it with NoFill. This is frequently the case for a slide
561
+ master background.
562
+
563
+ This is also the case when there is no explicitly defined background
564
+ (background is inherited); merely accessing this property will cause
565
+ the background to be set to NoFill and the inheritance link will be
566
+ interrupted. This is frequently the case for a slide background.
567
+
568
+ Of course, if you are accessing this property in order to set the
569
+ fill, then these changes are of no consequence, but the existing
570
+ background cannot be reliably interrogated using this property unless
571
+ you have already established it is an explicit fill.
572
+
573
+ If the background is already a fill, then accessing this property
574
+ makes no changes to the current background.
575
+ """
576
+ bgPr = self._cSld.get_or_add_bgPr()
577
+ return FillFormat.from_fill_parent(bgPr)