Home | History | Annotate | Download | only in misc
      1 """xmlWriter.py -- Simple XML authoring class"""
      2 
      3 from __future__ import print_function, division, absolute_import
      4 from fontTools.misc.py23 import *
      5 import sys
      6 import string
      7 
      8 INDENT = "  "
      9 
     10 
     11 class XMLWriter(object):
     12 	
     13 	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None):
     14 		if not hasattr(fileOrPath, "write"):
     15 			try:
     16 				# Python3 has encoding support.
     17 				self.file = open(fileOrPath, "w", encoding="utf-8")
     18 			except TypeError:
     19 				self.file = open(fileOrPath, "w")
     20 		else:
     21 			# assume writable file object
     22 			self.file = fileOrPath
     23 		self.indentwhite = indentwhite
     24 		self.indentlevel = 0
     25 		self.stack = []
     26 		self.needindent = 1
     27 		self.idlefunc = idlefunc
     28 		self.idlecounter = 0
     29 		self._writeraw('<?xml version="1.0" encoding="utf-8"?>')
     30 		self.newline()
     31 	
     32 	def close(self):
     33 		self.file.close()
     34 	
     35 	def write(self, string, indent=True):
     36 		"""Writes text."""
     37 		self._writeraw(escape(string), indent=indent)
     38 
     39 	def writecdata(self, string):
     40 		"""Writes text in a CDATA section."""
     41 		self._writeraw("<![CDATA[" + string + "]]>")
     42 
     43 	def write8bit(self, data, strip=False):
     44 		"""Writes a bytes() sequence into the XML, escaping
     45 		non-ASCII bytes.  When this is read in xmlReader,
     46 		the original bytes can be recovered by encoding to
     47 		'latin-1'."""
     48 		self._writeraw(escape8bit(data), strip=strip)
     49 
     50 	def write16bit(self, data, strip=False):
     51 		self._writeraw(escape16bit(data), strip=strip)
     52 	
     53 	def write_noindent(self, string):
     54 		"""Writes text without indentation."""
     55 		self._writeraw(escape(string), indent=False)
     56 	
     57 	def _writeraw(self, data, indent=True, strip=False):
     58 		"""Writes bytes, possibly indented."""
     59 		if indent and self.needindent:
     60 			self.file.write(self.indentlevel * self.indentwhite)
     61 			self.needindent = 0
     62 		s = tostr(data, encoding="utf-8")
     63 		if (strip):
     64 			s = s.strip()
     65 		self.file.write(s)
     66 	
     67 	def newline(self):
     68 		self.file.write("\n")
     69 		self.needindent = 1
     70 		idlecounter = self.idlecounter
     71 		if not idlecounter % 100 and self.idlefunc is not None:
     72 			self.idlefunc()
     73 		self.idlecounter = idlecounter + 1
     74 	
     75 	def comment(self, data):
     76 		data = escape(data)
     77 		lines = data.split("\n")
     78 		self._writeraw("<!-- " + lines[0])
     79 		for line in lines[1:]:
     80 			self.newline()
     81 			self._writeraw("     " + line)
     82 		self._writeraw(" -->")
     83 	
     84 	def simpletag(self, _TAG_, *args, **kwargs):
     85 		attrdata = self.stringifyattrs(*args, **kwargs)
     86 		data = "<%s%s/>" % (_TAG_, attrdata)
     87 		self._writeraw(data)
     88 	
     89 	def begintag(self, _TAG_, *args, **kwargs):
     90 		attrdata = self.stringifyattrs(*args, **kwargs)
     91 		data = "<%s%s>" % (_TAG_, attrdata)
     92 		self._writeraw(data)
     93 		self.stack.append(_TAG_)
     94 		self.indent()
     95 	
     96 	def endtag(self, _TAG_):
     97 		assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
     98 		del self.stack[-1]
     99 		self.dedent()
    100 		data = "</%s>" % _TAG_
    101 		self._writeraw(data)
    102 	
    103 	def dumphex(self, data):
    104 		linelength = 16
    105 		hexlinelength = linelength * 2
    106 		chunksize = 8
    107 		for i in range(0, len(data), linelength):
    108 			hexline = hexStr(data[i:i+linelength])
    109 			line = ""
    110 			white = ""
    111 			for j in range(0, hexlinelength, chunksize):
    112 				line = line + white + hexline[j:j+chunksize]
    113 				white = " "
    114 			self._writeraw(line)
    115 			self.newline()
    116 	
    117 	def indent(self):
    118 		self.indentlevel = self.indentlevel + 1
    119 	
    120 	def dedent(self):
    121 		assert self.indentlevel > 0
    122 		self.indentlevel = self.indentlevel - 1
    123 	
    124 	def stringifyattrs(self, *args, **kwargs):
    125 		if kwargs:
    126 			assert not args
    127 			attributes = sorted(kwargs.items())
    128 		elif args:
    129 			assert len(args) == 1
    130 			attributes = args[0]
    131 		else:
    132 			return ""
    133 		data = ""
    134 		for attr, value in attributes:
    135 			data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
    136 		return data
    137 	
    138 
    139 def escape(data):
    140 	data = tostr(data, 'utf-8')
    141 	data = data.replace("&", "&amp;")
    142 	data = data.replace("<", "&lt;")
    143 	data = data.replace(">", "&gt;")
    144 	return data
    145 
    146 def escapeattr(data):
    147 	data = escape(data)
    148 	data = data.replace('"', "&quot;")
    149 	return data
    150 
    151 def escape8bit(data):
    152 	"""Input is Unicode string."""
    153 	def escapechar(c):
    154 		n = ord(c)
    155 		if 32 <= n <= 127 and c not in "<&>":
    156 			return c
    157 		else:
    158 			return "&#" + repr(n) + ";"
    159 	return strjoin(map(escapechar, data.decode('latin-1')))
    160 
    161 def escape16bit(data):
    162 	import array
    163 	a = array.array("H")
    164 	a.fromstring(data)
    165 	if sys.byteorder != "big":
    166 		a.byteswap()
    167 	def escapenum(n, amp=byteord("&"), lt=byteord("<")):
    168 		if n == amp:
    169 			return "&amp;"
    170 		elif n == lt:
    171 			return "&lt;"
    172 		elif 32 <= n <= 127:
    173 			return chr(n)
    174 		else:
    175 			return "&#" + repr(n) + ";"
    176 	return strjoin(map(escapenum, a))
    177 
    178 
    179 def hexStr(s):
    180 	h = string.hexdigits
    181 	r = ''
    182 	for c in s:
    183 		i = byteord(c)
    184 		r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
    185 	return r
    186