Home | History | Annotate | Download | only in tables
      1 """fontTools.ttLib.tables.otTables -- A collection of classes representing the various
      2 OpenType subtables.
      3 
      4 Most are constructed upon import from data in otData.py, all are populated with
      5 converter objects from otConverters.py.
      6 """
      7 from __future__ import print_function, division, absolute_import
      8 from fontTools.misc.py23 import *
      9 from .otBase import BaseTable, FormatSwitchingBaseTable
     10 import operator
     11 import warnings
     12 
     13 
     14 class LookupOrder(BaseTable):
     15 	"""Dummy class; this table isn't defined, but is used, and is always NULL."""
     16 
     17 class 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 
     26 class FeatureParamsSize(FeatureParams):
     27 	pass
     28 
     29 class FeatureParamsStylisticSet(FeatureParams):
     30 	pass
     31 
     32 class FeatureParamsCharacterVariants(FeatureParams):
     33 	pass
     34 
     35 class 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 
    137 def doModulo(value):
    138 	if value < 0:
    139 		return value + 65536
    140 	return value
    141 
    142 class 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 
    219 class 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 
    334 class 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 
    402 class 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 
    513 def 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 
    567 def 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 
    598 def 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 
    625 splitTable = {	'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 
    649 def 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 
    694 def _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 
    780 def _getGlyphsFromCoverageTable(coverage):
    781 	if coverage is None:
    782 		# empty coverage table
    783 		return []
    784 	else:
    785 		return coverage.glyphs
    786