home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 21 PDrivers / 21-PDrivers.zip / psnupos2.zip / nup.pre.ps < prev    next >
Text File  |  1995-01-24  |  14KB  |  550 lines

  1. %! nup.pre.ps -- Prelude for n-up printing. $Revision: 4.2 $
  2. %
  3. % Ned Batchelder, University of Pennsylvania
  4. % ned@UPenn.CSnet
  5. %
  6. % This PostScript code is Copyright 1986 by Ned Batchelder and the Trustees of
  7. % the University of Pennsylvania. Permission is granted to freely distribute
  8. % this file provided that this notice is kept intact and that all its companion
  9. % files are distributed with it. Modified versions may be distributed provided 
  10. % that all changes are properly documented in the file itself, and that any
  11. % interesting modifications are reported back to me at the address above.
  12. %
  13. % Companions to this file:
  14. %    nup.post.ps
  15. %
  16.  
  17. %
  18. % Glossary:
  19. % --------
  20. %
  21. %    sheet:    The physical piece of paper (as opposed to page).
  22. %    spot:    One of the mini pages on the sheet.
  23. %    page:    A collection of marks originally designed to be printed
  24. %        together on a sheet.
  25. %    pod:    A new operator which will take the place of a standard one.
  26. %        They're designed to do pretty much the same thing, but a little
  27. %        different (ever see "Invasion of the Body Snatchers"?).
  28. %
  29.  
  30. %
  31. % We use a new dictionary to avoid name conflicts
  32. %
  33.  
  34. /$Nup 75 dict def
  35. $Nup begin
  36.  
  37. %
  38. % These three variables define how the page will be laid out.
  39. % The values are designed to be filled in by 'sed'.
  40. %
  41.  
  42. /spots @#@Pages@#@ def        % How many spots per sheet?
  43. /reverse? @#@Rev@#@ def        % Should the spots go left to right or vv?
  44. /startspot @#@Start@#@ def    % Which spot should we start with?
  45.  
  46. %========================<< Spot Transformations >>============================
  47. %
  48. % This is an array of transformations. We index this array with the spot number
  49. % to find the transformation that corresponds to it. These are the CTM's for
  50. % each spot, not the transforms from default user coordinates to spot
  51. % coordinates. By placing them in the array in a different order, we print the
  52. % pages on each sheet in a different place.
  53. %
  54. % This code was originally written by Bob Pellegrino at Prime, for Scribe
  55. % support.
  56. %
  57.  
  58. gsave
  59. /transforms [        
  60.     % Get an index for our arrays
  61.     
  62.     /ind 0 def        % Compute an index into the value arrays
  63.     [2 4 8 16] {
  64.         spots eq {
  65.             exit
  66.         } if
  67.         /ind ind 1 add def
  68.     } forall
  69.  
  70.     /upright?        % Are the spots upright (portrait)?
  71.         [false true false true]
  72.         ind get def
  73.     /numwide        % How many spots across?
  74.         [2 2 4 4]
  75.         ind get def
  76.     /sfactor        % The scale factor.
  77.         [0.5833 0.4444 0.2916 0.2222]
  78.         ind get def
  79.     /pwidth sfactor 612 mul def
  80.     /pheight sfactor 792 mul def
  81.  
  82.     upright? {        % The usable size depends on the orientation
  83.         /width 544 def
  84.         /height 704 def
  85.     } {
  86.         /width 714 def
  87.         /height 462 def
  88.     } ifelse
  89.  
  90.     reverse? {        % Is spot one in upper left or lower right?
  91.         spots 1 sub  -1  0    % This fills the array backwards.
  92.     } {
  93.         0  1  spots 1 sub    % This fills the array forwards.
  94.     } ifelse {
  95.         /p exch def
  96.         initmatrix        % Initialize the matrix
  97.         
  98.         upright? {        % Basic origin
  99.             34 44 translate
  100.         } {
  101.             537 39 translate
  102.             90 rotate
  103.         } ifelse
  104.  
  105.         p numwide mod pwidth mul    % Translate to the proper spot
  106.         height p numwide idiv 1 add
  107.         pheight mul sub translate
  108.  
  109.         sfactor dup scale        % Scale it to size.
  110.  
  111.         matrix currentmatrix        % Leave the CTM on the stack.
  112.     } for
  113. ] def
  114. grestore
  115.  
  116. %
  117. % Initialize the spot number.
  118. %
  119.  
  120. /spot startspot def
  121.  
  122. %=========================<< Pods: - Functions >>==============================
  123. %
  124. % Save the old definitions of some important operators
  125. %
  126.  
  127. /pods [                % The operators that we'll redefine.
  128.     /showpage /copypage /erasepage
  129.     /initgraphics /initmatrix /initclip
  130.     /defaultmatrix /currentmatrix /setmatrix
  131.     /restore
  132.     /gsave /grestore /grestoreall
  133. ] def
  134.  
  135. /+s 128 string dup 0 (+) putinterval def
  136. /-s 128 string dup 0 (-) putinterval def
  137. /namestr 128 string def
  138.  
  139. pods {
  140.     dup namestr cvs            % /foo ==> (foo)
  141.     dup length /l exch def        %
  142.     -s exch 1 exch putinterval    %      ==> (-foo)
  143.     systemdict exch get        % Get the definition of foo
  144.     -s 0 l 1 add getinterval    % Get (-foo)
  145.     exch def            % And define one to the other.
  146. } forall
  147.  
  148. %=======================<< Path Saving and Restoring >>========================
  149. %
  150. % - 'psave' path-obj
  151. %
  152. % Psave creates a 'path object' on the stack. The path object is actually an
  153. % array which rebuilds the current path when made executable and executed.
  154. % Note that everything is in terms of the current user coordinate system,
  155. % so that if the coordinate system is different when the path is restored,
  156. % the path will be different. This can actually be very useful.
  157. % The path is unaffected by psave.
  158. % Note that psave and prestore need not be called in a strictly stack-like
  159. % way. They are unaffected by the order of the calls.
  160. %
  161. % (Editorial: a thumbs down to Adobe for their pathforall-charpath restriction,
  162. % which will keep this from working in the general case).
  163. %
  164.  
  165. /psave {
  166.     -gsave
  167.     [
  168.     /newpath load
  169.     {/moveto load}
  170.     {/lineto load}
  171.     {/curveto load}
  172.     {/closepath load}
  173.     pathforall
  174.     ]
  175.     -grestore
  176. } def
  177.  
  178. %
  179. % path-obj `prestore' -
  180. %
  181. % Prestore takes a path object as produced by psave, and recreates the path 
  182. % represented therein. A path object is just an array which must be made
  183. % executable and executed.
  184. %
  185.  
  186. /prestore {
  187.     cvx exec
  188. } def
  189.  
  190. %
  191. % Create and store away the default path as a path object.
  192. %
  193.  
  194. gsave
  195. initgraphics clippath
  196. /page-clip psave def
  197. grestore
  198.  
  199. %===========================<< Array Hashing >>================================
  200. %
  201. % `Def' can take an array as its key, but it compares by object equivalence,
  202. % not value. These functions convert arrays into strings, and then use def to
  203. % associate a value with them in a dictionary.
  204. %
  205.  
  206. /astr 128 string def        % The string we store things in.
  207. /numstr 10 string def        % Temporary for converting numbers.
  208. /$arrays 50 dict def        % Where we hash them together.
  209.  
  210. %
  211. % array `a2s' string
  212. %
  213. % a2s converts an array into a string for use in the array hashing functions.
  214. % We round the values a bit (so as not to be too picky in comparing values),
  215. % and then string them together.
  216. %
  217.  
  218. /a2s {
  219.     /l 0 def            % Keep track of the length
  220.     {                % The array for `forall' is the arg.
  221.         100 mul cvi        % Round off to two places.
  222.         numstr cvs        % Convert to a string.
  223.         astr exch l exch    % We're adding to astr at l
  224.         dup length l add    % Compute the new length
  225.         /l exch def        % remember it.
  226.         putinterval        % Store the number in the string
  227.         astr l (:) putinterval    % Store a separating ':'
  228.         /l l 1 add def        % Increment l again.
  229.     } forall
  230.     astr 0 l 1 sub getinterval    % Retrieve all but the last ':'.
  231. } def
  232.  
  233. %
  234. % array value `arrdef' -
  235. %
  236. % Takes an array and a value and associates them together.
  237. %
  238.  
  239. /arrdef {
  240.     exch a2s exch        % Convert the array into a string.
  241.     $arrays 3 1 roll    % Stuff the dictionary in the stack.
  242.     put            % Associate them together.
  243. } def
  244.  
  245. %
  246. % array `arrload' value true
  247. %   or
  248. % array `arrload' false
  249. %
  250. % Takes an array and returns a boolean indicating if it was found, and if it
  251. % was, the value.
  252. %
  253.  
  254. /arrload {
  255.     a2s            % Convert the array into a string.
  256.     $arrays exch        % Stuff the dictionary into the stack.
  257.     2 copy known {        % Is this array known?
  258.         get        % Retrieve the value.
  259.         true        % And label it with `true'.
  260.     } {
  261.         false        % Otherwise, label it `false'.
  262.     } ifelse
  263. } def
  264.  
  265. %========================<< Graphics State Patching >>=========================
  266. %
  267. % First some named matrices:
  268. %
  269.  
  270. /m  matrix def
  271. /m2 matrix def
  272. /m3 matrix def
  273.  
  274. %
  275. % oldspot newspot `fix-gstate' -
  276. %
  277. % Adjusts the current graphics state to be where it should be.
  278. % This procedure is used when an old state has been restored, but it should
  279. % be at a new spot on the sheet now. The two arguments are the spot on the
  280. % page where the state was saved, and the spot it should be on now.
  281. % The path and the clipping boundary are saved in user coordinates, then
  282. % the transform is adjusted, and the paths are restored.
  283. %
  284.  
  285. /fix-gstate {
  286.     psave                % Save the current path.
  287.     clippath psave            % Save the clip boundary.
  288.  
  289.     4 2 roll            % Bring the two args back up.
  290.     fix-trans            % Fix up the transformation.
  291.  
  292.     -initclip            % Trash the old clip boundary.
  293.     prestore clip            % Recreate the clip boundary.
  294.     prestore            % Recreate the path.
  295. } def
  296.  
  297. %
  298. % oldspot newspot `fix-trans' -
  299. %
  300. % Adjusts the transformation from oldspot to newspot.
  301. % This procedure is called when an old matrix has been reinstated, as with
  302. % setmatrix. It is also called by fix-gstate.
  303. %
  304.  
  305. /fix-trans {
  306.     /newspot exch def
  307.     /oldspot exch def
  308.  
  309.     m -currentmatrix        % Get User x Nup x Def
  310.     transforms oldspot get        % Get Nup x Def
  311.     m2 invertmatrix            % Get 1/(Nup x Def)
  312.     m3 concatmatrix            % End up with just User!
  313.     transforms newspot get        % The new Nup x Def
  314.     m concatmatrix            % The new transform!
  315.     -setmatrix            % Install it.
  316. } def
  317.  
  318. %==========================<< Page Delimiting >>===============================
  319. %
  320. % - `page-edges' -
  321. %
  322. % Page-edges prints the edges of the pages.
  323. %
  324.  
  325. /page-edges {
  326.     -gsave
  327.  
  328.     +initgraphics        % We want the 'fake' mini-page matrix,
  329.     -initclip        % But the old paper-sized clip boundary.
  330.  
  331.     0.0 setlinewidth    % Produces a hairline
  332.  
  333.     newpath            % Outline a standard page.
  334.     0 0 moveto
  335.     0 792 lineto
  336.     612 792 lineto
  337.     612 0 lineto
  338.     closepath
  339.     stroke
  340.  
  341.     -grestore
  342. } def
  343.     
  344. %======================<< Pods: Graphics Initialization >>=====================
  345. %
  346. % New defaultmatrix
  347. % Simply return a copy of the proper matrix from our table of transforms.
  348. %
  349.  
  350. /+defaultmatrix {
  351.     transforms spot get        % Get the proper transform
  352.     exch copy            % And copy it (protect the original)
  353. } def
  354.  
  355. %
  356. % New initmatrix
  357. %
  358.  
  359. /+initmatrix {
  360.     m +defaultmatrix -setmatrix
  361. } def
  362.  
  363. %
  364. % New initgraphics
  365. %
  366.  
  367. /+initgraphics {
  368.     -initgraphics        % Reset everything
  369.     +initmatrix        % But get the new matrix
  370.     +initclip        % And the new clip boundary.
  371. } def
  372.  
  373. %
  374. % New initclip
  375. %
  376.  
  377. /+initclip {
  378.     psave            % Push a path object on the stack.
  379.     m -currentmatrix    % Push the current matrix on the stack.
  380.  
  381.     -initclip        % Initialize the clip boundary.
  382.     +defaultmatrix        % Set the proper matrix
  383.     page-clip prestore    % Build the mini default clip path.
  384.     clip            % And use it to clip.
  385.  
  386.     -setmatrix        % Restore the old matrix
  387.     prestore        % Restore the path off the stack
  388. } def
  389.  
  390. %=======================<< Pods: Page Printing >>==============================
  391. %
  392. % New showpage. This is what the book claims showpage is equivalent to.
  393. %
  394.  
  395. /+showpage {
  396.     +copypage
  397.     +erasepage
  398.     +initgraphics
  399. } def
  400.  
  401. %
  402. % New copypage
  403. %
  404.  
  405. /+copypage {
  406.     page-edges        % Print the edges of the page.
  407.     spot 1 add        % Increment the spot number
  408.  
  409.     dup spots eq {        % If this sheet is full:
  410.         -copypage    % Print the sheet,
  411.         -erasepage    % Clear the sheet,
  412.         pop 0        % And set spot back to zero.
  413.     } if
  414.  
  415.     /spot exch def        % Assign the number back to spot.
  416. } def
  417.  
  418. %
  419. % New erasepage. Fill the clipping boundary with white.
  420. %
  421.  
  422. /+erasepage {
  423.     -gsave        % "erasepage doesn't affect the current Graphics State"
  424.  
  425.     +initgraphics            % Make sure we know what our state is.
  426.     page-clip prestore        % Get the clip path.
  427.     1 setgray            % "User white".
  428.     fill                % Paint it.
  429.  
  430.     -grestore
  431. } def
  432.  
  433. %======================<< Pods: Saving and Restoring >>========================
  434. %
  435. % We push the value of spot on the stack and then restore, which will bring
  436. % back the value of spot at the time of the save. Then we can compare them
  437. % and know how to fix up the graphics state.
  438. %
  439.  
  440. /+restore {
  441.     spot exch                % The current spot is protected
  442.     -restore                % Restore the state.
  443.     dup spot ne {                % If from a different spot,
  444.         dup spot exch fix-gstate    % Fix the graphics state.
  445.         /spot exch def            % And fix up the spot number.
  446.     } {
  447.         pop                % Clean up the stack.
  448.     } ifelse
  449. } def
  450.  
  451. %
  452. % Gsave now records the spot number with the current matrix.
  453. %
  454.  
  455. /+gsave {
  456.     -gsave                % Save the state
  457.     m -currentmatrix        % Get the current matrix
  458.     spot arrdef            % And associate spot with it.
  459. } def
  460.  
  461. %
  462. % Grestore is much like restore, but it doesn't have the benefit of variables
  463. % being restored. The current matrix is hashed with the spot number, and when
  464. % the state is restored, we look up the current matrix and fix the graphics
  465. % state.
  466. %
  467.  
  468. /+grestore {
  469.     -grestore            % Do the restore
  470.     m -currentmatrix        % Get the current matrix
  471.     arrload {            % Look it up. If there,
  472.         spot fix-gstate        % Use value and spot to fix the state.
  473.     } if
  474. } def
  475.  
  476. %
  477. % Grestoreall works like grestore
  478. %
  479.  
  480. /+grestoreall {
  481.     -grestoreall            % Do the restore.
  482.     m -currentmatrix        % Get the CTM.
  483.     arrload {            % Look it up. If there,
  484.         spot fix-gstate        % Use value and spot to fix the state.
  485.     } if
  486. } def
  487.  
  488. %======================<< Pods: Matrix Manipulation >>=========================
  489. %
  490. % Currentmatrix must now record the fact that it gave away a certain matrix, so
  491. % that setmatrix can later look it up and fix it to the right spot on the
  492. % sheet.
  493. %
  494.  
  495. /+currentmatrix {
  496.     -currentmatrix
  497.     dup spot arrdef
  498. } def
  499.  
  500. %
  501. % Setmatrix now looks up the matrix. If it is in the dictionary, then we know
  502. % what spot was in effect originally, and we can patch it to the current spot.
  503. % If we haven't seen it, tough.
  504. %
  505.  
  506. /+setmatrix {
  507.     dup -setmatrix
  508.     dup                %$Debug
  509.     arrload {
  510.         spot fix-trans        % The old spot was pushed by `arrload'
  511.         pop            %$Debug
  512.     } {
  513.         (bad setmatrix: ) print ==    %$Debug
  514.     } ifelse
  515. } def
  516.  
  517. %=======================<< The Pod Switcheroo >>===============================
  518. %
  519. % Now we define the replacements.
  520. %
  521.  
  522. pods {
  523.     /pod exch def            % Save the name we were passed
  524.     userdict pod            % We'll redefine it in userdict
  525.     [                % We're building an executable array
  526.     $Nup /begin load        % "$Nup begin"
  527.     pod namestr cvs            % /foo ==> (foo)
  528.     dup length /l exch def        %
  529.     +s exch 1 exch putinterval    % ==> (+foo)
  530.     +s 0 l 1 add getinterval    %
  531.     cvn cvx                % ==> /+foo ==> +foo
  532.     /end load            % "end"
  533.     ] cvx put            % "} def"
  534. } forall
  535.  
  536. %============================<< Cleaning Up >>=================================
  537. %
  538. % Pop $Nup off the dictionary stack.
  539. %
  540.  
  541. end
  542.  
  543. %
  544. % Start things rolling
  545. %
  546.  
  547. initgraphics
  548.  
  549. % end of nup.pre.ps
  550.