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