[comp.sources.misc] v16i104: psnup - N-up Postscript printing, Part01/01

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.