xmlReader.py revision 30e691edd056ba22fa8970280e986747817bec3d
130e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom __future__ import print_function
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
37842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools import ttLib
47842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval
5f8fd4777d273836a1222b72f6761cb6fdf9ec87aJustfrom fontTools.ttLib.tables.DefaultTable import DefaultTable
64bb05c6ad4b112de40642d77663aa87d145f863fjvrimport os
77842e56b97ce677b83bdab09cda48bc2d89ac75aJust
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvrclass TTXParseError(Exception): pass
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
119b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvrBUFSIZE = 0x4000
129b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust
143ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbodclass XMLReader:
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust
163ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def __init__(self, fileName, ttFont, progress=None, quiet=False):
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.ttFont = ttFont
184bb05c6ad4b112de40642d77663aa87d145f863fjvr		self.fileName = fileName
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.progress = progress
2085af40e7d8c4f4f4a3df3562a5ec9e5731cfe43fDave Crossland		self.quiet = quiet
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.root = None
22ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.contentStack = []
23ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.stackSize = 0
24ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr
253ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def read(self):
263ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		if self.progress:
273ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod			import stat
283ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod			self.progress.set(0, os.stat(fileName)[stat.ST_SIZE] / 100 or 1)
2956bfea43176d03cede26f8de51638c4ab9f4bd64jvr		file = open(self.fileName)
303ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		self._parseFile(file)
3156bfea43176d03cede26f8de51638c4ab9f4bd64jvr		file.close()
3256bfea43176d03cede26f8de51638c4ab9f4bd64jvr
333ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def _parseFile(self, file):
34e493669d196f292f24d4c09ce340c3e67749bce5jvr		from xml.parsers.expat import ParserCreate
354b3df49b5b70b3618e91e3ab954cedca773831d6Behdad Esfahbod		parser = ParserCreate()
36e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser.returns_unicode = 0
373ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		parser.StartElementHandler = self._startElementHandler
383ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		parser.EndElementHandler = self._endElementHandler
393ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		parser.CharacterDataHandler = self._characterDataHandler
40e493669d196f292f24d4c09ce340c3e67749bce5jvr
419b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr		pos = 0
42ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		while True:
439b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			chunk = file.read(BUFSIZE)
449b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			if not chunk:
459b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				parser.Parse(chunk, 1)
469b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				break
479b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			pos = pos + len(chunk)
489b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			if self.progress:
499b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				self.progress.set(pos / 100)
509b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			parser.Parse(chunk, 0)
51ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr
523ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def _startElementHandler(self, name, attrs):
53ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		stackSize = self.stackSize
54e82b4d5658a7a38cf92b4b9392861257a2cfbb36jvr		self.stackSize = stackSize + 1
55f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		if not stackSize:
56180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if name != "ttFont":
57cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod				raise TTXParseError("illegal root tag: %s" % name)
584bb05c6ad4b112de40642d77663aa87d145f863fjvr			sfntVersion = attrs.get("sfntVersion")
594bb05c6ad4b112de40642d77663aa87d145f863fjvr			if sfntVersion is not None:
60180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod				if len(sfntVersion) != 4:
614bb05c6ad4b112de40642d77663aa87d145f863fjvr					sfntVersion = safeEval('"' + sfntVersion + '"')
624bb05c6ad4b112de40642d77663aa87d145f863fjvr				self.ttFont.sfntVersion = sfntVersion
63ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
64f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		elif stackSize == 1:
654bb05c6ad4b112de40642d77663aa87d145f863fjvr			subFile = attrs.get("src")
664bb05c6ad4b112de40642d77663aa87d145f863fjvr			if subFile is not None:
674bb05c6ad4b112de40642d77663aa87d145f863fjvr				subFile = os.path.join(os.path.dirname(self.fileName), subFile)
683ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod				subReader = XMLReader(subFile, self.ttFont, self.progress, self.quiet)
693ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod				subReader.read()
704bb05c6ad4b112de40642d77663aa87d145f863fjvr				self.contentStack.append([])
714bb05c6ad4b112de40642d77663aa87d145f863fjvr				return
720011bb691018c7feed1f8c3554d88b9ae096e127jvr			tag = ttLib.xmlToTag(name)
730011bb691018c7feed1f8c3554d88b9ae096e127jvr			msg = "Parsing '%s' table..." % tag
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self.progress:
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.progress.setlabel(msg)
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif self.ttFont.verbose:
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust				ttLib.debugmsg(msg)
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
7985af40e7d8c4f4f4a3df3562a5ec9e5731cfe43fDave Crossland				if not self.quiet:
803ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod					print(msg)
810011bb691018c7feed1f8c3554d88b9ae096e127jvr			if tag == "GlyphOrder":
820011bb691018c7feed1f8c3554d88b9ae096e127jvr				tableClass = ttLib.GlyphOrder
83bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			elif "ERROR" in attrs:
84f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				tableClass = DefaultTable
85f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust			else:
86f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				tableClass = ttLib.getTableClass(tag)
87f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				if tableClass is None:
88f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					tableClass = DefaultTable
89bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if tag == 'loca' and tag in self.ttFont:
90d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr				# Special-case the 'loca' table as we need the
91e82b4d5658a7a38cf92b4b9392861257a2cfbb36jvr				#    original if the 'glyf' table isn't recompiled.
92ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.currentTable = self.ttFont[tag]
931670f7af0edf5af2447f9fb157febe8cfb44b629Just			else:
94ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.currentTable = tableClass(tag)
95ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.ttFont[tag] = self.currentTable
96ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
97f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		elif stackSize == 2:
98ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
99ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.root = (name, attrs, self.contentStack[-1])
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust			list = []
102ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack[-1].append((name, attrs, list))
103ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append(list)
1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1053ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def _characterDataHandler(self, data):
106ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		if self.stackSize > 1:
107ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack[-1].append(data)
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1093ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod	def _endElementHandler(self, name):
110ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.stackSize = self.stackSize - 1
111ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		del self.contentStack[-1]
112ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		if self.stackSize == 1:
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.root = None
114ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		elif self.stackSize == 2:
1153a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod			name, attrs, content = self.root
1163a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod			self.currentTable.fromXML(name, attrs, content, self.ttFont)
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.root = None
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass ProgressPrinter:
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, title, maxval=100):
1233ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print(title)
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def set(self, val, maxval=None):
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pass
1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def increment(self, val=1):
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pass
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1316ab979cacaa9c15666a526e05c669b7f87bb6de9jvr	def setLabel(self, text):
1323ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print(text)
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust
134