17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""xmlWriter.py -- Simple XML authoring class""" 27842e56b97ce677b83bdab09cda48bc2d89ac75aJust 31ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 430e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 55cf40083364e1d2dce119de25cb42ce69d2fb53cBehdad Esfahbodimport sys 67842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string 77842e56b97ce677b83bdab09cda48bc2d89ac75aJust 87842e56b97ce677b83bdab09cda48bc2d89ac75aJustINDENT = " " 97842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1081b0c2b659aec8df184d09c0d7ab069956b87b28jvr 11e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass XMLWriter(object): 127842e56b97ce677b83bdab09cda48bc2d89ac75aJust 136962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None): 1490beb95b77fdef99cad25354f36bf615d2042197jvr if not hasattr(fileOrPath, "write"): 156962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod try: 166962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod # Python3 has encoding support. 176962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self.file = open(fileOrPath, "w", encoding="utf-8") 18c40e26ec4c40f3e9c60248d1663b8bedd7d706f2Behdad Esfahbod except TypeError: 19c40e26ec4c40f3e9c60248d1663b8bedd7d706f2Behdad Esfahbod self.file = open(fileOrPath, "w") 207842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 217842e56b97ce677b83bdab09cda48bc2d89ac75aJust # assume writable file object 2290beb95b77fdef99cad25354f36bf615d2042197jvr self.file = fileOrPath 237842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.indentwhite = indentwhite 247842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.indentlevel = 0 257842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.stack = [] 267842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.needindent = 1 2733f3327ad74d4550ee48ce85ce52a7374e55b997jvr self.idlefunc = idlefunc 2833f3327ad74d4550ee48ce85ce52a7374e55b997jvr self.idlecounter = 0 296962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw('<?xml version="1.0" encoding="utf-8"?>') 307842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.newline() 317842e56b97ce677b83bdab09cda48bc2d89ac75aJust 327842e56b97ce677b83bdab09cda48bc2d89ac75aJust def close(self): 337842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.file.close() 347842e56b97ce677b83bdab09cda48bc2d89ac75aJust 356962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod def write(self, string, indent=True): 366962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Writes text.""" 376962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(escape(string), indent=indent) 386962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod 396962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod def writecdata(self, string): 406962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Writes text in a CDATA section.""" 416962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw("<![CDATA[" + string + "]]>") 426962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod 431edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod def write8bit(self, data, strip=False): 446962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Writes a bytes() sequence into the XML, escaping 456962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod non-ASCII bytes. When this is read in xmlReader, 466962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod the original bytes can be recovered by encoding to 47ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod 'latin-1'.""" 481edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod self._writeraw(escape8bit(data), strip=strip) 49ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod 501edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod def write16bit(self, data, strip=False): 511edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod self._writeraw(escape16bit(data), strip=strip) 526962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod 536962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod def write_noindent(self, string): 546962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Writes text without indentation.""" 556962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(escape(string), indent=False) 566962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod 571edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod def _writeraw(self, data, indent=True, strip=False): 586962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Writes bytes, possibly indented.""" 596962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod if indent and self.needindent: 607842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.file.write(self.indentlevel * self.indentwhite) 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.needindent = 0 621edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod s = tostr(data, encoding="utf-8") 631edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod if (strip): 641edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod s = s.strip() 651edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod self.file.write(s) 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust def newline(self): 687842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.file.write("\n") 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.needindent = 1 7033f3327ad74d4550ee48ce85ce52a7374e55b997jvr idlecounter = self.idlecounter 7133f3327ad74d4550ee48ce85ce52a7374e55b997jvr if not idlecounter % 100 and self.idlefunc is not None: 7233f3327ad74d4550ee48ce85ce52a7374e55b997jvr self.idlefunc() 7333f3327ad74d4550ee48ce85ce52a7374e55b997jvr self.idlecounter = idlecounter + 1 747842e56b97ce677b83bdab09cda48bc2d89ac75aJust 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust def comment(self, data): 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = escape(data) 7714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod lines = data.split("\n") 786962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw("<!-- " + lines[0]) 797842e56b97ce677b83bdab09cda48bc2d89ac75aJust for line in lines[1:]: 807842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.newline() 816962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(" " + line) 826962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(" -->") 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust def simpletag(self, _TAG_, *args, **kwargs): 8566214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod attrdata = self.stringifyattrs(*args, **kwargs) 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = "<%s%s/>" % (_TAG_, attrdata) 876962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(data) 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust 897842e56b97ce677b83bdab09cda48bc2d89ac75aJust def begintag(self, _TAG_, *args, **kwargs): 9066214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod attrdata = self.stringifyattrs(*args, **kwargs) 917842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = "<%s%s>" % (_TAG_, attrdata) 926962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(data) 937842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.stack.append(_TAG_) 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.indent() 957842e56b97ce677b83bdab09cda48bc2d89ac75aJust 967842e56b97ce677b83bdab09cda48bc2d89ac75aJust def endtag(self, _TAG_): 977842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag" 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust del self.stack[-1] 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.dedent() 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = "</%s>" % _TAG_ 1016962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(data) 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust def dumphex(self, data): 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust linelength = 16 1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust hexlinelength = linelength * 2 1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust chunksize = 8 1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(0, len(data), linelength): 1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust hexline = hexStr(data[i:i+linelength]) 1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust line = "" 1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust white = "" 1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust for j in range(0, hexlinelength, chunksize): 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust line = line + white + hexline[j:j+chunksize] 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust white = " " 1146962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod self._writeraw(line) 1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.newline() 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust def indent(self): 1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.indentlevel = self.indentlevel + 1 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust def dedent(self): 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert self.indentlevel > 0 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.indentlevel = self.indentlevel - 1 1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust def stringifyattrs(self, *args, **kwargs): 1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust if kwargs: 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert not args 127ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod attributes = sorted(kwargs.items()) 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust elif args: 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust assert len(args) == 1 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust attributes = args[0] 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust return "" 1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = "" 1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust for attr, value in attributes: 1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data + ' %s="%s"' % (attr, escapeattr(str(value))) 1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1397842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escape(data): 1406962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod data = tostr(data, 'utf-8') 14114fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod data = data.replace("&", "&") 14214fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod data = data.replace("<", "<") 1436962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod data = data.replace(">", ">") 1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1467842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escapeattr(data): 1475cf40083364e1d2dce119de25cb42ce69d2fb53cBehdad Esfahbod data = escape(data) 14814fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod data = data.replace('"', """) 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escape8bit(data): 1526962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod """Input is Unicode string.""" 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust def escapechar(c): 1546962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod n = ord(c) 1556962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod if 32 <= n <= 127 and c not in "<&>": 1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust return c 1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 158dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod return "&#" + repr(n) + ";" 159ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return strjoin(map(escapechar, data.decode('latin-1'))) 160ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod 161ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahboddef escape16bit(data): 162ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod import array 163ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod a = array.array("H") 164ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod a.fromstring(data) 165ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod if sys.byteorder != "big": 166ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod a.byteswap() 167ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod def escapenum(n, amp=byteord("&"), lt=byteord("<")): 168ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod if n == amp: 169ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return "&" 170ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod elif n == lt: 171ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return "<" 172ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod elif 32 <= n <= 127: 173ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return chr(n) 174ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod else: 175ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return "&#" + repr(n) + ";" 176ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod return strjoin(map(escapenum, a)) 177ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod 1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1797842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef hexStr(s): 1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust h = string.hexdigits 1817842e56b97ce677b83bdab09cda48bc2d89ac75aJust r = '' 1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust for c in s: 183319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod i = byteord(c) 1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust r = r + h[(i >> 4) & 0xF] + h[i & 0xF] 1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust return r 186