/* $Id: psttf.cc,v 1.8 2006/05/18 11:56:39 rlaboiss Exp $

	PLplot PostScript device driver using LASi to provide fonts
	based on original ps.c PostScript driver

  Copyright (C) 1992, 2001  Geoffrey Furnish  
  Copyright (C) 1992, 1993, 1994, 1995, 2001  Maurice LeBrun
  Copyright (C) 2000, 2001, 2002, 2004, 2005  Alan W. Irwin 
  Copyright (C) 2001, 2002  Joao Cardoso  
  Copyright (C) 2001, 2003, 2004  Rafael Laboissiere
  Copyright (C) 2004, 2005  Thomas J. Duck
  Copyright (C) 2005, 2006  Andrew Ross

  This file is part of PLplot.

  PLplot is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Library Public License as published
  by the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  PLplot is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with PLplot; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

*/

#include "plDevs.h"

#define DEBUG

#if defined(PLD_psttf)

#define NEED_PLDEBUG
#include "plplotP.h"
#include "drivers.h"
#include "ps.h"

#include <string.h>
#include <time.h>
#include "plfreetype.h"
#include <iostream>
#include <fstream>
#include <LASi.h>

using namespace LASi;
using namespace std;

/* Device info */

char* plD_DEVICE_INFO_psttf = 
  "psttf:PostScript File (monochrome):0:psttf:55:psttfm\n"
  "psttfc:PostScript File (color):0:psttf:56:psttfc";


/* Prototypes for functions in this file. */

void plD_dispatch_init_psttfm	( PLDispatchTable *pdt );
void plD_dispatch_init_psttfc	( PLDispatchTable *pdt );

static char  *ps_getdate	(void);
static void  ps_init		(PLStream *);
static void  fill_polygon	(PLStream *pls);
static void  proc_str           (PLStream *, EscText *);
static void  esc_purge          (char *, char *);

static char  outbuf[128];
static int text = 1;
static int color;
static int hrshsym = 0;

/* Font style and weight lookup tables */

#define N_Pango_Lookup 5

const char * DefaultFamilyLookup[N_Pango_Lookup] = {
  "sans",
  "serif",
  "monospace",
  "sans,serif",
  "sans,serif"
};

const char * EnvFamilyLookup[N_Pango_Lookup] = {
  "PLPLOT_FREETYPE_SANS_FAMILY",
  "PLPLOT_FREETYPE_SERIF_FAMILY",
  "PLPLOT_FREETYPE_MONO_FAMILY",
  "PLPLOT_FREETYPE_SCRIPT_FAMILY",
  "PLPLOT_FREETYPE_SYMBOL_FAMILY"
};

char FamilyLookup[N_Pango_Lookup][1024];

const FontWeight WeightLookup[2] = {
  NORMAL_WEIGHT,
  BOLD
};

const FontStyle StyleLookup[3] = {
  NORMAL_STYLE,
  ITALIC,
  OBLIQUE
};

static DrvOpt ps_options[] = {{"text", DRV_INT, &text, "Use Postscript text (text=0|1)"},
			      {"color", DRV_INT, &color, "Use color (color=0|1)"},
			      {"hrshsym", DRV_INT, &hrshsym, "Use Hershey symbol set (hrshsym=0|1)"},
			      {NULL, DRV_INT, NULL, NULL}};

/* text > 0 uses some postscript tricks, namely a transformation matrix
   that scales, rotates (with slanting) and offsets text strings.
   It has yet some bugs for 3d plots. */


static void psttf_dispatch_init_helper( PLDispatchTable *pdt,
                                     char *menustr, char *devnam,
                                     int type, int seq, plD_init_fp init )
{
#ifndef ENABLE_DYNDRIVERS
    pdt->pl_MenuStr = menustr;
    pdt->pl_DevName = devnam;
#endif
    pdt->pl_type = type;
    pdt->pl_seq = seq;
    pdt->pl_init     = init;
    pdt->pl_line     = (plD_line_fp)     plD_line_psttf;
    pdt->pl_polyline = (plD_polyline_fp) plD_polyline_psttf;
    pdt->pl_eop      = (plD_eop_fp)      plD_eop_psttf;
    pdt->pl_bop      = (plD_bop_fp)      plD_bop_psttf;
    pdt->pl_tidy     = (plD_tidy_fp)     plD_tidy_psttf;
    pdt->pl_state    = (plD_state_fp)    plD_state_psttf;
    pdt->pl_esc      = (plD_esc_fp)      plD_esc_psttf;
}

void plD_dispatch_init_psttfm	( PLDispatchTable *pdt )
{
    psttf_dispatch_init_helper( pdt,
                             "PostScript File (monochrome)", "psttf",
                             plDevType_FileOriented, 55,
                             (plD_init_fp) plD_init_psttfm );
}

void plD_dispatch_init_psttfc	( PLDispatchTable *pdt )
{
    psttf_dispatch_init_helper( pdt,
                             "PostScript File (color)", "psttfc",
                             plDevType_FileOriented, 56,
                             (plD_init_fp) plD_init_psttfc );
}

/*--------------------------------------------------------------------------*\
 * plD_init_psttf()
 *
 * Initialize device.
\*--------------------------------------------------------------------------*/

void
plD_init_psttfm(PLStream *pls)
{
  color = 0;
  pls->color = 0;		/* Not a color device */

  plParseDrvOpts(ps_options);
  if (color)
    pls->color = 1;		/* But user wants color */
  ps_init(pls);
}

void
plD_init_psttfc(PLStream *pls)
{
  color = 1;
  pls->color = 1;		/* Is a color device */
  plParseDrvOpts(ps_options);

  if (!color)
    pls->color = 0;		/* But user does not want color */
  ps_init(pls);
}

#define MAX_NUM_TRIES	10
static void
ps_init(PLStream *pls)
{
    int i;
    char *a;
    PSDev *dev;

    PLFLT pxlx = YPSSIZE/LPAGE_X;
    PLFLT pxly = XPSSIZE/LPAGE_Y;

    if (text) 
     {
	pls->dev_text = 1; /* want to draw text */
	pls->dev_unicode = 1; /* want unicode */
	if(hrshsym) pls->dev_hrshsym = 1; /* want Hershey symbols */
     }

    pls->dev_fill0 = 1;		/* Can do solid fills */

/* Initialize family file info */

    plFamInit(pls);

/* Prompt for a file name if not already set */
    
    plOpenFile(pls);

/* Create postscript document object */
    if (pls->psdoc != NULL) 
      delete (PostscriptDocument *) pls->psdoc;

    pls->psdoc = new PostscriptDocument();

/* Allocate and initialize device-specific data */

    if (pls->dev != NULL)
	free((void *) pls->dev);

    pls->dev = calloc(1, (size_t) sizeof(PSDev));
    if (pls->dev == NULL)
	plexit("ps_init: Out of memory.");

    dev = (PSDev *) pls->dev;

    dev->xold = PL_UNDEFINED;
    dev->yold = PL_UNDEFINED;

    plP_setpxl(pxlx, pxly);

    dev->llx = XPSSIZE;
    dev->lly = YPSSIZE;
    dev->urx = 0;
    dev->ury = 0;
    dev->ptcnt = 0;

/* Rotate by 90 degrees since portrait mode addressing is used */

    dev->xmin = 0;
    dev->ymin = 0;
    dev->xmax = PSY;
    dev->ymax = PSX;
    dev->xlen = dev->xmax - dev->xmin;
    dev->ylen = dev->ymax - dev->ymin;

    plP_setphy(dev->xmin, dev->xmax, dev->ymin, dev->ymax);

/* If portrait mode is specified, then set up an additional rotation 
 * transformation with aspect ratio allowed to adjust via freeaspect.  
 * Default orientation is landscape (ORIENTATION == 3 or 90 deg rotation 
 * counter-clockwise from portrait).  (Legacy PLplot used seascape
 * which was equivalent to ORIENTATION == 1 or 90 deg clockwise rotation 
 * from portrait.) */

    if (pls->portrait) {
       plsdiori((PLFLT)(4 - ORIENTATION));
       pls->freeaspect = 1;
    }

    // File table for font families using either environment variables 
    // or defaults.
    for (i=0;i<N_Pango_Lookup;i++) {
      if ( (a = getenv(EnvFamilyLookup[i])) != NULL ) {
	strcpy(FamilyLookup[i],a);
      }
      else {
	strcpy(FamilyLookup[i],DefaultFamilyLookup[i]);
      }
    }

}


/*--------------------------------------------------------------------------*\
 * writeHeader()
 * 
 * Write plplot postscript commands into the header
\*--------------------------------------------------------------------------*/

void
writeHeader(PLStream *pls) {
    PostscriptDocument *doc = (PostscriptDocument *) (pls->psdoc);

    doc->osHeader() << "%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";
    
    doc->osHeader() << "%%Title: PLplot Graph\n";
    doc->osHeader() << "%%Creator: PLplot Version " << VERSION << "\n";
    doc->osHeader() << "%%CreationDate: " << ps_getdate() << "\n";
    doc->osHeader() << "%%Pages: (atend)\n";
    doc->osHeader() << "%%EndComments\n\n";

/* Definitions */
/* Save VM state */

    doc->osHeader() << "/PSSave save def\n";

/* Define a dictionary and start using it */

    doc->osHeader() << "/PSDict 200 dict def\n";
    doc->osHeader() << "PSDict begin\n";

    doc->osHeader() << "/@restore /restore load def\n";
    doc->osHeader() << "/restore\n";
    doc->osHeader() << "   {vmstatus pop\n";
    doc->osHeader() << "    dup @VMused lt {pop @VMused} if\n";
    doc->osHeader() << "    exch pop exch @restore /@VMused exch def\n";
    doc->osHeader() << "   } def\n";
    doc->osHeader() << "/@pri\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    ( ) print\n";
    doc->osHeader() << "    (                                       ) cvs print\n";
    doc->osHeader() << "   } def\n";

/* n @copies - */

    doc->osHeader() << "/@copies\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    /#copies exch def\n";
    doc->osHeader() << "   } def\n";

/* - @start -  -- start everything */

    doc->osHeader() << "/@start\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    vmstatus pop /@VMused exch def pop\n";
    doc->osHeader() << "   } def\n";

/* - @end -  -- finished */

    doc->osHeader() << "/@end\n";
    doc->osHeader() << "   {flush\n";
    doc->osHeader() << "    end\n";
    doc->osHeader() << "    PSSave restore\n";
    doc->osHeader() << "   } def\n";
    
/* bop -  -- begin a new page */
/* Only fill background if we are using color and if the bg isn't white */

    doc->osHeader() << "/bop\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    /SaveImage save def\n";
    doc->osHeader() << "   } def\n";

/* - eop -  -- end a page */

    doc->osHeader() << "/eop\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    showpage\n";
    doc->osHeader() << "    SaveImage restore\n";
    doc->osHeader() << "   } def\n";

/* Set line parameters */
    
    doc->osHeader() << "/@line\n";
    doc->osHeader() << "   {0 setlinecap\n";
    doc->osHeader() << "    0 setlinejoin\n";
    doc->osHeader() << "    1 setmiterlimit\n";
    doc->osHeader() << "   } def\n";

/* d @hsize -  horizontal clipping dimension */

    doc->osHeader() << "/@hsize   {/hs exch def} def\n";
    doc->osHeader() << "/@vsize   {/vs exch def} def\n";

/* d @hoffset - shift for the plots */

    doc->osHeader() << "/@hoffset {/ho exch def} def\n";
    doc->osHeader() << "/@voffset {/vo exch def} def\n";

/* Set line width */

    doc->osHeader() << "/lw " << (int) (
	(pls->width < MIN_WIDTH) ? DEF_WIDTH :
	(pls->width > MAX_WIDTH) ? MAX_WIDTH : pls->width) << " def\n";

/* Setup user specified offsets, scales, sizes for clipping */

    doc->osHeader() << "/@SetPlot\n";
    doc->osHeader() << "   {\n";
    doc->osHeader() << "    ho vo translate\n";
    doc->osHeader() << "    XScale YScale scale\n";
    doc->osHeader() << "    lw setlinewidth\n";
    doc->osHeader() << "   } def\n";

/* Setup x & y scales */
    
    doc->osHeader() << "/XScale\n";
    doc->osHeader() << "   {hs " << YPSSIZE << " div} def\n";
    doc->osHeader() << "/YScale\n";
    doc->osHeader() << "   {vs " << XPSSIZE << " div} def\n";
    
/* Macro definitions of common instructions, to keep output small */

    doc->osHeader() << "/M {moveto} def\n";
    doc->osHeader() << "/D {lineto} def\n";
    doc->osHeader() << "/A {0.5 0 360 arc} def\n";
    doc->osHeader() << "/S {stroke} def\n";
    doc->osHeader() << "/Z {stroke newpath} def\n";
    doc->osHeader() << "/F {fill} def\n";
    doc->osHeader() << "/C {setrgbcolor} def\n";
    doc->osHeader() << "/G {setgray} def\n";
    doc->osHeader() << "/W {setlinewidth} def\n";
    doc->osHeader() << "/R {rotate} def\n";
    doc->osHeader() << "/B {Z " << XMIN << " " << YMIN << " M " << XMIN << " " << YMAX << " D " << XMAX << " " << YMAX << " D " << XMAX << " " << YMIN << " D " << XMIN << " " << YMIN << " closepath} def\n";
    doc->osHeader() << "/CL {newpath M D D D closepath clip} def\n";
    
/* End of dictionary definition */

    doc->osHeader() << "end\n\n";

/* Set up the plots */

    doc->osHeader() << "PSDict begin\n";
    doc->osHeader() << "@start\n";
    doc->osHeader() << "%d @copies\n", COPIES;
    doc->osHeader() << "@line\n";
    doc->osHeader() << YSIZE << " @hsize\n";
    doc->osHeader() << XSIZE << " @vsize\n";
    doc->osHeader() << YOFFSET << " @hoffset\n";
    doc->osHeader() << XOFFSET << " @voffset\n";

    doc->osHeader() << "@SetPlot\n" << endl;
}

/*--------------------------------------------------------------------------*\
 * plD_line_psttf()
 *
 * Draw a line in the current color from (x1,y1) to (x2,y2).
\*--------------------------------------------------------------------------*/

void
plD_line_psttf(PLStream *pls, short x1a, short y1a, short x2a, short y2a)
{
    PSDev *dev = (PSDev *) pls->dev;
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
    PLINT x1 = x1a, y1 = y1a, x2 = x2a, y2 = y2a;

/* Rotate by 90 degrees */

    plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x1, &y1);
    plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x2, &y2);

    if (x1 == dev->xold && y1 == dev->yold && dev->ptcnt < 40) {
	if (pls->linepos + 12 > LINELENGTH) {
	    doc->osBody() << '\n';
	    pls->linepos = 0;
	}
	else
	    doc->osBody() << ' ';

	sprintf(outbuf, "%d %d D", x2, y2);
	dev->ptcnt++;
	pls->linepos += 12;
    }
    else {
        doc->osBody() << " Z\n";
	pls->linepos = 0;

	if (x1 == x2 && y1 == y2) /* must be a single dot, draw a circle */
	  sprintf(outbuf, "%d %d A", x1, y1);
	else
	  sprintf(outbuf, "%d %d M %d %d D", x1, y1, x2, y2);
	dev->llx = MIN(dev->llx, x1);
	dev->lly = MIN(dev->lly, y1);
	dev->urx = MAX(dev->urx, x1);
	dev->ury = MAX(dev->ury, y1);
	dev->ptcnt = 1;
	pls->linepos += 24;
    }
    dev->llx = MIN(dev->llx, x2);
    dev->lly = MIN(dev->lly, y2);
    dev->urx = MAX(dev->urx, x2);
    dev->ury = MAX(dev->ury, y2);

    doc->osBody() << outbuf;
    pls->bytecnt += 1 + strlen(outbuf);
    dev->xold = x2;
    dev->yold = y2;
}

/*--------------------------------------------------------------------------*\
 * plD_polyline_psttf()
 *
 * Draw a polyline in the current color.
\*--------------------------------------------------------------------------*/

void
plD_polyline_psttf(PLStream *pls, short *xa, short *ya, PLINT npts)
{
    PLINT i;

    for (i = 0; i < npts - 1; i++)
	plD_line_psttf(pls, xa[i], ya[i], xa[i + 1], ya[i + 1]);
}

/*--------------------------------------------------------------------------*\
 * plD_eop_psttf()
 *
 * End of page.
\*--------------------------------------------------------------------------*/

void
plD_eop_psttf(PLStream *pls)
{
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
    doc->osBody() << " S\neop\n";
}

/*--------------------------------------------------------------------------*\
 * plD_bop_psttf()
 *
 * Set up for the next page.
 * Advance to next family file if necessary (file output).
\*--------------------------------------------------------------------------*/

void
plD_bop_psttf(PLStream *pls)
{
    PSDev *dev = (PSDev *) pls->dev;
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;

    dev->xold = PL_UNDEFINED;
    dev->yold = PL_UNDEFINED;

    if (!pls->termin)
	plGetFam(pls);

    pls->page++;

    if (pls->family)
        doc->osBody() << "%%Page: " << (int) pls->page << " 1\n";
    else
      doc->osBody() << "%%Page: " << (int) pls->page << " " << (int) pls->page << "\n";

    doc->osBody() << "bop\n";
    if (pls->color) {
	PLFLT r, g, b;
	if (pls->cmap0[0].r != 0xFF ||
	    pls->cmap0[0].g != 0xFF ||
	    pls->cmap0[0].b != 0xFF ) {

	    r = ((PLFLT) pls->cmap0[0].r) / 255.;
	    g = ((PLFLT) pls->cmap0[0].g) / 255.;
	    b = ((PLFLT) pls->cmap0[0].b) / 255.;

	    doc->osBody() << "B " << r << " " << g << " " << b << " C F\n";
	}
    }
    pls->linepos = 0;

/* This ensures the color and line width are set correctly at the beginning of
   each page */

    plD_state_psttf(pls, PLSTATE_COLOR0);
    plD_state_psttf(pls, PLSTATE_WIDTH);
}

/*--------------------------------------------------------------------------*\
 * plD_tidy_psttf()
 *
 * Close graphics file or otherwise clean up.
\*--------------------------------------------------------------------------*/

void
plD_tidy_psttf(PLStream *pls)
{
    PSDev *dev = (PSDev *) pls->dev;
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;

    dev->llx /= ENLARGE;
    dev->lly /= ENLARGE;
    dev->urx /= ENLARGE;
    dev->ury /= ENLARGE;
    dev->llx += XOFFSET;
    dev->lly += YOFFSET;
    dev->urx += XOFFSET;
    dev->ury += YOFFSET;

/* changed for correct Bounding boundaries Jan Thorbecke  okt 1993*/
/* occurs from the integer truncation -- postscript uses fp arithmetic */

    dev->urx += 1;
    dev->ury += 1;

    if (pls->family)
        doc->osFooter() << "%%Pages: 1\n";
    else
        doc->osFooter() << "%%Pages: " << (int) pls->page << "\n";

    doc->osFooter() << "@end" << endl;

/* Now write the rest of the header */
    writeHeader(pls);

/* Write out postscript document to file and close*/
    /* For C++ stream we first need to close the file using 
       the C FILE * handle, then reopen as a ofstream. Yuck! */
    fclose(pls->OutFile);
    if (! strcmp(pls->FileName,"-")) {
      doc->write(cout,dev->llx,dev->lly,dev->urx,dev->ury);
    }
    else {      
      ofstream out;
      out.open(pls->FileName);
      doc->write(out,dev->llx,dev->lly,dev->urx,dev->ury);
      out.close();      
    }

    delete doc;
    pls->psdoc = NULL;

}

/*--------------------------------------------------------------------------*\
 * plD_state_psttf()
 *
 * Handle change in PLStream state (color, pen width, fill attribute, etc).
\*--------------------------------------------------------------------------*/

void 
plD_state_psttf(PLStream *pls, PLINT op)
{
    PSDev *dev = (PSDev *) pls->dev;
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;

    switch (op) {

    case PLSTATE_WIDTH:{
	int width = 
	    (pls->width < MIN_WIDTH) ? DEF_WIDTH :
	    (pls->width > MAX_WIDTH) ? MAX_WIDTH : pls->width;

	doc->osBody() << " S\n" << width << " W";

	dev->xold = PL_UNDEFINED;
	dev->yold = PL_UNDEFINED;
	break;
    }
    case PLSTATE_COLOR0:
 	if (! pls->color) {
	    doc->osBody() << " S\n" << (pls->icol0 ? 0.0 : 1.0) << " G";
	    break;
	}
	/* else fallthrough */
    case PLSTATE_COLOR1:
	if (pls->color) {
	    PLFLT r = ((PLFLT) pls->curcolor.r) / 255.0;
	    PLFLT g = ((PLFLT) pls->curcolor.g) / 255.0;
	    PLFLT b = ((PLFLT) pls->curcolor.b) / 255.0;

	    doc->osBody() << " S\n" << r << " " << g << " " << b << " C";
	}
	else {
	    PLFLT r = ((PLFLT) pls->curcolor.r) / 255.0;
	    doc->osBody() << " S\n" << 1.0 - r << " G";
	}
	break;
    }

/* Reinitialize current point location. */

    if (dev->xold != PL_UNDEFINED && dev->yold != PL_UNDEFINED) {
	doc->osBody() << " " << (int)dev->xold << " " << (int)dev->yold << " M \n";
    }
}

/*--------------------------------------------------------------------------*\
 * plD_esc_psttf()
 *
 * Escape function.
\*--------------------------------------------------------------------------*/

void
plD_esc_psttf(PLStream *pls, PLINT op, void *ptr)
{
    switch (op) {
    case PLESC_FILL:
      fill_polygon(pls);
      break;
    case PLESC_HAS_TEXT:
      proc_str(pls, (EscText *)ptr);
      break;
    }
}

/*--------------------------------------------------------------------------*\
 * fill_polygon()
 *
 * Fill polygon described in points pls->dev_x[] and pls->dev_y[].
 * Only solid color fill supported.
\*--------------------------------------------------------------------------*/

static void
fill_polygon(PLStream *pls)
{
    PSDev *dev = (PSDev *) pls->dev;
    PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
    PLINT n, ix = 0, iy = 0;
    PLINT x, y;

    doc->osBody() << " Z\n";

    for (n = 0; n < pls->dev_npts; n++) {
	x = pls->dev_x[ix++];
	y = pls->dev_y[iy++];

/* Rotate by 90 degrees */

	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, &x, &y);

/* First time through start with a x y moveto */

	if (n == 0) {
	    sprintf(outbuf, "%d %d M", x, y);
	    dev->llx = MIN(dev->llx, x);
	    dev->lly = MIN(dev->lly, y);
	    dev->urx = MAX(dev->urx, x);
	    dev->ury = MAX(dev->ury, y);
	    doc->osBody() << outbuf;
	    pls->bytecnt += strlen(outbuf);
	    continue;
	}

	if (pls->linepos + 21 > LINELENGTH) {
	    doc->osBody() << '\n';
	    pls->linepos = 0;
	}
	else
	    doc->osBody() << ' ';

	pls->bytecnt++;

	sprintf(outbuf, "%d %d D", x, y);
	dev->llx = MIN(dev->llx, x);
	dev->lly = MIN(dev->lly, y);
	dev->urx = MAX(dev->urx, x);
	dev->ury = MAX(dev->ury, y);

	doc->osBody() << outbuf;
	pls->bytecnt += strlen(outbuf);
	pls->linepos += 21;
    }
    dev->xold = PL_UNDEFINED;
    dev->yold = PL_UNDEFINED;
    doc->osBody() << " F ";
}

/*--------------------------------------------------------------------------*\
 * ps_getdate()
 *
 * Get the date and time
\*--------------------------------------------------------------------------*/

static char *
ps_getdate(void)
{
    int len;
    time_t t;
    char *p;

    t = time((time_t *) 0);
    p = ctime(&t);
    len = strlen(p);
    *(p + len - 1) = '\0';	/* zap the newline character */
    return p;
}


/*--------------------------------------------------------------------------*\
 * proc_str()
 *
 * Prints postscript strings.
 * N.B. Now unicode only, no string access!
 *
\*--------------------------------------------------------------------------*/

void
proc_str (PLStream *pls, EscText *args)
{
  PLFLT *t = args->xform, tt[4]; /* Transform matrices */
  PLFLT theta;  /* Rotation angle and shear from the matrix */
  PLFLT ft_ht, offset; /* Font height and offset */
  PLFLT cs,sn;
  PSDev *dev = (PSDev *) pls->dev;
  PostscriptDocument *doc = (PostscriptDocument *) pls->psdoc;
  char *font, esc;
  FontStyle style;
  FontWeight weight;
  /* Be generous.  Used to store lots of font changes which take
   * 3 characters per change.*/
  #define PROC_STR_STRING_LENGTH 1000
  char *strp, str[PROC_STR_STRING_LENGTH], *cur_strp, 
     cur_str[PROC_STR_STRING_LENGTH];
  float font_factor = 1.4;
  PLINT clxmin, clxmax, clymin, clymax; /* Clip limits */
  PLINT clipx[4],clipy[4]; /* Current clip limits */

  PLFLT scale = 1., up = 0.; /* Font scaling and shifting parameters */

  double lineSpacing, xAdvance, xwid, ymintmp, ymaxtmp, ymin, ymax, xmin, xmax;
  PLINT xx[4], yy[4];

  int i=0; /* String index */
  int k=0;

  short text_len;

  FT_Data *FT;

   /* unicode only! so test for it. */
   if (args->unicode_array_len>0)
     {

       FT = (FT_Data *) pls->FT;
	int j,s,f;
	char  *fonts[PROC_STR_STRING_LENGTH];
	FontStyle styles[PROC_STR_STRING_LENGTH];
	FontWeight weights[PROC_STR_STRING_LENGTH];
	const PLUNICODE *cur_text;
	const PLUNICODE *cur_text_limit;
	PLUNICODE fci;
	unsigned char fontfamily, fontstyle, fontweight;
	/* translate from unicode into type 1 font index. */
	/*
	 * Choose the font family, style, variant, and weight using
	 * the FCI (font characterization integer).
	 */

	plgesc(&esc);
	plgfci(&fci);
	plP_fci2hex(fci,&fontfamily,PL_FCI_FAMILY);
	plP_fci2hex(fci,&fontstyle,PL_FCI_STYLE);
	plP_fci2hex(fci,&fontweight,PL_FCI_WEIGHT);
	font = (char *) FamilyLookup[fontfamily];
	weight = WeightLookup[fontweight];
	style = StyleLookup[fontstyle];
	/* Need to add some error checking here */
	if (false) {
	   fprintf(stderr, "fci = 0x%x, font name pointer = NULL \n", fci);
	   plabort("proc_str: FCI inconsistent with TrueTypeLookup; "
		   "internal PLplot error");
	   return;
	}
	/*pldebug("proc_str", "fci = 0x%x, font name = %s\n", fci, font);*/
	cur_text =  args->unicode_array;
	for (f=s=j=0; j < args->unicode_array_len; j++) {
	   if (cur_text[j] & PL_FCI_MARK) {
	      /* process an FCI by saving it and escaping cur_str
	       * with an escff to make it a 2-character escape
	       * that is not used in legacy Hershey code
	       */
	      if ((f < PROC_STR_STRING_LENGTH) && (s+3 < PROC_STR_STRING_LENGTH)) {
		 plP_fci2hex(cur_text[j],&fontfamily,PL_FCI_FAMILY);
		 plP_fci2hex(cur_text[j],&fontstyle,PL_FCI_STYLE);
		 plP_fci2hex(cur_text[j],&fontweight,PL_FCI_WEIGHT);
		 fonts[f] = (char *) FamilyLookup[fontfamily];
		 weights[f] = WeightLookup[fontweight];
		 styles[f] = StyleLookup[fontstyle];
		 if (fonts[f] == NULL) {
		    fprintf(stderr, "string-supplied FCI = 0x%x, font name pointer = NULL \n", cur_text[j]);
		    plabort("proc_str: string-supplied FCI inconsistent with font lookup;");
		    return;
		 }
		 /*pldebug("proc_str", "string-supplied FCI = 0x%x, font name = %s\n", cur_text[j], fonts[f]);*/
		 cur_str[s++] = esc;
		 cur_str[s++] = 'f';
		 cur_str[s++] = 'f';
		 f++;
	      }
	   }
	   else if (s+1 < PROC_STR_STRING_LENGTH) {
	      s += ucs4_to_utf8(cur_text[j],&cur_str[s]);
	      /*pldebug("proc_str", "unicode = 0x%x, type 1 code = %d\n",
	                cur_text[j], cur_str[j]);*/
	   }
	}
	cur_str[s] = '\0';
	
	/* finish previous polyline */
	
	dev->xold = PL_UNDEFINED;
	dev->yold = PL_UNDEFINED;
	
	/* Determine the font height */
	ft_ht = pls->chrht * 72.0/25.4; /* ft_ht in points, ht is in mm */
	
	
	/* The transform matrix has only rotations and shears; extract them */
	theta = acos(t[0]) * 180. / PI;  /* Determine the rotation (in degrees)... */
	if (t[2] < 0.) theta *= -1.;     /* ... and sign ... */
	cs = cos(theta*PI/180.);
	sn = sin(theta*PI/180.);
	tt[0] = t[0]*cs + t[2]*sn;
	tt[1] = t[1]*cs + t[3]*sn;
	tt[2] = -t[0]*sn + t[2]*cs;
	tt[3] = -t[1]*sn + t[3]*cs;
	
	/* 
	 * Reference point conventions:
	 *   If base = 0, it is aligned with the center of the text box
	 *   If base = 1, it is aligned with the baseline of the text box
	 *   If base = 2, it is aligned with the top of the text box
	 *
	 * Currently plplot only uses base=0
	 * Postscript uses base=1
	 *
	 * We must calculate the difference between the two and apply the offset.
	 */ 
	
	if (args->base == 2) /* not supported by plplot */
	  offset = ENLARGE * ft_ht / 2.; /* half font height */
	else if (args->base == 1)
	  offset = 0.;
	else
	  offset = -ENLARGE * ft_ht / 2.;
	
	/* Determine the adjustment for page orientation */
	theta -= 90.*pls->diorot;
	args->y += (int) (offset*cos(theta*PI/180.));
	args->x -= (int) (offset*sin(theta*PI/180.));
		
	/* ps driver is rotated by default */
	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax, 
		 &(args->x), &(args->y));
	
	/* Correct for the fact ps driver uses landscape by default */
	theta += 90.;
	
	/* Output */
	/* Set clipping */
	clipx[0]=pls->clpxmi;
	clipx[2]=pls->clpxma;
	clipy[0]=pls->clpymi;
	clipy[2]=pls->clpyma;
	clipx[1]=clipx[2];
	clipy[1]=clipy[0];
	clipx[3]=clipx[0];
	clipy[3]=clipy[2];
	difilt(clipx, clipy, 4, &clxmin, &clxmax, &clymin, &clymax);
	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
	         &clipx[0], &clipy[0]);
	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
	         &clipx[1], &clipy[1]);
	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
	         &clipx[2], &clipy[2]);
	plRotPhy(ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
	         &clipx[3], &clipy[3]);
// 	doc->osBody() << " gsave " << clipx[0] << " " << clipy[0] << " " << 
// 	  clipx[1] << " " << clipy[1] << " " << clipx[2] << " " << 
// 	  clipy[2] << " " << clipx[3] << " " << clipy[3] << " CL\n";
	
	/* move to string reference point */
	doc->osBody() << " " << args->x << " " << args->y << " M\n";
	
	/* Save the current position and set the string rotation */
	doc->osBody() << "gsave " << theta << " R\n";

	doc->osBody() << "[" << tt[0] << " " << tt[2] << " " << tt[1]
		      << " " << tt[3] << " 0 0] concat\n";
	
	/* Purge escape sequences from string, to find it's 
	 * length. The string length is computed with the current font, 
	 * and can thus be wrong if there are font change escape sequences 
	 * in the string 
	 */	
	esc_purge(str, cur_str);

	doc->setFont(font,style,weight);
	doc->setFontSize(font_factor*ENLARGE*ft_ht);

	// Get the approximate length of the string to calculate offset
	// Also used later for bounding box
	doc->get_dimensions((const char *)str, &lineSpacing, &xAdvance);
	xmin = -xAdvance*args->just;
	xmax = xmin;
	ymin = 0;
	ymax = 0;

	/* Move relative to position to account for justification */
	doc->osBody() << " gsave " << xmin*tt[0] << " " <<
	  xmin*tt[2] << " rmoveto\n";
	
	/* Parse string for PLplot escape sequences and print everything out */
	
	cur_strp = cur_str;
	f = 0;
	do {
	   
	   strp = str;
	   
	   if (*cur_strp == esc) {
	      cur_strp++;
	      
	      if (*cur_strp == esc) { /* <esc><esc> */
		 *strp++ = *cur_strp++;
	      }
	      else if (*cur_strp == 'f') {
		 cur_strp++;
		 if (*cur_strp++ != 'f') {
		    /* escff occurs because of logic above. But any suffix
		     * other than "f" should never happen. */
		    plabort("proc_str, internal PLplot logic error;"
			    "wrong escf escape sequence");
		    return;
		 }
		 font = fonts[f];
		 style = styles[f];
		 weight = weights[f];
		 f++;
		 /*pldebug("proc_str", "string-specified fci = 0x%x, font name = %s\n", fci, font);*/
		 continue;
	      }
	      else switch (*cur_strp++) {
		 
	       case 'd':
	       case 'D':
		 if(up>0.) scale *= 1.25;  /* Subscript scaling parameter */
		 else scale *= 0.8;  /* Subscript scaling parameter */
		 up -= font_factor * ENLARGE * ft_ht / 2.;
		 break;
		 
	       case 'u':
	       case 'U':
		 if(up<0.) scale *= 1.25;  /* Subscript scaling parameter */
		 else scale *= 0.8;  /* Subscript scaling parameter */
		 up += font_factor * ENLARGE * ft_ht / 2.;
		 break;
		 
		 /* ignore the next sequences */
		 
	       case '+':
	       case '-':
	       case 'b':
	       case 'B':
		 plwarn("'+', '-', and 'b/B' text escape sequences not processed.");
		 break;
	      }
	   }
	   
	   /* copy from current to next token, adding a postscript escape 
	    * char '\' if necessary 
	    */
	   while(*cur_strp && *cur_strp != esc) {
	      *strp++ = *cur_strp++;
	   }
	   *strp = '\0';
	   
	   if(fabs(up)<0.001) up = 0.; /* Watch out for small differences */
	   
	   /* Set the font size */
	   doc->setFontSize(font_factor*ENLARGE*ft_ht*scale);
	   doc->setFont(font,style,weight);
	   doc->get_dimensions((const char *)str, &lineSpacing, &xAdvance, &ymintmp, &ymaxtmp);
	   ymin = MIN(ymintmp+up,ymin);
	   ymax = MAX(ymaxtmp+up,ymax);
	   xmax += xAdvance;

	   /* if up/down escape sequences, save current point and adjust baseline;
	    * take the shear into account */
	   if(up!=0.) 
	     doc->osBody() << "gsave " << up*tt[1] << " " << up*tt[3] << " rmoveto\n";
	   
	   /* print the string */
	   doc->osBody() << show((const char *)str);
	   
	   /* back to baseline */
	   if (up!=0.) 
	     doc->osBody() << "grestore (" << str << ") stringwidth rmoveto\n";
	   
	}while(*cur_strp);
	
	doc->osBody() << "grestore\n";
	doc->osBody() << "grestore\n";
	
	/*
	 * Estimate text bounding box from LASi get_dimensions function.
	 * xmin, xmax are text left and right extents,
	 * ymin, ymax are top and bottom extents.
	 * These need to be rotated / transformed to get the correct values
	 */
	xx[0] = (PLINT) (t[0]*xmin+t[1]*ymin);
	yy[0] = (PLINT) (t[2]*xmin+t[3]*ymin);
	xx[1] = (PLINT) (t[0]*xmin+t[1]*ymax);
	yy[1] = (PLINT) (t[2]*xmin+t[3]*ymax);
	xx[2] = (PLINT) (t[0]*xmax+t[1]*ymin);
	yy[2] = (PLINT) (t[2]*xmax+t[3]*ymin);
	xx[3] = (PLINT) (t[0]*xmax+t[1]*ymax);
	yy[3] = (PLINT) (t[2]*xmax+t[3]*ymax);

	plRotPhy(ORIENTATION, 0,0,0,0, &xx[0], &yy[0]);
	plRotPhy(ORIENTATION, 0,0,0,0, &xx[1], &yy[1]);
	plRotPhy(ORIENTATION, 0,0,0,0, &xx[2], &yy[2]);
	plRotPhy(ORIENTATION, 0,0,0,0, &xx[3], &yy[3]);


	xmin = MIN(MIN(MIN(xx[0],xx[1]),xx[2]),xx[3])+args->x;
	xmax = MAX(MAX(MAX(xx[0],xx[1]),xx[2]),xx[3])+args->x;
	ymin = MIN(MIN(MIN(yy[0],yy[1]),yy[2]),yy[3])+args->y;
	ymax = MAX(MAX(MAX(yy[0],yy[1]),yy[2]),yy[3])+args->y;
	
	dev->llx = (int) (MIN(dev->llx, xmin));
	dev->lly = (int) (MIN(dev->lly, ymin));
	dev->urx = (int) (MAX(dev->urx, xmax));
	dev->ury = (int) (MAX(dev->ury, ymax));
// 	doc->osBody() << "Z " << xmin << " " << ymin << " M "
// 		      << xmin << " " << ymax << " D " 
// 		      << xmax << " " << ymax << " D " 
// 		      << xmax << " " << ymin << " D " 
// 		      << xmin << " " << ymin << " closepath\n"
// 		      << "Z " << args->x << " " << args->y << " A closepath\n";
     }
}

static void
esc_purge(char *dstr, char *sstr) {
  char esc;

  plgesc(&esc);

  while(*sstr){
    if (*sstr != esc) {
      *dstr++ = *sstr++;
      continue;
    }

    sstr++;
    if (*sstr == esc) {
      *dstr++ = *sstr++;
      continue;
    }
     
    else {
      switch(*sstr++) {
      case 'f':
	sstr++;
	break; /* two chars sequence */

      default:
	break;  /* single char escape */
      }
    }
  }
  *dstr = '\0';
}

#else
int 
pldummy_psttf()
{
    return 0;
}

#endif				/* defined(PLD_psttf) || .... */


syntax highlighted by Code2HTML, v. 0.9.1