/* 
 * this code taken basically verbatim (changes and deletions only where
 * necessary) from the xmgr package by Paul J. Turner.  It looks like he
 * got if from somewhere else too, so credit to those folks as well.
 */

/* 
 *
 * driver for postscript printer
 *
 * courtesy of:
 *
 * Jim Hudgens
 * hudgens@ray.met.fsu.edu
 *
 * Further modifications by,
 * Ole Holm Nielsen
 * ohnielse@ltf.dth.dk
 *
 */

#include <stdio.h>
#include <ctype.h>
#include "version.h"

#define MAX_BUF_LEN 128

static void putps();
static int isoneof();
static void stripspecial ();

/* postscript page at scale = 0.25 */

/*
 * the following defines are tuned for our HP LaserJet IV
 * and may need adjustment for other printers
 */

#define PSXMIN 100
#define PSXMAX 2350
#define PSYMIN 100
#define PSYMAX 3045
#define DXPS 2250
#define DYPS 2945
#define CHARS 1.8

#define MINCOLOR 0
#define PSMAXPAT 30
#define MAXLINEWIDTH 9


static int psxmin = PSXMIN;
static int psxmax = PSXMAX;
static int psymin = PSYMIN;
static int psymax = PSYMAX;
static int psdx = DXPS;
static int psdy = DYPS;
static int pscolor = -1;
static int pslinewidth = -1;
static int psdmode;
static int pspattern = 0;
static int psfont = 0;
static double pscharsize = 1.7;
static int pslinestyle;
static char *fname;

/* reduced from 1000 to 800 PJT */

#define MAXPATHLEN 800		/* MAXPATHLEN points in a path between
				 * strokes */
static int   pathlength = 0;
static FILE *psout;

static int   prevx = 99999, 
             prevy = 99999, 
	     prevmode;

static void stroke()
{
    if (pathlength) {
	 fprintf(psout, "stroke\n");
	prevx = 99999;
	prevy = 99999;
	pathlength = 0;
    }
}

int pssetmode(mode, ps_fp)
    int    mode;
    FILE   *ps_fp;
{
    if (mode % 2) {
       psout = ps_fp;
       if (psout == NULL) 
           return 0;
    }

    switch (mode) {

    case 3:			/* EPS   portrait */
	pscharsize = CHARS;
        psxmin = PSXMIN;
	psxmax = PSXMAX;
	psymin = PSYMIN;
	psymax = PSYMAX;
	psdx = DXPS;
	psdy = DYPS;
	
	break;

    case 1:			/* EPS landscape */
	pscharsize = CHARS;
	psxmin = PSYMIN;
	psxmax = PSYMAX;
	psymin = PSXMIN;
	psymax = PSXMAX;
	psdx = DYPS;
	psdy = DXPS;
	
	break;

    case 2:
    case 4:
        stroke();
        fprintf(psout, "showpage\n");
        fprintf(psout, "%%%%Trailer\n");
        fclose(psout);
 
        break;
    }

    return mode;
}

void drawps(x2, y2, mode)
    int x2, y2, mode;
{
    int xtmp, ytmp;

    if (x2 < 0 || y2 < 0) { 	/* Eliminate garbage on output */
	return;
    } 
    xtmp = x2;
    ytmp = y2;

    if (mode) {
	if (prevmode && xtmp == prevx && ytmp == prevy) {
	    return;		/* previous mode was draw and points are the
				 * same */
	}
	fprintf(psout, "%d %d l\n", xtmp, ytmp);	/* lineto */
    } else {
	/* Avoid excessive moveto */
	if (xtmp == prevx && ytmp == prevy) {
	    return;
	}
	fprintf(psout, "%d %d m\n", xtmp, ytmp);	/* moveto */
    }
    pathlength++;
    prevx = xtmp;
    prevy = ytmp;

    /*
     * Printers have some maximum number of points in a path. See PostScript
     * Language Reference Manual (Red book), p. 261. Hence the fix that
     * follows 
     */

    prevmode = mode;
    if (pathlength > MAXPATHLEN) {
	stroke();
	prevmode = 0;
	fprintf(psout, "%d %d m\n", xtmp, ytmp);	/* moveto */
    }
}

int xconvps(x)
    double x;
{
    return ((int) (psxmin + x));
}

int yconvps(y)
    double y;
{
    return ((int) (psymin + y));
}

int pssetcolor(c)
    int c;
{
    static int		   first_time = 1;
    static unsigned char   red[16], 
		 	   green[16], 
			   blue[16];

    if (first_time) {
       first_time = 0;

    /* white  */ red[0] = 255;  green[0] = 255; blue[0] = 255; 
    /* black  */ red[1] = 0;    green[1] = 0;   blue[1] = 0;
    /* red    */ red[2] = 255;  green[2] = 0;   blue[2] = 0;
    /* green  */ red[3] = 0;    green[3] = 255; blue[3] = 0;
    /* blue   */ red[4] = 0;    green[4] = 0;   blue[4] = 255;
    /* yellow */ red[5] = 255;  green[5] = 255; blue[5] = 0;
    /* brown  */ red[6] = 188;  green[6] = 143; blue[6] = 143;
    /* gray   */ red[7] = 220;  green[7] = 220; blue[7] = 220;
    /* violet */ red[8] = 148;  green[8] = 0;   blue[8] = 211;
    /* cyan   */ red[9] = 0;    green[9] = 255; blue[9] = 255;
    /* magenta*/ red[10] = 255; green[10] = 0;  blue[10] = 211;
    /* orange */ red[11] = 255; green[11] = 138; blue[11] = 0;
    /* b violet*/ red[12] = 114; green[12] = 33; blue[12] = 188;
    /* maroon */ red[13] = 103; green[13] = 7;   blue[13] = 72;
    /* turq   */ red[14] = 72;  green[14] = 209; blue[14] = 204;
    /* f green*/ red[15] = 85;  green[15] = 192; blue[15] = 52;

    }
    
    stroke();
    if (c != pscolor) {
	if (c >= 0) {
	    fprintf(psout, "%f %f %f setrgbcolor\n", red[c] / 255.0, 
                                                     green[c] / 255.0, 
					             blue[c] / 255.0);
	    pscolor = 1;
	}
    }
    pscolor = c;
    return c;
}

int pssetlinewidth(c)
    int c;
{
    stroke();
    if (c != pslinewidth) {
	c = c % (MAXLINEWIDTH + 1);
	if (c == 1)
	    fprintf(psout, "1 setlinewidth\n");
	else
	    fprintf(psout, "%d setlinewidth\n", (int) (3.5 * c + 0.51));
    }
    pslinewidth = c;
    return c;
}

int pssetlinestyle(style)
    int style;
{
    stroke();
    if (style == pslinestyle) {
	return (pslinestyle);
    }
    switch (style) {
    case 1:			/* solid */
	fprintf(psout, "[] 0 setdash\n");
	break;
    case 2:			/* dotted */
	fprintf(psout, "[4 8] 0 setdash\n");
	break;
    case 3:			/* long dash */
	fprintf(psout, "[20 20] 0 setdash\n");
	break;
    case 4:			/* short dash */
	fprintf(psout, "[40 20] 0 setdash\n");
	break;
    case 5:			/* dot-dashed */
	fprintf(psout, "[40 20 12 20] 0 setdash\n");
	break;
    }
    return (pslinestyle = style);
}

char pscurfont[MAX_BUF_LEN] = "/Times-Roman findfont \n60 scalefont\n setfont";
int psfontsize = 60;

void pssetfont(n)
    int n;
{
    if (psfont == n) {
	return;
    }
    switch (n) {
    case 0:
	sprintf(pscurfont, "/Times-Roman findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 1:
	sprintf(pscurfont, "/Times-Bold findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 2:
	sprintf(pscurfont, "/Times-Italic findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 3:
	sprintf(pscurfont, "/Times-BoldItalic findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 4:
	sprintf(pscurfont, "/Helvetica findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 5:
	sprintf(pscurfont, "/Helvetica-Bold findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 6:
	sprintf(pscurfont, "/Helvetica-Oblique findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 7:
	sprintf(pscurfont, "/Helvetica-BoldOblique findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 8:
	sprintf(pscurfont, "/Courier findfont \n%d scalefont\n setfont", psfontsize);
        break; 
    case 9:
	sprintf(pscurfont, "/Courier-Bold findfont \n%d scalefont\n setfont", psfontsize);
        break; 
    case 10:
	sprintf(pscurfont, "/Courier-Oblique findfont \n%d scalefont\n setfont", psfontsize);
        break; 
    case 11:
	sprintf(pscurfont, "/Courier-BoldOblique findfont \n%d scalefont\n setfont", psfontsize);
        break; 
    case 12:
	sprintf(pscurfont, "/Symbol findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 13:
	sprintf(pscurfont, "/Symbol findfont \n%d scalefont\n setfont", psfontsize);
	break;
    case 14:
	sprintf(pscurfont, "/Symbol findfont \n%d scalefont\n setfont", psfontsize);
	break;
    }

    fprintf(psout, "%s\n", pscurfont);
    psfont = n;
}

void pssetfontsize(size)
    double size;
{
    static double	prev_size;
    int sf = psfont;

    if (size == prev_size)
       return;


    psfontsize = (int) (size * 4);	/* to account for the 0.25 scaling */
    psfont = -1;
    pssetfont(sf);
}

static void escape_paren(s)
    char *s;
{
    char t[256];
    int i, cnt = 0;
    for (i = 0; i < strlen(s); i++) {
	if (s[i] == '(' || s[i] == ')') {
	    t[cnt++] = '\\';
	}
	t[cnt++] = s[i];
    }
    t[cnt] = 0;
    strcpy(s, t);
}

void dispstrps(x, y, rot, s, just, fudge)
    int x, y, rot, just, fudge;
    char *s;
{
    char tmpstr[256];

    stroke();
    if (psfontsize == 0 || s == NULL || strlen(s) == 0) {
	return;
    }
    fprintf(psout, "%d %d m\n", x, y);
    fprintf(psout, "gsave\n");
    fprintf(psout, "%d %d translate\n", x, y);
    fprintf(psout, "%d rotate\n", rot);
    if (fudge) {
	fprintf(psout, "%d 0  m\n", psfontsize / 4);
    } else {
	fprintf(psout, "0 0  m\n");
    }
    switch (just) {
    case 0:
	break;
    case 1:
	stripspecial(s, tmpstr);
	escape_paren(tmpstr);
	fprintf(psout, "(%s) RJ\n", tmpstr);
	break;
    case 2:
	stripspecial(s, tmpstr);
	escape_paren(tmpstr);
	fprintf(psout, "(%s) CS\n", tmpstr);
	break;
    }
    putps(s);
    fprintf(psout, "grestore\n");
    fprintf(psout, "newpath\n");
}

static void putps(s)
    char *s;
{
    int i, slen = strlen(s), curcnt = 0;
    int underline = 0, offset = 0;
    double saves = psfontsize / 60.0, scale = psfontsize / 60.0;
    char curstr[256];
    int upperset = 0;
    int symfont = 0;

    if (psfont == 9) {
	symfont = 1;
	upperset = 0x80;
    } else {
	symfont = 0;
	upperset = 0;
    }
    for (i = 0; i < slen; i++) {
	if (s[i] == '-' && isdigit(s[i + 1])) {
	    /* s[i] = 0261; */
	} else if (s[i] == '\\' && isdigit(s[i + 1])) {
	    curstr[curcnt] = 0;
	    if (curcnt >= 1) {
		fprintf(psout, "(%s) show\n", curstr);
	    }
	    curcnt = 0;
	    if (symfont) {
		symfont = 0;
		upperset = 0;
	    }
	    pssetfont(s[i + 1] - '0');
	    if (psfont == 9) {
		symfont = 1;
		upperset = 0x80;
	    }
	    i++;
	    continue;
	} else if (s[i] == '(' || s[i] == ')') {
	    curstr[curcnt++] = '\\';
	} else if (s[i] == '\\' && isoneof(s[i + 1], "cCbxsSNuU+-")) {
	    switch (s[i + 1]) {
	    case 'x':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		if (symfont == 0) {
		    symfont = 1;
		    upperset = 0x80;
		}
		pssetfont(10);
		i++;
		break;
	    case 's':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		pssetfontsize(scale = 0.6 * saves);
		offset -= psfontsize / 2;
		fprintf(psout, "0 %d rmoveto\n", -(psfontsize / 2));
		i++;
		break;
	    case 'S':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		pssetfontsize(scale = 0.6 * saves);
		offset += psfontsize;
		fprintf(psout, "0 %d rmoveto\n", psfontsize);
		i++;
		break;
	    case 'N':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		scale = saves;
		pssetfontsize(scale);
		fprintf(psout, "0 %d rmoveto\n", -offset);
		offset = 0;
/*
		fprintf(psout, "0 %d rmoveto\n", psfontsize);
*/
		i++;
		break;
	    case 'b':
		i++;
		break;
	    case 'c':
		upperset = 0x80;
		i++;
		break;
	    case 'C':
		upperset = 0;
		i++;
		break;
	    case 'u':
		underline = 1;
		i++;
		break;
	    case 'U':
		underline = 0;
		i++;
		break;
	    case '-':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		scale -= 0.2;
		if (scale < 0.2) {
		    scale = 0.2;
		}
		pssetfontsize(scale);
		i++;
		break;
	    case '+':
		curstr[curcnt] = 0;
		if (curcnt >= 1) {
		    fprintf(psout, "(%s) show\n", curstr);
		}
		curcnt = 0;
		scale += 0.2;
		pssetfontsize(scale);
		i++;
		break;
	    }
	    continue;
	} else if (s[i] == '\\' && s[i + 1] == '\\') {
	    curstr[curcnt++] = '\\';
	    curstr[curcnt++] = s[i];
	    i++;
	    continue;
	}
	curstr[curcnt++] = s[i] + upperset;
    }
    curstr[curcnt] = 0;
    fprintf(psout, "(%s) show\n", curstr);
}

int pssetpat(k)
    int k;
{
    stroke();
    if (k > PSMAXPAT) {
	k = PSMAXPAT;
    } else if (k < 0) {
	k = 0;
	fprintf(psout, "0.0 setgray\n");
    }
    return (pspattern = k);
}

void psfill(n, px, py)
    int n;
    int px[], py[];

{
    int i;

    stroke();
    drawps(px[0], py[0], 0);
    for (i = 1; i < n; i++) {
	drawps(px[i], py[i], 1);
    }
    fprintf(psout, "closepath\n");
    fprintf(psout, "%f setgray\n", 1.0 - pspattern / (double) PSMAXPAT);
    fprintf(psout, "gsave eofill grestore\n");
    stroke();
    fprintf(psout, "0 setgray\n");
}

void psfillcolor(n, px, py)
    int n;
    int px[], py[];

{
    int i;

    stroke();
    drawps(px[0], py[0], 0);
    for (i = 1; i < n; i++) {
	drawps(px[i], py[i], 1);
    }
    fprintf(psout, "closepath\n");
    fprintf(psout, "gsave eofill grestore\n");
    stroke();
}

void psdrawarc(x, y, r, start, end) 
    int x, y, r, start, end;
{
    stroke();
    fprintf(psout, "%d %d %d %d %d arc\n", x, y, r, start, end);
    fprintf(psout, "stroke\n");
}

void psfillarc(x, y, r, start, end)
    int x, y, r, start, end;
{
    stroke();
    fprintf(psout, "%d %d %d %d %d arc\n", x, y, r, start, end);
    fprintf(psout, "gsave fill grestore\n");
    fprintf(psout, "stroke\n");
}

void psdrawellipse(x, y, xm, ym, start, end)
    int x, y, xm, ym, start, end;
{
    double scalex = (double) xm / (double) ym, scaley = 1.0;

    stroke();
    fprintf(psout, "gsave\n");
    fprintf(psout, "%f %f scale\n", scalex, scaley);
    fprintf(psout, "%d %d %d %d %d arc\n", (int) (x * 1.0 / scalex), 
                                           y, ym, start, end);
    fprintf(psout, "stroke\n");
    fprintf(psout, "grestore\n");
}

void psfillellipse(x, y, xm, ym, start, end)
    int x, y, xm, ym, start, end;
{
    double scalex = (double) xm / (double) ym, scaley = 1.0;

    stroke();
    fprintf(psout, "gsave\n");
    fprintf(psout, "%f %f scale\n", scalex, scaley);
/*
    fprintf(psout, "%d %d %d %d %d arc\n", x, y, ym, start, end);
*/
    fprintf(psout, "%d %d %d %d %d arc\n", (int) (x * 1.0 / scalex), 
                                            y, ym, start, end);
    fprintf(psout, "gsave fill grestore\n");
    fprintf(psout, "stroke\n");
    fprintf(psout, "grestore\n");
}

int psgetextents (x, y)
    int		*x, *y;
{
    *x = psdx;
    *y = psdy;

    return 0;
}

void psleavegraphics(fp)
     FILE *fp;
{
     pssetmode(psdmode + 1, fp);
}

/*           postscript initialization routine  */


int psinitgraphics(dmode, ps_fp)
    int   dmode;
    FILE  *ps_fp;
{
    psdmode = dmode;
    if (!pssetmode(psdmode, ps_fp)) {
	return -1;
    }

    fprintf(psout, "%%!PostScript\n");

    fprintf(psout, "%%%%Creator: Velvet %s\n", VERSION);
    fprintf(psout, "%%%%Title: %s\n", fname);

    fprintf(psout, "%%%%EndComments\n");
    fprintf(psout, "/m {moveto} bind def\n");
    fprintf(psout, "/l {lineto} bind def\n");
    fprintf(psout, "/RJ {\n");
    fprintf(psout, " stringwidth neg exch neg exch\n");
    fprintf(psout, " rmoveto\n");
    fprintf(psout, "} bind def\n");

    fprintf(psout, "/CS {\n");
    fprintf(psout, " stringwidth\n");
    fprintf(psout, " 2 div neg exch 2 div neg exch\n");
    fprintf(psout, " rmoveto\n");
    fprintf(psout, "} bind def\n");

    fprintf(psout, "0.25 0.25 scale\n");
    fprintf(psout, "1 setlinecap\n");

/*
 * rotate if in landscape mode
 */

    if (dmode == 1) {
	fprintf(psout, "%d 0 translate\n", 2 * psymin + psdy);
	fprintf(psout, "90 rotate\n");
    }

    pssetcolor(1);
    pssetlinewidth(1);
    pssetlinestyle(0);
    psfont = -1;
    pssetfont(2);
    return 0;
}

static int isoneof(c, s)
    int c;
    char *s;
{
    while (*s) {
	if (c == *s) {
	    return 1;
	} else {
	    s++;
	}
    }
    return 0;
}

static void stripspecial(s, cs)
    char *s, *cs;
{
    int i, slen = strlen(s), curcnt = 0;

    for (i = 0; i < slen; i++) {
	if (s[i] == '\\' && isdigit(s[i + 1])) {
	    i++;
	} else if (s[i] == '\\' && isoneof(s[i + 1], "cCbxsSNuU+-")) {
	    i++;
	} else if (s[i] == '\\' && s[i + 1] == '\\') {
	    i++;
	} else {
	    cs[curcnt++] = s[i];
	}
    }
    cs[curcnt] = 0;
}


syntax highlighted by Code2HTML, v. 0.9.1