/*
 * post.c
 */

/*
 * mpage:    a program to reduce pages of print so that several pages
 *           of output appear on one printed page.
 *
 * Copyright (c) 1994-2004 Marcel J.E. Mol, The Netherlands
 * Copyright (c) 1988 Mark P. Hahn, Herndon, Virginia
 *  
 *     Permission is granted to anyone to make or distribute verbatim
 *     copies of this document as received, in any medium, provided
 *     that this copyright notice is preserved, and that the
 *     distributor grants the recipient permission for further
 *     redistribution as permitted by this notice.
 *
 */


#include "mpage.h"
#include <string.h>


/*
 * character spaces used throughout for holding the current line from
 * the input file
 */
static char currline[LINESIZE];
static char *file_name;

/*
 * for ps documents, used to remember if we have come across the
 * tailer section.  reset at the beginning of processing for each file
 */
static int ps_at_trailer;

/*
 * this is the type of postscript document we are processing
 */
static int ps_posttype;

/*
 * set to one if we have a inputline that must be preceded by something...
 * used for dvips output without PS comments.
 */
static int have_line = 0;

/*
 * number of output lines on current logical page
 */
static int plcnt;


int have_showsheet = 0;

char ps_roff_xi [16]; /* to hold the DITROFF xi line... */

static char * tex1;   /* to capture important dvi2ps lines... */
static char * tex2;

/*
 * Function declarations 
 */
static int   ps_gettype();
static void  do_post_doc();
#if 0
static void  do_other_doc();
#endif
static void  ps_copyprolog();
static void  ps_roff_copyprolog();
static void  ps_mpage_copyprolog();
static void  ps_skip_to_page();
static int   do_post_sheet();
static void  ps_sheetsetup();
static int   post_onepage();
static int   post_flush_onepage();
static int   post_one_line();
static void  do_roff_tailer();
int   ps_check();
void  do_ps_doc();

static int post_flush_onepage();
static int  ps_store_to_page();

/*
 * Peek at the first two chacters on the open file and check for the
 * two character postscript flag "%!".  If the file is not postscript
 * then the characters are pushed back into the input stream (hopefully).
 */
int
ps_check(infd)
 FILE *infd;
{
    int firstchar;
    int secondchar;
    
    Debug(DB_PSCHECK, "%%ps_check: in ps_check\n", 0);

    /*
     * eliminate blank files
     */
    if ((firstchar = fgetc(infd)) == EOF) {
        Debug(DB_PSCHECK, "%%ps_check: file is blank\n", 0);
        return 0;
    }

    /*
     * Skip any CTRL-D chars
     * Hope there are no text files starting with ctrl-d's
     */
    while (firstchar == 4)
        firstchar = fgetc(infd);

    /*
     * eliminate non-postscript files
     */
    if (firstchar != '%') {
        Debug(DB_PSCHECK, "%ps_check: 1st char is '%c' not '%'\n", firstchar);
        if (ungetc(firstchar, infd) == EOF) {
            fprintf(stderr, "%s: Lost first character of file ", MPAGE);
            fprintf(stderr, "while checking for postscript\n.");
        }
        return 0;
    }
    Debug(DB_PSCHECK, "%%ps_check: 1st char is '%c'\n", firstchar);
    /*
     * eliminate one character files (containing only a %)
     */
    if ((secondchar = fgetc(infd)) == EOF) {
        Debug(DB_PSCHECK, "%%ps_check: no second char\n", 0);
        if (ungetc(firstchar, infd) == EOF) {
            fprintf(stderr, "%s: Lost first character of file ", MPAGE);
            fprintf(stderr, "while checking for postscript\n.");
        }
        return 0;
    }
    /*
     * eliminate files that don't have the full two character
     * sequence of "%!".
     */
    if (secondchar != '!') {
        Debug(DB_PSCHECK, "%%ps_check: 2nd char is '%c' not '!'\n", secondchar);
        if (ungetc(secondchar, infd) == EOF) {
            fprintf(stderr, "%s: Lost first two characters of ", MPAGE);
            fprintf(stderr, "file while checking for postscript\n.");
            return 0;
        }
        if (ungetc(firstchar, infd) == EOF) {
            fprintf(stderr, "%s: Lost first character of file ", MPAGE);
            fprintf(stderr, "while checking for postscript.\n");
        }
        return 0;
    }
    /*
     * for post script files the first two characters (the "%!") are
     * digested by this routine.  It's just easier than dealing
     * with the problems encounted if the characters can't be ungetc'ed.
     */
    Debug(DB_PSCHECK, "%%ps_check: 2nd char is '%c'\n", secondchar);
    Debug(DB_PSCHECK, "%%ps_check: input is postscript\n", 0);

    return 1;

} /* ps_check */



static int
ps_gettype(fd, outfd)
 FILE *fd;
 FILE *outfd;
{
    int ps_type, end_comments;

    Debug(DB_PSDOC, "%%ps_gettype: in ps_gettype\n", 0);
    /*
     * error check for truncated files
     */
    if (fgets(currline, LINESIZE-1, fd) == NULL) {
        Debug(DB_PSDOC, "%%ps_gettype: got eof on first line\n", 0);
        return PS_NONE;
    }
    /*
     * check for non-conforming postscript
     * Note that %! is already gone down the drain...
     */
    if (strncmp(currline, PS_FLAG, strlen(PS_FLAG)) != 0) {
        Debug(DB_PSDOC, "%%ps_gettype: no match PS_FLAG \"%s\"\n", currline);
        return PS_OTHER;
    }
    /*
     * we have some form of conforming postscript, try to identify the
     * type
     */
    Debug(DB_PSDOC, "%%ps_gettype: conforming postscript\n", 0);
    end_comments = 0;
    ps_type = PS_CONFORM;
    while (!end_comments) {
        /*
         * if we get end of file then we assume non-conforming PS
         */
        if (fgets(currline, LINESIZE-1, fd) == NULL) {
            Debug(DB_PSDOC, "%%ps_gettype: eof in comments\n", 0);
            return PS_OTHER;
        }
        /*
         * if we have run out of leading comments then assume 
         * conforming PS (because we had a valid "%!" line)
         */
        if (currline[0] != '%') {
            Debug(DB_PSDOC, "%%ps_gettype: out off comments\n", 0);
            fprintf(outfd, "%s", currline);
            return PS_CONFORM;
        }
        /*
         * print out the comment line with an extra % to disguise the comment
         */
        fprintf(outfd, "%%%s", currline);
        /*
         * check for the end of the leading comments section
         */
        if (strncmp(currline, "%%EndComments", 13) == 0)
            end_comments = 1;
        /*
         * Some tricky way to handle MS-windows postscript files...
         * probably doesn't work.
        */
        if (strncmp(currline, "%%Pages:", 8) == 0 &&
            ps_type == PS_MSWINDOWS)
            return ps_type;
        /*
         * once we know the type of PS, we no longer need to keep
         * checking.
         */
        if (ps_type != PS_CONFORM)
            continue;
        /*
         * check for mpage output
         */
        if (!strncmp(currline, "%%Creator: ", 11)) {
            if (!strncmp(currline+11, MPAGE, strlen(MPAGE))) {
                Debug(DB_PSDOC, "%%ps_gettype: mpage document\n", 0);
                ps_type = PS_MPAGE;
            }
            else if (!strncmp(currline+11, "Windows PSCRIPT", 15)) {
                Debug(DB_PSDOC, "%%ps_gettype: windows document\n", 0);
                ps_type = PS_MSWINDOWS;
            }
            else if (!strncmp(currline+11, "dvips", 5)) {
                Debug(DB_PSDOC, "%%ps_gettype: dvips\n", 0);
                ps_type = PS_TEX;
            }
        }
        /*
         * check for psroff/tex output
         */
        if (strncmp(currline, "%%Title: ", 9) == 0) {
            if (strstr(currline, "ditroff")) {
                Debug(DB_PSDOC, "%%ps_gettype: psroff\n", 0);
                ps_type = PS_PSROFF;
            }
            else if (strstr(currline, ".dvi")) {
                Debug(DB_PSDOC, "%%ps_gettype: dvi2ps\n", 0);
                ps_type = PS_TEX;
            }
        }
        if (strncmp(currline, "%DVIPS", 6) == 0) {
            Debug(DB_PSDOC, "%%ps_gettype: dvips\n", 0);
            if (ps_type != PS_TEX)
                ps_type = PS_TEX2;
            return ps_type; 
        }
    }
#ifdef DEBUG
    if (ps_type == PS_CONFORM) {
        Debug(DB_PSDOC, "%%ps_gettype: unknown type, conforming PS\n", 0);
    }
#endif

    return ps_type;

} /* ps_gettype */



void
do_ps_doc(fd, asheet, outfd, fname)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
 char * fname;
{

    Debug(DB_PSDOC, "%%do_ps_doc: postscript document\n", 0);

    file_name = fname;
    ps_posttype = ps_gettype(fd,outfd);
    Debug(DB_PSDOC, "%%do_ps_doc: document type is %d\n", ps_posttype);
    if (ps_posttype != PS_NONE)
        do_post_doc(fd, asheet, outfd);

    return;

} /* do_ps_doc */



static void
do_post_doc(fd, asheet, outfd)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
{

    ps_at_trailer = FALSE;
    Debug(DB_POST, "%%do_post_doc: prolog\n", 0);
    ps_copyprolog(fd, outfd);
    /*
     * while there is still input, print pages
     */
    Debug(DB_POST, "%%do_post_doc: pages\n", 0);
    do_sheets(do_post_sheet, fd, asheet, outfd);
    Debug(DB_POST, "%%do_post_doc: trailer\n", 0);
    do_roff_tailer(fd, outfd);

    return;

} /* do_post_doc */


#if 0
/* not used yet... */
static void
do_other_doc(fd, asheet, outfd)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
{

    ps_at_trailer = FALSE;
    ps_copyprolog(fd, outfd);

    return;

} /* do_other_doc */
#endif



static void
ps_copyprolog(fd, outfd)
 FILE *fd;
 FILE *outfd;
{

    Debug(DB_PSDOC, "%%ps_copyprolog: adding mpage prolog\n", 0);
    if (!have_showsheet) {
#if 1
        fprintf(outfd, "/showsheet  /showpage load def\n");
#else
        fprintf(outfd, "/showsheet { showpage } bind def\n");
#endif
        fprintf(outfd, "/showpage { } def\n");
        have_showsheet = 1;
    }
    had_ps = 1;
    Debug(DB_PSDOC, "%%ps_copyprolog: copying prolog\n", 0);
    if (ps_posttype == PS_PSROFF) {
        Debug(DB_PSDOC, "%%ps_copyprolog: calling ps_roff_copyprolog\n",0);
        ps_roff_copyprolog(fd, outfd);
        return;
    }
    if (ps_posttype == PS_MPAGE) {
        Debug(DB_PSDOC, "%%ps_copyprolog: calling ps_mpage_copyprolog\n",0);
        ps_mpage_copyprolog(fd, outfd);
        return;
    }
    while (fgets(currline, LINESIZE-1, fd) != NULL) {
        if (strncmp(currline, "%%Page:", 7) == 0) {
            fprintf(outfd, "%% %s", currline);
            return;
        }
        if (ps_posttype == PS_TEX || ps_posttype == PS_TEX2) { 
            if (ps_posttype == PS_TEX2 && strstr(currline, " bop ")) {
                /* dvips output without comments... */
                have_line = 1;
                return;
            }
            if (strncmp(currline, "TeXDict", 7) == 0) {
            /* SHOULD PROBABLY REMOVE THIS. SEE BELOW  /dictionarystack */
                /*
                 * Hope all dvi2ps progs work the same:
                 * capture the TeX init code so we can run it 'manually' for
                 * every page. This is needed as this code sets up a gstate 
                 * that conflicts with mpage...
                 * This trick seems to work for text, but figures within the dvi
                 * file seem to have a mind of their own...
                 */
                if (tex1)
                    free(tex1);
                tex1 = malloc(strlen(currline)+1);
                strcpy(tex1, currline);
                fprintf(outfd, "%s", currline);

                fgets(currline, LINESIZE-1, fd);
                if (tex2)
                    free(tex2);
                tex2 = malloc(strlen(currline)+1);
                strcpy(tex2, currline);
            }
        }
        fprintf(outfd, "%s", currline);
    }
    Debug(DB_PSDOC, "%%ps_copyprolog: eof before %%%%EndProlog\n", 0);
    fprintf(outfd, "%%%%EndProlog\n");

    return;

} /* ps_copyprolog */



static void
ps_roff_copyprolog(fd, outfd)
 FILE *fd;
 FILE *outfd;
{

    Debug(DB_PSDOC, "%%ps_roff_copyprolog: copying psroff prolog\n", 0);
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
 /*       if (strcmp(currline, "xi\n") == 0) */
        if (strstr(currline, "xi\n")) {
            fprintf(outfd, "%%%s", currline); 
            strcpy(ps_roff_xi, currline);
        }
        else if (strncmp(currline, "%%Page:", 7) == 0) {
            fprintf(outfd, "/p { } def\n");
            fprintf(outfd, "/xt { } def\n");
            fprintf(outfd, "/xs { } def\n");
            fprintf(outfd, "%% %s", currline);
            Debug(DB_PSDOC, "%%ps_copyprolog: Done\n", 0);
            return;
        }
        else
            fprintf(outfd, "%s", currline);
    }
    Debug(DB_PSDOC, "%%ps_copyprolog: eof before %%%%EndProlog\n", 0);
    fprintf(outfd, "/p { } def\n");
    fprintf(outfd, "/xt { } def\n");
    fprintf(outfd, "/xs { } def\n");
    fprintf(outfd, "%%%%EndProlog\n");

    return;

} /* ps_roff_copyprolog */



static void
ps_mpage_copyprolog(fd, outfd)
 FILE *fd;
 FILE *outfd;
{

    Debug(DB_PSDOC, "%%ps_mpage_copyprolog: skipping mpage prolog\n", 0);
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
        if (strncmp(currline, "%%Page:", 7) == 0)  {
            fprintf(outfd, "%% %s", currline);
            Debug(DB_PSDOC, "%%ps_copyprolog: Done\n", 0);
            return;
        }
    }
} /* ps_mpage_copyprolog */



static void
ps_skip_to_page(fd)
 FILE *fd;
{

    Debug(DB_PSDOC, "%%ps_skip to page: reading until %%%%Page:\n", 0);
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
        Debug(DB_PSDOC, "%% %s", currline);
        if (strncmp(currline, "%%Page:", 7) == 0)
            return;
    }
    Debug(DB_PSDOC, "%%ps_skip_to_page: eof before %%%%Page:\n", 0);

    return;

} /* ps_skip_to_page */



static char *stored_page = NULL;
static int stored_page_size = 0;

static int
ps_store_to_page(fd)
 FILE *fd;
{
    int totlen = 0;

    Debug(DB_PSDOC, "%%ps_store to page: reading until %%%%Page:\n", 0);
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
        int len;
        if (strncmp(currline, "%%Page:", 7) == 0)
            return totlen;
        len = strlen(currline);
        totlen += len;
        if (totlen > stored_page_size){
            stored_page = realloc(stored_page, totlen);
            stored_page_size = totlen;
        }
        /* do not copy the '\0' */
        memcpy(stored_page + totlen - len, currline, len); 
        Debug(DB_PSDOC, "%% %s", currline);
    }
    Debug(DB_PSDOC, "%%ps_store_to_page: eof before %%%%Page:\n", 0);

    return totlen;

} /* ps_store_to_page */



/* GPN */
/* #define NSCALE	to take care of previous scaling */
#ifdef NSCALE
char *NScale =  "/gpnsavematrix {orgmatrix currentmatrix pop} bind def\n"
                "/gpnrestorematrix {orgmatrix setmatrix} bind def\n"
                "/orgmatrix matrix def\n"
                "gpnsavematrix\n"
                "orgmatrix orgmatrix invertmatrix pop\n"
                "/gpnxs\n"
                "    orgmatrix 0 get 0.0000 eq\n"
                "     {orgmatrix 1 get abs}\n"
                "     {orgmatrix 0 get abs}\n"
                "    ifelse def\n"
                "/gpnys\n"
                "    orgmatrix 3 get 0.0000 eq\n"
                "     {orgmatrix 2 get abs}\n"
                "     {orgmatrix 3 get abs}\n"
                "    ifelse def\n"
                "/gpnxs gpnxs gscurrentresolution 0 get 72 div mul def\n"
                "/gpnys gpnys gscurrentresolution 1 get 72 div mul def\n";
#endif /* NSCALE */

static int
do_post_sheet(fd, asheet, outfd)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
{
    int rtn_val = FILE_MORE;
    int sh_high, sh_wide;
    struct pagepoints *stored_points = NULL, *flush_points = NULL;
    int flush = 0, totlen = 0;

    do {
      if ((points->pp_origin_x == 0 && !points->skip) || opt_file || flush) {
        /*
         * keep track of the pages processed
         */
        ps_pagenum++;
        fprintf(outfd, "%%%%Page: %d %d\n", ps_pagenum, ps_pagenum);
# ifdef DEBUG
        if (Debug_flag & DB_PSMPAGE)
            fprintf(outfd, "(Page: %d\\n) print flush\n", ps_pagenum);
# endif /* DEBUG */
#if 0
        /* seems to give memory problems... */
        fprintf(outfd, "/sheetsave save def\n");
#endif

        /*
         * Now is the time to print a sheet header...
         * for now, this has to be done before outline...
         */
        sheetheader(outfd, file_name);

        /*
         * print the page outline
         */
        mp_outline(outfd, asheet);

        /*
         * run through the list of base points for putting reduced pages
         * on the printed page
         */
        if (!flush)
            points = asheet->sh_pagepoints;
        else
            points++;
      }

      while ((points->pp_origin_x!=0 || points->skip) && rtn_val == FILE_MORE) {

        /* GPN. skip this page ?*/
        if (points->skip!=0)   {
            switch (points->skip) {
                case SKIP_PS:
                  /*
                   * Skip this page. User needs another run with the other
                   * of -O/-E option.
                   */
                  ps_skip_to_page(fd);
                  points++;
                  continue;
                case STORE_PS:
                  /*
                   *  stored_page could also be an array
                   *  if more than one page
                   *  is needed
                   */
                  totlen = ps_store_to_page(fd);
                  stored_points = points;
                  flush = STORE_PS;
                  points++;
                  continue;
                case FLUSH_PS:
                  if (stored_points) {
                      /*
                       * Ok I have to print the page and start a new one.
                       * This could be more elegant, but for now...
                       */
                      /*
                      fprintf(outfd, "showsheet\n");
                      ps_pagenum++;
                      fprintf(outfd, "%%%%Page: %d %d\n",
                                     ps_pagenum, ps_pagenum);
                      sheetheader(outfd, file_name);
                      mp_outline(outfd, asheet);
                      */
                      flush = FLUSH_PS;
                      flush_points = points;
                      points = stored_points;
                  }
                  else
                      flush = 0;
            }
        }

        /*
         * Save current graphics context so we can scale/translate
         * from known position and size.
         */
        fprintf(outfd, "gsave\n");

        /*
         * print one reduced page by moving to the proper point,
         * turning to the proper aspect, scaling to the proper
         * size, and setting up a clip path to prevent overwritting;
         * then print a reduced page of output.
         */
        Debug(DB_PSMPAGE, "%%%% %%%%ReducedPageStartsHere\n", outfd);
# ifdef DEBUG
        if (Debug_flag & DB_PSMPAGE) {
            fprintf(outfd, "(    %d %d translate %d rotate\\n)",
                            points->pp_origin_x(), points->pp_origin_y(),
                            asheet->sh_rotate);
            fprintf(outfd, " print flush\n");
        }
# endif /* DEBUG */

#ifdef  NSCALE /*GPN*/
        fprintf (outfd, NScale);

        fprintf(outfd, "%d gpnxs mul %d gpnxs mul translate %d rotate\n",
                      points->pp_origin_x(), points->pp_origin_y(),
   	     	      	                           asheet->sh_rotate);
#else /* NSCALE */
        fprintf(outfd, "%d %d translate\n",
                       points->pp_origin_x(), points->pp_origin_y());
   	if (asheet->sh_rotate)
            fprintf(outfd, "%d rotate\n", asheet->sh_rotate);

#endif
        sh_wide = (*asheet->sh_width)();
        sh_high = (*asheet->sh_height)();

        /* output the clip path */
#ifdef  NSCALE /*GPN*/
        fprintf(outfd, "0 0 moveto 0 %d gpnys mul lineto %d gpnxs mul"
                       " %d gpnys mul lineto ",
       	       	    sh_high, sh_wide, sh_high);
        fprintf(outfd, "%d gpnxs mul 0 lineto\n", sh_wide);
   	fprintf(outfd, "closepath clip\n");
#else /* NSCALE */
#if 0
        fprintf(outfd, "0 0 moveto 0 %d lineto %d %d lineto ",
                       sh_high, sh_wide, sh_high);
        fprintf(outfd, "%d 0 lineto\n", sh_wide);
#else
        fprintf(outfd, "1 1 moveto 1 %d lineto %d %d lineto ",
                       sh_high - 1, sh_wide - 1, sh_high - 1);
        fprintf(outfd, "%d 1 lineto\n", sh_wide - 1);
#endif
        fprintf(outfd, "closepath clip newpath\n");
#endif /* NSCALE */

        if (opt_square) {
            int newhigh = sh_high, newwide = sh_wide;

            if (sh_wide * ps_height > sh_high * ps_width)
                newwide = (sh_high * ps_width) / ps_height;
            else
                newhigh = (sh_wide * ps_height) / ps_width;

#ifdef  NSCALE /*GPN*/
    	    fprintf(outfd, "%d gpnxs mul %d gpnys mul translate\n",
#else
            fprintf(outfd, "%d %d translate\n",
#endif

                           (sh_wide - newwide) / 2, (sh_high - newhigh) / 2);
            sh_wide = newwide;
            sh_high = newhigh;
   	}

        fprintf(outfd, "%d %d div %d %d div scale\n",
                       sh_wide, ps_width, sh_high, ps_height);

        /*
         * Take extra page margins into account
         */
        fprintf(outfd, "%d %d div %d %d div scale\n",
               ps_width, ps_width + pagemargin_left + pagemargin_right,
               ps_height, ps_height + pagemargin_top + pagemargin_bottom);
#ifndef NSCALE
        fprintf(outfd, "%d %d translate\n", pagemargin_left, pagemargin_bottom);
#endif

#if 0
        /* does not seem to be neccessary. seems to create dictionary
         * stack underflows...
         */
        if ((ps_posttype == PS_TEX || ps_posttype == PS_TEX2) && tex1)
            /* start dvi2ps init every page */
	    fprintf(outfd, "%s%s", tex1, tex2);
#endif

        /*
         * do the individual sheet setup
         */
        ps_sheetsetup(outfd);
        /*
         * place one reduce page on the printed page
         */
        plcnt = 0;
        if (flush == FLUSH_PS) {
            rtn_val = post_flush_onepage(totlen, asheet, outfd);
            stored_points = NULL;
            points = flush_points;
        }
        else {
            rtn_val = post_onepage(fd, asheet, outfd);
            points++;
        }
        /*
         * clean up after mpage has drawn its page
         */
        fprintf(outfd, "grestore\n");
      }
#if 0
      fprintf(outfd, "sheetsave restore\n");
#endif

      /*
       * print the sheet
       */
      if (points->pp_origin_x == 0 || (rtn_val == FILE_EOF && opt_file))
          fprintf(outfd, "showsheet\n");

    } while (flush && rtn_val == FILE_MORE);

    if (stored_points) {
 
        flush_points = points;
        points = stored_points;

        /*
         * Save current graphics context so we can scale/translate
         * from known position and size.
         */
        fprintf(outfd, "gsave\n");

        /*
         * print one reduced page by moving to the proper point,
         * turning to the proper aspect, scaling to the proper
         * size, and setting up a clip path to prevent overwritting;
         * then print a reduced page of output.
         */
        Debug(DB_PSMPAGE, "%%%% %%%%ReducedPageStartsHere\n", outfd);
# ifdef DEBUG
        if (Debug_flag & DB_PSMPAGE) {
            fprintf(outfd, "(    %d %d translate %d rotate\\n)",
                            points->pp_origin_x(), points->pp_origin_y(),
                            asheet->sh_rotate);
            fprintf(outfd, " print flush\n");
        }
# endif /* DEBUG */

#ifdef  NSCALE /*GPN*/
        fprintf (outfd, NScale);
        fprintf(outfd, "%d gpnxs mul %d gpnxs mul translate %d rotate\n",
                      points->pp_origin_x(), points->pp_origin_y(),
   	     	      	                           asheet->sh_rotate);
#else /* NSCALE */
        fprintf(outfd, "%d %d translate\n",
                       points->pp_origin_x(), points->pp_origin_y());
   	if (asheet->sh_rotate)
            fprintf(outfd, "%d rotate\n", asheet->sh_rotate);

#endif
        sh_wide = (*asheet->sh_width)();
        sh_high = (*asheet->sh_height)();

        /* output the clip path */
#ifdef  NSCALE /*GPN*/
        fprintf(outfd, "0 0 moveto 0 %d gpnys mul lineto %d gpnxs mul"
                       " %d gpnys mul lineto ",
       	       	    sh_high, sh_wide, sh_high);
        fprintf(outfd, "%d gpnxs mul 0 lineto\n", sh_wide);
   	fprintf(outfd, "closepath clip\n");
#else /* NSCALE */
#if 0
        fprintf(outfd, "0 0 moveto 0 %d lineto %d %d lineto ",
                       sh_high, sh_wide, sh_high);
        fprintf(outfd, "%d 0 lineto\n", sh_wide);
#else
        fprintf(outfd, "1 1 moveto 1 %d lineto %d %d lineto ",
                       sh_high - 1, sh_wide - 1, sh_high - 1);
        fprintf(outfd, "%d 1 lineto\n", sh_wide - 1);
#endif
        fprintf(outfd, "closepath clip newpath\n");
#endif /* NSCALE */

        if (opt_square) {
            int newhigh = sh_high, newwide = sh_wide;

            if (sh_wide * ps_height > sh_high * ps_width)
                newwide = (sh_high * ps_width) / ps_height;
            else
                newhigh = (sh_wide * ps_height) / ps_width;

#ifdef  NSCALE /*GPN*/
    	    fprintf(outfd, "%d gpnxs mul %d gpnys mul translate\n",
#else
            fprintf(outfd, "%d %d translate\n",
#endif

                           (sh_wide - newwide) / 2, (sh_high - newhigh) / 2);
            sh_wide = newwide;
            sh_high = newhigh;
   	}

        fprintf(outfd, "%d %d div %d %d div scale\n",
                       sh_wide, ps_width, sh_high, ps_height);

        /*
         * Take extra page margins into account
         */
        fprintf(outfd, "%d %d div %d %d div scale\n",
               ps_width, ps_width + pagemargin_left + pagemargin_right,
               ps_height, ps_height + pagemargin_top + pagemargin_bottom);
#ifndef NSCALE
        fprintf(outfd, "%d %d translate\n", pagemargin_left, pagemargin_bottom);
#endif

#if 0
        /* does not seem to be neccessary. seems to create dictionary
         * stack underflows...
         */
        if ((ps_posttype == PS_TEX || ps_posttype == PS_TEX2) && tex1)
            /* start dvi2ps init every page */
	    fprintf(outfd, "%s%s", tex1, tex2);
#endif

        /*
         * do the individual sheet setup
         */
        ps_sheetsetup(outfd);
        /*
         * place one reduce page on the printed page
         */
        plcnt = 0;
        rtn_val = post_flush_onepage(totlen, asheet, outfd);
        stored_points = NULL;

        /*
         * clean up after mpage as drawn its page
         */
        fprintf(outfd, "grestore\n");
#if 0
        fprintf(outfd, "sheetsave restore\n");
#endif

        /*
         * print the sheet
         */
        if (points->pp_origin_x == 0 || (rtn_val == FILE_EOF && opt_file))
            fprintf(outfd, "showsheet\n");
      }

    /*
     * let the upper level know about the status of possible EOF
     */
    return rtn_val;

} /* do_post_sheet */



static void
ps_sheetsetup(outfd)
 FILE *outfd;
{

    switch (ps_posttype) {
        case PS_PSROFF: fprintf(outfd, "%s", ps_roff_xi);
                        fprintf(outfd, "/p {} def\n");
                        break;
/*
        case PS_MPAGE:  fprintf(outfd, "/showpage {} def\n");
                        break;
*/
    }

        return;

} /* ps_sheetsetup */



static int
post_onepage(fd, asheet, outfd)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
{
    int indoc = 0;

    Debug(DB_PSROFF, "%%post_onepage: Begin page\n", 0);
    if (ps_at_trailer) {
        Debug(DB_PSROFF, "%%post_onepage: still at trailer\n", 0);
        return FILE_EOF;
    }

    if (have_line) {
        fprintf(outfd, "%s", currline);
        have_line = 0;
    }
        
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
        int line_rc;
        line_rc = post_one_line(currline, fd, outfd, &indoc, 0);
        if (line_rc != FILE_CONT)
            return line_rc;
    }
    Debug(DB_PSROFF, "%%post_onepage: eof\n", 0);

    return FILE_EOF;

} /* post_onepage */



static int
post_flush_onepage(totlen, asheet, outfd)
 int totlen;
 struct sheet *asheet;
 FILE *outfd;
{
    int indoc = 0;

    Debug(DB_PSROFF, "%%post_flush_onepage: Begin page\n", 0);

    /*
    if (ps_at_trailer) {
        Debug(DB_PSROFF, "%%post_flush_onepage: still at trailer\n", 0);
        return FILE_EOF;
    }
    */

    if (have_line) {
        fprintf(outfd, "%s", currline);
        have_line = 0;
    }
        
    memgets_init(stored_page, totlen);
    while (memgets(currline, LINESIZE-1) != NULL) {
        int line_rc;
        line_rc = post_one_line(currline, (FILE *) NULL, outfd, &indoc, 1);
        if (line_rc != FILE_CONT)
            return line_rc;
    }
    Debug(DB_PSROFF, "%%post_flush_onepage: eof\n", 0);

    return FILE_MORE;

} /* post_flush_onepage */



static int
post_one_line(line, fd, outfd, indoc, flush_page)
 char * line;
 FILE *fd;
 FILE *outfd;
 int * indoc;
 int flush_page;
{
        if (strncmp(line, "%%BeginDocument", 15) == 0) {
            (*indoc)++;
        }
        if (strncmp(line, "%%EndDocument", 13) == 0) {
            (*indoc)--;
        }
	if (!*indoc) {
            if (strncmp(line, "%%Page:", 7) == 0) {
                fprintf(outfd, "%% %s", line);
                /*
                 * only if there is already output to this logical page
                 */
                if (!plcnt)
                    return FILE_CONT;
                Debug(DB_PSROFF, "%%post_one_line: next page\n", 0);
                return FILE_MORE;
            }
            if (opt_killtrail && (strncmp(line, "%%Trailer", 9) == 0 ||
                strncmp(line, "%%PSTrailer", 11) == 0)) {
                fprintf(outfd, "%% %s", line);
                Debug(DB_PSROFF, "%%post_one_line: found trailer\n", 0);
                ps_at_trailer = TRUE;
                return FILE_EOF;
            }
            /* For netscape output */
            if (strncmp(line, "%%EOF", 5) == 0) {
                fprintf(outfd, "%% %s", line);
                Debug(DB_PSROFF, "%%post_one_line: Netscape EOF\n", 0);
                return FILE_EOF;
            }
            if (ps_posttype == PS_MPAGE && strncmp(line, "showsheet", 9) == 0)
                return FILE_CONT;
            if (ps_posttype == PS_TEX || ps_posttype == PS_TEX2) {
                if (ps_posttype == PS_TEX2 &&
                    (strstr(line, "eop\n") || strstr(line, "eop end\n"))) {
                    fprintf(outfd, "%s", line);
                    Debug(DB_PSROFF, "%%post_one_line: found TEX eop\n", 0);
                    return FILE_MORE;
                }
                if (strncmp(line, "TeXDict", 7) == 0) {
                    /*
                     * Hope all dvi2ps progs work the same:
                     * capture the TeX init code so we can run it 'manually' for
                     * every page. This is needed as this code sets up a gstate 
                     * that conflicts with mpage...
                     * This trick seems to work for text, but figures within the dvi
                     * file seem to have a mind of their own...
                     */
                    if (tex1)
                        free(tex1);
                    tex1 = malloc(strlen(line)+1);
                    strcpy(tex1, line);
                    fprintf(outfd, "%s", line);
                    flush_page ?  memgets(line, LINESIZE-1) :
                                  fgets(line, LINESIZE-1, fd);
                    if (tex2)
                        free(tex2);
                    tex2 = malloc(strlen(line)+1);
                    strcpy(tex2, line);
                } 
            }
	}
        fprintf(outfd, "%s", line);
        plcnt++;

        return FILE_CONT;

} /* post_one_line */



static void
do_roff_tailer(fd, outfd)
 FILE *fd, *outfd;
{
#ifdef DEBUG
        int i = 0;
#endif

    Debug(DB_PSDOC, "%%do_roff_trailer: looking for eof\n", 0);
    while(fgets(currline, LINESIZE-1, fd) != NULL) {
#ifdef DEBUG
        i++;
        Debug(DB_PSDOC, "%%%s", currline);
#endif
        ;
    }
    Debug(DB_PSDOC, "%%do_roff_trailer: tailer of %d lines\n", i);

    return;

} /* do_roff_tailer */

