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