Home | History | Annotate | Download | only in tables
      1 from __future__ import print_function, division, absolute_import
      2 from fontTools.misc.py23 import *
      3 from fontTools.ttLib import getSearchRange
      4 from fontTools.misc.textTools import safeEval, readHex
      5 from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
      6 from . import DefaultTable
      7 import struct
      8 import warnings
      9 
     10 
     11 class 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 
     88 class 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 
    164 class 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 
    183 class 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 
    208 kern_classes = {0: KernTable_format_0, 2: KernTable_format_2}
    209