otTables.py revision 32c10eecffb4923e0721c395e4b80fb732543f18
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""" 732c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division 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): 209e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod assert featureParamTypes.get(writer['FeatureTag'], None) == self.__class__, "Wrong FeatureParams type for feature '%s': %s" % (writer['FeatureTag'], self.__class__.__name__) 219e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod BaseTable.compile(self, writer, font) 229e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 239e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsSize(FeatureParams): 249e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 259e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 269e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsStylisticSet(FeatureParams): 279e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 289e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 299e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbodclass FeatureParamsCharacterVariants(FeatureParams): 309e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod pass 31d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 32d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvrclass Coverage(FormatSwitchingBaseTable): 33d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 34d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # manual implementation to get rid of glyphID dependencies 35d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 361a53beb7b4bb252dc002dcdc2de70517d4727494jvr def postRead(self, rawTable, font): 37d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if self.Format == 1: 381a53beb7b4bb252dc002dcdc2de70517d4727494jvr self.glyphs = rawTable["GlyphArray"] 39d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr elif self.Format == 2: 40d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = self.glyphs = [] 411a53beb7b4bb252dc002dcdc2de70517d4727494jvr ranges = rawTable["RangeRecord"] 42183afe6408fade5d45dd45d062293c416a4adef1Behdad Esfahbod glyphOrder = font.getGlyphOrder() 4317700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # Some SIL fonts have coverage entries that don't have sorted 4417700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # StartCoverageIndex. If it is so, fixup and warn. We undo 4517700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod # this when writing font out. 4617700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod sorted_ranges = sorted(ranges, cmp=lambda a,b: cmp(a.StartCoverageIndex,b.StartCoverageIndex)) 4717700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if ranges != sorted_ranges: 4817700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.") 4917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod ranges = sorted_ranges 5017700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod del sorted_ranges 51d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for r in ranges: 52d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr assert r.StartCoverageIndex == len(glyphs), \ 53d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr (r.StartCoverageIndex, len(glyphs)) 54d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start = r.Start 55d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr end = r.End 566e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod try: 576e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod startID = font.getGlyphID(start, requireReal=1) 586e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod except KeyError: 596e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod warnings.warn("Coverage table has start glyph ID out of range: %s." % start) 606e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod continue 616e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod try: 626e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod endID = font.getGlyphID(end, requireReal=1) 636e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod except KeyError: 646e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod warnings.warn("Coverage table has end glyph ID out of range: %s." % end) 656e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod endID = len(glyphOrder) 66d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(start) 67183afe6408fade5d45dd45d062293c416a4adef1Behdad Esfahbod rangeList = [glyphOrder[glyphID] for glyphID in range(startID + 1, endID) ] 68823f8cd15f16bb9dc3991c2672f16dd90579711bjvr glyphs += rangeList 696e7066765929929d8315a719aeff1c5e5a5d9e9aBehdad Esfahbod if start != end and endID < len(glyphOrder): 70d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(end) 71d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr else: 721a53beb7b4bb252dc002dcdc2de70517d4727494jvr assert 0, "unknown format: %s" % self.Format 73d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 74d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def preWrite(self, font): 7531ae380735dbf75a460df002a7fc2bcd81e28153jvr glyphs = getattr(self, "glyphs", None) 76257fba71729b4552a73f296b8fbd5b6224cdcabbjvr if glyphs is None: 77257fba71729b4552a73f296b8fbd5b6224cdcabbjvr glyphs = self.glyphs = [] 78d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 1 79a138467da399c071dbd78682938642b8e8d8a4cejvr rawTable = {"GlyphArray": glyphs} 80823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 81a138467da399c071dbd78682938642b8e8d8a4cejvr if glyphs: 821a53beb7b4bb252dc002dcdc2de70517d4727494jvr # find out whether Format 2 is more compact or not 83823f8cd15f16bb9dc3991c2672f16dd90579711bjvr glyphIDs = [getGlyphID(glyphName) for glyphName in glyphs ] 8417700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod brokenOrder = sorted(glyphIDs) != glyphIDs 85d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 86d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphIDs[0] 87d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges = [[last]] 88d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphID in glyphIDs[1:]: 89a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if glyphID != last + 1: 90d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 91d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges.append([glyphID]) 92d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphID 93d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 94d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 9517700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word 96d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # Format 2 is more compact 97d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = 0 98d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for i in range(len(ranges)): 99d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start, end = ranges[i] 100d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r = RangeRecord() 10117700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod r.StartID = start 102d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.Start = font.getGlyphName(start) 103d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.End = font.getGlyphName(end) 104d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.StartCoverageIndex = index 105d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[i] = r 106d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = index + end - start + 1 10717700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod if brokenOrder: 10817700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod warnings.warn("GSUB/GPOS Coverage is not sorted by glyph ids.") 10917700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod ranges.sort(cmp=lambda a,b: cmp(a.StartID,b.StartID)) 11017700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod for r in ranges: 11117700bf926645d7def1d4013b2e119d2be4ff904Behdad Esfahbod del r.StartID 112d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 2 1131a53beb7b4bb252dc002dcdc2de70517d4727494jvr rawTable = {"RangeRecord": ranges} 114d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr #else: 115d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # fallthrough; Format 1 is more compact 116d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.Format = format 1171a53beb7b4bb252dc002dcdc2de70517d4727494jvr return rawTable 118d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 119d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def toXML2(self, xmlWriter, font): 120a138467da399c071dbd78682938642b8e8d8a4cejvr for glyphName in getattr(self, "glyphs", []): 121d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.simpletag("Glyph", value=glyphName) 122d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.newline() 123d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 1243a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 125d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = getattr(self, "glyphs", None) 126d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if glyphs is None: 127d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = [] 128d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.glyphs = glyphs 129d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(attrs["value"]) 130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 132823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef doModulo(value): 133823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if value < 0: 134823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value + 65536 135823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return value 136823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 1371d6360af4c553a7b26447b0e1e96f7b701c9325ejvrclass SingleSubst(FormatSwitchingBaseTable): 1381d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 1391d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def postRead(self, rawTable, font): 1401d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = {} 1411fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) 142823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lenMapping = len(input) 1431d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if self.Format == 1: 1441d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = rawTable["DeltaGlyphID"] 145823f8cd15f16bb9dc3991c2672f16dd90579711bjvr inputGIDS = [ font.getGlyphID(name) for name in input ] 146e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod inputGIDS = map(doModulo, inputGIDS) 147823f8cd15f16bb9dc3991c2672f16dd90579711bjvr outGIDS = [ glyphID + delta for glyphID in inputGIDS ] 148e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod outGIDS = map(doModulo, outGIDS) 149823f8cd15f16bb9dc3991c2672f16dd90579711bjvr outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ] 150e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [mapping]*lenMapping, input, outNames)) 1511d6360af4c553a7b26447b0e1e96f7b701c9325ejvr elif self.Format == 2: 1521d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert len(input) == rawTable["GlyphCount"], \ 1531d6360af4c553a7b26447b0e1e96f7b701c9325ejvr "invalid SingleSubstFormat2 table" 1541d6360af4c553a7b26447b0e1e96f7b701c9325ejvr subst = rawTable["Substitute"] 155e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [mapping]*lenMapping, input, subst)) 1561d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1571d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert 0, "unknown format: %s" % self.Format 1581d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.mapping = mapping 1591d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 1601d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def preWrite(self, font): 16131ae380735dbf75a460df002a7fc2bcd81e28153jvr mapping = getattr(self, "mapping", None) 16231ae380735dbf75a460df002a7fc2bcd81e28153jvr if mapping is None: 16331ae380735dbf75a460df002a7fc2bcd81e28153jvr mapping = self.mapping = {} 164c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(mapping.items()) 165823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 166823f8cd15f16bb9dc3991c2672f16dd90579711bjvr gidItems = [(getGlyphID(item[0]), getGlyphID(item[1])) for item in items] 167ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod sortableItems = sorted(zip(gidItems, items)) 168823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 169823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # figure out format 1701d6360af4c553a7b26447b0e1e96f7b701c9325ejvr format = 2 1711d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = None 172823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for inID, outID in gidItems: 1731d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if delta is None: 1741d6360af4c553a7b26447b0e1e96f7b701c9325ejvr delta = outID - inID 1751d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1761d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if delta != outID - inID: 1771d6360af4c553a7b26447b0e1e96f7b701c9325ejvr break 1781d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1791d6360af4c553a7b26447b0e1e96f7b701c9325ejvr format = 1 180823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 1811d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable = {} 1821d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.Format = format 1831d6360af4c553a7b26447b0e1e96f7b701c9325ejvr cov = Coverage() 184823f8cd15f16bb9dc3991c2672f16dd90579711bjvr input = [ item [1][0] for item in sortableItems] 185823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subst = [ item [1][1] for item in sortableItems] 186823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = input 1871d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["Coverage"] = cov 1881d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if format == 1: 1891d6360af4c553a7b26447b0e1e96f7b701c9325ejvr assert delta is not None 1901d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["DeltaGlyphID"] = delta 1911d6360af4c553a7b26447b0e1e96f7b701c9325ejvr else: 1921d6360af4c553a7b26447b0e1e96f7b701c9325ejvr rawTable["Substitute"] = subst 1931d6360af4c553a7b26447b0e1e96f7b701c9325ejvr return rawTable 1941d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 1951d6360af4c553a7b26447b0e1e96f7b701c9325ejvr def toXML2(self, xmlWriter, font): 196ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.mapping.items()) 1971d6360af4c553a7b26447b0e1e96f7b701c9325ejvr for inGlyph, outGlyph in items: 1981d6360af4c553a7b26447b0e1e96f7b701c9325ejvr xmlWriter.simpletag("Substitution", 1991d6360af4c553a7b26447b0e1e96f7b701c9325ejvr [("in", inGlyph), ("out", outGlyph)]) 2001d6360af4c553a7b26447b0e1e96f7b701c9325ejvr xmlWriter.newline() 2011d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 2023a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 2031d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = getattr(self, "mapping", None) 2041d6360af4c553a7b26447b0e1e96f7b701c9325ejvr if mapping is None: 2051d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping = {} 2061d6360af4c553a7b26447b0e1e96f7b701c9325ejvr self.mapping = mapping 2071d6360af4c553a7b26447b0e1e96f7b701c9325ejvr mapping[attrs["in"]] = attrs["out"] 2081d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 2091d6360af4c553a7b26447b0e1e96f7b701c9325ejvr 210a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvrclass ClassDef(FormatSwitchingBaseTable): 211a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 212a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def postRead(self, rawTable, font): 213a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = {} 214823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphName = font.getGlyphName 215823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 216a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if self.Format == 1: 217a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr start = rawTable["StartGlyph"] 218823f8cd15f16bb9dc3991c2672f16dd90579711bjvr classList = rawTable["ClassValueArray"] 219823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lenList = len(classList) 220a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr glyphID = font.getGlyphID(start) 22197dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod gidList = list(range(glyphID, glyphID + len(classList))) 222823f8cd15f16bb9dc3991c2672f16dd90579711bjvr keyList = [getGlyphName(glyphID) for glyphID in gidList] 223823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 224e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [classDefs]*lenList, keyList, classList)) 225823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 226a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr elif self.Format == 2: 227a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr records = rawTable["ClassRangeRecord"] 228a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for rec in records: 229a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr start = rec.Start 230a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr end = rec.End 231a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr cls = rec.Class 232a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs[start] = cls 23397dea0a5d02ba1655d27a06fe91540e3495b8ef9Behdad Esfahbod glyphIDs = list(range(font.getGlyphID(start) + 1, font.getGlyphID(end))) 234823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lenList = len(glyphIDs) 235823f8cd15f16bb9dc3991c2672f16dd90579711bjvr keyList = [getGlyphName(glyphID) for glyphID in glyphIDs] 236e5ca79699d00fdf7ac6eaceaed372aea8d6bc1fdBehdad Esfahbod list(map(operator.setitem, [classDefs]*lenList, keyList, [cls]*lenList)) 237a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs[end] = cls 238a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr else: 239a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr assert 0, "unknown format: %s" % self.Format 240a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr self.classDefs = classDefs 241a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 242a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def preWrite(self, font): 24331ae380735dbf75a460df002a7fc2bcd81e28153jvr classDefs = getattr(self, "classDefs", None) 24431ae380735dbf75a460df002a7fc2bcd81e28153jvr if classDefs is None: 24531ae380735dbf75a460df002a7fc2bcd81e28153jvr classDefs = self.classDefs = {} 246c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(classDefs.items()) 247823f8cd15f16bb9dc3991c2672f16dd90579711bjvr getGlyphID = font.getGlyphID 248a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for i in range(len(items)): 249a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr glyphName, cls = items[i] 250823f8cd15f16bb9dc3991c2672f16dd90579711bjvr items[i] = getGlyphID(glyphName), glyphName, cls 251a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr items.sort() 25231ae380735dbf75a460df002a7fc2bcd81e28153jvr if items: 25331ae380735dbf75a460df002a7fc2bcd81e28153jvr last, lastName, lastCls = items[0] 25431ae380735dbf75a460df002a7fc2bcd81e28153jvr rec = ClassRangeRecord() 25531ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.Start = lastName 25631ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.Class = lastCls 25731ae380735dbf75a460df002a7fc2bcd81e28153jvr ranges = [rec] 25831ae380735dbf75a460df002a7fc2bcd81e28153jvr for glyphID, glyphName, cls in items[1:]: 25931ae380735dbf75a460df002a7fc2bcd81e28153jvr if glyphID != last + 1 or cls != lastCls: 26031ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.End = lastName 26131ae380735dbf75a460df002a7fc2bcd81e28153jvr rec = ClassRangeRecord() 26231ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.Start = glyphName 26331ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.Class = cls 26431ae380735dbf75a460df002a7fc2bcd81e28153jvr ranges.append(rec) 26531ae380735dbf75a460df002a7fc2bcd81e28153jvr last = glyphID 26631ae380735dbf75a460df002a7fc2bcd81e28153jvr lastName = glyphName 26731ae380735dbf75a460df002a7fc2bcd81e28153jvr lastCls = cls 26831ae380735dbf75a460df002a7fc2bcd81e28153jvr rec.End = lastName 26931ae380735dbf75a460df002a7fc2bcd81e28153jvr else: 27031ae380735dbf75a460df002a7fc2bcd81e28153jvr ranges = [] 271a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr self.Format = 2 # currently no support for Format 1 272a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr return {"ClassRangeRecord": ranges} 273a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 274a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr def toXML2(self, xmlWriter, font): 275ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.classDefs.items()) 276a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr for glyphName, cls in items: 277a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)]) 278a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr xmlWriter.newline() 279a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 2803a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 281a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = getattr(self, "classDefs", None) 282a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr if classDefs is None: 283a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs = {} 284a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr self.classDefs = classDefs 285a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr classDefs[attrs["glyph"]] = int(attrs["class"]) 286a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 287a1dfa2b77a0ac7cf2e7cdca25e024e31de436b1ajvr 288b2486125e9438443a1419706c50958ab0676eb5ajvrclass AlternateSubst(FormatSwitchingBaseTable): 289b2486125e9438443a1419706c50958ab0676eb5ajvr 290b2486125e9438443a1419706c50958ab0676eb5ajvr def postRead(self, rawTable, font): 291b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = {} 292b2486125e9438443a1419706c50958ab0676eb5ajvr if self.Format == 1: 2931fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr input = _getGlyphsFromCoverageTable(rawTable["Coverage"]) 294b2486125e9438443a1419706c50958ab0676eb5ajvr alts = rawTable["AlternateSet"] 295823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if len(input) != len(alts): 296823f8cd15f16bb9dc3991c2672f16dd90579711bjvr assert len(input) == len(alts) 297b2486125e9438443a1419706c50958ab0676eb5ajvr for i in range(len(input)): 298b2486125e9438443a1419706c50958ab0676eb5ajvr alternates[input[i]] = alts[i].Alternate 299b2486125e9438443a1419706c50958ab0676eb5ajvr else: 300b2486125e9438443a1419706c50958ab0676eb5ajvr assert 0, "unknown format: %s" % self.Format 301b2486125e9438443a1419706c50958ab0676eb5ajvr self.alternates = alternates 302b2486125e9438443a1419706c50958ab0676eb5ajvr 303b2486125e9438443a1419706c50958ab0676eb5ajvr def preWrite(self, font): 304b2486125e9438443a1419706c50958ab0676eb5ajvr self.Format = 1 30531ae380735dbf75a460df002a7fc2bcd81e28153jvr alternates = getattr(self, "alternates", None) 30631ae380735dbf75a460df002a7fc2bcd81e28153jvr if alternates is None: 30731ae380735dbf75a460df002a7fc2bcd81e28153jvr alternates = self.alternates = {} 308c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(alternates.items()) 309b2486125e9438443a1419706c50958ab0676eb5ajvr for i in range(len(items)): 310b2486125e9438443a1419706c50958ab0676eb5ajvr glyphName, set = items[i] 311b2486125e9438443a1419706c50958ab0676eb5ajvr items[i] = font.getGlyphID(glyphName), glyphName, set 312b2486125e9438443a1419706c50958ab0676eb5ajvr items.sort() 313b2486125e9438443a1419706c50958ab0676eb5ajvr cov = Coverage() 314823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = [ item[1] for item in items] 315b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = [] 316823f8cd15f16bb9dc3991c2672f16dd90579711bjvr setList = [ item[-1] for item in items] 317823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for set in setList: 318b2486125e9438443a1419706c50958ab0676eb5ajvr alts = AlternateSet() 319b2486125e9438443a1419706c50958ab0676eb5ajvr alts.Alternate = set 320b2486125e9438443a1419706c50958ab0676eb5ajvr alternates.append(alts) 321823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # a special case to deal with the fact that several hundred Adobe Japan1-5 322823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # CJK fonts will overflow an offset if the coverage table isn't pushed to the end. 323823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Also useful in that when splitting a sub-table because of an offset overflow 324823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # I don't need to calculate the change in the subtable offset due to the change in the coverage table size. 325823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Allows packing more rules in subtable. 326823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.sortCoverageLast = 1 327b2486125e9438443a1419706c50958ab0676eb5ajvr return {"Coverage": cov, "AlternateSet": alternates} 328b2486125e9438443a1419706c50958ab0676eb5ajvr 329b2486125e9438443a1419706c50958ab0676eb5ajvr def toXML2(self, xmlWriter, font): 330ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.alternates.items()) 331b2486125e9438443a1419706c50958ab0676eb5ajvr for glyphName, alternates in items: 332b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.begintag("AlternateSet", glyph=glyphName) 333b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 334b2486125e9438443a1419706c50958ab0676eb5ajvr for alt in alternates: 335b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.simpletag("Alternate", glyph=alt) 336b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 337b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.endtag("AlternateSet") 338b2486125e9438443a1419706c50958ab0676eb5ajvr xmlWriter.newline() 339b2486125e9438443a1419706c50958ab0676eb5ajvr 3403a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 341b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = getattr(self, "alternates", None) 342b2486125e9438443a1419706c50958ab0676eb5ajvr if alternates is None: 343b2486125e9438443a1419706c50958ab0676eb5ajvr alternates = {} 344b2486125e9438443a1419706c50958ab0676eb5ajvr self.alternates = alternates 345b2486125e9438443a1419706c50958ab0676eb5ajvr glyphName = attrs["glyph"] 346b2486125e9438443a1419706c50958ab0676eb5ajvr set = [] 347b2486125e9438443a1419706c50958ab0676eb5ajvr alternates[glyphName] = set 348b2486125e9438443a1419706c50958ab0676eb5ajvr for element in content: 349b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 350b2486125e9438443a1419706c50958ab0676eb5ajvr continue 351b2486125e9438443a1419706c50958ab0676eb5ajvr name, attrs, content = element 352b2486125e9438443a1419706c50958ab0676eb5ajvr set.append(attrs["glyph"]) 353b2486125e9438443a1419706c50958ab0676eb5ajvr 354b2486125e9438443a1419706c50958ab0676eb5ajvr 355f2164abef39af7cfef989352fadfca628ed077b2jvrclass LigatureSubst(FormatSwitchingBaseTable): 356f2164abef39af7cfef989352fadfca628ed077b2jvr 357f2164abef39af7cfef989352fadfca628ed077b2jvr def postRead(self, rawTable, font): 358f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = {} 359f2164abef39af7cfef989352fadfca628ed077b2jvr if self.Format == 1: 360823f8cd15f16bb9dc3991c2672f16dd90579711bjvr input = rawTable["Coverage"].glyphs 361f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets = rawTable["LigatureSet"] 362f2164abef39af7cfef989352fadfca628ed077b2jvr assert len(input) == len(ligSets) 363f2164abef39af7cfef989352fadfca628ed077b2jvr for i in range(len(input)): 364f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures[input[i]] = ligSets[i].Ligature 365f2164abef39af7cfef989352fadfca628ed077b2jvr else: 366f2164abef39af7cfef989352fadfca628ed077b2jvr assert 0, "unknown format: %s" % self.Format 367f2164abef39af7cfef989352fadfca628ed077b2jvr self.ligatures = ligatures 368f2164abef39af7cfef989352fadfca628ed077b2jvr 369f2164abef39af7cfef989352fadfca628ed077b2jvr def preWrite(self, font): 37031ae380735dbf75a460df002a7fc2bcd81e28153jvr ligatures = getattr(self, "ligatures", None) 37131ae380735dbf75a460df002a7fc2bcd81e28153jvr if ligatures is None: 37231ae380735dbf75a460df002a7fc2bcd81e28153jvr ligatures = self.ligatures = {} 373c2297cd41d6c00b95f857b65bc9fd4b57559ac5eBehdad Esfahbod items = list(ligatures.items()) 374f2164abef39af7cfef989352fadfca628ed077b2jvr for i in range(len(items)): 375f2164abef39af7cfef989352fadfca628ed077b2jvr glyphName, set = items[i] 376f2164abef39af7cfef989352fadfca628ed077b2jvr items[i] = font.getGlyphID(glyphName), glyphName, set 377f2164abef39af7cfef989352fadfca628ed077b2jvr items.sort() 378f2164abef39af7cfef989352fadfca628ed077b2jvr cov = Coverage() 379823f8cd15f16bb9dc3991c2672f16dd90579711bjvr cov.glyphs = [ item[1] for item in items] 380823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 381f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets = [] 382823f8cd15f16bb9dc3991c2672f16dd90579711bjvr setList = [ item[-1] for item in items ] 383823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for set in setList: 384f2164abef39af7cfef989352fadfca628ed077b2jvr ligSet = LigatureSet() 385f2164abef39af7cfef989352fadfca628ed077b2jvr ligs = ligSet.Ligature = [] 386f2164abef39af7cfef989352fadfca628ed077b2jvr for lig in set: 387f2164abef39af7cfef989352fadfca628ed077b2jvr ligs.append(lig) 388f2164abef39af7cfef989352fadfca628ed077b2jvr ligSets.append(ligSet) 389823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Useful in that when splitting a sub-table because of an offset overflow 390823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # I don't need to calculate the change in subtabl offset due to the coverage table size. 391823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Allows packing more rules in subtable. 392823f8cd15f16bb9dc3991c2672f16dd90579711bjvr self.sortCoverageLast = 1 393f2164abef39af7cfef989352fadfca628ed077b2jvr return {"Coverage": cov, "LigatureSet": ligSets} 394f2164abef39af7cfef989352fadfca628ed077b2jvr 395f2164abef39af7cfef989352fadfca628ed077b2jvr def toXML2(self, xmlWriter, font): 396ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod items = sorted(self.ligatures.items()) 397f2164abef39af7cfef989352fadfca628ed077b2jvr for glyphName, ligSets in items: 398f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.begintag("LigatureSet", glyph=glyphName) 399f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 400f2164abef39af7cfef989352fadfca628ed077b2jvr for lig in ligSets: 401f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.simpletag("Ligature", glyph=lig.LigGlyph, 402f2164abef39af7cfef989352fadfca628ed077b2jvr components=",".join(lig.Component)) 403f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 404f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.endtag("LigatureSet") 405f2164abef39af7cfef989352fadfca628ed077b2jvr xmlWriter.newline() 406f2164abef39af7cfef989352fadfca628ed077b2jvr 4073a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, font): 408f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = getattr(self, "ligatures", None) 409f2164abef39af7cfef989352fadfca628ed077b2jvr if ligatures is None: 410f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures = {} 411f2164abef39af7cfef989352fadfca628ed077b2jvr self.ligatures = ligatures 412f2164abef39af7cfef989352fadfca628ed077b2jvr glyphName = attrs["glyph"] 413f2164abef39af7cfef989352fadfca628ed077b2jvr ligs = [] 414f2164abef39af7cfef989352fadfca628ed077b2jvr ligatures[glyphName] = ligs 415f2164abef39af7cfef989352fadfca628ed077b2jvr for element in content: 416b774f9f684c5a0f91f5fa177c9a461968789123fBehdad Esfahbod if not isinstance(element, tuple): 417f2164abef39af7cfef989352fadfca628ed077b2jvr continue 418f2164abef39af7cfef989352fadfca628ed077b2jvr name, attrs, content = element 419f2164abef39af7cfef989352fadfca628ed077b2jvr lig = Ligature() 420f2164abef39af7cfef989352fadfca628ed077b2jvr lig.LigGlyph = attrs["glyph"] 421f2164abef39af7cfef989352fadfca628ed077b2jvr lig.Component = attrs["components"].split(",") 422f2164abef39af7cfef989352fadfca628ed077b2jvr ligs.append(lig) 423f2164abef39af7cfef989352fadfca628ed077b2jvr 424f2164abef39af7cfef989352fadfca628ed077b2jvr 42564b5c80e80444a124da335e8d4d208bffcf2737bjvr# 42664b5c80e80444a124da335e8d4d208bffcf2737bjvr# For each subtable format there is a class. However, we don't really distinguish 42764b5c80e80444a124da335e8d4d208bffcf2737bjvr# between "field name" and "format name": often these are the same. Yet there's 42864b5c80e80444a124da335e8d4d208bffcf2737bjvr# a whole bunch of fields with different names. The following dict is a mapping 42964b5c80e80444a124da335e8d4d208bffcf2737bjvr# from "format name" to "field name". _buildClasses() uses this to create a 43064b5c80e80444a124da335e8d4d208bffcf2737bjvr# subclass for each alternate field name. 43164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 43264b5c80e80444a124da335e8d4d208bffcf2737bjvr_equivalents = { 43364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MarkArray': ("Mark1Array",), 43464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'LangSys': ('DefaultLangSys',), 43564b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage', 436d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage', 43727f108844ef1aeeb5c694a8304bf6c4944783d42jvr 'LookAheadCoverage'), 43864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef', 43927f108844ef1aeeb5c694a8304bf6c4944783d42jvr 'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'), 44064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor', 44164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Mark2Anchor', 'MarkAnchor'), 44264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice', 44364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'XDeviceTable', 'YDeviceTable', 'DeviceTable'), 44464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Axis': ('HorizAxis', 'VertAxis',), 44564b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MinMax': ('DefaultMinMax',), 44664b5c80e80444a124da335e8d4d208bffcf2737bjvr 'BaseCoord': ('MinCoord', 'MaxCoord',), 44764b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfLangSys': ('DefJstfLangSys',), 44864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB', 44964b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGSUB',), 45064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS', 45164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGPOS',), 45264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',), 45364b5c80e80444a124da335e8d4d208bffcf2737bjvr} 454d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 455e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# 456e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# OverFlow logic, to automatically create ExtensionLookups 457e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# XXX This should probably move to otBase.py 458e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# 459d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 460823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixLookupOverFlows(ttf, overflowRecord): 461823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ Either the offset from the LookupList to a lookup overflowed, or 462823f8cd15f16bb9dc3991c2672f16dd90579711bjvr an offset from a lookup to a subtable overflowed. 463823f8cd15f16bb9dc3991c2672f16dd90579711bjvr The table layout is: 464823f8cd15f16bb9dc3991c2672f16dd90579711bjvr GPSO/GUSB 465823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Script List 466823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Feature List 467823f8cd15f16bb9dc3991c2672f16dd90579711bjvr LookUpList 468823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Lookup[0] and contents 469823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable offset list 470823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[0] and contents 471823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 472823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[n] and contents 473823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 474823f8cd15f16bb9dc3991c2672f16dd90579711bjvr Lookup[n] and contents 475823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable offset list 476823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[0] and contents 477823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ... 478823f8cd15f16bb9dc3991c2672f16dd90579711bjvr SubTable[n] and contents 479823f8cd15f16bb9dc3991c2672f16dd90579711bjvr If the offset to a lookup overflowed (SubTableIndex == None) 480823f8cd15f16bb9dc3991c2672f16dd90579711bjvr we must promote the *previous* lookup to an Extension type. 481823f8cd15f16bb9dc3991c2672f16dd90579711bjvr If the offset from a lookup to subtable overflowed, then we must promote it 482823f8cd15f16bb9dc3991c2672f16dd90579711bjvr to an Extension Lookup type. 483823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 484823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 0 485823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = overflowRecord.LookupListIndex 486823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if (overflowRecord.SubTableIndex == None): 487823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = lookupIndex - 1 488823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if lookupIndex < 0: 489823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 490823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.tableType == 'GSUB': 491823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extType = 7 492823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.tableType == 'GPOS': 493823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extType = 9 494823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 495823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookups = ttf[overflowRecord.tableType].table.LookupList.Lookup 496823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = lookups[lookupIndex] 497823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # If the previous lookup is an extType, look further back. Very unlikely, but possible. 498823f8cd15f16bb9dc3991c2672f16dd90579711bjvr while lookup.LookupType == extType: 499823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookupIndex = lookupIndex -1 500823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if lookupIndex < 0: 501823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 502823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = lookups[lookupIndex] 503823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 504823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for si in range(len(lookup.SubTable)): 505823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subTable = lookup.SubTable[si] 506823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTableClass = lookupTypes[overflowRecord.tableType][extType] 507823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable = extSubTableClass() 508823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable.Format = 1 509823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable.ExtensionLookupType = lookup.LookupType 510823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable.ExtSubTable = subTable 511823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable[si] = extSubTable 512823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.LookupType = extType 513823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 514823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 515823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 516823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitAlternateSubst(oldSubTable, newSubTable, overflowRecord): 517823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 518823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.Format = oldSubTable.Format 519823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(oldSubTable, 'sortCoverageLast'): 520823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast 521823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 522ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod oldAlts = sorted(oldSubTable.alternates.items()) 523823f8cd15f16bb9dc3991c2672f16dd90579711bjvr oldLen = len(oldAlts) 524823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 525823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']: 526823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage table is written last. overflow is to or within the 527823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the coverage table. We will just cut the subtable in half. 52832c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod newLen = oldLen//2 529823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 530823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.itemName == 'AlternateSet': 531823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We just need to back up by two items 532823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # from the overflowed AlternateSet index to make sure the offset 533823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to the Coverage table doesn't overflow. 534823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newLen = overflowRecord.itemIndex - 1 535823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 536823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.alternates = {} 537823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(newLen, oldLen): 538823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = oldAlts[i] 539823f8cd15f16bb9dc3991c2672f16dd90579711bjvr key = item[0] 540823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.alternates[key] = item[1] 541823f8cd15f16bb9dc3991c2672f16dd90579711bjvr del oldSubTable.alternates[key] 542823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 543823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 544823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 545823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 546823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 547823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef splitLigatureSubst(oldSubTable, newSubTable, overflowRecord): 548823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 1 549823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.Format = oldSubTable.Format 550ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod oldLigs = sorted(oldSubTable.ligatures.items()) 551823f8cd15f16bb9dc3991c2672f16dd90579711bjvr oldLen = len(oldLigs) 552823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 553823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if overflowRecord.itemName in [ 'Coverage', 'RangeRecord']: 554823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # Coverage table is written last. overflow is to or within the 555823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # the coverage table. We will just cut the subtable in half. 55632c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod newLen = oldLen//2 557823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 558823f8cd15f16bb9dc3991c2672f16dd90579711bjvr elif overflowRecord.itemName == 'LigatureSet': 559823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We just need to back up by two items 560823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # from the overflowed AlternateSet index to make sure the offset 561823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to the Coverage table doesn't overflow. 562823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newLen = overflowRecord.itemIndex - 1 563823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 564823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.ligatures = {} 565823f8cd15f16bb9dc3991c2672f16dd90579711bjvr for i in range(newLen, oldLen): 566823f8cd15f16bb9dc3991c2672f16dd90579711bjvr item = oldLigs[i] 567823f8cd15f16bb9dc3991c2672f16dd90579711bjvr key = item[0] 568823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable.ligatures[key] = item[1] 569823f8cd15f16bb9dc3991c2672f16dd90579711bjvr del oldSubTable.ligatures[key] 570823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 571823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 572823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 573823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 574823f8cd15f16bb9dc3991c2672f16dd90579711bjvrsplitTable = { 'GSUB': { 575823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 1: splitSingleSubst, 576823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 2: splitMultipleSubst, 577823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 3: splitAlternateSubst, 578823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 4: splitLigatureSubst, 579823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 5: splitContextSubst, 580823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 6: splitChainContextSubst, 581823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 7: splitExtensionSubst, 582823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 8: splitReverseChainSingleSubst, 583823f8cd15f16bb9dc3991c2672f16dd90579711bjvr }, 584823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 'GPOS': { 585823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 1: splitSinglePos, 586823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 2: splitPairPos, 587823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 3: splitCursivePos, 588823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 4: splitMarkBasePos, 589823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 5: splitMarkLigPos, 590823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 6: splitMarkMarkPos, 591823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 7: splitContextPos, 592823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 8: splitChainContextPos, 593823f8cd15f16bb9dc3991c2672f16dd90579711bjvr# 9: splitExtensionPos, 594823f8cd15f16bb9dc3991c2672f16dd90579711bjvr } 595823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 596823f8cd15f16bb9dc3991c2672f16dd90579711bjvr } 597823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 598823f8cd15f16bb9dc3991c2672f16dd90579711bjvrdef fixSubTableOverFlows(ttf, overflowRecord): 599823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 600823f8cd15f16bb9dc3991c2672f16dd90579711bjvr An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts. 601823f8cd15f16bb9dc3991c2672f16dd90579711bjvr """ 602823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = 0 603823f8cd15f16bb9dc3991c2672f16dd90579711bjvr table = ttf[overflowRecord.tableType].table 604823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex] 605823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subIndex = overflowRecord.SubTableIndex 606823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subtable = lookup.SubTable[subIndex] 607823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 608823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(subtable, 'ExtSubTable'): 609823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # We split the subtable of the Extension table, and add a new Extension table 610823f8cd15f16bb9dc3991c2672f16dd90579711bjvr # to contain the new subtable. 611823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 612823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subTableType = subtable.ExtensionLookupType 613823f8cd15f16bb9dc3991c2672f16dd90579711bjvr extSubTable = subtable 614823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subtable = extSubTable.ExtSubTable 615823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTableClass = lookupTypes[overflowRecord.tableType][lookup.LookupType] 616823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable = newExtSubTableClass() 617823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable.Format = extSubTable.Format 618823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable.ExtensionLookupType = extSubTable.ExtensionLookupType 619823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable.insert(subIndex + 1, newExtSubTable) 620823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 621823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType] 622823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable = newSubTableClass() 623823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newExtSubTable.ExtSubTable = newSubTable 624823f8cd15f16bb9dc3991c2672f16dd90579711bjvr else: 625823f8cd15f16bb9dc3991c2672f16dd90579711bjvr subTableType = lookup.LookupType 626823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType] 627823f8cd15f16bb9dc3991c2672f16dd90579711bjvr newSubTable = newSubTableClass() 628823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTable.insert(subIndex + 1, newSubTable) 629823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 630823f8cd15f16bb9dc3991c2672f16dd90579711bjvr if hasattr(lookup, 'SubTableCount'): # may not be defined yet. 631823f8cd15f16bb9dc3991c2672f16dd90579711bjvr lookup.SubTableCount = lookup.SubTableCount + 1 632823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 633823f8cd15f16bb9dc3991c2672f16dd90579711bjvr try: 634823f8cd15f16bb9dc3991c2672f16dd90579711bjvr splitFunc = splitTable[overflowRecord.tableType][subTableType] 635823f8cd15f16bb9dc3991c2672f16dd90579711bjvr except KeyError: 636823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 637823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 638823f8cd15f16bb9dc3991c2672f16dd90579711bjvr ok = splitFunc(subtable, newSubTable, overflowRecord) 639823f8cd15f16bb9dc3991c2672f16dd90579711bjvr return ok 640823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 641e5b245fd074a2efa5fc8902cb7a11a5dcad0f3f5jvr# End of OverFlow logic 642823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 643823f8cd15f16bb9dc3991c2672f16dd90579711bjvr 644d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildClasses(): 645e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod import re 6462b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from .otData import otData 647d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 648d4d151390d1288f8d2df30f6dfa26a309c7334dajvr formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") 649d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace = globals() 650d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 651d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # populate module with classes 652d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 653d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = BaseTable 654d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 655d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 656d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, we only add the "base" table 657d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name = m.group(1) 658d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = FormatSwitchingBaseTable 659bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if name not in namespace: 66064b5c80e80444a124da335e8d4d208bffcf2737bjvr # the class doesn't exist yet, so the base implementation is used. 661e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod cls = type(name, (baseClass,), {}) 662d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace[name] = cls 663d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 66464b5c80e80444a124da335e8d4d208bffcf2737bjvr for base, alts in _equivalents.items(): 665d4d151390d1288f8d2df30f6dfa26a309c7334dajvr base = namespace[base] 666d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for alt in alts: 667e1d97b827791a4d190fbd0ec597f602aefd09025Behdad Esfahbod namespace[alt] = type(alt, (base,), {}) 668d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 669d4d151390d1288f8d2df30f6dfa26a309c7334dajvr global lookupTypes 670d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes = { 671d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GSUB': { 672d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SingleSubst, 673d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: MultipleSubst, 674d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: AlternateSubst, 675d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: LigatureSubst, 676d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: ContextSubst, 677d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: ChainContextSubst, 678d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ExtensionSubst, 6798e1d75b1a3fb4874bb78d9c34a11ba30de81a5ccjvr 8: ReverseChainSingleSubst, 680d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 681d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GPOS': { 682d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SinglePos, 683d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: PairPos, 684d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: CursivePos, 685d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: MarkBasePos, 686d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: MarkLigPos, 687d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: MarkMarkPos, 688d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ContextPos, 689d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8: ChainContextPos, 690d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 9: ExtensionPos, 691d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 692d4d151390d1288f8d2df30f6dfa26a309c7334dajvr } 693d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS 69464b5c80e80444a124da335e8d4d208bffcf2737bjvr for lookupEnum in lookupTypes.values(): 69564b5c80e80444a124da335e8d4d208bffcf2737bjvr for enum, cls in lookupEnum.items(): 69664b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.LookupType = enum 6979e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 6989e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod global featureParamTypes 6999e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes = { 7009e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod 'size': FeatureParamsSize, 7019e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod } 7029e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod for i in range(1, 20+1): 7039e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes['ss%02d' % i] = FeatureParamsStylisticSet 7049e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod for i in range(1, 99+1): 7059e1bd2d0b4416acc7fce623a3bae37064d61d15bBehdad Esfahbod featureParamTypes['cv%02d' % i] = FeatureParamsCharacterVariants 706d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 707d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # add converters to classes 7082b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbod from .otConverters import buildConverters 709d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 710d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 711d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 712d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, add converter to "base" table 713d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, format = m.groups() 714d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = int(format) 715d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 716d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(cls, "converters"): 717d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters = {} 718d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName = {} 71964b5c80e80444a124da335e8d4d208bffcf2737bjvr converters, convertersByName = buildConverters(table[1:], namespace) 720d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters[format] = converters 721d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName[format] = convertersByName 722d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 723d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 72464b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.converters, cls.convertersByName = buildConverters(table, namespace) 725d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 726d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 727d4d151390d1288f8d2df30f6dfa26a309c7334dajvr_buildClasses() 7281fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr 7291fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr 7301fae9b48d3a275c6e1dc0649c40ae121dfc4098djvrdef _getGlyphsFromCoverageTable(coverage): 7311fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr if coverage is None: 7321fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr # empty coverage table 7331fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr return [] 7341fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr else: 7351fae9b48d3a275c6e1dc0649c40ae121dfc4098djvr return coverage.glyphs 736