rhl@grendel.Princeton.EDU (Robert Lupton (the Good)) (08/15/90)
Well, I took "multi" off comp.sources.wanted, wrote a proper shell
wrapper with command options, wrote a man page and here it is.
Multi is a filter to print postscript with many logical pages per
physical page. It works just fine with (e.g.) the output of dvips.
Robert
-=-=-=-=-=-=-=-=-=-=-=--=-=-= Cut Here -=-=-=-=-=-=-=-=-=-=-=--=-=-=-
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# multi
# multi.1
# This archive created: Tue Aug 14 13:28:54 1990
export PATH; PATH=/bin:$PATH
if test -f 'multi'
then
echo shar: will not over-write existing file "'multi'"
else
cat << \SHAR_EOF > 'multi'
#!/bin/sh
#
# 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 everything I've thrown at it. I have wrapped multi in a shell
# script so one can do piping to it like psnup.
#
# Joakim Sernbrant
# <d87-jse@nada.kth.se>
#
# Good luck.
#
# ---cut-here--------------------------------------
#
# Better shell wrapper, taking arguments etc.
# rhl@astro.princeton.edu
#
#
usage="Usage: `basename $0` [+-d] [-h] [-l] [-1248] [-p] [-x#] [-y#] [file1 file2 ...]"
#
dividers=true
landscape=true
nx=2
ny=1
#
while [ x"$*" != x ]; do
case $1 in
+d*)
dividers=true
shift;
;;
-d*)
dividers=false
shift;
;;
-h*)
echo $usage
shift;
;;
-l*)
landscape=true
shift;
;;
-1)
nx=1; ny=1
landscape=false
shift;
;;
-2)
nx=2; ny=1
landscape=true
shift;
;;
-4)
nx=2; ny=2
landscape=false
shift;
;;
-8)
nx=4; ny=2
landscape=true
shift;
;;
-p*)
landscape=false
shift;
;;
-x*)
nx=`echo $1 | sed 's/-x//'`
shift;
;;
-y*)
ny=`echo $1 | sed 's/-y//'`
shift;
;;
*)
break;
;;
esac
done
#
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 $landscape $nx $ny $dividers multi
cat $*
echo endmulti
SHAR_EOF
chmod +x 'multi'
fi # end of overwriting check
if test -f 'multi.1'
then
echo shar: will not over-write existing file "'multi.1'"
else
cat << \SHAR_EOF > 'multi.1'
.\" man page for Multi
.TH MULTI 1 "14 August 1990"
.SH NAME
multi \- print postscript file with many logical pages per physical page
.SH SYNOPSIS
.B multi
[
.I +-d
] [
.I -h
] [
.I -l
] [
.I -1248
] [
.I -p
] [
.I -x#
] [
.I -y#
] [
.I file1 file2 ...
]
.SH DESCRIPTION
.B Multi
is a programme to read postscript files and print many logical pages on each
physical page.
If no files are specified it will read the standard input. To quote the
author, "it has worked with everything I've thrown at it". This includes
the output of such filters as
.B dvips
..
.SH OPTIONS
.IP +d
Draw dividers between logical pages (default).
.IP -d
Don't draw dividers between logical pages.
.IP -h
Print a help message.
.IP -l
Print in landscape mode (default).
.IP -1,-2,-4,-8
Plot 1, 2, 4, or 8 logical pages per physical page. Landscape or Portrait mode will be used as appropriate.
This are probably the simplest way to use
.B multi
..
.IP -p
Print in portrait mode.
.IP -x#
use
.I #
logical pages in the
.I x
direction (default: 2).
.IP -y#
use
.I #
logical pages in the
.I y
direction (default: 1).
.SH BUGS
.IP
.SH AUTHOR
Joakim Sernbrant, (d87-jse@nada.kth.se)
.br
(Shell script and man page by Robert Lupton, (rhl@astro.princeton.edu))
SHAR_EOF
fi # end of overwriting check
# End of shell archive
exit 0