_n_a_m_e.py revision 9005774c106a796473cae36edc1ca24413139e2f
12b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom . import DefaultTable 28413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodimport struct 38413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodfrom fontTools.misc import sstruct 47842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval 57842e56b97ce677b83bdab09cda48bc2d89ac75aJust 67842e56b97ce677b83bdab09cda48bc2d89ac75aJustnameRecordFormat = """ 77842e56b97ce677b83bdab09cda48bc2d89ac75aJust > # big endian 87842e56b97ce677b83bdab09cda48bc2d89ac75aJust platformID: H 97842e56b97ce677b83bdab09cda48bc2d89ac75aJust platEncID: H 107842e56b97ce677b83bdab09cda48bc2d89ac75aJust langID: H 117842e56b97ce677b83bdab09cda48bc2d89ac75aJust nameID: H 127842e56b97ce677b83bdab09cda48bc2d89ac75aJust length: H 137842e56b97ce677b83bdab09cda48bc2d89ac75aJust offset: H 147842e56b97ce677b83bdab09cda48bc2d89ac75aJust""" 157842e56b97ce677b83bdab09cda48bc2d89ac75aJust 16aabca6d793a14e728260d08a75149343b56ff073jvrnameRecordSize = sstruct.calcsize(nameRecordFormat) 17aabca6d793a14e728260d08a75149343b56ff073jvr 18aabca6d793a14e728260d08a75149343b56ff073jvr 197842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass table__n_a_m_e(DefaultTable.DefaultTable): 207842e56b97ce677b83bdab09cda48bc2d89ac75aJust 217842e56b97ce677b83bdab09cda48bc2d89ac75aJust def decompile(self, data, ttFont): 22b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr format, n, stringOffset = struct.unpack(">HHH", data[:6]) 23aabca6d793a14e728260d08a75149343b56ff073jvr expectedStringOffset = 6 + n * nameRecordSize 24b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr if stringOffset != expectedStringOffset: 25aabca6d793a14e728260d08a75149343b56ff073jvr # XXX we need a warn function 263ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset)) 27b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr stringData = data[stringOffset:] 28a87cde236e17d7a40ca3927edc6d188798917215jvr data = data[6:] 297842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.names = [] 307842e56b97ce677b83bdab09cda48bc2d89ac75aJust for i in range(n): 31980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr if len(data) < 12: 32980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr # compensate for buggy font 33980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr break 347842e56b97ce677b83bdab09cda48bc2d89ac75aJust name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord()) 357842e56b97ce677b83bdab09cda48bc2d89ac75aJust name.string = stringData[name.offset:name.offset+name.length] 367c0fb3173882dfc143b2446e7a8b86f973f969f0Just assert len(name.string) == name.length 377c0fb3173882dfc143b2446e7a8b86f973f969f0Just #if (name.platEncID, name.platformID) in ((0, 0), (1, 3)): 387c0fb3173882dfc143b2446e7a8b86f973f969f0Just # if len(name.string) % 2: 397c0fb3173882dfc143b2446e7a8b86f973f969f0Just # print "2-byte string doesn't have even length!" 407c0fb3173882dfc143b2446e7a8b86f973f969f0Just # print name.__dict__ 417842e56b97ce677b83bdab09cda48bc2d89ac75aJust del name.offset, name.length 427842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.names.append(name) 437842e56b97ce677b83bdab09cda48bc2d89ac75aJust 447842e56b97ce677b83bdab09cda48bc2d89ac75aJust def compile(self, ttFont): 453507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr if not hasattr(self, "names"): 463507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr # only happens when there are NO name table entries read 473507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr # from the TTX file 483507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr self.names = [] 497842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.names.sort() # sort according to the spec; see NameRecord.__cmp__() 507842e56b97ce677b83bdab09cda48bc2d89ac75aJust stringData = "" 517842e56b97ce677b83bdab09cda48bc2d89ac75aJust format = 0 527842e56b97ce677b83bdab09cda48bc2d89ac75aJust n = len(self.names) 53b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat) 54b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr data = struct.pack(">HHH", format, n, stringOffset) 557842e56b97ce677b83bdab09cda48bc2d89ac75aJust lastoffset = 0 567842e56b97ce677b83bdab09cda48bc2d89ac75aJust done = {} # remember the data so we can reuse the "pointers" 577842e56b97ce677b83bdab09cda48bc2d89ac75aJust for name in self.names: 58bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod if name.string in done: 597842e56b97ce677b83bdab09cda48bc2d89ac75aJust name.offset, name.length = done[name.string] 607842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 617842e56b97ce677b83bdab09cda48bc2d89ac75aJust name.offset, name.length = done[name.string] = len(stringData), len(name.string) 627842e56b97ce677b83bdab09cda48bc2d89ac75aJust stringData = stringData + name.string 637842e56b97ce677b83bdab09cda48bc2d89ac75aJust data = data + sstruct.pack(nameRecordFormat, name) 647842e56b97ce677b83bdab09cda48bc2d89ac75aJust return data + stringData 657842e56b97ce677b83bdab09cda48bc2d89ac75aJust 667842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 677842e56b97ce677b83bdab09cda48bc2d89ac75aJust for name in self.names: 687842e56b97ce677b83bdab09cda48bc2d89ac75aJust name.toXML(writer, ttFont) 697842e56b97ce677b83bdab09cda48bc2d89ac75aJust 703a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, ttFont): 71180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod if name != "namerecord": 727842e56b97ce677b83bdab09cda48bc2d89ac75aJust return # ignore unknown tags 737842e56b97ce677b83bdab09cda48bc2d89ac75aJust if not hasattr(self, "names"): 747842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.names = [] 757842e56b97ce677b83bdab09cda48bc2d89ac75aJust name = NameRecord() 767842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.names.append(name) 773a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod name.fromXML(name, attrs, content, ttFont) 787842e56b97ce677b83bdab09cda48bc2d89ac75aJust 79d29f289911059d042cc8d0044e4dafc9a1125677Just def getName(self, nameID, platformID, platEncID, langID=None): 807842e56b97ce677b83bdab09cda48bc2d89ac75aJust for namerecord in self.names: 817842e56b97ce677b83bdab09cda48bc2d89ac75aJust if ( namerecord.nameID == nameID and 827842e56b97ce677b83bdab09cda48bc2d89ac75aJust namerecord.platformID == platformID and 837842e56b97ce677b83bdab09cda48bc2d89ac75aJust namerecord.platEncID == platEncID): 847842e56b97ce677b83bdab09cda48bc2d89ac75aJust if langID is None or namerecord.langID == langID: 857842e56b97ce677b83bdab09cda48bc2d89ac75aJust return namerecord 867842e56b97ce677b83bdab09cda48bc2d89ac75aJust return None # not found 877842e56b97ce677b83bdab09cda48bc2d89ac75aJust 887842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __cmp__(self, other): 89ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod if not isinstance(self, type(other)): return cmp(type(self), type(other)) 900ba7aa7ab5153e6a490425dd0f859cc5947360f4Behdad Esfahbod if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__) 9196b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 927842e56b97ce677b83bdab09cda48bc2d89ac75aJust return cmp(self.names, other.names) 937842e56b97ce677b83bdab09cda48bc2d89ac75aJust 947842e56b97ce677b83bdab09cda48bc2d89ac75aJust 957842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass NameRecord: 967842e56b97ce677b83bdab09cda48bc2d89ac75aJust 977842e56b97ce677b83bdab09cda48bc2d89ac75aJust def toXML(self, writer, ttFont): 987842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.begintag("namerecord", [ 997842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("nameID", self.nameID), 1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platformID", self.platformID), 1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("platEncID", self.platEncID), 1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust ("langID", hex(self.langID)), 1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust ]) 1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 105d29f289911059d042cc8d0044e4dafc9a1125677Just if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)): 1066d925f5b9a6e1b58765ee09fe8552b75f75241c3Just if len(self.string) % 2: 1076d925f5b9a6e1b58765ee09fe8552b75f75241c3Just # no, shouldn't happen, but some of the Apple 1086d925f5b9a6e1b58765ee09fe8552b75f75241c3Just # tools cause this anyway :-( 1096d925f5b9a6e1b58765ee09fe8552b75f75241c3Just writer.write16bit(self.string + "\0") 1106d925f5b9a6e1b58765ee09fe8552b75f75241c3Just else: 1116d925f5b9a6e1b58765ee09fe8552b75f75241c3Just writer.write16bit(self.string) 1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.write8bit(self.string) 1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.endtag("namerecord") 1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust writer.newline() 1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1183a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod def fromXML(self, name, attrs, content, ttFont): 1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.nameID = safeEval(attrs["nameID"]) 1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platformID = safeEval(attrs["platformID"]) 1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.platEncID = safeEval(attrs["platEncID"]) 1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.langID = safeEval(attrs["langID"]) 1239005774c106a796473cae36edc1ca24413139e2fBehdad Esfahbod s = ''.join(content).decode("utf8").strip() 124b0e2817fad08e7867df1e66dc8c78c6f180060c3Just if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)): 125ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr self.string = s.encode("utf_16_be") 1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust else: 1279005774c106a796473cae36edc1ca24413139e2fBehdad Esfahbod self.string = s.encode("latin1") 1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __cmp__(self, other): 1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust """Compare method, so a list of NameRecords can be sorted 1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust according to the spec by just sorting it...""" 13296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 133ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod if not isinstance(self, type(other)): return cmp(type(self), type(other)) 13496b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod 13594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod selftuple = ( 13694118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(self, "platformID", None), 13794118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(self, "platEncID", None), 13894118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(self, "langID", None), 13994118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(self, "nameID", None), 14094118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(self, "string", None), 14194118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod ) 14294118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod othertuple = ( 14394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(other, "platformID", None), 14494118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(other, "platEncID", None), 14594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(other, "langID", None), 14694118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(other, "nameID", None), 14794118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod getattr(other, "string", None), 14894118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod ) 1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust return cmp(selftuple, othertuple) 1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust 1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust def __repr__(self): 1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % ( 1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust self.nameID, self.platformID, self.langID) 154