_n_a_m_e.py revision 821572c9a92d338a7ecbb4261c08ce378eb5434d
132c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbodfrom __future__ import print_function, division
230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
38413c108d21e8cf0e9059bbfffde8d13f2616340Behdad Esfahbodfrom fontTools.misc import sstruct
47842e56b97ce677b83bdab09cda48bc2d89ac75aJustfrom fontTools.misc.textTools import safeEval
530e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom . import DefaultTable
630e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport struct
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
283ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod			print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset))
29b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		stringData = data[stringOffset:]
30a87cde236e17d7a40ca3927edc6d188798917215jvr		data = data[6:]
317842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.names = []
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(n):
33980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr			if len(data) < 12:
34980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr				# compensate for buggy font
35980fae897a18cd1315879b9dcb8ec6ab28fbb5e9jvr				break
367842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
377842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name.string = stringData[name.offset:name.offset+name.length]
387c0fb3173882dfc143b2446e7a8b86f973f969f0Just			assert len(name.string) == name.length
397c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
407c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#	if len(name.string) % 2:
417c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#		print "2-byte string doesn't have even length!"
427c0fb3173882dfc143b2446e7a8b86f973f969f0Just			#		print name.__dict__
437842e56b97ce677b83bdab09cda48bc2d89ac75aJust			del name.offset, name.length
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.names.append(name)
457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def compile(self, ttFont):
473507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr		if not hasattr(self, "names"):
483507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			# only happens when there are NO name table entries read
493507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			# from the TTX file
503507eeb3d8e09172dffc3e9b91ac8b730d8162fdjvr			self.names = []
51b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		self.names.sort()  # sort according to the spec; see NameRecord.__lt__()
52821572c9a92d338a7ecbb4261c08ce378eb5434dBehdad Esfahbod		stringData = b""
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust		format = 0
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		n = len(self.names)
55b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
56b71d0f46f65e5ac935c444ae3a6d6088a5f42e84jvr		data = struct.pack(">HHH", format, n, stringOffset)
577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lastoffset = 0
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		done = {}  # remember the data so we can reuse the "pointers"
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for name in self.names:
60bc5e1cb195c0bfa1c8e7507326d5a9ad05aecb4bBehdad Esfahbod			if name.string in done:
617842e56b97ce677b83bdab09cda48bc2d89ac75aJust				name.offset, name.length = done[name.string]
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				stringData = stringData + name.string
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data = data + sstruct.pack(nameRecordFormat, name)
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data + stringData
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
697842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for name in self.names:
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust			name.toXML(writer, ttFont)
717842e56b97ce677b83bdab09cda48bc2d89ac75aJust
723a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
73180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod		if name != "namerecord":
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return # ignore unknown tags
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not hasattr(self, "names"):
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.names = []
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		name = NameRecord()
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.names.append(name)
793a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod		name.fromXML(name, attrs, content, ttFont)
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust
81d29f289911059d042cc8d0044e4dafc9a1125677Just	def getName(self, nameID, platformID, platEncID, langID=None):
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for namerecord in self.names:
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if (	namerecord.nameID == nameID and
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust					namerecord.platformID == platformID and
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust					namerecord.platEncID == platEncID):
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust				if langID is None or namerecord.langID == langID:
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust					return namerecord
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return None # not found
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
917842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass NameRecord:
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def toXML(self, writer, ttFont):
947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.begintag("namerecord", [
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("nameID", self.nameID),
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platformID", self.platformID),
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("platEncID", self.platEncID),
987842e56b97ce677b83bdab09cda48bc2d89ac75aJust				("langID", hex(self.langID)),
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust						])
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
101d29f289911059d042cc8d0044e4dafc9a1125677Just		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
1026d925f5b9a6e1b58765ee09fe8552b75f75241c3Just			if len(self.string) % 2:
1036d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# no, shouldn't happen, but some of the Apple
1046d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# tools cause this anyway :-(
1056d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				writer.write16bit(self.string + "\0")
1066d925f5b9a6e1b58765ee09fe8552b75f75241c3Just			else:
1076d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				writer.write16bit(self.string)
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust			writer.write8bit(self.string)
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.endtag("namerecord")
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writer.newline()
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1143a9fd301808f5a8991ca9ac44028d1ecb22d307fBehdad Esfahbod	def fromXML(self, name, attrs, content, ttFont):
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.nameID = safeEval(attrs["nameID"])
1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.platformID = safeEval(attrs["platformID"])
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.platEncID = safeEval(attrs["platEncID"])
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.langID =  safeEval(attrs["langID"])
1199005774c106a796473cae36edc1ca24413139e2fBehdad Esfahbod		s = ''.join(content).decode("utf8").strip()
120b0e2817fad08e7867df1e66dc8c78c6f180060c3Just		if self.platformID == 0 or (self.platformID == 3 and self.platEncID in (0, 1)):
121ca4c45681ef2ea9290c6845f8bf61dff281fc5c4jvr			self.string = s.encode("utf_16_be")
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1239005774c106a796473cae36edc1ca24413139e2fBehdad Esfahbod			self.string = s.encode("latin1")
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
125b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __lt__(self, other):
126b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
127b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod			raise TypeError("unordered types %s() < %s()", type(self), type(other))
12896b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
129b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		# implemented so that list.sort() sorts according to the spec.
130b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		selfTuple = (
13194118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platformID", None),
13294118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "platEncID", None),
13394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "langID", None),
13494118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "nameID", None),
13594118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(self, "string", None),
13694118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		)
137b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		otherTuple = (
13894118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platformID", None),
13994118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "platEncID", None),
14094118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "langID", None),
14194118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "nameID", None),
14294118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod			getattr(other, "string", None),
14394118dcea43bbc618b35774d9c9913738fa5e97aBehdad Esfahbod		)
144b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return selfTuple < otherTuple
1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __repr__(self):
1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.nameID, self.platformID, self.langID)
149