/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
#include "swindow/SPostscript.h"
#include "stoolkit/STypes.h"
#include <time.h>

/**
 * Bitmap font support
 */
#ifndef USE_WINAPI 
# if USE_X11
#  include <X11/Xlib.h>
#  include <X11/Xutil.h>
#  include "swindow/SAwt.h"
#  include "swindow/sx11/SX11Impl.h"
# endif
#endif

/**
 * @author: Gaspar Sinai <gaspar@yudit.org>
 * @version: 2000-04-23
 * This is a postscript renderer for yudit.
 */
SPostscript::SPostscript (const SWriter& wr, SMedia m, SOrientation o)  : out (wr)
{
  ticks = 0;
  timer = 0;
  isUgly = false;
  cacheIDMapCount = 0;
  media = m;
  orientation = o;
  unsigned int w;
  unsigned int h;
  switch (m)
  {
  case A3:
    w = 842;
    h = 1190;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case A4:
    w = 595;
    h = 842;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case A5:
    w = 420;
    h = 595;
    widthMargin = 45;
    heightMargin = 34;
    break;
  case B4:
    w = 792;
    h = 1032;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case B5:
    w = 516;
    h = 729;
    widthMargin = 45;
    heightMargin = 34;
    break;
  case Executive:
    w = 540;
    h = 720;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case Folio:
    w = 612;
    h = 936;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case Ledger:
    w = 980;
    h = 792;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case Legal:
    w = 612;
    h = 1008;
    widthMargin = 64;
    heightMargin = 48;
    break;
  case Letter:
    w = 612;
    h = 792;
    widthMargin = 24;
    heightMargin = 34;
    break;
  case Quarto:
    w = 610;
    h = 780;
    widthMargin = 24;
    heightMargin = 48;
    break;
  case Statement:
    w = 396;
    h = 612;
    widthMargin = 24;
    heightMargin = 48;
    break;
  case Tabloid:
    w = 792;
    h = 1028;
    widthMargin = 24;
    heightMargin = 48;
    break;
  default:
    w = 595;
    h = 842;
    widthMargin = 64;
    heightMargin = 48;
  }
  if (o==PORTRAIT)
  {
    width = w;
    height = h;
  }
  else
  {
    width = h;
    height = w;
  }
  status = true;

  SS_Matrix2D mat;
  matrix.append (mat);
}

SPostscript::~SPostscript()
{
  if (timer) delete timer;
}

/**
 * return the margin-stripped width.
 */
unsigned int
SPostscript::getWidth() const
{
  return width - 2 * widthMargin;
}

/**
 * return the margin-stripped height.
 */
unsigned int
SPostscript::getHeight() const
{
  return height - 2 * heightMargin;
}

/**
 * return the margin stripped X corner.
 */
int
SPostscript::getX() const
{
  return (int) widthMargin;
}

/**
 * return the margin stripped Y corner.
 */
int
SPostscript::getY() const
{
  return (int) heightMargin;
}

/**
 * Print out the postscript prolog
 */
bool
SPostscript::open (bool background)
{
  isUgly = false;
  status = true;
  isCacheOn = true;

  cache.clear();
  cacheCurrent.clear();
  cacheIDMapCount = 0;
  cacheID.clear();
  cacheIDMap.clear();

  SStringVector head("%!PS-Adobe-3.0");
  SString bb("%%BoundingBox: ");

  switch (orientation)
  {
  case LANDSCAPE:
 
        bb.print (heightMargin); bb.append (" ");
        bb.print (widthMargin); bb.append (" ");
        bb.print (height - heightMargin); bb.append (" ");
        bb.print (width - widthMargin);
        head.append (bb);
 
    break;
 
  case PORTRAIT:
  default:
 
        bb.print (widthMargin); bb.append (" ");
        bb.print (heightMargin); bb.append (" ");
        bb.print (width - widthMargin); bb.append (" ");
        bb.print (height - heightMargin);
        head.append (bb);
 
    break;
  }

  head.append ("%%Title: Untitled");
  SString creator ("%%Creator: yudit "); creator.append (SD_YUDIT_VERSION);
  creator.append (" GNU (C) Gaspar Sinai");
  head.append (creator);
  time_t now;
  struct tm* localTime;
  time(&now);
  localTime = localtime (&now);
  char strt[64];
  strftime (strt, sizeof (strt)-1, "%Y-%m-%d %H:%M:%S", localTime);
  SString t("%%CreationDate: ");
  creationDate = strt;
  t.append (creationDate);
  head.append (t);
  switch (orientation)
  {
  case LANDSCAPE:
    head.append ("%%Orientation: Landscape");
    break;
  case PORTRAIT:
  default:
    head.append ("%%Orientation: Portrait");
    break;
  }
  SString m ("%%DocumentMedia: ");
  switch (media)
  {
  case Legal:
     m.append ("Legal "); break;
  case Letter:
     m.append ("Letter "); break;
  case Tabloid:
     m.append ("Tabloid "); break;
  case Ledger:
     m.append ("Ledger "); break;
  case B4:
     m.append ("B4 "); break;
  case A3:
     m.append ("A3 "); break;
  case A4:
     m.append ("A4 "); break;
  case A5:
     m.append ("A5 "); break;
  case B5:
     m.append ("B5 "); break;
  default:
     m.append ("A4 ");
  }
  if (orientation == LANDSCAPE)
  {
    m.print (height); m.append (" ");
    m.print (width); m.append (" 0 () ()");
  }
  else 
  {
    m.print (width); m.append (" ");
    m.print (height); m.append (" 0 () ()");
  }
  head.append (m);
  // windows gs goes crazy if I uncomment this.
  //head.append ("");
  /* caching should be here */
  //head.append ("");
  SString all = head.join("\n");
  all.append ("\n");
  writeString (all);
  pages = 0;
  currentPage = 0;
  if (status && background)
  {
    timer = STimer::newTimer(1, this);
  }
  return status;
}


/**
 * Print out the ending lines.
 */
bool
SPostscript::close ()
{
  if (timer) delete timer;
  timer = 0;
  /* go back */
  isCacheOn = true;
  cache.clear();
  cacheCurrent.clear();
  cacheIDMapCount = 0;
  cacheID.clear();
  cacheIDMap.clear();

  if (currentPage!=0)
  {
    writeString ("\nshowpage restore\n");
  }
  SStringVector l("%%DocumentFonts: Times-Roman");
  char a[64];
  sprintf (a, "%%%%Pages: %u", pages);
  l.append (a);
  l.append ("%%Trailer");
  l.append ("%%EOF");
  SString all = l.join ("\n");
  all.append ("\n");
  writeString (all);
  return status;
}

void 
SPostscript::newPage()
{
  if (isCacheOn)
  {
    pages++;
  }
  else
  {
    if (currentPage!=0)
    {
      writeString ("\nshowpage restore\n");
    }
    currentPage++;
    char buff[128];
 
  switch (orientation)
  {
  case LANDSCAPE:
 
          sprintf (buff, "\n%%%%Page: %u %u\nsave\n90 rotate\n0 -%d.000 translate\n", pages, currentPage, height);
 
    break;
 
  case PORTRAIT:
  default:
 
          sprintf (buff, "\n%%%%Page: %u %u\nsave\n", pages, currentPage);
 
    break;
  }
 
 
 
    writeString (buff);
  }
}

#define SG(_ix)\
	(((_ix) > -1000 && (_ix) < 0)? "-" : "")

#define SM(_ix)\
	((_ix < 0) ? ((-_ix) % 1000) : ((_ix) % 1000))
/**
 * Try to do a fill from cache. return false on fail.
 * INTERNAL FUNCTION. PRINTS ON SCREEN.
 * @param id is the id to print.
 */
bool
SPostscript::_beginImage (double _x, double _y, const SString& _id)
{
  if (!cacheIDMap.get (_id)) return false;
  char ins[128];

  /**
   * if you noticed I negated all y coordinates, and now I add height to it
   * it should nicely re-invert yudit screen coordinates.
   * everything should be relative to this point
   * SGC locale can screw things up (. -> , )- use decimals.
   */
  int ix =  (int) (1000.0 * _x);
  int iy =  (int) (1000.0 * ((double)height-_y));
  sprintf (ins, "gsave %s%d.%03d %s%d.%03d translate ", 
     SG(ix), ix/1000, SM (ix), SG(iy), iy/1000, SM(iy));

  SString all;
  all.append (ins);
  all.append (cacheIDMap[_id]);
  all.append (" grestore\n");
  writeString (all);
  return true;
}

/**
 * Write out the cache, so that you can refer to it by the id
 * mapped by cacheIDMap.
 */
bool
SPostscript::cacheOn (bool on)
{
  /* you can only turn it off */
  if (on) return isCacheOn;

  char p[64];
  sprintf (p, "%u", pages);
  SString cs;
  cs.append ("%%Pages: "); 
  cs.append (p); cs.append ("\n");
  cs.append ("%%PageOrder: Ascend\n"); 
  cs.append ("%%EndComments\n");

  cs.append ("\n");
  cs.append ("%%BeginProlog\n");
  cs.append ("%%BeginResource: cache\n");
  cs.append ("\n");
  writeString (cs);
  cs = "";
//fprintf (stderr, "cache in flushed. size=%u\n", cache.size());
  for (unsigned int i=0; i<cache.size(); i++)
  {
    if (status == false) break;
    for (unsigned int j=0; j<cache.size(i); j++)
    {
      if (status == false) break;
      const SString* s = cache.get (i, j);
      if (s==0) continue;
      const SString id = cache.key (i, j);
      if (cacheIDMap.get (id)==0)
      {
         fprintf (stderr, "ID mismatch in SPostscript::cacheFlush\n");
         continue;
      }
      cs.clear();
      cs.append ("/");
      cs.append (cacheIDMap[id]);
      cs.append (" {\n");
      cs.append (*s);
      cs.append ("} bind def\n\n");
      writeString (cs);
    }
  }
  cs = "";
  cs.append ("%%EndResource: cache\n");
  cs.append ("%%EndProlog\n\n");
  cs.append ("%%EndProlog\n\n");
  writeString (cs);
  return SCanvas::cacheOn(on);
}

/**
 * start a new path. If cache is on and is is not ""
 * @return true if no more draing is needed.
 */
bool
SPostscript::beginImage (double _x, double _y, const SString& _id, const SColor& background)
{
  /* is it in the background ? */
  if (timer)
  {
    if (((++ticks)%20)==0) SEventHandler::next();
  }
  cacheID = _id;
  cacheCurrent.clear();
  cacheOriginX = _x;
  cacheOriginY = _y;

  if (!isCacheOn)
  {
    /* This is a hack. now I requere you to cahce first. */
    /* for some reason this one was inserting a newpath with no fill */
    // FIXME
    _beginImage (_x, _y, _id);
    return true;
  }
  /* have to draw it anyway later. */
  if (_id.size() == 0) return true;
  return false;
}

void
SPostscript::newpath () 
{
  /* put it in the cache */
  if (!isCacheOn) 
  {
//fprintf (stderr, "failed %*.*s\n", SSARGS(_id));
// FIXME
#if 0
      char arr[64];
      int ix =  (int) (1000.0 * _x);
      int iy =  (int) (1000.0 * ((double)height-_y));
      sprintf (arr, "gsave %s%d.%03d %s%d.%03d translate newpath\n", 
        SG(ix), ix/1000, SM(ix), SG(iy), iy/1000, SM(iy));
      // BUG-2011-08-04 Don't write. This happens between U+0000...U+001F
      //writeString (arr);
#endif
      return ;
  }
  cacheCurrent.append ("newpath\n");
}


/**
 * have to call fill if newpath does fail. 
 * This routine should not be called if cache is off.
 * and glyph is found. newpath returns treu for this case.
 */
void
SPostscript::fill (const SPen& pen)
{
  fill();
}

void
SPostscript::endImage () 
{
  if (isCacheOn)
  {
    if (cache.get (cacheID)) return;
    cache.put (cacheID, cacheCurrent);
    char buff[64];
    sprintf (buff, "Glyph%u", cacheIDMapCount++);
    cacheIDMap.put (cacheID, SString(buff));
    cacheCurrent.clear();
    cacheID.clear();
  }
}

/**
 * have to call fill if newpath does fail. 
 * This routine should not be called if cache is off.
 * and glyph is found. newpath returns treu for this case.
 */
void
SPostscript::fill ()
{
  if (isCacheOn)
  {
    if (cache.get (cacheID)) return;
    cacheCurrent.append ("fill\n");
  }
  else 
  {
    // BUG-2011-08-04 Don't write. This happens between U+0000...U+001F
    // Originally only newpath was commented out.
    // if (cache.get (cacheID) == 0) writeString ("fill grestore\n");
  }
}

/**
 * have to call fill if newpath does fail. 
 * This routine should not be called if cache is off.
 * and glyph is found. newpath returns treu for this case.
 */
void
SPostscript::stroke (const SPen& pen)
{
  if (isCacheOn)
  {
    if (cache.get (cacheID)) return;
    cacheCurrent.append ("stroke\n");
  }
  else 
  {
    if (cache.get (cacheID) == 0) writeString ("stroke grestore\n");
  }
}

void
SPostscript::moveto (double _x, double _y)
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  double x = m.x0 * _x + m.y0 * _y + m.t0;
  double y = m.x1 * _x + m.y1 * _y + m.t1;
  char ins[128];
  int ix =  (int) (1000.0 * (x-cacheOriginX));
  int iy =  (int) (1000.0 * (cacheOriginY-y));
  sprintf (ins, "%s%d.%03d %s%d.%03d moveto\n", 
     SG(ix), ix/1000, SM(ix), SG(iy), iy/1000, SM(iy));
  if (isCacheOn)
  {
    cacheCurrent.append (ins);
  }
  else
  {
    writeString (ins);
  }
}

void
SPostscript::lineto (double _x, double _y)
{
  SS_Matrix2D m = matrix[matrix.size()-1];

  double x = m.x0 * _x + m.y0 * _y + m.t0;
  double y = m.x1 * _x + m.y1 * _y + m.t1;
  char ins[128];
  int ix =  (int) (1000.0 * (x-cacheOriginX));
  int iy =  (int) (1000.0 * (cacheOriginY-y));
  sprintf (ins, "%s%d.%03d %s%d.%03d lineto\n", 
     SG(ix), ix/1000, SM(ix), SG(iy), iy/1000, SM(iy));
  if (isCacheOn)
  {
    cacheCurrent.append (ins);
  }
  else
  {
    writeString (ins);
  }
}

void
SPostscript::curveto (double _x0, double _y0, double _x1, double _y1, double _x2, double _y2)
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  
  double x0 = m.x0 * _x0 + m.y0 * _y0 + m.t0;
  double y0 = m.x1 * _x0 + m.y1 * _y0 + m.t1;

  double x1 = m.x0 * _x1 + m.y0 * _y1 + m.t0;
  double y1 = m.x1 * _x1 + m.y1 * _y1 + m.t1;

  double x2 = m.x0 * _x2 + m.y0 * _y2 + m.t0;
  double y2 = m.x1 * _x2 + m.y1 * _y2 + m.t1;
  char ins[128];
  int ix0 =  (int) (1000.0 * (x0-cacheOriginX));
  int iy0 =  (int) (1000.0 * (cacheOriginY-y0));
  int ix1 =  (int) (1000.0 * (x1-cacheOriginX));
  int iy1 =  (int) (1000.0 * (cacheOriginY-y1));
  int ix2 =  (int) (1000.0 * (x2-cacheOriginX));
  int iy2 =  (int) (1000.0 * (cacheOriginY-y2));
  sprintf (ins, "%s%d.%03d %s%d.%03d %s%d.%03d %s%d.%03d %s%d.%03d %s%d.%03d curveto\n", 
     SG(ix0), ix0/1000, SM(ix0), SG(iy0), iy0/1000, SM(iy0), 
     SG(ix1), ix1/1000, SM(ix1), SG(iy1), iy1/1000, SM(iy1), 
     SG(ix2), ix2/1000, SM(ix2), SG(iy2), iy2/1000, SM(iy2));
  if (isCacheOn)
  {
    cacheCurrent.append (ins);
  }
  else
  {
    writeString (ins);
  }
}

void
SPostscript::closepath()
{
  if (isCacheOn)
  {
    cacheCurrent.append ("closepath\n");
  }
  else
  {
    writeString ("closepath\n");
  }
}

void
SPostscript::pushmatrix()
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  matrix.append (m);
}

void
SPostscript::popmatrix()
{
  if (matrix.size())
  {
    matrix.truncate(matrix.size()-1);
  }
}

void
SPostscript::scale (double x, double y)
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  m.scale (x, y);
  popmatrix ();
  matrix.append (m);
}

void
SPostscript::translate (double x, double y)
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  m.translate (x, y);
  popmatrix ();
  matrix.append (m);
}

void
SPostscript::rotate (double angle)
{
  SS_Matrix2D m = matrix[matrix.size()-1];
  m.rotate (angle);
  popmatrix ();
  matrix.append (m);
}

void
SPostscript::bitfont (const SPen& pen, double x, double y, 
       void* native, char* data, unsigned int len)
{
#ifndef USE_WINAPI 
# if USE_X11
  SX11Impl* impl=(SX11Impl*) SAwt::delegate;
  if (impl == 0)
  {
    fprintf (stderr, "(impl) no luck in prining bitmap font.\n");
    return;
  }
  Window aWindow = (Window) impl->getAnyWindow();
  if (aWindow==None)
  {
    fprintf (stderr, "(window) no luck in prining bitmap font.\n");
    return;
  }

  SString key("NF");
  key.append (SString((long)native));
  key.append (SString(data, len));
  if (beginImage (x, y, key, pen.getBackground()))
  {
    endImage();
    return;
  }
  newpath();
  /* FIXME: draw font here */
  int bits = DefaultDepth (impl->display, impl->screen); 
  int nchars = len/2;
  int direction_return;
  int font_ascent_return;
  int font_descent_return;
  XCharStruct overall_return;
  
  XQueryTextExtents16 (
     impl->display, ((Font)native),   (XChar2b*) data, nchars, 
     &direction_return, 
     &font_ascent_return,
     &font_descent_return,
     &overall_return);

  int wi = overall_return.width;
  int ahe = (overall_return.ascent<0) ? -overall_return.ascent 
      : overall_return.ascent;
  int he = ahe + ((overall_return.descent<0) ? -overall_return.descent 
      : overall_return.descent);

  Pixmap pixmap = XCreatePixmap (impl->display, aWindow, wi+1, he+1, bits);
  if (pixmap==None)
  {
    fill (pen);
    endImage();
    fprintf (stderr, "(Pixmap) no luck in prining bitmap font.\n");
    return;
  }
  XGCValues   gcv;
  gcv.foreground = 1;
  gcv.background = 0;
  GC gc = XCreateGC (impl->display, pixmap, GCForeground | GCBackground, &gcv);
  XSetForeground (impl->display, gc, 0);
  XFillRectangle (impl->display, pixmap, gc, 0, 0, wi+1, he+1);
  XSetForeground (impl->display, gc, 1);
  XSetFont (impl->display, gc, (Font) native);
  XDrawString16 (impl->display, pixmap, gc, 0, ahe, (XChar2b*) data, len/2);
//fprintf (stderr, "getting pixmap=%u %u 0x%lx\n", wi, he, (unsigned long) pixmap);
  XImage* im = XGetImage (impl->display, pixmap, 0, 0, wi+1, he+1, AllPlanes, ZPixmap);
  XFreePixmap (impl->display, pixmap);
  XFreeGC (impl->display, gc);
  if (im==0)
  {
    fprintf (stderr, "(Image) no luck in prining bitmap font.\n");
    fill (pen);
    endImage();
    return;
  }

  /* draw here */
  //fprintf (stderr, "BITMAP FONT\n");
  for (int j=0; j<he; j++)
  {
    for (int i=0; i<wi; i++)
    {
      long l = XGetPixel (im, i, j);
      if (l==1) 
      {
        double squareX[4];
        double squareY[4];
        double delta = 1.0;

        squareX[0] = (double)i+x;
        squareY[0] = ((double)j+y - (double) ahe),

        squareX[1] = squareX[0] + delta;
        squareY[1] = squareY[0];

        squareX[2] = squareX[0] + delta;
        squareY[2] = squareY[0] + delta;

        squareX[3] = squareX[0];
        squareY[3] = squareY[0] + delta;

        moveto (squareX[0], squareY[0]);
        lineto (squareX[1], squareY[1]);
        lineto (squareX[2], squareY[2]);
        lineto (squareX[3], squareY[3]);
        closepath();
        //fprintf (stderr, "#");
      }
      else
      {
        //fprintf (stderr, ".");
      }
    }
    //fprintf (stderr, "\n");
  }
  isUgly = true;
  XDestroyImage (im); 
  fill (pen);
# endif
#endif
  endImage();
}
bool
SPostscript::hasNative () const
{
  return isUgly;
}


/**
 * Fill a solid rectangle
 * @param x is the upper left corner
 * @param y is the upper top corner
 * @param width is the width of the region to fill
 * @param height is the height of the region to fill
 */
void
SPostscript::bitfill (const SColor& bg, int x, int y, 
 unsigned int width, unsigned int height)
{
}

/**
 * Draw a solid line.
 * @param x is the starting x point
 * @param y is the starting y point
 * @param x is the ending non-exclusive  x point
 * @param y is the ending non-exclusive  y point
 */
void
SPostscript::bitline (const SColor& fg, int x, int y, int tox, int toy)
{
}

/**
 * Draw a solid line.
 * @param x is the x point
 * @param y is the y point
 */
void
SPostscript::bitpoint (const SColor& fg, int x, int y)
{
  SString key("BP");
  key.append (SString((long)x));
  key.append (SString((long)y));
  if (beginImage (x, y, key, SColor ("white")))
  {
    endImage ();
    return;
  }
  isUgly = true;
  double squareX[4];
  double squareY[4];
  double delta = 1.0;

  squareX[0] = (double)x;
  squareY[0] = (double)y;

  squareX[1] = squareX[0] + delta;
  squareY[1] = squareY[0];

  squareX[2] = squareX[0] + delta;
  squareY[2] = squareY[0] + delta;

  squareX[3] = squareX[0];
  squareY[3] = squareY[0] + delta;

  moveto (squareX[0], squareY[0]);
  lineto (squareX[1], squareY[1]);
  lineto (squareX[2], squareY[2]);
  lineto (squareX[3], squareY[3]);
  closepath();
  fill ();
}

void
SPostscript::bitpoints (const SColor& fg, const int* x, const int* y, 
         unsigned int _size)
{
  if (_size<=0) return;
  SString key("BPS");
  unsigned int i;
  for (i=0; i<_size; i++)
  {
    key.append (SString(((char*) &x[i]), sizeof(int)));
    key.append (SString(((char*) &y[i]), sizeof(int)));
  }
  if (beginImage (x[0], y[0], key, SColor ("white")))
  {
    endImage ();
    return;
  }
  newpath();
  for (i=0; i<_size; i++)
  {
    double squareX[4];
    double squareY[4];
    double delta = 1.0;

    squareX[0] = (double)x[i];
    squareY[0] = (double)y[i];

    squareX[1] = squareX[0] + delta;
    squareY[1] = squareY[0];

    squareX[2] = squareX[0] + delta;
    squareY[2] = squareY[0] + delta;

    squareX[3] = squareX[0];
    squareY[3] = squareY[0] + delta;

    moveto (squareX[0], squareY[0]);
    lineto (squareX[1], squareY[1]);
    lineto (squareX[2], squareY[2]);
    lineto (squareX[3], squareY[3]);
    closepath();
  }
  fill ();
  endImage ();
  isUgly = true;
}

bool
SPostscript::writeString (const SString& str)
{
  if (status == false) return false;
  if (out.write (str) == false) status = false;
  return status;
}

SString
SPostscript::getCreationDate() const
{
  return SString(creationDate);
}
bool
SPostscript::timeout (const SEventSource* s)
{
  if (timer==0) return false;
  return true;
}
