You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

535 lines
15 KiB

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