19682b4198b8449ba02ae2aaaa5fc76307167845dJust"""fontTools.ttLib -- a package for dealing with TrueType fonts.
27842e56b97ce677b83bdab09cda48bc2d89ac75aJust
37842e56b97ce677b83bdab09cda48bc2d89ac75aJustThis package offers translators to convert TrueType fonts to Python
49682b4198b8449ba02ae2aaaa5fc76307167845dJustobjects and vice versa, and additionally from Python to TTX (an XML-based
59682b4198b8449ba02ae2aaaa5fc76307167845dJusttext format) and vice versa.
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust
77842e56b97ce677b83bdab09cda48bc2d89ac75aJustExample interactive session:
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust
97842e56b97ce677b83bdab09cda48bc2d89ac75aJustPython 1.5.2c1 (#43, Mar  9 1999, 13:06:43)  [CW PPC w/GUSI w/MSL]
107842e56b97ce677b83bdab09cda48bc2d89ac75aJustCopyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> from fontTools import ttLib
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt = ttLib.TTFont("afont.ttf")
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt['maxp'].numGlyphs
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust242
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt['OS/2'].achVendID
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust'B&H\000'
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt['head'].unitsPerEm
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust2048
199682b4198b8449ba02ae2aaaa5fc76307167845dJust>>> tt.saveXML("afont.ttx")
207842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'LTSH' table...
217842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'OS/2' table...
227842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'VDMX' table...
237842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'cmap' table...
247842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'cvt ' table...
257842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'fpgm' table...
267842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'glyf' table...
277842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'hdmx' table...
287842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'head' table...
297842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'hhea' table...
307842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'hmtx' table...
317842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'loca' table...
327842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'maxp' table...
337842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'name' table...
347842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'post' table...
357842e56b97ce677b83bdab09cda48bc2d89ac75aJustDumping 'prep' table...
367842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt2 = ttLib.TTFont()
379682b4198b8449ba02ae2aaaa5fc76307167845dJust>>> tt2.importXML("afont.ttx")
387842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>> tt2['maxp'].numGlyphs
397842e56b97ce677b83bdab09cda48bc2d89ac75aJust242
407842e56b97ce677b83bdab09cda48bc2d89ac75aJust>>>
417842e56b97ce677b83bdab09cda48bc2d89ac75aJust
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
437842e56b97ce677b83bdab09cda48bc2d89ac75aJust
441ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
45bb0beb7385d00a0f5c99895e7299a5a1307ec193Behdad Esfahbodfrom fontTools.misc.py23 import *
4630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport os
4730e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport sys
481cff4cb190c49020bd34cfcc7a67fcb143055ae4jvr
491cff4cb190c49020bd34cfcc7a67fcb143055ae4jvrhaveMacSupport = 0
501cff4cb190c49020bd34cfcc7a67fcb143055ae4jvrif sys.platform == "mac":
511cff4cb190c49020bd34cfcc7a67fcb143055ae4jvr	haveMacSupport = 1
521cff4cb190c49020bd34cfcc7a67fcb143055ae4jvrelif sys.platform == "darwin" and sys.version_info[:3] != (2, 2, 0):
531cff4cb190c49020bd34cfcc7a67fcb143055ae4jvr	# Python 2.2's Mac support is broken, so don't enable it there.
541cff4cb190c49020bd34cfcc7a67fcb143055ae4jvr	haveMacSupport = 1
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust
56d04a3bb4f932dff5dfbd45ed33fa4c357b471527jvr
577842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass TTLibError(Exception): pass
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust
60e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass TTFont(object):
617842e56b97ce677b83bdab09cda48bc2d89ac75aJust
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""The main font object. It manages file input and output, and offers
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust	a convenient way of accessing tables.
641344bc9b6208d9f779ac67bd8121c82de0eb085fpabs	Tables will be only decompiled when necessary, ie. when they're actually
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust	accessed. This means that simple operations can be extremely fast.
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
680011bb691018c7feed1f8c3554d88b9ae096e127jvr	def __init__(self, file=None, res_name_or_index=None,
69dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			sfntVersion="\000\001\000\000", flavor=None, checkChecksums=False,
70dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			verbose=False, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
71497863a190b70a51e9f6c8013e19e2424a69acfbBehdad Esfahbod			recalcTimestamp=True, fontNumber=-1, lazy=False, quiet=False):
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust
737842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""The constructor can be called with a few different arguments.
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust		When reading a font from disk, 'file' should be either a pathname
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pointing to a file, or a readable file object.
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		It we're running on a Macintosh, 'res_name_or_index' maybe an sfnt
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		resource name or an sfnt resource index number or zero. The latter
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust		case will cause TTLib to autodetect whether the file is a flat file
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		or a suitcase. (If it's a suitcase, only the first 'sfnt' resource
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust		will be read!)
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust
83ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		The 'checkChecksums' argument is used to specify how sfnt
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust		checksums are treated upon reading a file from disk:
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust			0: don't check (default)
860011bb691018c7feed1f8c3554d88b9ae096e127jvr			1: check, print warnings if a wrong checksum is found
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust			2: check, raise an exception if a wrong checksum is found.
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		The TTFont constructor can also be called without a 'file'
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		argument: this is the way to create a new empty font.
9158d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod		In this case you can optionally supply the 'sfntVersion' argument,
9258d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod		and a 'flavor' which can be None, or 'woff'.
9388cb4f33c63dcad4f3ecebf03fe381cacb24799eJust
9498d780d766a62fb5077102a9785e77c30dbffefaJust		If the recalcBBoxes argument is false, a number of things will *not*
953e097c609540944dd9290ad58df346ca86492031Just		be recalculated upon save/compile:
9698d780d766a62fb5077102a9785e77c30dbffefaJust			1) glyph bounding boxes
9798d780d766a62fb5077102a9785e77c30dbffefaJust			2) maxp font bounding box
9898d780d766a62fb5077102a9785e77c30dbffefaJust			3) hhea min/max values
9998d780d766a62fb5077102a9785e77c30dbffefaJust		(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
1009682b4198b8449ba02ae2aaaa5fc76307167845dJust		Additionally, upon importing an TTX file, this option cause glyphs
10198d780d766a62fb5077102a9785e77c30dbffefaJust		to be compiled right away. This should reduce memory consumption
10298d780d766a62fb5077102a9785e77c30dbffefaJust		greatly, and therefore should have some impact on the time needed
10398d780d766a62fb5077102a9785e77c30dbffefaJust		to parse/compile large fonts.
104823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
105497863a190b70a51e9f6c8013e19e2424a69acfbBehdad Esfahbod		If the recalcTimestamp argument is false, the modified timestamp in the
106497863a190b70a51e9f6c8013e19e2424a69acfbBehdad Esfahbod		'head' table will *not* be recalculated upon save/compile.
107497863a190b70a51e9f6c8013e19e2424a69acfbBehdad Esfahbod
108823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		If the allowVID argument is set to true, then virtual GID's are
109823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		supported. Asking for a glyph ID with a glyph name or GID that is not in
110823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		the font will return a virtual GID.   This is valid for GSUB and cmap
111823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		tables. For SING glyphlets, the cmap table is used to specify Unicode
112ebefbbaa58f9e821dd535b354fcccecc2d24d914Behdad Esfahbod		values for virtual GI's used in GSUB/GPOS rules. If the gid N is requested
113823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		and does not exist in the font, or the glyphname has the form glyphN
114823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		and does not exist in the font, then N is used as the virtual GID.
115823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		Else, the first virtual GID is assigned as 0x1000 -1; for subsequent new
116823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		virtual GIDs, the next is one less than the previous.
1172545f16b54798fe809238c76e2240f901fce8f8djvr
1182545f16b54798fe809238c76e2240f901fce8f8djvr		If ignoreDecompileErrors is set to True, exceptions raised in
1192545f16b54798fe809238c76e2240f901fce8f8djvr		individual tables during decompilation will be ignored, falling
1202545f16b54798fe809238c76e2240f901fce8f8djvr		back to the DefaultTable implementation, which simply keeps the
1212545f16b54798fe809238c76e2240f901fce8f8djvr		binary data.
1227ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod
1237ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod		If lazy is set to True, many data structures are loaded lazily, upon
1247ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod		access only.
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust
127d8b32bf6e208a18421cfd4705801d6237e281f93Behdad Esfahbod		from fontTools.ttLib import sfnt
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.verbose = verbose
129d7efd5692cac74702d487e641ca7ddf9c6cfececDave Crossland		self.quiet = quiet
1307ef23a85ae49039ae491e337be173caba7bdd534Behdad Esfahbod		self.lazy = lazy
13188cb4f33c63dcad4f3ecebf03fe381cacb24799eJust		self.recalcBBoxes = recalcBBoxes
132497863a190b70a51e9f6c8013e19e2424a69acfbBehdad Esfahbod		self.recalcTimestamp = recalcTimestamp
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables = {}
1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.reader = None
135823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
136823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Permit the user to reference glyphs that are not int the font.
137823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.last_vid = 0xFFFE # Can't make it be 0xFFFF, as the world is full unsigned short integer counters that get incremented after the last seen GID value.
138823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.reverseVIDDict = {}
139823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.VIDDict = {}
140823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.allowVID = allowVID
1412545f16b54798fe809238c76e2240f901fce8f8djvr		self.ignoreDecompileErrors = ignoreDecompileErrors
142823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not file:
1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.sfntVersion = sfntVersion
14558d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod			self.flavor = flavor
14658d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod			self.flavorData = None
1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return
1480bb3ba522291076df18825a63c2f367d8ce3e48ajvr		if not hasattr(file, "read"):
1490bb3ba522291076df18825a63c2f367d8ce3e48ajvr			# assume file is a string
1500bb3ba522291076df18825a63c2f367d8ce3e48ajvr			if haveMacSupport and res_name_or_index is not None:
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust				# on the mac, we deal with sfnt resources as well as flat files
1522b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod				from . import macUtils
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if res_name_or_index == 0:
1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust					if macUtils.getSFNTResIndices(file):
1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust						# get the first available sfnt font.
1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust						file = macUtils.SFNTResourceReader(file, 1)
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust					else:
1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust						file = open(file, "rb")
1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust				else:
1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust					file = macUtils.SFNTResourceReader(file, res_name_or_index)
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust				file = open(file, "rb")
1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust			pass # assume "file" is a readable file object
1657e91e776c9d10d3b295de06ee7f665d8106306d8pabs		self.reader = sfnt.SFNTReader(file, checkChecksums, fontNumber=fontNumber)
1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.sfntVersion = self.reader.sfntVersion
16758d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod		self.flavor = self.reader.flavor
16858d7416124dc0ebaa3faccb1b77dd5f7926a628aBehdad Esfahbod		self.flavorData = self.reader.flavorData
1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def close(self):
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""If we still have a reader object, close it."""
1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.reader is not None:
1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.reader.close()
1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust
175dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod	def save(self, file, makeSuitcase=False, reorderTables=True):
1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Save the font to disk. Similarly to the constructor,
1777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		the 'file' argument can be either a pathname or a writable
1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		file object.
1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust
18088cb4f33c63dcad4f3ecebf03fe381cacb24799eJust		On the Mac, if makeSuitcase is true, a suitcase (resource fork)
18188cb4f33c63dcad4f3ecebf03fe381cacb24799eJust		file will we made instead of a flat .ttf file.
1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
1830f675860ce081e9aeaf880ccc4a8e5d49e83f553Just		from fontTools.ttLib import sfnt
1840bb3ba522291076df18825a63c2f367d8ce3e48ajvr		if not hasattr(file, "write"):
1850f675860ce081e9aeaf880ccc4a8e5d49e83f553Just			closeStream = 1
18688cb4f33c63dcad4f3ecebf03fe381cacb24799eJust			if os.name == "mac" and makeSuitcase:
1872b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod				from . import macUtils
1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust				file = macUtils.SFNTResourceWriter(file, self)
1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust				file = open(file, "wb")
1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if os.name == "mac":
19291bca4244286fb519c93fe92329da96b0e6f32eejvr					from fontTools.misc.macCreator import setMacCreatorAndType
19391bca4244286fb519c93fe92329da96b0e6f32eejvr					setMacCreatorAndType(file.name, 'mdos', 'BINA')
1947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1950f675860ce081e9aeaf880ccc4a8e5d49e83f553Just			# assume "file" is a writable file object
1960f675860ce081e9aeaf880ccc4a8e5d49e83f553Just			closeStream = 0
1977842e56b97ce677b83bdab09cda48bc2d89ac75aJust
198c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		tags = list(self.keys())
199700df031313741e79b0647f045949a1a04d03371jvr		if "GlyphOrder" in tags:
200700df031313741e79b0647f045949a1a04d03371jvr			tags.remove("GlyphOrder")
2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		numTables = len(tags)
20228ae1962292b66ad67117aef2a99d5735a70b779jvr		if reorderTables:
20328ae1962292b66ad67117aef2a99d5735a70b779jvr			import tempfile
20428ae1962292b66ad67117aef2a99d5735a70b779jvr			tmp = tempfile.TemporaryFile(prefix="ttx-fonttools")
20528ae1962292b66ad67117aef2a99d5735a70b779jvr		else:
20628ae1962292b66ad67117aef2a99d5735a70b779jvr			tmp = file
207b0dc6dfc8baf01db94782fccc2e734a281b9ba12Behdad Esfahbod		writer = sfnt.SFNTWriter(tmp, numTables, self.sfntVersion, self.flavor, self.flavorData)
2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		done = []
2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for tag in tags:
2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._writeTable(tag, writer, done)
2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
21328ae1962292b66ad67117aef2a99d5735a70b779jvr		writer.close()
21428ae1962292b66ad67117aef2a99d5735a70b779jvr
21528ae1962292b66ad67117aef2a99d5735a70b779jvr		if reorderTables:
21628ae1962292b66ad67117aef2a99d5735a70b779jvr			tmp.flush()
21728ae1962292b66ad67117aef2a99d5735a70b779jvr			tmp.seek(0)
21828ae1962292b66ad67117aef2a99d5735a70b779jvr			reorderFontTables(tmp, file)
21928ae1962292b66ad67117aef2a99d5735a70b779jvr			tmp.close()
22028ae1962292b66ad67117aef2a99d5735a70b779jvr
22128ae1962292b66ad67117aef2a99d5735a70b779jvr		if closeStream:
22228ae1962292b66ad67117aef2a99d5735a70b779jvr			file.close()
2237842e56b97ce677b83bdab09cda48bc2d89ac75aJust
22438fdae6342ae5339fbd148ef920f16291aa1e259Behdad Esfahbod	def saveXML(self, fileOrPath, progress=None, quiet=False,
22538fdae6342ae5339fbd148ef920f16291aa1e259Behdad Esfahbod			tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
226c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine			bitmapGlyphDataFormat='raw'):
2279682b4198b8449ba02ae2aaaa5fc76307167845dJust		"""Export the font as TTX (an XML-based text file), or as a series of text
2287dcc91674e8df02f80711ee2587fd2a6a205ca94Just		files when splitTables is true. In the latter case, the 'fileOrPath'
2297dcc91674e8df02f80711ee2587fd2a6a205ca94Just		argument should be a path to a directory.
230ff3499de6618199b152484e5a89d10bb6aff384dJust		The 'tables' argument must either be false (dump all tables) or a
231ff3499de6618199b152484e5a89d10bb6aff384dJust		list of tables to dump. The 'skipTables' argument may be a list of tables
232ff3499de6618199b152484e5a89d10bb6aff384dJust		to skip, but only when the 'tables' argument is false.
2337dcc91674e8df02f80711ee2587fd2a6a205ca94Just		"""
234d04a3bb4f932dff5dfbd45ed33fa4c357b471527jvr		from fontTools import version
235f65033e277ec5161fd77ec340ab899edc2e03764Behdad Esfahbod		from fontTools.misc import xmlWriter
23653602486b491e003b8dbba0b1c86709bd0c011bfJust
23753602486b491e003b8dbba0b1c86709bd0c011bfJust		self.disassembleInstructions = disassembleInstructions
238c33b0a22ef0046c392275e3dba974dfbadee24faMatt Fontaine		self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
2397842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not tables:
240c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod			tables = list(self.keys())
241700df031313741e79b0647f045949a1a04d03371jvr			if "GlyphOrder" not in tables:
242700df031313741e79b0647f045949a1a04d03371jvr				tables = ["GlyphOrder"] + tables
243ff3499de6618199b152484e5a89d10bb6aff384dJust			if skipTables:
244ff3499de6618199b152484e5a89d10bb6aff384dJust				for tag in skipTables:
245ff3499de6618199b152484e5a89d10bb6aff384dJust					if tag in tables:
246ff3499de6618199b152484e5a89d10bb6aff384dJust						tables.remove(tag)
2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust		numTables = len(tables)
2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if progress:
2496ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			progress.set(0, numTables)
2506ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			idlefunc = getattr(progress, "idle", None)
2516ab979cacaa9c15666a526e05c669b7f87bb6de9jvr		else:
2526ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			idlefunc = None
253fe665777eae7b67a274e6804295191c1b7a995bfjvr
2546ab979cacaa9c15666a526e05c669b7f87bb6de9jvr		writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc)
255dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod		writer.begintag("ttFont", sfntVersion=repr(self.sfntVersion)[1:-1],
256fe665777eae7b67a274e6804295191c1b7a995bfjvr				ttLibVersion=version)
257fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.newline()
258fe665777eae7b67a274e6804295191c1b7a995bfjvr
259d28479b066106e1c18528d2a4230b471cf76889dJust		if not splitTables:
260d28479b066106e1c18528d2a4230b471cf76889dJust			writer.newline()
261d28479b066106e1c18528d2a4230b471cf76889dJust		else:
2620f293034749578d29494c2560c042c01ced50601jvr			# 'fileOrPath' must now be a path
2630f293034749578d29494c2560c042c01ced50601jvr			path, ext = os.path.splitext(fileOrPath)
2640f293034749578d29494c2560c042c01ced50601jvr			fileNameTemplate = path + ".%s" + ext
265d28479b066106e1c18528d2a4230b471cf76889dJust
2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(numTables):
2676ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			if progress:
2686ab979cacaa9c15666a526e05c669b7f87bb6de9jvr				progress.set(i)
2697842e56b97ce677b83bdab09cda48bc2d89ac75aJust			tag = tables[i]
270d28479b066106e1c18528d2a4230b471cf76889dJust			if splitTables:
2718307fa42cd145bb1b961ff942bbe3eef63908e55jvr				tablePath = fileNameTemplate % tagToIdentifier(tag)
2726ab979cacaa9c15666a526e05c669b7f87bb6de9jvr				tableWriter = xmlWriter.XMLWriter(tablePath, idlefunc=idlefunc)
273fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.begintag("ttFont", ttLibVersion=version)
274fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.newline()
275fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.newline()
2760011bb691018c7feed1f8c3554d88b9ae096e127jvr				writer.simpletag(tagToXML(tag), src=os.path.basename(tablePath))
277d28479b066106e1c18528d2a4230b471cf76889dJust				writer.newline()
2787842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
279fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter = writer
280d7efd5692cac74702d487e641ca7ddf9c6cfececDave Crossland			self._tableToXML(tableWriter, tag, progress, quiet)
281d28479b066106e1c18528d2a4230b471cf76889dJust			if splitTables:
282fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.endtag("ttFont")
283fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.newline()
284fe665777eae7b67a274e6804295191c1b7a995bfjvr				tableWriter.close()
2856ab979cacaa9c15666a526e05c669b7f87bb6de9jvr		if progress:
2866ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			progress.set((i + 1))
287fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.endtag("ttFont")
288fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.newline()
289fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.close()
2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.verbose:
2919682b4198b8449ba02ae2aaaa5fc76307167845dJust			debugmsg("Done dumping TTX")
2927842e56b97ce677b83bdab09cda48bc2d89ac75aJust
293d7efd5692cac74702d487e641ca7ddf9c6cfececDave Crossland	def _tableToXML(self, writer, tag, progress, quiet):
294bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if tag in self:
295fe665777eae7b67a274e6804295191c1b7a995bfjvr			table = self[tag]
296fe665777eae7b67a274e6804295191c1b7a995bfjvr			report = "Dumping '%s' table..." % tag
297fe665777eae7b67a274e6804295191c1b7a995bfjvr		else:
298fe665777eae7b67a274e6804295191c1b7a995bfjvr			report = "No '%s' table found." % tag
299fe665777eae7b67a274e6804295191c1b7a995bfjvr		if progress:
3006ab979cacaa9c15666a526e05c669b7f87bb6de9jvr			progress.setLabel(report)
301fe665777eae7b67a274e6804295191c1b7a995bfjvr		elif self.verbose:
302fe665777eae7b67a274e6804295191c1b7a995bfjvr			debugmsg(report)
303fe665777eae7b67a274e6804295191c1b7a995bfjvr		else:
304d7efd5692cac74702d487e641ca7ddf9c6cfececDave Crossland			if not quiet:
3053ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				print(report)
306bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if tag not in self:
307fe665777eae7b67a274e6804295191c1b7a995bfjvr			return
3080011bb691018c7feed1f8c3554d88b9ae096e127jvr		xmlTag = tagToXML(tag)
309fe665777eae7b67a274e6804295191c1b7a995bfjvr		if hasattr(table, "ERROR"):
310fe665777eae7b67a274e6804295191c1b7a995bfjvr			writer.begintag(xmlTag, ERROR="decompilation error")
311fe665777eae7b67a274e6804295191c1b7a995bfjvr		else:
312fe665777eae7b67a274e6804295191c1b7a995bfjvr			writer.begintag(xmlTag)
313fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.newline()
314fe665777eae7b67a274e6804295191c1b7a995bfjvr		if tag in ("glyf", "CFF "):
315fe665777eae7b67a274e6804295191c1b7a995bfjvr			table.toXML(writer, self, progress)
316fe665777eae7b67a274e6804295191c1b7a995bfjvr		else:
317fe665777eae7b67a274e6804295191c1b7a995bfjvr			table.toXML(writer, self)
318fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.endtag(xmlTag)
319fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.newline()
320fe665777eae7b67a274e6804295191c1b7a995bfjvr		writer.newline()
321fe665777eae7b67a274e6804295191c1b7a995bfjvr
32238fdae6342ae5339fbd148ef920f16291aa1e259Behdad Esfahbod	def importXML(self, file, progress=None, quiet=False):
323ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr		"""Import a TTX file (an XML-based text format), so as to recreate
3247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		a font object.
3257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
326bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if "maxp" in self and "post" in self:
327d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr			# Make sure the glyph order is loaded, as it otherwise gets
328d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr			# lost if the XML doesn't contain the glyph order, yet does
329d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr			# contain the table which was originally used to extract the
330d57c4346e5725bbfe0e1b13067b292c3faaaeb8ejvr			# glyph names from (ie. 'post', 'cmap' or 'CFF ').
33122f068929cbc8f3e5d18931c69a14d757eaa4de1jvr			self.getGlyphOrder()
3323ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod
3333ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		from fontTools.misc import xmlReader
3343ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod
3353ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		reader = xmlReader.XMLReader(file, self, progress, quiet)
3363ebfea491ea795217e0f9649c23ee81df769a250Behdad Esfahbod		reader.read()
3377842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def isLoaded(self, tag):
3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Return true if the table identified by 'tag' has been
3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		decompiled and loaded into memory."""
341bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		return tag in self.tables
3427842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def has_key(self, tag):
3447842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.isLoaded(tag):
345dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			return True
346bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		elif self.reader and tag in self.reader:
347dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			return True
3480011bb691018c7feed1f8c3554d88b9ae096e127jvr		elif tag == "GlyphOrder":
349dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			return True
3507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
351dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod			return False
3527842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3538df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	__contains__ = has_key
3548df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def keys(self):
356c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		keys = list(self.tables.keys())
3577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.reader:
358c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod			for key in list(self.reader.keys()):
3597842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if key not in keys:
3607842e56b97ce677b83bdab09cda48bc2d89ac75aJust					keys.append(key)
361700df031313741e79b0647f045949a1a04d03371jvr
36228ae1962292b66ad67117aef2a99d5735a70b779jvr		if "GlyphOrder" in keys:
36328ae1962292b66ad67117aef2a99d5735a70b779jvr			keys.remove("GlyphOrder")
36428ae1962292b66ad67117aef2a99d5735a70b779jvr		keys = sortedTagList(keys)
36528ae1962292b66ad67117aef2a99d5735a70b779jvr		return ["GlyphOrder"] + keys
3667842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3677842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __len__(self):
368c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		return len(list(self.keys()))
3697842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3707842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __getitem__(self, tag):
371960280bbd6277b56be45595a050a720a49fd5917Behdad Esfahbod		tag = Tag(tag)
3727842e56b97ce677b83bdab09cda48bc2d89ac75aJust		try:
3737842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return self.tables[tag]
3747842e56b97ce677b83bdab09cda48bc2d89ac75aJust		except KeyError:
3750011bb691018c7feed1f8c3554d88b9ae096e127jvr			if tag == "GlyphOrder":
376c4af3964fbcfefb562fcc72171f5b48bb5065040Behdad Esfahbod				table = GlyphOrder(tag)
3770011bb691018c7feed1f8c3554d88b9ae096e127jvr				self.tables[tag] = table
3780011bb691018c7feed1f8c3554d88b9ae096e127jvr				return table
3797842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self.reader is not None:
380f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				import traceback
3817842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if self.verbose:
3826ab979cacaa9c15666a526e05c669b7f87bb6de9jvr					debugmsg("Reading '%s' table from disk" % tag)
3837842e56b97ce677b83bdab09cda48bc2d89ac75aJust				data = self.reader[tag]
3848307fa42cd145bb1b961ff942bbe3eef63908e55jvr				tableClass = getTableClass(tag)
3858307fa42cd145bb1b961ff942bbe3eef63908e55jvr				table = tableClass(tag)
3867842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.tables[tag] = table
3877842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if self.verbose:
3886ab979cacaa9c15666a526e05c669b7f87bb6de9jvr					debugmsg("Decompiling '%s' table" % tag)
389f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust				try:
390f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					table.decompile(data, self)
3912545f16b54798fe809238c76e2240f901fce8f8djvr				except:
3922545f16b54798fe809238c76e2240f901fce8f8djvr					if not self.ignoreDecompileErrors:
3932545f16b54798fe809238c76e2240f901fce8f8djvr						raise
3942545f16b54798fe809238c76e2240f901fce8f8djvr					# fall back to DefaultTable, retaining the binary table data
3953ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod					print("An exception occurred during the decompilation of the '%s' table" % tag)
3962b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod					from .tables.DefaultTable import DefaultTable
397b92c08059d49a13364257629dd5601dad4ff91d7Behdad Esfahbod					file = StringIO()
398f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					traceback.print_exc(file=file)
399f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					table = DefaultTable(tag)
400f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					table.ERROR = file.getvalue()
401f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					self.tables[tag] = table
402f8fd4777d273836a1222b72f6761cb6fdf9ec87aJust					table.decompile(data, self)
4037842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return table
4047842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
405cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod				raise KeyError("'%s' table not found" % tag)
4067842e56b97ce677b83bdab09cda48bc2d89ac75aJust
4077842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __setitem__(self, tag, table):
4085cf40083364e1d2dce119de25cb42ce69d2fb53cBehdad Esfahbod		self.tables[Tag(tag)] = table
4097842e56b97ce677b83bdab09cda48bc2d89ac75aJust
4107842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __delitem__(self, tag):
411bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if tag not in self:
412cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise KeyError("'%s' table not found" % tag)
413bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if tag in self.tables:
414f70746325687393330f7a765814a0fe78f11f847jvr			del self.tables[tag]
415bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if self.reader and tag in self.reader:
416f70746325687393330f7a765814a0fe78f11f847jvr			del self.reader[tag]
4179c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod
4189c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod	def get(self, tag, default=None):
4199c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod		try:
4209c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod			return self[tag]
4219c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod		except KeyError:
4229c5e2ce1b6cb09728b962ed9e055ffe8fd72b372Behdad Esfahbod			return default
4237842e56b97ce677b83bdab09cda48bc2d89ac75aJust
4247842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def setGlyphOrder(self, glyphOrder):
4257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.glyphOrder = glyphOrder
4267842e56b97ce677b83bdab09cda48bc2d89ac75aJust
4277842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def getGlyphOrder(self):
428cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr		try:
429cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			return self.glyphOrder
430cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr		except AttributeError:
431cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			pass
432bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if 'CFF ' in self:
4330b63b28615933a1332ea55ce4f5daedf5269192cjvr			cff = self['CFF ']
434700df031313741e79b0647f045949a1a04d03371jvr			self.glyphOrder = cff.getGlyphOrder()
435bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		elif 'post' in self:
436cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			# TrueType font
437cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			glyphOrder = self['post'].getGlyphOrder()
438cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			if glyphOrder is None:
439cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				#
440cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				# No names found in the 'post' table.
441cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				# Try to create glyph names from the unicode cmap (if available)
442cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				# in combination with the Adobe Glyph List (AGL).
443cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				#
444c91a95189abd06cd2c314f511f3f8cb306aba8e7Just				self._getGlyphNamesFromCmap()
445cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			else:
446cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr				self.glyphOrder = glyphOrder
447cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr		else:
448cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr			self._getGlyphNamesFromCmap()
4497842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self.glyphOrder
4507842e56b97ce677b83bdab09cda48bc2d89ac75aJust
4517842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def _getGlyphNamesFromCmap(self):
4529f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		#
4539f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# This is rather convoluted, but then again, it's an interesting problem:
4549f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - we need to use the unicode values found in the cmap table to
4559f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		#   build glyph names (eg. because there is only a minimal post table,
4569f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		#   or none at all).
4579f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - but the cmap parser also needs glyph names to work with...
4589f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# So here's what we do:
4599f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - make up glyph names based on glyphID
4609f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - load a temporary cmap table based on those names
4619f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - extract the unicode values, build the "real" glyph names
4629f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# - unload the temporary cmap table
4639f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		#
4649f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		if self.isLoaded("cmap"):
4659f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# Bootstrapping: we're getting called by the cmap parser
4669f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# itself. This means self.tables['cmap'] contains a partially
4679f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# loaded cmap, making it impossible to get at a unicode
4689f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# subtable here. We remove the partially loaded cmap and
4699f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# restore it later.
4709f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# This only happens if the cmap table is loaded before any
4719f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# other table that does f.getGlyphOrder()  or f.getGlyphName().
4729f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			cmapLoading = self.tables['cmap']
4739f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			del self.tables['cmap']
4749f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		else:
4759f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			cmapLoading = None
4769f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# Make up glyph names based on glyphID, which will be used by the
4779f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# temporary cmap and by the real cmap in case we don't find a unicode
4789f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# cmap.
4797842e56b97ce677b83bdab09cda48bc2d89ac75aJust		numGlyphs = int(self['maxp'].numGlyphs)
4807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphOrder = [None] * numGlyphs
4817842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphOrder[0] = ".notdef"
4827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(1, numGlyphs):
4837842e56b97ce677b83bdab09cda48bc2d89ac75aJust			glyphOrder[i] = "glyph%.5d" % i
4847842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# Set the glyph order, so the cmap parser has something
4859f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# to work with (so we don't get called recursively).
4867842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.glyphOrder = glyphOrder
4879f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		# Get a (new) temporary cmap (based on the just invented names)
4887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tempcmap = self['cmap'].getcmap(3, 1)
4897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if tempcmap is not None:
4907842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# we have a unicode cmap
4917dcc91674e8df02f80711ee2587fd2a6a205ca94Just			from fontTools import agl
4927842e56b97ce677b83bdab09cda48bc2d89ac75aJust			cmap = tempcmap.cmap
4937842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# create a reverse cmap dict
4947842e56b97ce677b83bdab09cda48bc2d89ac75aJust			reversecmap = {}
495c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod			for unicode, name in list(cmap.items()):
4967842e56b97ce677b83bdab09cda48bc2d89ac75aJust				reversecmap[name] = unicode
497a556f51db59a347549c08dd6483ba5b408d0bcfdJust			allNames = {}
4987842e56b97ce677b83bdab09cda48bc2d89ac75aJust			for i in range(numGlyphs):
4997842e56b97ce677b83bdab09cda48bc2d89ac75aJust				tempName = glyphOrder[i]
500bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod				if tempName in reversecmap:
5017842e56b97ce677b83bdab09cda48bc2d89ac75aJust					unicode = reversecmap[tempName]
502bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod					if unicode in agl.UV2AGL:
5037842e56b97ce677b83bdab09cda48bc2d89ac75aJust						# get name from the Adobe Glyph List
504a556f51db59a347549c08dd6483ba5b408d0bcfdJust						glyphName = agl.UV2AGL[unicode]
5057842e56b97ce677b83bdab09cda48bc2d89ac75aJust					else:
5067842e56b97ce677b83bdab09cda48bc2d89ac75aJust						# create uni<CODE> name
50714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod						glyphName = "uni%04X" % unicode
508a556f51db59a347549c08dd6483ba5b408d0bcfdJust					tempName = glyphName
509a556f51db59a347549c08dd6483ba5b408d0bcfdJust					n = 1
510bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod					while tempName in allNames:
511dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod						tempName = glyphName + "#" + repr(n)
512a556f51db59a347549c08dd6483ba5b408d0bcfdJust						n = n + 1
513a556f51db59a347549c08dd6483ba5b408d0bcfdJust					glyphOrder[i] = tempName
514a556f51db59a347549c08dd6483ba5b408d0bcfdJust					allNames[tempName] = 1
5159f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# Delete the temporary cmap table from the cache, so it can
5169f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# be parsed again with the right names.
5177842e56b97ce677b83bdab09cda48bc2d89ac75aJust			del self.tables['cmap']
5187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
5197842e56b97ce677b83bdab09cda48bc2d89ac75aJust			pass # no unicode cmap available, stick with the invented names
5207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.glyphOrder = glyphOrder
5219f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr		if cmapLoading:
5229f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# restore partially loaded cmap, so it can continue loading
5239f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			# using the proper names.
5249f1e14bec9dad6d59300bbff6a5a9d5a8d0828f9jvr			self.tables['cmap'] = cmapLoading
5257842e56b97ce677b83bdab09cda48bc2d89ac75aJust
5267842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def getGlyphNames(self):
5277842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Get a list of glyph names, sorted alphabetically."""
528ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		glyphNames = sorted(self.getGlyphOrder()[:])
5297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return glyphNames
5307842e56b97ce677b83bdab09cda48bc2d89ac75aJust
5317842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def getGlyphNames2(self):
5328bc8cf850df75c660e15d34fac8ac70f18a4da4aJust		"""Get a list of glyph names, sorted alphabetically,
5338bc8cf850df75c660e15d34fac8ac70f18a4da4aJust		but not case sensitive.
5348bc8cf850df75c660e15d34fac8ac70f18a4da4aJust		"""
5357842e56b97ce677b83bdab09cda48bc2d89ac75aJust		from fontTools.misc import textTools
5367842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return textTools.caselessSort(self.getGlyphOrder())
5377842e56b97ce677b83bdab09cda48bc2d89ac75aJust
538dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod	def getGlyphName(self, glyphID, requireReal=False):
5390b63b28615933a1332ea55ce4f5daedf5269192cjvr		try:
5400b63b28615933a1332ea55ce4f5daedf5269192cjvr			return self.getGlyphOrder()[glyphID]
5410b63b28615933a1332ea55ce4f5daedf5269192cjvr		except IndexError:
542823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if requireReal or not self.allowVID:
543823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# XXX The ??.W8.otf font that ships with OSX uses higher glyphIDs in
544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# the cmap table than there are glyphs. I don't think it's legal...
545823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				return "glyph%.5d" % glyphID
546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			else:
547823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				# user intends virtual GID support
548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				try:
549823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					glyphName = self.VIDDict[glyphID]
550823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				except KeyError:
551823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					glyphName  ="glyph%.5d" % glyphID
552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					self.last_vid = min(glyphID, self.last_vid )
553823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					self.reverseVIDDict[glyphName] = glyphID
554823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					self.VIDDict[glyphID] = glyphName
555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				return glyphName
556823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
557dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod	def getGlyphID(self, glyphName, requireReal=False):
5587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not hasattr(self, "_reverseGlyphOrderDict"):
5597842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._buildReverseGlyphOrderDict()
5607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphOrder = self.getGlyphOrder()
5617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		d = self._reverseGlyphOrderDict
562bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if glyphName not in d:
5637842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if glyphName in glyphOrder:
5647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self._buildReverseGlyphOrderDict()
5657842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return self.getGlyphID(glyphName)
5667842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
567e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod				if requireReal:
568cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod					raise KeyError(glyphName)
569e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod				elif not self.allowVID:
570e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod					# Handle glyphXXX only
571e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod					if glyphName[:5] == "glyph":
572e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod						try:
573e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod							return int(glyphName[5:])
574e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod						except (NameError, ValueError):
575e5bee3716e537cf53d3f0fe7a58cf76e78e7b7b9Behdad Esfahbod							raise KeyError(glyphName)
576823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				else:
577823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					# user intends virtual GID support
578823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					try:
579823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						glyphID = self.reverseVIDDict[glyphName]
580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					except KeyError:
581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						# if name is in glyphXXX format, use the specified name.
582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						if glyphName[:5] == "glyph":
583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							try:
584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								glyphID = int(glyphName[5:])
585823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							except (NameError, ValueError):
586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr								glyphID = None
5879e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod						if glyphID is None:
588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							glyphID = self.last_vid -1
589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr							self.last_vid = glyphID
590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						self.reverseVIDDict[glyphName] = glyphID
591823f8cd15f16bb9dc3991c2672f16dd90579711bjvr						self.VIDDict[glyphID] = glyphName
592823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					return glyphID
593823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
5947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphID = d[glyphName]
595180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if glyphName != glyphOrder[glyphID]:
5967842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._buildReverseGlyphOrderDict()
5977842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return self.getGlyphID(glyphName)
5987842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return glyphID
599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
600dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod	def getReverseGlyphMap(self, rebuild=False):
601823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
602823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			self._buildReverseGlyphOrderDict()
603823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return self._reverseGlyphOrderDict
604823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
6057842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def _buildReverseGlyphOrderDict(self):
6067842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._reverseGlyphOrderDict = d = {}
6077842e56b97ce677b83bdab09cda48bc2d89ac75aJust		glyphOrder = self.getGlyphOrder()
6087842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for glyphID in range(len(glyphOrder)):
6097842e56b97ce677b83bdab09cda48bc2d89ac75aJust			d[glyphOrder[glyphID]] = glyphID
6107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
6117842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def _writeTable(self, tag, writer, done):
6127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Internal helper function for self.save(). Keeps track of
6137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		inter-table dependencies.
6147842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
6157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if tag in done:
6167842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return
6178307fa42cd145bb1b961ff942bbe3eef63908e55jvr		tableClass = getTableClass(tag)
6188307fa42cd145bb1b961ff942bbe3eef63908e55jvr		for masterTable in tableClass.dependencies:
6197842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if masterTable not in done:
620bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod				if masterTable in self:
6217842e56b97ce677b83bdab09cda48bc2d89ac75aJust					self._writeTable(masterTable, writer, done)
6227842e56b97ce677b83bdab09cda48bc2d89ac75aJust				else:
6237842e56b97ce677b83bdab09cda48bc2d89ac75aJust					done.append(masterTable)
624cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr		tabledata = self.getTableData(tag)
6257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.verbose:
6267842e56b97ce677b83bdab09cda48bc2d89ac75aJust			debugmsg("writing '%s' table to disk" % tag)
6277842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer[tag] = tabledata
6287842e56b97ce677b83bdab09cda48bc2d89ac75aJust		done.append(tag)
6297842e56b97ce677b83bdab09cda48bc2d89ac75aJust
630cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr	def getTableData(self, tag):
631cf4b3b3d9b0f8b9777097a86810c842e7042d516jvr		"""Returns raw table data, whether compiled or directly read from disk.
6327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
6335cf40083364e1d2dce119de25cb42ce69d2fb53cBehdad Esfahbod		tag = Tag(tag)
6347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.isLoaded(tag):
6357842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self.verbose:
6367842e56b97ce677b83bdab09cda48bc2d89ac75aJust				debugmsg("compiling '%s' table" % tag)
6377842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return self.tables[tag].compile(self)
638bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		elif self.reader and tag in self.reader:
6397842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self.verbose:
6406ab979cacaa9c15666a526e05c669b7f87bb6de9jvr				debugmsg("Reading '%s' table from disk" % tag)
6417842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return self.reader[tag]
6427842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
643cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise KeyError(tag)
6448df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
645dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod	def getGlyphSet(self, preferCFF=True):
6468df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		"""Return a generic GlyphSet, which is a dict-like object
6478df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		mapping glyph names to glyph objects. The returned glyph objects
6488df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		have a .draw() method that supports the Pen protocol, and will
6498df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		have an attribute named 'width', but only *after* the .draw() method
6508df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		has been called.
6518df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6528df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		If the font is CFF-based, the outlines will be taken from the 'CFF '
6538df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		table. Otherwise the outlines will be taken from the 'glyf' table.
6548df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		If the font contains both a 'CFF ' and a 'glyf' table, you can use
6558df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		the 'preferCFF' argument to specify which one should be taken.
6568df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		"""
657bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if preferCFF and "CFF " in self:
658c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod			return list(self["CFF "].cff.values())[0].CharStrings
659bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if "glyf" in self:
6608df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			return _TTGlyphSet(self)
661bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if "CFF " in self:
662c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod			return list(self["CFF "].cff.values())[0].CharStrings
663cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise TTLibError("Font contains no outlines")
6648df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6658df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
666e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass _TTGlyphSet(object):
6678df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6688df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	"""Generic dict-like GlyphSet class, meant as a TrueType counterpart
6698df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	to CFF's CharString dict. See TTFont.getGlyphSet().
6708df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	"""
6718df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6728df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	# This class is distinct from the 'glyf' table itself because we need
6738df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	# access to the 'hmtx' table, which could cause a dependency problem
6748df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	# there when reading from XML.
6758df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6768df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def __init__(self, ttFont):
6778df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		self._ttFont = ttFont
6788df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6798df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def keys(self):
680c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		return list(self._ttFont["glyf"].keys())
6818df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6828df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def has_key(self, glyphName):
683bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		return glyphName in self._ttFont["glyf"]
6848df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6858df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	__contains__ = has_key
6868df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6878df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def __getitem__(self, glyphName):
6888df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		return _TTGlyph(glyphName, self._ttFont)
6898df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
6902e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr	def get(self, glyphName, default=None):
6912e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr		try:
6922e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			return self[glyphName]
6932e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr		except KeyError:
6942e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			return default
6952e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr
6968df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
697e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass _TTGlyph(object):
6988df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
699d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr	"""Wrapper for a TrueType glyph that supports the Pen protocol, meaning
700d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr	that it has a .draw() method that takes a pen object as its only
701d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr	argument. Additionally there is a 'width' attribute.
7028df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	"""
7038df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
7048df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def __init__(self, glyphName, ttFont):
7058df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		self._glyphName = glyphName
7068df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		self._ttFont = ttFont
707d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr		self.width, self.lsb = self._ttFont['hmtx'][self._glyphName]
7088df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr
7098df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr	def draw(self, pen):
710d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr		"""Draw the glyph onto Pen. See fontTools.pens.basePen for details
711d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr		how that works.
712d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr		"""
7138df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		glyfTable = self._ttFont['glyf']
7148df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		glyph = glyfTable[self._glyphName]
7158df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		if hasattr(glyph, "xMin"):
716d028b7be3cffa4ce796e494a9c9ea0f11a432e36jvr			offset = self.lsb - glyph.xMin
7178df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		else:
7188df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			offset = 0
7198df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		if glyph.isComposite():
7208df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			for component in glyph:
7218df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				glyphName, transform = component.getComponentInfo()
7228df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				pen.addComponent(glyphName, transform)
7238df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr		else:
7248df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			coordinates, endPts, flags = glyph.getCoordinates(glyfTable)
7258df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			if offset:
7268df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				coordinates = coordinates + (offset, 0)
7278df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			start = 0
7288df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr			for end in endPts:
7298df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				end = end + 1
7308df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				contour = coordinates[start:end].tolist()
7318df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				cFlags = flags[start:end].tolist()
7328df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				start = end
7338df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				if 1 not in cFlags:
7348df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					# There is not a single on-curve point on the curve,
7358df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					# use pen.qCurveTo's special case by specifying None
7368df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					# as the on-curve point.
7378df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					contour.append(None)
7388df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					pen.qCurveTo(*contour)
7398df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				else:
7401c9917bcf4fc789b0addc1dc078674972f13660ejvr					# Shuffle the points so that contour the is guaranteed
7411c9917bcf4fc789b0addc1dc078674972f13660ejvr					# to *end* in an on-curve point, which we'll use for
7421c9917bcf4fc789b0addc1dc078674972f13660ejvr					# the moveTo.
7438df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					firstOnCurve = cFlags.index(1) + 1
7448df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					contour = contour[firstOnCurve:] + contour[:firstOnCurve]
7458df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					cFlags = cFlags[firstOnCurve:] + cFlags[:firstOnCurve]
7468df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					pen.moveTo(contour[-1])
7478df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr					while contour:
7488df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr						nextOnCurve = cFlags.index(1) + 1
7498df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr						if nextOnCurve == 1:
7508df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr							pen.lineTo(contour[0])
7518df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr						else:
7528df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr							pen.qCurveTo(*contour[:nextOnCurve])
7538df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr						contour = contour[nextOnCurve:]
7548df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr						cFlags = cFlags[nextOnCurve:]
7558df8f6385e045bc70f538a0d047ea6d13c9cacf0jvr				pen.closePath()
7567842e56b97ce677b83bdab09cda48bc2d89ac75aJust
7577842e56b97ce677b83bdab09cda48bc2d89ac75aJust
758e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass GlyphOrder(object):
7590011bb691018c7feed1f8c3554d88b9ae096e127jvr
76022f068929cbc8f3e5d18931c69a14d757eaa4de1jvr	"""A pseudo table. The glyph order isn't in the font as a separate
76122f068929cbc8f3e5d18931c69a14d757eaa4de1jvr	table, but it's nice to present it as such in the TTX format.
7620011bb691018c7feed1f8c3554d88b9ae096e127jvr	"""
7630011bb691018c7feed1f8c3554d88b9ae096e127jvr
76450d6c7298ad51eade3a2b4f693ff8dff7913213eBehdad Esfahbod	def __init__(self, tag=None):
765c4af3964fbcfefb562fcc72171f5b48bb5065040Behdad Esfahbod		pass
7660011bb691018c7feed1f8c3554d88b9ae096e127jvr
7670011bb691018c7feed1f8c3554d88b9ae096e127jvr	def toXML(self, writer, ttFont):
768c4af3964fbcfefb562fcc72171f5b48bb5065040Behdad Esfahbod		glyphOrder = ttFont.getGlyphOrder()
7694e5af60930726d06a58a30bae45bb27ae50aea77jvr		writer.comment("The 'id' attribute is only for humans; "
7704e5af60930726d06a58a30bae45bb27ae50aea77jvr				"it is ignored when parsed.")
7710011bb691018c7feed1f8c3554d88b9ae096e127jvr		writer.newline()
772c4af3964fbcfefb562fcc72171f5b48bb5065040Behdad Esfahbod		for i in range(len(glyphOrder)):
773c4af3964fbcfefb562fcc72171f5b48bb5065040Behdad Esfahbod			glyphName = glyphOrder[i]
7740011bb691018c7feed1f8c3554d88b9ae096e127jvr			writer.simpletag("GlyphID", id=i, name=glyphName)
7750011bb691018c7feed1f8c3554d88b9ae096e127jvr			writer.newline()
7760011bb691018c7feed1f8c3554d88b9ae096e127jvr
7773a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
7780011bb691018c7feed1f8c3554d88b9ae096e127jvr		if not hasattr(self, "glyphOrder"):
7790011bb691018c7feed1f8c3554d88b9ae096e127jvr			self.glyphOrder = []
7800011bb691018c7feed1f8c3554d88b9ae096e127jvr			ttFont.setGlyphOrder(self.glyphOrder)
7810011bb691018c7feed1f8c3554d88b9ae096e127jvr		if name == "GlyphID":
7820011bb691018c7feed1f8c3554d88b9ae096e127jvr			self.glyphOrder.append(attrs["name"])
7830011bb691018c7feed1f8c3554d88b9ae096e127jvr
7840011bb691018c7feed1f8c3554d88b9ae096e127jvr
7857842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef getTableModule(tag):
7867842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Fetch the packer/unpacker module for a table.
7877842e56b97ce677b83bdab09cda48bc2d89ac75aJust	Return None when no module is found.
7887842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
7892b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod	from . import tables
7908307fa42cd145bb1b961ff942bbe3eef63908e55jvr	pyTag = tagToIdentifier(tag)
7917842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
7920bb3ba522291076df18825a63c2f367d8ce3e48ajvr		__import__("fontTools.ttLib.tables." + pyTag)
793223273f7923ef25e8b697b378a4b89ab1f88674fBehdad Esfahbod	except ImportError as err:
794400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		# If pyTag is found in the ImportError message,
795400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		# means table is not implemented.  If it's not
796400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		# there, then some other module is missing, don't
797400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		# suppress the error.
798400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		if str(err).find(pyTag) >= 0:
799400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod			return None
800400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod		else:
801400e9539fd5968fcbfeeb36abaea54d9c8997872Behdad Esfahbod			raise err
8027842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
8038307fa42cd145bb1b961ff942bbe3eef63908e55jvr		return getattr(tables, pyTag)
8047842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8057842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8067842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef getTableClass(tag):
8077842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Fetch the packer/unpacker class for a table.
8087842e56b97ce677b83bdab09cda48bc2d89ac75aJust	Return None when no class is found.
8097842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
8107842e56b97ce677b83bdab09cda48bc2d89ac75aJust	module = getTableModule(tag)
8117842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if module is None:
8122b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod		from .tables.DefaultTable import DefaultTable
8137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return DefaultTable
8148307fa42cd145bb1b961ff942bbe3eef63908e55jvr	pyTag = tagToIdentifier(tag)
8158307fa42cd145bb1b961ff942bbe3eef63908e55jvr	tableClass = getattr(module, "table_" + pyTag)
8168307fa42cd145bb1b961ff942bbe3eef63908e55jvr	return tableClass
8177842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
819d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahboddef getClassTag(klass):
820d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod	"""Fetch the table tag for a class object."""
821d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod	name = klass.__name__
822d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod	assert name[:6] == 'table_'
823d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod	name = name[6:] # Chop 'table_'
824d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod	return identifierToTag(name)
825d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod
826d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod
827d0a31f5a43c4743671c4941fe45dda059aada0beBehdad Esfahbod
8288307fa42cd145bb1b961ff942bbe3eef63908e55jvrdef newTable(tag):
8297842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Return a new instance of a table."""
8308307fa42cd145bb1b961ff942bbe3eef63908e55jvr	tableClass = getTableClass(tag)
8318307fa42cd145bb1b961ff942bbe3eef63908e55jvr	return tableClass(tag)
8327842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8337842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8347842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef _escapechar(c):
8358307fa42cd145bb1b961ff942bbe3eef63908e55jvr	"""Helper function for tagToIdentifier()"""
8367842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import re
8377842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if re.match("[a-z0-9]", c):
8387842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return "_" + c
8397842e56b97ce677b83bdab09cda48bc2d89ac75aJust	elif re.match("[A-Z]", c):
8407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return c + "_"
8417842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
842319c5fd10e2ea84304bd299b7483e05b5b0d5480Behdad Esfahbod		return hex(byteord(c))[2:]
8437842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8458307fa42cd145bb1b961ff942bbe3eef63908e55jvrdef tagToIdentifier(tag):
8467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Convert a table tag to a valid (but UGLY) python identifier,
8477842e56b97ce677b83bdab09cda48bc2d89ac75aJust	as well as a filename that's guaranteed to be unique even on a
8487842e56b97ce677b83bdab09cda48bc2d89ac75aJust	caseless file system. Each character is mapped to two characters.
8497842e56b97ce677b83bdab09cda48bc2d89ac75aJust	Lowercase letters get an underscore before the letter, uppercase
8507842e56b97ce677b83bdab09cda48bc2d89ac75aJust	letters get an underscore after the letter. Trailing spaces are
8517842e56b97ce677b83bdab09cda48bc2d89ac75aJust	trimmed. Illegal characters are escaped as two hex bytes. If the
8527842e56b97ce677b83bdab09cda48bc2d89ac75aJust	result starts with a number (as the result of a hex escape), an
8537842e56b97ce677b83bdab09cda48bc2d89ac75aJust	extra underscore is prepended. Examples:
8547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		'glyf' -> '_g_l_y_f'
8557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		'cvt ' -> '_c_v_t'
8567842e56b97ce677b83bdab09cda48bc2d89ac75aJust		'OS/2' -> 'O_S_2f_2'
8577842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
8587842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import re
859d2f5d2f8b4bce13ff7811cbf4c1ede81b73b8192Behdad Esfahbod	tag = Tag(tag)
860e5ae28e8424192ce7ea19897473601f590dc80eajvr	if tag == "GlyphOrder":
861e5ae28e8424192ce7ea19897473601f590dc80eajvr		return tag
8627842e56b97ce677b83bdab09cda48bc2d89ac75aJust	assert len(tag) == 4, "tag should be 4 characters long"
8637842e56b97ce677b83bdab09cda48bc2d89ac75aJust	while len(tag) > 1 and tag[-1] == ' ':
8647842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tag = tag[:-1]
8657842e56b97ce677b83bdab09cda48bc2d89ac75aJust	ident = ""
8667842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for c in tag:
8677842e56b97ce677b83bdab09cda48bc2d89ac75aJust		ident = ident + _escapechar(c)
8687842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if re.match("[0-9]", ident):
8697842e56b97ce677b83bdab09cda48bc2d89ac75aJust		ident = "_" + ident
8707842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return ident
8717842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8727842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8738307fa42cd145bb1b961ff942bbe3eef63908e55jvrdef identifierToTag(ident):
8748307fa42cd145bb1b961ff942bbe3eef63908e55jvr	"""the opposite of tagToIdentifier()"""
875e5ae28e8424192ce7ea19897473601f590dc80eajvr	if ident == "GlyphOrder":
876e5ae28e8424192ce7ea19897473601f590dc80eajvr		return ident
8777842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if len(ident) % 2 and ident[0] == "_":
8787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		ident = ident[1:]
8797842e56b97ce677b83bdab09cda48bc2d89ac75aJust	assert not (len(ident) % 2)
8807842e56b97ce677b83bdab09cda48bc2d89ac75aJust	tag = ""
8817842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for i in range(0, len(ident), 2):
8827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if ident[i] == "_":
8837842e56b97ce677b83bdab09cda48bc2d89ac75aJust			tag = tag + ident[i+1]
8847842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif ident[i+1] == "_":
8857842e56b97ce677b83bdab09cda48bc2d89ac75aJust			tag = tag + ident[i]
8867842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
8877842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# assume hex
8884bb028e44f6fc320a5935e80730002ca54d15ec3Behdad Esfahbod			tag = tag + chr(int(ident[i:i+2], 16))
8897842e56b97ce677b83bdab09cda48bc2d89ac75aJust	# append trailing spaces
8907842e56b97ce677b83bdab09cda48bc2d89ac75aJust	tag = tag + (4 - len(tag)) * ' '
891d2f5d2f8b4bce13ff7811cbf4c1ede81b73b8192Behdad Esfahbod	return Tag(tag)
8927842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8937842e56b97ce677b83bdab09cda48bc2d89ac75aJust
8948307fa42cd145bb1b961ff942bbe3eef63908e55jvrdef tagToXML(tag):
8958307fa42cd145bb1b961ff942bbe3eef63908e55jvr	"""Similarly to tagToIdentifier(), this converts a TT tag
8967842e56b97ce677b83bdab09cda48bc2d89ac75aJust	to a valid XML element name. Since XML element names are
8977842e56b97ce677b83bdab09cda48bc2d89ac75aJust	case sensitive, this is a fairly simple/readable translation.
8987842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
8997dcc91674e8df02f80711ee2587fd2a6a205ca94Just	import re
900d2f5d2f8b4bce13ff7811cbf4c1ede81b73b8192Behdad Esfahbod	tag = Tag(tag)
9017842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if tag == "OS/2":
9027842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return "OS_2"
9030011bb691018c7feed1f8c3554d88b9ae096e127jvr	elif tag == "GlyphOrder":
90428ae1962292b66ad67117aef2a99d5735a70b779jvr		return tag
9057842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if re.match("[A-Za-z_][A-Za-z_0-9]* *$", tag):
90614fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod		return tag.strip()
9077842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
9088307fa42cd145bb1b961ff942bbe3eef63908e55jvr		return tagToIdentifier(tag)
9097842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9118307fa42cd145bb1b961ff942bbe3eef63908e55jvrdef xmlToTag(tag):
9128307fa42cd145bb1b961ff942bbe3eef63908e55jvr	"""The opposite of tagToXML()"""
9137842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if tag == "OS_2":
914153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod		return Tag("OS/2")
9157842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if len(tag) == 8:
9168307fa42cd145bb1b961ff942bbe3eef63908e55jvr		return identifierToTag(tag)
9177842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
918153ec402094adbea673e914385b87f1d99191d0bBehdad Esfahbod		return Tag(tag + " " * (4 - len(tag)))
9197842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9207842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9217842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef debugmsg(msg):
9227842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import time
9233ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod	print(msg + time.strftime("  (%H:%M:%S)", time.localtime(time.time())))
9247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
925700df031313741e79b0647f045949a1a04d03371jvr
92628ae1962292b66ad67117aef2a99d5735a70b779jvr# Table order as recommended in the OpenType specification 1.4
92728ae1962292b66ad67117aef2a99d5735a70b779jvrTTFTableOrder = ["head", "hhea", "maxp", "OS/2", "hmtx", "LTSH", "VDMX",
92828ae1962292b66ad67117aef2a99d5735a70b779jvr                  "hdmx", "cmap", "fpgm", "prep", "cvt ", "loca", "glyf",
92928ae1962292b66ad67117aef2a99d5735a70b779jvr                  "kern", "name", "post", "gasp", "PCLT"]
930700df031313741e79b0647f045949a1a04d03371jvr
93128ae1962292b66ad67117aef2a99d5735a70b779jvrOTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post",
93228ae1962292b66ad67117aef2a99d5735a70b779jvr                  "CFF "]
933700df031313741e79b0647f045949a1a04d03371jvr
93428ae1962292b66ad67117aef2a99d5735a70b779jvrdef sortedTagList(tagList, tableOrder=None):
93528ae1962292b66ad67117aef2a99d5735a70b779jvr	"""Return a sorted copy of tagList, sorted according to the OpenType
93628ae1962292b66ad67117aef2a99d5735a70b779jvr	specification, or according to a custom tableOrder. If given and not
93728ae1962292b66ad67117aef2a99d5735a70b779jvr	None, tableOrder needs to be a list of tag names.
93828ae1962292b66ad67117aef2a99d5735a70b779jvr	"""
939ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod	tagList = sorted(tagList)
94028ae1962292b66ad67117aef2a99d5735a70b779jvr	if tableOrder is None:
94128ae1962292b66ad67117aef2a99d5735a70b779jvr		if "DSIG" in tagList:
94228ae1962292b66ad67117aef2a99d5735a70b779jvr			# DSIG should be last (XXX spec reference?)
94328ae1962292b66ad67117aef2a99d5735a70b779jvr			tagList.remove("DSIG")
94428ae1962292b66ad67117aef2a99d5735a70b779jvr			tagList.append("DSIG")
94528ae1962292b66ad67117aef2a99d5735a70b779jvr		if "CFF " in tagList:
94628ae1962292b66ad67117aef2a99d5735a70b779jvr			tableOrder = OTFTableOrder
94728ae1962292b66ad67117aef2a99d5735a70b779jvr		else:
94828ae1962292b66ad67117aef2a99d5735a70b779jvr			tableOrder = TTFTableOrder
94928ae1962292b66ad67117aef2a99d5735a70b779jvr	orderedTables = []
95028ae1962292b66ad67117aef2a99d5735a70b779jvr	for tag in tableOrder:
95128ae1962292b66ad67117aef2a99d5735a70b779jvr		if tag in tagList:
95228ae1962292b66ad67117aef2a99d5735a70b779jvr			orderedTables.append(tag)
95328ae1962292b66ad67117aef2a99d5735a70b779jvr			tagList.remove(tag)
95428ae1962292b66ad67117aef2a99d5735a70b779jvr	orderedTables.extend(tagList)
95528ae1962292b66ad67117aef2a99d5735a70b779jvr	return orderedTables
95628ae1962292b66ad67117aef2a99d5735a70b779jvr
95728ae1962292b66ad67117aef2a99d5735a70b779jvr
958dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahboddef reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
95928ae1962292b66ad67117aef2a99d5735a70b779jvr	"""Rewrite a font file, ordering the tables as recommended by the
96028ae1962292b66ad67117aef2a99d5735a70b779jvr	OpenType specification 1.4.
96128ae1962292b66ad67117aef2a99d5735a70b779jvr	"""
96228ae1962292b66ad67117aef2a99d5735a70b779jvr	from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
96328ae1962292b66ad67117aef2a99d5735a70b779jvr	reader = SFNTReader(inFile, checkChecksums=checkChecksums)
964d7921e33d9d1944c394e6c17b3746e7108dd1da4Roozbeh Pournader	writer = SFNTWriter(outFile, len(reader.tables), reader.sfntVersion, reader.flavor, reader.flavorData)
965c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod	tables = list(reader.keys())
96628ae1962292b66ad67117aef2a99d5735a70b779jvr	for tag in sortedTagList(tables, tableOrder):
96728ae1962292b66ad67117aef2a99d5735a70b779jvr		writer[tag] = reader[tag]
96828ae1962292b66ad67117aef2a99d5735a70b779jvr	writer.close()
96962dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod
97062dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod
97162dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahboddef maxPowerOfTwo(x):
97262dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	"""Return the highest exponent of two, so that
97362dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	(2 ** exponent) <= x.  Return 0 if x is 0.
97462dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	"""
97562dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	exponent = 0
97662dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	while x:
97762dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod		x = x >> 1
97862dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod		exponent = exponent + 1
97962dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	return max(exponent - 1, 0)
98062dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod
98162dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod
98262dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahboddef getSearchRange(n, itemSize):
98362dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	"""Calculate searchRange, entrySelector, rangeShift.
98462dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	"""
98562dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	# This stuff needs to be stored in the file, because?
98662dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	exponent = maxPowerOfTwo(n)
98762dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	searchRange = (2 ** exponent) * itemSize
98862dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	entrySelector = exponent
98962dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	rangeShift = max(0, n * itemSize - searchRange)
99062dd7b2a0e0ab1109b56572c568ef5f582d8a0fdBehdad Esfahbod	return searchRange, entrySelector, rangeShift
991