Hello !

I did ask yesterday an export for PGF from XFig. I actually did look into it, and I came with an alpha genpgf.c which produces output good enough to please me.

Would you be interested to actually include it in XFig, provided that I clean a bit more the code and that I add some features missing for now ?

I join the code as an attachement. Hope that it will be of help to anyone !

By the way, I implemented some code so that it would be really easy to make beamer overlays by simply writing some comments on the objects.

It comes under sense that in the case you (or the upstream maintainers) would want to actually include this export in XFIG distribution, I would be glad to provide all the documentations and cleaner code ;-) !

 Thanks for considering this !

Vincent Fourmond
/*
 * TransFig: Facility for Translating Fig code
 * Copyright (c) 1991 by Micah Beck
 * Parts Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2002 by Brian V. Smith
 * Parts Copyright (c) 2005 by Vincent Fourmond
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and
 * documentation files (the "Software"), including without limitation the
 * rights to use, copy, modify, merge, publish and/or distribute copies of
 * the Software, and to permit persons who receive copies from any such 
 * party to do so, with the only requirement being that this copyright 
 * notice remain intact.
 *
 */

/* 
 *	genpgf.c : Pgf driver for fig2dev
 *
 *	Author: Vincent Fourmond, 2005
 *
 */

#include "fig2dev.h"
#include "object.h"
#include "genpgf.h"
#include "setfigfont.h"
#include "texfonts.h"

extern void unpsfont();
extern Boolean	FontSizeOnly;	/* defined in setfigfont.c */

/* PGF for beamer specific : */
static int beamer = 0; /* things for beamer */



extern char *ISO1toTeX[];
extern char *ISO2toTeX[];

/* To be more verbose */
static int verbose = 0;


static int xSize = 0;
static int ySize = 0;
static int tmp;

#define SWAP(a,b) { int tmp = a; a = b; b = tmp;}
/* this macro converts a from fig to pgf dimensions */
#define F2P(a) ((double) ((a) * mag * 72.0/(ppi*1.0)))

/* this macro puts in x and y the coords of the
   point p */

#define PREP_COORDS(p) x = F2P(p->x - llx);y = F2P(ury - p->y);
#define PREP_COORDS2(a,b) x = F2P(a - llx);y = F2P(ury - b);

/* basic element of size...*/
#define PGFSize "%2.2fpt"



void
genpgf_option(opt, optarg)
  char opt, *optarg;
{
    int i;
    FontSizeOnly = False;

    switch (opt) {
    case 'v':
      verbose = 1;		/* verbose mode */
      break;
	  
    case 'o':
      beamer = 1;
      break;
    case 'L':
    case 'm':
    case 's':
      break;
      
    default:
      put_msg(Err_badarg, opt, "pgf");
      exit(1);
    }
}

void
genpgf_start(objects)
     F_compound	*objects;
{
  fprintf(tfp, "%% %d %d %d %d\n",
	  llx, lly, urx, ury);
  
  if (llx > urx) SWAP(llx,urx);
  if (lly > ury) SWAP(lly,ury);

  /* we initialize the sizes */
  xSize = urx - llx;
  ySize = ury - lly;
  
  /* Pgf start */
  
  /* print any whole-figure comments prefixed with "%" */
  if (objects->comments) {
    fprintf(tfp,"%%\n");
    print_comments("% ",objects->comments, "");
    fprintf(tfp,"%%\n");
  }
  
  /* define the SetFigFont macro */
  define_setfigfont(tfp);
  fprintf(tfp, "\\begin{pgfpicture}{0pt}{0pt}{"
	  PGFSize"}{"PGFSize"}\n",
	  F2P(xSize),
	  F2P(ySize));
}

int
genpgf_end()
{
  /* Pgf ending */
  fprintf(tfp, "\\end{pgfpicture}%%\n");
  
  /* all ok */
  return 0;
}

/* macro to compute line thickness */
#define L2P(a) ((double) ((a) * mag * 72.0/(ppi*2.3)))
/* 2.2 is a random value... */

static void
set_linewidth(w,fill_style)
     int	w;
{
  fprintf(tfp, "\\pgfsetlinewidth{"
	  PGFSize"}\n",
	  L2P(w));

}

/* macro for dash length */
#define D2P(a) ((double) ((a) * mag * 72.0* 15/(ppi*1.0)))
/* once again, the value 15 is based on nothing real */

static void 
set_linestyle(style,arg,w)
     int style, w;
     double arg;
{
  switch(style) {
  case SOLID_LINE:
    break; /* nothing to do */
  case DASH_LINE:
    /* we setup the dash : */
    fprintf(tfp, "\\pgfsetdash{{"PGFSize"}{"PGFSize"}}{0pt}\n",
	    D2P(arg),D2P(arg));
    break;
  case DOTTED_LINE:
    /* we setup the dots : line thickness and then spacing according
       to D2P */
    fprintf(tfp, "\\pgfsetdash{{"PGFSize"}{"PGFSize"}}{0pt}\n",
 	    L2P(w),D2P(arg));
    break;
  default:
    fprintf(stderr,"Line style not yet supported\n");
  }
}

static char *colors[] = {
  "0,0,0",    /* black */
  "0,0,1",    /* blue */
  "0,1,0",    /* green */
  "0,1,1",    /* cyan */
  "1,0,0",    /* red */
  "1,0,1",    /* magenta */
  "1,1,0",    /* yellow */
  "1,1,1",    /* white */
  "0,0,.56",	/* dk blue */
  "0,0,.69",	/* md blue */
  "0,0,.82",	/* lt blue */
  ".53,.81,1",	/* blue4 */
  "0,.56,0",	/* dk green */
  "0,.69,0",	/* md green */
  "0,.82,0",	/* lt green */
  "0,.56,.56",	/* dk cyan */
  "0,.69,.69",	/* md cyan */
  "0,.82,.82",	/* lt cyan */
  ".56,0,0",	/* dk red */
  ".69,0,0",	/* md red */
  ".82,0,0",	/* lt red */
  ".56,0,.56",	/* dk magenta */
  ".69,0,.69",	/* md magenta */
  ".82,0,.82",	/* lt magenta */
  ".5,.17,0",	/* dk brown */
  ".63,.25,0",	/* md brown1 */
  ".75,.38,0",	/* md brown1 */
  "1,.5,.5",	/* dk pink */
  "1,.63,.63",	/* md pink1 */
  "1,.75,.75",	/* md pink2 */
  "1,.88,.88",	/* lt pink */
  "1,.84,0",	/* gold */
};

static void set_color(col)
     int col;
{
   if (col != -1) {
     if (col < NUM_STD_COLS)
       fprintf(tfp, "\\color[rgb]{%s}%%\n",colors[col]);
     else
       fprintf(tfp, "\\color[rgb]{%.3f,%.3f,%.3f}%%\n",
	       user_colors[col-NUM_STD_COLS].r/255.0,
	       user_colors[col-NUM_STD_COLS].g/255.0,
	       user_colors[col-NUM_STD_COLS].b/255.0);
   }
}


/* this is to setup all the attributes  of a path...*/
static void setup() {
  /* we will enclose all the objects in
     a pgfscope, to avoid that they tread on each
     others feet
  */
  fprintf(tfp, "\\begin{pgfscope}\n");

  return;
}


static void end()
{
  fprintf(tfp, "\\end{pgfscope}\n");
}

/* this looks into the comments
   and issue beamer-specific commands if we have to...
   we enclose the specific part of the draw inside this
*/

#define BEAM "%%BEAMER"
   
static void prepareDraw(comments)
     F_comment * comments;
{
  if(beamer) {
    const char * str;
    const char * str2;
    while(comments) {
      str = strstr(comments->comment, BEAM);
      if(str) {
	if(str2 = strstr(str,"only")) 
	  fprintf(tfp, "\\%s{",
		  str+strlen(BEAM));
	else 
	  fprintf(tfp, "\\uncover%s{",
		  str+strlen(BEAM));
	return;
      }
      comments = comments->next;
    }
  }
  fprintf(tfp,"{");
} 

static void endDraw() {
    fprintf(tfp,"}\n");
}


/* a basic macro for drawing. It has to be a macro
   because even if the members always have the same name
   they dont always have the same position in the compounds */

#define pgfDrawPath(x) drawPath(x->comments,\
				x->pen_color,\
				x->fill_color,\
				x->fill_style,\
				x->thickness,\
				x->style,\
				x->style_val);
				



/* draw the content of the current path */
static void drawPath(comments,pen_color,fill_color, 
		     fill_style,thickness,
		     style, style_val)
     F_comment * comments;
     int pen_color,fill_color,fill_style,thickness,style;
     double style_val;
{
  prepareDraw(comments);
  /* we first fill that the lines are on top !! */
  switch(fill_style) {
  case WHITE_FILL: 
    set_color(pen_color);
    fprintf(tfp,"\\path\\pgffill{}");
    break;
  case BLACK_FILL: 
    set_color(fill_color);
    fprintf(tfp,"\\path\\pgffill{}");
    set_color(pen_color);
    break;
  default:
    fprintf(stderr,"Unsupported fill type : %d\n",
	    fill_style);
  case UNFILLED: 
    /* we initialize color */
    set_color(pen_color);
  }
  set_linewidth(thickness);
  set_linestyle(style, style_val,thickness);
  fprintf(tfp,"\\path\\pgfstroke{}");
  endDraw();
}

void
genpgf_line(l)
  F_line	*l;
{
  F_point		*p, *q;
  double x,y;
  int arrow;
  p = l->points;
  q = p->next;
  setup();
  
  /* here, some fancy stuff about putting
     the right attributes... */
  
  /* moveto the right position */

  /* we create a new path, we put in into \path */
  fprintf(tfp, "\\def\\path{");
  
  PREP_COORDS(p);
  fprintf(tfp,"\\pgfmoveto{\\pgfpoint{"PGFSize
	  "}{"PGFSize"}}\n",
	  x,y);
  
  while (q) {
    p = q;
    q = q->next;
    PREP_COORDS(p)
    fprintf(tfp,"\\pgflineto{\\pgfpoint{"PGFSize
	    "}{"PGFSize"}}\n",x,y);
  }
  if(l->type == T_POLYGON ||
     l->type == T_BOX) /* we need to close the path then */
    fprintf(tfp,"\\pgfclosepath{}\n");
  fprintf(tfp,"}%%\\def\\path\n");

  pgfDrawPath(l);
  end();
}

void
genpgf_spline(s)
  F_spline	*s;
{
  F_point		*p, *q;
  F_control *c;
  double x,y;
  int arrow;
  p = s->points;
  q = p->next;

  c = s->controls;
  fprintf(stderr,"Spline - %n\n",c);
  while(c) {
    fprintf(stderr,"%f %f %f %f %f\n",
	    c->lx, c->ly, c->rx, c->ry,c->s);
    c = c->next;
  }

  setup();
  
  /* here, some fancy stuff about putting
     the right attributes... */
  
  fprintf(tfp, "\\def\\path{");
  /* moveto the right position */
  PREP_COORDS(p);
  fprintf(tfp,"\\pgfmoveto{\\pgfpoint{"PGFSize
	  "}{"PGFSize"}}\n",
	  x,y);
  
  /* ça marche tel quel, on ne va pas s'embêter... */
  while (q->next) {
    p = q;
    q = q->next;
    PREP_COORDS(p)
    fprintf(tfp,"\\pgflineto{\\pgfpoint{"PGFSize
	    "}{"PGFSize"}}\n",x,y);
  }
  if(s->type % 2) /* we need to close the path then */
    fprintf(tfp,"\\pgfclosepath{}\n");
  fprintf(tfp,"}%\\def\\path\n");
  
  pgfDrawPath(s);
  end();

}

void
genpgf_ellipse(e)
  F_ellipse	*e;
{
  double x,y;
  setup();

  /* we need to translate the coords for the ellipse to
     be well-centered: */
  
  PREP_COORDS((&(e->center)));
  fprintf(tfp,"\\begin{pgftranslate}{\\pgfpoint{"PGFSize
	  "}{"PGFSize"}}",	  x,y);

  /* prepare the \path macro */

  fprintf(tfp, "\\def\\path{");
  if ((e->type == T_CIRCLE_BY_RAD || e->type == T_CIRCLE_BY_DIA))
    /* we draw a circle */
    fprintf(tfp,"\\pgfzerocircle{"PGFSize"}",F2P(e->radiuses.x));
  else 
    fprintf(tfp,"\\pgfzeroellipse{\\pgfpoint{"PGFSize"}{0pt}}"
	    "{\\pgfpoint{0pt}{"PGFSize"}}", 
	    F2P(e->radiuses.x),F2P(e->radiuses.y));
  /* then we close the def */
  fprintf(tfp, "}%%\n");
  
  /* we draw the actual object */
  pgfDrawPath(e);
  /* end of the translated environment */
  fprintf(tfp,"\\end{pgftranslate}\n");
  end();
}

void
genpgf_text(t)
  F_text	*t;
{
  double   	x, y;
  char	*tpos;
  unsigned char	*cp;

  /* this code has been copied as is from genlatex.c, with only small changes */
  
  if (verbose) 
    fprintf(tfp, "%%\n%% Fig TEXT object\n%%\n");
  
  /* print any comments prefixed with "%" */
  print_comments("% ",t->comments, "");

  PREP_COORDS2(t->base_x,t->base_y);
	      

  switch (t->type) {
    
  case T_LEFT_JUSTIFIED:
  case DEFAULT:
    tpos = "[lb]";
    break;
    
  case T_CENTER_JUSTIFIED:
    tpos = "[b]";
    break;
    
  case T_RIGHT_JUSTIFIED:
    tpos = "[rb]";
    break;
    
  default:
    fprintf(stderr, "Text incorrectly positioned\n");
    tpos = "[lb]";	/* make left in this case */
  }
  
  /* smash is used to position text at baseline */
  unpsfont(t);

  setup();
  fprintf(tfp, "\\pgfputat{\\pgfpoint{"
	  PGFSize"}{"PGFSize"}}{\\pgfbox[center,center]{", 
	  x, y);
  
  fprintf(tfp, "\\makebox(0,0)%s{\\smash{", tpos);
  
  setfigfont( t );	/* in genepic.c */
  set_color(t->color);
  
  if (!special_text(t))
    
    /* this loop escapes characters "$&%#_{}" */
    /* and deleted characters "~^\" */
    for(cp = (unsigned char*)t->cstring; *cp; cp++) {
      if (strchr("$&%#_{}", *cp)) 
	fputc('\\', tfp);
      if (strchr("~^\\", *cp))
	fprintf(stderr,
		"Bad character in text object '%c'\n" ,*cp);
      else
	fputc(*cp, tfp);
    }
  else 
    for(cp = (unsigned char*)t->cstring; *cp; cp++) {
#ifdef I18N
      extern Boolean support_i18n;
      if (support_i18n && (t->font <= 2))
	fputc(*cp, tfp);
      else
#endif
	if (*cp >= 0xa0) {
	  fputc(*cp, tfp);
	} else
	  fputc(*cp, tfp);
    }
  
  fprintf(tfp, "}}}}}\n");
  end();
}

void
genpgf_arc(a)
  F_arc	*a;
/*
 *  Approximates an arc by a sequence of quarter ovals.
 *
 *  Example:
 *
 *	Arc with center at (0,0) and radius 10 from +45 degree to +225 degree
 *	(arc from p1 = (7.07, 7.07) to p2 = (-7.07, -7.07) counterclockwise).
 *	This arc is approximated by three quarter ovals, one for each quadrant
 *	through which the arc goes:
 *
 *	 1. quarter oval from p1 to the intersection of arc and y-axis,
 *	    i.e., from (7.07, 7.07) to (0, 10) in quadrant 0
 *
 *		\put(0, 7.07){\oval(14.14, 5.86)[tr]}
 *
 *	 2. quarter oval from intersection arc/y-axis to intersection arc/x-axis
 *	    i.e., from (0, 10) to (-10, 0) in quadrant 1
 *
 *		\put(0, 0){\oval(20,20)[tl]}
 *
 *	 3. quarter oval from p1 to the intersection of arc and y-axis,
 *	    i.e., from (-10, 0) to (-7.07, -7.07) in quadrant 2
 *
 *		\put(-7.07, 0){\oval(5.86, 14.14)[bl]}
 */
{
  fprintf(stderr, "pgf: Arcs are not supported yet\n");
}

#define  MAXCOLORS 32

/* need this for communication between color routines. Sorry */
static int lastcolor=-1;

void
pfg_set_color(col)
int col;
{
   static char *colors[] = {
   "0,0,0",    /* black */
   "0,0,1",    /* blue */
   "0,1,0",    /* green */
   "0,1,1",    /* cyan */
   "1,0,0",    /* red */
   "1,0,1",    /* magenta */
   "1,1,0",    /* yellow */
   "1,1,1",    /* white */
   "0,0,.56",	/* dk blue */
   "0,0,.69",	/* md blue */
   "0,0,.82",	/* lt blue */
   ".53,.81,1",	/* blue4 */
   "0,.56,0",	/* dk green */
   "0,.69,0",	/* md green */
   "0,.82,0",	/* lt green */
   "0,.56,.56",	/* dk cyan */
   "0,.69,.69",	/* md cyan */
   "0,.82,.82",	/* lt cyan */
   ".56,0,0",	/* dk red */
   ".69,0,0",	/* md red */
   ".82,0,0",	/* lt red */
   ".56,0,.56",	/* dk magenta */
   ".69,0,.69",	/* md magenta */
   ".82,0,.82",	/* lt magenta */
   ".5,.17,0",	/* dk brown */
   ".63,.25,0",	/* md brown1 */
   ".75,.38,0",	/* md brown1 */
   "1,.5,.5",	/* dk pink */
   "1,.63,.63",	/* md pink1 */
   "1,.75,.75",	/* md pink2 */
   "1,.88,.88",	/* lt pink */
   "1,.84,0",	/* gold */
   };
   

#ifdef DVIPS
   if (col != -1) {
       /* we do not support nested colors, although Pgf would */
       if (lastcolor == -1)
	   fprintf(tfp, "{");
       /* switch to (other) color */
	if (col < NUM_STD_COLS)
	    fprintf(tfp, "\\color[rgb]{%s}",colors[col]);
	else
	    fprintf(tfp, "\\color[rgb]{%.3f,%.3f,%.3f}",
				user_colors[col-NUM_STD_COLS].r/255.0,
				user_colors[col-NUM_STD_COLS].g/255.0,
				user_colors[col-NUM_STD_COLS].b/255.0);
       lastcolor = col;
   }
#endif
   return;
}

static void
reset_color(col)
int col;
{
#ifdef DVIPS
    if (col != -1 && col < NUM_STD_COLS + MAX_USR_COLS) {
       /* end using the last color */
       fprintf(tfp, "}%%\n");
    }
    /* make sure the color indicator is reset */
    lastcolor = -1;
#endif
   return;
}


struct driver dev_pgf = {
     	genpgf_option,
	genpgf_start,
	gendev_null,
	genpgf_arc,
	genpgf_ellipse,
	genpgf_line,
	genpgf_spline,
	genpgf_text,
	genpgf_end,
	EXCLUDE_TEXT
};

Reply via email to