/*
 * Fig2dev: Translate Fig code to various Devices
 * Copyright (c) 1991 by Micah Beck
 * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 2015-2020 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies
 * of the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

/*
 * genibmgl.c: convert fig to IBMGL
 *		IBM 6180 Color Plotter with
 *		IBM Graphics Enhancement Cartridge
 * Author E. Robert Tisdale, University of California, 1/92 (edwin@cs.ucla.edu)
 *
 *	adapted from:
 *
 *genpictex.c: PiCTeX driver for fig2dev
 *
 *	Author Micah Beck, Cornell University, 4/88
 *	Color, rotated text and ISO-chars added by Herbert Bauer 11/91
 *	PCL job control option added Brian V. Smith 1/2001
 *	Page size handling, orientation, offset, centering
 *	   added by Glenn Burkhardt, 11/2003.
*/

/* Notes on offsets and scaling:

   When sending files to a PCL5 printer, e.g., HP4000, Lexmark E312,
   the origin of the plot is the origin of the PCL logical page, which
   is inside the physical page.  In order to get pictures centered on
   these printers, an offset of x=-0.25, y=-0.5 is needed.  Other printers
   have different built in offsets (sometimes, none!).

   Not all printers support all fonts; some printers support only the
   HP stick font.  Default font size values have been chosen to make
   the characters very close to the Courier fixed spacing font.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef	HAVE_STRINGS_H
#include <strings.h>
#endif
#include <math.h>

#include "fig2dev.h"	/* includes bool.h and object.h */
//#include "object.h"
#include "messages.h"
#include "pi.h"


static void set_style();

#define		FONTS			35
#define		COLORS			8
#define		PATTERNS		21
#define		DPR		180.0/M_PI	/* degrees/radian	*/
#define		DELTA		M_PI/36.0	/* radians		*/
#define		UNITS_PER_INCH		 1016.0	/* plotter units/inch
						   1 plotter unit = .025 mm
						*/

#define		ISO_A4_HEIGHT		842	/* 297 mm, in 1/72" points */
#define		ISO_A4_WIDTH		595	/* 210 mm, in 1/72" points */
#define		ANSI_A_HEIGHT		792	/* 11 in, in 1/72" points */
#define		ANSI_A_WIDTH		612	/* 8.5 in, in 1/72" points */
#define		SPEED_LIMIT		128.0	/* centimeters/second	*/

#ifdef IBMGEC
static	int	ibmgec		 = true;
#else
static	int	ibmgec		 = false;
#endif

static	bool	pcljcl		 = false;  /* flag to precede IBMGL (HP/GL) output with PCL job control */
static	bool	reflected	 = false;
static	bool	correct_font_size = false;
static	int	fonts		 = FONTS;
static	int	colors		 = COLORS;
static	int	patterns	 = PATTERNS;
static	int	line_color	 = DEFAULT;
static	int	line_style	 = SOLID_LINE;
static	int	fill_pattern	 = DEFAULT;
static	double	dash_length	 = DEFAULT;	/* in pixels		*/
#ifdef A4
static	double	pageheight	 = ISO_A4_HEIGHT;
static	double	pagewidth	 = ISO_A4_WIDTH;
#else
static	double	pageheight	 = ANSI_A_HEIGHT;
static	double	pagewidth	 = ANSI_A_WIDTH;
#endif
static	double	pen_speed	 = SPEED_LIMIT;
static	double	xz		 =  0.0;	/* inches		*/
static	double	yz		 =  0.0;	/* inches		*/

/* Default plottable area, changeable with "-d".  Use ISO-B0 for default
   bounds; the page size parameter can further restrict the plottable view.
*/
static	double	xl		 =  0.0;	/* inches		*/
static	double	yl		 =  0.0;	/* inches		*/
static	double	xu		 = 1456/25.4;	/* inches		*/
static	double	yu		 = 1030/25.4;	/* inches		*/

static	int	pen_number[]	 = { 1, 2, 3, 4, 5, 6, 7, 8, 1};
static	double	pen_thickness[]	 = {.3,.3,.3,.3,.3,.3,.3,.3,.3};

static	int	line_type[]	 =
	   {1, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6,-1,-1,-1,-1,-1,-1,-1,-1};
static	double	line_space[]	 =
	   {.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.5,.3,.3};
static	int	fill_type[]	 =
	   {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 1, 2};
static	double	fill_space[]	 =
	   {.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1};
static	double	fill_angle[]	 =
	   {0,-45,0,45,90,-45,0,45,90,-45,0,45,90,-45,0,45,90, 0, 0, 0, 0};

static	int	standard[]	 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static	int	alternate[]	 = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
static	double	slant[]		 = { 0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0,10,
	     0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0,10, 0, 0};

/* Make font width/height close to Courier fixed.
 */
static	double	wide[]
= {.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,
   .401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,
   .401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401,.401};
static	double	high[]
= {.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,
   .561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,
   .561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561,.561};

/* Map of Postscript font number to HPGL/2 Fonts
 */
static struct {
    int font, italic, bold;
} psfont2hpgl[] =
{
    /* "Times-Roman",			0 */  { 5, 0, 0 },
    /* "Times-Italic",			1 */  { 5, 1, 0 },
    /* "Times-Bold",			2 */  { 5, 0, 3 },
    /* "Times-BoldItalic",		3 */  { 5, 1, 3 },
    /* "AvantGarde-Book",		4 */  { 31, 0, 0 },
    /* "AvantGarde-BookOblique",	5 */  { 31, 1, 0 },
    /* "AvantGarde-Demi",		6 */  { 31, 0, 3 },
    /* "AvantGarde-DemiOblique",	7 */  { 31, 1, 3 },
    /* "Bookman-Light",			8 */  { 47, 0, 0 },
    /* "Bookman-LightItalic",		9 */  { 47, 1, 0 },
    /* "Bookman-Demi",			10 */ { 47, 0, 3 },
    /* "Bookman-DemiItalic",		11 */ { 47, 1, 3 },
    /* "Courier",			12 */ { 3, 0, 0 },
    /* "Courier-Oblique",		13 */ { 3, 1, 0 },
    /* "Courier-Bold",			14 */ { 3, 0, 3 },
    /* "Courier-BoldOblique",		15 */ { 3, 1, 3 },
    /* "Helvetica",			16 */ { 4, 0, 0 },
    /* "Helvetica-Oblique",		17 */ { 4, 1, 0 },
    /* "Helvetica-Bold",		18 */ { 4, 0, 3 },
    /* "Helvetica-BoldOblique",		19 */ { 4, 1, 3 },
    /* "Helvetica-Narrow",		20 */ { 4, 0, 0 },
    /* "Helvetica-Narrow-Oblique",	21 */ { 4, 1, 0 },
    /* "Helvetica-Narrow-Bold",		22 */ { 4, 0, 3 },
    /* "Helvetica-Narrow-BoldOblique",	23 */ { 4, 1, 3 },
    /* "NewCenturySchlbk-Roman",	24 */ { 23, 0, 0},
    /* "NewCenturySchlbk-Italic",	25 */ { 23, 1, 0},
    /* "NewCenturySchlbk-Bold",		26 */ { 23, 0, 3},
    /* "NewCenturySchlbk-BoldItalic",	27 */ { 23, 1, 3},
    /* "Palatino-Roman",		28 */ { 15, 0, 0 },
    /* "Palatino-Italic",		29 */ { 15, 1, 0 },
    /* "Palatino-Bold",			30 */ { 15, 0, 3 },
    /* "Palatino-BoldItalic",		31 */ { 15, 1, 3 },
    /* "Symbol",			32 */ { 52, 0, 0 }, /* Univers */
    /* "ZapfChancery-MediumItalic",	33 */ { 43, 0, 0 },
    /* "ZapfDingbats",			34 */ { 45, 0, 0 },
};

static void
genibmgl_option(char opt, char *optarg)
{
	FILE	*ffp;
	int	 font;
	int	 pattern;

	switch (opt) {
	case 'a':				/* paper size		*/
#ifdef A4
		pageheight	 = ANSI_A_HEIGHT;
		pagewidth	 = ANSI_A_WIDTH;
#else
		pageheight	 = ISO_A4_HEIGHT;
		pagewidth	 = ISO_A4_WIDTH;
#endif
		break;

	case 'c':				/* Graphics Enhancement	*/
		ibmgec		 = !ibmgec;	/* Cartridge emulation	*/
		break;

	case 'd':				/* position and window	*/
		sscanf(optarg, "%lf,%lf,%lf,%lf", &xl,&yl,&xu,&yu);
		/* inches		*/
		break;

	case 'f':				/* user's characters	*/
		if ((ffp = fopen(optarg, "r")) == NULL)
			fprintf(stderr, "Couldn't open %s\n", optarg);
		else
			for (font = 0; font <= fonts; ++font)
				fscanf(ffp, "%d%d%lf%lf%lf",
					&standard[font],  /* 0-4 6-9 30-39 */
					&alternate[font], /* 0-4 6-9 30-39 */
					&slant[font],	  /*   degrees	*/
					&wide[font],	  /*	~1.0	*/
					&high[font]);	  /*	~1.0	*/
		fclose(ffp);
		break;

	case 'k':		/* precede output with PCL job control */
		pcljcl = true;
		break;

	case 'l':				/* user's fill patterns	*/
		if ((ffp = fopen(optarg, "r")) == NULL)
			fprintf(stderr, "Couldn't open %s\n", optarg);
		else
			for (pattern = 0; pattern < patterns; pattern++)
				fscanf(ffp, "%d%lf%d%lf%lf",
					&line_type[pattern],	/*    -1-6   */
					&line_space[pattern],	/*   inches  */
					&fill_type[pattern],	/*    1-5    */
					&fill_space[pattern],	/*   inches  */
					&fill_angle[pattern]);	/*   degrees */
		fclose(ffp);
		break;

	case 'F':		/* use specified font, i.e., SD command */
		correct_font_size = true;
		break;

	case 'G':
	case 'L':				/* language		*/
		break;

	case 'p':				/* user's colors	*/
		{
			FILE	*ffp;
			int		color;
			if ((ffp = fopen(optarg, "r")) == NULL)
				fprintf(stderr, "Couldn't open %s\n", optarg);
			else
				for (color = 0; color <= colors; ++color)
					fscanf(ffp, "%d%lf",
						&pen_number[color], /* 1-8 */
						&pen_thickness[color]);	/* mm */
			fclose(ffp);
		}
		break;

	case 'P':				/* portrait mode	*/
		landscape	 = false;
		orientspec	 = true;	/* user-specified	*/
		break;

	case 'S':				/* select pen velocity	*/
		pen_speed	 = atof(optarg);
		break;

	case 'v':
		reflected	 = true;	/* mirror image		*/
		break;

	case 'x':
		xz = atof(optarg);		/* x offset, inches */
		break;

	case 'y':
		yz = atof(optarg);		/* y offset, inches */
		break;

	case 'z':				/* papersize */
		(void) strcpy (papersize, optarg);
		paperspec = true;		/* user-specified */
		break;

	default:
		put_msg(Err_badarg, opt, "ibmgl");
		exit(1);
	}
}

static double		cpi;			/*	 cent/inch	*/
static double		cpp;			/*	 cent/pixel	*/
static double		wcmpp;			/* centimeter/point	*/
static double		hcmpp;			/* centimeter/point	*/

void
genibmgl_start(F_compound *objects)
{
	(void)		objects;
	int		P1x, P1y, P2x, P2y;
	int		Xll, Yll, Xur, Yur;
	double		Xmin,Xmax,Ymin,Ymax,xoff=0,yoff=0;
	double		height, width, points_per_inch;
	const struct paperdef	*pd;

	if (fabs(mag) < 1.0/2048.0){
	    fprintf(stderr, "|mag| < 1/2048\n");
	    exit(1);
	    }

	if (paperspec) {
	    /* convert ledger (deprecated) to tabloid */
	    if (strcasecmp(papersize, "ledger") == 0)
		strcpy(papersize, "tabloid");

	    for (pd = paperdef; pd->name != NULL; pd++)
		if (strcasecmp (papersize, pd->name) == 0) {
		    pagewidth = pd->width;	/* in points, 1/72" */
		    pageheight = pd->height;
		    strcpy(papersize,pd->name);	/* use the "nice" form */
		    break;
		    }

	    if (pagewidth < 0 || pageheight < 0) {
		fprintf (stderr, "Unknown paper size `%s'\n", papersize);
		exit (1);
		}
	    }

	points_per_inch = 72;
	pagewidth  /= points_per_inch;	/* convert to inches */
	pageheight /= points_per_inch;
	wcmpp = hcmpp = 2.54/points_per_inch;

	if (xl < xu)
	    if (0.0 < xu)
		if (xl < pageheight) {
		    xl	 = (0.0 < xl) ? xl: 0.0;
		    xu	 = (xu < pageheight) ? xu: pageheight;
		    }
		else {
		    fprintf(stderr, "xll >= %.2f\n", pageheight);
		    exit(1);
		    }
	    else {
		fprintf(stderr, "xur <= 0.0\n");
		exit(1);
		}
	else {
	    fprintf(stderr, "xur <= xll\n");
	    exit(1);
	    }

	if (yl < yu)
	    if (0.0 < yu)
		if (yl < pagewidth) {
		    yl	 = (0.0 < yl) ? yl: 0.0;
		    yu	 = (yu < pagewidth) ? yu: pagewidth;
		    }
		else {
		    fprintf(stderr, "yll >= %.2f\n", pagewidth);
		    exit(1);
		    }
	    else {
		fprintf(stderr, "yur <= 0.0\n");
		exit(1);
		}
	else {
	    fprintf(stderr, "yur <= yll\n");
	    exit(1);
	    }

	cpi	 = mag*100.0/sqrt((xu-xl)*(xu-xl) + (yu-yl)*(yu-yl));
	cpp	 = cpi/ppi;

	/* IBMGL start */
	if (pcljcl) {
	    fprintf(tfp,"\033E");
	    if (landscape) fprintf(tfp, "\033&l1O");
	    fprintf(tfp, "\033%%0B");	/* reset and set to HP/GL mode */
	}

	fprintf(tfp, "BP;IN;\n");		/* initialize plotter	*/

	if (!landscape) {			/* portrait mode	*/
	    fprintf(tfp, "RO90;\n");		/* rotate 90 degrees	*/
	    Xll	 = yl*UNITS_PER_INCH;
	    Xur	 = yu*UNITS_PER_INCH;
	    Yll	 = (pageheight - xu)*UNITS_PER_INCH;
	    Yur	 = (pageheight - xl)*UNITS_PER_INCH;
	    height	 = yu - yl;
	    width	 = xu - xl;
	    P1x		 = Xll;
	    P2x		 = Xur;
	    if (reflected)			/* upside-down text	*/
		hcmpp	 = -hcmpp;
	    if (reflected) {			/* reflected */
		P1y	 = Yll;
		P2y	 = Yur;
	    } else {
		P1y	 = Yur;
		P2y	 = Yll;
	    }

	    /* If asked to center, use the plot bounds, and page size
	       to get offsets.
	    */
	    if (center) {
		yoff = (pageheight - (urx - llx)*mag/ppi)/2 - llx*mag/ppi;
		xoff = (pagewidth - (ury - lly)*mag/ppi)/2 - lly*mag/ppi;
	    }
	} else {				      /* landscape mode	*/
	    Xll	 = xl*UNITS_PER_INCH;
	    Yll	 = yl*UNITS_PER_INCH;
	    Yur	 = yu*UNITS_PER_INCH;
	    Xur	 = xu*UNITS_PER_INCH;
	    height	 = xu - xl;
	    width	 = yu - yl;
	    if (reflected) {			/* flipped   or not	*/
		wcmpp	 = -wcmpp;		/* backward text	*/
		P1x	 = Xur;
		P2x	 = Xll;
	    } else {				/* normal		*/
		P1x	 = Xll;
		P2x	 = Xur;
	    }
	    P1y	 = Yur;
	    P2y	 = Yll;

	    /* If asked to center, use the plot bounds, border, and page size
	       to get offsets.
	    */
	    if (center) {
		xoff = (pageheight - (urx - llx)*mag/ppi)/2 - llx*mag/ppi;
		yoff = (pagewidth - (ury - lly)*mag/ppi)/2 - lly*mag/ppi;
	    }
	}

	if (xoff < 0) xoff = 0;
	if (yoff < 0) yoff = 0;

	Xmin	 = -xz - xoff;			/* Inches */
	Ymin	 = yz - yoff;
	Xmax	 = -xz - xoff + height/mag;
	Ymax	 = yz - yoff + width/mag;

	fprintf(tfp, "IP%d,%d,%d,%d;\n",    /* reference points for scaling */
		P1x, P1y, P2x, P2y);
	fprintf(tfp, "IW%d,%d,%d,%d;\n",    /* soft clip limits */
		Xll, Yll, Xur, Yur);

	/* 'SC' maps the coordinates used in the plotting commands to the
	   absolute plotter coordinates (units of .025mm).  This
	   can both translate and scale.  The values given are mapped
	   onto the absolute plotter coords given in the 'IP' command
	*/
	fprintf(tfp, "SC%.4f,%.4f,%.4f,%.4f;\n",
		Xmin,Xmax,Ymin,Ymax);
	if (0.0 < pen_speed && pen_speed < SPEED_LIMIT)
	    fprintf(tfp, "VS%.2f;\n", pen_speed);
}

static void
arc_tangent(double x1, double y1, double x2, double y2, int direction, double *x, double *y)
{
	if (direction) { /* counter clockwise  */
	    *x = x2 - (y2 - y1);
	    *y = y2 + (x2 - x1);
	} else {
	    *x = x2 + (y2 - y1);
	    *y = y2 - (x2 - x1);
	}
}

/*	draw arrow heading from (x1, y1) to (x2, y2)	*/

static void
draw_arrow_head(double x1, double y1, double x2, double y2, double arrowht,
		double arrowwid)
{
	double	x, y, xb, yb, dx, dy, l, sina, cosa;
	double	xc, yc, xd, yd;
	int style;
	double length;

	dx	 = x2 - x1;
	dy	 = y1 - y2;
	l	 = sqrt(dx*dx+dy*dy);
	sina	 = dy/l;
	cosa	 = dx/l;
	xb	 = x2*cosa - y2*sina;
	yb	 = x2*sina + y2*cosa;
	x	 = xb - arrowht;
	y	 = yb - arrowwid/2.0;
	xc	 =  x*cosa + y*sina;
	yc	 = -x*sina + y*cosa;
	y	 = yb + arrowwid/2.0;
	xd	 =  x*cosa + y*sina;
	yd	 = -x*sina + y*cosa;

	/* save line style and set to solid */
	style	 = line_style;
	length	 = dash_length;
	set_style(SOLID_LINE, 0.0);

	fprintf(tfp, "PA%.4f,%.4f;PD%.4f,%.4f,%.4f,%.4f;PU\n",
		xc, yc, x2, y2, xd, yd);

	/* restore line style */
	set_style(style, length);
}

/*
 * set_style - issue line style commands as appropriate
 */
static void
set_style(int style, double length)
{
	if (style == line_style)
	    switch (line_style) {
		case SOLID_LINE:
		    break;

		case DASH_LINE:
		    if (dash_length != length && length > 0.0) {
			dash_length  = length;
			fprintf(tfp, "LT2,%.4f;\n", dash_length*2.0*cpp);
			}
		    break;

		case DOTTED_LINE:
		    if (dash_length != length && length > 0.0) {
			dash_length  = length;
			fprintf(tfp, "LT1,%.4f;\n", dash_length*2.0*cpp);
			}
		    break;
		}
	else {
	    line_style = style;
	    switch (line_style) {
		case SOLID_LINE:
		    fprintf(tfp, "LT;\n");
		    break;

		case DASH_LINE:
		    if (dash_length != length && length > 0.0)
			dash_length  = length;
		    if (dash_length > 0.0)
			fprintf(tfp, "LT2,%.4f;\n", dash_length*2.0*cpp);
		    else
			fprintf(tfp, "LT2,-1.0;\n");
		    break;

		case DOTTED_LINE:
		    if (dash_length != length && length > 0.0)
			dash_length  = length;
		    if (dash_length > 0.0)
			fprintf(tfp, "LT1,%.4f;\n", dash_length*2.0*cpp);
		    else
			fprintf(tfp, "LT1,-1.0;\n");
		    break;
		}
	    }
}

/*
 * set_width - issue line width commands as appropriate
 *		NOTE: HPGL/2 command used
 */
static void
set_width(int w)
{
    static int current_width=-1;

    if (w == current_width) return;

    /* Default line width is 0.3 mm; back off to original xfig pen
       thickness number, and re-size.
    */
    fprintf(tfp, "PW%.1f;\n", w*80/ppi * 0.3);

    current_width = w;
}

/*
 * set_color - issue line color commands as appropriate
 */
static void
set_color(int color)
{
    static	int	number		 = 0;	/* 1 <= number <= 8		*/
    static	double	thickness	 = 0.3;	/* pen thickness in millimeters	*/
	if (line_color != color) {
	    line_color	= color;
	    color	= (colors + color)%colors;
	    if (number != pen_number[color]) {
		number	= pen_number[color];
		fprintf(tfp, "SP%d;\n", pen_number[color]);
		}
	    if (thickness != pen_thickness[color]) {
		thickness  = pen_thickness[color];
		fprintf(tfp, "PW%.4f;\n", pen_thickness[color]);
		}
	    }
}

static void
fill_polygon(int pattern, int color)
{
	if (0 < pattern && pattern < patterns) {
	    int		style;
	    double	length;

	    set_color(color);
	    if (fill_pattern != pattern) {
		fill_pattern  = pattern;
		fprintf(tfp, "FT%d,%.4f,%.4f;", fill_type[pattern],
			fill_space[pattern],
			reflected ? -fill_angle[pattern]: fill_angle[pattern]);
		}
	    /*	  save line style */
	    style	 = line_style;
	    length	 = dash_length;
	    fprintf(tfp, "LT%d,%.4f;FP;\n",
		    line_type[pattern], line_space[pattern]*cpi);
	    /* restore line style */
	    line_style	 = DEFAULT;
	    dash_length	 = DEFAULT;
	    set_style(style, length);
	    }
}

void
arc(double sx, double sy, double cx, double cy, double theta, double delta)
{
	if (ibmgec)
	    if (delta == M_PI/36.0)		/* 5 degrees		*/
		fprintf(tfp, "AA%.4f,%.4f,%.4f;",
			cx, cy, theta*DPR);
	    else
		fprintf(tfp, "AA%.4f,%.4f,%.4f,%.4f;",
			cx, cy, theta*DPR, delta*DPR);
	else {
	    double	alpha;
	    if (theta < 0.0)
		delta = -fabs(delta);
	    else
		delta = fabs(delta);
	    for (alpha = delta; fabs(alpha) < fabs(theta); alpha += delta) {
		fprintf(tfp, "PA%.4f,%.4f;\n",
			cx + (sx - cx)*cos(alpha) - (sy - cy)*sin(alpha),
			cy + (sy - cy)*cos(alpha) + (sx - cx)*sin(alpha));
		}
	    fprintf(tfp, "PA%.4f,%.4f;\n",
		    cx + (sx - cx)*cos(theta) - (sy - cy)*sin(theta),
		    cy + (sy - cy)*cos(theta) + (sx - cx)*sin(theta));
	    }
}

void
genibmgl_arc(F_arc *a)
{
	if (a->thickness != 0 ||
		(ibmgec && 0 <= a->fill_style && a->fill_style < patterns)) {
	    double	x, y;
	    double	cx, cy, sx, sy, ex, ey;
	    double	dx1, dy1, dx2, dy2, theta;

	    set_style(a->style, a->style_val);
	    set_width(a->thickness);
	    set_color(a->pen_color);

	    cx		 = a->center.x/ppi;
	    cy		 = a->center.y/ppi;
	    sx		 = a->point[0].x/ppi;
	    sy		 = a->point[0].y/ppi;
	    ex		 = a->point[2].x/ppi;
	    ey		 = a->point[2].y/ppi;

	    dx1		 = sx - cx;
	    dy1		 = sy - cy;
	    dx2		 = ex - cx;
	    dy2		 = ey - cy;

	    theta	 = atan2(dy2, dx2) - atan2(dy1, dx1);
	    if (a->direction) {
		if (theta > 0.0)
		    theta	-= 2.0*M_PI;
	    } else {
		if (theta < 0.0)
		    theta	+= 2.0*M_PI;
	    }

	    if (a->type == T_OPEN_ARC && a->thickness != 0 && a->back_arrow) {
		arc_tangent(cx, cy, sx, sy, !a->direction, &x, &y);
		draw_arrow_head(x, y, sx, sy,
		a->back_arrow->ht/ppi, a->back_arrow->wid/ppi);
		}

	    fprintf(tfp, "PA%.4f,%.4f;PM;PD;", sx, sy);
	    arc(sx, sy, cx, cy, theta, DELTA);
	    fprintf(tfp, "PU;PM2;\n");

	    if (a->thickness != 0)
		fprintf(tfp, "EP;\n");

	    if (a->type == T_OPEN_ARC && a->thickness != 0 && a->for_arrow) {
		arc_tangent(cx, cy, ex, ey, a->direction, &x, &y);
		draw_arrow_head(x, y, ex, ey,
			a->for_arrow->ht/ppi, a->for_arrow->wid/ppi);
		}

	    if (0 < a->fill_style && a->fill_style < patterns)
		fill_polygon(a->fill_style, a->fill_color);
	    }
}

void
genibmgl_ellipse(F_ellipse *e)
{
	if (e->thickness != 0 ||
		(ibmgec && 0 <= e->fill_style && e->fill_style < patterns)) {
	    int		j;
	    double	alpha;
	    double	angle;
	    double	delta;
	    double	x0, y0;
	    double	a,  b;
	    double	x,  y;

	    set_style(e->style, e->style_val);
	    set_width(e->thickness);
	    set_color(e->pen_color);

	    a		 = e->radiuses.x/ppi;
	    b		 = e->radiuses.y/ppi;
	    x0		 = e->center.x/ppi;
	    y0		 = e->center.y/ppi;
	    angle	 = -e->angle;
	    delta	 = -DELTA;

	    x		 = x0 + cos(angle)*a;
	    y		 = y0 + sin(angle)*a;
	    fprintf(tfp, "PA%.4f,%.4f;PM;PD;\n", x, y);
	    for (j = 1; j <= 72; j++) {
		alpha	 = j*delta;
		x	 = x0 + cos(angle)*a*cos(alpha)
			 - sin(angle)*b*sin(alpha);
		y	 = y0 + sin(angle)*a*cos(alpha)
			 + cos(angle)*b*sin(alpha);
		fprintf(tfp, "PA%.4f,%.4f;\n", x, y);
	    }
	    fprintf(tfp, "PU;PM2;\n");

	    if (e->thickness != 0)
		fprintf(tfp, "EP;\n");

	    if (0 < e->fill_style && e->fill_style < patterns)
		fill_polygon((int)e->fill_style, e->fill_color);
	    }
}

void
swap(int *i, int *j)
{
	int	t;
	t = *i;
	*i = *j;
	*j = t;
}

void
genibmgl_line(F_line *l)
{
	if (l->thickness != 0 ||
		(ibmgec && 0 <= l->fill_style && l->fill_style < patterns)) {
	    F_point	*p, *q;

	    set_style(l->style, l->style_val);
	    set_width(l->thickness);
	    set_color(l->pen_color);

	    p	 = l->points;
	    q	 = p->next;

	    switch (l->type) {
		case	T_POLYLINE:
		case	T_BOX:
		case	T_POLYGON:
		    if (q == NULL)		/* A single point line */
			fprintf(tfp, "PA%.4f,%.4f;PD;PU;\n",
				p->x/ppi, p->y/ppi);
		    else {
			if (l->thickness != 0 && l->back_arrow)
			    draw_arrow_head(q->x/ppi, q->y/ppi,
				    p->x/ppi, p->y/ppi,
				    l->back_arrow->ht/ppi,
				    l->back_arrow->wid/ppi);

			fprintf(tfp, "PA%.4f,%.4f;PM;PD%.4f,%.4f;\n",
				p->x/ppi, p->y/ppi,
				q->x/ppi, q->y/ppi);
			while (q->next != NULL) {
			    p	 = q;
			    q	 = q->next;
			    fprintf(tfp, "PA%.4f,%.4f;\n",
				    q->x/ppi, q->y/ppi);
			    }
			fprintf(tfp, "PU;PM2;\n");

			if (l->thickness != 0)
			    fprintf(tfp, "EP;\n");

			if (l->thickness != 0 && l->for_arrow)
			    draw_arrow_head(p->x/ppi, p->y/ppi,
				    q->x/ppi, q->y/ppi,
				    l->for_arrow->ht/ppi,
				    l->for_arrow->wid/ppi);

			if (0 < l->fill_style && l->fill_style < patterns)
			    fill_polygon((int)l->fill_style, l->fill_color);
			}
		    break;

		case	T_ARC_BOX: {
		    int		llx, lly, urx, ury;
		    double	 x0,  y0,  x1,	y1;
		    double	dx, dy, angle;

		    llx	 = urx	= p->x;
		    lly	 = ury	= p->y;
		    while ((p = p->next) != NULL) {
			if (llx > p->x)
			    llx = p->x;
			if (urx < p->x)
			    urx = p->x;
			if (lly > p->y)
			    lly = p->y;
			if (ury < p->y)
			    ury = p->y;
			}

		    x0	 = llx/ppi;
		    x1	 = urx/ppi;
		    dx	 = l->radius/ppi;
		    y0	 = ury/ppi;
		    y1	 = lly/ppi;
		    dy	 = -dx;
		    angle = -M_PI/2.0;

		    fprintf(tfp, "PA%.4f,%.4f;PM;PD;\n",  x0, y0 + dy);
		    arc(x0, y0 + dy, x0 + dx, y0 + dy, angle, DELTA);
		    fprintf(tfp, "PA%.4f,%.4f;\n", x1 - dx, y0);
		    arc(x1 - dx, y0, x1 - dx, y0 + dy, angle, DELTA);
		    fprintf(tfp, "PA%.4f,%.4f;\n", x1, y1 - dy);
		    arc(x1, y1 - dy, x1 - dx, y1 - dy, angle, DELTA);
		    fprintf(tfp, "PA%.4f,%.4f;\n", x0 + dx, y1);
		    arc(x0 + dx, y1, x0 + dx, y1 - dy, angle, DELTA);
		    fprintf(tfp, "PA%.4f,%.4f;PU;PM2;\n", x0, y0 + dy);

		    if (l->thickness != 0)
			fprintf(tfp, "EP;\n");

		    if (0 < l->fill_style && l->fill_style < patterns)
			fill_polygon((int)l->fill_style, l->fill_color);
		    }
		    break;

		case	T_PIC_BOX:
		    fprintf(stderr,"Warning: Pictures not supported in IBMGL language\n");
		    break;
		}
	    }
}

#define		THRESHOLD	.05	/* inch */

static void
bezier_spline(double a0, double b0, double a1, double b1, double a2, double b2, double a3, double b3)
{
	double	x0, y0, x3, y3;
	double	sx1, sy1, sx2, sy2, tx, ty, tx1, ty1, tx2, ty2, xmid, ymid;

	x0 = a0; y0 = b0;
	x3 = a3; y3 = b3;
	if (fabs(x0 - x3) < THRESHOLD && fabs(y0 - y3) < THRESHOLD)
	    fprintf(tfp, "PA%.4f,%.4f;\n", x3, y3);

	else {
	    tx	 = (a1	+ a2 )/2.0;	ty   = (b1  + b2 )/2.0;
	    sx1  = (x0	+ a1 )/2.0;	sy1  = (y0  + b1 )/2.0;
	    sx2  = (sx1 + tx )/2.0;	sy2  = (sy1 + ty )/2.0;
	    tx2  = (a2	+ x3 )/2.0;	ty2  = (b2  + y3 )/2.0;
	    tx1  = (tx2 + tx )/2.0;	ty1  = (ty2 + ty )/2.0;
	    xmid = (sx2 + tx1)/2.0;	ymid = (sy2 + ty1)/2.0;

	    bezier_spline(x0, y0, sx1, sy1, sx2, sy2, xmid, ymid);
	    bezier_spline(xmid, ymid, tx1, ty1, tx2, ty2, x3, y3);
	    }
}

static void
genibmgl_itp_spline(F_spline *s)
{
	F_point		*p1, *p2;
	F_control	*cp1, *cp2;
	double		x1, x2, y1, y2;

	p1 = s->points;
	cp1 = s->controls;
	x2 = p1->x/ppi; y2 = p1->y/ppi;

	if (s->thickness != 0 && s->back_arrow)
	    draw_arrow_head(cp1->rx/ppi, cp1->ry/ppi, x2, y2,
		    s->back_arrow->ht/ppi, s->back_arrow->wid/ppi);

	fprintf(tfp, "PA%.4f,%.4f;PD;\n", x2, y2);
	for (p2 = p1->next, cp2 = cp1->next; p2 != NULL;
		p1 = p2, cp1 = cp2, p2 = p2->next, cp2 = cp2->next) {
	    x1	 = x2;
	    y1	 = y2;
	    x2	 = p2->x/ppi;
	    y2	 = p2->y/ppi;
	    bezier_spline(x1, y1, (double)cp1->rx/ppi, cp1->ry/ppi,
		(double)cp2->lx/ppi, cp2->ly/ppi, x2, y2);
	    }
	fprintf(tfp, "PU;\n");

	if (s->thickness != 0 && s->for_arrow)
	    draw_arrow_head(cp1->lx/ppi, cp1->ly/ppi, x2, y2,
		    s->for_arrow->ht/ppi, s->for_arrow->wid/ppi);
	}

static void
quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4, double b4)
{
	double	x1, y1, x4, y4;
	double	xmid, ymid;

	x1	 = a1; y1 = b1;
	x4	 = a4; y4 = b4;
	xmid	 = (a2 + a3)/2.0;
	ymid	 = (b2 + b3)/2.0;
	if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD)
	    fprintf(tfp, "PA%.4f,%.4f;\n", xmid, ymid);
	else {
	    quadratic_spline(x1, y1, ((x1+a2)/2.0), ((y1+b2)/2.0),
		((3.0*a2+a3)/4.0), ((3.0*b2+b3)/4.0), xmid, ymid);
	}

	if (fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD)
	    fprintf(tfp, "PA%.4f,%.4f;\n", x4, y4);
	else {
	    quadratic_spline(xmid, ymid, ((a2+3.0*a3)/4.0), ((b2+3.0*b3)/4.0),
			((a3+x4)/2.0), ((b3+y4)/2.0), x4, y4);
	}
}

static void
genibmgl_ctl_spline(F_spline *s)
{
	F_point	*p;
	double	cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
	double	x1, y1, x2, y2;

	p	 = s->points;
	x1	 = p->x/ppi;
	y1	 = p->y/ppi;
	p	 = p->next;
	x2	 = p->x/ppi;
	y2	 = p->y/ppi;
	cx1	 = (x1 + x2)/2.0;
	cy1	 = (y1 + y2)/2.0;
	cx2	 = (x1 + 3.0*x2)/4.0;
	cy2	 = (y1 + 3.0*y2)/4.0;

	if (closed_spline(s))
	    fprintf(tfp, "PA%.4f,%.4f;PD;\n ", cx1, cy1);
	else {
	    if (s->thickness != 0 && s->back_arrow)
		draw_arrow_head(cx1, cy1, x1, y1,
			s->back_arrow->ht/ppi, s->back_arrow->wid/ppi);
	    fprintf(tfp, "PA%.4f,%.4f;PD%.4f,%.4f;\n",
		    x1, y1, cx1, cy1);
	    }

	for (p = p->next; p != NULL; p = p->next) {
	    x1	 = x2;
	    y1	 = y2;
	    x2	 = p->x/ppi;
	    y2	 = p->y/ppi;
	    cx3	 = (3.0*x1 + x2)/4.0;
	    cy3	 = (3.0*y1 + y2)/4.0;
	    cx4	 = (x1 + x2)/2.0;
	    cy4	 = (y1 + y2)/2.0;
	    quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
	    cx1	 = cx4;
	    cy1	 = cy4;
	    cx2	 = (x1 + 3.0*x2)/4.0;
	    cy2	 = (y1 + 3.0*y2)/4.0;
	    }
	x1	 = x2;
	y1	 = y2;
	p	 = s->points->next;
	x2	 = p->x/ppi;
	y2	 = p->y/ppi;
	cx3	 = (3.0*x1 + x2)/4.0;
	cy3	 = (3.0*y1 + y2)/4.0;
	cx4	 = (x1 + x2)/2.0;
	cy4	 = (y1 + y2)/2.0;
	if (closed_spline(s)) {
	    quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
	    fprintf(tfp, "PU;\n");
	    }
	else {
	    fprintf(tfp, "PA%.4f,%.4f;PU;\n", x1, y1);
	    if (s->thickness != 0 && s->for_arrow)
		draw_arrow_head(cx1, cy1, x1, y1,
			s->for_arrow->ht/ppi, s->for_arrow->wid/ppi);
	    }
	}

void
genibmgl_spline(F_spline *s)
{
	if (s->thickness != 0) {
	    set_style(s->style, s->style_val);
	    set_width(s->thickness);
	    set_color(s->pen_color);

	    if (int_spline(s))
		genibmgl_itp_spline(s);
	    else
		genibmgl_ctl_spline(s);
	}

	if (0 < s->fill_style && s->fill_style < patterns)
	    fprintf(stderr, "Spline area fill not implemented\n");
}

#define	FONT(T) ((-1 < (T) && (T) < FONTS) ? (T): fonts)
void
genibmgl_text(F_text *t)
{
static	int	font	 = DEFAULT;	/* font				*/
static	int	size	 = DEFAULT;	/* font size	    in points	*/
static	double	theta	 = 0.0;		/* character slant  in degrees	*/
static	double	angle	 = 0.0;		/* label direction  in radians	*/
	double	width;			/* character width  in centimeters */
	double	height;			/* character height in centimeters */
	bool newfont=false, newsize=false;

	if (font != FONT(t->font)) {
	    font  = FONT(t->font);
	    /* Simulate italic fonts with a 10 degree slant */
	    if (theta != slant[font]) {
		theta  = slant[font];
		fprintf(tfp, "SL%.4f;", tan(theta*M_PI/180.0));
	    }
	    newfont = true;
	}

	if (size != t->size) {
	    size  = t->size;	/* in points */
	    newsize = true;
	    if (!correct_font_size) {
		/* HP Stick Font only:	use the 'SI' command to set the
		   cap height and pitch.
		*/
		width	 = size*wcmpp*wide[font];
		height	 = size*hcmpp*high[font];
		fprintf(tfp, "SI%.4f,%.4f;", width*mag, height*mag);
	    }
	}

	if (correct_font_size && (newfont || newsize)) {
	    /* Use 'SD' command to set the font */
	    fprintf(tfp, "SD2,1,4,%d,5,%d,6,%d,7,%d;SS;\n",
		    (int)(size*mag+.5), psfont2hpgl[font].italic,
		    psfont2hpgl[font].bold, psfont2hpgl[font].font);
	}

	if (angle != t->angle) {
	    angle  = t->angle;
	    fprintf(tfp, "DI%.4f,%.4f;",
		    cos(angle), sin(reflected ? -angle: angle));
	}
	set_color(t->color);

	fprintf(tfp, "PA%.4f,%.4f;\n", t->base_x/ppi, t->base_y/ppi);

	switch (t->type) {
	    case DEFAULT:
	    case T_LEFT_JUSTIFIED:
		break;
	    case T_CENTER_JUSTIFIED:
		fprintf(tfp, "CP%.4f,0.0;", -(double)(strlen(t->cstring)/2.0));
		break;
	    case T_RIGHT_JUSTIFIED:
		fprintf(tfp, "CP%.4f,0.0;", -(double)(strlen(t->cstring)));
		break;
	    default:
		fprintf(stderr, "unknown text position type\n");
		exit(1);
	}

	fprintf(tfp, "LB%s\003\n", t->cstring);
}

int
genibmgl_end(void)
{
	/* IBMGL ending */
	fprintf(tfp, "PU;SP;IN;\n");

	if (pcljcl)
	    fprintf(tfp, "\033%%0A\033E");	/* end job and eject page */

	/* all ok */
	return 0;
}

struct driver dev_ibmgl = {
	genibmgl_option,
	genibmgl_start,
	gendev_nogrid,
	genibmgl_arc,
	genibmgl_ellipse,
	genibmgl_line,
	genibmgl_spline,
	genibmgl_text,
	genibmgl_end,
	EXCLUDE_TEXT
	};
