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