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/design/layout.py ADDED
@@ -0,0 +1,370 @@
1
+ """Build-time layout helpers — :class:`Grid` and :class:`Stack`.
2
+
3
+ These objects compute ``(left, top, width, height)`` rectangles so callers
4
+ don't eyeball EMU values when placing shapes. They never read or mutate
5
+ slide XML on their own; geometry is only applied to a shape when the
6
+ caller passes it to :meth:`Grid.place` / :meth:`Stack.place` or assigns
7
+ the returned :class:`Box` to the shape's geometry properties directly.
8
+
9
+ Example::
10
+
11
+ from pptx2 import Presentation
12
+ from pptx2.design.layout import Grid, Stack
13
+ from pptx2.util import Inches, Pt
14
+
15
+ prs = Presentation()
16
+ slide = prs.slides.add_slide(prs.slide_layouts[6])
17
+
18
+ grid = Grid(slide, cols=12, rows=6, gutter=Pt(12), margin=Inches(0.5))
19
+ title_box = grid.cell(col=0, row=0, col_span=12, row_span=1)
20
+
21
+ stack = Stack(
22
+ direction="vertical", gap=Pt(8),
23
+ left=Inches(0.5), top=Inches(2),
24
+ width=Inches(9),
25
+ )
26
+ bullet_one = stack.next(height=Inches(0.5))
27
+ bullet_two = stack.next(height=Inches(0.5))
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from typing import TYPE_CHECKING, NamedTuple, Tuple, Union
33
+
34
+ from pptx2.util import Emu, Length
35
+
36
+ if TYPE_CHECKING:
37
+ from pptx2.shapes.base import BaseShape
38
+ from pptx2.slide import Slide
39
+
40
+ MarginSpec = Union[
41
+ int,
42
+ Length,
43
+ Tuple[int, int],
44
+ Tuple[int, int, int, int],
45
+ ]
46
+
47
+
48
+ class Box(NamedTuple):
49
+ """A rectangular region expressed as ``(left, top, width, height)``.
50
+
51
+ All four members are :class:`~pptx2.util.Length` (EMU) instances and so
52
+ can be assigned directly to a shape's positional properties::
53
+
54
+ box = grid.cell(col=0, row=0, col_span=6)
55
+ shape.left, shape.top = box.left, box.top
56
+ shape.width, shape.height = box.width, box.height
57
+ """
58
+
59
+ left: Length
60
+ top: Length
61
+ width: Length
62
+ height: Length
63
+
64
+
65
+ def _slide_dimensions(slide: "Slide") -> tuple[Length, Length]:
66
+ """Return ``(slide_width, slide_height)`` for `slide`.
67
+
68
+ Raises :class:`ValueError` if `slide` is not attached to a presentation
69
+ or if either dimension is unset on that presentation.
70
+ """
71
+ try:
72
+ presentation = slide.part.package.presentation_part.presentation
73
+ except AttributeError as e:
74
+ raise ValueError(
75
+ "slide must be attached to a presentation to use a Grid; "
76
+ "add it via `prs.slides.add_slide(...)` first"
77
+ ) from e
78
+ width, height = presentation.slide_width, presentation.slide_height
79
+ if width is None or height is None:
80
+ raise ValueError(
81
+ "slide width/height must be set on the presentation to use a Grid"
82
+ )
83
+ return width, height
84
+
85
+
86
+ def _apply_box(shape: "BaseShape", box: Box) -> "BaseShape":
87
+ shape.left, shape.top = box.left, box.top
88
+ shape.width, shape.height = box.width, box.height
89
+ return shape
90
+
91
+
92
+ class Grid:
93
+ """A column/row grid spanning a slide's content area.
94
+
95
+ Parameters
96
+ ----------
97
+ slide : Slide
98
+ Host slide. Used only to read the parent presentation's slide
99
+ dimensions; the slide is not mutated.
100
+ cols : int
101
+ Number of columns. Must be >= 1.
102
+ rows : int, optional
103
+ Number of rows. Defaults to 1; cells implicitly span the full
104
+ slide height when only one row is requested.
105
+ gutter : Length, optional
106
+ Spacing between cells, applied between columns and between rows.
107
+ Defaults to 0 (no gutter).
108
+ margin : Length or tuple, optional
109
+ Outer margin. Either a single :class:`Length` (uniform on all
110
+ four sides), or a 2-tuple ``(vertical, horizontal)``, or a
111
+ 4-tuple ``(top, right, bottom, left)``. Defaults to 0.
112
+ """
113
+
114
+ def __init__(
115
+ self,
116
+ slide: "Slide",
117
+ cols: int,
118
+ rows: int = 1,
119
+ gutter: int = 0,
120
+ margin: MarginSpec = 0,
121
+ ):
122
+ slide_w, slide_h = _slide_dimensions(slide)
123
+ self._init_extent(cols, rows, gutter, margin, slide_w, slide_h, 0, 0)
124
+ self._slide_width = slide_w
125
+ self._slide_height = slide_h
126
+
127
+ @classmethod
128
+ def from_box(
129
+ cls,
130
+ box: "Box | tuple[int, int, int, int]",
131
+ cols: int,
132
+ rows: int = 1,
133
+ gutter: int = 0,
134
+ margin: MarginSpec = 0,
135
+ ) -> "Grid":
136
+ """Return a grid that spans `box` rather than the whole slide.
137
+
138
+ `box` is any ``(left, top, width, height)`` sequence — a
139
+ :class:`Box`, a :class:`~pptx2.geometry.BBox`, or a plain
140
+ 4-tuple — so a panel, card row, or content column can carry its own
141
+ grid without a slide reference::
142
+
143
+ panel = BBox.from_inches(0.75, 2.4, 11.8, 3.6)
144
+ grid = Grid.from_box(panel, cols=5, rows=2, gutter=Pt(12))
145
+ grid.place(card, col=2, row=1)
146
+
147
+ No slide is touched, so :attr:`slide_width`-style questions don't
148
+ arise; cells are positioned relative to `box`'s own origin.
149
+ """
150
+ left, top, width, height = (int(v) for v in tuple(box))
151
+ grid = cls.__new__(cls)
152
+ grid._init_extent(cols, rows, gutter, margin, Emu(width), Emu(height), left, top)
153
+ grid._slide_width = None
154
+ grid._slide_height = None
155
+ return grid
156
+
157
+ def _init_extent(
158
+ self,
159
+ cols: int,
160
+ rows: int,
161
+ gutter: int,
162
+ margin: MarginSpec,
163
+ extent_w: Length,
164
+ extent_h: Length,
165
+ origin_left: int,
166
+ origin_top: int,
167
+ ) -> None:
168
+ """Shared constructor body for the slide-spanning and box-spanning cases."""
169
+ if cols < 1:
170
+ raise ValueError("Grid.cols must be >= 1, got %r" % cols)
171
+ if rows < 1:
172
+ raise ValueError("Grid.rows must be >= 1, got %r" % rows)
173
+
174
+ self._cols = int(cols)
175
+ self._rows = int(rows)
176
+ self._gutter = Emu(int(gutter))
177
+ self._top_m, self._right_m, self._bottom_m, self._left_m = self._coerce_margin(
178
+ margin
179
+ )
180
+ self._origin_left = Emu(int(origin_left))
181
+ self._origin_top = Emu(int(origin_top))
182
+
183
+ usable_w = extent_w - self._left_m - self._right_m - self._gutter * (self._cols - 1)
184
+ usable_h = extent_h - self._top_m - self._bottom_m - self._gutter * (self._rows - 1)
185
+ if usable_w <= 0 or usable_h <= 0:
186
+ raise ValueError(
187
+ "grid margins+gutters consume the entire slide; reduce them"
188
+ )
189
+ # store as float so col_span math is exact; we round on emission
190
+ self._col_w = usable_w / self._cols
191
+ self._row_h = usable_h / self._rows
192
+
193
+ @property
194
+ def cols(self) -> int:
195
+ return self._cols
196
+
197
+ @property
198
+ def rows(self) -> int:
199
+ return self._rows
200
+
201
+ def cell(self, col: int = 0, row: int = 0, col_span: int = 1, row_span: int = 1) -> Box:
202
+ """Return the :class:`Box` for the cell starting at (`col`, `row`).
203
+
204
+ `col_span` and `row_span` extend the cell across additional
205
+ columns/rows. Negative indices and out-of-bounds spans raise
206
+ :class:`IndexError`.
207
+ """
208
+ if col < 0 or row < 0:
209
+ raise IndexError("col/row must be non-negative")
210
+ if col_span < 1 or row_span < 1:
211
+ raise IndexError("col_span/row_span must be >= 1")
212
+ if col + col_span > self._cols or row + row_span > self._rows:
213
+ raise IndexError(
214
+ "cell (col=%d, row=%d, col_span=%d, row_span=%d) exceeds "
215
+ "%dx%d grid" % (col, row, col_span, row_span, self._cols, self._rows)
216
+ )
217
+
218
+ left = self._origin_left + self._left_m + (self._col_w + self._gutter) * col
219
+ top = self._origin_top + self._top_m + (self._row_h + self._gutter) * row
220
+ width = self._col_w * col_span + self._gutter * (col_span - 1)
221
+ height = self._row_h * row_span + self._gutter * (row_span - 1)
222
+ return Box(
223
+ Emu(int(round(left))),
224
+ Emu(int(round(top))),
225
+ Emu(int(round(width))),
226
+ Emu(int(round(height))),
227
+ )
228
+
229
+ def place(
230
+ self,
231
+ shape: "BaseShape",
232
+ col: int = 0,
233
+ row: int = 0,
234
+ col_span: int = 1,
235
+ row_span: int = 1,
236
+ ) -> "BaseShape":
237
+ """Move `shape` to the cell at (`col`, `row`) with the given span.
238
+
239
+ Returns the shape so calls can be chained.
240
+ """
241
+ return _apply_box(shape, self.cell(col, row, col_span, row_span))
242
+
243
+ @staticmethod
244
+ def _coerce_margin(margin: MarginSpec) -> Tuple[Length, Length, Length, Length]:
245
+ if isinstance(margin, (tuple, list)):
246
+ if len(margin) == 2:
247
+ v, h = margin
248
+ return Emu(int(v)), Emu(int(h)), Emu(int(v)), Emu(int(h))
249
+ if len(margin) == 4:
250
+ top, right, bottom, left = margin
251
+ return Emu(int(top)), Emu(int(right)), Emu(int(bottom)), Emu(int(left))
252
+ raise ValueError(
253
+ "margin tuple must have 2 or 4 elements, got %d" % len(margin)
254
+ )
255
+ m = Emu(int(margin))
256
+ return m, m, m, m
257
+
258
+
259
+ class Stack:
260
+ """A linear stack of cells laid out vertically or horizontally.
261
+
262
+ Parameters
263
+ ----------
264
+ direction : str
265
+ ``"vertical"`` (default) stacks downward; ``"horizontal"`` stacks
266
+ rightward.
267
+ gap : Length
268
+ Spacing between consecutive cells. Defaults to 0.
269
+ left, top : Length
270
+ Origin of the first cell. Default 0.
271
+ width, height : Length, optional
272
+ Cross-axis span. For a vertical stack, `width` is the cell width
273
+ and each call to :meth:`next` consumes `height`; for a horizontal
274
+ stack, `height` is the cell height and each call consumes `width`.
275
+
276
+ The stack maintains a running cursor that advances after every
277
+ :meth:`next` (or :meth:`place`) call. :meth:`reset` returns the
278
+ cursor to the origin.
279
+ """
280
+
281
+ _AXES = ("vertical", "horizontal")
282
+
283
+ def __init__(
284
+ self,
285
+ direction: str = "vertical",
286
+ gap: int = 0,
287
+ left: int = 0,
288
+ top: int = 0,
289
+ width: int | None = None,
290
+ height: int | None = None,
291
+ ):
292
+ if direction not in self._AXES:
293
+ raise ValueError(
294
+ "direction must be 'vertical' or 'horizontal', got %r" % direction
295
+ )
296
+ self._direction = direction
297
+ self._gap = Emu(int(gap))
298
+ self._origin_left = Emu(int(left))
299
+ self._origin_top = Emu(int(top))
300
+ self._width = None if width is None else Emu(int(width))
301
+ self._height = None if height is None else Emu(int(height))
302
+ self._cursor = 0
303
+
304
+ @property
305
+ def direction(self) -> str:
306
+ return self._direction
307
+
308
+ def reset(self) -> None:
309
+ """Reset the cursor so the next :meth:`next` call starts at the origin."""
310
+ self._cursor = 0
311
+
312
+ def next(self, *, width: int | None = None, height: int | None = None) -> Box:
313
+ """Allocate the next cell and return its :class:`Box`.
314
+
315
+ For a vertical stack, `height` is required; `width` overrides the
316
+ stack-level default. For a horizontal stack, `width` is required;
317
+ `height` overrides the stack-level default.
318
+ """
319
+ leading_gap = 0 if self._cursor == 0 else int(self._gap)
320
+
321
+ if self._direction == "vertical":
322
+ if height is None:
323
+ raise TypeError("vertical Stack.next() requires `height=`")
324
+ cell_h = int(height)
325
+ cell_w = self._width if width is None else int(width)
326
+ if cell_w is None:
327
+ raise TypeError(
328
+ "vertical Stack needs a `width` "
329
+ "(either on the constructor or as a per-cell override)"
330
+ )
331
+ self._cursor += leading_gap
332
+ box = Box(
333
+ self._origin_left,
334
+ Emu(self._origin_top + self._cursor),
335
+ Emu(cell_w),
336
+ Emu(cell_h),
337
+ )
338
+ self._cursor += cell_h
339
+ else:
340
+ if width is None:
341
+ raise TypeError("horizontal Stack.next() requires `width=`")
342
+ cell_w = int(width)
343
+ cell_h = self._height if height is None else int(height)
344
+ if cell_h is None:
345
+ raise TypeError(
346
+ "horizontal Stack needs a `height` "
347
+ "(either on the constructor or as a per-cell override)"
348
+ )
349
+ self._cursor += leading_gap
350
+ box = Box(
351
+ Emu(self._origin_left + self._cursor),
352
+ self._origin_top,
353
+ Emu(cell_w),
354
+ Emu(cell_h),
355
+ )
356
+ self._cursor += cell_w
357
+ return box
358
+
359
+ def place(
360
+ self,
361
+ shape: "BaseShape",
362
+ *,
363
+ width: int | None = None,
364
+ height: int | None = None,
365
+ ) -> "BaseShape":
366
+ """Allocate the next cell and apply it to `shape`.
367
+
368
+ Returns the shape so calls can be chained.
369
+ """
370
+ return _apply_box(shape, self.next(width=width, height=height))