1from __future__ import print_function, division, absolute_import
2from fontTools.misc.py23 import *
3from fontTools.ttLib import getSearchRange
4from fontTools.misc.textTools import safeEval, readHex
5from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
6from . import DefaultTable
7import struct
8import warnings
9
10
11class table__k_e_r_n(DefaultTable.DefaultTable):
12
13	def getkern(self, format):
14		for subtable in self.kernTables:
15			if subtable.version == format:
16				return subtable
17		return None  # not found
18
19	def decompile(self, data, ttFont):
20		version, nTables = struct.unpack(">HH", data[:4])
21		apple = False
22		if (len(data) >= 8) and (version == 1):
23			# AAT Apple's "new" format. Hm.
24			version, nTables = struct.unpack(">LL", data[:8])
25			self.version = fi2fl(version, 16)
26			data = data[8:]
27			apple = True
28		else:
29			self.version = version
30			data = data[4:]
31		tablesIndex = []
32		self.kernTables = []
33		for i in range(nTables):
34			if self.version == 1.0:
35				# Apple
36				length, coverage, tupleIndex = struct.unpack(">lHH", data[:8])
37				version = coverage & 0xff
38			else:
39				version, length = struct.unpack(">HH", data[:4])
40			length = int(length)
41			if version not in kern_classes:
42				subtable = KernTable_format_unkown(version)
43			else:
44				subtable = kern_classes[version]()
45			subtable.apple = apple
46			subtable.decompile(data[:length], ttFont)
47			self.kernTables.append(subtable)
48			data = data[length:]
49
50	def compile(self, ttFont):
51		if hasattr(self, "kernTables"):
52			nTables = len(self.kernTables)
53		else:
54			nTables = 0
55		if self.version == 1.0:
56			# AAT Apple's "new" format.
57			data = struct.pack(">ll", fl2fi(self.version, 16), nTables)
58		else:
59			data = struct.pack(">HH", self.version, nTables)
60		if hasattr(self, "kernTables"):
61			for subtable in self.kernTables:
62				data = data + subtable.compile(ttFont)
63		return data
64
65	def toXML(self, writer, ttFont):
66		writer.simpletag("version", value=self.version)
67		writer.newline()
68		for subtable in self.kernTables:
69			subtable.toXML(writer, ttFont)
70
71	def fromXML(self, name, attrs, content, ttFont):
72		if name == "version":
73			self.version = safeEval(attrs["value"])
74			return
75		if name != "kernsubtable":
76			return
77		if not hasattr(self, "kernTables"):
78			self.kernTables = []
79		format = safeEval(attrs["format"])
80		if format not in kern_classes:
81			subtable = KernTable_format_unkown(format)
82		else:
83			subtable = kern_classes[format]()
84		self.kernTables.append(subtable)
85		subtable.fromXML(name, attrs, content, ttFont)
86
87
88class KernTable_format_0(object):
89
90	def decompile(self, data, ttFont):
91		version, length, coverage = (0,0,0)
92		if not self.apple:
93			version, length, coverage = struct.unpack(">HHH", data[:6])
94			data = data[6:]
95		else:
96			version, length, coverage = struct.unpack(">LHH", data[:8])
97			data = data[8:]
98		self.version, self.coverage = int(version), int(coverage)
99
100		self.kernTable = kernTable = {}
101
102		nPairs, searchRange, entrySelector, rangeShift = struct.unpack(">HHHH", data[:8])
103		data = data[8:]
104
105		for k in range(nPairs):
106			if len(data) < 6:
107				# buggy kern table
108				data = b""
109				break
110			left, right, value = struct.unpack(">HHh", data[:6])
111			data = data[6:]
112			left, right = int(left), int(right)
113			kernTable[(ttFont.getGlyphName(left), ttFont.getGlyphName(right))] = value
114		if len(data):
115			warnings.warn("excess data in 'kern' subtable: %d bytes" % len(data))
116
117	def compile(self, ttFont):
118		nPairs = len(self.kernTable)
119		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
120		data = struct.pack(">HHHH", nPairs, searchRange, entrySelector, rangeShift)
121
122		# yeehee! (I mean, turn names into indices)
123		getGlyphID = ttFont.getGlyphID
124		kernTable = sorted((getGlyphID(left), getGlyphID(right), value) for ((left,right),value) in self.kernTable.items())
125		for left, right, value in kernTable:
126			data = data + struct.pack(">HHh", left, right, value)
127		return struct.pack(">HHH", self.version, len(data) + 6, self.coverage) + data
128
129	def toXML(self, writer, ttFont):
130		writer.begintag("kernsubtable", coverage=self.coverage, format=0)
131		writer.newline()
132		items = sorted(self.kernTable.items())
133		for (left, right), value in items:
134			writer.simpletag("pair", [
135					("l", left),
136					("r", right),
137					("v", value)
138					])
139			writer.newline()
140		writer.endtag("kernsubtable")
141		writer.newline()
142
143	def fromXML(self, name, attrs, content, ttFont):
144		self.coverage = safeEval(attrs["coverage"])
145		self.version = safeEval(attrs["format"])
146		if not hasattr(self, "kernTable"):
147			self.kernTable = {}
148		for element in content:
149			if not isinstance(element, tuple):
150				continue
151			name, attrs, content = element
152			self.kernTable[(attrs["l"], attrs["r"])] = safeEval(attrs["v"])
153
154	def __getitem__(self, pair):
155		return self.kernTable[pair]
156
157	def __setitem__(self, pair, value):
158		self.kernTable[pair] = value
159
160	def __delitem__(self, pair):
161		del self.kernTable[pair]
162
163
164class KernTable_format_2(object):
165
166	def decompile(self, data, ttFont):
167		self.data = data
168
169	def compile(self, ttFont):
170		return self.data
171
172	def toXML(self, writer):
173		writer.begintag("kernsubtable", format=2)
174		writer.newline()
175		writer.dumphex(self.data)
176		writer.endtag("kernsubtable")
177		writer.newline()
178
179	def fromXML(self, name, attrs, content, ttFont):
180		self.decompile(readHex(content), ttFont)
181
182
183class KernTable_format_unkown(object):
184
185	def __init__(self, format):
186		self.format = format
187
188	def decompile(self, data, ttFont):
189		self.data = data
190
191	def compile(self, ttFont):
192		return self.data
193
194	def toXML(self, writer, ttFont):
195		writer.begintag("kernsubtable", format=self.format)
196		writer.newline()
197		writer.comment("unknown 'kern' subtable format")
198		writer.newline()
199		writer.dumphex(self.data)
200		writer.endtag("kernsubtable")
201		writer.newline()
202
203	def fromXML(self, name, attrs, content, ttFont):
204		self.decompile(readHex(content), ttFont)
205
206
207
208kern_classes = {0: KernTable_format_0, 2: KernTable_format_2}
209