2 % This is a PostScript program for making an AFM file from
3 % PFB / PFA and (optionally) PFM files.
5 % Written in BOP s.c., Gda\'nsk, Poland
6 % e-mail contact: B.Jackowski@GUST.ORG.PL
7 % version 0.5 (18 XII 1997)
8 % version 0.55 (11 III 1998) -- unlimited number of chars in a font
9 % version 1.00 (27 III 1998) -- scanning PFM subdirectory added,
10 % code improved; version sent to LPD
11 % version 1.01 (1 II 2000) -- message changed
14 % gs [-dNODISPLAY] -- pf2afm.ps disk_font_name
16 % The result is written to the file disk_font_name.afm, provided such
17 % a file does not exist; otherwise program quits.
19 % The font can be either *.pfa or *.pfb; if no extension is supplied,
20 % first disk_font_name.pfb is examined, then disk_font_name.pfa.
21 % Moreover, if there is a *.pfm file in the same directory or in the
22 % subdirectory PFM, i.e., disk_font_name.pfm or PFM/disk_font_name.pfm,
23 % kern pairs from it are extracted, as well as additional font
24 % parameters, usually absent from Type 1 fonts.
27 % $Id: pf2afm.ps,v 1.8 2005/08/24 17:04:21 raph Exp $
28 % The program is based on James Clark's <jjc@jclark.uucp> printafm.ps
29 % (with alterations by d.love@dl.ac.uk and L. Peter Deutsch) from
30 % Ghostscript 5.10 distribution.
34 /WinAnsiEncoding dup /Encoding findresource def
36 % charnumber print-charname -
37 % prints the name of the encoded character
44 exch get =string cvs dup
48 print.to.ofi ( ) print.to.ofi
51 /printquit {print flush quit} def
53 % redirecting GS output to ``ofi'' file
55 /=only.to.ofi {ofi exch write=only} def % replaces GS's `=only'
56 /print.to.ofi {ofi exch writestring} def % replaces `print'
57 /=to.ofi { =only.to.ofi eolch print.to.ofi } def % replaces `='
59 % read and skip: byte, short, word, double and long
60 /readb-p {currPFMfile read not {(Unexpected EOF\n) printquit} if} def
61 /readw-p {readb-p readb-p 256 mul add} def
62 /reads-p {readw-p dup 32768 ge {65536 sub} if} def
63 /readd-p {readb-p readb-p readb-p readb-p 256 mul add 256 mul add 256 mul add} def
64 /readl-p /readd-p load def % double word is, in fact, long integer in GS
65 /skipb-p {readb-p pop} def
66 /skipw-p {skipb-p skipb-p} def
67 /skips-p /skipw-p load def
68 /skipd-p {skipb-p skipb-p skipb-p skipb-p} def
69 /skipl-p /skipd-p load def
70 /skipa-p { {skipb-p} repeat} def
72 % PFMfile readPFMheader -
73 % defines currPFMfile, PFMExtMetricOffset, PFMPairKernTableOffset
76 currPFMfile bytesavailable
80 skipw-p % PFM: version
81 readd-p % PFM: size (size is dword, not word as the documentation says)
82 ne {(Wrong file size\n) printquit} if
83 60 skipa-p % PFM: copyright
86 skipw-p % PFM: VertRes
87 skipw-p % PFM: HorizRes
89 skipw-p % PFM: InternalLeading
90 skipw-p % PFM: ExternalLeading
92 skipb-p % PFM: Underline
93 skipb-p % PFM: Stikeout
95 readb-p % PFM: CharSet
97 skipw-p % PFM: PixWidth
98 skipw-p % PFM: PixHeight
99 skipb-p % PFM: PitchAndFamily
100 skipw-p % PFM: AvgWidth
101 skipw-p % PFM: MaxWidth
102 skipb-p % PFM: FirstChar
103 skipb-p % PFM: LastChar
104 skipb-p % PFM: DefaultChar
105 skipb-p % PFM: BreakChar
106 skipw-p % PFM: WidthBytes
107 skipd-p % PFM: Device
109 skipd-p % PFM: BitsPointer
110 skipd-p % PFM: BitsOffset
111 % here we assume that it is a PostScript font, i.e., it always uses
112 % the extended width table, therefore the normal width table is empty
116 skipw-p % PFMEX: SizeFields
117 readd-p % PFMEX: ExtMetricOffset
118 /PFMExtMetricOffset edef
119 skipd-p % PFMEX: ExtentTable
120 skipd-p % PFMEX: OriginTable
121 readd-p % PFMEX: PairKernTable
122 /PFMPairKernTableOffset edef
123 skipd-p % PFMEX: TrackKernTable
124 skipd-p % PFMEX: DriverInfo
125 skipd-p % PFMEX: Reserved
128 % requires that currPFMfile, PFMExtMetricOffset are defined
130 % defines PFMNumberofKernPairs
133 currPFMfile PFMExtMetricOffset setfileposition
135 skips-p % EXTT: PointSize
136 skips-p % EXTT: Orientation
137 skips-p % EXTT: MasterHeight
138 skips-p % EXTT: MinScale
139 skips-p % EXTT: MaxScale
140 skips-p % EXTT: MasterUnit
141 reads-p % EXTT: CapHeight
143 reads-p % EXTT: XHeight
145 reads-p % EXTT: LowerCaseAscent
146 /PFMLowerCaseAscent edef
147 reads-p % EXTT: LowerCaseDescent
148 neg /PFMLowerCaseDescent edef
149 skips-p % EXTT: Slant
150 skips-p % EXTT: SuperScript
151 skips-p % EXTT: SubScript
152 skips-p % EXTT: SuperScriptSize
153 skips-p % EXTT: SubScriptSize
154 skips-p % EXTT: UnderlineOffset
155 skips-p % EXTT: UnderlineWidth
156 skips-p % EXTT: DoubleUpperUnderlineOffset
157 skips-p % EXTT: DoubleLowerUnderlineOffset
158 skips-p % EXTT: DoubleUpperUnderlineWidth
159 skips-p % EXTT: DoubleLowerUnderlineWidth
160 skips-p % EXTT: StrikeOutOffset
161 skips-p % EXTT: StrikeOutWidth
162 readw-p % EXTT: KernPairs
163 /PFMNumberofKernPairs edef
164 skipw-p % EXTT: KernTracks
167 % requires that currPFMfile, PFMPairKernTableOffset, PFMNumberofKernPairs are defined
169 % prints kern pairs table in the AFM format
174 PFMPairKernTableOffset 0 ne {
175 currPFMfile PFMPairKernTableOffset setfileposition
176 readw-p % undocumented kern count (although all remaining structures are
177 % explicitly preceded by their sizes); if it were a stable
178 % feature, EXTTEXTMETRICS could be skipped
182 (Inconsistent number of kern pairs\n) printquit
184 (StartKernData) =to.ofi
185 (StartKernPairs ) print.to.ofi
186 PFMNumberofKernPairs =to.ofi
190 /was.notdef false def
191 PFMNumberofKernPairs {
195 readb-p % second char
197 reads-p % kern amount
201 (.notdef character ocurred among kern pairs) =
202 (you'd better check the resulting AFM file.) =
204 (EndKernPairs) =to.ofi
205 (EndKernData) =to.ofi
211 % alias (for ``compatibility'' with J. Clark):
212 /printkernpairs /readPFMKernPairs load def
217 (StartCharMetrics ) print.to.ofi
218 /PFBencoding currfont /Encoding get dup length array copy def
219 /PFBcharstrings currfont /CharStrings get def
220 PFBcharstrings length
221 PFBcharstrings /.notdef known { 1 sub } if =to.ofi
222 currfont 1000 scalefont setfont
223 % checking Encoding array and CharStrings dictionary for
224 % the consistency of names
225 /was.inconsitent false def
227 dup PFBencoding exch get
228 PFBcharstrings exch known {
231 % dup PFBencoding exch get =
232 PFBencoding exch /.notdef put % fix Encoding array
233 /was.inconsitent true def
237 (Encoding array contains name(s) absent from CharStrings dictionary) =
239 % print metric data for each character in PFB encoding vector
241 dup PFBencoding exch get
248 % xPFBencoding contains an entry for each name in the original
250 /xPFBencoding PFBcharstrings length dict def
252 xPFBencoding exch true put
256 /TMPFontTemplate (TMP_FONT#000) def
258 % NewPFBencoding is the new encoding vector
259 /NewPFBencoding 256 array def
261 NewPFBencoding exch /.notdef put
263 % fill up NewPFBencoding with names from CharStrings dictionary that
264 % are not encoded so far
269 dup xPFBencoding exch known not {
270 dup xPFBencoding exch true put
271 NewPFBencoding i 3 -1 roll put
281 % define a new font with NewPFBencoding as its encoding vector
282 currfont maxlength dict /NewTMPfont edef
284 exch dup dup /FID ne exch /Encoding ne and {
285 exch NewTMPfont 3 1 roll put
290 % compute a unique name for a font to be registered
291 /fontiter fontiter 1 add def
292 TMPFontTemplate fontiter (000) cvs
293 dup length TMPFontTemplate length exch sub exch putinterval
294 /TMPFontName TMPFontTemplate cvn def
295 NewTMPfont /FontName TMPFontName put
296 NewTMPfont /Encoding NewPFBencoding put
297 % make this new font the current font
298 TMPFontName NewTMPfont definefont 1000 scalefont setfont
299 % print metric data for each character in the newly created encoding vector
301 dup NewPFBencoding exch get
310 (EndCharMetrics) =to.ofi
313 % name actual_code normal_code printmetric -
316 (C ) print.to.ofi =only.to.ofi
317 ( ; WX ) print.to.ofi
318 onechar 0 3 -1 roll put
319 onechar stringwidth pop round cvi =only.to.ofi
320 ( ; N ) print.to.ofi =only.to.ofi
323 onechar false charpath flattenpath pathbbox
325 round cvi /ury edef round cvi /urx edef
326 round cvi /lly edef round cvi /llx edef
327 ury lly eq {/ury 0 def /lly 0 def} if % normalize degenrated BB
328 urx llx eq {/urx 0 def /llx 0 def} if %
329 llx =only.to.ofi ( ) print.to.ofi lly =only.to.ofi ( ) print.to.ofi
330 urx =only.to.ofi ( ) print.to.ofi ury =only.to.ofi ( ) print.to.ofi
335 3 1 roll 2 copy known {
336 get dup type /stringtype ne { =string cvs } if exch
337 print.to.ofi ( ) print.to.ofi =to.ofi
344 (Comment AFM Generated by Ghostscript/pf2afm) =to.ofi
345 currfont /FontName (FontName) printinfoitem
347 currfont /FontInfo get
348 dup /FullName (FullName) printinfoitem
349 dup /FamilyName (FamilyName) printinfoitem
350 dup /Weight (Weight) printinfoitem
351 dup /Notice (Notice) printinfoitem
352 dup /ItalicAngle (ItalicAngle) printinfoitem
353 dup /isFixedPitch (IsFixedPitch) printinfoitem
354 dup /UnderlinePosition (UnderlinePosition) printinfoitem
355 dup /UnderlineThickness (UnderlineThickness) printinfoitem
356 /version (Version) printinfoitem
358 (EncodingScheme FontSpecific) =to.ofi
360 (FontBBox) print.to.ofi
361 currfont /FontBBox get {
362 ( ) print.to.ofi round cvi =only.to.ofi
368 dup /PFMCapHeight (CapHeight) printinfoitem
369 dup /PFMXHeight (XHeight) printinfoitem
370 dup /PFMLowerCaseDescent (Descender) printinfoitem
371 /PFMLowerCaseAscent (Ascender) printinfoitem
376 % make a shot of the actual font directory:
377 /oldFontDirectory FontDirectory dup length dict copy def
378 isPFB {% defined in `makeafm'
379 (r) file true /PFBDecode filter cvx % true is better (see gs_type1.ps)
382 (r) file mark exch run
385 % make a shot of the updated font directory:
386 /newFontDirectory FontDirectory dup length dict copy def
387 % spot the added font:
388 oldFontDirectory {pop newFontDirectory exch undef} forall
389 newFontDirectory length 1 ne {
390 newFontDirectory length =
391 (Weird PFB file?\n) printquit
393 newFontDirectory {pop} forall
394 findfont /currfont edef
399 (r) file /currPFMfile edef
400 10 dict dup /PFMdict edef begin
405 pop /currPFMfile () def
409 % pfmfilename pf[ba]filename filetype printafm -
410 % where filetype=(a) or (b)
415 (StartFontMetrics 2.0) =to.ofi
419 (EndFontMetrics) =to.ofi
422 % pf[ba]filename makeafm -
425 count 0 eq {(Missing font file name\n) printquit} if
427 ifn length 0 eq {(Empty font file name\n) printquit} if
428 % the following piece of the code does, in fact, the job of a system shell,
429 % i.e., it analyses the supplied names, appends extensions if needed,
431 /pfbn () def /pfan () def /pfmn () def % initialisation
433 3 -1 roll length 0 eq {% file name extension = ".pfb"
434 ifn dup length string copy /pfbn edef
439 3 -1 roll length 0 eq {% file name extension = ".pfa"
440 ifn dup length string copy /pfan edef
444 pfbn () eq pfan () eq and dup {% no extension was supplied, try ".pfb"
445 /pfbn ifn (.pfb) concatstrings def
447 pfbn () ne {% check whether "filename.pfb" exists
448 pfbn status {pop pop pop pop /isPFB true def}{/pfbn () def} ifelse
450 pfbn () eq and {% checking "filename.pfb" unsuccessful, try ".pfa"
451 /pfan ifn (.pfa) concatstrings def
453 pfan () ne {% check whether "filename.pfa" exists
454 pfan status {pop pop pop pop /isPFB false def}{/pfan () def} ifelse
457 pfbn () eq pfan () eq and {
458 (Neither pfa nor pfb found\n) printquit
461 /ofn ifn (.afm) concatstrings def
463 pop pop pop pop (Resulting file exists\n) printquit
465 /ofi ofn (w) file def
467 /pfmn ifn (.pfm) concatstrings def
473 4 -1 roll exch concatstrings exch concatstrings exch
478 (pfm/) exch concatstrings concatstrings
480 pop pop pop pop /pfmn edef
482 pop /pfmn () def (pfm file not found -- ignored\n) print
485 //systemdict /.setsafe known {
488 [ pfmn dup length 0 eq { pop } if
489 isPFB {pfbn}{pfan} ifelse
491 /PermitFileWriting [ ]
492 /PermitFileControl [ ]
499 isPFB {pfbn}{pfan} ifelse
504 % Check for command line arguments.
506 { ] dup length 1 eq {
509 (This is PF2AFM -- AFM generator \(ver. 1.00\)\n) print
510 (Usage: gs [-dNODISPLAY] -- pf2afm.ps disk_font_name\n) printquit