/*	Copyright 1986, 1987, 1988, 1989, 1990 Ithaca Software, Alameda, CA	 */

/*** 
	Driver notes:
		. if actor name ends with "eps", an EPS file will be created. 
		  on the MAC, this is a file of type "EPSF" instead of "TEXT" 
		  
		. on b&w printers, images will be automatically rendered as gray scales.
		  this driver has NOT been tested on color printers.		
***/


#include		"hoops.h"
#include		"database.h"
#include		"driver.h"


#ifndef POSTSCRIPT

global boolean HD_Postscript_Driver (actor, request)
	stack		Driver			*actor;
	stack		int				request; {
	extern		boolean			HD_No_Driver ();

	return HD_No_Driver (actor, request, "Postscript");
}

#else

#include		"patterns.h"
#include		"driver.h"

#ifdef	MAX
#	undef		MAX
#endif
#ifdef	MIN
#	undef		MIN
#endif

#include		<stdio.h>

#ifdef	MAC
#  undef		TRUE
#  undef		FALSE
#  undef		true
#  undef		false
#  define		Point	qd_Point
#  define		Style	qd_Style	 
#  ifdef applec				 
#		include <Files.h>		  
#		define C_TO_PSTR		c2pstr	   
#		define P_TO_CSTR		p2cstr	   
#  endif
#  ifdef THINK_C
#		include <FileMgr.h>		  
#		define C_TO_PSTR		CtoPstr		
#		define P_TO_CSTR		PtoCstr		
#  endif					 
#  undef		Style				
#  undef		Point
#  undef		true
#  undef		false
#  undef		TRUE
#  undef		FALSE
#  define		true	((boolean)-1) 
#  define		false	((boolean)0)
#  define		TRUE	(-1)
#  define		FALSE	0
#endif

#ifdef	MAX
#	undef		MAX
#endif
#ifdef	MIN
#	undef		MIN
#endif
#define MIN(a, b)		((a) < (b) ? (a) : (b))
#define MAX(a, b)		((a) > (b) ? (a) : (b))

#define EPSF_FILETYPE					1L
#define COLOR_RESOLUTION				256
#define RESOLUTION						300
#define DOT_RATIO						(RESOLUTION / 72.0)
#define PIXAR_SCALE						(RESOLUTION / 72.0)

#define PATTERN_WIDTH					3.0
#define PPLINE							30
#define PPDLINE							42
#define WPLINE							18
#define WPDLINE							24
#define PPDOT							6
#define WPDOT							4
#define PPCHKB							12
#define WPCHKB							8

#define MAX_CHAR_PER_LINE				80

typedef struct {
	FILE				*fp;
	boolean				EPSF_driver;
	short				page_count;
	short				pixar_gray_hscale,
						pixar_gray_vscale,
						pixar_gray_xtran,
						pixar_gray_ytran,
						pixar_gray_ncolumn,
						pixar_gray_lastcol_scale;
	short				pixar_rgb_hscale,
						pixar_rgb_vscale,
						pixar_rgb_xtran,
						pixar_rgb_ytran,
						pixar_rgb_ncolumn,
						pixar_rgb_lastcol_scale;	 
	Incarnation			last_clipping,
						last_rendition;
	float				last_lineweight;
	Whichever_Color		last_color;
	int					last_linepattern;
	int					next_font_num,
						last_font_prev_page;
}	PS_Data;

local	void			new_clipping();
local	void			get_actions();

global boolean HD_Postscript_Driver (actor, request)
	stack		Driver			*actor;
	stack		int				request; {
	extern		boolean			HD_Connect_Standard_Driver();

	return HD_Connect_Standard_Driver (actor, request, ADDR (get_actions));
}

local void set_mac_file_type (dc) 
	register	Display_Context			*dc;  {

#	ifdef MAC
	{
		register		PS_Data			*state =(PS_Data *) dc->data;
		register		FILE			*fp = state->fp;
		stack			short			volume_num;
		stack			char			volume_name[256];
		stack			FInfo			file_info;		
		
		C_TO_PSTR (dc->actor_name);
		
		volume_name[0] ='\0';
		C_TO_PSTR (volume_name);
		GetVol (volume_name, &volume_num);
		
		GetFInfo (dc->actor_name, volume_num, &file_info);
		
		file_info.fdCreator = 0x3F3F3F3F;				/* '????' */
			
		if (state->EPSF_driver)	 
			file_info.fdType = 0x45505346;				/* 'EPSF' */
		else
			file_info.fdType = 0x54455854;				/* 'TEXT' */
				
		SetFInfo (dc->actor_name, volume_num, &file_info);
		P_TO_CSTR (dc->actor_name);
	}
#	endif
}


local void set_color (state, color)
	stack		PS_Data					*state;
	register	Driver_Color			*color; {
	register	FILE					*fp = state->fp;

	if (state->last_color != color->whichever) {
		if (color->direct_rgb.r == color->direct_rgb.g &&
				color->direct_rgb.r == color->direct_rgb.b)
			fprintf (fp, "%.2f SG ",
						(int) color->direct_rgb.r / (float) COLOR_RESOLUTION);
		else
			fprintf (fp, "%.2f %.2f %.2f SRGB ",
						(int) color->direct_rgb.r / (float) COLOR_RESOLUTION,
						(int) color->direct_rgb.g / (float) COLOR_RESOLUTION,
						(int) color->direct_rgb.b / (float) COLOR_RESOLUTION);

		state->last_color = color->whichever;
	}
}


local boolean start_device (dc) 
	stack		Display_Context			*dc; {
	register	PS_Data					*state;
	stack		FILE					*fp;
	stack		char					*cp;

	if ((fp = fopen (dc->actor_name, "w")) == null) {
	   HI_Error (Sprintf_S(null, 
		 "Can not open Postscript driver output file - %s",
		 dc->actor_name));
	   return false;
	}
   
	ALLOC (state, PS_Data);
	dc->data = (void *) state;

	state->fp = fp;
	
	cp = dc->actor_name + dc->actor_name_length - 2;
	
	if (dc->actor_name_length > 3 && *cp == 's' && 
									 *(cp - 1) == 'p' && 
									 *(cp - 2) == 'e') 
		state->EPSF_driver = true;	  
	else 
		state->EPSF_driver = false;
	
	state->page_count = 0;

#	ifdef		MW
		if (isatty(fileno(fp))) putc ('\04', fp);
#	endif

	state->last_font_prev_page = 0;
	state->next_font_num = 1;

	return true;
}

local void stop_device (dc) 
	stack		Display_Context			*dc;  {
	stack		PS_Data					*state =(PS_Data *) dc->data;
	register	FILE					*fp = state->fp;

	fprintf (fp, "cleartomark end\n");
	fprintf (fp, "%%%%Trailer\n");

	fclose (fp);
	
	set_mac_file_type (dc);
}

local void get_physical_info (dc) 
	register	Display_Context			*dc; {

	if (dc->options.landscape_orientation) {
		dc->physical.number_of_pixels.x = RESOLUTION * 10.0;
		dc->physical.number_of_pixels.y = RESOLUTION *	7.5;
		dc->physical.size.x = 10.0 * 2.54;
		dc->physical.size.y =  7.5 * 2.54;
	}
	else {
		dc->physical.number_of_pixels.x = RESOLUTION *	7.5;
		dc->physical.number_of_pixels.y = RESOLUTION * 10.0;
		dc->physical.size.x =	7.5 * 2.54;
		dc->physical.size.y =  10.0 * 2.54;
	}

	dc->physical.offset_in_pixels.x = RESOLUTION * 0.5;
	dc->physical.offset_in_pixels.y = RESOLUTION * 0.5;
	
	dc->current.number_of_pixels.x = dc->physical.number_of_pixels.x;
	dc->current.number_of_pixels.y = dc->physical.number_of_pixels.y;

	dc->current.offset_in_pixels.x = dc->physical.offset_in_pixels.x;
	dc->current.offset_in_pixels.y = dc->physical.offset_in_pixels.y;

	dc->current.size.x = dc->physical.size.x;
	dc->current.size.y = dc->physical.size.y;

	dc->physical.color_system = DIRECT_RGB;
	dc->physical.nominal_color_resolution = COLOR_RESOLUTION;
	dc->physical.needs_gamma_correction = false;
	
	dc->physical.can_double_buffer = false;
	dc->physical.has_display = true;
	dc->physical.has_locater = false;
	dc->physical.has_keyboard = false;
	dc->physical.windowing_system = false;

	dc->physical.driver_type = "PostScript";
	dc->physical.display_type = "laser printer";

	dc->physical.max_polygon_points = 1000; /* something reasonable */
	dc->physical.max_polyline_points = 1000; /* something reasonable */
}

local void define_patterns (state) 
	stack		PS_Data			*state; {
	register	FILE			*fp = state->fp;
		 
	fprintf (fp,
		"/SPL {%.2f LW [] 0 SD NP} bind def\n", PATTERN_WIDTH);

	fprintf (fp,
		"/HBARS {/I exch def /H exch def /W exch def /Y exch def\n");
	fprintf (fp,
		" /X exch def SPL I I H {Y add X exch MT W 0 RLT} for SK} bind def\n");
	   
	fprintf (fp,
		"/VBARS {/I exch def /H exch def /W exch def /Y exch def\n");
	fprintf (fp,
		" /X exch def SPL I I W {X add Y MT 0 H RLT} for SK} bind def\n");

	fprintf (fp,
		"/CROSSH {5 copy HBARS VBARS} bind def\n");


	fprintf (fp,
		"/SLANTR {/I exch def /MAX exch def /Y exch def pop /X exch def SPL\n");
	fprintf (fp,
		" I I MAX {/D exch def X Y D sub MT X D add Y LT} for SK} bind def\n");

	fprintf (fp,
		"/SLANTL {/I exch def /MAX exch def pop /Y exch def /X exch def SPL\n");
	fprintf (fp,
		" I I MAX {/D exch def X D add Y MT X Y D add LT} for SK} bind def\n");

	fprintf (fp,
		"/DIAMONDS {5 copy SLANTR SLANTL} bind def\n");

	  
	fprintf (fp,
		"/SQUARE {/PROC exch def /SIZE exch def /Y1 exch def /X1 exch def\n");
	fprintf (fp,
		" /X2 {X1 SIZE add} def /Y2 {Y1 SIZE add} def\n");
	fprintf (fp,
		" SLP X1 Y1 MT X1 Y2 LT X2 Y2 LT X2 Y1 LT CP PROC} bind def\n");
		   
	fprintf (fp,
		"/SQDOTS {/SIZE exch def /I exch def /H exch def /W exch def\n");
	fprintf (fp,
		" /Y exch def /X exch def I I H {Y add /YLEVEL exch def\n"); 
	fprintf (fp,
		" I I W {X add YLEVEL SIZE {FL} SQUARE} for} for} bind def\n");

	fprintf (fp,
		"/CHECKBRD {/SIZE exch def /I exch def /H exch def /W exch def\n");
	fprintf (fp,
		" /Y exch def /X exch def I I H {Y add /YLEVEL exch def\n"); 
	fprintf (fp,
		" I I W {X add YLEVEL SIZE {SK} SQUARE} for} for} bind def\n");
}

local void define_reencode (state)
	register	PS_Data					*state; {
	register	FILE					*fp = state->fp;

	fprintf (fp, "/reencodedict 12 dict def /ReEncode ");
	fprintf (fp, "{reencodedict begin /newname exch def\n");
	fprintf (fp, "/basefont exch def /basefontdict ");
	fprintf (fp, "basefont findfont def /newfont\n");
	fprintf (fp, "basefontdict maxlength dict def ");
	fprintf (fp, "basefontdict {exch dup /FID ne {dup\n");
	fprintf (fp, "/Encoding eq {exch dup length array ");
	fprintf (fp, "copy newfont 3 1 roll put}{exch newfont\n");
	fprintf (fp, "3 1 roll put} ifelse}{pop pop} ifelse} ");
	fprintf (fp, "forall newfont /FontName newname put\n");
	fprintf (fp, "EncVec aload length 2 idiv {newfont ");
	fprintf (fp, "/Encoding get 3 1 roll put} repeat\n");
	fprintf (fp, "newname newfont definefont pop end} def\n");

	fprintf (fp, "/EncVec [ 8#244 /currency 8#246 ");
	fprintf (fp, "/.notdef 8#250 /hungarumlaut 8#251\n");
	fprintf (fp, "/.notdef 8#252 /ordfeminine 8#254 ");
	fprintf (fp, "/.notdef 8#255 /endash 8#256 /.notdef\n");
	fprintf (fp, "8#257 /emdash 8#260 /ring 8#261 ");
	fprintf (fp, "/.notdef 8#262 /.notdef 8#263 /.notdef\n");
	fprintf (fp, "8#264 /acute 8#265 /.notdef 8#267 ");
	fprintf (fp, "/periodcentered 8#270 /cedilla 8#271\n");
	fprintf (fp, "/.notdef 8#272 /ordmasculine 8#274 ");
	fprintf (fp, "/.notdef 8#275 /.notdef 8#276 /.notdef\n");
	fprintf (fp, "8#300 /Agrave 8#301 /Aacute 8#302 ");
	fprintf (fp, "/Acircumflex 8#303 /Atilde 8#304\n");
	fprintf (fp, "/Adieresis 8#305 /Aring 8#306 /AE ");
	fprintf (fp, "8#307 /Ccedilla 8#310 /Egrave 8#311\n");
	fprintf (fp, "/Eacute 8#312 /Ecircumflex 8#313 ");
	fprintf (fp, "/Edieresis 8#314 /Igrave 8#315 /Iacute\n");
	fprintf (fp, "8#316 /Icircumflex 8#317 /Idieresis ");
	fprintf (fp, "8#320 /.notdef 8#321 /Ntilde 8#322\n");
	fprintf (fp, "/Ograve 8#323 /Oacute 8#324 ");
	fprintf (fp, "/Ocircumflex 8#325 /Otilde 8#326\n");
	fprintf (fp, "/Odieresis 8#327 /.notdef 8#330 ");
	fprintf (fp, "/Oslash 8#331 /Ugrave 8#332 /Uacute\n");
	fprintf (fp, "8#333 /Ucircumflex 8#334 /Udieresis ");
	fprintf (fp, "8#335 /.notdef 8#336 /.notdef 8#337\n");
	fprintf (fp, "/germandbls 8#340 /agrave 8#341 ");
	fprintf (fp, "/aacute 8#342 /acircumflex 8#343\n");
	fprintf (fp, "/atilde 8#344 /adieresis 8#345 /aring ");
	fprintf (fp, "8#346 /ae 8#347 /ccedilla 8#350\n");
	fprintf (fp, "/egrave 8#351 /eacute 8#352 ");
	fprintf (fp, "/ecircumflex 8#353 /edieresis 8#354\n");
	fprintf (fp, "/igrave 8#355 /iacute 8#356 ");
	fprintf (fp, "/icircumflex 8#357 /idieresis 8#360\n");
	fprintf (fp, "/.notdef 8#361 /ntilde 8#362 /ograve ");
	fprintf (fp, "8#363 /oacute 8#364 /ocircumflex 8#365\n");
	fprintf (fp, "/otilde 8#366 /odieresis 8#367 ");
	fprintf (fp, "/.notdef 8#370 /oslash 8#371 /ugrave\n");
	fprintf (fp, "8#372 /uacute 8#373 /ucircumflex 8#374 ");
	fprintf (fp, "/udieresis 8#375 /.notdef 8#376\n");
	fprintf (fp, "/.notdef 8#377 /ydieresis 8#200 /acute ");
	fprintf (fp, "8#201 /grave 8#202 /circumflex 8#203\n");
	fprintf (fp, "/tilde 8#204 /dieresis 8#205 /cedilla ");
	fprintf (fp, "8#206 /ring 8#207 /dotlessi ] def\n");

	fprintf (fp, "/Courier /RCourier ReEncode ");
	fprintf (fp, "/Courier-Oblique /RCourier-Oblique ReEncode\n");
	fprintf (fp, "/Courier-Bold /RCourier-Bold ReEncode\n");
	fprintf (fp, "/Courier-BoldOblique /RCourier-BoldOblique ReEncode\n");
	fprintf (fp, "/Helvetica /RHelvetica ReEncode ");
	fprintf (fp, "/Helvetica-Bold /RHelvetica-Bold ReEncode\n");
	fprintf (fp, "/Helvetica-Oblique /RHelvetica-Oblique ReEncode\n");
	fprintf (fp, "/Helvetica-BoldOblique /RHelvetica-BoldOblique ReEncode\n");
	fprintf (fp, "/Times-Roman /RTimes-Roman ReEncode ");
	fprintf (fp, "/Times-Italic /RTimes-Italic ReEncode\n");
	fprintf (fp, "/Times-Bold /RTimes-Bold ReEncode ");
	fprintf (fp, "/Times-BoldItalic /RTimes-BoldItalic ReEncode\n");

}



local long get_outer_window (dc)
	stack		Display_Context			*dc; {
	stack		PS_Data					*state = (PS_Data *) dc->data;
	register	FILE					*fp = state->fp;

	if (state->page_count == 0) {
		if (!state->EPSF_driver) 
			fprintf (fp, "%%!PS-Adobe-2.0\n");
		else 
			fprintf (fp, "%%!PS-Adobe-2.0 EPSF-1.2\n");
	  
		fprintf (fp, "%%%%BoundingBox: %d %d %d %d\n", 
		(int) ((float) dc->outer.extent.left / RESOLUTION * 72.0 + 0.5),  
		(int) ((float) dc->outer.extent.bottom / RESOLUTION * 72.0 + 0.5),	
		(int) ((float) dc->outer.extent.right / RESOLUTION * 72.0 + 0.5),  
		(int) ((float) dc->outer.extent.top / RESOLUTION * 72.0 + 0.5));
		
		fprintf (fp, "%%%%EndComments\n");

		/* define useful macros to save space in output file */
		fprintf (fp,
			"240 dict begin\n/GS {gsave} bind def /GR {grestore} bind def\n");
		fprintf (fp,
			"/NP {newpath} bind def /CP {closepath} bind def\n");
		fprintf (fp,
			"/TR {translate} bind def /MT {moveto} bind def\n");
		fprintf (fp,
			"/LT {lineto} bind def /RLT {rlineto} bind def\n");
		fprintf (fp,
			"/EL {/yr exch def /xr exch def /y exch def /x exch def\n");
		fprintf (fp,
			" /mtrx matrix currentmatrix def x y translate xr yr scale\n");
		fprintf (fp,
			" 0 0 1 0 360 arc mtrx setmatrix} bind def\n");
		fprintf (fp,
			"/SK {stroke} bind def /FL {fill} bind def /CL {clip} bind def\n");
		fprintf (fp,
			"/SRGB {setrgbcolor} bind def /SD {setdash} bind def\n");
		fprintf (fp,
			"/LW {setlinewidth} bind def /SG {setgray} bind def\n");
		fprintf (fp,
			"/FA {false} bind def /IM {image} bind def\n");
		fprintf (fp,
			"/CM {colorimage} bind def /IE {ifelse} bind def\n");
		fprintf (fp,
			"/SC {scale} bind def /S {save} bind def /R {restore} bind def\n");
		fprintf (fp,
			"/SP {showpage} bind def\n");
		fprintf (fp,
			"/LANDSCAPE {8.5 72 mul 0 translate 90 rotate} bind def\n");
		fprintf (fp,
			"/UN_LANDSCAPE {-90 rotate -8.5 72 mul 0 translate} bind def\n");
		fprintf (fp,
			"/PAGE_SCALE {72 %d div dup scale} bind def\n", RESOLUTION);
		fprintf (fp,
			"/UN_PSCALE {%d 72 div dup scale} bind def\n",RESOLUTION); 

		define_patterns (state);
		define_reencode (state);
	  
		fprintf (fp, "%%%%EndProlog\n");
		fprintf (fp, "mark\n");
	}

	state->last_clipping = state->last_rendition = 0;

	return (long) fp;
}

local void free_outer_window (dc)
	stack		Display_Context			*dc; {
}
 
local void init_picture (dc) 
	stack		Display_Context			*dc;  {
	register	PS_Data					*state = (PS_Data *) dc->data;
	register	FILE					*fp = state->fp;
	
	++state->page_count;

	state->last_clipping = state->last_rendition = 0;
	state->last_lineweight = 0;
	state->last_color = -1;
	state->last_linepattern = -1;
	
	fprintf (fp, "%%%%Page: ? %d\n", state->page_count);
	
	if (dc->options.landscape_orientation) fprintf (fp, "LANDSCAPE ");

	fprintf (fp, "S PAGE_SCALE ");
	fprintf (fp, "GS\n");
}

local void finish_picture (dc, swap_buffers) 
	stack		Display_Context			*dc;  
	stack		boolean					swap_buffers; {
	stack		PS_Data					*state = (PS_Data *) dc->data;
	register	FILE					*fp = state->fp;

	fprintf (fp, "GR R	");
	if (!state->EPSF_driver) fprintf (fp, "SP");
	fprintf (fp, "\n");

	fflush (fp);

	state->last_font_prev_page = state->next_font_num - 1;
}

local void new_clipping (tr)
	register	Transform_Rendition		*tr; {
	register	PS_Data					*state = (PS_Data *)tr->h.data;
	register	FILE					*fp = state->fp;
	stack		Display_Context			*dc = tr->h.display_context;

	/* set new clipping bounds */
	if (dc->options.debug > 0) fprintf(fp, "%% new clipping\n");
	fprintf(fp, "GR GS\n");
	fprintf(fp, 
		"NP %d %d MT %d %d LT %d %d LT %d %d LT CP CL\n",
		tr->hard_clip.left, tr->hard_clip.bottom, 
		tr->hard_clip.left, tr->hard_clip.top,
		tr->hard_clip.right, tr->hard_clip.top,
		tr->hard_clip.right, tr->hard_clip.bottom);

	state->last_clipping = tr->h.incarnation;

	/* reset the graphics state	 */
	state->last_color = -1;
	state->last_linepattern = -1;
	state->last_lineweight = -1;
}
		
local void new_line_rendition (lr)
	register			Line_Rendition	*lr;  {
	register			PS_Data			*state = (PS_Data *)lr->h.data;
	stack				Display_Context *dc = lr->h.display_context;
	register			FILE			*fp = state->fp;

	if (lr->h.transform_rendition->h.incarnation != state->last_clipping) 
		new_clipping (lr->h.transform_rendition);

	if (dc->options.debug > 0) fprintf (fp, "%% new line rendition\n");

	if (state->last_linepattern != lr->pattern) {
		switch (lr->pattern) {
			case LP_SOLID: {
				fprintf (fp, "[] 0 SD ");
			}	break;

			case LP_DASHDOT: {
				fprintf (fp, "[%d %d %d %d] 0 SD ",
					(int) (19 * DOT_RATIO), (int) (8 * DOT_RATIO), 
					(int) DOT_RATIO, (int) (8 * DOT_RATIO));
			}	break;
			case LP_DASHED: {
				fprintf (fp, "[%d] 0 SD ", (int) (9 * DOT_RATIO));
			}	break;
			case LP_DOTTED: {
				fprintf (fp, "[0 %d %d 0] 0 SD ",
						 (int) (7 * DOT_RATIO), (int) DOT_RATIO);
			}	break;
			default: {
				HI_Error ("Invalid line pattern sent to PostScript driver");
			}	break;
		}

		state->last_linepattern = lr->pattern;
	}

	set_color (state, &lr->color);
		
	if (state->last_lineweight != lr->weight) {
		fprintf (fp, "%.2f LW", (DOT_RATIO / 2.0 * lr->weight));
		state->last_lineweight = lr->weight;
	}

	fprintf (fp, "\n");

	state->last_rendition = lr->h.incarnation;
}		
		
local void draw_dc_polyline (lr, count, points)
	stack		Line_Rendition			*lr;
	stack		int						count;
	stack		DC_Point				*points; {
	stack		PS_Data					*state = (PS_Data *)lr->h.data;
	register	FILE					*fp = state->fp;
	register	int						pair_count;
	register	DC_Point				*end_point = points + count;
	stack		Display_Context			*dc = lr->h.display_context;
		
	if (lr->h.incarnation != state->last_rendition)
		new_line_rendition (lr);

	if (dc->options.debug > 0) fprintf(fp, "%% polyline\n");

	fprintf (fp, "NP %d %d MT ", points->x, points->y);
	pair_count = 2; 
	while (++points != end_point) {
		if (++pair_count > 6) {
			fprintf (fp, "\n");
			pair_count = 0;
		}
		fprintf (fp, "%d %d LT ", points->x, points->y);
	}
	fprintf (fp, "SK\n");
}

local void draw_dc_outline_ellipse (lr, where, radii)
	stack		Line_Rendition			*lr;
	stack		DC_Point				*where;
	stack		DC_Point				*radii; {
	stack		PS_Data					*state = (PS_Data *)lr->h.data;
	register	FILE					*fp = state->fp;
	stack		Display_Context			*dc = lr->h.display_context;
		
	if (dc->options.debug > 0) fprintf(fp, "%% circumference\n");

	if (lr->h.incarnation != state->last_rendition)
		new_line_rendition (lr);

	fprintf (fp, "NP %d %d %d %d EL SK\n", where->x, where->y,
										radii->x, radii->y);
}

local void draw_dc_rectangle (fr, left, right, bottom, top)
	register	Face_Rendition			*fr;
	stack		int						left, right, bottom, top; {
	stack		PS_Data					*state = (PS_Data *) fr->h.data;
	register	FILE					*fp = state->fp;
	stack		Display_Context			*dc = fr->h.display_context;

	if (fr->h.transform_rendition->h.incarnation != state->last_clipping) 
		new_clipping (fr->h.transform_rendition);

	if (dc->options.debug > 0) fprintf (fp, "%% rectangle\n");

	fprintf (fp, "NP %d %d MT %d %d LT %d %d LT %d %d LT CP\n",
		left, bottom, left, top, right, top, right, bottom);

	set_color (state, &fr->color);

	if (fr->pattern == FP_SOLID)
		fprintf (fp, "FL\n");
	else {
		fprintf (fp, "GS FL GR GS CL ");
		set_color (state, &fr->contrast_color);

		switch (fr->pattern) {
			case FP_WINDOW_VERTICAL_BARS: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, right - left, top - bottom);
				fprintf (fp, "%d VBARS GR\n", WPLINE);
			}	break;

			case FP_WINDOW_HORIZONTAL_BARS: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, right - left, top - bottom);
				fprintf (fp, "%d HBARS GR\n", WPLINE);
			}	break;

			case FP_WINDOW_CROSSHATCH: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, right - left, top - bottom);
				fprintf (fp, "%d CROSSH GR\n", WPLINE);
			}	break;

			case FP_WINDOW_SLANT_RIGHT: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, top, right - left + top - bottom);
				fprintf (fp, "%d SLANTR GR\n", WPDLINE);
			}	break;

			case FP_WINDOW_SLANT_LEFT: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, top, right - left + top - bottom);
				fprintf (fp, "%d SLANTL GR\n", WPDLINE);
			}	break;

			case FP_WINDOW_DIAMONDS: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, top, right - left + top - bottom);
				fprintf (fp, "%d DIAMONDS GR\n", WPDLINE);
			}	break;

			case FP_WINDOW_SQUARE_DOTS: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, right - left, top - bottom);
				fprintf (fp, "%d %d SQDOTS GR\n", WPLINE, WPDOT);
			}	break;

			case FP_WINDOW_CHECKERBOARD: {
				fprintf (fp, "%d %d %d %d ", 
					left, bottom, right - left, top - bottom);
				fprintf (fp, "%d %d CHECKBRD GR\n", WPLINE, WPCHKB);
			}	break;

			default: {
				HI_Error ("Invalid window pattern sent to PostScript driver");
			}	break;		  
		}
	}

	state->last_rendition = fr->h.incarnation;
}


local void draw_dc_ellipse (fr, where, radii)
	stack		Face_Rendition			*fr;
	stack		DC_Point				*where;
	stack		DC_Point				*radii; {
	stack		PS_Data					*state = (PS_Data *)fr->h.data;
	register	FILE					*fp = state->fp;
	stack		Display_Context			*dc = fr->h.display_context;
		
	if (dc->options.debug > 0) fprintf(fp, "%% circle\n");

	if (fr->h.transform_rendition->h.incarnation != state->last_clipping) 
		new_clipping (fr->h.transform_rendition);
	set_color (state, &fr->color);
	state->last_rendition = fr->h.incarnation;

	fprintf (fp, "NP %d %d %d %d EL FL\n", where->x, where->y,
										radii->x, radii->y);
}


local void draw_dc_face (fr, count, points) 
	stack		Face_Rendition			*fr;
	stack		int						count;
	stack		DC_Point				*points; {
	stack		PS_Data					*state = (PS_Data *) fr->h.data;
	stack		short					xmin, ymin, xmax, ymax;
	register	FILE					*fp = state->fp;
	register	int						pair_count;
	register	DC_Point				*end_point = points + count-1;
	stack		Display_Context			*dc = fr->h.display_context;

	if (fr->h.transform_rendition->h.incarnation != state->last_clipping) 
		new_clipping (fr->h.transform_rendition);

	if (dc->options.debug > 0) fprintf (fp, "%% face\n");

	if (fr->pattern != FP_SOLID) {	  
	   xmin = xmax = points->x;
	   ymin = ymax = points->y;
	}
	fprintf (fp, "NP %d %d MT ", points->x, points->y);
	  
	pair_count = 2;
	while (++points <= end_point){
	  
		if (fr->pattern != FP_SOLID) {	  
			xmin = MIN (xmin, points->x);
			xmax = MAX (xmax, points->x);
			ymin = MIN (ymin, points->y);
			ymax = MAX (ymax, points->y);
		}

		if (++pair_count > 6) {
			fprintf (fp, "\n");
			pair_count = 0;
		}
		fprintf (fp, "%d %d LT ", points->x, points->y);
	}

	fprintf (fp, "CP\n");

	set_color (state, &fr->color);

	if (fr->pattern == FP_SOLID)
		fprintf (fp, "FL\n");
	else {
		fprintf (fp, "GS FL GR GS CL ");
		set_color (state, &fr->contrast_color);

		switch (fr->pattern) {
			case FP_VERTICAL_BARS: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, xmax - xmin, ymax - ymin);
				fprintf (fp, "%d VBARS GR\n", PPLINE);
			}	break;

			case FP_HORIZONTAL_BARS: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, xmax - xmin, ymax - ymin);
				fprintf (fp, "%d HBARS GR\n", PPLINE);
			}	break;

			case FP_CROSSHATCH: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, xmax - xmin, ymax - ymin);
				fprintf (fp, "%d CROSSH GR\n", PPLINE);
			}	break;

			case FP_SLANT_RIGHT: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, ymax, xmax - xmin + ymax - ymin);
				fprintf (fp, "%d SLANTR GR\n", PPDLINE);
			}	break;

			case FP_SLANT_LEFT: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, ymax, xmax - xmin + ymax - ymin);
				fprintf (fp, "%d SLANTL GR\n", PPDLINE);
			}	break;

			case FP_DIAMONDS: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, ymax, xmax - xmin + ymax - ymin);
				fprintf (fp, "%d DIAMONDS GR\n", PPDLINE);
			}	break;

			case FP_SQUARE_DOTS: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, xmax - xmin, ymax - ymin);
				fprintf (fp, "%d %d SQDOTS GR\n", PPLINE, PPDOT);
			}	break;

			case FP_CHECKERBOARD: {
				fprintf (fp, "%d %d %d %d ", 
					xmin, ymin, xmax - xmin, ymax - ymin);
				fprintf (fp, "%d %d CHECKBRD GR\n", PPLINE, PPCHKB);
			}	break;

			default: {
				HI_Error ("Invalid face pattern sent to PostScript driver");
			}	break;		  
		}
	}	 
	state->last_rendition = fr->h.incarnation;
}


local void pixar_to_rgb_and_gray_rasters (mr, point, 
								 vcount, hoffset, hcount,
								 pixar_rasters, action)
	stack		Misc_Rendition			*mr;
	stack		DC_Point				*point;
	stack		int						vcount, hoffset, hcount;
	stack		Color_Index				**pixar_rasters; 
	stack		void					(*action)(); {
	register	Driver_Color			*dcolor;
	register	Driver_Color			*map;
	extern		boolean					HD_Exit_Update();
	stack		unsigned char			*gray_raster;
	stack		Direct_RGB				*rgb_raster;
	stack		int						gray_size, rgb_size;
	
	map = mr->color_map_rendition->driver_colors;

	gray_size = hcount * sizeof(unsigned char);
	VALLOC (gray_raster, gray_size, unsigned char);

	rgb_size = hcount * sizeof(Direct_RGB);
	VALLOC (rgb_raster, rgb_size, Direct_RGB);
	
	do {
		register		unsigned char	*gray_out = gray_raster;
		register		Direct_RGB		*rgb_out = rgb_raster;
		register		Color_Index		*in = *pixar_rasters++ + hoffset;
		register		Direct_RGB		*end_of_raster = rgb_raster + hcount;
		stack			unsigned char	r, g, b;

		do {
		  dcolor = map + *in++;
		  r = rgb_out->r = (unsigned char) dcolor->direct_rgb.r;
		  g = rgb_out->g = (unsigned char) dcolor->direct_rgb.g;
		  b = rgb_out->b = (unsigned char) dcolor->direct_rgb.b;
		  *gray_out++ = (unsigned char)
				(((int) (r)) * 0.4 + ((int) (g)) * 0.3 + ((int) (b)) * 0.3);
		}					
		until (++rgb_out == end_of_raster);
		
		--point->y;
		(*action) (mr, point, hcount, gray_raster, rgb_raster);
	}
	until (--vcount == 0 ||
		   (OUR (timer_expired) && HD_Exit_Update (mr->h.display_context)));

	VFREE (gray_raster, gray_size, unsigned char);
	VFREE (rgb_raster, rgb_size, Direct_RGB);
}


local void draw_rgb_and_gray_raster (mr, point, hcount, gray_raster, 
									 rgb_raster)
	stack		Misc_Rendition			*mr;
	stack		DC_Point				*point;
	stack		int						hcount;
	stack		unsigned char			*gray_raster; 
	stack		Direct_RGB				*rgb_raster; {
	register	PS_Data					*state = (PS_Data *) mr->h.data;
	register	FILE					*fp = state->fp;
	register	unsigned char			*gray_out = gray_raster;
	register	Direct_RGB				*rgb_out = rgb_raster;
	register	short					i;

	/* The standard technique using {currentfile picstr readhexstring pop} to
	   read a large chunk of hex data doesn't work with the following 
	   conditional statement 
		   systemdict /colorimage known {proc1} {proc2} ifelse
		   
	   Have to feed PS one small chunk of hex at a time in the form
		   width height bits_per_pix [matrix] {<hexhexhexhex>} image
	*/
	
	/* write one rgb raster */

	fprintf (fp, "systemdict /colorimage known {\n");
	
	fprintf (fp, "%d %d TR %d 1 8 MX {<", 
		state->pixar_rgb_xtran, state->pixar_rgb_ytran,
		state->pixar_rgb_hscale);
		  
	for (i=0; i <  state->pixar_rgb_hscale; ++i) {
	   fprintf (fp, "%02X%02X%02X", rgb_out->r, rgb_out->g, rgb_out->b); 
	   ++rgb_out;
	}

	fprintf (fp, ">} FA 3 CM");
		
	{
	  register	short			d = 0;
	  register	short			n; 
	  
	  for (n = state->pixar_rgb_ncolumn - 1;
		   n > 0;  --n) {

		 fprintf (fp, "\n");

		 if (state->pixar_rgb_hscale != state->pixar_rgb_lastcol_scale	
			 && n == 1) {
			 
		   fprintf (fp, "1 0 TR GS %5.3f 1 SC %d 1 SC\n", 
			 1./(float) state->pixar_rgb_hscale, 
			 state->pixar_rgb_lastcol_scale, 
			 state->pixar_rgb_lastcol_scale);
			 
		   fprintf (fp, "%d 1 8 [%d 0 0 1 0 0] {<",
			 state->pixar_rgb_lastcol_scale,
			 state->pixar_rgb_lastcol_scale);
		 
		   for (i=0; i < state->pixar_rgb_lastcol_scale; ++i) {
			  fprintf (fp, "%02X%02X%02X", rgb_out->r, rgb_out->g, rgb_out->b); 
			  ++rgb_out;
		   }

		   fprintf (fp, ">} FA 3 CM GR");
		 }
		 
		 else {
		   fprintf (fp, "1 0 TR %d 1 8 MX {<",
			 state->pixar_rgb_hscale);
		 
		   for (i=0; i < state->pixar_rgb_hscale; ++i) {
			   fprintf (fp, "%02X%02X%02X", rgb_out->r, rgb_out->g, rgb_out->b); 
			   ++rgb_out;
		   }
					
		   fprintf (fp, ">} FA 3 CM");
		 }
		 
		 ++d;
	  } 
	  
	  if (d > 0) state->pixar_rgb_xtran = -d;
	  state->pixar_rgb_ytran = -1;
	}  
	fprintf (fp, "}\n");
	
	
	/* write one gray raster */
	
	fprintf (fp, "{%d %d TR %d 1 8 MX {<", 
		state->pixar_gray_xtran, state->pixar_gray_ytran,
		state->pixar_gray_hscale);
		  
	for (i=0; i <  state->pixar_gray_hscale; ++i) 
	   fprintf (fp, "%02X", *gray_out++); 

	fprintf (fp, ">} IM");
		
	{
	  register	short	d = 0;
	  register	short	n; 
	  
	  for (n = state->pixar_gray_ncolumn - 1;
		   n > 0;  --n) {

		 fprintf (fp, "\n");

		 if (state->pixar_gray_hscale != state->pixar_gray_lastcol_scale  
			 && n == 1) {
			 
		   fprintf (fp, "1 0 TR GS %5.3f 1 SC %d 1 SC\n", 
			 1./(float) state->pixar_gray_hscale, 
			 state->pixar_gray_lastcol_scale);
			 
		   fprintf (fp, "%d 1 8 [%d 0 0 1 0 0] {<",
			 state->pixar_gray_lastcol_scale, 
			 state->pixar_gray_lastcol_scale);
		 
		   for (i=0; i < state->pixar_gray_lastcol_scale; ++i) 
			 fprintf (fp, "%02X", *gray_out++);
		 
		   fprintf (fp, ">} IM GR");
		 }
		 
		 else {
		   fprintf (fp, "1 0 TR %d 1 8 MX {<",
			 state->pixar_gray_hscale);
		 
		   for (i=0; i < state->pixar_gray_hscale; ++i) 
			 fprintf (fp, "%02X", *gray_out++);
		 
		   fprintf (fp, ">} IM");
		 }
		 
		 ++d;
	  } 
	  
	  if (d > 0) state->pixar_gray_xtran = -d;
	  state->pixar_gray_ytran = -1;
	}  
	
	fprintf (fp, "} IE\n");		 
	state->last_rendition = mr->h.incarnation;
}

local void draw_dc_overlay_face (fr, count, points, bg) 
	stack		Face_Rendition			*fr;
	stack		int						count;
	stack		DC_Point				*points;
	stack		Driver_Color			*bg; {

	draw_dc_face (fr, count, points);
}

local void draw_dc_overlay_polyline (lr, count, points, bg)
	stack		Line_Rendition			*lr;
	stack		int						count;
	stack		DC_Point				*points;
	stack		Driver_Color			*bg; {

	draw_dc_polyline (lr, count, points);
}

local void draw_dc_pixel_array (mr, point, vcount, hoffset, hcount, rasters)
	stack		Misc_Rendition			*mr;
	stack		DC_Point				*point; 
	stack		int						vcount, hoffset, hcount;
	stack		Color_Index				**rasters; {
	register	PS_Data					*state = (PS_Data *) mr->h.data;
	register	FILE					*fp = state->fp;
	stack		Display_Context			*dc = mr->h.display_context;
				 
	{
#	   define	CHAR_USED		32
								/* make sure max_byte is multiple of 2 */
	   register short			max_byte = (MAX_CHAR_PER_LINE - CHAR_USED) / 2;

	   state->pixar_gray_xtran = 0;
	   state->pixar_gray_ytran = -1;
	   state->pixar_gray_vscale = 1;

	   if (hcount > max_byte) {
		  state->pixar_gray_hscale = 
			 state->pixar_gray_lastcol_scale = max_byte;
		  state->pixar_gray_ncolumn = hcount / max_byte;
		  
		  if ((hcount % max_byte) > 0) {
			 state->pixar_gray_ncolumn++;
			 state->pixar_gray_lastcol_scale = hcount % max_byte;
		  }
	   }
	   else {
		  state->pixar_gray_hscale = 
			  state->pixar_gray_lastcol_scale = hcount;
		  state->pixar_gray_ncolumn = 1;
	   }
	}

	{
#	   undef	CHAR_USED
#	   define	CHAR_USED		36
								/* make sure max_byte is a multiple of 6 */
	   register short			max_byte = (MAX_CHAR_PER_LINE - CHAR_USED) / 6;

	   state->pixar_rgb_xtran = 0;
	   state->pixar_rgb_ytran = -1;
	   state->pixar_rgb_vscale = 1;

	   if (hcount > max_byte) {
		  state->pixar_rgb_hscale = 
			 state->pixar_rgb_lastcol_scale = max_byte;
		  state->pixar_rgb_ncolumn = hcount / max_byte;
		  
		  if ((hcount % max_byte) > 0) {
			 state->pixar_rgb_ncolumn++;
			 state->pixar_rgb_lastcol_scale = hcount % max_byte;
		  }
	   }
	   else {
		  state->pixar_rgb_hscale = 
			  state->pixar_rgb_lastcol_scale = hcount;
		  state->pixar_rgb_ncolumn = 1;
	   }
	}

	/* if printer supports colorimage command, use it 
	   else use image command and create an equivalent gray scale image */
	if (dc->options.debug > 0) fprintf (fp, "%% pixel array\n");

	fprintf (fp, "GS %d %d TR UN_PSCALE	 ", point->x, point->y);
	
	fprintf (fp, "systemdict /colorimage known\n");

	fprintf (fp, "{%d %d SC /MX {[%d 0 0 1 0 0]} def} ",
	   state->pixar_rgb_hscale, state->pixar_rgb_vscale, 
	   state->pixar_rgb_hscale);

	fprintf (fp, "{%d %d SC /MX {[%d 0 0 1 0 0]} def} IE\n",
	   state->pixar_gray_hscale, state->pixar_gray_vscale,
	   state->pixar_gray_hscale);
	
	pixar_to_rgb_and_gray_rasters (mr, point, vcount, hoffset, hcount, rasters, 
						  ADDR (draw_rgb_and_gray_raster));

	fprintf (fp, "GR\n");						   
}

local void draw_3d_pixel_array (mr, pixar)
	stack		Misc_Rendition			*mr;
	register	Pixel_Array				*pixar; {
	register	Transform_Rendition		*tr = mr->h.transform_rendition;
	stack		int						hoffset, voffset,
										hcount, vcount;
	stack		FLOAT					fx, fy, fz, fw;
	stack		DC_Point				point;

	if (pixar->color_max >= mr->color_map_rendition->length) {
		HI_Warning (Sprintf_DD (null,
			"Pixel array max of %d may exceed color map max of %d - not drawn",
			(int)pixar->color_max, (int)mr->color_map_rendition->length - 1));
		return;
	}

	switch (tr->transform_type) {
		case TType_3x2: {
			fx = TRANS2X (tr->matrix, pixar->whereabouts); 
			fy = TRANS2Y (tr->matrix, pixar->whereabouts);
			fz = F(0);
		}		break;

		case TType_4x3: {
			fx = TRANSX (tr->matrix, pixar->whereabouts); 
			fy = TRANSY (tr->matrix, pixar->whereabouts);
			fz = F(0);
		}		break;

		case TType_4x4: {
			fw = TRANSW (tr->matrix, pixar->whereabouts);
			if (fw <= WLIMIT) return;

			fx = TRANSX (tr->matrix, pixar->whereabouts) / fw;
			fy = TRANSY (tr->matrix, pixar->whereabouts) / fw;
			fz = F(0);
		}		break;

		case TType_4x3_Z: {
			fx = TRANSX (tr->matrix, pixar->whereabouts); 
			fy = TRANSY (tr->matrix, pixar->whereabouts);
			fz = TRANSZ (tr->matrix, pixar->whereabouts);
		}		break;

		case TType_4x4_Z: {
			fw = TRANSW (tr->matrix, pixar->whereabouts);
			if (fw <= WLIMIT) return;

			fx = TRANSX (tr->matrix, pixar->whereabouts) / fw;
			fy = TRANSY (tr->matrix, pixar->whereabouts) / fw;
			fz = TRANSZ (tr->matrix, pixar->whereabouts) / fw;
		}		break;
	}

	fx += F(0.5) - pixar->width * F(0.5) * PIXAR_SCALE;
	fy += F(0.5) + pixar->height * F(0.5) * PIXAR_SCALE;

	point.x = (int)fx; /* (overflows OK) */
	point.y = (int)fy;

	/*** Need to know if we're incrementally redrawing at this point! ***/
	
	if (fx <= (FLOAT)(tr->hard_clip.right - pixar->width + 1)) {
		hcount = pixar->width;
	}
	else {
		if (fx >= (FLOAT)(tr->hard_clip.right + 1)) return;
		hcount = tr->hard_clip.right + 1 - point.x;
	}
	
	if (fx >= (FLOAT)tr->hard_clip.left) hoffset = 0;
	else {
		if ((FLOAT)hcount <= (FLOAT)tr->hard_clip.left - fx) return;
		hoffset = tr->hard_clip.left - point.x;
		hcount -= hoffset;
		point.x = tr->hard_clip.left;
	}

	if (fy >= (FLOAT)(tr->hard_clip.bottom + pixar->height - 1)) {
		vcount = pixar->height;
	}
	else {
		if (fy <= (FLOAT)(tr->hard_clip.bottom + 1)) return;
		if ((vcount = point.y - tr->hard_clip.bottom + 1) <= 0) return;
	}
	
	if (fy <= (FLOAT)tr->hard_clip.top) voffset = 0;
	else {
		if ((FLOAT)vcount <= fy - (FLOAT)tr->hard_clip.top) return;
		voffset = point.y - tr->hard_clip.top;
		if ((vcount -= voffset) <= 0) return;
		point.y = tr->hard_clip.top;
	}

	if (Z_TRANSFORM (tr->transform_type)) point.z = ZCLAMP (fz, tr);

	/* ("rasters" is from the upper left) */

	(*mr->h.display_context->actions.draw_dc_pixel_array) (mr,
										&point,
										vcount,
										hoffset, hcount,
										pixar->rasters + voffset);
}




local	const unsigned char		courier_widths[256 - 32] = {
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,	  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, 147, 147, 147, 147, 147,	 0, 147, 147,	0, 147, 147,   0, 147,	 0, 147,
 147,	0,	 0,	  0, 147,	0, 147, 147, 147,	0, 147, 147,   0,	0,	 0, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
   0, 147, 147, 147, 147, 147, 147,	  0, 147, 147, 147, 147, 147,	0,	 0, 147,
 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
   0, 147, 147, 147, 147, 147, 147,	  0, 147, 147, 147, 147, 147,	0,	 0, 147,
};

local	const unsigned char		helvetica_widths[256 - 32] = {
  68,  68,	87, 136, 136, 218, 163,	 54,  81,  81,	95, 143,  68,  81,	68,	 68,
 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,	68,	 68, 143, 143, 143, 136,
 248, 163, 163, 177, 177, 163, 150, 190, 177,  68, 122, 163, 136, 204, 177, 190,
 163, 190, 177, 163, 150, 177, 163, 231, 163, 163, 150,	 68,  68,  68, 115, 136,
  54, 136, 136, 122, 136, 136,	68, 136, 136,  54,	54, 122,  54, 204, 136, 136,
 136, 136,	81, 122,  68, 136, 122, 177, 122, 122, 122,	 82,  64,  82, 143,	  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,  81, 136, 136, 136, 136,	 0, 136,  81,	0,	91, 136,   0, 136,	 0, 245,
  81,	0,	 0,	  0,  81,	0, 131,	 68,  81,	0,	89, 136,   0,	0,	 0, 150,
 163, 163, 163, 163, 163, 163, 245, 177, 163, 163, 163, 163,  68,  68,	68,	 68,
   0, 177, 190, 190, 190, 190, 190,	  0, 190, 177, 177, 177, 177,	0,	 0, 150,
 136, 136, 136, 136, 136, 136, 218, 122, 136, 136, 136, 136,  68,  68,	68,	 68,
   0, 136, 136, 136, 136, 136, 136,	  0, 150, 136, 136, 136, 136,	0,	 0, 122,
};

local	const unsigned char		helvetica_bold_widths[256 - 32] = {
  68,  81, 116, 136, 136, 218, 177,	 68,  81,  81,	95, 143,  68,  81,	68,	 68,
 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,	81,	 81, 143, 143, 143, 150,
 239, 177, 177, 177, 177, 163, 150, 190, 177,  68, 136, 177, 150, 204, 177, 190,
 163, 190, 177, 163, 150, 177, 163, 231, 163, 163, 150,	 81,  68,  81, 143, 136,
  68, 136, 150, 136, 150, 136,	81, 150, 150,  68,	68, 136,  68, 218, 150, 150,
 150, 150,	95, 136,  81, 150, 136, 190, 136, 136, 122,	 95,  69,  95, 143,	  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,  81, 136, 136, 136, 136,	 0, 136,  81,	0,	91, 136,   0, 136,	 0, 245,
  81,	0,	 0,	  0,  81,	0, 136,	 68,  81,	0,	89, 136,   0,	0,	 0, 150,
 177, 177, 177, 177, 177, 177, 245, 177, 163, 163, 163, 163,  68,  68,	68,	 68,
   0, 177, 190, 190, 190, 190, 190,	  0, 190, 177, 177, 177, 177,	0,	 0, 150,
 136, 136, 136, 136, 136, 136, 218, 136, 136, 136, 136, 136,  68,  68,	68,	 68,
   0, 150, 150, 150, 150, 150, 150,	  0, 150, 150, 150, 150, 150,	0,	 0, 136,
};

local	const unsigned char		times_widths[256 - 32] = {
  61,  81, 100, 122, 122, 204, 190,	 81,  81,  81, 122, 138,  61,  81,	61,	 68,
 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,	68,	 68, 138, 138, 138, 109,
 225, 177, 163, 163, 177, 150, 136, 177, 177,  81,	95, 177, 150, 218, 177, 177,
 136, 177, 163, 136, 150, 177, 177, 231, 177, 177, 150,	 81,  68,  81, 115, 122,
  81, 109, 122, 109, 122, 109,	81, 122, 122,  68,	68, 122,  68, 190, 122, 122,
 122, 122,	81,	 95,  68, 122, 122, 177, 122, 122, 109, 117,  49, 117, 132,	  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,  81, 122, 122, 122, 122,	 0, 122,  81,	0,	68, 122,   0, 122,	 0, 245,
  81,	0,	 0,	  0,  81,	0, 111,	 61,  81,	0,	76, 122,   0,	0,	 0, 109,
 177, 177, 177, 177, 177, 177, 218, 163, 150, 150, 150, 150,  81,  81,	81,	 81,
   0, 177, 177, 177, 177, 177, 177,	  0, 177, 177, 177, 177, 177,	0,	 0, 122,
 109, 109, 109, 109, 109, 109, 163, 109, 109, 109, 109, 109,  68,  68,	68,	 68,
   0, 122, 122, 122, 122, 122, 122,	  0, 122, 122, 122, 122, 122,	0,	 0, 122,
};

local	const unsigned char		times_italic_widths[256 - 32] = {
  61,  81, 103, 122, 122, 204, 190,	 81,  81,  81, 122, 165,  61,  81,	61,	 68,
 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,	81,	 81, 165, 165, 165, 122,
 225, 150, 150, 163, 177, 150, 150, 177, 177,  81, 109, 163, 136, 204, 163, 177,
 150, 177, 150, 122, 136, 177, 150, 204, 150, 136, 136,	 95,  68,  95, 103, 122,
  81, 122, 122, 109, 122, 109,	68, 122, 122,  68,	68, 109,  68, 177, 122, 122,
 122, 122,	95,	 95,  68, 122, 109, 163, 109, 109,	95,	 98,  67,  98, 132,	  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,  95, 122, 122, 122, 122,	 0, 122,  81,	0,	68, 122,   0, 122,	 0, 218,
  81,	0,	 0,	  0,  81,	0, 128,	 61,  81,	0,	76, 122,   0,	0,	 0, 122,
 150, 150, 150, 150, 150, 150, 218, 163, 150, 150, 150, 150,  81,  81,	81,	 81,
   0, 163, 177, 177, 177, 177, 177,	  0, 177, 177, 177, 177, 177,	0,	 0, 122,
 122, 122, 122, 122, 122, 122, 163, 109, 109, 109, 109, 109,  68,  68,	68,	 68,
   0, 122, 122, 122, 122, 122, 122,	  0, 122, 122, 122, 122, 122,	0,	 0, 109,
};

local	const unsigned char		times_bold_widths[256 - 32] = {
  61,  81, 136, 122, 122, 245, 204,	 81,  81,  81, 122, 139,  61,  81,	61,	 68,
 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,	81,	 81, 139, 139, 139, 122,
 228, 177, 163, 177, 177, 163, 150, 190, 190,  95, 122, 190, 163, 231, 177, 190,
 150, 190, 177, 136, 163, 177, 177, 245, 177, 177, 163,	 81,  68,  81, 142, 122,
  81, 122, 136, 109, 136, 109,	81, 122, 136,  68,	81, 136,  68, 204, 136, 122,
 136, 136, 109,	 95,  81, 136, 122, 177, 122, 122, 109,	 96,  54,  96, 127,	  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,  81, 122, 122, 122, 122,	 0, 122,  81,	0,	73, 122,   0, 122,	 0, 245,
  81,	0,	 0,	  0,  81,	0, 132,	 61,  81,	0,	81, 122,   0,	0,	 0, 122,
 177, 177, 177, 177, 177, 177, 245, 177, 163, 163, 163, 163,  95,  95,	95,	 95,
   0, 177, 190, 190, 190, 190, 190,	  0, 190, 177, 177, 177, 177,	0,	 0, 136,
 122, 122, 122, 122, 122, 122, 177, 109, 109, 109, 109, 109,  68,  68,	68,	 68,
   0, 136, 122, 122, 122, 122, 122,	  0, 122, 136, 136, 136, 136,	0,	 0, 122,
};

local	const unsigned char		times_bolditalic_widths[256 - 32] = {
  61,  95, 136, 122, 122, 204, 190,	 81,  81,  81, 122, 139,  61,  81,	61,	 68,
 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,	81,	 81, 139, 139, 139, 122,
 204, 163, 163, 163, 177, 163, 163, 177, 190,  95, 122, 163, 150, 218, 177, 177,
 150, 177, 163, 136, 150, 177, 163, 218, 163, 150, 150,	 81,  68,  81, 139, 122,
  81, 122, 122, 109, 122, 109,	81, 122, 136,  68,	68, 122,  68, 190, 136, 122,
 122, 122,	95,	 95,  68, 136, 109, 163, 122, 109,	95,	 85,  54,  85, 139,	  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,  95, 122, 122, 122, 122,	 0, 122,  81,	0,	65, 122,   0, 122,	 0, 245,
  81,	0,	 0,	  0,  81,	0, 122,	 61,  81,	0,	73, 122,   0,	0,	 0, 122,
 163, 163, 163, 163, 163, 163, 231, 163, 163, 163, 163, 163,  95,  95,	95,	 95,
   0, 177, 177, 177, 177, 177, 177,	  0, 177, 177, 177, 177, 177,	0,	 0, 122,
 122, 122, 122, 122, 122, 122, 177, 109, 109, 109, 109, 109,  68,  68,	68,	 68,
   0, 136, 122, 122, 122, 122, 122,	  0, 122, 136, 136, 136, 136,	0,	 0, 109,
};

local	const unsigned char		symbol_widths[256 - 32] = {
  61,  81, 174, 122, 134, 204, 190, 107,  81,  81, 122, 134,  61, 134,	61,	 68,
 122, 122, 122, 122, 122, 122, 122, 122, 122, 122,	68,	 68, 134, 134, 134, 109,
 134, 177, 163, 177, 150, 150, 187, 148, 177,  81, 154, 177, 168, 218, 177, 177,
 188, 181, 136, 145, 150, 169, 107, 188, 158, 195, 150,	 81, 211,  81, 161, 122,
 122, 154, 134, 134, 121, 107, 128, 101, 148,  81, 148, 134, 134, 141, 128, 134,
 134, 128, 134, 148, 107, 141, 174, 168, 121, 168, 121, 117,  49, 117, 134,	  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, 152,	60, 134,  41, 174, 122, 184, 184, 184, 184, 255, 242, 148, 242, 148,
  98, 134, 101, 134, 134, 174, 121, 113, 134, 134, 134, 134, 245, 148, 245, 161,
 201, 168, 195, 242, 188, 188, 201, 188, 188, 174, 174, 174, 174, 174, 174, 174,
 188, 174, 193, 193, 218, 201, 134,	 61, 174, 148, 148, 255, 242, 148, 242, 148,
 121,  81, 193, 193, 192, 174,	94,	 94,  94,  94,	94,	 94, 121, 121, 121, 121,
   0,  81,	67, 168, 168, 168,	94,	 94,  94,  94,	94,	 94, 121, 121, 121,	  0,
};


local void find_all_fonts (dc)
	stack		Display_Context			*dc; {

#define FONT_SPACING(x) SCALEABLE_VSPACE_RATIO(F(1)/x)

	HD_Record_Font (dc, "Times",				"", 0, FONT_SPACING(0.6759));
	HD_Record_Font (dc, "Times-Italic",			"", 0, FONT_SPACING(0.6720));
	HD_Record_Font (dc, "Times-Bold",			"", 0, FONT_SPACING(0.6809));
	HD_Record_Font (dc, "Times-BoldItalic",		"", 0, FONT_SPACING(0.6759));
	HD_Record_Font (dc, "Helvetica",			"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Helvetica-Oblique",	"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Helvetica-Bold",		"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Helvetica-BoldOblique","", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Courier",				"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Courier-Oblique",		"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Courier-Bold",			"", 0, FONT_SPACING(0.7290));
	HD_Record_Font (dc, "Courier-BoldOblique",	"", 0, FONT_SPACING(0.7290));

	HD_Record_Font (dc, "Symbol",				"", 0, FONT_SPACING(0.6729));

	HD_Mark_Font_As_Generic (dc, "Times-Roman", GFN_ROMAN);
	HD_Mark_Font_As_Generic (dc, "Helvetica",	GFN_SANS_SERIF);
	HD_Mark_Font_As_Generic (dc, "Courier",		GFN_TYPEWRITER);
}

local long load_font (dc, name, Aheight, vspace, widths)
	stack		Display_Context			*dc; 
	stack		char					*name; 
	stack		int						*Aheight, 
										*vspace;
	stack		Character_Width			*widths; {
	register	PS_Data					*state = (PS_Data *) dc->data;
	register	FILE					*fp = state->fp;

	if (*name != 'S')
		fprintf (fp, "/HF%d {/R%s findfont %d scalefont setfont} bind def\n",
				 state->next_font_num, name, *vspace);
	else
		fprintf (fp, "/HF%d {/%s findfont %d scalefont setfont} bind def\n",
				 state->next_font_num, name, *vspace);

	if (widths != null) {
		register		unsigned char			*table;
		register		int						ii;
		register		int						height = *vspace;

		switch (*name) {
			case 'C':	table = courier_widths;			
						break;

			case 'S':	table = symbol_widths;	
						height = -height;		/* all are special */
						break;

			case 'H':	if (name[9] == '-' && name[10] == 'B')
							table = helvetica_bold_widths;
						else
							table = helvetica_widths;
						break;
			case 'T':
					switch (name[6]) {
						case 'R':		table = times_widths;			break;
						case 'I':		table = times_italic_widths;	break;
						case 'B':		if (name[10] == '\0')
											table = times_bold_widths;
										else
											table = times_bolditalic_widths;
										break;
					}
					break;
		}

		for (ii=32; ii<256; ii++)
			widths[ii] = 
				(Character_Width) (height *
								   UNSIGNED_CHARTOINT (table[ii - 32]) *
								   F(1.042 / 255));
	}

	return state->next_font_num++;
}

local void unload_font (dc, identifier)
	stack		Display_Context			*dc;
	stack		long					identifier; {
	register	PS_Data					*state = (PS_Data *) dc->data;
	register	FILE					*fp = state->fp;

	if (identifier > state->last_font_prev_page) 
		fprintf (fp, "/HF%d () def\n", identifier);
}

local void draw_dc_text (xr, where, count, string)
	stack		Text_Rendition			*xr;
	stack		DC_Point				*where;
	stack		int						count; 
	stack		char					*string; {
	stack		Display_Context			*dc = xr->h.display_context;
	stack		PS_Data					*state = (PS_Data *) xr->h.data;
	register	FILE					*fp = state->fp;
	register	int						value;
	register	int						len;

	/* check clipping */
	if (xr->h.transform_rendition->h.incarnation != state->last_clipping) 
		new_clipping (xr->h.transform_rendition);

	if (xr->h.incarnation != state->last_rendition) {

		if (xr->font->identifier <= state->last_font_prev_page) {
			/* the id is from the previous page; force a new load_font() */
			/* "Force_Font_Load" is solely for the Postscript driver. */
			HD_Force_Font_Load (xr);
		}
		
		set_color (state, &xr->color);

		state->last_rendition = xr->h.incarnation;

		fprintf (fp, "HF%d ", xr->font->identifier);
	}

	fprintf (fp, "%d %d MT\n", where->x, where->y);

	/* output string as something the printer will take */
	fprintf (fp, "(");
	len = 1;
	do {
		value = (int) *string++ & 0xFF;
		if (value < 32	||	value > 126) {
			fprintf (fp, "\\%03o", value);
			len += 4;
		}
		else switch ((char) value) {
			case '(':
			case ')':
			case '\\': {
						fprintf (fp, "\\%c", value);
						len += 2;
			} break;

			default: {
						fprintf (fp, "%c", value);
						len++;
			} break;
		}
		if (len > 70) {
			fprintf (fp, "\\\n");
			len = 0;
		}
	} until (--count == 0);
	fprintf (fp, ") show\n");
}


local void get_actions (at) 
	register	Action_Table			*at; {

	at->start_device			 = ADDR (start_device);
	at->stop_device				 = ADDR (stop_device);
	at->get_physical_info		 = ADDR (get_physical_info);
	at->draw_dc_polyline		 = ADDR (draw_dc_polyline);
	at->draw_dc_rectangle		 = ADDR (draw_dc_rectangle);
	at->draw_dc_overlay_polyline = ADDR (draw_dc_overlay_polyline);
	at->draw_dc_overlay_face	 = ADDR (draw_dc_overlay_face);
	at->draw_dc_face			 = ADDR (draw_dc_face);
	at->init_picture			 = ADDR (init_picture);
	at->finish_picture			 = ADDR (finish_picture);
	at->get_outer_window		 = ADDR (get_outer_window);
	at->free_outer_window		 = ADDR (free_outer_window);
	at->draw_dc_pixel_array		 = ADDR (draw_dc_pixel_array);
	at->draw_3d_pixel_array		 = ADDR (draw_3d_pixel_array);

	at->find_all_fonts			 = ADDR (find_all_fonts);
	at->load_font				 = ADDR (load_font);
	at->unload_font				 = ADDR (unload_font);
	at->draw_dc_text			 = ADDR (draw_dc_text);

	at->draw_dc_ellipse			 = ADDR (draw_dc_ellipse);
	at->draw_dc_outline_ellipse	 = ADDR (draw_dc_outline_ellipse);
}

#endif

/* (mustn't end with a '#' line) */
