blob: 4efd3f663e064244fd7db74cac32fb70aafc9a9c [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""xmlWriter.py -- Simple XML authoring class"""
2
Behdad Esfahbod32c10ee2013-11-27 17:46:17 -05003from __future__ import print_function, division
Behdad Esfahbod30e691e2013-11-27 17:27:45 -05004from fontTools.misc.py23 import *
Just7842e561999-12-16 21:34:53 +00005import string
6import struct
7
8INDENT = " "
9
jvr81b0c2b2002-09-09 18:17:12 +000010
Just7842e561999-12-16 21:34:53 +000011class XMLWriter:
12
Behdad Esfahbod4b3df492013-08-20 15:29:19 -040013 def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
jvr90beb952005-01-17 21:34:06 +000014 if not hasattr(fileOrPath, "write"):
15 self.file = open(fileOrPath, "w")
Just7842e561999-12-16 21:34:53 +000016 else:
17 # assume writable file object
jvr90beb952005-01-17 21:34:06 +000018 self.file = fileOrPath
Just7842e561999-12-16 21:34:53 +000019 self.indentwhite = indentwhite
20 self.indentlevel = 0
21 self.stack = []
22 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000023 self.idlefunc = idlefunc
24 self.idlecounter = 0
jvr81b0c2b2002-09-09 18:17:12 +000025 if encoding:
26 self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
27 else:
28 self.writeraw('<?xml version="1.0"?>')
Just7842e561999-12-16 21:34:53 +000029 self.newline()
Just7842e561999-12-16 21:34:53 +000030
31 def close(self):
32 self.file.close()
33
34 def write(self, data):
35 self.writeraw(escape(data))
36
37 def write_noindent(self, data):
38 self.file.write(escape(data))
39
40 def write8bit(self, data):
41 self.writeraw(escape8bit(data))
42
43 def write16bit(self, data):
44 self.writeraw(escape16bit(data))
45
46 def writeraw(self, data):
47 if self.needindent:
48 self.file.write(self.indentlevel * self.indentwhite)
49 self.needindent = 0
50 self.file.write(data)
51
52 def newline(self):
53 self.file.write("\n")
54 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000055 idlecounter = self.idlecounter
56 if not idlecounter % 100 and self.idlefunc is not None:
57 self.idlefunc()
58 self.idlecounter = idlecounter + 1
Just7842e561999-12-16 21:34:53 +000059
60 def comment(self, data):
61 data = escape(data)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -050062 lines = data.split("\n")
Just7842e561999-12-16 21:34:53 +000063 self.writeraw("<!-- " + lines[0])
64 for line in lines[1:]:
65 self.newline()
66 self.writeraw(" " + line)
67 self.writeraw(" -->")
68
69 def simpletag(self, _TAG_, *args, **kwargs):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050070 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000071 data = "<%s%s/>" % (_TAG_, attrdata)
72 self.writeraw(data)
73
74 def begintag(self, _TAG_, *args, **kwargs):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050075 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000076 data = "<%s%s>" % (_TAG_, attrdata)
77 self.writeraw(data)
78 self.stack.append(_TAG_)
79 self.indent()
80
81 def endtag(self, _TAG_):
82 assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
83 del self.stack[-1]
84 self.dedent()
85 data = "</%s>" % _TAG_
86 self.writeraw(data)
87
88 def dumphex(self, data):
89 linelength = 16
90 hexlinelength = linelength * 2
91 chunksize = 8
92 for i in range(0, len(data), linelength):
93 hexline = hexStr(data[i:i+linelength])
94 line = ""
95 white = ""
96 for j in range(0, hexlinelength, chunksize):
97 line = line + white + hexline[j:j+chunksize]
98 white = " "
99 self.writeraw(line)
100 self.newline()
101
102 def indent(self):
103 self.indentlevel = self.indentlevel + 1
104
105 def dedent(self):
106 assert self.indentlevel > 0
107 self.indentlevel = self.indentlevel - 1
108
109 def stringifyattrs(self, *args, **kwargs):
110 if kwargs:
111 assert not args
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500112 attributes = sorted(kwargs.items())
Just7842e561999-12-16 21:34:53 +0000113 elif args:
114 assert len(args) == 1
115 attributes = args[0]
116 else:
117 return ""
118 data = ""
119 for attr, value in attributes:
120 data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
121 return data
122
123
124def escape(data):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500125 data = data.replace("&", "&amp;")
126 data = data.replace("<", "&lt;")
Just7842e561999-12-16 21:34:53 +0000127 return data
128
129def escapeattr(data):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500130 data = data.replace("&", "&amp;")
131 data = data.replace("<", "&lt;")
132 data = data.replace('"', "&quot;")
Just7842e561999-12-16 21:34:53 +0000133 return data
134
135def escape8bit(data):
136 def escapechar(c):
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500137 n = byteord(c)
Just7842e561999-12-16 21:34:53 +0000138 if c in "<&":
139 if c == "&":
140 return "&amp;"
141 else:
142 return "&lt;"
143 elif 32 <= n <= 127:
144 return c
145 else:
Behdad Esfahboddc7e6f32013-11-27 02:44:56 -0500146 return "&#" + repr(n) + ";"
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500147 return "".join(map(escapechar, data))
Just7842e561999-12-16 21:34:53 +0000148
149needswap = struct.pack("h", 1) == "\001\000"
150
151def escape16bit(data):
152 import array
153 a = array.array("H")
154 a.fromstring(data)
155 if needswap:
156 a.byteswap()
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500157 def escapenum(n, amp=byteord("&"), lt=byteord("<")):
Just7842e561999-12-16 21:34:53 +0000158 if n == amp:
159 return "&amp;"
160 elif n == lt:
161 return "&lt;"
162 elif 32 <= n <= 127:
Behdad Esfahbodb7a2d792013-11-27 15:19:40 -0500163 return bytechr(n)
Just7842e561999-12-16 21:34:53 +0000164 else:
Behdad Esfahboddc7e6f32013-11-27 02:44:56 -0500165 return "&#" + repr(n) + ";"
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500166 return "".join(map(escapenum, a))
Just7842e561999-12-16 21:34:53 +0000167
168
169def hexStr(s):
170 h = string.hexdigits
171 r = ''
172 for c in s:
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500173 i = byteord(c)
Just7842e561999-12-16 21:34:53 +0000174 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
175 return r
176