Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/xlsxwriter/xmlwriter.py: 76%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1###############################################################################
2#
3# XMLwriter - A base class for XlsxWriter classes.
4#
5# Used in conjunction with XlsxWriter.
6#
7# SPDX-License-Identifier: BSD-2-Clause
8# Copyright 2013-2024, John McNamara, jmcnamara@cpan.org
9#
11# Standard packages.
12import re
13from io import StringIO
15# Compile performance critical regular expressions.
16re_control_chars_1 = re.compile("(_x[0-9a-fA-F]{4}_)")
17re_control_chars_2 = re.compile(r"([\x00-\x08\x0b-\x1f])")
18xml_escapes = re.compile('["&<>\n]')
21class XMLwriter(object):
22 """
23 Simple XML writer class.
25 """
27 def __init__(self):
28 self.fh = None
29 self.internal_fh = False
31 def _set_filehandle(self, filehandle):
32 # Set the writer filehandle directly. Mainly for testing.
33 self.fh = filehandle
34 self.internal_fh = False
36 def _set_xml_writer(self, filename):
37 # Set the XML writer filehandle for the object.
38 if isinstance(filename, StringIO):
39 self.internal_fh = False
40 self.fh = filename
41 else:
42 self.internal_fh = True
43 self.fh = open(filename, "w", encoding="utf-8")
45 def _xml_close(self):
46 # Close the XML filehandle if we created it.
47 if self.internal_fh:
48 self.fh.close()
50 def _xml_declaration(self):
51 # Write the XML declaration.
52 self.fh.write("""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n""")
54 def _xml_start_tag(self, tag, attributes=[]):
55 # Write an XML start tag with optional attributes.
56 for key, value in attributes:
57 value = self._escape_attributes(value)
58 tag += ' %s="%s"' % (key, value)
60 self.fh.write("<%s>" % tag)
62 def _xml_start_tag_unencoded(self, tag, attributes=[]):
63 # Write an XML start tag with optional, unencoded, attributes.
64 # This is a minor speed optimization for elements that don't
65 # need encoding.
66 for key, value in attributes:
67 tag += ' %s="%s"' % (key, value)
69 self.fh.write("<%s>" % tag)
71 def _xml_end_tag(self, tag):
72 # Write an XML end tag.
73 self.fh.write("</%s>" % tag)
75 def _xml_empty_tag(self, tag, attributes=[]):
76 # Write an empty XML tag with optional attributes.
77 for key, value in attributes:
78 value = self._escape_attributes(value)
79 tag += ' %s="%s"' % (key, value)
81 self.fh.write("<%s/>" % tag)
83 def _xml_empty_tag_unencoded(self, tag, attributes=[]):
84 # Write an empty XML tag with optional, unencoded, attributes.
85 # This is a minor speed optimization for elements that don't
86 # need encoding.
87 for key, value in attributes:
88 tag += ' %s="%s"' % (key, value)
90 self.fh.write("<%s/>" % tag)
92 def _xml_data_element(self, tag, data, attributes=[]):
93 # Write an XML element containing data with optional attributes.
94 end_tag = tag
96 for key, value in attributes:
97 value = self._escape_attributes(value)
98 tag += ' %s="%s"' % (key, value)
100 data = self._escape_data(data)
101 data = self._escape_control_characters(data)
103 self.fh.write("<%s>%s</%s>" % (tag, data, end_tag))
105 def _xml_string_element(self, index, attributes=[]):
106 # Optimized tag writer for <c> cell string elements in the inner loop.
107 attr = ""
109 for key, value in attributes:
110 value = self._escape_attributes(value)
111 attr += ' %s="%s"' % (key, value)
113 self.fh.write("""<c%s t="s"><v>%d</v></c>""" % (attr, index))
115 def _xml_si_element(self, string, attributes=[]):
116 # Optimized tag writer for shared strings <si> elements.
117 attr = ""
119 for key, value in attributes:
120 value = self._escape_attributes(value)
121 attr += ' %s="%s"' % (key, value)
123 string = self._escape_data(string)
125 self.fh.write("""<si><t%s>%s</t></si>""" % (attr, string))
127 def _xml_rich_si_element(self, string):
128 # Optimized tag writer for shared strings <si> rich string elements.
130 self.fh.write("""<si>%s</si>""" % string)
132 def _xml_number_element(self, number, attributes=[]):
133 # Optimized tag writer for <c> cell number elements in the inner loop.
134 attr = ""
136 for key, value in attributes:
137 value = self._escape_attributes(value)
138 attr += ' %s="%s"' % (key, value)
140 self.fh.write("""<c%s><v>%.16G</v></c>""" % (attr, number))
142 def _xml_formula_element(self, formula, result, attributes=[]):
143 # Optimized tag writer for <c> cell formula elements in the inner loop.
144 attr = ""
146 for key, value in attributes:
147 value = self._escape_attributes(value)
148 attr += ' %s="%s"' % (key, value)
150 self.fh.write(
151 """<c%s><f>%s</f><v>%s</v></c>"""
152 % (attr, self._escape_data(formula), self._escape_data(result))
153 )
155 def _xml_inline_string(self, string, preserve, attributes=[]):
156 # Optimized tag writer for inlineStr cell elements in the inner loop.
157 attr = ""
158 t_attr = ""
160 # Set the <t> attribute to preserve whitespace.
161 if preserve:
162 t_attr = ' xml:space="preserve"'
164 for key, value in attributes:
165 value = self._escape_attributes(value)
166 attr += ' %s="%s"' % (key, value)
168 string = self._escape_data(string)
170 self.fh.write(
171 """<c%s t="inlineStr"><is><t%s>%s</t></is></c>""" % (attr, t_attr, string)
172 )
174 def _xml_rich_inline_string(self, string, attributes=[]):
175 # Optimized tag writer for rich inlineStr in the inner loop.
176 attr = ""
178 for key, value in attributes:
179 value = self._escape_attributes(value)
180 attr += ' %s="%s"' % (key, value)
182 self.fh.write("""<c%s t="inlineStr"><is>%s</is></c>""" % (attr, string))
184 def _escape_attributes(self, attribute):
185 # Escape XML characters in attributes.
186 try:
187 if not xml_escapes.search(attribute):
188 return attribute
189 except TypeError:
190 return attribute
192 attribute = (
193 attribute.replace("&", "&")
194 .replace('"', """)
195 .replace("<", "<")
196 .replace(">", ">")
197 .replace("\n", "
")
198 )
199 return attribute
201 def _escape_data(self, data):
202 # Escape XML characters in data sections of tags. Note, this
203 # is different from _escape_attributes() in that double quotes
204 # are not escaped by Excel.
205 try:
206 if not xml_escapes.search(data):
207 return data
208 except TypeError:
209 return data
211 data = data.replace("&", "&").replace("<", "<").replace(">", ">")
212 return data
214 @staticmethod
215 def _escape_control_characters(data):
216 # Excel escapes control characters with _xHHHH_ and also escapes any
217 # literal strings of that type by encoding the leading underscore.
218 # So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_.
219 # The following substitutions deal with those cases.
220 try:
221 # Escape the escape.
222 data = re_control_chars_1.sub(r"_x005F\1", data)
223 except TypeError:
224 return data
226 # Convert control character to the _xHHHH_ escape.
227 data = re_control_chars_2.sub(
228 lambda match: "_x%04X_" % ord(match.group(1)), data
229 )
231 # Escapes non characters in strings.
232 data = data.replace("\uFFFE", "_xFFFE_").replace("\uFFFF", "_xFFFF_")
234 return data