1###############################################################################
2#
3# Metadata - A class for writing the Excel XLSX Metadata file.
4#
5# SPDX-License-Identifier: BSD-2-Clause
6# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org
7#
8
9from . import xmlwriter
10
11
12class Metadata(xmlwriter.XMLwriter):
13 """
14 A class for writing the Excel XLSX Metadata file.
15
16
17 """
18
19 ###########################################################################
20 #
21 # Public API.
22 #
23 ###########################################################################
24
25 def __init__(self):
26 """
27 Constructor.
28
29 """
30
31 super(Metadata, self).__init__()
32 self.has_dynamic_functions = False
33 self.has_embedded_images = False
34 self.num_embedded_images = 0
35
36 ###########################################################################
37 #
38 # Private API.
39 #
40 ###########################################################################
41
42 def _assemble_xml_file(self):
43 # Assemble and write the XML file.
44
45 if self.num_embedded_images > 0:
46 self.has_embedded_images = True
47
48 # Write the XML declaration.
49 self._xml_declaration()
50
51 # Write the metadata element.
52 self._write_metadata()
53
54 # Write the metadataTypes element.
55 self._write_metadata_types()
56
57 # Write the futureMetadata elements.
58 if self.has_dynamic_functions:
59 self._write_cell_future_metadata()
60 if self.has_embedded_images:
61 self._write_value_future_metadata()
62
63 # Write the cellMetadata element.
64 if self.has_dynamic_functions:
65 self._write_cell_metadata()
66 if self.has_embedded_images:
67 self._write_value_metadata()
68
69 self._xml_end_tag("metadata")
70
71 # Close the file.
72 self._xml_close()
73
74 ###########################################################################
75 #
76 # XML methods.
77 #
78 ###########################################################################
79
80 def _write_metadata(self):
81 # Write the <metadata> element.
82 xmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
83 schema = "http://schemas.microsoft.com/office/spreadsheetml"
84
85 attributes = [("xmlns", xmlns)]
86
87 if self.has_embedded_images:
88 attributes.append(("xmlns:xlrd", schema + "/2017/richdata"))
89
90 if self.has_dynamic_functions:
91 attributes.append(("xmlns:xda", schema + "/2017/dynamicarray"))
92
93 self._xml_start_tag("metadata", attributes)
94
95 def _write_metadata_types(self):
96 # Write the <metadataTypes> element.
97 count = 0
98
99 if self.has_dynamic_functions:
100 count += 1
101 if self.has_embedded_images:
102 count += 1
103
104 attributes = [("count", count)]
105
106 self._xml_start_tag("metadataTypes", attributes)
107
108 # Write the metadataType element.
109 if self.has_dynamic_functions:
110 self._write_cell_metadata_type()
111 if self.has_embedded_images:
112 self._write_value_metadata_type()
113
114 self._xml_end_tag("metadataTypes")
115
116 def _write_cell_metadata_type(self):
117 # Write the <metadataType> element.
118 attributes = [
119 ("name", "XLDAPR"),
120 ("minSupportedVersion", 120000),
121 ("copy", 1),
122 ("pasteAll", 1),
123 ("pasteValues", 1),
124 ("merge", 1),
125 ("splitFirst", 1),
126 ("rowColShift", 1),
127 ("clearFormats", 1),
128 ("clearComments", 1),
129 ("assign", 1),
130 ("coerce", 1),
131 ("cellMeta", 1),
132 ]
133
134 self._xml_empty_tag("metadataType", attributes)
135
136 def _write_value_metadata_type(self):
137 # Write the <metadataType> element.
138 attributes = [
139 ("name", "XLRICHVALUE"),
140 ("minSupportedVersion", 120000),
141 ("copy", 1),
142 ("pasteAll", 1),
143 ("pasteValues", 1),
144 ("merge", 1),
145 ("splitFirst", 1),
146 ("rowColShift", 1),
147 ("clearFormats", 1),
148 ("clearComments", 1),
149 ("assign", 1),
150 ("coerce", 1),
151 ]
152
153 self._xml_empty_tag("metadataType", attributes)
154
155 def _write_cell_future_metadata(self):
156 # Write the <futureMetadata> element.
157 attributes = [
158 ("name", "XLDAPR"),
159 ("count", 1),
160 ]
161
162 self._xml_start_tag("futureMetadata", attributes)
163 self._xml_start_tag("bk")
164 self._xml_start_tag("extLst")
165 self._write_cell_ext()
166 self._xml_end_tag("extLst")
167 self._xml_end_tag("bk")
168 self._xml_end_tag("futureMetadata")
169
170 def _write_value_future_metadata(self):
171 # Write the <futureMetadata> element.
172 attributes = [
173 ("name", "XLRICHVALUE"),
174 ("count", self.num_embedded_images),
175 ]
176
177 self._xml_start_tag("futureMetadata", attributes)
178
179 for index in range(self.num_embedded_images):
180 self._xml_start_tag("bk")
181 self._xml_start_tag("extLst")
182 self._write_value_ext(index)
183 self._xml_end_tag("extLst")
184 self._xml_end_tag("bk")
185
186 self._xml_end_tag("futureMetadata")
187
188 def _write_cell_ext(self):
189 # Write the <ext> element.
190 attributes = [("uri", "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}")]
191
192 self._xml_start_tag("ext", attributes)
193
194 # Write the xda:dynamicArrayProperties element.
195 self._write_xda_dynamic_array_properties()
196
197 self._xml_end_tag("ext")
198
199 def _write_xda_dynamic_array_properties(self):
200 # Write the <xda:dynamicArrayProperties> element.
201 attributes = [
202 ("fDynamic", 1),
203 ("fCollapsed", 0),
204 ]
205
206 self._xml_empty_tag("xda:dynamicArrayProperties", attributes)
207
208 def _write_value_ext(self, index):
209 # Write the <ext> element.
210 attributes = [("uri", "{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}")]
211
212 self._xml_start_tag("ext", attributes)
213
214 # Write the xlrd:rvb element.
215 self._write_xlrd_rvb(index)
216
217 self._xml_end_tag("ext")
218
219 def _write_xlrd_rvb(self, index):
220 # Write the <xlrd:rvb> element.
221 attributes = [("i", index)]
222
223 self._xml_empty_tag("xlrd:rvb", attributes)
224
225 def _write_cell_metadata(self):
226 # Write the <cellMetadata> element.
227 attributes = [("count", 1)]
228
229 self._xml_start_tag("cellMetadata", attributes)
230 self._xml_start_tag("bk")
231
232 # Write the rc element.
233 self._write_rc(1, 0)
234
235 self._xml_end_tag("bk")
236 self._xml_end_tag("cellMetadata")
237
238 def _write_value_metadata(self):
239 # Write the <valueMetadata> element.
240 count = self.num_embedded_images
241 type = 1
242
243 if self.has_dynamic_functions:
244 type = 2
245
246 attributes = [("count", count)]
247
248 self._xml_start_tag("valueMetadata", attributes)
249
250 # Write the rc elements.
251 for index in range(self.num_embedded_images):
252 self._xml_start_tag("bk")
253 self._write_rc(type, index)
254 self._xml_end_tag("bk")
255
256 self._xml_end_tag("valueMetadata")
257
258 def _write_rc(self, type, index):
259 # Write the <rc> element.
260 attributes = [
261 ("t", type),
262 ("v", index),
263 ]
264
265 self._xml_empty_tag("rc", attributes)