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/presentation.py
ADDED
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"""Main presentation object."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import IO, TYPE_CHECKING, Literal, cast
|
|
7
|
+
|
|
8
|
+
from pptx2.section import Sections
|
|
9
|
+
from pptx2.shared import PartElementProxy
|
|
10
|
+
from pptx2.slide import SlideMasters, Slides
|
|
11
|
+
from pptx2.util import lazyproperty
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pptx2.enum.presentation import MSO_TRANSITION_TYPE
|
|
15
|
+
from pptx2.lint import LintIssue
|
|
16
|
+
from pptx2.oxml.presentation import CT_Presentation, CT_SlideId
|
|
17
|
+
from pptx2.parts.presentation import PresentationPart
|
|
18
|
+
from pptx2.parts.slide import SlidePart
|
|
19
|
+
from pptx2.slide import NotesMaster, Slide, SlideLayouts
|
|
20
|
+
from pptx2.util import Length
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Sentinel used by `set_transition` so callers can distinguish "leave the
|
|
25
|
+
# existing value alone" from "explicitly clear it" (which is `None`).
|
|
26
|
+
_UNSET = object()
|
|
27
|
+
|
|
28
|
+
LintOnSaveMode = Literal["off", "warn", "raise"]
|
|
29
|
+
|
|
30
|
+
#: Accepted values for :attr:`Presentation.lint_on_save`.
|
|
31
|
+
_LINT_ON_SAVE_MODES: tuple[LintOnSaveMode, ...] = ("off", "warn", "raise")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Presentation(PartElementProxy):
|
|
35
|
+
"""PresentationML (PML) presentation.
|
|
36
|
+
|
|
37
|
+
Not intended to be constructed directly. Use :func:`pptx2.Presentation` to open or
|
|
38
|
+
create a presentation.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
_element: CT_Presentation
|
|
42
|
+
part: PresentationPart # pyright: ignore[reportIncompatibleMethodOverride]
|
|
43
|
+
|
|
44
|
+
# ---- `lint_on_save` is a plain instance attribute, not part of the XML.
|
|
45
|
+
# ---- `PresentationPart.presentation` is a `lazyproperty`, so a package
|
|
46
|
+
# ---- hands back this same proxy object every time and the setting sticks
|
|
47
|
+
# ---- for the life of the package. It is deliberately *not* persisted.
|
|
48
|
+
_lint_on_save: LintOnSaveMode = "off"
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def core_properties(self):
|
|
52
|
+
"""|CoreProperties| instance for this presentation.
|
|
53
|
+
|
|
54
|
+
Provides read/write access to the Dublin Core document properties for the presentation.
|
|
55
|
+
"""
|
|
56
|
+
return self.part.core_properties
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def notes_master(self) -> NotesMaster:
|
|
60
|
+
"""Instance of |NotesMaster| for this presentation.
|
|
61
|
+
|
|
62
|
+
If the presentation does not have a notes master, one is created from a default template
|
|
63
|
+
and returned. The same single instance is returned on each call.
|
|
64
|
+
"""
|
|
65
|
+
return self.part.notes_master
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def lint_on_save(self) -> LintOnSaveMode:
|
|
69
|
+
"""What :meth:`save` does about error-severity lint issues. Read/write.
|
|
70
|
+
|
|
71
|
+
One of:
|
|
72
|
+
|
|
73
|
+
``"off"`` *(default)*
|
|
74
|
+
No checks are run at save time; :meth:`save` does no lint work at
|
|
75
|
+
all. This is the default so that existing code keeps working
|
|
76
|
+
unchanged.
|
|
77
|
+
|
|
78
|
+
``"warn"``
|
|
79
|
+
Every slide is linted before the file is written and each
|
|
80
|
+
error-severity issue is logged (stdlib :mod:`logging`, logger
|
|
81
|
+
``"pptx2.presentation"``). The file is still written.
|
|
82
|
+
|
|
83
|
+
``"raise"``
|
|
84
|
+
Every slide is linted *before* the file is written and
|
|
85
|
+
:class:`~pptx2.exc.LintError` is raised if any slide has an
|
|
86
|
+
error-severity issue, so a failing deck never reaches disk.
|
|
87
|
+
|
|
88
|
+
Example::
|
|
89
|
+
|
|
90
|
+
prs.lint_on_save = "raise"
|
|
91
|
+
prs.save("deck.pptx") # raises LintError if a shape is off-slide
|
|
92
|
+
|
|
93
|
+
This is a setting on the in-memory |Presentation| object; it is not
|
|
94
|
+
stored in the ``.pptx`` file, so a deck re-opened from disk starts
|
|
95
|
+
out at ``"off"`` again.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
ValueError: if assigned anything other than ``"off"``, ``"warn"``,
|
|
99
|
+
or ``"raise"``.
|
|
100
|
+
"""
|
|
101
|
+
return self._lint_on_save
|
|
102
|
+
|
|
103
|
+
@lint_on_save.setter
|
|
104
|
+
def lint_on_save(self, value: LintOnSaveMode) -> None:
|
|
105
|
+
if value not in _LINT_ON_SAVE_MODES:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"lint_on_save must be one of 'off', 'warn', or 'raise', got {value!r}"
|
|
108
|
+
)
|
|
109
|
+
# ---- the `not in` check above narrows `value` to `LintOnSaveMode` ----
|
|
110
|
+
self._lint_on_save = value
|
|
111
|
+
|
|
112
|
+
def save(self, file: str | IO[bytes]):
|
|
113
|
+
"""Writes this presentation to `file`.
|
|
114
|
+
|
|
115
|
+
`file` can be either a file-path or a file-like object open for writing bytes.
|
|
116
|
+
|
|
117
|
+
When :attr:`lint_on_save` is ``"warn"`` or ``"raise"``, every slide is
|
|
118
|
+
linted before anything is written; in ``"raise"`` mode a
|
|
119
|
+
:class:`~pptx2.exc.LintError` propagates and no file is written.
|
|
120
|
+
"""
|
|
121
|
+
# ---- the default ("off") does no lint work whatsoever ----
|
|
122
|
+
if self._lint_on_save != "off":
|
|
123
|
+
_lint_before_save(self, self._lint_on_save)
|
|
124
|
+
self.part.save(file)
|
|
125
|
+
|
|
126
|
+
def render_thumbnails(self, **kwargs):
|
|
127
|
+
"""Render PNG thumbnails for every slide via headless LibreOffice.
|
|
128
|
+
|
|
129
|
+
Thin wrapper around :func:`pptx2.render.render_slide_thumbnails` that
|
|
130
|
+
forwards `out_dir`, `slide_indexes`, `soffice_bin`, `timeout`, and
|
|
131
|
+
`return_bytes` keyword arguments. Requires ``soffice`` (LibreOffice)
|
|
132
|
+
on PATH; raises ``ThumbnailRendererUnavailable`` otherwise.
|
|
133
|
+
"""
|
|
134
|
+
from pptx2.render import render_slide_thumbnails
|
|
135
|
+
|
|
136
|
+
return render_slide_thumbnails(self, **kwargs)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def slide_height(self) -> Length | None:
|
|
140
|
+
"""Height of slides in this presentation, in English Metric Units (EMU).
|
|
141
|
+
|
|
142
|
+
Returns |None| if no slide width is defined. Read/write.
|
|
143
|
+
"""
|
|
144
|
+
sldSz = self._element.sldSz
|
|
145
|
+
if sldSz is None:
|
|
146
|
+
return None
|
|
147
|
+
return sldSz.cy
|
|
148
|
+
|
|
149
|
+
@slide_height.setter
|
|
150
|
+
def slide_height(self, height: Length):
|
|
151
|
+
sldSz = self._element.get_or_add_sldSz()
|
|
152
|
+
sldSz.cy = height
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def slide_layouts(self) -> SlideLayouts:
|
|
156
|
+
"""|SlideLayouts| collection belonging to the first |SlideMaster| of this presentation.
|
|
157
|
+
|
|
158
|
+
A presentation can have more than one slide master and each master will have its own set
|
|
159
|
+
of layouts. This property is a convenience for the common case where the presentation has
|
|
160
|
+
only a single slide master.
|
|
161
|
+
"""
|
|
162
|
+
return self.slide_masters[0].slide_layouts
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def slide_master(self):
|
|
166
|
+
"""
|
|
167
|
+
First |SlideMaster| object belonging to this presentation. Typically,
|
|
168
|
+
presentations have only a single slide master. This property provides
|
|
169
|
+
simpler access in that common case.
|
|
170
|
+
"""
|
|
171
|
+
return self.slide_masters[0]
|
|
172
|
+
|
|
173
|
+
@lazyproperty
|
|
174
|
+
def slide_masters(self) -> SlideMasters:
|
|
175
|
+
"""|SlideMasters| collection of slide-masters belonging to this presentation."""
|
|
176
|
+
return SlideMasters(self._element.get_or_add_sldMasterIdLst(), self)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def slide_width(self):
|
|
180
|
+
"""
|
|
181
|
+
Width of slides in this presentation, in English Metric Units (EMU).
|
|
182
|
+
Returns |None| if no slide width is defined. Read/write.
|
|
183
|
+
"""
|
|
184
|
+
sldSz = self._element.sldSz
|
|
185
|
+
if sldSz is None:
|
|
186
|
+
return None
|
|
187
|
+
return sldSz.cx
|
|
188
|
+
|
|
189
|
+
@slide_width.setter
|
|
190
|
+
def slide_width(self, width: Length):
|
|
191
|
+
sldSz = self._element.get_or_add_sldSz()
|
|
192
|
+
sldSz.cx = width
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def theme(self):
|
|
196
|
+
"""Return a |Theme| object providing read-only access to the color palette and fonts.
|
|
197
|
+
|
|
198
|
+
Navigates to the theme part of the first slide master, which is where
|
|
199
|
+
Office applications store the active theme. Returns ``None`` if no
|
|
200
|
+
slide master (and therefore no theme) is present.
|
|
201
|
+
|
|
202
|
+
Example::
|
|
203
|
+
|
|
204
|
+
from pptx2.enum.dml import MSO_THEME_COLOR
|
|
205
|
+
|
|
206
|
+
rgb = prs.theme.colors[MSO_THEME_COLOR.ACCENT_1]
|
|
207
|
+
major = prs.theme.fonts.major # e.g. "Calibri"
|
|
208
|
+
"""
|
|
209
|
+
return self.slide_master.part.theme
|
|
210
|
+
|
|
211
|
+
@lazyproperty
|
|
212
|
+
def slides(self):
|
|
213
|
+
"""|Slides| object containing the slides in this presentation."""
|
|
214
|
+
sldIdLst = self._element.get_or_add_sldIdLst()
|
|
215
|
+
self.part.rename_slide_parts([cast("CT_SlideId", sldId).rId for sldId in sldIdLst])
|
|
216
|
+
return Slides(sldIdLst, self)
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def sections(self) -> Sections:
|
|
220
|
+
"""|Sections| collection of named slide groupings in this presentation.
|
|
221
|
+
|
|
222
|
+
Sections appear in PowerPoint's outline / slide-sorter pane and are
|
|
223
|
+
stored as a PowerPoint-2010 extension on the presentation part. The
|
|
224
|
+
returned collection supports ``len()``, indexed access, iteration,
|
|
225
|
+
``.add(name, start_slide_index=None)``, and ``.remove(section)``.
|
|
226
|
+
|
|
227
|
+
Reading this property never modifies the deck; the extension
|
|
228
|
+
container is created only when the first section is added.
|
|
229
|
+
"""
|
|
230
|
+
return Sections(self._element, self)
|
|
231
|
+
|
|
232
|
+
def import_slide(
|
|
233
|
+
self,
|
|
234
|
+
source_slide: Slide,
|
|
235
|
+
merge_master: Literal["dedupe", "clone"] = "dedupe",
|
|
236
|
+
) -> Slide:
|
|
237
|
+
"""Copy *source_slide* into this presentation and return the new |Slide|.
|
|
238
|
+
|
|
239
|
+
The imported slide is appended after any existing slides.
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
source_slide:
|
|
244
|
+
A |Slide| object from any |Presentation| instance, including this one.
|
|
245
|
+
merge_master:
|
|
246
|
+
Controls how the slide master is handled:
|
|
247
|
+
|
|
248
|
+
``'dedupe'`` *(default)*
|
|
249
|
+
Reuse an existing master in this presentation if its XML is
|
|
250
|
+
identical to the source master's XML (normalised byte-for-byte
|
|
251
|
+
compare). Clone the master otherwise.
|
|
252
|
+
|
|
253
|
+
``'clone'``
|
|
254
|
+
Always clone the source master, even if an identical one
|
|
255
|
+
already exists.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
Slide
|
|
260
|
+
The newly imported slide.
|
|
261
|
+
|
|
262
|
+
Example::
|
|
263
|
+
|
|
264
|
+
src = pptx2.Presentation("source.pptx")
|
|
265
|
+
dst = pptx2.Presentation("dest.pptx")
|
|
266
|
+
new_slide = dst.import_slide(src.slides[0])
|
|
267
|
+
dst.save("merged.pptx")
|
|
268
|
+
"""
|
|
269
|
+
from pptx2._slide_importer import import_slide as _import_slide
|
|
270
|
+
|
|
271
|
+
return _import_slide(source_slide.part, self.part, merge_master=merge_master)
|
|
272
|
+
|
|
273
|
+
def apply_template(
|
|
274
|
+
self,
|
|
275
|
+
template_path_or_stream: str | IO[bytes],
|
|
276
|
+
) -> None:
|
|
277
|
+
"""Re-point every slide in this presentation at masters from *template_path_or_stream*.
|
|
278
|
+
|
|
279
|
+
After this call every slide inherits theme, fonts, and colours from
|
|
280
|
+
the template. Slide content (shapes, text, animations) is preserved.
|
|
281
|
+
|
|
282
|
+
The template can be a ``.potx`` or any ``.pptx``/``.pptm`` file — the
|
|
283
|
+
master(s) inside are used regardless of the extension.
|
|
284
|
+
|
|
285
|
+
Layout matching (in priority order):
|
|
286
|
+
|
|
287
|
+
1. Same ``<p:cSld name="…">`` name.
|
|
288
|
+
2. Same layout ``type`` attribute (e.g. ``"title"``, ``"obj"``).
|
|
289
|
+
3. Fall back to the template's first layout.
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
template_path_or_stream:
|
|
294
|
+
Path to a ``.potx``/``.pptx`` file, or a file-like object.
|
|
295
|
+
|
|
296
|
+
Example::
|
|
297
|
+
|
|
298
|
+
prs = pptx2.Presentation("deck.pptx")
|
|
299
|
+
prs.apply_template("brand.potx")
|
|
300
|
+
prs.save("branded_deck.pptx")
|
|
301
|
+
"""
|
|
302
|
+
from pptx2._template_applier import apply_template as _apply_template
|
|
303
|
+
from pptx2.api import Presentation as _Presentation
|
|
304
|
+
|
|
305
|
+
tpl = _Presentation(template_path_or_stream)
|
|
306
|
+
_apply_template(self.part, tpl.part)
|
|
307
|
+
|
|
308
|
+
def set_transition(
|
|
309
|
+
self,
|
|
310
|
+
kind: "MSO_TRANSITION_TYPE | None" = cast("MSO_TRANSITION_TYPE", _UNSET),
|
|
311
|
+
*,
|
|
312
|
+
duration: int | None = cast(int, _UNSET),
|
|
313
|
+
advance_on_click: bool | None = cast(bool, _UNSET),
|
|
314
|
+
advance_after: int | None = cast(int, _UNSET),
|
|
315
|
+
force: bool = False,
|
|
316
|
+
) -> None:
|
|
317
|
+
"""Apply a transition to every slide in this presentation.
|
|
318
|
+
|
|
319
|
+
Convenience for the common "give the whole deck the same transition"
|
|
320
|
+
case; equivalent to looping over :attr:`slides` and assigning to each
|
|
321
|
+
slide's :attr:`~pptx2.slide.Slide.transition` properties.
|
|
322
|
+
|
|
323
|
+
Any argument left unspecified is left untouched on each slide, so
|
|
324
|
+
partial updates (e.g. only changing ``duration``) are safe::
|
|
325
|
+
|
|
326
|
+
from pptx2.enum.presentation import MSO_TRANSITION
|
|
327
|
+
|
|
328
|
+
prs.set_transition(MSO_TRANSITION.MORPH, duration=750)
|
|
329
|
+
|
|
330
|
+
# later, just bump the duration without disturbing the kind
|
|
331
|
+
prs.set_transition(duration=500)
|
|
332
|
+
|
|
333
|
+
Passing ``kind=None`` clears the transition element on every slide
|
|
334
|
+
(restoring inheritance/defaults). Passing ``duration=None``,
|
|
335
|
+
``advance_on_click=None``, or ``advance_after=None`` clears that
|
|
336
|
+
individual attribute on every slide.
|
|
337
|
+
|
|
338
|
+
Per-slide overrides are preserved by default
|
|
339
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
340
|
+
|
|
341
|
+
When ``kind`` is supplied, slides that already have an explicit
|
|
342
|
+
transition kind are **left untouched** — the deck-wide call no
|
|
343
|
+
longer silently clobbers an earlier ``slide.transition.kind = …``.
|
|
344
|
+
This is the historical footgun that prompted item 2 in
|
|
345
|
+
``IMPROVEMENT_PLAN.md``. To restore the old "force every slide to
|
|
346
|
+
match" behaviour, pass ``force=True``::
|
|
347
|
+
|
|
348
|
+
slide_2.transition.kind = MSO_TRANSITION_TYPE.MORPH
|
|
349
|
+
prs.set_transition(MSO_TRANSITION_TYPE.FADE) # slide 2 keeps MORPH
|
|
350
|
+
prs.set_transition(MSO_TRANSITION_TYPE.FADE, force=True) # slide 2 → FADE
|
|
351
|
+
|
|
352
|
+
``duration``, ``advance_on_click``, and ``advance_after`` are
|
|
353
|
+
always applied to every slide regardless of ``force``; only
|
|
354
|
+
``kind`` participates in the override-preservation behaviour.
|
|
355
|
+
"""
|
|
356
|
+
for slide in self.slides:
|
|
357
|
+
transition = slide.transition
|
|
358
|
+
if kind is not _UNSET:
|
|
359
|
+
# Skip slides that already have an explicit kind unless
|
|
360
|
+
# the caller has opted in to force-overwrite. The
|
|
361
|
+
# ``transition.kind`` getter returns ``None`` only when
|
|
362
|
+
# no ``<p:transition>`` element is present. An explicit
|
|
363
|
+
# ``<p:transition/>`` reads back as
|
|
364
|
+
# ``MSO_TRANSITION_TYPE.NONE`` and counts as an existing
|
|
365
|
+
# explicit choice (the slide author asked for "no
|
|
366
|
+
# transition"), so it is preserved unless ``force=True``.
|
|
367
|
+
#
|
|
368
|
+
# ``kind=None`` on the caller side means "clear" — apply
|
|
369
|
+
# unconditionally so callers can still wipe the
|
|
370
|
+
# transition off every slide in one call without an
|
|
371
|
+
# explicit ``force=True``.
|
|
372
|
+
if kind is None or transition.kind is None or force:
|
|
373
|
+
transition.kind = kind
|
|
374
|
+
if duration is not _UNSET:
|
|
375
|
+
transition.duration = duration
|
|
376
|
+
if advance_on_click is not _UNSET:
|
|
377
|
+
transition.advance_on_click = advance_on_click
|
|
378
|
+
if advance_after is not _UNSET:
|
|
379
|
+
transition.advance_after = advance_after
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _lint_before_save(prs: Presentation, mode: LintOnSaveMode) -> None:
|
|
383
|
+
"""Lint every slide of `prs` and warn or raise per `mode`.
|
|
384
|
+
|
|
385
|
+
Mirrors the ``lint`` option of :func:`pptx2.compose.from_spec`: only
|
|
386
|
+
ERROR-severity issues are acted on, and ``"raise"`` reports every offending
|
|
387
|
+
issue in a single :class:`~pptx2.exc.LintError`.
|
|
388
|
+
"""
|
|
389
|
+
from pptx2.exc import LintError
|
|
390
|
+
from pptx2.lint import LintSeverity
|
|
391
|
+
|
|
392
|
+
errors: list[tuple[int, LintIssue]] = [
|
|
393
|
+
(idx, issue)
|
|
394
|
+
for idx, slide in enumerate(prs.slides)
|
|
395
|
+
for issue in slide.lint().issues
|
|
396
|
+
if issue.severity == LintSeverity.ERROR
|
|
397
|
+
]
|
|
398
|
+
|
|
399
|
+
if not errors:
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
if mode == "warn":
|
|
403
|
+
for idx, issue in errors:
|
|
404
|
+
logger.warning("pptx lint: slide %d: %s", idx, issue)
|
|
405
|
+
return
|
|
406
|
+
|
|
407
|
+
msgs = "; ".join(f"slide {idx}: {issue}" for idx, issue in errors)
|
|
408
|
+
raise LintError(f"Lint errors in presentation: {msgs}")
|
pptx2/py.typed
ADDED
|
File without changes
|