otTables.py revision 8e1d75b1a3fb4874bb78d9c34a11ba30de81a5cc
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""fontTools.ttLib.tables.otTables -- A collection of classes representing the various 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OpenType subtables. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Most are constructed upon import from data in otData.py, all are populated with 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)converter objects from otConverters.py. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from otBase import BaseTable, FormatSwitchingBaseTable 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)from types import TupleType 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class LookupOrder(BaseTable): 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Dummy class; this table isn't defined, but is used, and is always NULL.""" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class FeatureParams(BaseTable): 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Dummy class; this table isn't defined, but is used, and is always NULL.""" 181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci # XXX The above is no longer true; the 'size' feature uses FeatureParams now. 191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Coverage(FormatSwitchingBaseTable): 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # manual implementation to get rid of glyphID dependencies 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def postRead(self, rawTable, font): 26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if self.Format == 1: 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.glyphs = rawTable["GlyphArray"] 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif self.Format == 2: 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs = self.glyphs = [] 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ranges = rawTable["RangeRecord"] 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for r in ranges: 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert r.StartCoverageIndex == len(glyphs), \ 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (r.StartCoverageIndex, len(glyphs)) 344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) start = r.Start 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) end = r.End 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) startID = font.getGlyphID(start) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) endID = font.getGlyphID(end) 38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch glyphs.append(start) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphID in range(startID + 1, endID): 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs.append(font.getGlyphName(glyphID)) 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if start != end: 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs.append(end) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert 0, "unknown format: %s" % self.Format 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 46ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch def preWrite(self, font): 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs = getattr(self, "glyphs") 48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if glyphs is None: 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs = self.glyphs = [] 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format = 1 5168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) rawTable = {"GlyphArray": glyphs} 5268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles) if glyphs: 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # find out whether Format 2 is more compact or not 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphIDs = [] 551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for glyphName in glyphs: 561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphIDs.append(font.getGlyphID(glyphName)) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci last = glyphIDs[0] 591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ranges = [[last]] 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphID in glyphIDs[1:]: 611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if glyphID != last + 1: 62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ranges[-1].append(last) 63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ranges.append([glyphID]) 64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) last = glyphID 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ranges[-1].append(last) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Format 2 is more compact 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) index = 0 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(ranges)): 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) start, end = ranges[i] 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r = RangeRecord() 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.Start = font.getGlyphName(start) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.End = font.getGlyphName(end) 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) r.StartCoverageIndex = index 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci ranges[i] = r 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) index = index + end - start + 1 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format = 2 791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci rawTable = {"RangeRecord": ranges} 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) #else: 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # fallthrough; Format 1 is more compact 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Format = format 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rawTable 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def toXML2(self, xmlWriter, font): 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphName in getattr(self, "glyphs", []): 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.simpletag("Glyph", value=glyphName) 881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci xmlWriter.newline() 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def fromXML(self, (name, attrs, content), font): 911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphs = getattr(self, "glyphs", None) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if glyphs is None: 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs = [] 941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.glyphs = glyphs 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs.append(attrs["value"]) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SingleSubst(FormatSwitchingBaseTable): 997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def postRead(self, rawTable, font): 1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mapping = {} 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) input = rawTable["Coverage"].glyphs 1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if self.Format == 1: 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delta = rawTable["DeltaGlyphID"] 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for inGlyph in input: 1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphID = font.getGlyphID(inGlyph) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mapping[inGlyph] = font.getGlyphName(glyphID + delta) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif self.Format == 2: 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert len(input) == rawTable["GlyphCount"], \ 1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci "invalid SingleSubstFormat2 table" 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) subst = rawTable["Substitute"] 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(input)): 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mapping[input[i]] = subst[i] 1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci else: 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert 0, "unknown format: %s" % self.Format 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.mapping = mapping 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def preWrite(self, font): 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.mapping.items() 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(items)): 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) inGlyph, outGlyph = items[i] 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items[i] = font.getGlyphID(inGlyph), font.getGlyphID(outGlyph), \ 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) inGlyph, outGlyph 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items.sort() 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci format = 2 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) delta = None 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for inID, outID, inGlyph, outGlyph in items: 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if delta is None: 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delta = outID - inID 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if delta != outID - inID: 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break 134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) else: 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format = 1 136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rawTable = {} 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Format = format 139e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch cov = Coverage() 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov.glyphs = input = [] 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci subst = [] 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for inID, outID, inGlyph, outGlyph in items: 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci input.append(inGlyph) 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci subst.append(outGlyph) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rawTable["Coverage"] = cov 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if format == 1: 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert delta is not None 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rawTable["DeltaGlyphID"] = delta 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rawTable["Substitute"] = subst 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return rawTable 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def toXML2(self, xmlWriter, font): 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.mapping.items() 1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) items.sort() 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for inGlyph, outGlyph in items: 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.simpletag("Substitution", 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) [("in", inGlyph), ("out", outGlyph)]) 1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) xmlWriter.newline() 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def fromXML(self, (name, attrs, content), font): 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mapping = getattr(self, "mapping", None) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if mapping is None: 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mapping = {} 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.mapping = mapping 1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) mapping[attrs["in"]] = attrs["out"] 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ClassDef(FormatSwitchingBaseTable): 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def postRead(self, rawTable, font): 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs = {} 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if self.Format == 1: 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) start = rawTable["StartGlyph"] 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphID = font.getGlyphID(start) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for cls in rawTable["ClassValueArray"]: 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs[font.getGlyphName(glyphID)] = cls 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphID = glyphID + 1 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif self.Format == 2: 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) records = rawTable["ClassRangeRecord"] 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for rec in records: 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) start = rec.Start 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) end = rec.End 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cls = rec.Class 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs[start] = cls 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphID in range(font.getGlyphID(start) + 1, 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) font.getGlyphID(end)): 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs[font.getGlyphName(glyphID)] = cls 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs[end] = cls 1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) else: 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert 0, "unknown format: %s" % self.Format 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.classDefs = classDefs 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def preWrite(self, font): 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.classDefs.items() 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(items)): 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphName, cls = items[i] 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items[i] = font.getGlyphID(glyphName), glyphName, cls 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items.sort() 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last, lastName, lastCls = items[0] 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec = ClassRangeRecord() 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec.Start = lastName 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec.Class = lastCls 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ranges = [rec] 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphID, glyphName, cls in items[1:]: 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if glyphID != last + 1 or cls != lastCls: 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec.End = lastName 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec = ClassRangeRecord() 2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) rec.Start = glyphName 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) rec.Class = cls 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ranges.append(rec) 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last = glyphID 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lastName = glyphName 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lastCls = cls 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) rec.End = lastName 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Format = 2 # currently no support for Format 1 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return {"ClassRangeRecord": ranges} 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) def toXML2(self, xmlWriter, font): 2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) items = self.classDefs.items() 2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) items.sort() 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphName, cls in items: 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.simpletag("ClassDef", [("glyph", glyphName), ("class", cls)]) 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.newline() 2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def fromXML(self, (name, attrs, content), font): 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs = getattr(self, "classDefs", None) 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if classDefs is None: 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs = {} 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.classDefs = classDefs 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) classDefs[attrs["glyph"]] = int(attrs["class"]) 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciclass AlternateSubst(FormatSwitchingBaseTable): 2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def postRead(self, rawTable, font): 2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci alternates = {} 2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if self.Format == 1: 2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci input = rawTable["Coverage"].glyphs 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) alts = rawTable["AlternateSet"] 2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci assert len(input) == len(alts) 242a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) for i in range(len(input)): 243a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) alternates[input[i]] = alts[i].Alternate 244a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) else: 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert 0, "unknown format: %s" % self.Format 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.alternates = alternates 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def preWrite(self, font): 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Format = 1 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.alternates.items() 2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) for i in range(len(items)): 252116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch glyphName, set = items[i] 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items[i] = font.getGlyphID(glyphName), glyphName, set 2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci items.sort() 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov = Coverage() 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphs = [] 2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci alternates = [] 2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) cov.glyphs = glyphs 259116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for glyphID, glyphName, set in items: 2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphs.append(glyphName) 261116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch alts = AlternateSet() 262116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch alts.Alternate = set 263116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch alternates.append(alts) 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return {"Coverage": cov, "AlternateSet": alternates} 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci def toXML2(self, xmlWriter, font): 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.alternates.items() 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items.sort() 2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for glyphName, alternates in items: 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.begintag("AlternateSet", glyph=glyphName) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.newline() 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for alt in alternates: 2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci xmlWriter.simpletag("Alternate", glyph=alt) 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.newline() 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.endtag("AlternateSet") 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.newline() 277116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 278116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def fromXML(self, (name, attrs, content), font): 279116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch alternates = getattr(self, "alternates", None) 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if alternates is None: 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) alternates = {} 2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci self.alternates = alternates 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) glyphName = attrs["glyph"] 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set = [] 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) alternates[glyphName] = set 2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for element in content: 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(element) != TupleType: 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) name, attrs, content = element 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set.append(attrs["glyph"]) 2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class LigatureSubst(FormatSwitchingBaseTable): 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def postRead(self, rawTable, font): 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligatures = {} 297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if self.Format == 1: 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) input = rawTable["Coverage"].glyphs 2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ligSets = rawTable["LigatureSet"] 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert len(input) == len(ligSets) 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(input)): 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligatures[input[i]] = ligSets[i].Ligature 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) assert 0, "unknown format: %s" % self.Format 305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self.ligatures = ligatures 306116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 307116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def preWrite(self, font): 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self.Format = 1 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items = self.ligatures.items() 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for i in range(len(items)): 3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) glyphName, set = items[i] 3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) items[i] = font.getGlyphID(glyphName), glyphName, set 3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci items.sort() 3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphs = [] 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) cov = Coverage() 3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci cov.glyphs = glyphs 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligSets = [] 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphID, glyphName, set in items: 3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci glyphs.append(glyphName) 320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ligSet = LigatureSet() 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligs = ligSet.Ligature = [] 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for lig in set: 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligs.append(lig) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligSets.append(ligSet) 325116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return {"Coverage": cov, "LigatureSet": ligSets} 326116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 327116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def toXML2(self, xmlWriter, font): 328116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch items = self.ligatures.items() 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) items.sort() 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for glyphName, ligSets in items: 3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci xmlWriter.begintag("LigatureSet", glyph=glyphName) 3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci xmlWriter.newline() 3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for lig in ligSets: 3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci xmlWriter.simpletag("Ligature", glyph=lig.LigGlyph, 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) components=",".join(lig.Component)) 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xmlWriter.newline() 337116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch xmlWriter.endtag("LigatureSet") 338116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch xmlWriter.newline() 339116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 340116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch def fromXML(self, (name, attrs, content), font): 341116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ligatures = getattr(self, "ligatures", None) 342116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if ligatures is None: 343116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ligatures = {} 344116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self.ligatures = ligatures 345116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch glyphName = attrs["glyph"] 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligs = [] 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ligatures[glyphName] = ligs 348116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch for element in content: 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if type(element) != TupleType: 350116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch continue 351116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch name, attrs, content = element 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lig = Ligature() 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) lig.LigGlyph = attrs["glyph"] 354116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch lig.Component = attrs["components"].split(",") 355116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ligs.append(lig) 356116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 357116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 358116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# 359116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# For each subtable format there is a class. However, we don't really distinguish 360116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# between "field name" and "format name": often these are the same. Yet there's 361116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# a whole bunch of fields with different names. The following dict is a mapping 362116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# from "format name" to "field name". _buildClasses() uses this to create a 363116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# subclass for each alternate field name. 364116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch# 365116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch_equivalents = { 366116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 'MarkArray': ("Mark1Array",), 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'LangSys': ('DefaultLangSys',), 368 'Coverage': ('MarkCoverage', 'BaseCoverage', 'LigatureCoverage', 'Mark1Coverage', 369 'Mark2Coverage', 'BacktrackCoverage', 'InputCoverage', 370 'LookAheadCoverage'), 371 'ClassDef': ('ClassDef1', 'ClassDef2', 'BacktrackClassDef', 'InputClassDef', 372 'LookAheadClassDef', 'GlyphClassDef', 'MarkAttachClassDef'), 373 'Anchor': ('EntryAnchor', 'ExitAnchor', 'BaseAnchor', 'LigatureAnchor', 374 'Mark2Anchor', 'MarkAnchor'), 375 'Device': ('XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice', 376 'XDeviceTable', 'YDeviceTable', 'DeviceTable'), 377 'Axis': ('HorizAxis', 'VertAxis',), 378 'MinMax': ('DefaultMinMax',), 379 'BaseCoord': ('MinCoord', 'MaxCoord',), 380 'JstfLangSys': ('DefJstfLangSys',), 381 'JstfGSUBModList': ('ShrinkageEnableGSUB', 'ShrinkageDisableGSUB', 'ExtensionEnableGSUB', 382 'ExtensionDisableGSUB',), 383 'JstfGPOSModList': ('ShrinkageEnableGPOS', 'ShrinkageDisableGPOS', 'ExtensionEnableGPOS', 384 'ExtensionDisableGPOS',), 385 'JstfMax': ('ShrinkageJstfMax', 'ExtensionJstfMax',), 386} 387 388 389def _buildClasses(): 390 import new, re 391 from otData import otData 392 393 formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$") 394 namespace = globals() 395 396 # populate module with classes 397 for name, table in otData: 398 baseClass = BaseTable 399 m = formatPat.match(name) 400 if m: 401 # XxxFormatN subtable, we only add the "base" table 402 name = m.group(1) 403 baseClass = FormatSwitchingBaseTable 404 if not namespace.has_key(name): 405 # the class doesn't exist yet, so the base implementation is used. 406 cls = new.classobj(name, (baseClass,), {}) 407 namespace[name] = cls 408 409 for base, alts in _equivalents.items(): 410 base = namespace[base] 411 for alt in alts: 412 namespace[alt] = new.classobj(alt, (base,), {}) 413 414 global lookupTypes 415 lookupTypes = { 416 'GSUB': { 417 1: SingleSubst, 418 2: MultipleSubst, 419 3: AlternateSubst, 420 4: LigatureSubst, 421 5: ContextSubst, 422 6: ChainContextSubst, 423 7: ExtensionSubst, 424 8: ReverseChainSingleSubst, 425 }, 426 'GPOS': { 427 1: SinglePos, 428 2: PairPos, 429 3: CursivePos, 430 4: MarkBasePos, 431 5: MarkLigPos, 432 6: MarkMarkPos, 433 7: ContextPos, 434 8: ChainContextPos, 435 9: ExtensionPos, 436 }, 437 } 438 lookupTypes['JSTF'] = lookupTypes['GPOS'] # JSTF contains GPOS 439 for lookupEnum in lookupTypes.values(): 440 for enum, cls in lookupEnum.items(): 441 cls.LookupType = enum 442 443 # add converters to classes 444 from otConverters import buildConverters 445 for name, table in otData: 446 m = formatPat.match(name) 447 if m: 448 # XxxFormatN subtable, add converter to "base" table 449 name, format = m.groups() 450 format = int(format) 451 cls = namespace[name] 452 if not hasattr(cls, "converters"): 453 cls.converters = {} 454 cls.convertersByName = {} 455 converters, convertersByName = buildConverters(table[1:], namespace) 456 cls.converters[format] = converters 457 cls.convertersByName[format] = convertersByName 458 else: 459 cls = namespace[name] 460 cls.converters, cls.convertersByName = buildConverters(table, namespace) 461 462 463_buildClasses() 464