unit afm; interface procedure openafm (var afmin, afmout: text); procedure closeafm (var afmin, afmout: text); procedure getafmchar (var C, WX: integer; name: string; var lx, ly, rx, ry: integer); procedure putafmchar (C, WX: integer; pname: string; lx, ly, rx, ry: integer); procedure opencc (var ccin: text); procedure composeCCs; implementation uses strtype, data, pathunit; function makeint (s: string): integer; var i, c: integer; begin val (s, i, c); makeint := i end; function intstr (i: integer): string; var s: string; begin str (i, s); intstr := s end; var line: strec; procedure readline (var f: text); (* accept DOS, Unix, and Mac line-ends *) var s: string; c: char; begin c := ' '; s := ''; while not eoln (f) and (c <> #$0A) do begin read (f, c); if c <> #$0A then s := s + c end; if eoln (f) then readln (f); line.ini (s) end; type ligpoi = ^ ligature; ligature = record second, ligname: string [30]; nextlig: ligpoi end; charmetrics = record C: integer; WX: integer; N: string [30]; lx, ly, rx, ry: integer; lig: ligpoi; end; charmetricspoi = ^ charmetrics; composespec = record name, name1, name2: string [30]; x1, y1, x2, y2: integer; end; composespecpoi = ^ composespec; const maxmetricsentry = 600; maxcomposeentry = 300; var charentries: array [0..maxmetricsentry] of charmetricspoi; (* 0..255 are encoded entries, those above are unencoded *) composeentries: array [1..maxcomposeentry] of composespecpoi; unencodedindex: integer; afmopen: Boolean; entrycount, composecount: integer; CapHeight, XHeight, Ascender, Descender: Boolean; afmintermediate: text; procedure parseCC (line: strec; fromafm: Boolean); (* parse a line like CC Aacute 2 ; PCC A 0 0 ; PCC acute 183 227 ; *) var composeentry: composespec; begin with composeentry do begin name := line.nexttoken; if line.nexttoken <> '2' then begin if fromafm (* or afmopen ? But then include StartComposites etc. *) then writeln (afmintermediate, line.str); end else begin if fromafm (* or afmopen ? *) then writeln (afmintermediate, line.str); (* or re-generate entries? *) line.gotonexttoken; line.gotonexttoken; name1 := line.nexttoken; x1 := makeint (line.nexttoken); y1 := makeint (line.nexttoken); line.gotonexttoken; line.gotonexttoken; name2 := line.nexttoken; x2 := makeint (line.nexttoken); y2 := makeint (line.nexttoken); composecount := composecount + 1; new (composeentries [composecount]); composeentries [composecount] ^ := composeentry; end end end; procedure openafm (var afmin, afmout: text); var charentry: charmetrics; newmetric: charmetricspoi; afmcmd: string; FontBBoxlx, FontBBoxly, FontBBoxrx, FontBBoxry: integer; i: integer; function getligature: ligpoi; var newlig: ligpoi; begin line.gotonexttoken; if line.nexttoken = 'L' then begin new (newlig); newlig ^. second := line.nexttoken; newlig ^. ligname := line.nexttoken; newlig ^. nextlig := getligature; getligature := newlig end else getligature := nil end; begin afmopen := true; rewrite (afmout); (* set defaults: *) FontBBoxlx := 0; FontBBoxly := 0; FontBBoxrx := 0; FontBBoxry := 0; CapHeight := false; XHeight := false; Ascender := false; Descender := false; for i := 0 to maxmetricsentry do charentries [i] := nil; unencodedindex := 255; readline (afmin); afmcmd := line.firsttoken; while afmcmd <> 'StartCharMetrics' do begin writeln (afmout, line.str); if afmcmd = 'FontBBox' then begin FontBBoxlx := makeint (line.nexttoken); FontBBoxly := makeint (line.nexttoken); FontBBoxrx := makeint (line.nexttoken); FontBBoxry := makeint (line.nexttoken); end else if afmcmd = 'CapHeight' then CapHeight := true else if afmcmd = 'XHeight' then XHeight := true else if afmcmd = 'Ascender' then Ascender := true else if afmcmd = 'Descender' then Descender := true ; readline (afmin); afmcmd := line.firsttoken end; readline (afmin); while line.nexttoken = 'C' do with charentry do begin C := makeint (line.nexttoken); line.gotonexttoken; line.gotonexttoken; WX := makeint (line.nexttoken); line.gotonexttoken; line.gotonexttoken; N := line.nexttoken; if N <> ';' (* name may be missing, e.g. with wfnboss's files *) then begin if stdenc then C := getSEcode ('/' + N); entrycount := entrycount + 1; line.gotonexttoken; if line.nexttoken = 'B' then begin lx := makeint (line.nexttoken); ly := makeint (line.nexttoken); rx := makeint (line.nexttoken); ry := makeint (line.nexttoken); end else begin lx := FontBBoxlx; ly := FontBBoxly; rx := FontBBoxrx; ry := FontBBoxry; end; lig := getligature; if C = 0 then C := -1 (* makepfm doesn't like 0 *); new (newmetric); newmetric^ := charentry; if C >= 0 then charentries [C] := newmetric else begin unencodedindex := unencodedindex + 1; charentries [unencodedindex] := newmetric; end; end; readline (afmin); end; makefilebuffer (afmintermediate, 'afminter.med'); repeat writeln (afmintermediate, line.str); if not eof (afmin) then readline (afmin); until eof (afmin) or (line.firsttoken = 'CC'); if not eof (afmin) then while line.firsttoken = 'CC' do begin parseCC (line, true); readline (afmin); end; end; procedure opencc (var ccin: text); var s: string; ccline: strec; begin while not eof (ccin) do begin readln (ccin, s); ccline.ini (s); if ccline.firsttoken = 'CC' then parseCC (ccline, false); end end; procedure closeafm (var afmin, afmout: text); var c: char; i: integer; procedure writelig (lp: ligpoi); begin if lp <> nil then begin write (afmout, ' L ', lp^.second, ' ', lp^.ligname, ' ;'); writelig (lp^.nextlig) end end; begin if not CapHeight then if charentries [ord ('H')] <> nil then writeln (afmout, 'CapHeight ', charentries [ord ('H')]^.ry); if not XHeight then if charentries [ord ('x')] <> nil then writeln (afmout, 'XHeight ', charentries [ord ('x')]^.ry); if not Ascender then if charentries [ord ('d')] <> nil then writeln (afmout, 'Ascender ', charentries [ord ('d')]^.ry); if not Descender then if charentries [ord ('p')] <> nil then writeln (afmout, 'Descender ', charentries [ord ('p')]^.ly); writeln (afmout, 'StartCharMetrics ', entrycount); for i := 0 to maxmetricsentry do if charentries [i] <> nil then with charentries [i]^ do begin write (afmout, 'C ', C, ' ; WX ', WX, ' ; N ', N, ' ; B ', lx, ' ', ly, ' ', rx, ' ', ry, ' ;'); writelig (lig); if (N = 'f') and (lig = nil) then begin if getCharString ('/fi') <> nil then write (afmout, ' L i fi ;'); if getCharString ('/fl') <> nil then write (afmout, ' L l fl ;'); end; writeln (afmout) end; getfilebuffer (afmintermediate, afmout); writeln (afmout, line.str); while not eof (afmin) do begin read (afmin, c); write (afmout, c); end; close (afmin); close (afmout); end; function purename (s: string): string; begin if pos ('/', s) = 1 then purename := copy (s, 2, length (s) - 1) else purename := s end; procedure composeCCs; var i: integer; cc1, cc2: integer; CS1, CS2: CharStrpointer; pname, pname1, pname2: string [30]; begin for i := 1 to composecount do with composeentries [i] ^ do begin pname := '/' + name; pname1 := '/' + name1; pname2 := '/' + name2; if getCharString (pname) = nil then if (x1 = 0) and (y1 = 0) then begin cc1 := getSEcode (pname1); cc2 := getSEcode (pname2); CS1 := getCharString (pname1); CS2 := getCharString (pname2); if (cc1 >= 0) and (cc2 >= 0) and (CS1 <> nil) and (CS2 <> nil) then begin CharStringscount := CharStringscount + 1; new (CharStrings [CharStringscount], ini); CharStrings [CharStringscount] ^ . setname (pname); CharStrings [CharStringscount] ^ . compose (CS1, CS2, cc1, cc2, x2, y2) end end else writeln ('% cannot compose with first char offset') end end; procedure getafmchar (var C, WX: integer; name: string; var lx, ly, rx, ry: integer); var i: integer; N: string; begin N := purename (name); C := -2; (* error indication if not found *) WX := 0; lx := 0; ly := 0; rx := 0; ry := 0; if afmopen then begin for i := 0 to maxmetricsentry do if charentries [i] <> nil then if charentries [i]^.N = N then begin C := charentries [i]^.C; WX := charentries [i]^.WX; lx := charentries [i]^.lx; ly := charentries [i]^.ly; rx := charentries [i]^.rx; ry := charentries [i]^.ry; end end end; procedure putafmchar (C, WX: integer; pname: string; lx, ly, rx, ry: integer); var index: integer; name: string [30]; found: Boolean; begin name := purename (pname); if afmopen then begin if C >= 0 then if charentries [C] = nil then index := C else if charentries [C]^.N = name then index := C else C := -1; if C < 0 then begin index := 255; (* last encoded index *) found := false; repeat index := index + 1; if index > unencodedindex then begin found := true; unencodedindex := index; end else if charentries [index] <> nil then if charentries [index]^.N = name then found := true until found end; if charentries [index] = nil then begin entrycount := entrycount + 1; new (charentries [index]); charentries [index]^.N := name; charentries [index]^.lig := nil; end; charentries [index]^.C := C; charentries [index]^.WX := WX; charentries [index]^.lx := lx; charentries [index]^.ly := ly; charentries [index]^.rx := rx; charentries [index]^.ry := ry; end end; begin afmopen := false; ccopen := false; entrycount := 0; composecount := 0; end.