pf2afm.ps 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. %!
  2. % This is a PostScript program for making an AFM file from
  3. % PFB / PFA and (optionally) PFM files.
  4. %
  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
  12. % Usage:
  13. % gs [-dNODISPLAY] -- pf2afm.ps disk_font_name
  14. %
  15. % The result is written to the file disk_font_name.afm, provided such
  16. % a file does not exist; otherwise program quits.
  17. %
  18. % The font can be either *.pfa or *.pfb; if no extension is supplied,
  19. % first disk_font_name.pfb is examined, then disk_font_name.pfa.
  20. % Moreover, if there is a *.pfm file in the same directory or in the
  21. % subdirectory PFM, i.e., disk_font_name.pfm or PFM/disk_font_name.pfm,
  22. % kern pairs from it are extracted, as well as additional font
  23. % parameters, usually absent from Type 1 fonts.
  24. % Tribute:
  25. % The program is based on James Clark's <jjc@jclark.uucp> printafm.ps
  26. % (with alterations by d.love@dl.ac.uk and L. Peter Deutsch) from
  27. % Ghostscript 5.10 distribution.
  28. /onechar 1 string def
  29. /edef {exch def} def
  30. % charnumber print-charname -
  31. % prints the name of the encoded character
  32. /print-charname {
  33. PFBencoding exch get =string cvs dup
  34. (.notdef) eq {
  35. /was.notdef true def
  36. } if
  37. print.to.ofi ( ) print.to.ofi
  38. } def
  39. /printquit {print flush quit} def
  40. % redirecting GS output to ``ofi'' file
  41. /eolch (\r\n) def
  42. /=only.to.ofi {ofi exch write=only} def % replaces GS's `=only'
  43. /print.to.ofi {ofi exch writestring} def % replaces `print'
  44. /=to.ofi { =only.to.ofi eolch print.to.ofi } def % replaces `='
  45. % read and skip: byte, short, word, double and long
  46. /readb-p {currPFMfile read not {(Unexpected EOF) printquit} if} def
  47. /readw-p {readb-p readb-p 256 mul add} def
  48. /reads-p {readw-p dup 32768 ge {65536 sub} if} def
  49. /readd-p {readb-p readb-p readb-p readb-p 256 mul add 256 mul add 256 mul add} def
  50. /readl-p /readd-p load def % double word is, in fact, long integer in GS
  51. /skipb-p {readb-p pop} def
  52. /skipw-p {skipb-p skipb-p} def
  53. /skips-p /skipw-p load def
  54. /skipd-p {skipb-p skipb-p skipb-p skipb-p} def
  55. /skipl-p /skipd-p load def
  56. /skipa-p { {skipb-p} repeat} def
  57. % PFMfile readPFMheader -
  58. % defines currPFMfile, PFMExtMetricOffset, PFMPairKernTableOffset
  59. /readPFMheader {
  60. currPFMfile bytesavailable
  61. % ---------------
  62. % PFM MAIN HEADER
  63. % ---------------
  64. skipw-p % PFM: version
  65. readd-p % PFM: size (size is dword, not word as the documentation says)
  66. ne {(Wrong file size) printquit} if
  67. 60 skipa-p % PFM: copyright
  68. skipw-p % PFM: Type
  69. skipw-p % PFM: Points
  70. skipw-p % PFM: VertRes
  71. skipw-p % PFM: HorizRes
  72. skipw-p % PFM: Ascent
  73. skipw-p % PFM: InternalLeading
  74. skipw-p % PFM: ExternalLeading
  75. skipb-p % PFM: Italic
  76. skipb-p % PFM: Underline
  77. skipb-p % PFM: Stikeout
  78. skipw-p % PFM: Weight
  79. skipb-p % PFM: CharSet
  80. skipw-p % PFM: PixWidth
  81. skipw-p % PFM: PixHeight
  82. skipb-p % PFM: PitchAndFamily
  83. skipw-p % PFM: AvgWidth
  84. skipw-p % PFM: MaxWidth
  85. skipb-p % PFM: FirstChar
  86. skipb-p % PFM: LastChar
  87. skipb-p % PFM: DefaultChar
  88. skipb-p % PFM: BreakChar
  89. skipw-p % PFM: WidthBytes
  90. skipd-p % PFM: Device
  91. skipd-p % PFM: Face
  92. skipd-p % PFM: BitsPointer
  93. skipd-p % PFM: BitsOffset
  94. % here we assume that it is a PostScript font, i.e., it always uses
  95. % the extended width table, therefore the normal width table is empty
  96. % -------------
  97. % PFM EXTENSION
  98. % -------------
  99. skipw-p % PFMEX: SizeFields
  100. readd-p % PFMEX: ExtMetricOffset
  101. /PFMExtMetricOffset edef
  102. skipd-p % PFMEX: ExtentTable
  103. skipd-p % PFMEX: OriginTable
  104. readd-p % PFMEX: PairKernTable
  105. /PFMPairKernTableOffset edef
  106. skipd-p % PFMEX: TrackKernTable
  107. skipd-p % PFMEX: DriverInfo
  108. skipd-p % PFMEX: Reserved
  109. } def
  110. % requires that currPFMfile, PFMExtMetricOffset are defined
  111. % readPFMExtMetric -
  112. % defines PFMNumberofKernPairs
  113. /readPFMExtMetric {
  114. currPFMfile PFMExtMetricOffset setfileposition
  115. skips-p % EXTT: Size
  116. skips-p % EXTT: PointSize
  117. skips-p % EXTT: Orientation
  118. skips-p % EXTT: MasterHeight
  119. skips-p % EXTT: MinScale
  120. skips-p % EXTT: MaxScale
  121. skips-p % EXTT: MasterUnit
  122. reads-p % EXTT: CapHeight
  123. /PFMCapHeight edef
  124. reads-p % EXTT: XHeight
  125. /PFMXHeight edef
  126. reads-p % EXTT: LowerCaseAscent
  127. /PFMLowerCaseAscent edef
  128. reads-p % EXTT: LowerCaseDescent
  129. neg /PFMLowerCaseDescent edef
  130. skips-p % EXTT: Slant
  131. skips-p % EXTT: SuperScript
  132. skips-p % EXTT: SubScript
  133. skips-p % EXTT: SuperScriptSize
  134. skips-p % EXTT: SubScriptSize
  135. skips-p % EXTT: UnderlineOffset
  136. skips-p % EXTT: UnderlineWidth
  137. skips-p % EXTT: DoubleUpperUnderlineOffset
  138. skips-p % EXTT: DoubleLowerUnderlineOffset
  139. skips-p % EXTT: DoubleUpperUnderlineWidth
  140. skips-p % EXTT: DoubleLowerUnderlineWidth
  141. skips-p % EXTT: StrikeOutOffset
  142. skips-p % EXTT: StrikeOutWidth
  143. readw-p % EXTT: KernPairs
  144. /PFMNumberofKernPairs edef
  145. skipw-p % EXTT: KernTracks
  146. } def
  147. % requires that currPFMfile, PFMPairKernTableOffset, PFMNumberofKernPairs are defined
  148. % readPFMExtMetric -
  149. % prints kern pairs table in the AFM format
  150. /readPFMKernPairs {
  151. currPFMfile () ne {
  152. PFMdict begin
  153. PFMPairKernTableOffset 0 ne {
  154. currPFMfile PFMPairKernTableOffset setfileposition
  155. readw-p % undocumented kern count (although all remaining structures are
  156. % explicitly preceded by their sizes); if it were a stable
  157. % feature, EXTTEXTMETRICS could be skipped
  158. PFMNumberofKernPairs
  159. % 2 copy = =
  160. ne {
  161. (Inconsistent number of kern pairs) printquit
  162. } if
  163. (StartKernData) =to.ofi
  164. (StartKernPairs ) print.to.ofi
  165. PFMNumberofKernPairs =to.ofi
  166. % ---------
  167. % MAIN LOOP
  168. % ---------
  169. /was.notdef false def
  170. PFMNumberofKernPairs {
  171. (KPX ) print.to.ofi
  172. readb-p % first char
  173. print-charname
  174. readb-p % second char
  175. print-charname
  176. reads-p % kern amount
  177. =to.ofi
  178. } repeat
  179. was.notdef {
  180. (.notdef character ocurred among kern pairs) =
  181. (you'd better check the resulting AFM file.) =
  182. } if
  183. (EndKernPairs) =to.ofi
  184. (EndKernData) =to.ofi
  185. } if
  186. end
  187. } if
  188. } def
  189. % alias (for ``compatibility'' with J. Clark):
  190. /printkernpairs /readPFMKernPairs load def
  191. % printcharmetrics -
  192. /printcharmetrics {
  193. (StartCharMetrics ) print.to.ofi
  194. /PFBencoding currfont /Encoding get dup length array copy def
  195. /PFBcharstrings currfont /CharStrings get def
  196. PFBcharstrings length
  197. PFBcharstrings /.notdef known { 1 sub } if =to.ofi
  198. currfont 1000 scalefont setfont
  199. % checking Encoding array and CharStrings dictionary for
  200. % the consistency of names
  201. /was.inconsitent false def
  202. 0 1 255 {
  203. dup PFBencoding exch get
  204. PFBcharstrings exch known {
  205. pop
  206. }{
  207. % dup PFBencoding exch get =
  208. PFBencoding exch /.notdef put % fix Encoding array
  209. /was.inconsitent true def
  210. } ifelse
  211. } for
  212. was.inconsitent {
  213. (Encoding array contains name(s) absent from CharStrings dictionary) =
  214. } if
  215. % print metric data for each character in PFB encoding vector
  216. 0 1 255 {
  217. dup PFBencoding exch get
  218. dup /.notdef ne {
  219. exch dup printmetric
  220. }{
  221. pop pop
  222. } ifelse
  223. } for
  224. % xPFBencoding contains an entry for each name in the original
  225. % encoding vector
  226. /xPFBencoding PFBcharstrings length dict def
  227. PFBencoding {
  228. xPFBencoding exch true put
  229. } forall
  230. /fontiter 0 def
  231. /TMPFontTemplate (TMP_FONT#000) def
  232. {
  233. % NewPFBencoding is the new encoding vector
  234. /NewPFBencoding 256 array def
  235. 0 1 255 {
  236. NewPFBencoding exch /.notdef put
  237. } for
  238. % fill up NewPFBencoding with names from CharStrings dictionary that
  239. % are not encoded so far
  240. /i 0 def
  241. PFBcharstrings {
  242. pop
  243. i 255 le {
  244. dup xPFBencoding exch known not {
  245. dup xPFBencoding exch true put
  246. NewPFBencoding i 3 -1 roll put
  247. /i i 1 add def
  248. }{
  249. pop
  250. } ifelse
  251. }{
  252. pop exit
  253. } ifelse
  254. } forall
  255. i 0 eq {exit} if
  256. % define a new font with NewPFBencoding as its encoding vector
  257. currfont maxlength dict /NewTMPfont edef
  258. currfont {
  259. exch dup dup /FID ne exch /Encoding ne and {
  260. exch NewTMPfont 3 1 roll put
  261. }{
  262. pop pop
  263. } ifelse
  264. } forall
  265. % compute a unique name for a font to be registered
  266. /fontiter fontiter 1 add def
  267. TMPFontTemplate fontiter (000) cvs
  268. dup length TMPFontTemplate length exch sub exch putinterval
  269. /TMPFontName TMPFontTemplate cvn def
  270. NewTMPfont /FontName TMPFontName put
  271. NewTMPfont /Encoding NewPFBencoding put
  272. % make this new font the current font
  273. TMPFontName NewTMPfont definefont 1000 scalefont setfont
  274. % print metric data for each character in the newly created encoding vector
  275. 0 1 255 {
  276. dup NewPFBencoding exch get
  277. dup /.notdef ne {
  278. exch -1 printmetric
  279. }{
  280. pop pop exit
  281. } ifelse
  282. } for
  283. i 255 lt {exit} if
  284. } loop
  285. (EndCharMetrics) =to.ofi
  286. } def
  287. % name actual_code normal_code printmetric -
  288. /printmetric {
  289. (C ) print.to.ofi =only.to.ofi
  290. ( ; WX ) print.to.ofi
  291. onechar 0 3 -1 roll put
  292. onechar stringwidth pop round cvi =only.to.ofi
  293. ( ; N ) print.to.ofi =only.to.ofi
  294. ( ; B ) print.to.ofi
  295. newpath 0 0 moveto
  296. onechar false charpath flattenpath pathbbox
  297. newpath
  298. round cvi /ury edef round cvi /urx edef
  299. round cvi /lly edef round cvi /llx edef
  300. ury lly eq {/ury 0 def /lly 0 def} if % normalize degenrated BB
  301. urx llx eq {/urx 0 def /llx 0 def} if %
  302. llx =only.to.ofi ( ) print.to.ofi lly =only.to.ofi ( ) print.to.ofi
  303. urx =only.to.ofi ( ) print.to.ofi ury =only.to.ofi ( ) print.to.ofi
  304. (;) =to.ofi
  305. } def
  306. /printinfoitem {
  307. 3 1 roll 2 copy known {
  308. get =string cvs exch
  309. print.to.ofi ( ) print.to.ofi =to.ofi
  310. }{
  311. pop pop pop
  312. } ifelse
  313. } def
  314. /printfontinfo {
  315. (Comment AFM Generated by Ghostscript/pf2afm) =to.ofi
  316. currfont /FontName (FontName) printinfoitem
  317. %
  318. currfont /FontInfo get
  319. dup /FullName (FullName) printinfoitem
  320. dup /FamilyName (FamilyName) printinfoitem
  321. dup /Weight (Weight) printinfoitem
  322. dup /Notice (Notice) printinfoitem
  323. dup /ItalicAngle (ItalicAngle) printinfoitem
  324. dup /isFixedPitch (IsFixedPitch) printinfoitem
  325. dup /UnderlinePosition (UnderlinePosition) printinfoitem
  326. dup /UnderlineThickness (UnderlineThickness) printinfoitem
  327. /version (Version) printinfoitem
  328. %
  329. (EncodingScheme FontSpecific) =to.ofi
  330. %
  331. (FontBBox) print.to.ofi
  332. currfont /FontBBox get {
  333. ( ) print.to.ofi round cvi =only.to.ofi
  334. } forall
  335. eolch print.to.ofi
  336. %
  337. currPFMfile () ne {
  338. PFMdict
  339. dup /PFMCapHeight (CapHeight) printinfoitem
  340. dup /PFMXHeight (XHeight) printinfoitem
  341. dup /PFMLowerCaseDescent (Descender) printinfoitem
  342. /PFMLowerCaseAscent (Ascender) printinfoitem
  343. } if
  344. } def
  345. /readPFBfile {
  346. % make a shot of the actual font directory:
  347. /oldFontDirectory FontDirectory dup length dict copy def
  348. isPFB {% defined in `makeafm'
  349. (r) file true /PFBDecode filter cvx % true is better (see gs_type1.ps)
  350. mark exch exec
  351. }{
  352. (r) file mark exch run
  353. } ifelse
  354. cleartomark
  355. % make a shot of the updated font directory:
  356. /newFontDirectory FontDirectory dup length dict copy def
  357. % spot the added font:
  358. oldFontDirectory {pop newFontDirectory exch undef} forall
  359. newFontDirectory length 1 ne {
  360. newFontDirectory length =
  361. (Weird PFB file?) printquit
  362. } if
  363. newFontDirectory {pop} forall
  364. findfont /currfont edef
  365. } def
  366. /readPFMfile {
  367. dup () ne {
  368. (r) file /currPFMfile edef
  369. 10 dict dup /PFMdict edef begin
  370. readPFMheader
  371. readPFMExtMetric
  372. end
  373. }{
  374. pop /currPFMfile () def
  375. } ifelse
  376. } def
  377. % pfmfilename pf[ba]filename filetype printafm -
  378. % where filetype=(a) or (b)
  379. /printafm {
  380. readPFBfile
  381. readPFMfile
  382. (StartFontMetrics 2.0) =to.ofi
  383. printfontinfo
  384. printcharmetrics
  385. printkernpairs
  386. (EndFontMetrics) =to.ofi
  387. } def
  388. % pf[ba]filename makeafm -
  389. /makeafm {
  390. count 0 eq {(Missing font file name) printquit} if
  391. /ifn edef
  392. ifn length 0 eq {(Empty font file name) printquit} if
  393. % the following piece of the code does, in fact, the job of a system shell,
  394. % i.e., it analyses the supplied names, appends extensions if needed,
  395. % and check files:
  396. /pfbn () def /pfan () def /pfmn () def % initialisation
  397. ifn (.pfb) search {
  398. 3 -1 roll length 0 eq {% file name extension = ".pfb"
  399. ifn dup length string copy /pfbn edef
  400. /ifn edef
  401. }{pop} ifelse
  402. } if pop
  403. ifn (.pfa) search {
  404. 3 -1 roll length 0 eq {% file name extension = ".pfa"
  405. ifn dup length string copy /pfan edef
  406. /ifn edef
  407. }{pop} ifelse
  408. } if pop
  409. pfbn () eq pfan () eq and dup {% no extension was supplied, try ".pfb"
  410. /pfbn ifn (.pfb) concatstrings def
  411. } if
  412. pfbn () ne {% check whether "filename.pfb" exists
  413. pfbn status {pop pop pop pop /isPFB true def}{/pfbn () def} ifelse
  414. } if
  415. pfbn () eq and {% checking "filename.pfb" unsuccessful, try ".pfa"
  416. /pfan ifn (.pfa) concatstrings def
  417. } if
  418. pfan () ne {% check whether "filename.pfa" exists
  419. pfan status {pop pop pop pop /isPFB false def}{/pfan () def} ifelse
  420. } if
  421. pfbn () eq pfan () eq and {
  422. (Neither pfa nor pfb found) printquit
  423. } if
  424. /ofn ifn (.afm) concatstrings def
  425. ofn status {
  426. pop pop pop pop (Resulting file exists) printquit
  427. } if
  428. /ofi ofn (w) file def
  429. //systemdict /.setsafe known { .setsafe } if
  430. /pfmn ifn (.pfm) concatstrings def
  431. pfmn status {
  432. pop pop pop pop
  433. }{
  434. () pfmn {
  435. (/) search {
  436. 4 -1 roll exch concatstrings exch concatstrings exch
  437. }{
  438. exit
  439. } ifelse
  440. } loop
  441. (pfm/) exch concatstrings concatstrings
  442. dup status {
  443. pop pop pop pop /pfmn edef
  444. }{
  445. pop /pfmn () def (pfm file not found -- ignored) print
  446. } ifelse
  447. } ifelse
  448. pfmn
  449. isPFB {pfbn}{pfan} ifelse
  450. printafm
  451. } def
  452. % Check for command line arguments.
  453. [ shellarguments
  454. { ] dup length 1 eq {
  455. 0 get makeafm
  456. }{
  457. (This is PF2AFM -- AFM generator \(ver. 1.00\)\n)
  458. (Usage: gs [-dNODISPLAY] -- pf2afm.ps disk_font_name\n) printquit
  459. } ifelse
  460. }
  461. {pop}
  462. ifelse