home *** CD-ROM | disk | FTP | other *** search
- % nup.pre.ps -- Prelude for n-up printing. $Revision: 4.2 $
- %
- % Ned Batchelder, University of Pennsylvania
- % ned@UPenn.CSnet
- %
- % This PostScript code is Copyright 1986 by Ned Batchelder and the Trustees of
- % the University of Pennsylvania. Permission is granted to freely distribute
- % this file provided that this notice is kept intact and that all its companion
- % files are distributed with it. Modified versions may be distributed provided
- % that all changes are properly documented in the file itself, and that any
- % interesting modifications are reported back to me at the address above.
- %
- % Companions to this file:
- % nup.post.ps
- %
-
- %
- % Glossary:
- % --------
- %
- % sheet: The physical piece of paper (as opposed to page).
- % spot: One of the mini pages on the sheet.
- % page: A collection of marks originally designed to be printed
- % together on a sheet.
- % pod: A new operator which will take the place of a standard one.
- % They're designed to do pretty much the same thing, but a little
- % different (ever see "Invasion of the Body Snatchers"?).
- %
-
- %
- % We use a new dictionary to avoid name conflicts
- %
-
- /$Nup 75 dict def
- $Nup begin
-
- %
- % These three variables define how the page will be laid out.
- % The values are designed to be filled in by 'sed'.
- %
-
- /spots @#@Pages@#@ def % How many spots per sheet?
- /reverse? @#@Rev@#@ def % Should the spots go left to right or vv?
- /startspot @#@Start@#@ def % Which spot should we start with?
-
- %========================<< Spot Transformations >>============================
- %
- % This is an array of transformations. We index this array with the spot number
- % to find the transformation that corresponds to it. These are the CTM's for
- % each spot, not the transforms from default user coordinates to spot
- % coordinates. By placing them in the array in a different order, we print the
- % pages on each sheet in a different place.
- %
- % This code was originally written by Bob Pellegrino at Prime, for Scribe
- % support.
- %
-
- gsave
- /transforms [
- % Get an index for our arrays
-
- /ind 0 def % Compute an index into the value arrays
- [2 4 8 16] {
- spots eq {
- exit
- } if
- /ind ind 1 add def
- } forall
-
- /upright? % Are the spots upright (portrait)?
- [false true false true]
- ind get def
- /numwide % How many spots across?
- [2 2 4 4]
- ind get def
- /sfactor % The scale factor.
- [0.5833 0.4444 0.2916 0.2222]
- ind get def
- /pwidth sfactor 612 mul def
- /pheight sfactor 792 mul def
-
- upright? { % The usable size depends on the orientation
- /width 544 def
- /height 704 def
- } {
- /width 714 def
- /height 462 def
- } ifelse
-
- reverse? { % Is spot one in upper left or lower right?
- spots 1 sub -1 0 % This fills the array backwards.
- } {
- 0 1 spots 1 sub % This fills the array forwards.
- } ifelse {
- /p exch def
- initmatrix % Initialize the matrix
-
- upright? { % Basic origin
- 34 44 translate
- } {
- 537 39 translate
- 90 rotate
- } ifelse
-
- p numwide mod pwidth mul % Translate to the proper spot
- height p numwide idiv 1 add
- pheight mul sub translate
-
- sfactor dup scale % Scale it to size.
-
- matrix currentmatrix % Leave the CTM on the stack.
- } for
- ] def
- grestore
-
- %
- % Initialize the spot number.
- %
-
- /spot startspot def
-
- %=========================<< Pods: - Functions >>==============================
- %
- % Save the old definitions of some important operators
- %
-
- /pods [ % The operators that we'll redefine.
- /showpage /copypage /erasepage
- /initgraphics /initmatrix /initclip
- /defaultmatrix /currentmatrix /setmatrix
- /restore
- /gsave /grestore /grestoreall
- ] def
-
- /+s 128 string dup 0 (+) putinterval def
- /-s 128 string dup 0 (-) putinterval def
- /namestr 128 string def
-
- pods {
- dup namestr cvs % /foo ==> (foo)
- dup length /l exch def %
- -s exch 1 exch putinterval % ==> (-foo)
- systemdict exch get % Get the definition of foo
- -s 0 l 1 add getinterval % Get (-foo)
- exch def % And define one to the other.
- } forall
-
- %=======================<< Path Saving and Restoring >>========================
- %
- % - 'psave' path-obj
- %
- % Psave creates a 'path object' on the stack. The path object is actually an
- % array which rebuilds the current path when made executable and executed.
- % Note that everything is in terms of the current user coordinate system,
- % so that if the coordinate system is different when the path is restored,
- % the path will be different. This can actually be very useful.
- % The path is unaffected by psave.
- % Note that psave and prestore need not be called in a strictly stack-like
- % way. They are unaffected by the order of the calls.
- %
- % (Editorial: a thumbs down to Adobe for their pathforall-charpath restriction,
- % which will keep this from working in the general case).
- %
-
- /psave {
- -gsave
- [
- /newpath load
- {/moveto load}
- {/lineto load}
- {/curveto load}
- {/closepath load}
- pathforall
- ]
- -grestore
- } def
-
- %
- % path-obj `prestore' -
- %
- % Prestore takes a path object as produced by psave, and recreates the path
- % represented therein. A path object is just an array which must be made
- % executable and executed.
- %
-
- /prestore {
- cvx exec
- } def
-
- %
- % Create and store away the default path as a path object.
- %
-
- gsave
- initgraphics clippath
- /page-clip psave def
- grestore
-
- %===========================<< Array Hashing >>================================
- %
- % `Def' can take an array as its key, but it compares by object equivalence,
- % not value. These functions convert arrays into strings, and then use def to
- % associate a value with them in a dictionary.
- %
-
- /astr 128 string def % The string we store things in.
- /numstr 10 string def % Temporary for converting numbers.
- /$arrays 50 dict def % Where we hash them together.
-
- %
- % array `a2s' string
- %
- % a2s converts an array into a string for use in the array hashing functions.
- % We round the values a bit (so as not to be too picky in comparing values),
- % and then string them together.
- %
-
- /a2s {
- /l 0 def % Keep track of the length
- { % The array for `forall' is the arg.
- 100 mul cvi % Round off to two places.
- numstr cvs % Convert to a string.
- astr exch l exch % We're adding to astr at l
- dup length l add % Compute the new length
- /l exch def % remember it.
- putinterval % Store the number in the string
- astr l (:) putinterval % Store a separating ':'
- /l l 1 add def % Increment l again.
- } forall
- astr 0 l 1 sub getinterval % Retrieve all but the last ':'.
- } def
-
- %
- % array value `arrdef' -
- %
- % Takes an array and a value and associates them together.
- %
-
- /arrdef {
- exch a2s exch % Convert the array into a string.
- $arrays 3 1 roll % Stuff the dictionary in the stack.
- put % Associate them together.
- } def
-
- %
- % array `arrload' value true
- % or
- % array `arrload' false
- %
- % Takes an array and returns a boolean indicating if it was found, and if it
- % was, the value.
- %
-
- /arrload {
- a2s % Convert the array into a string.
- $arrays exch % Stuff the dictionary into the stack.
- 2 copy known { % Is this array known?
- get % Retrieve the value.
- true % And label it with `true'.
- } {
- false % Otherwise, label it `false'.
- } ifelse
- } def
-
- %========================<< Graphics State Patching >>=========================
- %
- % First some named matrices:
- %
-
- /m matrix def
- /m2 matrix def
- /m3 matrix def
-
- %
- % oldspot newspot `fix-gstate' -
- %
- % Adjusts the current graphics state to be where it should be.
- % This procedure is used when an old state has been restored, but it should
- % be at a new spot on the sheet now. The two arguments are the spot on the
- % page where the state was saved, and the spot it should be on now.
- % The path and the clipping boundary are saved in user coordinates, then
- % the transform is adjusted, and the paths are restored.
- %
-
- /fix-gstate {
- psave % Save the current path.
- clippath psave % Save the clip boundary.
-
- 4 2 roll % Bring the two args back up.
- fix-trans % Fix up the transformation.
-
- -initclip % Trash the old clip boundary.
- prestore clip % Recreate the clip boundary.
- prestore % Recreate the path.
- } def
-
- %
- % oldspot newspot `fix-trans' -
- %
- % Adjusts the transformation from oldspot to newspot.
- % This procedure is called when an old matrix has been reinstated, as with
- % setmatrix. It is also called by fix-gstate.
- %
-
- /fix-trans {
- /newspot exch def
- /oldspot exch def
-
- m -currentmatrix % Get User x Nup x Def
- transforms oldspot get % Get Nup x Def
- m2 invertmatrix % Get 1/(Nup x Def)
- m3 concatmatrix % End up with just User!
- transforms newspot get % The new Nup x Def
- m concatmatrix % The new transform!
- -setmatrix % Install it.
- } def
-
- %==========================<< Page Delimiting >>===============================
- %
- % - `page-edges' -
- %
- % Page-edges prints the edges of the pages.
- %
-
- /page-edges {
- -gsave
-
- +initgraphics % We want the 'fake' mini-page matrix,
- -initclip % But the old paper-sized clip boundary.
-
- 0.0 setlinewidth % Produces a hairline
-
- newpath % Outline a standard page.
- 0 0 moveto
- 0 792 lineto
- 612 792 lineto
- 612 0 lineto
- closepath
- stroke
-
- -grestore
- } def
-
- %======================<< Pods: Graphics Initialization >>=====================
- %
- % New defaultmatrix
- % Simply return a copy of the proper matrix from our table of transforms.
- %
-
- /+defaultmatrix {
- transforms spot get % Get the proper transform
- exch copy % And copy it (protect the original)
- } def
-
- %
- % New initmatrix
- %
-
- /+initmatrix {
- m +defaultmatrix -setmatrix
- } def
-
- %
- % New initgraphics
- %
-
- /+initgraphics {
- -initgraphics % Reset everything
- +initmatrix % But get the new matrix
- +initclip % And the new clip boundary.
- } def
-
- %
- % New initclip
- %
-
- /+initclip {
- psave % Push a path object on the stack.
- m -currentmatrix % Push the current matrix on the stack.
-
- -initclip % Initialize the clip boundary.
- +defaultmatrix % Set the proper matrix
- page-clip prestore % Build the mini default clip path.
- clip % And use it to clip.
-
- -setmatrix % Restore the old matrix
- prestore % Restore the path off the stack
- } def
-
- %=======================<< Pods: Page Printing >>==============================
- %
- % New showpage. This is what the book claims showpage is equivalent to.
- %
-
- /+showpage {
- +copypage
- +erasepage
- +initgraphics
- } def
-
- %
- % New copypage
- %
-
- /+copypage {
- page-edges % Print the edges of the page.
- spot 1 add % Increment the spot number
-
- dup spots eq { % If this sheet is full:
- -copypage % Print the sheet,
- -erasepage % Clear the sheet,
- pop 0 % And set spot back to zero.
- } if
-
- /spot exch def % Assign the number back to spot.
- } def
-
- %
- % New erasepage. Fill the clipping boundary with white.
- %
-
- /+erasepage {
- -gsave % "erasepage doesn't affect the current Graphics State"
-
- +initgraphics % Make sure we know what our state is.
- page-clip prestore % Get the clip path.
- 1 setgray % "User white".
- fill % Paint it.
-
- -grestore
- } def
-
- %======================<< Pods: Saving and Restoring >>========================
- %
- % We push the value of spot on the stack and then restore, which will bring
- % back the value of spot at the time of the save. Then we can compare them
- % and know how to fix up the graphics state.
- %
-
- /+restore {
- spot exch % The current spot is protected
- -restore % Restore the state.
- dup spot ne { % If from a different spot,
- dup spot exch fix-gstate % Fix the graphics state.
- /spot exch def % And fix up the spot number.
- } {
- pop % Clean up the stack.
- } ifelse
- } def
-
- %
- % Gsave now records the spot number with the current matrix.
- %
-
- /+gsave {
- -gsave % Save the state
- m -currentmatrix % Get the current matrix
- spot arrdef % And associate spot with it.
- } def
-
- %
- % Grestore is much like restore, but it doesn't have the benefit of variables
- % being restored. The current matrix is hashed with the spot number, and when
- % the state is restored, we look up the current matrix and fix the graphics
- % state.
- %
-
- /+grestore {
- -grestore % Do the restore
- m -currentmatrix % Get the current matrix
- arrload { % Look it up. If there,
- spot fix-gstate % Use value and spot to fix the state.
- } if
- } def
-
- %
- % Grestoreall works like grestore
- %
-
- /+grestoreall {
- -grestoreall % Do the restore.
- m -currentmatrix % Get the CTM.
- arrload { % Look it up. If there,
- spot fix-gstate % Use value and spot to fix the state.
- } if
- } def
-
- %======================<< Pods: Matrix Manipulation >>=========================
- %
- % Currentmatrix must now record the fact that it gave away a certain matrix, so
- % that setmatrix can later look it up and fix it to the right spot on the
- % sheet.
- %
-
- /+currentmatrix {
- -currentmatrix
- dup spot arrdef
- } def
-
- %
- % Setmatrix now looks up the matrix. If it is in the dictionary, then we know
- % what spot was in effect originally, and we can patch it to the current spot.
- % If we haven't seen it, tough.
- %
-
- /+setmatrix {
- dup -setmatrix
- dup %$Debug
- arrload {
- spot fix-trans % The old spot was pushed by `arrload'
- pop %$Debug
- } {
- (bad setmatrix: ) print == %$Debug
- } ifelse
- } def
-
- %=======================<< The Pod Switcheroo >>===============================
- %
- % Now we define the replacements.
- %
-
- pods {
- /pod exch def % Save the name we were passed
- userdict pod % We'll redefine it in userdict
- [ % We're building an executable array
- $Nup /begin load % "$Nup begin"
- pod namestr cvs % /foo ==> (foo)
- dup length /l exch def %
- +s exch 1 exch putinterval % ==> (+foo)
- +s 0 l 1 add getinterval %
- cvn cvx % ==> /+foo ==> +foo
- /end load % "end"
- ] cvx put % "} def"
- } forall
-
- %============================<< Cleaning Up >>=================================
- %
- % Pop $Nup off the dictionary stack.
- %
-
- end
-
- %
- % Start things rolling
- %
-
- initgraphics
-
- % end of nup.pre.ps
-