home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
ps
/
psnup
/
nup.pre.ps
< prev
next >
Wrap
Text File
|
1991-02-26
|
14KB
|
550 lines
% 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