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