pf2afm.ps 15 KB

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