otTables.py revision d41386e7f7b950d2429fbf6d1964ba129d2bc2a6
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""" 7d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8d4d151390d1288f8d2df30f6dfa26a309c7334dajvrfrom otBase import BaseTable, FormatSwitchingBaseTable 9d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 10d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 11d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass LookupOrder(BaseTable): 12d4d151390d1288f8d2df30f6dfa26a309c7334dajvr """Dummy class; this table isn't defined, but is used, and is always NULL.""" 13d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 14d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 15d4d151390d1288f8d2df30f6dfa26a309c7334dajvrclass FeatureParams(BaseTable): 16d4d151390d1288f8d2df30f6dfa26a309c7334dajvr """Dummy class; this table isn't defined, but is used, and is always NULL.""" 17d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # XXX The above is no longer true; the 'size' feature uses FeatureParams now. 18d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 19d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 20d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvrclass Coverage(FormatSwitchingBaseTable): 21d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 22d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # manual implementation to get rid of glyphID dependencies 23d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 24d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def postRead(self, table, font): 25d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if self.Format == 1: 26d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.glyphs = table["GlyphArray"] 27d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr elif self.Format == 2: 28d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = self.glyphs = [] 29d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges = table["RangeRecord"] 30d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for r in ranges: 31d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr assert r.StartCoverageIndex == len(glyphs), \ 32d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr (r.StartCoverageIndex, len(glyphs)) 33d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start = r.Start 34d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr end = r.End 35d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr startID = font.getGlyphID(start) 36d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr endID = font.getGlyphID(end) 37d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(start) 38d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphID in range(startID + 1, endID): 39d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(font.getGlyphName(glyphID)) 40d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if start != end: 41d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(end) 42d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr else: 43d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr assert 0 44d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 45d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def preWrite(self, font): 46d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 1 47d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr table = {"GlyphArray": self.glyphs} 48d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if self.glyphs: 49d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphIDs = [] 50d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphName in self.glyphs: 51d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphIDs.append(font.getGlyphID(glyphName)) 52d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 53d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphIDs[0] 54d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges = [[last]] 55d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphID in glyphIDs[1:]: 56d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if glyphID == last + 1: 57d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr pass 58d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr else: 59d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 60d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges.append([glyphID]) 61d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr last = glyphID 62d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[-1].append(last) 63d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 64d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if len(ranges) * 3 < len(self.glyphs): # 3 words vs. 1 word 65d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # Format 2 is more compact 66d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = 0 67d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for i in range(len(ranges)): 68d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr start, end = ranges[i] 69d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r = RangeRecord() 70d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.Start = font.getGlyphName(start) 71d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.End = font.getGlyphName(end) 72d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr r.StartCoverageIndex = index 73d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr ranges[i] = r 74d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr index = index + end - start + 1 75d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr format = 2 76d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr table = {"RangeRecord": ranges} 77d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr #else: 78d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr # fallthrough; Format 1 is more compact 79d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.Format = format 80d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr return table 81d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 82d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def toXML2(self, xmlWriter, font): 83d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr for glyphName in self.glyphs: 84d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.simpletag("Glyph", value=glyphName) 85d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr xmlWriter.newline() 86d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr 87d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr def fromXML(self, (name, attrs, content), font): 88d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = getattr(self, "glyphs", None) 89d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr if glyphs is None: 90d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs = [] 91d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr self.glyphs = glyphs 92d41386e7f7b950d2429fbf6d1964ba129d2bc2a6jvr glyphs.append(attrs["value"]) 93d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 94d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 9564b5c80e80444a124da335e8d4d208bffcf2737bjvr# 9664b5c80e80444a124da335e8d4d208bffcf2737bjvr# For each subtable format there is a class. However, we don't really distinguish 9764b5c80e80444a124da335e8d4d208bffcf2737bjvr# between "field name" and "format name": often these are the same. Yet there's 9864b5c80e80444a124da335e8d4d208bffcf2737bjvr# a whole bunch of fields with different names. The following dict is a mapping 9964b5c80e80444a124da335e8d4d208bffcf2737bjvr# from "format name" to "field name". _buildClasses() uses this to create a 10064b5c80e80444a124da335e8d4d208bffcf2737bjvr# subclass for each alternate field name. 10164b5c80e80444a124da335e8d4d208bffcf2737bjvr# 10264b5c80e80444a124da335e8d4d208bffcf2737bjvr_equivalents = { 10364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MarkArray': ("Mark1Array",), 10464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'LangSys': ('DefaultLangSys',), 10564b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage', 106d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage', 10764b5c80e80444a124da335e8d4d208bffcf2737bjvr 'LookaheadCoverage'), 10864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef', 10964b5c80e80444a124da335e8d4d208bffcf2737bjvr 'LookaheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'), 11064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor', 11164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Mark2Anchor', 'MarkAnchor'), 11264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice', 11364b5c80e80444a124da335e8d4d208bffcf2737bjvr 'XDeviceTable', 'YDeviceTable', 'DeviceTable'), 11464b5c80e80444a124da335e8d4d208bffcf2737bjvr 'Axis': ('HorizAxis', 'VertAxis',), 11564b5c80e80444a124da335e8d4d208bffcf2737bjvr 'MinMax': ('DefaultMinMax',), 11664b5c80e80444a124da335e8d4d208bffcf2737bjvr 'BaseCoord': ('MinCoord', 'MaxCoord',), 11764b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfLangSys': ('DefJstfLangSys',), 11864b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB', 11964b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGSUB',), 12064b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS', 12164b5c80e80444a124da335e8d4d208bffcf2737bjvr 'ExtensionDisableGPOS',), 12264b5c80e80444a124da335e8d4d208bffcf2737bjvr 'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',), 12364b5c80e80444a124da335e8d4d208bffcf2737bjvr} 124d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 125d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 126d4d151390d1288f8d2df30f6dfa26a309c7334dajvrdef _buildClasses(): 127d4d151390d1288f8d2df30f6dfa26a309c7334dajvr import new, re 128d4d151390d1288f8d2df30f6dfa26a309c7334dajvr from otData import otData 129d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 130d4d151390d1288f8d2df30f6dfa26a309c7334dajvr formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") 131d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace = globals() 132d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 133d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # populate module with classes 134d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 135d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = BaseTable 136d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 137d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 138d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, we only add the "base" table 139d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name = m.group(1) 140d4d151390d1288f8d2df30f6dfa26a309c7334dajvr baseClass = FormatSwitchingBaseTable 141d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not namespace.has_key(name): 14264b5c80e80444a124da335e8d4d208bffcf2737bjvr # the class doesn't exist yet, so the base implementation is used. 143d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = new.classobj(name, (baseClass,), {}) 144d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace[name] = cls 145d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 14664b5c80e80444a124da335e8d4d208bffcf2737bjvr for base, alts in _equivalents.items(): 147d4d151390d1288f8d2df30f6dfa26a309c7334dajvr base = namespace[base] 148d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for alt in alts: 149d4d151390d1288f8d2df30f6dfa26a309c7334dajvr namespace[alt] = new.classobj(alt, (base,), {}) 150d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 151d4d151390d1288f8d2df30f6dfa26a309c7334dajvr global lookupTypes 152d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes = { 153d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GSUB': { 154d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SingleSubst, 155d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: MultipleSubst, 156d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: AlternateSubst, 157d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: LigatureSubst, 158d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: ContextSubst, 159d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: ChainContextSubst, 160d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ExtensionSubst, 161d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 162d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 'GPOS': { 163d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 1: SinglePos, 164d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 2: PairPos, 165d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 3: CursivePos, 166d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 4: MarkBasePos, 167d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 5: MarkLigPos, 168d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 6: MarkMarkPos, 169d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 7: ContextPos, 170d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 8: ChainContextPos, 171d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 9: ExtensionPos, 172d4d151390d1288f8d2df30f6dfa26a309c7334dajvr }, 173d4d151390d1288f8d2df30f6dfa26a309c7334dajvr } 174d4d151390d1288f8d2df30f6dfa26a309c7334dajvr lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS 17564b5c80e80444a124da335e8d4d208bffcf2737bjvr for lookupEnum in lookupTypes.values(): 17664b5c80e80444a124da335e8d4d208bffcf2737bjvr for enum, cls in lookupEnum.items(): 17764b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.LookupType = enum 178d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 179d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # add converters to classes 18064b5c80e80444a124da335e8d4d208bffcf2737bjvr from otConverters import buildConverters 181d4d151390d1288f8d2df30f6dfa26a309c7334dajvr for name, table in otData: 182d4d151390d1288f8d2df30f6dfa26a309c7334dajvr m = formatPat.match(name) 183d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if m: 184d4d151390d1288f8d2df30f6dfa26a309c7334dajvr # XxxFormatN subtable, add converter to "base" table 185d4d151390d1288f8d2df30f6dfa26a309c7334dajvr name, format = m.groups() 186d4d151390d1288f8d2df30f6dfa26a309c7334dajvr format = int(format) 187d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 188d4d151390d1288f8d2df30f6dfa26a309c7334dajvr if not hasattr(cls, "converters"): 189d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters = {} 190d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName = {} 19164b5c80e80444a124da335e8d4d208bffcf2737bjvr converters, convertersByName = buildConverters(table[1:], namespace) 192d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.converters[format] = converters 193d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls.convertersByName[format] = convertersByName 194d4d151390d1288f8d2df30f6dfa26a309c7334dajvr else: 195d4d151390d1288f8d2df30f6dfa26a309c7334dajvr cls = namespace[name] 19664b5c80e80444a124da335e8d4d208bffcf2737bjvr cls.converters, cls.convertersByName = buildConverters(table, namespace) 197d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 198d4d151390d1288f8d2df30f6dfa26a309c7334dajvr 199d4d151390d1288f8d2df30f6dfa26a309c7334dajvr_buildClasses() 200