_n_a_m_e.py revision 180ace6a5ff1399ec53bc696e8bef7cce6eef39a
12b06aaa2a6bcd363c25fb0c43f6bb906906594bdBehdad Esfahbodfrom . import DefaultTable
28413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodimport struct
38413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodfrom fontTools.misc import sstruct
47842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval
57842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string
67842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport types
77842e56b97ce677b83bdab09cda48bc2d89ac75aJust
87842e56b97ce677b83bdab09cda48bc2d89ac75aJustnameRecordFormat = """
97842e56b97ce677b83bdab09cda48bc2d89ac75aJust		>	# big endian
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust		platformID:	H
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust		platEncID:	H
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		langID:		H
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		nameID:		H
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust		length:		H
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		offset:		H
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust
18aabca6d793a14e728260d08a75149343b56ff073jvrnameRecordSize = sstruct.calcsize(nameRecordFormat)
19aabca6d793a14e728260d08a75149343b56ff073jvr
20aabca6d793a14e728260d08a75149343b56ff073jvr
217842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass table__n_a_m_e(DefaultTable.DefaultTable):
227842e56b97ce677b83bdab09cda48bc2d89ac75aJust
237842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def decompile(self, data, ttFont):
24b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		format, n, stringOffset = struct.unpack(">HHH", data[:6])
25aabca6d793a14e728260d08a75149343b56ff073jvr		expectedStringOffset = 6 + n * nameRecordSize
26b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		if stringOffset != expectedStringOffset:
27aabca6d793a14e728260d08a75149343b56ff073jvr			# XXX we need a warn function
28b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr			print "Warning: 'name' table stringOffset incorrect.",
29b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr			print "Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset)
30b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		stringData = data[stringOffset:]
31a87cde236e17d7a40ca3927edc6d188798917215jvr		data = data[6:]
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.names = []
337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(n):
34980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr			if len(data) < 12:
35980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr				# compensate for buggy font
36980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr				break
377842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
387842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name.string = stringData[name.offset:name.offset+name.length]
397c0fb3173882dfc143b2446e7a8b86f973f969f0Just			assert len(name.string) == name.length
407c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
417c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#	if len(name.string) % 2:
427c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#		print "2-byte string doesn't have even length!"
437c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#		print name.__dict__
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust			del name.offset, name.length
457842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.names.append(name)
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust
477842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
483507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr		if not hasattr(self, "names"):
493507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			# only happens when there are NO name table entries read
503507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			# from the TTX file
513507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			self.names = []
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.names.sort()  # sort according to the spec; see NameRecord.__cmp__()
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust		stringData = ""
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		format = 0
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		n = len(self.names)
56b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
57b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		data = struct.pack(">HHH", format, n, stringOffset)
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lastoffset = 0
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		done = {}  # remember the data so we can reuse the "pointers"
607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for name in self.names:
61bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if name.string in done:
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust				name.offset, name.length = done[name.string]
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust				stringData = stringData + name.string
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data = data + sstruct.pack(nameRecordFormat, name)
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data + stringData
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust
697842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for name in self.names:
717842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name.toXML(writer, ttFont)
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust
737842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def fromXML(self, (name, attrs, content), ttFont):
74180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if name != "namerecord":
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return # ignore unknown tags
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not hasattr(self, "names"):
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.names = []
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		name = NameRecord()
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.names.append(name)
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		name.fromXML((name, attrs, content), ttFont)
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust
82d29f289911059d042cc8d0044e4dafc9a1125677Just	def getName(self, nameID, platformID, platEncID, langID=None):
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for namerecord in self.names:
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if (	namerecord.nameID == nameID and
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust					namerecord.platformID == platformID and
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust					namerecord.platEncID == platEncID):
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if langID is None or namerecord.langID == langID:
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust					return namerecord
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return None # not found
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
917842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __cmp__(self, other):
920ba7aa7ab5153e6a490425dd0f859cc5947360f4Behdad Esfahbod		if type(self) != type(other): return cmp(type(self), type(other))
930ba7aa7ab5153e6a490425dd0f859cc5947360f4Behdad Esfahbod		if self.__class__ != other.__class__: return cmp(self.__class__, other.__class__)
9496b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return cmp(self.names, other.names)
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust
987842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass NameRecord:
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.begintag("namerecord", [
1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("nameID", self.nameID),
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platformID", self.platformID),
1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platEncID", self.platEncID),
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("langID", hex(self.langID)),
1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust						])
1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
108d29f289911059d042cc8d0044e4dafc9a1125677Just		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
1096d925f5b9a6e1b58765ee09fe8552b75f75241c3Just			if len(self.string) % 2:
1106d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# no, shouldn't happen, but some of the Apple
1116d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# tools cause this anyway :-(
1126d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				writer.write16bit(self.string + "\0")
1136d925f5b9a6e1b58765ee09fe8552b75f75241c3Just			else:
1146d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				writer.write16bit(self.string)
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust			writer.write8bit(self.string)
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.endtag("namerecord")
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def fromXML(self, (name, attrs, content), ttFont):
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.nameID = safeEval(attrs["nameID"])
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.platformID = safeEval(attrs["platformID"])
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.platEncID = safeEval(attrs["platEncID"])
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.langID =  safeEval(attrs["langID"])
126b0e2817fad08e7867df1e66dc8c78c6f180060c3Just		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
127ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			s = ""
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust			for element in content:
129ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr				s = s + element
130ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			s = unicode(s, "utf8")
131ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			s = s.strip()
132ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.string = s.encode("utf_16_be")
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
134ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			s = string.strip(string.join(content, ""))
135ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.string = unicode(s, "utf8").encode("latin1")
1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __cmp__(self, other):
1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Compare method, so a list of NameRecords can be sorted
1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust		according to the spec by just sorting it..."""
14096b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
1410ba7aa7ab5153e6a490425dd0f859cc5947360f4Behdad Esfahbod		if type(self) != type(other): return cmp(type(self), type(other))
14296b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
14394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		selftuple = (
14494118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platformID", None),
14594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platEncID", None),
14694118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "langID", None),
14794118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "nameID", None),
14894118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "string", None),
14994118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		)
15094118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		othertuple = (
15194118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platformID", None),
15294118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platEncID", None),
15394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "langID", None),
15494118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "nameID", None),
15594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "string", None),
15694118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		)
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return cmp(selftuple, othertuple)
1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __repr__(self):
1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.nameID, self.platformID, self.langID)
162