/*
 * DEVICE dependent, font independent
 */

#include	"defs.h"
#include	"emit.h"
#include	"global.h"
#include	"ps.h"

/* define for "optimal" relative postioning, rather
   than absolute.  Relative can reduce size of postcript
   output 20% (and reduce print time by almost as much */
#define	USERELPOS	1

int hconvresolution, vconvresolution;

int ordinal = 0;		/* page's position in the page sequence */
BOOLEAN epsf = FALSE;

int hconv, vconv;		/* converts DVI units to pixels */
int ps_h = 0;			/* current h on device */
int ps_v = 0;			/* current v on device */
int *ps_move;

extern int num, den;

#define Letter		0
#define Legal		1
#define Tabloid		2
#define B4		3
#define B5		4
#define A5		5
#define A4		6
#define A3		7
#define Envelope	8

struct {
    char	*size;		/* paper size */
    char	*command;	/* paper command */
    int		width;		/* paper width */
    int		height;		/* paper height */
} paperinfo[] = {
    {"Letter",	"letter",	612, 792},
    {"Legal",	"legal",	612, 1008},
    {"Tabloid",	"tabloid",	792, 1224},
    {"B4",	"b4", 		709, 1001},
    {"B5",	"b5",		499, 709},
    {"A5",	"a5",		420, 595},
    {"A4",	"a4",		595, 842},
    {"A3",	"a3",		842, 1191},
    {"Letter",	"envelope",	612, 792}
    };

#ifndef	PAPERTYPE
#define	PAPERTYPE	Letter
#endif
#ifndef LARGEAREA
#define LARGEAREA	TRUE
#endif

int paper = PAPERTYPE;
int large = LARGEAREA;
BOOLEAN manualfeed = FALSE;
BOOLEAN landscape = FALSE;

dev_arg(option, c)
char option;
char *c;
{
    switch (option) {
    case 'o':
	if (!ChkOpt(c))
	    Fatal("%s is an invalid option\n", c);
	break;
    }
}

/*-->ChkOpt*/   /* check a user supplied option for validity */
#define ISOPT(s) (strcmp(str,s)==0)

ChkOpt(str)
char *str;
{
    if (ISOPT("landscape")) {
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("manualfeed")) {
	manualfeed = TRUE;
	return TRUE;
    } else if (ISOPT("large")) {
	large = TRUE;
	return TRUE;
    } else if (ISOPT("small")) {
	large = FALSE;
	return TRUE;
    } else if (ISOPT("letter")) {
	paper = Letter;
	return TRUE;
    } else if (ISOPT("note")) {
	paper = Letter;
	large = FALSE;
	return TRUE;
    } else if (ISOPT("legal")) {
	paper = Legal;
	return TRUE;
    } else if (ISOPT("tabloid")) {
	paper = Tabloid;
	return TRUE;
    } else if (ISOPT("a3")) {
	paper = A3;
	return TRUE;
    } else if (ISOPT("a4")) {
	paper = A4;
	return TRUE;
    } else if (ISOPT("a5")) {
	paper = A5;
	return TRUE;
    } else if (ISOPT("b4")) {
	paper = B4;
	return TRUE;
    } else if (ISOPT("b5")) {
	paper = B5;
	return TRUE;
    } else if (ISOPT("letterlandscape")) {
	paper = Letter;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("notelandscape")) {
	paper = Letter;
	large = FALSE;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("legallandscape")) {
	paper = Legal;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("tabloidlandscape")) {
	paper = Tabloid;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("a3landscape")) {
	paper = A3;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("a4landscape")) {
	paper = A4;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("a5landscape")) {
	paper = A5;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("b4landscape")) {
	paper = B4;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("b5landscape")) {
	paper = B5;
	landscape = TRUE;
	return TRUE;
    } else if (ISOPT("envelope")) {
	paper = Envelope;
	manualfeed = TRUE;
	landscape = TRUE;
	return TRUE;
    }

    return FALSE;
}

dev_init()
{
    initcolor();
}

dev_prologue()
{
    if (epsf && ordinal > 1) {
	Warning("We cannot make it EPSF.");
	epsf = FALSE;
    }
    if (!epsf)
	EMIT(outfp, "%%!PS-Adobe-3.0\n");
    else
	EMIT(outfp, "%%!PS-Adobe-3.0 EPSF-3.0\n");
    EMIT(outfp, "%%%%Creator: %s\n", G_progname);
    EMIT(outfp, "%%%%Title: %s\n", dvifilename);
    if (!epsf)
	EMIT(outfp, "%%%%Pages: %d\n", ordinal);
    EMIT(outfp, "%%%%BoundingBox: 0 0 %d %d\n",
	 paperinfo[paper].width, paperinfo[paper].height);
    /*EMIT(outfp, "%%%%DocumentPaperSizes: %s\n", paperinfo[paper].size);*/
    if (!epsf)
	EMIT(outfp, "%%%%Orientation: %s\n",
	     (landscape?"Landscape":"Portrait"));
    EMIT(outfp, "%%%%EndComments\n");
    /* include file given in fontdesc */
    do_include();
    dev_outbangspecials();
    EMIT(outfp, "%%%%EndProlog\n");
}

dev_setup()
{
    hconvresolution = resolution;
    vconvresolution = resolution;
    hconv = DoConv(num, den, mag, hconvresolution);
    vconv = DoConv(num, den, mag, vconvresolution);
#ifdef DEBUG
    if (Debug) {
	(void)fprintf(stderr, "hconv = %d\n", hconv);
	(void)fprintf(stderr, "vconv = %d\n", vconv);
    }
#endif
    ordinal = 0;

    EMIT(outfp, "%%%%BeginSetup\n");
    if (!epsf)
	EMIT(outfp, "%%%%Feature: *ManualFeed %s\n",
	     (manualfeed?"True":"False"));
    EMIT(outfp, "%%%%Feature: *Resolution %d\n", resolution);
    EMIT(outfp, "TeXDict begin\n");
    if (!epsf) {
	EMIT(outfp, "%%%%PaperSize: %s\n", paperinfo[paper].size);
	if (manualfeed)
	    EMIT(outfp, "@manualfeed\n");
	if (landscape)
	    EMIT(outfp, "@landscape\n");
	EMIT(outfp, "@%s\n", (large?"large":"small"));
	EMIT(outfp, "@%s\n", paperinfo[paper].command);
	if (ncopies > 1)
	    EMIT(outfp, "%d COPIES\n", ncopies);
    } else
	EMIT(outfp, "@%ssize\n", paperinfo[paper].command);
}

dev_endsetup()
{
    if (useifont())
	ifont_setup();
    /* setup file given in fontdesc */
    do_setup();
    EMIT(outfp, "%d %.3f START\n", resolution, (float)mag/1000);
    EMIT(outfp, "%%%%EndSetup\n");
}

dev_finish()
{
    end_string();
    EMIT(outfp,"%%%%Trailer\n");
    EMIT(outfp,"end\n");
    EMIT(outfp,"userdict /end-hook known{end-hook}if\n");
    EMIT(outfp,"%%%%EOF\n");
}

#define	DF_ALWAYS	0
#define	DF_SPEC		1
#define	DF_PSMOD	2
#define	DF_PSIFONT	3
#define	DF_COLOR	4

dev_devfilekind(kind)
char *kind;
{
    if (STREQ(kind, "special"))
	return DF_SPEC;
    else if (STREQ(kind, "psmod"))
	return DF_PSMOD;
    else if (STREQ(kind, "psifont"))
	return DF_PSIFONT;
    else if (STREQ(kind, "color"))
	return DF_COLOR;
    else
	return DF_ALWAYS;
}

dev_devfileif(kind)
int kind;
{
    extern BOOLEAN usesspecial;
    extern BOOLEAN usespsmod;
    extern BOOLEAN usescolor;

    if (kind == DF_SPEC)
	return usesspecial;
    else if (kind == DF_PSMOD)
	return usespsmod;
    else if (kind == DF_PSIFONT)
	return useifont();
    else if (kind == DF_COLOR)
	return usescolor;
}


/*
 * each ps font is assigned a unique numbler
 */
static DEV_FONT ps_curf;	/* current font on device */
static char psfnamebuf[256];
static int devicefont = 0;

char *
psfname(psf)
int psf;
{
    (void)sprintf(psfnamebuf, "f%d", (unsigned int)psf);
    return psfnamebuf;
}

dev_newdevfont()
{
    return devicefont++;
}

dev_initpage()
{
    ps_h = ps_v = 0;
    dev_initfont();
}

dev_prebop(count)
int count;
{
    extern int pagenum ;

    ++ordinal;
    pagenum = count;
    bopcolor(FALSE);
}

dev_bop(count)
int count;
{
    end_string();
    EMITC('\n');
    if (!epsf)
	EMIT(outfp, "%%%%Page: %d %d\n", count, ++ordinal);
    EMIT(outfp, "BP\n");
    bopcolor(TRUE);
}

dev_eop()
{
    end_string();
    EMIT(outfp, "EP\n");
}

float
dev_fontmag(fe)
struct font_entry *fe;
{
    return (((float)fe->s/(float)fe->d)
#ifdef USEGLOBALMAG
	    * ((float)mag/1000.0)
#endif
	    );
}

dev_initfont()
{
    ps_curf = DEV_NULLFONT;
}

dev_setfont(psf)
int psf;
{
    if (ps_curf != psf) {
	end_string();
	EMIT(outfp, "%s SF\n", psfname(psf));
	ps_curf = psf;
    }
}

dev_setposn(x, y)	/* output a positioning command */
int x, y;
{
    int rx;
#ifdef USERELPOS
    if (y == ps_v) {	/* use relative movement if just moving horizontally */
	if (x != ps_h) {
	    if ((rx=pixround(x-ps_h,hconv)) != 0) {
		end_string();
		EMITN(rx);
		EMITS("r ");
		ps_h += rx*hconv;
	    }
	}
    } else {
#endif
	end_string();
	EMITN(rx=pixround(x,hconv));
	EMITN(pixround(y,vconv));	/* ry */
	EMITS("p ");
	ps_h = rx*hconv;	/* must know where device "really" is horizontally, for rel. posning. */
	ps_v = y;		/* but we always use direct positioning for vertical movement */
#ifdef USERELPOS
    }
#endif
}

dev_setposn_abs(x, y)	/* output a positioning command */
int x, y;
{
    int rx;

    end_string();
    EMITN(rx=pixround(x,hconv));
    EMITN(pixround(y,vconv));	/* ry */
    EMITS("p ");
    ps_h = rx*hconv;	/* must know where device "really" is horizontally, for rel. posning. */
    ps_v = y;		/* but we always use direct positioning for vertical movement */
}

dev_setrule(a, b)
int a, b;
{
    end_string();
    EMITN(pixround(b,hconv));	/* width */
    EMITN(pixround(a,vconv));	/* height */
    EMITS("ru\n");
}

char *endscom = "s";
char *endvscom = "rs";
char *endstrcom;

dev_dir(d)
int d;
{
    end_string();
    if (d == HOR) {
	ps_move = &ps_h;
	endstrcom = endscom;
    } else if (d == VER) {
	ps_move = &ps_v;
	endstrcom = endvscom;
    } else
	Fatal("direction %d not supported", d);
}

BOOLEAN instring = FALSE;
BOOLEAN vertstring = FALSE;

begin_string()
{
    if (!instring) {
	instring = TRUE;
	EMITC('(');
    }
}

begin_string_v()
{
    if (!instring) {
	instring = TRUE;
	vertstring = TRUE;
	EMITC('(');
    }
}

end_string()
{
    if (instring) {
	instring = FALSE;
	if (vertstring) {
	    EMITS(")s\n");
	    vertstring = FALSE;
	} else
	    EMIT(outfp, ")%s\n", endstrcom);
    }
}

/*-->DoConv*/
/*********************************************************************/
/********************************  DoConv  ***************************/
/*********************************************************************/
/*
	1 sp = (num/den)*10^(-5) cm
	  (num = 254*10^5, den = 7227*2^16)
	1 inch = 254*10^(-2) cm
	Therefore, 1 sp = conv pixel-unit
		   return-value sp = 1 pixel-unit
*/

int DoConv(num, den, mag, convResolution)
int num, den, mag, convResolution;
{
    float conv;
    conv = ((float)num/(float)den) *
#ifdef USEGLOBALMAG
	   ((float) mag/1000.0) *
#endif
	   ((float)convResolution/254000.0);
    return (int)(1.0 / conv + 0.5);
}

int
pixround(x, conv)	/* return rounded number of pixels */
int x;			/* in DVI units     */
int conv;		/* conversion factor */
{
    return (int)((x + (conv >> 1)) / conv);
}
