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/parts/slide.py ADDED
@@ -0,0 +1,371 @@
1
+ """Slide and related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import IO, TYPE_CHECKING, cast
6
+
7
+ from pptx2.enum.shapes import PROG_ID
8
+ from pptx2.opc.constants import CONTENT_TYPE as CT
9
+ from pptx2.opc.constants import RELATIONSHIP_TYPE as RT
10
+ from pptx2.opc.package import XmlPart
11
+ from pptx2.opc.oxml import serialize_part_xml
12
+ from pptx2.opc.packuri import PackURI
13
+ from pptx2.oxml import parse_xml
14
+ from pptx2.oxml.slide import (
15
+ CT_NotesMaster,
16
+ CT_NotesSlide,
17
+ CT_Slide,
18
+ slide_has_p14_transition,
19
+ unwrap_p14_transitions,
20
+ wrap_p14_transitions,
21
+ )
22
+ from pptx2.oxml.theme import CT_OfficeStyleSheet
23
+ from pptx2.parts.chart import ChartPart
24
+ from pptx2.parts.embeddedpackage import EmbeddedPackagePart
25
+ from pptx2.slide import NotesMaster, NotesSlide, Slide, SlideLayout, SlideMaster
26
+ from pptx2.util import lazyproperty
27
+
28
+ if TYPE_CHECKING:
29
+ from pptx2.chart.data import ChartData
30
+ from pptx2.enum.chart import XL_CHART_TYPE
31
+ from pptx2.media import Video
32
+ from pptx2.parts.image import Image, ImagePart
33
+
34
+
35
+ class BaseSlidePart(XmlPart):
36
+ """Base class for slide parts.
37
+
38
+ This includes slide, slide-layout, and slide-master parts, but also notes-slide,
39
+ notes-master, and handout-master parts.
40
+ """
41
+
42
+ _element: CT_Slide
43
+
44
+ @classmethod
45
+ def load(cls, partname, content_type, package, blob):
46
+ """Parse *blob* and normalise any PowerPoint-2010 transition wrapper.
47
+
48
+ A morph/p14 transition is stored on disk inside ``<mc:AlternateContent>``;
49
+ we unwrap it to a plain ``<p:transition>`` so the high-level
50
+ ``slide.transition`` accessors see a normal element. ``blob`` re-wraps it
51
+ on save.
52
+ """
53
+ element = cast("CT_Slide", parse_xml(blob))
54
+ unwrap_p14_transitions(element)
55
+ return cls(partname, content_type, package, element)
56
+
57
+ @property
58
+ def blob(self) -> bytes:
59
+ """XML serialization, re-wrapping p14 transitions in mc:AlternateContent.
60
+
61
+ The wrap is done on a copy so the live element tree keeps its plain
62
+ ``<p:transition>`` (and the accessors that read it) intact after a save.
63
+ """
64
+ element = self._element
65
+ if not slide_has_p14_transition(element):
66
+ return serialize_part_xml(element)
67
+ from copy import deepcopy
68
+
69
+ copy = deepcopy(element)
70
+ wrap_p14_transitions(copy)
71
+ return serialize_part_xml(copy)
72
+
73
+ def get_image(self, rId: str) -> Image:
74
+ """Return an |Image| object containing the image related to this slide by *rId*.
75
+
76
+ Raises |KeyError| if no image is related by that id, which would generally indicate a
77
+ corrupted .pptx file.
78
+ """
79
+ return cast("ImagePart", self.related_part(rId)).image
80
+
81
+ def get_or_add_image_part(self, image_file: str | IO[bytes]):
82
+ """Return `(image_part, rId)` pair corresponding to `image_file`.
83
+
84
+ The returned |ImagePart| object contains the image in `image_file` and is
85
+ related to this slide with the key `rId`. If either the image part or
86
+ relationship already exists, they are reused, otherwise they are newly created.
87
+ """
88
+ image_part = self._package.get_or_add_image_part(image_file)
89
+ rId = self.relate_to(image_part, RT.IMAGE)
90
+ return image_part, rId
91
+
92
+ @property
93
+ def name(self) -> str:
94
+ """Internal name of this slide."""
95
+ return self._element.cSld.name
96
+
97
+
98
+ class NotesMasterPart(BaseSlidePart):
99
+ """Notes master part.
100
+
101
+ Corresponds to package file `ppt/notesMasters/notesMaster1.xml`.
102
+ """
103
+
104
+ @classmethod
105
+ def create_default(cls, package):
106
+ """
107
+ Create and return a default notes master part, including creating the
108
+ new theme it requires.
109
+ """
110
+ notes_master_part = cls._new(package)
111
+ theme_part = cls._new_theme_part(package)
112
+ notes_master_part.relate_to(theme_part, RT.THEME)
113
+ return notes_master_part
114
+
115
+ @lazyproperty
116
+ def notes_master(self):
117
+ """
118
+ Return the |NotesMaster| object that proxies this notes master part.
119
+ """
120
+ return NotesMaster(self._element, self)
121
+
122
+ @classmethod
123
+ def _new(cls, package):
124
+ """
125
+ Create and return a standalone, default notes master part based on
126
+ the built-in template (without any related parts, such as theme).
127
+ """
128
+ return NotesMasterPart(
129
+ PackURI("/ppt/notesMasters/notesMaster1.xml"),
130
+ CT.PML_NOTES_MASTER,
131
+ package,
132
+ CT_NotesMaster.new_default(),
133
+ )
134
+
135
+ @classmethod
136
+ def _new_theme_part(cls, package):
137
+ """Return new default theme-part suitable for use with a notes master."""
138
+ return ThemePart(
139
+ package.next_partname("/ppt/theme/theme%d.xml"),
140
+ CT.OFC_THEME,
141
+ package,
142
+ CT_OfficeStyleSheet.new_default(),
143
+ )
144
+
145
+
146
+ class ThemePart(XmlPart):
147
+ """Package part for an Office theme (``ppt/theme/themeN.xml``).
148
+
149
+ Themes were previously loaded as generic |Part| blobs which made
150
+ high-level write operations on |Theme| transient (every read parsed
151
+ a fresh element). Promoting them to |XmlPart| keeps a single live
152
+ ``<a:theme>`` element on the part so that mutations from
153
+ :class:`pptx2.theme.Theme` round-trip on save.
154
+ """
155
+
156
+ _element: CT_OfficeStyleSheet
157
+
158
+
159
+ class NotesSlidePart(BaseSlidePart):
160
+ """Notes slide part.
161
+
162
+ Contains the slide notes content and the layout for the slide handout page.
163
+ Corresponds to package file `ppt/notesSlides/notesSlide[1-9][0-9]*.xml`.
164
+ """
165
+
166
+ @classmethod
167
+ def new(cls, package, slide_part):
168
+ """Return new |NotesSlidePart| for the slide in `slide_part`.
169
+
170
+ The new notes-slide part is based on the (singleton) notes master and related to
171
+ both the notes-master part and `slide_part`. If no notes-master is present,
172
+ one is created based on the default template.
173
+ """
174
+ notes_master_part = package.presentation_part.notes_master_part
175
+ notes_slide_part = cls._add_notes_slide_part(package, slide_part, notes_master_part)
176
+ notes_slide = notes_slide_part.notes_slide
177
+ notes_slide.clone_master_placeholders(notes_master_part.notes_master)
178
+ return notes_slide_part
179
+
180
+ @lazyproperty
181
+ def notes_master(self):
182
+ """Return the |NotesMaster| object this notes slide inherits from."""
183
+ notes_master_part = self.part_related_by(RT.NOTES_MASTER)
184
+ return notes_master_part.notes_master
185
+
186
+ @lazyproperty
187
+ def notes_slide(self):
188
+ """Return the |NotesSlide| object that proxies this notes slide part."""
189
+ return NotesSlide(self._element, self)
190
+
191
+ @classmethod
192
+ def _add_notes_slide_part(cls, package, slide_part, notes_master_part):
193
+ """Create and return a new notes-slide part.
194
+
195
+ The return part is fully related, but has no shape content (i.e. placeholders
196
+ not cloned).
197
+ """
198
+ notes_slide_part = NotesSlidePart(
199
+ package.next_partname("/ppt/notesSlides/notesSlide%d.xml"),
200
+ CT.PML_NOTES_SLIDE,
201
+ package,
202
+ CT_NotesSlide.new(),
203
+ )
204
+ notes_slide_part.relate_to(notes_master_part, RT.NOTES_MASTER)
205
+ notes_slide_part.relate_to(slide_part, RT.SLIDE)
206
+ return notes_slide_part
207
+
208
+
209
+ class SlidePart(BaseSlidePart):
210
+ """Slide part. Corresponds to package files ppt/slides/slide[1-9][0-9]*.xml."""
211
+
212
+ @classmethod
213
+ def new(cls, partname, package, slide_layout_part):
214
+ """Return newly-created blank slide part.
215
+
216
+ The new slide-part has `partname` and a relationship to `slide_layout_part`.
217
+ """
218
+ slide_part = cls(partname, CT.PML_SLIDE, package, CT_Slide.new())
219
+ slide_part.relate_to(slide_layout_part, RT.SLIDE_LAYOUT)
220
+ return slide_part
221
+
222
+ def add_chart_part(self, chart_type: XL_CHART_TYPE, chart_data: ChartData):
223
+ """Return str rId of new |ChartPart| object containing chart of `chart_type`.
224
+
225
+ The chart depicts `chart_data` and is related to the slide contained in this
226
+ part by `rId`.
227
+ """
228
+ return self.relate_to(ChartPart.new(chart_type, chart_data, self._package), RT.CHART)
229
+
230
+ def add_embedded_ole_object_part(
231
+ self, prog_id: PROG_ID | str, ole_object_file: str | IO[bytes]
232
+ ):
233
+ """Return rId of newly-added OLE-object part formed from `ole_object_file`."""
234
+ relationship_type = RT.PACKAGE if isinstance(prog_id, PROG_ID) else RT.OLE_OBJECT
235
+ return self.relate_to(
236
+ EmbeddedPackagePart.factory(
237
+ prog_id, self._blob_from_file(ole_object_file), self._package
238
+ ),
239
+ relationship_type,
240
+ )
241
+
242
+ def get_or_add_video_media_part(self, video: Video) -> tuple[str, str]:
243
+ """Return rIds for media and video relationships to media part.
244
+
245
+ A new |MediaPart| object is created if it does not already exist
246
+ (such as would occur if the same video appeared more than once in
247
+ a presentation). Two relationships to the media part are created,
248
+ one each with MEDIA and VIDEO relationship types. The need for two
249
+ appears to be for legacy support for an earlier (pre-Office 2010)
250
+ PowerPoint media embedding strategy.
251
+ """
252
+ media_part = self._package.get_or_add_media_part(video)
253
+ media_rId = self.relate_to(media_part, RT.MEDIA)
254
+ video_rId = self.relate_to(media_part, RT.VIDEO)
255
+ return media_rId, video_rId
256
+
257
+ @property
258
+ def has_notes_slide(self):
259
+ """
260
+ Return True if this slide has a notes slide, False otherwise. A notes
261
+ slide is created by the :attr:`notes_slide` property when one doesn't
262
+ exist; use this property to test for a notes slide without the
263
+ possible side-effect of creating one.
264
+ """
265
+ try:
266
+ self.part_related_by(RT.NOTES_SLIDE)
267
+ except KeyError:
268
+ return False
269
+ return True
270
+
271
+ @lazyproperty
272
+ def notes_slide(self) -> NotesSlide:
273
+ """The |NotesSlide| instance associated with this slide.
274
+
275
+ If the slide does not have a notes slide, a new one is created. The same single instance
276
+ is returned on each call.
277
+ """
278
+ try:
279
+ notes_slide_part = self.part_related_by(RT.NOTES_SLIDE)
280
+ except KeyError:
281
+ notes_slide_part = self._add_notes_slide_part()
282
+ return notes_slide_part.notes_slide
283
+
284
+ @lazyproperty
285
+ def slide(self):
286
+ """
287
+ The |Slide| object representing this slide part.
288
+ """
289
+ return Slide(self._element, self)
290
+
291
+ @property
292
+ def slide_id(self) -> int:
293
+ """Return the slide identifier stored in the presentation part for this slide part."""
294
+ presentation_part = self.package.presentation_part
295
+ return presentation_part.slide_id(self)
296
+
297
+ @property
298
+ def slide_layout(self) -> SlideLayout:
299
+ """|SlideLayout| object the slide in this part inherits appearance from."""
300
+ slide_layout_part = self.part_related_by(RT.SLIDE_LAYOUT)
301
+ return slide_layout_part.slide_layout
302
+
303
+ def _add_notes_slide_part(self):
304
+ """
305
+ Return a newly created |NotesSlidePart| object related to this slide
306
+ part. Caller is responsible for ensuring this slide doesn't already
307
+ have a notes slide part.
308
+ """
309
+ notes_slide_part = NotesSlidePart.new(self.package, self)
310
+ self.relate_to(notes_slide_part, RT.NOTES_SLIDE)
311
+ return notes_slide_part
312
+
313
+
314
+ class SlideLayoutPart(BaseSlidePart):
315
+ """Slide layout part.
316
+
317
+ Corresponds to package files ``ppt/slideLayouts/slideLayout[1-9][0-9]*.xml``.
318
+ """
319
+
320
+ @lazyproperty
321
+ def slide_layout(self):
322
+ """
323
+ The |SlideLayout| object representing this part.
324
+ """
325
+ return SlideLayout(self._element, self)
326
+
327
+ @property
328
+ def slide_master(self) -> SlideMaster:
329
+ """Slide master from which this slide layout inherits properties."""
330
+ return self.part_related_by(RT.SLIDE_MASTER).slide_master
331
+
332
+
333
+ class SlideMasterPart(BaseSlidePart):
334
+ """Slide master part.
335
+
336
+ Corresponds to package files ppt/slideMasters/slideMaster[1-9][0-9]*.xml.
337
+ """
338
+
339
+ def related_slide_layout(self, rId: str) -> SlideLayout:
340
+ """Return |SlideLayout| related to this slide-master by key `rId`."""
341
+ return self.related_part(rId).slide_layout
342
+
343
+ @lazyproperty
344
+ def slide_master(self):
345
+ """
346
+ The |SlideMaster| object representing this part.
347
+ """
348
+ return SlideMaster(self._element, self)
349
+
350
+ @property
351
+ def theme(self):
352
+ """Return a |Theme| object for the theme related to this slide master.
353
+
354
+ Returns ``None`` when no theme relationship is present.
355
+ """
356
+ from pptx2.oxml import parse_xml
357
+ from pptx2.theme import Theme
358
+
359
+ try:
360
+ theme_part = self.part_related_by(RT.THEME)
361
+ except KeyError:
362
+ return None
363
+ # Theme parts are typed as ThemePart (an XmlPart subclass) and
364
+ # always carry a live `_element`. Older packages or generic
365
+ # Parts fall through to a fresh parse so the read still works
366
+ # (writes won't persist on a generic Part — but they do on
367
+ # any presentation loaded through the registered factory).
368
+ elm = getattr(theme_part, "_element", None)
369
+ if elm is None:
370
+ elm = parse_xml(theme_part.blob)
371
+ return Theme(elm)