otTables.py revision b23b4cefda2acf417bfecf2135357554dbd32277
1"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various
2OpenType subtables.
3
4Most are constructed upon import from data in otData.py, all are populated with
5converter objects from otConverters.py.
6"""
7from __future__ import print_function, division
8from fontTools.misc.py23 import *
9from .otBase import BaseTable, FormatSwitchingBaseTable
10import operator
11import warnings
12
13
14class LookupOrder(BaseTable):
15	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
16
17class FeatureParams(BaseTable):
18
19	def compile(self, writer, font):
20		assert featureParamTypes.get(writer['FeatureTag'], None) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__)
21		BaseTable.compile(self, writer, font)
22
23class FeatureParamsSize(FeatureParams):
24	pass
25
26class FeatureParamsStylisticSet(FeatureParams):
27	pass
28
29class FeatureParamsCharacterVariants(FeatureParams):
30	pass
31
32class Coverage(FormatSwitchingBaseTable):
33
34	# manual implementation to get rid of glyphID dependencies
35
36	def postRead(self, rawTable, font):
37		if self.Format == 1:
38			# TODO only allow glyphs that are valid?
39			self.glyphs = rawTable["GlyphArray"]
40		elif self.Format == 2:
41			glyphs = self.glyphs = []
42			ranges = rawTable["RangeRecord"]
43			glyphOrder = font.getGlyphOrder()
44			# Some SIL fonts have coverage entries that don't have sorted
45			# StartCoverageIndex.  If it is so, fixup and warn.  We undo
46			# this when writing font out.
47			sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex)
48			if ranges != sorted_ranges:
49				warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
50				ranges = sorted_ranges
51			del sorted_ranges
52			for r in ranges:
53				assert r.StartCoverageIndex == len(glyphs), \
54					(r.StartCoverageIndex, len(glyphs))
55				start = r.Start
56				end = r.End
57				try:
58					startID = font.getGlyphID(start, requireReal=True)
59				except KeyError:
60					warnings.warn("Coverage table has start glyph ID out of range: %s." % start)
61					continue
62				try:
63					endID = font.getGlyphID(end, requireReal=True) + 1
64				except KeyError:
65					# Apparently some tools use 65535 to "match all" the range
66					if end != 'glyph65535':
67						warnings.warn("Coverage table has end glyph ID out of range: %s." % end)
68					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
69					# but none that we have seen in the wild.
70					endID = len(glyphOrder)
71				glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
72		else:
73			assert 0, "unknown format: %s" % self.Format
74
75	def preWrite(self, font):
76		glyphs = getattr(self, "glyphs", None)
77		if glyphs is None:
78			glyphs = self.glyphs = []
79		format = 1
80		rawTable = {"GlyphArray": glyphs}
81		getGlyphID = font.getGlyphID
82		if glyphs:
83			# find out whether Format 2 is more compact or not
84			glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
85			brokenOrder = sorted(glyphIDs) != glyphIDs
86
87			last = glyphIDs[0]
88			ranges = [[last]]
89			for glyphID in glyphIDs[1:]:
90				if glyphID != last + 1:
91					ranges[-1].append(last)
92					ranges.append([glyphID])
93				last = glyphID
94			ranges[-1].append(last)
95
96			if brokenOrder or len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
97				# Format 2 is more compact
98				index = 0
99				for i in range(len(ranges)):
100					start, end = ranges[i]
101					r = RangeRecord()
102					r.StartID = start
103					r.Start = font.getGlyphName(start)
104					r.End = font.getGlyphName(end)
105					r.StartCoverageIndex = index
106					ranges[i] = r
107					index = index + end - start + 1
108				if brokenOrder:
109					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
110					ranges.sort(key=lambda a: a.StartID)
111				for r in ranges:
112					del r.StartID
113				format = 2
114				rawTable = {"RangeRecord": ranges}
115			#else:
116			#	fallthrough; Format 1 is more compact
117		self.Format = format
118		return rawTable
119
120	def toXML2(self, xmlWriter, font):
121		for glyphName in getattr(self, "glyphs", []):
122			xmlWriter.simpletag("Glyph", value=glyphName)
123			xmlWriter.newline()
124
125	def fromXML(self, name, attrs, content, font):
126		glyphs = getattr(self, "glyphs", None)
127		if glyphs is None:
128			glyphs = []
129			self.glyphs = glyphs
130		glyphs.append(attrs["value"])
131
132
133def doModulo(value):
134	if value < 0:
135		return value + 65536
136	return value
137
138class SingleSubst(FormatSwitchingBaseTable):
139
140	def postRead(self, rawTable, font):
141		mapping = {}
142		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
143		lenMapping = len(input)
144		if self.Format == 1:
145			delta = rawTable["DeltaGlyphID"]
146			inputGIDS =  [ font.getGlyphID(name) for name in input ]
147			outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
148			outGIDS = map(doModulo, outGIDS)
149			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
150			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
151		elif self.Format == 2:
152			assert len(input) == rawTable["GlyphCount"], \
153					"invalid SingleSubstFormat2 table"
154			subst = rawTable["Substitute"]
155			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
156		else:
157			assert 0, "unknown format: %s" % self.Format
158		self.mapping = mapping
159
160	def preWrite(self, font):
161		mapping = getattr(self, "mapping", None)
162		if mapping is None:
163			mapping = self.mapping = {}
164		items = list(mapping.items())
165		getGlyphID = font.getGlyphID
166		gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items]
167		sortableItems = sorted(zip(gidItems, items))
168
169		# figure out format
170		format = 2
171		delta = None
172		for inID, outID in gidItems:
173			if delta is None:
174				delta = outID - inID
175				if delta < -32768:
176					delta += 65536
177				elif delta > 32767:
178					delta -= 65536
179			else:
180				if delta != outID - inID:
181					break
182		else:
183			format = 1
184
185		rawTable = {}
186		self.Format = format
187		cov = Coverage()
188		input =  [ item [1][0] for item in sortableItems]
189		subst =  [ item [1][1] for item in sortableItems]
190		cov.glyphs = input
191		rawTable["Coverage"] = cov
192		if format == 1:
193			assert delta is not None
194			rawTable["DeltaGlyphID"] = delta
195		else:
196			rawTable["Substitute"] = subst
197		return rawTable
198
199	def toXML2(self, xmlWriter, font):
200		items = sorted(self.mapping.items())
201		for inGlyph, outGlyph in items:
202			xmlWriter.simpletag("Substitution",
203					[("in", inGlyph), ("out", outGlyph)])
204			xmlWriter.newline()
205
206	def fromXML(self, name, attrs, content, font):
207		mapping = getattr(self, "mapping", None)
208		if mapping is None:
209			mapping = {}
210			self.mapping = mapping
211		mapping[attrs["in"]] = attrs["out"]
212
213
214class ClassDef(FormatSwitchingBaseTable):
215
216	def postRead(self, rawTable, font):
217		classDefs = {}
218		glyphOrder = font.getGlyphOrder()
219
220		if self.Format == 1:
221			start = rawTable["StartGlyph"]
222			classList = rawTable["ClassValueArray"]
223			try:
224				startID = font.getGlyphID(start, requireReal=True)
225			except KeyError:
226				warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
227				startID = len(glyphOrder)
228			endID = startID + len(classList)
229			if endID > len(glyphOrder):
230				warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
231				# NOTE: We clobber out-of-range things here.  There are legit uses for those,
232				# but none that we have seen in the wild.
233				endID = len(glyphOrder)
234
235			for glyphID, cls in zip(range(startID, endID), classList):
236				classDefs[glyphOrder[glyphID]] = cls
237
238		elif self.Format == 2:
239			records = rawTable["ClassRangeRecord"]
240			for rec in records:
241				start = rec.Start
242				end = rec.End
243				cls = rec.Class
244				try:
245					startID = font.getGlyphID(start, requireReal=True)
246				except KeyError:
247					warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
248					continue
249				try:
250					endID = font.getGlyphID(end, requireReal=True) + 1
251				except KeyError:
252					# Apparently some tools use 65535 to "match all" the range
253					if end != 'glyph65535':
254						warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
255					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
256					# but none that we have seen in the wild.
257					endID = len(glyphOrder)
258				for glyphID in range(startID, endID):
259					classDefs[glyphOrder[glyphID]] = cls
260		else:
261			assert 0, "unknown format: %s" % self.Format
262		self.classDefs = classDefs
263
264	def preWrite(self, font):
265		classDefs = getattr(self, "classDefs", None)
266		if classDefs is None:
267			classDefs = self.classDefs = {}
268		items = list(classDefs.items())
269		format = 2
270		rawTable = {"ClassRangeRecord": []}
271		getGlyphID = font.getGlyphID
272		for i in range(len(items)):
273			glyphName, cls = items[i]
274			items[i] = getGlyphID(glyphName), glyphName, cls
275		items.sort()
276		if items:
277			last, lastName, lastCls = items[0]
278			ranges = [[lastCls, last, lastName]]
279			for glyphID, glyphName, cls in items[1:]:
280				if glyphID != last + 1 or cls != lastCls:
281					ranges[-1].extend([last, lastName])
282					ranges.append([cls, glyphID, glyphName])
283				last = glyphID
284				lastName = glyphName
285				lastCls = cls
286			ranges[-1].extend([last, lastName])
287
288			startGlyph = ranges[0][1]
289			endGlyph = ranges[-1][3]
290			glyphCount = endGlyph - startGlyph + 1
291			if len(ranges) * 3 < glyphCount + 1:
292				# Format 2 is more compact
293				for i in range(len(ranges)):
294					cls, start, startName, end, endName = ranges[i]
295					rec = ClassRangeRecord()
296					rec.Start = startName
297					rec.End = endName
298					rec.Class = cls
299					ranges[i] = rec
300				format = 2
301				rawTable = {"ClassRangeRecord": ranges}
302			else:
303				# Format 1 is more compact
304				startGlyphName = ranges[0][2]
305				classes = [0] * glyphCount
306				for cls, start, startName, end, endName in ranges:
307					for g in range(start - startGlyph, end - startGlyph + 1):
308						classes[g] = cls
309				format = 1
310				rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
311		self.Format = format
312		return rawTable
313
314	def toXML2(self, xmlWriter, font):
315		items = sorted(self.classDefs.items())
316		for glyphName, cls in items:
317			xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
318			xmlWriter.newline()
319
320	def fromXML(self, name, attrs, content, font):
321		classDefs = getattr(self, "classDefs", None)
322		if classDefs is None:
323			classDefs = {}
324			self.classDefs = classDefs
325		classDefs[attrs["glyph"]] = int(attrs["class"])
326
327
328class AlternateSubst(FormatSwitchingBaseTable):
329
330	def postRead(self, rawTable, font):
331		alternates = {}
332		if self.Format == 1:
333			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
334			alts = rawTable["AlternateSet"]
335			if len(input) != len(alts):
336				assert len(input) == len(alts)
337			for i in range(len(input)):
338				alternates[input[i]] = alts[i].Alternate
339		else:
340			assert 0, "unknown format: %s" % self.Format
341		self.alternates = alternates
342
343	def preWrite(self, font):
344		self.Format = 1
345		alternates = getattr(self, "alternates", None)
346		if alternates is None:
347			alternates = self.alternates = {}
348		items = list(alternates.items())
349		for i in range(len(items)):
350			glyphName, set = items[i]
351			items[i] = font.getGlyphID(glyphName), glyphName, set
352		items.sort()
353		cov = Coverage()
354		cov.glyphs = [ item[1] for item in items]
355		alternates = []
356		setList = [ item[-1] for item in items]
357		for  set in setList:
358			alts = AlternateSet()
359			alts.Alternate = set
360			alternates.append(alts)
361		# a special case to deal with the fact that several hundred Adobe Japan1-5
362		# CJK fonts will overflow an offset if the coverage table isn't pushed to the end.
363		# Also useful in that when splitting a sub-table because of an offset overflow
364		# I don't need to calculate the change in the subtable offset due to the change in the coverage table size.
365		# Allows packing more rules in subtable.
366		self.sortCoverageLast = 1
367		return {"Coverage": cov, "AlternateSet": alternates}
368
369	def toXML2(self, xmlWriter, font):
370		items = sorted(self.alternates.items())
371		for glyphName, alternates in items:
372			xmlWriter.begintag("AlternateSet", glyph=glyphName)
373			xmlWriter.newline()
374			for alt in alternates:
375				xmlWriter.simpletag("Alternate", glyph=alt)
376				xmlWriter.newline()
377			xmlWriter.endtag("AlternateSet")
378			xmlWriter.newline()
379
380	def fromXML(self, name, attrs, content, font):
381		alternates = getattr(self, "alternates", None)
382		if alternates is None:
383			alternates = {}
384			self.alternates = alternates
385		glyphName = attrs["glyph"]
386		set = []
387		alternates[glyphName] = set
388		for element in content:
389			if not isinstance(element, tuple):
390				continue
391			name, attrs, content = element
392			set.append(attrs["glyph"])
393
394
395class LigatureSubst(FormatSwitchingBaseTable):
396
397	def postRead(self, rawTable, font):
398		ligatures = {}
399		if self.Format == 1:
400			input = rawTable["Coverage"].glyphs
401			ligSets = rawTable["LigatureSet"]
402			assert len(input) == len(ligSets)
403			for i in range(len(input)):
404				ligatures[input[i]] = ligSets[i].Ligature
405		else:
406			assert 0, "unknown format: %s" % self.Format
407		self.ligatures = ligatures
408
409	def preWrite(self, font):
410		ligatures = getattr(self, "ligatures", None)
411		if ligatures is None:
412			ligatures = self.ligatures = {}
413		items = list(ligatures.items())
414		for i in range(len(items)):
415			glyphName, set = items[i]
416			items[i] = font.getGlyphID(glyphName), glyphName, set
417		items.sort()
418		cov = Coverage()
419		cov.glyphs = [ item[1] for item in items]
420
421		ligSets = []
422		setList = [ item[-1] for item in items ]
423		for set in setList:
424			ligSet = LigatureSet()
425			ligs = ligSet.Ligature = []
426			for lig in set:
427				ligs.append(lig)
428			ligSets.append(ligSet)
429		# Useful in that when splitting a sub-table because of an offset overflow
430		# I don't need to calculate the change in subtabl offset due to the coverage table size.
431		# Allows packing more rules in subtable.
432		self.sortCoverageLast = 1
433		return {"Coverage": cov, "LigatureSet": ligSets}
434
435	def toXML2(self, xmlWriter, font):
436		items = sorted(self.ligatures.items())
437		for glyphName, ligSets in items:
438			xmlWriter.begintag("LigatureSet", glyph=glyphName)
439			xmlWriter.newline()
440			for lig in ligSets:
441				xmlWriter.simpletag("Ligature", glyph=lig.LigGlyph,
442					components=",".join(lig.Component))
443				xmlWriter.newline()
444			xmlWriter.endtag("LigatureSet")
445			xmlWriter.newline()
446
447	def fromXML(self, name, attrs, content, font):
448		ligatures = getattr(self, "ligatures", None)
449		if ligatures is None:
450			ligatures = {}
451			self.ligatures = ligatures
452		glyphName = attrs["glyph"]
453		ligs = []
454		ligatures[glyphName] = ligs
455		for element in content:
456			if not isinstance(element, tuple):
457				continue
458			name, attrs, content = element
459			lig = Ligature()
460			lig.LigGlyph = attrs["glyph"]
461			lig.Component = attrs["components"].split(",")
462			ligs.append(lig)
463
464
465#
466# For each subtable format there is a class. However, we don't really distinguish
467# between "field name" and "format name": often these are the same. Yet there's
468# a whole bunch of fields with different names. The following dict is a mapping
469# from "format name" to "field name". _buildClasses() uses this to create a
470# subclass for each alternate field name.
471#
472_equivalents = {
473	'MarkArray': ("Mark1Array",),
474	'LangSys': ('DefaultLangSys',),
475	'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage',
476			'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage',
477			'LookAheadCoverage'),
478	'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
479			'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
480	'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
481			'Mark2Anchor', 'MarkAnchor'),
482	'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
483			'XDeviceTable', 'YDeviceTable', 'DeviceTable'),
484	'Axis': ('HorizAxis', 'VertAxis',),
485	'MinMax': ('DefaultMinMax',),
486	'BaseCoord': ('MinCoord', 'MaxCoord',),
487	'JstfLangSys': ('DefJstfLangSys',),
488	'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
489			'ExtensionDisableGSUB',),
490	'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
491			'ExtensionDisableGPOS',),
492	'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
493}
494
495#
496# OverFlow logic, to automatically create ExtensionLookups
497# XXX This should probably move to otBase.py
498#
499
500def fixLookupOverFlows(ttf, overflowRecord):
501	""" Either the offset from the LookupList to a lookup overflowed, or
502	an offset from a lookup to a subtable overflowed.
503	The table layout is:
504	GPSO/GUSB
505		Script List
506		Feature List
507		LookUpList
508			Lookup[0] and contents
509				SubTable offset list
510					SubTable[0] and contents
511					...
512					SubTable[n] and contents
513			...
514			Lookup[n] and contents
515				SubTable offset list
516					SubTable[0] and contents
517					...
518					SubTable[n] and contents
519	If the offset to a lookup overflowed (SubTableIndex is None)
520		we must promote the *previous*	lookup to an Extension type.
521	If the offset from a lookup to subtable overflowed, then we must promote it
522		to an Extension Lookup type.
523	"""
524	ok = 0
525	lookupIndex = overflowRecord.LookupListIndex
526	if (overflowRecord.SubTableIndex is None):
527		lookupIndex = lookupIndex - 1
528	if lookupIndex < 0:
529		return ok
530	if overflowRecord.tableType == 'GSUB':
531		extType = 7
532	elif overflowRecord.tableType == 'GPOS':
533		extType = 9
534
535	lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
536	lookup = lookups[lookupIndex]
537	# If the previous lookup is an extType, look further back. Very unlikely, but possible.
538	while lookup.LookupType == extType:
539		lookupIndex = lookupIndex -1
540		if lookupIndex < 0:
541			return ok
542		lookup = lookups[lookupIndex]
543
544	for si in range(len(lookup.SubTable)):
545		subTable = lookup.SubTable[si]
546		extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
547		extSubTable = extSubTableClass()
548		extSubTable.Format = 1
549		extSubTable.ExtensionLookupType = lookup.LookupType
550		extSubTable.ExtSubTable = subTable
551		lookup.SubTable[si] = extSubTable
552	lookup.LookupType = extType
553	ok = 1
554	return ok
555
556def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
557	ok = 1
558	newSubTable.Format = oldSubTable.Format
559	if hasattr(oldSubTable, 'sortCoverageLast'):
560		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
561
562	oldAlts = sorted(oldSubTable.alternates.items())
563	oldLen = len(oldAlts)
564
565	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
566		# Coverage table is written last. overflow is to or within the
567		# the coverage table. We will just cut the subtable in half.
568		newLen = oldLen//2
569
570	elif overflowRecord.itemName == 'AlternateSet':
571		# We just need to back up by two items
572		# from the overflowed AlternateSet index to make sure the offset
573		# to the Coverage table doesn't overflow.
574		newLen  = overflowRecord.itemIndex - 1
575
576	newSubTable.alternates = {}
577	for i in range(newLen, oldLen):
578		item = oldAlts[i]
579		key = item[0]
580		newSubTable.alternates[key] = item[1]
581		del oldSubTable.alternates[key]
582
583
584	return ok
585
586
587def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
588	ok = 1
589	newSubTable.Format = oldSubTable.Format
590	oldLigs = sorted(oldSubTable.ligatures.items())
591	oldLen = len(oldLigs)
592
593	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
594		# Coverage table is written last. overflow is to or within the
595		# the coverage table. We will just cut the subtable in half.
596		newLen = oldLen//2
597
598	elif overflowRecord.itemName == 'LigatureSet':
599		# We just need to back up by two items
600		# from the overflowed AlternateSet index to make sure the offset
601		# to the Coverage table doesn't overflow.
602		newLen  = overflowRecord.itemIndex - 1
603
604	newSubTable.ligatures = {}
605	for i in range(newLen, oldLen):
606		item = oldLigs[i]
607		key = item[0]
608		newSubTable.ligatures[key] = item[1]
609		del oldSubTable.ligatures[key]
610
611	return ok
612
613
614splitTable = {	'GSUB': {
615#					1: splitSingleSubst,
616#					2: splitMultipleSubst,
617					3: splitAlternateSubst,
618					4: splitLigatureSubst,
619#					5: splitContextSubst,
620#					6: splitChainContextSubst,
621#					7: splitExtensionSubst,
622#					8: splitReverseChainSingleSubst,
623					},
624				'GPOS': {
625#					1: splitSinglePos,
626#					2: splitPairPos,
627#					3: splitCursivePos,
628#					4: splitMarkBasePos,
629#					5: splitMarkLigPos,
630#					6: splitMarkMarkPos,
631#					7: splitContextPos,
632#					8: splitChainContextPos,
633#					9: splitExtensionPos,
634					}
635
636			}
637
638def fixSubTableOverFlows(ttf, overflowRecord):
639	"""
640	An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
641	"""
642	ok = 0
643	table = ttf[overflowRecord.tableType].table
644	lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex]
645	subIndex = overflowRecord.SubTableIndex
646	subtable = lookup.SubTable[subIndex]
647
648	if hasattr(subtable, 'ExtSubTable'):
649		# We split the subtable of the Extension table, and add a new Extension table
650		# to contain the new subtable.
651
652		subTableType = subtable.ExtensionLookupType
653		extSubTable = subtable
654		subtable = extSubTable.ExtSubTable
655		newExtSubTableClass = lookupTypes[overflowRecord.tableType][lookup.LookupType]
656		newExtSubTable = newExtSubTableClass()
657		newExtSubTable.Format = extSubTable.Format
658		newExtSubTable.ExtensionLookupType = extSubTable.ExtensionLookupType
659		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
660
661		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
662		newSubTable = newSubTableClass()
663		newExtSubTable.ExtSubTable = newSubTable
664	else:
665		subTableType = lookup.LookupType
666		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
667		newSubTable = newSubTableClass()
668		lookup.SubTable.insert(subIndex + 1, newSubTable)
669
670	if hasattr(lookup, 'SubTableCount'): # may not be defined yet.
671		lookup.SubTableCount = lookup.SubTableCount + 1
672
673	try:
674		splitFunc = splitTable[overflowRecord.tableType][subTableType]
675	except KeyError:
676		return ok
677
678	ok = splitFunc(subtable, newSubTable, overflowRecord)
679	return ok
680
681# End of OverFlow logic
682
683
684def _buildClasses():
685	import re
686	from .otData import otData
687
688	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
689	namespace = globals()
690
691	# populate module with classes
692	for name, table in otData:
693		baseClass = BaseTable
694		m = formatPat.match(name)
695		if m:
696			# XxxFormatN subtable, we only add the "base" table
697			name = m.group(1)
698			baseClass = FormatSwitchingBaseTable
699		if name not in namespace:
700			# the class doesn't exist yet, so the base implementation is used.
701			cls = type(name, (baseClass,), {})
702			namespace[name] = cls
703
704	for base, alts in _equivalents.items():
705		base = namespace[base]
706		for alt in alts:
707			namespace[alt] = type(alt, (base,), {})
708
709	global lookupTypes
710	lookupTypes = {
711		'GSUB': {
712			1: SingleSubst,
713			2: MultipleSubst,
714			3: AlternateSubst,
715			4: LigatureSubst,
716			5: ContextSubst,
717			6: ChainContextSubst,
718			7: ExtensionSubst,
719			8: ReverseChainSingleSubst,
720		},
721		'GPOS': {
722			1: SinglePos,
723			2: PairPos,
724			3: CursivePos,
725			4: MarkBasePos,
726			5: MarkLigPos,
727			6: MarkMarkPos,
728			7: ContextPos,
729			8: ChainContextPos,
730			9: ExtensionPos,
731		},
732	}
733	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
734	for lookupEnum in lookupTypes.values():
735		for enum, cls in lookupEnum.items():
736			cls.LookupType = enum
737
738	global featureParamTypes
739	featureParamTypes = {
740		'size': FeatureParamsSize,
741	}
742	for i in range(1, 20+1):
743		featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
744	for i in range(1, 99+1):
745		featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
746
747	# add converters to classes
748	from .otConverters import buildConverters
749	for name, table in otData:
750		m = formatPat.match(name)
751		if m:
752			# XxxFormatN subtable, add converter to "base" table
753			name, format = m.groups()
754			format = int(format)
755			cls = namespace[name]
756			if not hasattr(cls, "converters"):
757				cls.converters = {}
758				cls.convertersByName = {}
759			converters, convertersByName = buildConverters(table[1:], namespace)
760			cls.converters[format] = converters
761			cls.convertersByName[format] = convertersByName
762		else:
763			cls = namespace[name]
764			cls.converters, cls.convertersByName = buildConverters(table, namespace)
765
766
767_buildClasses()
768
769
770def _getGlyphsFromCoverageTable(coverage):
771	if coverage is None:
772		# empty coverage table
773		return []
774	else:
775		return coverage.glyphs
776