1d4d151390d1288f8d2df30f6dfa26a309c7334dajvr"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various
2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrOpenType subtables.
3d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
464b5c80e80444a124da335e8d4d208bffcf2737bjvrMost are constructed upon import from data in otData.py, all are populated with
564b5c80e80444a124da335e8d4d208bffcf2737bjvrconverter objects from otConverters.py.
6d4d151390d1288f8d2df30f6dfa26a309c7334dajvr"""
71ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
830e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
92b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .otBase import BaseTable, FormatSwitchingBaseTable
1030e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport operator
116e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbodimport warnings
12d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
14d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass LookupOrder(BaseTable):
15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
17d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FeatureParams(BaseTable):
189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
199e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	def compile(self, writer, font):
20f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod		assert featureParamTypes.get(writer['FeatureTag']) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__)
219e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		BaseTable.compile(self, writer, font)
229e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
23d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod	def toXML(self, xmlWriter, font, attrs=None, name=None):
24d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod		BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__)
25d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod
269e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsSize(FeatureParams):
279e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	pass
289e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsStylisticSet(FeatureParams):
309e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	pass
319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
329e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsCharacterVariants(FeatureParams):
339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	pass
34d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
35d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvrclass Coverage(FormatSwitchingBaseTable):
36d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
37d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	# manual implementation to get rid of glyphID dependencies
38d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
391a53beb7b4bb252dc002dcdc2de70517d4727494jvr	def postRead(self, rawTable, font):
40d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		if self.Format == 1:
4143f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod			# TODO only allow glyphs that are valid?
421a53beb7b4bb252dc002dcdc2de70517d4727494jvr			self.glyphs = rawTable["GlyphArray"]
43d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		elif self.Format == 2:
44d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			glyphs = self.glyphs = []
451a53beb7b4bb252dc002dcdc2de70517d4727494jvr			ranges = rawTable["RangeRecord"]
46183afe6408fade5d45dd45d062293c416a4adef1Behdad Esfahbod			glyphOrder = font.getGlyphOrder()
4717700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			# Some SIL fonts have coverage entries that don't have sorted
4817700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			# StartCoverageIndex.  If it is so, fixup and warn.  We undo
4917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			# this when writing font out.
50b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
5117700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			if ranges != sorted_ranges:
5217700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod				warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
5317700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod				ranges = sorted_ranges
5417700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			del sorted_ranges
55d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			for r in ranges:
56d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				assert r.StartCoverageIndex == len(glyphs), \
57d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					(r.StartCoverageIndex, len(glyphs))
58d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				start = r.Start
59d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				end = r.End
606e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod				try:
61dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod					startID = font.getGlyphID(start, requireReal=True)
626e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod				except KeyError:
636e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod					warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
646e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod					continue
656e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod				try:
6643f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod					endID = font.getGlyphID(end, requireReal=True) + 1
676e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod				except KeyError:
68650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# Apparently some tools use 65535 to "match all" the range
69650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					if end != 'glyph65535':
70650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod						warnings.warn("Coverage table has end glyph ID out of range: %s." % end)
71650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
72650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# but none that we have seen in the wild.
736e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod					endID = len(glyphOrder)
7443f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod				glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
75d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		else:
761a53beb7b4bb252dc002dcdc2de70517d4727494jvr			assert 0, "unknown format: %s" % self.Format
77e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		del self.Format # Don't need this anymore
78d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
79d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def preWrite(self, font):
8031ae380735dbf75a460df002a7fc2bcd81e28153jvr		glyphs = getattr(self, "glyphs", None)
81257fba71729b4552a73f296b8fbd5b6224cdcabbjvr		if glyphs is None:
82257fba71729b4552a73f296b8fbd5b6224cdcabbjvr			glyphs = self.glyphs = []
83d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		format = 1
84a138467da399c071dbd78682938642b8e8d8a4cejvr		rawTable = {"GlyphArray": glyphs}
85823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		getGlyphID = font.getGlyphID
86a138467da399c071dbd78682938642b8e8d8a4cejvr		if glyphs:
871a53beb7b4bb252dc002dcdc2de70517d4727494jvr			# find out whether Format 2 is more compact or not
88823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
8917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			brokenOrder = sorted(glyphIDs) != glyphIDs
90d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
91d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			last = glyphIDs[0]
92d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			ranges = [[last]]
93d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			for glyphID in glyphIDs[1:]:
94a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr				if glyphID != last + 1:
95d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges[-1].append(last)
96d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges.append([glyphID])
97d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				last = glyphID
98d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			ranges[-1].append(last)
99d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
10017700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod			if brokenOrder or len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
101d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				# Format 2 is more compact
102d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				index = 0
103d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				for i in range(len(ranges)):
104d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					start, end = ranges[i]
105d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r = RangeRecord()
10617700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod					r.StartID = start
107d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.Start = font.getGlyphName(start)
108d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.End = font.getGlyphName(end)
109d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					r.StartCoverageIndex = index
110d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					ranges[i] = r
111d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr					index = index + end - start + 1
11217700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod				if brokenOrder:
11317700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
114b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod					ranges.sort(key=lambda a: a.StartID)
11517700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod				for r in ranges:
11617700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod					del r.StartID
117d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr				format = 2
1181a53beb7b4bb252dc002dcdc2de70517d4727494jvr				rawTable = {"RangeRecord": ranges}
119d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			#else:
120d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			#	fallthrough; Format 1 is more compact
121d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		self.Format = format
1221a53beb7b4bb252dc002dcdc2de70517d4727494jvr		return rawTable
123d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
124d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr	def toXML2(self, xmlWriter, font):
125a138467da399c071dbd78682938642b8e8d8a4cejvr		for glyphName in getattr(self, "glyphs", []):
126d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			xmlWriter.simpletag("Glyph", value=glyphName)
127d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			xmlWriter.newline()
128d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr
1293a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
130d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		glyphs = getattr(self, "glyphs", None)
131d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		if glyphs is None:
132d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			glyphs = []
133d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr			self.glyphs = glyphs
134d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr		glyphs.append(attrs["value"])
135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
137823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef doModulo(value):
138823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if value < 0:
139823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return value + 65536
140823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return value
141823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
1421d6360af4c553a7b26447b0e1e96f7b701c9325ejvrclass SingleSubst(FormatSwitchingBaseTable):
1431d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
1441d6360af4c553a7b26447b0e1e96f7b701c9325ejvr	def postRead(self, rawTable, font):
1451d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		mapping = {}
1461fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lenMapping = len(input)
1481d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		if self.Format == 1:
1491d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			delta = rawTable["DeltaGlyphID"]
150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			inputGIDS =  [ font.getGlyphID(name) for name in input ]
151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
152e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			outGIDS = map(doModulo, outGIDS)
153823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
154e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
1551d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		elif self.Format == 2:
1561d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			assert len(input) == rawTable["GlyphCount"], \
1571d6360af4c553a7b26447b0e1e96f7b701c9325ejvr					"invalid SingleSubstFormat2 table"
1581d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			subst = rawTable["Substitute"]
159e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
1601d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		else:
1611d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			assert 0, "unknown format: %s" % self.Format
1621d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		self.mapping = mapping
163e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		del self.Format # Don't need this anymore
1641d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
1651d6360af4c553a7b26447b0e1e96f7b701c9325ejvr	def preWrite(self, font):
16631ae380735dbf75a460df002a7fc2bcd81e28153jvr		mapping = getattr(self, "mapping", None)
16731ae380735dbf75a460df002a7fc2bcd81e28153jvr		if mapping is None:
16831ae380735dbf75a460df002a7fc2bcd81e28153jvr			mapping = self.mapping = {}
169c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		items = list(mapping.items())
170823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		getGlyphID = font.getGlyphID
171e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod		gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items]
172ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		sortableItems = sorted(zip(gidItems, items))
173823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
174823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# figure out format
1751d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		format = 2
1761d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		delta = None
177823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for inID, outID in gidItems:
1781d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			if delta is None:
1791d6360af4c553a7b26447b0e1e96f7b701c9325ejvr				delta = outID - inID
180e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod				if delta < -32768:
181e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod					delta += 65536
182e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod				elif delta > 32767:
183e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod					delta -= 65536
1841d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			else:
1851d6360af4c553a7b26447b0e1e96f7b701c9325ejvr				if delta != outID - inID:
1861d6360af4c553a7b26447b0e1e96f7b701c9325ejvr					break
1871d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		else:
1881d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			format = 1
189823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
1901d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		rawTable = {}
1911d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		self.Format = format
1921d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		cov = Coverage()
193823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		input =  [ item [1][0] for item in sortableItems]
194823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subst =  [ item [1][1] for item in sortableItems]
195823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		cov.glyphs = input
1961d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		rawTable["Coverage"] = cov
1971d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		if format == 1:
1981d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			assert delta is not None
1991d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			rawTable["DeltaGlyphID"] = delta
2001d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		else:
2011d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			rawTable["Substitute"] = subst
2021d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		return rawTable
2031d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
2041d6360af4c553a7b26447b0e1e96f7b701c9325ejvr	def toXML2(self, xmlWriter, font):
205ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		items = sorted(self.mapping.items())
2061d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		for inGlyph, outGlyph in items:
2071d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			xmlWriter.simpletag("Substitution",
2081d6360af4c553a7b26447b0e1e96f7b701c9325ejvr					[("in", inGlyph), ("out", outGlyph)])
2091d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			xmlWriter.newline()
2101d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
2113a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
2121d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		mapping = getattr(self, "mapping", None)
2131d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		if mapping is None:
2141d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			mapping = {}
2151d6360af4c553a7b26447b0e1e96f7b701c9325ejvr			self.mapping = mapping
2161d6360af4c553a7b26447b0e1e96f7b701c9325ejvr		mapping[attrs["in"]] = attrs["out"]
2171d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
2181d6360af4c553a7b26447b0e1e96f7b701c9325ejvr
219a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvrclass ClassDef(FormatSwitchingBaseTable):
220a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
221a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr	def postRead(self, rawTable, font):
222a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		classDefs = {}
22363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod		glyphOrder = font.getGlyphOrder()
224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
225a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		if self.Format == 1:
226a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			start = rawTable["StartGlyph"]
227823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			classList = rawTable["ClassValueArray"]
22863b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod			try:
22963b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				startID = font.getGlyphID(start, requireReal=True)
23063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod			except KeyError:
23163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
23263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				startID = len(glyphOrder)
23363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod			endID = startID + len(classList)
23463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod			if endID > len(glyphOrder):
23563b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
236650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod				# NOTE: We clobber out-of-range things here.  There are legit uses for those,
237650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod				# but none that we have seen in the wild.
23863b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				endID = len(glyphOrder)
239823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
24063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod			for glyphID, cls in zip(range(startID, endID), classList):
24163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				classDefs[glyphOrder[glyphID]] = cls
242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
243a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		elif self.Format == 2:
244a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			records = rawTable["ClassRangeRecord"]
245a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			for rec in records:
246a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr				start = rec.Start
247a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr				end = rec.End
248a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr				cls = rec.Class
24963b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				try:
25063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod					startID = font.getGlyphID(start, requireReal=True)
25163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				except KeyError:
25263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod					warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
25363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod					continue
25463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				try:
255b23b4cefda2acf417bfecf2135357554dbd32277Behdad Esfahbod					endID = font.getGlyphID(end, requireReal=True) + 1
25663b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				except KeyError:
257650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# Apparently some tools use 65535 to "match all" the range
258650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					if end != 'glyph65535':
259650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod						warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
260650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
261650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod					# but none that we have seen in the wild.
26263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod					endID = len(glyphOrder)
26363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod				for glyphID in range(startID, endID):
26463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod					classDefs[glyphOrder[glyphID]] = cls
265a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		else:
266a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			assert 0, "unknown format: %s" % self.Format
267a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		self.classDefs = classDefs
268e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		del self.Format # Don't need this anymore
269a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
270a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr	def preWrite(self, font):
27131ae380735dbf75a460df002a7fc2bcd81e28153jvr		classDefs = getattr(self, "classDefs", None)
27231ae380735dbf75a460df002a7fc2bcd81e28153jvr		if classDefs is None:
27331ae380735dbf75a460df002a7fc2bcd81e28153jvr			classDefs = self.classDefs = {}
274c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		items = list(classDefs.items())
27583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod		format = 2
27683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod		rawTable = {"ClassRangeRecord": []}
277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		getGlyphID = font.getGlyphID
278a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		for i in range(len(items)):
279a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			glyphName, cls = items[i]
280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			items[i] = getGlyphID(glyphName), glyphName, cls
281a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		items.sort()
28231ae380735dbf75a460df002a7fc2bcd81e28153jvr		if items:
28331ae380735dbf75a460df002a7fc2bcd81e28153jvr			last, lastName, lastCls = items[0]
28483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			ranges = [[lastCls, last, lastName]]
28531ae380735dbf75a460df002a7fc2bcd81e28153jvr			for glyphID, glyphName, cls in items[1:]:
28631ae380735dbf75a460df002a7fc2bcd81e28153jvr				if glyphID != last + 1 or cls != lastCls:
28783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					ranges[-1].extend([last, lastName])
28883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					ranges.append([cls, glyphID, glyphName])
28931ae380735dbf75a460df002a7fc2bcd81e28153jvr				last = glyphID
29031ae380735dbf75a460df002a7fc2bcd81e28153jvr				lastName = glyphName
29131ae380735dbf75a460df002a7fc2bcd81e28153jvr				lastCls = cls
29283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			ranges[-1].extend([last, lastName])
29383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod
29483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			startGlyph = ranges[0][1]
29583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			endGlyph = ranges[-1][3]
29683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			glyphCount = endGlyph - startGlyph + 1
29783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			if len(ranges) * 3 < glyphCount + 1:
29883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				# Format 2 is more compact
29983e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				for i in range(len(ranges)):
30083e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					cls, start, startName, end, endName = ranges[i]
30183e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					rec = ClassRangeRecord()
30283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					rec.Start = startName
30383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					rec.End = endName
30483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					rec.Class = cls
30583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					ranges[i] = rec
30683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				format = 2
30783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				rawTable = {"ClassRangeRecord": ranges}
30883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod			else:
30983e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				# Format 1 is more compact
31083e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				startGlyphName = ranges[0][2]
31183e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				classes = [0] * glyphCount
31283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				for cls, start, startName, end, endName in ranges:
31383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod					for g in range(start - startGlyph, end - startGlyph + 1):
31483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod						classes[g] = cls
31583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				format = 1
31683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod				rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
31783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod		self.Format = format
31883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod		return rawTable
319a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
320a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr	def toXML2(self, xmlWriter, font):
321ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		items = sorted(self.classDefs.items())
322a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		for glyphName, cls in items:
323a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
324a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			xmlWriter.newline()
325a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
3263a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
327a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		classDefs = getattr(self, "classDefs", None)
328a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		if classDefs is None:
329a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			classDefs = {}
330a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr			self.classDefs = classDefs
331a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr		classDefs[attrs["glyph"]] = int(attrs["class"])
332a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
333a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr
334b2486125e9438443a1419706c50958ab0676eb5ajvrclass AlternateSubst(FormatSwitchingBaseTable):
335b2486125e9438443a1419706c50958ab0676eb5ajvr
336b2486125e9438443a1419706c50958ab0676eb5ajvr	def postRead(self, rawTable, font):
337b2486125e9438443a1419706c50958ab0676eb5ajvr		alternates = {}
338b2486125e9438443a1419706c50958ab0676eb5ajvr		if self.Format == 1:
3391fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
340b2486125e9438443a1419706c50958ab0676eb5ajvr			alts = rawTable["AlternateSet"]
341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			if len(input) != len(alts):
342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				assert len(input) == len(alts)
343b2486125e9438443a1419706c50958ab0676eb5ajvr			for i in range(len(input)):
344b2486125e9438443a1419706c50958ab0676eb5ajvr				alternates[input[i]] = alts[i].Alternate
345b2486125e9438443a1419706c50958ab0676eb5ajvr		else:
346b2486125e9438443a1419706c50958ab0676eb5ajvr			assert 0, "unknown format: %s" % self.Format
347b2486125e9438443a1419706c50958ab0676eb5ajvr		self.alternates = alternates
348e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		del self.Format # Don't need this anymore
349b2486125e9438443a1419706c50958ab0676eb5ajvr
350b2486125e9438443a1419706c50958ab0676eb5ajvr	def preWrite(self, font):
351b2486125e9438443a1419706c50958ab0676eb5ajvr		self.Format = 1
35231ae380735dbf75a460df002a7fc2bcd81e28153jvr		alternates = getattr(self, "alternates", None)
35331ae380735dbf75a460df002a7fc2bcd81e28153jvr		if alternates is None:
35431ae380735dbf75a460df002a7fc2bcd81e28153jvr			alternates = self.alternates = {}
355c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		items = list(alternates.items())
356b2486125e9438443a1419706c50958ab0676eb5ajvr		for i in range(len(items)):
357b2486125e9438443a1419706c50958ab0676eb5ajvr			glyphName, set = items[i]
358b2486125e9438443a1419706c50958ab0676eb5ajvr			items[i] = font.getGlyphID(glyphName), glyphName, set
359b2486125e9438443a1419706c50958ab0676eb5ajvr		items.sort()
360b2486125e9438443a1419706c50958ab0676eb5ajvr		cov = Coverage()
361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		cov.glyphs = [ item[1] for item in items]
362b2486125e9438443a1419706c50958ab0676eb5ajvr		alternates = []
363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		setList = [ item[-1] for item in items]
364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for  set in setList:
365b2486125e9438443a1419706c50958ab0676eb5ajvr			alts = AlternateSet()
366b2486125e9438443a1419706c50958ab0676eb5ajvr			alts.Alternate = set
367b2486125e9438443a1419706c50958ab0676eb5ajvr			alternates.append(alts)
368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# a special case to deal with the fact that several hundred Adobe Japan1-5
369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# CJK fonts will overflow an offset if the coverage table isn't pushed to the end.
370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Also useful in that when splitting a sub-table because of an offset overflow
371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Allows packing more rules in subtable.
373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.sortCoverageLast = 1
374b2486125e9438443a1419706c50958ab0676eb5ajvr		return {"Coverage": cov, "AlternateSet": alternates}
375b2486125e9438443a1419706c50958ab0676eb5ajvr
376b2486125e9438443a1419706c50958ab0676eb5ajvr	def toXML2(self, xmlWriter, font):
377ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		items = sorted(self.alternates.items())
378b2486125e9438443a1419706c50958ab0676eb5ajvr		for glyphName, alternates in items:
379b2486125e9438443a1419706c50958ab0676eb5ajvr			xmlWriter.begintag("AlternateSet", glyph=glyphName)
380b2486125e9438443a1419706c50958ab0676eb5ajvr			xmlWriter.newline()
381b2486125e9438443a1419706c50958ab0676eb5ajvr			for alt in alternates:
382b2486125e9438443a1419706c50958ab0676eb5ajvr				xmlWriter.simpletag("Alternate", glyph=alt)
383b2486125e9438443a1419706c50958ab0676eb5ajvr				xmlWriter.newline()
384b2486125e9438443a1419706c50958ab0676eb5ajvr			xmlWriter.endtag("AlternateSet")
385b2486125e9438443a1419706c50958ab0676eb5ajvr			xmlWriter.newline()
386b2486125e9438443a1419706c50958ab0676eb5ajvr
3873a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
388b2486125e9438443a1419706c50958ab0676eb5ajvr		alternates = getattr(self, "alternates", None)
389b2486125e9438443a1419706c50958ab0676eb5ajvr		if alternates is None:
390b2486125e9438443a1419706c50958ab0676eb5ajvr			alternates = {}
391b2486125e9438443a1419706c50958ab0676eb5ajvr			self.alternates = alternates
392b2486125e9438443a1419706c50958ab0676eb5ajvr		glyphName = attrs["glyph"]
393b2486125e9438443a1419706c50958ab0676eb5ajvr		set = []
394b2486125e9438443a1419706c50958ab0676eb5ajvr		alternates[glyphName] = set
395b2486125e9438443a1419706c50958ab0676eb5ajvr		for element in content:
396b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
397b2486125e9438443a1419706c50958ab0676eb5ajvr				continue
398b2486125e9438443a1419706c50958ab0676eb5ajvr			name, attrs, content = element
399b2486125e9438443a1419706c50958ab0676eb5ajvr			set.append(attrs["glyph"])
400b2486125e9438443a1419706c50958ab0676eb5ajvr
401b2486125e9438443a1419706c50958ab0676eb5ajvr
402f2164abef39af7cfef989352fadfca628ed077b2jvrclass LigatureSubst(FormatSwitchingBaseTable):
403f2164abef39af7cfef989352fadfca628ed077b2jvr
404f2164abef39af7cfef989352fadfca628ed077b2jvr	def postRead(self, rawTable, font):
405f2164abef39af7cfef989352fadfca628ed077b2jvr		ligatures = {}
406f2164abef39af7cfef989352fadfca628ed077b2jvr		if self.Format == 1:
407ea82d6dfd194f6ff6dfc7a65e7d9a0f9d671d827Behdad Esfahbod			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
408f2164abef39af7cfef989352fadfca628ed077b2jvr			ligSets = rawTable["LigatureSet"]
409f2164abef39af7cfef989352fadfca628ed077b2jvr			assert len(input) == len(ligSets)
410f2164abef39af7cfef989352fadfca628ed077b2jvr			for i in range(len(input)):
411f2164abef39af7cfef989352fadfca628ed077b2jvr				ligatures[input[i]] = ligSets[i].Ligature
412f2164abef39af7cfef989352fadfca628ed077b2jvr		else:
413f2164abef39af7cfef989352fadfca628ed077b2jvr			assert 0, "unknown format: %s" % self.Format
414f2164abef39af7cfef989352fadfca628ed077b2jvr		self.ligatures = ligatures
415e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		del self.Format # Don't need this anymore
416f2164abef39af7cfef989352fadfca628ed077b2jvr
417f2164abef39af7cfef989352fadfca628ed077b2jvr	def preWrite(self, font):
418e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod		self.Format = 1
41931ae380735dbf75a460df002a7fc2bcd81e28153jvr		ligatures = getattr(self, "ligatures", None)
42031ae380735dbf75a460df002a7fc2bcd81e28153jvr		if ligatures is None:
42131ae380735dbf75a460df002a7fc2bcd81e28153jvr			ligatures = self.ligatures = {}
422c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod		items = list(ligatures.items())
423f2164abef39af7cfef989352fadfca628ed077b2jvr		for i in range(len(items)):
424f2164abef39af7cfef989352fadfca628ed077b2jvr			glyphName, set = items[i]
425f2164abef39af7cfef989352fadfca628ed077b2jvr			items[i] = font.getGlyphID(glyphName), glyphName, set
426f2164abef39af7cfef989352fadfca628ed077b2jvr		items.sort()
427f2164abef39af7cfef989352fadfca628ed077b2jvr		cov = Coverage()
428823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		cov.glyphs = [ item[1] for item in items]
429823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
430f2164abef39af7cfef989352fadfca628ed077b2jvr		ligSets = []
431823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		setList = [ item[-1] for item in items ]
432823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		for set in setList:
433f2164abef39af7cfef989352fadfca628ed077b2jvr			ligSet = LigatureSet()
434f2164abef39af7cfef989352fadfca628ed077b2jvr			ligs = ligSet.Ligature = []
435f2164abef39af7cfef989352fadfca628ed077b2jvr			for lig in set:
436f2164abef39af7cfef989352fadfca628ed077b2jvr				ligs.append(lig)
437f2164abef39af7cfef989352fadfca628ed077b2jvr			ligSets.append(ligSet)
438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Useful in that when splitting a sub-table because of an offset overflow
439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# I don't need to calculate the change in subtabl offset due to the coverage table size.
440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Allows packing more rules in subtable.
441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		self.sortCoverageLast = 1
442f2164abef39af7cfef989352fadfca628ed077b2jvr		return {"Coverage": cov, "LigatureSet": ligSets}
443f2164abef39af7cfef989352fadfca628ed077b2jvr
444f2164abef39af7cfef989352fadfca628ed077b2jvr	def toXML2(self, xmlWriter, font):
445ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod		items = sorted(self.ligatures.items())
446f2164abef39af7cfef989352fadfca628ed077b2jvr		for glyphName, ligSets in items:
447f2164abef39af7cfef989352fadfca628ed077b2jvr			xmlWriter.begintag("LigatureSet", glyph=glyphName)
448f2164abef39af7cfef989352fadfca628ed077b2jvr			xmlWriter.newline()
449f2164abef39af7cfef989352fadfca628ed077b2jvr			for lig in ligSets:
450f2164abef39af7cfef989352fadfca628ed077b2jvr				xmlWriter.simpletag("Ligature", glyph=lig.LigGlyph,
451f2164abef39af7cfef989352fadfca628ed077b2jvr					components=",".join(lig.Component))
452f2164abef39af7cfef989352fadfca628ed077b2jvr				xmlWriter.newline()
453f2164abef39af7cfef989352fadfca628ed077b2jvr			xmlWriter.endtag("LigatureSet")
454f2164abef39af7cfef989352fadfca628ed077b2jvr			xmlWriter.newline()
455f2164abef39af7cfef989352fadfca628ed077b2jvr
4563a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, font):
457f2164abef39af7cfef989352fadfca628ed077b2jvr		ligatures = getattr(self, "ligatures", None)
458f2164abef39af7cfef989352fadfca628ed077b2jvr		if ligatures is None:
459f2164abef39af7cfef989352fadfca628ed077b2jvr			ligatures = {}
460f2164abef39af7cfef989352fadfca628ed077b2jvr			self.ligatures = ligatures
461f2164abef39af7cfef989352fadfca628ed077b2jvr		glyphName = attrs["glyph"]
462f2164abef39af7cfef989352fadfca628ed077b2jvr		ligs = []
463f2164abef39af7cfef989352fadfca628ed077b2jvr		ligatures[glyphName] = ligs
464f2164abef39af7cfef989352fadfca628ed077b2jvr		for element in content:
465b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod			if not isinstance(element, tuple):
466f2164abef39af7cfef989352fadfca628ed077b2jvr				continue
467f2164abef39af7cfef989352fadfca628ed077b2jvr			name, attrs, content = element
468f2164abef39af7cfef989352fadfca628ed077b2jvr			lig = Ligature()
469f2164abef39af7cfef989352fadfca628ed077b2jvr			lig.LigGlyph = attrs["glyph"]
470f2164abef39af7cfef989352fadfca628ed077b2jvr			lig.Component = attrs["components"].split(",")
471f2164abef39af7cfef989352fadfca628ed077b2jvr			ligs.append(lig)
472f2164abef39af7cfef989352fadfca628ed077b2jvr
473f2164abef39af7cfef989352fadfca628ed077b2jvr
47464b5c80e80444a124da335e8d4d208bffcf2737bjvr#
47564b5c80e80444a124da335e8d4d208bffcf2737bjvr# For each subtable format there is a class. However, we don't really distinguish
47664b5c80e80444a124da335e8d4d208bffcf2737bjvr# between "field name" and "format name": often these are the same. Yet there's
47764b5c80e80444a124da335e8d4d208bffcf2737bjvr# a whole bunch of fields with different names. The following dict is a mapping
47864b5c80e80444a124da335e8d4d208bffcf2737bjvr# from "format name" to "field name". _buildClasses() uses this to create a
47964b5c80e80444a124da335e8d4d208bffcf2737bjvr# subclass for each alternate field name.
48064b5c80e80444a124da335e8d4d208bffcf2737bjvr#
48164b5c80e80444a124da335e8d4d208bffcf2737bjvr_equivalents = {
48264b5c80e80444a124da335e8d4d208bffcf2737bjvr	'MarkArray': ("Mark1Array",),
48364b5c80e80444a124da335e8d4d208bffcf2737bjvr	'LangSys': ('DefaultLangSys',),
48464b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
486f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod			'LookAheadCoverage', 'VertGlyphCoverage', 'HorizGlyphCoverage',
487f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod			'TopAccentCoverage', 'ExtendedShapeCoverage', 'MathKernCoverage'),
48864b5c80e80444a124da335e8d4d208bffcf2737bjvr	'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
48927f108844ef1aeeb5c694a8304bf6c4944783d42jvr			'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
49064b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
49164b5c80e80444a124da335e8d4d208bffcf2737bjvr			'Mark2Anchor', 'MarkAnchor'),
49264b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
49364b5c80e80444a124da335e8d4d208bffcf2737bjvr			'XDeviceTable', 'YDeviceTable', 'DeviceTable'),
49464b5c80e80444a124da335e8d4d208bffcf2737bjvr	'Axis': ('HorizAxis', 'VertAxis',),
49564b5c80e80444a124da335e8d4d208bffcf2737bjvr	'MinMax': ('DefaultMinMax',),
49664b5c80e80444a124da335e8d4d208bffcf2737bjvr	'BaseCoord': ('MinCoord', 'MaxCoord',),
49764b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfLangSys': ('DefJstfLangSys',),
49864b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
49964b5c80e80444a124da335e8d4d208bffcf2737bjvr			'ExtensionDisableGSUB',),
50064b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
50164b5c80e80444a124da335e8d4d208bffcf2737bjvr			'ExtensionDisableGPOS',),
50264b5c80e80444a124da335e8d4d208bffcf2737bjvr	'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
503f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod	'MathKern': ('TopRightMathKern', 'TopLeftMathKern', 'BottomRightMathKern',
504f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod			'BottomLeftMathKern'),
505f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod	'MathGlyphConstruction': ('VertGlyphConstruction', 'HorizGlyphConstruction'),
50664b5c80e80444a124da335e8d4d208bffcf2737bjvr}
507d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
508e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr#
509e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# OverFlow logic, to automatically create ExtensionLookups
510e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# XXX This should probably move to otBase.py
511e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr#
512d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
513823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixLookupOverFlows(ttf, overflowRecord):
514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	""" Either the offset from the LookupList to a lookup overflowed, or
515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	an offset from a lookup to a subtable overflowed.
516823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	The table layout is:
517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	GPSO/GUSB
518823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		Script List
519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		Feature List
520823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		LookUpList
521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			Lookup[0] and contents
522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTable offset list
523823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					SubTable[0] and contents
524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					...
525823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					SubTable[n] and contents
526823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			...
527823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			Lookup[n] and contents
528823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				SubTable offset list
529823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					SubTable[0] and contents
530823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					...
531823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					SubTable[n] and contents
5329e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod	If the offset to a lookup overflowed (SubTableIndex is None)
533823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		we must promote the *previous*	lookup to an Extension type.
534823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	If the offset from a lookup to subtable overflowed, then we must promote it
535823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		to an Extension Lookup type.
536823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	"""
537823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = 0
538823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	lookupIndex = overflowRecord.LookupListIndex
5399e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod	if (overflowRecord.SubTableIndex is None):
540823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookupIndex = lookupIndex - 1
541823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if lookupIndex < 0:
542823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return ok
543823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if overflowRecord.tableType == 'GSUB':
544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extType = 7
545823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	elif overflowRecord.tableType == 'GPOS':
546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extType = 9
547823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
549823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	lookup = lookups[lookupIndex]
550823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	# If the previous lookup is an extType, look further back. Very unlikely, but possible.
551319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod	while lookup.SubTable[0].__class__.LookupType == extType:
552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookupIndex = lookupIndex -1
553823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		if lookupIndex < 0:
554823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			return ok
555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookup = lookups[lookupIndex]
556823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
557823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	for si in range(len(lookup.SubTable)):
558823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subTable = lookup.SubTable[si]
559823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
560823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extSubTable = extSubTableClass()
561823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extSubTable.Format = 1
562823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extSubTable.ExtSubTable = subTable
563823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookup.SubTable[si] = extSubTable
564823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = 1
565823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return ok
566823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
567823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
568823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = 1
569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	newSubTable.Format = oldSubTable.Format
570823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if hasattr(oldSubTable, 'sortCoverageLast'):
571823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
572823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
573ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod	oldAlts = sorted(oldSubTable.alternates.items())
574823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	oldLen = len(oldAlts)
575823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
576823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
577823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Coverage table is written last. overflow is to or within the
578823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# the coverage table. We will just cut the subtable in half.
57932c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		newLen = oldLen//2
580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	elif overflowRecord.itemName == 'AlternateSet':
582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We just need to back up by two items
583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# from the overflowed AlternateSet index to make sure the offset
584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# to the Coverage table doesn't overflow.
585823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newLen  = overflowRecord.itemIndex - 1
586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
587823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	newSubTable.alternates = {}
588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	for i in range(newLen, oldLen):
589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		item = oldAlts[i]
590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		key = item[0]
591823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTable.alternates[key] = item[1]
592823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		del oldSubTable.alternates[key]
593823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
594823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
595823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return ok
596823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
597823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
598823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = 1
600823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	newSubTable.Format = oldSubTable.Format
601ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod	oldLigs = sorted(oldSubTable.ligatures.items())
602823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	oldLen = len(oldLigs)
603823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
604823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
605823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# Coverage table is written last. overflow is to or within the
606823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# the coverage table. We will just cut the subtable in half.
60732c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod		newLen = oldLen//2
608823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
609823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	elif overflowRecord.itemName == 'LigatureSet':
610823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We just need to back up by two items
611823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# from the overflowed AlternateSet index to make sure the offset
612823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# to the Coverage table doesn't overflow.
613823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newLen  = overflowRecord.itemIndex - 1
614823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
615823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	newSubTable.ligatures = {}
616823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	for i in range(newLen, oldLen):
617823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		item = oldLigs[i]
618823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		key = item[0]
619823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTable.ligatures[key] = item[1]
620823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		del oldSubTable.ligatures[key]
621823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
622823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return ok
623823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
624823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
625823f8cd15f16bb9dc3991c2672f16dd90579711bjvrsplitTable = {	'GSUB': {
626823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					1: splitSingleSubst,
627823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					2: splitMultipleSubst,
628823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					3: splitAlternateSubst,
629823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					4: splitLigatureSubst,
630823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					5: splitContextSubst,
631823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					6: splitChainContextSubst,
632823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					7: splitExtensionSubst,
633823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					8: splitReverseChainSingleSubst,
634823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					},
635823f8cd15f16bb9dc3991c2672f16dd90579711bjvr				'GPOS': {
636823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					1: splitSinglePos,
637823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					2: splitPairPos,
638823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					3: splitCursivePos,
639823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					4: splitMarkBasePos,
640823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					5: splitMarkLigPos,
641823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					6: splitMarkMarkPos,
642823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					7: splitContextPos,
643823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					8: splitChainContextPos,
644823f8cd15f16bb9dc3991c2672f16dd90579711bjvr#					9: splitExtensionPos,
645823f8cd15f16bb9dc3991c2672f16dd90579711bjvr					}
646823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
647823f8cd15f16bb9dc3991c2672f16dd90579711bjvr			}
648823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
649823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixSubTableOverFlows(ttf, overflowRecord):
650823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	"""
651823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
652823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	"""
653823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = 0
654823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	table = ttf[overflowRecord.tableType].table
655823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex]
656823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	subIndex = overflowRecord.SubTableIndex
657823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	subtable = lookup.SubTable[subIndex]
658823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
659823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if hasattr(subtable, 'ExtSubTable'):
660823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# We split the subtable of the Extension table, and add a new Extension table
661823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		# to contain the new subtable.
662823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
663319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod		subTableType = subtable.ExtSubTable.__class__.LookupType
664823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		extSubTable = subtable
665823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		subtable = extSubTable.ExtSubTable
666319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod		newExtSubTableClass = lookupTypes[overflowRecord.tableType][subtable.__class__.LookupType]
667823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newExtSubTable = newExtSubTableClass()
668823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newExtSubTable.Format = extSubTable.Format
669823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
670823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
671823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
672823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTable = newSubTableClass()
673823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newExtSubTable.ExtSubTable = newSubTable
674823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	else:
675319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod		subTableType = subtable.__class__.LookupType
676823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
677823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		newSubTable = newSubTableClass()
678823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookup.SubTable.insert(subIndex + 1, newSubTable)
679823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
680823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	if hasattr(lookup, 'SubTableCount'): # may not be defined yet.
681823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		lookup.SubTableCount = lookup.SubTableCount + 1
682823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
683823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	try:
684823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		splitFunc = splitTable[overflowRecord.tableType][subTableType]
685823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	except KeyError:
686823f8cd15f16bb9dc3991c2672f16dd90579711bjvr		return ok
687823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
688823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	ok = splitFunc(subtable, newSubTable, overflowRecord)
689823f8cd15f16bb9dc3991c2672f16dd90579711bjvr	return ok
690823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
691e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# End of OverFlow logic
692823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
693823f8cd15f16bb9dc3991c2672f16dd90579711bjvr
694d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildClasses():
695e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod	import re
6962b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod	from .otData import otData
697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
699d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	namespace = globals()
700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
701d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# populate module with classes
702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for name, table in otData:
703d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		baseClass = BaseTable
704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		m = formatPat.match(name)
705d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if m:
706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# XxxFormatN subtable, we only add the "base" table
707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name = m.group(1)
708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			baseClass = FormatSwitchingBaseTable
709bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod		if name not in namespace:
71064b5c80e80444a124da335e8d4d208bffcf2737bjvr			# the class doesn't exist yet, so the base implementation is used.
711e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod			cls = type(name, (baseClass,), {})
712d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			namespace[name] = cls
713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
71464b5c80e80444a124da335e8d4d208bffcf2737bjvr	for base, alts in _equivalents.items():
715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		base = namespace[base]
716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		for alt in alts:
717e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod			namespace[alt] = type(alt, (base,), {})
718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	global lookupTypes
720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	lookupTypes = {
721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		'GSUB': {
722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			1: SingleSubst,
723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			2: MultipleSubst,
724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			3: AlternateSubst,
725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			4: LigatureSubst,
726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			5: ContextSubst,
727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			6: ChainContextSubst,
728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			7: ExtensionSubst,
7298e1d75b1a3fb4874bb78d9c34a11ba30de81a5ccjvr			8: ReverseChainSingleSubst,
730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		},
731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		'GPOS': {
732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			1: SinglePos,
733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			2: PairPos,
734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			3: CursivePos,
735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			4: MarkBasePos,
736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			5: MarkLigPos,
737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			6: MarkMarkPos,
738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			7: ContextPos,
739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			8: ChainContextPos,
740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			9: ExtensionPos,
741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		},
742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	}
743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
74464b5c80e80444a124da335e8d4d208bffcf2737bjvr	for lookupEnum in lookupTypes.values():
74564b5c80e80444a124da335e8d4d208bffcf2737bjvr		for enum, cls in lookupEnum.items():
74664b5c80e80444a124da335e8d4d208bffcf2737bjvr			cls.LookupType = enum
7479e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod
7489e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	global featureParamTypes
7499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	featureParamTypes = {
7509e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		'size': FeatureParamsSize,
7519e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	}
7529e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	for i in range(1, 20+1):
7539e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
7549e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod	for i in range(1, 99+1):
7559e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod		featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	# add converters to classes
7582b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod	from .otConverters import buildConverters
759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr	for name, table in otData:
760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		m = formatPat.match(name)
761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		if m:
762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			# XxxFormatN subtable, add converter to "base" table
763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			name, format = m.groups()
764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			format = int(format)
765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls = namespace[name]
766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			if not hasattr(cls, "converters"):
767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				cls.converters = {}
768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr				cls.convertersByName = {}
76964b5c80e80444a124da335e8d4d208bffcf2737bjvr			converters, convertersByName = buildConverters(table[1:], namespace)
770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls.converters[format] = converters
771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls.convertersByName[format] = convertersByName
772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr		else:
773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr			cls = namespace[name]
77464b5c80e80444a124da335e8d4d208bffcf2737bjvr			cls.converters, cls.convertersByName = buildConverters(table, namespace)
775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr
777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr_buildClasses()
7781fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr
7791fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr
7801fae9b48d3a275c6e1dc0649c40ae121dfc4098djvrdef _getGlyphsFromCoverageTable(coverage):
7811fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr	if coverage is None:
7821fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr		# empty coverage table
7831fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr		return []
7841fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr	else:
7851fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr		return coverage.glyphs
786