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.

606 lines
18 KiB

1 month ago
  1. % Copyright (C) 2001-2023 Artifex Software, Inc.
  2. % All Rights Reserved.
  3. %
  4. % This software is provided AS-IS with no warranty, either express or
  5. % implied.
  6. %
  7. % This software is distributed under license and may not be copied,
  8. % modified or distributed except as expressly authorized under the terms
  9. % of the license contained in the file LICENSE in this distribution.
  10. %
  11. % Refer to licensing information at http://www.artifex.com or contact
  12. % Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  13. % CA 94129, USA, for further information.
  14. %
  15. % font2pcl.ps
  16. % Write out a font as a PCL bitmap font.
  17. % This program must be run with -dNOSAFER, as it uses the in-memory image device
  18. % which is unavailable with SAFER.
  19. /pcldict 60 dict def
  20. % Write out the current font as a PCL bitmap font.
  21. % The current transformation matrix defines the font size and orientation.
  22. /WriteResolution? false def % true=use "resolution bound font" format,
  23. % false=use older format
  24. /LJ4 false def % true=use LJ4 Typeface code
  25. % false=use LJIIP/IID/IIIx Typeface code
  26. pcldict begin % internal procedures
  27. /findstring % <string> <substring> findstring <bool>
  28. { search { pop pop pop true } { pop false } ifelse
  29. } def
  30. % Determine which set of keywords is present in a string.
  31. % The last keyword set must be empty.
  32. /keysearch % <string> <array of arrays of keywords> keysearch <index>
  33. { 0 1 2 index length 1 sub
  34. { 2 copy get true exch
  35. { % Stack: <string> <a.a.k.> <index> <bool> <keyword>
  36. 4 index exch findstring and
  37. }
  38. forall
  39. { 0 exch getinterval exit
  40. }
  41. if pop
  42. }
  43. for
  44. exch pop length % invalid index if missing
  45. } def
  46. % Determine the device height of a string in quarter-dots.
  47. /charheight % <string> charheight <int>
  48. { gsave newpath 0 0 moveto false charpath
  49. pathbbox exch pop exch sub exch pop 0 exch grestore
  50. dtransform add abs 4 mul cvi
  51. } def
  52. % Compute an integer version of the transformed FontBBox.
  53. /inflate % <num> inflate <num>
  54. { dup 0 gt { ceiling } { floor } ifelse
  55. } def
  56. /ixbbox % - ixbbox <llx> <lly> <urx> <ury>
  57. { /FontBBox load aload pop % might be executable or literal
  58. 4 2 roll transform exch truncate cvi exch truncate cvi
  59. 4 2 roll transform exch inflate cvi exch inflate cvi
  60. } def
  61. % Determine the original font of a possibly transformed font.
  62. % Since some badly behaved PostScript files construct transformed
  63. % fonts "by hand", we can't just rely on the OrigFont pointers.
  64. % Instead, if a font with the given name exists, and if its
  65. % entries for FontType and UniqueID match those of the font we
  66. % obtain by following the OrigFont chain, we use that font.
  67. /origfont
  68. { { dup /OrigFont known not { exit } if /OrigFont get } loop
  69. FontDirectory 1 index /FontName get .knownget
  70. { % Stack: origfont namedfont
  71. 1 index /FontType get 1 index /FontType get eq
  72. { 1 index /UniqueID .knownget
  73. { 1 index /UniqueID .knownget
  74. { eq { exch } if }
  75. { pop }
  76. ifelse
  77. }
  78. if
  79. }
  80. if pop
  81. }
  82. if
  83. } def
  84. % Determine the bounding box of the current device's image.
  85. % Free variables: row, zerow.
  86. /devbbox % <rw> <rh> devbbox <ymin> <ymax1> <xmin> <xmax1>
  87. { % Find top and bottom whitespace.
  88. dup
  89. { dup 0 eq { exit } if 1 sub
  90. dup currentdevice exch row copyscanlines
  91. zerow ne { 1 add exit } if
  92. }
  93. loop % ymax1
  94. 0
  95. { 2 copy eq { exit } if
  96. dup currentdevice exch row copyscanlines
  97. zerow ne { exit } if
  98. 1 add
  99. }
  100. loop % ymin
  101. exch
  102. % Find left and right whitespace.
  103. 3 index 0
  104. % Stack: rw rh ymin ymax1 xmin xmax1
  105. 3 index 1 4 index 1 sub
  106. { currentdevice exch row copyscanlines .findzeros
  107. exch 4 1 roll .max 3 1 roll .min exch
  108. }
  109. for % xmin xmax1
  110. % Special check: xmin > xmax1 if height = 0
  111. 2 copy gt { exch pop dup } if
  112. 6 -2 roll pop pop
  113. } def
  114. % Write values on outfile.
  115. /w1 { 255 and outfile exch write } def
  116. /w2 { dup -8 bitshift w1 w1 } def
  117. /wbyte % <byte> <label> wbyte
  118. { VDEBUG { print ( =byte= ) print dup == flush } { pop } ifelse w1
  119. } def
  120. /wword % <word16> <label> wword
  121. { VDEBUG { print ( =word= ) print dup == flush } { pop } ifelse w2
  122. } def
  123. /wdword % <word32> <label> wdword
  124. { VDEBUG { print ( =dword= ) print dup == flush } { pop } ifelse
  125. dup -16 bitshift w2 w2
  126. } def
  127. /style.posture.keys
  128. [ { (Italic) } { (Oblique) }
  129. { }
  130. ] def
  131. /style.posture.values <010100> def
  132. /style.appearance.width.keys
  133. [ { (Ultra) (Compressed) }
  134. { (Extra) (Compressed) }
  135. { (Extra) (Condensed) }
  136. { (Extra) (Extended) }
  137. { (Extra) (Expanded) }
  138. { (Compressed) }
  139. { (Condensed) }
  140. { (Extended) }
  141. { (Expanded) }
  142. { }
  143. ] def
  144. /style.appearance.width.values <04030207070201060600> def
  145. /width.type.keys
  146. [ { (Ultra) (Compressed) }
  147. { (Extra) (Compressed) }
  148. { (Extra) (Condensed) }
  149. { (Extra) (Expanded) }
  150. { (Compressed) }
  151. { (Condensed) }
  152. { (Expanded) }
  153. { }
  154. ] def
  155. /width.type.values <fbfcfd03fdfe0200> def
  156. /stroke.weight.keys
  157. [ { (Ultra) (Thin) }
  158. { (Ultra) (Black) }
  159. { (Extra) (Thin) }
  160. { (Extra) (Light) }
  161. { (Extra) (Bold) }
  162. { (Extra) (Black) }
  163. { (Demi) (Light) }
  164. { (Demi) (Bold) }
  165. { (Semi) (Light) }
  166. { (Semi) (Bold) }
  167. { (Thin) }
  168. { (Light) }
  169. { (Bold) }
  170. { (Black) }
  171. { }
  172. ] def
  173. /stroke.weight.values <f907fafc0406fe02ff01fbfd030500> def
  174. /vendor.keys
  175. [ { (Agfa) }
  176. { (Bitstream) }
  177. { (Linotype) }
  178. { (Monotype) }
  179. { (Adobe) }
  180. { }
  181. ] def
  182. /vendor.default.index 4 def % might as well be Adobe
  183. /old.vendor.values <020406080a00> def
  184. /new.vendor.values <010203040500> def
  185. /vendor.initials (CBLMA\000) def
  186. currentdict readonly end pop % pcldict
  187. % Convert and write a PCL font for the current font and transformation.
  188. % Write the font header. We split this off only to avoid overflowing
  189. % the limit on the maximum size of a procedure.
  190. % Free variables: outfile uury u0y rw rh orientation uh ully
  191. /writefontheader
  192. { outfile (\033\)s) writestring
  193. outfile 64 WriteResolution? { 4 add } if
  194. Copyright length add write==only
  195. outfile (W) writestring
  196. WriteResolution? { 20 68 } { 0 64 } ifelse
  197. (Font Descriptor Size) wword
  198. (Header Format) wbyte
  199. 1 (Font Type) wbyte
  200. FullName style.posture.keys keysearch style.posture.values exch get
  201. FullName style.appearance.width.keys keysearch
  202. style.appearance.width.values exch get 4 mul add
  203. PaintType 2 eq { 32 add } if
  204. /style exch def
  205. style -8 bitshift (Style MSB) wbyte
  206. 0 (Reserved) wbyte
  207. /baseline uury 1 sub u0y sub def
  208. baseline (Baseline Position) wword
  209. rw (Cell Width) wword
  210. rh (Cell Height) wword
  211. orientation (Orientation) wbyte
  212. FontInfo /isFixedPitch .knownget not { false } if
  213. { 0 } { 1 } ifelse (Spacing) wbyte
  214. % Use loop/exit to fake a multiple-exit block.
  215. { Encoding StandardEncoding eq { 10 (J) exit } if
  216. Encoding ISOLatin1Encoding eq { 11 (J) exit } if
  217. Encoding SymbolEncoding eq { 19 (M) exit } if
  218. Encoding DingbatsEncoding eq { 10 (L) exit } if
  219. % (Warning: unknown Encoding, using ISOLatin1.\n) print flush
  220. 11 (J) exit
  221. }
  222. loop
  223. 0 get 64 sub exch 32 mul add (Symbol Set) wword
  224. ( ) stringwidth pop 0 dtransform add abs 4 mul
  225. /pitch exch def
  226. pitch cvi (Pitch) wword
  227. uh 4 mul (Height) wword % Height
  228. (x) charheight (x-Height) wword
  229. FullName width.type.keys keysearch
  230. width.type.values exch get (Width Type) wbyte
  231. style 255 and (Style LSB) wbyte
  232. FullName stroke.weight.keys keysearch
  233. stroke.weight.values exch get (Stroke Weight) wbyte
  234. FullName vendor.keys keysearch
  235. dup vendor.initials exch get 0 eq
  236. { % No vendor in FullName, try Notice
  237. pop Copyright vendor.keys keysearch
  238. dup vendor.initials exch get 0 eq { pop vendor.default.index } if
  239. }
  240. if
  241. /vendor.index exch def
  242. 0 (Typeface LSB) wbyte % punt
  243. 0 (Typeface MSB) wbyte % punt
  244. 0 (Serif Style) wbyte % punt
  245. 2 (Quality) wbyte
  246. 0 (Placement) wbyte
  247. gsave FontMatrix concat rot neg rotate
  248. /ulwidth
  249. FontInfo /UnderlineThickness .knownget
  250. { 0 exch dtransform exch pop abs }
  251. { resolution 100 div }
  252. ifelse def
  253. FontInfo /UnderlinePosition .knownget
  254. { 0 exch transform exch pop negY ulwidth 2 div add }
  255. { ully ulwidth add }
  256. ifelse u0y sub
  257. round cvi 1 .max 255 .min (Underline Position) wbyte
  258. ulwidth round cvi 1 .max 255 .min (Underline Thickness) wbyte
  259. grestore
  260. uh 1.2 mul 4 mul cvi (Text Height) wword
  261. (average lowercase character) dup stringwidth
  262. pop 0 dtransform add abs
  263. exch length div 4 mul cvi (Text Width) wword
  264. 0
  265. { dup Encoding exch get /.notdef ne { exit } if
  266. 1 add
  267. }
  268. loop (First Code) wword
  269. 255
  270. { dup Encoding exch get /.notdef ne { exit } if
  271. 1 sub
  272. }
  273. loop (Last Code) wword
  274. pitch dup cvi sub 256 mul cvi (Pitch Extended) wbyte
  275. 0 (Height Extended) wbyte
  276. 0 (Cap Height) wword % (default)
  277. currentfont /UniqueID known { UniqueID } { 0 } ifelse
  278. 16#c1000000 add (Font Number (Adobe UniqueID)) wdword
  279. FontName length 16 .max string
  280. dup FontName exch cvs pop
  281. outfile exch 0 16 getinterval writestring % Font Name
  282. WriteResolution?
  283. { resolution dup (X Resolution) wword (Y Resolution) wword
  284. }
  285. if
  286. outfile Copyright writestring % Copyright
  287. } def
  288. /writePCL % <fontfile> <resolution> writePCL -
  289. {
  290. save
  291. currentfont begin
  292. pcldict begin
  293. 80 dict begin % allow for recursion
  294. /saved exch def
  295. /resolution exch def
  296. /outfile exch def
  297. matrix currentmatrix dup 4 0 put dup 5 0 put setmatrix
  298. % Supply some default values so we don't have to check later.
  299. currentfont /FontInfo known not { /FontInfo 1 dict def } if
  300. currentfont /FontName known not { /FontName () def } if
  301. /Copyright FontInfo /Notice .knownget not { () } if def
  302. /FullName
  303. FontInfo /FullName .knownget not
  304. { FontName dup length string cvs }
  305. if def
  306. % Determine the original font, and its relationship to this one.
  307. /OrigFont currentfont origfont def
  308. /OrigMatrix OrigFont /FontMatrix get def
  309. /OrigMatrixInverse OrigMatrix matrix invertmatrix def
  310. /ScaleMatrix matrix currentfont OrigFont ne
  311. { FontMatrix exch OrigMatrixInverse exch concatmatrix
  312. } if
  313. def
  314. /CurrentScaleMatrix
  315. matrix currentmatrix
  316. matrix defaultmatrix
  317. dup 0 get 1 index 3 get mul 0 lt
  318. 1 index dup 1 get exch 2 get mul 0 gt or
  319. /flipY exch def
  320. dup invertmatrix
  321. dup concatmatrix
  322. def
  323. /negY flipY { {neg} } { {} } ifelse def
  324. % Print debugging information.
  325. /CDEBUG where { pop } { /CDEBUG false def } ifelse
  326. /VDEBUG where { pop } { /VDEBUG false def } ifelse
  327. CDEBUG { /VDEBUG true def } if
  328. DEBUG
  329. { (currentmatrix: ) print matrix currentmatrix ==
  330. (defaultmatrix: ) print matrix defaultmatrix ==
  331. (flipY: ) print flipY ==
  332. (scaling matrix: ) print CurrentScaleMatrix ==
  333. (FontMatrix: ) print FontMatrix ==
  334. (FontBBox: ) print /FontBBox load ==
  335. currentfont OrigFont ne
  336. { OrigFont /FontName .knownget { (orig FontName: ) print == } if
  337. (orig FontMatrix: ) print OrigMatrix ==
  338. } if
  339. currentfont /ScaleMatrix .knownget { (ScaleMatrix: ) print == } if
  340. gsave
  341. FontMatrix concat
  342. (combined matrix: ) print matrix currentmatrix ==
  343. grestore
  344. flush
  345. } if
  346. % Determine the orientation.
  347. ScaleMatrix matrix currentmatrix dup concatmatrix
  348. 0 1 3
  349. { 1 index 1 get 0 eq 2 index 2 get 0 eq and 2 index 0 get 0 gt and
  350. { exit } if
  351. pop -90 matrix rotate exch dup concatmatrix
  352. }
  353. for
  354. dup type /integertype ne
  355. { (Only rotations by multiples of 90 degrees are supported:\n) print
  356. == flush
  357. saved end end end restore stop
  358. }
  359. if
  360. /orientation exch def
  361. /rot orientation 90 mul def
  362. DEBUG { (orientation: ) print orientation == flush } if
  363. dup dup 0 get exch 3 get negY sub abs 0.5 ge
  364. { (Only identical scaling in X and Y is supported:\n) print
  365. exch flipY 3 array astore ==
  366. %
  367. % .devicename has been deprecated
  368. % currentdevice .devicename ==
  369. %
  370. currentpagedevice /Name get ==
  371. matrix defaultmatrix == flush
  372. saved end end end restore stop
  373. }
  374. if pop
  375. % Determine the font metrics, in the PCL character coordinate system,
  376. % which has +Y going towards the top of the page.
  377. gsave
  378. FontMatrix concat
  379. 0 0 transform
  380. negY round cvi /r0y exch def
  381. round cvi /r0x exch def
  382. ixbbox
  383. negY /rury exch def /rurx exch def
  384. negY /rlly exch def /rllx exch def
  385. /rminx rllx rurx .min def
  386. /rminy rlly negY rury negY .min def
  387. /rw rurx rllx sub abs def
  388. /rh rury rlly sub abs def
  389. gsave rot neg rotate
  390. 0 0 transform
  391. negY round cvi /u0y exch def
  392. round cvi /u0x exch def
  393. ixbbox
  394. negY /uury exch def /uurx exch def
  395. negY /ully exch def /ullx exch def
  396. /uw uurx ullx sub def
  397. /uh uury ully sub def
  398. grestore
  399. DEBUG
  400. { (rmatrix: ) print matrix currentmatrix ==
  401. (rFontBBox: ) print [rllx rlly rurx rury] ==
  402. (uFontBBox: ) print [ullx ully uurx uury] ==
  403. flush
  404. } if
  405. grestore
  406. % Disable the character cache, to avoid excessive allocation
  407. % and memory sandbars.
  408. mark cachestatus /upper exch def
  409. cleartomark 0 setcachelimit
  410. % Write the font header.
  411. writefontheader
  412. % Establish an image device for rasterizing characters.
  413. matrix currentmatrix
  414. dup 4 rminx neg put
  415. dup 5 rminy neg put
  416. % Round the width up to a multiple of 8
  417. % so we don't get garbage bits in the last byte of each row.
  418. rw 7 add -8 and rh <ff 00> makeimagedevice
  419. /cdevice exch def
  420. nulldevice % prevent page device switching
  421. cdevice setdevice
  422. % Rasterize each character in turn.
  423. /raster rw 7 add 8 idiv def
  424. /row raster string def
  425. /zerow row length string def
  426. 0 1 Encoding length 1 sub
  427. { /cindex exch def
  428. Encoding cindex get /.notdef ne
  429. { VDEBUG { Encoding cindex get == flush } if
  430. erasepage initgraphics
  431. 0 0 moveto currentpoint transform add
  432. ( ) dup 0 cindex put show
  433. currentpoint transform add exch sub round cvi
  434. /cwidth exch abs def
  435. rw rh devbbox
  436. VDEBUG
  437. { (image bbox: ) print 4 copy 4 2 roll 4 array astore == flush
  438. } if
  439. % Save the device bounding box.
  440. % Note that this is in current device coordinates,
  441. % not PCL (right-handed) coordinates.
  442. /bqx exch def /bpx exch def /bqy exch def /bpy exch def
  443. % Re-render with the character justified to (0,0).
  444. % This may be either the lower left or the upper left corner.
  445. bpx neg bpy neg idtransform moveto
  446. erasepage
  447. VDEBUG { (show point: ) print [ currentpoint transform ] == flush } if
  448. ( ) dup 0 cindex put show
  449. % Find the bounding box. Note that xmin and ymin are now 0,
  450. % xmax1 = xw, and ymax1 = yh.
  451. rw rh devbbox
  452. /xw exch def
  453. % xmin or ymin can be non-zero only if the character is blank.
  454. xw 0 eq
  455. { pop }
  456. { dup 0 ne { (Non-zero xmin! ) print = } { pop } ifelse }
  457. ifelse
  458. /yh exch def
  459. yh 0 eq
  460. { pop }
  461. { dup 0 ne { (Non-zero ymin! ) print = } { pop } ifelse }
  462. ifelse
  463. /xbw xw 7 add 8 idiv def
  464. /xright raster 8 mul xw sub def
  465. % Write the Character Code command.
  466. outfile (\033*c) writestring
  467. outfile cindex write==only
  468. outfile (E) writestring
  469. % Write the Character Definition command.
  470. outfile (\033\(s) writestring
  471. yh xbw mul 16 add
  472. outfile exch write=only
  473. % Record the character position for the .PCM file.
  474. /cfpos outfile fileposition 1 add def
  475. outfile (W\004\000\016\001) writestring
  476. orientation (Orientation) wbyte 0 (Reserved) wbyte
  477. rminx bpx add r0x sub (Left Offset) wword
  478. flipY { rminy bpy add neg } { rminy bqy add } ifelse r0y sub
  479. (Top Offset) wword
  480. xw (Character Width) wword
  481. yh (Character Height) wword
  482. cwidth orientation 2 ge { neg } if 4 mul (Delta X) wword
  483. % Write the character data.
  484. flipY { 0 1 yh 1 sub } { yh 1 sub -1 0 } ifelse
  485. { cdevice exch row copyscanlines
  486. 0 xbw getinterval
  487. CDEBUG
  488. { dup
  489. { 8
  490. { dup 128 ge { (+) } { (.) } ifelse print
  491. 127 and 1 bitshift
  492. }
  493. repeat pop
  494. }
  495. forall (\n) print
  496. }
  497. if
  498. outfile exch writestring
  499. }
  500. for
  501. }
  502. { /bpx 0 def /bpy 0 def /bqx 0 def /bqy 0 def
  503. /cwidth 0 def
  504. /cfpos 0 def
  505. }
  506. ifelse
  507. }
  508. for
  509. % Wrap up.
  510. upper setcachelimit
  511. outfile closefile
  512. nulldevice % prevent page device switching
  513. saved end end end restore
  514. } def
  515. % Provide definitions for testing with older or non-custom interpreters.
  516. /.findzeros where { pop (%END) .skipeof } if
  517. /.findzeros
  518. { userdict begin /zs exch def /zl zs length def
  519. 0 { dup zl ge { exit } if dup zs exch get 0 ne { exit } if 1 add } loop
  520. zl { dup 0 eq { exit } if dup 1 sub zs exch get 0 ne { exit } if 1 sub } loop
  521. exch 3 bitshift exch 3 bitshift
  522. 2 copy lt
  523. { exch zs 1 index -3 bitshift get
  524. { dup 16#80 and 0 ne { exit } if exch 1 add exch 1 bitshift } loop pop
  525. exch zs 1 index -3 bitshift 1 sub get
  526. { dup 1 and 0 ne { exit } if exch 1 sub exch -1 bitshift } loop pop
  527. }
  528. if end
  529. } bind def
  530. %END
  531. /write=only where { pop (%END) .skipeof } if
  532. /w=s 128 string def
  533. /write=only
  534. { w=s cvs writestring
  535. } bind def
  536. %END
  537. %**************** Test
  538. /PCLTEST where {
  539. pop
  540. /DEBUG true def
  541. /CDEBUG true def
  542. /VDEBUG true def
  543. /Times-Roman findfont 10 scalefont setfont
  544. (t.pcf) (w) file
  545. 300 72 div dup scale
  546. 300 writePCL
  547. flush quit
  548. } if