afmLib.py revision 7842e56b97ce677b83bdab09cda48bc2d89ac75a
17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""Module for reading and writing AFM files."""
27842e56b97ce677b83bdab09cda48bc2d89ac75aJust
37842e56b97ce677b83bdab09cda48bc2d89ac75aJust# XXX reads AFM's generated by Fog, not tested with much else.
47842e56b97ce677b83bdab09cda48bc2d89ac75aJust# It does not implement the full spec (Adobe Technote 5004, Adobe Font Metrics
57842e56b97ce677b83bdab09cda48bc2d89ac75aJust# File Format Specification). Still, it should read most "common" AFM files.
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust
77842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport re
87842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string
97842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport types
107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust__version__ = "$Id: afmLib.py,v 1.1 1999-12-16 21:34:51 Just Exp $"
127842e56b97ce677b83bdab09cda48bc2d89ac75aJust
137842e56b97ce677b83bdab09cda48bc2d89ac75aJust
147842e56b97ce677b83bdab09cda48bc2d89ac75aJust# every single line starts with a "word"
157842e56b97ce677b83bdab09cda48bc2d89ac75aJustidentifierRE = re.compile("^([A-Za-z]+).*")
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust
177842e56b97ce677b83bdab09cda48bc2d89ac75aJust# regular expression to parse char lines
187842e56b97ce677b83bdab09cda48bc2d89ac75aJustcharRE = re.compile(
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# charnum
207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s*;\s*WX\s+"		# ; WX
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(\d+)"			# width
227842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s*;\s*N\s+"		# ; N
237842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(\.?[A-Za-z0-9_]+)"	# charname
247842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s*;\s*B\s+"		# ; B
257842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# left
267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s+"				#
277842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# bottom
287842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s+"				#
297842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# right
307842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s+"				#
317842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# top
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s*;\s*"			# ;
337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		)
347842e56b97ce677b83bdab09cda48bc2d89ac75aJust
357842e56b97ce677b83bdab09cda48bc2d89ac75aJust# regular expression to parse kerning lines
367842e56b97ce677b83bdab09cda48bc2d89ac75aJustkernRE = re.compile(
377842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"([.A-Za-z0-9_]+)"	# leftchar
387842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s+"				#
397842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"([.A-Za-z0-9_]+)"	# rightchar
407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s+"				#
417842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"(-?\d+)"			# value
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust		"\s*"				#
437842e56b97ce677b83bdab09cda48bc2d89ac75aJust		)
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
457842e56b97ce677b83bdab09cda48bc2d89ac75aJusterror = "AFM.error"
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust
477842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass AFM:
487842e56b97ce677b83bdab09cda48bc2d89ac75aJust
497842e56b97ce677b83bdab09cda48bc2d89ac75aJust	_keywords = ['StartFontMetrics',
507842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'EndFontMetrics',
517842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'StartCharMetrics',
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'EndCharMetrics',
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'StartKernData',
547842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'StartKernPairs',
557842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'EndKernPairs',
567842e56b97ce677b83bdab09cda48bc2d89ac75aJust			'EndKernData', ]
577842e56b97ce677b83bdab09cda48bc2d89ac75aJust
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __init__(self, path = None):
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._attrs = {}
607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._chars = {}
617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._kerning = {}
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._index = {}
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._comments = []
647842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if path is not None:
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.read(path)
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def read(self, path):
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines = readlines(path)
697842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for line in lines:
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if not string.strip(line):
717842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust			m = identifierRE.match(line)
737842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if m is None:
747842e56b97ce677b83bdab09cda48bc2d89ac75aJust				raise error, "syntax error in AFM file: " + `line`
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust			pos = m.regs[1][1]
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust			word = line[:pos]
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust			rest = string.strip(line[pos:])
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if word in self._keywords:
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust				continue
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if word == 'C':
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.parsechar(rest)
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust			elif word == "KPX":
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.parsekernpair(rest)
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self.parseattr(word, rest)
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def parsechar(self, rest):
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		m = charRE.match(rest)
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if m is None:
917842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise error, "syntax error in AFM file: " + `rest`
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		things = []
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for fr, to in m.regs[1:]:
947842e56b97ce677b83bdab09cda48bc2d89ac75aJust			things.append(rest[fr:to])
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust		charname = things[2]
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust		del things[2]
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust		charnum, width, l, b, r, t = map(string.atoi, things)
987842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._chars[charname] = charnum, width, (l, b, r, t)
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def parsekernpair(self, rest):
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		m = kernRE.match(rest)
1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if m is None:
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise error, "syntax error in AFM file: " + `rest`
1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust		things = []
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for fr, to in m.regs[1:]:
1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust			things.append(rest[fr:to])
1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust		leftchar, rightchar, value = things
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust		value = string.atoi(value)
1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self._kerning[(leftchar, rightchar)] = value
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1117842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def parseattr(self, word, rest):
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if word == "FontBBox":
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust			l, b, r, t = map(string.atoi, string.split(rest))
1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._attrs[word] = l, b, r, t
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif word == "Comment":
1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._comments.append(rest)
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust			try:
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust				value = string.atoi(rest)
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust			except (ValueError, OverflowError):
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self._attrs[word] = rest
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJust				self._attrs[word] = value
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def write(self, path, sep = '\r'):
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust		import time
1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines = [	"StartFontMetrics 2.0",
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJust				"Comment Generated by afmLib, version %s; at %s" %
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust						(string.split(__version__)[2],
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust						time.strftime("%m/%d/%Y %H:%M:%S",
1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust						time.localtime(time.time())))]
1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# write attributes
1347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items = self._attrs.items()
1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items.sort()		# XXX proper ordering???
1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for attr, value in items:
1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if attr == "FontBBox":
1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust				value = string.join(map(str, value), " ")
1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust			lines.append(attr + " " + str(value))
1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# write char metrics
1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("StartCharMetrics " + `len(self._chars)`)
1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items = map(lambda (charname, (charnum, width, box)):
1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust			(charnum, (charname, width, box)),
1457842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._chars.items())
1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust		def myCmp(a, b):
1487842e56b97ce677b83bdab09cda48bc2d89ac75aJust			"""Custom compare function to make sure unencoded chars (-1)
1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust			end up at the end of the list after sorting."""
1507842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if a[0] == -1:
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust				a = (0xffff,) + a[1:]  # 0xffff is an arbitrary large number
1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if b[0] == -1:
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust				b = (0xffff,) + b[1:]
1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return cmp(a, b)
1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items.sort(myCmp)
1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for charnum, (charname, width, (l, b, r, t)) in items:
1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust			lines.append("C %d ; WX %d ; N %s ; B %d %d %d %d ;" %
1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust					(charnum, width, charname, l, b, r, t))
1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("EndCharMetrics")
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# write kerning info
1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("StartKernData")
1647842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("StartKernPairs " + `len(self._kerning)`)
1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items = self._kerning.items()
1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		items.sort()		# XXX is order important?
1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for (leftchar, rightchar), value in items:
1687842e56b97ce677b83bdab09cda48bc2d89ac75aJust			lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("EndKernPairs")
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("EndKernData")
1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines.append("EndFontMetrics")
1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust		writelines(path, lines, sep)
1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1767842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def has_kernpair(self, pair):
1777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self._kerning.has_key(pair)
1787842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1797842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def kernpairs(self):
1807842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self._kerning.keys()
1817842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1827842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def has_char(self, char):
1837842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self._chars.has_key(char)
1847842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1857842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def chars(self):
1867842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self._chars.keys()
1877842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1887842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def comments(self):
1897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return self._comments
1907842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1917842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __getattr__(self, attr):
1927842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self._attrs.has_key(attr):
1937842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return self._attrs[attr]
1947842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1957842e56b97ce677b83bdab09cda48bc2d89ac75aJust			raise AttributeError, attr
1967842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1977842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __setattr__(self, attr, value):
1987842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# all attrs *not* starting with "_" are consider to be AFM keywords
1997842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if attr[:1] == "_":
2007842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.__dict__[attr] = value
2017842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
2027842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self._attrs[attr] = value
2037842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2047842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __getitem__(self, key):
2057842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if type(key) == types.TupleType:
2067842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# key is a tuple, return the kernpair
2077842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self._kerning.has_key(key):
2087842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return self._kerning[key]
2097842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2107842e56b97ce677b83bdab09cda48bc2d89ac75aJust				raise KeyError, "no kerning pair: " + str(key)
2117842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
2127842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# return the metrics instead
2137842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if self._chars.has_key(key):
2147842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return self._chars[key]
2157842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
2167842e56b97ce677b83bdab09cda48bc2d89ac75aJust				raise KeyError, "metrics index " + str(key) + " out of range"
2177842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2187842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def __repr__(self):
2197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if hasattr(self, "FullName"):
2207842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return '<AFM object for %s>' % self.FullName
2217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
2227842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return '<AFM object at %x>' % id(self)
2237842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2247842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2257842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef readlines(path):
2267842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f = open(path, 'rb')
2277842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = f.read()
2287842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f.close()
2297842e56b97ce677b83bdab09cda48bc2d89ac75aJust	# read any text file, regardless whether it's formatted for Mac, Unix or Dos
2307842e56b97ce677b83bdab09cda48bc2d89ac75aJust	sep = ""
2317842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if '\r' in data:
2327842e56b97ce677b83bdab09cda48bc2d89ac75aJust		sep = sep + '\r'	# mac or dos
2337842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if '\n' in data:
2347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		sep = sep + '\n'	# unix or dos
2357842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return string.split(data, sep)
2367842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2377842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef writelines(path, lines, sep = '\r'):
2387842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f = open(path, 'wb')
2397842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for line in lines:
2407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		f.write(line + sep)
2417842e56b97ce677b83bdab09cda48bc2d89ac75aJust	f.close()
2427842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2437842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
2457842e56b97ce677b83bdab09cda48bc2d89ac75aJustif __name__ == "__main__":
2467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import macfs
2477842e56b97ce677b83bdab09cda48bc2d89ac75aJust	fss, ok = macfs.StandardGetFile('TEXT')
2487842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if ok:
2497842e56b97ce677b83bdab09cda48bc2d89ac75aJust		path = fss.as_pathname()
2507842e56b97ce677b83bdab09cda48bc2d89ac75aJust		afm = AFM(path)
2517842e56b97ce677b83bdab09cda48bc2d89ac75aJust		char = 'A'
2527842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if afm.has_char(char):
2537842e56b97ce677b83bdab09cda48bc2d89ac75aJust			print afm[char]	# print charnum, width and boundingbox
2547842e56b97ce677b83bdab09cda48bc2d89ac75aJust		pair = ('A', 'V')
2557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if afm.has_kernpair(pair):
2567842e56b97ce677b83bdab09cda48bc2d89ac75aJust			print afm[pair]	# print kerning value for pair
2577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print afm.Version	# various other afm entries have become attributes
2587842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print afm.Weight
2597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		# afm.comments() returns a list of all Comment lines found in the AFM
2607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print afm.comments()
2617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		#print afm.chars()
2627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		#print afm.kernpairs()
2637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		print afm
2647842e56b97ce677b83bdab09cda48bc2d89ac75aJust		afm.write(path + ".xxx")
2657842e56b97ce677b83bdab09cda48bc2d89ac75aJust
266