/*
 * $Id: hpgl.trm%v 3.50 1993/07/09 05:35:24 woo Exp $
 */

/* GNUPLOT - hpgl.trm */
/*
 * Copyright (C) 1990   
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted, 
 * provided that the above copyright notice appear in all copies and 
 * that both that copyright notice and this permission notice appear 
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the modified code.  Modifications are to be distributed 
 * as patches to released version.
 *  
 * This software  is provided "as is" without express or implied warranty.
 * 
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *  hpgl, hp7580b, HP Laserjet III
 *
 * AUTHORS
 *  Colin Kelley, Thomas Williams, Russell Lang
 * 
 * send your comments or suggestions to (info-gnuplot@dartmouth.edu).
 * 
 */

/*
 *
 * MODIFIED for expanded HPGL/2 and PCL utilites
 *  Tom Swiler (tom@silica.mse.ufl.edu)
 * 
 */

/*
 * The maximum plot size, in plotter units:
 */

#define HPGL_PUPI	1016	/* Plotter units per inch */
  
#define HPGL_XMAX_A	10000
#define HPGL_YMAX_A	7500
  
#define HPGL_XMAX_B	15200
#define HPGL_YMAX_B	10000

#define HPGL_XMAX	HPGL_XMAX_A
#define HPGL_YMAX	HPGL_YMAX_A
  
#define PCL_XMAX	HPGL_XMAX_A
#define PCL_YMAX	HPGL_YMAX_A-60

/*
 * Tic sizes
 */

#define HPGL_VTIC	(HPGL_YMAX/70)
#define HPGL_HTIC	(HPGL_YMAX/70)

#define PCL_VTIC	(PCL_YMAX/70)
#define PCL_HTIC	(PCL_YMAX/70)

/*
 * Font size for HPGL
 */

#define HPGL_VCHAR	(HPGL_YMAX/100*32/10) /* 3.2% */
#define HPGL_HCHAR	(HPGL_XMAX/100*12/10) /* 1.2% */

/*
 * Font size for HPGL/2
 */

#define HPGL2_DEF_POINT	14	/* Height of font */

#define HPGL2_DEF_PITCH	(3 * 72 / (HPGL2_DEF_POINT * 2))
#define HPGL2_VCHAR	((int) HPGL_PUPI * HPGL2_DEF_POINT / 72)
#define HPGL2_HCHAR	(HPGL2_VCHAR * 2 / 3)

/*
 * Control constants
 */

#define DOWN		0	/* Pen is down */
#define UP 		1	/* Pen is up */
#define UNKNOWN		-10	/* Unknown status for lots of things */

/*
 * For Polyline Encoded, either use base 64 or base 32.
 * Save space with base 64, but get 8-bit characters.
 */

#define HPGL2_BASE64 TRUE

#if HPGL2_BASE64
#define HPGL2_BITS 6
#define HPGL2_LOW_OFFS 63
#define HPGL2_HIGH_OFFS 191
#define HPGL2_MASK 63
#else
#define HPGL2_BITS 5
#define HPGL2_LOW_OFFS 63
#define HPGL2_HIGH_OFFS 95
#define HPGL2_MASK 31
#endif

/*
 * Data structures for options
 */

struct HPGL2_font_str {
    char           *compare,
                   *name;
    int             symbol_set,
                    spacing;
    double          pitch,
                    height;
    int             posture,
                    stroke_weight,
                    typeface;
};

struct PCL_mode_str {
    char           *compare,
                   *name,
                   *command;
    int             xmax,
                    ymax;
};

/*
 * The default font goes first.  Although it is the ugliest,  the
 * stick font is probably supported by the most devices, so it
 * becomes the default.
 */

static struct HPGL2_font_str GPFAR HPGL2_font_table[] = {
	    {"u$nivers", "univers", 277, 1, 0.0, HPGL2_DEF_POINT, 0, 0, 4148},
		 {"s$tick", "stick", 277, 0, HPGL2_DEF_PITCH, 0.0, 0, 0, 48},
	   {"c$g_times", "cg_times", 277, 1, 0.0, HPGL2_DEF_POINT, 0, 0, 4101}
};

#define HPGL2_FONTS (sizeof(HPGL2_font_table) / sizeof (struct HPGL2_font_str))
static struct HPGL2_font_str *HPGL2_font = &HPGL2_font_table[0];

/*
 * The default mode goes first.  Landscape style plots are probably the
 * most compatable with other HPGL devices.
 */

static struct PCL_mode_str GPFAR PCL_mode_table[] = {
	      {"l$andscape", "landscape", "\033&l1O", PCL_XMAX, PCL_YMAX},
		 {"p$ortrait", "portrait", "\033&l0O", PCL_YMAX, PCL_XMAX}
};

#define PCL_MODES (sizeof(PCL_mode_table) / sizeof (struct PCL_mode_str))
static struct PCL_mode_str *PCL_mode = &PCL_mode_table[0];

/*
 * Various line types and widths to distinguish data sets
 */

static char    *HPGL2_lt[] = {"", "4,2", "5,2", "6,2", "7,2", "8,2"},
               *HPGL2_pw[] = {".15", ".12", ".08"};

#define HPGL2_LINETYPES (sizeof(HPGL2_lt) / sizeof(char *))
#define HPGL2_PENWIDTHS (sizeof(HPGL2_pw) / sizeof(char *))

/*
 * Static variables to keep track of where we are, etc.
 */

static int      HPGL_ang = 0,
                HPGL_x = UNKNOWN,
                HPGL_y = UNKNOWN,
                HPGL_penstate = UNKNOWN,
                HPGL_pentype = UNKNOWN,
                HPGL2_in_pe,
                HPGL2_lost;

/*
 * The subroutines, grouped by function for different versions.
 */

HPGL2_options ()
{
	extern double real();
    struct termentry *t = &term_tbl[term];
    int             i;
    double          point_size;
    char	   tmp_options[MAX_ID_LEN];
    if (!END_OF_COMMAND) {
	for (i = 0; i < HPGL2_FONTS &&
	     !almost_equals (c_token, HPGL2_font_table[i].compare); i++);
	if (i < HPGL2_FONTS) {
	    HPGL2_font = &HPGL2_font_table[i];
	} else
	    int_error ("expecting font: stick, cg_times, or univers", c_token);
	c_token++;
	if (!END_OF_COMMAND) {
	    if ((point_size = real(&token[c_token].l_val)) > 0.0) {
		t->v_char = (int) HPGL_PUPI *point_size / 72;
		t->h_char = t->v_char * 2 / 3;
		if (HPGL2_font->spacing)
		    HPGL2_font->height = point_size;
		else
		    HPGL2_font->pitch = 72 * 3 / (point_size * 2);
	    } else
		int_error ("expecting font point size: real number",c_token);
	    c_token++;
	}
    }
    sprintf (tmp_options, " %s", HPGL2_font->name);
    strcat(term_options,tmp_options);
    if (HPGL2_font->spacing){
	sprintf (tmp_options, " %lf",
		  HPGL2_font->height);
	strcat(term_options,tmp_options);
    }else{
	sprintf (tmp_options, " %lf",
		  HPGL2_font->pitch);
        strcat(term_options,tmp_options);
    }
}

PCL_options ()
{
    int             i;
    if (!END_OF_COMMAND) {
	for (i = 0; i < PCL_MODES &&
	     !almost_equals (c_token, PCL_mode_table[i].compare); i++);
	if (i < PCL_MODES)
	    PCL_mode = &PCL_mode_table[i];
	else
	    int_error ("expecting mode: portrait or landscape", c_token);
	c_token++;
    }
    sprintf (term_options, " %s", PCL_mode->name);
    HPGL2_options ();
}

HPGL_init ()
{
}

HPGL2_init ()
{
}

PCL_init ()
{
    struct termentry *t = &term_tbl[term];
/*
 * Reset printer, set to one copy, orientation of user's choice.
 * Make the change to the new orientation all at once.
 */
    fprintf (outfile, "\033E\033&l1X%s\n", PCL_mode->command);
    t->xmax = PCL_mode->xmax;
    t->ymax = PCL_mode->ymax;
}

HPGL_graphics()
{
	fputs("\033.Y\n\033.I81;;17:\033.N;19:\033.M500:\n",outfile);
/*	       1
	1. enable eavesdropping
*/
	fprintf(outfile,
	"IN;\nSC0,%d,0,%d;\nSR%f,%f;\n",
		HPGL_XMAX,HPGL_YMAX,
		((double)(HPGL_HCHAR)*200/3/HPGL_XMAX),
		((double)(HPGL_VCHAR)*100/2/HPGL_YMAX) );
/*	 1    2             3 
	1. reset to power-up defaults
	2. set SCaling
	3. set character size
*/
	HPGL_ang = 0;
}

HPGL2_graphics ()
{
/*
 * IN - Initialize
 * SP - Select pen
 * SD - Set default font
 */
    fprintf (outfile, "INSP1SD1,%d,2,%d,",
	     HPGL2_font->symbol_set, HPGL2_font->spacing);
    if (HPGL2_font->spacing)
	fprintf (outfile, "4,%lf,", HPGL2_font->height);
    else
	fprintf (outfile, "3,%lf,", HPGL2_font->pitch);
    fprintf (outfile, "5,%d,6,%d,7,%d\n", HPGL2_font->posture,
	     HPGL2_font->stroke_weight, HPGL2_font->typeface);
/*
 * Control variables
 */
    HPGL_ang = 0;		/* Horizontal */
    HPGL2_in_pe = FALSE;	/* Not in PE command */
    HPGL2_lost = TRUE;		/* Pen position is unknown */
    HPGL_penstate = UP;		/* Pen is up */
}

PCL_graphics ()
{
/*
 * Enter HPGL/2 graphics mode
 */
    fputs ("\033%0B", outfile);
    HPGL2_graphics ();
}

HPGL_text ()
{
    fputs ("PUSP0;\033.Z\n\0", outfile);
/*	       1 2   3
	1. pen up
	2. park pen
	3. disable eavesdropping
*/
    HPGL_penstate = UP;
}

HPGL2_text ()
{
/*
 * If in Polyline Encoded command, leave Polyline Encoded command
 */
    if (HPGL2_in_pe) {
	fputs (";\n", outfile);
	HPGL2_in_pe = 0;
    }
/*
 * Pen up, park pen
 */
    fputs ("PUSP0;", outfile);
}

PCL_text ()
{
    if (HPGL2_in_pe) {
	fputs (";\n", outfile);
	HPGL2_in_pe = 0;
    }
/*
 * Go into PCL mode and eject the page
 */
    fputs ("\033%1A\033&l0H\n\0", outfile);
}

HPGL_linetype (linetype)
    int             linetype;
{
/* allow for 6 pens */
    linetype = (linetype + 2) % 6 + 1;
/* only select pen if necessary */
    if (HPGL_pentype != linetype) {
	fprintf (outfile, "PU;\nSP%d;\n", linetype);
	HPGL_pentype = linetype;
	HPGL_penstate = UP;
    }
}

HP75_linetype (linetype)
    int             linetype;
{
/* allow for 4 pens */
    linetype = (linetype + 2) % 4 + 1;
/* only select pen if necessary */
    if (HPGL_pentype != linetype) {
	fprintf (outfile, "PU;\nSP%d;\n", linetype);
	HPGL_pentype = linetype;
	HPGL_penstate = UP;
    }
}

HPGL2_linetype (linetype)
    int             linetype;
{
/*
 * If in Polyline Encoded command, leave Polyline Encoded command
 */
    if (HPGL2_in_pe) {
	fputs (";\n", outfile);
	HPGL2_in_pe = 0;
    }
/*
 * Allow for lots of linetypes
 */
    if (linetype >= 0)
	linetype = linetype % (HPGL2_LINETYPES * HPGL2_PENWIDTHS);
    if (linetype != HPGL_pentype) {
	if (linetype >= 0) {
	    fprintf (outfile, "PW%sLT%s",
		     HPGL2_pw[linetype / HPGL2_LINETYPES],
		     HPGL2_lt[linetype % HPGL2_LINETYPES]);
	} else if (linetype == -2)
/*
 * Borders and tics
 */
	    fprintf (outfile, "PW.2LT");
	else if (linetype == -1)
/*
 * Axes and grids
 */
	    fprintf (outfile, "PW.1LT1,.25");
	HPGL_pentype = linetype;
    }
}

HPGL_put_text (x, y, str)
    int             x,
                    y;
    char           *str;
{
    if (HPGL_ang == 1)
	HPGL_move (x + HPGL_VCHAR / 4, y);
    else
	HPGL_move (x, y - HPGL_VCHAR / 4);
    fprintf (outfile, "LB%s\003\n", str);
}

HPGL2_put_text (x, y, str)
    int             x,
                    y;
    char           *str;
{
    struct termentry *t = &term_tbl[term];
/*
 * Position the pen
 */
    if (HPGL_ang == 1)
	HPGL2_move (x + t->v_char / 4, y);
    else
	HPGL2_move (x, y - t->v_char / 4);
/*
 * If in Polyline Encoded command, leave Polyline Encoded command
 */
    if (HPGL2_in_pe) {
	fputs (";\n", outfile);
	HPGL2_in_pe = 0;
    }
/*
 * Print the text string
 */
    fprintf (outfile, "LB%s\003\n", str);
    HPGL2_lost = 1;
}
/*
 * Some early HPGL plotters (e.g. HP7220C) require the
 * Pen Up/Down and Pen (move) Absolute commands to be separate.
 */

HPGL_move (x, y)
    int             x,
                    y;
{
    if (HPGL_x != x || HPGL_y != y) {		/* only move if necessary */
	fprintf (outfile, "PU;PA%d,%d;\n", x, y);
	HPGL_penstate = UP;
	HPGL_x = x;
	HPGL_y = y;
    }
}

HPGL_vector (x, y)
    int             x,
                    y;
{
    if (HPGL_penstate != DOWN) {
	fprintf (outfile, "PD;PA%d,%d;\n", x, y);
	HPGL_penstate = DOWN;
    } else
	fprintf (outfile, "PA%d,%d;\n", x, y);
    HPGL_x = x;
    HPGL_y = y;
}

HPGL2_move (x, y)
    int             x,
                    y;
{
    register int    dx,
                    dy;
    if (HPGL2_in_pe) {
	dx = x - HPGL_x;
	dy = y - HPGL_y;
	fputs ("<", outfile);
    } else {
#if HPGL2_BASE64
	fputs ("PE<", outfile);
#else
	fputs ("PE7<", outfile);
#endif
	if (HPGL2_lost) {
	    dx = x;
	    dy = y;
	    HPGL2_lost = 0;
	    fputs ("=", outfile);
	} else {
	    dx = x - HPGL_x;
	    dy = y - HPGL_y;
	}
	HPGL2_in_pe = 1;
    }
#if HPGL2_EXPLICIT_PD
    if (HPGL_penstate == DOWN)
	HPGL_penstate = UP;
#endif
    HPGL2_encode (dx);
    HPGL2_encode (dy);
    fputs("\n",outfile);
    HPGL_x = x;
    HPGL_y = y;
}

HPGL2_vector (x, y)
    int             x,
                    y;
{
    register int    dx,
                    dy;
    if (HPGL2_in_pe) {
	dx = x - HPGL_x;
	dy = y - HPGL_y;
    } else {
#if HPGL2_BASE64
	fputs ("PE", outfile);
#else
	fputs ("PE7", outfile);
#endif
	if (HPGL2_lost) {
	    dx = x;
	    dy = y;
	    HPGL2_lost = 0;
	    fputs ("=", outfile);
	} else {
	    dx = x - HPGL_x;
	    dy = y - HPGL_y;
	}
	HPGL2_in_pe = 1;
    }
#if HPGL2_EXPLICIT_PD
/*
 * Put the pen down in the current position,
 * relative vector of 0,0.
 */
    if (HPGL_penstate == UP) {
	fputc ((char) HPGL2_HIGH_OFFS, outfile);
	fputc ((char) HPGL2_HIGH_OFFS, outfile);
	HPGL_penstate = DOWN;
    }
#endif
    HPGL2_encode (dx);
    HPGL2_encode (dy);
    fputs("\n",outfile);
    HPGL_x = x;
    HPGL_y = y;
}

/*
 * Routine to encode position in base 32 or base 64 characters
 */

HPGL2_encode (d)
    register int    d;
{
    register int    c;
    if ((d <<= 1) < 0)
	d = 1 - d;
    do {
	c = d & HPGL2_MASK;
	d >>= HPGL2_BITS;
	if (d > 0)
	    fputc ((char) (c + HPGL2_LOW_OFFS), outfile);
	else
	    fputc ((char) (c + HPGL2_HIGH_OFFS), outfile);
    } while (d > 0);
}

int
HPGL_text_angle (ang)
    int             ang;
{
    HPGL_ang = ang;
    if (ang == 1)
/*
 *  Vertical
 */
	fprintf (outfile, "DI0,1;\n");
    else
/*
 * Horizontal
 */
	fprintf (outfile, "DI1,0;\n");
    return TRUE;
}

int
HPGL2_text_angle (ang)
    int             ang;
{
/*
 * If in Polyline Encoded command, leave Polyline Encoded command
 */
    if (HPGL2_in_pe) {
	fputs (";", outfile);
	HPGL2_in_pe = 0;
    }
    if (ang == 1)
/*
 *  Vertical
 */
	fprintf (outfile, "DI0,1");
    else
/*
 * Horizontal
 */
	fprintf (outfile, "DI1,0");
    HPGL_ang = ang;
    return TRUE;
}

HPGL_reset ()
{
/*
 * I am not sure that "PG" is a command in HPGL.
    fputs ("PG;", outfile);
 */
}

HPGL2_reset ()
{
/*
 * Park the pen
 * Advance a page
 * End with ";"
 */
    fputs ("SP0PG;\n", outfile);
}

PCL_reset ()
{
/*
 * Return to PCL mode
 * Printer reset (conditional eject)
 */
    fputs ("\033%0A\033E\n", outfile);
}

HPGL2_justify_text (just)
    int             just;
{
/*
 * If in Polyline Encoded command, leave Polyline Encoded command
 */
    if (HPGL2_in_pe) {
	fputs (";\n", outfile);
	HPGL2_in_pe = 0;
    }
    switch (just) {
    case LEFT:
	fputs ("LO1", outfile);
	break;
    case CENTRE:
	fputs ("LO4", outfile);
	break;
    case RIGHT:
	fputs ("LO7", outfile);
	break;
    default:
	return 0;
    }
    return 1;
}

