# ifndef lint
static char *rcs_id =
  "@(#) $Header: /usr2/src/mpage/RCS/mp_text.c,v 2.10 90/06/07 00:45:24 slevy Exp $";
# endif

/*
 * mpage:    a program to reduce pages of print so that several pages
 *           of output appear on one printed sheet.
 *
 * Written by:
 *   ...!uunet!\                       Mark Hahn, Sr Systems Engineer
 *              >pyrdc!mark            Pyramid Technology Corporation
 * ...!pyramid!/                       Vienna, Va    (703)848-2050
 *
 *
 * 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 notice is preserved, and that the
 *     distributor grants the recipient permission for further
 *     redistribution as permitted by this notice.
 *
 */

/* $Log:    mp_text.c,v $
 * Revision 2.10  90/06/07  00:45:24  slevy
 * Check in do_text_sheet() at the beginning of a sheet
 * whether we're just about to hit EOF.  If so, don't print it.
 * 
 * Revision 2.9  90/02/01  14:33:09  slevy
 * Extract do_text_sheet() loop into do_sheets() for page selection &c.
 * 
 * Revision 2.8  89/06/23  13:14:00  mark
 * checked in with -k by slevy at 90.02.01.14.27.26.
 * 
 * Revision 2.8  89/06/23  13:14:00  mark
 * Fixed range checking for control characters so that tilde was
 * included as a printable character.
 * 
 * Revision 2.7  89/06/21  08:58:56  mark
 * moved the bottom of the clipping region for text below zero so that
 * decenders on the last line can be seen.  Currently the font, font size
 * and this extra space are hard coded.  Something more reasonable should
 * be done.  Probably involves more command line options.  (Sorry, "Something
 * more reasonable" should be "something more flexible")
 * 
 * Revision 2.6  89/05/25  10:20:11  mark
 * changed the format of debugging prints in the PS code.
 * 
 * Revision 2.5  89/05/25  09:01:51  mark
 * rearranged the rcs header keywords for better readability.
 * 
 * Revision 2.4  89/05/24  17:36:56  mark
 * fixed the $Log:    mp_text.c,v $
 * Revision 2.10  90/06/07  00:45:24  slevy
 * Check in do_text_sheet() at the beginning of a sheet
 * whether we're just about to hit EOF.  If so, don't print it.
 * 
 * Revision 2.9  90/02/01  14:33:09  slevy
 * Extract do_text_sheet() loop into do_sheets() for page selection &c.
 * 
 * Revision 2.8  89/06/23  13:14:00  mark
 * checked in with -k by slevy at 90.02.01.14.27.26.
 * 
 * Revision 2.8  89/06/23  13:14:00  mark
 * Fixed range checking for control characters so that tilde was
 * included as a printable character.
 * 
 * Revision 2.7  89/06/21  08:58:56  mark
 * moved the bottom of the clipping region for text below zero so that
 * decenders on the last line can be seen.  Currently the font, font size
 * and this extra space are hard coded.  Something more reasonable should
 * be done.  Probably involves more command line options.  (Sorry, "Something
 * more reasonable" should be "something more flexible")
 * 
 * Revision 2.6  89/05/25  10:20:11  mark
 * changed the format of debugging prints in the PS code.
 * 
 * Revision 2.5  89/05/25  09:01:51  mark
 * rearranged the rcs header keywords for better readability.
 *  rcs keyword.
 *  */

#include "mp_head.h"
#include <time.h>
#include <sys/stat.h>

/*
 * keeps track of the current location on the sheet.  it is kept global
 * to while file printing process because of form feeds in particular.
 * form feeds change the vertical page location (line number) but not
 * the horizontal location (character column)
 */
struct pageloc {
    int pl_line;
    int pl_col;
    int pl_new_line;
    int pl_new_col;
};

static struct pageloc loc;
static char text[LINESIZE];

static char *file_name;
static int file_pagenum;
static char file_date[LINESIZE];

/*
 * Function Declarations
 */
static int do_text_sheet();
static int text_onepage();
static char *mp_get_text();


/*
 * do_text_doc processes an input stream fd, reducing output to fit on
 * a printed page as decribed by asheet, and prints this on outfd.
 */
void
do_text_doc(fd, asheet, outfd, fname)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
 char * fname;
{
    struct stat file_stat;

    /*
     * initalize the postion on the first printed page
     */
    loc.pl_line = 1;
    loc.pl_col = opt_indent;
    file_name = fname;
    file_pagenum = 0;
    fstat(fileno(fd), &file_stat);
    strftime(file_date, LINESIZE, "%c", localtime(&file_stat.st_ctime));
    /*
     * while we have input, print a page
     */
    do_sheets(do_text_sheet, fd, asheet, outfd );

    return;
} /* do_text_doc */



/*
 * do_text_sheet creates one printed sheet consisting of several reduced pages
 */
static int
do_text_sheet(fd, asheet, outfd)
 FILE *fd;
 struct sheet *asheet;
 FILE *outfd;
{
    int rtn_val = FILE_MORE;
    int peekc;

    if ((peekc = getc(fd)) == EOF)
        return FILE_EOF;
    ungetc(peekc, fd);

    if (points->pp_origin_x == 0 || opt_file) {
        /*
         * keep track of the pages printed
         */
        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
        fprintf(outfd, "save\n"); /* for better memory usage */
        /*
         * print the page outline, which draws lines and such
         */
        mp_outline(outfd, asheet);
        /*
         * run through the list of base points for putting reduced pages
         * on the printed page
         */
        points = asheet->sh_pagepoints;
    }
    while (points->pp_origin_x != 0 && rtn_val == FILE_MORE) {
        /*
         * print one reduced page by moveing to the proper point,
         * turning to the proper aspect, scaling to the proper
         * size, and setting up a clip path to prevent overwritting;
         * the print a reduced page of output
         */
        int pheight;

        fprintf(outfd, "gsave\n");
# 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
        /*
         * Take position on paper
         */
        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);
        /*
         * Clip to logical page
         */
        fprintf(outfd, "0 0 moveto 0 %d rlineto %d 0 rlineto "
                       "0 %d rlineto closepath clip\n",
                       (*asheet->sh_height)(), (*asheet->sh_width)(),
                       -(*asheet->sh_height)());
        pheight = asheet->sh_plength * fsize +
                  (opt_mp_header ? HSIZE + 2 : 0);
        /*
         * Scale to logical page
         */
        fprintf(outfd, "%d %d mp_a_x mul div %d %d div scale\n",
               (*asheet->sh_width)(), asheet->sh_cwidth, 
               (*asheet->sh_height)(), pheight);
        /*
         * Draw header bar and print header when needed 
         */
        if (opt_mp_header) {
            int pos = (asheet->sh_plength) * fsize;
            fprintf(outfd, "0 %d moveto %d mp_a_x mul 0 rlineto stroke\n",
                           pos, asheet->sh_cwidth);
            pos += 4;
            fprintf(outfd, "headerfont setfont\n");
            if (opt_doheader)
                fprintf(outfd, "3 %d moveto (%s) show\n", pos, opt_header);
            else {
                fprintf(outfd, "3 %d moveto (%s) show\n", pos, file_date);
                fprintf(outfd, "%d mp_a_x mul dup (Page %d) stringwidth pop "
                               "sub 3 sub %d moveto",
                               asheet->sh_cwidth, ++file_pagenum, pos);
                fprintf(outfd, " (Page %d) show\n", file_pagenum);
                fprintf(outfd, "fnamefont setfont\n");
                fprintf(outfd, "(%s) stringwidth pop sub 2 div %d moveto\n",
                               file_name, pos);
                fprintf(outfd, "(%s) show\n", file_name);
            }
        }

        /*
         * Take pagemargin and font descenders (fsize/4) into account
         * and scale again
         */
        fprintf(outfd, "%d %d translate %d %d div %d %d div scale\n",
               pagemargin_left, pagemargin_bottom + fsize/4,
               (*asheet->sh_width)() - pagemargin_left - pagemargin_right,
               (*asheet->sh_width)(),
               asheet->sh_plength * fsize - pagemargin_top - pagemargin_bottom,
               asheet->sh_plength * fsize);

        fprintf(outfd, "textfont setfont\n");

        /*
         * place one reduced page on the printed page
         */
        rtn_val = text_onepage(fd, asheet, outfd);
        /*
         * clean up this page and move to the next
         */
        fprintf(outfd, "grestore\n");
        points++;
    }
    /*
     * release PS vm used, and eject the sheet
     */
    if (points->pp_origin_x == 0 ||
            (rtn_val == FILE_EOF && opt_file)) {
        fprintf(outfd, "restore\n");
            if (had_ps)
            fprintf(outfd, "showsheet\n");
            else
            fprintf(outfd, "showpage\n");
        }
    /*
     * let the upper level know about the status of possible EOF
     */
    return rtn_val;
}



/*
 * text_onepage places on page of reduced output on the printed page
 * all scaling, translation, and rotation has already been done before
 */
static int
text_onepage(file, asheet, outfd)
 FILE *file;
 struct sheet *asheet;
 FILE *outfd;
{
    char *text;

    /*
     * as we move from one page to the next, restart printing text at
     * the head of the page. horziontal location is not reset because
     * form feeds leave the column the same from page to page.
     */
    Debug(DB_ONEPAGE, "%% reseting line to top of page\n", 0);
    loc.pl_line = 1;
    /*
     * keep getting lines of input, until we have filled a page
     */
    while (loc.pl_line <= asheet->sh_plength) {
        text = mp_get_text(file, &loc, asheet);
        Debug(DB_ONEPAGE, "%% text = %d\n", text);
        if (text == 0) {
            return FILE_EOF;
        }
        Debug(DB_ONEPAGE, "%% text = (%s)\n", text);
        Debug(DB_ONEPAGE, "%% loc.pl_line = %d\n", loc.pl_line);
        Debug(DB_ONEPAGE, "%% loc.pl_col = %d\n", loc.pl_col);
        Debug(DB_ONEPAGE, "%% loc.pl_new_line = %d\n",loc.pl_new_line);
        Debug(DB_ONEPAGE, "%% loc.pl_new_col = %d\n", loc.pl_new_col);
        if (text[0] != 0) {
            switch (loc.pl_col) {
            /* fprintf(outfd, "(%s\\n) print flush\n", text); */
            case 0: putc('0', outfd);
                break;
            case 1: fprintf(outfd, "mp_a_x");
                break;
            default: fprintf(outfd, "%d mp_a_x mul", loc.pl_col);
                break;
            }
            fprintf(outfd, " %d moveto (%s) show\n",
                (asheet->sh_plength - loc.pl_line) * fsize,
                text);
        }
        if (loc.pl_new_line == -1) {
            loc.pl_col = loc.pl_new_col;
            return FILE_MORE;
        }
        loc.pl_line = loc.pl_new_line;
        loc.pl_col = loc.pl_new_col;
    }
    return FILE_MORE;
} /* text_onepage */



static char *
mp_get_text(infile, locp, asheet)
 FILE *infile;
 struct pageloc *locp;
 struct sheet *asheet;
{
    int gathering;
    int tabcnt;
    int ichr;
    static int prevchar=0;
    char *textp;

    textp = text;
    locp->pl_new_line = locp->pl_line;
    locp->pl_new_col = locp->pl_col;

    gathering = 1;
    while (gathering) {
        if (prevchar) {
            ichr = prevchar;
            prevchar = 0;
        }
        else
            ichr = fgetc(infile);
        Debug(DB_GETLINE, "%%called fgetc ichr = %d", ichr);
        Debug(DB_GETLINE, "(%d)\n", EOF);
        /*
         * this prevents nulls in the input from confusing the
         * program logic with truncated strings
         */
        if (ichr == 0) {
            ichr = 1;
        }
        switch (ichr) {
        case EOF:
            gathering = 0;
            return 0;
            break;
        case '\n':
            locp->pl_new_line += 1;
            locp->pl_new_col = opt_indent;
            gathering = 0;
            break;
        case '\r':
            locp->pl_new_col = opt_indent;
            gathering = 0;
            break;
        case '\b':
            locp->pl_new_col -= 1;
            if (locp->pl_new_col < opt_indent) {
                locp->pl_new_col = opt_indent;
            }
            gathering = 0;
            break;
        case '\f':
            locp->pl_new_line = -1;
            gathering = 0;
            break;
        case '\t':
            tabcnt = 8 - ((locp->pl_new_col-opt_indent) % 8);
            locp->pl_new_col += tabcnt;
            gathering = 0;
            break;
/*
 *        case ' ':
 *            locp->pl_new_col += 1;
 *            gathering = 0;
 *            break;
 */
        default: /* keep on gathering if it fits on the line ... */
            if (opt_fold &&
                (locp->pl_new_col >= asheet->sh_cwidth)) {
                prevchar = ichr;
                gathering = 0;
                locp->pl_new_line += 1;
                locp->pl_new_col = opt_indent;
                break;
            }
            if (ichr == ')' || ichr == '(' || ichr == '\\') {
                *textp++ = '\\';
                *textp++ = ichr;
            }
            else if (ichr >= ' ' && ichr <= '~') {
                *textp++ = ichr;
            } else {
                *textp++ = '\\';
                *textp++ = '2';
                *textp++ = '7';
                *textp++ = '7';
            }
            locp->pl_new_col += 1;
            break;
        }
    }
    *textp = 0;
    /*
     * remove any spaces at the front of the text string by
     * "converting" it to a position change
     */
    textp = text;
    while (*textp && *textp == ' ') {
        /*
         * this affects the starting position of this text string
         * (not the next)
         */
        locp->pl_col += 1;
        textp++;
    }
    return textp;
} /* mp_get_text */

