blob: a1ba65fa9a949e41c588deb6d744b2e8b6b17dd4 [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 *
Behdad Esfahbod5cf40082013-11-27 19:51:59 -05005import sys
Just7842e561999-12-16 21:34:53 +00006import string
7import struct
8
9INDENT = " "
10
jvr81b0c2b2002-09-09 18:17:12 +000011
Just7842e561999-12-16 21:34:53 +000012class XMLWriter:
13
Behdad Esfahbod4b3df492013-08-20 15:29:19 -040014 def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
jvr90beb952005-01-17 21:34:06 +000015 if not hasattr(fileOrPath, "write"):
16 self.file = open(fileOrPath, "w")
Just7842e561999-12-16 21:34:53 +000017 else:
18 # assume writable file object
jvr90beb952005-01-17 21:34:06 +000019 self.file = fileOrPath
Just7842e561999-12-16 21:34:53 +000020 self.indentwhite = indentwhite
21 self.indentlevel = 0
22 self.stack = []
23 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000024 self.idlefunc = idlefunc
25 self.idlecounter = 0
jvr81b0c2b2002-09-09 18:17:12 +000026 if encoding:
27 self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
28 else:
29 self.writeraw('<?xml version="1.0"?>')
Just7842e561999-12-16 21:34:53 +000030 self.newline()
Just7842e561999-12-16 21:34:53 +000031
32 def close(self):
33 self.file.close()
34
35 def write(self, data):
36 self.writeraw(escape(data))
37
38 def write_noindent(self, data):
39 self.file.write(escape(data))
40
41 def write8bit(self, data):
42 self.writeraw(escape8bit(data))
43
44 def write16bit(self, data):
45 self.writeraw(escape16bit(data))
46
47 def writeraw(self, data):
48 if self.needindent:
49 self.file.write(self.indentlevel * self.indentwhite)
50 self.needindent = 0
51 self.file.write(data)
52
53 def newline(self):
54 self.file.write("\n")
55 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000056 idlecounter = self.idlecounter
57 if not idlecounter % 100 and self.idlefunc is not None:
58 self.idlefunc()
59 self.idlecounter = idlecounter + 1
Just7842e561999-12-16 21:34:53 +000060
61 def comment(self, data):
62 data = escape(data)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -050063 lines = data.split("\n")
Just7842e561999-12-16 21:34:53 +000064 self.writeraw("<!-- " + lines[0])
65 for line in lines[1:]:
66 self.newline()
67 self.writeraw(" " + line)
68 self.writeraw(" -->")
69
70 def simpletag(self, _TAG_, *args, **kwargs):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050071 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000072 data = "<%s%s/>" % (_TAG_, attrdata)
73 self.writeraw(data)
74
75 def begintag(self, _TAG_, *args, **kwargs):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050076 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000077 data = "<%s%s>" % (_TAG_, attrdata)
78 self.writeraw(data)
79 self.stack.append(_TAG_)
80 self.indent()
81
82 def endtag(self, _TAG_):
83 assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
84 del self.stack[-1]
85 self.dedent()
86 data = "</%s>" % _TAG_
87 self.writeraw(data)
88
89 def dumphex(self, data):
90 linelength = 16
91 hexlinelength = linelength * 2
92 chunksize = 8
93 for i in range(0, len(data), linelength):
94 hexline = hexStr(data[i:i+linelength])
95 line = ""
96 white = ""
97 for j in range(0, hexlinelength, chunksize):
98 line = line + white + hexline[j:j+chunksize]
99 white = " "
100 self.writeraw(line)
101 self.newline()
102
103 def indent(self):
104 self.indentlevel = self.indentlevel + 1
105
106 def dedent(self):
107 assert self.indentlevel > 0
108 self.indentlevel = self.indentlevel - 1
109
110 def stringifyattrs(self, *args, **kwargs):
111 if kwargs:
112 assert not args
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500113 attributes = sorted(kwargs.items())
Just7842e561999-12-16 21:34:53 +0000114 elif args:
115 assert len(args) == 1
116 attributes = args[0]
117 else:
118 return ""
119 data = ""
120 for attr, value in attributes:
121 data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
122 return data
123
124
125def escape(data):
Behdad Esfahbod5cf40082013-11-27 19:51:59 -0500126 data = tostr(data)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500127 data = data.replace("&", "&amp;")
128 data = data.replace("<", "&lt;")
Just7842e561999-12-16 21:34:53 +0000129 return data
130
131def escapeattr(data):
Behdad Esfahbod5cf40082013-11-27 19:51:59 -0500132 data = escape(data)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500133 data = data.replace('"', "&quot;")
Just7842e561999-12-16 21:34:53 +0000134 return data
135
136def escape8bit(data):
Behdad Esfahbod5cf40082013-11-27 19:51:59 -0500137 data = tostr(data)
Just7842e561999-12-16 21:34:53 +0000138 def escapechar(c):
Behdad Esfahbod319c5fd2013-11-27 18:13:48 -0500139 n = byteord(c)
Just7842e561999-12-16 21:34:53 +0000140 if c in "<&":
141 if c == "&":
142 return "&amp;"
143 else:
144 return "&lt;"
145 elif 32 <= n <= 127:
146 return c
147 else:
Behdad Esfahboddc7e6f32013-11-27 02:44:56 -0500148 return "&#" + repr(n) + ";"
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500149 return "".join(map(escapechar, data))
Just7842e561999-12-16 21:34:53 +0000150
Just7842e561999-12-16 21:34:53 +0000151def escape16bit(data):
152 import array
153 a = array.array("H")
154 a.fromstring(data)
Behdad Esfahbod5cf40082013-11-27 19:51:59 -0500155 if sys.byteorder != "big":
Just7842e561999-12-16 21:34:53 +0000156 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 Esfahbod5cf40082013-11-27 19:51:59 -0500163 return chr(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