home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #27 / NN_1992_27.iso / spool / comp / lang / postscri / 5535 < prev    next >
Encoding:
Text File  |  1992-11-15  |  24.8 KB  |  908 lines

  1. Newsgroups: comp.lang.postscript
  2. Path: sparky!uunet!gumby!wupost!micro-heart-of-gold.mit.edu!news.media.mit.edu!news
  3. From: mike@media-lab.mit.edu (Michael Hawley)
  4. Subject: quarto
  5. Message-ID: <1992Nov16.023153.29054@news.media.mit.edu>
  6. Sender: news@news.media.mit.edu (USENET News System)
  7. Reply-To: mike@media-lab.mit.edu
  8. Organization: MIT Media Laboratory
  9. Date: Mon, 16 Nov 1992 02:31:53 GMT
  10. Lines: 896
  11.  
  12. There have been a few more requests for an "N-up" program
  13. (that works with embedded EPS illustrations)
  14. ways of listing pages, printing booklets in signatures, etc.
  15. Here's a simple one I use on the NeXT (known to work on other
  16. Unix machines).
  17.     quarto -l your-dissertation.ps
  18. lists the pages, and
  19.     quarto -2 your-nobel-prize-acceptance-speech.ps | lpr
  20. prints 2 pages per sheet.
  21.     quarto -8 ... | quarto -3 | ...
  22. prints 8*3 = 24 little pages per sheet, if that's really what you want.
  23.  
  24. - - - -
  25. /*
  26.  * quarto.c
  27.  *
  28.  * Quickie program to filter PostScript (EPS) files and...
  29.  *   - extract selected pages
  30.  *         ... | quarto -p1,2,6-8 ...    print those pages
  31.  *   - change page order (reverse, even, odd; etc)
  32.  *         ... | quarto -r ...           print in reverse order
  33.  *         ... | quarto -p odd ...       select the odd pages
  34.  *   - print "N-up" (quarto/octavo; N=2,3,4,6,8,9) layouts
  35.  *         ... | quarto -4 -c -b ...     print 4 (small) pages per side
  36.  *
  37.  * For printing books or booklets in signatures, e.g.,
  38.  *   quarto -2 -S 4 ...
  39.  * means that a signature is 4 sheets, 2 (small) pages per side.
  40.  * The page ordering is such that you can run them through a copier
  41.  * printing 2 pages onto 1 double-sided page (or take every other sheet,
  42.  * flip it over, and glue it back to back on the previous sheet).
  43.  *
  44.  * Currently only tested and used with a NeXT printer + Letter-size  
  45. paper,
  46.  * with input in portrait orientation.  It should be easy to fix for
  47.  * other varieties; check PBox, the test pattern, the calculation of  
  48. 'Scale',
  49.  * and the macro 'pgx()' (used by the 'putNup()' routines).  If you can
  50.  * print out test patterns (the "-d" option) you're close if not  
  51. working.
  52.  *
  53.  * Enjoy at your own peril, but please send me any improvements.
  54.  *
  55.  * Also, if you like this program and get good use out of it, consider
  56.  * sending $4.37 to:
  57.  *    Bob Greene
  58.  *    Media Lab Crew Treasurer
  59.  *    MIT Media Lab
  60.  *    20 Ames Street
  61.  *    Cambridge, MA 02139
  62.  * 
  63.  *    Say "Yo Bob!  I'm dying to find out how your boys did at Henley.
  64.  *         Did they really row in the nude because you are too poor
  65.  *         to buy uniforms?  Perhaps this miserly contribution will  
  66. help."
  67.  *
  68.  *
  69.  * M. J. Hawley
  70.  * MIT media Laboratory
  71.  * 20 Ames Street
  72.  * Cambridge, MA 02139
  73.  * mike@media-lab.mit.edu
  74.  * Copyright (c) MIT Media Laboratory 1991.
  75.  * Top secret!  Burn before reading!  This means you!!
  76.  *
  77.  *
  78.  * Notes -- version 1.1
  79.  *
  80.  * - added signature printing and fixed the nested-page bug (November,  
  81. 1992)
  82.  */
  83. #include <stdio.h>
  84. #include <ctype.h>
  85. #include <sys/types.h>
  86.  
  87. /*
  88.  * Some general utilities ----------------------------------------
  89.  */
  90. char *av0;
  91. #define Case break; case
  92. #define Default break; default
  93. static char *_arg, *_argp; /* use by 'for_each_argument */
  94. extern char *av0;       /* will hold name of the command */
  95. #define argument  (_arg=(*_argp? _argp : av[++i==ac? --i :  
  96. i]),_argp+=strlen(_argp),_arg)
  97. #define for_each_argument av0 = av[0]; for (i=1;i<ac &&  
  98. *av[i]=='-';i++)\
  99.                         for (_argp = &av[i][1]; *_argp;)\
  100.                                 switch(*_argp++)
  101.  
  102. extern char *malloc(), *index();
  103. extern int atoi();
  104. extern double atof();
  105.  
  106. stripnl(s) char *s; { /* remove trailing \n and space from 's' */
  107.     char *p = s + strlen(s)-1;
  108.     while (p>s && (*p=='\n' || *p == ' ')) *p-- = '\0';
  109. }
  110.  
  111. char *
  112. skipsp(s) char *s; {
  113.     while (*s==' ' || *s=='\t' || *s == '\n') ++s;
  114.     return s;
  115. }
  116.  
  117. error(a,b,c,d,e,f) int a,b,c,d,e,f; { /* printf an error msg */
  118.     fprintf(stderr,(char *)a,b,c,d,e,f); fprintf(stderr,"\n");
  119. }
  120.  
  121. /* ---------------------------------------------------------------- */
  122.  
  123. FILE *Input, *Output;
  124. char *CurFile;
  125.  
  126. typedef struct { float x, y; } Point;
  127. typedef struct { Point o, c; } Rectangle;
  128. #define HS(r) (r.c.x-r.o.x)
  129. #define VS(r) (r.c.y-r.o.y)
  130.  
  131. Rectangle BBox = { 0, 0, 612, 792 }; /* bbox of the document */
  132.  
  133. Rectangle PBox = { 10, 14, 600, 780 }; /* Letter-sized page-bounding box  
  134. */
  135.                  /* These values probably depend on the printer and
  136.                   * paper size; use the test pattern to adjust them for  
  137. now.
  138.                   */
  139. Rectangle NullRect = {0,0,0,0};
  140.  
  141. int TestPattern = 0;    /* if true, overlay a test pattern on the page*/
  142. int ListPages = 0;      /* if true, list pages in the document (no  
  143. print) */
  144.  
  145. #define _N_up 1
  146. int N_up = 1;           /* default # pages to fit on a sheet */
  147.  
  148. #define _Gutter 12.
  149. float Gutter= _Gutter;
  150. #define _CutMarks 0
  151. #define _Borders  1
  152. int CutMarks=_CutMarks, /* print cutmarks around shrunken pages */
  153.     Borders=_Borders;   /* print dashed borders around shrunken pages */
  154. int ReversePages = 0;
  155. int Signatures = 0;    /* determine signature page order */
  156.  
  157. float Scale = .45;    /* Scale is normally set by the tiling routines;  
  158. */
  159. int ScaleSet = 0;    /* made a user option just in case. */
  160. int Rotate = 0;         /* will be true when landscaping for -2,6,8 */
  161.  
  162. #define MaxP 1024
  163. #define Trailer (MaxP-1)
  164. int Page[MaxP], PageLen[MaxP];
  165. Rectangle PageBbox[MaxP], pageBbox;
  166. int NP=0;
  167.  
  168. findPageOffsets(){
  169.     char s[4192], *s2=s+2;
  170.     int i,prevpos=0,prevp=0,p,nested=0;
  171.     FILE *f = Input;
  172.  
  173.     for(i=0;i<MaxP;i++){
  174.         Page[i]=PageLen[i]=0;
  175.         PageBbox[i] = NullRect;
  176.     }
  177.  
  178.     rewind(f);
  179.     while (fgets(s,sizeof s,f)){
  180.      if (s[0]=='%' && s[1]=='%'){
  181.       if (strncmp(s2,"BeginDocument",13)==0 ||
  182.           strncmp(s2,"BeginBinary",11)==0){
  183.             nested++;
  184.       } else
  185.       if (strncmp(s2,"EndDocument",11)==0 ||
  186.           strncmp(s2,"EndBinary",9)==0){
  187.             if (nested>0) nested--;
  188.       } else
  189.       if (nested==0){
  190.         if (strncmp(s2,"Page:",5)==0){
  191.             sscanf(s,"%%%%Page: %*s %d",&p);
  192.             Page[p] = ftell(f)-strlen(s);
  193.             PageLen[prevp] = Page[p] - prevpos;
  194.             prevp = p;
  195.             prevpos = Page[p];
  196.             NP++;
  197.         } else
  198.         if (strncmp(s2,"Trailer",7)==0){
  199.             Page[Trailer] = ftell(f)-strlen(s);
  200.             PageLen[prevp] = Page[Trailer] - prevpos;
  201.             prevpos = Page[Trailer];
  202.         } else
  203.         if (strncmp(s2,"BoundingBox:",12)==0){
  204.             char *q = skipsp(index(s,':')+1);
  205.             if (isdigit(*q))
  206.                 sscanf(q,"%f %f %f %f",
  207.                          &BBox.o.x,&BBox.o.y,&BBox.c.x,&BBox.c.y);
  208.         } else
  209.         if (strncmp(s2,"PageBoundingBox:",16)==0){
  210.             Rectangle r;
  211.             char *q = skipsp(index(s,':')+1);
  212.             if (isdigit(*q)){
  213.                 sscanf(q,"%f %f %f %f", &r.o.x,&r.o.y,&r.c.x,&r.c.y);
  214.                 if (NP) PageBbox[NP] = r;
  215.             }
  216.         }
  217.       }
  218.      }
  219.     }
  220.     PageLen[Trailer] = ftell(f) - prevpos;
  221.     pageBbox = PageBbox[1]; /* hack ... */
  222.         /*
  223.          * Some NeXT programs put out PageBoundingBoxes
  224.          * with various origins for each page,
  225.          * but draw relative to the *first* PageBoundingBox.
  226.          * See "hack", below.
  227.          */
  228. }
  229.  
  230. showPageOffsets() {
  231.     char s[4192];
  232.     int i,j;
  233.     FILE *f = Input;
  234.  
  235.     printf("file: %s, %d pages\n",CurFile,NP);
  236.     printf("%8s %8s\n","offset","length");
  237.     for (i=j=0;j<=NP;i++){
  238.         if (Page[i] || i==0){
  239.             fseek(f,Page[i],0);
  240.             fgets(s,sizeof s,f);
  241.             printf("%8d %8d -- %s%s",Page[i], PageLen[i],i?"":"(header)  
  242. ",s);
  243.             j++;
  244.         }
  245.     }
  246.     fseek(f,Page[Trailer],0);
  247.     fgets(s,sizeof s,f);
  248.     printf("%8d %8d -- (trailer) %s",Page[Trailer],PageLen[Trailer],s);
  249. }
  250.  
  251. static char *_s = (char *)0;
  252. static int _sn = 0;
  253.  
  254. char *
  255. readPage(n){
  256.     size_t r;
  257.     if (_s) *_s = '\0';
  258.     if (Page[n] || (n==0 && PageLen[n])){
  259.         if (_sn < PageLen[n]){
  260.             _sn = 2*PageLen[n];
  261.             _s = (_s? (char *)realloc(_s,_sn) : (char *)malloc(_sn));
  262.             if (!_s) error("%s: no memory!",av0), exit(1);
  263.         }
  264.         rewind(Input);
  265.         fseek(Input,Page[n],0);
  266.         *_s = '\0';
  267.         fread(_s,PageLen[n], (size_t)1, Input);
  268.     }
  269.     return _s;
  270. }
  271.  
  272. copyPage(n)
  273. /*
  274.  * Write the 'n'th page in 'f' onto 'o'.
  275.  */
  276. {
  277.     char *s = readPage(n);
  278.     if (*s) fwrite(s,PageLen[n],1,Output);
  279. }
  280.  
  281. P(a,b,c,d,e,f,g,h,i){
  282.     fprintf(Output,(char *)a,b,c,d,e,f,g,h,i);
  283.     fprintf(Output,"\n");
  284. }
  285.  
  286. putProlog(n){
  287.     char *date, *ctime();
  288.     time_t t;
  289.  
  290.     time(&t); date = ctime(&t); stripnl(date);
  291.     P("%%!PS-Adobe-2.0");
  292.     P("%%%%Creator: %s, file: %s",av0,CurFile);
  293.     P("%%%%CreationDate: %s",date);
  294.     P("%%%%Pages: (atend)",n);
  295.     P("%%%%BoundingBox: 0 0 612 792");  /* should have options to set */
  296.     P("%%%%EndComments");
  297.     P("");
  298.     P("%% ---------------------- (routines for \"%s\")",av0);
  299.     P("/q_W %f def  /q_H %f def  /q_margin 4 def",BBox.c.x,BBox.c.y);
  300.     P("/q_factor %f def",Scale);
  301.     P("/q_bbox {");
  302.     if (Borders){
  303.     P("    /q_y 0 q_factor mul def");
  304.     P("    /q_x 0 q_factor mul def");
  305.     P("    /q_w q_W q_margin 2 mul add def");
  306.     P("    /q_h q_H q_margin 2 mul add def");
  307.     P("    q_x q_margin sub q_y q_margin sub moveto");
  308.     P("    .2 setgray");
  309.     P("    0 setlinewidth");
  310.     P("    q_w 0 rlineto");
  311.     P("    0 q_h rlineto");
  312.     P("    0 q_w sub 0 rlineto");
  313.     P("    0 0 q_h sub rlineto");
  314.     P("    stroke");
  315.     }
  316.     P("} def");
  317.     P("");
  318.     P("/q_cuts {");
  319.     if (CutMarks){
  320.     P("    /q_y 0 q_factor mul q_margin sub def");
  321.     P("    /q_x 0 q_factor mul q_margin sub def");
  322.     P("    /q_w q_W q_margin 2 mul add def");
  323.     P("    /q_h q_H q_margin 2 mul add def");
  324.     P("    /q_d 10 q_factor mul def");
  325.     P("    0 setgray");
  326.     P("    0 setlinewidth");
  327.     P("    q_x q_d sub q_y moveto -1 -1 rmoveto");
  328.     P("        q_d 0 rlineto 0 0 q_d sub rlineto stroke");
  329.     P("    q_x q_d add q_w add q_y moveto 1 -1 rmoveto");
  330.     P("        0 q_d sub 0 rlineto 0 0 q_d sub rlineto stroke");
  331.     P("    q_x q_d add q_w add q_y q_h add moveto 1 1 rmoveto");
  332.     P("        0 q_d sub 0 rlineto 0 q_d rlineto stroke");
  333.     P("    q_x q_d sub q_y q_h add moveto -1 1 rmoveto");
  334.     P("        q_d 0 rlineto 0 0 q_d rlineto stroke");
  335.     }
  336.     P("} def");
  337.     P("");
  338.     P("/StartShrunkenPage {");
  339.     P("    /q_y exch def /q_x exch def");
  340.     P("    gsave");
  341.     P("    q_x q_y translate");
  342.     P("    q_factor q_factor scale");
  343.     if (Rotate) P("    -90 rotate");
  344.     P("} def");
  345.     P("");
  346.     P("/EndShrunkenPage {");
  347.     P("    grestore");
  348.     P("} def");
  349.     P("");
  350.     if (N_up!=1){
  351.        P("/oldshowpage {showpage} bind def");
  352.        P("/showpage { } bind def");
  353.     }
  354.     P("");
  355.     P("/q_circ { %% x y size ... draw circle + crosshairs");
  356.     P("    /q_size exch def /q_y exch def /q_x exch def");
  357.     P("    0 setlinewidth");
  358.     P("    q_x q_y moveto");
  359.     P("    q_size 0 rlineto");
  360.     P("    -2 q_size mul 0 rlineto");
  361.     P("    q_size 0 rlineto");
  362.     P("    0 q_size rlineto");
  363.     P("    0 -2 q_size mul rlineto");
  364.     P("    stroke");
  365.     P("    q_x q_y moveto");
  366.     P("    q_x q_y q_size 2 div 0 360 arc stroke");
  367.     P("} def");
  368.     P("");
  369.     P("/q_box { %% lx ly ux uy ... draw a box");
  370.     P("    /q_uy exch def /q_ux exch def /q_ly exch def /q_lx exch  
  371. def");
  372.     P("    q_lx q_ly moveto");
  373.     P("    q_ux q_ly lineto");
  374.     P("    q_ux q_uy lineto");
  375.     P("    q_lx q_uy lineto");
  376.     P("    q_lx q_ly lineto");
  377.     P("    stroke");
  378.     P("} def");
  379.     P("");
  380.     P("/q_testPattern {");
  381.     P("    0 setgray 0 setlinewidth");
  382.     P("    %f %f %f %f q_box", PBox.o.x,PBox.o.y,PBox.c.x,PBox.c.y);
  383.     P("    %f %f moveto 8 0 rlineto stroke",  
  384. PBox.o.x-4,VS(PBox)/2+PBox.o.y);
  385.     P("    %f %f moveto -8 0 rlineto stroke",  
  386. PBox.c.x+4,VS(PBox)/2+PBox.o.y);
  387.     P("    %f %f moveto 0 8 rlineto stroke",  
  388. HS(PBox)/2+PBox.o.x,PBox.o.y-4);
  389.     P("    %f %f moveto 0 -8 rlineto stroke",  
  390. HS(PBox)/2+PBox.o.x,PBox.c.y+4);
  391.     P("    %f %f 5 q_circ",HS(PBox)/2+PBox.o.x,VS(PBox)/2+PBox.o.y);
  392.     P("} def");
  393.     P("");
  394.     P("%% ----------------------(end of \"%s\" routines)\n",av0);
  395.     copyPage(0);  /* put out the previous document header page */
  396. }
  397.  
  398. ShowPage(){
  399.     if (TestPattern) P("q_testPattern");
  400.     P("q_sheet restore\noldshowpage");
  401. }
  402.  
  403. putTrailer(n){
  404.     copyPage(Trailer);
  405.     P("%%%%Pages: %d %d",n,n);
  406. }
  407.  
  408. static Point
  409. center(r) Rectangle r; {
  410.     Point c;
  411.     c.x = HS(r)/2. + r.o.x;
  412.     c.y = VS(r)/2. + r.o.y;
  413.     return c;
  414. }
  415.  
  416. static Point
  417. add(a,b) Point a,b; {
  418.     a.x += b.x;
  419.     a.y += b.y;
  420.     return a;
  421. }
  422.  
  423. static Point
  424. sub(a,b) Point a,b; {
  425.     a.x -= b.x;
  426.     a.y -= b.y;
  427.     return a;
  428. }
  429.  
  430. hideOriginalPages(s) char *s; {
  431.     s[2] = 'p';
  432.     while ((s = index(s,'\n')+1) != (char *)1)
  433.         if (strncmp(s,"%%Page:",7)==0) s[2] = 'p'; 
  434. }
  435.  
  436. pput(n,p) Point p; {
  437.     char *s = readPage(n);
  438.     Rectangle r;
  439.     if (*s){
  440.         hideOriginalPages(s);
  441.         r = pageBbox; /* PageBbox[n]; ... Thus endeth the hack. */
  442.     P("%f %f StartShrunkenPage",p.x,p.y);
  443.     P("%f %f translate",-1.*Scale*r.o.x,-1.*Scale*r.o.y);
  444.         fwrite(s,PageLen[n],1,Output);
  445.         P("EndShrunkenPage");
  446.     }
  447. }
  448.  
  449. float
  450. fmin(a,b) float a,b; {
  451.     return a<b? a : b;
  452. }
  453.  
  454. #define maxQ 16
  455.  
  456. #define up_decls \
  457.     int i, j, N = (NP+(N_up-1))/N_up, page[maxQ];\
  458.     float gap = Gutter;\
  459.     Point c, p, g, b, q[maxQ];\
  460.     g.x=g.y = gap/2.; b = BBox.c
  461.  
  462. #define nup_init() c = center(PBox); \
  463.                    b.x *= Scale; b.y *= Scale; \
  464.                    putProlog(N)
  465.  
  466. #define pgx(inc,d) pput(i*N_up+j+1,p); page[j] = *_s; q[j++]=p; p.d +=  
  467. inc
  468. #define putbox() for (j=0; j<N_up;j++) \
  469.                    if (page[j])\
  470.                     P("%f %f StartShrunkenPage q_bbox q_cuts  
  471. EndShrunkenPage",\
  472.                         q[j].x,q[j].y); \
  473.                  ShowPage()
  474.  
  475.  
  476. put2up(){
  477.     up_decls;
  478.  
  479.     if (!ScaleSet)
  480.         Scale =  
  481. fmin((VS(PBox)/2.-(2.*gap))/b.x,((HS(PBox)-(2.*gap))/b.y));
  482.     nup_init();
  483.  
  484.     for (i=0;i<N;i++){
  485.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  486.         j = 0;
  487.  
  488.         p = c; p.y += g.x + b.x; p.x -= b.y/2.;
  489.         pgx(-(gap+b.x),y);
  490.         pgx(-(gap+b.x),y);
  491.     
  492.         putbox();   /* put out boxes, cut marks, & ShowPage */
  493.     }
  494.     return N;
  495. }
  496.  
  497. put3up(){
  498.     up_decls;
  499.  
  500.     if (!ScaleSet)
  501.         Scale =  
  502. fmin((VS(PBox)/3.-(2.*gap))/b.x,((HS(PBox)-(2.*gap))/b.y));
  503.     nup_init();
  504.  
  505.     for (i=0;i<N;i++){
  506.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  507.         j = 0;
  508.  
  509.         p = c; p.y += g.x + 1.5*b.x; p.x -= b.y/2.;
  510.         pgx(-(gap+b.x),y);
  511.         pgx(-(gap+b.x),y);
  512.         pgx(-(gap+b.x),y);
  513.     
  514.         putbox();   /* put out boxes, cut marks, & ShowPage */
  515.     }
  516.     return N;
  517. }
  518.  
  519. put6up(){
  520.     up_decls;
  521.  
  522.     if (!ScaleSet)
  523.         Scale =  
  524. fmin((VS(PBox)/3.-(2.*gap))/b.x,((HS(PBox)/2.-(2.*gap))/b.y));
  525.     nup_init();
  526.  
  527.     for (i=0;i<N;i++){
  528.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  529.         j = 0;
  530.  
  531.         p = c; p.y += gap + 1.5*b.x; p.x += g.x;
  532.         pgx(-(gap+b.x),y);
  533.         pgx(-(gap+b.x),y);
  534.         pgx(-(gap+b.x),y);
  535.     
  536.         p = c; p.y += gap + 1.5*b.x; p.x -= g.x + b.y;
  537.         pgx(-(gap+b.x),y);
  538.         pgx(-(gap+b.x),y);
  539.         pgx(-(gap+b.x),y);
  540.     
  541.         putbox();   /* put out boxes, cut marks, & ShowPage */
  542.     }
  543.     return N;
  544. }
  545.  
  546. put8up(){
  547.     up_decls;
  548.  
  549.     if (!ScaleSet)
  550.         Scale =  
  551. fmin((VS(PBox)/4.-(2.*gap))/b.x,((HS(PBox)/2.-(2.*gap))/b.y));
  552.     nup_init();
  553.  
  554.     for (i=0;i<N;i++){
  555.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  556.         j = 0;
  557.  
  558.         p = c; p.y += 1.5*gap + 2.*b.x; p.x += g.x;
  559.         pgx(-(gap+b.x),y);
  560.         pgx(-(gap+b.x),y);
  561.         pgx(-(gap+b.x),y);
  562.         pgx(-(gap+b.x),y);
  563.     
  564.         p = c; p.y += 1.5*gap + 2.*b.x; p.x -= g.x + b.y;
  565.         pgx(-(gap+b.x),y);
  566.         pgx(-(gap+b.x),y);
  567.         pgx(-(gap+b.x),y);
  568.         pgx(-(gap+b.x),y);
  569.     
  570.         putbox();   /* put out boxes, cut marks, & ShowPage */
  571.     }
  572.     return N;
  573. }
  574.  
  575. put4up(){
  576.     up_decls;
  577.  
  578.     if (!ScaleSet)
  579.         Scale =  
  580. 5*fmin((HS(PBox)-(2.*gap))/b.x,(VS(PBox)-(2.*gap))/b.y);
  581.     nup_init();
  582.  
  583.     for (i=0;i<N;i++){
  584.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  585.         j = 0;
  586.  
  587.         p = c; p.x -= g.x + b.x; p.y += g.y;
  588.         pgx(gap+b.x,x);
  589.         pgx(gap+b.x,x);
  590.         p = c; p.x -= g.x + b.x; p.y -= g.y + b.y;
  591.         pgx(gap+b.x,x);
  592.         pgx(gap+b.x,x);
  593.     
  594.         putbox();   /* put out boxes, cut marks, & ShowPage */
  595.     }
  596.     return N;
  597. }
  598.  
  599. put9up(){
  600.     up_decls;
  601.  
  602.     if (!ScaleSet)
  603.         Scale =  
  604. 333*fmin((HS(PBox)-(3.*gap))/b.x,(VS(PBox)-(2.*gap))/b.y);
  605.     nup_init();
  606.  
  607.     for (i=0;i<N;i++){
  608.     P("\n%%%%Page: %d %d\n/q_sheet save def",i+1,i+1);
  609.         j = 0;
  610.  
  611.         p = c; p.x -= g.x + 1.5*b.x; p.y += g.y + .5*b.y;
  612.         pgx(g.x+b.x,x);
  613.         pgx(g.x+b.x,x);
  614.         pgx(g.x+b.x,x);
  615.  
  616.         p = c; p.x -= g.x + 1.5*b.x; p.y -= .5*b.y;
  617.         pgx(g.x+b.x,x);
  618.         pgx(g.x+b.x,x);
  619.         pgx(g.x+b.x,x);
  620.     
  621.         p = c; p.x -= g.x + 1.5*b.x; p.y -= g.y + 1.5*b.y;
  622.         pgx(g.x+b.x,x);
  623.         pgx(g.x+b.x,x);
  624.         pgx(g.x+b.x,x);
  625.  
  626.         putbox();   /* put out boxes, cut marks, & ShowPage */
  627.     }
  628.     return N;
  629. }
  630.  
  631. putpages(){
  632.     int i;
  633.     char *s, *p, *index();
  634.  
  635.     putProlog(NP);
  636.     for (i=1;i<=NP;i++){
  637.         s = readPage(i);
  638.         if (*s){
  639.             p = index(s,'\n');
  640.             if (p){
  641.                 p+1;
  642.                 fprintf(Output,"%%%%Page: %d %d\n",i,i);
  643.                 fwrite(p,PageLen[i] - (p-s),1,Output);
  644.                 fprintf(Output,"q_bbox\n");
  645.             }
  646.         } else { /* emit blank page, e.g., for signatures */
  647.                 fprintf(Output,"%%%%Page: %d %d\n",i,i);
  648.                 fprintf(Output,"q_bbox\n");
  649.         }
  650.     }
  651. }
  652.  
  653. char *SelectPages = (char *)0;
  654.  
  655. ReorderPages(){
  656.     int i,j;
  657.     int pages[MaxP], po[MaxP], pl[MaxP], np=0;
  658.     Rectangle pr[MaxP];
  659.  
  660.     bzero(pages,sizeof(pages));
  661.  
  662.     if (SelectPages){
  663.         char *s = skipsp(SelectPages);
  664.         int start=0,end;
  665.         while (*s){
  666.             if (strncmp(s,"even",4)==0){
  667.                 for (i=2;i<=NP;i+=2) pages[np++]=i;
  668.                 s+=4;
  669.             } else
  670.             if (strncmp(s,"odd",3)==0){
  671.                 for (i=1;i<=NP;i+=2) pages[np++]=i;
  672.                 s+=3;
  673.             } else
  674.             if (strncmp(s,"blank",5)==0){
  675.                 pages[np++]=NP+1;
  676.                 s+=5;
  677.             } else
  678.             if (strncmp(s,"all",3)==0){
  679.                 for (i=1;i<=NP;i++) pages[np++]=i;
  680.                 s+=3;
  681.             } else
  682.             if (*s == '-'){
  683.                 start = 1;
  684.                 ++s;
  685.                 end = atoi(s);
  686.                 while (isdigit(*s)) ++s;
  687.                 for (i=start;i<=end;i++) pages[np++] = i;
  688.             } else
  689.             if (isdigit(*s)){
  690.                 pages[np++] = atoi(s);
  691.                 while (isdigit(*s) || isspace(*s)) ++s;
  692.                 if (*s == '-'){
  693.                     ++s;
  694.                     while (*s && !isdigit(*s)) ++s;
  695.                     end = *s? atoi(s) : NP;
  696.                     while (*s && isdigit(*s)) ++s;
  697.                     for (i=pages[np-1]+1;i<=end;i++) pages[np++] = i;
  698.                 }
  699.             } else
  700.                 ++s;
  701.             while (*s== ',' || *s=='-' || isspace(*s)) ++s;
  702.         }
  703.         for (i=0;i<np;i++){
  704.             po[i] = Page[pages[i]];
  705.             pl[i] = PageLen[pages[i]];
  706.             pr[i] = PageBbox[pages[i]];
  707.         }
  708.         for (i=0;i<np;i++){
  709.             Page[i+1] = po[i];
  710.             PageLen[i+1] = pl[i];
  711.             PageBbox[i+1] = pr[i];
  712.         }
  713.         for (i=np+1;i<=NP;i++) Page[i] = PageLen[i] = 0;
  714.         NP = np;
  715.     } else
  716.         for (i=0;i<NP;i++) pages[i] = i+1;
  717.     if (ReversePages){
  718. #define swap(t, a,b) t = a; a = b; b = t
  719.         for (i=1,j=NP; i<j; i++,j--){
  720.             int t;
  721.             Rectangle r;
  722.             swap(t, pages[i],pages[j]);
  723.             swap(t, Page[i],Page[j]);
  724.             swap(t, PageLen[i],PageLen[j]);
  725.             swap(r,PageBbox[i],PageBbox[j]);
  726.         }
  727.     }
  728.     if (Signatures){
  729.       /* reorder appropriately for 2-sided signatures of
  730.          'Signature' sheets per packet.
  731.          With signatures of 2 sheets, a page ordering of
  732.          1 2 3 4 5 6 7 8 9 10 11 12 . . .
  733.          becomes
  734.          8 1 2 7 6 3 4 5    16 9 10 15 . . .
  735.        */
  736.     int i, p, s, *P, tmp, PP, page[MaxP];
  737.     int SidesPerSig = Signatures*2,
  738.         PagesPerSig = SidesPerSig*2,
  739.         NumSigs = (NP + PagesPerSig - 1) / PagesPerSig;
  740.         bzero(page,sizeof(page));
  741.     for (s=0,i=0; s<NumSigs; s++){
  742.         for (p=0, P = page+s*PagesPerSig+1;p<SidesPerSig;p++, P+=2)
  743.         *P = pages[i++];
  744.         for (p=0, P-=3 ;p<SidesPerSig;p++, P-=2)
  745.         *P = pages[i++];
  746.         for (p=0, P=page+s*PagesPerSig+2; p<PagesPerSig;p++,P+=4)
  747.         tmp=P[0],P[0]=P[1],P[1]=tmp;
  748.     }
  749.         PP = NP; NP = PagesPerSig*NumSigs;
  750.         for (i=0;i<NP;i++) if (page[i]>PP || !page[i]) page[i]=NP;
  751.  
  752.         for (i=0;i<NP;i++){
  753.             po[i+1] = Page[page[i]];
  754.             pl[i+1] = PageLen[page[i]];
  755.             pr[i+1] = PageBbox[page[i]];
  756.         }
  757.         for (i=0;i<NP;i++){
  758.             Page[i+1] = po[i+1];
  759.             PageLen[i+1] = pl[i+1];
  760.             PageBbox[i+1] = pr[i+1];
  761.         }
  762.     fprintf(stderr,"%d %d-page signatures:\n  ",NumSigs,Signatures);
  763.     for (i=0;i<NP;i++){
  764.         if (i && i%4 == 0) fprintf(stderr,"  ");
  765.         if (i && i%PagesPerSig == 0) fprintf(stderr,"\n  ");
  766.         if (page[i]==NP)
  767.         fprintf(stderr," . ");
  768.         else
  769.         fprintf(stderr,"%2d ",page[i]);
  770.     }
  771.     fprintf(stderr,"\n");
  772.     }
  773. }
  774.  
  775. doFile(s) char *s; {
  776.     FILE *f = fopen(s,"r");
  777.     int n;
  778.     if (!f) return error("%s: couldn't open %s",av0,s);
  779.     Input = f;
  780.     findPageOffsets();
  781.  
  782.     if (ListPages)
  783.         showPageOffsets();
  784.     else {
  785.     ReorderPages();
  786.     
  787.     switch (N_up){
  788.     case 2: n=put2up();
  789.     Case 3: n=put3up();
  790.     Case 4: n=put4up();
  791.     Case 6: n=put6up();
  792.     Case 8: n=put8up();
  793.     Case 9: n=put9up();
  794.     Default:n=putpages();
  795.     }
  796.     
  797.     putTrailer(n);
  798.     }
  799.     fclose(f);
  800. }
  801.  
  802. savetmp(s,f) char *s; FILE *f;
  803. /* 
  804.  * Copy 'f' into a tmp file; put the name in s,
  805.  * and return 1 if success.
  806.  */
  807. {
  808.     FILE *o;
  809.     int n, F, O;
  810.     char b[8192];
  811.     strcpy(s,"/tmp/quartoXXXXXX");
  812.     mktemp(s);
  813.     strcat(s,".eps");
  814.     if (o = fopen(s,"w")){
  815.         F = fileno(f);
  816.         O = fileno(o);
  817.         while ((n=read(F,b,sizeof b))>0)
  818.             write(O,b,n);
  819.         fclose(o);
  820.         return 1;
  821.     } else
  822.         return 0;    
  823. }
  824.  
  825. use(){
  826.     error("use: %s [-[234689]] [-p <pages>] [-S #] [-bcdlr] [-g #] [PS  
  827. file]",av0);
  828.     error("Select pages from a PostScript file, and print in a tiled  
  829. format.");
  830.     error("(The input must contain EPS-like directives  
  831. '%%%%Page:...')");
  832.     error("  -p... print the given pages; e.g., '-p1,2,4-8,12-'");
  833.     error("        '-p -4' prints through page four;");
  834.     error("        '-p 12-' prints from page 12 through the end;");
  835.     error("        numbers are original ordinal page numbers, starting  
  836. at 1");
  837.     error("        also, '-p even', '-p odd', '-p1-4,1-4,1-4,1-4'  
  838. etc.");
  839.     error("        'blank' or 0 inserts a blank page; 'all' inserts all  
  840. the pages");
  841.     error("  -2    print 2 tiled, shrunken pages per page;");
  842.     error("        # may be 2,3,4,6,8 or 9 (default 1);");
  843.     error("        portrait or landscape layout will be chosen  
  844. accordingly.");
  845.     error("  -b    %sprint borders around each tiled page.",
  846.                    _Borders?"don't ":"");
  847.     error("  -c    %sprint cut-marks for each tiled page.",
  848.                    _CutMarks?"don't ":"");
  849.     error("  -d    overlay a test pattern on the page.");
  850.     error("        (a box around the paper sheet border, ");
  851.     error("         and a circle and cross in the middle;");
  852.     error("         intended to help calibrate for different  
  853. printers)");
  854.     error("  -S #  reorder pages to print them in signatures of #  
  855. sheets;");
  856.     error("        e.g., '-2 -S 3' prints pages 2-up per page for  
  857. signatures");
  858.     error("        of 3 double-sided sheets that will be folded and  
  859. bound together.");
  860.     error("  -g #  set the gap between tiles to # (%3f)",_Gutter);
  861.     error("  -l    list the pages+offsets (don't output any  
  862. PostScript).");
  863.     error("  -r    reverse the page order");
  864.     exit(1);
  865. }
  866.  
  867. main(ac,av) char *av[]; {
  868.     int i;
  869.  
  870.     for_each_argument {
  871.     case 'l': ListPages = 1;
  872.     Case 'p': SelectPages = argument;
  873.     Case 'r': ReversePages = 1;
  874.     Case 'd': TestPattern = !TestPattern;
  875.     Case 'c': CutMarks = !CutMarks;
  876.     Case 'b': Borders = !Borders;
  877.     Case '1': N_up = 1;
  878.     Case '2': N_up = 2; Rotate = 1;
  879.     Case '3': N_up = 3; Rotate = 1;
  880.     Case '4': N_up = 4;
  881.     Case '6': N_up = 6; Rotate = 1;
  882.     Case '8': N_up = 8; Rotate = 1;
  883.     Case '9': N_up = 9;
  884.     Case 'g': Gutter = atof(argument);
  885.     Case 's': Scale = atof(argument); ScaleSet++; /* not mentioned as  
  886. option */
  887.     Case 'S': Signatures = atof(argument);
  888.               if (N_up != 2)
  889.                  error("You may want to specify -2 sheets per page.");
  890.     Default : use();
  891.     }
  892.  
  893.     Output = stdout;
  894.     if (i==ac){
  895.         char s[1024];
  896.         if (savetmp(s,stdin))
  897.             CurFile="stdin", doFile(s), unlink(s);
  898.         else
  899.             error("%s: couldn't save stdin to a tmp file.",av0),  
  900. exit(1);
  901.     } else
  902.     if ((i+1)==ac)
  903.         CurFile=av[i], doFile(av[i++]);
  904.     else
  905.         error("%s: accepts only one file, or stdin...\n",av0), use();
  906.     exit(0);
  907. }
  908.