sfnt.py revision 28ae1962292b66ad67117aef2a99d5735a70b779
17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""ttLib/sfnt.py -- low-level module to deal with the sfnt file format.
27842e56b97ce677b83bdab09cda48bc2d89ac75aJust
37842e56b97ce677b83bdab09cda48bc2d89ac75aJustDefines two public classes:
47842e56b97ce677b83bdab09cda48bc2d89ac75aJust	SFNTReader
57842e56b97ce677b83bdab09cda48bc2d89ac75aJust	SFNTWriter
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust
77842e56b97ce677b83bdab09cda48bc2d89ac75aJust(Normally you don't have to use these classes explicitly; they are
87842e56b97ce677b83bdab09cda48bc2d89ac75aJustused automatically by ttLib.TTFont.)
97842e56b97ce677b83bdab09cda48bc2d89ac75aJust
107842e56b97ce677b83bdab09cda48bc2d89ac75aJustThe reading and writing of sfnt files is separated in two distinct
117842e56b97ce677b83bdab09cda48bc2d89ac75aJustclasses, since whenever to number of tables changes or whenever
127842e56b97ce677b83bdab09cda48bc2d89ac75aJusta table's length chages you need to rewrite the whole file anyway.
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust
157842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport struct, sstruct
167842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport Numeric
177842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport os
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1904b3204dd168571635133d430a68175297282b1fjvr
207842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass SFNTReader:
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
22ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	def __init__(self, file, checkChecksums=1):
237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file = file
24ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		self.checkChecksums = checkChecksums
257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = self.file.read(sfntDirectorySize)
267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if len(data) <> sfntDirectorySize:
277842e56b97ce677b83bdab09cda48bc2d89ac75aJust			from fontTools import ttLib
287842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise ttLib.TTLibError, "Not a TrueType or OpenType font (not enough data)"
297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		sstruct.unpack(sfntDirectoryFormat, data, self)
307842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.sfntVersion not in ("\000\001\000\000", "OTTO", "true"):
317842e56b97ce677b83bdab09cda48bc2d89ac75aJust			from fontTools import ttLib
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise ttLib.TTLibError, "Not a TrueType or OpenType font (bad sfntVersion)"
337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables = {}
347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(self.numTables):
357842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry = SFNTDirectoryEntry()
36ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr			entry.fromFile(self.file)
37ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr			if entry.length > 0:
38ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				self.tables[entry.tag] = entry
39ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr			else:
40ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				# Ignore zero-length tables. This doesn't seem to be documented,
41ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				# yet it's apparently how the Windows TT rasterizer behaves.
42ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				# Besides, at least one font has been sighted which actually
43ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				# *has* a zero-length table.
44ce1d50aca8642b52b28deb0d0cb4f3e8594e574ejvr				pass
457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def has_key(self, tag):
477842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self.tables.has_key(tag)
487842e56b97ce677b83bdab09cda48bc2d89ac75aJust
497842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def keys(self):
507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self.tables.keys()
517842e56b97ce677b83bdab09cda48bc2d89ac75aJust
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __getitem__(self, tag):
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Fetch the raw table data."""
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		entry = self.tables[tag]
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(entry.offset)
567842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = self.file.read(entry.length)
57ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		if self.checkChecksums:
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if tag == 'head':
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust				# Beh: we have to special-case the 'head' table.
60ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr				checksum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
617842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
62ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr				checksum = calcChecksum(data)
63ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr			if self.checkChecksums > 1:
647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				# Be obnoxious, and barf when it's wrong
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust				assert checksum == entry.checksum, "bad checksum for '%s' table" % tag
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif checksum <> entry.checkSum:
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust				# Be friendly, and just print a warning.
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust				print "bad checksum for '%s' table" % tag
697842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust
71f70746325687393330f7a765814a0fe78f11f847jvr	def __delitem__(self, tag):
72f70746325687393330f7a765814a0fe78f11f847jvr		del self.tables[tag]
73f70746325687393330f7a765814a0fe78f11f847jvr
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def close(self):
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.close()
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust
787842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass SFNTWriter:
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, file, numTables, sfntVersion="\000\001\000\000"):
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file = file
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.numTables = numTables
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.sfntVersion = sfntVersion
84ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(numTables)
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.nextTableOffset = sfntDirectorySize + numTables * sfntDirectoryEntrySize
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# clear out directory area
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(self.nextTableOffset)
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# make sure we're actually where we want to be. (XXX old cStringIO bug)
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables = {}
917842e56b97ce677b83bdab09cda48bc2d89ac75aJust
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __setitem__(self, tag, data):
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""Write raw table data to disk."""
947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.tables.has_key(tag):
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# We've written this table to file before. If the length
9604b3204dd168571635133d430a68175297282b1fjvr			# of the data is still the same, we allow overwriting it.
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry = self.tables[tag]
987842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if len(data) <> entry.length:
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust				from fontTools import ttLib
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust				raise ttLib.TTLibError, "cannot rewrite '%s' table: length does not match directory entry" % tag
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry = SFNTDirectoryEntry()
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry.tag = tag
1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry.offset = self.nextTableOffset
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust			entry.length = len(data)
1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.nextTableOffset = self.nextTableOffset + ((len(data) + 3) & ~3)
1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(entry.offset)
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write(data)
1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(self.nextTableOffset)
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# make sure we're actually where we want to be. (XXX old cStringIO bug)
1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write('\0' * (self.nextTableOffset - self.file.tell()))
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if tag == 'head':
114ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr			entry.checkSum = calcChecksum(data[:8] + '\0\0\0\0' + data[12:])
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
116ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr			entry.checkSum = calcChecksum(data)
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.tables[tag] = entry
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
11928ae1962292b66ad67117aef2a99d5735a70b779jvr	def close(self):
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""All tables must have been written to disk. Now write the
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		directory.
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"""
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tables = self.tables.items()
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tables.sort()
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if len(tables) <> self.numTables:
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust			from fontTools import ttLib
1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise ttLib.TTLibError, "wrong number of tables; expected %d, found %d" % (self.numTables, len(tables))
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		directory = sstruct.pack(sfntDirectoryFormat, self)
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(sfntDirectorySize)
132f509c0f0707a85184525243dfb6efeba043dc793jvr		seenHead = 0
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for tag, entry in tables:
134f509c0f0707a85184525243dfb6efeba043dc793jvr			if tag == "head":
135f509c0f0707a85184525243dfb6efeba043dc793jvr				seenHead = 1
136ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr			directory = directory + entry.toString()
137f509c0f0707a85184525243dfb6efeba043dc793jvr		if seenHead:
138f509c0f0707a85184525243dfb6efeba043dc793jvr			self.calcMasterChecksum(directory)
1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(0)
1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write(directory)
1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust
142ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	def calcMasterChecksum(self, directory):
1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# calculate checkSumAdjustment
1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust		tags = self.tables.keys()
1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust		checksums = Numeric.zeros(len(tags)+1)
1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(len(tags)):
1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust			checksums[i] = self.tables[tags[i]].checkSum
1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		assert directory_end == len(directory)
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust
152ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr		checksums[-1] = calcChecksum(directory)
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust		checksum = Numeric.add.reduce(checksums)
1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# BiboAfba!
15502e76e905f08c0dcb2fb36bd5566fb86da13a906jvr		checksumadjustment = Numeric.array(0xb1b0afbaL - 0x100000000L,
15602e76e905f08c0dcb2fb36bd5566fb86da13a906jvr				Numeric.Int32) - checksum
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# write the checksum to the file
1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.seek(self.tables['head'].offset + 8)
15958629637261cbc9ee7f536b040ce960882aa907ejvr		self.file.write(struct.pack(">l", checksumadjustment))
1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust# -- sfnt directory helpers and cruft
1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1647842e56b97ce677b83bdab09cda48bc2d89ac75aJustsfntDirectoryFormat = """
1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust		> # big endian
166b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		sfntVersion:    4s
167b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		numTables:      H    # number of tables
168b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		searchRange:    H    # (max2 <= numTables)*16
169b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		entrySelector:  H    # log2(max2 <= numTables)
170b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		rangeShift:     H    # numTables*16-searchRange
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1737842e56b97ce677b83bdab09cda48bc2d89ac75aJustsfntDirectorySize = sstruct.calcsize(sfntDirectoryFormat)
1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1757842e56b97ce677b83bdab09cda48bc2d89ac75aJustsfntDirectoryEntryFormat = """
1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		> # big endian
177b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		tag:            4s
178b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		checkSum:       l
179b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		offset:         l
180b0e5f299ffc5794f1960e694a855a05fc9cea01ejvr		length:         l
1817842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1837842e56b97ce677b83bdab09cda48bc2d89ac75aJustsfntDirectoryEntrySize = sstruct.calcsize(sfntDirectoryEntryFormat)
1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1857842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass SFNTDirectoryEntry:
1867842e56b97ce677b83bdab09cda48bc2d89ac75aJust
187ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	def fromFile(self, file):
1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		sstruct.unpack(sfntDirectoryEntryFormat,
1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust				file.read(sfntDirectoryEntrySize), self)
1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
191ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	def fromString(self, str):
1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		sstruct.unpack(sfntDirectoryEntryFormat, str, self)
1937842e56b97ce677b83bdab09cda48bc2d89ac75aJust
194ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	def toString(self):
1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return sstruct.pack(sfntDirectoryEntryFormat, self)
1967842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1977842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __repr__(self):
1987842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if hasattr(self, "tag"):
1997842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return "<SFNTDirectoryEntry '%s' at %x>" % (self.tag, id(self))
2007842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return "<SFNTDirectoryEntry at %x>" % id(self)
2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust
204ea9dfa9fb28966175bf2275d20aeb62c3040c86djvrdef calcChecksum(data, start=0):
2057842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Calculate the checksum for an arbitrary block of data.
2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust	Optionally takes a 'start' argument, which allows you to
2077842e56b97ce677b83bdab09cda48bc2d89ac75aJust	calculate a checksum in chunks by feeding it a previous
2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust	result.
2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust	If the data length is not a multiple of four, it assumes
2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust	it is to be padded with null byte.
2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust	from fontTools import ttLib
2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust	remainder = len(data) % 4
2157842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if remainder:
2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = data + '\0' * (4-remainder)
2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust	a = Numeric.fromstring(struct.pack(">l", start) + data, Numeric.Int32)
2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if ttLib.endian <> "big":
2197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		a = a.byteswapped()
2207842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return Numeric.add.reduce(a)
2217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust
223ea9dfa9fb28966175bf2275d20aeb62c3040c86djvrdef maxPowerOfTwo(x):
2247842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Return the highest exponent of two, so that
2257842e56b97ce677b83bdab09cda48bc2d89ac75aJust	(2 ** exponent) <= x
2267842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
2277842e56b97ce677b83bdab09cda48bc2d89ac75aJust	exponent = 0
2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust	while x:
2297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		x = x >> 1
2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust		exponent = exponent + 1
231fdea99d2655f4d22542f94a8499e5a3308607776Just	return max(exponent - 1, 0)
2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust
234ea9dfa9fb28966175bf2275d20aeb62c3040c86djvrdef getSearchRange(n):
2357842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""Calculate searchRange, entrySelector, rangeShift for the
2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust	sfnt directory. 'n' is the number of tables.
2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
2387842e56b97ce677b83bdab09cda48bc2d89ac75aJust	# This stuff needs to be stored in the file, because?
2397842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import math
240ea9dfa9fb28966175bf2275d20aeb62c3040c86djvr	exponent = maxPowerOfTwo(n)
2417842e56b97ce677b83bdab09cda48bc2d89ac75aJust	searchRange = (2 ** exponent) * 16
2427842e56b97ce677b83bdab09cda48bc2d89ac75aJust	entrySelector = exponent
2437842e56b97ce677b83bdab09cda48bc2d89ac75aJust	rangeShift = n * 16 - searchRange
2447842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return searchRange, entrySelector, rangeShift
2457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
246