diff options
Diffstat (limited to 'Build/source/utils/psutils/psnup.c')
-rw-r--r-- | Build/source/utils/psutils/psnup.c | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/Build/source/utils/psutils/psnup.c b/Build/source/utils/psutils/psnup.c new file mode 100644 index 00000000000..03fa0e901ef --- /dev/null +++ b/Build/source/utils/psutils/psnup.c @@ -0,0 +1,336 @@ +/* psnup.c + * Copyright (C) Angus J. C. Duggan 1991-1995 + * See file LICENSE for details. + * + * put multiple pages onto one physical sheet of paper + * + * Usage: + * psnup [-q] [-w<dim>] [-h<dim>] [-ppaper] [-b<dim>] [-m<dim>] + * [-l] [-c] [-f] [-sscale] [-d<wid>] [-nup] [in [out]] + * -w<dim> sets the paper width + * -h<dim> sets the paper height + * -ppaper sets the paper size (width and height) by name + * -W<dim> sets the input paper width, if different from output + * -H<dim> sets the input paper height, if different from output + * -Ppaper sets the input paper size, if different from output + * -m<dim> sets the margin around the paper + * -b<dim> sets the border around each page + * -sscale alters the scale at which the pages are displayed + * -l used if pages are in landscape orientation (rot left) + * -r used if pages are in seascape orientation (rot right) + * -c for column-major layout + * -f for flipped (wider than tall) pages + * -d<wid> to draw the page boundaries + */ + +#include "psutil.h" +#include "psspec.h" +#include "pserror.h" +#include "patchlev.h" + +char *program ; +int pages ; +int verbose ; +FILE *infile ; +FILE *outfile ; +char pagelabel[BUFSIZ] ; +int pageno ; + +static void usage(void) +{ + fprintf(stderr, "%s release %d patchlevel %d\n", program, RELEASE, PATCHLEVEL); + fprintf(stderr, "Copyright (C) Angus J. C. Duggan, 1991-1995. See file LICENSE for details.\n"); + fprintf(stderr, "Usage: %s [-q] [-wwidth] [-hheight] [-ppaper] [-Wwidth] [-Hheight] [-Ppaper] [-l] [-r] [-c] [-f] [-mmargin] [-bborder] [-dlwidth] [-sscale] [-nup] [infile [outfile]]\n", + program); + fflush(stderr); + exit(1); +} + +static void argerror(void) +{ + message(FATAL, "bad dimension\n"); +} + +#define MIN(x,y) ((x) > (y) ? (y) : (x)) +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +/* return next larger exact divisor of number, or 0 if none. There is probably + * a much more efficient method of doing this, but the numbers involved are + * small, so it's not a big loss. */ +static int nextdiv(int n, int m) +{ + while (++n <= m) { + if (m%n == 0) + return (n); + } + return (0); +} + +void main(int argc, char *argv[]) +{ + int horiz, vert, rotate, column, flip, leftright, topbottom; + int nup = 1; + double draw = 0; /* draw page borders */ + double scale; /* page scale */ + double uscale = 0; /* user supplied scale */ + double ppwid, pphgt; /* paper dimensions */ + double margin, border; /* paper & page margins */ + double vshift, hshift; /* page centring shifts */ + double iwidth, iheight ; /* input paper size */ + double tolerance = 100000; /* layout tolerance */ + Paper *paper; + +#ifdef PAPER + if ( (paper = findpaper(PAPER)) != (Paper *)0 ) { + width = (double)PaperWidth(paper); + height = (double)PaperHeight(paper); + } +#endif + + margin = border = vshift = hshift = column = flip = 0; + leftright = topbottom = 1; + iwidth = iheight = -1 ; + + infile = stdin; + outfile = stdout; + verbose = 1; + for (program = *argv++; --argc; argv++) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'q': /* quiet */ + verbose = 0; + break; + case 'd': /* draw borders */ + if (argv[0][2]) + draw = singledimen(*argv+2, argerror, usage); + else + draw = 1; + break; + case 'l': /* landscape (rotated left) */ + column = !column; + topbottom = !topbottom; + break; + case 'r': /* seascape (rotated right) */ + column = !column; + leftright = !leftright; + break; + case 'f': /* flipped */ + flip = 1; + break; + case 'c': /* column major layout */ + column = !column; + break; + case 'w': /* page width */ + width = singledimen(*argv+2, argerror, usage); + break; + case 'W': /* input page width */ + iwidth = singledimen(*argv+2, argerror, usage); + break; + case 'h': /* page height */ + height = singledimen(*argv+2, argerror, usage); + break; + case 'H': /* input page height */ + iheight = singledimen(*argv+2, argerror, usage); + break; + case 'm': /* margins around whole page */ + margin = singledimen(*argv+2, argerror, usage); + break; + case 'b': /* border around individual pages */ + border = singledimen(*argv+2, argerror, usage); + break; + case 't': /* layout tolerance */ + tolerance = atof(*argv+2); + break; + case 's': /* override scale */ + uscale = atof(*argv+2); + break; + case 'p': /* output (and by default input) paper type */ + if ( (paper = findpaper(*argv+2)) != (Paper *)0 ) { + width = (double)PaperWidth(paper); + height = (double)PaperHeight(paper); + } else + message(FATAL, "paper size '%s' not recognised\n", *argv+2); + break; + case 'P': /* paper type */ + if ( (paper = findpaper(*argv+2)) != (Paper *)0 ) { + iwidth = (double)PaperWidth(paper); + iheight = (double)PaperHeight(paper); + } else + message(FATAL, "paper size '%s' not recognised\n", *argv+2); + break; + case 'n': /* n-up, for compatibility with other psnups */ + if (argc >= 2) { + argv++; + argc--; + if ((nup = atoi(*argv)) < 1) + message(FATAL, "-n %d too small\n", nup); + } else + message(FATAL, "argument expected for -n\n"); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + nup = atoi(*argv+1); + break; + case 'v': /* version */ + default: + usage(); + } + } else if (infile == stdin) { + if ((infile = fopen(*argv, OPEN_READ)) == NULL) + message(FATAL, "can't open input file %s\n", *argv); + } else if (outfile == stdout) { + if ((outfile = fopen(*argv, OPEN_WRITE)) == NULL) + message(FATAL, "can't open output file %s\n", *argv); + } else usage(); + } +#if defined(MSDOS) || defined(WINNT) || defined(WIN32) + if ( infile == stdin ) { + int fd = fileno(stdin) ; + if ( setmode(fd, O_BINARY) < 0 ) + message(FATAL, "can't open input file %s\n", argv[4]); + } + if ( outfile == stdout ) { + int fd = fileno(stdout) ; + if ( setmode(fd, O_BINARY) < 0 ) + message(FATAL, "can't reset stdout to binary mode\n"); + } +#endif + if ((infile=seekable(infile))==NULL) + message(FATAL, "can't seek input\n"); + + if (width <= 0 || height <= 0) + message(FATAL, "page width and height must be set\n"); + + /* subtract paper margins from height & width */ + ppwid = width - margin*2; + pphgt = height - margin*2; + + if (ppwid <= 0 || pphgt <= 0) + message(FATAL, "paper margins are too large\n"); + + /* set default values of input height & width */ + if ( iwidth > 0 ) + width = iwidth ; + if ( iheight > 0 ) + height = iheight ; + + /* Finding the best layout is an optimisation problem. We try all of the + * combinations of width*height in both normal and rotated form, and + * minimise the wasted space. */ + { + double best = tolerance; + int hor; + for (hor = 1; hor; hor = nextdiv(hor, nup)) { + int ver = nup/hor; + /* try normal orientation first */ + double scl = MIN(pphgt/(height*ver), ppwid/(width*hor)); + double optim = (ppwid-scl*width*hor)*(ppwid-scl*width*hor) + + (pphgt-scl*height*ver)*(pphgt-scl*height*ver); + if (optim < best) { + best = optim; + /* recalculate scale to allow for internal borders */ + scale = MIN((pphgt-2*border*ver)/(height*ver), + (ppwid-2*border*hor)/(width*hor)); + hshift = (ppwid/hor - width*scale)/2; + vshift = (pphgt/ver - height*scale)/2; + horiz = hor; vert = ver; + rotate = flip; + } + /* try rotated orientation */ + scl = MIN(pphgt/(width*hor), ppwid/(height*ver)); + optim = (pphgt-scl*width*hor)*(pphgt-scl*width*hor) + + (ppwid-scl*height*ver)*(ppwid-scl*height*ver); + if (optim < best) { + best = optim; + /* recalculate scale to allow for internal borders */ + scale = MIN((pphgt-2*border*hor)/(width*hor), + (ppwid-2*border*ver)/(height*ver)); + hshift = (ppwid/ver - height*scale)/2; + vshift = (pphgt/hor - width*scale)/2; + horiz = ver; vert = hor; + rotate = !flip; + } + } + + /* fail if nothing better than worst tolerance was found */ + if (best == tolerance) + message(FATAL, "can't find acceptable layout for %d-up\n", nup); + } + + if (flip) { /* swap width & height for clipping */ + double tmp = width; + width = height; + height = tmp; + } + + if (rotate) { /* rotate leftright and topbottom orders */ + int tmp = topbottom; + topbottom = !leftright; + leftright = tmp; + column = !column; + } + + /* now construct specification list and run page rearrangement procedure */ + { + int page = 0; + PageSpec *specs, *tail; + + tail = specs = newspec(); + + while (page < nup) { + int up, across; /* page index */ + + if (column) { + if (leftright) /* left to right */ + across = page/vert; + else /* right to left */ + across = horiz-1-page/vert; + if (topbottom) /* top to bottom */ + up = vert-1-page%vert; + else /* bottom to top */ + up = page%vert; + } else { + if (leftright) /* left to right */ + across = page%horiz; + else /* right to left */ + across = horiz-1-page%horiz; + if (topbottom) /* top to bottom */ + up = vert-1-page/horiz; + else /* bottom to top */ + up = page/horiz; + } + if (rotate) { + tail->xoff = margin + (across+1)*ppwid/horiz - hshift; + tail->rotate = 90; + tail->flags |= ROTATE; + } else { + tail->xoff = margin + across*ppwid/horiz + hshift; + } + tail->pageno = page; + if (uscale > 0) + tail->scale = uscale; + else + tail->scale = scale; + tail->flags |= SCALE; + tail->yoff = margin + up*pphgt/vert + vshift; + tail->flags |= OFFSET; + if (++page < nup) { + tail->flags |= ADD_NEXT; + tail->next = newspec(); + tail = tail->next; + } + } + + pstops(nup, 1, 0, specs, draw); /* do page rearrangement */ + } + + exit(0); +} + |