ps-python-pptx 1.1.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 (117) hide show
  1. pptx/__init__.py +84 -0
  2. pptx/action.py +270 -0
  3. pptx/api.py +49 -0
  4. pptx/chart/__init__.py +0 -0
  5. pptx/chart/axis.py +523 -0
  6. pptx/chart/category.py +200 -0
  7. pptx/chart/chart.py +280 -0
  8. pptx/chart/data.py +864 -0
  9. pptx/chart/datalabel.py +288 -0
  10. pptx/chart/legend.py +79 -0
  11. pptx/chart/marker.py +70 -0
  12. pptx/chart/plot.py +412 -0
  13. pptx/chart/point.py +101 -0
  14. pptx/chart/series.py +258 -0
  15. pptx/chart/xlsx.py +272 -0
  16. pptx/chart/xmlwriter.py +1840 -0
  17. pptx/dml/__init__.py +0 -0
  18. pptx/dml/chtfmt.py +40 -0
  19. pptx/dml/color.py +301 -0
  20. pptx/dml/effect.py +41 -0
  21. pptx/dml/fill.py +468 -0
  22. pptx/dml/line.py +100 -0
  23. pptx/enum/__init__.py +0 -0
  24. pptx/enum/action.py +71 -0
  25. pptx/enum/base.py +175 -0
  26. pptx/enum/chart.py +492 -0
  27. pptx/enum/dml.py +405 -0
  28. pptx/enum/lang.py +685 -0
  29. pptx/enum/shapes.py +1029 -0
  30. pptx/enum/text.py +371 -0
  31. pptx/exc.py +23 -0
  32. pptx/media.py +197 -0
  33. pptx/opc/__init__.py +0 -0
  34. pptx/opc/constants.py +331 -0
  35. pptx/opc/oxml.py +188 -0
  36. pptx/opc/package.py +762 -0
  37. pptx/opc/packuri.py +109 -0
  38. pptx/opc/serialized.py +296 -0
  39. pptx/opc/shared.py +20 -0
  40. pptx/opc/spec.py +44 -0
  41. pptx/oxml/__init__.py +492 -0
  42. pptx/oxml/action.py +53 -0
  43. pptx/oxml/chart/__init__.py +0 -0
  44. pptx/oxml/chart/axis.py +297 -0
  45. pptx/oxml/chart/chart.py +282 -0
  46. pptx/oxml/chart/datalabel.py +252 -0
  47. pptx/oxml/chart/legend.py +72 -0
  48. pptx/oxml/chart/marker.py +61 -0
  49. pptx/oxml/chart/plot.py +345 -0
  50. pptx/oxml/chart/series.py +254 -0
  51. pptx/oxml/chart/shared.py +219 -0
  52. pptx/oxml/coreprops.py +288 -0
  53. pptx/oxml/dml/__init__.py +0 -0
  54. pptx/oxml/dml/color.py +111 -0
  55. pptx/oxml/dml/fill.py +197 -0
  56. pptx/oxml/dml/line.py +12 -0
  57. pptx/oxml/ns.py +129 -0
  58. pptx/oxml/presentation.py +130 -0
  59. pptx/oxml/shapes/__init__.py +19 -0
  60. pptx/oxml/shapes/autoshape.py +455 -0
  61. pptx/oxml/shapes/connector.py +107 -0
  62. pptx/oxml/shapes/graphfrm.py +342 -0
  63. pptx/oxml/shapes/groupshape.py +280 -0
  64. pptx/oxml/shapes/picture.py +270 -0
  65. pptx/oxml/shapes/shared.py +523 -0
  66. pptx/oxml/simpletypes.py +740 -0
  67. pptx/oxml/slide.py +347 -0
  68. pptx/oxml/table.py +588 -0
  69. pptx/oxml/text.py +748 -0
  70. pptx/oxml/theme.py +29 -0
  71. pptx/oxml/xmlchemy.py +717 -0
  72. pptx/package.py +222 -0
  73. pptx/parts/__init__.py +0 -0
  74. pptx/parts/chart.py +95 -0
  75. pptx/parts/coreprops.py +167 -0
  76. pptx/parts/embeddedpackage.py +93 -0
  77. pptx/parts/image.py +275 -0
  78. pptx/parts/media.py +37 -0
  79. pptx/parts/presentation.py +126 -0
  80. pptx/parts/slide.py +297 -0
  81. pptx/parts/theme.py +13 -0
  82. pptx/presentation.py +113 -0
  83. pptx/py.typed +0 -0
  84. pptx/shapes/__init__.py +26 -0
  85. pptx/shapes/autoshape.py +355 -0
  86. pptx/shapes/base.py +293 -0
  87. pptx/shapes/connector.py +297 -0
  88. pptx/shapes/freeform.py +337 -0
  89. pptx/shapes/graphfrm.py +166 -0
  90. pptx/shapes/group.py +69 -0
  91. pptx/shapes/picture.py +203 -0
  92. pptx/shapes/placeholder.py +407 -0
  93. pptx/shapes/shapetree.py +1190 -0
  94. pptx/shared.py +82 -0
  95. pptx/slide.py +577 -0
  96. pptx/spec.py +632 -0
  97. pptx/table.py +520 -0
  98. pptx/templates/default.pptx +0 -0
  99. pptx/templates/docx-icon.emf +0 -0
  100. pptx/templates/generic-icon.emf +0 -0
  101. pptx/templates/notes.xml +23 -0
  102. pptx/templates/notesMaster.xml +352 -0
  103. pptx/templates/pptx-icon.emf +0 -0
  104. pptx/templates/theme.xml +321 -0
  105. pptx/templates/xlsx-icon.emf +0 -0
  106. pptx/text/__init__.py +0 -0
  107. pptx/text/fonts.py +399 -0
  108. pptx/text/layout.py +325 -0
  109. pptx/text/text.py +770 -0
  110. pptx/theme.py +135 -0
  111. pptx/types.py +36 -0
  112. pptx/util.py +284 -0
  113. ps_python_pptx-1.1.0.dist-info/METADATA +75 -0
  114. ps_python_pptx-1.1.0.dist-info/RECORD +117 -0
  115. ps_python_pptx-1.1.0.dist-info/WHEEL +5 -0
  116. ps_python_pptx-1.1.0.dist-info/licenses/LICENSE +20 -0
  117. ps_python_pptx-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,219 @@
1
+ """Shared oxml objects for charts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pptx.oxml import parse_xml
6
+ from pptx.oxml.ns import nsdecls
7
+ from pptx.oxml.simpletypes import (
8
+ ST_LayoutMode,
9
+ XsdBoolean,
10
+ XsdDouble,
11
+ XsdString,
12
+ XsdUnsignedInt,
13
+ )
14
+ from pptx.oxml.xmlchemy import (
15
+ BaseOxmlElement,
16
+ OptionalAttribute,
17
+ RequiredAttribute,
18
+ ZeroOrOne,
19
+ )
20
+
21
+
22
+ class CT_Boolean(BaseOxmlElement):
23
+ """
24
+ Common complex type used for elements having a True/False value.
25
+ """
26
+
27
+ val = OptionalAttribute("val", XsdBoolean, default=True)
28
+
29
+
30
+ class CT_Boolean_Explicit(BaseOxmlElement):
31
+ """Always spells out the `val` attribute, e.g. `val=1`.
32
+
33
+ At least one boolean element is improperly interpreted by one or more
34
+ versions of PowerPoint. The `c:overlay` element is interpreted as |False|
35
+ when no `val` attribute is present, contrary to the behavior described in
36
+ the schema. A remedy for this is to interpret a missing `val` attribute
37
+ as |True| (consistent with the spec), but always write the attribute
38
+ whenever there is occasion for changing the element.
39
+ """
40
+
41
+ _val = OptionalAttribute("val", XsdBoolean, default=True)
42
+
43
+ @property
44
+ def val(self):
45
+ return self._val
46
+
47
+ @val.setter
48
+ def val(self, value):
49
+ val_str = "1" if bool(value) is True else "0"
50
+ self.set("val", val_str)
51
+
52
+
53
+ class CT_Double(BaseOxmlElement):
54
+ """
55
+ Used for floating point values.
56
+ """
57
+
58
+ val = RequiredAttribute("val", XsdDouble)
59
+
60
+
61
+ class CT_Layout(BaseOxmlElement):
62
+ """
63
+ ``<c:layout>`` custom element class
64
+ """
65
+
66
+ manualLayout = ZeroOrOne("c:manualLayout", successors=("c:extLst",))
67
+
68
+ @property
69
+ def horz_offset(self):
70
+ """
71
+ The float value in ./c:manualLayout/c:x when
72
+ c:layout/c:manualLayout/c:xMode@val == "factor". 0.0 if that XPath
73
+ expression finds no match.
74
+ """
75
+ manualLayout = self.manualLayout
76
+ if manualLayout is None:
77
+ return 0.0
78
+ return manualLayout.horz_offset
79
+
80
+ @horz_offset.setter
81
+ def horz_offset(self, offset):
82
+ """
83
+ Set the value of ./c:manualLayout/c:x@val to *offset* and
84
+ ./c:manualLayout/c:xMode@val to "factor". Remove ./c:manualLayout if
85
+ *offset* == 0.
86
+ """
87
+ if offset == 0.0:
88
+ self._remove_manualLayout()
89
+ return
90
+ manualLayout = self.get_or_add_manualLayout()
91
+ manualLayout.horz_offset = offset
92
+
93
+
94
+ class CT_LayoutMode(BaseOxmlElement):
95
+ """
96
+ Used for ``<c:xMode>``, ``<c:yMode>``, ``<c:wMode>``, and ``<c:hMode>``
97
+ child elements of CT_ManualLayout.
98
+ """
99
+
100
+ val = OptionalAttribute("val", ST_LayoutMode, default=ST_LayoutMode.FACTOR)
101
+
102
+
103
+ class CT_ManualLayout(BaseOxmlElement):
104
+ """
105
+ ``<c:manualLayout>`` custom element class
106
+ """
107
+
108
+ _tag_seq = (
109
+ "c:layoutTarget",
110
+ "c:xMode",
111
+ "c:yMode",
112
+ "c:wMode",
113
+ "c:hMode",
114
+ "c:x",
115
+ "c:y",
116
+ "c:w",
117
+ "c:h",
118
+ "c:extLst",
119
+ )
120
+ xMode = ZeroOrOne("c:xMode", successors=_tag_seq[2:])
121
+ x = ZeroOrOne("c:x", successors=_tag_seq[6:])
122
+ del _tag_seq
123
+
124
+ @property
125
+ def horz_offset(self):
126
+ """
127
+ The float value in ./c:x@val when ./c:xMode@val == "factor". 0.0 when
128
+ ./c:x is not present or ./c:xMode@val != "factor".
129
+ """
130
+ x, xMode = self.x, self.xMode
131
+ if x is None or xMode is None or xMode.val != ST_LayoutMode.FACTOR:
132
+ return 0.0
133
+ return x.val
134
+
135
+ @horz_offset.setter
136
+ def horz_offset(self, offset):
137
+ """
138
+ Set the value of ./c:x@val to *offset* and ./c:xMode@val to "factor".
139
+ """
140
+ self.get_or_add_xMode().val = ST_LayoutMode.FACTOR
141
+ self.get_or_add_x().val = offset
142
+
143
+
144
+ class CT_NumFmt(BaseOxmlElement):
145
+ """
146
+ ``<c:numFmt>`` element specifying the formatting for number labels on a
147
+ tick mark or data point.
148
+ """
149
+
150
+ formatCode = RequiredAttribute("formatCode", XsdString)
151
+ sourceLinked = OptionalAttribute("sourceLinked", XsdBoolean)
152
+
153
+
154
+ class CT_Title(BaseOxmlElement):
155
+ """`c:title` custom element class."""
156
+
157
+ _tag_seq = ("c:tx", "c:layout", "c:overlay", "c:spPr", "c:txPr", "c:extLst")
158
+ tx = ZeroOrOne("c:tx", successors=_tag_seq[1:])
159
+ spPr = ZeroOrOne("c:spPr", successors=_tag_seq[4:])
160
+ del _tag_seq
161
+
162
+ def get_or_add_tx_rich(self):
163
+ """Return `c:tx/c:rich`, newly created if not present.
164
+
165
+ Return the `c:rich` grandchild at `c:tx/c:rich`. Both the `c:tx` and
166
+ `c:rich` elements are created if not already present. Any
167
+ `c:tx/c:strRef` element is removed. (Such an element would contain
168
+ a cell reference for the axis title text in the chart's Excel
169
+ worksheet.)
170
+ """
171
+ tx = self.get_or_add_tx()
172
+ tx._remove_strRef()
173
+ return tx.get_or_add_rich()
174
+
175
+ @property
176
+ def tx_rich(self):
177
+ """Return `c:tx/c:rich` or |None| if not present."""
178
+ richs = self.xpath("c:tx/c:rich")
179
+ if not richs:
180
+ return None
181
+ return richs[0]
182
+
183
+ @staticmethod
184
+ def new_title():
185
+ """Return "loose" `c:title` element containing default children."""
186
+ return parse_xml(
187
+ "<c:title %s>" " <c:layout/>" ' <c:overlay val="0"/>' "</c:title>" % nsdecls("c")
188
+ )
189
+
190
+
191
+ class CT_Tx(BaseOxmlElement):
192
+ """
193
+ ``<c:tx>`` element containing the text for a label on a data point or
194
+ other chart item.
195
+ """
196
+
197
+ strRef = ZeroOrOne("c:strRef")
198
+ rich = ZeroOrOne("c:rich")
199
+
200
+ def _new_rich(self):
201
+ return parse_xml(
202
+ "<c:rich %s>"
203
+ " <a:bodyPr/>"
204
+ " <a:lstStyle/>"
205
+ " <a:p>"
206
+ " <a:pPr>"
207
+ " <a:defRPr/>"
208
+ " </a:pPr>"
209
+ " </a:p>"
210
+ "</c:rich>" % nsdecls("c", "a")
211
+ )
212
+
213
+
214
+ class CT_UnsignedInt(BaseOxmlElement):
215
+ """
216
+ ``<c:idx>`` element and others.
217
+ """
218
+
219
+ val = RequiredAttribute("val", XsdUnsignedInt)
pptx/oxml/coreprops.py ADDED
@@ -0,0 +1,288 @@
1
+ """lxml custom element classes for core properties-related XML elements."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime as dt
6
+ import re
7
+ from typing import Callable, cast
8
+
9
+ from lxml.etree import _Element # pyright: ignore[reportPrivateUsage]
10
+
11
+ from pptx.oxml import parse_xml
12
+ from pptx.oxml.ns import nsdecls, qn
13
+ from pptx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne
14
+
15
+
16
+ class CT_CoreProperties(BaseOxmlElement):
17
+ """`cp:coreProperties` element.
18
+
19
+ The root element of the Core Properties part stored as `/docProps/core.xml`. Implements many
20
+ of the Dublin Core document metadata elements. String elements resolve to an empty string ('')
21
+ if the element is not present in the XML. String elements are limited in length to 255 unicode
22
+ characters.
23
+ """
24
+
25
+ get_or_add_revision: Callable[[], _Element]
26
+
27
+ category = ZeroOrOne("cp:category", successors=())
28
+ contentStatus = ZeroOrOne("cp:contentStatus", successors=())
29
+ created = ZeroOrOne("dcterms:created", successors=())
30
+ creator = ZeroOrOne("dc:creator", successors=())
31
+ description = ZeroOrOne("dc:description", successors=())
32
+ identifier = ZeroOrOne("dc:identifier", successors=())
33
+ keywords = ZeroOrOne("cp:keywords", successors=())
34
+ language = ZeroOrOne("dc:language", successors=())
35
+ lastModifiedBy = ZeroOrOne("cp:lastModifiedBy", successors=())
36
+ lastPrinted = ZeroOrOne("cp:lastPrinted", successors=())
37
+ modified = ZeroOrOne("dcterms:modified", successors=())
38
+ revision: _Element | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
39
+ "cp:revision", successors=()
40
+ )
41
+ subject = ZeroOrOne("dc:subject", successors=())
42
+ title = ZeroOrOne("dc:title", successors=())
43
+ version = ZeroOrOne("cp:version", successors=())
44
+
45
+ _coreProperties_tmpl = "<cp:coreProperties %s/>\n" % nsdecls("cp", "dc", "dcterms")
46
+
47
+ @staticmethod
48
+ def new_coreProperties() -> CT_CoreProperties:
49
+ """Return a new `cp:coreProperties` element"""
50
+ return cast(CT_CoreProperties, parse_xml(CT_CoreProperties._coreProperties_tmpl))
51
+
52
+ @property
53
+ def author_text(self) -> str:
54
+ return self._text_of_element("creator")
55
+
56
+ @author_text.setter
57
+ def author_text(self, value: str):
58
+ self._set_element_text("creator", value)
59
+
60
+ @property
61
+ def category_text(self) -> str:
62
+ return self._text_of_element("category")
63
+
64
+ @category_text.setter
65
+ def category_text(self, value: str):
66
+ self._set_element_text("category", value)
67
+
68
+ @property
69
+ def comments_text(self) -> str:
70
+ return self._text_of_element("description")
71
+
72
+ @comments_text.setter
73
+ def comments_text(self, value: str):
74
+ self._set_element_text("description", value)
75
+
76
+ @property
77
+ def contentStatus_text(self) -> str:
78
+ return self._text_of_element("contentStatus")
79
+
80
+ @contentStatus_text.setter
81
+ def contentStatus_text(self, value: str):
82
+ self._set_element_text("contentStatus", value)
83
+
84
+ @property
85
+ def created_datetime(self):
86
+ return self._datetime_of_element("created")
87
+
88
+ @created_datetime.setter
89
+ def created_datetime(self, value: dt.datetime):
90
+ self._set_element_datetime("created", value)
91
+
92
+ @property
93
+ def identifier_text(self) -> str:
94
+ return self._text_of_element("identifier")
95
+
96
+ @identifier_text.setter
97
+ def identifier_text(self, value: str):
98
+ self._set_element_text("identifier", value)
99
+
100
+ @property
101
+ def keywords_text(self) -> str:
102
+ return self._text_of_element("keywords")
103
+
104
+ @keywords_text.setter
105
+ def keywords_text(self, value: str):
106
+ self._set_element_text("keywords", value)
107
+
108
+ @property
109
+ def language_text(self) -> str:
110
+ return self._text_of_element("language")
111
+
112
+ @language_text.setter
113
+ def language_text(self, value: str):
114
+ self._set_element_text("language", value)
115
+
116
+ @property
117
+ def lastModifiedBy_text(self) -> str:
118
+ return self._text_of_element("lastModifiedBy")
119
+
120
+ @lastModifiedBy_text.setter
121
+ def lastModifiedBy_text(self, value: str):
122
+ self._set_element_text("lastModifiedBy", value)
123
+
124
+ @property
125
+ def lastPrinted_datetime(self):
126
+ return self._datetime_of_element("lastPrinted")
127
+
128
+ @lastPrinted_datetime.setter
129
+ def lastPrinted_datetime(self, value: dt.datetime):
130
+ self._set_element_datetime("lastPrinted", value)
131
+
132
+ @property
133
+ def modified_datetime(self):
134
+ return self._datetime_of_element("modified")
135
+
136
+ @modified_datetime.setter
137
+ def modified_datetime(self, value: dt.datetime):
138
+ self._set_element_datetime("modified", value)
139
+
140
+ @property
141
+ def revision_number(self) -> int:
142
+ """Integer value of revision property."""
143
+ revision = self.revision
144
+ if revision is None:
145
+ return 0
146
+ revision_str = revision.text
147
+ if revision_str is None:
148
+ return 0
149
+ try:
150
+ revision = int(revision_str)
151
+ except ValueError:
152
+ # -- non-integer revision strings also resolve to 0 --
153
+ return 0
154
+ # -- as do negative integers --
155
+ if revision < 0:
156
+ return 0
157
+ return revision
158
+
159
+ @revision_number.setter
160
+ def revision_number(self, value: int):
161
+ """Set revision property to string value of integer `value`."""
162
+ if not isinstance(value, int) or value < 1: # pyright: ignore[reportUnnecessaryIsInstance]
163
+ tmpl = "revision property requires positive int, got '%s'"
164
+ raise ValueError(tmpl % value)
165
+ revision = self.get_or_add_revision()
166
+ revision.text = str(value)
167
+
168
+ @property
169
+ def subject_text(self) -> str:
170
+ return self._text_of_element("subject")
171
+
172
+ @subject_text.setter
173
+ def subject_text(self, value: str):
174
+ self._set_element_text("subject", value)
175
+
176
+ @property
177
+ def title_text(self) -> str:
178
+ return self._text_of_element("title")
179
+
180
+ @title_text.setter
181
+ def title_text(self, value: str):
182
+ self._set_element_text("title", value)
183
+
184
+ @property
185
+ def version_text(self) -> str:
186
+ return self._text_of_element("version")
187
+
188
+ @version_text.setter
189
+ def version_text(self, value: str):
190
+ self._set_element_text("version", value)
191
+
192
+ def _datetime_of_element(self, property_name: str) -> dt.datetime | None:
193
+ element = cast("_Element | None", getattr(self, property_name))
194
+ if element is None:
195
+ return None
196
+ datetime_str = element.text
197
+ if datetime_str is None:
198
+ return None
199
+ try:
200
+ return self._parse_W3CDTF_to_datetime(datetime_str)
201
+ except ValueError:
202
+ # invalid datetime strings are ignored
203
+ return None
204
+
205
+ def _get_or_add(self, prop_name: str):
206
+ """Return element returned by 'get_or_add_' method for `prop_name`."""
207
+ get_or_add_method_name = "get_or_add_%s" % prop_name
208
+ get_or_add_method = getattr(self, get_or_add_method_name)
209
+ element = get_or_add_method()
210
+ return element
211
+
212
+ @classmethod
213
+ def _offset_dt(cls, datetime: dt.datetime, offset_str: str):
214
+ """Return |datetime| instance offset from `datetime` by offset specified in `offset_str`.
215
+
216
+ `offset_str` is a string like `'-07:00'`.
217
+ """
218
+ match = cls._offset_pattern.match(offset_str)
219
+ if match is None:
220
+ raise ValueError(f"{repr(offset_str)} is not a valid offset string")
221
+ sign, hours_str, minutes_str = match.groups()
222
+ sign_factor = -1 if sign == "+" else 1
223
+ hours = int(hours_str) * sign_factor
224
+ minutes = int(minutes_str) * sign_factor
225
+ td = dt.timedelta(hours=hours, minutes=minutes)
226
+ return datetime + td
227
+
228
+ _offset_pattern = re.compile(r"([+-])(\d\d):(\d\d)")
229
+
230
+ @classmethod
231
+ def _parse_W3CDTF_to_datetime(cls, w3cdtf_str: str) -> dt.datetime:
232
+ # valid W3CDTF date cases:
233
+ # yyyy e.g. '2003'
234
+ # yyyy-mm e.g. '2003-12'
235
+ # yyyy-mm-dd e.g. '2003-12-31'
236
+ # UTC timezone e.g. '2003-12-31T10:14:55Z'
237
+ # numeric timezone e.g. '2003-12-31T10:14:55-08:00'
238
+ templates = ("%Y-%m-%dT%H:%M:%S", "%Y-%m-%d", "%Y-%m", "%Y")
239
+ # strptime isn't smart enough to parse literal timezone offsets like
240
+ # '-07:30', so we have to do it ourselves
241
+ parseable_part = w3cdtf_str[:19]
242
+ offset_str = w3cdtf_str[19:]
243
+ timestamp = None
244
+ for tmpl in templates:
245
+ try:
246
+ timestamp = dt.datetime.strptime(parseable_part, tmpl)
247
+ except ValueError:
248
+ continue
249
+ if timestamp is None:
250
+ tmpl = "could not parse W3CDTF datetime string '%s'"
251
+ raise ValueError(tmpl % w3cdtf_str)
252
+ if len(offset_str) == 6:
253
+ return cls._offset_dt(timestamp, offset_str)
254
+ return timestamp
255
+
256
+ def _set_element_datetime(self, prop_name: str, value: dt.datetime) -> None:
257
+ """Set date/time value of child element having `prop_name` to `value`."""
258
+ if not isinstance(value, dt.datetime): # pyright: ignore[reportUnnecessaryIsInstance]
259
+ tmpl = "property requires <type 'datetime.datetime'> object, got %s"
260
+ raise ValueError(tmpl % type(value))
261
+ element = self._get_or_add(prop_name)
262
+ dt_str = value.strftime("%Y-%m-%dT%H:%M:%SZ")
263
+ element.text = dt_str
264
+ if prop_name in ("created", "modified"):
265
+ # These two require an explicit 'xsi:type="dcterms:W3CDTF"'
266
+ # attribute. The first and last line are a hack required to add
267
+ # the xsi namespace to the root element rather than each child
268
+ # element in which it is referenced
269
+ self.set(qn("xsi:foo"), "bar")
270
+ element.set(qn("xsi:type"), "dcterms:W3CDTF")
271
+ del self.attrib[qn("xsi:foo")]
272
+
273
+ def _set_element_text(self, prop_name: str, value: str) -> None:
274
+ """Set string value of `name` property to `value`."""
275
+ value = str(value)
276
+ if len(value) > 255:
277
+ tmpl = "exceeded 255 char limit for property, got:\n\n'%s'"
278
+ raise ValueError(tmpl % value)
279
+ element = self._get_or_add(prop_name)
280
+ element.text = value
281
+
282
+ def _text_of_element(self, property_name: str) -> str:
283
+ element = getattr(self, property_name)
284
+ if element is None:
285
+ return ""
286
+ if element.text is None:
287
+ return ""
288
+ return element.text
File without changes
pptx/oxml/dml/color.py ADDED
@@ -0,0 +1,111 @@
1
+ """lxml custom element classes for DrawingML-related XML elements."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pptx.enum.dml import MSO_THEME_COLOR
6
+ from pptx.oxml.simpletypes import ST_HexColorRGB, ST_Percentage
7
+ from pptx.oxml.xmlchemy import (
8
+ BaseOxmlElement,
9
+ Choice,
10
+ RequiredAttribute,
11
+ ZeroOrOne,
12
+ ZeroOrOneChoice,
13
+ )
14
+
15
+
16
+ class _BaseColorElement(BaseOxmlElement):
17
+ """
18
+ Base class for <a:srgbClr> and <a:schemeClr> elements.
19
+ """
20
+
21
+ lumMod = ZeroOrOne("a:lumMod")
22
+ lumOff = ZeroOrOne("a:lumOff")
23
+
24
+ def add_lumMod(self, value):
25
+ """
26
+ Return a newly added <a:lumMod> child element.
27
+ """
28
+ lumMod = self._add_lumMod()
29
+ lumMod.val = value
30
+ return lumMod
31
+
32
+ def add_lumOff(self, value):
33
+ """
34
+ Return a newly added <a:lumOff> child element.
35
+ """
36
+ lumOff = self._add_lumOff()
37
+ lumOff.val = value
38
+ return lumOff
39
+
40
+ def clear_lum(self):
41
+ """
42
+ Return self after removing any <a:lumMod> and <a:lumOff> child
43
+ elements.
44
+ """
45
+ self._remove_lumMod()
46
+ self._remove_lumOff()
47
+ return self
48
+
49
+
50
+ class CT_Color(BaseOxmlElement):
51
+ """Custom element class for `a:fgClr`, `a:bgClr` and perhaps others."""
52
+
53
+ eg_colorChoice = ZeroOrOneChoice(
54
+ (
55
+ Choice("a:scrgbClr"),
56
+ Choice("a:srgbClr"),
57
+ Choice("a:hslClr"),
58
+ Choice("a:sysClr"),
59
+ Choice("a:schemeClr"),
60
+ Choice("a:prstClr"),
61
+ ),
62
+ successors=(),
63
+ )
64
+
65
+
66
+ class CT_HslColor(_BaseColorElement):
67
+ """
68
+ Custom element class for <a:hslClr> element.
69
+ """
70
+
71
+
72
+ class CT_Percentage(BaseOxmlElement):
73
+ """
74
+ Custom element class for <a:lumMod> and <a:lumOff> elements.
75
+ """
76
+
77
+ val = RequiredAttribute("val", ST_Percentage)
78
+
79
+
80
+ class CT_PresetColor(_BaseColorElement):
81
+ """
82
+ Custom element class for <a:prstClr> element.
83
+ """
84
+
85
+
86
+ class CT_SchemeColor(_BaseColorElement):
87
+ """
88
+ Custom element class for <a:schemeClr> element.
89
+ """
90
+
91
+ val = RequiredAttribute("val", MSO_THEME_COLOR)
92
+
93
+
94
+ class CT_ScRgbColor(_BaseColorElement):
95
+ """
96
+ Custom element class for <a:scrgbClr> element.
97
+ """
98
+
99
+
100
+ class CT_SRgbColor(_BaseColorElement):
101
+ """
102
+ Custom element class for <a:srgbClr> element.
103
+ """
104
+
105
+ val = RequiredAttribute("val", ST_HexColorRGB)
106
+
107
+
108
+ class CT_SystemColor(_BaseColorElement):
109
+ """
110
+ Custom element class for <a:sysClr> element.
111
+ """