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