# Source code for fontTools.misc.xmlWriter

    """xmlWriter.py -- Simple XML authoring class"""

    from fontTools.misc.textTools import byteord, strjoin, tobytes, tostr
    import sys
    import os
    import string

    INDENT = "  "

    class XMLWriter(object):

    	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf_8",
    			newlinestr="\n"):
    		if encoding.lower().replace('-','').replace('_','') != 'utf8':
    			raise Exception('Only UTF-8 encoding is supported.')
    		if fileOrPath == '-':
    			fileOrPath = sys.stdout
    		if not hasattr(fileOrPath, "write"):
    			self.filename = fileOrPath
    			self.file = open(fileOrPath, "wb")
    			self._closeStream = True
    		else:
    			self.filename = None
    			# assume writable file object
    			self.file = fileOrPath
    			self._closeStream = False

    		# Figure out if writer expects bytes or unicodes
    		try:
    			# The bytes check should be first.  See:
    			# https://github.com/fonttools/fonttools/pull/233
    			self.file.write(b'')
    			self.totype = tobytes
    		except TypeError:
    			# This better not fail.
    			self.file.write('')
    			self.totype = tostr
    		self.indentwhite = self.totype(indentwhite)
    		if newlinestr is None:
    			self.newlinestr = self.totype(os.linesep)
    		else:
    			self.newlinestr = self.totype(newlinestr)
    		self.indentlevel = 0
    		self.stack = []
    		self.needindent = 1
    		self.idlefunc = idlefunc
    		self.idlecounter = 0
    		self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
    		self.newline()

    	def __enter__(self):
    		return self

    	def __exit__(self, exception_type, exception_value, traceback):
    		self.close()

    	def close(self):
    		if self._closeStream:
    			self.file.close()

    	def write(self, string, indent=True):
    		"""Writes text."""
    		self._writeraw(escape(string), indent=indent)

    	def writecdata(self, string):
    		"""Writes text in a CDATA section."""
    		self._writeraw("<![CDATA[" + string + "]]>")

    	def write8bit(self, data, strip=False):
    		"""Writes a bytes() sequence into the XML, escaping
    		non-ASCII bytes.  When this is read in xmlReader,
    		the original bytes can be recovered by encoding to
    		'latin-1'."""
    		self._writeraw(escape8bit(data), strip=strip)

    	def write_noindent(self, string):
    		"""Writes text without indentation."""
    		self._writeraw(escape(string), indent=False)

    	def _writeraw(self, data, indent=True, strip=False):
    		"""Writes bytes, possibly indented."""
    		if indent and self.needindent:
    			self.file.write(self.indentlevel * self.indentwhite)
    			self.needindent = 0
    		s = self.totype(data, encoding="utf_8")
    		if (strip):
    			s = s.strip()
    		self.file.write(s)

    	def newline(self):
    		self.file.write(self.newlinestr)
    		self.needindent = 1
    		idlecounter = self.idlecounter
    		if not idlecounter % 100 and self.idlefunc is not None:
    			self.idlefunc()
    		self.idlecounter = idlecounter + 1

    	def comment(self, data):
    		data = escape(data)
    		lines = data.split("\n")
    		self._writeraw("<!-- " + lines[0])
    		for line in lines[1:]:
    			self.newline()
    			self._writeraw("     " + line)
    		self._writeraw(" -->")

    	def simpletag(self, _TAG_, *args, **kwargs):
    		attrdata = self.stringifyattrs(*args, **kwargs)
    		data = "<%s%s/>" % (_TAG_, attrdata)
    		self._writeraw(data)

    	def begintag(self, _TAG_, *args, **kwargs):
    		attrdata = self.stringifyattrs(*args, **kwargs)
    		data = "<%s%s>" % (_TAG_, attrdata)
    		self._writeraw(data)
    		self.stack.append(_TAG_)
    		self.indent()

    	def endtag(self, _TAG_):
    		assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
    		del self.stack[-1]
    		self.dedent()
    		data = "</%s>" % _TAG_
    		self._writeraw(data)

    	def dumphex(self, data):
    		linelength = 16
    		hexlinelength = linelength * 2
    		chunksize = 8
    		for i in range(0, len(data), linelength):
    			hexline = hexStr(data[i:i+linelength])
    			line = ""
    			white = ""
    			for j in range(0, hexlinelength, chunksize):
    				line = line + white + hexline[j:j+chunksize]
    				white = " "
    			self._writeraw(line)
    			self.newline()

    	def indent(self):
    		self.indentlevel = self.indentlevel + 1

    	def dedent(self):
    		assert self.indentlevel > 0
    		self.indentlevel = self.indentlevel - 1

    	def stringifyattrs(self, *args, **kwargs):
    		if kwargs:
    			assert not args
    			attributes = sorted(kwargs.items())
    		elif args:
    			assert len(args) == 1
    			attributes = args[0]
    		else:
    			return ""
    		data = ""
    		for attr, value in attributes:
    			if not isinstance(value, (bytes, str)):
    				value = str(value)
    			data = data + ' %s="%s"' % (attr, escapeattr(value))
    		return data

    def escape(data):
    	data = tostr(data, 'utf_8')
    	data = data.replace("&", "&amp;")
    	data = data.replace("<", "&lt;")
    	data = data.replace(">", "&gt;")
    	data = data.replace("\r", "&#13;")
    	return data

    def escapeattr(data):
    	data = escape(data)
    	data = data.replace('"', "&quot;")
    	return data

    def escape8bit(data):
    	"""Input is Unicode string."""
    	def escapechar(c):
    		n = ord(c)
    		if 32 <= n <= 127 and c not in "<&>":
    			return c
    		else:
    			return "&#" + repr(n) + ";"
    	return strjoin(map(escapechar, data.decode('latin-1')))

    def hexStr(s):
    	h = string.hexdigits
    	r = ''
    	for c in s:
    		i = byteord(c)
    		r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
    	return r