_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