/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998 Alexander Larsson
 *
 * diapsrenderer.c -- implements the base class for Postscript rendering
 *   It is mostly refactoring of render_eps.c (some stuff not from the 
 *   latest version but from 1.24) before PS rendering became multi-pass
 *   and text rendering became (necessary) complicated.
 * Refatoring: Copyright (C) 2002 Hans Breuer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <config.h>

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

#include "diapsrenderer.h"
#include "message.h"
#include "dia_image.h"
#include "font.h"
#include "textline.h"

static void
draw_text_line(DiaRenderer *self,
	       const TextLine *text_line,
	       Point *pos, Color *color);

#define DTOSTR_BUF_SIZE G_ASCII_DTOSTR_BUF_SIZE
#define psrenderer_dtostr(buf,d) \
	g_ascii_formatd(buf, sizeof(buf), "%f", d)

/* Returns TRUE if this file is an encapsulated postscript file
 * (including e.g. epsi).
 */
static gboolean renderer_is_eps(DiaPsRenderer *renderer) {
  return renderer->pstype == PSTYPE_EPS ||
    renderer->pstype == PSTYPE_EPSI;
}

/** Returns TRUE if this file is an EPSI file */
static gboolean renderer_is_epsi(DiaPsRenderer *renderer) {
  return renderer->pstype == PSTYPE_EPSI;
}

void
lazy_setcolor(DiaPsRenderer *renderer,
              Color *color)
{
  gchar r_buf[DTOSTR_BUF_SIZE];
  gchar g_buf[DTOSTR_BUF_SIZE];
  gchar b_buf[DTOSTR_BUF_SIZE];

  if (!color_equals(color, &(renderer->lcolor))) {
    renderer->lcolor = *color;
    fprintf(renderer->file, "%s %s %s srgb\n",
            psrenderer_dtostr(r_buf, (gdouble) color->red),
            psrenderer_dtostr(g_buf, (gdouble) color->green),
            psrenderer_dtostr(b_buf, (gdouble) color->blue) );    
  }
}

static void
begin_render(DiaRenderer *self)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  time_t time_now;

  g_assert (renderer->file != NULL);

  time_now  = time(NULL);

  if (renderer_is_eps(renderer))
    fprintf(renderer->file,
            "%%!PS-Adobe-2.0 EPSF-2.0\n");
  else
    fprintf(renderer->file,
            "%%!PS-Adobe-2.0\n");
  fprintf(renderer->file,
          "%%%%Title: %s\n"
          "%%%%Creator: Dia v%s\n"
          "%%%%CreationDate: %s"
          "%%%%For: %s\n"
          "%%%%Orientation: %s\n",
          renderer->title ? renderer->title : "(NULL)" ,
          VERSION,
          ctime(&time_now),
          g_get_user_name(),
          renderer->is_portrait ? "Portrait" : "Landscape");

  if (renderer_is_epsi(renderer)) {
    g_assert(!"Preview image not implmented");
    /* but it *may* belong here ... */
  }
  if (renderer_is_eps(renderer))
    fprintf(renderer->file,
            "%%%%Magnification: 1.0000\n"
	  "%%%%BoundingBox: 0 0 %d %d\n",
            (int) ceil(  (renderer->extent.right - renderer->extent.left) 
                       * renderer->scale),
            (int) ceil(  (renderer->extent.bottom - renderer->extent.top) 
                       * renderer->scale) );

  else
    fprintf(renderer->file,
	  "%%%%DocumentPaperSizes: %s\n",
            renderer->paper ? renderer->paper : "(NULL)");

  fprintf(renderer->file,
          "%%%%BeginSetup\n");
  fprintf(renderer->file,
          "%%%%EndSetup\n"
          "%%%%EndComments\n");

  DIA_PS_RENDERER_GET_CLASS(self)->begin_prolog(renderer);
  /* get our font definitions */
  DIA_PS_RENDERER_GET_CLASS(self)->dump_fonts(renderer);
  /* done it */
  DIA_PS_RENDERER_GET_CLASS(self)->end_prolog(renderer);
}

static void
end_render(DiaRenderer *self)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);

  if (renderer_is_eps(renderer))
    fprintf(renderer->file, "showpage\n");

  if (self->font != NULL) {
    dia_font_unref(self->font);
  }
}

static void
set_linewidth(DiaRenderer *self, real linewidth)
{  /* 0 == hairline **/
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  gchar lw_buf[DTOSTR_BUF_SIZE];

  /* Adobe's advice:  Set to very small but fixed size, to avoid changes
   * due to different resolution output. */
  /* .01 cm becomes <5 dots on 1200 DPI */
  if (linewidth == 0.0) linewidth=.01;
  fprintf(renderer->file, "%s slw\n",
	  psrenderer_dtostr(lw_buf, (gdouble) linewidth) );
}

static void
set_linecaps(DiaRenderer *self, LineCaps mode)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  int ps_mode;
  
  switch(mode) {
  case LINECAPS_BUTT:
    ps_mode = 0;
    break;
  case LINECAPS_ROUND:
    ps_mode = 1;
    break;
  case LINECAPS_PROJECTING:
    ps_mode = 2;
    break;
  default:
    ps_mode = 0;
  }

  fprintf(renderer->file, "%d slc\n", ps_mode);
}

static void
set_linejoin(DiaRenderer *self, LineJoin mode)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  int ps_mode;
  
  switch(mode) {
  case LINEJOIN_MITER:
    ps_mode = 0;
    break;
  case LINEJOIN_ROUND:
    ps_mode = 1;
    break;
  case LINEJOIN_BEVEL:
    ps_mode = 2;
    break;
  default:
    ps_mode = 0;
  }

  fprintf(renderer->file, "%d slj\n", ps_mode);
}

static void
set_linestyle(DiaRenderer *self, LineStyle mode)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  real hole_width;
  gchar dashl_buf[DTOSTR_BUF_SIZE];
  gchar dotl_buf[DTOSTR_BUF_SIZE];
  gchar holew_buf[DTOSTR_BUF_SIZE];

  renderer->saved_line_style = mode;
  
  switch(mode) {
  case LINESTYLE_SOLID:
    fprintf(renderer->file, "[] 0 sd\n");
    break;
  case LINESTYLE_DASHED:
    fprintf(renderer->file, "[%s] 0 sd\n",
	    psrenderer_dtostr(dashl_buf, renderer->dash_length) );
    break;
  case LINESTYLE_DASH_DOT:
    hole_width = (renderer->dash_length - renderer->dot_length) / 2.0;
    psrenderer_dtostr(holew_buf, hole_width);
    psrenderer_dtostr(dashl_buf, renderer->dash_length);
    psrenderer_dtostr(dotl_buf, renderer->dot_length);
    fprintf(renderer->file, "[%s %s %s %s] 0 sd\n",
	    dashl_buf,
	    holew_buf,
	    dotl_buf,
	    holew_buf );
    break;
  case LINESTYLE_DASH_DOT_DOT:
    hole_width = (renderer->dash_length - 2.0*renderer->dot_length) / 3.0;
    psrenderer_dtostr(holew_buf, hole_width);
    psrenderer_dtostr(dashl_buf, renderer->dash_length);
    psrenderer_dtostr(dotl_buf, renderer->dot_length);
    fprintf(renderer->file, "[%s %s %s %s %s %s] 0 sd\n",
	    dashl_buf,
	    holew_buf,
	    dotl_buf,
	    holew_buf,
	    dotl_buf,
	    holew_buf );
    break;
  case LINESTYLE_DOTTED:
    fprintf(renderer->file, "[%s] 0 sd\n",
	    psrenderer_dtostr(dotl_buf, renderer->dot_length) );
    break;
  }
}

static void
set_dashlength(DiaRenderer *self, real length)
{  /* dot = 20% of len */
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);

  if (length<0.001)
    length = 0.001;
  
  renderer->dash_length = length;
  renderer->dot_length = length*0.2;
  
  set_linestyle(self, renderer->saved_line_style);
}

static void
set_fillstyle(DiaRenderer *self, FillStyle mode)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);

  switch(mode) {
  case FILLSTYLE_SOLID:
    break;
  default:
    message_error("%s : Unsupported fill mode specified!\n",
                  G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
  }
}

static void
set_font(DiaRenderer *self, DiaFont *font, real height)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  gchar h_buf[DTOSTR_BUF_SIZE];

  if (font != self->font || height != self->font_height) {
    DiaFont *old_font;

    fprintf(renderer->file, "/%s-latin1 ff %s scf sf\n",
	    dia_font_get_psfontname(font),
	    psrenderer_dtostr(h_buf, (gdouble) height*0.7) );
    old_font = self->font;
    self->font = font;
    dia_font_ref(self->font);
    if (old_font != NULL) {
      dia_font_unref(old_font);
    }
    self->font_height = height;
  }
}

static void
draw_line(DiaRenderer *self, 
	  Point *start, Point *end, 
	  Color *line_color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  gchar sx_buf[DTOSTR_BUF_SIZE];
  gchar sy_buf[DTOSTR_BUF_SIZE];
  gchar ex_buf[DTOSTR_BUF_SIZE];
  gchar ey_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer,line_color);

  fprintf(renderer->file, "n %s %s m %s %s l s\n",
	  psrenderer_dtostr(sx_buf, start->x),
	  psrenderer_dtostr(sy_buf, start->y),
	  psrenderer_dtostr(ex_buf, end->x),
	  psrenderer_dtostr(ey_buf, end->y) );
}

static void
draw_polyline(DiaRenderer *self, 
	      Point *points, int num_points, 
	      Color *line_color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  int i;
  gchar px_buf[DTOSTR_BUF_SIZE];
  gchar py_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer,line_color);
  
  fprintf(renderer->file, "n %s %s m ",
	  psrenderer_dtostr(px_buf, points[0].x),
	  psrenderer_dtostr(py_buf, points[0].y) );

  for (i=1;i<num_points;i++) {
    fprintf(renderer->file, "%s %s l ",
	    psrenderer_dtostr(px_buf, points[i].x),
	    psrenderer_dtostr(py_buf, points[i].y) );
  }

  fprintf(renderer->file, "s\n");
}

static void
psrenderer_polygon(DiaPsRenderer *renderer,
		   Point *points,
		   gint num_points,
		   Color *line_color,
		   gboolean filled)
{
  gint i;
  gchar px_buf[DTOSTR_BUF_SIZE];
  gchar py_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer, line_color);

  fprintf(renderer->file, "n %s %s m ",
	  psrenderer_dtostr(px_buf, points[0].x),
	  psrenderer_dtostr(py_buf, points[0].y) );


  for (i=1;i<num_points;i++) {
    fprintf(renderer->file, "%s %s l ",
	    psrenderer_dtostr(px_buf, points[i].x),
	    psrenderer_dtostr(py_buf, points[i].y) );
  }
  if (filled)
    fprintf(renderer->file, "ef\n");
  else
    fprintf(renderer->file, "cp s\n");
}

static void
draw_polygon(DiaRenderer *self, 
	      Point *points, int num_points, 
	      Color *line_color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_polygon(renderer, points, num_points, line_color, FALSE);
}

static void
fill_polygon(DiaRenderer *self, 
	      Point *points, int num_points, 
	      Color *fill_color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_polygon(renderer, points, num_points, fill_color, TRUE);
}

static void
psrenderer_rect(DiaPsRenderer *renderer,
		Point *ul_corner,
		Point *lr_corner,
		Color *color,
		gboolean filled)
{
  gchar ulx_buf[DTOSTR_BUF_SIZE];
  gchar uly_buf[DTOSTR_BUF_SIZE];
  gchar lrx_buf[DTOSTR_BUF_SIZE];
  gchar lry_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer,color);

  psrenderer_dtostr(ulx_buf, (gdouble) ul_corner->x);
  psrenderer_dtostr(uly_buf, (gdouble) ul_corner->y);
  psrenderer_dtostr(lrx_buf, (gdouble) lr_corner->x);
  psrenderer_dtostr(lry_buf, (gdouble) lr_corner->y);
  
  fprintf(renderer->file, "n %s %s m %s %s l %s %s l %s %s l %s\n",
	  ulx_buf, uly_buf,
	  ulx_buf, lry_buf,
	  lrx_buf, lry_buf,
	  lrx_buf, uly_buf,
	  filled ? "f" : "cp s" );
}

static void
draw_rect(DiaRenderer *self, 
	  Point *ul_corner, Point *lr_corner,
	  Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_rect(renderer, ul_corner, lr_corner, color, FALSE);
}

static void
fill_rect(DiaRenderer *self, 
	  Point *ul_corner, Point *lr_corner,
	  Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_rect(renderer, ul_corner, lr_corner, color, TRUE);
}

static void
psrenderer_arc(DiaPsRenderer *renderer,
	       Point *center,
	       real width, real height,
	       real angle1, real angle2,
	       Color *color,
	       gboolean filled)
{
  gchar cx_buf[DTOSTR_BUF_SIZE];
  gchar cy_buf[DTOSTR_BUF_SIZE];
  gchar a1_buf[DTOSTR_BUF_SIZE];
  gchar a2_buf[DTOSTR_BUF_SIZE];
  gchar w_buf[DTOSTR_BUF_SIZE];
  gchar h_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer, color);

  psrenderer_dtostr(cx_buf, (gdouble) center->x);
  psrenderer_dtostr(cy_buf, (gdouble) center->y);
  psrenderer_dtostr(a1_buf, (gdouble) 360.0 - angle1);
  psrenderer_dtostr(a2_buf, (gdouble) 360.0 - angle2);
  psrenderer_dtostr(w_buf, (gdouble) width / 2.0);
  psrenderer_dtostr(h_buf, (gdouble) height / 2.0);

  fprintf(renderer->file, "n ");

  if (filled)
    fprintf(renderer->file, "%s %s m ", cx_buf, cy_buf);

  fprintf(renderer->file, "%s %s %s %s %s %s ellipse %s\n",
	  cx_buf, cy_buf, w_buf, h_buf, a2_buf, a1_buf,
	  filled ? "f" : "s" );
}


static void
draw_arc(DiaRenderer *self,
	 Point *center,
	 real width, real height,
	 real angle1, real angle2,
	 Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_arc(renderer, center, width, height, angle1, angle2, color, FALSE);
}

static void
fill_arc(DiaRenderer *self, 
	 Point *center,
	 real width, real height,
	 real angle1, real angle2,
	 Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_arc(renderer, center, width, height, angle1, angle2, color, TRUE);
}

static void
psrenderer_ellipse(DiaPsRenderer *renderer,
		   Point *center,
		   real width, real height,
		   Color *color,
		   gboolean filled)
{
  gchar cx_buf[DTOSTR_BUF_SIZE];
  gchar cy_buf[DTOSTR_BUF_SIZE];
  gchar w_buf[DTOSTR_BUF_SIZE];
  gchar h_buf[DTOSTR_BUF_SIZE];
  
  lazy_setcolor(renderer,color);

  fprintf(renderer->file, "n %s %s %s %s 0 360 ellipse %s\n",
	  psrenderer_dtostr(cx_buf, (gdouble) center->x),
	  psrenderer_dtostr(cy_buf, (gdouble) center->y),
	  psrenderer_dtostr(w_buf, (gdouble) width / 2.0),
	  psrenderer_dtostr(h_buf, (gdouble) height / 2.0),
	  filled ? "f" : "cp s" );
}

static void
draw_ellipse(DiaRenderer *self, 
	     Point *center,
	     real width, real height,
	     Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_ellipse(renderer, center, width, height, color, FALSE);
}

static void
fill_ellipse(DiaRenderer *self, 
	     Point *center,
	     real width, real height,
	     Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_ellipse(renderer, center, width, height, color, TRUE);
}

static void
psrenderer_bezier(DiaPsRenderer *renderer,
		  BezPoint *points,
		  gint numpoints,
		  Color *color,
		  gboolean filled)
{
  gint  i;
  gchar p1x_buf[DTOSTR_BUF_SIZE];
  gchar p1y_buf[DTOSTR_BUF_SIZE];
  gchar p2x_buf[DTOSTR_BUF_SIZE];
  gchar p2y_buf[DTOSTR_BUF_SIZE];
  gchar p3x_buf[DTOSTR_BUF_SIZE];
  gchar p3y_buf[DTOSTR_BUF_SIZE];

  lazy_setcolor(renderer,color);

  if (points[0].type != BEZ_MOVE_TO)
    g_warning("first BezPoint must be a BEZ_MOVE_TO");

  fprintf(renderer->file, "n %s %s m",
	  psrenderer_dtostr(p1x_buf, (gdouble) points[0].p1.x),
	  psrenderer_dtostr(p1y_buf, (gdouble) points[0].p1.y) );
  
  for (i = 1; i < numpoints; i++)
    switch (points[i].type) {
    case BEZ_MOVE_TO:
      g_warning("only first BezPoint can be a BEZ_MOVE_TO");
      break;
    case BEZ_LINE_TO:
      fprintf(renderer->file, " %s %s l",
	      psrenderer_dtostr(p1x_buf, (gdouble) points[i].p1.x),
	      psrenderer_dtostr(p1y_buf, (gdouble) points[i].p1.y) );
      break;
    case BEZ_CURVE_TO:
      fprintf(renderer->file, " %s %s %s %s %s %s c",
	      psrenderer_dtostr(p1x_buf, (gdouble) points[i].p1.x),
	      psrenderer_dtostr(p1y_buf, (gdouble) points[i].p1.y),
	      psrenderer_dtostr(p2x_buf, (gdouble) points[i].p2.x),
	      psrenderer_dtostr(p2y_buf, (gdouble) points[i].p2.y),
	      psrenderer_dtostr(p3x_buf, (gdouble) points[i].p3.x),
	      psrenderer_dtostr(p3y_buf, (gdouble) points[i].p3.y) );
      break;
    }
  
  if (filled)
    fprintf(renderer->file, " ef\n");
  else
    fprintf(renderer->file, " s\n");
}

static void
draw_bezier(DiaRenderer *self, 
	    BezPoint *points,
	    int numpoints, /* numpoints = 4+3*n, n=>0 */
	    Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_bezier(renderer, points, numpoints, color, FALSE);
}

static void
fill_bezier(DiaRenderer *self, 
	    BezPoint *points, /* Last point must be same as first point */
	    int numpoints,
	    Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  psrenderer_bezier(renderer, points, numpoints, color, TRUE);
}

static char*
ps_convert_string(const char *text)
{
  char *buffer;
  gchar *localestr;
  const gchar *str;
  gint len;
  GError * error = NULL;

  localestr = g_convert(text, -1, "LATIN1", "UTF-8", NULL, NULL, &error);

  if (localestr == NULL) {
    message_error("Can't convert string %s: %s\n", text, error->message);
    localestr = g_strdup(text);
  }

  /* Escape all '(' and ')':  */
  buffer = g_malloc(2*strlen(localestr)+1);
  *buffer = 0;
  str = localestr;
  while (*str != 0) {
    len = strcspn(str,"()\\");
    strncat(buffer, str, len);
    str += len;
    if (*str != 0) {
      strcat(buffer,"\\");
      strncat(buffer, str, 1);
      str++;
    }
  }
  g_free(localestr);
  return buffer;
}

static void
draw_string(DiaRenderer *self,
	    const char *text,
	    Point *pos, Alignment alignment,
	    Color *color)
{
#define DRAW_STRING_WITH_TEXT_LINE
#ifdef DRAW_STRING_WITH_TEXT_LINE
  TextLine *text_line = text_line_new(text, self->font, self->font_height);
  real width = text_line_get_width(text_line);
  Point realigned_pos = *pos;
  switch (alignment) {
      case ALIGN_LEFT:
	break;
      case ALIGN_CENTER:
	realigned_pos.x -= width/2;
	break;
      case ALIGN_RIGHT:
	realigned_pos.x -= width;
	break;
  }
  draw_text_line(self, text_line, &realigned_pos, color);
#else
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  gchar *buffer;
  gchar px_buf[DTOSTR_BUF_SIZE];
  gchar py_buf[DTOSTR_BUF_SIZE];
  GError * error = NULL;

  if (1 > strlen(text))
    return;

  lazy_setcolor(renderer,color);

  buffer = ps_convert_string(text);

  fprintf(renderer->file, "(%s) ", buffer);
  g_free(buffer);

  switch (alignment) {
  case ALIGN_LEFT:
    fprintf(renderer->file, "%s %s m",
	    psrenderer_dtostr(px_buf, pos->x),
	    psrenderer_dtostr(py_buf, pos->y) );
    break;
  case ALIGN_CENTER:
    fprintf(renderer->file, "dup sw 2 div %s ex sub %s m",
	    psrenderer_dtostr(px_buf, pos->x),
	    psrenderer_dtostr(py_buf, pos->y) );
    break;
  case ALIGN_RIGHT:
    fprintf(renderer->file, "dup sw %s ex sub %s m",
	    psrenderer_dtostr(px_buf, pos->x),
	    psrenderer_dtostr(py_buf, pos->y) );
    break;
  }
  
  fprintf(renderer->file, " gs 1 -1 sc sh gr\n");
#endif
}

static void
draw_text_line(DiaRenderer *self,
	       const TextLine *text_line,
	       Point *pos, Color *color)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  gchar *buffer;
  gchar px_buf[DTOSTR_BUF_SIZE];
  gchar py_buf[DTOSTR_BUF_SIZE];
  real width;
  gchar *text = text_line_get_string(text_line);
  int n_chars = g_utf8_strlen(text, -1);
  real adjust;

  if (1 > n_chars)
    return;

  set_font(self, text_line_get_font(text_line),
	   text_line_get_height(text_line));

  lazy_setcolor(renderer, color);

  buffer = ps_convert_string(text);

  fprintf(renderer->file, "(%s) ", buffer);
  g_free(buffer);

  adjust = text_line_get_descent(text_line);

  fprintf(renderer->file, "%s %s m \n",
	  psrenderer_dtostr(px_buf, pos->x),
	  psrenderer_dtostr(py_buf, pos->y - adjust) );
  
  /* Perform magic to ensure the right size */
  width = text_line_get_width(text_line);
  
  /* Find the difference in length */
  fprintf(renderer->file, "dup sw %f exch sub \n", width);

  /* Divide by number of chars and set up for ashow */
  fprintf(renderer->file, "%d div exch 0.0 exch \n", n_chars);
  
  fprintf(renderer->file, " gs 1 -1 sc ashow gr\n");
}

static void
draw_image(DiaRenderer *self,
	   Point *point,
	   real width, real height,
	   DiaImage image)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
  int img_width, img_height, img_rowstride;
  int x, y;
  real ratio;
  guint8 *rgb_data;
  guint8 *mask_data;
  gchar d1_buf[DTOSTR_BUF_SIZE];
  gchar d2_buf[DTOSTR_BUF_SIZE];

  img_width = dia_image_width(image);
  img_rowstride = dia_image_rowstride(image);
  img_height = dia_image_height(image);

  rgb_data = dia_image_rgb_data(image);
  mask_data = dia_image_mask_data(image);

  ratio = height/width;

  fprintf(renderer->file, "gs\n");

  /* color output only */
  fprintf(renderer->file, "/pix %i string def\n", img_width * 3);
  fprintf(renderer->file, "%i %i 8\n", img_width, img_height);
  fprintf(renderer->file, "%s %s tr\n",
			   psrenderer_dtostr(d1_buf, point->x),
			   psrenderer_dtostr(d2_buf, point->y) );
  fprintf(renderer->file, "%s %s sc\n",
			   psrenderer_dtostr(d1_buf, width),
			   psrenderer_dtostr(d2_buf, height) );
  fprintf(renderer->file, "[%i 0 0 %i 0 0]\n", img_width, img_height);

  fprintf(renderer->file, "{currentfile pix readhexstring pop}\n");
  fprintf(renderer->file, "false 3 colorimage\n");
  fprintf(renderer->file, "\n");

  if (mask_data) {
    for (y = 0; y < img_height; y++) {
      for (x = 0; x < img_width; x++) {
	int i = y*img_rowstride+x*3;
	int m = y*img_width+x;
        fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i])/255));
        fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i+1])/255));
        fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i+2])/255));
      }
      fprintf(renderer->file, "\n");
    }
  } else {
    for (y = 0; y < img_height; y++) {
      for (x = 0; x < img_width; x++) {
	int i = y*img_rowstride+x*3;
        fprintf(renderer->file, "%02x", (int)(rgb_data[i]));
        fprintf(renderer->file, "%02x", (int)(rgb_data[i+1]));
        fprintf(renderer->file, "%02x", (int)(rgb_data[i+2]));
      }
      fprintf(renderer->file, "\n");
    }
  }
  fprintf(renderer->file, "gr\n");
  fprintf(renderer->file, "\n");
   
  g_free(rgb_data);
  g_free(mask_data);
}

static void
begin_prolog (DiaPsRenderer *renderer)
{
  g_assert(renderer->file != NULL);

  fprintf(renderer->file, "%%%%BeginProlog\n");
  fprintf(renderer->file,
	  "[ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n"
	  "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one\n"
	  "/two /three /four /five /six /seven /eight /nine /colon /semicolon\n"
	  "/less /equal /greater /question /at /A /B /C /D /E\n"
	  "/F /G /H /I /J /K /L /M /N /O\n"
	  "/P /Q /R /S /T /U /V /W /X /Y\n"
	  "/Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c\n"
	  "/d /e /f /g /h /i /j /k /l /m\n"
	  "/n /o /p /q /r /s /t /u /v /w\n"
	  "/x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
	  "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright\n"
	  "/ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior\n"
	  "/acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf\n"
	  "/threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
	  "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde\n"
	  "/Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex\n"
	  "/Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring\n"
	  "/ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
	  "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave\n"
	  "/uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def\n");

  fprintf(renderer->file,
	  "/cp {closepath} bind def\n"
	  "/c {curveto} bind def\n"
	  "/f {fill} bind def\n"
	  "/a {arc} bind def\n"
	  "/ef {eofill} bind def\n"
	  "/ex {exch} bind def\n"
	  "/gr {grestore} bind def\n"
	  "/gs {gsave} bind def\n"
	  "/sa {save} bind def\n"
	  "/rs {restore} bind def\n"
	  "/l {lineto} bind def\n"
	  "/m {moveto} bind def\n"
	  "/rm {rmoveto} bind def\n"
	  "/n {newpath} bind def\n"
	  "/s {stroke} bind def\n"
	  "/sh {show} bind def\n"
	  "/slc {setlinecap} bind def\n"
	  "/slj {setlinejoin} bind def\n"
	  "/slw {setlinewidth} bind def\n"
	  "/srgb {setrgbcolor} bind def\n"
	  "/rot {rotate} bind def\n"
	  "/sc {scale} bind def\n"
	  "/sd {setdash} bind def\n"
	  "/ff {findfont} bind def\n"
	  "/sf {setfont} bind def\n"
	  "/scf {scalefont} bind def\n"
	  "/sw {stringwidth pop} bind def\n"
	  "/tr {translate} bind def\n"

	  "\n/ellipsedict 8 dict def\n"
	  "ellipsedict /mtrx matrix put\n"
	  "/ellipse\n"
	  "{ ellipsedict begin\n"
          "   /endangle exch def\n"
          "   /startangle exch def\n"
          "   /yrad exch def\n"
          "   /xrad exch def\n"
          "   /y exch def\n"
          "   /x exch def"
	  "   /savematrix mtrx currentmatrix def\n"
          "   x y tr xrad yrad sc\n"
          "   0 0 1 startangle endangle arc\n"
          "   savematrix setmatrix\n"
          "   end\n"
	  "} def\n\n"
	  "/mergeprocs {\n"
	  "dup length\n"
	  "3 -1 roll\n"
	  "dup\n"
	  "length\n"
	  "dup\n"
	  "5 1 roll\n"
	  "3 -1 roll\n"
	  "add\n"
	  "array cvx\n"
	  "dup\n"
	  "3 -1 roll\n"
	  "0 exch\n"
	  "putinterval\n"
	  "dup\n"
	  "4 2 roll\n"
	  "putinterval\n"
	  "} bind def\n");

}

/* helper function */
static void 
print_reencode_font(FILE *file, char *fontname)
{
  /* Don't reencode the Symbol font, as it doesn't work in latin1 encoding.
   * Instead, just define Symbol-latin1 to be the same as Symbol. */
  if (!strcmp(fontname, "Symbol"))
    fprintf(file,
	    "/%s-latin1\n"
	    "    /%s findfont\n"
	    "definefont pop\n", fontname, fontname);
  else
    fprintf(file,
	    "/%s-latin1\n"
	    "    /%s findfont\n"
	    "    dup length dict begin\n"
	    "	{1 index /FID ne {def} {pop pop} ifelse} forall\n"
	    "	/Encoding isolatin1encoding def\n"
	    "    currentdict end\n"
	    "definefont pop\n", fontname, fontname);
}

static void
dump_fonts (DiaPsRenderer *renderer)
{
  print_reencode_font(renderer->file, "Times-Roman");
  print_reencode_font(renderer->file, "Times-Italic");
  print_reencode_font(renderer->file, "Times-Bold");
  print_reencode_font(renderer->file, "Times-BoldItalic");
  print_reencode_font(renderer->file, "AvantGarde-Book");
  print_reencode_font(renderer->file, "AvantGarde-BookOblique");
  print_reencode_font(renderer->file, "AvantGarde-Demi");
  print_reencode_font(renderer->file, "AvantGarde-DemiOblique");
  print_reencode_font(renderer->file, "Bookman-Light");
  print_reencode_font(renderer->file, "Bookman-LightItalic");
  print_reencode_font(renderer->file, "Bookman-Demi");
  print_reencode_font(renderer->file, "Bookman-DemiItalic");
  print_reencode_font(renderer->file, "Courier");
  print_reencode_font(renderer->file, "Courier-Oblique");
  print_reencode_font(renderer->file, "Courier-Bold");
  print_reencode_font(renderer->file, "Courier-BoldOblique");
  print_reencode_font(renderer->file, "Helvetica");
  print_reencode_font(renderer->file, "Helvetica-Oblique");
  print_reencode_font(renderer->file, "Helvetica-Bold");
  print_reencode_font(renderer->file, "Helvetica-BoldOblique");
  print_reencode_font(renderer->file, "Helvetica-Narrow");
  print_reencode_font(renderer->file, "Helvetica-Narrow-Oblique");
  print_reencode_font(renderer->file, "Helvetica-Narrow-Bold");
  print_reencode_font(renderer->file, "Helvetica-Narrow-BoldOblique");
  print_reencode_font(renderer->file, "NewCenturySchoolbook-Roman");
  print_reencode_font(renderer->file, "NewCenturySchoolbook-Italic");
  print_reencode_font(renderer->file, "NewCenturySchoolbook-Bold");
  print_reencode_font(renderer->file, "NewCenturySchoolbook-BoldItalic");
  print_reencode_font(renderer->file, "Palatino-Roman");
  print_reencode_font(renderer->file, "Palatino-Italic");
  print_reencode_font(renderer->file, "Palatino-Bold");
  print_reencode_font(renderer->file, "Palatino-BoldItalic");
  print_reencode_font(renderer->file, "Symbol");
  print_reencode_font(renderer->file, "ZapfChancery-MediumItalic");
  print_reencode_font(renderer->file, "ZapfDingbats");
}

static void
end_prolog (DiaPsRenderer *renderer)
{
  gchar d1_buf[DTOSTR_BUF_SIZE];
  gchar d2_buf[DTOSTR_BUF_SIZE];
  
  if (renderer_is_eps(renderer)) {
    fprintf(renderer->file,
            "%s %s scale\n",
	    psrenderer_dtostr(d1_buf, renderer->scale),
	    psrenderer_dtostr(d2_buf, -renderer->scale) );
    fprintf(renderer->file,
            "%s %s translate\n",
	    psrenderer_dtostr(d1_buf, -renderer->extent.left),
	    psrenderer_dtostr(d2_buf, -renderer->extent.bottom) );
  } else {
    /* done by BoundingBox above */
  }

  fprintf(renderer->file,
	  "%%%%EndProlog\n\n\n");
}

/* constructor */
static void
ps_renderer_init (GTypeInstance *instance, gpointer g_class)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER (instance);

  renderer->file = NULL;

  renderer->lcolor.red = -1.0;
  
  renderer->dash_length = 1.0;
  renderer->dot_length = 0.2;
  renderer->saved_line_style = LINESTYLE_SOLID;
  renderer->is_portrait = TRUE;

  renderer->scale = 28.346;
}

/* GObject stuff */
static void dia_ps_renderer_class_init (DiaPsRendererClass *klass);

static gpointer parent_class = NULL;

GType
dia_ps_renderer_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
      static const GTypeInfo object_info =
      {
        sizeof (DiaPsRendererClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) dia_ps_renderer_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (DiaPsRenderer),
        0,              /* n_preallocs */
        ps_renderer_init       /* init */
      };

      object_type = g_type_register_static (DIA_TYPE_RENDERER,
                                            "DiaPsRenderer",
                                            &object_info, 0);
    }
  
  return object_type;
}

static void
dia_ps_renderer_finalize (GObject *object)
{
  DiaPsRenderer *renderer = DIA_PS_RENDERER (object);

  g_free(renderer->title);
  /*  fclose(renderer->file);*/

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
dia_ps_renderer_class_init (DiaPsRendererClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
  DiaPsRendererClass *ps_renderer_class = DIA_PS_RENDERER_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = dia_ps_renderer_finalize;

  renderer_class->begin_render = begin_render;
  renderer_class->end_render   = end_render;

  renderer_class->set_linewidth  = set_linewidth;
  renderer_class->set_linecaps   = set_linecaps;
  renderer_class->set_linejoin   = set_linejoin;
  renderer_class->set_linestyle  = set_linestyle;
  renderer_class->set_dashlength = set_dashlength;
  renderer_class->set_fillstyle  = set_fillstyle;
  renderer_class->set_font       = set_font;

  renderer_class->draw_line    = draw_line;
  renderer_class->fill_polygon = fill_polygon;
  renderer_class->draw_arc     = draw_arc;
  renderer_class->fill_arc     = fill_arc;
  renderer_class->draw_ellipse = draw_ellipse;
  renderer_class->fill_ellipse = fill_ellipse;
  renderer_class->draw_string  = draw_string;
  renderer_class->draw_image   = draw_image;

  /* medium level functions */
  renderer_class->draw_bezier  = draw_bezier;
  renderer_class->fill_bezier  = fill_bezier;
  renderer_class->draw_rect = draw_rect;
  renderer_class->fill_rect = fill_rect;
  renderer_class->draw_polyline  = draw_polyline;
  renderer_class->draw_polygon   = draw_polygon;
  renderer_class->draw_text_line = draw_text_line;

  /* ps specific */
  ps_renderer_class->begin_prolog = begin_prolog;
  ps_renderer_class->dump_fonts = dump_fonts;
  ps_renderer_class->end_prolog = end_prolog;
}

