11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
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
9056da965344d25223d41e202aa7fd463dc6228752Behdad Esfahbod
91e388db566b9ba42669c7e353db4293cf27bc2a5bBehdad Esfahbodclass NameRecord(object):
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust
9377ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod	def isUnicode(self):
9477ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod		return (self.platformID == 0 or
9577ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod			(self.platformID == 3 and self.platEncID in [0, 1, 10]))
9677ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod
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()
10577ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod		if self.isUnicode():
1066d925f5b9a6e1b58765ee09fe8552b75f75241c3Just			if len(self.string) % 2:
1076d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# no, shouldn't happen, but some of the Apple
1086d925f5b9a6e1b58765ee09fe8552b75f75241c3Just				# tools cause this anyway :-(
1091edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod				writer.write16bit(self.string + b"\0", strip=True)
110ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod			else:
1111edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod				writer.write16bit(self.string, strip=True)
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1131edfe576563d4e260a958301eeeb956adc7d0610Behdad Esfahbod			writer.write8bit(self.string, strip=True)
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"])
1235f6418d9e1fa15a89dcec29cdc433ba2c99732c3Behdad Esfahbod		s = strjoin(content).strip()
12477ccf45eb0c59830fb90830d6829108e59e93192Behdad Esfahbod		if self.isUnicode():
125ca80208a1514ea4d7c55114ae1979f0ba6b41d16Behdad Esfahbod			self.string = s.encode("utf_16_be")
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1276962f0cfb2b7edfcfa1ff291e99fec756f06c6f8Behdad Esfahbod			# This is the inverse of write8bit...
1289005774c106a796473cae36edc1ca24413139e2fBehdad Esfahbod			self.string = s.encode("latin1")
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust
130b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod	def __lt__(self, other):
131b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		if type(self) != type(other):
132273a90074ac209d67b5e2cb8ea510cd6c2b10272Behdad Esfahbod			return NotImplemented
13396b321c8aea4dc64110d15a541c6f85152ae19cfBehdad Esfahbod
134b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		# implemented so that list.sort() sorts according to the spec.
135b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad 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		)
142b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad 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		)
149b7fd2e19138b177403689bdd6989cfd2402aa2b3Behdad Esfahbod		return selfTuple < otherTuple
1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __repr__(self):
1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.nameID, self.platformID, self.langID)
154