t1Lib.py revision 30e691edd056ba22fa8970280e986747817bec3d
17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""fontTools.t1Lib.py -- Tools for PostScript Type 1 fonts
27842e56b97ce677b83bdab09cda48bc2d89ac75aJust
37842e56b97ce677b83bdab09cda48bc2d89ac75aJustFunctions for reading and writing raw Type 1 data:
47842e56b97ce677b83bdab09cda48bc2d89ac75aJust
57842e56b97ce677b83bdab09cda48bc2d89ac75aJustread(path)
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust	reads any Type 1 font file, returns the raw data and a type indicator:
77842e56b97ce677b83bdab09cda48bc2d89ac75aJust	'LWFN', 'PFB' or 'OTHER', depending on the format of the file pointed
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust	to by 'path'.
97842e56b97ce677b83bdab09cda48bc2d89ac75aJust	Raises an error when the file does not contain valid Type 1 data.
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
115810aa9967488207b039cb2d300fa53c91d4df2fJustwrite(path, data, kind='OTHER', dohex=0)
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust	writes raw Type 1 data to the file pointed to by 'path'.
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust	'kind' can be one of 'LWFN', 'PFB' or 'OTHER'; it defaults to 'OTHER'.
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust	'dohex' is a flag which determines whether the eexec encrypted
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust	part should be written as hexadecimal or binary, but only if kind
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust	is 'LWFN' or 'PFB'.
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust__author__ = "jvr"
207842e56b97ce677b83bdab09cda48bc2d89ac75aJust__version__ = "1.0b2"
217842e56b97ce677b83bdab09cda48bc2d89ac75aJustDEBUG = 0
227842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2330e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom __future__ import print_function
2430e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import *
25c2be3d982b04c4bbb1c11d9ab8452f78415d6522Justfrom fontTools.misc import eexec
2645d1f3b3b552297484bc2b8e9a2e999630bb5e50jvrfrom fontTools.misc.macCreatorType import getMacCreatorAndType
277842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport os
2830e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodimport re
297842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3022433b1f52c34a04cf13e21103ee978f7f5b501cjvr
3122433b1f52c34a04cf13e21103ee978f7f5b501cjvrtry:
3225ccb9c3460b05522eae9283a4c9ea783da87aa4jvr	try:
3325ccb9c3460b05522eae9283a4c9ea783da87aa4jvr		from Carbon import Res
3425ccb9c3460b05522eae9283a4c9ea783da87aa4jvr	except ImportError:
3525ccb9c3460b05522eae9283a4c9ea783da87aa4jvr		import Res  # MacPython < 2.2
36e568dc77d3baccc303d5115e2686f126c96c8a26jvrexcept ImportError:
37e568dc77d3baccc303d5115e2686f126c96c8a26jvr	haveMacSupport = 0
38e568dc77d3baccc303d5115e2686f126c96c8a26jvrelse:
39e568dc77d3baccc303d5115e2686f126c96c8a26jvr	haveMacSupport = 1
40b19141e48930ddd3106a6fe303fa2e6b76d35f47jvr	import MacOS
4159afba7684d4c2f7760be5dbc1aeffceb8ced46fjvr
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust
43e568dc77d3baccc303d5115e2686f126c96c8a26jvrclass T1Error(Exception): pass
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
457842e56b97ce677b83bdab09cda48bc2d89ac75aJust
467842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass T1Font:
477842e56b97ce677b83bdab09cda48bc2d89ac75aJust
483618300613e796ff81abddde967b1092ac1dc915Just	"""Type 1 font class.
493618300613e796ff81abddde967b1092ac1dc915Just
503618300613e796ff81abddde967b1092ac1dc915Just	Uses a minimal interpeter that supports just about enough PS to parse
513618300613e796ff81abddde967b1092ac1dc915Just	Type 1 fonts.
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, path=None):
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if path is not None:
567842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.data, type = read(path)
577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust			pass # XXX
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust
607842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def saveAs(self, path, type):
618c74f4639a4494d9d84d1220cbe895de4ffb8aacjvr		write(path, self.getData(), type)
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def getData(self):
643618300613e796ff81abddde967b1092ac1dc915Just		# XXX Todo: if the data has been converted to Python object,
653618300613e796ff81abddde967b1092ac1dc915Just		# recreate the PS stream
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self.data
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
687d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr	def getGlyphSet(self):
697d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		"""Return a generic GlyphSet, which is a dict-like object
707d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		mapping glyph names to glyph objects. The returned glyph objects
717d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		have a .draw() method that supports the Pen protocol, and will
727d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		have an attribute named 'width', but only *after* the .draw() method
737d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		has been called.
747d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr
757d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		In the case of Type 1, the GlyphSet is simply the CharStrings dict.
767d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		"""
777d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr		return self["CharStrings"]
787d4b693627c128bea3ad0c6dd49e923fe5ca4947jvr
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __getitem__(self, key):
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if not hasattr(self, "font"):
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.parse()
823618300613e796ff81abddde967b1092ac1dc915Just		return self.font[key]
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def parse(self):
85528614e6e254dfe3c501ff440c291c6c55de5e6fJust		from fontTools.misc import psLib
86528614e6e254dfe3c501ff440c291c6c55de5e6fJust		from fontTools.misc import psCharStrings
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.font = psLib.suckfont(self.data)
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		charStrings = self.font["CharStrings"]
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lenIV = self.font["Private"].get("lenIV", 4)
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		assert lenIV >= 0
91489d76a340845361def6af9ab7d9152f8e66f417jvr		subrs = self.font["Private"]["Subrs"]
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for glyphName, charString in charStrings.items():
93c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just			charString, R = eexec.decrypt(charString, 4330)
94489d76a340845361def6af9ab7d9152f8e66f417jvr			charStrings[glyphName] = psCharStrings.T1CharString(charString[lenIV:],
95489d76a340845361def6af9ab7d9152f8e66f417jvr					subrs=subrs)
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(len(subrs)):
97c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just			charString, R = eexec.decrypt(subrs[i], 4330)
98489d76a340845361def6af9ab7d9152f8e66f417jvr			subrs[i] = psCharStrings.T1CharString(charString[lenIV:], subrs=subrs)
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust		del self.data
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1023618300613e796ff81abddde967b1092ac1dc915Just# low level T1 data read and write functions
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust
10410fd22a9890ce20091a20e70660677db779d4b64jvrdef read(path, onlyHeader=0):
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""reads any Type 1 font file, returns raw data"""
10614fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	normpath = path.lower()
10745d1f3b3b552297484bc2b8e9a2e999630bb5e50jvr	creator, type = getMacCreatorAndType(path)
10845d1f3b3b552297484bc2b8e9a2e999630bb5e50jvr	if type == 'LWFN':
10945d1f3b3b552297484bc2b8e9a2e999630bb5e50jvr		return readLWFN(path, onlyHeader), 'LWFN'
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if normpath[-4:] == '.pfb':
11110fd22a9890ce20091a20e70660677db779d4b64jvr		return readPFB(path, onlyHeader), 'PFB'
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
1135810aa9967488207b039cb2d300fa53c91d4df2fJust		return readOther(path), 'OTHER'
1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef write(path, data, kind='OTHER', dohex=0):
1165810aa9967488207b039cb2d300fa53c91d4df2fJust	assertType1(data)
11714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	kind = kind.upper()
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		os.remove(path)
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust	except os.error:
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pass
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust	err = 1
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if kind == 'LWFN':
1255810aa9967488207b039cb2d300fa53c91d4df2fJust			writeLWFN(path, data)
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif kind == 'PFB':
1275810aa9967488207b039cb2d300fa53c91d4df2fJust			writePFB(path, data)
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1295810aa9967488207b039cb2d300fa53c91d4df2fJust			writeOther(path, data, dohex)
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust		err = 0
1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust	finally:
1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if err and not DEBUG:
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust			try:
1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust				os.remove(path)
1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust			except os.error:
1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust				pass
1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust# -- internal --
1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1417842e56b97ce677b83bdab09cda48bc2d89ac75aJustLWFNCHUNKSIZE = 2000
1427842e56b97ce677b83bdab09cda48bc2d89ac75aJustHEXLINELENGTH = 80
1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
145da0d805d2603dd9b83c695006521f887b68d5505jvrdef readLWFN(path, onlyHeader=0):
1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""reads an LWFN font file, returns raw data"""
14791bca4244286fb519c93fe92329da96b0e6f32eejvr	resRef = Res.FSOpenResFile(path, 1)  # read-only
1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
149e9601bf9e1253f77b8a66f27685fae453ce98b14Just		Res.UseResFile(resRef)
1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		n = Res.Count1Resources('POST')
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = []
1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(501, 501 + n):
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust			res = Res.Get1Resource('POST', i)
1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust			code = ord(res.data[0])
155180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if ord(res.data[1]) != 0:
156cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod				raise T1Error('corrupt LWFN file')
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if code in [1, 2]:
158da0d805d2603dd9b83c695006521f887b68d5505jvr				if onlyHeader and code == 2:
159da0d805d2603dd9b83c695006521f887b68d5505jvr					break
16005a16f2310e26193557a3dc223ac0efeb166789fjvr				data.append(res.data[2:])
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif code in [3, 5]:
1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust				break
1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif code == 4:
1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				f = open(path, "rb")
1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust				data.append(f.read())
1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust				f.close()
1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif code == 0:
1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust				pass # comment, ignore
1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
170dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod				raise T1Error('bad chunk code: ' + repr(code))
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust	finally:
172e9601bf9e1253f77b8a66f27685fae453ce98b14Just		Res.CloseResFile(resRef)
17314fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	data = ''.join(data)
1745810aa9967488207b039cb2d300fa53c91d4df2fJust	assertType1(data)
1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return data
1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1775810aa9967488207b039cb2d300fa53c91d4df2fJustdef readPFB(path, onlyHeader=0):
1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""reads a PFB font file, returns raw data"""
1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f = open(path, "rb")
1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = []
181ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod	while True:
182b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		if f.read(1) != bytechr(128):
183cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise T1Error('corrupt PFB file')
1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust		code = ord(f.read(1))
1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if code in [1, 2]:
1865810aa9967488207b039cb2d300fa53c91d4df2fJust			chunklen = stringToLong(f.read(4))
1873618300613e796ff81abddde967b1092ac1dc915Just			chunk = f.read(chunklen)
1883618300613e796ff81abddde967b1092ac1dc915Just			assert len(chunk) == chunklen
1893618300613e796ff81abddde967b1092ac1dc915Just			data.append(chunk)
1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif code == 3:
1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust			break
1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
193dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod			raise T1Error('bad chunk code: ' + repr(code))
1945810aa9967488207b039cb2d300fa53c91d4df2fJust		if onlyHeader:
1955810aa9967488207b039cb2d300fa53c91d4df2fJust			break
1967842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f.close()
19714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	data = ''.join(data)
1985810aa9967488207b039cb2d300fa53c91d4df2fJust	assertType1(data)
1997842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return data
2007842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2015810aa9967488207b039cb2d300fa53c91d4df2fJustdef readOther(path):
2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust	"""reads any (font) file, returns raw data"""
2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f = open(path, "rb")
2047842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = f.read()
2057842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f.close()
2065810aa9967488207b039cb2d300fa53c91d4df2fJust	assertType1(data)
2077842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2085810aa9967488207b039cb2d300fa53c91d4df2fJust	chunks = findEncryptedChunks(data)
2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = []
2105810aa9967488207b039cb2d300fa53c91d4df2fJust	for isEncrypted, chunk in chunks:
2115810aa9967488207b039cb2d300fa53c91d4df2fJust		if isEncrypted and isHex(chunk[:4]):
2125810aa9967488207b039cb2d300fa53c91d4df2fJust			data.append(deHexString(chunk))
2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data.append(chunk)
21514fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	return ''.join(data)
2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust# file writing tools
2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2195810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeLWFN(path, data):
220e9601bf9e1253f77b8a66f27685fae453ce98b14Just	Res.FSpCreateResFile(path, "just", "LWFN", 0)
22191bca4244286fb519c93fe92329da96b0e6f32eejvr	resRef = Res.FSOpenResFile(path, 2)  # write-only
2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
223e9601bf9e1253f77b8a66f27685fae453ce98b14Just		Res.UseResFile(resRef)
2247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		resID = 501
2255810aa9967488207b039cb2d300fa53c91d4df2fJust		chunks = findEncryptedChunks(data)
2265810aa9967488207b039cb2d300fa53c91d4df2fJust		for isEncrypted, chunk in chunks:
2275810aa9967488207b039cb2d300fa53c91d4df2fJust			if isEncrypted:
2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 2
2297842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 1
2317842e56b97ce677b83bdab09cda48bc2d89ac75aJust			while chunk:
232b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod				res = Res.Resource(bytechr(code) + '\0' + chunk[:LWFNCHUNKSIZE - 2])
2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust				res.AddResource('POST', resID, '')
2347842e56b97ce677b83bdab09cda48bc2d89ac75aJust				chunk = chunk[LWFNCHUNKSIZE - 2:]
2357842e56b97ce677b83bdab09cda48bc2d89ac75aJust				resID = resID + 1
236b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		res = Res.Resource(bytechr(5) + '\0')
2377842e56b97ce677b83bdab09cda48bc2d89ac75aJust		res.AddResource('POST', resID, '')
2387842e56b97ce677b83bdab09cda48bc2d89ac75aJust	finally:
239e9601bf9e1253f77b8a66f27685fae453ce98b14Just		Res.CloseResFile(resRef)
2407842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2415810aa9967488207b039cb2d300fa53c91d4df2fJustdef writePFB(path, data):
2425810aa9967488207b039cb2d300fa53c91d4df2fJust	chunks = findEncryptedChunks(data)
2433618300613e796ff81abddde967b1092ac1dc915Just	f = open(path, "wb")
2447842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
2455810aa9967488207b039cb2d300fa53c91d4df2fJust		for isEncrypted, chunk in chunks:
2465810aa9967488207b039cb2d300fa53c91d4df2fJust			if isEncrypted:
2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 2
2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2497842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 1
250b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod			f.write(bytechr(128) + bytechr(code))
2515810aa9967488207b039cb2d300fa53c91d4df2fJust			f.write(longToString(len(chunk)))
2527842e56b97ce677b83bdab09cda48bc2d89ac75aJust			f.write(chunk)
253b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		f.write(bytechr(128) + bytechr(3))
2547842e56b97ce677b83bdab09cda48bc2d89ac75aJust	finally:
2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		f.close()
2567842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2575810aa9967488207b039cb2d300fa53c91d4df2fJustdef writeOther(path, data, dohex = 0):
2585810aa9967488207b039cb2d300fa53c91d4df2fJust	chunks = findEncryptedChunks(data)
2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f = open(path, "wb")
2607842e56b97ce677b83bdab09cda48bc2d89ac75aJust	try:
2617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		hexlinelen = HEXLINELENGTH / 2
2625810aa9967488207b039cb2d300fa53c91d4df2fJust		for isEncrypted, chunk in chunks:
2635810aa9967488207b039cb2d300fa53c91d4df2fJust			if isEncrypted:
2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 2
2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2667842e56b97ce677b83bdab09cda48bc2d89ac75aJust				code = 1
2677842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if code == 2 and dohex:
2687842e56b97ce677b83bdab09cda48bc2d89ac75aJust				while chunk:
269c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just					f.write(eexec.hexString(chunk[:hexlinelen]))
2707842e56b97ce677b83bdab09cda48bc2d89ac75aJust					f.write('\r')
2717842e56b97ce677b83bdab09cda48bc2d89ac75aJust					chunk = chunk[hexlinelen:]
2727842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2737842e56b97ce677b83bdab09cda48bc2d89ac75aJust				f.write(chunk)
2747842e56b97ce677b83bdab09cda48bc2d89ac75aJust	finally:
2757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		f.close()
2767842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2777842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2787842e56b97ce677b83bdab09cda48bc2d89ac75aJust# decryption tools
2797842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2807842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGIN = "currentfile eexec"
2817842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECEND = '0' * 64
2827842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECINTERNALEND = "currentfile closefile"
2837842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECBEGINMARKER = "%-- eexec start\r"
2847842e56b97ce677b83bdab09cda48bc2d89ac75aJustEEXECENDMARKER = "%-- eexec end\r"
2857842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2867842e56b97ce677b83bdab09cda48bc2d89ac75aJust_ishexRE = re.compile('[0-9A-Fa-f]*$')
2877842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2885810aa9967488207b039cb2d300fa53c91d4df2fJustdef isHex(text):
2897842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return _ishexRE.match(text) is not None
2907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2917842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2925810aa9967488207b039cb2d300fa53c91d4df2fJustdef decryptType1(data):
2935810aa9967488207b039cb2d300fa53c91d4df2fJust	chunks = findEncryptedChunks(data)
2947842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = []
2955810aa9967488207b039cb2d300fa53c91d4df2fJust	for isEncrypted, chunk in chunks:
2965810aa9967488207b039cb2d300fa53c91d4df2fJust		if isEncrypted:
2975810aa9967488207b039cb2d300fa53c91d4df2fJust			if isHex(chunk[:4]):
2985810aa9967488207b039cb2d300fa53c91d4df2fJust				chunk = deHexString(chunk)
299c2be3d982b04c4bbb1c11d9ab8452f78415d6522Just			decrypted, R = eexec.decrypt(chunk, 55665)
3007842e56b97ce677b83bdab09cda48bc2d89ac75aJust			decrypted = decrypted[4:]
301180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod			if decrypted[-len(EEXECINTERNALEND)-1:-1] != EEXECINTERNALEND \
302180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod					and decrypted[-len(EEXECINTERNALEND)-2:-2] != EEXECINTERNALEND:
303cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod				raise T1Error("invalid end of eexec part")
3047842e56b97ce677b83bdab09cda48bc2d89ac75aJust			decrypted = decrypted[:-len(EEXECINTERNALEND)-2] + '\r'
3057842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data.append(EEXECBEGINMARKER + decrypted + EEXECENDMARKER)
3067842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
3077842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if chunk[-len(EEXECBEGIN)-1:-1] == EEXECBEGIN:
3087842e56b97ce677b83bdab09cda48bc2d89ac75aJust				data.append(chunk[:-len(EEXECBEGIN)-1])
3097842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
3107842e56b97ce677b83bdab09cda48bc2d89ac75aJust				data.append(chunk)
31114fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	return ''.join(data)
3127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3135810aa9967488207b039cb2d300fa53c91d4df2fJustdef findEncryptedChunks(data):
3147842e56b97ce677b83bdab09cda48bc2d89ac75aJust	chunks = []
315ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod	while True:
31614fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod		eBegin = data.find(EEXECBEGIN)
3175810aa9967488207b039cb2d300fa53c91d4df2fJust		if eBegin < 0:
3187842e56b97ce677b83bdab09cda48bc2d89ac75aJust			break
31990290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr		eBegin = eBegin + len(EEXECBEGIN) + 1
32014fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod		eEnd = data.find(EEXECEND, eBegin)
3215810aa9967488207b039cb2d300fa53c91d4df2fJust		if eEnd < 0:
322cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise T1Error("can't find end of eexec part")
323db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr		cypherText = data[eBegin:eEnd + 2]
324e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr		if isHex(cypherText[:4]):
325e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr			cypherText = deHexString(cypherText)
326db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr		plainText, R = eexec.decrypt(cypherText, 55665)
32714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod		eEndLocal = plainText.find(EEXECINTERNALEND)
328db1f2800e1fe8eedb102c997e9f133ed74b3af13jvr		if eEndLocal < 0:
329cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise T1Error("can't find end of eexec part")
33090290b7bd9d8d1e31d98637126aafc7dd8d0dfacjvr		chunks.append((0, data[:eBegin]))
331e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr		chunks.append((1, cypherText[:eEndLocal + len(EEXECINTERNALEND) + 1]))
3325810aa9967488207b039cb2d300fa53c91d4df2fJust		data = data[eEnd:]
3337842e56b97ce677b83bdab09cda48bc2d89ac75aJust	chunks.append((0, data))
3347842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return chunks
3357842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3365810aa9967488207b039cb2d300fa53c91d4df2fJustdef deHexString(hexstring):
33714fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	return eexec.deHexString(''.join(hexstring.split()))
3387842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3397842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3407842e56b97ce677b83bdab09cda48bc2d89ac75aJust# Type 1 assertion
3417842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3427842e56b97ce677b83bdab09cda48bc2d89ac75aJust_fontType1RE = re.compile(r"/FontType\s+1\s+def")
3437842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3445810aa9967488207b039cb2d300fa53c91d4df2fJustdef assertType1(data):
345e56bc902cf6a707349ae6ddfe8a83a1bd7b155b9jvr	for head in ['%!PS-AdobeFont', '%!FontType1']:
3467842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if data[:len(head)] == head:
3477842e56b97ce677b83bdab09cda48bc2d89ac75aJust			break
3487842e56b97ce677b83bdab09cda48bc2d89ac75aJust	else:
349cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise T1Error("not a PostScript font")
3507842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if not _fontType1RE.search(data):
351cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise T1Error("not a Type 1 font")
35214fb031125b773f0a15eb19be4f02ed8540b2db6Behdad Esfahbod	if data.find("currentfile eexec") < 0:
353cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise T1Error("not an encrypted Type 1 font")
3547842e56b97ce677b83bdab09cda48bc2d89ac75aJust	# XXX what else?
3557842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return data
3567842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3577842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3587842e56b97ce677b83bdab09cda48bc2d89ac75aJust# pfb helpers
3597842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3605810aa9967488207b039cb2d300fa53c91d4df2fJustdef longToString(long):
3617842e56b97ce677b83bdab09cda48bc2d89ac75aJust	str = ""
3627842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for i in range(4):
363b7a2d797a40fb658d1e6dca6c08c9d2e1d83e78aBehdad Esfahbod		str = str + bytechr((long & (0xff << (i * 8))) >> i * 8)
3647842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return str
3657842e56b97ce677b83bdab09cda48bc2d89ac75aJust
3665810aa9967488207b039cb2d300fa53c91d4df2fJustdef stringToLong(str):
367180ace6a5ff1399ec53bc696e8bef7cce6eef39aBehdad Esfahbod	if len(str) != 4:
368cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod		raise ValueError('string must be 4 bytes long')
3697842e56b97ce677b83bdab09cda48bc2d89ac75aJust	long = 0
3707842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for i in range(4):
3717842e56b97ce677b83bdab09cda48bc2d89ac75aJust		long = long + (ord(str[i]) << (i * 8))
3727842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return long
3735810aa9967488207b039cb2d300fa53c91d4df2fJust
374