• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, absolute_import
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']) == 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		del self.Format # Don't need this anymore
78
79	def preWrite(self, font):
80		glyphs = getattr(self, "glyphs", None)
81		if glyphs is None:
82			glyphs = self.glyphs = []
83		format = 1
84		rawTable = {"GlyphArray": glyphs}
85		getGlyphID = font.getGlyphID
86		if glyphs:
87			# find out whether Format 2 is more compact or not
88			glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ]
89			brokenOrder = sorted(glyphIDs) != glyphIDs
90
91			last = glyphIDs[0]
92			ranges = [[last]]
93			for glyphID in glyphIDs[1:]:
94				if glyphID != last + 1:
95					ranges[-1].append(last)
96					ranges.append([glyphID])
97				last = glyphID
98			ranges[-1].append(last)
99
100			if brokenOrder or len(ranges) * 3 < len(glyphs):  # 3 words vs. 1 word
101				# Format 2 is more compact
102				index = 0
103				for i in range(len(ranges)):
104					start, end = ranges[i]
105					r = RangeRecord()
106					r.StartID = start
107					r.Start = font.getGlyphName(start)
108					r.End = font.getGlyphName(end)
109					r.StartCoverageIndex = index
110					ranges[i] = r
111					index = index + end - start + 1
112				if brokenOrder:
113					warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.")
114					ranges.sort(key=lambda a: a.StartID)
115				for r in ranges:
116					del r.StartID
117				format = 2
118				rawTable = {"RangeRecord": ranges}
119			#else:
120			#	fallthrough; Format 1 is more compact
121		self.Format = format
122		return rawTable
123
124	def toXML2(self, xmlWriter, font):
125		for glyphName in getattr(self, "glyphs", []):
126			xmlWriter.simpletag("Glyph", value=glyphName)
127			xmlWriter.newline()
128
129	def fromXML(self, name, attrs, content, font):
130		glyphs = getattr(self, "glyphs", None)
131		if glyphs is None:
132			glyphs = []
133			self.glyphs = glyphs
134		glyphs.append(attrs["value"])
135
136
137def doModulo(value):
138	if value < 0:
139		return value + 65536
140	return value
141
142class SingleSubst(FormatSwitchingBaseTable):
143
144	def postRead(self, rawTable, font):
145		mapping = {}
146		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
147		lenMapping = len(input)
148		if self.Format == 1:
149			delta = rawTable["DeltaGlyphID"]
150			inputGIDS =  [ font.getGlyphID(name) for name in input ]
151			outGIDS = [ glyphID + delta for glyphID in inputGIDS ]
152			outGIDS = map(doModulo, outGIDS)
153			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
154			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
155		elif self.Format == 2:
156			assert len(input) == rawTable["GlyphCount"], \
157					"invalid SingleSubstFormat2 table"
158			subst = rawTable["Substitute"]
159			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
160		else:
161			assert 0, "unknown format: %s" % self.Format
162		self.mapping = mapping
163		del self.Format # Don't need this anymore
164
165	def preWrite(self, font):
166		mapping = getattr(self, "mapping", None)
167		if mapping is None:
168			mapping = self.mapping = {}
169		items = list(mapping.items())
170		getGlyphID = font.getGlyphID
171		gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items]
172		sortableItems = sorted(zip(gidItems, items))
173
174		# figure out format
175		format = 2
176		delta = None
177		for inID, outID in gidItems:
178			if delta is None:
179				delta = outID - inID
180				if delta < -32768:
181					delta += 65536
182				elif delta > 32767:
183					delta -= 65536
184			else:
185				if delta != outID - inID:
186					break
187		else:
188			format = 1
189
190		rawTable = {}
191		self.Format = format
192		cov = Coverage()
193		input =  [ item [1][0] for item in sortableItems]
194		subst =  [ item [1][1] for item in sortableItems]
195		cov.glyphs = input
196		rawTable["Coverage"] = cov
197		if format == 1:
198			assert delta is not None
199			rawTable["DeltaGlyphID"] = delta
200		else:
201			rawTable["Substitute"] = subst
202		return rawTable
203
204	def toXML2(self, xmlWriter, font):
205		items = sorted(self.mapping.items())
206		for inGlyph, outGlyph in items:
207			xmlWriter.simpletag("Substitution",
208					[("in", inGlyph), ("out", outGlyph)])
209			xmlWriter.newline()
210
211	def fromXML(self, name, attrs, content, font):
212		mapping = getattr(self, "mapping", None)
213		if mapping is None:
214			mapping = {}
215			self.mapping = mapping
216		mapping[attrs["in"]] = attrs["out"]
217
218
219class ClassDef(FormatSwitchingBaseTable):
220
221	def postRead(self, rawTable, font):
222		classDefs = {}
223		glyphOrder = font.getGlyphOrder()
224
225		if self.Format == 1:
226			start = rawTable["StartGlyph"]
227			classList = rawTable["ClassValueArray"]
228			try:
229				startID = font.getGlyphID(start, requireReal=True)
230			except KeyError:
231				warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
232				startID = len(glyphOrder)
233			endID = startID + len(classList)
234			if endID > len(glyphOrder):
235				warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList)))
236				# NOTE: We clobber out-of-range things here.  There are legit uses for those,
237				# but none that we have seen in the wild.
238				endID = len(glyphOrder)
239
240			for glyphID, cls in zip(range(startID, endID), classList):
241				classDefs[glyphOrder[glyphID]] = cls
242
243		elif self.Format == 2:
244			records = rawTable["ClassRangeRecord"]
245			for rec in records:
246				start = rec.Start
247				end = rec.End
248				cls = rec.Class
249				try:
250					startID = font.getGlyphID(start, requireReal=True)
251				except KeyError:
252					warnings.warn("ClassDef table has start glyph ID out of range: %s." % start)
253					continue
254				try:
255					endID = font.getGlyphID(end, requireReal=True) + 1
256				except KeyError:
257					# Apparently some tools use 65535 to "match all" the range
258					if end != 'glyph65535':
259						warnings.warn("ClassDef table has end glyph ID out of range: %s." % end)
260					# NOTE: We clobber out-of-range things here.  There are legit uses for those,
261					# but none that we have seen in the wild.
262					endID = len(glyphOrder)
263				for glyphID in range(startID, endID):
264					classDefs[glyphOrder[glyphID]] = cls
265		else:
266			assert 0, "unknown format: %s" % self.Format
267		self.classDefs = classDefs
268		del self.Format # Don't need this anymore
269
270	def preWrite(self, font):
271		classDefs = getattr(self, "classDefs", None)
272		if classDefs is None:
273			classDefs = self.classDefs = {}
274		items = list(classDefs.items())
275		format = 2
276		rawTable = {"ClassRangeRecord": []}
277		getGlyphID = font.getGlyphID
278		for i in range(len(items)):
279			glyphName, cls = items[i]
280			items[i] = getGlyphID(glyphName), glyphName, cls
281		items.sort()
282		if items:
283			last, lastName, lastCls = items[0]
284			ranges = [[lastCls, last, lastName]]
285			for glyphID, glyphName, cls in items[1:]:
286				if glyphID != last + 1 or cls != lastCls:
287					ranges[-1].extend([last, lastName])
288					ranges.append([cls, glyphID, glyphName])
289				last = glyphID
290				lastName = glyphName
291				lastCls = cls
292			ranges[-1].extend([last, lastName])
293
294			startGlyph = ranges[0][1]
295			endGlyph = ranges[-1][3]
296			glyphCount = endGlyph - startGlyph + 1
297			if len(ranges) * 3 < glyphCount + 1:
298				# Format 2 is more compact
299				for i in range(len(ranges)):
300					cls, start, startName, end, endName = ranges[i]
301					rec = ClassRangeRecord()
302					rec.Start = startName
303					rec.End = endName
304					rec.Class = cls
305					ranges[i] = rec
306				format = 2
307				rawTable = {"ClassRangeRecord": ranges}
308			else:
309				# Format 1 is more compact
310				startGlyphName = ranges[0][2]
311				classes = [0] * glyphCount
312				for cls, start, startName, end, endName in ranges:
313					for g in range(start - startGlyph, end - startGlyph + 1):
314						classes[g] = cls
315				format = 1
316				rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes}
317		self.Format = format
318		return rawTable
319
320	def toXML2(self, xmlWriter, font):
321		items = sorted(self.classDefs.items())
322		for glyphName, cls in items:
323			xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)])
324			xmlWriter.newline()
325
326	def fromXML(self, name, attrs, content, font):
327		classDefs = getattr(self, "classDefs", None)
328		if classDefs is None:
329			classDefs = {}
330			self.classDefs = classDefs
331		classDefs[attrs["glyph"]] = int(attrs["class"])
332
333
334class AlternateSubst(FormatSwitchingBaseTable):
335
336	def postRead(self, rawTable, font):
337		alternates = {}
338		if self.Format == 1:
339			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
340			alts = rawTable["AlternateSet"]
341			if len(input) != len(alts):
342				assert len(input) == len(alts)
343			for i in range(len(input)):
344				alternates[input[i]] = alts[i].Alternate
345		else:
346			assert 0, "unknown format: %s" % self.Format
347		self.alternates = alternates
348		del self.Format # Don't need this anymore
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 = list(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 = sorted(self.alternates.items())
378		for glyphName, alternates in items:
379			xmlWriter.begintag("AlternateSet", glyph=glyphName)
380			xmlWriter.newline()
381			for alt in alternates:
382				xmlWriter.simpletag("Alternate", glyph=alt)
383				xmlWriter.newline()
384			xmlWriter.endtag("AlternateSet")
385			xmlWriter.newline()
386
387	def fromXML(self, name, attrs, content, font):
388		alternates = getattr(self, "alternates", None)
389		if alternates is None:
390			alternates = {}
391			self.alternates = alternates
392		glyphName = attrs["glyph"]
393		set = []
394		alternates[glyphName] = set
395		for element in content:
396			if not isinstance(element, tuple):
397				continue
398			name, attrs, content = element
399			set.append(attrs["glyph"])
400
401
402class LigatureSubst(FormatSwitchingBaseTable):
403
404	def postRead(self, rawTable, font):
405		ligatures = {}
406		if self.Format == 1:
407			input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
408			ligSets = rawTable["LigatureSet"]
409			assert len(input) == len(ligSets)
410			for i in range(len(input)):
411				ligatures[input[i]] = ligSets[i].Ligature
412		else:
413			assert 0, "unknown format: %s" % self.Format
414		self.ligatures = ligatures
415		del self.Format # Don't need this anymore
416
417	def preWrite(self, font):
418		self.Format = 1
419		ligatures = getattr(self, "ligatures", None)
420		if ligatures is None:
421			ligatures = self.ligatures = {}
422		items = list(ligatures.items())
423		for i in range(len(items)):
424			glyphName, set = items[i]
425			items[i] = font.getGlyphID(glyphName), glyphName, set
426		items.sort()
427		cov = Coverage()
428		cov.glyphs = [ item[1] for item in items]
429
430		ligSets = []
431		setList = [ item[-1] for item in items ]
432		for set in setList:
433			ligSet = LigatureSet()
434			ligs = ligSet.Ligature = []
435			for lig in set:
436				ligs.append(lig)
437			ligSets.append(ligSet)
438		# Useful in that when splitting a sub-table because of an offset overflow
439		# I don't need to calculate the change in subtabl offset due to the coverage table size.
440		# Allows packing more rules in subtable.
441		self.sortCoverageLast = 1
442		return {"Coverage": cov, "LigatureSet": ligSets}
443
444	def toXML2(self, xmlWriter, font):
445		items = sorted(self.ligatures.items())
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 not isinstance(element, tuple):
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', 'VertGlyphCoverage', 'HorizGlyphCoverage',
487			'TopAccentCoverage', 'ExtendedShapeCoverage', 'MathKernCoverage'),
488	'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef',
489			'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'),
490	'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor',
491			'Mark2Anchor', 'MarkAnchor'),
492	'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice',
493			'XDeviceTable', 'YDeviceTable', 'DeviceTable'),
494	'Axis': ('HorizAxis', 'VertAxis',),
495	'MinMax': ('DefaultMinMax',),
496	'BaseCoord': ('MinCoord', 'MaxCoord',),
497	'JstfLangSys': ('DefJstfLangSys',),
498	'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB',
499			'ExtensionDisableGSUB',),
500	'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS',
501			'ExtensionDisableGPOS',),
502	'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',),
503	'MathKern': ('TopRightMathKern', 'TopLeftMathKern', 'BottomRightMathKern',
504			'BottomLeftMathKern'),
505	'MathGlyphConstruction': ('VertGlyphConstruction', 'HorizGlyphConstruction'),
506}
507
508#
509# OverFlow logic, to automatically create ExtensionLookups
510# XXX This should probably move to otBase.py
511#
512
513def fixLookupOverFlows(ttf, overflowRecord):
514	""" Either the offset from the LookupList to a lookup overflowed, or
515	an offset from a lookup to a subtable overflowed.
516	The table layout is:
517	GPSO/GUSB
518		Script List
519		Feature List
520		LookUpList
521			Lookup[0] and contents
522				SubTable offset list
523					SubTable[0] and contents
524					...
525					SubTable[n] and contents
526			...
527			Lookup[n] and contents
528				SubTable offset list
529					SubTable[0] and contents
530					...
531					SubTable[n] and contents
532	If the offset to a lookup overflowed (SubTableIndex is None)
533		we must promote the *previous*	lookup to an Extension type.
534	If the offset from a lookup to subtable overflowed, then we must promote it
535		to an Extension Lookup type.
536	"""
537	ok = 0
538	lookupIndex = overflowRecord.LookupListIndex
539	if (overflowRecord.SubTableIndex is None):
540		lookupIndex = lookupIndex - 1
541	if lookupIndex < 0:
542		return ok
543	if overflowRecord.tableType == 'GSUB':
544		extType = 7
545	elif overflowRecord.tableType == 'GPOS':
546		extType = 9
547
548	lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup
549	lookup = lookups[lookupIndex]
550	# If the previous lookup is an extType, look further back. Very unlikely, but possible.
551	while lookup.SubTable[0].__class__.LookupType == extType:
552		lookupIndex = lookupIndex -1
553		if lookupIndex < 0:
554			return ok
555		lookup = lookups[lookupIndex]
556
557	for si in range(len(lookup.SubTable)):
558		subTable = lookup.SubTable[si]
559		extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
560		extSubTable = extSubTableClass()
561		extSubTable.Format = 1
562		extSubTable.ExtSubTable = subTable
563		lookup.SubTable[si] = extSubTable
564	ok = 1
565	return ok
566
567def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
568	ok = 1
569	newSubTable.Format = oldSubTable.Format
570	if hasattr(oldSubTable, 'sortCoverageLast'):
571		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
572
573	oldAlts = sorted(oldSubTable.alternates.items())
574	oldLen = len(oldAlts)
575
576	if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']:
577		# Coverage table is written last. overflow is to or within the
578		# the coverage table. We will just cut the subtable in half.
579		newLen = oldLen//2
580
581	elif overflowRecord.itemName == 'AlternateSet':
582		# We just need to back up by two items
583		# from the overflowed AlternateSet index to make sure the offset
584		# to the Coverage table doesn't overflow.
585		newLen  = overflowRecord.itemIndex - 1
586
587	newSubTable.alternates = {}
588	for i in range(newLen, oldLen):
589		item = oldAlts[i]
590		key = item[0]
591		newSubTable.alternates[key] = item[1]
592		del oldSubTable.alternates[key]
593
594
595	return ok
596
597
598def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
599	ok = 1
600	newSubTable.Format = oldSubTable.Format
601	oldLigs = sorted(oldSubTable.ligatures.items())
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 = 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.ExtSubTable.__class__.LookupType
664		extSubTable = subtable
665		subtable = extSubTable.ExtSubTable
666		newExtSubTableClass = lookupTypes[overflowRecord.tableType][subtable.__class__.LookupType]
667		newExtSubTable = newExtSubTableClass()
668		newExtSubTable.Format = extSubTable.Format
669		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
670
671		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
672		newSubTable = newSubTableClass()
673		newExtSubTable.ExtSubTable = newSubTable
674	else:
675		subTableType = subtable.__class__.LookupType
676		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
677		newSubTable = newSubTableClass()
678		lookup.SubTable.insert(subIndex + 1, newSubTable)
679
680	if hasattr(lookup, 'SubTableCount'): # may not be defined yet.
681		lookup.SubTableCount = lookup.SubTableCount + 1
682
683	try:
684		splitFunc = splitTable[overflowRecord.tableType][subTableType]
685	except KeyError:
686		return ok
687
688	ok = splitFunc(subtable, newSubTable, overflowRecord)
689	return ok
690
691# End of OverFlow logic
692
693
694def _buildClasses():
695	import re
696	from .otData import otData
697
698	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
699	namespace = globals()
700
701	# populate module with classes
702	for name, table in otData:
703		baseClass = BaseTable
704		m = formatPat.match(name)
705		if m:
706			# XxxFormatN subtable, we only add the "base" table
707			name = m.group(1)
708			baseClass = FormatSwitchingBaseTable
709		if name not in namespace:
710			# the class doesn't exist yet, so the base implementation is used.
711			cls = type(name, (baseClass,), {})
712			namespace[name] = cls
713
714	for base, alts in _equivalents.items():
715		base = namespace[base]
716		for alt in alts:
717			namespace[alt] = type(alt, (base,), {})
718
719	global lookupTypes
720	lookupTypes = {
721		'GSUB': {
722			1: SingleSubst,
723			2: MultipleSubst,
724			3: AlternateSubst,
725			4: LigatureSubst,
726			5: ContextSubst,
727			6: ChainContextSubst,
728			7: ExtensionSubst,
729			8: ReverseChainSingleSubst,
730		},
731		'GPOS': {
732			1: SinglePos,
733			2: PairPos,
734			3: CursivePos,
735			4: MarkBasePos,
736			5: MarkLigPos,
737			6: MarkMarkPos,
738			7: ContextPos,
739			8: ChainContextPos,
740			9: ExtensionPos,
741		},
742	}
743	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
744	for lookupEnum in lookupTypes.values():
745		for enum, cls in lookupEnum.items():
746			cls.LookupType = enum
747
748	global featureParamTypes
749	featureParamTypes = {
750		'size': FeatureParamsSize,
751	}
752	for i in range(1, 20+1):
753		featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet
754	for i in range(1, 99+1):
755		featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants
756
757	# add converters to classes
758	from .otConverters import buildConverters
759	for name, table in otData:
760		m = formatPat.match(name)
761		if m:
762			# XxxFormatN subtable, add converter to "base" table
763			name, format = m.groups()
764			format = int(format)
765			cls = namespace[name]
766			if not hasattr(cls, "converters"):
767				cls.converters = {}
768				cls.convertersByName = {}
769			converters, convertersByName = buildConverters(table[1:], namespace)
770			cls.converters[format] = converters
771			cls.convertersByName[format] = convertersByName
772		else:
773			cls = namespace[name]
774			cls.converters, cls.convertersByName = buildConverters(table, namespace)
775
776
777_buildClasses()
778
779
780def _getGlyphsFromCoverageTable(coverage):
781	if coverage is None:
782		# empty coverage table
783		return []
784	else:
785		return coverage.glyphs
786