/* -*- Mode: C; c-basic-offset: 4 -*- */
/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998 Alexander Larsson
 *
 * render_pgf.c: Exporting module/plug-in to TeX PGF package
 * Copyright (C) 2005 Moritz Kirmse
 * Originally derived from render_pstricks.c (pstricks plug-in)
 * Copyright (C) 2000 Jacek Pliszka <pliszka@fuw.edu.pl>
 *
 * 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.
 */

/*
Implementation choices:

The PGF package is written with the idea in mind that the typesetting software will
(usually) do a better and more beautiful job than the user.

This export filter respects thes and therefore some export features are special:

-text is NOT escaped: (La)TeX will interpret this code.
Therefore text size wil not be consistent and bounding boxes will not work.
When text is inside a box (as for example in the UML modules), the result wil not
be as expected.

-some arrows from the PGF-library are used (latex, stealth and to)
for these arrows, size selection and bounding box won't work

-the rounded corners for polyline-paths are slightly different:
	the radius measure is not the radius of the arc,
	but the length cut off at the angle from each segment of the polyline
however the PGF native corner rounding was implemented, since I observed a discontinuous
line thickness at the interface between the segments and arcs

TODO:
-Image exporting function
-Implement PGF basic layer throughout
-Implement all the Arrow styles
*/


#include <config.h>

#include <string.h>
#include <time.h>
#include <math.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>

#include <glib.h>
#include <glib/gstdio.h>

#include "intl.h"
#include "render_pgf.h"
#include "message.h"
#include "diagramdata.h"
#include "dia_image.h"
#include "filter.h"
#include "arrows.h"

#define POINTS_in_INCH 28.346
#define DTOSTR_BUF_SIZE G_ASCII_DTOSTR_BUF_SIZE
#define pgf_dtostr(buf,d) \
	g_ascii_formatd(buf,sizeof(buf),"%f",d)
#define pgf_itostr(buf,i) \
	g_sprintf(buf,"%d",i)

static void begin_render(DiaRenderer *self);
static void end_render(DiaRenderer *self);
static void set_linewidth(DiaRenderer *self, real linewidth);
static void set_linecaps(DiaRenderer *self, LineCaps mode);
static void set_linejoin(DiaRenderer *self, LineJoin mode);
static void set_linestyle(DiaRenderer *self, LineStyle mode);
static void set_dashlength(DiaRenderer *self, real length);
static void set_fillstyle(DiaRenderer *self, FillStyle mode);
static void set_font(DiaRenderer *self, DiaFont *font, real height);
static void draw_line(DiaRenderer *self, 
		      Point *start, Point *end, 
		      Color *line_color);
static void draw_polyline(DiaRenderer *self, 
			  Point *points, int num_points, 
			  Color *line_color);
static void draw_rounded_polyline (DiaRenderer *self,
                        Point *points, int num_points,
                        Color *color, real radius);
static void draw_polygon(DiaRenderer *self, 
			 Point *points, int num_points, 
			 Color *line_color);
static void fill_polygon(DiaRenderer *self, 
			 Point *points, int num_points, 
			 Color *line_color);
static void draw_rect(DiaRenderer *self, 
		      Point *ul_corner, Point *lr_corner,
		      Color *color);
static void fill_rect(DiaRenderer *self, 
		      Point *ul_corner, Point *lr_corner,
		      Color *color);
static void draw_rounded_rect(DiaRenderer *self, 
			      Point *ul_corner, Point *lr_corner,
			      Color *color, real radius);
static void fill_rounded_rect(DiaRenderer *self, 
			      Point *ul_corner, Point *lr_corner,
			      Color *color, real radius);
static void draw_arc(DiaRenderer *self, 
		     Point *center,
		     real width, real height,
		     real angle1, real angle2,
		     Color *color);
static void fill_arc(DiaRenderer *self, 
		     Point *center,
		     real width, real height,
		     real angle1, real angle2,
		     Color *color);
static void draw_ellipse(DiaRenderer *self, 
			 Point *center,
			 real width, real height,
			 Color *color);
static void fill_ellipse(DiaRenderer *self, 
			 Point *center,
			 real width, real height,
			 Color *color);
static void draw_bezier(DiaRenderer *self, 
			BezPoint *points,
			int numpoints,
			Color *color);
static void fill_bezier(DiaRenderer *self, 
			BezPoint *points, /* Last point must be same as first point */
			int numpoints,
			Color *color);
static void draw_string(DiaRenderer *self,
			const char *text,
			Point *pos, Alignment alignment,
			Color *color);
static void draw_image(DiaRenderer *self,
		       Point *point,
		       real width, real height,
		       DiaImage *image);
  
static void draw_line_with_arrows(DiaRenderer *renderer, Point *start, Point *end, 
                                  real line_width, Color *line_color,
                                  Arrow *start_arrow, Arrow *end_arrow);
static void draw_arc_with_arrows(DiaRenderer *renderer, Point *start, Point *end, Point *midpoint,
                                 real line_width, Color *color,
                                 Arrow *start_arrow, Arrow *end_arrow);
static void draw_polyline_with_arrows(DiaRenderer *renderer, Point *points, int num_points,
                                     real line_width, Color *color,
                                     Arrow *start_arrow, Arrow *end_arrow);
static void draw_rounded_polyline_with_arrows(DiaRenderer *renderer, 
                                     Point *points, int num_points, real line_width, Color *color, 
				     Arrow *start_arrow, Arrow *end_arrow, real radius);
static void draw_bezier_with_arrows(DiaRenderer *renderer, BezPoint *points, int num_points,
                                   real line_width, Color *color,
                                   Arrow *start_arrow, Arrow *end_arrow);

/*store the higher level arrow functions for arrows not (yet) implemented in this PGF macro*/
void (*orig_draw_line_with_arrows)  (DiaRenderer *renderer, Point *start, Point *end, 
                                  real line_width, Color *line_color,
                                  Arrow *start_arrow, Arrow *end_arrow);
  
void (*orig_draw_arc_with_arrows)  (DiaRenderer *renderer, Point *start, Point *end, Point *midpoint,
                                 real line_width, Color *color,
                                 Arrow *start_arrow, Arrow *end_arrow);
  
void (*orig_draw_polyline_with_arrows) (DiaRenderer *renderer, Point *points, int num_points,
                                     real line_width, Color *color,
                                     Arrow *start_arrow, Arrow *end_arrow);
  
void (*orig_draw_rounded_polyline_with_arrows) (DiaRenderer *renderer, 
                                     Point *points, int num_points, real line_width, Color *color, 
				     Arrow *start_arrow, Arrow *end_arrow, real radius);

void (*orig_draw_bezier_with_arrows) (DiaRenderer *renderer, BezPoint *points, int num_points,
                                   real line_width, Color *color,
                                   Arrow *start_arrow, Arrow *end_arrow);



static void pgf_renderer_class_init (PgfRendererClass *klass);

static gpointer parent_class = NULL;

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

  if (!object_type)
    {
      static const GTypeInfo object_info =
      {
        sizeof (PgfRendererClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) pgf_renderer_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (PgfRenderer),
        0,              /* n_preallocs */
	NULL            /* init */
      };

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

static void
pgf_renderer_finalize (GObject *object)
{
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
pgf_renderer_class_init (PgfRendererClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize = pgf_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->draw_polyline = draw_polyline;

  /*problem: the round angles behave slightly differently under PGF:
	the radius measure is not the radius, but the length cut off at the angle
	from each segment of the polyline
	however, to keep the inherited round corner generation, this command can be commented out*/  
  renderer_class->draw_rounded_polyline = draw_rounded_polyline;
  
  renderer_class->draw_polygon = draw_polygon;
  renderer_class->fill_polygon = fill_polygon;

  renderer_class->draw_rect = draw_rect;
  renderer_class->fill_rect = fill_rect;

  renderer_class->draw_rounded_rect = draw_rounded_rect;
  renderer_class->fill_rounded_rect = fill_rounded_rect;

  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_bezier = draw_bezier;
  renderer_class->fill_bezier = fill_bezier;

/* to keep the dia arrows instead of the native PGF ones, comment out this block of commands */
  orig_draw_line_with_arrows = renderer_class->draw_line_with_arrows;
  renderer_class->draw_line_with_arrows = draw_line_with_arrows;
  orig_draw_arc_with_arrows = renderer_class->draw_arc_with_arrows;
  renderer_class->draw_arc_with_arrows = draw_arc_with_arrows;
  orig_draw_polyline_with_arrows = renderer_class->draw_polyline_with_arrows;
  renderer_class->draw_polyline_with_arrows = draw_polyline_with_arrows;
  orig_draw_rounded_polyline_with_arrows = renderer_class->draw_rounded_polyline_with_arrows;
  renderer_class->draw_rounded_polyline_with_arrows = draw_rounded_polyline_with_arrows;
  orig_draw_bezier_with_arrows = renderer_class->draw_bezier_with_arrows;
  renderer_class->draw_bezier_with_arrows = draw_bezier_with_arrows;
  
  renderer_class->draw_string = draw_string;

  renderer_class->draw_image = draw_image;
}


static void 
set_line_color(PgfRenderer *renderer,Color *color)
{
    gchar red_buf[DTOSTR_BUF_SIZE];
    gchar green_buf[DTOSTR_BUF_SIZE];
    gchar blue_buf[DTOSTR_BUF_SIZE];

    fprintf(renderer->file, "\\definecolor{dialinecolor}{rgb}{%s, %s, %s}\n",
	    pgf_dtostr(red_buf, (gdouble) color->red),
	    pgf_dtostr(green_buf, (gdouble) color->green),
	    pgf_dtostr(blue_buf, (gdouble) color->blue) );
    fprintf(renderer->file,"\\pgfsetstrokecolor{dialinecolor}\n");
}

static void 
set_fill_color(PgfRenderer *renderer,Color *color)
{
    gchar red_buf[DTOSTR_BUF_SIZE];
    gchar green_buf[DTOSTR_BUF_SIZE];
    gchar blue_buf[DTOSTR_BUF_SIZE];

    fprintf(renderer->file, "\\definecolor{dialinecolor}{rgb}{%s, %s, %s}\n",
	    pgf_dtostr(red_buf, (gdouble) color->red),
	    pgf_dtostr(green_buf, (gdouble) color->green),
	    pgf_dtostr(blue_buf, (gdouble) color->blue) );
    fprintf(renderer->file,"\\pgfsetfillcolor{dialinecolor}\n");
}

static void
begin_render(DiaRenderer *self)
{
}

static void
end_render(DiaRenderer *self)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
  
    fprintf(renderer->file,"\\end{tikzpicture}\n");
    fclose(renderer->file);
}

static void
set_linewidth(DiaRenderer *self, real linewidth)
{  /* 0 == hairline (thinnest possible line on device) **/
    PgfRenderer *renderer = PGF_RENDERER(self);
    gchar d_buf[DTOSTR_BUF_SIZE];


    fprintf(renderer->file, "\\pgfsetlinewidth{%s\\du}\n",
	    pgf_dtostr(d_buf, (gdouble) linewidth) );
}

static void
set_linecaps(DiaRenderer *self, LineCaps mode)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
  
    switch(mode) {
    case LINECAPS_BUTT:
	fprintf(renderer->file, "\\pgfsetbuttcap\n");
	break;
    case LINECAPS_ROUND:
	fprintf(renderer->file, "\\pgfsetroundcap\n");
	break;
    case LINECAPS_PROJECTING:
	fprintf(renderer->file, "\\pgfsetrectcap\n");
	break;
    default:
	fprintf(renderer->file, "\\pgfsetbuttcap\n");
    }

}

static void
set_linejoin(DiaRenderer *self, LineJoin mode)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
    /* int ps_mode; */
  
    switch(mode) {
    case LINEJOIN_MITER:
	fprintf(renderer->file, "\\pgfsetmiterjoin\n");
	break;
    case LINEJOIN_ROUND:
	fprintf(renderer->file, "\\pgfsetroundjoin\n");
	break;
    case LINEJOIN_BEVEL:
	fprintf(renderer->file, "\\pgfsetbeveljoin\n");
	break;
    default:
	fprintf(renderer->file, "\\pgfsetmiterjoin\n");
    }

}

static void
set_linestyle(DiaRenderer *self, LineStyle mode)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
    real hole_width;
    gchar dash_length_buf[DTOSTR_BUF_SIZE];
    gchar dot_length_buf[DTOSTR_BUF_SIZE];
    gchar hole_width_buf[DTOSTR_BUF_SIZE];

    renderer->saved_line_style = mode;
  
    switch(mode) {
    case LINESTYLE_SOLID:
	fprintf(renderer->file, "\\pgfsetdash{}{0pt}\n");
	break;
    case LINESTYLE_DASHED:
	pgf_dtostr(dash_length_buf,renderer->dash_length);
	fprintf(renderer->file, "\\pgfsetdash{{%s\\du}{%s\\du}}{0\\du}\n", 
		dash_length_buf,
		dash_length_buf);
	break;
    case LINESTYLE_DASH_DOT:
	hole_width = (renderer->dash_length - renderer->dot_length) / 2.0;
	pgf_dtostr(hole_width_buf,hole_width);
	pgf_dtostr(dot_length_buf,renderer->dot_length);
	pgf_dtostr(dash_length_buf,renderer->dash_length);
	fprintf(renderer->file, "\\pgfsetdash{{%s\\du}{%s\\du}{%s\\du}{%s\\du}}{0cm}\n",
		dash_length_buf,
		hole_width_buf,
		dot_length_buf,
		hole_width_buf );
	break;
    case LINESTYLE_DASH_DOT_DOT:
	hole_width = (renderer->dash_length - 2.0*renderer->dot_length) / 3.0;
	pgf_dtostr(hole_width_buf,hole_width);
	pgf_dtostr(dot_length_buf,renderer->dot_length);
	pgf_dtostr(dash_length_buf,renderer->dash_length);
	fprintf(renderer->file, "\\pgfsetdash{{%s\\du}{%s\\du}{%s\\du}{%s\\du}{%s\\du}{%s\\du}}{0cm}\n",
		dash_length_buf,
		hole_width_buf,
		dot_length_buf,
		hole_width_buf,
		dot_length_buf,
		hole_width_buf );
	break;
    case LINESTYLE_DOTTED:
	pgf_dtostr(dot_length_buf,renderer->dot_length);
	fprintf(renderer->file, "\\pgfsetdash{{\\pgflinewidth}{%s\\du}}{0cm}\n", dot_length_buf);
	break;
    }
}

static void
set_dashlength(DiaRenderer *self, real length)
{  /* dot = 20% of len */
    PgfRenderer *renderer = PGF_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)
{
    switch(mode) {
    case FILLSTYLE_SOLID:
	break;
    default:
	message_error("pgf_renderer: Unsupported fill mode specified!\n");
    }
}

static void
set_font(DiaRenderer *self, DiaFont *font, real height)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
    /* gchar d_buf[DTOSTR_BUF_SIZE]; */

    fprintf(renderer->file, "%% setfont left to latex\n");
}

static void
draw_line(DiaRenderer *self, 
	  Point *start, Point *end, 
	  Color *line_color)
{
    PgfRenderer *renderer = PGF_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];

    set_line_color(renderer,line_color);

    fprintf(renderer->file, "\\draw (%s\\du,%s\\du)--(%s\\du,%s\\du);\n",
	    pgf_dtostr(sx_buf,start->x),
	    pgf_dtostr(sy_buf,start->y),
	    pgf_dtostr(ex_buf,end->x),
	    pgf_dtostr(ey_buf,end->y) );
}

static void
draw_polyline(DiaRenderer *self, 
	      Point *points, int num_points, 
	      Color *line_color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
    int i;
    gchar px_buf[DTOSTR_BUF_SIZE];
    gchar py_buf[DTOSTR_BUF_SIZE];
  
    set_line_color(renderer,line_color);  
    fprintf(renderer->file, "\\draw (%s\\du,%s\\du)",
	    pgf_dtostr(px_buf,points[0].x),
	    pgf_dtostr(py_buf,points[0].y) );

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

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

/*problem: the round angles behave slightly differently under PGF:
	the radius measure is not the radius, but the length cut off at the angle
	from each segment of the polyline*/
static void draw_rounded_polyline (DiaRenderer *self,
                        Point *points, int num_points,
                        Color *color, real radius)
{
	gchar rad_buf[DTOSTR_BUF_SIZE];
	
	PgfRenderer *renderer = PGF_RENDERER(self);
	pgf_dtostr(rad_buf, (gdouble) radius);
	fprintf(renderer->file, "{\\pgfsetcornersarced{\\pgfpoint{%s\\du}{%s\\du}}",
					rad_buf, rad_buf);
	draw_polyline(self,points,num_points,color);
	fprintf(renderer->file, "}");
}

static void
pgf_polygon(PgfRenderer *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];

    if (!filled) {set_line_color(renderer,line_color);}
    else {set_fill_color(renderer,line_color);}
    	
    
    fprintf(renderer->file, "\\%s (%s\\du,%s\\du)",
	    (filled?"fill":"draw"),
	    pgf_dtostr(px_buf,points[0].x),
	    pgf_dtostr(py_buf,points[0].y) );
    
    for (i=1;i<num_points;i++) {
	fprintf(renderer->file, "--(%s\\du,%s\\du)",
		pgf_dtostr(px_buf,points[i].x),
		pgf_dtostr(py_buf,points[i].y) );
    }
    fprintf(renderer->file,"--cycle;\n");
}

static void
draw_polygon(DiaRenderer *self, 
	     Point *points, int num_points, 
	     Color *line_color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_polygon(renderer,points,num_points,line_color,FALSE);
}

static void
fill_polygon(DiaRenderer *self, 
	     Point *points, int num_points, 
	     Color *line_color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_polygon(renderer,points,num_points,line_color,TRUE);
}

static void
pgf_rect(PgfRenderer *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];

    if (!filled) {set_line_color(renderer,color);}
    else {set_fill_color(renderer,color);}

    pgf_dtostr(ulx_buf, (gdouble) ul_corner->x);
    pgf_dtostr(uly_buf, (gdouble) ul_corner->y);
    pgf_dtostr(lrx_buf, (gdouble) lr_corner->x);
    pgf_dtostr(lry_buf, (gdouble) lr_corner->y);
    
    fprintf(renderer->file, "\\%s (%s\\du,%s\\du)--(%s\\du,%s\\du)--(%s\\du,%s\\du)--(%s\\du,%s\\du)--cycle;\n",
	    (filled?"fill":"draw"),
	    ulx_buf, uly_buf,
	    ulx_buf, lry_buf,
	    lrx_buf, lry_buf,
	    lrx_buf, uly_buf );
}

static void
draw_rect(DiaRenderer *self, 
	  Point *ul_corner, Point *lr_corner,
	  Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_rect(renderer,ul_corner,lr_corner,color,FALSE);
}

static void
fill_rect(DiaRenderer *self, 
	  Point *ul_corner, Point *lr_corner,
	  Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_rect(renderer,ul_corner,lr_corner,color,TRUE);
}

static void 
draw_rounded_rect(DiaRenderer *self, 
			      Point *ul_corner, Point *lr_corner,
			      Color *color, real radius)
{
	gchar rad_buf[DTOSTR_BUF_SIZE];
	
	PgfRenderer *renderer = PGF_RENDERER(self);
	pgf_dtostr(rad_buf, (gdouble) radius);
	fprintf(renderer->file, "{\\pgfsetcornersarced{\\pgfpoint{%s\\du}{%s\\du}}",
					rad_buf, rad_buf);
	pgf_rect(renderer,ul_corner,lr_corner,color,FALSE);
	fprintf(renderer->file, "}");
}

static void 
fill_rounded_rect(DiaRenderer *self, 
			      Point *ul_corner, Point *lr_corner,
			      Color *color, real radius)
{
	PgfRenderer *renderer = PGF_RENDERER(self);
	gchar rad_buf[DTOSTR_BUF_SIZE];
	pgf_dtostr(rad_buf, (gdouble) radius);
	fprintf(renderer->file, "{\\pgfsetcornersarced{\\pgfpoint{%s\\du}{%s\\du}}",
					rad_buf, rad_buf);
	pgf_rect(renderer,ul_corner,lr_corner,color,TRUE);
	fprintf(renderer->file, "}");
}


static void
pgf_arc(PgfRenderer *renderer, 
	     Point *center,
	     real width, real height,
	     real angle1, real angle2,
	     Color *color,int filled)
{
    double radius1,radius2;
    int ang1,ang2;
    gchar stx_buf[DTOSTR_BUF_SIZE];
    gchar sty_buf[DTOSTR_BUF_SIZE];
    gchar cx_buf[DTOSTR_BUF_SIZE];
    gchar cy_buf[DTOSTR_BUF_SIZE];
    gchar r1_buf[DTOSTR_BUF_SIZE];
    gchar r2_buf[DTOSTR_BUF_SIZE];
    gchar sqrt_buf[DTOSTR_BUF_SIZE];
    gchar angle1_buf[DTOSTR_BUF_SIZE];
    gchar angle2_buf[DTOSTR_BUF_SIZE];
    
    radius1=(double) width/2.0;
    radius2=(double) height/2.0;

    pgf_dtostr(stx_buf,center->x+ radius1*cos(angle1*.017453));
    pgf_dtostr(sty_buf,center->y- radius2*sin(angle1*.017453));
    pgf_dtostr(cx_buf,center->x);
    pgf_dtostr(cy_buf,center->y);
    pgf_dtostr(r1_buf,radius1);
    pgf_dtostr(r2_buf,radius2);
    pgf_dtostr(sqrt_buf,sqrt(radius1*radius1+radius2*radius2));
    ang1=(int)angle1;
    ang2=(int)angle2;
    ang2=ang1+(360+ang2-ang1)%360;
    pgf_itostr(angle1_buf,360-ang1);
    pgf_itostr(angle2_buf,360-ang2);

    if (!filled) {set_line_color(renderer,color);}
    else {set_fill_color(renderer,color);}

   
    fprintf(renderer->file,"\\pgfpathmoveto{\\pgfpoint{%s\\du}{%s\\du}}\n",
	    stx_buf, sty_buf);
    fprintf(renderer->file,"\\pgfpatharc{%s}{%s}{%s\\du and %s\\du}\n",
	    angle1_buf, angle2_buf,
	    r1_buf, r2_buf);
	    
    if (filled)
	fprintf(renderer->file, "\\pgfusepath{fill}\n");
    else
	fprintf(renderer->file, "\\pgfusepath{stroke}\n");
}

static void
draw_arc(DiaRenderer *self, 
	 Point *center,
	 real width, real height,
	 real angle1, real angle2,
	 Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_arc(renderer,center,width,height,angle1,angle2,color,0);
}

static void
fill_arc(DiaRenderer *self, 
	 Point *center,
	 real width, real height,
	 real angle1, real angle2,
	 Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_arc(renderer,center,width,height,angle1,angle2,color,1);
}

static void
pgf_ellipse(PgfRenderer *renderer,
		 Point *center,
		 real width, real height,
		 Color *color, gboolean filled)
{
    gchar cx_buf[DTOSTR_BUF_SIZE];
    gchar cy_buf[DTOSTR_BUF_SIZE];
    gchar width_buf[DTOSTR_BUF_SIZE];
    gchar height_buf[DTOSTR_BUF_SIZE];

    if (!filled) {set_line_color(renderer,color);}
    else {set_fill_color(renderer,color);}

/* "\\%sdraw (%s\\du,%s\\du) ellipse (%s\\du and %s\\du)\n", */
    fprintf(renderer->file, 
	    "\\pgfpathellipse{\\pgfpoint{%s\\du}{%s\\du}}"
	                    "{\\pgfpoint{%s\\du}{0\\du}}"
	                    "{\\pgfpoint{0\\du}{%s\\du}}\n"
	    "\\pgfusepath{%s}\n",
	    pgf_dtostr(cx_buf,center->x),
	    pgf_dtostr(cy_buf,center->y),
	    pgf_dtostr(width_buf,width/2.0),
	    pgf_dtostr(height_buf,height/2.0),
	    (filled?"fill":"stroke") );
}

static void
draw_ellipse(DiaRenderer *self, 
	     Point *center,
	     real width, real height,
	     Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_ellipse(renderer,center,width,height,color,FALSE);
}

static void
fill_ellipse(DiaRenderer *self, 
	     Point *center,
	     real width, real height,
	     Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_ellipse(renderer,center,width,height,color,TRUE);
}

static void
pgf_bezier(PgfRenderer *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];

    if (!filled) {set_line_color(renderer,color);}
    else {set_fill_color(renderer,color);}

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

    fprintf(renderer->file, "\\pgfpathmoveto{\\pgfpoint{%s\\du}{%s\\du}}\n",
	    pgf_dtostr(p1x_buf,points[0].p1.x),
	    pgf_dtostr(p1y_buf,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, "\\pgfpathlineto{\\pgfpoint{%s\\du}{%s\\du}}\n",
		    pgf_dtostr(p1x_buf,points[i].p1.x),
		    pgf_dtostr(p1y_buf,points[i].p1.y) );
	    break;
	case BEZ_CURVE_TO:
	    fprintf(renderer->file, "\\pgfpathcurveto{\\pgfpoint{%s\\du}{%s\\du}}"
	    "{\\pgfpoint{%s\\du}{%s\\du}}"
	    "{\\pgfpoint{%s\\du}{%s\\du}}\n",
		    pgf_dtostr(p1x_buf,points[i].p1.x),
		    pgf_dtostr(p1y_buf,points[i].p1.y),
		    pgf_dtostr(p2x_buf,points[i].p2.x),
		    pgf_dtostr(p2y_buf,points[i].p2.y),
		    pgf_dtostr(p3x_buf,points[i].p3.x),
		    pgf_dtostr(p3y_buf,points[i].p3.y) );
	    break;
	}
    
    if (filled)
	fprintf(renderer->file, "\\pgfusepath{fill}\n");
	
/*	fill[fillstyle=solid,fillcolor=diafillcolor,linecolor=diafillcolor]}\n"); */
    else
	fprintf(renderer->file, "\\pgfusepath{stroke}\n");
}

static void
draw_bezier(DiaRenderer *self, 
	    BezPoint *points,
	    int numpoints, /* numpoints = 4+3*n, n=>0 */
	    Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_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)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    pgf_bezier(renderer,points,numpoints,color,TRUE);
}

static int 
set_arrows(PgfRenderer *renderer, Arrow *start_arrow, Arrow *end_arrow)
{
    int modified = 2|1; /*=3*/
    fprintf(renderer->file, "%% was here!!!\n");
    switch (start_arrow->type)
    {
    case ARROW_NONE:
       break;
    case ARROW_LINES:
       fprintf(renderer->file, "\\pgfsetarrowsstart{to}\n");
       break;
    case ARROW_FILLED_TRIANGLE:
       fprintf(renderer->file, "\\pgfsetarrowsstart{latex}\n");
       break;
    case ARROW_FILLED_CONCAVE:
       fprintf(renderer->file, "\\pgfsetarrowsstart{stealth}\n");
       break;
    default:
       modified ^= 2;
    }
    if (modified & 2) start_arrow->type = ARROW_NONE;
    
    switch (end_arrow->type)
    {
    case ARROW_NONE:
       break;
    case ARROW_LINES:
       fprintf(renderer->file, "\\pgfsetarrowsend{to}\n");
       break;
    case ARROW_FILLED_TRIANGLE:
       fprintf(renderer->file, "\\pgfsetarrowsend{latex}\n");
       break;
    case ARROW_FILLED_CONCAVE:
       fprintf(renderer->file, "\\pgfsetarrowsend{stealth}\n");
       break;
    default:
       modified ^= 1;
    }
    if (modified & 1) end_arrow->type = ARROW_NONE;
    
    return modified; /* =0 if no native arrow is used */
}

static void 
draw_line_with_arrows(DiaRenderer *self, Point *start, Point *end, 
                                  real line_width, Color *line_color,
                                  Arrow *start_arrow, Arrow *end_arrow)
{
    int nat_arr;
    Arrow st_arrow;
    Arrow e_arrow;
    PgfRenderer *renderer = PGF_RENDERER(self);

    if (start_arrow != NULL) {
	st_arrow = *start_arrow;
    } else {
	st_arrow.type = ARROW_NONE;
    }

    if (end_arrow != NULL) {
	e_arrow = *end_arrow;
    } else {
	e_arrow.type = ARROW_NONE;
    }
    fprintf(renderer->file, "{\n");
    set_fill_color(renderer, line_color);
    nat_arr = set_arrows(renderer, &st_arrow, &e_arrow);
    if (nat_arr != 0)
    {
/*      static void set_linewidth(self, line_width);
      draw_line(self, start, end, line_width, color);*/
      orig_draw_line_with_arrows(self, start, end, line_width, line_color, NULL, NULL);
    }
    fprintf(renderer->file, "}\n");
    if (nat_arr != 3)
    {
      orig_draw_line_with_arrows(self, start, end, line_width, line_color, &st_arrow, &e_arrow);
    }
    
}

static void 
draw_arc_with_arrows(DiaRenderer *self, Point *start, Point *end, Point *midpoint,
                                 real line_width, Color *color,
                                 Arrow *start_arrow, Arrow *end_arrow)
{
    int nat_arr;
    Arrow st_arrow;
    Arrow e_arrow;
    PgfRenderer *renderer = PGF_RENDERER(self);

    if (start_arrow != NULL) {
	st_arrow = *start_arrow;
    } else {
	st_arrow.type = ARROW_NONE;
    }

    if (end_arrow != NULL) {
	e_arrow = *end_arrow;
    } else {
	e_arrow.type = ARROW_NONE;
    }

    fprintf(renderer->file, "{\n");
    set_fill_color(renderer, color);
    nat_arr = set_arrows(renderer, &st_arrow, &e_arrow);
    if (nat_arr != 0)
    {
      orig_draw_arc_with_arrows(self, start, end, midpoint, line_width, color, NULL, NULL);
    }
    fprintf(renderer->file, "}\n");
    if (nat_arr != 3)
    {
      orig_draw_arc_with_arrows(self, start, end, midpoint, line_width, color, &st_arrow, &e_arrow);
    }
}

static void 
draw_polyline_with_arrows(DiaRenderer *self, Point *points, int num_points,
                                     real line_width, Color *color,
                                     Arrow *start_arrow, Arrow *end_arrow)
{
    int nat_arr;
    Arrow st_arrow;
    Arrow e_arrow;
    PgfRenderer *renderer = PGF_RENDERER(self);

    if (start_arrow != NULL) {
	st_arrow = *start_arrow;
    } else {
	st_arrow.type = ARROW_NONE;
    }

    if (end_arrow != NULL) {
	e_arrow = *end_arrow;
    } else {
	e_arrow.type = ARROW_NONE;
    }

    fprintf(renderer->file, "{\n");
    set_fill_color(renderer, color);
    nat_arr = set_arrows(renderer, &st_arrow, &e_arrow);
    if (nat_arr != 0)
    {
      orig_draw_polyline_with_arrows(self, points, num_points, line_width, color, NULL, NULL);
    }
    fprintf(renderer->file, "}\n");
    if (nat_arr != 3)
    {
      orig_draw_polyline_with_arrows(self, points, num_points, line_width, color, &st_arrow, &e_arrow);
    }
}

static void 
draw_rounded_polyline_with_arrows(DiaRenderer *self, 
                                     Point *points, int num_points, real line_width, Color *color, 
				     Arrow *start_arrow, Arrow *end_arrow, real radius)
{
    int nat_arr;
    Arrow st_arrow;
    Arrow e_arrow;
    PgfRenderer *renderer = PGF_RENDERER(self);

    if (start_arrow != NULL) {
	st_arrow = *start_arrow;
    } else {
	st_arrow.type = ARROW_NONE;
    }

    if (end_arrow != NULL) {
	e_arrow = *end_arrow;
    } else {
	e_arrow.type = ARROW_NONE;
    }

    fprintf(renderer->file, "{\n");
    set_fill_color(renderer, color);
    nat_arr = set_arrows(renderer, &st_arrow, &e_arrow);
    if (nat_arr != 0)
    {
      orig_draw_rounded_polyline_with_arrows(self, points, num_points, 
                     line_width, color, NULL, NULL, radius);
    }
    fprintf(renderer->file, "}\n");
    if (nat_arr != 3)
    {
      orig_draw_rounded_polyline_with_arrows(self, points, num_points, 
                     line_width, color, &st_arrow, &e_arrow, radius);
    }
}

static void 
draw_bezier_with_arrows(DiaRenderer *self, BezPoint *points, int num_points,
                                   real line_width, Color *color,
                                   Arrow *start_arrow, Arrow *end_arrow)
{
    int nat_arr;
    Arrow st_arrow;
    Arrow e_arrow;
    PgfRenderer *renderer = PGF_RENDERER(self);

    if (start_arrow != NULL) {
	st_arrow = *start_arrow;
    } else {
	st_arrow.type = ARROW_NONE;
    }

    if (end_arrow != NULL) {
	e_arrow = *end_arrow;
    } else {
	e_arrow.type = ARROW_NONE;
    }

    fprintf(renderer->file, "{\n");
    set_fill_color(renderer, color);
    nat_arr = set_arrows(renderer, &st_arrow, &e_arrow);
    if (nat_arr != 0)
    {
      orig_draw_bezier_with_arrows(self, points, num_points, line_width, color, &st_arrow, &e_arrow);
    }
    fprintf(renderer->file, "}\n");
    if (nat_arr != 3)
    {
      orig_draw_bezier_with_arrows(self, points, num_points, line_width, color, NULL, NULL);
    }
}





/* Do we really want to do this?  What if the text is intended as 
 * TeX text?  Jacek says leave it as a TeX string.  TeX uses should know
 * how to escape stuff anyway.  Later versions will get an export option.
 *
 * Later (hb): given the UML issue bug #112377 an manually tweaking
 * is not possible as the # is added before anything a user can add. So IMO
 * we need to want this. If there later (much later?) is an export it probably
 * shouldn't produce broken output either ...
 */
static gchar *
tex_escape_string(const gchar *src)
{
    GString *dest = g_string_sized_new(g_utf8_strlen(src, -1));
    gchar *p;

    if (!g_utf8_validate(src, -1, NULL)) {
	message_error(_("Not valid UTF8"));
	return g_strdup(src);
    }

    p = (char *) src;
    while (*p != '\0') {
	switch (*p) {
	case '%': g_string_append(dest, "\\%"); break;
	case '#': g_string_append(dest, "\\#"); break;
	case '$': g_string_append(dest, "\\$"); break;
	case '&': g_string_append(dest, "\\&"); break;
	case '~': g_string_append(dest, "\\~{}"); break;
	case '_': g_string_append(dest, "\\_"); break;
	case '^': g_string_append(dest, "\\^{}"); break;
	case '\\': g_string_append(dest, "\\ensuremath{\\backslash}"); break;
	case '{': g_string_append(dest, "\\{"); break;
	case '}': g_string_append(dest, "\\}"); break;
	case '[': g_string_append(dest, "\\ensuremath{[}"); break;
	case ']': g_string_append(dest, "\\ensuremath{]}"); break;
	default: 
            /* if we really have utf8 append the whole 'glyph' */
            g_string_append_len(dest, p, g_utf8_skip[(unsigned char)*p]);
	}
        p = g_utf8_next_char(p);
    }

    p = dest->str;
    g_string_free(dest, FALSE);
    return p;
}

static void
draw_string(DiaRenderer *self,
	    const char *text,
	    Point *pos, Alignment alignment,
	    Color *color)
{
    PgfRenderer *renderer = PGF_RENDERER(self);
    gchar *escaped = tex_escape_string(text);
    gchar px_buf[DTOSTR_BUF_SIZE];
    gchar py_buf[DTOSTR_BUF_SIZE];

    set_line_color(renderer,color);

    fprintf(renderer->file,"\\node");
    switch (alignment) {
    case ALIGN_LEFT:
	fprintf(renderer->file,"[anchor=west]");
	break;
    case ALIGN_CENTER:
	break;
    case ALIGN_RIGHT:
	fprintf(renderer->file,"[anchor=east]");
	break;
    }
    fprintf(renderer->file," at (%s\\du,%s\\du){%s};\n",
	    pgf_dtostr(px_buf,pos->x),
	    pgf_dtostr(py_buf,pos->y),
	    escaped);
    g_free(escaped);
}

static void
draw_image(DiaRenderer *self,
	   Point *point,
	   real width, real height,
	   DiaImage *image)
{
    PgfRenderer *renderer = PGF_RENDERER(self);

    fprintf(renderer->file, "%% image rendering not supported");
}

/* --- export filter interface --- */
static void
export_pgf(DiagramData *data, const gchar *filename, 
                const gchar *diafilename, void* user_data)
{
    PgfRenderer *renderer;
    FILE *file;
    time_t time_now;
    double scale;
    Rectangle *extent;
    const char *name;
    /* gchar el_buf[DTOSTR_BUF_SIZE];
    gchar er_buf[DTOSTR_BUF_SIZE];
    gchar eb_buf[DTOSTR_BUF_SIZE];
    gchar et_buf[DTOSTR_BUF_SIZE]; */
    gchar scale1_buf[DTOSTR_BUF_SIZE];
    gchar scale2_buf[DTOSTR_BUF_SIZE];

    Color initial_color;
 
    file = g_fopen(filename, "wb");

    if (file==NULL) {
	message_error(_("Can't open output file %s: %s\n"), 
		      dia_message_filename(filename), strerror(errno));
    }

    renderer = g_object_new(PGF_TYPE_RENDERER, NULL);

    renderer->pagenum = 1;
    renderer->file = file;

    renderer->dash_length = 1.0;
    renderer->dot_length = 0.2;
    renderer->saved_line_style = LINESTYLE_SOLID;
  
    time_now  = time(NULL);
    extent = &data->extents;
  
    scale = POINTS_in_INCH * data->paper.scaling;
  
    name = g_get_user_name();
  
    fprintf(file,
	"%% Graphic for TeX using PGF\n"
	"%% Title: %s\n"
	"%% Creator: Dia v%s\n"
	"%% CreationDate: %s"
	"%% For: %s\n"
	"%% \\usepackage{tikz}\n"
	"%% The following commands are not supported in PSTricks at present\n"
	"%% We define them conditionally, so when they are implemented,\n"
	"%% this pgf file will use them.\n"
	
	"\\ifx\\du\\undefined\n"
  	"  \\newlength{\\du}\n"
	"\\fi\n"
	"\\setlength{\\du}{15\\unitlength}\n"
	"\\begin{tikzpicture}\n",
	diafilename,
	VERSION,
	ctime(&time_now),
	name);

/*    fprintf(renderer->file,"\\pspicture(%s,%s)(%s,%s)\n",
	    pgf_dtostr(el_buf,extent->left * data->paper.scaling),
	    pgf_dtostr(eb_buf,-extent->bottom * data->paper.scaling),
	    pgf_dtostr(er_buf,extent->right * data->paper.scaling),
	    pgf_dtostr(et_buf,-extent->top * data->paper.scaling) );
*/
    fprintf(renderer->file,"\\pgftransformxscale{%s}\n"
	                   "\\pgftransformyscale{%s}\n",
	    pgf_dtostr(scale1_buf,data->paper.scaling),
	    pgf_dtostr(scale2_buf,-data->paper.scaling) );

    initial_color.red=0.;
    initial_color.green=0.;
    initial_color.blue=0.;
    set_line_color(renderer,&initial_color);

    initial_color.red=1.;
    initial_color.green=1.;
    initial_color.blue=1.;
    set_fill_color(renderer,&initial_color);

    data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);

    g_object_unref(renderer);
}

static const gchar *extensions[] = { "tex", NULL };
DiaExportFilter pgf_export_filter = {
    N_("LaTeX PGF macros"),
    extensions,
    export_pgf,
	NULL,
	"pgf-tex"
};
