- added support for composite info

- write attributes in a decent order


git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@132 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
Just 2001-04-30 14:40:17 +00:00
parent 344757f42d
commit 32f868492c

View File

@ -8,7 +8,7 @@ import re
import string import string
import types import types
__version__ = "$Id: afmLib.py,v 1.1 1999-12-16 21:34:51 Just Exp $" __version__ = "$Id: afmLib.py,v 1.2 2001-04-30 14:40:17 Just Exp $"
# every single line starts with a "word" # every single line starts with a "word"
@ -18,7 +18,7 @@ identifierRE = re.compile("^([A-Za-z]+).*")
charRE = re.compile( charRE = re.compile(
"(-?\d+)" # charnum "(-?\d+)" # charnum
"\s*;\s*WX\s+" # ; WX "\s*;\s*WX\s+" # ; WX
"(\d+)" # width "(\d+)" # width
"\s*;\s*N\s+" # ; N "\s*;\s*N\s+" # ; N
"(\.?[A-Za-z0-9_]+)" # charname "(\.?[A-Za-z0-9_]+)" # charname
"\s*;\s*B\s+" # ; B "\s*;\s*B\s+" # ; B
@ -42,7 +42,46 @@ kernRE = re.compile(
"\s*" # "\s*" #
) )
error = "AFM.error" # regular expressions to parse composite info lines of the form:
# Aacute 2 ; PCC A 0 0 ; PCC acute 182 211 ;
compositeRE = re.compile(
"([.A-Za-z0-9_]+)" # char name
"\s+" #
"(\d+)" # number of parts
"\s*;\s*" #
)
componentRE = re.compile(
"PCC\s+" # PPC
"([.A-Za-z0-9_]+)" # base char name
"\s+" #
"(-?\d+)" # x offset
"\s+" #
"(-?\d+)" # y offset
"\s*;\s*" #
)
preferredAttributeOrder = [
"FontName",
"FullName",
"FamilyName",
"Weight",
"ItalicAngle",
"IsFixedPitch",
"FontBBox",
"UnderlinePosition",
"UnderlineThickness",
"Version",
"Notice",
"EncodingScheme",
"CapHeight",
"XHeight",
"Ascender",
"Descender",
]
class error(Exception): pass
class AFM: class AFM:
@ -53,14 +92,18 @@ class AFM:
'StartKernData', 'StartKernData',
'StartKernPairs', 'StartKernPairs',
'EndKernPairs', 'EndKernPairs',
'EndKernData', ] 'EndKernData',
'StartComposites',
'EndComposites',
]
def __init__(self, path = None): def __init__(self, path=None):
self._attrs = {} self._attrs = {}
self._chars = {} self._chars = {}
self._kerning = {} self._kerning = {}
self._index = {} self._index = {}
self._comments = [] self._comments = []
self._composites = {}
if path is not None: if path is not None:
self.read(path) self.read(path)
@ -78,10 +121,12 @@ class AFM:
rest = string.strip(line[pos:]) rest = string.strip(line[pos:])
if word in self._keywords: if word in self._keywords:
continue continue
if word == 'C': if word == "C":
self.parsechar(rest) self.parsechar(rest)
elif word == "KPX": elif word == "KPX":
self.parsekernpair(rest) self.parsekernpair(rest)
elif word == "CC":
self.parsecomposite(rest)
else: else:
self.parseattr(word, rest) self.parseattr(word, rest)
@ -122,6 +167,28 @@ class AFM:
else: else:
self._attrs[word] = value self._attrs[word] = value
def parsecomposite(self, rest):
m = compositeRE.match(rest)
if m is None:
raise error, "syntax error in AFM file: " + `rest`
charname = m.group(1)
ncomponents = int(m.group(2))
rest = rest[m.regs[0][1]:]
components = []
while 1:
m = componentRE.match(rest)
if m is None:
raise error, "syntax error in AFM file: " + `rest`
basechar = m.group(1)
xoffset = int(m.group(2))
yoffset = int(m.group(3))
components.append((basechar, xoffset, yoffset))
rest = rest[m.regs[0][1]:]
if not rest:
break
assert len(components) == ncomponents
self._composites[charname] = components
def write(self, path, sep = '\r'): def write(self, path, sep = '\r'):
import time import time
lines = [ "StartFontMetrics 2.0", lines = [ "StartFontMetrics 2.0",
@ -130,12 +197,27 @@ class AFM:
time.strftime("%m/%d/%Y %H:%M:%S", time.strftime("%m/%d/%Y %H:%M:%S",
time.localtime(time.time())))] time.localtime(time.time())))]
# write attributes # write comments, assuming (possibly wrongly!) they should
items = self._attrs.items() # all appear at the top
items.sort() # XXX proper ordering??? for comment in self._comments:
lines.append("Comment " + comment)
# write attributes, first the ones we know about, in
# a preferred order
attrs = self._attrs
for attr in preferredAttributeOrder:
if attrs.has_key(attr):
value = attrs[attr]
if attr == "FontBBox":
value = "%s %s %s %s" % value
lines.append(attr + " " + str(value))
# then write the attributes we don't know about,
# in alphabetical order
items = attrs.items()
items.sort()
for attr, value in items: for attr, value in items:
if attr == "FontBBox": if attr in preferredAttributeOrder:
value = string.join(map(str, value), " ") continue
lines.append(attr + " " + str(value)) lines.append(attr + " " + str(value))
# write char metrics # write char metrics
@ -166,9 +248,20 @@ class AFM:
items.sort() # XXX is order important? items.sort() # XXX is order important?
for (leftchar, rightchar), value in items: for (leftchar, rightchar), value in items:
lines.append("KPX %s %s %d" % (leftchar, rightchar, value)) lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
lines.append("EndKernPairs") lines.append("EndKernPairs")
lines.append("EndKernData") lines.append("EndKernData")
if self._composites:
composites = self._composites.items()
composites.sort()
lines.append("StartComposites %s" % len(self._composites))
for charname, components in composites:
line = "CC %s %s ;" % (charname, len(components))
for basechar, xoffset, yoffset in components:
line = line + " PCC %s %s %s ;" % (basechar, xoffset, yoffset)
lines.append(line)
lines.append("EndComposites")
lines.append("EndFontMetrics") lines.append("EndFontMetrics")
writelines(path, lines, sep) writelines(path, lines, sep)
@ -234,7 +327,7 @@ def readlines(path):
sep = sep + '\n' # unix or dos sep = sep + '\n' # unix or dos
return string.split(data, sep) return string.split(data, sep)
def writelines(path, lines, sep = '\r'): def writelines(path, lines, sep='\r'):
f = open(path, 'wb') f = open(path, 'wb')
for line in lines: for line in lines:
f.write(line + sep) f.write(line + sep)
@ -261,5 +354,5 @@ if __name__ == "__main__":
#print afm.chars() #print afm.chars()
#print afm.kernpairs() #print afm.kernpairs()
print afm print afm
afm.write(path + ".xxx") afm.write(path + ".muck")