/*
#  poster - resize a postscript image to print on larger media and/or multiple sheets
#
#  This program scales a PostScript page to a given size (a poster).
#  The output can be tiled on multiple sheets, and output
#  media size can be chosen independently.
#  Each tile (sheet) of a will bear cropmarks and slightly overlapping
#  image for easier poster assembly.
#  In principle it requires the input file to adhere to 'eps'
#  (encapsulated postscript) conventions but it will work for many
#  'normal' postscript files as well.
#
#  Compile this program with:
#        cc -O -o poster poster.c -lm
#  or something alike.
#
#  Maybe you want to change the `DefaultMedia' and `DefaultImage'
#  settings in the few lines below, to reflect your local situation.
#  Names can to be chosen from the `mediatable' further down.
#
#  The `Gv_gs_orientbug 1' disables a feature of this program to
#  ask for landscape previewing of rotated images.
#  Our currently installed combination of ghostview 1.5 with ghostscript 3.33
#  cannot properly do a landscape viewing of the `poster' output.
#  The problem does not exist in combination with an older ghostscript 2.x,
#  and has the attention of the ghostview authors.
#  (The problem is in the evaluation of the `setpagedevice' call.)
#  If you have a different previewing environment,
#  you might want to set `Gv_gs_orientbug 0'
#
# --------------------------------------------------------------
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation.
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY;
#  The full text of the GNU General Public License is supplied
#  with 'poster' in the file LICENSE.
#
#  Copyright (C) 1999 Jos T.J. van Eijndhoven
# --------------------------------------------------------------
# email: [email protected]
*/

#define Gv_gs_orientbug 1
#define DefaultMedia  "A4"
#define DefaultImage  "A4"
#define DefaultCutMargin "5%"
#define DefaultWhiteMargin "0"
#define BUFSIZE 1024

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <math.h>


extern char *optarg;        /* silently set by getopt() */
extern int optind, opterr;  /* silently set by getopt() */

static void usage();
static void dsc_head1();
static int dsc_infile( double ps_bb[4]);
static void dsc_head2( void);
static void printposter( void);
static void printprolog();
static void tile ( int row, int col);
static void printfile( void);
static void postersize( char *scalespec, char *posterspec);
static void box_convert( char *boxspec, double psbox[4]);
static void boxerr( char *spec);
static void margin_convert( char *spec, double margin[2]);
static int mystrncasecmp( const char *s1, const char *s2, int n);

int verbose;
char *myname;
char *infile;
int rotate, nrows, ncols;
int manualfeed = 0;
int tail_cntl_D = 0;
#define Xl 0
#define Yb 1
#define Xr 2
#define Yt 3
#define X 0
#define Y 1
double posterbb[4];     /* final image in ps units */
double imagebb[4];      /* original image in ps units */
double mediasize[4];    /* [34] = size of media to print on, [01] not used! */
double cutmargin[2];
double whitemargin[2];
double scale;           /* linear scaling factor */

/* defaults: */
char *imagespec = NULL;
char *posterspec = NULL;
char *mediaspec = NULL;
char *cutmarginspec = NULL;
char *whitemarginspec = NULL;
char *scalespec = NULL;
char *filespec = NULL;

/* media sizes in ps units (1/72 inch) */
static char *mediatable[][2] =
{       { "Letter",   "612,792"},
       { "Legal",    "612,1008"},
       { "Tabloid",  "792,1224"},
       { "Ledger",   "792,1224"},
       { "Executive","540,720"},
       { "Monarch",  "279,540"},
       { "Statement","396,612"},
       { "Folio",    "612,936"},
       { "Quarto",   "610,780"},
       { "C5",       "459,649"},
       { "B4",       "729,1032"},
       { "B5",       "516,729"},
       { "Dl",       "312,624"},
       { "A0",       "2380,3368"},
       { "A1",       "1684,2380"},
       { "A2",       "1190,1684"},
       { "A3",       "842,1190"},
       { "A4",       "595,842"},
       { "A5",       "420,595"},
       { "A6",       "297,421"},

       /* as fall-back: linear units of measurement: */
       { "p",        "1,1"},
       { "i",        "72,72"},
       { "ft",       "864,864"},
       { "mm",       "2.83465,2.83465"},
       { "cm",       "28.3465,28.3465"},
       { "m",        "2834.65,2834.65"},
       { NULL, NULL}
};


int main( int argc, char *argv[])
{
       int opt;
       double ps_bb[4];
       int got_bb;

       myname = argv[0];

       while ((opt = getopt( argc, argv, "vfi:c:w:m:p:s:o:")) != EOF)
       {       switch( opt)
               { case 'v':     verbose++; break;
                 case 'f':     manualfeed = 1; break;
                 case 'i':     imagespec = optarg; break;
                 case 'c':     cutmarginspec = optarg; break;
                 case 'w':     whitemarginspec = optarg; break;
                 case 'm':     mediaspec = optarg; break;
                 case 'p':     posterspec = optarg; break;
                 case 's':     scalespec = optarg; break;
                 case 'o':     filespec = optarg; break;
                 default:      usage(); break;
               }
       }

       /*** check command line arguments ***/
       if (scalespec && posterspec)
       {       fprintf( stderr, "Please don't specify both -s and -o, ignoring -s!\n");
               scalespec = NULL;
       }

       if (optind < argc)
               infile = argv[ optind];
       else
       {       fprintf( stderr, "Filename argument missing!\n");
               usage();
       }

       /*** decide on media size ***/
       if (!mediaspec)
       {       mediaspec = DefaultMedia;
               if (verbose)
                       fprintf( stderr,
                               "Using default media of %s\n",
                               mediaspec);
       }
       box_convert( mediaspec, mediasize);
       if (mediasize[3] < mediasize[2])
       {       fprintf( stderr, "Media should always be specified in portrait format!\n");
               exit(1);
       }
       if (mediasize[2]-mediasize[0] <= 10.0 || mediasize[3]-mediasize[1] <= 10.0)
       {       fprintf( stderr, "Media size is ridiculous!\n");
               exit(1);
       }

       /*** defaulting poster size ? **/
       if (!scalespec && !posterspec)
       {       /* inherit postersize from given media size */
               posterspec = mediaspec;
               if (verbose)
                       fprintf( stderr,
                               "Defaulting poster size to media size of %s\n",
                               mediaspec);
       }

       /*** decide the cutmargin size, after knowing media size ***/
       if (!cutmarginspec)
       {       /* if (!strcmp( posterspec, mediaspec)) */
                       /* zero cutmargin if printing to 1 sheet */
               /*      marginspec = "0%";
               else */ cutmarginspec = DefaultCutMargin;
               if (verbose)
                       fprintf( stderr,
                               "Using default cutmargin of %s\n",
                               cutmarginspec);
       }
       margin_convert( cutmarginspec, cutmargin);

       /*** decide the whitemargin size, after knowing media size ***/
       if (!whitemarginspec)
       {       whitemarginspec = DefaultWhiteMargin;
               if (verbose)
                       fprintf( stderr,
                               "Using default whitemargin of %s\n",
                               whitemarginspec);
       }
       margin_convert( whitemarginspec, whitemargin);


       /******************* now start doing things **************************/
       /* open output file */
       if (filespec)
       {       if (!freopen( filespec, "w", stdout))
               {       fprintf( stderr, "Cannot open '%s' for writing!\n",
                                filespec);
                       exit(1);
               } else if (verbose)
                       fprintf( stderr, "Opened '%s' for writing\n",
                                filespec);
       }

       /******* I might need to read some input to find picture size ********/
       /* start DSC header on output */
       dsc_head1();

       /* pass input DSC lines to output, get BoundingBox spec if there */
       got_bb = dsc_infile( ps_bb);

       /**** decide the input image bounding box ****/
       if (!got_bb && !imagespec)
       {       imagespec = DefaultImage;
               if (verbose)
                       fprintf( stderr,
                               "Using default input image of %s\n",
                               imagespec);
       }
       if (imagespec)
               box_convert( imagespec, imagebb);
       else
       {       int i;
               for (i=0; i<4; i++)
                       imagebb[i] = ps_bb[i];
       }

       if (verbose > 1)
               fprintf( stderr, "   Input image is: [%g,%g,%g,%g]\n",
                       imagebb[0], imagebb[1], imagebb[2], imagebb[3]);

       if (imagebb[2]-imagebb[0] <= 0.0 || imagebb[3]-imagebb[1] <= 0.0)
       {       fprintf( stderr, "Input image should have positive size!\n");
               exit(1);
       }


       /*** decide on the scale factor and poster size ***/
       postersize( scalespec, posterspec);

       if (verbose > 1)
               fprintf( stderr, "   Output image is: [%g,%g,%g,%g]\n",
                       posterbb[0], posterbb[1], posterbb[2], posterbb[3]);


       dsc_head2();

       printposter();

       exit (0);
}

static void usage()
{
       fprintf( stderr, "Usage: %s <options> infile\n\n", myname);
       fprintf( stderr, "options are:\n");
       fprintf( stderr, "   -v:         be verbose\n");
       fprintf( stderr, "   -f:         ask manual feed on plotting/printing device\n");
       fprintf( stderr, "   -i<box>:    specify input image size\n");
       fprintf( stderr, "   -c<margin>: horizontal and vertical cutmargin\n");
       fprintf( stderr, "   -w<margin>: horizontal and vertical additional white margin\n");
       fprintf( stderr, "   -m<box>:    media paper size\n");
       fprintf( stderr, "   -p<box>:    output poster size\n");
       fprintf( stderr, "   -s<number>: linear scale factor for poster\n");
       fprintf( stderr, "   -o<file>:   output redirection to named file\n\n");
       fprintf( stderr, "   At least one of -s -p -m is mandatory, and don't give both -s and -p\n");
       fprintf( stderr, "   <box> is like 'A4', '3x3letter', '10x25cm', '200x200+10,10p'\n");
       fprintf( stderr, "   <margin> is either a simple <box> or <number>%%\n\n");

       fprintf( stderr, "   Defaults are: '-m%s', '-c%s', '-i<box>' read from input file.\n",
               DefaultMedia, DefaultCutMargin);
       fprintf( stderr, "                 and output written to stdout.\n");

       exit(1);
}

#define exch( x, y)     {double h; h=x; x=y; y=h;}

static void postersize( char *scalespec, char *posterspec)
{       /* exactly one the arguments is NULL ! */
       /* media and image sizes are fixed already */

       int nx0, ny0, nx1, ny1;
       double sizex, sizey;    /* size of the scaled image in ps units */
       double drawablex, drawabley; /* effective drawable size of media */
       double mediax, mediay;
       double tmpposter[4];

       /* available drawing area per sheet: */
       drawablex = mediasize[2] - 2.0*cutmargin[0];
       drawabley = mediasize[3] - 2.0*cutmargin[1];

       /*** decide on number of pages  ***/
       if (scalespec)
       {       /* user specified scale factor */
               scale = atof( scalespec);
               if (scale < 0.01 || scale > 1.0e6)
               {       fprintf( stderr, "Illegal scale value %s!\n", scalespec);
                       exit(1);
               }
               sizex = (imagebb[2] - imagebb[0]) * scale + 2*whitemargin[0];
               sizey = (imagebb[3] - imagebb[1]) * scale + 2*whitemargin[1];

               /* without rotation */
               nx0 = ceil( sizex / drawablex);
               ny0 = ceil( sizey / drawabley);

               /* with rotation */
               nx1 = ceil( sizex / drawabley);
               ny1 = ceil( sizey / drawablex);

       } else
       {       /* user specified output size */
               box_convert( posterspec, tmpposter);
               if (tmpposter[0]!=0.0 || tmpposter[1]!=0.0)
               {       fprintf( stderr, "Poster lower-left coordinates are assumed 0!\n");
                       tmpposter[0] = tmpposter[1] = 0.0;
               }
               if (tmpposter[2]-tmpposter[0] <= 0.0 || tmpposter[3]-tmpposter[1] <= 0.0)
               {       fprintf( stderr, "Poster should have positive size!\n");
                       exit(1);
               }

               if ((tmpposter[3]-tmpposter[1]) < (tmpposter[2]-tmpposter[0]))
               {       /* hmmm... landscape spec, change to portrait for now */
                       exch( tmpposter[0], tmpposter[1]);
                       exch( tmpposter[2], tmpposter[3]);
               }


               /* Should we tilt the poster to landscape style? */
               if ((imagebb[3] - imagebb[1]) < (imagebb[2] - imagebb[0]))
               {       /* image has landscape format ==> make landscape poster */
                       exch( tmpposter[0], tmpposter[1]);
                       exch( tmpposter[2], tmpposter[3]);
               }

               /* without rotation */ /* assuming tmpposter[0],[1] = 0,0 */
               nx0 = ceil( 0.95 * tmpposter[2] / mediasize[2]);
               ny0 = ceil( 0.95 * tmpposter[3] / mediasize[3]);

               /* with rotation */
               nx1 = ceil( 0.95 * tmpposter[2] / mediasize[3]);
               ny1 = ceil( 0.95 * tmpposter[3] / mediasize[2]);
               /* (rotation is considered as media versus image, which is totally */
               /*  independent of the portrait or landscape style of the final poster) */
       }

       /* decide for rotation to get the minimum page count */
       rotate = nx0*ny0 > nx1*ny1;

       ncols = rotate ? nx1 : nx0;
       nrows = rotate ? ny1 : ny0;

       if (verbose)
               fprintf( stderr,
                       "Deciding for %d column%s and %d row%s of %s pages.\n",
                       ncols, (ncols==1)?"":"s", nrows, (nrows==1)?"":"s",
                       rotate?"landscape":"portrait");

       if (nrows * ncols > 400)
       {       fprintf( stderr, "However %dx%d pages seems ridiculous to me!\n",
                       ncols, nrows);
               exit(1);
       }

       mediax = ncols * (rotate ? drawabley : drawablex);
       mediay = nrows * (rotate ? drawablex : drawabley);

       if (!scalespec)  /* no scaling number given by user */
       {       double scalex, scaley;
               scalex = (mediax - 2*whitemargin[0]) / (imagebb[2] - imagebb[0]);
               scaley = (mediay - 2*whitemargin[1]) / (imagebb[3] - imagebb[1]);
               scale = (scalex < scaley) ? scalex : scaley;

               if (verbose)
                       fprintf( stderr,
                               "Deciding for a scale factor of %g\n", scale);
               sizex = scale * (imagebb[2] - imagebb[0]);
               sizey = scale * (imagebb[3] - imagebb[1]);
       }

       /* set poster size as if it were a continuous surface without margins */
       posterbb[0] = (mediax - sizex) / 2.0; /* center picture on paper */
       posterbb[1] = (mediay - sizey) / 2.0; /* center picture on paper */
       posterbb[2] = posterbb[0] + sizex;
       posterbb[3] = posterbb[1] + sizey;

}

static void margin_convert( char *spec, double margin[2])
{       double x;
       int i, n;

       if (1==sscanf( spec, "%lf%n", &x, &n) && x==0.0 && n==strlen(spec))
       {       /* margin spec of 0, dont bother about a otherwise mandatory unit */
               margin[0] = margin[1] = 0.0;
       } else if (spec[ strlen( spec) - 1] == '%')
       {       /* margin relative to media size */
               if (1 != sscanf( spec, "%lf%%", &x))
               {       fprintf( stderr, "Illegal margin specification!\n");
                       exit( 1);
               }
               margin[0] = 0.01 * x * mediasize[2];
               margin[1] = 0.01 * x * mediasize[3];
       } else
       {       /* absolute margin value */
               double marg[4];
               box_convert( spec, marg);
               margin[0] = marg[2];
               margin[1] = marg[3];
       }

       for (i=0; i<2; i++)
       {       if (margin[i] < 0 || 2.0*margin[i] >= mediasize[i+2])
               {       fprintf( stderr, "Margin value '%s' out of range!\n",
                               spec);
                       exit(1);
               }
       }
}

static void box_convert( char *boxspec, double psbox[4])
{       /* convert user textual box spec into numbers in ps units */
       /* box = [fixed x fixed][+ fixed , fixed] unit */
       /* fixed = digits [ . digits] */
       /* unit = medianame | i | cm | mm | m | p */

       double mx, my, ox, oy, ux, uy;
       int n, r, i, l, inx;
       char *spec;

       mx = my = 1.0;
       ox = oy = 0.0;

       spec = boxspec;
       /* read 'fixed x fixed' */
       if (isdigit( spec[0]))
       {       r = sscanf( spec, "%lfx%lf%n", &mx, &my, &n);
               if (r != 2)
               {       r = sscanf( spec, "%lf*%lf%n", &mx, &my, &n);
                       if (r != 2) boxerr( boxspec);
               }
               spec += n;
       }

       /* read '+ fixed , fixed' */
       if (1 < (r = sscanf( spec, "+%lf,%lf%n", &ox, &oy, &n)))
       {       if (r != 2) boxerr( boxspec);
               spec += n;
       }

       /* read unit */
       l = strlen( spec);
       for (n=i=0; mediatable[i][0]; i++)
       {       if (!mystrncasecmp( mediatable[i][0], spec, l))
               {       /* found */
                       n++;
                       inx = i;
                       if (l == strlen( mediatable[i][0]))
                       {       /* match is exact */
                               n = 1;
                               break;
                       }
               }
       }
       if (!n) boxerr( boxspec);
       if (n>1)
       {       fprintf( stderr, "Your box spec '%s' is not unique! (give more chars)\n",
                       spec);
               exit(1);
       }
       sscanf( mediatable[inx][1], "%lf,%lf", &ux, &uy);

       psbox[0] = ox * ux;
       psbox[1] = oy * uy;
       psbox[2] = mx * ux;
       psbox[3] = my * uy;

       if (verbose > 1)
               fprintf( stderr, "   Box_convert: '%s' into [%g,%g,%g,%g]\n",
                       boxspec, psbox[0], psbox[1], psbox[2], psbox[3]);

       for (i=0; i<2; i++)
       {       if (psbox[i] < 0.0 || psbox[i+2] < psbox[i])
               {       fprintf( stderr, "Your specification `%s' leads to "
                               "negative values!\n", boxspec);
                       exit(1);
               }
       }
}

static void boxerr( char *spec)
{       int i;

       fprintf( stderr, "I don't understand your box specification `%s'!\n",
               spec);

       fprintf( stderr, "The proper format is: ([text] meaning optional text)\n");
       fprintf( stderr, "  [multiplier][offset]unit\n");
       fprintf( stderr, "  with multiplier:  numberxnumber\n");
       fprintf( stderr, "  with offset:      +number,number\n");
       fprintf( stderr, "  with unit one of:");

       for (i=0; mediatable[i][0]; i++)
               fprintf( stderr, "%c%-10s", (i%7)?' ':'\n', mediatable[i][0]);
       fprintf( stderr, "\nYou can use a shorthand for these unit names,\n"
               "provided it resolves unique.\n");
       exit( 1);
}

/*********************************************/
/* output first part of DSC header           */
/*********************************************/
static void dsc_head1()
{
       printf ("%%!PS-Adobe-3.0\n");
       printf ("%%%%Creator: %s\n", myname);
}

/*********************************************/
/* pass some DSC info from the infile in the new DSC header */
/* such as document fonts and */
/* extract BoundingBox info from the PS file */
/*********************************************/
static int dsc_infile( double ps_bb[4])
{
       char *c, buf[BUFSIZE];
       int gotall, atend, level, dsc_cont, inbody, got_bb;

       if (freopen (infile, "r", stdin) == NULL) {
               fprintf (stderr, "%s: fail to open file '%s'!\n",
                       myname, infile);
               exit (1);
       }

       got_bb = 0;
       dsc_cont = inbody = gotall = level = atend = 0;
       while (!gotall && (gets(buf) != NULL))
       {
               if (buf[0] != '%')
               {       dsc_cont = 0;
                       if (!inbody) inbody = 1;
                       if (!atend) gotall = 1;
                       continue;
               }

               if (!strncmp( buf, "%%+",3) && dsc_cont)
               {       puts( buf);
                       continue;
               }

               dsc_cont = 0;
               if      (!strncmp( buf, "%%EndComments", 13))
               {       inbody = 1;
                       if (!atend) gotall = 1;
               }
               else if (!strncmp( buf, "%%BeginDocument", 15) ||
                        !strncmp( buf, "%%BeginData", 11)) level++;
               else if (!strncmp( buf, "%%EndDocument", 13) ||
                        !strncmp( buf, "%%EndData", 9)) level--;
               else if (!strncmp( buf, "%%Trailer", 9) && level == 0)
                       inbody = 2;
               else if (!strncmp( buf, "%%BoundingBox:", 14) &&
                        inbody!=1 && !level)
               {       for (c=buf+14; *c==' ' || *c=='\t'; c++);
                       if (!strncmp( c, "(atend)", 7)) atend = 1;
                       else
                       {       sscanf( c, "%lf %lf %lf %lf",
                                      ps_bb, ps_bb+1, ps_bb+2, ps_bb+3);
                               got_bb = 1;
                       }
               }
               else if (!strncmp( buf, "%%Document", 10) &&
                        inbody!=1 && !level)  /* several kinds of doc props */
               {       for (c=buf+10; *c && *c!=' ' && *c!='\t'; c++);
                       for (; *c==' ' || *c=='\t'; c++);
                       if (!strncmp( c, "(atend)", 7)) atend = 1;
                       else
                       {       /* pass this DSC to output */
                               puts( buf);
                               dsc_cont = 1;
                       }
               }
       }
       return got_bb;
}

/*********************************************/
/* output last part of DSC header            */
/*********************************************/
static void dsc_head2()
{
       printf ("%%%%Pages: %d\n", nrows*ncols);

#ifndef Gv_gs_orientbug
       printf ("%%%%Orientation: %s\n", rotate?"Landscape":"Portrait");
#endif
       printf ("%%%%DocumentMedia: %s %d %d 0 white ()\n",
               mediaspec, (int)(mediasize[2]), (int)(mediasize[3]));
       printf ("%%%%BoundingBox: 0 0 %d %d\n", (int)(mediasize[2]), (int)(mediasize[3]));
       printf ("%%%%EndComments\n\n");

       printf ("%% Print poster %s in %dx%d tiles with %.3g magnification\n",
               infile, nrows, ncols, scale);
}

/*********************************************/
/* output the poster, create tiles if needed */
/*********************************************/
static void printposter()
{
       int row, col;

       printprolog();
       for (row = 1; row <= nrows; row++)
               for (col = 1; col <= ncols; col++)
                       tile( row, col);
       printf ("%%%%EOF\n");

       if (tail_cntl_D)
       {       printf("%c", 0x4);
       }
}

/*******************************************************/
/* output PS prolog of the scaling and tiling routines */
/*******************************************************/
static void printprolog()
{
       printf( "%%%%BeginProlog\n");

       printf( "/cutmark       %% - cutmark -\n"
               "{              %% draw cutline\n"
               "       0.23 setlinewidth 0 setgray\n"
               "       clipmargin\n"
               "       dup 0 moveto\n"
               "       dup neg leftmargin add 0 rlineto stroke\n"
               "       %% draw sheet alignment mark\n"
               "       dup dup neg moveto\n"
               "       dup 0 rlineto\n"
               "       dup dup lineto\n"
               "       0 rlineto\n"
               "       closepath fill\n"
               "} bind def\n\n");

       printf( "%% usage:      row col tileprolog ps-code tilepilog\n"
               "%% these procedures output the tile specified by row & col\n"
               "/tileprolog\n"
               "{      %%def\n"
               "       gsave\n"
               "       leftmargin botmargin translate\n"
               "       do_turn {exch} if\n"
               "       /colcount exch def\n"
               "       /rowcount exch def\n"
               "       %% clip page contents\n"
               "       clipmargin neg dup moveto\n"
               "       pagewidth clipmargin 2 mul add 0 rlineto\n"
               "       0 pageheight clipmargin 2 mul add rlineto\n"
               "       pagewidth clipmargin 2 mul add neg 0 rlineto\n"
               "       closepath clip\n"
               "       %% set page contents transformation\n"
               "       do_turn\n"
               "       {       pagewidth 0 translate\n"
               "               90 rotate\n"
               "       } if\n"
               "       pagewidth colcount 1 sub mul neg\n"
               "       pageheight rowcount 1 sub mul neg\n"
               "       do_turn {exch} if\n"
               "       translate\n"
               "       posterxl posteryb translate\n"
               "       sfactor dup scale\n"
               "       imagexl neg imageyb neg translate\n"
               "       tiledict begin\n"
               "       0 setgray 0 setlinecap 1 setlinewidth\n"
               "       0 setlinejoin 10 setmiterlimit [] 0 setdash newpath\n"
               "} bind def\n\n");

       printf( "/tileepilog\n"
               "{      end %% of tiledict\n"
               "       grestore\n"
               "       %% print the cutmarks\n"
               "       gsave\n"
               "       leftmargin botmargin translate\n"
               "       pagewidth pageheight translate cutmark 90 rotate cutmark\n"
               "       0 pagewidth translate cutmark 90 rotate cutmark\n"
               "       0 pageheight translate cutmark 90 rotate cutmark\n"
               "       0 pagewidth translate cutmark 90 rotate cutmark\n"
               "       grestore\n"
               "       %% print the page label\n"
               "       0 setgray\n"
               "       leftmargin clipmargin 3 mul add clipmargin labelsize add neg botmargin add moveto\n"
               "       (Grid \\( ) show\n"
               "       rowcount strg cvs show\n"
               "       ( , ) show\n"
               "       colcount strg cvs show\n"
               "       ( \\)) show\n"
               "       showpage\n"
               "} bind def\n\n");

       printf( "%%%%EndProlog\n\n");
       printf( "%%%%BeginSetup\n");
       printf( "%% Try to inform the printer about the desired media size:\n"
               "/setpagedevice where   %% level-2 page commands available...\n"
               "{      pop             %% ignore where found\n"
               "       3 dict dup /PageSize [ %d %d ] put\n"
               "       dup /Duplex false put\n%s"
               "       setpagedevice\n"
               "} if\n",
              (int)(mediasize[2]), (int)(mediasize[3]),
              manualfeed?"       dup /ManualFeed true put\n":"");

       printf( "/sfactor %.10f def\n"
               "/leftmargin %d def\n"
               "/botmargin %d def\n"
               "/pagewidth %d def\n"
               "/pageheight %d def\n"
               "/imagexl %d def\n"
               "/imageyb %d def\n"
               "/posterxl %d def\n"
               "/posteryb %d def\n"
               "/do_turn %s def\n"
               "/strg 10 string def\n"
               "/clipmargin 6 def\n"
               "/labelsize 9 def\n"
               "/tiledict 250 dict def\n"
               "tiledict begin\n"
               "%% delay users showpage until cropmark is printed.\n"
               "/showpage {} def\n"
               "/setpagedevice { pop } def\n"
               "end\n",
               scale, (int)(cutmargin[0]), (int)(cutmargin[1]),
               (int)(mediasize[2]-2.0*cutmargin[0]), (int)(mediasize[3]-2.0*cutmargin[1]),
               (int)imagebb[0], (int)imagebb[1], (int)posterbb[0], (int)posterbb[1],
               rotate?"true":"false");

       printf( "/Helvetica findfont labelsize scalefont setfont\n");

       printf( "%%%%EndSetup\n");
}

/*****************************/
/* output one tile at a time */
/*****************************/
static void tile ( int row, int col)
{
       static int page=1;

       if (verbose) fprintf( stderr, "print page %d\n", page);

       printf ("\n%%%%Page: %d %d\n", page, page);
       printf ("%d %d tileprolog\n", row, col);
       printf ("%%%%BeginDocument: %s\n", infile);
       printfile ();
       printf ("\n%%%%EndDocument\n");
       printf ("tileepilog\n");

       page++;
}

/******************************/
/* copy the PS file to output */
/******************************/
static void printfile ()
{
       /* use a double line buffer, so that when I print */
       /* a line, I know whether it is the last or not */
       /* I surely dont want to print a 'cntl_D' on the last line */
       /* The double buffer removes the need to scan each line for each page*/
       /* Furthermore allows cntl_D within binary transmissions */

       char buf[2][BUFSIZE];
       int bp;
       char *c;

       if (freopen (infile, "r", stdin) == NULL) {
               fprintf (stderr, "%s: fail to open file '%s'!\n",
                       myname, infile);
               printf ("/systemdict /showpage get exec\n");
               printf ("%%%%EOF\n");
               exit (1);
       }

       /* fill first buffer for the first time */
       fgets( buf[bp=0], BUFSIZE, stdin);

       /* read subsequent lines by rotating the buffers */
       while (fgets(buf[1-bp], BUFSIZE, stdin))
       {       /* print line from the previous fgets */
               /* do not print postscript comment lines: those (DSC) lines */
               /* sometimes disturb proper previewing of the result with ghostview */
               if (buf[bp][0] != '%')
                       fputs( buf[bp], stdout);
               bp = 1-bp;
       }

       /* print buf from last successfull fgets, after removing cntlD */
       for (c=buf[bp]; *c && *c != '\04'; c++);
       if (*c == '\04')
       {       tail_cntl_D = 1;
               *c = '\0';
       }
       if (buf[bp][0] != '%' && strlen( buf[bp]))
               fputs( buf[bp], stdout);
}

static int mystrncasecmp( const char *s1, const char *s2, int n)
{       /* compare case-insensitive s1 and s2 for at most n chars */
       /* return 0 if equal. */
       /* Although standard available on some machines, */
       /* this function seems not everywhere around... */

       char c1, c2;
       int i;

       if (!s1) s1 = "";
       if (!s2) s2 = "";

       for (i=0; i<n && *s1 && *s2; i++, s1++, s2++)
       {       c1 = tolower( *s1);
               c2 = tolower( *s2);
               if (c1 != c2) break;
       }

       return (i < n && (*s1 || *s2));
}