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

119 statements  

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# 

10 

11# Standard packages. 

12import re 

13from io import StringIO 

14 

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]') 

19 

20 

21class XMLwriter(object): 

22 """ 

23 Simple XML writer class. 

24 

25 """ 

26 

27 def __init__(self): 

28 self.fh = None 

29 self.internal_fh = False 

30 

31 def _set_filehandle(self, filehandle): 

32 # Set the writer filehandle directly. Mainly for testing. 

33 self.fh = filehandle 

34 self.internal_fh = False 

35 

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") 

44 

45 def _xml_close(self): 

46 # Close the XML filehandle if we created it. 

47 if self.internal_fh: 

48 self.fh.close() 

49 

50 def _xml_declaration(self): 

51 # Write the XML declaration. 

52 self.fh.write("""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n""") 

53 

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) 

59 

60 self.fh.write("<%s>" % tag) 

61 

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) 

68 

69 self.fh.write("<%s>" % tag) 

70 

71 def _xml_end_tag(self, tag): 

72 # Write an XML end tag. 

73 self.fh.write("</%s>" % tag) 

74 

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) 

80 

81 self.fh.write("<%s/>" % tag) 

82 

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) 

89 

90 self.fh.write("<%s/>" % tag) 

91 

92 def _xml_data_element(self, tag, data, attributes=[]): 

93 # Write an XML element containing data with optional attributes. 

94 end_tag = tag 

95 

96 for key, value in attributes: 

97 value = self._escape_attributes(value) 

98 tag += ' %s="%s"' % (key, value) 

99 

100 data = self._escape_data(data) 

101 data = self._escape_control_characters(data) 

102 

103 self.fh.write("<%s>%s</%s>" % (tag, data, end_tag)) 

104 

105 def _xml_string_element(self, index, attributes=[]): 

106 # Optimized tag writer for <c> cell string elements in the inner loop. 

107 attr = "" 

108 

109 for key, value in attributes: 

110 value = self._escape_attributes(value) 

111 attr += ' %s="%s"' % (key, value) 

112 

113 self.fh.write("""<c%s t="s"><v>%d</v></c>""" % (attr, index)) 

114 

115 def _xml_si_element(self, string, attributes=[]): 

116 # Optimized tag writer for shared strings <si> elements. 

117 attr = "" 

118 

119 for key, value in attributes: 

120 value = self._escape_attributes(value) 

121 attr += ' %s="%s"' % (key, value) 

122 

123 string = self._escape_data(string) 

124 

125 self.fh.write("""<si><t%s>%s</t></si>""" % (attr, string)) 

126 

127 def _xml_rich_si_element(self, string): 

128 # Optimized tag writer for shared strings <si> rich string elements. 

129 

130 self.fh.write("""<si>%s</si>""" % string) 

131 

132 def _xml_number_element(self, number, attributes=[]): 

133 # Optimized tag writer for <c> cell number elements in the inner loop. 

134 attr = "" 

135 

136 for key, value in attributes: 

137 value = self._escape_attributes(value) 

138 attr += ' %s="%s"' % (key, value) 

139 

140 self.fh.write("""<c%s><v>%.16G</v></c>""" % (attr, number)) 

141 

142 def _xml_formula_element(self, formula, result, attributes=[]): 

143 # Optimized tag writer for <c> cell formula elements in the inner loop. 

144 attr = "" 

145 

146 for key, value in attributes: 

147 value = self._escape_attributes(value) 

148 attr += ' %s="%s"' % (key, value) 

149 

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 ) 

154 

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 = "" 

159 

160 # Set the <t> attribute to preserve whitespace. 

161 if preserve: 

162 t_attr = ' xml:space="preserve"' 

163 

164 for key, value in attributes: 

165 value = self._escape_attributes(value) 

166 attr += ' %s="%s"' % (key, value) 

167 

168 string = self._escape_data(string) 

169 

170 self.fh.write( 

171 """<c%s t="inlineStr"><is><t%s>%s</t></is></c>""" % (attr, t_attr, string) 

172 ) 

173 

174 def _xml_rich_inline_string(self, string, attributes=[]): 

175 # Optimized tag writer for rich inlineStr in the inner loop. 

176 attr = "" 

177 

178 for key, value in attributes: 

179 value = self._escape_attributes(value) 

180 attr += ' %s="%s"' % (key, value) 

181 

182 self.fh.write("""<c%s t="inlineStr"><is>%s</is></c>""" % (attr, string)) 

183 

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 

191 

192 attribute = ( 

193 attribute.replace("&", "&amp;") 

194 .replace('"', "&quot;") 

195 .replace("<", "&lt;") 

196 .replace(">", "&gt;") 

197 .replace("\n", "&#xA;") 

198 ) 

199 return attribute 

200 

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 

210 

211 data = data.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;") 

212 return data 

213 

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 

225 

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 ) 

230 

231 # Escapes non characters in strings. 

232 data = data.replace("\uFFFE", "_xFFFE_").replace("\uFFFF", "_xFFFF_") 

233 

234 return data