xmlReader.py revision d57c4346e5725bbfe0e1b13067b292c3faaaeb8e
17842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools import ttLib
27842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval
3f8fd4777d273836a1222b72f6761cb6fdf9ec87aJustfrom fontTools.ttLib.tables.DefaultTable import DefaultTable
44bb05c6ad4b112de40642d77663aa87d145f863fjvrimport os
57842e56b97ce677b83bdab09cda48bc2d89ac75aJust
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust
7ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvrclass TTXParseError(Exception): pass
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust
99b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvrBUFSIZE = 0x4000
109b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust
12ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvrclass ExpatParser:
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust
144bb05c6ad4b112de40642d77663aa87d145f863fjvr	def __init__(self, ttFont, fileName, progress=None):
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.ttFont = ttFont
164bb05c6ad4b112de40642d77663aa87d145f863fjvr		self.fileName = fileName
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.progress = progress
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.root = None
19ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.contentStack = []
20ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.stackSize = 0
21ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr
224bb05c6ad4b112de40642d77663aa87d145f863fjvr	def parse(self):
2356bfea43176d03cede26f8de51638c4ab9f4bd64jvr		file = open(self.fileName)
2456bfea43176d03cede26f8de51638c4ab9f4bd64jvr		self.parseFile(file)
2556bfea43176d03cede26f8de51638c4ab9f4bd64jvr		file.close()
2656bfea43176d03cede26f8de51638c4ab9f4bd64jvr
2756bfea43176d03cede26f8de51638c4ab9f4bd64jvr	def parseFile(self, file):
28e493669d196f292f24d4c09ce340c3e67749bce5jvr		from xml.parsers.expat import ParserCreate
29e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser = ParserCreate()
30e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser.returns_unicode = 0
31e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser.StartElementHandler = self.startElementHandler
32e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser.EndElementHandler = self.endElementHandler
33e493669d196f292f24d4c09ce340c3e67749bce5jvr		parser.CharacterDataHandler = self.characterDataHandler
34e493669d196f292f24d4c09ce340c3e67749bce5jvr
359b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr		pos = 0
369b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr		while 1:
379b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			chunk = file.read(BUFSIZE)
389b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			if not chunk:
399b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				parser.Parse(chunk, 1)
409b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				break
419b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			pos = pos + len(chunk)
429b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			if self.progress:
439b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr				self.progress.set(pos / 100)
449b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr			parser.Parse(chunk, 0)
45ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr
46e493669d196f292f24d4c09ce340c3e67749bce5jvr	def startElementHandler(self, name, attrs):
47ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		stackSize = self.stackSize
48e82b4d5658a7a38cf92b4b9392861257a2cfbb36jvr		self.stackSize = stackSize + 1
49f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		if not stackSize:
507842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if name <> "ttFont":
51ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				raise TTXParseError, "illegal root tag: %s" % name
524bb05c6ad4b112de40642d77663aa87d145f863fjvr			sfntVersion = attrs.get("sfntVersion")
534bb05c6ad4b112de40642d77663aa87d145f863fjvr			if sfntVersion is not None:
544bb05c6ad4b112de40642d77663aa87d145f863fjvr				if len(sfntVersion) <> 4:
554bb05c6ad4b112de40642d77663aa87d145f863fjvr					sfntVersion = safeEval('"' + sfntVersion + '"')
564bb05c6ad4b112de40642d77663aa87d145f863fjvr				self.ttFont.sfntVersion = sfntVersion
57ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
58f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		elif stackSize == 1:
594bb05c6ad4b112de40642d77663aa87d145f863fjvr			subFile = attrs.get("src")
604bb05c6ad4b112de40642d77663aa87d145f863fjvr			if subFile is not None:
614bb05c6ad4b112de40642d77663aa87d145f863fjvr				subFile = os.path.join(os.path.dirname(self.fileName), subFile)
624bb05c6ad4b112de40642d77663aa87d145f863fjvr				importXML(self.ttFont, subFile, self.progress)
634bb05c6ad4b112de40642d77663aa87d145f863fjvr				self.contentStack.append([])
644bb05c6ad4b112de40642d77663aa87d145f863fjvr				return
650011bb691018c7feed1f8c3554d88b9ae096e127jvr			tag = ttLib.xmlToTag(name)
660011bb691018c7feed1f8c3554d88b9ae096e127jvr			msg = "Parsing '%s' table..." % tag
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self.progress:
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.progress.setlabel(msg)
697842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif self.ttFont.verbose:
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust				ttLib.debugmsg(msg)
717842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust				print msg
730011bb691018c7feed1f8c3554d88b9ae096e127jvr			if tag == "GlyphOrder":
740011bb691018c7feed1f8c3554d88b9ae096e127jvr				tableClass = ttLib.GlyphOrder
750011bb691018c7feed1f8c3554d88b9ae096e127jvr			elif attrs.has_key("ERROR"):
76f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				tableClass = DefaultTable
77f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust			else:
78f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				tableClass = ttLib.getTableClass(tag)
79f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				if tableClass is None:
80f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					tableClass = DefaultTable
81d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr			if tag == 'loca' and self.ttFont.has_key(tag):
82d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr				# Special-case the 'loca' table as we need the
83e82b4d5658a7a38cf92b4b9392861257a2cfbb36jvr				#    original if the 'glyf' table isn't recompiled.
84ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.currentTable = self.ttFont[tag]
851670f7af0edf5af2447f9fb157febe8cfb44b629Just			else:
86ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.currentTable = tableClass(tag)
87ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				self.ttFont[tag] = self.currentTable
88ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
89f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust		elif stackSize == 2:
90ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append([])
91ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.root = (name, attrs, self.contentStack[-1])
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust			list = []
94ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack[-1].append((name, attrs, list))
95ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack.append(list)
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust
97e493669d196f292f24d4c09ce340c3e67749bce5jvr	def characterDataHandler(self, data):
98ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		if self.stackSize > 1:
99ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.contentStack[-1].append(data)
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust
101e493669d196f292f24d4c09ce340c3e67749bce5jvr	def endElementHandler(self, name):
102ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		self.stackSize = self.stackSize - 1
103ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		del self.contentStack[-1]
104ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		if self.stackSize == 1:
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.root = None
106ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		elif self.stackSize == 2:
107ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.currentTable.fromXML(self.root, self.ttFont)
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.root = None
1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1117842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass ProgressPrinter:
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, title, maxval=100):
1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print title
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def set(self, val, maxval=None):
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pass
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def increment(self, val=1):
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pass
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def setlabel(self, text):
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print text
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust
126ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvrdef importXML(ttFont, fileName, progress=None):
127ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr	"""Import a TTX file (an XML-based text format), so as to recreate
128ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr	a font object.
129ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr	"""
1309b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr	if progress:
1319b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr		import stat
1329b7b8e0f5b5f968acb95f8c3a484ea8d42924d65jvr		progress.set(0, os.stat(fileName)[stat.ST_SIZE] / 100 or 1)
1334bb05c6ad4b112de40642d77663aa87d145f863fjvr	p = ExpatParser(ttFont, fileName, progress)
1344bb05c6ad4b112de40642d77663aa87d145f863fjvr	p.parse()
135ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr
136