[comp.sources.wanted] 2 pages/physical page in Postscript ? SUMMARY OF RESPONSES

tombre@crin.fr (Karl Tombre) (08/13/90)

>>>>> In article <TOMBRE.90Aug9154221@weissenburger.crin.fr>, I wrote:

KT> Is there something like a program able to take a postscript file
KT> and modify it so that 2 (logical) pages are printed on one physical
KT> page, i.e. in landscape format, reduced fonts etc. ???

KT> I'm thinking of some general filter which might as well be put at the
KT> output of some dvi2ps or any other text-to-prostscript filter (program
KT> prettyprinters especially)

KT> Please mail me if you know of such a program. Thanks in advance,

Thanks to everybody who answered my request. Here is a summary of the
responses I got. I think I will start with having a look at psnup...

------------------------------
Several people (Nick Holloway (alfie@cs.warwick.ac.uk), Steve Rumsby
<steve@maths.warwick.ac.uk>, Glenn M. Lewis <glewis@fws204.intel.com>
Bill Stapleton <wls@uwm.edu>), suggested that I look at psnup, written
by Ned Batchelder <ned@Upenn.CSNet>. It can print 2, 4, 8 or 16
logical pages on a physical page.

Nick Holloway (alfie@cs.warwick.ac.uk) has written a shell script
wrapper, and a manual page to go with it.  Here is the manual page
(thank you, Nick).

+++++++++++
PSNUP(1)                 USER COMMANDS                   PSNUP(1)

NAME
     psnup - print n logical pages on each physical page

SYNOPSIS
     psnup [ -r ] [ -n pages ] [ -s start ] [ files ...  ]

DESCRIPTION
     psnup alters PostScript input so that n  logical  pages  are
     printed  on each physical page. This can be useful for view-
     ing the general layout of a document  on  fewer  pages.  For
     example:
          psroff -t -ms doc.ms | psnup -n 16 | lpr -PPostScript

     For printing plain text files, you need to convert  them  to
     PostScript format first. For example, using enscript(1):
          enscript -l -p - | psnup | lpr -PPostScript

OPTIONS
     -r   start printing in the bottom right corner, rather  than
          the top left corner.  This is useful for input that has
          been page reversed.

     -npages
          on each physical page print pages logical pages.  pages
          must be one of 2, 4, 8, or 16. The default is 2.

     -sstart
          specify where the first page  will  start.  This  value
          must be in the range 0 to pages-1.  This can be thought
          of the number of pages to skip before  starting  print-
          ing.  The default is 0.

FILES
     /cs/share/lib/psnup/nup.pro
          psnup prologue file

     /cs/share/lib/psnup/nup.epi
          psnup epilogue file

SEE ALSO
     psroff(1), enscript(1), psrev(1).

DIAGNOSTICS
     Hopefully self explanatory!

BUGS
     If the input is page reversed, the  start  spot  has  to  be
     given  for the first page to finish up in the top left spot.
     e.g. if input consisted of 7 pages in reverse order,  to  be
     printed  8  up,  the  start  spot would have to 1, otherwise
     there would be a blank page in the top left  corner,  rather
     than the bottom right.

     The output is not Adobe conforming PostScript, and so output
     will not be page reversed when printed.

++++++++++++
Bill Stapleton <wls@uwm.edu> provided a copy of an article in
comp.lang.postscript, where Roy Smith <roy@phri.nyu.edu> wrote:

RS>         Ftp to goober.phri.nyu.edu and grab ~ftp/pub/misc/psnup.tar.Z.  It
RS> does almost exactly what you want; takes a PS file and prints it n-up,
RS> where n is 2, 4, 8, or 16.  Sorry, no 6-up.  Non-powers-of-2-up starts to
RS> get tricky, since the page aspect ratios don't work out right.  Ned
RS> Batchelder wrote psnup, I just repackaged it a little.
RS> 
RS>         I have also heard of something called "Save A Tree" which prints
RS> mac text files 2-up or 4-up.  I've never used it, but is sounds good.
RS> According to the information I have, you can grab ~ftp/pub/SaveATree.hqx
RS> from thylacine.cs.wisc.edu (128.105.1.208)

----------------------------
Alan Butcher (ab@cerc.wvu.wvnet.edu) writes:

A program called mpage was posted to comp.sources.misc. Might try it.

Here is the header from the posting.

Posting-number: Volume 9, Issue 88
Submitted-by: mark@pyrdc.UUCP (Mark Hahn)
Archive-name: mpage/part01

Mpage will print text or Postscript input in n-up format.  I.e. 4
pages of normal text or postscript is reduced to fit on one sheet of
paper (4-up).  Choices are 1, 2, 4, 8, and 16 pages per sheet of paper
with 2 and 4 being most useful and readable.  It accepts Postscript as
input, or at least it works on most of the stuff I get from the Adobe
File Server.

------------------------------------------

Georges Buffet <Georges.Buffet@mirsa.inria.fr> reminded me that
enscript also has this capability when you want to output text files
(enscript -2r).

--
Karl Tombre - INRIA Lorraine / CRIN
EMAIL : tombre@loria.crin.fr - POST : BP 239, 54506 VANDOEUVRE CEDEX, France

tombre@crin.fr (Karl Tombre) (08/14/90)

I got an additional mail today which completes my summary. Here it is,
for the benefit of everybody. And thanks to Joakim Sernbrant
<d87-jse@nada.kth.se>, who writes:

Hi. Since you didn't mention multi, I thought I would.
Multi is an all PostScript version of "n pages on one" and it has worked
with everyting I've thrown at it.  I have wrapped multi in a shell
script so one can do piping to it like psnup.

Good luck.

---cut-here--------------------------------------
cat << 'EOF'
%!PS-Adobe-1.0
%%Creator: Ross Cartlidge <rossc@extro.ucc.su.oz>
%%Title: Multiple pages on one page
%%CreationDate: Tuesday July 25 18:00:00 1989
%%Pages: 0
%%DocumentFonts:
%%BoundingBox: 0 0 0 0
%%EndComments
%
% Uncomment the next line if you wish to load multi into the "exitserver"
% state of the PostScript device
% serverdict begin 0 exitserver
%
%
%	make each operator to overlay a procedure so a bind in 
%	a prolog will not stop the overlaying by "multi"
%

[
	/gsave
	/grestore
	/grestoreall
	/initgraphics
	/initmatrix
	/currentmatrix
	/setmatrix
	% Path construction operators
	/initclip
	% Virtual memory operators
	/save
	% ones which needed special overloading
	/showpage
	/erasepage
	/copypage
	/restore
	% ignore these
	/letter
	/legal
	/a4
	/b5
	/lettersmall
	/note
]
{
%	if exists check if operator else define {}
	dup where
	{
		pop
% 		If operator then make into procedure
		dup load type /operatortype eq
		{
			1 array cvx dup
			0
			3 index cvx		% /n -> n
			put			% {}[0] -> n
			bind
			def
		}
		{
			pop
		}
		ifelse
	}
	{
		{} def
	}
	ifelse
}
forall

%
%	Initialise endmulti to execute an error
%
/endmulti
{
	count array astore /ostack exch def
	250 array execstack /estack exch def
	20 array dictstack /dstack exch def
	$error /newerror true put
	$error /errorname (No matching multi) cvn put
	$error /command (endmulti) put
	$error /ostack ostack put
	$error /estack estack put
	$error /dstack dstack put
	stop
}
bind
def

%
%	Put multiple logical pages on one physical page
%	until "endmulti" called
%	
%	landscape nrows ncols dividers multi -
%
%	landscape	boolean, if true divide page in landscape orientation
%	nrows		integer, number of logical pages down physical page
%	ncols		integer, number of logical pages across physical page
%	dividers	boolean, if true divide logical pages by lines
%
/multi
{
	currentdict
	64 dict begin
	/initdict exch def	% store initial dict for backward reference
	/dividers exch def
	/cols exch def
	/rows exch def

%
%	get size of current page
%
	initgraphics clippath pathbbox
	/Y exch def	% Max Y
	/X exch def	% Max X
	/y exch def	% Min Y
	/x exch def	% Min X
	/W X x add def	% Width of Page
	/H Y y add def	% Height of page

%	if landscape
	{
%
%		Note: x and y are reversed
%
		/w Y y sub def	% Width of imageable region
		/h X x sub def	% Height of imageable region
		/L		% Map to landscape
			-90 matrix rotate
			0 H matrix translate
			matrix concatmatrix
		def
		/O y x matrix translate def	% Move to origin
	}
	{
		/w X x sub def
		/h Y y sub def
		/L matrix def
		/O x y matrix translate def
	}
	ifelse

%
%	CTM (multi) = C x T x M x L x I
%	CTM (normal) = C x I
%	CTM (normal) = CTM (multi) x (T x M x L x I)-1 x I
%	M = (Scale rows/cols) x (Scale logical to physical) x
%		(Translate to physical clip origin
%	T = (Convert logical page to spot and physical)
%	L = (Convert to landscape)
%	I = Initial Physical CTM
%	C = Random transform on logical page
	/I
		matrix currentmatrix
	def
	/I_inv
		I matrix invertmatrix
	def

	/M
			w W div cols div
			h H div rows div
		matrix scale			%TMP
		O
		matrix concatmatrix
	def

%	matrix T <current T>
	/T
	{
		page# cols mod W mul
		rows page# cols idiv sub 1 sub H mul
		3 -1 roll translate
	}
	def

%
%	Utility functions
%	NB: *_t1 are temporary variables
%

%	matrix fromcanon <I-1 x T x M x L x I>
	/From_t1 matrix def
	/From_t2 matrix def
	/From_t3 matrix def
	/From_t4 matrix def
	/fromcanon
	{
		I_inv
		From_t1 T
		M
		L
		I
		From_t2 concatmatrix	
		From_t3 concatmatrix
		From_t4 concatmatrix
		3 -1 roll concatmatrix
	}
	def

%	/n {} mkmulti -
%	makes a new function called "n" in previous dict with:-
%		{}[0] = /n
%		{}[1] = currentdict
%		currentdict.n = prevdict.n
%
	/mkmulti
	{
		1 index dup load def	%define old val in current dict
		5 array cvx
		dup 3 4 -1 roll put	% A[3] = {}
		dup 0 3 index put	% A[0] = /n
		dup 1 currentdict put	% A[1] = currentdict
		dup 2 /begin cvx put	% A[2] = begin
		dup 4 /exec cvx put	% A[4] = exec
		initdict 3 1 roll
		put			% define initdict.n to multi function
	}
	def

%
%	path_to_proc {}
%		make proc represenation of current path
%
	/path_to_proc
	{
		{
			[
				/newpath cvx
				{ /moveto cvx}
				{ /lineto cvx}
				{ /curveto  cvx}
				{ /closepath cvx }
				pathforall
			]
			cvx
			exch pop
		}
		stopped
		{
			$error /errorname get /invalidaccess eq
			{
				cleartomark
				$error /newerror false put
				(%%Warning%% charpath in path - path nulled) =
				cvx exec
			}
			{
				stop
			}
			ifelse
		}
		if
	}
	def
	/path_def
	{
		{ currentpoint } stopped
		{
			$error /newerror false put
			{ newpath }
		}
		{
			/newpath cvx 3 1 roll /moveto cvx 4 array astore cvx
		}
		ifelse
	}
	cvlit def

%
%	Draw lines round logical pages
%
	/draw_dividers
	{
		initgraphics
		0 setlinewidth
		L concat
		M concat
		1 1 cols 1 sub
		{
			W mul
			dup
			0 moveto
			rows H mul lineto
		}
		for
		1 1 rows 1 sub
		{
			H mul
			dup
			0 exch moveto
			cols W mul exch lineto
		}
		for
		stroke
	}
	def

%
%	for each graphics operator which affects absolute state
%
	/M1 matrix def
	/M3 matrix def
	/M2 matrix def
	[
		/gsave
		/grestore
		/grestoreall
		/initgraphics
		/initmatrix
		/currentmatrix
		/setmatrix
		% Path construction operators
		/initclip
		% Virtual memory operators
		/save
	]
	{
		{
%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Tocano (canon mode)
%
			M1 currentmatrix
			Tocanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec

			load exec

%			Save paths
			path_def path_to_proc
			clippath  { {} } path_to_proc

%
%			CTM <- CTM x Fromcanon (Non canon mode)
%
			M1 currentmatrix
			Fromcanon
			M2
			concatmatrix
			setmatrix

%			Restore paths
			initclip exec clip
			exec
			end
		}
		mkmulti
	}
	forall

%
%	Define the operators which can't use the standard template
%
	/showpage
	{
		/page# page# 1 add def

%		Update the transform matrices
		page# npages eq
		{
			dividers
			{
				draw_dividers
			}
			if
			load exec	% the previous showpage
			/page# 0 def
		}
		{
			pop
		}
		ifelse
		/Fromcanon Fromcanon fromcanon def
		/Tocanon Fromcanon Tocanon invertmatrix def
		end
		initgraphics	% the new initgraphics
	}
	mkmulti

	/copypage
	{
		pop
		end
		gsave
		showpage
		grestore
	}
	mkmulti

	/erasepage
	{
		pop
		end
		gsave
		initclip
		clippath
		1 setgray fill
		grestore
	}
	mkmulti
	[
		/letter
		/legal
		/a4
		/b5
		/lettersmall
		/note
	]
	{
		{
			pop end
			(%%Warning%% Device change ignored) =
		}
		mkmulti
	}
	forall

%
%	Define restore separately as it affects the value of page#, etc
%
	/restore
	{
		pop
%		Push the values to restore after restore
		mark exch 	% put mark under -save-
		page#
		Fromcanon aload pop
		Tocanon aload pop

		counttomark -1 roll	% get -save- to the top
		restore

%		Restore popped values
		Tocanon astore pop
		Fromcanon astore pop
		/page# exch def
		pop	% mark

%		Save paths
		path_def path_to_proc
		clippath  { { } } path_to_proc

%
%		CTM <- CTM x Fromcanon (Non canon mode)
%
		M1 currentmatrix
		Fromcanon
		M2
		concatmatrix
		setmatrix

%		Restore paths
		initclip exec clip
		exec
		end
	}
	mkmulti
%
%	procedure to undo the effect of multi
%
	/endmulti
	{
		pop	% don't need /endmulti
		[
			/gsave
			/grestore
			/grestoreall
			/initgraphics
			/initmatrix
			/currentmatrix
			/setmatrix
			% Path construction operators
			/initclip
			% Virtual memory operators
			/save
			% ones which needed special overloading
			/showpage
			/erasepage
			/copypage
			/restore
			% ignore these
			/letter
			/legal
			/a4
			/b5
			/lettersmall
			/note
			%
			/endmulti
		]
		{
			initdict exch
			dup load 		% get old value
			put			% restore old value
		}
		forall
		page# 0 ne	% if not at new page show uncomplete page
		{
			dividers
			{
				draw_dividers
			}
			if
			showpage
		}
		if
		end
	}
	mkmulti

%
%	Set up in multi(non canon) mode
%
	/page# 0 def
	/npages rows cols mul def
	/Fromcanon matrix fromcanon def
	/Tocanon Fromcanon matrix invertmatrix def
	end
	initgraphics
}
bind
def
EOF
echo true 1 2 true multi
cat $*
echo endmulti

exit 0

--
Karl Tombre - INRIA Lorraine / CRIN
EMAIL : tombre@loria.crin.fr - POST : BP 239, 54506 VANDOEUVRE CEDEX, France