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/oxml/text.py ADDED
@@ -0,0 +1,815 @@
1
+ """Custom element classes for text-related XML elements"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import TYPE_CHECKING, Callable, cast
7
+
8
+ from pptx2.enum.lang import MSO_LANGUAGE_ID
9
+ from pptx2.enum.text import (
10
+ MSO_AUTO_SIZE,
11
+ MSO_TEXT_UNDERLINE_TYPE,
12
+ MSO_VERTICAL_ANCHOR,
13
+ PP_PARAGRAPH_ALIGNMENT,
14
+ )
15
+ from pptx2.exc import InvalidXmlError
16
+ from pptx2.oxml import parse_xml, register_element_cls
17
+ from pptx2.oxml.dml.fill import CT_GradientFillProperties
18
+ from pptx2.oxml.ns import nsdecls, qn
19
+ from pptx2.oxml.simpletypes import (
20
+ ST_Coordinate32,
21
+ ST_Percentage,
22
+ ST_PositiveCoordinate32,
23
+ ST_TextAutonumberScheme,
24
+ ST_TextBulletStartAtNum,
25
+ ST_TextCapsType,
26
+ ST_TextColumnCount,
27
+ ST_TextFontScalePercentOrPercentString,
28
+ ST_TextFontSize,
29
+ ST_TextIndentLevelType,
30
+ ST_TextPoint,
31
+ ST_TextSpacingPercentOrPercentString,
32
+ ST_TextSpacingPoint,
33
+ ST_TextStrikeType,
34
+ ST_TextTabAlignType,
35
+ ST_TextTypeface,
36
+ ST_TextWrappingType,
37
+ XsdBoolean,
38
+ )
39
+ from pptx2.oxml.xmlchemy import (
40
+ BaseOxmlElement,
41
+ Choice,
42
+ OneAndOnlyOne,
43
+ OneOrMore,
44
+ OptionalAttribute,
45
+ RequiredAttribute,
46
+ ZeroOrMore,
47
+ ZeroOrOne,
48
+ ZeroOrOneChoice,
49
+ )
50
+ from pptx2.util import Emu, Length
51
+
52
+ if TYPE_CHECKING:
53
+ from pptx2.oxml.action import CT_Hyperlink
54
+ from pptx2.oxml.dml.effect import CT_EffectList
55
+ from pptx2.oxml.shapes.shared import CT_LineProperties
56
+
57
+
58
+ class CT_RegularTextRun(BaseOxmlElement):
59
+ """`a:r` custom element class"""
60
+
61
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
62
+
63
+ rPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
64
+ "a:rPr", successors=("a:t",)
65
+ )
66
+ t: BaseOxmlElement = OneAndOnlyOne("a:t") # pyright: ignore[reportAssignmentType]
67
+
68
+ @property
69
+ def text(self) -> str:
70
+ """All text of (required) `a:t` child."""
71
+ text = self.t.text
72
+ # -- t.text is None when t element is empty, e.g. '<a:t/>' --
73
+ return text or ""
74
+
75
+ @text.setter
76
+ def text(self, value: str): # pyright: ignore[reportIncompatibleMethodOverride]
77
+ self.t.text = self._escape_ctrl_chars(value)
78
+
79
+ @staticmethod
80
+ def _escape_ctrl_chars(s: str) -> str:
81
+ """Return str after replacing each control character with a plain-text escape.
82
+
83
+ For example, a BEL character (x07) would appear as "_x0007_". Horizontal-tab
84
+ (x09) and line-feed (x0A) are not escaped. All other characters in the range
85
+ x00-x1F are escaped.
86
+ """
87
+ return re.sub(r"([\x00-\x08\x0B-\x1F])", lambda match: "_x%04X_" % ord(match.group(1)), s)
88
+
89
+
90
+ class CT_TextBody(BaseOxmlElement):
91
+ """`p:txBody` custom element class.
92
+
93
+ Also used for `c:txPr` in charts and perhaps other elements.
94
+ """
95
+
96
+ add_p: Callable[[], CT_TextParagraph]
97
+ p_lst: list[CT_TextParagraph]
98
+ get_or_add_lstStyle: Callable[[], CT_TextListStyle]
99
+
100
+ bodyPr: CT_TextBodyProperties = OneAndOnlyOne( # pyright: ignore[reportAssignmentType]
101
+ "a:bodyPr"
102
+ )
103
+ lstStyle: CT_TextListStyle | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
104
+ "a:lstStyle", successors=("a:p",)
105
+ )
106
+ p: CT_TextParagraph = OneOrMore("a:p") # pyright: ignore[reportAssignmentType]
107
+
108
+ def clear_content(self):
109
+ """Remove all `a:p` children, but leave any others.
110
+
111
+ cf. lxml `_Element.clear()` method which removes all children.
112
+ """
113
+ for p in self.p_lst:
114
+ self.remove(p)
115
+
116
+ @property
117
+ def defRPr(self) -> CT_TextCharacterProperties:
118
+ """`a:defRPr` element of required first `p` child, added with its ancestors if not present.
119
+
120
+ Used when element is a ``c:txPr`` in a chart and the `p` element is used only to specify
121
+ formatting, not content.
122
+ """
123
+ p = self.p_lst[0]
124
+ pPr = p.get_or_add_pPr()
125
+ defRPr = pPr.get_or_add_defRPr()
126
+ return defRPr
127
+
128
+ @property
129
+ def is_empty(self) -> bool:
130
+ """True if only a single empty `a:p` element is present."""
131
+ ps = self.p_lst
132
+ if len(ps) > 1:
133
+ return False
134
+
135
+ if not ps:
136
+ raise InvalidXmlError("p:txBody must have at least one a:p")
137
+
138
+ if ps[0].text != "":
139
+ return False
140
+ return True
141
+
142
+ @classmethod
143
+ def new(cls):
144
+ """Return a new `p:txBody` element tree."""
145
+ xml = cls._txBody_tmpl()
146
+ txBody = parse_xml(xml)
147
+ return txBody
148
+
149
+ @classmethod
150
+ def new_a_txBody(cls) -> CT_TextBody:
151
+ """Return a new `a:txBody` element tree.
152
+
153
+ Suitable for use in a table cell and possibly other situations.
154
+ """
155
+ xml = cls._a_txBody_tmpl()
156
+ txBody = cast(CT_TextBody, parse_xml(xml))
157
+ return txBody
158
+
159
+ @classmethod
160
+ def new_p_txBody(cls):
161
+ """Return a new `p:txBody` element tree, suitable for use in an `p:sp` element."""
162
+ xml = cls._p_txBody_tmpl()
163
+ return parse_xml(xml)
164
+
165
+ @classmethod
166
+ def new_txPr(cls):
167
+ """Return a `c:txPr` element tree.
168
+
169
+ Suitable for use in a chart object like data labels or tick labels.
170
+ The trailing ``<a:endParaRPr lang="en-US"/>`` is required by
171
+ PowerPoint's strict open-time validator — bare ``<a:p>...</a:p>``
172
+ passes the OOXML schema and LibreOffice but triggers the
173
+ "PowerPoint found a problem with content. Repair?" dialog,
174
+ after which PowerPoint silently empties the chart.
175
+ """
176
+ xml = (
177
+ "<c:txPr %s>\n"
178
+ " <a:bodyPr/>\n"
179
+ " <a:lstStyle/>\n"
180
+ " <a:p>\n"
181
+ " <a:pPr>\n"
182
+ " <a:defRPr/>\n"
183
+ " </a:pPr>\n"
184
+ ' <a:endParaRPr lang="en-US"/>\n'
185
+ " </a:p>\n"
186
+ "</c:txPr>\n"
187
+ ) % nsdecls("c", "a")
188
+ txPr = parse_xml(xml)
189
+ return txPr
190
+
191
+ def unclear_content(self):
192
+ """Ensure p:txBody has at least one a:p child.
193
+
194
+ Intuitively, reverse a ".clear_content()" operation to minimum conformance with spec
195
+ (single empty paragraph).
196
+ """
197
+ if len(self.p_lst) > 0:
198
+ return
199
+ self.add_p()
200
+
201
+ @classmethod
202
+ def _a_txBody_tmpl(cls):
203
+ return "<a:txBody %s>\n <a:bodyPr/>\n <a:p/>\n</a:txBody>\n" % (nsdecls("a"))
204
+
205
+ @classmethod
206
+ def _p_txBody_tmpl(cls):
207
+ return "<p:txBody %s>\n <a:bodyPr/>\n <a:p/>\n</p:txBody>\n" % (nsdecls("p", "a"))
208
+
209
+ @classmethod
210
+ def _txBody_tmpl(cls):
211
+ return "<p:txBody %s>\n <a:bodyPr/>\n <a:lstStyle/>\n <a:p/>\n</p:txBody>\n" % (
212
+ nsdecls("a", "p")
213
+ )
214
+
215
+
216
+ class CT_TextListStyle(BaseOxmlElement):
217
+ """`a:lstStyle` custom element class.
218
+
219
+ The per-indent-level default text properties of a text body. Only
220
+ `a:lvl1pPr` is modelled; the other eight levels are rarely addressed
221
+ programmatically and are preserved verbatim when present.
222
+
223
+ Defaults written here outlive a text replacement — ``TextFrame.text = ...``
224
+ removes the `a:p` children and nothing else — which is what makes this the
225
+ right home for "style this text frame, populate it later".
226
+ """
227
+
228
+ get_or_add_lvl1pPr: Callable[[], CT_TextParagraphProperties]
229
+
230
+ _lvl_tag_seq = tuple("a:lvl%dpPr" % n for n in range(1, 10)) + ("a:extLst",)
231
+ lvl1pPr: CT_TextParagraphProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
232
+ "a:lvl1pPr", successors=_lvl_tag_seq[1:]
233
+ )
234
+ del _lvl_tag_seq
235
+
236
+
237
+ class CT_TextBodyProperties(BaseOxmlElement):
238
+ """`a:bodyPr` custom element class."""
239
+
240
+ _add_noAutofit: Callable[[], BaseOxmlElement]
241
+ _add_normAutofit: Callable[[], CT_TextNormalAutofit]
242
+ _add_spAutoFit: Callable[[], BaseOxmlElement]
243
+ _remove_eg_textAutoFit: Callable[[], None]
244
+
245
+ noAutofit: BaseOxmlElement | None
246
+ normAutofit: CT_TextNormalAutofit | None
247
+ spAutoFit: BaseOxmlElement | None
248
+
249
+ eg_textAutoFit = ZeroOrOneChoice(
250
+ (Choice("a:noAutofit"), Choice("a:normAutofit"), Choice("a:spAutoFit")),
251
+ successors=("a:scene3d", "a:sp3d", "a:flatTx", "a:extLst"),
252
+ )
253
+ lIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
254
+ "lIns", ST_Coordinate32, default=Emu(91440)
255
+ )
256
+ tIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
257
+ "tIns", ST_Coordinate32, default=Emu(45720)
258
+ )
259
+ rIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
260
+ "rIns", ST_Coordinate32, default=Emu(91440)
261
+ )
262
+ bIns: Length = OptionalAttribute( # pyright: ignore[reportAssignmentType]
263
+ "bIns", ST_Coordinate32, default=Emu(45720)
264
+ )
265
+ anchor: MSO_VERTICAL_ANCHOR | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
266
+ "anchor", MSO_VERTICAL_ANCHOR
267
+ )
268
+ wrap: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
269
+ "wrap", ST_TextWrappingType
270
+ )
271
+ numCol: int | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
272
+ "numCol", ST_TextColumnCount
273
+ )
274
+ spcCol: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
275
+ "spcCol", ST_PositiveCoordinate32
276
+ )
277
+
278
+ @property
279
+ def autofit(self):
280
+ """The autofit setting for the text frame, a member of the `MSO_AUTO_SIZE` enumeration."""
281
+ if self.noAutofit is not None:
282
+ return MSO_AUTO_SIZE.NONE
283
+ if self.normAutofit is not None:
284
+ return MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
285
+ if self.spAutoFit is not None:
286
+ return MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT
287
+ return None
288
+
289
+ @autofit.setter
290
+ def autofit(self, value: MSO_AUTO_SIZE | None):
291
+ if value is not None and value not in MSO_AUTO_SIZE:
292
+ raise ValueError(
293
+ f"only None or a member of the MSO_AUTO_SIZE enumeration can be assigned to"
294
+ f" CT_TextBodyProperties.autofit, got {value}"
295
+ )
296
+ self._remove_eg_textAutoFit()
297
+ if value == MSO_AUTO_SIZE.NONE:
298
+ self._add_noAutofit()
299
+ elif value == MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE:
300
+ self._add_normAutofit()
301
+ elif value == MSO_AUTO_SIZE.SHAPE_TO_FIT_TEXT:
302
+ self._add_spAutoFit()
303
+
304
+
305
+ class CT_TextCharacterProperties(BaseOxmlElement):
306
+ """Custom element class for `a:rPr`, `a:defRPr`, and `a:endParaRPr`.
307
+
308
+ 'rPr' is short for 'run properties', and it corresponds to the |Font| proxy class.
309
+ """
310
+
311
+ get_or_add_hlinkClick: Callable[[], CT_Hyperlink]
312
+ get_or_add_latin: Callable[[], CT_TextFont]
313
+ get_or_add_ln: Callable[[], CT_LineProperties]
314
+ get_or_add_effectLst: Callable[[], CT_EffectList]
315
+ _remove_latin: Callable[[], None]
316
+ _remove_hlinkClick: Callable[[], None]
317
+ _remove_ln: Callable[[], None]
318
+ _remove_effectLst: Callable[[], None]
319
+
320
+ # -- Per ISO-IEC-29500-1 `CT_TextCharacterProperties`, the child element
321
+ # -- order is: ln, EG_FillProperties, EG_EffectProperties (effectLst /
322
+ # -- effectDag), highlight, EG_TextUnderlineLine (uLnTx / uLn),
323
+ # -- EG_TextUnderlineFill (uFillTx / uFill), latin, ea, cs, sym, hlinkClick,
324
+ # -- hlinkMouseOver, rtl, extLst.
325
+ ln: CT_LineProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
326
+ "a:ln",
327
+ successors=(
328
+ "a:noFill",
329
+ "a:solidFill",
330
+ "a:gradFill",
331
+ "a:blipFill",
332
+ "a:pattFill",
333
+ "a:grpFill",
334
+ "a:effectLst",
335
+ "a:effectDag",
336
+ "a:highlight",
337
+ "a:uLnTx",
338
+ "a:uLn",
339
+ "a:uFillTx",
340
+ "a:uFill",
341
+ "a:latin",
342
+ "a:ea",
343
+ "a:cs",
344
+ "a:sym",
345
+ "a:hlinkClick",
346
+ "a:hlinkMouseOver",
347
+ "a:rtl",
348
+ "a:extLst",
349
+ ),
350
+ )
351
+ eg_fillProperties = ZeroOrOneChoice(
352
+ (
353
+ Choice("a:noFill"),
354
+ Choice("a:solidFill"),
355
+ Choice("a:gradFill"),
356
+ Choice("a:blipFill"),
357
+ Choice("a:pattFill"),
358
+ Choice("a:grpFill"),
359
+ ),
360
+ successors=(
361
+ "a:effectLst",
362
+ "a:effectDag",
363
+ "a:highlight",
364
+ "a:uLnTx",
365
+ "a:uLn",
366
+ "a:uFillTx",
367
+ "a:uFill",
368
+ "a:latin",
369
+ "a:ea",
370
+ "a:cs",
371
+ "a:sym",
372
+ "a:hlinkClick",
373
+ "a:hlinkMouseOver",
374
+ "a:rtl",
375
+ "a:extLst",
376
+ ),
377
+ )
378
+ effectLst: CT_EffectList | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
379
+ "a:effectLst",
380
+ successors=(
381
+ "a:effectDag",
382
+ "a:highlight",
383
+ "a:uLnTx",
384
+ "a:uLn",
385
+ "a:uFillTx",
386
+ "a:uFill",
387
+ "a:latin",
388
+ "a:ea",
389
+ "a:cs",
390
+ "a:sym",
391
+ "a:hlinkClick",
392
+ "a:hlinkMouseOver",
393
+ "a:rtl",
394
+ "a:extLst",
395
+ ),
396
+ )
397
+ latin: CT_TextFont | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
398
+ "a:latin",
399
+ successors=(
400
+ "a:ea",
401
+ "a:cs",
402
+ "a:sym",
403
+ "a:hlinkClick",
404
+ "a:hlinkMouseOver",
405
+ "a:rtl",
406
+ "a:extLst",
407
+ ),
408
+ )
409
+ hlinkClick: CT_Hyperlink | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
410
+ "a:hlinkClick", successors=("a:hlinkMouseOver", "a:rtl", "a:extLst")
411
+ )
412
+
413
+ lang: MSO_LANGUAGE_ID | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
414
+ "lang", MSO_LANGUAGE_ID
415
+ )
416
+ sz: int | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
417
+ "sz", ST_TextFontSize
418
+ )
419
+ b: bool | None = OptionalAttribute("b", XsdBoolean) # pyright: ignore[reportAssignmentType]
420
+ i: bool | None = OptionalAttribute("i", XsdBoolean) # pyright: ignore[reportAssignmentType]
421
+ u: MSO_TEXT_UNDERLINE_TYPE | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
422
+ "u", MSO_TEXT_UNDERLINE_TYPE
423
+ )
424
+ strike: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
425
+ "strike", ST_TextStrikeType
426
+ )
427
+ cap: str | None = OptionalAttribute("cap", ST_TextCapsType) # pyright: ignore[reportAssignmentType]
428
+ spc: Length | None = OptionalAttribute("spc", ST_TextPoint) # pyright: ignore[reportAssignmentType]
429
+ baseline: float | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
430
+ "baseline", ST_Percentage
431
+ )
432
+
433
+ def _new_gradFill(self):
434
+ return CT_GradientFillProperties.new_gradFill()
435
+
436
+ def add_hlinkClick(self, rId: str) -> CT_Hyperlink:
437
+ """Add an `a:hlinkClick` child element with r:id attribute set to `rId`."""
438
+ hlinkClick = self.get_or_add_hlinkClick()
439
+ hlinkClick.rId = rId
440
+ return hlinkClick
441
+
442
+
443
+ class CT_TextField(BaseOxmlElement):
444
+ """`a:fld` field element, for either a slide number or date field."""
445
+
446
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
447
+
448
+ rPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
449
+ "a:rPr", successors=("a:pPr", "a:t")
450
+ )
451
+ t: BaseOxmlElement | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
452
+ "a:t", successors=()
453
+ )
454
+
455
+ @property
456
+ def text(self) -> str: # pyright: ignore[reportIncompatibleMethodOverride]
457
+ """The text of the `a:t` child element."""
458
+ t = self.t
459
+ if t is None:
460
+ return ""
461
+ return t.text or ""
462
+
463
+
464
+ class CT_TextFont(BaseOxmlElement):
465
+ """Custom element class for `a:latin`, `a:ea`, `a:cs`, and `a:sym`.
466
+
467
+ These occur as child elements of CT_TextCharacterProperties, e.g. `a:rPr`.
468
+ """
469
+
470
+ typeface: str = RequiredAttribute( # pyright: ignore[reportAssignmentType]
471
+ "typeface", ST_TextTypeface
472
+ )
473
+
474
+
475
+ class CT_TextLineBreak(BaseOxmlElement):
476
+ """`a:br` line break element"""
477
+
478
+ get_or_add_rPr: Callable[[], CT_TextCharacterProperties]
479
+
480
+ rPr = ZeroOrOne("a:rPr", successors=())
481
+
482
+ @property
483
+ def text(self): # pyright: ignore[reportIncompatibleMethodOverride]
484
+ """Unconditionally a single vertical-tab character.
485
+
486
+ A line break element can contain no text other than the implicit line feed it
487
+ represents.
488
+ """
489
+ return "\v"
490
+
491
+
492
+ class CT_OfficeMathMarker(BaseOxmlElement):
493
+ """`a14:m` marker PowerPoint wraps around Office Math (OMML).
494
+
495
+ DrawingML paragraphs store native equations as this element containing
496
+ ``m:oMath`` / ``m:oMathPara``. The marker itself has no visible run
497
+ text; :attr:`text` is always the empty string so paragraph concatenation
498
+ and ``clear()`` treat it like other content children.
499
+ """
500
+
501
+ @property
502
+ def text(self) -> str:
503
+ return ""
504
+
505
+
506
+ class CT_TextNormalAutofit(BaseOxmlElement):
507
+ """`a:normAutofit` element specifying fit text to shape font reduction, etc."""
508
+
509
+ fontScale = OptionalAttribute(
510
+ "fontScale", ST_TextFontScalePercentOrPercentString, default=100.0
511
+ )
512
+
513
+
514
+ class CT_TextParagraph(BaseOxmlElement):
515
+ """`a:p` custom element class"""
516
+
517
+ get_or_add_endParaRPr: Callable[[], CT_TextCharacterProperties]
518
+ get_or_add_pPr: Callable[[], CT_TextParagraphProperties]
519
+ r_lst: list[CT_RegularTextRun]
520
+ _add_br: Callable[[], CT_TextLineBreak]
521
+ _add_r: Callable[[], CT_RegularTextRun]
522
+
523
+ pPr: CT_TextParagraphProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
524
+ "a:pPr", successors=("a:r", "a:br", "a:fld", "a14:m", "a:endParaRPr")
525
+ )
526
+ r = ZeroOrMore("a:r", successors=("a:endParaRPr",))
527
+ br = ZeroOrMore("a:br", successors=("a:endParaRPr",))
528
+ endParaRPr: CT_TextCharacterProperties | None = ZeroOrOne("a:endParaRPr", successors=()) # pyright: ignore[reportAssignmentType]
529
+
530
+ def add_br(self) -> CT_TextLineBreak:
531
+ """Return a newly appended `a:br` element."""
532
+ return self._add_br()
533
+
534
+ def add_r(self, text: str | None = None) -> CT_RegularTextRun:
535
+ """Return a newly appended `a:r` element."""
536
+ r = self._add_r()
537
+ if text:
538
+ r.text = text
539
+ return r
540
+
541
+ def add_math(self, math_elm: CT_OfficeMathMarker) -> CT_OfficeMathMarker:
542
+ """Append an `a14:m` equation marker before `a:endParaRPr`."""
543
+ self.insert_element_before(math_elm, "a:endParaRPr")
544
+ return math_elm
545
+
546
+ def append_text(self, text: str):
547
+ """Append `a:r` and `a:br` elements to `p` based on `text`.
548
+
549
+ Any `\n` or `\v` (vertical-tab) characters in `text` delimit `a:r` (run) elements and
550
+ themselves are translated to `a:br` (line-break) elements. The vertical-tab character
551
+ appears in clipboard text from PowerPoint at "soft" line-breaks (new-line, but not new
552
+ paragraph).
553
+ """
554
+ for idx, r_str in enumerate(re.split("\n|\v", text)):
555
+ # ---breaks are only added _between_ items, not at start---
556
+ if idx > 0:
557
+ self.add_br()
558
+ # ---runs that would be empty are not added---
559
+ if r_str:
560
+ self.add_r(r_str)
561
+
562
+ @property
563
+ def content_children(
564
+ self,
565
+ ) -> tuple[CT_RegularTextRun | CT_TextLineBreak | CT_TextField | CT_OfficeMathMarker, ...]:
566
+ """Sequence containing text-container child elements of this `a:p` element.
567
+
568
+ These include `a:r`, `a:br`, `a:fld`, and `a14:m` (native equations).
569
+ """
570
+ return tuple(
571
+ e
572
+ for e in self
573
+ if isinstance(
574
+ e, (CT_RegularTextRun, CT_TextLineBreak, CT_TextField, CT_OfficeMathMarker)
575
+ )
576
+ )
577
+
578
+ @property
579
+ def text(self) -> str: # pyright: ignore[reportIncompatibleMethodOverride]
580
+ """str text contained in this paragraph."""
581
+ # ---note this shadows the lxml _Element.text---
582
+ return "".join([child.text for child in self.content_children])
583
+
584
+ def _new_r(self):
585
+ r_xml = "<a:r %s><a:t/></a:r>" % nsdecls("a")
586
+ return parse_xml(r_xml)
587
+
588
+
589
+ class CT_TextParagraphProperties(BaseOxmlElement):
590
+ """`a:pPr` custom element class."""
591
+
592
+ get_or_add_defRPr: Callable[[], CT_TextCharacterProperties]
593
+ get_or_add_buAutoNum: Callable[[], CT_TextAutonumberBullet]
594
+ get_or_add_tabLst: Callable[[], CT_TextTabStopList]
595
+ _add_lnSpc: Callable[[], CT_TextSpacing]
596
+ _add_spcAft: Callable[[], CT_TextSpacing]
597
+ _add_spcBef: Callable[[], CT_TextSpacing]
598
+ _remove_lnSpc: Callable[[], None]
599
+ _remove_spcAft: Callable[[], None]
600
+ _remove_spcBef: Callable[[], None]
601
+
602
+ _tag_seq = (
603
+ "a:lnSpc",
604
+ "a:spcBef",
605
+ "a:spcAft",
606
+ "a:buClrTx",
607
+ "a:buClr",
608
+ "a:buSzTx",
609
+ "a:buSzPct",
610
+ "a:buSzPts",
611
+ "a:buFontTx",
612
+ "a:buFont",
613
+ "a:buNone",
614
+ "a:buAutoNum",
615
+ "a:buChar",
616
+ "a:buBlip",
617
+ "a:tabLst",
618
+ "a:defRPr",
619
+ "a:extLst",
620
+ )
621
+ lnSpc: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
622
+ "a:lnSpc", successors=_tag_seq[1:]
623
+ )
624
+ spcBef: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
625
+ "a:spcBef", successors=_tag_seq[2:]
626
+ )
627
+ spcAft: CT_TextSpacing | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
628
+ "a:spcAft", successors=_tag_seq[3:]
629
+ )
630
+ buAutoNum: CT_TextAutonumberBullet | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
631
+ "a:buAutoNum", successors=_tag_seq[12:]
632
+ )
633
+ tabLst: CT_TextTabStopList | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
634
+ "a:tabLst", successors=_tag_seq[15:]
635
+ )
636
+ defRPr: CT_TextCharacterProperties | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
637
+ "a:defRPr", successors=_tag_seq[16:]
638
+ )
639
+ lvl: int = OptionalAttribute( # pyright: ignore[reportAssignmentType]
640
+ "lvl", ST_TextIndentLevelType, default=0
641
+ )
642
+ algn: PP_PARAGRAPH_ALIGNMENT | None = OptionalAttribute("algn", PP_PARAGRAPH_ALIGNMENT) # pyright: ignore[reportAssignmentType]
643
+ rtl: bool | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
644
+ "rtl", XsdBoolean
645
+ )
646
+ del _tag_seq
647
+
648
+ @property
649
+ def line_spacing(self) -> float | Length | None:
650
+ """The spacing between baselines of successive lines in this paragraph.
651
+
652
+ A float value indicates a number of lines. A |Length| value indicates a fixed spacing.
653
+ Value is contained in `./a:lnSpc/a:spcPts/@val` or `./a:lnSpc/a:spcPct/@val`. Value is
654
+ |None| if no element is present.
655
+ """
656
+ lnSpc = self.lnSpc
657
+ if lnSpc is None:
658
+ return None
659
+ if lnSpc.spcPts is not None:
660
+ return lnSpc.spcPts.val
661
+ return cast(CT_TextSpacingPercent, lnSpc.spcPct).val
662
+
663
+ @line_spacing.setter
664
+ def line_spacing(self, value: float | Length | None):
665
+ self._remove_lnSpc()
666
+ if value is None:
667
+ return
668
+ if isinstance(value, Length):
669
+ self._add_lnSpc().set_spcPts(value)
670
+ else:
671
+ self._add_lnSpc().set_spcPct(value)
672
+
673
+ @property
674
+ def space_after(self) -> Length | None:
675
+ """The EMU equivalent of the centipoints value in `./a:spcAft/a:spcPts/@val`."""
676
+ spcAft = self.spcAft
677
+ if spcAft is None:
678
+ return None
679
+ spcPts = spcAft.spcPts
680
+ if spcPts is None:
681
+ return None
682
+ return spcPts.val
683
+
684
+ @space_after.setter
685
+ def space_after(self, value: Length | None):
686
+ self._remove_spcAft()
687
+ if value is not None:
688
+ self._add_spcAft().set_spcPts(value)
689
+
690
+ @property
691
+ def space_before(self):
692
+ """The EMU equivalent of the centipoints value in `./a:spcBef/a:spcPts/@val`."""
693
+ spcBef = self.spcBef
694
+ if spcBef is None:
695
+ return None
696
+ spcPts = spcBef.spcPts
697
+ if spcPts is None:
698
+ return None
699
+ return spcPts.val
700
+
701
+ @space_before.setter
702
+ def space_before(self, value: Length | None):
703
+ self._remove_spcBef()
704
+ if value is not None:
705
+ self._add_spcBef().set_spcPts(value)
706
+
707
+ def get_or_add_buAutoNum_only(self) -> CT_TextAutonumberBullet:
708
+ """Return the `a:buAutoNum` child, adding it if not present.
709
+
710
+ The bullet element group (``buNone``/``buAutoNum``/``buChar``/``buBlip``) is an
711
+ XSD choice — at most one may be present — so any sibling bullet element is
712
+ removed first to keep the paragraph schema-valid.
713
+ """
714
+ if self.buAutoNum is None:
715
+ for tag in ("a:buNone", "a:buChar", "a:buBlip"):
716
+ for sib in self.findall(qn(tag)):
717
+ self.remove(sib)
718
+ return self.get_or_add_buAutoNum()
719
+
720
+
721
+ class CT_TextAutonumberBullet(BaseOxmlElement):
722
+ """`a:buAutoNum` element, an automatically-numbered list bullet."""
723
+
724
+ type: str = RequiredAttribute( # pyright: ignore[reportAssignmentType]
725
+ "type", ST_TextAutonumberScheme
726
+ )
727
+ startAt: int | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
728
+ "startAt", ST_TextBulletStartAtNum
729
+ )
730
+
731
+
732
+ class CT_TextTabStop(BaseOxmlElement):
733
+ """`a:tab` element, a single tab stop in a `a:tabLst`."""
734
+
735
+ pos: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
736
+ "pos", ST_Coordinate32
737
+ )
738
+ algn: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType]
739
+ "algn", ST_TextTabAlignType
740
+ )
741
+
742
+
743
+ class CT_TextTabStopList(BaseOxmlElement):
744
+ """`a:tabLst` element, the ordered list of tab stops for a paragraph."""
745
+
746
+ _add_tab: Callable[[], CT_TextTabStop]
747
+ tab_lst: list[CT_TextTabStop]
748
+
749
+ tab = ZeroOrMore("a:tab", successors=())
750
+
751
+ def add_tab(self, pos: Length, algn: str | None = None) -> CT_TextTabStop:
752
+ """Append and return an `a:tab` child with the given position and alignment."""
753
+ tab = self._add_tab()
754
+ tab.pos = pos
755
+ if algn is not None:
756
+ tab.algn = algn
757
+ return tab
758
+
759
+
760
+ class CT_TextSpacing(BaseOxmlElement):
761
+ """Used for `a:lnSpc`, `a:spcBef`, and `a:spcAft` elements."""
762
+
763
+ get_or_add_spcPct: Callable[[], CT_TextSpacingPercent]
764
+ get_or_add_spcPts: Callable[[], CT_TextSpacingPoint]
765
+ _remove_spcPct: Callable[[], None]
766
+ _remove_spcPts: Callable[[], None]
767
+
768
+ # this should actually be a OneAndOnlyOneChoice, but that's not
769
+ # implemented yet.
770
+ spcPct: CT_TextSpacingPercent | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
771
+ "a:spcPct"
772
+ )
773
+ spcPts: CT_TextSpacingPoint | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
774
+ "a:spcPts"
775
+ )
776
+
777
+ def set_spcPct(self, value: float):
778
+ """Set spacing to `value` lines, e.g. 1.75 lines.
779
+
780
+ A ./a:spcPts child is removed if present.
781
+ """
782
+ self._remove_spcPts()
783
+ spcPct = self.get_or_add_spcPct()
784
+ spcPct.val = value
785
+
786
+ def set_spcPts(self, value: Length):
787
+ """Set spacing to `value` points. A ./a:spcPct child is removed if present."""
788
+ self._remove_spcPct()
789
+ spcPts = self.get_or_add_spcPts()
790
+ spcPts.val = value
791
+
792
+
793
+ class CT_TextSpacingPercent(BaseOxmlElement):
794
+ """`a:spcPct` element, specifying spacing in thousandths of a percent in its `val` attribute."""
795
+
796
+ val: float = RequiredAttribute( # pyright: ignore[reportAssignmentType]
797
+ "val", ST_TextSpacingPercentOrPercentString
798
+ )
799
+
800
+
801
+ class CT_TextSpacingPoint(BaseOxmlElement):
802
+ """`a:spcPts` element, specifying spacing in centipoints in its `val` attribute."""
803
+
804
+ val: Length = RequiredAttribute( # pyright: ignore[reportAssignmentType]
805
+ "val", ST_TextSpacingPoint
806
+ )
807
+
808
+
809
+ # -- These element classes are introduced by the python-pptx2 fork; the global
810
+ # -- registry in `pptx2.oxml.__init__` doesn't know about them, so register
811
+ # -- them here (the `register_element_cls` callable is already defined by the
812
+ # -- time this module is imported during `pptx2.oxml` initialization). --
813
+ register_element_cls("a:buAutoNum", CT_TextAutonumberBullet)
814
+ register_element_cls("a:tabLst", CT_TextTabStopList)
815
+ register_element_cls("a:tab", CT_TextTabStop)