Home | History | Annotate | Download | only in misc
      1 from __future__ import print_function, division, absolute_import
      2 from fontTools.misc.py23 import *
      3 from fontTools import ttLib
      4 from fontTools.misc.textTools import safeEval
      5 from fontTools.ttLib.tables.DefaultTable import DefaultTable
      6 import sys
      7 import os
      8 import logging
      9 
     10 
     11 log = logging.getLogger(__name__)
     12 
     13 class TTXParseError(Exception): pass
     14 
     15 BUFSIZE = 0x4000
     16 
     17 
     18 class XMLReader(object):
     19 
     20 	def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False):
     21 		if fileOrPath == '-':
     22 			fileOrPath = sys.stdin
     23 		if not hasattr(fileOrPath, "read"):
     24 			self.file = open(fileOrPath, "rb")
     25 			self._closeStream = True
     26 		else:
     27 			# assume readable file object
     28 			self.file = fileOrPath
     29 			self._closeStream = False
     30 		self.ttFont = ttFont
     31 		self.progress = progress
     32 		if quiet is not None:
     33 			from fontTools.misc.loggingTools import deprecateArgument
     34 			deprecateArgument("quiet", "configure logging instead")
     35 			self.quiet = quiet
     36 		self.root = None
     37 		self.contentStack = []
     38 		self.contentOnly = contentOnly
     39 		self.stackSize = 0
     40 
     41 	def read(self, rootless=False):
     42 		if rootless:
     43 			self.stackSize += 1
     44 		if self.progress:
     45 			self.file.seek(0, 2)
     46 			fileSize = self.file.tell()
     47 			self.progress.set(0, fileSize // 100 or 1)
     48 			self.file.seek(0)
     49 		self._parseFile(self.file)
     50 		if self._closeStream:
     51 			self.close()
     52 		if rootless:
     53 			self.stackSize -= 1
     54 
     55 	def close(self):
     56 		self.file.close()
     57 
     58 	def _parseFile(self, file):
     59 		from xml.parsers.expat import ParserCreate
     60 		parser = ParserCreate()
     61 		parser.StartElementHandler = self._startElementHandler
     62 		parser.EndElementHandler = self._endElementHandler
     63 		parser.CharacterDataHandler = self._characterDataHandler
     64 
     65 		pos = 0
     66 		while True:
     67 			chunk = file.read(BUFSIZE)
     68 			if not chunk:
     69 				parser.Parse(chunk, 1)
     70 				break
     71 			pos = pos + len(chunk)
     72 			if self.progress:
     73 				self.progress.set(pos // 100)
     74 			parser.Parse(chunk, 0)
     75 
     76 	def _startElementHandler(self, name, attrs):
     77 		if self.stackSize == 1 and self.contentOnly:
     78 			# We already know the table we're parsing, skip
     79 			# parsing the table tag and continue to
     80 			# stack '2' which begins parsing content
     81 			self.contentStack.append([])
     82 			self.stackSize = 2
     83 			return
     84 		stackSize = self.stackSize
     85 		self.stackSize = stackSize + 1
     86 		subFile = attrs.get("src")
     87 		if subFile is not None:
     88 			if hasattr(self.file, 'name'):
     89 				# if file has a name, get its parent directory
     90 				dirname = os.path.dirname(self.file.name)
     91 			else:
     92 				# else fall back to using the current working directory
     93 				dirname = os.getcwd()
     94 			subFile = os.path.join(dirname, subFile)
     95 		if not stackSize:
     96 			if name != "ttFont":
     97 				raise TTXParseError("illegal root tag: %s" % name)
     98 			sfntVersion = attrs.get("sfntVersion")
     99 			if sfntVersion is not None:
    100 				if len(sfntVersion) != 4:
    101 					sfntVersion = safeEval('"' + sfntVersion + '"')
    102 				self.ttFont.sfntVersion = sfntVersion
    103 			self.contentStack.append([])
    104 		elif stackSize == 1:
    105 			if subFile is not None:
    106 				subReader = XMLReader(subFile, self.ttFont, self.progress)
    107 				subReader.read()
    108 				self.contentStack.append([])
    109 				return
    110 			tag = ttLib.xmlToTag(name)
    111 			msg = "Parsing '%s' table..." % tag
    112 			if self.progress:
    113 				self.progress.setLabel(msg)
    114 			log.info(msg)
    115 			if tag == "GlyphOrder":
    116 				tableClass = ttLib.GlyphOrder
    117 			elif "ERROR" in attrs or ('raw' in attrs and safeEval(attrs['raw'])):
    118 				tableClass = DefaultTable
    119 			else:
    120 				tableClass = ttLib.getTableClass(tag)
    121 				if tableClass is None:
    122 					tableClass = DefaultTable
    123 			if tag == 'loca' and tag in self.ttFont:
    124 				# Special-case the 'loca' table as we need the
    125 				#    original if the 'glyf' table isn't recompiled.
    126 				self.currentTable = self.ttFont[tag]
    127 			else:
    128 				self.currentTable = tableClass(tag)
    129 				self.ttFont[tag] = self.currentTable
    130 			self.contentStack.append([])
    131 		elif stackSize == 2 and subFile is not None:
    132 			subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
    133 			subReader.read()
    134 			self.contentStack.append([])
    135 			self.root = subReader.root
    136 		elif stackSize == 2:
    137 			self.contentStack.append([])
    138 			self.root = (name, attrs, self.contentStack[-1])
    139 		else:
    140 			l = []
    141 			self.contentStack[-1].append((name, attrs, l))
    142 			self.contentStack.append(l)
    143 
    144 	def _characterDataHandler(self, data):
    145 		if self.stackSize > 1:
    146 			self.contentStack[-1].append(data)
    147 
    148 	def _endElementHandler(self, name):
    149 		self.stackSize = self.stackSize - 1
    150 		del self.contentStack[-1]
    151 		if not self.contentOnly:
    152 			if self.stackSize == 1:
    153 				self.root = None
    154 			elif self.stackSize == 2:
    155 				name, attrs, content = self.root
    156 				self.currentTable.fromXML(name, attrs, content, self.ttFont)
    157 				self.root = None
    158 
    159 
    160 class ProgressPrinter(object):
    161 
    162 	def __init__(self, title, maxval=100):
    163 		print(title)
    164 
    165 	def set(self, val, maxval=None):
    166 		pass
    167 
    168 	def increment(self, val=1):
    169 		pass
    170 
    171 	def setLabel(self, text):
    172 		print(text)
    173