cffLib.py revision cd5aad92f23737ff93a110d5c73d624658a28da8
19f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project"""cffLib.py -- read/write tools for Adobe CFF fonts."""
29f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
39f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project#
49f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project# $Id: cffLib.py,v 1.34 2008-03-07 19:56:17 jvr Exp $
59f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project#
69f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
79f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectimport struct
89f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectfrom fontTools.misc import sstruct
99f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectimport string
109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectfrom fontTools.misc import psCharStrings
119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectfrom fontTools.misc.textTools import safeEval
129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source ProjectDEBUG = 0
149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source ProjectcffHeaderFormat = """
179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	major:   B
189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	minor:   B
199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	hdrSize: B
209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	offSize: B
219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project"""
229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass CFFFontSet:
249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __init__(self):
269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		pass
279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def decompile(self, file, otFont):
299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		sstruct.unpack(cffHeaderFormat, file.read(4), self)
309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		assert self.major == 1 and self.minor == 0, \
319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				"unknown CFF format: %d.%d" % (self.major, self.minor)
329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.seek(self.hdrSize)
349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.fontNames = list(Index(file))
359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.topDictIndex = TopDictIndex(file)
369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.strings = IndexedStrings(file)
379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.GlobalSubrs = GlobalSubrsIndex(file)
389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.topDictIndex.strings = self.strings
399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.topDictIndex.GlobalSubrs = self.GlobalSubrs
409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __len__(self):
429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return len(self.fontNames)
439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def keys(self):
459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return list(self.fontNames)
469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def values(self):
489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return self.topDictIndex
499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __getitem__(self, name):
519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		try:
529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			index = self.fontNames.index(name)
539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		except ValueError:
549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			raise KeyError(name)
559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return self.topDictIndex[index]
569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def compile(self, file, otFont):
589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		strings = IndexedStrings()
599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer = CFFWriter()
609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.add(sstruct.pack(cffHeaderFormat, self))
619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		fontNames = Index()
629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for name in self.fontNames:
639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			fontNames.append(name)
649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.add(fontNames.getCompiler(strings, None))
659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		topCompiler = self.topDictIndex.getCompiler(strings, None)
669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.add(topCompiler)
679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.add(strings.getCompiler())
689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.add(self.GlobalSubrs.getCompiler(strings, None))
699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for topDict in self.topDictIndex:
719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if not hasattr(topDict, "charset") or topDict.charset is None:
729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = otFont.getGlyphOrder()
739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				topDict.charset = charset
749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for child in topCompiler.getChildren(strings):
769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			writer.add(child)
779f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writer.toFile(file)
799f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
809f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toXML(self, xmlWriter, progress=None):
819f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
829f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for fontName in self.fontNames:
839f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.begintag("CFFFont", name=fontName)
849f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.newline()
859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			font = self[fontName]
869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			font.toXML(xmlWriter, progress)
879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.endtag("CFFFont")
889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.newline()
899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.begintag("GlobalSubrs")
919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.GlobalSubrs.toXML(xmlWriter, progress)
939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.endtag("GlobalSubrs")
949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def fromXML(self, (name, attrs, content)):
989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if not hasattr(self, "GlobalSubrs"):
999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.GlobalSubrs = GlobalSubrsIndex()
1009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.major = 1
1019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.minor = 0
1029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.hdrSize = 4
1039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.offSize = 4  # XXX ??
1049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if name == "CFFFont":
1059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if not hasattr(self, "fontNames"):
1069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				self.fontNames = []
1079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				self.topDictIndex = TopDictIndex()
1089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			fontName = attrs["name"]
1099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
1109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			topDict.charset = None  # gets filled in later
1119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.fontNames.append(fontName)
1129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.topDictIndex.append(topDict)
1139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			for element in content:
1149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				if isinstance(element, basestring):
1159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project					continue
1169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				topDict.fromXML(element)
1179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		elif name == "GlobalSubrs":
1189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			for element in content:
1199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				if isinstance(element, basestring):
1209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project					continue
1219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				name, attrs, content = element
1229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				subr = psCharStrings.T2CharString()
1239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				subr.fromXML((name, attrs, content))
1249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				self.GlobalSubrs.append(subr)
1259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass CFFWriter:
1289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __init__(self):
1309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.data = []
1319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def add(self, table):
1339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.data.append(table)
1349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toFile(self, file):
1369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		lastPosList = None
1379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		count = 1
1389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		while 1:
1399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if DEBUG:
1409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				print "CFFWriter.toFile() iteration:", count
1419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			count = count + 1
1429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			pos = 0
1439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			posList = [pos]
1449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			for item in self.data:
1459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				if hasattr(item, "getDataLength"):
1469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project					endPos = pos + item.getDataLength()
1479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				else:
1489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project					endPos = pos + len(item)
1499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				if hasattr(item, "setPos"):
1509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project					item.setPos(pos, endPos)
1519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				pos = endPos
1529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				posList.append(pos)
1539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if posList == lastPosList:
1549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				break
1559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			lastPosList = posList
1569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if DEBUG:
1579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			print "CFFWriter.toFile() writing to file."
1589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		begin = file.tell()
159f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		posList = [0]
160f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		for item in self.data:
161f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if hasattr(item, "toFile"):
162f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				item.toFile(file)
163f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			else:
164f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				file.write(item)
165f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			posList.append(file.tell() - begin)
166f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		assert posList == lastPosList
1679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef calcOffSize(largestOffset):
1709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if largestOffset < 0x100:
1719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offSize = 1
1729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	elif largestOffset < 0x10000:
173e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen		offSize = 2
174e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	elif largestOffset < 0x1000000:
175e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen		offSize = 3
176e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	else:
177e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen		offSize = 4
1789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return offSize
179e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
180e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
181e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wenclass IndexCompiler:
182e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
183e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	def __init__(self, items, strings, parent):
184e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen		self.items = self.getItems(items, strings)
1859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.parent = parent
1869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getItems(self, items, strings):
1889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return items
1899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getOffsets(self):
1919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		pos = 1
1929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offsets = [pos]
1939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for item in self.items:
1949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if hasattr(item, "getDataLength"):
1959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				pos = pos + item.getDataLength()
1969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
1979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				pos = pos + len(item)
1989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			offsets.append(pos)
1999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return offsets
2009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getDataLength(self):
2029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		lastOffset = self.getOffsets()[-1]
2039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offSize = calcOffSize(lastOffset)
2049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		dataLength = (
2059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			2 +                                # count
2069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			1 +                                # offSize
2079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			(len(self.items) + 1) * offSize +  # the offsets
2089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			lastOffset - 1                     # size of object data
2099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		)
2109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return dataLength
211e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
212e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	def toFile(self, file):
2139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offsets = self.getOffsets()
2149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writeCard16(file, len(self.items))
2159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offSize = calcOffSize(offsets[-1])
2169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writeCard8(file, offSize)
2179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offSize = -offSize
2189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		pack = struct.pack
2199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for offset in offsets:
2209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			binOffset = pack(">l", offset)[offSize:]
2219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			assert len(binOffset) == -offSize
2229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			file.write(binOffset)
2239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for item in self.items:
2249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if hasattr(item, "toFile"):
2259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				item.toFile(file)
2269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
2279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				file.write(item)
2289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass IndexedStringsCompiler(IndexCompiler):
2319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getItems(self, items, strings):
2339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return items.strings
2349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass TopDictIndexCompiler(IndexCompiler):
2379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getItems(self, items, strings):
2399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		out = []
2409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for item in items:
2419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			out.append(item.getCompiler(strings, self))
2429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return out
2439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getChildren(self, strings):
2459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		children = []
2469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for topDict in self.items:
2479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			children.extend(topDict.getChildren(strings))
2489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return children
2499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass FDArrayIndexCompiler(IndexCompiler):
2529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getItems(self, items, strings):
2549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		out = []
2559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for item in items:
2569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			out.append(item.getCompiler(strings, self))
2579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return out
2589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getChildren(self, strings):
2609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		children = []
2619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for fontDict in self.items:
2629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			children.extend(fontDict.getChildren(strings))
2639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return children
2649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
2659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toFile(self, file):
2669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offsets = self.getOffsets()
2679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writeCard16(file, len(self.items))
2689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offSize = calcOffSize(offsets[-1])
2699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		writeCard8(file, offSize)
270f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		offSize = -offSize
2719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		pack = struct.pack
2729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for offset in offsets:
2739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			binOffset = pack(">l", offset)[offSize:]
2749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			assert len(binOffset) == -offSize
2759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			file.write(binOffset)
2769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for item in self.items:
277f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if hasattr(item, "toFile"):
278f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				item.toFile(file)
279f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			else:
2802fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen				file.write(item)
2812fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
2822fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen	def setPos(self, pos, endPos):
2832fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		self.parent.rawDict["FDArray"] = pos
2842fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
2852fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
2862fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chenclass GlobalSubrsCompiler(IndexCompiler):
2872fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen	def getItems(self, items, strings):
288cc66ecf44d1407039b05ffd7b3342389f95c17b7Joseph Wen		out = []
289f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		for cs in items:
2902fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen			cs.compile()
291f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			out.append(cs.bytecode)
292f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return out
293f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
2949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass SubrsCompiler(GlobalSubrsCompiler):
2959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def setPos(self, pos, endPos):
2969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offset = pos - self.parent.pos
2972fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		self.parent.rawDict["Subrs"] = offset
298f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
299f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass CharStringsCompiler(GlobalSubrsCompiler):
300f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def setPos(self, pos, endPos):
301f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.parent.rawDict["CharStrings"] = pos
302f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
303f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
304f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass Index:
305f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
306f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	"""This class represents what the CFF spec calls an INDEX."""
307f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
3082fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen	compilerClass = IndexCompiler
3092fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
310f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __init__(self, file=None):
3112fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		name = self.__class__.__name__
3129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if file is None:
3139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.items = []
3149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return
3153147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		if DEBUG:
3163147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			print "loading %s at %s" % (name, file.tell())
3173147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		self.file = file
3183147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		count = readCard16(file)
3193147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		self.count = count
3203147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		self.items = [None] * count
321f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if count == 0:
322f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.items = []
3232fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen			return
324f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		offSize = readCard8(file)
325f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if DEBUG:
326f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			print "    index count: %s offSize: %s" % (count, offSize)
327f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		assert offSize <= 4, "offSize too large: %s" % offSize
3283147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		self.offsets = offsets = []
3293147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		pad = '\0' * (4 - offSize)
3309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for index in range(count+1):
3312fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen			chunk = file.read(offSize)
3322fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen			chunk = pad + chunk
3339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			offset, = struct.unpack(">L", chunk)
3349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			offsets.append(int(offset))
3352fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		self.offsetBase = file.tell() - 1
3362fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		file.seek(self.offsetBase + offsets[-1])  # pretend we've read the whole lot
3372fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen		if DEBUG:
3382fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen			print "    end of %s at %s" % (name, file.tell())
3399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
3409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __len__(self):
3419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return len(self.items)
3429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
3439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __getitem__(self, index):
3449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		item = self.items[index]
3459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if item is not None:
3469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return item
3479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		offset = self.offsets[index] + self.offsetBase
3489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		size = self.offsets[index+1] - self.offsets[index]
3499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file = self.file
3509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.seek(offset)
3519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data = file.read(size)
3529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		assert len(data) == size
3539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		item = self.produceItem(index, data, file, offset, size)
354f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.items[index] = item
355f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return item
356f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
357f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def produceItem(self, index, data, file, offset, size):
358f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return data
359f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
360f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def append(self, item):
361f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.items.append(item)
362f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
363f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def getCompiler(self, strings, parent):
364f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return self.compilerClass(self, strings, parent)
365f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
366f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
367f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass GlobalSubrsIndex(Index):
368f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
369f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	compilerClass = GlobalSubrsCompiler
370f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
371f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __init__(self, file=None, globalSubrs=None, private=None, fdSelect=None, fdArray=None):
372f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		Index.__init__(self, file)
373f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.globalSubrs = globalSubrs
374f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.private = private
375f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if fdSelect:
376f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.fdSelect = fdSelect
377f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if fdArray:
378f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.fdArray = fdArray
379f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
380f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def produceItem(self, index, data, file, offset, size):
381f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if self.private is not None:
382f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			private = self.private
383f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		elif hasattr(self, 'fdArray') and self.fdArray is not None:
384f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			private = self.fdArray[self.fdSelect[index]].Private
3853147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		else:
3863147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			private = None
3873147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		return psCharStrings.T2CharString(data, private=private, globalSubrs=self.globalSubrs)
3883147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
3893147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	def toXML(self, xmlWriter, progress):
3903147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		xmlWriter.comment("The 'index' attribute is only for humans; it is ignored when parsed.")
391f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		xmlWriter.newline()
392f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		for i in range(len(self)):
3933147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			subr = self[i]
3943147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			if subr.needsDecompilation():
3953147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen				xmlWriter.begintag("CharString", index=i, raw=1)
3963147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			else:
3973147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen				xmlWriter.begintag("CharString", index=i)
3983147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			xmlWriter.newline()
3993147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			subr.toXML(xmlWriter)
400f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			xmlWriter.endtag("CharString")
401f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			xmlWriter.newline()
4023147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4033147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	def fromXML(self, (name, attrs, content)):
404f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if name != "CharString":
4053147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			return
406f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		subr = psCharStrings.T2CharString()
407f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		subr.fromXML((name, attrs, content))
4083147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		self.append(subr)
4093147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
410f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def getItemAndSelector(self, index):
4113147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		sel = None
4123147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		if hasattr(self, 'fdSelect'):
4133147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			sel = self.fdSelect[index]
4143147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		return self[index], sel
4153147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4163147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4173147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wenclass SubrsIndex(GlobalSubrsIndex):
4183147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	compilerClass = SubrsCompiler
419f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
420f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
4213147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wenclass TopDictIndex(Index):
4223147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4233147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	compilerClass = TopDictIndexCompiler
4243147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4253147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	def produceItem(self, index, data, file, offset, size):
4263147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		top = TopDict(self.strings, file, offset, self.GlobalSubrs)
4273147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		top.decompile(data)
4283147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		return top
4293147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4303147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	def toXML(self, xmlWriter, progress):
4313147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		for i in range(len(self)):
4323147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			xmlWriter.begintag("FontDict", index=i)
4333147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			xmlWriter.newline()
4343147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			self[i].toXML(xmlWriter, progress)
4353147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			xmlWriter.endtag("FontDict")
4363147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen			xmlWriter.newline()
4373147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4383147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4393147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wenclass FDArrayIndex(TopDictIndex):
4403147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4413147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	compilerClass = FDArrayIndexCompiler
4423147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen
4433147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen	def fromXML(self, (name, attrs, content)):
4443147fbe7688fc353e6ae03825a37cf101a4ee01dJoseph Wen		if name != "FontDict":
445f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return
446f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		fontDict = FontDict()
447f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		for element in content:
448f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if isinstance(element, basestring):
449f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				continue
450f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			fontDict.fromXML(element)
451f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.append(fontDict)
452f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
453f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
454f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass	FDSelect:
455f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __init__(self, file = None, numGlyphs = None, format=None):
456f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if file:
457f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			# read data in from file
458f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.format = readCard8(file)
459f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if self.format == 0:
460f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				from array import array
461f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				self.gidArray = array("B", file.read(numGlyphs)).tolist()
462f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			elif self.format == 3:
463f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				gidArray = [None] * numGlyphs
464f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				nRanges = readCard16(file)
465f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				prev = None
466f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				for i in range(nRanges):
467f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					first = readCard16(file)
468f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					if prev is not None:
469f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen						for glyphID in range(prev, first):
470f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen							gidArray[glyphID] = fd
471f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					prev = first
472f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					fd = readCard8(file)
473f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				if prev is not None:
474f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					first = readCard16(file)
475f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen					for glyphID in range(prev, first):
476f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen						gidArray[glyphID] = fd
477f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				self.gidArray = gidArray
478f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			else:
479f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				assert 0, "unsupported FDSelect format: %s" % format
480f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		else:
481f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			# reading from XML. Make empty gidArray,, and leave format as passed in.
482f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			# format == None will result in the smallest representation being used.
483f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.format = format
484f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.gidArray = []
485f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
486f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
487f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __len__(self):
488f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return len(self.gidArray)
489f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
490f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __getitem__(self, index):
491f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return self.gidArray[index]
492f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
493f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __setitem__(self, index, fdSelectValue):
494f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.gidArray[index] = fdSelectValue
495f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
496f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def append(self, fdSelectValue):
497f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		self.gidArray.append(fdSelectValue)
498f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
499f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
500f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass CharStrings:
501f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
502f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray):
503f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if file is not None:
504f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStringsIndex = SubrsIndex(file, globalSubrs, private, fdSelect, fdArray)
505f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStrings = charStrings = {}
506f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			for i in range(len(charset)):
507f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				charStrings[charset[i]] = i
508f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStringsAreIndexed = 1
509f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		else:
510f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStrings = {}
511f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStringsAreIndexed = 0
512f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.globalSubrs = globalSubrs
513f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.private = private
514f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if fdSelect != None:
515f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				self.fdSelect = fdSelect
516f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if fdArray!= None:
517f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				self.fdArray = fdArray
518f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
519f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def keys(self):
520f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return self.charStrings.keys()
521f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
522f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def values(self):
523f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if self.charStringsAreIndexed:
524f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return self.charStringsIndex
525f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		else:
526f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return self.charStrings.values()
527f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
528f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def has_key(self, name):
529f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return name in self.charStrings
530f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
531f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __len__(self):
532f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return len(self.charStrings)
533f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
534f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __getitem__(self, name):
535f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		charString = self.charStrings[name]
536f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if self.charStringsAreIndexed:
537f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			charString = self.charStringsIndex[charString]
538f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return charString
539f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
540f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def __setitem__(self, name, charString):
541f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if self.charStringsAreIndexed:
542f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			index = self.charStrings[name]
543f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			self.charStringsIndex[index] = charString
5449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
5459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.charStrings[name] = charString
5469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
5479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getItemAndSelector(self, name):
5489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if self.charStringsAreIndexed:
5499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			index = self.charStrings[name]
5509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return self.charStringsIndex.getItemAndSelector(index)
5519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
5529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if hasattr(self, 'fdSelect'):
5539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				sel = self.fdSelect[index]  # index is not defined at this point. Read R. ?
5549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
5559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				raise KeyError("fdSelect array not yet defined.")
5569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return self.charStrings[name], sel
5579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
5589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toXML(self, xmlWriter, progress):
5599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		names = self.keys()
5609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		names.sort()
5619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		i = 0
5629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		step = 10
5639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		numGlyphs = len(names)
5649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for name in names:
5659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			charStr, fdSelectIndex = self.getItemAndSelector(name)
5669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if charStr.needsDecompilation():
5679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				raw = [("raw", 1)]
5689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
5699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				raw = []
5709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if fdSelectIndex is None:
5719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				xmlWriter.begintag("CharString", [('name', name)] + raw)
5729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
5739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				xmlWriter.begintag("CharString",
5749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project						[('name', name), ('fdSelectIndex', fdSelectIndex)] + raw)
5759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.newline()
5769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			charStr.toXML(xmlWriter)
5779f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.endtag("CharString")
5789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			xmlWriter.newline()
5799f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if not i % step and progress is not None:
5809f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				progress.setLabel("Dumping 'CFF ' table... (%s)" % name)
5819f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				progress.increment(step / float(numGlyphs))
5829f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			i = i + 1
5839f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
584f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def fromXML(self, (name, attrs, content)):
5859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for element in content:
5869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if isinstance(element, basestring):
5879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				continue
5889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			name, attrs, content = element
5899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if name != "CharString":
5909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				continue
5919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			fdID = -1
5929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if hasattr(self, "fdArray"):
5939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				fdID = safeEval(attrs["fdSelectIndex"])
5949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				private = self.fdArray[fdID].Private
5959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
596e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen				private = self.private
597e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
598e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen			glyphName = attrs["name"]
599e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen			charString = psCharStrings.T2CharString(
6002fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen					private=private,
601e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen					globalSubrs=self.globalSubrs)
602e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen			charString.fromXML((name, attrs, content))
603e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen			if fdID >= 0:
604e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen				charString.fdSelectIndex = fdID
605e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen			self[glyphName] = charString
6062fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
6072fa18d458a5545e7f6cc431954f19e136dfedc9eWei-Ta Chen
608e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wendef readCard8(file):
6099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return ord(file.read(1))
6109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef readCard16(file):
612e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	value, = struct.unpack(">H", file.read(2))
613e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen	return value
614e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
6159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef writeCard8(file, value):
6169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	file.write(chr(value))
6179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef writeCard16(file, value):
6199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	file.write(struct.pack(">H", value))
6209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef packCard8(value):
6229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return chr(value)
6239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef packCard16(value):
6259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return struct.pack(">H", value)
6269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef buildOperatorDict(table):
6289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	d = {}
6299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for op, name, arg, default, conv in table:
6309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		d[op] = (name, arg)
6319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return d
6329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef buildOpcodeDict(table):
6349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	d = {}
6359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for op, name, arg, default, conv in table:
6369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if isinstance(op, tuple):
6379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			op = chr(op[0]) + chr(op[1])
6389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
6399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			op = chr(op)
6409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		d[name] = (op, arg)
6419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return d
6429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef buildOrder(table):
6449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	l = []
6459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for op, name, arg, default, conv in table:
6469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		l.append(name)
6479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return l
6489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef buildDefaults(table):
6509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	d = {}
6519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for op, name, arg, default, conv in table:
6529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if default is not None:
6539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			d[name] = default
6549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return d
6559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef buildConverters(table):
6579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	d = {}
6589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for op, name, arg, default, conv in table:
6599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		d[name] = conv
6609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return d
6619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass SimpleConverter:
6649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def read(self, parent, value):
6659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return value
6669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def write(self, parent, value):
6679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return value
6689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlWrite(self, xmlWriter, name, value, progress):
6699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.simpletag(name, value=value)
6709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
6719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
6729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return attrs["value"]
6739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass Latin1Converter(SimpleConverter):
6759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlWrite(self, xmlWriter, name, value, progress):
6769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		# Store as UTF-8
6779f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		value = unicode(value, "latin-1").encode("utf-8")
6789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.simpletag(name, value=value)
6799f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
6809f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
6819f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		s = unicode(attrs["value"], "utf-8")
6829f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return s.encode("latin-1")
6839f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6849f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef parseNum(s):
6869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	try:
6879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		value = int(s)
6889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	except:
6899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		value = float(s)
6909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return value
6919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass NumberConverter(SimpleConverter):
6939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
6949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return parseNum(attrs["value"])
6959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
6969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass ArrayConverter(SimpleConverter):
6979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlWrite(self, xmlWriter, name, value, progress):
6989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		value = map(str, value)
6999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.simpletag(name, value=" ".join(value))
7009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
7019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
7029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		values = attrs["value"].split()
7039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return map(parseNum, values)
7049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
7059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass TableConverter(SimpleConverter):
7069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlWrite(self, xmlWriter, name, value, progress):
7079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.begintag(name)
7089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
7099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		value.toXML(xmlWriter, progress)
7109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.endtag(name)
7119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
7129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
7139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		ob = self.getClass()()
7149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for element in content:
7159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if isinstance(element, basestring):
7169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				continue
7179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			ob.fromXML(element)
7189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return ob
7199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
7209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass PrivateDictConverter(TableConverter):
7219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getClass(self):
7229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return PrivateDict
7239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def read(self, parent, value):
7249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		size, offset = value
7259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file = parent.file
7269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		priv = PrivateDict(parent.strings, file, offset)
7279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.seek(offset)
7289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data = file.read(size)
7299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		len(data) == size
7309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		priv.decompile(data)
7319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return priv
7329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def write(self, parent, value):
7339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return (0, 0)  # dummy value
7349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
7359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass SubrsConverter(TableConverter):
7369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getClass(self):
7379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return SubrsIndex
7389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def read(self, parent, value):
7399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file = parent.file
7409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.seek(parent.offset + value)  # Offset(self)
7419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return SubrsIndex(file)
7429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def write(self, parent, value):
7439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return 0  # dummy value
7449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
7459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass CharStringsConverter(TableConverter):
7469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def read(self, parent, value):
7479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file = parent.file
7489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		charset = parent.charset
7499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		globalSubrs = parent.GlobalSubrs
7509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if hasattr(parent, "ROS"):
7519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			fdSelect, fdArray = parent.FDSelect, parent.FDArray
7529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			private = None
7539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
7549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			fdSelect, fdArray = None, None
7559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			private = parent.Private
7569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.seek(value)  # Offset(0)
7579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return CharStrings(file, charset, globalSubrs, private, fdSelect, fdArray)
7589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def write(self, parent, value):
7599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return 0  # dummy value
7609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
7619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if hasattr(parent, "ROS"):
7629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			# if it is a CID-keyed font, then the private Dict is extracted from the parent.FDArray
7639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			private, fdSelect, fdArray = None, parent.FDSelect, parent.FDArray
7649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
7659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			# if it is a name-keyed font, then the private dict is in the top dict, and there is no fdArray.
7669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			private, fdSelect, fdArray = parent.Private, None, None
7679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		charStrings = CharStrings(None, None, parent.GlobalSubrs, private, fdSelect, fdArray)
7689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		charStrings.fromXML((name, attrs, content))
7699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return charStrings
7709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
7719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass CharsetConverter:
7729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def read(self, parent, value):
7739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		isCID = hasattr(parent, "ROS")
7749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if value > 2:
7759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			numGlyphs = parent.numGlyphs
7769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			file = parent.file
7779f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			file.seek(value)
7789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if DEBUG:
7799f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				print "loading charset at %s" % value
7809f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			format = readCard8(file)
7819f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if format == 0:
7829f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
7839f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			elif format == 1 or format == 2:
7849f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
7859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			else:
7869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				raise NotImplementedError
7879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			assert len(charset) == numGlyphs
7889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if DEBUG:
7899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				print "    charset end at %s" % file.tell()
7909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else: # offset == 0 -> no charset data.
7919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if isCID or "CharStrings" not in parent.rawDict:
7929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				assert value == 0 # We get here only when processing fontDicts from the FDArray of CFF-CID fonts. Only the real topDict references the chrset.
7939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = None
7949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			elif value == 0:
7959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = cffISOAdobeStrings
7969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			elif value == 1:
7979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = cffIExpertStrings
7989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			elif value == 2:
7999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset = cffExpertSubsetStrings
8009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return charset
8019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def write(self, parent, value):
8039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return 0  # dummy value
8049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlWrite(self, xmlWriter, name, value, progress):
8059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		# XXX only write charset when not in OT/TTX context, where we
8069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		# dump charset as a separate "GlyphOrder" table.
8079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		##xmlWriter.simpletag("charset")
8089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
8099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
8109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
8119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if 0:
8129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return safeEval(attrs["value"])
8139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass CharsetCompiler:
8169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __init__(self, strings, charset, parent):
8189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		assert charset[0] == '.notdef'
8199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		isCID = hasattr(parent.dictObj, "ROS")
8209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data0 = packCharset0(charset, isCID, strings)
8219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data = packCharset(charset, isCID, strings)
8229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if len(data) < len(data0):
8239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.data = data
8249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
8259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.data = data0
8269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.parent = parent
8279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def setPos(self, pos, endPos):
8299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.parent.rawDict["charset"] = pos
8309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def getDataLength(self):
8329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return len(self.data)
8339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toFile(self, file):
8359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.write(self.data)
8369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef getCIDfromName(name, strings):
8399f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return int(name[3:])
8409f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8419f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef getSIDfromName(name, strings):
8429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return strings.getSID(name)
8439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef packCharset0(charset, isCID, strings):
8459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	format = 0
8469f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	data = [packCard8(format)]
8479f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if isCID:
8489f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		getNameID = getCIDfromName
8499f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	else:
8509f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		getNameID = getSIDfromName
8519f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8529f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for name in charset[1:]:
8539f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data.append(packCard16(getNameID(name,strings)))
8549f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return "".join(data)
8559f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8569f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8579f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef packCharset(charset, isCID, strings):
8589f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	format = 1
8599f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	ranges = []
8609f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	first = None
8619f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	end = 0
8629f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if isCID:
8639f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		getNameID = getCIDfromName
8649f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	else:
8659f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		getNameID = getSIDfromName
8669f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8679f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for name in charset[1:]:
8689f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		SID = getNameID(name, strings)
8699f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if first is None:
8709f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			first = SID
8719f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		elif end + 1 != SID:
8729f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			nLeft = end - first
8739f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if nLeft > 255:
8749f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				format = 2
8759f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			ranges.append((first, nLeft))
8769f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			first = SID
8779f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		end = SID
8789f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	nLeft = end - first
8799f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if nLeft > 255:
8809f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		format = 2
8819f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	ranges.append((first, nLeft))
8829f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8839f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	data = [packCard8(format)]
8849f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if format == 1:
8859f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeftFunc = packCard8
8869f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	else:
8879f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeftFunc = packCard16
8889f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for first, nLeft in ranges:
8899f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data.append(packCard16(first) + nLeftFunc(nLeft))
8909f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return "".join(data)
8919f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
8929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef parseCharset0(numGlyphs, file, strings, isCID):
8939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	charset = [".notdef"]
8949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if isCID:
8959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for i in range(numGlyphs - 1):
8969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			CID = readCard16(file)
8979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			charset.append("cid" + string.zfill(str(CID), 5) )
8989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	else:
8999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for i in range(numGlyphs - 1):
9009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			SID = readCard16(file)
9019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			charset.append(strings[SID])
9029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return charset
9039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef parseCharset(numGlyphs, file, strings, isCID, format):
9059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	charset = ['.notdef']
9069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	count = 1
9079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	if format == 1:
9089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeftFunc = readCard8
9099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	else:
9109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeftFunc = readCard16
9119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	while count < numGlyphs:
9129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		first = readCard16(file)
9139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeft = nLeftFunc(file)
9149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if isCID:
9159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			for CID in range(first, first+nLeft+1):
9169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset.append("cid" + string.zfill(str(CID), 5) )
9179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
9189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			for SID in range(first, first+nLeft+1):
9199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				charset.append(strings[SID])
9209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		count = count + nLeft + 1
9219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return charset
9229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectclass EncodingCompiler:
9259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def __init__(self, strings, encoding, parent):
9279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		assert not isinstance(encoding, basestring)
9289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
9299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
9309f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if len(data0) < len(data1):
9319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.data = data0
9329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		else:
9339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			self.data = data1
9349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.parent = parent
9359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def setPos(self, pos, endPos):
9379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		self.parent.rawDict["Encoding"] = pos
9389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
939f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def getDataLength(self):
940f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return len(self.data)
941e0eadaa39b72e33f032220246c771d7302ebeaf8Joseph Wen
9429f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def toFile(self, file):
9439f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		file.write(self.data)
9449f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9459f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
946f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wenclass EncodingConverter(SimpleConverter):
947f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
948f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def read(self, parent, value):
949f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if value == 0:
950f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return "StandardEncoding"
951f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		elif value == 1:
952f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return "ExpertEncoding"
953f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		else:
954f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			assert value > 1
955f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			file = parent.file
956f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			file.seek(value)
957f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if DEBUG:
958f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				print "loading Encoding at %s" % value
959f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			format = readCard8(file)
960f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			haveSupplement = format & 0x80
961f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if haveSupplement:
962f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				raise NotImplementedError("Encoding supplements are not yet supported")
963f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			format = format & 0x7f
964f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if format == 0:
965f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				encoding = parseEncoding0(parent.charset, file, haveSupplement,
966f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen						parent.strings)
967f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			elif format == 1:
968f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				encoding = parseEncoding1(parent.charset, file, haveSupplement,
969f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen						parent.strings)
970f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return encoding
971f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
972f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def write(self, parent, value):
973f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if value == "StandardEncoding":
974f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return 0
975f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		elif value == "ExpertEncoding":
976f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return 1
977f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		return 0  # dummy value
978f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen
979f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen	def xmlWrite(self, xmlWriter, name, value, progress):
980f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		if value in ("StandardEncoding", "ExpertEncoding"):
981f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			xmlWriter.simpletag(name, name=value)
982f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			xmlWriter.newline()
983f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			return
984f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		xmlWriter.begintag(name)
985f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		xmlWriter.newline()
986f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		for code in range(len(value)):
987f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			glyphName = value[code]
988f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen			if glyphName != ".notdef":
989f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				xmlWriter.simpletag("map", code=hex(code), name=glyphName)
990f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen				xmlWriter.newline()
991f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wen		xmlWriter.endtag(name)
9929f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		xmlWriter.newline()
9939f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
9949f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	def xmlRead(self, (name, attrs, content), parent):
9959f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if "name" in attrs:
9969f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			return attrs["name"]
9979f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		encoding = [".notdef"] * 256
9989f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for element in content:
9999f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			if isinstance(element, basestring):
10009f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project				continue
10019f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			name, attrs, content = element
10029f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			code = safeEval(attrs["code"])
10039f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			glyphName = attrs["name"]
10049f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			encoding[code] = glyphName
10059f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		return encoding
10069f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
10079f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
10089f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef parseEncoding0(charset, file, haveSupplement, strings):
10099f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	nCodes = readCard8(file)
10109f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	encoding = [".notdef"] * 256
10119f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for glyphID in range(1, nCodes + 1):
10129f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		code = readCard8(file)
10139f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if code != 0:
10149f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			encoding[code] = charset[glyphID]
10159f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return encoding
10169f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
10179f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Projectdef parseEncoding1(charset, file, haveSupplement, strings):
10189f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	nRanges = readCard8(file)
10199f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	encoding = [".notdef"] * 256
10209f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	glyphID = 1
10219f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for i in range(nRanges):
10229f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		code = readCard8(file)
10239f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		nLeft = readCard8(file)
10249f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		for glyphID in range(glyphID, glyphID + nLeft + 1):
10259f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			encoding[code] = charset[glyphID]
10269f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			code = code + 1
10279f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		glyphID = glyphID + 1
10289f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	return encoding
10299f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project
1030f5b94eebe742df1a9bb3941fc0a0ec0137e936efJoseph Wendef packEncoding0(charset, encoding, strings):
10319f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	format = 0
10329f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	m = {}
10339f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for code in range(len(encoding)):
10349f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		name = encoding[code]
10359f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project		if name != ".notdef":
10369f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project			m[name] = code
10379f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	codes = []
10389f5d49a1588e438ae7ceabd0c94172117e3303aaThe Android Open Source Project	for name in charset[1:]:
1039		code = m.get(name)
1040		codes.append(code)
1041
1042	while codes and codes[-1] is None:
1043		codes.pop()
1044
1045	data = [packCard8(format), packCard8(len(codes))]
1046	for code in codes:
1047		if code is None:
1048			code = 0
1049		data.append(packCard8(code))
1050	return "".join(data)
1051
1052def packEncoding1(charset, encoding, strings):
1053	format = 1
1054	m = {}
1055	for code in range(len(encoding)):
1056		name = encoding[code]
1057		if name != ".notdef":
1058			m[name] = code
1059	ranges = []
1060	first = None
1061	end = 0
1062	for name in charset[1:]:
1063		code = m.get(name, -1)
1064		if first is None:
1065			first = code
1066		elif end + 1 != code:
1067			nLeft = end - first
1068			ranges.append((first, nLeft))
1069			first = code
1070		end = code
1071	nLeft = end - first
1072	ranges.append((first, nLeft))
1073
1074	# remove unencoded glyphs at the end.
1075	while ranges and ranges[-1][0] == -1:
1076		ranges.pop()
1077
1078	data = [packCard8(format), packCard8(len(ranges))]
1079	for first, nLeft in ranges:
1080		if first == -1:  # unencoded
1081			first = 0
1082		data.append(packCard8(first) + packCard8(nLeft))
1083	return "".join(data)
1084
1085
1086class FDArrayConverter(TableConverter):
1087
1088	def read(self, parent, value):
1089		file = parent.file
1090		file.seek(value)
1091		fdArray = FDArrayIndex(file)
1092		fdArray.strings = parent.strings
1093		fdArray.GlobalSubrs = parent.GlobalSubrs
1094		return fdArray
1095
1096	def write(self, parent, value):
1097		return 0  # dummy value
1098
1099	def xmlRead(self, (name, attrs, content), parent):
1100		fdArray = FDArrayIndex()
1101		for element in content:
1102			if isinstance(element, basestring):
1103				continue
1104			fdArray.fromXML(element)
1105		return fdArray
1106
1107
1108class FDSelectConverter:
1109
1110	def read(self, parent, value):
1111		file = parent.file
1112		file.seek(value)
1113		fdSelect = FDSelect(file, parent.numGlyphs)
1114		return 	fdSelect
1115
1116	def write(self, parent, value):
1117		return 0  # dummy value
1118
1119	# The FDSelect glyph data is written out to XML in the charstring keys,
1120	# so we write out only the format selector
1121	def xmlWrite(self, xmlWriter, name, value, progress):
1122		xmlWriter.simpletag(name, [('format', value.format)])
1123		xmlWriter.newline()
1124
1125	def xmlRead(self, (name, attrs, content), parent):
1126		format = safeEval(attrs["format"])
1127		file = None
1128		numGlyphs = None
1129		fdSelect = FDSelect(file, numGlyphs, format)
1130		return fdSelect
1131
1132
1133def packFDSelect0(fdSelectArray):
1134	format = 0
1135	data = [packCard8(format)]
1136	for index in fdSelectArray:
1137		data.append(packCard8(index))
1138	return "".join(data)
1139
1140
1141def packFDSelect3(fdSelectArray):
1142	format = 3
1143	fdRanges = []
1144	first = None
1145	end = 0
1146	lenArray = len(fdSelectArray)
1147	lastFDIndex = -1
1148	for i in range(lenArray):
1149		fdIndex = fdSelectArray[i]
1150		if lastFDIndex != fdIndex:
1151			fdRanges.append([i, fdIndex])
1152			lastFDIndex = fdIndex
1153	sentinelGID = i + 1
1154
1155	data = [packCard8(format)]
1156	data.append(packCard16( len(fdRanges) ))
1157	for fdRange in fdRanges:
1158		data.append(packCard16(fdRange[0]))
1159		data.append(packCard8(fdRange[1]))
1160	data.append(packCard16(sentinelGID))
1161	return "".join(data)
1162
1163
1164class FDSelectCompiler:
1165
1166	def __init__(self, fdSelect, parent):
1167		format = fdSelect.format
1168		fdSelectArray = fdSelect.gidArray
1169		if format == 0:
1170			self.data = packFDSelect0(fdSelectArray)
1171		elif format == 3:
1172			self.data = packFDSelect3(fdSelectArray)
1173		else:
1174			# choose smaller of the two formats
1175			data0 = packFDSelect0(fdSelectArray)
1176			data3 = packFDSelect3(fdSelectArray)
1177			if len(data0) < len(data3):
1178				self.data = data0
1179				fdSelect.format = 0
1180			else:
1181				self.data = data3
1182				fdSelect.format = 3
1183
1184		self.parent = parent
1185
1186	def setPos(self, pos, endPos):
1187		self.parent.rawDict["FDSelect"] = pos
1188
1189	def getDataLength(self):
1190		return len(self.data)
1191
1192	def toFile(self, file):
1193		file.write(self.data)
1194
1195
1196class ROSConverter(SimpleConverter):
1197
1198	def xmlWrite(self, xmlWriter, name, value, progress):
1199		registry, order, supplement = value
1200		xmlWriter.simpletag(name, [('Registry', registry), ('Order', order),
1201			('Supplement', supplement)])
1202		xmlWriter.newline()
1203
1204	def xmlRead(self, (name, attrs, content), parent):
1205		return (attrs['Registry'], attrs['Order'], safeEval(attrs['Supplement']))
1206
1207
1208
1209topDictOperators = [
1210#	opcode     name                  argument type   default    converter
1211	((12, 30), 'ROS',        ('SID','SID','number'), None,      ROSConverter()),
1212	((12, 20), 'SyntheticBase',      'number',       None,      None),
1213	(0,        'version',            'SID',          None,      None),
1214	(1,        'Notice',             'SID',          None,      Latin1Converter()),
1215	((12, 0),  'Copyright',          'SID',          None,      Latin1Converter()),
1216	(2,        'FullName',           'SID',          None,      None),
1217	((12, 38), 'FontName',           'SID',          None,      None),
1218	(3,        'FamilyName',         'SID',          None,      None),
1219	(4,        'Weight',             'SID',          None,      None),
1220	((12, 1),  'isFixedPitch',       'number',       0,         None),
1221	((12, 2),  'ItalicAngle',        'number',       0,         None),
1222	((12, 3),  'UnderlinePosition',  'number',       None,      None),
1223	((12, 4),  'UnderlineThickness', 'number',       50,        None),
1224	((12, 5),  'PaintType',          'number',       0,         None),
1225	((12, 6),  'CharstringType',     'number',       2,         None),
1226	((12, 7),  'FontMatrix',         'array',  [0.001,0,0,0.001,0,0],  None),
1227	(13,       'UniqueID',           'number',       None,      None),
1228	(5,        'FontBBox',           'array',  [0,0,0,0],       None),
1229	((12, 8),  'StrokeWidth',        'number',       0,         None),
1230	(14,       'XUID',               'array',        None,      None),
1231	((12, 21), 'PostScript',         'SID',          None,      None),
1232	((12, 22), 'BaseFontName',       'SID',          None,      None),
1233	((12, 23), 'BaseFontBlend',      'delta',        None,      None),
1234	((12, 31), 'CIDFontVersion',     'number',       0,         None),
1235	((12, 32), 'CIDFontRevision',    'number',       0,         None),
1236	((12, 33), 'CIDFontType',        'number',       0,         None),
1237	((12, 34), 'CIDCount',           'number',       8720,      None),
1238	(15,       'charset',            'number',       0,         CharsetConverter()),
1239	((12, 35), 'UIDBase',            'number',       None,      None),
1240	(16,       'Encoding',           'number',       0,         EncodingConverter()),
1241	(18,       'Private',       ('number','number'), None,      PrivateDictConverter()),
1242	((12, 37), 'FDSelect',           'number',       None,      FDSelectConverter()),
1243	((12, 36), 'FDArray',            'number',       None,      FDArrayConverter()),
1244	(17,       'CharStrings',        'number',       None,      CharStringsConverter()),
1245]
1246
1247# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
1248# in order for the font to compile back from xml.
1249
1250
1251privateDictOperators = [
1252#	opcode     name                  argument type   default    converter
1253	(6,        'BlueValues',         'delta',        None,      None),
1254	(7,        'OtherBlues',         'delta',        None,      None),
1255	(8,        'FamilyBlues',        'delta',        None,      None),
1256	(9,        'FamilyOtherBlues',   'delta',        None,      None),
1257	((12, 9),  'BlueScale',          'number',       0.039625,  None),
1258	((12, 10), 'BlueShift',          'number',       7,         None),
1259	((12, 11), 'BlueFuzz',           'number',       1,         None),
1260	(10,       'StdHW',              'number',       None,      None),
1261	(11,       'StdVW',              'number',       None,      None),
1262	((12, 12), 'StemSnapH',          'delta',        None,      None),
1263	((12, 13), 'StemSnapV',          'delta',        None,      None),
1264	((12, 14), 'ForceBold',          'number',       0,         None),
1265	((12, 15), 'ForceBoldThreshold', 'number',       None,      None),  # deprecated
1266	((12, 16), 'lenIV',              'number',       None,      None),  # deprecated
1267	((12, 17), 'LanguageGroup',      'number',       0,         None),
1268	((12, 18), 'ExpansionFactor',    'number',       0.06,      None),
1269	((12, 19), 'initialRandomSeed',  'number',       0,         None),
1270	(20,       'defaultWidthX',      'number',       0,         None),
1271	(21,       'nominalWidthX',      'number',       0,         None),
1272	(19,       'Subrs',              'number',       None,      SubrsConverter()),
1273]
1274
1275def addConverters(table):
1276	for i in range(len(table)):
1277		op, name, arg, default, conv = table[i]
1278		if conv is not None:
1279			continue
1280		if arg in ("delta", "array"):
1281			conv = ArrayConverter()
1282		elif arg == "number":
1283			conv = NumberConverter()
1284		elif arg == "SID":
1285			conv = SimpleConverter()
1286		else:
1287			assert 0
1288		table[i] = op, name, arg, default, conv
1289
1290addConverters(privateDictOperators)
1291addConverters(topDictOperators)
1292
1293
1294class TopDictDecompiler(psCharStrings.DictDecompiler):
1295	operators = buildOperatorDict(topDictOperators)
1296
1297
1298class PrivateDictDecompiler(psCharStrings.DictDecompiler):
1299	operators = buildOperatorDict(privateDictOperators)
1300
1301
1302class DictCompiler:
1303
1304	def __init__(self, dictObj, strings, parent):
1305		assert isinstance(strings, IndexedStrings)
1306		self.dictObj = dictObj
1307		self.strings = strings
1308		self.parent = parent
1309		rawDict = {}
1310		for name in dictObj.order:
1311			value = getattr(dictObj, name, None)
1312			if value is None:
1313				continue
1314			conv = dictObj.converters[name]
1315			value = conv.write(dictObj, value)
1316			if value == dictObj.defaults.get(name):
1317				continue
1318			rawDict[name] = value
1319		self.rawDict = rawDict
1320
1321	def setPos(self, pos, endPos):
1322		pass
1323
1324	def getDataLength(self):
1325		return len(self.compile("getDataLength"))
1326
1327	def compile(self, reason):
1328		if DEBUG:
1329			print "-- compiling %s for %s" % (self.__class__.__name__, reason)
1330			print "in baseDict: ", self
1331		rawDict = self.rawDict
1332		data = []
1333		for name in self.dictObj.order:
1334			value = rawDict.get(name)
1335			if value is None:
1336				continue
1337			op, argType = self.opcodes[name]
1338			if isinstance(argType, tuple):
1339				l = len(argType)
1340				assert len(value) == l, "value doesn't match arg type"
1341				for i in range(l):
1342					arg = argType[i]
1343					v = value[i]
1344					arghandler = getattr(self, "arg_" + arg)
1345					data.append(arghandler(v))
1346			else:
1347				arghandler = getattr(self, "arg_" + argType)
1348				data.append(arghandler(value))
1349			data.append(op)
1350		return "".join(data)
1351
1352	def toFile(self, file):
1353		file.write(self.compile("toFile"))
1354
1355	def arg_number(self, num):
1356		return encodeNumber(num)
1357	def arg_SID(self, s):
1358		return psCharStrings.encodeIntCFF(self.strings.getSID(s))
1359	def arg_array(self, value):
1360		data = []
1361		for num in value:
1362			data.append(encodeNumber(num))
1363		return "".join(data)
1364	def arg_delta(self, value):
1365		out = []
1366		last = 0
1367		for v in value:
1368			out.append(v - last)
1369			last = v
1370		data = []
1371		for num in out:
1372			data.append(encodeNumber(num))
1373		return "".join(data)
1374
1375
1376def encodeNumber(num):
1377	if isinstance(num, float):
1378		return psCharStrings.encodeFloat(num)
1379	else:
1380		return psCharStrings.encodeIntCFF(num)
1381
1382
1383class TopDictCompiler(DictCompiler):
1384
1385	opcodes = buildOpcodeDict(topDictOperators)
1386
1387	def getChildren(self, strings):
1388		children = []
1389		if hasattr(self.dictObj, "charset") and self.dictObj.charset:
1390			children.append(CharsetCompiler(strings, self.dictObj.charset, self))
1391		if hasattr(self.dictObj, "Encoding"):
1392			encoding = self.dictObj.Encoding
1393			if not isinstance(encoding, basestring):
1394				children.append(EncodingCompiler(strings, encoding, self))
1395		if hasattr(self.dictObj, "FDSelect"):
1396			# I have not yet supported merging a ttx CFF-CID font, as there are interesting
1397			# issues about merging the FDArrays. Here I assume that
1398			# either the font was read from XML, and teh FDSelect indices are all
1399			# in the charstring data, or the FDSelect array is already fully defined.
1400			fdSelect = self.dictObj.FDSelect
1401			if len(fdSelect) == 0: # probably read in from XML; assume fdIndex in CharString data
1402				charStrings = self.dictObj.CharStrings
1403				for name in self.dictObj.charset:
1404					charstring = charStrings[name]
1405					fdSelect.append(charStrings[name].fdSelectIndex)
1406			fdSelectComp = FDSelectCompiler(fdSelect, self)
1407			children.append(fdSelectComp)
1408		if hasattr(self.dictObj, "CharStrings"):
1409			items = []
1410			charStrings = self.dictObj.CharStrings
1411			for name in self.dictObj.charset:
1412				items.append(charStrings[name])
1413			charStringsComp = CharStringsCompiler(items, strings, self)
1414			children.append(charStringsComp)
1415		if hasattr(self.dictObj, "FDArray"):
1416			# I have not yet supported merging a ttx CFF-CID font, as there are interesting
1417			# issues about merging the FDArrays. Here I assume that the FDArray info is correct
1418			# and complete.
1419			fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
1420			children.append(fdArrayIndexComp)
1421			children.extend(fdArrayIndexComp.getChildren(strings))
1422		if hasattr(self.dictObj, "Private"):
1423			privComp = self.dictObj.Private.getCompiler(strings, self)
1424			children.append(privComp)
1425			children.extend(privComp.getChildren(strings))
1426		return children
1427
1428
1429class FontDictCompiler(DictCompiler):
1430
1431	opcodes = buildOpcodeDict(topDictOperators)
1432
1433	def getChildren(self, strings):
1434		children = []
1435		if hasattr(self.dictObj, "Private"):
1436			privComp = self.dictObj.Private.getCompiler(strings, self)
1437			children.append(privComp)
1438			children.extend(privComp.getChildren(strings))
1439		return children
1440
1441
1442class PrivateDictCompiler(DictCompiler):
1443
1444	opcodes = buildOpcodeDict(privateDictOperators)
1445
1446	def setPos(self, pos, endPos):
1447		size = endPos - pos
1448		self.parent.rawDict["Private"] = size, pos
1449		self.pos = pos
1450
1451	def getChildren(self, strings):
1452		children = []
1453		if hasattr(self.dictObj, "Subrs"):
1454			children.append(self.dictObj.Subrs.getCompiler(strings, self))
1455		return children
1456
1457
1458class BaseDict:
1459
1460	def __init__(self, strings=None, file=None, offset=None):
1461		self.rawDict = {}
1462		if DEBUG:
1463			print "loading %s at %s" % (self.__class__.__name__, offset)
1464		self.file = file
1465		self.offset = offset
1466		self.strings = strings
1467		self.skipNames = []
1468
1469	def decompile(self, data):
1470		if DEBUG:
1471			print "    length %s is %s" % (self.__class__.__name__, len(data))
1472		dec = self.decompilerClass(self.strings)
1473		dec.decompile(data)
1474		self.rawDict = dec.getDict()
1475		self.postDecompile()
1476
1477	def postDecompile(self):
1478		pass
1479
1480	def getCompiler(self, strings, parent):
1481		return self.compilerClass(self, strings, parent)
1482
1483	def __getattr__(self, name):
1484		value = self.rawDict.get(name)
1485		if value is None:
1486			value = self.defaults.get(name)
1487		if value is None:
1488			raise AttributeError(name)
1489		conv = self.converters[name]
1490		value = conv.read(self, value)
1491		setattr(self, name, value)
1492		return value
1493
1494	def toXML(self, xmlWriter, progress):
1495		for name in self.order:
1496			if name in self.skipNames:
1497				continue
1498			value = getattr(self, name, None)
1499			if value is None:
1500				continue
1501			conv = self.converters[name]
1502			conv.xmlWrite(xmlWriter, name, value, progress)
1503
1504	def fromXML(self, (name, attrs, content)):
1505		conv = self.converters[name]
1506		value = conv.xmlRead((name, attrs, content), self)
1507		setattr(self, name, value)
1508
1509
1510class TopDict(BaseDict):
1511
1512	defaults = buildDefaults(topDictOperators)
1513	converters = buildConverters(topDictOperators)
1514	order = buildOrder(topDictOperators)
1515	decompilerClass = TopDictDecompiler
1516	compilerClass = TopDictCompiler
1517
1518	def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1519		BaseDict.__init__(self, strings, file, offset)
1520		self.GlobalSubrs = GlobalSubrs
1521
1522	def getGlyphOrder(self):
1523		return self.charset
1524
1525	def postDecompile(self):
1526		offset = self.rawDict.get("CharStrings")
1527		if offset is None:
1528			return
1529		# get the number of glyphs beforehand.
1530		self.file.seek(offset)
1531		self.numGlyphs = readCard16(self.file)
1532
1533	def toXML(self, xmlWriter, progress):
1534		if hasattr(self, "CharStrings"):
1535			self.decompileAllCharStrings(progress)
1536		if hasattr(self, "ROS"):
1537			self.skipNames = ['Encoding']
1538		if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
1539			# these values have default values, but I only want them to show up
1540			# in CID fonts.
1541			self.skipNames = ['CIDFontVersion', 'CIDFontRevision', 'CIDFontType',
1542					'CIDCount']
1543		BaseDict.toXML(self, xmlWriter, progress)
1544
1545	def decompileAllCharStrings(self, progress):
1546		# XXX only when doing ttdump -i?
1547		i = 0
1548		for charString in self.CharStrings.values():
1549			try:
1550				charString.decompile()
1551			except:
1552				print "Error in charstring ", i
1553				import sys
1554				type, value = sys. exc_info()[0:2]
1555				raise type(value)
1556			if not i % 30 and progress:
1557				progress.increment(0)  # update
1558			i = i + 1
1559
1560
1561class FontDict(BaseDict):
1562
1563	defaults = buildDefaults(topDictOperators)
1564	converters = buildConverters(topDictOperators)
1565	order = buildOrder(topDictOperators)
1566	decompilerClass = None
1567	compilerClass = FontDictCompiler
1568
1569	def __init__(self, strings=None, file=None, offset=None, GlobalSubrs=None):
1570		BaseDict.__init__(self, strings, file, offset)
1571		self.GlobalSubrs = GlobalSubrs
1572
1573	def getGlyphOrder(self):
1574		return self.charset
1575
1576	def toXML(self, xmlWriter, progress):
1577		self.skipNames = ['Encoding']
1578		BaseDict.toXML(self, xmlWriter, progress)
1579
1580
1581
1582class PrivateDict(BaseDict):
1583	defaults = buildDefaults(privateDictOperators)
1584	converters = buildConverters(privateDictOperators)
1585	order = buildOrder(privateDictOperators)
1586	decompilerClass = PrivateDictDecompiler
1587	compilerClass = PrivateDictCompiler
1588
1589
1590class IndexedStrings:
1591
1592	"""SID -> string mapping."""
1593
1594	def __init__(self, file=None):
1595		if file is None:
1596			strings = []
1597		else:
1598			strings = list(Index(file))
1599		self.strings = strings
1600
1601	def getCompiler(self):
1602		return IndexedStringsCompiler(self, None, None)
1603
1604	def __len__(self):
1605		return len(self.strings)
1606
1607	def __getitem__(self, SID):
1608		if SID < cffStandardStringCount:
1609			return cffStandardStrings[SID]
1610		else:
1611			return self.strings[SID - cffStandardStringCount]
1612
1613	def getSID(self, s):
1614		if not hasattr(self, "stringMapping"):
1615			self.buildStringMapping()
1616		if s in cffStandardStringMapping:
1617			SID = cffStandardStringMapping[s]
1618		elif s in self.stringMapping:
1619			SID = self.stringMapping[s]
1620		else:
1621			SID = len(self.strings) + cffStandardStringCount
1622			self.strings.append(s)
1623			self.stringMapping[s] = SID
1624		return SID
1625
1626	def getStrings(self):
1627		return self.strings
1628
1629	def buildStringMapping(self):
1630		self.stringMapping = {}
1631		for index in range(len(self.strings)):
1632			self.stringMapping[self.strings[index]] = index + cffStandardStringCount
1633
1634
1635# The 391 Standard Strings as used in the CFF format.
1636# from Adobe Technical None #5176, version 1.0, 18 March 1998
1637
1638cffStandardStrings = ['.notdef', 'space', 'exclam', 'quotedbl', 'numbersign',
1639		'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
1640		'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one',
1641		'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon',
1642		'semicolon', 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C',
1643		'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
1644		'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash',
1645		'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c',
1646		'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1647		's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright',
1648		'asciitilde', 'exclamdown', 'cent', 'sterling', 'fraction', 'yen', 'florin',
1649		'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft',
1650		'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'endash', 'dagger',
1651		'daggerdbl', 'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
1652		'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand',
1653		'questiondown', 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve',
1654		'dotaccent', 'dieresis', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron',
1655		'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
1656		'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior',
1657		'logicalnot', 'mu', 'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn',
1658		'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
1659		'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior',
1660		'copyright', 'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring',
1661		'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave',
1662		'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute',
1663		'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute',
1664		'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron',
1665		'aacute', 'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
1666		'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
1667		'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
1668		'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis', 'ugrave',
1669		'yacute', 'ydieresis', 'zcaron', 'exclamsmall', 'Hungarumlautsmall',
1670		'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall',
1671		'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader',
1672		'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle',
1673		'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle',
1674		'nineoldstyle', 'commasuperior', 'threequartersemdash', 'periodsuperior',
1675		'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
1676		'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
1677		'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'ffi', 'ffl', 'parenleftinferior',
1678		'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
1679		'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall',
1680		'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall',
1681		'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall',
1682		'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
1683		'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall',
1684		'Dieresissmall', 'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
1685		'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall', 'Cedillasmall',
1686		'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths',
1687		'onethird', 'twothirds', 'zerosuperior', 'foursuperior', 'fivesuperior',
1688		'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
1689		'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior',
1690		'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior',
1691		'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
1692		'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
1693		'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall',
1694		'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
1695		'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall',
1696		'Ocircumflexsmall', 'Otildesmall', 'Odieresissmall', 'OEsmall', 'Oslashsmall',
1697		'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
1698		'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000', '001.001', '001.002',
1699		'001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman',
1700		'Semibold'
1701]
1702
1703cffStandardStringCount = 391
1704assert len(cffStandardStrings) == cffStandardStringCount
1705# build reverse mapping
1706cffStandardStringMapping = {}
1707for _i in range(cffStandardStringCount):
1708	cffStandardStringMapping[cffStandardStrings[_i]] = _i
1709
1710cffISOAdobeStrings = [".notdef", "space", "exclam", "quotedbl", "numbersign",
1711"dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
1712"asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
1713"three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
1714"less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G",
1715"H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
1716"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
1717"underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
1718"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1719"braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent",
1720"sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle",
1721"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl",
1722"endash", "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
1723"quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis",
1724"perthousand", "questiondown", "grave", "acute", "circumflex", "tilde",
1725"macron", "breve", "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
1726"ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash", "Oslash", "OE",
1727"ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls",
1728"onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus",
1729"Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn",
1730"threequarters", "twosuperior", "registered", "minus", "eth", "multiply",
1731"threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
1732"Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave",
1733"Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute",
1734"Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute",
1735"Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute",
1736"acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute",
1737"ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis",
1738"igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
1739"scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis",
1740"zcaron"]
1741
1742cffISOAdobeStringCount = 229
1743assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
1744
1745cffIExpertStrings = [".notdef", "space", "exclamsmall", "Hungarumlautsmall",
1746"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
1747"parenleftsuperior", "parenrightsuperior", "twodotenleader", "onedotenleader",
1748"comma", "hyphen", "period", "fraction", "zerooldstyle", "oneoldstyle",
1749"twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
1750"sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon", "semicolon",
1751"commasuperior", "threequartersemdash", "periodsuperior", "questionsmall",
1752"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1753"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1754"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1755"parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
1756"Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall",
1757"Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
1758"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall",
1759"Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall",
1760"exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
1761"Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall",
1762"figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
1763"onequarter", "onehalf", "threequarters", "questiondownsmall", "oneeighth",
1764"threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds",
1765"zerosuperior", "onesuperior", "twosuperior", "threesuperior", "foursuperior",
1766"fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior",
1767"zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior",
1768"fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior",
1769"centinferior", "dollarinferior", "periodinferior", "commainferior",
1770"Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall",
1771"Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall",
1772"Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall",
1773"Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
1774"Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
1775"Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
1776"Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
1777"Ydieresissmall"]
1778
1779cffExpertStringCount = 166
1780assert len(cffIExpertStrings) == cffExpertStringCount
1781
1782cffExpertSubsetStrings = [".notdef", "space", "dollaroldstyle",
1783"dollarsuperior", "parenleftsuperior", "parenrightsuperior", "twodotenleader",
1784"onedotenleader", "comma", "hyphen", "period", "fraction", "zerooldstyle",
1785"oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle",
1786"sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "colon",
1787"semicolon", "commasuperior", "threequartersemdash", "periodsuperior",
1788"asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
1789"lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
1790"tsuperior", "ff", "fi", "fl", "ffi", "ffl", "parenleftinferior",
1791"parenrightinferior", "hyphensuperior", "colonmonetary", "onefitted", "rupiah",
1792"centoldstyle", "figuredash", "hypheninferior", "onequarter", "onehalf",
1793"threequarters", "oneeighth", "threeeighths", "fiveeighths", "seveneighths",
1794"onethird", "twothirds", "zerosuperior", "onesuperior", "twosuperior",
1795"threesuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior",
1796"eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
1797"threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior",
1798"eightinferior", "nineinferior", "centinferior", "dollarinferior",
1799"periodinferior", "commainferior"]
1800
1801cffExpertSubsetStringCount = 87
1802assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
1803