diff options
Diffstat (limited to 'Build/source/texk/psutils/psutils-src/psnup.c')
-rw-r--r-- | Build/source/texk/psutils/psutils-src/psnup.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/Build/source/texk/psutils/psutils-src/psnup.c b/Build/source/texk/psutils/psutils-src/psnup.c new file mode 100644 index 00000000000..5efb2c13385 --- /dev/null +++ b/Build/source/texk/psutils/psutils-src/psnup.c @@ -0,0 +1,315 @@ +/* psnup.c + * Put multiple pages on to one page + * + * (c) Reuben Thomas 2012-2014 + * (c) Angus J. C. Duggan 1991-1997 + * See file LICENSE for details. + */ + +#include "config.h" + +#include <unistd.h> +#include <string.h> +#include "progname.h" +#include "binary-io.h" + +#include "psutil.h" +#include "psspec.h" + +const char *syntax = "[-q] [-wWIDTH] [-hHEIGHT] [-pPAPER] [-WWIDTH] [-HHEIGHT] [-PPAPER] [-l] [-r] [-c] [-f] [-mMARGIN] [-bBORDER] [-dLWIDTH] [-sSCALE] [-NUP] [INFILE [OUTFILE]]\n"; + +const char *argerr_message = "bad dimension\n"; + +/* 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. */ +_GL_ATTRIBUTE_CONST static int nextdiv(int n, int m) +{ + while (++n <= m) { + if (m%n == 0) + return (n); + } + return (0); +} + +int +main(int argc, char *argv[]) +{ + int horiz = 0, vert = 0, rotate = 0, column = 0; + int flip = 0, leftright = 0, topbottom = 0; + int nup = 1; + double draw = 0; /* draw page borders */ + double scale = 1.0; /* 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 */ + off_t sizeheaders[20]; /* headers to remove */ + int opt; + + set_program_name (argv[0]); + + verbose = 1; + + if (!paper_size(NULL, &width, &height)) + die("could not get default paper size"); + + margin = border = vshift = hshift = column = flip = 0; + leftright = topbottom = 1; + iwidth = iheight = -1 ; + + while((opt = + getopt(argc, argv, + "qd::lrfcw:W:h:H:m:b:t:s:p:P:n:1::2::3::4::5::6::7::8::9::")) + != EOF) { + switch(opt) { + case 'q': /* quiet */ + verbose = 0; + break; + case 'd': /* draw borders */ + if (optarg) + draw = singledimen(optarg); + 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(optarg); + break; + case 'W': /* input page width */ + iwidth = singledimen(optarg); + break; + case 'h': /* page height */ + height = singledimen(optarg); + break; + case 'H': /* input page height */ + iheight = singledimen(optarg); + break; + case 'm': /* margins around whole page */ + margin = singledimen(optarg); + break; + case 'b': /* border around individual pages */ + border = singledimen(optarg); + break; + case 't': /* layout tolerance */ + tolerance = atof(optarg); + break; + case 's': /* override scale */ + uscale = atof(optarg); + break; + case 'p': /* output (and by default input) paper type */ + if (!paper_size(optarg, &width, &height)) + die("paper size '%s' not recognised", optarg); + break; + case 'P': /* paper type */ + if (!paper_size(optarg, &width, &height)) + die("paper size '%s' not recognised", optarg); + break; + case 'n': /* n-up, for compatibility with other psnups */ + if ((nup = atoi(optarg)) < 1) + die("-n %d too small", nup); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if(optarg) { + char *valuestr = (char *) malloc(strlen(optarg) + 2); + valuestr[0] = opt; + strcpy(&(valuestr[1]), optarg); + + /* really should check that valuestr is only digits here...*/ + if ((nup = atoi(valuestr)) < 1) + die("-n %d too small", nup); + free(valuestr); + } else { + nup = (opt - '0'); + } + break; + case 'v': /* version */ + default: + usage(); + } + } + + infile = stdin; + outfile = stdout; + + /* Be defensive */ + if((argc - optind) < 0 || (argc - optind) > 2) usage(); + + if (optind != argc) { + /* User specified an input file */ + if ((infile = fopen(argv[optind], "rb")) == NULL) + die("can't open input file %s", argv[optind]); + optind++; + } + + if (optind != argc) { + /* User specified an output file */ + if ((outfile = fopen(argv[optind], "wb")) == NULL) + die("can't open output file %s", argv[optind]); + optind++; + } + + if (optind != argc) usage(); + + if (infile == stdin && set_binary_mode(fileno(stdin), O_BINARY) < 0) + die("can't reset stdin to binary mode"); + if (outfile == stdout && set_binary_mode(fileno(stdout), O_BINARY) < 0) + die("can't reset stdout to binary mode"); + + if ((infile=seekable(infile))==NULL) + die("can't seek input"); + + if (width <= 0 || height <= 0) + die("page width and height must be set"); + + /* subtract paper margins from height & width */ + ppwid = width - margin*2; + pphgt = height - margin*2; + + if (ppwid <= 0 || pphgt <= 0) + die("paper margins are too large"); + + scanpages(sizeheaders); + + /* set default values of input height & width */ + if ( iwidth > 0 && width == -1 ) + width = iwidth ; + if ( iheight > 0 && height == -1 ) + 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) + die("can't find acceptable layout for %d-up", 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_write(nup, 1, 0, specs, draw, sizeheaders); /* do page rearrangement */ + } + + return 0; +} + |