/*
 * Smithsonian Astrophysical Observatory, Cambridge, MA, USA
 * This code has been modified under the terms listed below and is made
 * available under the same terms.
 */

/*
 *	Copyright 1991-2004 George A Howlett.
 *
 *	Permission is hereby granted, free of charge, to any person obtaining
 *	a copy of this software and associated documentation files (the
 *	"Software"), to deal in the Software without restriction, including
 *	without limitation the rights to use, copy, modify, merge, publish,
 *	distribute, sublicense, and/or sell copies of the Software, and to
 *	permit persons to whom the Software is furnished to do so, subject to
 *	the following conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <cmath>

#include "tk.h"

// copied from tk3d.h

typedef struct TkBorder {
    Screen *screen;		/* Screen on which the border will be used. */
    Visual *visual;		/* Visual for all windows and pixmaps using
				 * the border. */
    int depth;			/* Number of bits per pixel of drawables where
				 * the border will be used. */
    Colormap colormap;		/* Colormap out of which pixels are
				 * allocated. */
    int resourceRefCount;	/* Number of active uses of this color (each
				 * active use corresponds to a call to
				 * Tk_Alloc3DBorderFromObj or Tk_Get3DBorder).
				 * If this count is 0, then this structure is
				 * no longer valid and it isn't present in
				 * borderTable: it is being kept around only
				 * because there are objects referring to it.
				 * The structure is freed when objRefCount and
				 * resourceRefCount are both 0. */
    int objRefCount;		/* The number of Tcl objects that reference
				 * this structure. */
    XColor *bgColorPtr;		/* Background color (intensity between
				 * lightColorPtr and darkColorPtr). */
    XColor *darkColorPtr;	/* Color for darker areas (must free when
				 * deleting structure). NULL means shadows
				 * haven't been allocated yet.*/
    XColor *lightColorPtr;	/* Color used for lighter areas of border
				 * (must free this when deleting structure).
				 * NULL means shadows haven't been allocated
				 * yet. */
    Pixmap shadow;		/* Stipple pattern to use for drawing shadows
				 * areas. Used for displays with <= 64 colors
				 * or where colormap has filled up. */
    GC bgGC;			/* Used (if necessary) to draw areas in the
				 * background color. */
    GC darkGC;			/* Used to draw darker parts of the border.
				 * None means the shadow colors haven't been
				 * allocated yet.*/
    GC lightGC;			/* Used to draw lighter parts of the border.
				 * None means the shadow colors haven't been
				 * allocated yet. */
    Tcl_HashEntry *hashPtr;	/* Entry in borderTable (needed in order to
				 * delete structure). */
    struct TkBorder *nextPtr;	/* Points to the next TkBorder structure with
				 * the same color name. Borders with the same
				 * name but different screens or colormaps are
				 * chained together off a single entry in
				 * borderTable. */
} TkBorder;

#include "tkbltGraph.h"
#include "tkbltGrPostscript.h"
#include "tkbltGrPSOutput.h"

using namespace Blt;

PSOutput::PSOutput(Graph* graphPtr)
{
  graphPtr_ = graphPtr;

  Tcl_DStringInit(&dString_);
}

PSOutput::~PSOutput()
{
  Tcl_DStringFree(&dString_);
}

void PSOutput::printPolyline(Point2d* screenPts, int nScreenPts)
{
  Point2d* pp = screenPts;
  append("newpath\n");
  format("  %g %g moveto\n", pp->x, pp->y);

  Point2d* pend;
  for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++)
    format("  %g %g lineto\n", pp->x, pp->y);
}

void PSOutput::printMaxPolyline(Point2d* points, int nPoints)
{
  if (nPoints <= 0)
    return;

  for (int nLeft = nPoints; nLeft > 0; nLeft -= 1500) {
    int length = MIN(1500, nLeft);
    printPolyline(points, length);
    append("DashesProc stroke\n");
    points += length;
  }
}

void PSOutput::printSegments(Segment2d* segments, int nSegments)
{
  append("newpath\n");

  for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) {
    format("  %g %g moveto %g %g lineto\n", sp->p.x, sp->p.y, sp->q.x, sp->q.y);
    append("DashesProc stroke\n");
  }
}

void PSOutput::computeBBox(int width, int height)
{
  Postscript* setupPtr = graphPtr_->postscript_;
  PostscriptOptions* pops = (PostscriptOptions*)setupPtr->ops_;

  // scale from points to pica
  double pica = 25.4 / 72 *
    WidthOfScreen(Tk_Screen(graphPtr_->tkwin_)) / 
    WidthMMOfScreen(Tk_Screen(graphPtr_->tkwin_));

  double hBorder = 2*pops->xPad/pica;
  double vBorder = 2*pops->yPad/pica;
  int hSize = !pops->landscape ? width : height; 
  int vSize = !pops->landscape ? height : width;

  // If the paper size wasn't specified, set it to the graph size plus the
  // paper border.
  double paperWidth = pops->reqPaperWidth > 0 ? pops->reqPaperWidth/pica :
    hSize + hBorder;
  double paperHeight = pops->reqPaperHeight > 0 ? pops->reqPaperHeight/pica :
    vSize + vBorder;

  // Scale the plot size if it's bigger than the paper
  double hScale = (hSize+hBorder) > paperWidth ? 1.0 :
    paperWidth - hBorder / hSize;
  double vScale = (vSize + vBorder) > paperHeight ? 1.0 :
    paperHeight - vBorder / vSize;

  double scale = MIN(hScale, vScale);
  if (scale != 1.0) {
    hSize = (int)(hSize*scale + 0.5);
    vSize = (int)(vSize*scale + 0.5);
  }

  int x = (int)((paperWidth > hSize) && pops->center ?
		(paperWidth - hSize) / 2 : pops->xPad/pica);
  int y = (int)((paperHeight > vSize) && pops->center ?
		(paperHeight - vSize) / 2 : pops->yPad/pica);

  setupPtr->left = x;
  setupPtr->bottom = y;
  setupPtr->right = x + hSize - 1;
  setupPtr->top = y + vSize - 1;
  setupPtr->scale = scale;
  setupPtr->paperHeight = (int)paperHeight;
  setupPtr->paperWidth = (int)paperWidth;
}

const char* PSOutput::getValue(int* lengthPtr)
{
  *lengthPtr = strlen(Tcl_DStringValue(&dString_));
  return Tcl_DStringValue(&dString_);
}

void PSOutput::append(const char* string)
{
  Tcl_DStringAppend(&dString_, string, -1);
}

void PSOutput::format(const char* fmt, ...)
{
  va_list argList;

  va_start(argList, fmt);
  vsnprintf(scratchArr_, POSTSCRIPT_BUFSIZ, fmt, argList);
  va_end(argList);
  Tcl_DStringAppend(&dString_, scratchArr_, -1);
}

void PSOutput::setLineWidth(int lineWidth)
{
  if (lineWidth < 1)
    lineWidth = 1;
  format("%d setlinewidth\n", lineWidth);
}

void PSOutput::printRectangle(double x, double y, int width, int height)
{
  append("newpath\n");
  format("  %g %g moveto\n", x, y);
  format("  %d %d rlineto\n", width, 0);
  format("  %d %d rlineto\n", 0, height);
  format("  %d %d rlineto\n", -width, 0);
  append("closepath\n");
  append("stroke\n");
}

void PSOutput::fillRectangle(double x, double y, int width, int height)
{
  append("newpath\n");
  format("  %g %g moveto\n", x, y);
  format("  %d %d rlineto\n", width, 0);
  format("  %d %d rlineto\n", 0, height);
  format("  %d %d rlineto\n", -width, 0);
  append("closepath\n");
  append("fill\n");
}

void PSOutput::fillRectangles(Rectangle* rectangles, int nRectangles)
{
  for (Rectangle *rp = rectangles, *rend = rp + nRectangles; rp < rend; rp++)
    fillRectangle((double)rp->x, (double)rp->y, (int)rp->width,(int)rp->height);
}

void PSOutput::setBackground(XColor* colorPtr)
{
  PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_;
  printXColor(colorPtr);
  append(" setrgbcolor\n");
  if (pops->greyscale)
    append(" currentgray setgray\n");
}

void PSOutput::setForeground(XColor* colorPtr)
{
  PostscriptOptions* pops = (PostscriptOptions*)graphPtr_->postscript_->ops_;
  printXColor(colorPtr);
  append(" setrgbcolor\n");
  if (pops->greyscale)
    append(" currentgray setgray\n");
}

void PSOutput::setBackground(Tk_3DBorder border)
{
  TkBorder* borderPtr = (TkBorder*)border;
  setBackground(borderPtr->bgColorPtr);
}

void PSOutput::setFont(Tk_Font font) 
{
  Tcl_DString psdstr;
  Tcl_DStringInit(&psdstr);
  int psSize = Tk_PostscriptFontName(font, &psdstr);
  format("%d /%s SetFont\n", psSize, Tcl_DStringValue(&psdstr));
  Tcl_DStringFree(&psdstr);
}

void PSOutput::setLineAttributes(XColor* colorPtr,int lineWidth,
				   Dashes* dashesPtr, int capStyle, 
				   int joinStyle)
{
  setJoinStyle(joinStyle);
  setCapStyle(capStyle);
  setForeground(colorPtr);
  setLineWidth(lineWidth);
  setDashes(dashesPtr);
  append("/DashesProc {} def\n");
}

void PSOutput::fill3DRectangle(Tk_3DBorder border, double x, double y,
				 int width, int height, int borderWidth, 
				 int relief)
{
  TkBorder* borderPtr = (TkBorder*)border;

  setBackground(borderPtr->bgColorPtr);
  fillRectangle(x, y, width, height);
  print3DRectangle(border, x, y, width, height, borderWidth, relief);
}

void PSOutput::setClearBackground()
{
  append("1 1 1 setrgbcolor\n");
}

void PSOutput::setDashes(Dashes* dashesPtr)
{

  append("[ ");
  if (dashesPtr) {
    for (unsigned char* vp = dashesPtr->values; *vp != 0; vp++)
      format(" %d", *vp);
  }
  append("] 0 setdash\n");
}

void PSOutput::fillPolygon(Point2d *screenPts, int nScreenPts)
{
  printPolygon(screenPts, nScreenPts);
  append("fill\n");
}

void PSOutput::setJoinStyle(int joinStyle)
{
  // miter = 0, round = 1, bevel = 2
  format("%d setlinejoin\n", joinStyle);
}

void PSOutput::setCapStyle(int capStyle)
{
  // X11:not last = 0, butt = 1, round = 2, projecting = 3
  // PS: butt = 0, round = 1, projecting = 2
  if (capStyle > 0)
    capStyle--;

  format("%d setlinecap\n", capStyle);
}

void PSOutput::printPolygon(Point2d *screenPts, int nScreenPts)
{
  Point2d* pp = screenPts;
  append("newpath\n");
  format("  %g %g moveto\n", pp->x, pp->y);

  Point2d* pend;
  for (pp++, pend = screenPts + nScreenPts; pp < pend; pp++) 
    format("  %g %g lineto\n", pp->x, pp->y);

  format("  %g %g lineto\n", screenPts[0].x, screenPts[0].y);
  append("closepath\n");
}

void PSOutput::print3DRectangle(Tk_3DBorder border, double x, double y,
				 int width, int height, int borderWidth,
				 int relief)
{
  int twiceWidth = (borderWidth * 2);
  if ((width < twiceWidth) || (height < twiceWidth))
    return;

  TkBorder* borderPtr = (TkBorder*)border;

  // Handle grooves and ridges with recursive calls
  if ((relief == TK_RELIEF_GROOVE) || (relief == TK_RELIEF_RIDGE)) {
    int halfWidth = borderWidth / 2;
    int insideOffset = borderWidth - halfWidth;
    print3DRectangle(border, (double)x, (double)y, width, height, halfWidth, 
		    (relief == TK_RELIEF_GROOVE) ? 
		    TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
    print3DRectangle(border, (double)(x + insideOffset), 
		    (double)(y + insideOffset), width - insideOffset * 2, 
		    height - insideOffset * 2, halfWidth,
		    (relief == TK_RELIEF_GROOVE) ? 
		    TK_RELIEF_RAISED : TK_RELIEF_SUNKEN);
    return;
  }

  XColor* lightPtr = borderPtr->lightColorPtr;
  XColor* darkPtr = borderPtr->darkColorPtr;
  XColor light;
  if (!lightPtr) {
    light.red = 0x00;
    light.blue = 0x00;
    light.green = 0x00;
    lightPtr = &light;
  }
  XColor dark;
  if (!darkPtr) {
    dark.red = 0x00;
    dark.blue = 0x00;
    dark.green = 0x00;
    darkPtr = &dark;
  }

  XColor* topPtr, *bottomPtr;
  if (relief == TK_RELIEF_RAISED) {
    topPtr = lightPtr;
    bottomPtr = darkPtr;
  }
  else if (relief == TK_RELIEF_SUNKEN) {
    topPtr = darkPtr;
    bottomPtr = lightPtr;
  }
  else if (relief == TK_RELIEF_SOLID) {
    topPtr = lightPtr;
    bottomPtr = lightPtr;
  }
  else {
    topPtr = borderPtr->bgColorPtr;
    bottomPtr = borderPtr->bgColorPtr;
  }

  setBackground(bottomPtr);
  fillRectangle(x, y + height - borderWidth, width, borderWidth);
  fillRectangle(x + width - borderWidth, y, borderWidth, height);

  Point2d points[7];
  points[0].x = points[1].x = points[6].x = x;
  points[0].y = points[6].y = y + height;
  points[1].y = points[2].y = y;
  points[2].x = x + width;
  points[3].x = x + width - borderWidth;
  points[3].y = points[4].y = y + borderWidth;
  points[4].x = points[5].x = x + borderWidth;
  points[5].y = y + height - borderWidth;
  if (relief != TK_RELIEF_FLAT)
    setBackground(topPtr);

  fillPolygon(points, 7);
}

void PSOutput::printXColor(XColor* colorPtr)
{
  format("%g %g %g",
	 ((double)(colorPtr->red >> 8) / 255.0),
	 ((double)(colorPtr->green >> 8) / 255.0),
	 ((double)(colorPtr->blue >> 8) / 255.0));
}

int PSOutput::preamble(const char* fileName)
{
  Postscript* setupPtr = graphPtr_->postscript_;
  PostscriptOptions* ops = (PostscriptOptions*)setupPtr->ops_;

  if (!fileName)
    fileName = Tk_PathName(graphPtr_->tkwin_);

  // Comments
  append("%!PS-Adobe-3.0 EPSF-3.0\n");

  // The "BoundingBox" comment is required for EPS files. The box
  // coordinates are integers, so we need round away from the center of the
  // box.
  format("%%%%BoundingBox: %d %d %d %d\n",
	 setupPtr->left, setupPtr->paperHeight - setupPtr->top,
	 setupPtr->right, setupPtr->paperHeight - setupPtr->bottom);
	
  append("%%Pages: 0\n");

  format("%%%%Creator: (%s %s %s)\n", PACKAGE_NAME, PACKAGE_VERSION,
	 Tk_Class(graphPtr_->tkwin_));

  time_t ticks = time((time_t *) NULL);
  char date[200];
  strcpy(date, ctime(&ticks));
  char* newline = date + strlen(date) - 1;
  if (*newline == '\n')
    *newline = '\0';

  format("%%%%CreationDate: (%s)\n", date);
  format("%%%%Title: (%s)\n", fileName);
  append("%%DocumentData: Clean7Bit\n");
  if (ops->landscape)
    append("%%Orientation: Landscape\n");
  else
    append("%%Orientation: Portrait\n");

  append("%%DocumentNeededResources: font Helvetica Courier\n");
  addComments(ops->comments);
  append("%%EndComments\n\n");

  // Prolog
  prolog();

  // Setup
  append("%%BeginSetup\n");
  append("gsave\n");
  append("1 setlinewidth\n");
  append("1 setlinejoin\n");
  append("0 setlinecap\n");
  append("[] 0 setdash\n");
  append("0 0 0 setrgbcolor\n");

  if (ops->footer) {
    const char* who = getenv("LOGNAME");
    if (!who)
      who = "???";

    append("8 /Helvetica SetFont\n");
    append("10 30 moveto\n");
    format("(Date: %s) show\n", date);
    append("10 20 moveto\n");
    format("(File: %s) show\n", fileName);
    append("10 10 moveto\n");
    format("(Created by: %s@%s) show\n", who, Tcl_GetHostName());
    append("0 0 moveto\n");
  }

  // Set the conversion from postscript to X11 coordinates. Scale pica to
  // pixels and flip the y-axis (the origin is the upperleft corner).
  // Papersize is in pixels. Translate the new origin *after* changing the scale
  append("% Transform coordinate system to use X11 coordinates\n");
  append("% 1. Flip y-axis over by reversing the scale,\n");
  append("% 2. Translate the origin to the other side of the page,\n");
  append("%    making the origin the upper left corner\n");
  append("1 -1 scale\n");
  format("0 %d translate\n", -setupPtr->paperHeight);

  // Set Origin
  format("%% Set origin\n%d %d translate\n\n", setupPtr->left,setupPtr->bottom);
  if (ops->landscape)
    format("%% Landscape orientation\n0 %g translate\n-90 rotate\n",
	   ((double)graphPtr_->width_ * setupPtr->scale));

  append("\n%%EndSetup\n\n");

  return TCL_OK;
}

void PSOutput::addComments(const char** comments)
{
  if (!comments)
    return;

  for (const char** pp = comments; *pp; pp+=2) {
    if (*(pp+1) == NULL)
      break;
    format("%% %s: %s\n", *pp, *(pp+1));
  }
}

unsigned char PSOutput::reverseBits(unsigned char byte)
{
  byte = ((byte >> 1) & 0x55) | ((byte << 1) & 0xaa);
  byte = ((byte >> 2) & 0x33) | ((byte << 2) & 0xcc);
  byte = ((byte >> 4) & 0x0f) | ((byte << 4) & 0xf0);
  return byte;
}

void PSOutput::byteToHex(unsigned char byte, char* string)
{
  static char hexDigits[] = "0123456789ABCDEF";

  string[0] = hexDigits[byte >> 4];
  string[1] = hexDigits[byte & 0x0F];
}

void PSOutput::prolog()
{
  append(
"%%BeginProlog\n"
"%\n"
"% PostScript prolog file of the BLT graph widget.\n"
"%\n"
"% Copyright 1989-1992 Regents of the University of California.\n"
"% Permission to use, copy, modify, and distribute this\n"
"% software and its documentation for any purpose and without\n"
"% fee is hereby granted, provided that the above copyright\n"
"% notice appear in all copies.  The University of California\n"
"% makes no representations about the suitability of this\n"
"% software for any purpose.  It is provided 'as is' without\n"
"% express or implied warranty.\n"
"%\n"
"% Copyright 1991-1997 Bell Labs Innovations for Lucent Technologies.\n"
"%\n"
"% Permission to use, copy, modify, and distribute this software and its\n"
"% documentation for any purpose and without fee is hereby granted, provided\n"
"% that the above copyright notice appear in all copies and that both that the\n"
"% copyright notice and warranty disclaimer appear in supporting documentation,\n"
"% and that the names of Lucent Technologies any of their entities not be used\n"
"% in advertising or publicity pertaining to distribution of the software\n"
"% without specific, written prior permission.\n"
"%\n"
"% Lucent Technologies disclaims all warranties with regard to this software,\n"
"% including all implied warranties of merchantability and fitness.  In no event\n"
"% shall Lucent Technologies be liable for any special, indirect or\n"
"% consequential damages or any damages whatsoever resulting from loss of use,\n"
"% data or profits, whether in an action of contract, negligence or other\n"
"% tortuous action, arising out of or in connection with the use or performance\n"
"% of this software.\n"
"%\n"
"\n"
"200 dict begin\n"
"\n"
"/BaseRatio 1.3467736870885982 def	% Ratio triangle base / symbol size\n"
"/DrawSymbolProc 0 def			% Routine to draw symbol outline/fill\n"
"/DashesProc 0 def			% Dashes routine (line segments)\n"
"\n"
"% Define the array ISOLatin1Encoding (which specifies how characters are \n"
"% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript \n"
"% level 2 is supposed to define it, but level 1 doesn't). \n"
"\n"
"systemdict /ISOLatin1Encoding known not { \n"
"  /ISOLatin1Encoding [ \n"
"    /space /space /space /space /space /space /space /space \n"
"    /space /space /space /space /space /space /space /space \n"
"    /space /space /space /space /space /space /space /space \n"
"    /space /space /space /space /space /space /space /space \n"
"    /space /exclam /quotedbl /numbersign /dollar /percent /ampersand \n"
"    /quoteright \n"
"    /parenleft /parenright /asterisk /plus /comma /minus /period /slash \n"
"    /zero /one /two /three /four /five /six /seven \n"
"    /eight /nine /colon /semicolon /less /equal /greater /question \n"
"    /at /A /B /C /D /E /F /G \n"
"    /H /I /J /K /L /M /N /O \n"
"    /P /Q /R /S /T /U /V /W \n"
"    /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore \n"
"    /quoteleft /a /b /c /d /e /f /g \n"
"    /h /i /j /k /l /m /n /o \n"
"    /p /q /r /s /t /u /v /w \n"
"    /x /y /z /braceleft /bar /braceright /asciitilde /space \n"
"    /space /space /space /space /space /space /space /space \n"
"    /space /space /space /space /space /space /space /space \n"
"    /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent \n"
"    /dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron \n"
"    /space /exclamdown /cent /sterling /currency /yen /brokenbar /section \n"
"    /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen \n"
"    /registered /macron \n"
"    /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph \n"
"    /periodcentered \n"
"    /cedillar /onesuperior /ordmasculine /guillemotright /onequarter \n"
"    /onehalf /threequarters /questiondown \n"
"    /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla \n"
"    /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex \n"
"    /Idieresis \n"
"    /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply \n"
"    /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn \n"
"    /germandbls \n"
"    /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla \n"
"    /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex \n"
"    /idieresis \n"
"    /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide \n"
"    /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn \n"
"    /ydieresis \n"
"  ] def \n"
"} if \n"
"\n"
"% font ISOEncode font \n"
"% This procedure changes the encoding of a font from the default \n"
"% Postscript encoding to ISOLatin1.  It is typically invoked just \n"
"% before invoking 'setfont'.  The body of this procedure comes from \n"
"% Section 5.6.1 of the Postscript book. \n"
"\n"
"/ISOEncode { \n"
"  dup length dict\n"
"  begin \n"
"    {1 index /FID ne {def} {pop pop} ifelse} forall \n"
"    /Encoding ISOLatin1Encoding def \n"
"    currentdict \n"
"  end \n"
"\n"
"  % I'm not sure why it's necessary to use 'definefont' on this new \n"
"  % font, but it seems to be important; just use the name 'Temporary' \n"
"  % for the font. \n"
"\n"
"  /Temporary exch definefont \n"
"} bind def \n"
"\n"
"/Stroke {\n"
"  gsave\n"
"    stroke\n"
"  grestore\n"
"} def\n"
"\n"
"/Fill {\n"
"  gsave\n"
"    fill\n"
"  grestore\n"
"} def\n"
"\n"
"/SetFont { 	\n"
"  % Stack: pointSize fontName\n"
"  findfont exch scalefont ISOEncode setfont\n"
"} def\n"
"\n"
"/Box {\n"
"  % Stack: x y width height\n"
"  newpath\n"
"    exch 4 2 roll moveto\n"
"    dup 0 rlineto\n"
"    exch 0 exch rlineto\n"
"    neg 0 rlineto\n"
"  closepath\n"
"} def\n"
"\n"
"/LS {	% Stack: x1 y1 x2 y2\n"
"  newpath \n"
"    4 2 roll moveto \n"
"    lineto \n"
"  closepath\n"
"  stroke\n"
"} def\n"
"\n"
"/baselineSampler ( TXygqPZ) def\n"
"% Put an extra-tall character in; done this way to avoid encoding trouble\n"
"baselineSampler 0 196 put\n"
"\n"
"/cstringshow {\n"
"  {\n"
"    dup type /stringtype eq\n"
"    { show } { glyphshow }\n"
"    ifelse\n"
"  } forall\n"
"} bind def\n"
"\n"
"/cstringwidth {\n"
"  0 exch 0 exch\n"
"  {\n"
"    dup type /stringtype eq\n"
"    { stringwidth } {\n"
"      currentfont /Encoding get exch 1 exch put (\001)\n"
"      stringwidth\n"
"    }\n"
"    ifelse\n"
"    exch 3 1 roll add 3 1 roll add exch\n"
"  } forall\n"
"} bind def\n" 
"\n"
"/DrawText {\n"
"  gsave\n"
"  /justify exch def\n"
"  /yoffset exch def\n"
"  /xoffset exch def\n"
"  /strings exch def\n"
"  % Compute the baseline offset and the actual font height.\n"
"  0 0 moveto baselineSampler false charpath\n"
"  pathbbox dup /baseline exch def\n"
"  exch pop exch sub /height exch def pop\n"
"  newpath\n"
"  % overall width\n"
"  /ww 0 def\n"
"  strings {\n"
"    cstringwidth pop\n"
"    dup ww gt {/ww exch def} {pop} ifelse\n"
"    newpath\n"
"  } forall\n"
"  % overall height\n"
"  /hh 0 def\n"
"  strings length height mul /hh exch def\n"
"  newpath\n"
"  % Translate to x,y\n"
"  translate\n"
"  % Translate to offset\n"
"  ww xoffset mul hh yoffset mul translate\n"
"  % rotate\n"
"  ww 2 div hh 2 div translate\n"
"  neg rotate\n"
"  ww -2 div hh -2 div translate\n"
"  % Translate to justify and baseline\n"
"  justify ww mul baseline translate\n"
"  % For each line, justify and display\n"
"  strings {\n"
"    dup cstringwidth pop\n"
"    justify neg mul 0 moveto\n"
"    gsave\n"
"    1 -1 scale\n"
"    cstringshow\n"
"    grestore\n"
"    0 height translate\n"
"  } forall\n"
"  grestore\n"
"} bind def \n"
"\n"
"% Symbols:\n"
"\n"
"% Skinny-cross\n"
"/Sc {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate 45 rotate\n"
"    0 0 3 -1 roll Sp\n"
"  grestore\n"
"} def\n"
"\n"
"% Skinny-plus\n"
"/Sp {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate\n"
"    2 div\n"
"    dup 2 copy\n"
"    newpath \n"
"      neg 0 \n"
"      moveto 0 \n"
"      lineto\n"
"    DrawSymbolProc\n"
"    newpath \n"
"      neg 0 \n"
"      exch moveto 0 \n"
"      exch lineto\n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"% Cross\n"
"/Cr {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate 45 rotate\n"
"    0 0 3 -1 roll Pl\n"
"  grestore\n"
"} def\n"
"\n"
"% Plus\n"
"/Pl {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate\n"
"    dup 2 div\n"
"    exch 6 div\n"
"\n"
"    %\n"
"    %          2   3		The plus/cross symbol is a\n"
"    %				closed polygon of 12 points.\n"
"    %      0   1   4    5	The diagram to the left\n"
"    %           x,y		represents the positions of\n"
"    %     11  10   7    6	the points which are computed\n"
"    %				below.\n"
"    %          9   8\n"
"    %\n"
"\n"
"    newpath\n"
"      2 copy exch neg exch neg moveto \n"
"      dup neg dup lineto\n"
"      2 copy neg exch neg lineto\n"
"      2 copy exch neg lineto\n"
"      dup dup neg lineto \n"
"      2 copy neg lineto 2 copy lineto\n"
"      dup dup lineto \n"
"      2 copy exch lineto \n"
"      2 copy neg exch lineto\n"
"      dup dup neg exch lineto \n"
"      exch neg exch lineto\n"
"    closepath\n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"% Circle\n"
"/Ci {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 copy pop moveto \n"
"    newpath\n"
"      2 div 0 360 arc\n"
"    closepath \n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"% Square\n"
"/Sq {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    dup dup 2 div dup\n"
"    6 -1 roll exch sub exch\n"
"    5 -1 roll exch sub 4 -2 roll Box\n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"% Line\n"
"/Li {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 1 roll exch 3 -1 roll 2 div 3 copy\n"
"    newpath\n"
"      sub exch moveto \n"
"      add exch lineto\n"
"    closepath\n"
"    stroke\n"
"  grestore\n"
"} def\n"
"\n"
"% Diamond\n"
"/Di {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 1 roll translate 45 rotate 0 0 3 -1 roll Sq\n"
"  grestore\n"
"} def\n"
"    \n"
"% Triangle\n"
"/Tr {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate\n"
"    BaseRatio mul 0.5 mul		% Calculate 1/2 base\n"
"    dup 0 exch 30 cos mul		% h1 = height above center point\n"
"    neg				% b2 0 -h1\n"
"    newpath \n"
"      moveto				% point 1;  b2\n"
"      dup 30 sin 30 cos div mul	% h2 = height below center point\n"
"      2 copy lineto			% point 2;  b2 h2\n"
"      exch neg exch lineto		% \n"
"    closepath\n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"% Arrow\n"
"/Ar {\n"
"  % Stack: x y symbolSize\n"
"  gsave\n"
"    3 -2 roll translate\n"
"    BaseRatio mul 0.5 mul		% Calculate 1/2 base\n"
"    dup 0 exch 30 cos mul		% h1 = height above center point\n"
"					% b2 0 h1\n"
"    newpath moveto			% point 1;  b2\n"
"    dup 30 sin 30 cos div mul		% h2 = height below center point\n"
"    neg				% -h2 b2\n"
"    2 copy lineto			% point 2;  b2 h2\n"
"    exch neg exch lineto		% \n"
"    closepath\n"
"    DrawSymbolProc\n"
"  grestore\n"
"} def\n"
"\n"
"%%EndProlog\n"
);
}
