#!/usr/pkg/bin/python2.2 # combine zefflores and zeicbl into one virtual TeX font # (could/should be generalised for others) import string class PLLexer: # rough-n-ready intake for font propety-list files def __init__(self, file): self.currlex = None self.char = None self.file=file # get the next lexeme def lexeme(self): self._fill() x = self.currlex self.currlex = None return x # peek at the next lexeme (but don't advance) def peek(self): self._fill() return self.currlex def peekbyte(self): if not self.char: self.char = self.file.read(1) if ""==self.char: self.char=None return self.char def byte(self): x = self.peekbyte(); self.char=None return x # internal def _fill(self): if not self.currlex: self.currlex=self._lexeme() # internal def _lexeme(self): while 1: x = self.byte() if not x: return None if x not in string.whitespace: break if x in ")(": return x if x in "+-0123456789.": while self.peekbyte() in "0123456789.": x=x+self.byte() else: while self.peekbyte() not in (")("+string.whitespace): x=x+self.byte() return x def readexpr(self): x = self.lexeme() if not x: return None if "("==x: y = list(); while ")"!=self.peek(): y.append(self.readexpr()) self.lexeme() return y lx=string.lower(x) z = None try: # i want to downcase keywords and reduce the numeric # codes; but something that looks like a numeric intro # might not actually be one... try it and see if lx=="c" and self.peek() not in ")(": z=ord(self.peek()) if lx=="d": z=int(self.peek(),10) if lx=="o": z=int(self.peek(),8) if lx=="r": z=float(self.peek()) except: pass if type(None)!=type(z): # it worked out as a numeric intro, # so return the number and eat its token lx = z self.lexeme() return lx class PLFont: all = range(0,256) dims = ["slant","space","stretch","shrink","xheight","quad"] def __init__(self): self.font = list() # imported fonts self.slot = list() # character data self.metrics = {} # dimension data for i in PLFont.all: self.slot.append(PLCharacter()) # copy of character c from font n def take(self, f, c): x = PLCharacter(self.font[f].slot[c]) x.mapto=(f,c) return x # read the file stream as a TeX VPL def read(self, file): lex = PLLexer(file) while 1: la = lex.readexpr() if not la: break if "ligtable"==la[0]: ch = None for lb in la[1:]: if "label"==lb[0]: ch = self.slot[lb[1]] if "krn"==lb[0] or "lig"==lb[0]: ch.liquor.append((lb[1],lb[2])) if "stop"==lb[0]: ch = None elif "character"==la[0]: ch = self.slot[la[1]] for lb in la[2:]: if "charwd"==lb[0]: ch.width=lb[1] if "charht"==lb[0]: ch.height=lb[1] if "chardp"==lb[0]: ch.depth=lb[1] elif "fontdimen"==la[0]: for lb in la[1:]: if lb[0] in PLFont.dims and type(lb[1])==type(0.): self.metrics[lb[0]]=lb[1] else: pass # return a tring of TeX VPL def output(self): str="(comment VPL for `eiffle' by Kwantus' Python pgm)" str+="\n(fontdimen" for i in self.metrics.keys(): str+="\n (%s r %f)"%(i,self.metrics[i]) str+=")" for i in range(0,len(self.font)): str+="\n(mapfont d "+`i` str+="\n (fontname "+self.font[i].name+")" str+=")" # ligature/kerning info str+="\n(ligtable" for ch in self.slot: str+=ch.liquorout() str+=")" # collect character info for ch in self.slot: str+=ch.output() return str # handy tightening agent; give it a string and list of numbers def tighten(font, str, *lst): for i in range(0,len(lst)): font.slot[ord(str[i])].tighten(ord(str[i+1]),lst[i]) class PLCharacter: def __init__(self, x=None): if type(x)==type(self): self.width=x.width self.height=x.height self.depth=x.depth self.shift=x.shift self.liquor=x.liquor[:] else: self.liquor=list() # kerning/ligature info self.width=0 # origin increment self.height=0 # amount glyph rises above baseline self.depth=0 # amount glyph descends below baseline self.shift=(0,0) # amount to shift glyph (right, up) # self.mapto=(0,0) # font & slot from which to draw glyph # self.slot # slot the character occupies (slippery but improves much) def movedown(self, r): self.height-=r self.depth+=r self.shift = (self.shift[0],self.shift[1]-r) return self # reduce the kerning def tighten(self, n, r): c=-1 L=len(self.liquor) for i in range(0,L): (c,x)=self.liquor[i] if c==n: break if c!=n: c=n x=0. self.liquor.append(None) i=L if type(x)!=type(0.): error("was a ligature") x-=r self.liquor[i]=(c,x) def liquorout(ch): lk = ch.liquor if lk: str="\n (label d "+`ch.slot`+")" for (c,x) in lk: str+=((type(x)==type(0.) and "\n (krn d %d r %f)") or "\n (lig d %d d %d)")%(c,x) str+="\n (stop)" else: str="" return str def output(self): x="\n(character d "+`self.slot` x+="\n (charwd r %f)"%self.width x+="\n (charht r %f)"%self.height if self.depth>0: x+="\n (chardp r %f)"%self.depth x+="\n (map\n (selectfont d %d)"%self.mapto[0] if self.shift[1]>0: x+="\n (moveup r %f)"%self.shift[1] if self.shift[1]<0: x+="\n (movedown r %f)"%-self.shift[1] x+="\n (setchar d %d))"%self.mapto[1] x+=")" return x vf = PLFont() # import zeffloresce for i in ["zefflores","zeicbl"]: x=PLFont() f=file(i+".pl") x.read(f) x.name=i f.close() vf.font.append(x) # copy `default' for i in PLFont.all: vf.slot[i]=vf.take(0,i) vf.slot[i].slot=i vf.metrics=vf.font[0].metrics.copy() # replace the caps caps=range(ord("A"),ord("Z")+1) for i in caps: vf.slot[i] = vf.take(1,i).movedown(.07) vf.slot[i].width -= .05 vf.slot[i].slot = i # these need a bit more fiddling vf.slot[ord("I")].movedown(.018) vf.slot[ord("M")].movedown(.012) vf.slot[ord("O")].movedown(.015) vf.slot[ord("L")].movedown(.021) vf.slot[ord("D")].movedown(-.02) vf.slot[ord("X")].movedown(.01) vf.slot[ord("Z")].movedown(.015) # delete inter-face kerns and ligatures #fixme: i'm knowing too much about the data structures here, # add some abstractions to hide this #fixme: i'm not happy, overall, with a `pairlist' for liquor # i need either to abstract pairlists or use a dictionary or other mapping type for i in PLFont.all: kn = list() f = i in caps for j in vf.slot[i].liquor: if f==(j[0] in caps): kn.append(j) vf.slot[i].liquor=kn # tighten some kerns # (some of these need to be advanced to zefflores) vf.tighten("ted", .06, .05) vf.tighten("rch", .039, .03) vf.tighten("Ba", .04) vf.tighten("Xa", .03) vf.tighten("vavecce", .04, .02, .04, .03, .05, .04) vf.tighten("fawa", .07, .02, .04) vf.tighten("fo", .06) vf.tighten("fe", .08) vf.tighten("co", .03) for i in ["b", .03, "o", .02, "e", .02, "p", .02, "r", .12, "f", .07, "v", .08, "w", .08, "y", .08]: if type(i)==type(0.): vf.tighten(x+".", i) vf.tighten(x+",", i) else: x=i # a rarity: these were too close vf.tighten("ib", -.01) vf.tighten("ir", -.01) vf.tighten("mu", -.007) vf.tighten("Or", -.012) # write the VPL print vf.output() #eof
Hosted by www.Geocities.ws