/***********************************************************************
 *
 * TITLE:
 *	gr_plot.c
 *
 * AUTHOR:
 *	Kevin J. Miller, Ana Maria Guerrero, Chester Joe
 *
 * DESCRIPTION:
 *	This module is the source code for the gr_ graphics ploting
 *	routines.
 *
 *				CHANGE HISTORY
 *
 * $Log: gr_plot.c,v $
 * Revision 1.35  1994/11/30  02:17:23  kevin
 * added gr_circle_fill
 *
 * Revision 1.34  1994/07/15  21:36:40  kevin
 * updated PICT commands for sfosedt & xopps
 *
 * Revision 1.33  1994/06/17  03:48:03  kevin
 * make option for AppleSingle or PICT
 *
 * Revision 1.32  1994/06/15  03:17:36  kevin
 * updated metafile commands
 *
 * Revision 1.31  1994/06/15  00:05:26  kevin
 * updated constants
 *
 * Revision 1.30  1994/06/14  03:15:13  kevin
 * added gr_point function
 *
 * Revision 1.29  1994/05/20  22:40:35  kevin
 * removed unnecessary gc_val
 *
 * Revision 1.28  1994/05/16  18:47:46  kevin
 * added partial WMF & univeral header
 *
 * Revision 1.27  1994/04/29  00:19:02  kevin
 * ANSI port - first stage
 *
 * Revision 1.26  1993/11/15  23:46:22  kevin
 * added subset of PICT codes
 *
 * Revision 1.25  1993/02/09  17:24:12  jjj
 * enabled rotate_text to handle null strings.
 *
 * Revision 1.24  1993/02/09  17:23:38  clm
 * fixed gr_rect_clear
 *
 * Revision 1.23  1992/12/31  02:53:52  chester
 * add gr_rotate_text function for rotated text.
 * add gr_set_clip and gr_reset_clip.
 *
 * Revision 1.22  1992/12/30  23:13:16  chester
 * add gr_current_state function to get current graphics state.
 * add gr_masked_line function for lines with a white background.
 * add gr_poly_clear for whiting out a polygon.
 *
 * Revision 1.21  1992/01/13  16:17:47  ana
 * Added the "letter" command to gr_laser_on.
 *
 * Revision 1.20  1991/11/23  02:38:40  kevin
 * reset current font to UNKNOWN each time screen is turned on
 *
 * Revision 1.19  1991/10/31  21:33:52  chester
 * fixed the gr_poly laser stroke bug.
 *
 * Revision 1.18  1991/10/04  16:53:49  chester
 * remove gr_curve and math library from gr routines
 *
 * Revision 1.17  1991/09/16  17:07:42  chester
 * fixed line connection bug in gr_poly function.
 *
 * Revision 1.16  1991/09/06  19:51:13  ana
 * Added gr_rect_shade()
 *
 * Revision 1.15  1991/08/28  20:22:28  chester
 * corrected the bug in gr..._fill routines.
 *
 * Revision 1.14  1991/08/28  19:58:31  chester
 * change Pixel to unsigned long.
 *
 * Revision 1.13  1991/08/27  20:55:28  chester
 * added ana's gr_init_colors, gr_hilight functions.
 *
 * Revision 1.12  1991/08/27  20:18:46  chester
 * moved NUM_FONTS constant to header file.
 * added different line types to draw.
 * added gr_set_line function to change drawing line type.
 * added gr_poly_fill to draw a filled polygon.
 * added gr_rect_fill to draw a filled rectangle.
 * added more fonts to screen font.
 * added masking variable to gr_text.
 *
 * Revision 1.11  1991/08/27  20:11:07  kevin
 * added standard headers
 *
 * Revision 1.10  1991/04/30  00:06:45  kevin
 * de-linted and added comments
 *
 * Revision 1.9  1991/04/29  19:56:48  kevin
 * added standard header
 *
 * Revision 1.8  1991/04/23  23:23:47  kevin
 * fixed unnecessary include of Motif
 *
 * Revision 1.7  1990/12/28  15:42:39  kevin
 * now use image function to match screen behavior
 *
 * Revision 1.6  90/12/28  13:02:53  kevin
 * changed name to gr_ and fixed so there is only one include
 * 
 * Revision 1.5  90/12/27  17:21:52  kevin
 * finished up new image routines
 * 
 * Revision 1.4  90/12/19  14:28:06  kevin
 * removed memory functions, and added a screen font routine
 * 
 * Revision 1.3  90/12/12  09:10:12  ana
 * Added gr_image.
 * 
 * Revision 1.2  90/11/29  15:35:29  ana
 * New Version.
 * 
 ***********************************************************************
 *
 * WARNINGS:
 *
 * EXTERNAL CALLABLE COMPONENTS (PUBLIC):
 *	void gr_init(int, int, int)
 *	void gr_init_colors(unsigned long, unsigned long, unsigned long)
 *	int gr_current_state(void)
 *	void gr_screen_on(GRDisplay *)
 *	void gr_screen_off(void)
 *	void gr_laser_on(FILE *)
 *	void gr_laser_off(void)
 *	int gr_pict_on(char *, int)
 *	void gr_pict_off(void)
 *	int gr_wmf_on(char *)
 *	void gr_wmf_off(void)
 *	int gr_debug_on(char *)
 *	void gr_debug_off(void)
 *	void gr_point(int, int)
 *	void gr_move(int, int)
 *	void gr_set_line(int)
 *	void gr_draw(int, int)
 *	void gr_line(int, int, int, int)
 *	void gr_masked_line(int, int, int, int)
 *	void gr_arc(int, int, int, int, int, int)
 *	void gr_circle(int, int, int)
 *	void gr_circle_fill(int, int, int)
 *	void gr_poly(XPoint *, int)
 *	void gr_rectangle(int, int, int, int)
 *	void gr_clear(void)
 *	void gr_page(void)
 *	void gr_poly_clear(XPoint *, int)
 *	void gr_rect_clear(int, int, int, int)
 *	void gr_poly_fill(XPoint *, int, char *, int)
 *	void gr_rect_fill(int, int, int, int, char *, int)
 *	void gr_rect_shade(int, int, int, int, int)
 *	void gr_hilight(int, int, int, int)
 *	void gr_free_fonts(void)
 *	void gr_text(int, int, char *, int, int, int)
 *	void gr_rotate_text(int, int, char *, int, int)
 *	void gr_vert_text(int, int, char *, int, int, int)
 *	int gr_strwidth(char *, int)
 *	int gr_txwidth(int)
 *	int gr_txheight(int)
 *	int gr_txdescent(int)
 *	int gr_txascent(int)
 *	void gr_set_color(char *, int)
 *	void gr_image(int, int, GRBitmap *, GRBitmap *, int)
 *	void gr_set_clip(int, int, int, int, Region)
 *	void gr_reset_clip(void)
 *
 * GLOBALS:
 *	None
 *
 * WAIVERS:
 *	None
 *
 * NOTES:
 *	None
 *
 * MANUAL PAGE:
 *	See the header file: gr_plot.h
 *
 ***********************************************************************/

#ifndef lint
static char rcsid[] =
    "$Id: gr_plot.c,v 1.35 1994/11/30 02:17:23 kevin OEL $";
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include "gr_plot.h"

#define BATCH_SIZE 512

static enum {				/* current output device */
    GR_OFF, GR_SCREEN, GR_LASER, GR_PICT, GR_WMF, GR_DEBUG
} gr_state;

static enum {				/* handles stroke in PostScript */
    PS_PATH_OFF, PS_PATH_ON
} ps_state;

#define MAX_PS_PATH_LEN 100

static ps_path_len;			/* length of current path */

static GRDisplay *xdw;			/* all display identifiers */
static FILE *lp;			/* printer file pointer */
static int xo, yo;			/* current position */
static int xp, yp;			/* start of path for PostScript */
static int p_height;			/* page height */
static int p_width;			/* page width */
static int p_mode;			/* laser printer rotation mode */
static unsigned long background;	/* background color index */
static unsigned long foreground;	/* foreground color index */
static unsigned long highlight;		/* highlight color index */
static int line_state;			/* Solid, Dashed, Dotted... */

static FILE *pict_fp;			/* Mac PICT file pointer */
static int pict_apple_single;		/* TRUE if using AppleSingle format */
static int pict_font;			/* old font value */

static FILE *wmf_fp;			/* Windows Metafile file pointer */
static long wmf_size;			/* size of metafile in 16 bit words */
static long wmf_maxrec;			/* maximum record size */
static int wmf_font;			/* current font index */

static char *out_buffer = NULL;		/* output buffer */
static unsigned int out_avail = 0;	/* size of output buffer */
static unsigned int out_len;		/* length of current record */

static FILE *debug_fp;			/* debug file pointer */

/*
 * local function prototypes
 */
static void open_xfont(char *, int);
static void screen_font(int);
static void set_font(int);
static int char_mid(char *);
static unsigned int find_color_black(char *);
static int pict_open(char *, int);
static void pict_cmd(int);
static void pict_i16(int);
static void pict_i32(long);
static void pict_str(char *, int);
static void pict_set(int, int);
static void pict_out(void);
static void pict_close(void);
static int wmf_open(char *);
static void wmf_cmd(int);
static void wmf_i16(int);
static void wmf_i32(long);
static void wmf_str(char *, int);
static void wmf_out(void);
static void wmf_close(void);

#define NUM_FONTS 96

static int nfonts;			/* current number of fonts */
struct font_info {			/* font array to hold info */
    int fontnumber;
    XFontStruct *font_struct;
};
static struct font_info cur_font; 
static struct font_info x_fonts[NUM_FONTS];

static char dotted[2] = {1, 3};
static char short_dashed[2] = {4, 4};
static char long_dashed[2] = {6, 4};

static Region save_clip_region = NULL;	/* holds the original clip region */
static Region sub_region = NULL;	/* holds the new clip region */
static Region sub_region2 = NULL;	/* holds the new clip region */

/*
 * conversion for laser printer coordinates
 */
#define scx(x) ((double)(x))
#define scy(y) ((double)(p_height - (y)))

/*
 * conversion for PICT coordinates
 */
#define psc(z) ((72 * (z)) / ((p_mode & GR_EXPAND) ? 100 : 75))

/* arc constants */
#define X_DEGREES 64
#define FULL_CIRCLE 360

/* font mask constants */
#define POINTMASK 0xff
#define NAMEMASK 0xff00

/*
 * Apple Single codes
 */
#define APPLE_SINGLE_MAGIC		0x51600
#define APPLE_SINGLE_VERSION		0x20000
#define APPLE_SINGLE_FINDER_INFO	9
#define APPLE_SINGLE_DATA_FORK		1

/*
 * PICT opcodes
 */
#define PICT_NOP	0x00
#define PICT_CLIP	0x01
#define PICT_BKPAT	0x02
#define PICT_TXFONT	0x03
#define PICT_TXFACE	0x04
#define PICT_TXMODE	0x05
#define PICT_SPEXTRA	0x06
#define PICT_PNSIZE	0x07
#define PICT_PNMODE	0x08
#define PICT_PNPAT	0x09
#define PICT_TXSIZE	0x0d
#define PICT_VERSION	0x11
#define PICT_LINE	0x20
#define PICT_LONGTEXT	0x28
#define PICT_DVTEXT	0x2a
#define PICT_FRAMERECT	0x30
#define PICT_ERASERECT	0x32
#define PICT_FILLRECT	0x34
#define PICT_FRAMEOVAL	0x50
#define PICT_ERASEOVAL	0x52
#define PICT_FILLOVAL	0x54
#define PICT_FRAMEPOLY	0x70
#define PICT_ERASEPOLY	0x72
#define PICT_FILLPOLY	0x74
#define PICT_END	(char)0xff

/*
 * Windows Metafile opcodes
 */
#define WMF_SETMAPMODE		0x0103
#define WMF_SELECTOBJECT	0x012d
#define WMF_SETWINDOWORG	0x020b
#define WMF_SETWINDOWEXT	0x020c
#define WMF_LINETO		0x0213
#define WMF_MOVETO		0x0214
#define WMF_DELETEOBJECT	0x01f0
#define WMF_CREATEPENINDIRECT	0x02fa
#define WMF_CREATEFONTINDIRECT	0x02fb
#define WMF_CREATEBRUSHINDIRECT	0x02fc
#define WMF_POLYGON		0x0324
#define WMF_POLYLINE		0x0325
#define WMF_TEXTOUT		0x0521

/***********************************************************************
 *
 * FUNCTION:
 *	gr_init()
 *
 * INPUTS:
 *	tp_width	- (int) width of plotting area in pixels
 *	tp_height	- (int) height of plotting area in pixels
 *	tp_mode		- (int) mapping of screen to hardcopy
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *	p_height	- (static int) width of plotting area in pixels
 *	p_width		- (static int) height of plotting area in pixels
 *	p_mode		- (static int) mapping of screen to hardcopy
 *	nfonts		- (static int) number of fonts set to 0
 *	cur_font.fontnumber	- (static int) 0xFFFF "unknown"
 *	gr_state	- (static enum) set to GR_OFF
 *
 * DESCRIPTION:
 *	Set parameters which define mapping of screen to hardcopy.
 *	This is done to center hardcopy on a page.  In additon, the
 *	current font is set to "unknown", and the output device to
 *	"off".
 */

void 
gr_init(int tp_width, int tp_height, int tp_mode)
{
    p_height = tp_height;
    p_width  = tp_width;
    p_mode  = tp_mode;
    nfonts = 0;
    cur_font.fontnumber = 0xFFFF;
    line_state = GR_SOLID_LINE;
    gr_state = GR_OFF;
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_init_colors()
 *
 * INPUTS:
 *      p_background    - (unsigned long) index of background color
 *      p_foreground    - (unsigned long) index of foreground color
 *      p_highlight     - (unsigned long) index of highlight color
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *      background      - (unsigned long) index of background color
 *      foreground      - (unsigned long) index of foreground color
 *      highlight       - (unsigned long) index of highlight color
 *
 * DESCRIPTION:
 *      This routine sets the three colors used for the display.
 */

void 
gr_init_colors(unsigned long p_background, unsigned long p_foreground,
    unsigned long p_highlight)
{
    foreground = p_foreground;
    background = p_background; highlight = p_highlight;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_current_state()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *	the current state of gr_routines
 *	GR_OFF
 *	GR_SCREEN
 *	GR_LASER
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	returns the current graphics state
 */

int 
gr_current_state(void) 
{
    return((int)gr_state);
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_screen_on()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Set up for screen mode.
 */

void 
gr_screen_on(GRDisplay *disp)
{
    xdw = disp;
    cur_font.fontnumber = 0xFFFF;
    gr_state = GR_SCREEN;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_screen_off()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Turn off screen mode.
 */

void 
gr_screen_off(void)
{
    gr_state = GR_OFF;
    return;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_laser_on()
 *
 * INPUTS:
 *	ulp		- (FILE *) pipe to laser printer process
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Initialize laser printer.
 */

void 
gr_laser_on(FILE *ulp)
{
    float scale, xoff, yoff;

    lp = ulp;
    gr_state = GR_LASER;
    ps_state = PS_PATH_OFF;
    ps_path_len = 0;
    (void)fprintf(lp, "%%!PS-Adobe-1.0\n");
    (void)fprintf(lp, "%%%%\n");
    (void)fprintf(lp, "letter\n");
    (void)fprintf(lp, "/C { closepath } def\n");
    (void)fprintf(lp, "/D { lineto } def\n");
    (void)fprintf(lp, "/EX { exch } def\n");
    (void)fprintf(lp, "/EO { eoclip } def\n");
    (void)fprintf(lp, "/GS { gsave } def\n");
    (void)fprintf(lp, "/GR { grestore } def\n");
    (void)fprintf(lp, "/M { moveto } def\n");
    (void)fprintf(lp, "/N { newpath } def\n");
    (void)fprintf(lp, "/R { rotate } def\n");
    (void)fprintf(lp, "/RL { rlineto } def\n");
    (void)fprintf(lp, "/S { stroke } def\n");
    (void)fprintf(lp, "/T { translate } def\n");
    (void)fprintf(lp, "/PT { newpath moveto -0.25 0 rmoveto 0.5 0 rlineto\n");
    (void)fprintf(lp, "    stroke } def\n");

    /* elliptical arcs and circles */
    (void)fprintf(lp, "/ellipsedict 8 dict def\n");
    (void)fprintf(lp, "ellipsedict /mtrx matrix put\n");
    (void)fprintf(lp, "/ellipse {\n");
    (void)fprintf(lp, "  ellipsedict begin\n");
    (void)fprintf(lp, "    /endangle exch def\n");
    (void)fprintf(lp, "    /startangle exch def\n");
    (void)fprintf(lp, "    /yrad exch def\n");
    (void)fprintf(lp, "    /xrad exch def\n");
    (void)fprintf(lp, "    /yctr exch def\n");
    (void)fprintf(lp, "    /xctr exch def\n");
    (void)fprintf(lp, "    /savematrix mtrx currentmatrix def\n");
    (void)fprintf(lp, "    xctr yctr translate\n");
    (void)fprintf(lp, "    xrad yrad scale\n");
    (void)fprintf(lp, "    0 0 1 startangle endangle arc\n");
    (void)fprintf(lp, "    savematrix setmatrix\n");
    (void)fprintf(lp, "  end\n");
    (void)fprintf(lp, "} def\n");
    (void)fprintf(lp, "/P0 { show } def\n");
    (void)fprintf(lp,
	"/P1 { dup stringwidth pop 2 div neg 0 rmoveto show } def\n");
    (void)fprintf(lp, "/P2 { dup stringwidth pop neg 0 rmoveto show } def\n");

    /* define scale: GR_EXPAND = 100 dpi, else 75 dpi */
    if (p_mode & GR_EXPAND) {
        scale = 0.72;           /* 100 pixels * 0.72 = 72 points = 1 inch */
    } else {
        scale = 0.96;           /* 75 pixels * 0.96 = 72 points = 1 inch */
    }
    /* set up so figure is centered */
    if (p_mode & GR_LANDSCAPE) {
        xoff = ((11.0 * 72.0) / scale - p_width) / 2.0;
        yoff = - (p_height + (8.5 * 72.0) / scale) / 2.0;
        (void)fprintf(lp, "90 rotate %.3f %.3f scale %.3f %.3f translate\n",
            scale, scale, xoff, yoff);
    } else {
        xoff = ((8.5 * 72.0) / scale - p_width) / 2.0;
        yoff = ((11.0 * 72.0) / scale - p_height) / 2.0;
        (void)fprintf(lp, "%.3f %.3f scale %.3f %.3f translate\n",
            scale, scale, xoff, yoff);
    }
    (void)fprintf(lp, "%%%%\n");
    (void)fprintf(lp, ".5 setlinewidth\n");
    (void)fprintf(lp, "0 setgray\n");
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_laser_off()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Turn off laser printer.
 */

void 
gr_laser_off(void)
{
    gr_state = GR_OFF;
    if (ps_state == PS_PATH_ON) {
        (void)fprintf(lp, "S\n");
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
    }
    (void)fprintf(lp, "%%%%\n");
    (void)fprintf(lp, "showpage\n");
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_pict_on()
 *
 * INPUTS:
 *	name		- (char *) name of pict file
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Open a pict file and write the header.
 */

int 
gr_pict_on(char *name, int apple_single)
{
    int status;

    status = pict_open(name, apple_single);
    gr_state = (status == 0) ? GR_PICT : GR_OFF;
    return status;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_pict_off()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Turn off PICT mode.
 */

void 
gr_pict_off(void)
{
    pict_close();
    gr_state = GR_OFF;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_wmf_on()
 *
 * INPUTS:
 *	name		- (char *) name of WMF file
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	0		- (int) on success
 *	-1		- (int) on failure
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	gr_state	- (static enum) state of gr output
 *
 * DESCRIPTION:
 *	Open a Windows Metafile and write the header plus a few
 *	initialization commands.
 */

int 
gr_wmf_on(char *name)
{
    int status;

    status = wmf_open(name);
    gr_state = (status == 0) ? GR_WMF : GR_OFF;
    return status;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_wmf_off()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	gr_state	- (static enum) state of gr output
 *
 * DESCRIPTION:
 *	Turn off WMF mode.
 */

void
gr_wmf_off(void)
{
    wmf_close();
    gr_state = GR_OFF;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_debug_on()
 *
 * INPUTS:
 *	name		- (char *) name of debug file
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Open a debug file and write the header.
 */

int 
gr_debug_on(char *name)
{
    if ((debug_fp = fopen(name, "w")) == NULL) {
	return -1;
    }
    gr_state = GR_DEBUG;
    fprintf(debug_fp, "#include <stdio.h>\n");
    fprintf(debug_fp, "#include <X11/Xlib.h>\n");
    fprintf(debug_fp, "#include <X11/Intrinsic.h>\n");
    fprintf(debug_fp, "#include \"gr_plot.h\"\n\n");
    fprintf(debug_fp, "void\ngrdbx_init()\n{\n");
    fprintf(debug_fp, "    gr_init(%d, %d, %d);\n", p_width, p_height, p_mode);
    fprintf(debug_fp, "    gr_init_colors(%lu, %lu, %lu);\n", background,
	foreground, highlight);
    fprintf(debug_fp, "}\n\nvoid\ngrdbx_refresh()\n{\n");
    return 0;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_debug_off()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Turn off DEBUG mode.
 */

void 
gr_debug_off(void)
{
    gr_state = GR_OFF;
    fprintf(debug_fp, "}\n");
    fclose(debug_fp);
    debug_fp = NULL;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_point
 *
 * INPUTS:
 *	x		- (int) horizontal location
 *	y		- (int) vertical location
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw a point at the specified location.
 */

void
gr_point(int x, int y)
{
    switch (gr_state) {
    case GR_SCREEN:
	xo = x;
	yo = y;
	XDrawPoint(xdw->display, xdw->win, xdw->gc, x, y);
	break;
    case GR_LASER:
        xo = x;
        yo = y;
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
        }
	(void)fprintf(lp, "%.1f %.1f PT\n", scx(x), scy(y));
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
        break;
    case GR_PICT:
	break;
    case GR_WMF:
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_point(%d, %d);\n", x, y);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_move()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Move to point.
 */

void 
gr_move(int x, int y)
{
    switch (gr_state) {
    case GR_SCREEN:
        xo = x;
        yo = y;
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
	ps_path_len = 0;
        xp = x;
        yp = y;
        xo = x;
        yo = y;
        break;
    case GR_PICT:
        xo = x;
        yo = y;
        break;
    case GR_WMF:
	wmf_cmd(WMF_MOVETO);
	wmf_i16(y);
	wmf_i16(x);
	wmf_out();
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_move(%d, %d);\n", x, y);
	break;
    case GR_OFF:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_set_line()
 *
 * INPUTS:
 *      line    - (int)
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	change line setting
 */

void 
gr_set_line(int line)
{
    XGCValues gc_val;

    switch (gr_state) {
    case GR_SCREEN:
	switch (line) {
	case GR_SOLID_LINE:
            gc_val.line_style = LineSolid;
	    break;
	case GR_DOT_LINE:
            gc_val.line_style = LineOnOffDash;
            XSetDashes(xdw->display, xdw->gc, 0, dotted, 2);
            break;
        case GR_SHORT_DASH:
            gc_val.line_style = LineOnOffDash;
            XSetDashes(xdw->display, xdw->gc, 0, short_dashed, 2);
            break;
        case GR_LONG_DASH:
            gc_val.line_style = LineOnOffDash;
            XSetDashes(xdw->display, xdw->gc, 0, long_dashed, 2);
            break;
        }
        XChangeGC(xdw->display, xdw->gc, GCLineStyle, &gc_val);
        line_state = line;
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
            ps_state = PS_PATH_OFF;
	    ps_path_len = 0;
        }
        switch (line) {
        case GR_SOLID_LINE:
            (void)fprintf(lp, "[] 0 setdash\n");
            break;
        case GR_DOT_LINE:
            (void)fprintf(lp, "[1 3] 0 setdash\n");
            break;
        case GR_SHORT_DASH:
            (void)fprintf(lp, "[4 4] 0 setdash\n");
            break;
        case GR_LONG_DASH:
            (void)fprintf(lp, "[6 4] 0 setdash\n");
            break;
        }
        line_state = line;
        break;
    case GR_PICT:
	pict_cmd(PICT_PNPAT);
	switch (line) {
	case GR_SOLID_LINE:
	    pict_set(0xff, 8);
	    break;
	case GR_DOT_LINE:
	    pict_set(0xaa, 1); pict_set(0x55, 1);
	    pict_set(0xaa, 1); pict_set(0x55, 1);
	    pict_set(0xaa, 1); pict_set(0x55, 1);
	    pict_set(0xaa, 1); pict_set(0x55, 1);
	    break;
	case GR_SHORT_DASH:
	    pict_set(0xcc, 2); pict_set(0x33, 2);
	    pict_set(0xcc, 2); pict_set(0x33, 2);
	    break;
	case GR_LONG_DASH:
	    pict_set(0xf0, 4); pict_set(0x0f, 4);
	    break;
	}
	pict_out();
        break;
    case GR_WMF:
	wmf_cmd(WMF_DELETEOBJECT);
	wmf_i16(0);
	wmf_out();
    
	wmf_cmd(WMF_CREATEPENINDIRECT);
	switch (line) {
	case GR_SOLID_LINE:
	    wmf_i16(0);
	    break;
	case GR_DOT_LINE:
	    wmf_i16(2);
	    break;
	case GR_SHORT_DASH:
	    wmf_i16(1);
	    break;
	case GR_LONG_DASH:
	    wmf_i16(1);
	    break;
	}
	wmf_i16(1);
	wmf_i16(0);
	wmf_i16(0);
	wmf_i16(0);
	wmf_out();
    
	wmf_cmd(WMF_SELECTOBJECT);
	wmf_i16(0);
	wmf_out();
        break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_set_line(%d);\n", line);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_draw()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw to point.
 */

void 
gr_draw(int x, int y)
{
    switch (gr_state) {
    case GR_SCREEN:
        XDrawLine(xdw->display, xdw->win, xdw->gc, xo, yo, x, y);
        xo = x;
        yo = y;
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_OFF) {
            (void)fprintf(lp, "N %.1f %.1f M %.1f %.1f D\n", scx(xo),
		scy(yo), scx(x), scy(y));
            ps_state = PS_PATH_ON;
        } else if ((x == xp) && (y == yp)) {
            (void)fprintf(lp, "%.1f %.1f D C S\n", scx(x), scy(y));
            ps_state = PS_PATH_OFF;
        } else {
            (void)fprintf(lp, "%.1f %.1f D\n", scx(x), scy(y));
        }
	++ps_path_len;
	if (ps_path_len > MAX_PS_PATH_LEN) {
	    (void)fprintf(lp, "S N %.1f %.1f M\n", scx(x), scy(y));
	    ps_path_len = 0;
	}
        xo = x;
        yo = y;
        break;
    case GR_PICT:
	pict_cmd(PICT_LINE);
	pict_i16(psc(yo));
	pict_i16(psc(xo));
	pict_i16(psc(y));
	pict_i16(psc(x));
	pict_out();
        xo = x;
        yo = y;
	break;
    case GR_WMF:
	wmf_cmd(WMF_LINETO);
	wmf_i16(y);
	wmf_i16(x);
	wmf_out();
	xo = x;
	yo = y;
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_draw(%d, %d);\n", x, y);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_line()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw a line between two points.
 */

void 
gr_line(int x1, int y1, int x2, int y2)
{
    switch (gr_state) {
    case GR_SCREEN:
	XDrawLine(xdw->display, xdw->win, xdw->gc, x1, y1, x2, y2);
	xo = x2;
	yo = y2;
	break;
    case GR_LASER:
	if (ps_state == PS_PATH_ON) {
	    (void)fprintf(lp, "S\n");
	}
	(void)fprintf(lp, "N %.1f %.1f M %.1f %.1f D\n", scx(x1), scy(y1),
	    scx(x2), scy(y2));
	ps_state = PS_PATH_ON;
	ps_path_len = 1;
	xo = x2;
	yo = y2;
	break;
    case GR_PICT:
	pict_cmd(PICT_LINE);
	pict_i16(psc(y1));
	pict_i16(psc(x1));
	pict_i16(psc(y2));
	pict_i16(psc(x2));
	pict_out();
        xo = x2;
        yo = y2;
	break;
    case GR_WMF:
	wmf_cmd(WMF_MOVETO);
	wmf_i16(y1);
	wmf_i16(x1);
	wmf_out();
    
	wmf_cmd(WMF_LINETO);
	wmf_i16(y2);
	wmf_i16(x2);
	wmf_out();
        xo = x2;
        yo = y2;
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_line(%d, %d, %d, %d);\n", x1, y1, x2, y2);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_masked_line()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	draw a usually non-solid line and overwrite background
 */

void 
gr_masked_line(int x1, int y1, int x2, int y2)
{
    XGCValues gcval;
    int temp_state;		/* Solid, Dashed, Dotted... */
    

    XGetGCValues(xdw->display, xdw->gc,
                 GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle, &gcval);
    switch (gr_state) {
    case GR_SCREEN:
        if (gcval.line_style != LineSolid) {
            XSetLineAttributes(xdw->display, xdw->gc,
		(unsigned int)gcval.line_width, LineDoubleDash,
		gcval.cap_style, gcval.join_style);
        }
        gr_line(x1, y1, x2, y2);
        XChangeGC(xdw->display, xdw->gc, GCLineStyle, &gcval);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        temp_state = line_state;
        gr_set_line(GR_SOLID_LINE);
        fprintf(lp, "N %.1f %.1f M %.1f %.1f D 1 setgray C S 0 setgray\n",
                scx(x1), scy(y1), scx(x2), scy(y2));
	ps_state = PS_PATH_OFF;
        gr_set_line(temp_state);
        gr_line(x1, y1, x2, y2);
        break;
    case GR_PICT:
	pict_cmd(PICT_PNMODE);
	pict_i16(8);		/* patCopy */
	gr_line(x1, y1, x2, y2);
	pict_cmd(PICT_PNMODE);
	pict_i16(9);		/* patOr */
	pict_out();
	break;
    case GR_WMF:
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_masked_line(%d, %d, %d, %d);\n", x1, y1,
	    x2, y2);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_arc()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Plot an arc.
 */

void 
gr_arc(int x, int y, int dx, int dy, int ang1, int ang2)
{
    float xctr, yctr;           /* center of laser arc */
    float xrad, yrad;           /* radii of laser arc */

    xrad = dx/2; 
    yrad = dy/2;
    xctr = x + xrad;  /* get arc center pts */
    yctr = y + yrad;  /* get arc center pts */
    switch (gr_state) {
        case GR_SCREEN:
            XDrawArc(xdw->display, xdw->win, xdw->gc, x, y, (unsigned int)dx,
		(unsigned int)dy, ang1 * X_DEGREES, ang2 * X_DEGREES);
            xo = x + dx;
            yo = y + dy;
        break;
        case GR_LASER:
            if (ps_state == PS_PATH_ON) {
                 (void)fprintf(lp, "S\n");
            }
            if (dx != dy) {
                (void)fprintf(lp,
		    "N %4.1f %4.1f %.1f %.1f %.1f %.1f ellipse S\n", scx(xctr),
		    scy(yctr), xrad, yrad, (float)ang1, (float)(ang1 + ang2));
            } else {
                (void)fprintf(lp, "N %4.1f %4.1f %.1f %.1f %.1f arc S\n",
                        scx(xctr), scy(yctr), xrad,
                        (float)ang1, (float)(ang1 + ang2) );
            }
            ps_state = PS_PATH_OFF;
	    ps_path_len = 0;
            xo = x + dx;
            yo = y + dy;
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_arc(%d, %d, %d, %d, %d, %d);\n", x, y,
	    dx, dy, ang1, ang2);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_circle()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw a circle.
 */

void 
gr_circle(int x, int y, int r)
{
    int xtmp, ytmp;
    unsigned int width, height;

    xtmp = x - r;                /* get the upper left coordinates, the */
    ytmp = y - r;                /* width, and the height of the rectangle */
    width = 2 * r;               /* which contains the circle */
    height = 2 * r; 

    switch(gr_state) {
    case GR_SCREEN:
	/*
	 * draw a circle inside a square.  start arc at 0 degrees and
	 * continue for 360 * 64 degrees
	 */
	XDrawArc(xdw->display, xdw->win, xdw->gc, xtmp, ytmp, width, height,
	    0, FULL_CIRCLE * X_DEGREES);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
        }
        (void)fprintf(lp, "N %.1f %.1f %d 0 360 arc S\n", scx(x), scy(y), r);
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
        break;
    case GR_PICT:
	pict_cmd(PICT_FRAMEOVAL);
	pict_i16(psc(y - r));
	pict_i16(psc(x - r));
	pict_i16(psc(y + r));
	pict_i16(psc(x + r));
	pict_out();
	break;
    case GR_WMF:
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_circle(%d, %d, %d);\n", x, y, r);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_circle_fill()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw a circle.
 */

void 
gr_circle_fill(int x, int y, int r)
{
    int xtmp, ytmp;
    unsigned int width, height;

    xtmp = x - r;                /* get the upper left coordinates, the */
    ytmp = y - r;                /* width, and the height of the rectangle */
    width = 2 * r;               /* which contains the circle */
    height = 2 * r; 

    switch(gr_state) {
    case GR_SCREEN:
	/*
	 * draw a circle inside a square.  start arc at 0 degrees and
	 * continue for 360 * 64 degrees
	 */
	XFillArc(xdw->display, xdw->win, xdw->gc, xtmp, ytmp, width, height,
	    0, FULL_CIRCLE * X_DEGREES);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
        }
        (void)fprintf(lp, "N %.1f %.1f %d 0 360 arc fill\n", scx(x), scy(y), r);
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
        break;
    case GR_PICT:
	pict_cmd(PICT_FRAMEOVAL);
	pict_i16(psc(y - r));
	pict_i16(psc(x - r));
	pict_i16(psc(y + r));
	pict_i16(psc(x + r));
	pict_out();
	break;
    case GR_WMF:
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_circle(%d, %d, %d);\n", x, y, r);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_poly()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Plots out a polygon.
 */

void 
gr_poly(XPoint *points,	int npoints)
{
    int i;
    int xmin, ymin, xmax, ymax;

    switch(gr_state) {
    case GR_SCREEN:
	XDrawLines(xdw->display, xdw->win, xdw->gc, points, npoints,
	    CoordModeOrigin);
	/*
	 * if the first and last points are not
	 * the same, then draw a line from the
	 * first point to the last point
	 */
	if ((points[0].x != points[npoints-1].x) ||
	    (points[0].y != points[npoints-1].y)) {
	    XDrawLine(xdw->display, xdw->win, xdw->gc, points[0].x,
		points[0].y, points[npoints-1].x, points[npoints-1].y);
	    }
	break;
    case GR_LASER:
	if (ps_state==PS_PATH_ON) {
	    (void)fprintf(lp, "S\n");
	}
	i = 0;
	(void)fprintf(lp,"N %.1f  %.1f M\n",scx(points[i].x), scy(points[i].y));
	for(i = 1; i < npoints; i++) {
	    (void)fprintf(lp, "  %4.1f  %4.1f D\n",
                scx(points[i].x), scy(points[i].y));
	}
        /*
         * if the first and last points are not
         * the same, then draw a line from the
         * first point to the last point
         */
        if ( (points[0].x != points[npoints-1].x) ||
             (points[0].y != points[npoints-1].y)) {
            (void)fprintf(lp, "  %4.1f  %4.1f D\n",
                scx(points[0].x), scy(points[0].y));
        }
	(void)fprintf(lp,"C S\n");
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
	break;
    case GR_PICT:
	xmin = xmax = points[0].x;
	ymin = ymax = points[0].y;
	for (i = 1; i < npoints; ++i) {
	    if (points[i].x < xmin) xmin = points[i].x;
	    if (points[i].x > xmax) xmax = points[i].x;
	    if (points[i].y < ymin) ymin = points[i].y;
	    if (points[i].y > ymax) ymax = points[i].y;
	}
	pict_cmd(PICT_FRAMEPOLY);
	pict_i16(10 + 4 * (npoints + 1));	/* nbytes */
	pict_i16(psc(ymin));			/* rectangle */
	pict_i16(psc(xmin));
	pict_i16(psc(ymax));
	pict_i16(psc(xmax));
	for (i = 0; i < npoints; ++i) {		/* points */
	    pict_i16(psc(points[i].y));
	    pict_i16(psc(points[i].x));
	}
	pict_i16(psc(points[0].y));
	pict_i16(psc(points[0].x));
	pict_out();
	break;
    case GR_WMF:
	wmf_cmd(WMF_POLYLINE);
	wmf_i16(npoints + 1);
	for (i = 0; i < npoints; ++i) {
	    wmf_i16(points[i].x);
	    wmf_i16(points[i].y);
	}
	wmf_i16(points[0].x);
	wmf_i16(points[0].y);
	wmf_out();
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    {\n\tXPoint p[] = {\n");
	for(i = 0; i < npoints - 1; i++) {
	    fprintf(debug_fp, "\t    { %d, %d },\n", points[i].x, points[i].y);
	}
	fprintf(debug_fp, "\t    { %d, %d }\n", points[i].x, points[i].y);
	fprintf(debug_fp, "\t};\n\n\tgr_poly(p, %d);\n    }\n",
	    npoints);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_rectangle()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw a rect, coords in pixels.
 */

void 
gr_rectangle(int x, int y, int dx, int dy)
{
    switch (gr_state) {
    case GR_SCREEN:
        XDrawRectangle(xdw->display, xdw->win, xdw->gc, x, y,
	    (unsigned int)dx, (unsigned int)dy);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf (lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        (void)fprintf(lp, "N %4.1f %4.1f M\n", scx(x), scy(y));
        (void)fprintf(lp, "  %4.1f %4.1f D\n", scx(x + dx), scy(y));
        (void)fprintf(lp, "  %4.1f %4.1f D\n", scx(x + dx), scy(y + dy));
        (void)fprintf(lp, "  %4.1f %4.1f D C S\n", scx(x), scy(y + dy));
        ps_state = PS_PATH_OFF;
	ps_path_len = 0;
        break;
    case GR_PICT:
	pict_cmd(PICT_FRAMERECT);
	pict_i16(psc(y));
	pict_i16(psc(x));
	pict_i16(psc(y + dy));
	pict_i16(psc(x + dx));
	pict_out();
	break;
    case GR_WMF:
	wmf_cmd(WMF_POLYLINE);
	wmf_i16(5);
	wmf_i16(x);
	wmf_i16(y);
	wmf_i16(x + dx);
	wmf_i16(y);
	wmf_i16(x + dx);
	wmf_i16(y + dy);
	wmf_i16(x);
	wmf_i16(y + dy);
	wmf_i16(x);
	wmf_i16(y);
	wmf_out();
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_rectangle(%d, %d, %d, %d);\n", x, y, dx, dy);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_clear()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	In screen mode, clear the screen.
 */

void 
gr_clear(void)
{
    switch(gr_state) {
    case GR_SCREEN:
	XClearWindow(xdw->display, xdw->win);
	break;
    case GR_LASER:
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_clear();\n");
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_page()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	In printer mode, output a page.
 */

void 
gr_page(void)
{
    switch(gr_state) {
    case GR_SCREEN:
	XClearWindow(xdw->display, xdw->win);
	break;
    case GR_LASER:
	if (ps_state == PS_PATH_ON) {
	    (void)fprintf(lp, "S\n");
	    ps_state = PS_PATH_OFF;
	    ps_path_len = 0;
	}
	(void)fprintf(lp, "%%%%\n");
	(void)fprintf(lp, "showpage\n");
	break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_page();\n");
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_poly_clear()
 *
 * INPUTS:
 *      points  - (XPoint *)
 *      npoints - (int)
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *      Clear (set to background color) the specified polygon.
 *
 */

void 
gr_poly_clear(XPoint *points, int npoints)
{
    int i;
    int xmin, ymin, xmax, ymax;
    XGCValues gc_val;

    switch(gr_state) {
    case GR_SCREEN:
        XGetGCValues(xdw->display, xdw->gc, GCForeground|GCBackground, &gc_val);
        XSetForeground(xdw->display, xdw->gc, gc_val.background);
        XFillPolygon(xdw->display, xdw->win, xdw->gc, points, npoints,
                     Complex, CoordModeOrigin);
        XSetForeground(xdw->display, xdw->gc, gc_val.foreground);
        break;
    case GR_LASER:
        if (ps_state==PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
        }
        i = 0;
        (void)fprintf(lp,"N %.1f  %.1f M\n",scx(points[i].x), scy(points[i].y));
        for(i = 1; i < npoints; i++) {
            (void)fprintf(lp, "  %4.1f  %4.1f D\n", scx(points[i].x),
                          scy(points[i].y));
        }
        (void)fprintf(lp, "1 setgray fill 0 setgray\n");
        break;
    case GR_PICT:
	xmin = xmax = points[0].x;
	ymin = ymax = points[0].y;
	for (i = 1; i < npoints; ++i) {
	    if (points[i].x < xmin) xmin = points[i].x;
	    if (points[i].x > xmax) xmax = points[i].x;
	    if (points[i].y < ymin) ymin = points[i].y;
	    if (points[i].y > ymax) ymax = points[i].y;
	}
	pict_cmd(PICT_ERASEPOLY);
	pict_i16(10 + 4 * npoints);		/* nbytes */
	pict_i16(psc(ymin));			/* rectangle */
	pict_i16(psc(xmin));
	pict_i16(psc(ymax));
	pict_i16(psc(xmax));
	for (i = 0; i < npoints; ++i) {		/* points */
	    pict_i16(psc(points[i].y));
	    pict_i16(psc(points[i].x));
	}
	pict_out();
	break;
    case GR_WMF:
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    {\n\tXPoint p[] = {\n");
	for(i = 0; i < npoints - 1; i++) {
	    fprintf(debug_fp, "\t    { %d, %d },\n", points[i].x, points[i].y);
	}
	fprintf(debug_fp, "\t    { %d, %d }\n", points[i].x, points[i].y);
	fprintf(debug_fp, "\t};\n\n\tgr_poly_clear(p, %d);\n    }\n",
	    npoints);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_rect_clear()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Clear (set to background color) the specified rectangle.
 */

void 
gr_rect_clear(int x, int y, int dx, int dy)
{
    switch (gr_state) {
    case GR_SCREEN:
	XSetForeground(xdw->display, xdw->gc, background);
        XFillRectangle(xdw->display, xdw->win, xdw->gc, x, y, (unsigned int)dx,
	    (unsigned int)dy);
        XSetForeground(xdw->display, xdw->gc, foreground);
	break;
    case GR_LASER:
	if (ps_state == PS_PATH_ON) {
	    (void)fprintf (lp, "S\n");
	    ps_state = PS_PATH_OFF;
	}
	(void)fprintf(lp, "N %.1f %.1f M\n", scx(x - 1), scy(y - 1));
	(void)fprintf(lp, "  %.1f %.1f D\n", scx(x + dx), scy(y - 1));
	(void)fprintf(lp, "  %.1f %.1f D\n", scx(x + dx), scy(y + dy));
	(void)fprintf(lp, "  %.1f %.1f D C\n", scx(x - 1), scy(y + dy));
	(void)fprintf(lp, "1 setgray fill 0 setgray\n");
	break;
    case GR_PICT:
	pict_cmd(PICT_ERASERECT);
	pict_i16(psc(y));
	pict_i16(psc(x));
	pict_i16(psc(y + dy));
	pict_i16(psc(x + dx));
	pict_out();
        break;
    case GR_WMF:
        break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_rect_clear(%d, %d, %d, %d);\n", x, y, dx, dy);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_poly_fill()
 *
 * INPUTS:
 *      points  - (XPoint *)
 *      npoints - (int)
 *	fcolor	- (char *)
 *      gs      - (int)
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *
 */

void 
gr_poly_fill(XPoint *points, int npoints, char *fcolor, int gs)
{
    int i;
    int xmin, ymin, xmax, ymax;
    XGCValues gc_val;

    switch(gr_state) {
    case GR_SCREEN:
        if (strcmp(fcolor, "") != 0) {
            XGetGCValues(xdw->display, xdw->gc, GCForeground, &gc_val);
            XSetForeground(xdw->display, xdw->gc, find_color_black(fcolor));
        }
        XDrawLines(xdw->display, xdw->win, xdw->gc, points, npoints,
                   CoordModeOrigin);
        XFillPolygon(xdw->display, xdw->win, xdw->gc, points, npoints,
                     Complex, CoordModeOrigin);
        if (strcmp(fcolor, "") != 0) {
            XSetForeground(xdw->display, xdw->gc, gc_val.foreground);
        }
        break;
    case GR_LASER:
        if (ps_state==PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
        }
        i = 0;
        (void)fprintf(lp,"N %.1f  %.1f M\n",scx(points[i].x), scy(points[i].y));
        for(i = 1; i < npoints; i++) {
            (void)fprintf(lp, "  %4.1f  %4.1f D\n", scx(points[i].x),
                          scy(points[i].y));
        }
        (void)fprintf(lp, "%.2f setgray fill 0 setgray\n", 1.0 - (gs * 0.01));
        break;
    case GR_PICT:
	xmin = xmax = points[0].x;
	ymin = ymax = points[0].y;
	for (i = 1; i < npoints; ++i) {
	    if (points[i].x < xmin) xmin = points[i].x;
	    if (points[i].x > xmax) xmax = points[i].x;
	    if (points[i].y < ymin) ymin = points[i].y;
	    if (points[i].y > ymax) ymax = points[i].y;
	}
	pict_cmd(PICT_FILLPOLY);
	pict_i16(10 + 4 * npoints);		/* nbytes */
	pict_i16(psc(ymin));			/* rectangle */
	pict_i16(psc(xmin));
	pict_i16(psc(ymax));
	pict_i16(psc(xmax));
	for (i = 0; i < npoints; ++i) {		/* points */
	    pict_i16(psc(points[i].y));
	    pict_i16(psc(points[i].x));
	}
	pict_out();
        break;
    case GR_WMF:
	wmf_cmd(WMF_POLYGON);
	wmf_i16(npoints);
	for (i = 0; i < npoints; ++i) {
	    wmf_i16(points[i].x);
	    wmf_i16(points[i].y);
	}
	wmf_out();
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    {\n\tXPoint p[] = {\n");
	for(i = 0; i < npoints - 1; i++) {
	    fprintf(debug_fp, "\t    { %d, %d },\n", points[i].x, points[i].y);
	}
	fprintf(debug_fp, "\t    { %d, %d }\n", points[i].x, points[i].y);
	fprintf(debug_fp, "\t};\n\n");
	fprintf(debug_fp, "\tgr_poly_fill(p, %d, \"%s\", %d);\n    }\n",
	    npoints, fcolor, gs);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_rect_fill()
 *
 * INPUTS:
 *      x       - (int)
 *      y       - (int)
 *      dx      - (int)
 *      dy      - (int)
 *	fcolor	- (char *)
 *      gs      - (int)
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	fill a rectangle
 *
 */

void 
gr_rect_fill(int x, int y, int dx, int dy, char *fcolor, int gs)
{
    XGCValues gc_val;

    switch (gr_state) {
    case GR_SCREEN:
        if (strcmp(fcolor, "") != 0) {
            XGetGCValues(xdw->display, xdw->gc, GCForeground, &gc_val);
            XSetForeground(xdw->display, xdw->gc, find_color_black(fcolor));
        }
        XDrawRectangle(xdw->display, xdw->win, xdw->gc, x, y, (unsigned int)dx,
	    (unsigned int)dy);
        XFillRectangle(xdw->display, xdw->win, xdw->gc, x, y, (unsigned int)dx,
	    (unsigned int)dy);
        if (strcmp(fcolor, "") != 0) {
            XSetForeground(xdw->display, xdw->gc, gc_val.foreground);
        }
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            fprintf (lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        (void)fprintf(lp, "N %.1f %.1f M\n", scx(x), scy(y));
        (void)fprintf(lp, "%.1f %.1f D\n", scx(x + dx), scy(y));
        (void)fprintf(lp, "%.1f %.1f D\n", scx(x + dx), scy(y + dy));
        (void)fprintf(lp, "%.1f %.1f D C\n", scx(x), scy(y + dy));
        (void)fprintf(lp, "%.2f setgray fill 0 setgray\n", 1.0 - (gs * 0.01));
        break;
    case GR_PICT:
	pict_cmd(PICT_FILLRECT);
	pict_i16(psc(y));
	pict_i16(psc(x));
	pict_i16(psc(y + dy));
	pict_i16(psc(x + dx));
	pict_out();
        break;
    case GR_WMF:
	wmf_cmd(WMF_POLYGON);
	wmf_i16(5);
	wmf_i16(x);
	wmf_i16(y);
	wmf_i16(x + dx);
	wmf_i16(y);
	wmf_i16(x + dx);
	wmf_i16(y + dy);
	wmf_i16(x);
	wmf_i16(y + dy);
	wmf_i16(x);
	wmf_i16(y);
	wmf_out();
        break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_rect_fill(%d, %d, %d, %d, \"%s\", %d);\n",
	    x, y, dx, dy, fcolor, gs);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_rect_shade()
 *
 * INPUTS:
 *      x       - (int)
 *      y       - (int)
 *      dx      - (int)
 *      dy      - (int)
 *      gs      - (int)
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	shades a rectangle to the printer and not to the screen.
 *
 */

void 
gr_rect_shade(int x, int y, int dx, int dy, int gc)
{
    switch (gr_state) {
        case GR_SCREEN:
            XDrawRectangle(xdw->display, xdw->win, xdw->gc, x, y,
		(unsigned int)dx, (unsigned int)dy);
            break;
        case GR_LASER:
            if (ps_state == PS_PATH_ON) {
                (void)fprintf (lp, "S\n");
                ps_state = PS_PATH_OFF;
            }
            (void)fprintf(lp, "N %4.1f %4.1f M\n", scx(x - 1), scy(y - 1));
            (void)fprintf(lp, "  %4.1f %4.1f D\n", scx(x - 1 + dx),
                scy(y - 1));
            (void)fprintf(lp, "  %4.1f %4.1f D\n", scx(x - 1 + dx),
                scy(y - 1 + dy));
            (void)fprintf(lp, "  %4.1f %4.1f D C\n", scx(x - 1),
                scy(y - 1 + dy));
            (void)fprintf(lp, "%.2f setgray fill 0 setgray\n", 1.0 -
		(gc * 0.01));
            ps_state = PS_PATH_ON;
            break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_rect_shade(%d, %d, %d, %d, %d);\n",
	    x, y, dx, dy, gc);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *      gr_hilight()
 *
 * INPUTS:
 *      x, y            - (int) upper left corner of highlight
 *      height, width   - (int) width and height of highlight
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *      This routine sets a highlight for a specified rectangle.
 */

void 
gr_hilight(int x, int y, int width, int height)
{
    switch (gr_state) {
    case GR_SCREEN:
        XSetForeground(xdw->display, xdw->gc, highlight);
        XFillRectangle(xdw->display, xdw->win, xdw->gc, x, y,
            (unsigned int)width, (unsigned int)height);
        XSetForeground(xdw->display, xdw->gc, foreground);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            (void)fprintf (lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        (void)fprintf(lp, "N %4.1f %4.1f M\n", scx(x - 1), scy(y - 1));
        (void)fprintf(lp, "  %4.1f %4.1f D\n", scx(x - 1 + width), scy(y - 1));
        (void)fprintf(lp, "  %4.1f %4.1f D\n",
                      scx(x - 1 + width), scy(y - 1 + height));
        (void)fprintf(lp, "  %4.1f %4.1f D C\n", scx(x - 1),
                      scy(y - 1 + height));
        (void)fprintf(lp, "%.2f setgray fill 0 setgray\n", .85);
        ps_state = PS_PATH_ON;
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_hilight(%d, %d, %d, %d);\n", x, y, width,
	    height);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	open_xfont()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Open a font.
 */

static void 
open_xfont(char *fontname, int fontindex)
{
    if ((x_fonts[nfonts].font_struct = XLoadQueryFont(xdw->display, fontname))
	== NULL) {
        (void)printf("Font %s not found\n", fontname);
        return;
    }
    XSetFont(xdw->display, xdw->gc, x_fonts[nfonts].font_struct->fid);
    x_fonts[nfonts].fontnumber = fontindex;
    cur_font = x_fonts[nfonts];
    nfonts++;
}

/***********************************************************************
 *
 * FUNCTION:
 *	screen_font()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Set screen font, if necessary open new fonts.
 */

static void 
screen_font(int font)
{
    int point;			/* point size for font */
    int fname;			/* font integer name */
    char *name;			/* font name for laser and screen */
    char fontname[64];		/* complete font name */
    int i;			/* index */

    if (cur_font.fontnumber == font) return;
    point = font & POINTMASK;
    fname = font & NAMEMASK;
    for (i = 0; i < nfonts; i++) {
	if (font == x_fonts[i].fontnumber) {
	    XSetFont(xdw->display, xdw->gc, x_fonts[i].font_struct->fid);
	    cur_font = x_fonts[i];
	    return;
	}
    }
    switch(fname) {
    case GR_COURIER:			name = "courier-medium-r";	break;
    case GR_COURIER_BOLD:		name = "courier-bold-r";	break;
    case GR_COURIER_ITALIC:		name = "courier-medium-o";	break;
    case GR_COURIER_BOLD_ITALIC:	name = "courier-bold-o";	break;
    case GR_HELVETICA:			name = "helvetica-medium-r";	break;
    case GR_HELVETICA_BOLD:		name = "helvetica-bold-r";	break;
    case GR_HELVETICA_ITALIC:		name = "helvetica-medium-o";	break;
    case GR_HELVETICA_BOLD_ITALIC:	name = "helvetica-bold-o";	break;
    case GR_TIMES:			name = "times-medium-r";	break;
    case GR_TIMES_BOLD:			name = "times-bold-r";		break;
    case GR_TIMES_ITALIC:		name = "times-medium-i";	break;
    case GR_TIMES_BOLD_ITALIC:		name = "times-bold-i";		break;
    case GR_SYMBOL:			name = "symbol-medium-r";	break;
    default:				name = "courier-medium-r";	break;
    }
    (void)sprintf(fontname, "*-%s-*--%d-*", name, point);
    open_xfont(fontname, font);        /* open it and set it */
}

/***********************************************************************
 *
 * FUNCTION:
 *	set_font()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Local function to set font for both screen and laser.
 */

static void 
set_font(int font)
{
    int point;			/* point size for font */
    int fname;			/* font integer name */
    char *name;			/* font name for laser and screen */
    int face;			/* PICT typeface */

    switch(gr_state) {
    case GR_SCREEN:
	screen_font(font);
        break;
    case GR_LASER:
	point = font & POINTMASK;
	fname = font & NAMEMASK;
	switch(fname) {
	case GR_COURIER:		name = "Courier";		break;
	case GR_COURIER_BOLD:		name = "Courier-Bold";		break;
	case GR_COURIER_ITALIC:		name = "Courier-Oblique";	break;
        case GR_COURIER_BOLD_ITALIC:	name = "Courier-BoldOblique";	break;
	case GR_HELVETICA:		name = "Helvetica";		break;
	case GR_HELVETICA_BOLD:		name = "Helvetica-Bold";	break;
	case GR_HELVETICA_ITALIC:	name = "Helvetica-Oblique";	break;
        case GR_HELVETICA_BOLD_ITALIC:	name = "Helvetica-BoldOblique";	break;
	case GR_TIMES:			name = "Times-Roman";		break;
	case GR_TIMES_BOLD:		name = "Times-Bold";		break;
	case GR_TIMES_ITALIC:		name = "Times-Italic";		break;
        case GR_TIMES_BOLD_ITALIC:	name = "Times-BoldItalic";	break;
	case GR_SYMBOL:			name = "Symbol";		break;
	default:			name = "Courier";		break;
	}
	(void)fprintf(lp,
            "/%s findfont\n %d scalefont \n setfont\n", name, point);
        break;
    case GR_PICT:
	if (font != pict_font) {
	    pict_font = font;
	    point = font & POINTMASK;
	    fname = font & NAMEMASK;
	    switch(fname) {
	    case GR_COURIER:			fname = 22; face = 0; break;
	    case GR_COURIER_BOLD:		fname = 22; face = 1; break;
	    case GR_COURIER_ITALIC:		fname = 22; face = 2; break;
            case GR_COURIER_BOLD_ITALIC:	fname = 22; face = 3; break;
	    case GR_HELVETICA:			fname = 21; face = 0; break;
	    case GR_HELVETICA_BOLD:		fname = 21; face = 1; break;
	    case GR_HELVETICA_ITALIC:		fname = 21; face = 2; break;
            case GR_HELVETICA_BOLD_ITALIC:	fname = 21; face = 3; break;
	    case GR_TIMES:			fname = 20; face = 0; break;
	    case GR_TIMES_BOLD:			fname = 20; face = 1; break;
	    case GR_TIMES_ITALIC:		fname = 20; face = 2; break;
            case GR_TIMES_BOLD_ITALIC:		fname = 20; face = 3; break;
	    case GR_SYMBOL:			fname = 23; face = 0; break;
	    default:				fname = 22; face = 0; break;
	    }
	    pict_cmd(PICT_TXFONT);
	    pict_i16(fname);
	    pict_cmd(PICT_TXFACE);
	    pict_set(face, 1);
	    pict_cmd(PICT_TXSIZE);
	    pict_i16(psc(point));
	    pict_out();
	}
        break;
    case GR_WMF:
	if (font != wmf_font) {
	    wmf_cmd(WMF_DELETEOBJECT);
	    wmf_i16(1);
	    wmf_out();
	
	    wmf_cmd(WMF_CREATEFONTINDIRECT);
	    wmf_i16(-(font & GR_FONT_SIZE));
	    wmf_i16(0);
	    wmf_i16(0);
	    wmf_i16(0);
	    wmf_i16((font & GR_BOLD) ? 700 : 400);
	    wmf_i16((font & GR_ITALIC) ? 1 : 0);
	    wmf_i16(0);
	    wmf_i16(0);
	    switch (font & GR_FONT_FAMILY) {
	    case GR_COURIER:
		wmf_i16(0x3000);
		wmf_str("Courier New", (int)strlen("Courier New") + 1);
		break;
	    case GR_HELVETICA:
		wmf_i16(0x2000);
		wmf_str("Arial", (int)strlen("Arial") + 1);
		break;
	    case GR_TIMES:
		wmf_i16(0x1000);
		wmf_str("Times New Roman", (int)strlen("Times New Roman") + 1);
		break;
	    case GR_SYMBOL:
		wmf_i16(0x5000);
		wmf_str("Symbol", (int)strlen("Symbol") + 1);
		break;
	    }
	    wmf_out();
	
	    wmf_cmd(WMF_SELECTOBJECT);
	    wmf_i16(1);
	    wmf_out();
	
	    wmf_font = font;
	}
	break;
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	char_mid()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find pixel midpoint of a string.
 */

static int 
char_mid(char *character)
{
    int mid;

    if (cur_font.font_struct->per_char == NULL) {
        mid = 0;
    } else  {
        mid = (cur_font.font_struct->per_char[character[0]].lbearing +
                (cur_font.font_struct->max_bounds.width -
                cur_font.font_struct->per_char[character[0]].rbearing)) / 2;
    }
    return(mid);
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_free_fonts()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Free all fonts that have been loaded.
 */

void 
gr_free_fonts(void)
{
    int i;

    for (i = 0; i < nfonts; i++) {
        XFreeFont(xdw->display, x_fonts[i].font_struct);
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_text()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw text at specified point.
 */

void 
gr_text(int x, int y, char *str, int just, int font, int mask)
{
    int xs;			/* shift of origin */
    int ydes;			/* y descent */
    int dx, dy;			/* text width, text height */
    int sx, sy;			/* start x & start y for laser */
    int i, j;			/* text indices */
    int length;			/* character length of text */
    char buf[256];

    xs = 0;
    set_font(font);
    length = strlen(str);
    dx = gr_strwidth(str, font);
    dy = gr_txheight(font);
    ydes = gr_txdescent(font);
    switch (just) {
    case GR_LEFT:
        xs = 0;
        break;
    case GR_CENTER:
        xs = dx / 2;
        break;
    case GR_RIGHT:
        xs = dx;
        break;
    }
    switch (gr_state) {
    case GR_SCREEN:
        if (mask) { /* white out the background rectangle */
            XDrawImageString(xdw->display, xdw->win, xdw->gc, x - xs,
                             y - ydes, str, length);
        } else {
	    XDrawString(xdw->display, xdw->win, xdw->gc, x - xs, y - ydes,
	                str, length);
        }
	break;
    case GR_LASER:
	if (ps_state == PS_PATH_ON) {
	    (void)fprintf(lp, "S\n");
	    ps_state = PS_PATH_OFF;
	}
	i = 0;
	j = 0;
	while (str[i]) {
	    switch (str[i]) {
	    case ')':
	    case '(':
	    case '\\':
		buf[j++] = '\\';
	    default:
		buf[j++] = str[i++];
	    }
	}
	buf[j] = '\0';
        sx = x - xs;
        sy = y - dy;
        if (mask) {
            (void)fprintf(lp, "N %.1f %.1f M\n", scx(sx), scy(sy));
            (void)fprintf(lp, "  %.1f %.1f D\n", scx(sx + dx), scy(sy));
            (void)fprintf(lp, "  %.1f %.1f D\n", scx(sx + dx), scy(sy + dy));
            (void)fprintf(lp, "  %.1f %.1f D C\n", scx(sx), scy(sy + dy));
            (void)fprintf(lp, "1 setgray fill 0 setgray\n");
        }
	(void)fprintf(lp, "%.1f %.1f M (%s) P%d\n", scx(x), scy(y-ydes),
	    buf, just);
	break;
    case GR_PICT:
	pict_cmd(PICT_LONGTEXT);
	pict_i16(psc(y - ydes));
	pict_i16(psc(x - xs));
	pict_set(length, 1);
	pict_str(str, length);
	pict_out();
	break;
    case GR_WMF:
	set_font(font);
	length = strlen(str);
	wmf_cmd(WMF_TEXTOUT);
	wmf_i16(length);
	wmf_str(str, length);
	wmf_i16(y - dy);
	wmf_i16(x - xs);
	wmf_out();
	break;
    case GR_OFF:
	break;
    case GR_DEBUG:
	fprintf(debug_fp, "    gr_text(%d, %d, \"%s\", %d, %d, %d);\n",
	    x, y, str, just, font, mask);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_rotate_text()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Draw rotated text at specified point.
 */

void 
gr_rotate_text(int x, int y, char *str, int font, int rotate)
{
    int ydes;			/* y descent */
    int i, j;			/* text indices */
    char buff[64];
    int length;			/* character length of text */
    unsigned int tex_length;	/* width in pixels of text */
    unsigned int tex_height;	/* height in pixels of text */
    Pixmap pixtemp1;		/* temporary pixmap image of text */
    Pixmap pixtemp2;		/* temporary rotated pixmap of image */
    XImage *xi1;		/* temporary image of text */
    XImage *xi2;		/* rotated image of text */
    unsigned long pixval;	/* pixel value */
    int old_x, old_y;		/* image position */
    int xrotate, yrotate;	/* rotated image position */

    if (! *str) {             /* null string */
        return;
    }
    set_font(font);
    length = strlen(str);
    ydes = gr_txdescent(font);
    tex_length = gr_strwidth(str, font);
    tex_height = gr_txheight(font);
    switch (gr_state) {
    case GR_SCREEN:
        pixtemp1 = XCreatePixmap(xdw->display, xdw->win, tex_length, tex_height,
	    (unsigned int)DefaultDepth(xdw->display, xdw->screen));
        pixtemp2 = XCreatePixmap(xdw->display, xdw->win, tex_height, tex_length,
	    (unsigned int)DefaultDepth(xdw->display, xdw->screen));
                                           /* draw and transfer to image */
        XDrawImageString(xdw->display, pixtemp1, xdw->gc, 0, (int)tex_height
	    - ydes, str, length);
        xi1 = XGetImage(xdw->display, pixtemp1, 0, 0, tex_length, tex_height,
                        AllPlanes, ZPixmap);
        xi2 = XGetImage(xdw->display, pixtemp2, 0, 0, tex_height, tex_length,
                        AllPlanes, ZPixmap);
        for (old_x = 0; old_x < tex_length; old_x++) {
            for (old_y = 0; old_y < tex_height; old_y++) {
                switch (rotate) {
                case GR_DEGREE90:
                    xrotate = old_y;
                    yrotate = tex_length - old_x - 1;
                    break;
                case GR_DEGREE270:
                    xrotate = tex_height - old_y - 1;
                    yrotate = old_x;
                    break;
                }
                pixval = XGetPixel(xi1, old_x, old_y);
                XPutPixel(xi2, xrotate, yrotate, pixval);
            }
        }
        switch (rotate) {
        case GR_DEGREE90:
            XPutImage(xdw->display, pixtemp2, xdw->gc, xi2, 0, 0,
                      0, 0, tex_height, tex_length);
            XCopyArea(xdw->display, pixtemp2, xdw->win, xdw->gc, 0, 0,
                      tex_height, tex_length, x - (int)tex_height,
		      y - (int)tex_length);
            break;
        case GR_DEGREE270:
            XPutImage(xdw->display, xdw->win, xdw->gc, xi2, 0, 0,
                      x, y, tex_height, tex_length);
            break;
        }
        XFreePixmap(xdw->display, pixtemp1);
        XFreePixmap(xdw->display, pixtemp2);
        XDestroyImage(xi1);
        XDestroyImage(xi2);
        break;
    case GR_LASER:
        if (ps_state == PS_PATH_ON) {
            fprintf(lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        i = 0;
        j = 0;
        while (str[i]) {
            switch (str[i]) {
            case ')':
            case '(':
            case '\\':
                buff[j++] = '\\';
            default:
                buff[j++] = str[i++];
            }
        }
        buff[j] = '\0';
        switch (rotate) {
        case GR_DEGREE90:
            fprintf(lp, "GS %.1f %.1f T %d R 0 0 M (%s) P%d GR\n",
                    scx(x-ydes), scy(y), rotate, buff, 0);
            break;
        case GR_DEGREE270:
            fprintf(lp, "GS %.1f %.1f T %d R 0 0 M (%s) P%d GR\n",
                    scx(x+ydes), scy(y), rotate, buff, 0);
            break;
        }
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_vert_text()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Add vertical text.
 */

void 
gr_vert_text(int x, int y, char *str, int just, int font, int delta)
{
    int length;
    int height;
    int ys, xmid;
    int i, j;
    int ydes;

    length = strlen(str);
    set_font(font);
    height = gr_txheight(font);
    ydes = gr_txdescent(font);
    y -= ydes;
    switch(just) {
    case GR_TOP:
        ys = 0;
        break;
    case GR_CENTER:
        ys = ((length * gr_txheight(font)) + ((length - 1) * delta)) / 2;
        break;
    case GR_BOTTOM:
        ys = (length * gr_txheight(font)) + ((length - 1) * delta);
        break;
    }
    ys -= gr_txheight(font);       /* text is referenced by the bottom */
    switch (gr_state) {
    case GR_SCREEN:
        j = length;
        for (i = 0; i < j; i++) {
            xmid = char_mid(str);
            XDrawString(xdw->display, xdw->win, xdw->gc, x+xmid,
                        y-ys+((height+delta)*i), str++, 1);
        }
        xo = x;
        yo = y;
        break;
    case GR_LASER:
        xmid = gr_txwidth(font)/2;
        if (ps_state == PS_PATH_ON) {
            (void)fprintf(lp, "S\n");
            ps_state = PS_PATH_OFF;
        }
        for (i = 0; str[i]; i++) {
            if ((str[i] == '(') || (str[i] == ')') ||
                (str[i] == '\\')) {
                (void)fprintf(lp, "%.1f %.1f M (\\%c) P%d\n", scx(x+xmid),
                        scy(y - ys + ((height+delta)*i)), str[i], 1);
            } else {
                (void)fprintf(lp, "%.1f %.1f M (%c) P%d\n", scx(x+xmid),
                        scy(y - ys + ((height+delta)*i)), str[i], 1);
            }
        }
        xo = x;
        yo = y;
        break;
    case GR_PICT:
	xmid = char_mid(str);
	pict_cmd(PICT_LONGTEXT);
	pict_i16(psc(y - ys));
	pict_i16(psc(x + xmid));
	pict_set(1, 1);
	pict_set(*(str++), 1);
	for (j = 1; j < length; ++j) {
	    pict_cmd(PICT_DVTEXT);
	    pict_set(psc(height + delta), 1);
	    pict_set(1, 1);
	    pict_set(*(str++), 1);
	}
	pict_out();
        break;
    case GR_WMF:
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_strwidth()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find the width of the string in pixels.
 */

int 
gr_strwidth(char *str, int font)
{
    int width;			/* width of string */
    int len;			/* character count of string */

    len = strlen(str);
    screen_font(font);
    width = XTextWidth(cur_font.font_struct, str, len);
    return(width);
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_txwidth()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find the maximum width of the specified font.
 */

int 
gr_txwidth(int font)
{
    screen_font(font);
    return(cur_font.font_struct->max_bounds.width);
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_txheight()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find the maximum height of the font.
 */

int 
gr_txheight(int font)
{
    int height;				/* height of font */

    screen_font(font);
    height = cur_font.font_struct->ascent + cur_font.font_struct->descent;
    return height ;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_txdescent()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find the descent of the font.
 */

int 
gr_txdescent(int font)
{
    screen_font(font);
    return cur_font.font_struct->descent;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_txascent()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Find the ascent of the font.
 */

int 
gr_txascent(int font)
{
    screen_font(font);
    return(cur_font.font_struct->ascent);
}

/***********************************************************************
 *
 * FUNCTION:
 *	find_color_black()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Allocate color, and default to black if unavailable.
 */

static unsigned int 
find_color_black(char *cname)
{
    int index;
    Colormap cmap;
    XColor color;

    cmap = DefaultColormap(xdw->display, xdw->screen);
    index = (xdw->iscolor && XParseColor(xdw->display, cmap, cname, &color)
        && XAllocColor(xdw->display, cmap, &color)) ? color.pixel :
        BlackPixel(xdw->display, xdw->screen);
    return index;
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_set_color()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Set foreground color.
 */

void 
gr_set_color(char *cname, int grayscale)
{
    switch (gr_state) {
    case GR_SCREEN:
	XSetForeground(xdw->display, xdw->gc, find_color_black(cname));
        break;
    case GR_LASER:
	/*
	 * the grayscale is defined so that 0 = white, 100 = black
	 * in PostScript, 1.0 = white, 0.0 = black
	 */
	(void)fprintf(lp, "%.2f setgray\n", 0.01 * (float)(100 - grayscale));
	break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_image()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	Graph image
 */

void 
gr_image(int x, int y, GRBitmap *fsimage, GRBitmap *rdimage, int scale)
{
    int i, k;				/* text indices */
    int length;				/* length of image on bytes */
    char out[80];			/* output buffer */
    static char hex[16] = {		/* bitmap hex to ascii conversion */
        'F', '7', 'B', '3', 'D', '5', '9', '1',
        'E', '6', 'A', '2', 'C', '4', '8', '0' };
    Pixmap temp_data;			/* 1d pixmap for image */

    switch(gr_state) {			/* clear image data space */
    case GR_SCREEN:
	temp_data = XCreatePixmapFromBitmapData(xdw->display, xdw->win,
	    rdimage->bits, rdimage->width, rdimage->height, 1, 0, 1);
	XCopyPlane(xdw->display, temp_data, xdw->win, xdw->gc, 0, 0,
	    rdimage->width, rdimage->height, x, y, 1);
	XFreePixmap(xdw->display, temp_data);
	break;
    case GR_LASER:
	if (ps_state==PS_PATH_ON) {
	    (void)fprintf(lp, "S\n");
	}
	(void)fprintf(lp, "gsave /picstr 39 string def %.1f %.1f translate\n",
	    scx(x), scy(y + fsimage->height / scale));
	(void)fprintf(lp, "%.1f %.1f scale %d %d 1 [%d 0 0 -%d 0 %d]\n",
	    scx(fsimage->width / scale ), scx(fsimage->height / scale),
	    fsimage->width, fsimage->height, fsimage->width, fsimage->height,
	    fsimage->height);;
	(void)fprintf(lp, "{currentfile picstr readhexstring pop} image\n");
	length = fsimage->height * ((fsimage->width - 1) / 8 + 1);
	k = 0;
	for (i = 0; i < length; i++) {
	    out[k++] = hex[(*(fsimage->bits + i)) & 0xf];
	    out[k++] = hex[((*((fsimage->bits + i))) >> 4) & 0xf];
	    if (k == 78) {
		out[k] = '\0';
		(void)fprintf(lp, "%s\n", out);
		k = 0;
	    }
	}
	if (k) {
	    while (k < 78) out[k++] = '0';
	    out[k] = '\0';
	    (void)fprintf(lp, "%s\n", out);
	}
	(void)fprintf(lp, "grestore\n");
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
	break;
    case GR_DEBUG:

	/*
	 * full size image
	 */
	fprintf(debug_fp, "    {\n\tchar fsbits[] = {");
	length = fsimage->height * ((fsimage->width - 1) / 8 + 1);
	for(i = 0; i < length; i++) {
	    if ((i % 11) == 0) {
		fprintf(debug_fp, "\n\t    ");
	    } else {
		fprintf(debug_fp, " ");
	    }
	    fprintf(debug_fp, "0x%02x", fsimage->bits[i] & 0xff);
	    if (i < length - 1) {
		fprintf(debug_fp, ",");
	    }
	}
	fprintf(debug_fp, "\n\t};\n\tGRBitmap fsimage = { %d, %d, fsbits };\n",
	    fsimage->width, fsimage->height);

	/*
	 * reduced image
	 */
	fprintf(debug_fp, "\tchar rdbits[] = {");
	length = rdimage->height * ((rdimage->width - 1) / 8 + 1);
	for(i = 0; i < length; i++) {
	    if ((i % 11) == 0) {
		fprintf(debug_fp, "\n\t    ");
	    } else {
		fprintf(debug_fp, " ");
	    }
	    fprintf(debug_fp, "0x%02x", rdimage->bits[i] & 0xff);
	    if (i < length - 1) {
		fprintf(debug_fp, ",");
	    }
	}
	fprintf(debug_fp, "\n\t};\n\tGRBitmap rdimage = { %d, %d, rdbits };\n",
	    rdimage->width, rdimage->height);

	/*
	 * command
	 */
	fprintf(debug_fp, "\n\tgr_image(%d, %d, &fsimage, &rdimage, %d);\n"
	    "    }\n", x, y, scale);
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_set_clip()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	set graph clip region
 */

void 
gr_set_clip(int x, int y, int dx, int dy, Region orig_clip_region)
{
    XRectangle rectangle;

    switch(gr_state) {				/* clear image data space */
    case GR_SCREEN:
        save_clip_region = orig_clip_region;
        rectangle.x = x;
        rectangle.y = y;
        rectangle.width = dx;
        rectangle.height = dy;
        if (sub_region == NULL) sub_region = XCreateRegion();
        if (sub_region2 == NULL) sub_region2 = XCreateRegion();
        XUnionRectWithRegion(&rectangle, sub_region, sub_region);
        if (save_clip_region != NULL) {
            XIntersectRegion(save_clip_region, sub_region, sub_region2);
            XSetRegion(xdw->display, xdw->gc, sub_region2);
        } else {
            XSetRegion(xdw->display, xdw->gc, sub_region);
        }
        break;
    case GR_LASER:
	/*
	 * the space at the end is important in not
	 * getting the macros concantenated.
	 */
        fprintf(lp, "S GS %.1f %.1f M %.1f %.1f RL %.1f %.1f RL %.1f %.1f RL ",
            scx(x), scy(y), scx(dx), (double)0, (double)0, (double)-dy,
            scx(-dx), (double)0);
        fprintf(lp, "C clip N\n");
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	gr_reset_clip()
 *
 * INPUTS:
 *
 * OUTPUTS:
 *
 * RETURNS:
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *
 * DESCRIPTION:
 *	reset graph clip region	
 */

void 
gr_reset_clip(void)
{
    switch(gr_state) {				/* clear image data space */
    case GR_SCREEN:
        if (sub_region != NULL) {
            XDestroyRegion(sub_region);
            sub_region = NULL;
        }
        if (sub_region2 != NULL) {
            XDestroyRegion(sub_region2);
            sub_region2 = NULL;
        }
        if (save_clip_region != NULL) {
            XSetRegion(xdw->display, xdw->gc, save_clip_region);
            save_clip_region = NULL;
        }
        break;
    case GR_LASER:
        fprintf(lp, "S GR\n");
        break;
    case GR_PICT:
    case GR_WMF:
    case GR_OFF:
    case GR_DEBUG:
	break;
    }
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_open()
 *
 * INPUTS:
 *	file			- (char *) output filename
 *	apple_single		- (int) type of file
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	0			- on success
 *	-1			- on failure
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	pict_fp			- (FILE *) output file pointer
 *	pict_font		- (int) current font
 *	pict_apple_single	- (int) type of file
 *	out_buffer		- (char *) output buffer
 *	out_avail		- (int) size of output buffer
 *	out_len			- (long) length of current record
 *
 * DESCRIPTION:
 *	Open and initialize a Mac PICT file.
 */

static int
pict_open(char *name, int apple_single)
{
    if ((pict_fp = fopen(name, "w")) == NULL) {
	return -1;
    }
    if (out_buffer == NULL) {
	out_avail = BATCH_SIZE;
	out_buffer = (char *)malloc(out_avail);
    }
    out_len = 0;

    pict_apple_single = apple_single;
    pict_font = -1;

    if (pict_apple_single) {
	/*
	 * start Apple Single header
	 */
	pict_i32(APPLE_SINGLE_MAGIC);
	pict_i32(APPLE_SINGLE_VERSION);
	pict_set(0, 16);
	pict_i16(2);		/* number of entries */
    
	pict_i32(APPLE_SINGLE_FINDER_INFO);
	pict_i32(50);		/* offset */
	pict_i32(32);		/* length */
    
	pict_i32(APPLE_SINGLE_DATA_FORK);
	pict_i32(82);		/* offset */
	pict_i32(0);		/* length of PICT data (fix later) */
    
	pict_str("PICT????", 8);
	pict_set(0, 24);
    
	pict_out();
    }

    /*
     * start PICT file
     */
    pict_set(0, 512);
    pict_out();

    pict_i16(0);		/* length of PICT data (fix later) */
    pict_i16(0);
    pict_i16(0);
    pict_i16(psc(p_height));
    pict_i16(psc(p_width));

    pict_cmd(PICT_VERSION);
    pict_set(1, 1);

    pict_cmd(PICT_CLIP);
    pict_i16(10);
    pict_i16(0);
    pict_i16(0);
    pict_i16(psc(p_height));
    pict_i16(psc(p_width));

    pict_cmd(PICT_PNMODE);
    pict_i16(9);		/* patOr */

    pict_out();

    return 0;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_cmd()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a command to the output buffer.
 */

static void
pict_cmd(int val)
{
    if (out_len + 1 > out_avail) {
	out_avail += BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    out_buffer[out_len++] = val & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_i16()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a 16 bit integer to the output buffer.
 */

static void
pict_i16(int val)
{
    if (out_len + 2 > out_avail) {
	out_avail += BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    out_buffer[out_len++] = (val >> 8) & 0xff;
    out_buffer[out_len++] = val & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_i32()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a 32 bit integer to the output buffer.
 */

static void
pict_i32(long val)
{
    if (out_len + 4 > out_avail) {
	out_avail += BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    out_buffer[out_len++] = (val >> 24) & 0xff;
    out_buffer[out_len++] = (val >> 16) & 0xff;
    out_buffer[out_len++] = (val >> 8) & 0xff;
    out_buffer[out_len++] = val & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_str()
 *
 * INPUTS:
 *	ptr		- (char *) pointer to string
 *	len		- (int) length of string
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a string to the metafile buffer.
 */

static void
pict_str(char *ptr, int len)
{
    if (out_len + len > out_avail) {
	out_avail = (((out_len + len - 1) / BATCH_SIZE) + 1) * BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    memcpy(out_buffer + out_len, ptr, (unsigned int)len);
    out_len += len;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_set()
 *
 * INPUTS:
 *	val		- (int) value to set
 *	len		- (int) length to set
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Repeat set a value to the output string.
 */

static void
pict_set(int val, int len)
{
    if (out_len + len > out_avail) {
	out_avail = (((out_len + len - 1) / BATCH_SIZE) + 1) * BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    memset(out_buffer + out_len, val, (unsigned int)len);
    out_len += len;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_out()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	pict_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	out_buffer	- (char *) output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Output the metafile buffer.
 */

static void
pict_out(void)
{
    fwrite(out_buffer, 1, out_len, pict_fp);
    out_len = 0;
}

/***********************************************************************
 *
 * FUNCTION:
 *	pict_close()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *
 * EXTERNALLY MODIFIED:
 *	pict_fp		- (FILE *) output file pointer
 *
 * DESCRIPTION:
 *	Close the metafile.
 */

static void
pict_close(void)
{
    long loc;

    /*
     * end PICT file
     */
    pict_cmd(PICT_END);
    pict_out();

    /*
     * get length of file
     *
     *	data_len = loc - 82;
     *  pict_len = data_len - 512;
     */
    loc = ftell(pict_fp);

    if (pict_apple_single) {
	/*
	 * fix data length in Apple Single header
	 */
	fseek(pict_fp, 46L, 0);
	loc -= 82L;
	pict_i32(loc);
	pict_out();
    }

    /*
     * fix PICT length in PICT header
     */
    fseek(pict_fp, (pict_apple_single) ? (512L + 82L) : 512L, 0);
    loc -= 512L;
    pict_i16(loc & 0xffff);
    pict_out();

    /*
     * close PICT file
     */
    fclose(pict_fp);
    pict_fp = NULL;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_open()
 *
 * INPUTS:
 *	file		- (char *) output filename
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	0		- on success
 *	-1		- on failure
 *
 * EXTERNALLY READ:
 *	none
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	wmf_font	- (int) current font
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Open and initialize a Windows Metafile.
 */

static int
wmf_open(char *name)
{
    short inch;
    short cksum;

    if ((wmf_fp = fopen(name, "wb")) == NULL) {
	return -1;
    }
    if (out_buffer == NULL) {
	out_avail = BATCH_SIZE;
	out_buffer = (char *)malloc(out_avail);
    }
    wmf_size = 9;
    wmf_maxrec = 0;

    inch = (p_mode & GR_EXPAND) ? 100 : 75;
    cksum = 0x9ac6 ^ 0xcdd7 ^ p_width ^ p_height ^ inch;

    out_len = 0;

    wmf_i32((int)0x9ac6cdd7);	/* magic number */
    wmf_i16(0);			/* hmf (reserved) */
    wmf_i16(0);			/* left */
    wmf_i16(0);			/* top */
    wmf_i16(p_width);		/* right */
    wmf_i16(p_height);		/* bottom */
    wmf_i16(inch);		/* inch */
    wmf_i32(0);			/* reserved */
    wmf_i16(cksum);		/* checksum */

    wmf_i16(1);			/* type */
    wmf_i16(9);			/* header size */
    wmf_i16(0x300);		/* version */
    wmf_i32(0);			/* size */
    wmf_i16(3);			/* no. objects */
    wmf_i32(0);			/* max record */
    wmf_i16(0);			/* no. parameters */

    fwrite(out_buffer, 1, out_len, wmf_fp);

    wmf_cmd(WMF_SETMAPMODE);
    wmf_i16(8);
    wmf_out();

    wmf_cmd(WMF_SETWINDOWORG);
    wmf_i16(0);
    wmf_i16(0);
    wmf_out();

    wmf_cmd(WMF_SETWINDOWEXT);
    wmf_i16(p_height);
    wmf_i16(p_width);
    wmf_out();

    wmf_cmd(WMF_CREATEPENINDIRECT);
    wmf_i16(0);
    wmf_i16(1);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(0);
    wmf_out();

    wmf_cmd(WMF_SELECTOBJECT);
    wmf_i16(0);
    wmf_out();

    wmf_font = GR_HELVETICA | 10;

    wmf_cmd(WMF_CREATEFONTINDIRECT);
    wmf_i16(-10);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(400);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(8192);
    wmf_str("Arial", (int)strlen("Arial") + 1);
    wmf_out();

    wmf_cmd(WMF_SELECTOBJECT);
    wmf_i16(1);
    wmf_out();

    wmf_cmd(WMF_CREATEBRUSHINDIRECT);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(0);
    wmf_i16(0);
    wmf_out();

    wmf_cmd(WMF_SELECTOBJECT);
    wmf_i16(2);
    wmf_out();

    return 0;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_cmd()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a command to the metafile buffer.
 */

static void
wmf_cmd(int val)
{
    out_len = 4;
    out_buffer[out_len++] = val & 0xff;
    out_buffer[out_len++] = (val >> 8) & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_i16()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a 16 bit integer to the metafile buffer.
 */

static void
wmf_i16(int val)
{
    if (out_len + 2 > out_avail) {
	out_avail += BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    out_buffer[out_len++] = val & 0xff;
    out_buffer[out_len++] = (val >> 8) & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_i32()
 *
 * INPUTS:
 *	val		- (int) number to output
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a 32 bit integer to the metafile buffer.
 */

static void
wmf_i32(long val)
{
    if (out_len + 4 > out_avail) {
	out_avail += BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    out_buffer[out_len++] = val & 0xff;
    out_buffer[out_len++] = (val >> 8) & 0xff;
    out_buffer[out_len++] = (val >> 16) & 0xff;
    out_buffer[out_len++] = (val >> 24) & 0xff;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_str()
 *
 * INPUTS:
 *	ptr		- (char *) pointer to string
 *	len		- (int) length of string
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	out_buffer	- (char *) output buffer
 *	out_avail	- (int) size of output buffer
 *	out_len		- (long) length of current record
 *
 * DESCRIPTION:
 *	Write a string to the metafile buffer.
 */

static void
wmf_str(char *ptr, int len)
{
    if (out_len + len > out_avail) {
	out_avail = (((out_len + len - 1) / BATCH_SIZE) + 1) * BATCH_SIZE;
	out_buffer = (char *)realloc(out_buffer, out_avail);
    }
    memcpy(out_buffer + out_len, ptr, (unsigned int)len);
    out_len += len + (len % 2);
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_out()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * EXTERNALLY MODIFIED:
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *	out_buffer	- (char *) output buffer
 *
 * DESCRIPTION:
 *	Output the metafile buffer.
 */

static void
wmf_out(void)
{
    long len;

    len = out_len / 2;
    out_buffer[0] = len & 0xff;
    out_buffer[1] = (len >> 8) & 0xff;
    out_buffer[2] = (len >> 16) & 0xff;
    out_buffer[3] = (len >> 24) & 0xff;
    fwrite(out_buffer, 1, out_len, wmf_fp);
    wmf_size += out_len / 2;
    if (wmf_maxrec < out_len / 2) wmf_maxrec = out_len / 2;
}

/***********************************************************************
 *
 * FUNCTION:
 *	wmf_close()
 *
 * INPUTS:
 *	none
 *
 * OUTPUTS:
 *	none
 *
 * RETURNS:
 *	none
 *
 * EXTERNALLY READ:
 *	wmf_size	- (long) output file size
 *	wmf_maxrec	- (long) maximum record size
 *
 * EXTERNALLY MODIFIED:
 *	wmf_fp		- (FILE *) output file pointer
 *
 * DESCRIPTION:
 *	Close the metafile.
 */

static void
wmf_close(void)
{
    wmf_cmd(0);
    wmf_out();

    out_len = 0;
    wmf_i32(wmf_size);
    fseek(wmf_fp, 28L, 0);
    fwrite(out_buffer, 1, 4, wmf_fp);

    out_len = 0;
    wmf_i32(wmf_maxrec);
    fseek(wmf_fp, 34L, 0);
    fwrite(out_buffer, 1, 4, wmf_fp);

    fclose(wmf_fp);
}
