[BACK]Return to pdf.trm CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gnuplot / term

File: [local] / OpenXM_contrib / gnuplot / term / Attic / pdf.trm (download)

Revision 1.1, Mon Sep 15 07:09:38 2003 UTC (20 years, 8 months ago) by ohara
Branch: MAIN

Initial revision

/*Hello, Emacs, this is -*-C-*- ! */
/*------------------------------
	GNUPLOT - pdf.trm

	This file is included by ../term.c.

	This driver uses PDFlib from www.pdflib.com

	Author:

		Hans-Bernhard Br"oker
		broeker@physik.rwth-aachen.de

	Licence: see the gnuplot copyright (to be merged into here...)

	Options: can #define PDF_DONT_COMPRESS to avoid PDF output
	generated being compressed (by the 'deflate' algorithm as used
	in 'zip' or 'gzip'). That helps in debugging.

------------------------------*/

/* CODEME: Add patterned lines (?). Implement the PM3D stuff.  */

#include "driver.h"

#ifdef TERM_REGISTER
register_term (pdf)
#endif

#ifdef TERM_PROTO
TERM_PUBLIC void PDF_options __PROTO ((void));
TERM_PUBLIC void PDF_init __PROTO ((void));
TERM_PUBLIC void PDF_graphics __PROTO ((void));
TERM_PUBLIC void PDF_text __PROTO ((void));
TERM_PUBLIC void PDF_linetype __PROTO ((int linetype));
TERM_PUBLIC void PDF_move __PROTO ((unsigned int x, unsigned int y));
TERM_PUBLIC void PDF_vector __PROTO ((unsigned int x, unsigned int y));
TERM_PUBLIC void PDF_put_text __PROTO ((unsigned int x, unsigned int y, char *str));
TERM_PUBLIC void PDF_reset __PROTO ((void));
TERM_PUBLIC int PDF_justify_text __PROTO ((enum JUSTIFY mode));
TERM_PUBLIC int PDF_text_angle __PROTO ((int ang));
TERM_PUBLIC void PDF_point __PROTO ((unsigned int x, unsigned int y, int pointstyle));
TERM_PUBLIC int PDF_set_font __PROTO ((char *font));
TERM_PUBLIC void PDF_boxfill __PROTO((int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height));
TERM_PUBLIC void PDF_linewidth __PROTO ((double linewidth));

#define PDF_NUM_POINTTYPES 75	/* number of point symbol types not counting the dot */

#define PDF_RESOLUTION  (20)	/* number of terminal pixels per pt */
#define PDF_XMAX	(5*72*PDF_RESOLUTION) /* 5 inches, 72 pt/inch */
#define PDF_YMAX	(3*72*PDF_RESOLUTION) /* 3 inches, 72 pt/inch */

#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY

#include <pdflib.h>

PDF *myPDF = NULL;

unsigned int PDF_xLast = UINT_MAX;	/* current pen horizontal position*/
unsigned int PDF_yLast = UINT_MAX;	/* current pen vertical position*/

int PDF_LineType = -3;		/* current line type*/
double PDF_LineWidth = 1.0;	/* current line width*/
int PDF_TextAngle = 0;		/* current text orientation*/
enum JUSTIFY PDF_TextJust = LEFT; /* current text justification*/

char PDF_fontNameDef[MAX_ID_LEN + 1] = "Helvetica"; /* default text font family*/
double PDF_fontSizeDef = 6;	/* default text size*/
char PDF_fontNameCur[MAX_ID_LEN + 1] = "Helvetica"; /* current text font family*/
double PDF_fontSizeCur = 6;	/* current text size*/

TBOOLEAN PDF_pageIsOpen = FALSE; /* already started a page ?? */
TBOOLEAN PDF_pathIsOpen = FALSE; /* open path flag*/

int PDF_fontAscent = 0;		/* estimated current font ascent*/
int PDF_fontDescent = 0;	/* estimated current font descent*/
int PDF_fontLeading = 0;	/* estimated current font leading*/
int PDF_fontAvWidth = 0;	/* estimated current font char average width*/

static short PDF_Pen_RealID __PROTO ((int));
static void PDF_PathOpen __PROTO ((void));
static void PDF_PathClose __PROTO ((void));
static void PDF_SetFont __PROTO ((void));

/*------------------------ helper functions -------------------*/

static short 
PDF_Pen_RealID (inPenCode)
    int inPenCode;
{
    if (inPenCode >= 13)
	inPenCode %= 13;	/* normalize pen code*/
    if (inPenCode < -2)
	inPenCode = -2;

    return (inPenCode + 2);
}

/* Functions to ensure that as many move() and vector() calls as
 * possible get converted into a single long 'path', before closing it
 * with a stroke or similar command. */
static void
PDF_PathOpen ()
{
    PDF_pathIsOpen = TRUE;
}

static void
PDF_PathClose ()
{
    if (PDF_pathIsOpen) {
	PDF_stroke(myPDF);

	PDF_pathIsOpen = FALSE;
    }
}

/* Helper function to deal with switching over to a newly selected
 * font.  For now, this does not try to embed fonts into the PDF
 * file. This relies on the user restricting herself to the 14
 * 'classical' fonts built into every valid PDF reader application */
static void
PDF_SetFont ()
{
    int font_handle = PDF_findfont(myPDF, PDF_fontNameCur, "host", 0);

    PDF_setfont(myPDF, font_handle, PDF_fontSizeCur * PDF_RESOLUTION);

    /* Ask PDFlib for the actual numbers */
    PDF_fontAscent = (int) (PDF_RESOLUTION * PDF_fontSizeCur * PDF_get_value(myPDF, "ascender", 0));	
    PDF_fontDescent = (int) (- PDF_RESOLUTION * PDF_fontSizeCur * PDF_get_value(myPDF, "descender", 0));	
    PDF_fontLeading = (int) (PDF_RESOLUTION * PDF_fontSizeCur * 0.25); 
    
    /* Assume this particular string is a somewhat reasonable typical
     * output, for getting at the average character width */
    PDF_fontAvWidth = (int)
	(PDF_RESOLUTION * PDF_stringwidth(myPDF, "01234567890123456789",
					  font_handle, PDF_fontSizeCur)
	 / 20.0);
}

/*------------------- the terminal entry functions --------------------*/


TERM_PUBLIC void
PDF_options ()
{
    struct value a;

    if (!END_OF_COMMAND) {	/* get default font family name*/
	if (almost_equals (c_token, "fn$ame"))  {
	    c_token++;

	    if (!END_OF_COMMAND && isstring (c_token)) {
		quote_str (PDF_fontNameDef, c_token, MAX_ID_LEN);
		c_token++;
	    } else
		int_error("fname: expecting font name", c_token);
	}
    }

    if (!END_OF_COMMAND) {	/* get default font size*/
	if (almost_equals (c_token, "fs$ize")) {
	    c_token++;

	    if (END_OF_COMMAND)
		int_error("fsize: expecting font size", c_token);
	    PDF_fontSizeDef = real (const_express (&a));
	}
    }

    if (!END_OF_COMMAND)
	 int_error("unexpected text at end of command", c_token);

    /* Save options back into options string in normalized format */
    sprintf(term_options, "fname '%s'  fsize %g",
	    PDF_fontNameDef, PDF_fontSizeDef);
}


TERM_PUBLIC void
PDF_init ()
{
    static TBOOLEAN PDFlib_booted = FALSE;
    
    if (!PDFlib_booted) {
	PDF_boot();
	PDFlib_booted = TRUE;
    }

    if (!myPDF)
	myPDF = PDF_new();

    /*open new PDF file */ 
    if (PDF_open_fp(myPDF, gpoutfile) == -1)
	int_error("Error:cannot open PDF file .\n", NO_CARET);

#ifdef PDF_DONT_COMPRESS
    /* for easier debugging of the output, turn off PDF stream
     * compression */
    PDF_set_value(myPDF, "compress", 0);
#endif

    PDF_set_info(myPDF,"Creator","gnuplot-3.8e");
    PDF_set_info(myPDF,"Author","Hans-Bernhard Broeker"); /* FIXME: put in username? */
    PDF_set_info(myPDF,"Title","gnuplot diagram"); /* FIXME: use 'set title', if any ? */
    
    PDF_LineType = -3;

    /* set current font to default */
    strcpy(PDF_fontNameCur, PDF_fontNameDef);
    PDF_fontSizeCur = PDF_fontSizeDef;

    /* Have to start the first page now, in order to know the actual
     * size of the selected font */
    PDF_graphics();

    /* set h_char, v_char*/
    term->h_char = PDF_fontAvWidth;
    term->v_char = (PDF_fontAscent + PDF_fontDescent + PDF_fontLeading);

    /* set h_tic, v_tic*/
    term->h_tic = term->v_tic = 3 * PDF_RESOLUTION;

    /* initialize terminal's pointsize from "set pointsize" value */
    term_pointsize = pointsize;	
}


TERM_PUBLIC void
PDF_graphics ()
{
    if (PDF_pageIsOpen) 
	return;			/* already open --> nothing to do */

    PDF_pathIsOpen = FALSE;
    PDF_xLast = PDF_yLast = UINT_MAX;

    /* set xmax, ymax*/
    term->xmax = PDF_XMAX * xsize;
    term->ymax = PDF_YMAX * ysize;
    
    PDF_begin_page(myPDF, (double)term->xmax / PDF_RESOLUTION,
		   (double)term->ymax / PDF_RESOLUTION);
    PDF_scale(myPDF, 1.0/PDF_RESOLUTION, 1.0/PDF_RESOLUTION);
    if (title.text && title.text[0])
	/* a title has been set --> use it as the bookmark name, too */
	PDF_add_bookmark(myPDF, title.text, 0, 1);
    PDF_pageIsOpen = TRUE;

    PDF_SetFont();
}


TERM_PUBLIC void
PDF_text ()
{
    PDF_PathClose();
    PDF_end_page(myPDF);
    PDF_pageIsOpen = FALSE;
}


TERM_PUBLIC void
PDF_reset ()
{
    assert(PDF_pageIsOpen == FALSE);
    PDF_close(myPDF);
    PDF_delete(myPDF);
    myPDF = NULL;
}


TERM_PUBLIC void
PDF_linetype (int linetype)
{
    if (linetype != PDF_LineType) {
	int real_linetype = PDF_Pen_RealID(linetype);
	struct rgb *this_color = web_color_rgbs + 1 + real_linetype;

	PDF_PathClose ();
	PDF_LineType = linetype;
	
	PDF_setrgbcolor(myPDF, this_color->r / 255.0,
			this_color->g / 255.0, this_color->b / 255.0);
    }
}


TERM_PUBLIC void
PDF_linewidth (double linewidth)
{
    PDF_PathClose();
    PDF_LineWidth = PDF_RESOLUTION * linewidth / 4.0;
    PDF_setlinewidth(myPDF, PDF_LineWidth);
}


TERM_PUBLIC void
PDF_move (unsigned int x, unsigned int y)
{
    if (PDF_pathIsOpen && x == PDF_xLast && y == PDF_yLast)  
	return;

    PDF_PathOpen ();
    PDF_moveto(myPDF, x, y);

    PDF_xLast = x;
    PDF_yLast = y;
}


TERM_PUBLIC void
PDF_vector (unsigned int x, unsigned int y)
{
    if (PDF_pathIsOpen && x == PDF_xLast && y == PDF_yLast) 
	return;

    PDF_PathOpen ();
    PDF_lineto(myPDF, x, y);

    PDF_xLast = x;
    PDF_yLast = y;
}

/* Helper function. Many symbols have an additional dot in their
 * center, so isolate its drawing into a separate function. */
static GP_INLINE void
PDF_dot (unsigned int x, unsigned int y)
{
    /* Imitate PS's way of creating a small dot by a zero-length line
     * segment with rounded endpoints */
    PDF_setlinecap(myPDF, 1); /* rounded ends */
    PDF_moveto(myPDF, x, y);
    PDF_lineto(myPDF, x, y);
    PDF_stroke(myPDF);
}
    

TERM_PUBLIC void
PDF_point (unsigned int x, unsigned int y, int number)
{
    PDF_PathClose ();
    PDF_save(myPDF);

    if (number < 0) {
	/* Like the PostScript driver, treat all negative numbers as
         * 'with dots'. */
	PDF_dot(x, y);
    } else {
	/* Change coordinate system so the point symbols themselves
	 * can be drawn without depending on position or size (-->
	 * better compression and less coding for gnuplot) */
	/* NB: I use the do_pointsize() default implementation, which
	 * just stores the last set pointsize into `term_pointsize',
	 * to avoid introducing another static driver-local variable
	 * */
	PDF_translate(myPDF, x, y);
	PDF_scale(myPDF, term->h_tic / 2.0 * term_pointsize,
		  term->v_tic / 2.0 * term_pointsize);
	/* Correct linewidth to counter the scaling effect --- assume
	 * h_tic is usable, to avoid having to average h_ and v_tic */
	PDF_setlinewidth(myPDF,
			 PDF_LineWidth / (term->h_tic / 2.0 * term_pointsize));
	switch (number %= PDF_NUM_POINTTYPES) {
	case 0:			/* Plus */
	    PDF_moveto(myPDF, -1, 0);
	    PDF_lineto(myPDF, 1, 0);
	    PDF_moveto(myPDF, 0, -1);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_stroke(myPDF);
	    break;
	case 2:			/* Star */
	    PDF_moveto(myPDF, -1, 0);
	    PDF_lineto(myPDF, 1, 0);
	    PDF_moveto(myPDF, 0, -1);
	    PDF_lineto(myPDF, 0, 1);
	    /* FALLTHROUGH */
	case 1:			/* Cross */
	    PDF_moveto(myPDF, -1, -1);
	    PDF_lineto(myPDF, 1, 1);
	    PDF_moveto(myPDF, 1, -1);
	    PDF_lineto(myPDF, -1, 1);
	    PDF_stroke(myPDF);
	    break;

/* For each x = 0..5, 4 shapes are defined: 
 * 3 + 2*x --> hollow symbol with a dot at its center
 * 4 + 2*x --> solid symbol filled in linetype's color
 * 63 + x  --> hollow symbol without the center dot 
 * 69 + x  --> symbol filled with white --> opaque symbol */
 
	case 63+0:		/* BoxEmpty */
	case 3+2*0:		/* Box */
	    PDF_moveto(myPDF, -1, -1);
	    PDF_lineto(myPDF, 1, -1);
	    PDF_lineto(myPDF, 1, 1);
	    PDF_lineto(myPDF, -1, 1);
	    PDF_closepath_stroke(myPDF);
	    if (number == 3) PDF_dot(0,0);
	    break;
	case 69+0:		/* BoxWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*0:		/* BoxFilled */
	    PDF_moveto(myPDF, -1, -1);
	    PDF_lineto(myPDF, 1, -1);
	    PDF_lineto(myPDF, 1, 1);
	    PDF_lineto(myPDF, -1, 1);
	    PDF_closepath_fill_stroke(myPDF);
	    break;

	case 63+1:		/* CircleEmpty */
	case 3+2*1:		/* Circle */
	    PDF_circle(myPDF, 0, 0, 1);
	    PDF_stroke(myPDF);
	    if (number == 5) PDF_dot(0,0);
	    break;
	case 69+1:		/* CircleWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*1:		/* CircleFilled */
	    PDF_circle(myPDF, 0, 0, 1);
	    PDF_fill_stroke(myPDF);
	    break;

	case 63+2:		/* TriangleUpEmpty */
	case 3+2*2:		/* TriangleUp */
	    PDF_moveto(myPDF, 0, 1.12);
	    PDF_lineto(myPDF, -1, -0.5);
	    PDF_lineto(myPDF, 1, -0.5);
	    PDF_closepath_stroke(myPDF);
	    if (number == 7) PDF_dot(0,0);
	    break;
	case 69+2:		/* TriangleUpWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*2:			/* TriangleUpFilled */
	    PDF_moveto(myPDF, 0, 1.12);
	    PDF_lineto(myPDF, -1, -0.5);
	    PDF_lineto(myPDF, 1, -0.5);
	    PDF_closepath_fill_stroke(myPDF);
	    break;

	case 63+3:		/* TriangleDownEmpty */
	case 3+2*3:		/* TriangleDown */
	    PDF_moveto(myPDF, 0, -1.12);
	    PDF_lineto(myPDF, -1, 0.5);
	    PDF_lineto(myPDF, 1, 0.5);
	    PDF_closepath_stroke(myPDF);
	    if (number == 9) PDF_dot(0,0);
	    break;
	case 69+3:		/* TriangleDownWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*3:		/* TriangleDownFilled */
	    PDF_moveto(myPDF, 0, -1.12);
	    PDF_lineto(myPDF, -1, 0.5);
	    PDF_lineto(myPDF, 1, 0.5);
	    PDF_closepath_fill_stroke(myPDF);
	    break;

	case 63+4:		/* DiamondEmpty */
	case 3+2*4:		/* Diamond */
	    PDF_moveto(myPDF, 0, -1);
	    PDF_lineto(myPDF, 1, 0);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_lineto(myPDF, -1, 0);
	    PDF_closepath_stroke(myPDF);
	    if (number == 11) PDF_dot(0,0);
	    break;
	case 69+4:		/* DiamondWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*4:		/* DiamondFilled */
	    PDF_moveto(myPDF, 0, -1);
	    PDF_lineto(myPDF, 1, 0);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_lineto(myPDF, -1, 0);
	    PDF_closepath_fill_stroke(myPDF);
	    break;

	case 63+5:		/* PentagonEmpty */
	case 3+2*5:		/* Pentagon */
	    PDF_moveto(myPDF, 0, 1);
	    PDF_lineto(myPDF, -0.95, 0.31);
	    PDF_lineto(myPDF, -0.58, -0.81);
	    PDF_lineto(myPDF, +0.58, -0.81);
	    PDF_lineto(myPDF, +0.95, 0.31);
	    PDF_closepath_stroke(myPDF);
	    if (number == 13) PDF_dot(0,0);
	    break;
	case 69+5:		/* PentagonWhitefilled */
	    PDF_setgray_fill(myPDF, 1);
	    /* FALLTHROUGH */
	case 4+2*5:		/* PentagonFilled */
	    PDF_moveto(myPDF, 0, 1);
	    PDF_lineto(myPDF, -0.95, 0.31);
	    PDF_lineto(myPDF, -0.58, -0.81);
	    PDF_lineto(myPDF, +0.58, -0.81);
	    PDF_lineto(myPDF, +0.95, 0.31);
	    PDF_closepath_fill_stroke(myPDF);
	    break;
	    
/* 15 + (0..15): circles with varying parts of'em filled. The added
 * number is a bit-pattern of the 4 quadrants: 1 signals a quadrant
 * filled */
	case 15+0:
	    PDF_moveto(myPDF, 0, 0);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_arc(myPDF, 0, 0, 1, 90, 360+90);
	    PDF_closepath_stroke(myPDF);
	    break;

/* Generalize common code into a macro... */
#define CIRCLE_SINGLE_PIESLICE(x, y, angle1, angle2)		\
	    PDF_moveto(myPDF, 0, 0);				\
	    PDF_lineto(myPDF, (x), (y));			\
	    PDF_arc(myPDF, 0, 0, 1, (angle1), (angle2));	\
	    PDF_lineto(myPDF, 0, 0);				\
	    PDF_closepath(myPDF);				\
	    PDF_fill_stroke(myPDF);				\
	    PDF_arc(myPDF, 0, 0, 1, (angle2), (angle1) + 360);	\
	    PDF_stroke(myPDF);					\
	    break;

#define CIRCLE_SINGLE_QUADRANT(x, y, angle)			\
	    CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+90);
	case 15+1:
	    CIRCLE_SINGLE_QUADRANT(1, 0, 0);
	case 15+2:
	    CIRCLE_SINGLE_QUADRANT(0, 1, 90);
	case 15+4:
	    CIRCLE_SINGLE_QUADRANT(-1, 0, 180);
	case 15+8:
	    CIRCLE_SINGLE_QUADRANT(0, -1, 270);
#undef CIRCLE_SINGLE_QUADRANT

#define CIRCLE_TWO_NEIGHBOR_QUADRANTS(x, y, angle)		\
	    CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+180)
	case 15+3:	
	    CIRCLE_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
	case 15+6:
	    CIRCLE_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
	case 15+12:
	    CIRCLE_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
	case 15+9:
	    CIRCLE_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
#undef CIRCLE_TWO_NEIGHBOR_QUADRANTS
	  
#define CIRCLE_TWO_OPPOSING_QUADRANTS(x, y, angle)		\
	    PDF_moveto(myPDF, 0, 0);				\
	    PDF_lineto(myPDF, x, y);				\
	    PDF_arc(myPDF, 0, 0, 1, angle, angle + 90);		\
	    PDF_lineto(myPDF, 0, 0);				\
	    PDF_fill_stroke(myPDF);				\
	    PDF_moveto(myPDF, 0, 0);				\
	    PDF_lineto(myPDF, -x, -y);				\
	    PDF_arc(myPDF, 0, 0, 1, angle + 180, angle + 270);	\
	    PDF_lineto(myPDF, 0, 0);				\
	    PDF_fill_stroke(myPDF);				\
	    PDF_arc(myPDF, 0, 0, 1, angle + 90, angle + 360);	\
	    PDF_stroke(myPDF);					\
	    break;
	case 15+5:
	    CIRCLE_TWO_OPPOSING_QUADRANTS(1, 0, 0);
	case 15+10:
	    CIRCLE_TWO_OPPOSING_QUADRANTS(0, 1, 90);
#undef CIRCLE_TWO_OPPOSING_QUADRANTS
	    
#define CIRCLE_THREE_QUADRANTS(x, y, angle)			\
	    CIRCLE_SINGLE_PIESLICE(x, y, angle, angle+270)
	case 15+7:
	    CIRCLE_THREE_QUADRANTS(1, 0, 0);	    
	case 15+14:
	    CIRCLE_THREE_QUADRANTS(0, 1, 90);	    
	case 15+13:
	    CIRCLE_THREE_QUADRANTS(-1, 0, 180);	    
	case 15+11:
	    CIRCLE_THREE_QUADRANTS(0, -1, 270);	    
#undef CIRCLE_THREE_QUADRANTS
#undef CIRCLE_SINGLE_PIESLICE

	case 15+15:		
	    PDF_circle(myPDF, 0, 0, 1);
	    PDF_closepath_fill_stroke(myPDF);
	    break;


/*************************************************************************/
/* 31 + (0..15): squares with different quadrants of them filled in. */
/*************************************************************************/
/*************************************************************************/
/* 47 + (0..15): diamonds with filled quadrants as given by bit pattern  */
/*   Diamonds are drawn as squares rotated by 45 degrees, so can use
 * fall-through from diamond to squares, and re-use some macros. */
/*************************************************************************/
	case 47+0:
	    PDF_rotate(myPDF, 45);
	    /* FALLTHROUGH */
	case 31+0:
	    PDF_moveto(myPDF, 0, 0);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_lineto(myPDF, -1, 1);
	    PDF_lineto(myPDF, -1, -1);
	    PDF_lineto(myPDF, 1, -1);
	    PDF_lineto(myPDF, 1, 1);
	    PDF_lineto(myPDF, 0, 1);
	    PDF_stroke(myPDF);
	    break;

	case 47+15:
	    PDF_rotate(myPDF, 45);
	    /* FALLTHROUGH */
	case 31+15:		
	    PDF_moveto(myPDF, -1, 1);
	    PDF_lineto(myPDF, -1, -1);
	    PDF_lineto(myPDF, 1, -1);
	    PDF_lineto(myPDF, 1, 1);
	    PDF_closepath_fill_stroke(myPDF);
	    break;

/* macros defining shapes of the partly filled symbols. Done by
 * rotating the starting point (x0, y0) by 90 degrees or 45 degrees
 * (with length adjustment).  The rotations can be done without
 * trigonometric function calls, since their values are known:
 * cos(90)=0, sin(90)=1, cos(45)=sin(45)=1/sqrt(2).  A good compiler
 * should be able to optimize away all the local variables and
 * loops...  */

#define SQUARE_SINGLE_PIESLICE(x0, y0, quadrants)			\
	    {								\
		int quadrant = 0;					\
		int x= x0, y=y0;					\
		PDF_moveto(myPDF, 0, 0);				\
		PDF_lineto(myPDF, x, y);				\
		/* poor man's rotation by 45 and 90 degrees around the	\
		 * square's outline. */					\
		while (quadrant++ < quadrants) {			\
		    int dummy;						\
		    PDF_lineto(myPDF, x-y, x+y);			\
		    dummy = x; x = -y; y = dummy;			\
		}							\
		PDF_lineto(myPDF, x, y);				\
		PDF_closepath_fill_stroke(myPDF);			\
		PDF_moveto(myPDF, x, y);				\
		while (quadrant++ <= 4) {				\
		    int dummy;						\
		    PDF_lineto(myPDF, x-y, x+y);			\
		    dummy = x; x = -y; y = dummy;			\
		}							\
		PDF_lineto(myPDF, x, y);				\
		PDF_stroke(myPDF);					\
	    }								\
	    break;

#define SQUARE_TWO_OPPOSING_QUADRANTS(x0, y0, angle)	\
	    {						\
		int x = x0, y = y0, dummy;		\
		int counter = 0;			\
							\
		while (counter++ < 2) {			\
		    PDF_moveto(myPDF, 0, 0);		\
		    PDF_lineto(myPDF, x, y);		\
		    PDF_lineto(myPDF, x-y, x+y);	\
		    dummy = x; x = -y; y = dummy;	\
		    PDF_lineto(myPDF, x, y);		\
		    PDF_closepath_fill_stroke(myPDF);	\
							\
		    PDF_moveto(myPDF, x, y);		\
		    PDF_lineto(myPDF, x-y, x+y);	\
		    dummy = x; x = -y; y = dummy;	\
		    PDF_lineto(myPDF, x, y);		\
		    PDF_stroke(myPDF);			\
		}					\
		break;					\
	    }

/* Macros for diamonds just prepend the rotation and then call those
 * for squares: */
#define DIAMOND_SINGLE_PIESLICE(x, y, quadrants)	\
	    PDF_rotate(myPDF, 45);			\
	    SQUARE_SINGLE_PIESLICE(x, y, quadrants);
#define DIAMOND_TWO_OPPOSING_QUADRANTS(x, y, angle)	\
	    PDF_rotate(myPDF, 45);			\
	    SQUARE_TWO_OPPOSING_QUADRANTS(x, y, angle);

/* ... and now all the individual cases. The 'angle' arguments' are
 * purely for the sake of easing cut'n'paste with the circle case */
#define SQUARE_SINGLE_QUADRANT(x, y, angle)			\
	    SQUARE_SINGLE_PIESLICE(x, y, 1);
	case 31+1:
	    SQUARE_SINGLE_QUADRANT(1, 0, 0);
	case 31+2:
	    SQUARE_SINGLE_QUADRANT(0, 1, 90);
	case 31+4:
	    SQUARE_SINGLE_QUADRANT(-1, 0, 180);
	case 31+8:
	    SQUARE_SINGLE_QUADRANT(0, -1, 270);
#undef SQUARE_SINGLE_QUADRANT

#define SQUARE_TWO_NEIGHBOR_QUADRANTS(x, y, angle)		\
	    SQUARE_SINGLE_PIESLICE(x, y, 2)
	case 31+3:	
	    SQUARE_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
	case 31+6:
	    SQUARE_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
	case 31+12:
	    SQUARE_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
	case 31+9:
	    SQUARE_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
#undef SQUARE_TWO_NEIGHBOR_QUADRANTS
	  
	case 31+5:
	    SQUARE_TWO_OPPOSING_QUADRANTS(1, 0, 0);
	case 31+10:
	    SQUARE_TWO_OPPOSING_QUADRANTS(0, 1, 90);
	    
#define SQUARE_THREE_QUADRANTS(x, y, angle)			\
	    SQUARE_SINGLE_PIESLICE(x, y, 3)
	case 31+7:
	    SQUARE_THREE_QUADRANTS(1, 0, 0);	    
	case 31+14:
	    SQUARE_THREE_QUADRANTS(0, 1, 90);	    
	case 31+13:
	    SQUARE_THREE_QUADRANTS(-1, 0, 180);	    
	case 31+11:
	    SQUARE_THREE_QUADRANTS(0, -1, 270);	    
#undef SQUARE_THREE_QUADRANTS

#define DIAMOND_SINGLE_QUADRANT(x, y, angle)			\
	    DIAMOND_SINGLE_PIESLICE(x, y, 1)
	case 47+1:
	    DIAMOND_SINGLE_QUADRANT(1, 0, 0);
	case 47+2:
	    DIAMOND_SINGLE_QUADRANT(0, 1, 90);
	case 47+4:
	    DIAMOND_SINGLE_QUADRANT(-1, 0, 180);
	case 47+8:
	    DIAMOND_SINGLE_QUADRANT(0, -1, 270);
#undef DIAMOND_SINGLE_QUADRANT

#define DIAMOND_TWO_NEIGHBOR_QUADRANTS(x, y, angle)		\
	    DIAMOND_SINGLE_PIESLICE(x, y, 2)
	case 47+3:	
	    DIAMOND_TWO_NEIGHBOR_QUADRANTS(1, 0, 0);
	case 47+6:
	    DIAMOND_TWO_NEIGHBOR_QUADRANTS(0, 1, 90);
	case 47+12:
	    DIAMOND_TWO_NEIGHBOR_QUADRANTS(-1, 0, 180);
	case 47+9:
	    DIAMOND_TWO_NEIGHBOR_QUADRANTS(0, -1, 270);
#undef DIAMOND_TWO_NEIGHBOR_QUADRANTS
	  

	case 47+5:
	    DIAMOND_TWO_OPPOSING_QUADRANTS(1, 0, 0);
	case 47+10:
	    DIAMOND_TWO_OPPOSING_QUADRANTS(0, 1, 90);
#undef DIAMOND_TWO_OPPOSING_QUADRANTS
#undef SQUARE_TWO_OPPOSING_QUADRANTS
	    
#define DIAMOND_THREE_QUADRANTS(x, y, angle)			\
	    DIAMOND_SINGLE_PIESLICE(x, y, 3)
	case 47+7:
	    DIAMOND_THREE_QUADRANTS(1, 0, 0);	    
	case 47+14:
	    DIAMOND_THREE_QUADRANTS(0, 1, 90);	    
	case 47+13:
	    DIAMOND_THREE_QUADRANTS(-1, 0, 180);	    
	case 47+11:
	    DIAMOND_THREE_QUADRANTS(0, -1, 270);	    
#undef DIAMOND_THREE_QUADRANTS
#undef DIAMOND_SINGLE_PIESLICE
#undef SQUARE_SINGLE_PIESLICE

	default:
	    {
	    char temp[64];
	    sprintf(temp, "PDF: unknown point type number %d", number);
	    int_warn(temp,NO_CARET);
	    }
	}	
    }

    PDF_restore(myPDF);
    PDF_xLast = x;
    PDF_yLast = y;
}


TERM_PUBLIC int
PDF_justify_text (enum JUSTIFY mode)
{
    PDF_TextJust = mode;
    return (TRUE);
}


TERM_PUBLIC int
PDF_text_angle (int ang)
{
    if (ang == 0 || ang == 1) {
	/* FIXME: need to do this in a different way? */
	PDF_TextAngle = ang;
	return (TRUE);
    }

    return (FALSE);
}


TERM_PUBLIC void
PDF_put_text (unsigned int x, unsigned int y, char *str)
{
    char *alignment = NULL;
    double h = x, v = y;
    
    PDF_PathClose ();

    /* horizontal justification*/
    switch (PDF_TextJust) {
    case LEFT:
	alignment = "left";
	break;
    case CENTRE:
	alignment = "center";
	break;
    case RIGHT:
	alignment = "right";
	break;
    }

    /* vertical justification*/
    switch (PDF_TextAngle) {
    case 1:
	h += (PDF_fontAscent - PDF_fontDescent) / 2;
	break;		/* vertical text*/
    default:
	v -= (PDF_fontAscent - PDF_fontDescent) / 2;
	break;		/* horizontal text*/
    }

    if (PDF_TextAngle) {
	PDF_save(myPDF);
	PDF_rotate(myPDF, 90);
	PDF_show_boxed(myPDF, str, v , -h, 0, 0, alignment, NULL);
	PDF_restore(myPDF);
    } else {
	PDF_show_boxed(myPDF, str, h , v, 0, 0, alignment, NULL);
    }

}


TERM_PUBLIC int
PDF_set_font (char *font)
{
    if (strlen (font) > 0) {	/* if available, parse the font specification ("fontname,fontsize")*/
	short index;
	char *token,
	    seps[] = ",", *buffer = (char *) malloc (strlen (font) + 1);

	if (buffer == NULL)
	    return (FALSE);
	strcpy (buffer, font);

	for (token = strtok (buffer, seps), index = 1;
	     token != NULL; token = strtok (NULL, seps), index++
	    ) {
	    switch (index) {
	    case 1:
		strcpy (PDF_fontNameCur, token);
		break;	/* font name*/
	    case 2:
		PDF_fontSizeCur = atoi (token);
		break;	/* font size*/
	    default:
		break;
	    }
	}

	free (buffer);
    } else {			/* otherwise simply reset the default font*/
	strcpy (PDF_fontNameCur, PDF_fontNameDef);
	PDF_fontSizeCur = PDF_fontSizeDef;
    }


    PDF_PathClose();
    PDF_SetFont();
    return (TRUE);
}

TERM_PUBLIC void
PDF_boxfill(int style, unsigned int x1, unsigned int y1,
	    unsigned int width, unsigned int height)
{
    (void) style;		/* unused */
    PDF_PathClose();
    PDF_save(myPDF);
    PDF_setgray(myPDF, 1);
    PDF_moveto(myPDF, x1, y1);
    PDF_lineto(myPDF, x1+width, y1);
    PDF_lineto(myPDF, x1+width, y1+height);
    PDF_lineto(myPDF, x1, y1+height);
    PDF_fill(myPDF);
    PDF_restore(myPDF);
}
    
#endif /* TERM_BODY */

#ifdef TERM_TABLE
TERM_TABLE_START (pdf_driver)
    "pdf", "PDF (Portable Document File) file driver",
    0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ ,
    0 /* vtic */ , 0 /* htic */ ,
    PDF_options, PDF_init, PDF_reset, PDF_text, null_scale, PDF_graphics,
    PDF_move, PDF_vector, PDF_linetype, PDF_put_text, PDF_text_angle,
    PDF_justify_text, PDF_point, do_arrow, PDF_set_font, do_pointsize,
    TERM_BINARY,
    0 /* suspend */, 0 /* resume */ , PDF_boxfill, PDF_linewidth
TERM_TABLE_END (pdf_driver)
#undef LAST_TERM
#define LAST_TERM pdf_driver
#endif /* TERM_TABLE */

#endif /* TERM_PROTO_ONLY */

#ifdef TERM_HELP
    START_HELP (pdf)
    "1 pdf",
    "?commands set terminal pdf",
    "?set terminal pdf",
    "?set term pdf",
    "?terminal pdf",
    "?term pdf",
    "?pdf",
    " This terminal produces files in the Adobe Portable Document Format",
    " (PDF), useable for printing or display with tools like Acrobat Reader",
    "",
    " Syntax:",
    "       set terminal pdf {fname \"<font>\"} {fsize <fontsize>}",
    "",
    " where <font> is the name of the default font to use (default Helvetica)",
    " and <fontsize> is the font size (in points, default 12)"
    END_HELP (pdf)
#endif