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.
Files changed (175) hide show
  1. pptx2/__init__.py +152 -0
  2. pptx2/_color.py +75 -0
  3. pptx2/_slide_importer.py +597 -0
  4. pptx2/_svg.py +155 -0
  5. pptx2/_template_applier.py +292 -0
  6. pptx2/_textstyle.py +187 -0
  7. pptx2/accessibility.py +365 -0
  8. pptx2/action.py +270 -0
  9. pptx2/animation.py +2237 -0
  10. pptx2/api.py +49 -0
  11. pptx2/audit.py +258 -0
  12. pptx2/chart/__init__.py +0 -0
  13. pptx2/chart/analytics.py +381 -0
  14. pptx2/chart/axis.py +543 -0
  15. pptx2/chart/category.py +200 -0
  16. pptx2/chart/chart.py +670 -0
  17. pptx2/chart/data.py +864 -0
  18. pptx2/chart/datalabel.py +406 -0
  19. pptx2/chart/legend.py +86 -0
  20. pptx2/chart/marker.py +70 -0
  21. pptx2/chart/palettes.py +129 -0
  22. pptx2/chart/plot.py +462 -0
  23. pptx2/chart/point.py +101 -0
  24. pptx2/chart/quick_layouts.py +325 -0
  25. pptx2/chart/series.py +334 -0
  26. pptx2/chart/xlsx.py +272 -0
  27. pptx2/chart/xmlwriter.py +1845 -0
  28. pptx2/compose/__init__.py +28 -0
  29. pptx2/compose/from_spec.py +1094 -0
  30. pptx2/design/__init__.py +8 -0
  31. pptx2/design/components.py +607 -0
  32. pptx2/design/figures.py +389 -0
  33. pptx2/design/layout.py +370 -0
  34. pptx2/design/recipes.py +1967 -0
  35. pptx2/design/style.py +209 -0
  36. pptx2/design/tokens.py +915 -0
  37. pptx2/diagrams.py +754 -0
  38. pptx2/dml/__init__.py +0 -0
  39. pptx2/dml/chtfmt.py +40 -0
  40. pptx2/dml/color.py +496 -0
  41. pptx2/dml/effect.py +909 -0
  42. pptx2/dml/fill.py +691 -0
  43. pptx2/dml/line.py +287 -0
  44. pptx2/dml/picture.py +212 -0
  45. pptx2/dml/three_d.py +381 -0
  46. pptx2/enum/__init__.py +0 -0
  47. pptx2/enum/action.py +71 -0
  48. pptx2/enum/animation.py +31 -0
  49. pptx2/enum/base.py +218 -0
  50. pptx2/enum/chart.py +574 -0
  51. pptx2/enum/dml.py +740 -0
  52. pptx2/enum/lang.py +685 -0
  53. pptx2/enum/presentation.py +133 -0
  54. pptx2/enum/shapes.py +1029 -0
  55. pptx2/enum/text.py +230 -0
  56. pptx2/exc.py +42 -0
  57. pptx2/formats.py +139 -0
  58. pptx2/geometry.py +420 -0
  59. pptx2/inherit.py +109 -0
  60. pptx2/lint.py +2256 -0
  61. pptx2/math.py +177 -0
  62. pptx2/media.py +197 -0
  63. pptx2/opc/__init__.py +0 -0
  64. pptx2/opc/constants.py +332 -0
  65. pptx2/opc/oxml.py +188 -0
  66. pptx2/opc/package.py +762 -0
  67. pptx2/opc/packuri.py +109 -0
  68. pptx2/opc/serialized.py +296 -0
  69. pptx2/opc/shared.py +20 -0
  70. pptx2/opc/spec.py +45 -0
  71. pptx2/oxml/__init__.py +555 -0
  72. pptx2/oxml/action.py +53 -0
  73. pptx2/oxml/chart/__init__.py +0 -0
  74. pptx2/oxml/chart/axis.py +337 -0
  75. pptx2/oxml/chart/chart.py +481 -0
  76. pptx2/oxml/chart/datalabel.py +253 -0
  77. pptx2/oxml/chart/legend.py +72 -0
  78. pptx2/oxml/chart/marker.py +61 -0
  79. pptx2/oxml/chart/plot.py +365 -0
  80. pptx2/oxml/chart/series.py +425 -0
  81. pptx2/oxml/chart/shared.py +220 -0
  82. pptx2/oxml/coreprops.py +288 -0
  83. pptx2/oxml/dml/__init__.py +0 -0
  84. pptx2/oxml/dml/color.py +135 -0
  85. pptx2/oxml/dml/effect.py +213 -0
  86. pptx2/oxml/dml/fill.py +316 -0
  87. pptx2/oxml/dml/line.py +12 -0
  88. pptx2/oxml/dml/three_d.py +110 -0
  89. pptx2/oxml/ns.py +135 -0
  90. pptx2/oxml/presentation.py +313 -0
  91. pptx2/oxml/shapes/__init__.py +19 -0
  92. pptx2/oxml/shapes/autoshape.py +467 -0
  93. pptx2/oxml/shapes/connector.py +107 -0
  94. pptx2/oxml/shapes/graphfrm.py +347 -0
  95. pptx2/oxml/shapes/groupshape.py +329 -0
  96. pptx2/oxml/shapes/picture.py +270 -0
  97. pptx2/oxml/shapes/shared.py +577 -0
  98. pptx2/oxml/simpletypes.py +1027 -0
  99. pptx2/oxml/slide.py +563 -0
  100. pptx2/oxml/table.py +650 -0
  101. pptx2/oxml/text.py +815 -0
  102. pptx2/oxml/theme.py +36 -0
  103. pptx2/oxml/xmlchemy.py +717 -0
  104. pptx2/package.py +222 -0
  105. pptx2/parts/__init__.py +0 -0
  106. pptx2/parts/chart.py +95 -0
  107. pptx2/parts/coreprops.py +167 -0
  108. pptx2/parts/diagram.py +37 -0
  109. pptx2/parts/embeddedpackage.py +93 -0
  110. pptx2/parts/image.py +275 -0
  111. pptx2/parts/media.py +37 -0
  112. pptx2/parts/presentation.py +136 -0
  113. pptx2/parts/slide.py +371 -0
  114. pptx2/presentation.py +408 -0
  115. pptx2/py.typed +0 -0
  116. pptx2/render.py +586 -0
  117. pptx2/section.py +272 -0
  118. pptx2/shapes/__init__.py +26 -0
  119. pptx2/shapes/autoshape.py +442 -0
  120. pptx2/shapes/base.py +1078 -0
  121. pptx2/shapes/connector.py +297 -0
  122. pptx2/shapes/freeform.py +337 -0
  123. pptx2/shapes/graphfrm.py +316 -0
  124. pptx2/shapes/group.py +264 -0
  125. pptx2/shapes/picture.py +422 -0
  126. pptx2/shapes/placeholder.py +468 -0
  127. pptx2/shapes/shapetree.py +2027 -0
  128. pptx2/shared.py +82 -0
  129. pptx2/skill/SKILL.md +450 -0
  130. pptx2/skill/__init__.py +78 -0
  131. pptx2/skill/__main__.py +64 -0
  132. pptx2/skill/references/animations.md +189 -0
  133. pptx2/skill/references/basics.md +421 -0
  134. pptx2/skill/references/charts.md +254 -0
  135. pptx2/skill/references/compose.md +234 -0
  136. pptx2/skill/references/design.md +366 -0
  137. pptx2/skill/references/effects.md +249 -0
  138. pptx2/skill/references/end-to-end-deck.md +231 -0
  139. pptx2/skill/references/geometry-and-arrows.md +334 -0
  140. pptx2/skill/references/lint.md +275 -0
  141. pptx2/skill/references/math.md +86 -0
  142. pptx2/skill/references/picture-effects.md +129 -0
  143. pptx2/skill/references/render.md +151 -0
  144. pptx2/skill/references/smart-art.md +75 -0
  145. pptx2/skill/references/space-aware-authoring.md +249 -0
  146. pptx2/skill/references/tables.md +244 -0
  147. pptx2/skill/references/theme.md +127 -0
  148. pptx2/skill/references/three-d.md +109 -0
  149. pptx2/skill/references/transitions.md +100 -0
  150. pptx2/slide.py +1244 -0
  151. pptx2/smart_art.py +220 -0
  152. pptx2/spec.py +633 -0
  153. pptx2/table.py +1181 -0
  154. pptx2/table_styles.py +184 -0
  155. pptx2/templates/default.pptx +0 -0
  156. pptx2/templates/docx-icon.emf +0 -0
  157. pptx2/templates/generic-icon.emf +0 -0
  158. pptx2/templates/notes.xml +23 -0
  159. pptx2/templates/notesMaster.xml +352 -0
  160. pptx2/templates/pptx-icon.emf +0 -0
  161. pptx2/templates/theme.xml +321 -0
  162. pptx2/templates/xlsx-icon.emf +0 -0
  163. pptx2/text/__init__.py +0 -0
  164. pptx2/text/fonts.py +482 -0
  165. pptx2/text/layout.py +374 -0
  166. pptx2/text/text.py +1272 -0
  167. pptx2/theme.py +721 -0
  168. pptx2/types.py +36 -0
  169. pptx2/util.py +263 -0
  170. python_pptx2-2.13.0.dist-info/METADATA +351 -0
  171. python_pptx2-2.13.0.dist-info/RECORD +175 -0
  172. python_pptx2-2.13.0.dist-info/WHEEL +5 -0
  173. python_pptx2-2.13.0.dist-info/entry_points.txt +3 -0
  174. python_pptx2-2.13.0.dist-info/licenses/LICENSE +22 -0
  175. python_pptx2-2.13.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,389 @@
1
+ """Figure / plot embedding for slides — Plotly, Matplotlib, SVG, HTML.
2
+
3
+ PowerPoint can't render Plotly traces, Matplotlib axes, or HTML
4
+ documents directly: every external visualisation has to land on a
5
+ slide as a static image (PNG / SVG via Office's SVG extension) or, for
6
+ HTML, as a screenshot. This module owns those conversion paths so
7
+ recipes and authoring code don't have to re-derive them.
8
+
9
+ Every adapter is **optional**: the third-party libraries the adapters
10
+ need are imported lazily, and missing dependencies surface a clear
11
+ ``ImportError`` naming the right install command rather than a deep
12
+ stack trace from inside the integration.
13
+
14
+ Public functions
15
+ ----------------
16
+
17
+ * :func:`add_plotly_figure` — render a Plotly ``Figure`` and embed it.
18
+ * :func:`add_matplotlib_figure` — render a Matplotlib ``Figure`` and embed it.
19
+ * :func:`add_svg_figure` — embed any SVG markup (path, file-like, or bytes).
20
+ * :func:`add_html_figure` — render an HTML snippet via headless Playwright
21
+ and embed the screenshot.
22
+
23
+ Each returns the embedded :class:`~pptx2.shapes.picture.Picture` shape.
24
+
25
+ Quick install matrix
26
+ ~~~~~~~~~~~~~~~~~~~~
27
+
28
+ * Plotly → ``pip install plotly kaleido``
29
+ * Plotly with SVG output → also ``pip install cairosvg``
30
+ * Matplotlib → ``pip install matplotlib``
31
+ * HTML → ``pip install playwright && playwright install chromium``
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import io
37
+ import os
38
+ from typing import IO, TYPE_CHECKING, Any, Optional, Tuple, Union
39
+
40
+ from pptx2.util import Length
41
+
42
+ if TYPE_CHECKING:
43
+ from pptx2.shapes.picture import Picture
44
+ from pptx2.slide import Slide
45
+
46
+
47
+ PathOrFile = Union[str, "os.PathLike[str]", IO[bytes], bytes]
48
+
49
+
50
+ __all__ = (
51
+ "add_plotly_figure",
52
+ "add_matplotlib_figure",
53
+ "add_svg_figure",
54
+ "add_html_figure",
55
+ "FigureBackendUnavailable",
56
+ )
57
+
58
+
59
+ class FigureBackendUnavailable(ImportError):
60
+ """Raised when an optional figure-rendering dependency is missing.
61
+
62
+ Subclasses :class:`ImportError` so callers can still catch it via
63
+ ``except ImportError`` while letting code that knows about the
64
+ figure pipeline distinguish a missing-dep failure from any other
65
+ import error.
66
+ """
67
+
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # Plotly
71
+ # ---------------------------------------------------------------------------
72
+
73
+
74
+ def add_plotly_figure(
75
+ slide: "Slide",
76
+ figure: Any,
77
+ left: Length,
78
+ top: Length,
79
+ width: Optional[Length] = None,
80
+ height: Optional[Length] = None,
81
+ *,
82
+ format: str = "auto",
83
+ scale: float = 2.0,
84
+ width_px: Optional[int] = None,
85
+ height_px: Optional[int] = None,
86
+ ) -> "Picture":
87
+ """Render a Plotly ``Figure`` and embed the result on *slide*.
88
+
89
+ *figure* is a ``plotly.graph_objects.Figure`` (or anything that
90
+ exposes a ``.to_image(format=...)`` method, which is the public
91
+ plotly contract). *left* / *top* / *width* / *height* are slide
92
+ coordinates; if *width* / *height* are omitted the picture's
93
+ rendered size determines them, exactly like
94
+ :meth:`ShapeTree.add_picture`.
95
+
96
+ *format* picks the embedded image format:
97
+
98
+ * ``"svg"`` — vector, sharpest at any zoom; embedded via Office's
99
+ SVG extension with a PNG fallback (requires ``cairosvg``).
100
+ * ``"png"`` — raster, no extra dependencies beyond ``kaleido``.
101
+ * ``"auto"`` (default) — SVG when ``cairosvg`` is available, PNG
102
+ otherwise.
103
+
104
+ *scale* scales raster output to higher pixel density (2.0 ≈ retina).
105
+ *width_px* / *height_px* override the renderer's pixel canvas; by
106
+ default Plotly's own layout sizing wins.
107
+
108
+ Requires ``plotly`` and ``kaleido``::
109
+
110
+ pip install plotly kaleido
111
+ """
112
+ blob, fmt = _plotly_to_blob(
113
+ figure, format=format, scale=scale,
114
+ width_px=width_px, height_px=height_px,
115
+ )
116
+ return _embed_blob(slide, blob, fmt, left, top, width, height)
117
+
118
+
119
+ def _plotly_to_blob(
120
+ figure: Any,
121
+ *,
122
+ format: str,
123
+ scale: float,
124
+ width_px: Optional[int],
125
+ height_px: Optional[int],
126
+ ) -> Tuple[bytes, str]:
127
+ if not hasattr(figure, "to_image"):
128
+ raise TypeError(
129
+ "add_plotly_figure expects a Plotly Figure (or an object "
130
+ "exposing .to_image(format=...)); got "
131
+ f"{type(figure).__name__!r}. If you have a dict spec, wrap "
132
+ "it with plotly.graph_objects.Figure(spec) first."
133
+ )
134
+
135
+ fmt = _resolve_format(format)
136
+ if fmt == "svg":
137
+ try:
138
+ blob = figure.to_image(
139
+ format="svg",
140
+ scale=scale,
141
+ width=width_px,
142
+ height=height_px,
143
+ )
144
+ except Exception as exc:
145
+ raise FigureBackendUnavailable(
146
+ "Plotly figure → SVG export failed. Ensure both "
147
+ "`plotly` and `kaleido` are installed (`pip install "
148
+ "plotly kaleido`); the underlying error was: %s"
149
+ % (exc,)
150
+ ) from exc
151
+ return blob, "svg"
152
+
153
+ try:
154
+ blob = figure.to_image(
155
+ format="png",
156
+ scale=scale,
157
+ width=width_px,
158
+ height=height_px,
159
+ )
160
+ except Exception as exc:
161
+ raise FigureBackendUnavailable(
162
+ "Plotly figure → PNG export failed. Ensure both `plotly` "
163
+ "and `kaleido` are installed (`pip install plotly kaleido`); "
164
+ "the underlying error was: %s" % (exc,)
165
+ ) from exc
166
+ return blob, "png"
167
+
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # Matplotlib
171
+ # ---------------------------------------------------------------------------
172
+
173
+
174
+ def add_matplotlib_figure(
175
+ slide: "Slide",
176
+ figure: Any,
177
+ left: Length,
178
+ top: Length,
179
+ width: Optional[Length] = None,
180
+ height: Optional[Length] = None,
181
+ *,
182
+ format: str = "auto",
183
+ dpi: int = 200,
184
+ ) -> "Picture":
185
+ """Render a Matplotlib ``Figure`` and embed the result on *slide*.
186
+
187
+ *figure* is a ``matplotlib.figure.Figure`` (or anything with a
188
+ ``.savefig(buf, format=...)`` method). Geometry args mirror
189
+ :func:`add_plotly_figure`.
190
+
191
+ *format* picks the embedded image format:
192
+
193
+ * ``"svg"`` — vector, sharpest at zoom; needs ``cairosvg`` for the
194
+ PNG fallback (Office's SVG extension requires both).
195
+ * ``"png"`` — raster only.
196
+ * ``"auto"`` (default) — SVG when ``cairosvg`` is available, PNG
197
+ otherwise.
198
+
199
+ *dpi* applies to PNG output only; SVG is resolution-independent.
200
+
201
+ Requires ``matplotlib``::
202
+
203
+ pip install matplotlib
204
+ """
205
+ if not hasattr(figure, "savefig"):
206
+ raise TypeError(
207
+ "add_matplotlib_figure expects a Matplotlib Figure (or an "
208
+ "object exposing .savefig); got "
209
+ f"{type(figure).__name__!r}."
210
+ )
211
+
212
+ fmt = _resolve_format(format)
213
+ buf = io.BytesIO()
214
+ try:
215
+ if fmt == "svg":
216
+ figure.savefig(buf, format="svg", bbox_inches="tight")
217
+ else:
218
+ figure.savefig(buf, format="png", dpi=dpi, bbox_inches="tight")
219
+ except ImportError as exc: # pragma: no cover - defensive
220
+ raise FigureBackendUnavailable(
221
+ "matplotlib not installed; `pip install matplotlib`."
222
+ ) from exc
223
+ return _embed_blob(slide, buf.getvalue(), fmt, left, top, width, height)
224
+
225
+
226
+ # ---------------------------------------------------------------------------
227
+ # SVG (any source)
228
+ # ---------------------------------------------------------------------------
229
+
230
+
231
+ def add_svg_figure(
232
+ slide: "Slide",
233
+ svg: PathOrFile,
234
+ left: Length,
235
+ top: Length,
236
+ width: Optional[Length] = None,
237
+ height: Optional[Length] = None,
238
+ *,
239
+ png_fallback: Optional[PathOrFile] = None,
240
+ ) -> "Picture":
241
+ """Embed an arbitrary SVG (path, file-like, or bytes) on *slide*.
242
+
243
+ Thin wrapper over :meth:`ShapeTree.add_svg_picture` — useful as the
244
+ same-shape entry point alongside :func:`add_plotly_figure` /
245
+ :func:`add_matplotlib_figure` so authoring code can route every
246
+ figure kind through the ``pptx2.design.figures`` module.
247
+
248
+ When *png_fallback* is omitted the SVG is rasterised via
249
+ ``cairosvg`` for the Office SVG-extension PNG companion.
250
+ """
251
+ return slide.shapes.add_svg_picture(
252
+ svg, left, top, width, height, png_fallback=png_fallback
253
+ )
254
+
255
+
256
+ # ---------------------------------------------------------------------------
257
+ # HTML (headless browser proxy)
258
+ # ---------------------------------------------------------------------------
259
+
260
+
261
+ def add_html_figure(
262
+ slide: "Slide",
263
+ html: Union[str, bytes],
264
+ left: Length,
265
+ top: Length,
266
+ width: Optional[Length] = None,
267
+ height: Optional[Length] = None,
268
+ *,
269
+ viewport: Tuple[int, int] = (1280, 720),
270
+ device_scale_factor: float = 2.0,
271
+ wait_until: str = "networkidle",
272
+ full_page: bool = False,
273
+ timeout_ms: int = 15000,
274
+ ) -> "Picture":
275
+ """Render an HTML snippet to a screenshot via Playwright and embed it.
276
+
277
+ PowerPoint has no HTML-rendering surface. This adapter screenshots
278
+ the rendered DOM in a headless Chromium and embeds the PNG —
279
+ suitable for diagrams from web-based tools (Mermaid, D3, Vega-Lite
280
+ via mini-HTML wrappers, branded layouts) that don't have a direct
281
+ image export.
282
+
283
+ *html* may be raw markup or already-encoded ``bytes``. External
284
+ URLs (``<img src="https://...">``) are loaded as long as the
285
+ process has network access.
286
+
287
+ *viewport* sizes the headless browser canvas in CSS pixels;
288
+ *device_scale_factor* renders at the equivalent of a retina
289
+ display by default for crisper text. *full_page=True* captures
290
+ the entire scroll height instead of just the viewport — useful
291
+ when the rendered content overflows.
292
+
293
+ Requires ``playwright`` and a browser install::
294
+
295
+ pip install playwright
296
+ playwright install chromium
297
+ """
298
+ blob = _html_to_png(
299
+ html,
300
+ viewport=viewport,
301
+ device_scale_factor=device_scale_factor,
302
+ wait_until=wait_until,
303
+ full_page=full_page,
304
+ timeout_ms=timeout_ms,
305
+ )
306
+ return _embed_blob(slide, blob, "png", left, top, width, height)
307
+
308
+
309
+ def _html_to_png(
310
+ html: Union[str, bytes],
311
+ *,
312
+ viewport: Tuple[int, int],
313
+ device_scale_factor: float,
314
+ wait_until: str,
315
+ full_page: bool,
316
+ timeout_ms: int,
317
+ ) -> bytes:
318
+ try:
319
+ from playwright.sync_api import sync_playwright # type: ignore[import-not-found]
320
+ except ImportError as exc:
321
+ raise FigureBackendUnavailable(
322
+ "HTML rendering needs Playwright; install with "
323
+ "`pip install playwright && playwright install chromium`."
324
+ ) from exc
325
+
326
+ if isinstance(html, bytes):
327
+ html = html.decode("utf-8", "replace")
328
+
329
+ with sync_playwright() as pw:
330
+ try:
331
+ browser = pw.chromium.launch()
332
+ except Exception as exc:
333
+ raise FigureBackendUnavailable(
334
+ "Playwright failed to launch Chromium. Did you run "
335
+ "`playwright install chromium`? Original error: %s"
336
+ % (exc,)
337
+ ) from exc
338
+ try:
339
+ page = browser.new_page(
340
+ viewport={"width": viewport[0], "height": viewport[1]},
341
+ device_scale_factor=device_scale_factor,
342
+ )
343
+ page.set_default_timeout(timeout_ms)
344
+ page.set_content(html, wait_until=wait_until)
345
+ return page.screenshot(full_page=full_page, type="png")
346
+ finally:
347
+ browser.close()
348
+
349
+
350
+ # ---------------------------------------------------------------------------
351
+ # Internal: embed a blob (PNG or SVG) using the right shapetree path.
352
+ # ---------------------------------------------------------------------------
353
+
354
+
355
+ def _embed_blob(
356
+ slide: "Slide",
357
+ blob: bytes,
358
+ fmt: str,
359
+ left: Length,
360
+ top: Length,
361
+ width: Optional[Length],
362
+ height: Optional[Length],
363
+ ) -> "Picture":
364
+ if fmt == "svg":
365
+ return slide.shapes.add_svg_picture(
366
+ blob, left, top, width, height
367
+ )
368
+ return slide.shapes.add_picture(
369
+ io.BytesIO(blob), left, top, width=width, height=height
370
+ )
371
+
372
+
373
+ def _resolve_format(format: str) -> str:
374
+ """Return ``"svg"`` or ``"png"`` based on *format* and cairosvg availability."""
375
+ if format not in ("auto", "svg", "png"):
376
+ raise ValueError(
377
+ f"format must be 'auto', 'svg', or 'png'; got {format!r}"
378
+ )
379
+ if format == "auto":
380
+ # SVG is the visual win when both halves of the Office SVG
381
+ # extension can be produced — i.e. when cairosvg is installed
382
+ # to rasterise the PNG fallback. Fall back to PNG otherwise
383
+ # so callers without cairosvg still get a working picture.
384
+ try:
385
+ import cairosvg # type: ignore[import-not-found] # noqa: F401
386
+ except ImportError:
387
+ return "png"
388
+ return "svg"
389
+ return format