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