New macro to handle uppercase text
[ccbib.git] / psutils / psnup.c
blob73cc44aeaf3c2528bbb91ba9b326eb9b10983000
1 /* psnup.c
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
7 * Usage:
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
26 #include "psutil.h"
27 #include "psspec.h"
28 #include "pserror.h"
29 #include "patchlev.h"
31 char *program ;
32 int pages ;
33 int verbose ;
34 FILE *infile ;
35 FILE *outfile ;
36 char pagelabel[BUFSIZ] ;
37 int pageno ;
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",
44 program);
45 fflush(stderr);
46 exit(1);
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)
62 while (++n <= m) {
63 if (m%n == 0)
64 return (n);
66 return (0);
69 void main(int argc, char *argv[])
71 int horiz, vert, rotate, column, flip, leftright, topbottom;
72 int nup = 1;
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 */
81 Paper *paper;
83 #ifdef PAPER
84 if ( (paper = findpaper(PAPER)) != (Paper *)0 ) {
85 width = (double)PaperWidth(paper);
86 height = (double)PaperHeight(paper);
88 #endif
90 margin = border = vshift = hshift = column = flip = 0;
91 leftright = topbottom = 1;
92 iwidth = iheight = -1 ;
94 infile = stdin;
95 outfile = stdout;
96 verbose = 1;
97 for (program = *argv++; --argc; argv++) {
98 if (argv[0][0] == '-') {
99 switch (argv[0][1]) {
100 case 'q': /* quiet */
101 verbose = 0;
102 break;
103 case 'd': /* draw borders */
104 if (argv[0][2])
105 draw = singledimen(*argv+2, argerror, usage);
106 else
107 draw = 1;
108 break;
109 case 'l': /* landscape (rotated left) */
110 column = !column;
111 topbottom = !topbottom;
112 break;
113 case 'r': /* seascape (rotated right) */
114 column = !column;
115 leftright = !leftright;
116 break;
117 case 'f': /* flipped */
118 flip = 1;
119 break;
120 case 'c': /* column major layout */
121 column = !column;
122 break;
123 case 'w': /* page width */
124 width = singledimen(*argv+2, argerror, usage);
125 break;
126 case 'W': /* input page width */
127 iwidth = singledimen(*argv+2, argerror, usage);
128 break;
129 case 'h': /* page height */
130 height = singledimen(*argv+2, argerror, usage);
131 break;
132 case 'H': /* input page height */
133 iheight = singledimen(*argv+2, argerror, usage);
134 break;
135 case 'm': /* margins around whole page */
136 margin = singledimen(*argv+2, argerror, usage);
137 break;
138 case 'b': /* border around individual pages */
139 border = singledimen(*argv+2, argerror, usage);
140 break;
141 case 't': /* layout tolerance */
142 tolerance = atof(*argv+2);
143 break;
144 case 's': /* override scale */
145 uscale = atof(*argv+2);
146 break;
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);
151 } else
152 message(FATAL, "paper size '%s' not recognised\n", *argv+2);
153 break;
154 case 'P': /* paper type */
155 if ( (paper = findpaper(*argv+2)) != (Paper *)0 ) {
156 iwidth = (double)PaperWidth(paper);
157 iheight = (double)PaperHeight(paper);
158 } else
159 message(FATAL, "paper size '%s' not recognised\n", *argv+2);
160 break;
161 case 'n': /* n-up, for compatibility with other psnups */
162 if (argc >= 2) {
163 argv++;
164 argc--;
165 if ((nup = atoi(*argv)) < 1)
166 message(FATAL, "-n %d too small\n", nup);
167 } else
168 message(FATAL, "argument expected for -n\n");
169 break;
170 case '1':
171 case '2':
172 case '3':
173 case '4':
174 case '5':
175 case '6':
176 case '7':
177 case '8':
178 case '9':
179 nup = atoi(*argv+1);
180 break;
181 case 'v': /* version */
182 default:
183 usage();
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);
191 } else usage();
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");
204 #endif
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 */
219 if ( iwidth > 0 )
220 width = iwidth ;
221 if ( iheight > 0 )
222 height = iheight ;
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;
229 int hor;
230 for (hor = 1; hor; hor = nextdiv(hor, nup)) {
231 int ver = nup/hor;
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);
236 if (optim < best) {
237 best = optim;
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;
244 rotate = flip;
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);
250 if (optim < best) {
251 best = optim;
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;
258 rotate = !flip;
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 */
268 double tmp = width;
269 width = height;
270 height = tmp;
273 if (rotate) { /* rotate leftright and topbottom orders */
274 int tmp = topbottom;
275 topbottom = !leftright;
276 leftright = tmp;
277 column = !column;
280 /* now construct specification list and run page rearrangement procedure */
282 int page = 0;
283 PageSpec *specs, *tail;
285 tail = specs = newspec();
287 while (page < nup) {
288 int up, across; /* page index */
290 if (column) {
291 if (leftright) /* left to right */
292 across = page/vert;
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 */
298 up = page%vert;
299 } else {
300 if (leftright) /* left to right */
301 across = page%horiz;
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 */
307 up = page/horiz;
309 if (rotate) {
310 tail->xoff = margin + (across+1)*ppwid/horiz - hshift;
311 tail->rotate = 90;
312 tail->flags |= ROTATE;
313 } else {
314 tail->xoff = margin + across*ppwid/horiz + hshift;
316 tail->pageno = page;
317 if (uscale > 0)
318 tail->scale = uscale;
319 else
320 tail->scale = scale;
321 tail->flags |= SCALE;
322 tail->yoff = margin + up*pphgt/vert + vshift;
323 tail->flags |= OFFSET;
324 if (++page < nup) {
325 tail->flags |= ADD_NEXT;
326 tail->next = newspec();
327 tail = tail->next;
331 pstops(nup, 1, 0, specs, draw); /* do page rearrangement */
334 exit(0);