1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools.misc import sstruct
4from fontTools.misc.textTools import safeEval
5from . import DefaultTable
6import struct
7
8nameRecordFormat = """
9		>	# big endian
10		platformID:	H
11		platEncID:	H
12		langID:		H
13		nameID:		H
14		length:		H
15		offset:		H
16"""
17
18nameRecordSize = sstruct.calcsize(nameRecordFormat)
19
20
21class table__n_a_m_e(DefaultTable.DefaultTable):
22
23	def decompile(self, data, ttFont):
24		format, n, stringOffset = struct.unpack(">HHH", data[:6])
25		expectedStringOffset = 6 + n * nameRecordSize
26		if stringOffset != expectedStringOffset:
27			# XXX we need a warn function
28			print("Warning: 'name' table stringOffset incorrect. Expected: %s; Actual: %s" % (expectedStringOffset, stringOffset))
29		stringData = data[stringOffset:]
30		data = data[6:]
31		self.names = []
32		for i in range(n):
33			if len(data) < 12:
34				# compensate for buggy font
35				break
36			name, data = sstruct.unpack2(nameRecordFormat, data, NameRecord())
37			name.string = stringData[name.offset:name.offset+name.length]
38			assert len(name.string) == name.length
39			#if (name.platEncID, name.platformID) in ((0, 0), (1, 3)):
40			#	if len(name.string) % 2:
41			#		print "2-byte string doesn't have even length!"
42			#		print name.__dict__
43			del name.offset, name.length
44			self.names.append(name)
45
46	def compile(self, ttFont):
47		if not hasattr(self, "names"):
48			# only happens when there are NO name table entries read
49			# from the TTX file
50			self.names = []
51		self.names.sort()  # sort according to the spec; see NameRecord.__lt__()
52		stringData = b""
53		format = 0
54		n = len(self.names)
55		stringOffset = 6 + n * sstruct.calcsize(nameRecordFormat)
56		data = struct.pack(">HHH", format, n, stringOffset)
57		lastoffset = 0
58		done = {}  # remember the data so we can reuse the "pointers"
59		for name in self.names:
60			if name.string in done:
61				name.offset, name.length = done[name.string]
62			else:
63				name.offset, name.length = done[name.string] = len(stringData), len(name.string)
64				stringData = stringData + name.string
65			data = data + sstruct.pack(nameRecordFormat, name)
66		return data + stringData
67
68	def toXML(self, writer, ttFont):
69		for name in self.names:
70			name.toXML(writer, ttFont)
71
72	def fromXML(self, name, attrs, content, ttFont):
73		if name != "namerecord":
74			return # ignore unknown tags
75		if not hasattr(self, "names"):
76			self.names = []
77		name = NameRecord()
78		self.names.append(name)
79		name.fromXML(name, attrs, content, ttFont)
80
81	def getName(self, nameID, platformID, platEncID, langID=None):
82		for namerecord in self.names:
83			if (	namerecord.nameID == nameID and
84					namerecord.platformID == platformID and
85					namerecord.platEncID == platEncID):
86				if langID is None or namerecord.langID == langID:
87					return namerecord
88		return None # not found
89
90
91class NameRecord(object):
92
93	def isUnicode(self):
94		return (self.platformID == 0 or
95			(self.platformID == 3 and self.platEncID in [0, 1, 10]))
96
97	def toXML(self, writer, ttFont):
98		writer.begintag("namerecord", [
99				("nameID", self.nameID),
100				("platformID", self.platformID),
101				("platEncID", self.platEncID),
102				("langID", hex(self.langID)),
103						])
104		writer.newline()
105		if self.isUnicode():
106			if len(self.string) % 2:
107				# no, shouldn't happen, but some of the Apple
108				# tools cause this anyway :-(
109				writer.write16bit(self.string + b"\0", strip=True)
110			else:
111				writer.write16bit(self.string, strip=True)
112		else:
113			writer.write8bit(self.string, strip=True)
114		writer.newline()
115		writer.endtag("namerecord")
116		writer.newline()
117
118	def fromXML(self, name, attrs, content, ttFont):
119		self.nameID = safeEval(attrs["nameID"])
120		self.platformID = safeEval(attrs["platformID"])
121		self.platEncID = safeEval(attrs["platEncID"])
122		self.langID =  safeEval(attrs["langID"])
123		s = strjoin(content).strip()
124		if self.isUnicode():
125			self.string = s.encode("utf_16_be")
126		else:
127			# This is the inverse of write8bit...
128			self.string = s.encode("latin1")
129
130	def __lt__(self, other):
131		if type(self) != type(other):
132			return NotImplemented
133
134		# implemented so that list.sort() sorts according to the spec.
135		selfTuple = (
136			getattr(self, "platformID", None),
137			getattr(self, "platEncID", None),
138			getattr(self, "langID", None),
139			getattr(self, "nameID", None),
140			getattr(self, "string", None),
141		)
142		otherTuple = (
143			getattr(other, "platformID", None),
144			getattr(other, "platEncID", None),
145			getattr(other, "langID", None),
146			getattr(other, "nameID", None),
147			getattr(other, "string", None),
148		)
149		return selfTuple < otherTuple
150
151	def __repr__(self):
152		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
153				self.nameID, self.platformID, self.langID)
154