2 * Copyright (C) Angus J. C. Duggan 1991-1995
3 * See file LICENSE for details.
5 * put multiple pages onto one physical sheet of paper
8 * psnup [-q] [-w<dim>] [-h<dim>] [-ppaper] [-b<dim>] [-m<dim>]
9 * [-l] [-c] [-f] [-sscale] [-d<wid>] [-nup] [in [out]]
10 * -w<dim> sets the paper width
11 * -h<dim> sets the paper height
12 * -ppaper sets the paper size (width and height) by name
13 * -W<dim> sets the input paper width, if different from output
14 * -H<dim> sets the input paper height, if different from output
15 * -Ppaper sets the input paper size, if different from output
16 * -m<dim> sets the margin around the paper
17 * -b<dim> sets the border around each page
18 * -sscale alters the scale at which the pages are displayed
19 * -l used if pages are in landscape orientation (rot left)
20 * -r used if pages are in seascape orientation (rot right)
21 * -c for column-major layout
22 * -f for flipped (wider than tall) pages
23 * -d<wid> to draw the page boundaries
36 char pagelabel
[BUFSIZ
] ;
39 static void usage(void)
41 fprintf(stderr
, "%s release %d patchlevel %d\n", program
, RELEASE
, PATCHLEVEL
);
42 fprintf(stderr
, "Copyright (C) Angus J. C. Duggan, 1991-1995. See file LICENSE for details.\n");
43 fprintf(stderr
, "Usage: %s [-q] [-wwidth] [-hheight] [-ppaper] [-Wwidth] [-Hheight] [-Ppaper] [-l] [-r] [-c] [-f] [-mmargin] [-bborder] [-dlwidth] [-sscale] [-nup] [infile [outfile]]\n",
49 static void argerror(void)
51 message(FATAL
, "bad dimension\n");
54 #define MIN(x,y) ((x) > (y) ? (y) : (x))
55 #define MAX(x,y) ((x) > (y) ? (x) : (y))
57 /* return next larger exact divisor of number, or 0 if none. There is probably
58 * a much more efficient method of doing this, but the numbers involved are
59 * small, so it's not a big loss. */
60 static int nextdiv(int n
, int m
)
69 void main(int argc
, char *argv
[])
71 int horiz
, vert
, rotate
, column
, flip
, leftright
, topbottom
;
73 double draw
= 0; /* draw page borders */
74 double scale
; /* page scale */
75 double uscale
= 0; /* user supplied scale */
76 double ppwid
, pphgt
; /* paper dimensions */
77 double margin
, border
; /* paper & page margins */
78 double vshift
, hshift
; /* page centring shifts */
79 double iwidth
, iheight
; /* input paper size */
80 double tolerance
= 100000; /* layout tolerance */
84 if ( (paper
= findpaper(PAPER
)) != (Paper
*)0 ) {
85 width
= (double)PaperWidth(paper
);
86 height
= (double)PaperHeight(paper
);
90 margin
= border
= vshift
= hshift
= column
= flip
= 0;
91 leftright
= topbottom
= 1;
92 iwidth
= iheight
= -1 ;
97 for (program
= *argv
++; --argc
; argv
++) {
98 if (argv
[0][0] == '-') {
100 case 'q': /* quiet */
103 case 'd': /* draw borders */
105 draw
= singledimen(*argv
+2, argerror
, usage
);
109 case 'l': /* landscape (rotated left) */
111 topbottom
= !topbottom
;
113 case 'r': /* seascape (rotated right) */
115 leftright
= !leftright
;
117 case 'f': /* flipped */
120 case 'c': /* column major layout */
123 case 'w': /* page width */
124 width
= singledimen(*argv
+2, argerror
, usage
);
126 case 'W': /* input page width */
127 iwidth
= singledimen(*argv
+2, argerror
, usage
);
129 case 'h': /* page height */
130 height
= singledimen(*argv
+2, argerror
, usage
);
132 case 'H': /* input page height */
133 iheight
= singledimen(*argv
+2, argerror
, usage
);
135 case 'm': /* margins around whole page */
136 margin
= singledimen(*argv
+2, argerror
, usage
);
138 case 'b': /* border around individual pages */
139 border
= singledimen(*argv
+2, argerror
, usage
);
141 case 't': /* layout tolerance */
142 tolerance
= atof(*argv
+2);
144 case 's': /* override scale */
145 uscale
= atof(*argv
+2);
147 case 'p': /* output (and by default input) paper type */
148 if ( (paper
= findpaper(*argv
+2)) != (Paper
*)0 ) {
149 width
= (double)PaperWidth(paper
);
150 height
= (double)PaperHeight(paper
);
152 message(FATAL
, "paper size '%s' not recognised\n", *argv
+2);
154 case 'P': /* paper type */
155 if ( (paper
= findpaper(*argv
+2)) != (Paper
*)0 ) {
156 iwidth
= (double)PaperWidth(paper
);
157 iheight
= (double)PaperHeight(paper
);
159 message(FATAL
, "paper size '%s' not recognised\n", *argv
+2);
161 case 'n': /* n-up, for compatibility with other psnups */
165 if ((nup
= atoi(*argv
)) < 1)
166 message(FATAL
, "-n %d too small\n", nup
);
168 message(FATAL
, "argument expected for -n\n");
181 case 'v': /* version */
185 } else if (infile
== stdin
) {
186 if ((infile
= fopen(*argv
, OPEN_READ
)) == NULL
)
187 message(FATAL
, "can't open input file %s\n", *argv
);
188 } else if (outfile
== stdout
) {
189 if ((outfile
= fopen(*argv
, OPEN_WRITE
)) == NULL
)
190 message(FATAL
, "can't open output file %s\n", *argv
);
193 #if defined(MSDOS) || defined(WINNT)
194 if ( infile
== stdin
) {
195 int fd
= fileno(stdin
) ;
196 if ( setmode(fd
, O_BINARY
) < 0 )
197 message(FATAL
, "can't open input file %s\n", argv
[4]);
199 if ( outfile
== stdout
) {
200 int fd
= fileno(stdout
) ;
201 if ( setmode(fd
, O_BINARY
) < 0 )
202 message(FATAL
, "can't reset stdout to binary mode\n");
205 if ((infile
=seekable(infile
))==NULL
)
206 message(FATAL
, "can't seek input\n");
208 if (width
<= 0 || height
<= 0)
209 message(FATAL
, "page width and height must be set\n");
211 /* subtract paper margins from height & width */
212 ppwid
= width
- margin
*2;
213 pphgt
= height
- margin
*2;
215 if (ppwid
<= 0 || pphgt
<= 0)
216 message(FATAL
, "paper margins are too large\n");
218 /* set default values of input height & width */
224 /* Finding the best layout is an optimisation problem. We try all of the
225 * combinations of width*height in both normal and rotated form, and
226 * minimise the wasted space. */
228 double best
= tolerance
;
230 for (hor
= 1; hor
; hor
= nextdiv(hor
, nup
)) {
232 /* try normal orientation first */
233 double scl
= MIN(pphgt
/(height
*ver
), ppwid
/(width
*hor
));
234 double optim
= (ppwid
-scl
*width
*hor
)*(ppwid
-scl
*width
*hor
) +
235 (pphgt
-scl
*height
*ver
)*(pphgt
-scl
*height
*ver
);
238 /* recalculate scale to allow for internal borders */
239 scale
= MIN((pphgt
-2*border
*ver
)/(height
*ver
),
240 (ppwid
-2*border
*hor
)/(width
*hor
));
241 hshift
= (ppwid
/hor
- width
*scale
)/2;
242 vshift
= (pphgt
/ver
- height
*scale
)/2;
243 horiz
= hor
; vert
= ver
;
246 /* try rotated orientation */
247 scl
= MIN(pphgt
/(width
*hor
), ppwid
/(height
*ver
));
248 optim
= (pphgt
-scl
*width
*hor
)*(pphgt
-scl
*width
*hor
) +
249 (ppwid
-scl
*height
*ver
)*(ppwid
-scl
*height
*ver
);
252 /* recalculate scale to allow for internal borders */
253 scale
= MIN((pphgt
-2*border
*hor
)/(width
*hor
),
254 (ppwid
-2*border
*ver
)/(height
*ver
));
255 hshift
= (ppwid
/ver
- height
*scale
)/2;
256 vshift
= (pphgt
/hor
- width
*scale
)/2;
257 horiz
= ver
; vert
= hor
;
262 /* fail if nothing better than worst tolerance was found */
263 if (best
== tolerance
)
264 message(FATAL
, "can't find acceptable layout for %d-up\n", nup
);
267 if (flip
) { /* swap width & height for clipping */
273 if (rotate
) { /* rotate leftright and topbottom orders */
275 topbottom
= !leftright
;
280 /* now construct specification list and run page rearrangement procedure */
283 PageSpec
*specs
, *tail
;
285 tail
= specs
= newspec();
288 int up
, across
; /* page index */
291 if (leftright
) /* left to right */
293 else /* right to left */
294 across
= horiz
-1-page
/vert
;
295 if (topbottom
) /* top to bottom */
296 up
= vert
-1-page
%vert
;
297 else /* bottom to top */
300 if (leftright
) /* left to right */
302 else /* right to left */
303 across
= horiz
-1-page
%horiz
;
304 if (topbottom
) /* top to bottom */
305 up
= vert
-1-page
/horiz
;
306 else /* bottom to top */
310 tail
->xoff
= margin
+ (across
+1)*ppwid
/horiz
- hshift
;
312 tail
->flags
|= ROTATE
;
314 tail
->xoff
= margin
+ across
*ppwid
/horiz
+ hshift
;
318 tail
->scale
= uscale
;
321 tail
->flags
|= SCALE
;
322 tail
->yoff
= margin
+ up
*pphgt
/vert
+ vshift
;
323 tail
->flags
|= OFFSET
;
325 tail
->flags
|= ADD_NEXT
;
326 tail
->next
= newspec();
331 pstops(nup
, 1, 0, specs
, draw
); /* do page rearrangement */