clewis@ferret.ocunix.on.ca (Chris Lewis) (02/16/91)
Submitted-by: Chris Lewis <clewis@ferret.ocunix.on.ca> Posting-number: Volume 16, Issue 104 Archive-name: psnup/part01 The psnup program takes arbitrary postscript as input, and wraps it with additional postscript to implement n-up printing. The input postscript is taken from the file arguments, or stdin if no arguments are specified. The output is sent to stdout. Chris ------ #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: README MANIFEST Makefile nup.post.ps nup.pre.ps psc.c # psnup.1 psnup.sh # Wrapped by clewis@ecicrl on Thu Feb 14 01:49:36 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 1 (of 1)."' if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(2920 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' X Nup is designed to be used as a wrap around any PostScript file. XSimply prepend nup.pre.ps, and append nup.post.ps, and it should work. There Xare three tokens in nup.pre.ps which need to be changed before sending it to Xthe printer: X @#@Pages@#@ should be changed to the number of pages per sheet. X 2 gets you 2-up, 16 gets you 16-up, etc. X @#@Rev@#@ Should be changed to 'true' or 'false', depending on X whether you want the first page in the file to X appear in the lower right corner of the page or the X upper left. This is designed to accomodate files X which have had their pages reversed. X @#@Start@#@ Should be changed to the number of the spot where X you want the first page to appear. This is to allow X for 2-up printing with the first page on the right X hand side, like a book. X XI change these for each run with sed in a shell script that puts it all Xtogether. X Since Nup is heavily commented, I have provided psc, which Xcompresses out white space and comments to provide a more efficient Xdownloadable file. I call the compressed versions nup.pro and nup.epi, which Xis in keeping with Adobe's naming scheme. X As the header for the file indicates, I'd like to see any Xinteresting modification of Nup. Deletion of unwanted features ("I got rid Xof those borders") does not count as interesting. X Finally, If you have produced any other code to do things like this, Xannounce it on the net. There are lots of people, including me, who would Xlike to see it. X X --Ned Batchelder X (ned@UPenn.CSNet) X------------------------------------------------- XIncluded with this release is a shell script (psnup) that acts as a user Xinterface to Ned's "nup" code. You hand it arbitrary Postscript code, and Xit pre and post appends nup.pro and nup.epi, substituting in the above Xmentioned parameters as it goes. See the manual page that I've Xincluded. Edit the makefile and go. The only parameter I believe Xyou need is the optional definition of "-Dindex=strchr" in the Makefile Xif your system doesn't have the str??? routines (ie: as vanilla BSD/V7). XInstallation consists simply of copying psnup to your local bin directory Xand copying the manual page to your local manual page directory. X XTo make things simpler, psnup is generated by inserting in the nup.pro Xand nup.epi into marked places in the psnup.sh file. In this manner Xyou don't need a library directory, and psnup is entirely self-contained Xin one file. I have made absolutely no changes to Ned's code, other than Xto remove a "%!" at the beginning of nup.pre.ps which will fool some XPostscript interpreters (eg: Pageview). Psnup is careful to maintain Xtrailing control-d's and the Document Formatting Convention if the input Xpostscript has them. X XA number of people have been requesting psnup, but I tried to get a hold Xof Ned before posting it. I've been unable to contact him. So, here it Xis. X X Chris Lewis X (clewis@ferret.ocunix.on.ca) END_OF_FILE if test 2920 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(468 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X---------------------------------------------------------- X README 1 README file. X MANIFEST 1 This shipping list X Makefile 1 Makefile X nup.post.ps 1 n-up epilog code X nup.pre.ps 1 n-up prolog code X psc.c 1 Postscript compactor X psnup.1 1 psnup manual page X psnup.sh 1 psnup shell script END_OF_FILE if test 468 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(739 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# Makefile from the nup distribution X XDir = /usr/lib/ps X# Delete the -Dindex=strchr on BSD systems without str*** routines. XCFLAGS = -O -Dindex=strchr X Xall: nup.pro nup.epi psnup X Xnup.pro: nup.pre.ps psc X psc <nup.pre.ps >nup.pro Xnup.epi: nup.post.ps psc X psc <nup.post.ps >nup.epi Xpsc: psc.c X $(CC) $(CFLAGS) -o psc psc.c X Xpsnup: nup.pro nup.epi psnup.sh X sed -e '/@@@nup.pro@@@/r nup.pro' \ X -e '/@@@nup.pro@@@/d' \ X -e '/@@@nup.epi@@@/r nup.epi' \ X -e '/@@@nup.epi@@@/d' \ X psnup.sh > t X mv t psnup X chmod 755 psnup X Xinstall: $(Dir)/nup.pro $(Dir)/nup.epi X$(Dir)/nup.pro: nup.pro X cp nup.pro $(Dir) X$(Dir)/nup.epi: nup.epi X cp nup.epi $(Dir) X Xtidy: X -rm -f *.BAK *.CKP '#'* core Xclean: X -rm -f nup.pro nup.epi psc psnup END_OF_FILE if test 739 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'nup.post.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'nup.post.ps'\" else echo shar: Extracting \"'nup.post.ps'\" \(713 characters\) sed "s/^X//" >'nup.post.ps' <<'END_OF_FILE' X%! nup.post.ps -- Postlude for n-up printing. $Revision: 4.2 $ X% X% Ned Batchelder, University of Pennsylvania X% ned@UPenn.CSnet X% X% This PostScript code is Copyright 1986 by Ned Batchelder and the Trustees of X% the University of Pennsylvania. Permission is granted to freely distribute X% this file provided that this notice is kept intact and that all its X% companion % files are distributed with it. Modified versions may be X% distributed provided % that all changes are properly documented in the file X% itself, and that any % interesting modifications are reported back to me at X% the address above. X% X% Companions to this file: X% nup.pre.ps X% X X$Nup begin Xspot 0 ne { X -showpage X} if Xend X X% end of nup.post.ps END_OF_FILE if test 713 -ne `wc -c <'nup.post.ps'`; then echo shar: \"'nup.post.ps'\" unpacked with wrong size! fi # end of 'nup.post.ps' fi if test -f 'nup.pre.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'nup.pre.ps'\" else echo shar: Extracting \"'nup.pre.ps'\" \(13597 characters\) sed "s/^X//" >'nup.pre.ps' <<'END_OF_FILE' X% nup.pre.ps -- Prelude for n-up printing. $Revision: 4.2 $ X% X% Ned Batchelder, University of Pennsylvania X% ned@UPenn.CSnet X% X% This PostScript code is Copyright 1986 by Ned Batchelder and the Trustees of X% the University of Pennsylvania. Permission is granted to freely distribute X% this file provided that this notice is kept intact and that all its companion X% files are distributed with it. Modified versions may be distributed provided X% that all changes are properly documented in the file itself, and that any X% interesting modifications are reported back to me at the address above. X% X% Companions to this file: X% nup.post.ps X% X X% X% Glossary: X% -------- X% X% sheet: The physical piece of paper (as opposed to page). X% spot: One of the mini pages on the sheet. X% page: A collection of marks originally designed to be printed X% together on a sheet. X% pod: A new operator which will take the place of a standard one. X% They're designed to do pretty much the same thing, but a little X% different (ever see "Invasion of the Body Snatchers"?). X% X X% X% We use a new dictionary to avoid name conflicts X% X X/$Nup 75 dict def X$Nup begin X X% X% These three variables define how the page will be laid out. X% The values are designed to be filled in by 'sed'. X% X X/spots @#@Pages@#@ def % How many spots per sheet? X/reverse? @#@Rev@#@ def % Should the spots go left to right or vv? X/startspot @#@Start@#@ def % Which spot should we start with? X X%========================<< Spot Transformations >>============================ X% X% This is an array of transformations. We index this array with the spot number X% to find the transformation that corresponds to it. These are the CTM's for X% each spot, not the transforms from default user coordinates to spot X% coordinates. By placing them in the array in a different order, we print the X% pages on each sheet in a different place. X% X% This code was originally written by Bob Pellegrino at Prime, for Scribe X% support. X% X Xgsave X/transforms [ X % Get an index for our arrays X X /ind 0 def % Compute an index into the value arrays X [2 4 8 16] { X spots eq { X exit X } if X /ind ind 1 add def X } forall X X /upright? % Are the spots upright (portrait)? X [false true false true] X ind get def X /numwide % How many spots across? X [2 2 4 4] X ind get def X /sfactor % The scale factor. X [0.5833 0.4444 0.2916 0.2222] X ind get def X /pwidth sfactor 612 mul def X /pheight sfactor 792 mul def X X upright? { % The usable size depends on the orientation X /width 544 def X /height 704 def X } { X /width 714 def X /height 462 def X } ifelse X X reverse? { % Is spot one in upper left or lower right? X spots 1 sub -1 0 % This fills the array backwards. X } { X 0 1 spots 1 sub % This fills the array forwards. X } ifelse { X /p exch def X initmatrix % Initialize the matrix X X upright? { % Basic origin X 34 44 translate X } { X 537 39 translate X 90 rotate X } ifelse X X p numwide mod pwidth mul % Translate to the proper spot X height p numwide idiv 1 add X pheight mul sub translate X X sfactor dup scale % Scale it to size. X X matrix currentmatrix % Leave the CTM on the stack. X } for X] def Xgrestore X X% X% Initialize the spot number. X% X X/spot startspot def X X%=========================<< Pods: - Functions >>============================== X% X% Save the old definitions of some important operators X% X X/pods [ % The operators that we'll redefine. X /showpage /copypage /erasepage X /initgraphics /initmatrix /initclip X /defaultmatrix /currentmatrix /setmatrix X /restore X /gsave /grestore /grestoreall X] def X X/+s 128 string dup 0 (+) putinterval def X/-s 128 string dup 0 (-) putinterval def X/namestr 128 string def X Xpods { X dup namestr cvs % /foo ==> (foo) X dup length /l exch def % X -s exch 1 exch putinterval % ==> (-foo) X systemdict exch get % Get the definition of foo X -s 0 l 1 add getinterval % Get (-foo) X exch def % And define one to the other. X} forall X X%=======================<< Path Saving and Restoring >>======================== X% X% - 'psave' path-obj X% X% Psave creates a 'path object' on the stack. The path object is actually an X% array which rebuilds the current path when made executable and executed. X% Note that everything is in terms of the current user coordinate system, X% so that if the coordinate system is different when the path is restored, X% the path will be different. This can actually be very useful. X% The path is unaffected by psave. X% Note that psave and prestore need not be called in a strictly stack-like X% way. They are unaffected by the order of the calls. X% X% (Editorial: a thumbs down to Adobe for their pathforall-charpath restriction, X% which will keep this from working in the general case). X% X X/psave { X -gsave X [ X /newpath load X {/moveto load} X {/lineto load} X {/curveto load} X {/closepath load} X pathforall X ] X -grestore X} def X X% X% path-obj `prestore' - X% X% Prestore takes a path object as produced by psave, and recreates the path X% represented therein. A path object is just an array which must be made X% executable and executed. X% X X/prestore { X cvx exec X} def X X% X% Create and store away the default path as a path object. X% X Xgsave Xinitgraphics clippath X/page-clip psave def Xgrestore X X%===========================<< Array Hashing >>================================ X% X% `Def' can take an array as its key, but it compares by object equivalence, X% not value. These functions convert arrays into strings, and then use def to X% associate a value with them in a dictionary. X% X X/astr 128 string def % The string we store things in. X/numstr 10 string def % Temporary for converting numbers. X/$arrays 50 dict def % Where we hash them together. X X% X% array `a2s' string X% X% a2s converts an array into a string for use in the array hashing functions. X% We round the values a bit (so as not to be too picky in comparing values), X% and then string them together. X% X X/a2s { X /l 0 def % Keep track of the length X { % The array for `forall' is the arg. X 100 mul cvi % Round off to two places. X numstr cvs % Convert to a string. X astr exch l exch % We're adding to astr at l X dup length l add % Compute the new length X /l exch def % remember it. X putinterval % Store the number in the string X astr l (:) putinterval % Store a separating ':' X /l l 1 add def % Increment l again. X } forall X astr 0 l 1 sub getinterval % Retrieve all but the last ':'. X} def X X% X% array value `arrdef' - X% X% Takes an array and a value and associates them together. X% X X/arrdef { X exch a2s exch % Convert the array into a string. X $arrays 3 1 roll % Stuff the dictionary in the stack. X put % Associate them together. X} def X X% X% array `arrload' value true X% or X% array `arrload' false X% X% Takes an array and returns a boolean indicating if it was found, and if it X% was, the value. X% X X/arrload { X a2s % Convert the array into a string. X $arrays exch % Stuff the dictionary into the stack. X 2 copy known { % Is this array known? X get % Retrieve the value. X true % And label it with `true'. X } { X false % Otherwise, label it `false'. X } ifelse X} def X X%========================<< Graphics State Patching >>========================= X% X% First some named matrices: X% X X/m matrix def X/m2 matrix def X/m3 matrix def X X% X% oldspot newspot `fix-gstate' - X% X% Adjusts the current graphics state to be where it should be. X% This procedure is used when an old state has been restored, but it should X% be at a new spot on the sheet now. The two arguments are the spot on the X% page where the state was saved, and the spot it should be on now. X% The path and the clipping boundary are saved in user coordinates, then X% the transform is adjusted, and the paths are restored. X% X X/fix-gstate { X psave % Save the current path. X clippath psave % Save the clip boundary. X X 4 2 roll % Bring the two args back up. X fix-trans % Fix up the transformation. X X -initclip % Trash the old clip boundary. X prestore clip % Recreate the clip boundary. X prestore % Recreate the path. X} def X X% X% oldspot newspot `fix-trans' - X% X% Adjusts the transformation from oldspot to newspot. X% This procedure is called when an old matrix has been reinstated, as with X% setmatrix. It is also called by fix-gstate. X% X X/fix-trans { X /newspot exch def X /oldspot exch def X X m -currentmatrix % Get User x Nup x Def X transforms oldspot get % Get Nup x Def X m2 invertmatrix % Get 1/(Nup x Def) X m3 concatmatrix % End up with just User! X transforms newspot get % The new Nup x Def X m concatmatrix % The new transform! X -setmatrix % Install it. X} def X X%==========================<< Page Delimiting >>=============================== X% X% - `page-edges' - X% X% Page-edges prints the edges of the pages. X% X X/page-edges { X -gsave X X +initgraphics % We want the 'fake' mini-page matrix, X -initclip % But the old paper-sized clip boundary. X X 0.0 setlinewidth % Produces a hairline X X newpath % Outline a standard page. X 0 0 moveto X 0 792 lineto X 612 792 lineto X 612 0 lineto X closepath X stroke X X -grestore X} def X X%======================<< Pods: Graphics Initialization >>===================== X% X% New defaultmatrix X% Simply return a copy of the proper matrix from our table of transforms. X% X X/+defaultmatrix { X transforms spot get % Get the proper transform X exch copy % And copy it (protect the original) X} def X X% X% New initmatrix X% X X/+initmatrix { X m +defaultmatrix -setmatrix X} def X X% X% New initgraphics X% X X/+initgraphics { X -initgraphics % Reset everything X +initmatrix % But get the new matrix X +initclip % And the new clip boundary. X} def X X% X% New initclip X% X X/+initclip { X psave % Push a path object on the stack. X m -currentmatrix % Push the current matrix on the stack. X X -initclip % Initialize the clip boundary. X +defaultmatrix % Set the proper matrix X page-clip prestore % Build the mini default clip path. X clip % And use it to clip. X X -setmatrix % Restore the old matrix X prestore % Restore the path off the stack X} def X X%=======================<< Pods: Page Printing >>============================== X% X% New showpage. This is what the book claims showpage is equivalent to. X% X X/+showpage { X +copypage X +erasepage X +initgraphics X} def X X% X% New copypage X% X X/+copypage { X page-edges % Print the edges of the page. X spot 1 add % Increment the spot number X X dup spots eq { % If this sheet is full: X -copypage % Print the sheet, X -erasepage % Clear the sheet, X pop 0 % And set spot back to zero. X } if X X /spot exch def % Assign the number back to spot. X} def X X% X% New erasepage. Fill the clipping boundary with white. X% X X/+erasepage { X -gsave % "erasepage doesn't affect the current Graphics State" X X +initgraphics % Make sure we know what our state is. X page-clip prestore % Get the clip path. X 1 setgray % "User white". X fill % Paint it. X X -grestore X} def X X%======================<< Pods: Saving and Restoring >>======================== X% X% We push the value of spot on the stack and then restore, which will bring X% back the value of spot at the time of the save. Then we can compare them X% and know how to fix up the graphics state. X% X X/+restore { X spot exch % The current spot is protected X -restore % Restore the state. X dup spot ne { % If from a different spot, X dup spot exch fix-gstate % Fix the graphics state. X /spot exch def % And fix up the spot number. X } { X pop % Clean up the stack. X } ifelse X} def X X% X% Gsave now records the spot number with the current matrix. X% X X/+gsave { X -gsave % Save the state X m -currentmatrix % Get the current matrix X spot arrdef % And associate spot with it. X} def X X% X% Grestore is much like restore, but it doesn't have the benefit of variables X% being restored. The current matrix is hashed with the spot number, and when X% the state is restored, we look up the current matrix and fix the graphics X% state. X% X X/+grestore { X -grestore % Do the restore X m -currentmatrix % Get the current matrix X arrload { % Look it up. If there, X spot fix-gstate % Use value and spot to fix the state. X } if X} def X X% X% Grestoreall works like grestore X% X X/+grestoreall { X -grestoreall % Do the restore. X m -currentmatrix % Get the CTM. X arrload { % Look it up. If there, X spot fix-gstate % Use value and spot to fix the state. X } if X} def X X%======================<< Pods: Matrix Manipulation >>========================= X% X% Currentmatrix must now record the fact that it gave away a certain matrix, so X% that setmatrix can later look it up and fix it to the right spot on the X% sheet. X% X X/+currentmatrix { X -currentmatrix X dup spot arrdef X} def X X% X% Setmatrix now looks up the matrix. If it is in the dictionary, then we know X% what spot was in effect originally, and we can patch it to the current spot. X% If we haven't seen it, tough. X% X X/+setmatrix { X dup -setmatrix X dup %$Debug X arrload { X spot fix-trans % The old spot was pushed by `arrload' X pop %$Debug X } { X (bad setmatrix: ) print == %$Debug X } ifelse X} def X X%=======================<< The Pod Switcheroo >>=============================== X% X% Now we define the replacements. X% X Xpods { X /pod exch def % Save the name we were passed X userdict pod % We'll redefine it in userdict X [ % We're building an executable array X $Nup /begin load % "$Nup begin" X pod namestr cvs % /foo ==> (foo) X dup length /l exch def % X +s exch 1 exch putinterval % ==> (+foo) X +s 0 l 1 add getinterval % X cvn cvx % ==> /+foo ==> +foo X /end load % "end" X ] cvx put % "} def" X} forall X X%============================<< Cleaning Up >>================================= X% X% Pop $Nup off the dictionary stack. X% X Xend X X% X% Start things rolling X% X Xinitgraphics X X% end of nup.pre.ps END_OF_FILE if test 13597 -ne `wc -c <'nup.pre.ps'`; then echo shar: \"'nup.pre.ps'\" unpacked with wrong size! fi # end of 'nup.pre.ps' fi if test -f 'psc.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'psc.c'\" else echo shar: Extracting \"'psc.c'\" \(2890 characters\) sed "s/^X//" >'psc.c' <<'END_OF_FILE' X/*** X *** psc.c -- $Revision: 1.6 $ $Date: 86/04/04 10:35:54 $ X *** X *** Compress a PostScript file. X *** X *** Ned Batchelder, University of Pennsylvania X *** ned@UPenn.CSnet X ***/ X X# include <stdio.h> X X# define RightMargin 70 X Xchar *selfd = "[]{}/"; /* Self delimiting: Need no space around */ Xchar *ws = " \t\n"; /* White space characters */ Xchar lp = '('; /* Opens a balanced text string */ Xchar rp = ')'; /* Closes a balanced text string */ Xchar lh = '<'; /* Opens a hex string */ Xchar rh = '>'; /* Closes a hex string*/ Xchar lit = '\\'; /* Quotes characters in strings */ Xchar com = '%'; /* Introduces comments (to end of line) */ X X/* X * Psc runs as a pure filter. The input is compressed to the output. X */ X Xmain() X{ X int plevel = 0; /* The level of parens in a string */ X int needspace = 0; /* Found some space, must delimit */ X int eatspace = 1; /* We don't need any space now */ X int incomment = 0; /* Are we skipping a comment? */ X int inhex = 0; /* Are we in a hex string? */ X int column = 0; /* Counts output columns to keep lines short */ X int keepch = 0; /* For breaking strings */ X char c; /* The current character */ X X# define put(c) {putchar(c); column++;} X X /* X * First we copy the first line verbatim. This is to copy the comment X * for the file, the name of the file, and the initial '%!' if X * necessary. X */ X X while ((c = getchar()) != '\n') { X putchar(c); X } X putchar('\n'); X X /* X * Now we start compressing. X */ X X while ((c = getchar()) != EOF) { X if (incomment) { X if (c == '\n') { X incomment = 0; X } X continue; X } else if (plevel) { X if (column > RightMargin && keepch <= 0) { X putchar('\\'); X putchar('\n'); X column = 0; X } X if (c == lit) { X put(lit); X put(getchar()); X keepch = 2; /* Protect \ddd */ X } else if (c == lp) { X put(lp); X plevel++; X keepch = 0; X } else if (c == rp) { X put(rp); X plevel--; X if (plevel == 0) { X eatspace = 1; X needspace = 0; X } X } else { X put(c); X keepch--; X } X } else if (inhex) { X if (column > RightMargin) { X putchar('\n'); X column = 0; X } X if (!index(ws, c)) { X put(c); X } X if (c == rh) { X eatspace = 1; X needspace = 0; X inhex = 0; X } X } else if (c == lh) { X put(lh); X inhex++; X } else if (c == com) { X if (column > RightMargin) { X putchar('\n'); X column = 0; X } X incomment = 1; X if (!eatspace) { X needspace = 1; X } X } else if (index(ws, c)) { X if (!eatspace) { X needspace = 1; X } X } else if (index(selfd, c)) { X if (column > RightMargin) { X putchar('\n'); X column = 0; X } X put(c); X eatspace = 1; X needspace = 0; X } else if (c == lp) { X put(lp); X plevel = 1; X keepch = 0; X } else { X if (needspace) { X putchar('\n'); X column = 0; X needspace = 0; X } X put(c); X eatspace = 0; X } X } X X putchar('\n'); X X return 0; X} X X/* end of psc.c */ END_OF_FILE if test 2890 -ne `wc -c <'psc.c'`; then echo shar: \"'psc.c'\" unpacked with wrong size! fi # end of 'psc.c' fi if test -f 'psnup.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'psnup.1'\" else echo shar: Extracting \"'psnup.1'\" \(1650 characters\) sed "s/^X//" >'psnup.1' <<'END_OF_FILE' X.\"Copyright 1991 by Chris Lewis %I% %E% X.TH PSNUP 1 local X.SH NAME Xpsnup \- Insert N-up code into Postscript files X.SH SYNOPSIS X.B psnup X.BI "[-p" n "]" X.BI "[-r]" X.BI "[-R]" X.BI "[-s" n "]" Xfiles ... X.SH DESCRIPTION XThe X.B psnup Xprogram takes arbitrary postscript as input, and wraps it with Xadditional postscript to implement n-up printing. XThe input postscript is taken from the file arguments, or stdin Xif no arguments are specified. XThe output is sent to stdout. X.P XThe X.BI -p n Xoption indicates how many pages of Postscript (or ``spots'' should be Xprinted per piece of paper. X.I N Xis constrained to be 2, 4, 8 or 16. X.P XThe X.B -r Xoption indicates that the first spot goes in the lower right and progresses Xto the upper left. XThis is handy for when the pages have already been reversed by Xanother program, and you are printing on a printer that reverses Xpages. X.P XThe X.B -R Xoption indicates that the first spot goes in the upper left hand corner Xand progresses to the lower right. XThis is for non-reversing printers. X.P XThe X.BI -s n Xoption indicates which spot the first page of output occurs in. X.P XThe defaults are: X.BR -p2 , X.BR -r , Xand X.BR -s1 , X.P X.B Psnup Xtries very hard to retain a trailing control-D if the file has Xone, as well as embedding the included postscript in the right Xplaces, so as to not violate the Adobe Formatting Conventions. X.SH CAVEATS X.B Psnup Xis written in X.B sed Xand uses temporary files in order to remain small and Xstand-alone, but it ain't fast.. X.SH AUTHOR X.B psnup Xshell script and this manual page were written by Chris Lewis. XThe postscript used to perform the n-up handling was written Xby Ned Batchelder. END_OF_FILE if test 1650 -ne `wc -c <'psnup.1'`; then echo shar: \"'psnup.1'\" unpacked with wrong size! fi # end of 'psnup.1' fi if test -f 'psnup.sh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'psnup.sh'\" else echo shar: Extracting \"'psnup.sh'\" \(2000 characters\) sed "s/^X//" >'psnup.sh' <<'END_OF_FILE' X: use /bin/sh X# Copyright 1991, Chris Lewis, All Rights Reserved. X# X# Permission to copy and further distribute is freely given provided X# this copyright notice remains intact and that this software is not X# sold for profit. X# Xtrap "rm -f /tmp/?$$" 0 Xrm -f /tmp/?$$ X Xpages=2 X# Default is reversed for use with psroff with psxlate in the X# pipeline to reverse pages. Xreverse=true Xstart=1 X Xfor i Xdo X case $i in X -p2 | -p4 | -p8 | -p16) X pages=`echo $i | sed -e 's/^..//'` X if [ -z "$pages" -o -n "`echo $pages | sed -e 's/[0-9]*//'`" ] X then X echo "$0: Non-numeric pages per sheet option $i" >&2 X exit 1 X fi X ;; X -p*) X echo "$0: -p option ($i) must be 2, 4, 8 or 16" >&2 X exit 1 X ;; X -r) X reverse=false X ;; X -R) X reverse=true X ;; X -s*) X start=`echo $i | sed -e 's/^..//'` X if [ -z "$start" -o -n "`echo $start | sed -e 's/[0-9]*//'`" ] X then X echo "$0: Non-numeric start spot option $i" >&2 X exit 1 X fi X ;; X -*) X echo "$0: invalid option $i" >&2 X echo "usage: $0 [-pn] [-r | -t] [-sn] files" >&2 X exit 1 X ;; X *) X files="$files $i" X ;; X esac X shift Xdone Xif [ $start -gt $pages ] Xthen X echo "$0: start option (-s$start) must be <= pages/sheet (-p$pages)" \ X >&2 X exit 1 Xfi X#echo pages $pages X#echo rev $reverse X#echo start $start X Xeof=`echo X | tr 'X' '\004'` X Xif [ -z "$files" ] Xthen X files=- Xfi X Xtouch /tmp/A$$ /tmp/B$$ X X( X cat $files X echo X) | sed -e '1w /tmp/A'$$ \ X -e '1d' \ X -e '/^'$eof'$/w /tmp/B'$$ \ X -e '/^'$eof'$/d' > /tmp/C$$ X Xif grep '^%' /tmp/A$$ > /dev/null Xthen X cat /tmp/A$$ X > /tmp/A$$ Xelse X echo "%!" Xfi X Xsed -e '/@#@Pages@#@/s//'$pages'/' \ X -e '/@#@Rev@#@/s//'$reverse'/' \ X -e '/@#@Start@#@/s//'$start'/' <<\!ENDPRO X@@@nup.pro@@@ X!ENDPRO X Xcat /tmp/A$$ Xcat /tmp/C$$ X Xcat <<\!ENDEPI X@@@nup.epi@@@ X!ENDEPI Xif [ -s /tmp/B$$ ] Xthen X echo "\c" > /tmp/A$$ X if [ -s /tmp/A$$ ] X then X echo -n "$eof" X else X echo "$eof\c" X fi Xfi END_OF_FILE if test 2000 -ne `wc -c <'psnup.sh'`; then echo shar: \"'psnup.sh'\" unpacked with wrong size! fi chmod +x 'psnup.sh' # end of 'psnup.sh' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 -- Chris Lewis, Phone: (613) 832-0541, Internet: clewis@ferret.ocunix.on.ca UUCP: uunet!mitel!cunews!latour!ecicrl!clewis; Ferret Mailing List: (ferret-request@eci386); Psroff (not Adobe Transcript) enquiries: psroff-request@eci386, current patchlevel is *7*. exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.