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
pptx2/types.py ADDED
@@ -0,0 +1,36 @@
1
+ """Abstract types used by `python-pptx`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from typing_extensions import Protocol
8
+
9
+ if TYPE_CHECKING:
10
+ from pptx2.opc.package import XmlPart
11
+ from pptx2.util import Length
12
+
13
+
14
+ class ProvidesExtents(Protocol):
15
+ """An object that has width and height."""
16
+
17
+ @property
18
+ def height(self) -> Length:
19
+ """Distance between top and bottom extents of shape in EMUs."""
20
+ ...
21
+
22
+ @property
23
+ def width(self) -> Length:
24
+ """Distance between left and right extents of shape in EMUs."""
25
+ ...
26
+
27
+
28
+ class ProvidesPart(Protocol):
29
+ """An object that provides access to its XmlPart.
30
+
31
+ This type is for objects that need access to their part, possibly because they need access to
32
+ the package or related parts.
33
+ """
34
+
35
+ @property
36
+ def part(self) -> XmlPart: ...
pptx2/util.py ADDED
@@ -0,0 +1,263 @@
1
+ """Utility functions and classes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ from typing import Any, Callable, Generic, TypeVar, cast
7
+
8
+
9
+ def _coerce_emu(value):
10
+ """Internal: coerce a coordinate value to integer EMU.
11
+
12
+ Accepts int / Length passes through unchanged, floats are rounded
13
+ half-to-even, and ``None`` is passed through (callers may use ``None``
14
+ for "don't set"). ``bool`` is rejected — booleans are an int subclass
15
+ in Python but a boolean coordinate is always a programming error.
16
+
17
+ OOXML's ``CT_Point2D`` (``<a:off>``) requires ``x``/``y`` to be
18
+ ``xs:long`` and ``CT_PositiveSize2D`` (``<a:ext>``) requires
19
+ ``cx``/``cy`` to be ``xs:nonNegativeInteger``. Float-valued strings
20
+ in those attributes are schema-invalid; PowerPoint's strict
21
+ open-time validator rejects them with the "Repair?" dialog even
22
+ though python-pptx, the OOXML XSDs, and LibreOffice all accept
23
+ them silently. Coercing at constructor entry catches the common
24
+ case of ``(Inches(N) - gutter) / 2`` producing a float.
25
+ """
26
+ if value is None:
27
+ return None
28
+ if isinstance(value, bool):
29
+ raise TypeError("bool is not a valid EMU coordinate")
30
+ if isinstance(value, int):
31
+ return value
32
+ # Reject str/bytes explicitly — they're convertible via float() but
33
+ # passing a coordinate as a string is always a programming error,
34
+ # not a unit-conversion case we want to silently accept.
35
+ if isinstance(value, (str, bytes, bytearray)):
36
+ raise TypeError(
37
+ f"Expected int, float, or Emu-derived length; got "
38
+ f"{type(value).__name__}: {value!r}"
39
+ )
40
+ if isinstance(value, float):
41
+ try:
42
+ return int(round(value))
43
+ except (OverflowError, ValueError) as exc:
44
+ # NaN → ValueError, ±inf → OverflowError. Normalise to
45
+ # TypeError so callers get a single, informative exception.
46
+ raise TypeError(
47
+ f"Cannot coerce non-finite float to EMU: {value!r}"
48
+ ) from exc
49
+ try:
50
+ return int(round(float(value)))
51
+ except (TypeError, ValueError, OverflowError):
52
+ raise TypeError(
53
+ f"Expected int, float, or Emu-derived length; got "
54
+ f"{type(value).__name__}: {value!r}"
55
+ )
56
+
57
+
58
+ class Length(int):
59
+ """Base class for length classes Inches, Emu, Cm, Mm, and Pt.
60
+
61
+ Provides properties for converting length values to convenient units.
62
+ """
63
+
64
+ _EMUS_PER_INCH = 914400
65
+ _EMUS_PER_CENTIPOINT = 127
66
+ _EMUS_PER_CM = 360000
67
+ _EMUS_PER_MM = 36000
68
+ _EMUS_PER_PT = 12700
69
+
70
+ def __new__(cls, emu: int):
71
+ return int.__new__(cls, emu)
72
+
73
+ @property
74
+ def inches(self) -> float:
75
+ """Floating point length in inches."""
76
+ return self / float(self._EMUS_PER_INCH)
77
+
78
+ @property
79
+ def centipoints(self) -> int:
80
+ """Integer length in hundredths of a point (1/7200 inch).
81
+
82
+ Used internally because PowerPoint stores font size in centipoints.
83
+ """
84
+ return self // self._EMUS_PER_CENTIPOINT
85
+
86
+ @property
87
+ def cm(self) -> float:
88
+ """Floating point length in centimeters."""
89
+ return self / float(self._EMUS_PER_CM)
90
+
91
+ @property
92
+ def emu(self) -> int:
93
+ """Integer length in English Metric Units."""
94
+ return self
95
+
96
+ @property
97
+ def mm(self) -> float:
98
+ """Floating point length in millimeters."""
99
+ return self / float(self._EMUS_PER_MM)
100
+
101
+ @property
102
+ def pt(self) -> float:
103
+ """Floating point length in points."""
104
+ return self / float(self._EMUS_PER_PT)
105
+
106
+
107
+ class Inches(Length):
108
+ """Convenience constructor for length in inches."""
109
+
110
+ def __new__(cls, inches: float):
111
+ emu = int(inches * Length._EMUS_PER_INCH)
112
+ return Length.__new__(cls, emu)
113
+
114
+
115
+ class Centipoints(Length):
116
+ """Convenience constructor for length in hundredths of a point."""
117
+
118
+ def __new__(cls, centipoints: int):
119
+ emu = int(centipoints * Length._EMUS_PER_CENTIPOINT)
120
+ return Length.__new__(cls, emu)
121
+
122
+
123
+ class Cm(Length):
124
+ """Convenience constructor for length in centimeters."""
125
+
126
+ def __new__(cls, cm: float):
127
+ emu = int(cm * Length._EMUS_PER_CM)
128
+ return Length.__new__(cls, emu)
129
+
130
+
131
+ class Emu(Length):
132
+ """Convenience constructor for length in english metric units."""
133
+
134
+ def __new__(cls, emu: int):
135
+ return Length.__new__(cls, int(emu))
136
+
137
+
138
+ class Mm(Length):
139
+ """Convenience constructor for length in millimeters."""
140
+
141
+ def __new__(cls, mm: float):
142
+ emu = int(mm * Length._EMUS_PER_MM)
143
+ return Length.__new__(cls, emu)
144
+
145
+
146
+ class Pt(Length):
147
+ """Convenience value class for specifying a length in points."""
148
+
149
+ def __new__(cls, points: float):
150
+ emu = int(points * Length._EMUS_PER_PT)
151
+ return Length.__new__(cls, emu)
152
+
153
+
154
+ _T = TypeVar("_T")
155
+
156
+
157
+ class lazyproperty(Generic[_T]):
158
+ """Decorator like @property, but evaluated only on first access.
159
+
160
+ Like @property, this can only be used to decorate methods having only a `self` parameter, and
161
+ is accessed like an attribute on an instance, i.e. trailing parentheses are not used. Unlike
162
+ @property, the decorated method is only evaluated on first access; the resulting value is
163
+ cached and that same value returned on second and later access without re-evaluation of the
164
+ method.
165
+
166
+ Like @property, this class produces a *data descriptor* object, which is stored in the __dict__
167
+ of the *class* under the name of the decorated method ('fget' nominally). The cached value is
168
+ stored in the __dict__ of the *instance* under that same name.
169
+
170
+ Because it is a data descriptor (as opposed to a *non-data descriptor*), its `__get__()` method
171
+ is executed on each access of the decorated attribute; the __dict__ item of the same name is
172
+ "shadowed" by the descriptor.
173
+
174
+ While this may represent a performance improvement over a property, its greater benefit may be
175
+ its other characteristics. One common use is to construct collaborator objects, removing that
176
+ "real work" from the constructor, while still only executing once. It also de-couples client
177
+ code from any sequencing considerations; if it's accessed from more than one location, it's
178
+ assured it will be ready whenever needed.
179
+
180
+ Loosely based on: https://stackoverflow.com/a/6849299/1902513.
181
+
182
+ A lazyproperty is read-only. There is no counterpart to the optional "setter" (or deleter)
183
+ behavior of an @property. This is critically important to maintaining its immutability and
184
+ idempotence guarantees. Attempting to assign to a lazyproperty raises AttributeError
185
+ unconditionally.
186
+
187
+ The parameter names in the methods below correspond to this usage example::
188
+
189
+ class Obj(object)
190
+
191
+ @lazyproperty
192
+ def fget(self):
193
+ return 'some result'
194
+
195
+ obj = Obj()
196
+
197
+ Not suitable for wrapping a function (as opposed to a method) because it is not callable.
198
+ """
199
+
200
+ def __init__(self, fget: Callable[..., _T]) -> None:
201
+ """*fget* is the decorated method (a "getter" function).
202
+
203
+ A lazyproperty is read-only, so there is only an *fget* function (a regular
204
+ @property can also have an fset and fdel function). This name was chosen for
205
+ consistency with Python's `property` class which uses this name for the
206
+ corresponding parameter.
207
+ """
208
+ # --- maintain a reference to the wrapped getter method
209
+ self._fget = fget
210
+ # --- and store the name of that decorated method
211
+ self._name = fget.__name__
212
+ # --- adopt fget's __name__, __doc__, and other attributes
213
+ functools.update_wrapper(self, fget) # pyright: ignore
214
+
215
+ def __get__(self, obj: Any, type: Any = None) -> _T:
216
+ """Called on each access of 'fget' attribute on class or instance.
217
+
218
+ *self* is this instance of a lazyproperty descriptor "wrapping" the property
219
+ method it decorates (`fget`, nominally).
220
+
221
+ *obj* is the "host" object instance when the attribute is accessed from an
222
+ object instance, e.g. `obj = Obj(); obj.fget`. *obj* is None when accessed on
223
+ the class, e.g. `Obj.fget`.
224
+
225
+ *type* is the class hosting the decorated getter method (`fget`) on both class
226
+ and instance attribute access.
227
+ """
228
+ # --- when accessed on class, e.g. Obj.fget, just return this descriptor
229
+ # --- instance (patched above to look like fget).
230
+ if obj is None:
231
+ return self # type: ignore
232
+
233
+ # --- when accessed on instance, start by checking instance __dict__ for
234
+ # --- item with key matching the wrapped function's name
235
+ value = obj.__dict__.get(self._name)
236
+ if value is None:
237
+ # --- on first access, the __dict__ item will be absent. Evaluate fget()
238
+ # --- and store that value in the (otherwise unused) host-object
239
+ # --- __dict__ value of same name ('fget' nominally)
240
+ value = self._fget(obj)
241
+ obj.__dict__[self._name] = value
242
+ return cast(_T, value)
243
+
244
+ def __set__(self, obj: Any, value: Any) -> None:
245
+ """Raises unconditionally, to preserve read-only behavior.
246
+
247
+ This decorator is intended to implement immutable (and idempotent) object
248
+ attributes. For that reason, assignment to this property must be explicitly
249
+ prevented.
250
+
251
+ If this __set__ method was not present, this descriptor would become a
252
+ *non-data descriptor*. That would be nice because the cached value would be
253
+ accessed directly once set (__dict__ attrs have precedence over non-data
254
+ descriptors on instance attribute lookup). The problem is, there would be
255
+ nothing to stop assignment to the cached value, which would overwrite the result
256
+ of `fget()` and break both the immutability and idempotence guarantees of this
257
+ decorator.
258
+
259
+ The performance with this __set__() method in place was roughly 0.4 usec per
260
+ access when measured on a 2.8GHz development machine; so quite snappy and
261
+ probably not a rich target for optimization efforts.
262
+ """
263
+ raise AttributeError("can't set attribute")
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-pptx2
3
+ Version: 2.13.0
4
+ Summary: Create, read, and update PowerPoint 2007+ (.pptx) files. Fork of power-pptx / python-pptx, published as python-pptx2.
5
+ Author: Matěj Štágl
6
+ Author-email: stagl@wattlescript.org
7
+ Maintainer: Matěj Štágl
8
+ Maintainer-email: stagl@wattlescript.org
9
+ License: MIT
10
+ Project-URL: Changelog, https://github.com/lofcz/python-pptx2/blob/master/HISTORY.rst
11
+ Project-URL: Documentation, https://github.com/lofcz/python-pptx2
12
+ Project-URL: Homepage, https://github.com/lofcz/python-pptx2
13
+ Project-URL: Issues, https://github.com/lofcz/python-pptx2/issues
14
+ Project-URL: Repository, https://github.com/lofcz/python-pptx2
15
+ Project-URL: Roadmap, https://github.com/lofcz/python-pptx2/blob/master/ROADMAP.md
16
+ Project-URL: ParentFork, https://github.com/CodeHalwell/power-pptx
17
+ Project-URL: Upstream, https://github.com/scanny/python-pptx
18
+ Project-URL: UpstreamDocumentation, https://python-pptx.readthedocs.io/en/latest/
19
+ Keywords: powerpoint,ppt,pptx,openxml,office,presentation
20
+ Classifier: Development Status :: 5 - Production/Stable
21
+ Classifier: Environment :: Console
22
+ Classifier: Intended Audience :: Developers
23
+ Classifier: License :: OSI Approved :: MIT License
24
+ Classifier: Operating System :: OS Independent
25
+ Classifier: Programming Language :: Python
26
+ Classifier: Programming Language :: Python :: 3
27
+ Classifier: Programming Language :: Python :: 3.9
28
+ Classifier: Programming Language :: Python :: 3.10
29
+ Classifier: Programming Language :: Python :: 3.11
30
+ Classifier: Programming Language :: Python :: 3.12
31
+ Classifier: Programming Language :: Python :: 3.13
32
+ Classifier: Topic :: Office/Business :: Office Suites
33
+ Classifier: Topic :: Software Development :: Libraries
34
+ Requires-Python: >=3.9
35
+ Description-Content-Type: text/x-rst
36
+ License-File: LICENSE
37
+ Requires-Dist: Pillow>=3.3.2
38
+ Requires-Dist: XlsxWriter>=0.5.7
39
+ Requires-Dist: lxml>=3.1.0
40
+ Requires-Dist: typing_extensions>=4.9.0
41
+ Provides-Extra: math
42
+ Requires-Dist: latex2mathml>=3.0; extra == "math"
43
+ Requires-Dist: mathml2omml>=0.0.2; extra == "math"
44
+ Dynamic: license-file
45
+
46
+ python-pptx2
47
+ =============
48
+
49
+ |PyPI| |PyPI - Python Versions| |CI| |License| |Documentation|
50
+
51
+ .. |PyPI| image:: https://img.shields.io/pypi/v/python-pptx2.svg
52
+ :target: https://pypi.org/project/python-pptx2/
53
+ :alt: PyPI
54
+
55
+ .. |PyPI - Python Versions| image:: https://img.shields.io/pypi/pyversions/python-pptx2.svg
56
+ :target: https://pypi.org/project/python-pptx2/
57
+ :alt: Python 3.9 – 3.13
58
+
59
+ .. |CI| image:: https://github.com/lofcz/python-pptx2/actions/workflows/ci.yml/badge.svg
60
+ :target: https://github.com/lofcz/python-pptx2/actions/workflows/ci.yml
61
+ :alt: CI status
62
+
63
+ .. |License| image:: https://img.shields.io/badge/license-MIT-blue.svg
64
+ :target: https://github.com/lofcz/python-pptx2/blob/master/LICENSE
65
+ :alt: MIT License
66
+
67
+ .. |Documentation| image:: https://img.shields.io/badge/docs-github.com%2Flofcz%2Fpython--pptx2-blue.svg
68
+ :target: https://github.com/lofcz/python-pptx2
69
+ :alt: Documentation
70
+
71
+ **PowerPoint decks from Python, that actually fit.**
72
+
73
+ *python-pptx2* is a fork of `power-pptx`_ (Daniel Halwell) and, through
74
+ it, of `python-pptx`_ by `Steve Canny`_. It is a Python library for
75
+ creating, reading, and updating PowerPoint (.pptx) files, plus native
76
+ LaTeX equations.
77
+
78
+ +----------------+----------------------------------------------------------+
79
+ | **Package** | ``python-pptx2`` on PyPI (imports as ``pptx2``) |
80
+ +----------------+----------------------------------------------------------+
81
+ | **Python** | 3.9 – 3.13 |
82
+ +----------------+----------------------------------------------------------+
83
+ | **License** | MIT |
84
+ +----------------+----------------------------------------------------------+
85
+ | **Source** | https://github.com/lofcz/python-pptx2 |
86
+ +----------------+----------------------------------------------------------+
87
+
88
+ .. _`power-pptx`: https://github.com/CodeHalwell/power-pptx
89
+
90
+ A typical use is generating a PowerPoint presentation from dynamic
91
+ content such as a database query, an analytics output, an LLM payload,
92
+ or a JSON spec — and downloading the generated .pptx file. It runs on
93
+ any Python-capable platform (macOS, Linux, Windows) and does not
94
+ require Microsoft PowerPoint to be installed or licensed.
95
+
96
+ **Why this fork exists: space-aware authoring.** The headline
97
+ proposition is that text doesn't overflow its container and shapes
98
+ don't slide off the edges of the slide. Three layered tools used
99
+ together catch ~all real-world layout issues:
100
+
101
+ 1. ``TextFrame.fit_text(...)`` measures with Pillow font metrics and
102
+ bakes a fitting size into the XML *before* save.
103
+ 2. ``text_frame.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE`` lets
104
+ PowerPoint shrink at render time as a fallback.
105
+ 3. ``slide.lint()`` catches what slipped through; ``auto_fix()`` (or
106
+ the one-call ``slide.tidy()``) nudges off-slide shapes back inside.
107
+
108
+ Reach for python-pptx2 whenever the deck is generated dynamically and
109
+ has to look right without manual cleanup.
110
+
111
+ Installation
112
+ ------------
113
+
114
+ Install from PyPI::
115
+
116
+ pip install python-pptx2
117
+
118
+ Then in Python::
119
+
120
+ from pptx2 import Presentation
121
+
122
+ ``python-pptx2`` imports as ``pptx2`` so it can sit beside upstream
123
+ ``python-pptx`` (``pptx``) and the parent fork ``power-pptx``
124
+ (``power_pptx``). To migrate from those packages, replace
125
+ ``from pptx import`` / ``from power_pptx import`` with
126
+ ``from pptx2 import``.
127
+
128
+ Optional dependencies:
129
+
130
+ * ``cairosvg`` — install only if you want ``add_svg_picture(...)`` to
131
+ auto-rasterise the PNG fallback.
132
+ * ``pyyaml`` — install only if you want ``DesignTokens.from_yaml``.
133
+ * ``python-pptx2[math]`` (``latex2mathml`` + ``mathml2omml``) — required
134
+ for ``slide.shapes.add_equation(...)`` / ``paragraph.add_math(...)``.
135
+ * ``soffice`` (LibreOffice) on PATH — required for
136
+ ``Presentation.render_thumbnails()``.
137
+ * ``pdftoppm`` (Poppler) or ``pypdfium2`` — required for the
138
+ PDF→PNG split path in the thumbnail renderer.
139
+
140
+ Claude Code skill
141
+ -----------------
142
+
143
+ python-pptx2 ships a Claude Code skill alongside the library — pip-install
144
+ the package and the skill files are already on disk inside it. Install
145
+ the skill into your local Claude Code skills directory with::
146
+
147
+ python -m pptx2.skill install
148
+
149
+ This copies ``SKILL.md`` and the ``references/`` directory into
150
+ ``~/.claude/skills/python-pptx2/``. Claude Code (and any compatible
151
+ Claude Agent SDK harness) will pick it up automatically the next time
152
+ it starts.
153
+
154
+ Other useful commands::
155
+
156
+ # Print the skill source path inside the installed package
157
+ python -m pptx2.skill path
158
+
159
+ # Install into a custom directory
160
+ python -m pptx2.skill install --target /path/to/skills/python-pptx2
161
+
162
+ # Refuse to overwrite an existing install
163
+ python -m pptx2.skill install --no-overwrite
164
+
165
+ The skill documents the headline space-aware-authoring workflow, the
166
+ ``BBox`` value object, the one-call ``add_text`` / ``add_arrow``
167
+ helpers, the diagram recipes (``horizontal_pipeline``,
168
+ ``hub_and_spoke``, ``cycle``, ``decision_tree``,
169
+ ``comparison_columns``), and 16 focused reference docs covering
170
+ effects, animations, transitions, theming, charts, tables, 3D,
171
+ SmartArt, and rendering. It also includes an *anti-patterns* section
172
+ calling out the mistakes LLMs commonly make (mis-comparing wrapper
173
+ objects, assuming ``add_connector`` puts an arrowhead on the line,
174
+ sizing a diagram to a broken picture's bbox rather than its enclosing
175
+ card, …).
176
+
177
+ Quick start
178
+ -----------
179
+
180
+ A minimal end-to-end deck-generation pattern::
181
+
182
+ from pptx2 import Presentation, BBox, audit
183
+ from pptx2.diagrams import horizontal_pipeline
184
+ from pptx2.util import Inches
185
+
186
+ prs = Presentation()
187
+ slide = prs.slides.add_slide(prs.slide_layouts[5])
188
+ slide.shapes.title.text = "Pipeline overview"
189
+
190
+ horizontal_pipeline(
191
+ slide,
192
+ BBox.from_inches(0.5, 2.5, 9, 2.2),
193
+ steps=["Extract", "Classify", "Enrich", "Output"],
194
+ accent="#0B5CFF",
195
+ )
196
+
197
+ slide.shapes.add_text(
198
+ BBox.from_inches(0.5, 5.5, 9, 1),
199
+ text="Four-stage data pipeline.",
200
+ align="center", size_pt=14, color="#666666",
201
+ )
202
+
203
+ # Lint + safe auto-fixes
204
+ slide.tidy()
205
+
206
+ # Optional: full-deck audit
207
+ print(audit(prs).markdown())
208
+
209
+ prs.save("out.pptx")
210
+
211
+ What's new in the fork
212
+ ----------------------
213
+
214
+ The fork extends the 1.0.2 surface with features the upstream roadmap
215
+ did not cover. All additions are drop-in compatible — existing
216
+ scripts keep working — and every new feature ships with a round-trip
217
+ regression test.
218
+
219
+ * **Space-aware authoring** — ``TextFrame.fit_text`` bakes a fitting
220
+ size before save; ``auto_size`` flags shrink at render time; the
221
+ layout linter reports text overflow / off-slide shapes /
222
+ collisions. Three-tier safety so generated decks look right
223
+ without manual cleanup.
224
+
225
+ * **Geometry and convenience helpers (v2.8)** — first-class
226
+ ``BBox`` value object with ``inset`` / ``split_h`` / ``split_v`` /
227
+ ``grid`` / ``contains`` / ``intersection``. One-call
228
+ ``slide.shapes.add_text(bb, text=..., color="#0B5CFF",
229
+ align="center")`` collapses the historical seven-line styling
230
+ ritual. Hex-string shortcuts (``shape.fill_hex("#0B5CFF")``,
231
+ ``shape.line_hex(...)``). ``shape.set_text_preserving_format(new)``
232
+ for templated placeholders.
233
+
234
+ * **Real arrows** —
235
+ ``slide.shapes.add_arrow(start=a, end=b, head="triangle",
236
+ color="#0B5CFF", inset_pt=6)`` produces a connector with an
237
+ arrowhead and auto-routed endpoints (mid-edge of target shape,
238
+ pulled back by ``inset_pt``). No XML required.
239
+
240
+ * **Diagram recipes** — ``horizontal_pipeline``, ``vertical_pipeline``,
241
+ ``hub_and_spoke``, ``cycle``, ``decision_tree``,
242
+ ``comparison_columns`` from ``pptx2.diagrams`` cover ~80% of
243
+ architecture-deck patterns. Each takes a slide, a ``BBox``, and a
244
+ content spec.
245
+
246
+ * **Picture and slide helpers** — ``picture.replace_with(builder,
247
+ padding=...)`` deletes a broken / sub-quality picture and calls a
248
+ builder in its place. ``picture.enclosing_container()`` finds the
249
+ surrounding card so the rebuild fills the right area.
250
+ ``slide.tidy()`` is the one-call lint + safe auto-fix.
251
+ ``slide.find_empty_region(...)`` returns an unused area for
252
+ greenfield placement.
253
+
254
+ * **Whole-deck audit** — ``pptx2.audit(prs)`` returns an
255
+ ``AuditReport`` with lint issues, broken pictures, empty slides,
256
+ uncommon-font warnings, and oversized-picture warnings. Markdown
257
+ output for chat replies.
258
+
259
+ * **Visual effects** — outer shadow, glow, soft edges, blur, and
260
+ reflection exposed as non-mutating proxies on every shape;
261
+ alpha-tinted colours (``RGBColor.alpha``); gradient fills with
262
+ ``linear`` / ``radial`` / ``rectangular`` / ``shape`` kinds and
263
+ mutable stops; line ends, caps, joins, and compound lines.
264
+
265
+ * **Animations and transitions** — preset entrance / exit / emphasis
266
+ effects; motion-path presets; per-paragraph reveal; sequencing
267
+ context manager; per-slide and deck-wide transitions including
268
+ Morph and the other ``p14:`` extension transitions.
269
+
270
+ * **JSON authoring** — ``pptx2.compose.from_spec(...)`` builds a
271
+ deck from a JSON-shaped spec; ``import_slide`` and
272
+ ``apply_template`` cover cross-presentation operations.
273
+
274
+ * **Theme reader and writer** — read theme colours and fonts; write
275
+ fresh ``<a:srgbClr>`` values into the clrScheme; apply a theme
276
+ imported from a ``.potx``.
277
+
278
+ * **Picture effects** — transparency, brightness, contrast, recolor
279
+ (grayscale, sepia, washout, duotone); native SVG embedding with
280
+ PNG fallback.
281
+
282
+ * **Design-system layer** — ``DesignTokens`` (palette, typography,
283
+ shadows, radii, spacings) loadable from a dict, YAML, or a
284
+ ``.pptx``; a token-resolving ``shape.style`` facade; ``Grid`` /
285
+ ``Stack`` layout primitives; opinionated slide recipes
286
+ (``title``, ``bullet``, ``kpi``, ``quote``, ``image_hero``); a
287
+ starter pack of three example token sets.
288
+
289
+ * **Shape-level building blocks** — ``add_kpi_card``,
290
+ ``add_progress_bar``, ``add_gauge``, ``add_status_pill``,
291
+ ``add_stat_strip``, ``add_article_card``: token-driven cards that
292
+ return small dataclasses exposing the constituent shapes for
293
+ further tweaks.
294
+
295
+ * **Charting** — chart palette presets independent of
296
+ ``chart_style``; ten quick-layout presets; per-series gradient
297
+ and pattern fills.
298
+
299
+ * **3D primitives and SmartArt text substitution** — bevel and
300
+ extrusion via ``shape.three_d``;
301
+ ``slide.smart_art[i].set_text([...])``.
302
+
303
+ * **Slide thumbnails** — ``Presentation.render_thumbnails()`` or
304
+ ``pptx2.render.render_slides(prs, slides=[0, 1, 2],
305
+ name_template="slide-{:02d}.png")`` shells out to LibreOffice for
306
+ PNG previews.
307
+
308
+ See ``HISTORY.rst`` for the full changelog and ``ROADMAP.md`` for the
309
+ broader plan.
310
+
311
+ Attribution
312
+ -----------
313
+
314
+ This project is a fork of `scanny/python-pptx`_, originally created and
315
+ maintained by Steve Canny under the MIT License. The original
316
+ copyright notice is preserved in ``LICENSE``. Sincere thanks to Steve
317
+ and to all the upstream contributors whose work this project builds
318
+ on.
319
+
320
+ The fork was created to continue development of features the upstream
321
+ roadmap did not cover (notably effects, transitions, animations, theme
322
+ customization, and a higher-level design layer). See ``HISTORY.rst``
323
+ for the divergence point and changelog from there forward.
324
+
325
+ This project is **not** affiliated with or endorsed by Microsoft.
326
+ "PowerPoint" is a trademark of Microsoft Corporation; it is used here
327
+ only descriptively to identify the file format the library reads and
328
+ writes.
329
+
330
+ Documentation
331
+ -------------
332
+
333
+ **Project site:** https://github.com/lofcz/python-pptx2 — the
334
+ Astro/React documentation site (source in ``site/``), plus the
335
+ Sphinx docs under ``docs/``.
336
+
337
+ The Sphinx documentation lives under ``docs/`` and covers both the
338
+ inherited 1.0.2 API and every feature added by the fork. Browse
339
+ `examples with screenshots`_ to get a quick idea what you can do.
340
+
341
+ The bundled Claude Code skill (``python -m pptx2.skill install``)
342
+ is the most up-to-date entry point for using the post-fork APIs.
343
+
344
+ .. _`python-pptx`:
345
+ https://github.com/scanny/python-pptx
346
+ .. _`scanny/python-pptx`:
347
+ https://github.com/scanny/python-pptx
348
+ .. _`Steve Canny`:
349
+ https://github.com/scanny
350
+ .. _`examples with screenshots`:
351
+ https://python-pptx.readthedocs.org/en/latest/user/quickstart.html