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
@@ -0,0 +1,1845 @@
1
+ """Composers for default chart XML for various chart types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from copy import deepcopy
6
+ from xml.sax.saxutils import escape
7
+
8
+ from pptx2.enum.chart import XL_CHART_TYPE
9
+ from pptx2.oxml import parse_xml
10
+ from pptx2.oxml.ns import nsdecls
11
+
12
+
13
+ def ChartXmlWriter(chart_type, chart_data):
14
+ """
15
+ Factory function returning appropriate XML writer object for
16
+ *chart_type*, loaded with *chart_type* and *chart_data*.
17
+ """
18
+ XL_CT = XL_CHART_TYPE
19
+ try:
20
+ BuilderCls = {
21
+ XL_CT.AREA: _AreaChartXmlWriter,
22
+ XL_CT.AREA_STACKED: _AreaChartXmlWriter,
23
+ XL_CT.AREA_STACKED_100: _AreaChartXmlWriter,
24
+ XL_CT.BAR_CLUSTERED: _BarChartXmlWriter,
25
+ XL_CT.BAR_STACKED: _BarChartXmlWriter,
26
+ XL_CT.BAR_STACKED_100: _BarChartXmlWriter,
27
+ XL_CT.BUBBLE: _BubbleChartXmlWriter,
28
+ XL_CT.BUBBLE_THREE_D_EFFECT: _BubbleChartXmlWriter,
29
+ XL_CT.COLUMN_CLUSTERED: _BarChartXmlWriter,
30
+ XL_CT.COLUMN_STACKED: _BarChartXmlWriter,
31
+ XL_CT.COLUMN_STACKED_100: _BarChartXmlWriter,
32
+ XL_CT.DOUGHNUT: _DoughnutChartXmlWriter,
33
+ XL_CT.DOUGHNUT_EXPLODED: _DoughnutChartXmlWriter,
34
+ XL_CT.LINE: _LineChartXmlWriter,
35
+ XL_CT.LINE_MARKERS: _LineChartXmlWriter,
36
+ XL_CT.LINE_MARKERS_STACKED: _LineChartXmlWriter,
37
+ XL_CT.LINE_MARKERS_STACKED_100: _LineChartXmlWriter,
38
+ XL_CT.LINE_STACKED: _LineChartXmlWriter,
39
+ XL_CT.LINE_STACKED_100: _LineChartXmlWriter,
40
+ XL_CT.PIE: _PieChartXmlWriter,
41
+ XL_CT.PIE_EXPLODED: _PieChartXmlWriter,
42
+ XL_CT.RADAR: _RadarChartXmlWriter,
43
+ XL_CT.RADAR_FILLED: _RadarChartXmlWriter,
44
+ XL_CT.RADAR_MARKERS: _RadarChartXmlWriter,
45
+ XL_CT.XY_SCATTER: _XyChartXmlWriter,
46
+ XL_CT.XY_SCATTER_LINES: _XyChartXmlWriter,
47
+ XL_CT.XY_SCATTER_LINES_NO_MARKERS: _XyChartXmlWriter,
48
+ XL_CT.XY_SCATTER_SMOOTH: _XyChartXmlWriter,
49
+ XL_CT.XY_SCATTER_SMOOTH_NO_MARKERS: _XyChartXmlWriter,
50
+ }[chart_type]
51
+ except KeyError:
52
+ raise NotImplementedError("XML writer for chart type %s not yet implemented" % chart_type)
53
+ return BuilderCls(chart_type, chart_data)
54
+
55
+
56
+ def SeriesXmlRewriterFactory(chart_type, chart_data):
57
+ """
58
+ Return a |_BaseSeriesXmlRewriter| subclass appropriate to *chart_type*.
59
+ """
60
+ XL_CT = XL_CHART_TYPE
61
+
62
+ RewriterCls = {
63
+ # There are 73 distinct chart types, only specify non-category
64
+ # types, others default to _CategorySeriesXmlRewriter. Stock-type
65
+ # charts are multi-plot charts, so no guaratees on how they turn
66
+ # out.
67
+ XL_CT.BUBBLE: _BubbleSeriesXmlRewriter,
68
+ XL_CT.BUBBLE_THREE_D_EFFECT: _BubbleSeriesXmlRewriter,
69
+ XL_CT.XY_SCATTER: _XySeriesXmlRewriter,
70
+ XL_CT.XY_SCATTER_LINES: _XySeriesXmlRewriter,
71
+ XL_CT.XY_SCATTER_LINES_NO_MARKERS: _XySeriesXmlRewriter,
72
+ XL_CT.XY_SCATTER_SMOOTH: _XySeriesXmlRewriter,
73
+ XL_CT.XY_SCATTER_SMOOTH_NO_MARKERS: _XySeriesXmlRewriter,
74
+ }.get(chart_type, _CategorySeriesXmlRewriter)
75
+
76
+ return RewriterCls(chart_data)
77
+
78
+
79
+ class _BaseChartXmlWriter(object):
80
+ """
81
+ Generates XML text (unicode) for a default chart, like the one added by
82
+ PowerPoint when you click the *Add Column Chart* button on the ribbon.
83
+ Differentiated XML for different chart types is provided by subclasses.
84
+ """
85
+
86
+ def __init__(self, chart_type, series_seq):
87
+ super(_BaseChartXmlWriter, self).__init__()
88
+ self._chart_type = chart_type
89
+ self._chart_data = series_seq
90
+ self._series_seq = list(series_seq)
91
+
92
+ @property
93
+ def xml(self):
94
+ """
95
+ The full XML stream for the chart specified by this chart builder, as
96
+ unicode text. This method must be overridden by each subclass.
97
+ """
98
+ raise NotImplementedError("must be implemented by all subclasses")
99
+
100
+
101
+ class _BaseSeriesXmlWriter(object):
102
+ """
103
+ Provides shared members for series XML writers.
104
+ """
105
+
106
+ def __init__(self, series, date_1904=False):
107
+ super(_BaseSeriesXmlWriter, self).__init__()
108
+ self._series = series
109
+ self._date_1904 = date_1904
110
+
111
+ @property
112
+ def name(self):
113
+ """
114
+ The XML-escaped name for this series.
115
+ """
116
+ return escape(self._series.name)
117
+
118
+ def numRef_xml(self, wksht_ref, number_format, values):
119
+ """
120
+ Return the ``<c:numRef>`` element specified by the parameters as
121
+ unicode text.
122
+ """
123
+ pt_xml = self.pt_xml(values)
124
+ return (
125
+ " <c:numRef>\n"
126
+ " <c:f>{wksht_ref}</c:f>\n"
127
+ " <c:numCache>\n"
128
+ " <c:formatCode>{number_format}</c:formatCode>\n"
129
+ "{pt_xml}"
130
+ " </c:numCache>\n"
131
+ " </c:numRef>\n"
132
+ ).format(**{"wksht_ref": wksht_ref, "number_format": number_format, "pt_xml": pt_xml})
133
+
134
+ def pt_xml(self, values):
135
+ """
136
+ Return the ``<c:ptCount>`` and sequence of ``<c:pt>`` elements
137
+ corresponding to *values* as a single unicode text string.
138
+ `c:ptCount` refers to the number of `c:pt` elements in this sequence.
139
+ The `idx` attribute value for `c:pt` elements locates the data point
140
+ in the overall data point sequence of the chart and is started at
141
+ *offset*.
142
+ """
143
+ xml = (' <c:ptCount val="{pt_count}"/>\n').format(pt_count=len(values))
144
+
145
+ pt_tmpl = (
146
+ ' <c:pt idx="{idx}">\n'
147
+ " <c:v>{value}</c:v>\n"
148
+ " </c:pt>\n"
149
+ )
150
+ for idx, value in enumerate(values):
151
+ if value is None:
152
+ continue
153
+ xml += pt_tmpl.format(idx=idx, value=value)
154
+
155
+ return xml
156
+
157
+ @property
158
+ def tx(self):
159
+ """
160
+ Return a ``<c:tx>`` oxml element for this series, containing the
161
+ series name.
162
+ """
163
+ xml = self._tx_tmpl.format(
164
+ **{
165
+ "wksht_ref": self._series.name_ref,
166
+ "series_name": self.name,
167
+ "nsdecls": " %s" % nsdecls("c"),
168
+ }
169
+ )
170
+ return parse_xml(xml)
171
+
172
+ @property
173
+ def tx_xml(self):
174
+ """
175
+ Return the ``<c:tx>`` (tx is short for 'text') element for this
176
+ series as unicode text. This element contains the series name.
177
+ """
178
+ return self._tx_tmpl.format(
179
+ **{
180
+ "wksht_ref": self._series.name_ref,
181
+ "series_name": self.name,
182
+ "nsdecls": "",
183
+ }
184
+ )
185
+
186
+ @property
187
+ def _tx_tmpl(self):
188
+ """
189
+ The string formatting template for the ``<c:tx>`` element for this
190
+ series, containing the series title and spreadsheet range reference.
191
+ """
192
+ return (
193
+ " <c:tx{nsdecls}>\n"
194
+ " <c:strRef>\n"
195
+ " <c:f>{wksht_ref}</c:f>\n"
196
+ " <c:strCache>\n"
197
+ ' <c:ptCount val="1"/>\n'
198
+ ' <c:pt idx="0">\n'
199
+ " <c:v>{series_name}</c:v>\n"
200
+ " </c:pt>\n"
201
+ " </c:strCache>\n"
202
+ " </c:strRef>\n"
203
+ " </c:tx>\n"
204
+ )
205
+
206
+
207
+ class _BaseSeriesXmlRewriter(object):
208
+ """
209
+ Base class for series XML rewriters.
210
+ """
211
+
212
+ def __init__(self, chart_data):
213
+ super(_BaseSeriesXmlRewriter, self).__init__()
214
+ self._chart_data = chart_data
215
+
216
+ def replace_series_data(self, chartSpace):
217
+ """
218
+ Rewrite the series data under *chartSpace* using the chart data
219
+ contents. All series-level formatting is left undisturbed. If
220
+ the chart data contains fewer series than *chartSpace*, the extra
221
+ series in *chartSpace* are deleted. If *chart_data* contains more
222
+ series than the *chartSpace* element, new series are added to the
223
+ last plot in the chart and series formatting is "cloned" from the
224
+ last series in that plot.
225
+ """
226
+ plotArea, date_1904 = chartSpace.plotArea, chartSpace.date_1904
227
+ chart_data = self._chart_data
228
+ self._adjust_ser_count(plotArea, len(chart_data))
229
+ for ser, series_data in zip(plotArea.sers, chart_data):
230
+ self._rewrite_ser_data(ser, series_data, date_1904)
231
+
232
+ def _add_cloned_sers(self, plotArea, count):
233
+ """
234
+ Add `c:ser` elements to the last xChart element in *plotArea*, cloned
235
+ from the last `c:ser` child of that last xChart.
236
+ """
237
+
238
+ def clone_ser(ser):
239
+ new_ser = deepcopy(ser)
240
+ new_ser.idx.val = plotArea.next_idx
241
+ new_ser.order.val = plotArea.next_order
242
+ ser.addnext(new_ser)
243
+ return new_ser
244
+
245
+ last_ser = plotArea.last_ser
246
+ for _ in range(count):
247
+ last_ser = clone_ser(last_ser)
248
+
249
+ def _adjust_ser_count(self, plotArea, new_ser_count):
250
+ """
251
+ Adjust the number of c:ser elements in *plotArea* to *new_ser_count*.
252
+ Excess c:ser elements are deleted from the end, along with any xChart
253
+ elements that are left empty as a result. Series elements are
254
+ considered in xChart + series order. Any new c:ser elements required
255
+ are added to the last xChart element and cloned from the last c:ser
256
+ element in that xChart.
257
+ """
258
+ ser_count_diff = new_ser_count - len(plotArea.sers)
259
+ if ser_count_diff > 0:
260
+ self._add_cloned_sers(plotArea, ser_count_diff)
261
+ elif ser_count_diff < 0:
262
+ self._trim_ser_count_by(plotArea, abs(ser_count_diff))
263
+
264
+ def _rewrite_ser_data(self, ser, series_data, date_1904):
265
+ """
266
+ Rewrite selected child elements of *ser* based on the values in
267
+ *series_data*.
268
+ """
269
+ raise NotImplementedError("must be implemented by each subclass")
270
+
271
+ def _trim_ser_count_by(self, plotArea, count):
272
+ """
273
+ Remove the last *count* ser elements from *plotArea*. Any xChart
274
+ elements having no ser child elements after trimming are also
275
+ removed.
276
+ """
277
+ extra_sers = plotArea.sers[-count:]
278
+ for ser in extra_sers:
279
+ parent = ser.getparent()
280
+ parent.remove(ser)
281
+ extra_xCharts = [xChart for xChart in plotArea.iter_xCharts() if len(xChart.sers) == 0]
282
+ for xChart in extra_xCharts:
283
+ parent = xChart.getparent()
284
+ parent.remove(xChart)
285
+
286
+
287
+ class _AreaChartXmlWriter(_BaseChartXmlWriter):
288
+ """
289
+ Provides specialized methods particular to the ``<c:areaChart>`` element.
290
+ """
291
+
292
+ @property
293
+ def xml(self):
294
+ return (
295
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
296
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
297
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
298
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
299
+ 'iceDocument/2006/relationships">\n'
300
+ ' <c:date1904 val="0"/>\n'
301
+ ' <c:roundedCorners val="0"/>\n'
302
+ " <c:chart>\n"
303
+ ' <c:autoTitleDeleted val="0"/>\n'
304
+ " <c:plotArea>\n"
305
+ " <c:layout/>\n"
306
+ " <c:areaChart>\n"
307
+ "{grouping_xml}"
308
+ ' <c:varyColors val="0"/>\n'
309
+ "{ser_xml}"
310
+ " <c:dLbls>\n"
311
+ ' <c:showLegendKey val="0"/>\n'
312
+ ' <c:showVal val="0"/>\n'
313
+ ' <c:showCatName val="0"/>\n'
314
+ ' <c:showSerName val="0"/>\n'
315
+ ' <c:showPercent val="0"/>\n'
316
+ ' <c:showBubbleSize val="0"/>\n'
317
+ " </c:dLbls>\n"
318
+ ' <c:axId val="46323720"/>\n'
319
+ ' <c:axId val="46765400"/>\n'
320
+ " </c:areaChart>\n"
321
+ "{cat_ax_xml}"
322
+ " <c:valAx>\n"
323
+ ' <c:axId val="46765400"/>\n'
324
+ " <c:scaling>\n"
325
+ ' <c:orientation val="minMax"/>\n'
326
+ " </c:scaling>\n"
327
+ ' <c:delete val="0"/>\n'
328
+ ' <c:axPos val="l"/>\n'
329
+ " <c:majorGridlines/>\n"
330
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
331
+ ' <c:majorTickMark val="out"/>\n'
332
+ ' <c:minorTickMark val="none"/>\n'
333
+ ' <c:tickLblPos val="nextTo"/>\n'
334
+ ' <c:crossAx val="46323720"/>\n'
335
+ ' <c:crosses val="autoZero"/>\n'
336
+ ' <c:crossBetween val="midCat"/>\n'
337
+ " </c:valAx>\n"
338
+ " </c:plotArea>\n"
339
+ " <c:legend>\n"
340
+ ' <c:legendPos val="r"/>\n'
341
+ " <c:layout/>\n"
342
+ ' <c:overlay val="0"/>\n'
343
+ " </c:legend>\n"
344
+ ' <c:plotVisOnly val="1"/>\n'
345
+ ' <c:dispBlanksAs val="zero"/>\n'
346
+ ' <c:showDLblsOverMax val="0"/>\n'
347
+ " </c:chart>\n"
348
+ " <c:txPr>\n"
349
+ " <a:bodyPr/>\n"
350
+ " <a:lstStyle/>\n"
351
+ " <a:p>\n"
352
+ " <a:pPr>\n"
353
+ ' <a:defRPr sz="1800"/>\n'
354
+ " </a:pPr>\n"
355
+ ' <a:endParaRPr lang="en-US"/>\n'
356
+ " </a:p>\n"
357
+ " </c:txPr>\n"
358
+ "</c:chartSpace>\n"
359
+ ).format(
360
+ **{
361
+ "grouping_xml": self._grouping_xml,
362
+ "ser_xml": self._ser_xml,
363
+ "cat_ax_xml": self._cat_ax_xml,
364
+ }
365
+ )
366
+
367
+ @property
368
+ def _cat_ax_xml(self):
369
+ categories = self._chart_data.categories
370
+
371
+ if categories.are_dates:
372
+ return (
373
+ " <c:dateAx>\n"
374
+ ' <c:axId val="46323720"/>\n'
375
+ " <c:scaling>\n"
376
+ ' <c:orientation val="minMax"/>\n'
377
+ " </c:scaling>\n"
378
+ ' <c:delete val="0"/>\n'
379
+ ' <c:axPos val="b"/>\n'
380
+ ' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
381
+ ' <c:majorTickMark val="out"/>\n'
382
+ ' <c:minorTickMark val="none"/>\n'
383
+ ' <c:tickLblPos val="nextTo"/>\n'
384
+ ' <c:crossAx val="46765400"/>\n'
385
+ ' <c:crosses val="autoZero"/>\n'
386
+ ' <c:auto val="1"/>\n'
387
+ ' <c:lblOffset val="100"/>\n'
388
+ ' <c:baseTimeUnit val="days"/>\n'
389
+ " </c:dateAx>\n"
390
+ ).format(**{"nf": categories.number_format})
391
+
392
+ return (
393
+ " <c:catAx>\n"
394
+ ' <c:axId val="46323720"/>\n'
395
+ " <c:scaling>\n"
396
+ ' <c:orientation val="minMax"/>\n'
397
+ " </c:scaling>\n"
398
+ ' <c:delete val="0"/>\n'
399
+ ' <c:axPos val="b"/>\n'
400
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
401
+ ' <c:majorTickMark val="out"/>\n'
402
+ ' <c:minorTickMark val="none"/>\n'
403
+ ' <c:tickLblPos val="nextTo"/>\n'
404
+ ' <c:crossAx val="46765400"/>\n'
405
+ ' <c:crosses val="autoZero"/>\n'
406
+ ' <c:auto val="1"/>\n'
407
+ ' <c:lblAlgn val="ctr"/>\n'
408
+ ' <c:lblOffset val="100"/>\n'
409
+ ' <c:noMultiLvlLbl val="0"/>\n'
410
+ " </c:catAx>\n"
411
+ )
412
+
413
+ @property
414
+ def _grouping_xml(self):
415
+ val = {
416
+ XL_CHART_TYPE.AREA: "standard",
417
+ XL_CHART_TYPE.AREA_STACKED: "stacked",
418
+ XL_CHART_TYPE.AREA_STACKED_100: "percentStacked",
419
+ }[self._chart_type]
420
+ return ' <c:grouping val="%s"/>\n' % val
421
+
422
+ @property
423
+ def _ser_xml(self):
424
+ xml = ""
425
+ for series in self._chart_data:
426
+ xml_writer = _CategorySeriesXmlWriter(series)
427
+ xml += (
428
+ " <c:ser>\n"
429
+ ' <c:idx val="{ser_idx}"/>\n'
430
+ ' <c:order val="{ser_order}"/>\n'
431
+ "{tx_xml}"
432
+ "{cat_xml}"
433
+ "{val_xml}"
434
+ " </c:ser>\n"
435
+ ).format(
436
+ **{
437
+ "ser_idx": series.index,
438
+ "ser_order": series.index,
439
+ "tx_xml": xml_writer.tx_xml,
440
+ "cat_xml": xml_writer.cat_xml,
441
+ "val_xml": xml_writer.val_xml,
442
+ }
443
+ )
444
+ return xml
445
+
446
+
447
+ class _BarChartXmlWriter(_BaseChartXmlWriter):
448
+ """
449
+ Provides specialized methods particular to the ``<c:barChart>`` element.
450
+ """
451
+
452
+ @property
453
+ def xml(self):
454
+ return (
455
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
456
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
457
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
458
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
459
+ 'iceDocument/2006/relationships">\n'
460
+ ' <c:date1904 val="0"/>\n'
461
+ " <c:chart>\n"
462
+ ' <c:autoTitleDeleted val="0"/>\n'
463
+ " <c:plotArea>\n"
464
+ " <c:barChart>\n"
465
+ "{barDir_xml}"
466
+ "{grouping_xml}"
467
+ "{ser_xml}"
468
+ "{overlap_xml}"
469
+ ' <c:axId val="79456312"/>\n'
470
+ ' <c:axId val="33489208"/>\n'
471
+ " </c:barChart>\n"
472
+ "{cat_ax_xml}"
473
+ " <c:valAx>\n"
474
+ ' <c:axId val="33489208"/>\n'
475
+ " <c:scaling/>\n"
476
+ ' <c:delete val="0"/>\n'
477
+ ' <c:axPos val="{val_ax_pos}"/>\n'
478
+ " <c:majorGridlines/>\n"
479
+ ' <c:majorTickMark val="out"/>\n'
480
+ ' <c:minorTickMark val="none"/>\n'
481
+ ' <c:tickLblPos val="nextTo"/>\n'
482
+ ' <c:crossAx val="79456312"/>\n'
483
+ ' <c:crosses val="autoZero"/>\n'
484
+ " </c:valAx>\n"
485
+ " </c:plotArea>\n"
486
+ ' <c:dispBlanksAs val="gap"/>\n'
487
+ " </c:chart>\n"
488
+ " <c:txPr>\n"
489
+ " <a:bodyPr/>\n"
490
+ " <a:lstStyle/>\n"
491
+ " <a:p>\n"
492
+ " <a:pPr>\n"
493
+ ' <a:defRPr sz="1800"/>\n'
494
+ " </a:pPr>\n"
495
+ ' <a:endParaRPr lang="en-US"/>\n'
496
+ " </a:p>\n"
497
+ " </c:txPr>\n"
498
+ "</c:chartSpace>\n"
499
+ ).format(
500
+ **{
501
+ "barDir_xml": self._barDir_xml,
502
+ "grouping_xml": self._grouping_xml,
503
+ "ser_xml": self._ser_xml,
504
+ "overlap_xml": self._overlap_xml,
505
+ "cat_ax_xml": self._cat_ax_xml,
506
+ "val_ax_pos": self._val_ax_pos,
507
+ }
508
+ )
509
+
510
+ @property
511
+ def _barDir_xml(self):
512
+ XL = XL_CHART_TYPE
513
+ bar_types = (XL.BAR_CLUSTERED, XL.BAR_STACKED, XL.BAR_STACKED_100)
514
+ col_types = (XL.COLUMN_CLUSTERED, XL.COLUMN_STACKED, XL.COLUMN_STACKED_100)
515
+ if self._chart_type in bar_types:
516
+ return ' <c:barDir val="bar"/>\n'
517
+ elif self._chart_type in col_types:
518
+ return ' <c:barDir val="col"/>\n'
519
+ raise NotImplementedError("no _barDir_xml() for chart type %s" % self._chart_type)
520
+
521
+ @property
522
+ def _cat_ax_pos(self):
523
+ return {
524
+ XL_CHART_TYPE.BAR_CLUSTERED: "l",
525
+ XL_CHART_TYPE.BAR_STACKED: "l",
526
+ XL_CHART_TYPE.BAR_STACKED_100: "l",
527
+ XL_CHART_TYPE.COLUMN_CLUSTERED: "b",
528
+ XL_CHART_TYPE.COLUMN_STACKED: "b",
529
+ XL_CHART_TYPE.COLUMN_STACKED_100: "b",
530
+ }[self._chart_type]
531
+
532
+ @property
533
+ def _cat_ax_xml(self):
534
+ categories = self._chart_data.categories
535
+
536
+ if categories.are_dates:
537
+ return (
538
+ " <c:dateAx>\n"
539
+ ' <c:axId val="79456312"/>\n'
540
+ " <c:scaling>\n"
541
+ ' <c:orientation val="minMax"/>\n'
542
+ " </c:scaling>\n"
543
+ ' <c:delete val="0"/>\n'
544
+ ' <c:axPos val="{cat_ax_pos}"/>\n'
545
+ ' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
546
+ ' <c:majorTickMark val="out"/>\n'
547
+ ' <c:minorTickMark val="none"/>\n'
548
+ ' <c:tickLblPos val="nextTo"/>\n'
549
+ ' <c:crossAx val="33489208"/>\n'
550
+ ' <c:crosses val="autoZero"/>\n'
551
+ ' <c:auto val="1"/>\n'
552
+ ' <c:lblOffset val="100"/>\n'
553
+ ' <c:baseTimeUnit val="days"/>\n'
554
+ " </c:dateAx>\n"
555
+ ).format(**{"cat_ax_pos": self._cat_ax_pos, "nf": categories.number_format})
556
+
557
+ return (
558
+ " <c:catAx>\n"
559
+ ' <c:axId val="79456312"/>\n'
560
+ " <c:scaling>\n"
561
+ ' <c:orientation val="minMax"/>\n'
562
+ " </c:scaling>\n"
563
+ ' <c:delete val="0"/>\n'
564
+ ' <c:axPos val="{cat_ax_pos}"/>\n'
565
+ ' <c:majorTickMark val="out"/>\n'
566
+ ' <c:minorTickMark val="none"/>\n'
567
+ ' <c:tickLblPos val="nextTo"/>\n'
568
+ ' <c:crossAx val="33489208"/>\n'
569
+ ' <c:crosses val="autoZero"/>\n'
570
+ ' <c:auto val="1"/>\n'
571
+ ' <c:lblAlgn val="ctr"/>\n'
572
+ ' <c:lblOffset val="100"/>\n'
573
+ ' <c:noMultiLvlLbl val="0"/>\n'
574
+ " </c:catAx>\n"
575
+ ).format(**{"cat_ax_pos": self._cat_ax_pos})
576
+
577
+ @property
578
+ def _grouping_xml(self):
579
+ XL = XL_CHART_TYPE
580
+ clustered_types = (XL.BAR_CLUSTERED, XL.COLUMN_CLUSTERED)
581
+ stacked_types = (XL.BAR_STACKED, XL.COLUMN_STACKED)
582
+ percentStacked_types = (XL.BAR_STACKED_100, XL.COLUMN_STACKED_100)
583
+ if self._chart_type in clustered_types:
584
+ return ' <c:grouping val="clustered"/>\n'
585
+ elif self._chart_type in stacked_types:
586
+ return ' <c:grouping val="stacked"/>\n'
587
+ elif self._chart_type in percentStacked_types:
588
+ return ' <c:grouping val="percentStacked"/>\n'
589
+ raise NotImplementedError("no _grouping_xml() for chart type %s" % self._chart_type)
590
+
591
+ @property
592
+ def _overlap_xml(self):
593
+ XL = XL_CHART_TYPE
594
+ percentStacked_types = (
595
+ XL.BAR_STACKED,
596
+ XL.BAR_STACKED_100,
597
+ XL.COLUMN_STACKED,
598
+ XL.COLUMN_STACKED_100,
599
+ )
600
+ if self._chart_type in percentStacked_types:
601
+ return ' <c:overlap val="100"/>\n'
602
+ return ""
603
+
604
+ @property
605
+ def _ser_xml(self):
606
+ xml = ""
607
+ for series in self._chart_data:
608
+ xml_writer = _CategorySeriesXmlWriter(series)
609
+ xml += (
610
+ " <c:ser>\n"
611
+ ' <c:idx val="{ser_idx}"/>\n'
612
+ ' <c:order val="{ser_order}"/>\n'
613
+ "{tx_xml}"
614
+ "{cat_xml}"
615
+ "{val_xml}"
616
+ " </c:ser>\n"
617
+ ).format(
618
+ **{
619
+ "ser_idx": series.index,
620
+ "ser_order": series.index,
621
+ "tx_xml": xml_writer.tx_xml,
622
+ "cat_xml": xml_writer.cat_xml,
623
+ "val_xml": xml_writer.val_xml,
624
+ }
625
+ )
626
+ return xml
627
+
628
+ @property
629
+ def _val_ax_pos(self):
630
+ return {
631
+ XL_CHART_TYPE.BAR_CLUSTERED: "b",
632
+ XL_CHART_TYPE.BAR_STACKED: "b",
633
+ XL_CHART_TYPE.BAR_STACKED_100: "b",
634
+ XL_CHART_TYPE.COLUMN_CLUSTERED: "l",
635
+ XL_CHART_TYPE.COLUMN_STACKED: "l",
636
+ XL_CHART_TYPE.COLUMN_STACKED_100: "l",
637
+ }[self._chart_type]
638
+
639
+
640
+ class _DoughnutChartXmlWriter(_BaseChartXmlWriter):
641
+ """
642
+ Provides specialized methods particular to the ``<c:doughnutChart>``
643
+ element.
644
+ """
645
+
646
+ @property
647
+ def xml(self):
648
+ return (
649
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
650
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
651
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
652
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
653
+ 'iceDocument/2006/relationships">\n'
654
+ ' <c:date1904 val="0"/>\n'
655
+ ' <c:roundedCorners val="0"/>\n'
656
+ " <c:chart>\n"
657
+ ' <c:autoTitleDeleted val="0"/>\n'
658
+ " <c:plotArea>\n"
659
+ " <c:layout/>\n"
660
+ " <c:doughnutChart>\n"
661
+ ' <c:varyColors val="1"/>\n'
662
+ "{ser_xml}"
663
+ " <c:dLbls>\n"
664
+ ' <c:showLegendKey val="0"/>\n'
665
+ ' <c:showVal val="0"/>\n'
666
+ ' <c:showCatName val="0"/>\n'
667
+ ' <c:showSerName val="0"/>\n'
668
+ ' <c:showPercent val="0"/>\n'
669
+ ' <c:showBubbleSize val="0"/>\n'
670
+ ' <c:showLeaderLines val="1"/>\n'
671
+ " </c:dLbls>\n"
672
+ ' <c:firstSliceAng val="0"/>\n'
673
+ ' <c:holeSize val="50"/>\n'
674
+ " </c:doughnutChart>\n"
675
+ " </c:plotArea>\n"
676
+ " <c:legend>\n"
677
+ ' <c:legendPos val="r"/>\n'
678
+ " <c:layout/>\n"
679
+ ' <c:overlay val="0"/>\n'
680
+ " </c:legend>\n"
681
+ ' <c:plotVisOnly val="1"/>\n'
682
+ ' <c:dispBlanksAs val="gap"/>\n'
683
+ ' <c:showDLblsOverMax val="0"/>\n'
684
+ " </c:chart>\n"
685
+ " <c:txPr>\n"
686
+ " <a:bodyPr/>\n"
687
+ " <a:lstStyle/>\n"
688
+ " <a:p>\n"
689
+ " <a:pPr>\n"
690
+ ' <a:defRPr sz="1800"/>\n'
691
+ " </a:pPr>\n"
692
+ ' <a:endParaRPr lang="en-US"/>\n'
693
+ " </a:p>\n"
694
+ " </c:txPr>\n"
695
+ "</c:chartSpace>\n"
696
+ ).format(**{"ser_xml": self._ser_xml})
697
+
698
+ @property
699
+ def _explosion_xml(self):
700
+ if self._chart_type == XL_CHART_TYPE.DOUGHNUT_EXPLODED:
701
+ return ' <c:explosion val="25"/>\n'
702
+ return ""
703
+
704
+ @property
705
+ def _ser_xml(self):
706
+ xml = ""
707
+ for series in self._chart_data:
708
+ xml_writer = _CategorySeriesXmlWriter(series)
709
+ xml += (
710
+ " <c:ser>\n"
711
+ ' <c:idx val="{ser_idx}"/>\n'
712
+ ' <c:order val="{ser_order}"/>\n'
713
+ "{tx_xml}"
714
+ "{explosion_xml}"
715
+ "{cat_xml}"
716
+ "{val_xml}"
717
+ " </c:ser>\n"
718
+ ).format(
719
+ **{
720
+ "ser_idx": series.index,
721
+ "ser_order": series.index,
722
+ "tx_xml": xml_writer.tx_xml,
723
+ "explosion_xml": self._explosion_xml,
724
+ "cat_xml": xml_writer.cat_xml,
725
+ "val_xml": xml_writer.val_xml,
726
+ }
727
+ )
728
+ return xml
729
+
730
+
731
+ class _LineChartXmlWriter(_BaseChartXmlWriter):
732
+ """
733
+ Provides specialized methods particular to the ``<c:lineChart>`` element.
734
+ """
735
+
736
+ @property
737
+ def xml(self):
738
+ return (
739
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
740
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
741
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
742
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
743
+ 'iceDocument/2006/relationships">\n'
744
+ ' <c:date1904 val="0"/>\n'
745
+ " <c:chart>\n"
746
+ ' <c:autoTitleDeleted val="0"/>\n'
747
+ " <c:plotArea>\n"
748
+ " <c:lineChart>\n"
749
+ "{grouping_xml}"
750
+ ' <c:varyColors val="0"/>\n'
751
+ "{ser_xml}"
752
+ ' <c:marker val="1"/>\n'
753
+ ' <c:smooth val="0"/>\n'
754
+ ' <c:axId val="2118791784"/>\n'
755
+ ' <c:axId val="2140495176"/>\n'
756
+ " </c:lineChart>\n"
757
+ "{cat_ax_xml}"
758
+ " <c:valAx>\n"
759
+ ' <c:axId val="2140495176"/>\n'
760
+ " <c:scaling/>\n"
761
+ ' <c:delete val="0"/>\n'
762
+ ' <c:axPos val="l"/>\n'
763
+ " <c:majorGridlines/>\n"
764
+ ' <c:majorTickMark val="out"/>\n'
765
+ ' <c:minorTickMark val="none"/>\n'
766
+ ' <c:tickLblPos val="nextTo"/>\n'
767
+ ' <c:crossAx val="2118791784"/>\n'
768
+ ' <c:crosses val="autoZero"/>\n'
769
+ " </c:valAx>\n"
770
+ " </c:plotArea>\n"
771
+ " <c:legend>\n"
772
+ ' <c:legendPos val="r"/>\n'
773
+ " <c:layout/>\n"
774
+ ' <c:overlay val="0"/>\n'
775
+ " </c:legend>\n"
776
+ ' <c:plotVisOnly val="1"/>\n'
777
+ ' <c:dispBlanksAs val="gap"/>\n'
778
+ ' <c:showDLblsOverMax val="0"/>\n'
779
+ " </c:chart>\n"
780
+ " <c:txPr>\n"
781
+ " <a:bodyPr/>\n"
782
+ " <a:lstStyle/>\n"
783
+ " <a:p>\n"
784
+ " <a:pPr>\n"
785
+ ' <a:defRPr sz="1800"/>\n'
786
+ " </a:pPr>\n"
787
+ ' <a:endParaRPr lang="en-US"/>\n'
788
+ " </a:p>\n"
789
+ " </c:txPr>\n"
790
+ "</c:chartSpace>\n"
791
+ ).format(
792
+ **{
793
+ "grouping_xml": self._grouping_xml,
794
+ "ser_xml": self._ser_xml,
795
+ "cat_ax_xml": self._cat_ax_xml,
796
+ }
797
+ )
798
+
799
+ @property
800
+ def _cat_ax_xml(self):
801
+ categories = self._chart_data.categories
802
+
803
+ if categories.are_dates:
804
+ return (
805
+ " <c:dateAx>\n"
806
+ ' <c:axId val="2118791784"/>\n'
807
+ " <c:scaling>\n"
808
+ ' <c:orientation val="minMax"/>\n'
809
+ " </c:scaling>\n"
810
+ ' <c:delete val="0"/>\n'
811
+ ' <c:axPos val="b"/>\n'
812
+ ' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
813
+ ' <c:majorTickMark val="out"/>\n'
814
+ ' <c:minorTickMark val="none"/>\n'
815
+ ' <c:tickLblPos val="nextTo"/>\n'
816
+ ' <c:crossAx val="2140495176"/>\n'
817
+ ' <c:crosses val="autoZero"/>\n'
818
+ ' <c:auto val="1"/>\n'
819
+ ' <c:lblOffset val="100"/>\n'
820
+ ' <c:baseTimeUnit val="days"/>\n'
821
+ " </c:dateAx>\n"
822
+ ).format(**{"nf": categories.number_format})
823
+
824
+ return (
825
+ " <c:catAx>\n"
826
+ ' <c:axId val="2118791784"/>\n'
827
+ " <c:scaling>\n"
828
+ ' <c:orientation val="minMax"/>\n'
829
+ " </c:scaling>\n"
830
+ ' <c:delete val="0"/>\n'
831
+ ' <c:axPos val="b"/>\n'
832
+ ' <c:majorTickMark val="out"/>\n'
833
+ ' <c:minorTickMark val="none"/>\n'
834
+ ' <c:tickLblPos val="nextTo"/>\n'
835
+ ' <c:crossAx val="2140495176"/>\n'
836
+ ' <c:crosses val="autoZero"/>\n'
837
+ ' <c:auto val="1"/>\n'
838
+ ' <c:lblAlgn val="ctr"/>\n'
839
+ ' <c:lblOffset val="100"/>\n'
840
+ ' <c:noMultiLvlLbl val="0"/>\n'
841
+ " </c:catAx>\n"
842
+ )
843
+
844
+ @property
845
+ def _grouping_xml(self):
846
+ XL = XL_CHART_TYPE
847
+ standard_types = (XL.LINE, XL.LINE_MARKERS)
848
+ stacked_types = (XL.LINE_STACKED, XL.LINE_MARKERS_STACKED)
849
+ percentStacked_types = (XL.LINE_STACKED_100, XL.LINE_MARKERS_STACKED_100)
850
+ if self._chart_type in standard_types:
851
+ return ' <c:grouping val="standard"/>\n'
852
+ elif self._chart_type in stacked_types:
853
+ return ' <c:grouping val="stacked"/>\n'
854
+ elif self._chart_type in percentStacked_types:
855
+ return ' <c:grouping val="percentStacked"/>\n'
856
+ raise NotImplementedError("no _grouping_xml() for chart type %s" % self._chart_type)
857
+
858
+ @property
859
+ def _marker_xml(self):
860
+ XL = XL_CHART_TYPE
861
+ no_marker_types = (XL.LINE, XL.LINE_STACKED, XL.LINE_STACKED_100)
862
+ if self._chart_type in no_marker_types:
863
+ return (
864
+ " <c:marker>\n"
865
+ ' <c:symbol val="none"/>\n'
866
+ " </c:marker>\n"
867
+ )
868
+ return ""
869
+
870
+ @property
871
+ def _ser_xml(self):
872
+ xml = ""
873
+ for series in self._chart_data:
874
+ xml_writer = _CategorySeriesXmlWriter(series)
875
+ xml += (
876
+ " <c:ser>\n"
877
+ ' <c:idx val="{ser_idx}"/>\n'
878
+ ' <c:order val="{ser_order}"/>\n'
879
+ "{tx_xml}"
880
+ "{marker_xml}"
881
+ "{cat_xml}"
882
+ "{val_xml}"
883
+ ' <c:smooth val="0"/>\n'
884
+ " </c:ser>\n"
885
+ ).format(
886
+ **{
887
+ "ser_idx": series.index,
888
+ "ser_order": series.index,
889
+ "tx_xml": xml_writer.tx_xml,
890
+ "marker_xml": self._marker_xml,
891
+ "cat_xml": xml_writer.cat_xml,
892
+ "val_xml": xml_writer.val_xml,
893
+ }
894
+ )
895
+ return xml
896
+
897
+
898
+ class _PieChartXmlWriter(_BaseChartXmlWriter):
899
+ """
900
+ Provides specialized methods particular to the ``<c:pieChart>`` element.
901
+ """
902
+
903
+ @property
904
+ def xml(self):
905
+ return (
906
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
907
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
908
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
909
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
910
+ 'iceDocument/2006/relationships">\n'
911
+ " <c:chart>\n"
912
+ ' <c:autoTitleDeleted val="0"/>\n'
913
+ " <c:plotArea>\n"
914
+ " <c:pieChart>\n"
915
+ ' <c:varyColors val="1"/>\n'
916
+ "{ser_xml}"
917
+ " </c:pieChart>\n"
918
+ " </c:plotArea>\n"
919
+ ' <c:dispBlanksAs val="gap"/>\n'
920
+ " </c:chart>\n"
921
+ " <c:txPr>\n"
922
+ " <a:bodyPr/>\n"
923
+ " <a:lstStyle/>\n"
924
+ " <a:p>\n"
925
+ " <a:pPr>\n"
926
+ ' <a:defRPr sz="1800"/>\n'
927
+ " </a:pPr>\n"
928
+ ' <a:endParaRPr lang="en-US"/>\n'
929
+ " </a:p>\n"
930
+ " </c:txPr>\n"
931
+ "</c:chartSpace>\n"
932
+ ).format(**{"ser_xml": self._ser_xml})
933
+
934
+ @property
935
+ def _explosion_xml(self):
936
+ if self._chart_type == XL_CHART_TYPE.PIE_EXPLODED:
937
+ return ' <c:explosion val="25"/>\n'
938
+ return ""
939
+
940
+ @property
941
+ def _ser_xml(self):
942
+ xml_writer = _CategorySeriesXmlWriter(self._chart_data[0])
943
+ xml = (
944
+ " <c:ser>\n"
945
+ ' <c:idx val="0"/>\n'
946
+ ' <c:order val="0"/>\n'
947
+ "{tx_xml}"
948
+ "{explosion_xml}"
949
+ "{cat_xml}"
950
+ "{val_xml}"
951
+ " </c:ser>\n"
952
+ ).format(
953
+ **{
954
+ "tx_xml": xml_writer.tx_xml,
955
+ "explosion_xml": self._explosion_xml,
956
+ "cat_xml": xml_writer.cat_xml,
957
+ "val_xml": xml_writer.val_xml,
958
+ }
959
+ )
960
+ return xml
961
+
962
+
963
+ class _RadarChartXmlWriter(_BaseChartXmlWriter):
964
+ """
965
+ Generates XML for the ``<c:radarChart>`` element.
966
+ """
967
+
968
+ @property
969
+ def xml(self):
970
+ return (
971
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
972
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
973
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
974
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
975
+ 'iceDocument/2006/relationships">\n'
976
+ ' <c:date1904 val="0"/>\n'
977
+ ' <c:roundedCorners val="0"/>\n'
978
+ ' <mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.'
979
+ 'org/markup-compatibility/2006">\n'
980
+ ' <mc:Choice xmlns:c14="http://schemas.microsoft.com/office/d'
981
+ 'rawing/2007/8/2/chart" Requires="c14">\n'
982
+ ' <c14:style val="118"/>\n'
983
+ " </mc:Choice>\n"
984
+ " <mc:Fallback>\n"
985
+ ' <c:style val="18"/>\n'
986
+ " </mc:Fallback>\n"
987
+ " </mc:AlternateContent>\n"
988
+ " <c:chart>\n"
989
+ ' <c:autoTitleDeleted val="0"/>\n'
990
+ " <c:plotArea>\n"
991
+ " <c:layout/>\n"
992
+ " <c:radarChart>\n"
993
+ ' <c:radarStyle val="{radar_style}"/>\n'
994
+ ' <c:varyColors val="0"/>\n'
995
+ "{ser_xml}"
996
+ ' <c:axId val="2073612648"/>\n'
997
+ ' <c:axId val="34711432"/>\n'
998
+ " </c:radarChart>\n"
999
+ " <c:catAx>\n"
1000
+ ' <c:axId val="2073612648"/>\n'
1001
+ " <c:scaling>\n"
1002
+ ' <c:orientation val="minMax"/>\n'
1003
+ " </c:scaling>\n"
1004
+ ' <c:delete val="0"/>\n'
1005
+ ' <c:axPos val="b"/>\n'
1006
+ " <c:majorGridlines/>\n"
1007
+ ' <c:numFmt formatCode="m/d/yy" sourceLinked="1"/>\n'
1008
+ ' <c:majorTickMark val="out"/>\n'
1009
+ ' <c:minorTickMark val="none"/>\n'
1010
+ ' <c:tickLblPos val="nextTo"/>\n'
1011
+ ' <c:crossAx val="34711432"/>\n'
1012
+ ' <c:crosses val="autoZero"/>\n'
1013
+ ' <c:auto val="1"/>\n'
1014
+ ' <c:lblAlgn val="ctr"/>\n'
1015
+ ' <c:lblOffset val="100"/>\n'
1016
+ ' <c:noMultiLvlLbl val="0"/>\n'
1017
+ " </c:catAx>\n"
1018
+ " <c:valAx>\n"
1019
+ ' <c:axId val="34711432"/>\n'
1020
+ " <c:scaling>\n"
1021
+ ' <c:orientation val="minMax"/>\n'
1022
+ " </c:scaling>\n"
1023
+ ' <c:delete val="0"/>\n'
1024
+ ' <c:axPos val="l"/>\n'
1025
+ " <c:majorGridlines/>\n"
1026
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
1027
+ ' <c:majorTickMark val="cross"/>\n'
1028
+ ' <c:minorTickMark val="none"/>\n'
1029
+ ' <c:tickLblPos val="nextTo"/>\n'
1030
+ ' <c:crossAx val="2073612648"/>\n'
1031
+ ' <c:crosses val="autoZero"/>\n'
1032
+ ' <c:crossBetween val="between"/>\n'
1033
+ " </c:valAx>\n"
1034
+ " </c:plotArea>\n"
1035
+ ' <c:plotVisOnly val="1"/>\n'
1036
+ ' <c:dispBlanksAs val="gap"/>\n'
1037
+ ' <c:showDLblsOverMax val="0"/>\n'
1038
+ " </c:chart>\n"
1039
+ " <c:txPr>\n"
1040
+ " <a:bodyPr/>\n"
1041
+ " <a:lstStyle/>\n"
1042
+ " <a:p>\n"
1043
+ " <a:pPr>\n"
1044
+ ' <a:defRPr sz="1800"/>\n'
1045
+ " </a:pPr>\n"
1046
+ ' <a:endParaRPr lang="en-US"/>\n'
1047
+ " </a:p>\n"
1048
+ " </c:txPr>\n"
1049
+ "</c:chartSpace>\n"
1050
+ ).format(**{"radar_style": self._radar_style, "ser_xml": self._ser_xml})
1051
+
1052
+ @property
1053
+ def _marker_xml(self):
1054
+ if self._chart_type == XL_CHART_TYPE.RADAR:
1055
+ return (
1056
+ " <c:marker>\n"
1057
+ ' <c:symbol val="none"/>\n'
1058
+ " </c:marker>\n"
1059
+ )
1060
+ return ""
1061
+
1062
+ @property
1063
+ def _radar_style(self):
1064
+ if self._chart_type == XL_CHART_TYPE.RADAR_FILLED:
1065
+ return "filled"
1066
+ return "marker"
1067
+
1068
+ @property
1069
+ def _ser_xml(self):
1070
+ xml = ""
1071
+ for series in self._chart_data:
1072
+ xml_writer = _CategorySeriesXmlWriter(series)
1073
+ xml += (
1074
+ " <c:ser>\n"
1075
+ ' <c:idx val="{ser_idx}"/>\n'
1076
+ ' <c:order val="{ser_order}"/>\n'
1077
+ "{tx_xml}"
1078
+ "{marker_xml}"
1079
+ "{cat_xml}"
1080
+ "{val_xml}"
1081
+ # NB: no <c:smooth/> — CT_RadarSer does not admit it (it is a
1082
+ # line/scatter-series element). Emitting it here failed schema
1083
+ # validation and PowerPoint repaired the file.
1084
+ " </c:ser>\n"
1085
+ ).format(
1086
+ **{
1087
+ "ser_idx": series.index,
1088
+ "ser_order": series.index,
1089
+ "tx_xml": xml_writer.tx_xml,
1090
+ "marker_xml": self._marker_xml,
1091
+ "cat_xml": xml_writer.cat_xml,
1092
+ "val_xml": xml_writer.val_xml,
1093
+ }
1094
+ )
1095
+ return xml
1096
+
1097
+
1098
+ class _XyChartXmlWriter(_BaseChartXmlWriter):
1099
+ """
1100
+ Generates XML for the ``<c:scatterChart>`` element.
1101
+ """
1102
+
1103
+ @property
1104
+ def xml(self):
1105
+ xml = (
1106
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
1107
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
1108
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
1109
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
1110
+ 'iceDocument/2006/relationships">\n'
1111
+ " <c:chart>\n"
1112
+ " <c:plotArea>\n"
1113
+ " <c:scatterChart>\n"
1114
+ ' <c:scatterStyle val="%s"/>\n'
1115
+ ' <c:varyColors val="0"/>\n'
1116
+ "%s"
1117
+ ' <c:axId val="18542776"/>\n'
1118
+ ' <c:axId val="17839736"/>\n'
1119
+ " </c:scatterChart>\n"
1120
+ " <c:valAx>\n"
1121
+ ' <c:axId val="18542776"/>\n'
1122
+ " <c:scaling>\n"
1123
+ ' <c:orientation val="minMax"/>\n'
1124
+ " </c:scaling>\n"
1125
+ ' <c:delete val="0"/>\n'
1126
+ ' <c:axPos val="b"/>\n'
1127
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
1128
+ ' <c:majorTickMark val="out"/>\n'
1129
+ ' <c:minorTickMark val="none"/>\n'
1130
+ ' <c:tickLblPos val="nextTo"/>\n'
1131
+ ' <c:crossAx val="17839736"/>\n'
1132
+ ' <c:crosses val="autoZero"/>\n'
1133
+ ' <c:crossBetween val="midCat"/>\n'
1134
+ " </c:valAx>\n"
1135
+ " <c:valAx>\n"
1136
+ ' <c:axId val="17839736"/>\n'
1137
+ " <c:scaling>\n"
1138
+ ' <c:orientation val="minMax"/>\n'
1139
+ " </c:scaling>\n"
1140
+ ' <c:delete val="0"/>\n'
1141
+ ' <c:axPos val="l"/>\n'
1142
+ " <c:majorGridlines/>\n"
1143
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
1144
+ ' <c:majorTickMark val="out"/>\n'
1145
+ ' <c:minorTickMark val="none"/>\n'
1146
+ ' <c:tickLblPos val="nextTo"/>\n'
1147
+ ' <c:crossAx val="18542776"/>\n'
1148
+ ' <c:crosses val="autoZero"/>\n'
1149
+ ' <c:crossBetween val="midCat"/>\n'
1150
+ " </c:valAx>\n"
1151
+ " </c:plotArea>\n"
1152
+ " <c:legend>\n"
1153
+ ' <c:legendPos val="r"/>\n'
1154
+ " <c:layout/>\n"
1155
+ ' <c:overlay val="0"/>\n'
1156
+ " </c:legend>\n"
1157
+ ' <c:plotVisOnly val="1"/>\n'
1158
+ ' <c:dispBlanksAs val="gap"/>\n'
1159
+ ' <c:showDLblsOverMax val="0"/>\n'
1160
+ " </c:chart>\n"
1161
+ " <c:txPr>\n"
1162
+ " <a:bodyPr/>\n"
1163
+ " <a:lstStyle/>\n"
1164
+ " <a:p>\n"
1165
+ " <a:pPr>\n"
1166
+ ' <a:defRPr sz="1800"/>\n'
1167
+ " </a:pPr>\n"
1168
+ ' <a:endParaRPr lang="en-US"/>\n'
1169
+ " </a:p>\n"
1170
+ " </c:txPr>\n"
1171
+ "</c:chartSpace>\n"
1172
+ ) % (self._scatterStyle_val, self._ser_xml)
1173
+ return xml
1174
+
1175
+ @property
1176
+ def _marker_xml(self):
1177
+ no_marker_types = (
1178
+ XL_CHART_TYPE.XY_SCATTER_LINES_NO_MARKERS,
1179
+ XL_CHART_TYPE.XY_SCATTER_SMOOTH_NO_MARKERS,
1180
+ )
1181
+ if self._chart_type in no_marker_types:
1182
+ return (
1183
+ " <c:marker>\n"
1184
+ ' <c:symbol val="none"/>\n'
1185
+ " </c:marker>\n"
1186
+ )
1187
+ return ""
1188
+
1189
+ @property
1190
+ def _scatterStyle_val(self):
1191
+ smooth_types = (
1192
+ XL_CHART_TYPE.XY_SCATTER_SMOOTH,
1193
+ XL_CHART_TYPE.XY_SCATTER_SMOOTH_NO_MARKERS,
1194
+ )
1195
+ if self._chart_type in smooth_types:
1196
+ return "smoothMarker"
1197
+ # Markers-only scatter: ``val="marker"`` is the OOXML way to
1198
+ # say "no lines" at the chart-type level, so callers that recolour
1199
+ # a series via ``series.format.line.color.rgb`` don't accidentally
1200
+ # re-enable the line by overwriting the per-series ``noFill``
1201
+ # suppression we used to emit. See IMPROVEMENTS item 5.
1202
+ if self._chart_type == XL_CHART_TYPE.XY_SCATTER:
1203
+ return "marker"
1204
+ return "lineMarker"
1205
+
1206
+ @property
1207
+ def _ser_xml(self):
1208
+ xml = ""
1209
+ for series in self._chart_data:
1210
+ xml_writer = _XySeriesXmlWriter(series)
1211
+ xml += (
1212
+ " <c:ser>\n"
1213
+ ' <c:idx val="{ser_idx}"/>\n'
1214
+ ' <c:order val="{ser_order}"/>\n'
1215
+ "{tx_xml}"
1216
+ "{spPr_xml}"
1217
+ "{marker_xml}"
1218
+ "{xVal_xml}"
1219
+ "{yVal_xml}"
1220
+ ' <c:smooth val="0"/>\n'
1221
+ " </c:ser>\n"
1222
+ ).format(
1223
+ **{
1224
+ "ser_idx": series.index,
1225
+ "ser_order": series.index,
1226
+ "tx_xml": xml_writer.tx_xml,
1227
+ "spPr_xml": self._spPr_xml,
1228
+ "marker_xml": self._marker_xml,
1229
+ "xVal_xml": xml_writer.xVal_xml,
1230
+ "yVal_xml": xml_writer.yVal_xml,
1231
+ }
1232
+ )
1233
+ return xml
1234
+
1235
+ @property
1236
+ def _spPr_xml(self):
1237
+ # XY_SCATTER now communicates "no lines" via the chart-type
1238
+ # ``<c:scatterStyle val="marker"/>`` rather than a per-series
1239
+ # ``<a:ln><a:noFill/>`` override (see ``_scatterStyle_val``), so
1240
+ # no per-series spPr is required.
1241
+ return ""
1242
+
1243
+
1244
+ class _BubbleChartXmlWriter(_XyChartXmlWriter):
1245
+ """
1246
+ Provides specialized methods particular to the ``<c:bubbleChart>``
1247
+ element.
1248
+ """
1249
+
1250
+ @property
1251
+ def xml(self):
1252
+ xml = (
1253
+ "<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
1254
+ '<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
1255
+ 'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
1256
+ 'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
1257
+ 'iceDocument/2006/relationships">\n'
1258
+ " <c:chart>\n"
1259
+ ' <c:autoTitleDeleted val="0"/>\n'
1260
+ " <c:plotArea>\n"
1261
+ " <c:layout/>\n"
1262
+ " <c:bubbleChart>\n"
1263
+ ' <c:varyColors val="0"/>\n'
1264
+ "%s"
1265
+ " <c:dLbls>\n"
1266
+ ' <c:showLegendKey val="0"/>\n'
1267
+ ' <c:showVal val="0"/>\n'
1268
+ ' <c:showCatName val="0"/>\n'
1269
+ ' <c:showSerName val="0"/>\n'
1270
+ ' <c:showPercent val="0"/>\n'
1271
+ ' <c:showBubbleSize val="0"/>\n'
1272
+ " </c:dLbls>\n"
1273
+ ' <c:bubbleScale val="100"/>\n'
1274
+ ' <c:showNegBubbles val="0"/>\n'
1275
+ ' <c:axId val="31763576"/>\n'
1276
+ ' <c:axId val="31760088"/>\n'
1277
+ " </c:bubbleChart>\n"
1278
+ " <c:valAx>\n"
1279
+ ' <c:axId val="31763576"/>\n'
1280
+ " <c:scaling>\n"
1281
+ ' <c:orientation val="minMax"/>\n'
1282
+ " </c:scaling>\n"
1283
+ ' <c:delete val="0"/>\n'
1284
+ ' <c:axPos val="b"/>\n'
1285
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
1286
+ ' <c:majorTickMark val="out"/>\n'
1287
+ ' <c:minorTickMark val="none"/>\n'
1288
+ ' <c:tickLblPos val="nextTo"/>\n'
1289
+ ' <c:crossAx val="31760088"/>\n'
1290
+ ' <c:crosses val="autoZero"/>\n'
1291
+ ' <c:crossBetween val="midCat"/>\n'
1292
+ " </c:valAx>\n"
1293
+ " <c:valAx>\n"
1294
+ ' <c:axId val="31760088"/>\n'
1295
+ " <c:scaling>\n"
1296
+ ' <c:orientation val="minMax"/>\n'
1297
+ " </c:scaling>\n"
1298
+ ' <c:delete val="0"/>\n'
1299
+ ' <c:axPos val="l"/>\n'
1300
+ " <c:majorGridlines/>\n"
1301
+ ' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
1302
+ ' <c:majorTickMark val="out"/>\n'
1303
+ ' <c:minorTickMark val="none"/>\n'
1304
+ ' <c:tickLblPos val="nextTo"/>\n'
1305
+ ' <c:crossAx val="31763576"/>\n'
1306
+ ' <c:crosses val="autoZero"/>\n'
1307
+ ' <c:crossBetween val="midCat"/>\n'
1308
+ " </c:valAx>\n"
1309
+ " </c:plotArea>\n"
1310
+ " <c:legend>\n"
1311
+ ' <c:legendPos val="r"/>\n'
1312
+ " <c:layout/>\n"
1313
+ ' <c:overlay val="0"/>\n'
1314
+ " </c:legend>\n"
1315
+ ' <c:plotVisOnly val="1"/>\n'
1316
+ ' <c:dispBlanksAs val="gap"/>\n'
1317
+ ' <c:showDLblsOverMax val="0"/>\n'
1318
+ " </c:chart>\n"
1319
+ " <c:txPr>\n"
1320
+ " <a:bodyPr/>\n"
1321
+ " <a:lstStyle/>\n"
1322
+ " <a:p>\n"
1323
+ " <a:pPr>\n"
1324
+ ' <a:defRPr sz="1800"/>\n'
1325
+ " </a:pPr>\n"
1326
+ ' <a:endParaRPr lang="en-US"/>\n'
1327
+ " </a:p>\n"
1328
+ " </c:txPr>\n"
1329
+ "</c:chartSpace>\n"
1330
+ ) % self._ser_xml
1331
+ return xml
1332
+
1333
+ @property
1334
+ def _bubble3D_val(self):
1335
+ if self._chart_type == XL_CHART_TYPE.BUBBLE_THREE_D_EFFECT:
1336
+ return "1"
1337
+ return "0"
1338
+
1339
+ @property
1340
+ def _ser_xml(self):
1341
+ xml = ""
1342
+ for series in self._chart_data:
1343
+ xml_writer = _BubbleSeriesXmlWriter(series)
1344
+ xml += (
1345
+ " <c:ser>\n"
1346
+ ' <c:idx val="{ser_idx}"/>\n'
1347
+ ' <c:order val="{ser_order}"/>\n'
1348
+ "{tx_xml}"
1349
+ ' <c:invertIfNegative val="0"/>\n'
1350
+ "{xVal_xml}"
1351
+ "{yVal_xml}"
1352
+ "{bubbleSize_xml}"
1353
+ ' <c:bubble3D val="{bubble3D_val}"/>\n'
1354
+ " </c:ser>\n"
1355
+ ).format(
1356
+ **{
1357
+ "ser_idx": series.index,
1358
+ "ser_order": series.index,
1359
+ "tx_xml": xml_writer.tx_xml,
1360
+ "xVal_xml": xml_writer.xVal_xml,
1361
+ "yVal_xml": xml_writer.yVal_xml,
1362
+ "bubbleSize_xml": xml_writer.bubbleSize_xml,
1363
+ "bubble3D_val": self._bubble3D_val,
1364
+ }
1365
+ )
1366
+ return xml
1367
+
1368
+
1369
+ class _CategorySeriesXmlWriter(_BaseSeriesXmlWriter):
1370
+ """
1371
+ Generates XML snippets particular to a category chart series.
1372
+ """
1373
+
1374
+ @property
1375
+ def cat(self):
1376
+ """
1377
+ Return the ``<c:cat>`` element XML for this series, as an oxml
1378
+ element.
1379
+ """
1380
+ categories = self._series.categories
1381
+
1382
+ if categories.are_numeric:
1383
+ return parse_xml(
1384
+ self._numRef_cat_tmpl.format(
1385
+ **{
1386
+ "wksht_ref": self._series.categories_ref,
1387
+ "number_format": categories.number_format,
1388
+ "cat_count": categories.leaf_count,
1389
+ "cat_pt_xml": self._cat_num_pt_xml,
1390
+ "nsdecls": " %s" % nsdecls("c"),
1391
+ }
1392
+ )
1393
+ )
1394
+
1395
+ if categories.depth == 1:
1396
+ return parse_xml(
1397
+ self._cat_tmpl.format(
1398
+ **{
1399
+ "wksht_ref": self._series.categories_ref,
1400
+ "cat_count": categories.leaf_count,
1401
+ "cat_pt_xml": self._cat_pt_xml,
1402
+ "nsdecls": " %s" % nsdecls("c"),
1403
+ }
1404
+ )
1405
+ )
1406
+
1407
+ return parse_xml(
1408
+ self._multiLvl_cat_tmpl.format(
1409
+ **{
1410
+ "wksht_ref": self._series.categories_ref,
1411
+ "cat_count": categories.leaf_count,
1412
+ "lvl_xml": self._lvl_xml(categories),
1413
+ "nsdecls": " %s" % nsdecls("c"),
1414
+ }
1415
+ )
1416
+ )
1417
+
1418
+ @property
1419
+ def cat_xml(self):
1420
+ """
1421
+ The unicode XML snippet for the ``<c:cat>`` element for this series,
1422
+ containing the category labels and spreadsheet reference.
1423
+ """
1424
+ categories = self._series.categories
1425
+
1426
+ if categories.are_numeric:
1427
+ return self._numRef_cat_tmpl.format(
1428
+ **{
1429
+ "wksht_ref": self._series.categories_ref,
1430
+ "number_format": categories.number_format,
1431
+ "cat_count": categories.leaf_count,
1432
+ "cat_pt_xml": self._cat_num_pt_xml,
1433
+ "nsdecls": "",
1434
+ }
1435
+ )
1436
+
1437
+ if categories.depth == 1:
1438
+ return self._cat_tmpl.format(
1439
+ **{
1440
+ "wksht_ref": self._series.categories_ref,
1441
+ "cat_count": categories.leaf_count,
1442
+ "cat_pt_xml": self._cat_pt_xml,
1443
+ "nsdecls": "",
1444
+ }
1445
+ )
1446
+
1447
+ return self._multiLvl_cat_tmpl.format(
1448
+ **{
1449
+ "wksht_ref": self._series.categories_ref,
1450
+ "cat_count": categories.leaf_count,
1451
+ "lvl_xml": self._lvl_xml(categories),
1452
+ "nsdecls": "",
1453
+ }
1454
+ )
1455
+
1456
+ @property
1457
+ def val(self):
1458
+ """
1459
+ The ``<c:val>`` XML for this series, as an oxml element.
1460
+ """
1461
+ xml = self._val_tmpl.format(
1462
+ **{
1463
+ "nsdecls": " %s" % nsdecls("c"),
1464
+ "values_ref": self._series.values_ref,
1465
+ "number_format": self._series.number_format,
1466
+ "val_count": len(self._series),
1467
+ "val_pt_xml": self._val_pt_xml,
1468
+ }
1469
+ )
1470
+ return parse_xml(xml)
1471
+
1472
+ @property
1473
+ def val_xml(self):
1474
+ """
1475
+ Return the unicode XML snippet for the ``<c:val>`` element describing
1476
+ this series, containing the series values and their spreadsheet range
1477
+ reference.
1478
+ """
1479
+ return self._val_tmpl.format(
1480
+ **{
1481
+ "nsdecls": "",
1482
+ "values_ref": self._series.values_ref,
1483
+ "number_format": self._series.number_format,
1484
+ "val_count": len(self._series),
1485
+ "val_pt_xml": self._val_pt_xml,
1486
+ }
1487
+ )
1488
+
1489
+ @property
1490
+ def _cat_num_pt_xml(self):
1491
+ """
1492
+ The unicode XML snippet for the ``<c:pt>`` elements when category
1493
+ labels are numeric (including date type).
1494
+ """
1495
+ xml = ""
1496
+ for idx, category in enumerate(self._series.categories):
1497
+ xml += (
1498
+ ' <c:pt idx="{cat_idx}">\n'
1499
+ " <c:v>{cat_lbl_str}</c:v>\n"
1500
+ " </c:pt>\n"
1501
+ ).format(
1502
+ **{
1503
+ "cat_idx": idx,
1504
+ "cat_lbl_str": category.numeric_str_val(self._date_1904),
1505
+ }
1506
+ )
1507
+ return xml
1508
+
1509
+ @property
1510
+ def _cat_pt_xml(self):
1511
+ """
1512
+ The unicode XML snippet for the ``<c:pt>`` elements containing the
1513
+ category names for this series.
1514
+ """
1515
+ xml = ""
1516
+ for idx, category in enumerate(self._series.categories):
1517
+ xml += (
1518
+ ' <c:pt idx="{cat_idx}">\n'
1519
+ " <c:v>{cat_label}</c:v>\n"
1520
+ " </c:pt>\n"
1521
+ ).format(**{"cat_idx": idx, "cat_label": escape(str(category.label))})
1522
+ return xml
1523
+
1524
+ @property
1525
+ def _cat_tmpl(self):
1526
+ """
1527
+ The template for the ``<c:cat>`` element for this series, containing
1528
+ the category labels and spreadsheet reference.
1529
+ """
1530
+ return (
1531
+ " <c:cat{nsdecls}>\n"
1532
+ " <c:strRef>\n"
1533
+ " <c:f>{wksht_ref}</c:f>\n"
1534
+ " <c:strCache>\n"
1535
+ ' <c:ptCount val="{cat_count}"/>\n'
1536
+ "{cat_pt_xml}"
1537
+ " </c:strCache>\n"
1538
+ " </c:strRef>\n"
1539
+ " </c:cat>\n"
1540
+ )
1541
+
1542
+ def _lvl_xml(self, categories):
1543
+ """
1544
+ The unicode XML snippet for the ``<c:lvl>`` elements containing
1545
+ multi-level category names.
1546
+ """
1547
+
1548
+ def lvl_pt_xml(level):
1549
+ xml = ""
1550
+ for idx, name in level:
1551
+ xml += (
1552
+ ' <c:pt idx="%d">\n'
1553
+ " <c:v>%s</c:v>\n"
1554
+ " </c:pt>\n"
1555
+ ) % (idx, escape("%s" % name))
1556
+ return xml
1557
+
1558
+ xml = ""
1559
+ for level in categories.levels:
1560
+ xml += (" <c:lvl>\n" "{lvl_pt_xml}" " </c:lvl>\n").format(
1561
+ **{"lvl_pt_xml": lvl_pt_xml(level)}
1562
+ )
1563
+ return xml
1564
+
1565
+ @property
1566
+ def _multiLvl_cat_tmpl(self):
1567
+ """
1568
+ The template for the ``<c:cat>`` element for this series when there
1569
+ are multi-level (nested) categories.
1570
+ """
1571
+ return (
1572
+ " <c:cat{nsdecls}>\n"
1573
+ " <c:multiLvlStrRef>\n"
1574
+ " <c:f>{wksht_ref}</c:f>\n"
1575
+ " <c:multiLvlStrCache>\n"
1576
+ ' <c:ptCount val="{cat_count}"/>\n'
1577
+ "{lvl_xml}"
1578
+ " </c:multiLvlStrCache>\n"
1579
+ " </c:multiLvlStrRef>\n"
1580
+ " </c:cat>\n"
1581
+ )
1582
+
1583
+ @property
1584
+ def _numRef_cat_tmpl(self):
1585
+ """
1586
+ The template for the ``<c:cat>`` element for this series when the
1587
+ labels are numeric (or date) values.
1588
+ """
1589
+ return (
1590
+ " <c:cat{nsdecls}>\n"
1591
+ " <c:numRef>\n"
1592
+ " <c:f>{wksht_ref}</c:f>\n"
1593
+ " <c:numCache>\n"
1594
+ " <c:formatCode>{number_format}</c:formatCode>\n"
1595
+ ' <c:ptCount val="{cat_count}"/>\n'
1596
+ "{cat_pt_xml}"
1597
+ " </c:numCache>\n"
1598
+ " </c:numRef>\n"
1599
+ " </c:cat>\n"
1600
+ )
1601
+
1602
+ @property
1603
+ def _val_pt_xml(self):
1604
+ """
1605
+ The unicode XML snippet containing the ``<c:pt>`` elements containing
1606
+ the values for this series.
1607
+ """
1608
+ xml = ""
1609
+ for idx, value in enumerate(self._series.values):
1610
+ if value is None:
1611
+ continue
1612
+ xml += (
1613
+ ' <c:pt idx="{val_idx:d}">\n'
1614
+ " <c:v>{value}</c:v>\n"
1615
+ " </c:pt>\n"
1616
+ ).format(**{"val_idx": idx, "value": value})
1617
+ return xml
1618
+
1619
+ @property
1620
+ def _val_tmpl(self):
1621
+ """
1622
+ The template for the ``<c:val>`` element for this series, containing
1623
+ the series values and their spreadsheet range reference.
1624
+ """
1625
+ return (
1626
+ " <c:val{nsdecls}>\n"
1627
+ " <c:numRef>\n"
1628
+ " <c:f>{values_ref}</c:f>\n"
1629
+ " <c:numCache>\n"
1630
+ " <c:formatCode>{number_format}</c:formatCode>\n"
1631
+ ' <c:ptCount val="{val_count}"/>\n'
1632
+ "{val_pt_xml}"
1633
+ " </c:numCache>\n"
1634
+ " </c:numRef>\n"
1635
+ " </c:val>\n"
1636
+ )
1637
+
1638
+
1639
+ class _XySeriesXmlWriter(_BaseSeriesXmlWriter):
1640
+ """
1641
+ Generates XML snippets particular to an XY series.
1642
+ """
1643
+
1644
+ @property
1645
+ def xVal(self):
1646
+ """
1647
+ Return the ``<c:xVal>`` element for this series as an oxml element.
1648
+ This element contains the X values for this series.
1649
+ """
1650
+ xml = self._xVal_tmpl.format(
1651
+ **{
1652
+ "nsdecls": " %s" % nsdecls("c"),
1653
+ "numRef_xml": self.numRef_xml(
1654
+ self._series.x_values_ref,
1655
+ self._series.number_format,
1656
+ self._series.x_values,
1657
+ ),
1658
+ }
1659
+ )
1660
+ return parse_xml(xml)
1661
+
1662
+ @property
1663
+ def xVal_xml(self):
1664
+ """
1665
+ Return the ``<c:xVal>`` element for this series as unicode text. This
1666
+ element contains the X values for this series.
1667
+ """
1668
+ return self._xVal_tmpl.format(
1669
+ **{
1670
+ "nsdecls": "",
1671
+ "numRef_xml": self.numRef_xml(
1672
+ self._series.x_values_ref,
1673
+ self._series.number_format,
1674
+ self._series.x_values,
1675
+ ),
1676
+ }
1677
+ )
1678
+
1679
+ @property
1680
+ def yVal(self):
1681
+ """
1682
+ Return the ``<c:yVal>`` element for this series as an oxml element.
1683
+ This element contains the Y values for this series.
1684
+ """
1685
+ xml = self._yVal_tmpl.format(
1686
+ **{
1687
+ "nsdecls": " %s" % nsdecls("c"),
1688
+ "numRef_xml": self.numRef_xml(
1689
+ self._series.y_values_ref,
1690
+ self._series.number_format,
1691
+ self._series.y_values,
1692
+ ),
1693
+ }
1694
+ )
1695
+ return parse_xml(xml)
1696
+
1697
+ @property
1698
+ def yVal_xml(self):
1699
+ """
1700
+ Return the ``<c:yVal>`` element for this series as unicode text. This
1701
+ element contains the Y values for this series.
1702
+ """
1703
+ return self._yVal_tmpl.format(
1704
+ **{
1705
+ "nsdecls": "",
1706
+ "numRef_xml": self.numRef_xml(
1707
+ self._series.y_values_ref,
1708
+ self._series.number_format,
1709
+ self._series.y_values,
1710
+ ),
1711
+ }
1712
+ )
1713
+
1714
+ @property
1715
+ def _xVal_tmpl(self):
1716
+ """
1717
+ The template for the ``<c:xVal>`` element for this series, containing
1718
+ the X values and their spreadsheet range reference.
1719
+ """
1720
+ return " <c:xVal{nsdecls}>\n" "{numRef_xml}" " </c:xVal>\n"
1721
+
1722
+ @property
1723
+ def _yVal_tmpl(self):
1724
+ """
1725
+ The template for the ``<c:yVal>`` element for this series, containing
1726
+ the Y values and their spreadsheet range reference.
1727
+ """
1728
+ return " <c:yVal{nsdecls}>\n" "{numRef_xml}" " </c:yVal>\n"
1729
+
1730
+
1731
+ class _BubbleSeriesXmlWriter(_XySeriesXmlWriter):
1732
+ """
1733
+ Generates XML snippets particular to a bubble chart series.
1734
+ """
1735
+
1736
+ @property
1737
+ def bubbleSize(self):
1738
+ """
1739
+ Return the ``<c:bubbleSize>`` element for this series as an oxml
1740
+ element. This element contains the bubble size values for this
1741
+ series.
1742
+ """
1743
+ xml = self._bubbleSize_tmpl.format(
1744
+ **{
1745
+ "nsdecls": " %s" % nsdecls("c"),
1746
+ "numRef_xml": self.numRef_xml(
1747
+ self._series.bubble_sizes_ref,
1748
+ self._series.number_format,
1749
+ self._series.bubble_sizes,
1750
+ ),
1751
+ }
1752
+ )
1753
+ return parse_xml(xml)
1754
+
1755
+ @property
1756
+ def bubbleSize_xml(self):
1757
+ """
1758
+ Return the ``<c:bubbleSize>`` element for this series as unicode
1759
+ text. This element contains the bubble size values for all the
1760
+ data points in the chart.
1761
+ """
1762
+ return self._bubbleSize_tmpl.format(
1763
+ **{
1764
+ "nsdecls": "",
1765
+ "numRef_xml": self.numRef_xml(
1766
+ self._series.bubble_sizes_ref,
1767
+ self._series.number_format,
1768
+ self._series.bubble_sizes,
1769
+ ),
1770
+ }
1771
+ )
1772
+
1773
+ @property
1774
+ def _bubbleSize_tmpl(self):
1775
+ """
1776
+ The template for the ``<c:bubbleSize>`` element for this series,
1777
+ containing the bubble size values and their spreadsheet range
1778
+ reference.
1779
+ """
1780
+ return " <c:bubbleSize{nsdecls}>\n" "{numRef_xml}" " </c:bubbleSize>\n"
1781
+
1782
+
1783
+ class _BubbleSeriesXmlRewriter(_BaseSeriesXmlRewriter):
1784
+ """
1785
+ A series rewriter suitable for bubble charts.
1786
+ """
1787
+
1788
+ def _rewrite_ser_data(self, ser, series_data, date_1904):
1789
+ """
1790
+ Rewrite the ``<c:tx>``, ``<c:cat>`` and ``<c:val>`` child elements
1791
+ of *ser* based on the values in *series_data*.
1792
+ """
1793
+ ser._remove_tx()
1794
+ ser._remove_xVal()
1795
+ ser._remove_yVal()
1796
+ ser._remove_bubbleSize()
1797
+
1798
+ xml_writer = _BubbleSeriesXmlWriter(series_data)
1799
+
1800
+ ser._insert_tx(xml_writer.tx)
1801
+ ser._insert_xVal(xml_writer.xVal)
1802
+ ser._insert_yVal(xml_writer.yVal)
1803
+ ser._insert_bubbleSize(xml_writer.bubbleSize)
1804
+
1805
+
1806
+ class _CategorySeriesXmlRewriter(_BaseSeriesXmlRewriter):
1807
+ """
1808
+ A series rewriter suitable for category charts.
1809
+ """
1810
+
1811
+ def _rewrite_ser_data(self, ser, series_data, date_1904):
1812
+ """
1813
+ Rewrite the ``<c:tx>``, ``<c:cat>`` and ``<c:val>`` child elements
1814
+ of *ser* based on the values in *series_data*.
1815
+ """
1816
+ ser._remove_tx()
1817
+ ser._remove_cat()
1818
+ ser._remove_val()
1819
+
1820
+ xml_writer = _CategorySeriesXmlWriter(series_data, date_1904)
1821
+
1822
+ ser._insert_tx(xml_writer.tx)
1823
+ ser._insert_cat(xml_writer.cat)
1824
+ ser._insert_val(xml_writer.val)
1825
+
1826
+
1827
+ class _XySeriesXmlRewriter(_BaseSeriesXmlRewriter):
1828
+ """
1829
+ A series rewriter suitable for XY (aka. scatter) charts.
1830
+ """
1831
+
1832
+ def _rewrite_ser_data(self, ser, series_data, date_1904):
1833
+ """
1834
+ Rewrite the ``<c:tx>``, ``<c:xVal>`` and ``<c:yVal>`` child elements
1835
+ of *ser* based on the values in *series_data*.
1836
+ """
1837
+ ser._remove_tx()
1838
+ ser._remove_xVal()
1839
+ ser._remove_yVal()
1840
+
1841
+ xml_writer = _XySeriesXmlWriter(series_data)
1842
+
1843
+ ser._insert_tx(xml_writer.tx)
1844
+ ser._insert_xVal(xml_writer.xVal)
1845
+ ser._insert_yVal(xml_writer.yVal)