1d4d151390d1288f8d2df30f6dfa26a309c7334dajvr"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various 2d4d151390d1288f8d2df30f6dfa26a309c7334dajvrOpenType subtables. 3d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 464b5c80e80444a124da335e8d4d208bffcf2737bjvrMost are constructed upon import from data in otData.py, all are populated with 564b5c80e80444a124da335e8d4d208bffcf2737bjvrconverter objects from otConverters.py. 6d4d151390d1288f8d2df30f6dfa26a309c7334dajvr""" 71ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 830e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 92b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom .otBase import BaseTable, FormatSwitchingBaseTable 1030e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport operator 116e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbodimport warnings 12d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 14d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass LookupOrder(BaseTable): 15d4d151390d1288f8d2df30f6dfa26a309c7334dajvr """Dummy class; this table isn't defined, but is used, and is always NULL.""" 16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 17d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FeatureParams(BaseTable): 189e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 199e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod def compile(self, writer, font): 20f6502635b91a0b3ae0e3ef8add9b5fb2ed9e3872Behdad Esfahbod assert featureParamTypes.get(writer['FeatureTag']) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__) 219e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod BaseTable.compile(self, writer, font) 229e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 23d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod def toXML(self, xmlWriter, font, attrs=None, name=None): 24d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod BaseTable.toXML(self, xmlWriter, font, attrs, name=self.__class__.__name__) 25d76fa68785bc843ad83e2901ada96f2a5b02397dBehdad Esfahbod 269e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsSize(FeatureParams): 279e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 289e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsStylisticSet(FeatureParams): 309e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 319e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 329e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsCharacterVariants(FeatureParams): 339e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 34d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 35d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvrclass Coverage(FormatSwitchingBaseTable): 36d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 37d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # manual implementation to get rid of glyphID dependencies 38d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 391a53beb7b4bb252dc002dcdc2de70517d4727494jvr def postRead(self, rawTable, font): 40d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if self.Format == 1: 4143f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod # TODO only allow glyphs that are valid? 421a53beb7b4bb252dc002dcdc2de70517d4727494jvr self.glyphs = rawTable["GlyphArray"] 43d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr elif self.Format == 2: 44d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = self.glyphs = [] 451a53beb7b4bb252dc002dcdc2de70517d4727494jvr ranges = rawTable["RangeRecord"] 46183afe6408fade5d45dd45d062293c416a4adef1Behdad Esfahbod glyphOrder = font.getGlyphOrder() 4717700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # Some SIL fonts have coverage entries that don't have sorted 4817700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # StartCoverageIndex. If it is so, fixup and warn. We undo 4917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # this when writing font out. 50b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod sorted_ranges = sorted(ranges, key=lambda a: a.StartCoverageIndex) 5117700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if ranges != sorted_ranges: 5217700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.") 5317700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod ranges = sorted_ranges 5417700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod del sorted_ranges 55d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for r in ranges: 56d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr assert r.StartCoverageIndex == len(glyphs), \ 57d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr (r.StartCoverageIndex, len(glyphs)) 58d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start = r.Start 59d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr end = r.End 606e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod try: 61dc87372c88dfd3bb4418c4113d9301102324359eBehdad Esfahbod startID = font.getGlyphID(start, requireReal=True) 626e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod except KeyError: 636e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod warnings.warn("Coverage table has start glyph ID out of range: %s." % start) 646e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod continue 656e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod try: 6643f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod endID = font.getGlyphID(end, requireReal=True) + 1 676e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod except KeyError: 68650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # Apparently some tools use 65535 to "match all" the range 69650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod if end != 'glyph65535': 70650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod warnings.warn("Coverage table has end glyph ID out of range: %s." % end) 71650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # NOTE: We clobber out-of-range things here. There are legit uses for those, 72650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # but none that we have seen in the wild. 736e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod endID = len(glyphOrder) 7443f6e363475caee9df35d52f536d6de4cf2d443bBehdad Esfahbod glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID)) 75d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr else: 761a53beb7b4bb252dc002dcdc2de70517d4727494jvr assert 0, "unknown format: %s" % self.Format 77e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod del self.Format # Don't need this anymore 78d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 79d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def preWrite(self, font): 8031ae380735dbf75a460df002a7fc2bcd81e28153jvr glyphs = getattr(self, "glyphs", None) 81257fba71729b4552a73f296b8fbd5b6224cdcabbjvr if glyphs is None: 82257fba71729b4552a73f296b8fbd5b6224cdcabbjvr glyphs = self.glyphs = [] 83d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 1 84a138467da399c071dbd78682938642b8e8d8a4cejvr rawTable = {"GlyphArray": glyphs} 85823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 86a138467da399c071dbd78682938642b8e8d8a4cejvr if glyphs: 871a53beb7b4bb252dc002dcdc2de70517d4727494jvr # find out whether Format 2 is more compact or not 88823f8cd15f16bb9dc3991c2672f16dd90579711bjvr glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ] 8917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod brokenOrder = sorted(glyphIDs) != glyphIDs 90d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 91d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphIDs[0] 92d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges = [[last]] 93d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphID in glyphIDs[1:]: 94a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if glyphID != last + 1: 95d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 96d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges.append([glyphID]) 97d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphID 98d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 99d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 10017700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word 101d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # Format 2 is more compact 102d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = 0 103d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for i in range(len(ranges)): 104d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start, end = ranges[i] 105d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r = RangeRecord() 10617700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod r.StartID = start 107d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.Start = font.getGlyphName(start) 108d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.End = font.getGlyphName(end) 109d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.StartCoverageIndex = index 110d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[i] = r 111d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = index + end - start + 1 11217700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if brokenOrder: 11317700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.") 114b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod ranges.sort(key=lambda a: a.StartID) 11517700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod for r in ranges: 11617700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod del r.StartID 117d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 2 1181a53beb7b4bb252dc002dcdc2de70517d4727494jvr rawTable = {"RangeRecord": ranges} 119d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr #else: 120d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # fallthrough; Format 1 is more compact 121d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.Format = format 1221a53beb7b4bb252dc002dcdc2de70517d4727494jvr return rawTable 123d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 124d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def toXML2(self, xmlWriter, font): 125a138467da399c071dbd78682938642b8e8d8a4cejvr for glyphName in getattr(self, "glyphs", []): 126d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.simpletag("Glyph", value=glyphName) 127d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.newline() 128d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 1293a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 130d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = getattr(self, "glyphs", None) 131d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if glyphs is None: 132d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = [] 133d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.glyphs = glyphs 134d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(attrs["value"]) 135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 137823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef doModulo(value): 138823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if value < 0: 139823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value + 65536 140823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 141823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 1421d6360af4c553a7b26447b0e1e96f7b701c9325ejvrclass SingleSubst(FormatSwitchingBaseTable): 1431d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 1441d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def postRead(self, rawTable, font): 1451d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = {} 1461fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) 147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lenMapping = len(input) 1481d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if self.Format == 1: 1491d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = rawTable["DeltaGlyphID"] 150823f8cd15f16bb9dc3991c2672f16dd90579711bjvr inputGIDS = [ font.getGlyphID(name) for name in input ] 151823f8cd15f16bb9dc3991c2672f16dd90579711bjvr outGIDS = [ glyphID + delta for glyphID in inputGIDS ] 152e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod outGIDS = map(doModulo, outGIDS) 153823f8cd15f16bb9dc3991c2672f16dd90579711bjvr outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ] 154e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [mapping]*lenMapping, input, outNames)) 1551d6360af4c553a7b26447b0e1e96f7b701c9325ejvr elif self.Format == 2: 1561d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert len(input) == rawTable["GlyphCount"], \ 1571d6360af4c553a7b26447b0e1e96f7b701c9325ejvr "invalid SingleSubstFormat2 table" 1581d6360af4c553a7b26447b0e1e96f7b701c9325ejvr subst = rawTable["Substitute"] 159e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [mapping]*lenMapping, input, subst)) 1601d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1611d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert 0, "unknown format: %s" % self.Format 1621d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.mapping = mapping 163e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod del self.Format # Don't need this anymore 1641d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 1651d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def preWrite(self, font): 16631ae380735dbf75a460df002a7fc2bcd81e28153jvr mapping = getattr(self, "mapping", None) 16731ae380735dbf75a460df002a7fc2bcd81e28153jvr if mapping is None: 16831ae380735dbf75a460df002a7fc2bcd81e28153jvr mapping = self.mapping = {} 169c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(mapping.items()) 170823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 171e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod gidItems = [(getGlyphID(a), getGlyphID(b)) for a,b in items] 172ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod sortableItems = sorted(zip(gidItems, items)) 173823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 174823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # figure out format 1751d6360af4c553a7b26447b0e1e96f7b701c9325ejvr format = 2 1761d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = None 177823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for inID, outID in gidItems: 1781d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if delta is None: 1791d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = outID - inID 180e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod if delta < -32768: 181e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod delta += 65536 182e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod elif delta > 32767: 183e06166b83aa82582dab2e011520b1a77f72aa9f9Behdad Esfahbod delta -= 65536 1841d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1851d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if delta != outID - inID: 1861d6360af4c553a7b26447b0e1e96f7b701c9325ejvr break 1871d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1881d6360af4c553a7b26447b0e1e96f7b701c9325ejvr format = 1 189823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 1901d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable = {} 1911d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.Format = format 1921d6360af4c553a7b26447b0e1e96f7b701c9325ejvr cov = Coverage() 193823f8cd15f16bb9dc3991c2672f16dd90579711bjvr input = [ item [1][0] for item in sortableItems] 194823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subst = [ item [1][1] for item in sortableItems] 195823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = input 1961d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["Coverage"] = cov 1971d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if format == 1: 1981d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert delta is not None 1991d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["DeltaGlyphID"] = delta 2001d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 2011d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["Substitute"] = subst 2021d6360af4c553a7b26447b0e1e96f7b701c9325ejvr return rawTable 2031d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 2041d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def toXML2(self, xmlWriter, font): 205ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.mapping.items()) 2061d6360af4c553a7b26447b0e1e96f7b701c9325ejvr for inGlyph, outGlyph in items: 2071d6360af4c553a7b26447b0e1e96f7b701c9325ejvr xmlWriter.simpletag("Substitution", 2081d6360af4c553a7b26447b0e1e96f7b701c9325ejvr [("in", inGlyph), ("out", outGlyph)]) 2091d6360af4c553a7b26447b0e1e96f7b701c9325ejvr xmlWriter.newline() 2101d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 2113a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 2121d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = getattr(self, "mapping", None) 2131d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if mapping is None: 2141d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = {} 2151d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.mapping = mapping 2161d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping[attrs["in"]] = attrs["out"] 2171d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 2181d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 219a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvrclass ClassDef(FormatSwitchingBaseTable): 220a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 221a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def postRead(self, rawTable, font): 222a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = {} 22363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod glyphOrder = font.getGlyphOrder() 224823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 225a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if self.Format == 1: 226a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr start = rawTable["StartGlyph"] 227823f8cd15f16bb9dc3991c2672f16dd90579711bjvr classList = rawTable["ClassValueArray"] 22863b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod try: 22963b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod startID = font.getGlyphID(start, requireReal=True) 23063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod except KeyError: 23163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod warnings.warn("ClassDef table has start glyph ID out of range: %s." % start) 23263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod startID = len(glyphOrder) 23363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod endID = startID + len(classList) 23463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod if endID > len(glyphOrder): 23563b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod warnings.warn("ClassDef table has entries for out of range glyph IDs: %s,%s." % (start, len(classList))) 236650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # NOTE: We clobber out-of-range things here. There are legit uses for those, 237650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # but none that we have seen in the wild. 23863b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod endID = len(glyphOrder) 239823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 24063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod for glyphID, cls in zip(range(startID, endID), classList): 24163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod classDefs[glyphOrder[glyphID]] = cls 242823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 243a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr elif self.Format == 2: 244a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr records = rawTable["ClassRangeRecord"] 245a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for rec in records: 246a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr start = rec.Start 247a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr end = rec.End 248a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr cls = rec.Class 24963b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod try: 25063b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod startID = font.getGlyphID(start, requireReal=True) 25163b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod except KeyError: 25263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod warnings.warn("ClassDef table has start glyph ID out of range: %s." % start) 25363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod continue 25463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod try: 255b23b4cefda2acf417bfecf2135357554dbd32277Behdad Esfahbod endID = font.getGlyphID(end, requireReal=True) + 1 25663b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod except KeyError: 257650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # Apparently some tools use 65535 to "match all" the range 258650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod if end != 'glyph65535': 259650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod warnings.warn("ClassDef table has end glyph ID out of range: %s." % end) 260650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # NOTE: We clobber out-of-range things here. There are legit uses for those, 261650a38fffc19e23426816994bf059441a8853341Behdad Esfahbod # but none that we have seen in the wild. 26263b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod endID = len(glyphOrder) 26363b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod for glyphID in range(startID, endID): 26463b257e677993295ee015e02b92ff438a5e91135Behdad Esfahbod classDefs[glyphOrder[glyphID]] = cls 265a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr else: 266a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr assert 0, "unknown format: %s" % self.Format 267a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr self.classDefs = classDefs 268e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod del self.Format # Don't need this anymore 269a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 270a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def preWrite(self, font): 27131ae380735dbf75a460df002a7fc2bcd81e28153jvr classDefs = getattr(self, "classDefs", None) 27231ae380735dbf75a460df002a7fc2bcd81e28153jvr if classDefs is None: 27331ae380735dbf75a460df002a7fc2bcd81e28153jvr classDefs = self.classDefs = {} 274c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(classDefs.items()) 27583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod format = 2 27683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rawTable = {"ClassRangeRecord": []} 277823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 278a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for i in range(len(items)): 279a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr glyphName, cls = items[i] 280823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = getGlyphID(glyphName), glyphName, cls 281a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr items.sort() 28231ae380735dbf75a460df002a7fc2bcd81e28153jvr if items: 28331ae380735dbf75a460df002a7fc2bcd81e28153jvr last, lastName, lastCls = items[0] 28483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod ranges = [[lastCls, last, lastName]] 28531ae380735dbf75a460df002a7fc2bcd81e28153jvr for glyphID, glyphName, cls in items[1:]: 28631ae380735dbf75a460df002a7fc2bcd81e28153jvr if glyphID != last + 1 or cls != lastCls: 28783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod ranges[-1].extend([last, lastName]) 28883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod ranges.append([cls, glyphID, glyphName]) 28931ae380735dbf75a460df002a7fc2bcd81e28153jvr last = glyphID 29031ae380735dbf75a460df002a7fc2bcd81e28153jvr lastName = glyphName 29131ae380735dbf75a460df002a7fc2bcd81e28153jvr lastCls = cls 29283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod ranges[-1].extend([last, lastName]) 29383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod 29483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod startGlyph = ranges[0][1] 29583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod endGlyph = ranges[-1][3] 29683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod glyphCount = endGlyph - startGlyph + 1 29783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod if len(ranges) * 3 < glyphCount + 1: 29883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod # Format 2 is more compact 29983e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod for i in range(len(ranges)): 30083e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod cls, start, startName, end, endName = ranges[i] 30183e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rec = ClassRangeRecord() 30283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rec.Start = startName 30383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rec.End = endName 30483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rec.Class = cls 30583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod ranges[i] = rec 30683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod format = 2 30783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rawTable = {"ClassRangeRecord": ranges} 30883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod else: 30983e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod # Format 1 is more compact 31083e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod startGlyphName = ranges[0][2] 31183e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod classes = [0] * glyphCount 31283e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod for cls, start, startName, end, endName in ranges: 31383e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod for g in range(start - startGlyph, end - startGlyph + 1): 31483e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod classes[g] = cls 31583e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod format = 1 31683e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod rawTable = {"StartGlyph": startGlyphName, "ClassValueArray": classes} 31783e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod self.Format = format 31883e2f34194696f76eb89c384bdddd10419b88e10Behdad Esfahbod return rawTable 319a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 320a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def toXML2(self, xmlWriter, font): 321ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.classDefs.items()) 322a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for glyphName, cls in items: 323a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)]) 324a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr xmlWriter.newline() 325a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 3263a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 327a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = getattr(self, "classDefs", None) 328a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if classDefs is None: 329a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = {} 330a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr self.classDefs = classDefs 331a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs[attrs["glyph"]] = int(attrs["class"]) 332a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 333a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 334b2486125e9438443a1419706c50958ab0676eb5ajvrclass AlternateSubst(FormatSwitchingBaseTable): 335b2486125e9438443a1419706c50958ab0676eb5ajvr 336b2486125e9438443a1419706c50958ab0676eb5ajvr def postRead(self, rawTable, font): 337b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = {} 338b2486125e9438443a1419706c50958ab0676eb5ajvr if self.Format == 1: 3391fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) 340b2486125e9438443a1419706c50958ab0676eb5ajvr alts = rawTable["AlternateSet"] 341823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if len(input) != len(alts): 342823f8cd15f16bb9dc3991c2672f16dd90579711bjvr assert len(input) == len(alts) 343b2486125e9438443a1419706c50958ab0676eb5ajvr for i in range(len(input)): 344b2486125e9438443a1419706c50958ab0676eb5ajvr alternates[input[i]] = alts[i].Alternate 345b2486125e9438443a1419706c50958ab0676eb5ajvr else: 346b2486125e9438443a1419706c50958ab0676eb5ajvr assert 0, "unknown format: %s" % self.Format 347b2486125e9438443a1419706c50958ab0676eb5ajvr self.alternates = alternates 348e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod del self.Format # Don't need this anymore 349b2486125e9438443a1419706c50958ab0676eb5ajvr 350b2486125e9438443a1419706c50958ab0676eb5ajvr def preWrite(self, font): 351b2486125e9438443a1419706c50958ab0676eb5ajvr self.Format = 1 35231ae380735dbf75a460df002a7fc2bcd81e28153jvr alternates = getattr(self, "alternates", None) 35331ae380735dbf75a460df002a7fc2bcd81e28153jvr if alternates is None: 35431ae380735dbf75a460df002a7fc2bcd81e28153jvr alternates = self.alternates = {} 355c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(alternates.items()) 356b2486125e9438443a1419706c50958ab0676eb5ajvr for i in range(len(items)): 357b2486125e9438443a1419706c50958ab0676eb5ajvr glyphName, set = items[i] 358b2486125e9438443a1419706c50958ab0676eb5ajvr items[i] = font.getGlyphID(glyphName), glyphName, set 359b2486125e9438443a1419706c50958ab0676eb5ajvr items.sort() 360b2486125e9438443a1419706c50958ab0676eb5ajvr cov = Coverage() 361823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = [ item[1] for item in items] 362b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = [] 363823f8cd15f16bb9dc3991c2672f16dd90579711bjvr setList = [ item[-1] for item in items] 364823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for set in setList: 365b2486125e9438443a1419706c50958ab0676eb5ajvr alts = AlternateSet() 366b2486125e9438443a1419706c50958ab0676eb5ajvr alts.Alternate = set 367b2486125e9438443a1419706c50958ab0676eb5ajvr alternates.append(alts) 368823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # a special case to deal with the fact that several hundred Adobe Japan1-5 369823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # CJK fonts will overflow an offset if the coverage table isn't pushed to the end. 370823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Also useful in that when splitting a sub-table because of an offset overflow 371823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # I don't need to calculate the change in the subtable offset due to the change in the coverage table size. 372823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Allows packing more rules in subtable. 373823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.sortCoverageLast = 1 374b2486125e9438443a1419706c50958ab0676eb5ajvr return {"Coverage": cov, "AlternateSet": alternates} 375b2486125e9438443a1419706c50958ab0676eb5ajvr 376b2486125e9438443a1419706c50958ab0676eb5ajvr def toXML2(self, xmlWriter, font): 377ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.alternates.items()) 378b2486125e9438443a1419706c50958ab0676eb5ajvr for glyphName, alternates in items: 379b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.begintag("AlternateSet", glyph=glyphName) 380b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 381b2486125e9438443a1419706c50958ab0676eb5ajvr for alt in alternates: 382b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.simpletag("Alternate", glyph=alt) 383b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 384b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.endtag("AlternateSet") 385b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 386b2486125e9438443a1419706c50958ab0676eb5ajvr 3873a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 388b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = getattr(self, "alternates", None) 389b2486125e9438443a1419706c50958ab0676eb5ajvr if alternates is None: 390b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = {} 391b2486125e9438443a1419706c50958ab0676eb5ajvr self.alternates = alternates 392b2486125e9438443a1419706c50958ab0676eb5ajvr glyphName = attrs["glyph"] 393b2486125e9438443a1419706c50958ab0676eb5ajvr set = [] 394b2486125e9438443a1419706c50958ab0676eb5ajvr alternates[glyphName] = set 395b2486125e9438443a1419706c50958ab0676eb5ajvr for element in content: 396b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 397b2486125e9438443a1419706c50958ab0676eb5ajvr continue 398b2486125e9438443a1419706c50958ab0676eb5ajvr name, attrs, content = element 399b2486125e9438443a1419706c50958ab0676eb5ajvr set.append(attrs["glyph"]) 400b2486125e9438443a1419706c50958ab0676eb5ajvr 401b2486125e9438443a1419706c50958ab0676eb5ajvr 402f2164abef39af7cfef989352fadfca628ed077b2jvrclass LigatureSubst(FormatSwitchingBaseTable): 403f2164abef39af7cfef989352fadfca628ed077b2jvr 404f2164abef39af7cfef989352fadfca628ed077b2jvr def postRead(self, rawTable, font): 405f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = {} 406f2164abef39af7cfef989352fadfca628ed077b2jvr if self.Format == 1: 407ea82d6dfd194f6ff6dfc7a65e7d9a0f9d671d827Behdad Esfahbod input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) 408f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets = rawTable["LigatureSet"] 409f2164abef39af7cfef989352fadfca628ed077b2jvr assert len(input) == len(ligSets) 410f2164abef39af7cfef989352fadfca628ed077b2jvr for i in range(len(input)): 411f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures[input[i]] = ligSets[i].Ligature 412f2164abef39af7cfef989352fadfca628ed077b2jvr else: 413f2164abef39af7cfef989352fadfca628ed077b2jvr assert 0, "unknown format: %s" % self.Format 414f2164abef39af7cfef989352fadfca628ed077b2jvr self.ligatures = ligatures 415e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod del self.Format # Don't need this anymore 416f2164abef39af7cfef989352fadfca628ed077b2jvr 417f2164abef39af7cfef989352fadfca628ed077b2jvr def preWrite(self, font): 418e93e29125f6c88a352e15b4d7732fc94a6402548Behdad Esfahbod self.Format = 1 41931ae380735dbf75a460df002a7fc2bcd81e28153jvr ligatures = getattr(self, "ligatures", None) 42031ae380735dbf75a460df002a7fc2bcd81e28153jvr if ligatures is None: 42131ae380735dbf75a460df002a7fc2bcd81e28153jvr ligatures = self.ligatures = {} 422c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(ligatures.items()) 423f2164abef39af7cfef989352fadfca628ed077b2jvr for i in range(len(items)): 424f2164abef39af7cfef989352fadfca628ed077b2jvr glyphName, set = items[i] 425f2164abef39af7cfef989352fadfca628ed077b2jvr items[i] = font.getGlyphID(glyphName), glyphName, set 426f2164abef39af7cfef989352fadfca628ed077b2jvr items.sort() 427f2164abef39af7cfef989352fadfca628ed077b2jvr cov = Coverage() 428823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = [ item[1] for item in items] 429823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 430f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets = [] 431823f8cd15f16bb9dc3991c2672f16dd90579711bjvr setList = [ item[-1] for item in items ] 432823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for set in setList: 433f2164abef39af7cfef989352fadfca628ed077b2jvr ligSet = LigatureSet() 434f2164abef39af7cfef989352fadfca628ed077b2jvr ligs = ligSet.Ligature = [] 435f2164abef39af7cfef989352fadfca628ed077b2jvr for lig in set: 436f2164abef39af7cfef989352fadfca628ed077b2jvr ligs.append(lig) 437f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets.append(ligSet) 438823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Useful in that when splitting a sub-table because of an offset overflow 439823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # I don't need to calculate the change in subtabl offset due to the coverage table size. 440823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Allows packing more rules in subtable. 441823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.sortCoverageLast = 1 442f2164abef39af7cfef989352fadfca628ed077b2jvr return {"Coverage": cov, "LigatureSet": ligSets} 443f2164abef39af7cfef989352fadfca628ed077b2jvr 444f2164abef39af7cfef989352fadfca628ed077b2jvr def toXML2(self, xmlWriter, font): 445ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.ligatures.items()) 446f2164abef39af7cfef989352fadfca628ed077b2jvr for glyphName, ligSets in items: 447f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.begintag("LigatureSet", glyph=glyphName) 448f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 449f2164abef39af7cfef989352fadfca628ed077b2jvr for lig in ligSets: 450f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.simpletag("Ligature", glyph=lig.LigGlyph, 451f2164abef39af7cfef989352fadfca628ed077b2jvr components=",".join(lig.Component)) 452f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 453f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.endtag("LigatureSet") 454f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 455f2164abef39af7cfef989352fadfca628ed077b2jvr 4563a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 457f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = getattr(self, "ligatures", None) 458f2164abef39af7cfef989352fadfca628ed077b2jvr if ligatures is None: 459f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = {} 460f2164abef39af7cfef989352fadfca628ed077b2jvr self.ligatures = ligatures 461f2164abef39af7cfef989352fadfca628ed077b2jvr glyphName = attrs["glyph"] 462f2164abef39af7cfef989352fadfca628ed077b2jvr ligs = [] 463f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures[glyphName] = ligs 464f2164abef39af7cfef989352fadfca628ed077b2jvr for element in content: 465b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 466f2164abef39af7cfef989352fadfca628ed077b2jvr continue 467f2164abef39af7cfef989352fadfca628ed077b2jvr name, attrs, content = element 468f2164abef39af7cfef989352fadfca628ed077b2jvr lig = Ligature() 469f2164abef39af7cfef989352fadfca628ed077b2jvr lig.LigGlyph = attrs["glyph"] 470f2164abef39af7cfef989352fadfca628ed077b2jvr lig.Component = attrs["components"].split(",") 471f2164abef39af7cfef989352fadfca628ed077b2jvr ligs.append(lig) 472f2164abef39af7cfef989352fadfca628ed077b2jvr 473f2164abef39af7cfef989352fadfca628ed077b2jvr 47464b5c80e80444a124da335e8d4d208bffcf2737bjvr# 47564b5c80e80444a124da335e8d4d208bffcf2737bjvr# For each subtable format there is a class. However, we don't really distinguish 47664b5c80e80444a124da335e8d4d208bffcf2737bjvr# between "field name" and "format name": often these are the same. Yet there's 47764b5c80e80444a124da335e8d4d208bffcf2737bjvr# a whole bunch of fields with different names. The following dict is a mapping 47864b5c80e80444a124da335e8d4d208bffcf2737bjvr# from "format name" to "field name". _buildClasses() uses this to create a 47964b5c80e80444a124da335e8d4d208bffcf2737bjvr# subclass for each alternate field name. 48064b5c80e80444a124da335e8d4d208bffcf2737bjvr# 48164b5c80e80444a124da335e8d4d208bffcf2737bjvr_equivalents = { 48264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MarkArray': ("Mark1Array",), 48364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'LangSys': ('DefaultLangSys',), 48464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage', 485d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage', 486f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod 'LookAheadCoverage', 'VertGlyphCoverage', 'HorizGlyphCoverage', 487f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod 'TopAccentCoverage', 'ExtendedShapeCoverage', 'MathKernCoverage'), 48864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef', 48927f108844ef1aeeb5c694a8304bf6c4944783d42jvr 'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'), 49064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor', 49164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Mark2Anchor', 'MarkAnchor'), 49264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice', 49364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'XDeviceTable', 'YDeviceTable', 'DeviceTable'), 49464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Axis': ('HorizAxis', 'VertAxis',), 49564b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MinMax': ('DefaultMinMax',), 49664b5c80e80444a124da335e8d4d208bffcf2737bjvr 'BaseCoord': ('MinCoord', 'MaxCoord',), 49764b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfLangSys': ('DefJstfLangSys',), 49864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB', 49964b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGSUB',), 50064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS', 50164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGPOS',), 50264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',), 503f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod 'MathKern': ('TopRightMathKern', 'TopLeftMathKern', 'BottomRightMathKern', 504f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod 'BottomLeftMathKern'), 505f952a22af7b59862d49ee2947932f4baab37a5edBehdad Esfahbod 'MathGlyphConstruction': ('VertGlyphConstruction', 'HorizGlyphConstruction'), 50664b5c80e80444a124da335e8d4d208bffcf2737bjvr} 507d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 508e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# 509e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# OverFlow logic, to automatically create ExtensionLookups 510e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# XXX This should probably move to otBase.py 511e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# 512d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 513823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixLookupOverFlows(ttf, overflowRecord): 514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ Either the offset from the LookupList to a lookup overflowed, or 515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr an offset from a lookup to a subtable overflowed. 516823f8cd15f16bb9dc3991c2672f16dd90579711bjvr The table layout is: 517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr GPSO/GUSB 518823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Script List 519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Feature List 520823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookUpList 521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Lookup[0] and contents 522823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable offset list 523823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[0] and contents 524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 525823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[n] and contents 526823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 527823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Lookup[n] and contents 528823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable offset list 529823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[0] and contents 530823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 531823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[n] and contents 5329e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod If the offset to a lookup overflowed (SubTableIndex is None) 533823f8cd15f16bb9dc3991c2672f16dd90579711bjvr we must promote the *previous* lookup to an Extension type. 534823f8cd15f16bb9dc3991c2672f16dd90579711bjvr If the offset from a lookup to subtable overflowed, then we must promote it 535823f8cd15f16bb9dc3991c2672f16dd90579711bjvr to an Extension Lookup type. 536823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 537823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 0 538823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = overflowRecord.LookupListIndex 5399e6ef94b5554c5b7dda2de9c863c11ed4b996b7aBehdad Esfahbod if (overflowRecord.SubTableIndex is None): 540823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = lookupIndex - 1 541823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if lookupIndex < 0: 542823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 543823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.tableType == 'GSUB': 544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extType = 7 545823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.tableType == 'GPOS': 546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extType = 9 547823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup 549823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = lookups[lookupIndex] 550823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # If the previous lookup is an extType, look further back. Very unlikely, but possible. 551319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod while lookup.SubTable[0].__class__.LookupType == extType: 552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = lookupIndex -1 553823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if lookupIndex < 0: 554823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = lookups[lookupIndex] 556823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 557823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for si in range(len(lookup.SubTable)): 558823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subTable = lookup.SubTable[si] 559823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTableClass = lookupTypes[overflowRecord.tableType][extType] 560823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable = extSubTableClass() 561823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable.Format = 1 562823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable.ExtSubTable = subTable 563823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable[si] = extSubTable 564823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 565823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 566823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 567823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitAlternateSubst(oldSubTable, newSubTable, overflowRecord): 568823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.Format = oldSubTable.Format 570823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(oldSubTable, 'sortCoverageLast'): 571823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast 572823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 573ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod oldAlts = sorted(oldSubTable.alternates.items()) 574823f8cd15f16bb9dc3991c2672f16dd90579711bjvr oldLen = len(oldAlts) 575823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 576823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']: 577823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage table is written last. overflow is to or within the 578823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the coverage table. We will just cut the subtable in half. 57932c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod newLen = oldLen//2 580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.itemName == 'AlternateSet': 582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We just need to back up by two items 583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # from the overflowed AlternateSet index to make sure the offset 584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to the Coverage table doesn't overflow. 585823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newLen = overflowRecord.itemIndex - 1 586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 587823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.alternates = {} 588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(newLen, oldLen): 589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = oldAlts[i] 590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr key = item[0] 591823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.alternates[key] = item[1] 592823f8cd15f16bb9dc3991c2672f16dd90579711bjvr del oldSubTable.alternates[key] 593823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 594823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 595823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 596823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 597823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 598823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitLigatureSubst(oldSubTable, newSubTable, overflowRecord): 599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 600823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.Format = oldSubTable.Format 601ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod oldLigs = sorted(oldSubTable.ligatures.items()) 602823f8cd15f16bb9dc3991c2672f16dd90579711bjvr oldLen = len(oldLigs) 603823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 604823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']: 605823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage table is written last. overflow is to or within the 606823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the coverage table. We will just cut the subtable in half. 60732c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod newLen = oldLen//2 608823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 609823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.itemName == 'LigatureSet': 610823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We just need to back up by two items 611823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # from the overflowed AlternateSet index to make sure the offset 612823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to the Coverage table doesn't overflow. 613823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newLen = overflowRecord.itemIndex - 1 614823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 615823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.ligatures = {} 616823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(newLen, oldLen): 617823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = oldLigs[i] 618823f8cd15f16bb9dc3991c2672f16dd90579711bjvr key = item[0] 619823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.ligatures[key] = item[1] 620823f8cd15f16bb9dc3991c2672f16dd90579711bjvr del oldSubTable.ligatures[key] 621823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 622823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 623823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 624823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 625823f8cd15f16bb9dc3991c2672f16dd90579711bjvrsplitTable = { 'GSUB': { 626823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 1: splitSingleSubst, 627823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 2: splitMultipleSubst, 628823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 3: splitAlternateSubst, 629823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 4: splitLigatureSubst, 630823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 5: splitContextSubst, 631823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 6: splitChainContextSubst, 632823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 7: splitExtensionSubst, 633823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 8: splitReverseChainSingleSubst, 634823f8cd15f16bb9dc3991c2672f16dd90579711bjvr }, 635823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 'GPOS': { 636823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 1: splitSinglePos, 637823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 2: splitPairPos, 638823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 3: splitCursivePos, 639823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 4: splitMarkBasePos, 640823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 5: splitMarkLigPos, 641823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 6: splitMarkMarkPos, 642823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 7: splitContextPos, 643823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 8: splitChainContextPos, 644823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 9: splitExtensionPos, 645823f8cd15f16bb9dc3991c2672f16dd90579711bjvr } 646823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 647823f8cd15f16bb9dc3991c2672f16dd90579711bjvr } 648823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 649823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixSubTableOverFlows(ttf, overflowRecord): 650823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 651823f8cd15f16bb9dc3991c2672f16dd90579711bjvr An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts. 652823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 653823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 0 654823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table = ttf[overflowRecord.tableType].table 655823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex] 656823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subIndex = overflowRecord.SubTableIndex 657823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subtable = lookup.SubTable[subIndex] 658823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 659823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(subtable, 'ExtSubTable'): 660823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We split the subtable of the Extension table, and add a new Extension table 661823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to contain the new subtable. 662823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 663319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod subTableType = subtable.ExtSubTable.__class__.LookupType 664823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable = subtable 665823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subtable = extSubTable.ExtSubTable 666319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod newExtSubTableClass = lookupTypes[overflowRecord.tableType][subtable.__class__.LookupType] 667823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable = newExtSubTableClass() 668823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable.Format = extSubTable.Format 669823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable.insert(subIndex + 1, newExtSubTable) 670823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 671823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType] 672823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable = newSubTableClass() 673823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable.ExtSubTable = newSubTable 674823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 675319643ade2891343cab878ac22eb8fc76470f08aBehdad Esfahbod subTableType = subtable.__class__.LookupType 676823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType] 677823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable = newSubTableClass() 678823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable.insert(subIndex + 1, newSubTable) 679823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 680823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(lookup, 'SubTableCount'): # may not be defined yet. 681823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTableCount = lookup.SubTableCount + 1 682823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 683823f8cd15f16bb9dc3991c2672f16dd90579711bjvr try: 684823f8cd15f16bb9dc3991c2672f16dd90579711bjvr splitFunc = splitTable[overflowRecord.tableType][subTableType] 685823f8cd15f16bb9dc3991c2672f16dd90579711bjvr except KeyError: 686823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 687823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 688823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = splitFunc(subtable, newSubTable, overflowRecord) 689823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 690823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 691e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# End of OverFlow logic 692823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 693823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 694d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildClasses(): 695e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod import re 6962b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from .otData import otData 697d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 698d4d151390d1288f8d2df30f6dfa26a309c7334dajvr formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") 699d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace = globals() 700d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 701d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # populate module with classes 702d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 703d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = BaseTable 704d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 705d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, we only add the "base" table 707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name = m.group(1) 708d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = FormatSwitchingBaseTable 709bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if name not in namespace: 71064b5c80e80444a124da335e8d4d208bffcf2737bjvr # the class doesn't exist yet, so the base implementation is used. 711e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod cls = type(name, (baseClass,), {}) 712d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace[name] = cls 713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 71464b5c80e80444a124da335e8d4d208bffcf2737bjvr for base, alts in _equivalents.items(): 715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr base = namespace[base] 716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for alt in alts: 717e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod namespace[alt] = type(alt, (base,), {}) 718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 719d4d151390d1288f8d2df30f6dfa26a309c7334dajvr global lookupTypes 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes = { 721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GSUB': { 722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SingleSubst, 723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: MultipleSubst, 724d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: AlternateSubst, 725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: LigatureSubst, 726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: ContextSubst, 727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: ChainContextSubst, 728d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ExtensionSubst, 7298e1d75b1a3fb4874bb78d9c34a11ba30de81a5ccjvr 8: ReverseChainSingleSubst, 730d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 731d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GPOS': { 732d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SinglePos, 733d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: PairPos, 734d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: CursivePos, 735d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: MarkBasePos, 736d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: MarkLigPos, 737d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: MarkMarkPos, 738d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ContextPos, 739d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8: ChainContextPos, 740d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 9: ExtensionPos, 741d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 742d4d151390d1288f8d2df30f6dfa26a309c7334dajvr } 743d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS 74464b5c80e80444a124da335e8d4d208bffcf2737bjvr for lookupEnum in lookupTypes.values(): 74564b5c80e80444a124da335e8d4d208bffcf2737bjvr for enum, cls in lookupEnum.items(): 74664b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.LookupType = enum 7479e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 7489e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod global featureParamTypes 7499e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes = { 7509e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 'size': FeatureParamsSize, 7519e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod } 7529e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod for i in range(1, 20+1): 7539e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet 7549e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod for i in range(1, 99+1): 7559e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants 756d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 757d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # add converters to classes 7582b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from .otConverters import buildConverters 759d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 760d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 761d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 762d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, add converter to "base" table 763d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, format = m.groups() 764d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = int(format) 765d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 766d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(cls, "converters"): 767d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters = {} 768d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName = {} 76964b5c80e80444a124da335e8d4d208bffcf2737bjvr converters, convertersByName = buildConverters(table[1:], namespace) 770d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters[format] = converters 771d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName[format] = convertersByName 772d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 773d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 77464b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.converters, cls.convertersByName = buildConverters(table, namespace) 775d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 776d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 777d4d151390d1288f8d2df30f6dfa26a309c7334dajvr_buildClasses() 7781fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr 7791fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr 7801fae9b48d3a275c6e1dc0649c40ae121dfc4098djvrdef _getGlyphsFromCoverageTable(coverage): 7811fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr if coverage is None: 7821fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr # empty coverage table 7831fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr return [] 7841fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr else: 7851fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr return coverage.glyphs 786