]> granicus.if.org Git - graphviz/commitdiff
Extend xdot output to allow graphical attributes. This is necessary because
authorerg <devnull@localhost>
Thu, 14 Apr 2005 21:13:33 +0000 (21:13 +0000)
committererg <devnull@localhost>
Thu, 14 Apr 2005 21:13:33 +0000 (21:13 +0000)
colors, fonts, etc. can change within node or edge label.

cmd/dotty/dotty_layout.lefty
lib/common/const.h
lib/common/emit.c
lib/common/output.c
lib/common/renderprocs.h
lib/common/shapes.c
lib/common/utils.c
lib/common/xdgen.c

index ded10d5019270ff9ec5da84e9dc36ce788ce8efa..8e04dad209d45649248e80ed79df92b337fa5bd2 100644 (file)
@@ -338,6 +338,18 @@ dotty.protogt.unpackdraw = function (attr) {
             for (j = 1; j < l; j = j + 1)
                 s = concat (s, tt[j]);
             o.s = s;
+        } else if (t[i] == 'C') {
+            i = i + 3;
+            continue;
+        } else if (t[i] == 'c') {
+            i = i + 3;
+            continue;
+        } else if (t[i] == 'F') {
+            i = i + 4;
+            continue;
+        } else if (t[i] == 'S') {
+            i = i + 3;
+            continue;
         } else {
             dotty.message (0, concat ('draw language parser error: ', t[i]));
             return null;
index 193c8318266be37bd7803b9aaf052068b5613762..5f52d8e53ed8971772ce6d3a7d0aeb3aaa98e09c 100644 (file)
 #define RANKDIR_BT     2
 #define RANKDIR_RL     3
 
+/* values specifying emit state */
+#define EMIT_DRAW      0
+#define EMIT_LABEL     1
+#define EMIT_TDRAW     2
+#define EMIT_HDRAW     3
+#define EMIT_TLABEL    4
+#define EMIT_HLABEL    5
+
 /* user-specified node position: ND_pinned */
 #define P_SET    1             /* position supplied by user */
 #define P_FIX    2             /* position fixed during topological layout */
index cba1dda907753375be54a2a42c2ad3d7c68a32ea..5ff5d3ddf2d2cb02a76de49217220044a4f0830f 100644 (file)
@@ -25,6 +25,7 @@
 #include       "gvc.h"
 
 static char *defaultlinestyle[3] = { "solid\0", "setlinewidth\0001\0", 0 };
+int    emitState;
 
 /* parse_layers:
  * Split input string into tokens, with separators specified by
@@ -363,7 +364,7 @@ static boolean write_node_test(Agraph_t * g, Agnode_t * n)
     return TRUE;
 }
 
-static void emit_background(GVC_t * gvc, graph_t *g)
+void emit_background(GVC_t * gvc, graph_t *g)
 {
     gvrender_job_t * job = gvc->job;
     char *str;
@@ -721,7 +722,7 @@ static boolean edge_in_pageBox(GVC_t *gvc, edge_t * e)
     return boxf_overlap(job->pageBox, b);
 }
 
-static void emit_edge(GVC_t * gvc, edge_t * e)
+void emit_edge_graphics(GVC_t * gvc, edge_t * e)
 {
     int i, j, cnum, numc = 0;
     char *color, *style;
@@ -735,35 +736,10 @@ static void emit_edge(GVC_t * gvc, edge_t * e)
     boolean saved = FALSE;
     double scale, numc2;
     char *p;
-    char *s, *url = NULL, *label = NULL, *tooltip = NULL, *target = NULL;
-    textlabel_t *lab = NULL;
+    extern int xdemitState;
 
 #define SEP 2.0
 
-#if 0
-fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
-        edge_in_layer(gvc, e->head->graph, e)?"true":"false",
-        edge_in_pageBox(gvc, e)?"true":"false");
-#endif
-
-    if (! edge_in_pageBox(gvc, e) || ! edge_in_layer(gvc, e->head->graph, e))
-       return;
-
-    gvrender_begin_edge(gvc, e);
-    if (((s = agget(e, "href")) && s[0])
-       || ((s = agget(e, "URL")) && s[0])) {
-       url = strdup_and_subst_edge(s, e);
-       if ((lab = ED_label(e))) {
-           label = lab->text;
-       }
-       if ((s = agget(e, "tooltip")) && s[0])
-           tooltip = strdup_and_subst_edge(s, e);
-       else if (label)
-           tooltip = strdup_and_subst_edge(label, e);
-       if ((s = agget(e, "target")) && s[0])
-           target = strdup_and_subst_edge(s, e);
-       gvrender_begin_anchor(gvc, url, tooltip, target);
-    }
     style = late_string(e, E_style, "");
     /* We shortcircuit drawing an invisible edge because the arrowhead
      * code resets the style to solid, and most of the code generators
@@ -779,6 +755,7 @@ fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
            }
        }
     }
+    xdemitState = EMIT_DRAW;
     if (ED_spl(e)) {
        scale = late_double(e, E_arrowsz, 1.0, 0.0);
        color = late_string(e, E_color, "");
@@ -855,8 +832,10 @@ fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
                                         FALSE, FALSE);
                }
            }
+           xdemitState = EMIT_TDRAW;
            if (bz.sflag)
                arrow_gen(gvc, bz.sp, bz.list[0], scale, bz.sflag);
+           xdemitState = EMIT_HDRAW;
            if (bz.eflag)
                arrow_gen(gvc, bz.ep, bz.list[bz.size - 1], scale,
                          bz.eflag);
@@ -887,8 +866,10 @@ fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
                } else {
                    gvrender_beziercurve(gvc, bzf.list, bz.size, FALSE,
                                         FALSE);
+                   xdemitState = EMIT_TDRAW;
                    if (bz.sflag)
                        arrow_gen(gvc, bz.sp, bz.list[0], scale, bz.sflag);
+                   xdemitState = EMIT_HDRAW;
                    if (bz.eflag)
                        arrow_gen(gvc, bz.ep, bz.list[bz.size - 1], scale,
                                  bz.eflag);
@@ -897,18 +878,53 @@ fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
            }
        }
     }
+    xdemitState = EMIT_LABEL;
     if (ED_label(e)) {
        emit_label(gvc, ED_label(e), (void *) e);
        if (mapbool(late_string(e, E_decorate, "false")) && ED_spl(e))
            emit_attachment(gvc, ED_label(e), ED_spl(e));
     }
+    xdemitState = EMIT_HLABEL;
     if (ED_head_label(e))
        emit_label(gvc, ED_head_label(e), (void *) e);  /* vladimir */
+    xdemitState = EMIT_TLABEL;
     if (ED_tail_label(e))
        emit_label(gvc, ED_tail_label(e), (void *) e);  /* vladimir */
 
     if (saved)
        gvrender_end_context(gvc);
+}
+
+static void emit_edge(GVC_t * gvc, edge_t * e)
+{
+    char *s, *url = NULL, *label = NULL, *tooltip = NULL, *target = NULL;
+    textlabel_t *lab = NULL;
+
+#if 0
+fprintf(stderr,"edge_in_layer %s edge_in_pageBox %s\n",
+        edge_in_layer(gvc, e->head->graph, e)?"true":"false",
+        edge_in_pageBox(gvc, e)?"true":"false");
+#endif
+
+    if (! edge_in_pageBox(gvc, e) || ! edge_in_layer(gvc, e->head->graph, e))
+       return;
+
+    gvrender_begin_edge(gvc, e);
+    if (((s = agget(e, "href")) && s[0])
+       || ((s = agget(e, "URL")) && s[0])) {
+       url = strdup_and_subst_edge(s, e);
+       if ((lab = ED_label(e))) {
+           label = lab->text;
+       }
+       if ((s = agget(e, "tooltip")) && s[0])
+           tooltip = strdup_and_subst_edge(s, e);
+       else if (label)
+           tooltip = strdup_and_subst_edge(label, e);
+       if ((s = agget(e, "target")) && s[0])
+           target = strdup_and_subst_edge(s, e);
+       gvrender_begin_anchor(gvc, url, tooltip, target);
+    }
+    emit_edge_graphics (gvc, e);
     if (url) {
        gvrender_end_anchor(gvc);
        free(url);
@@ -1329,6 +1345,7 @@ void emit_clusters(GVC_t * gvc, Agraph_t * g, int flags)
     node_t *n;
     edge_t *e;
     char *s, *url = NULL, *tooltip = NULL, *target = NULL;
+    extern int xdemitState;
 
     for (c = 1; c <= GD_n_cluster(g); c++) {
        sg = GD_clust(g)[c];
@@ -1353,6 +1370,7 @@ void emit_clusters(GVC_t * gvc, Agraph_t * g, int flags)
        }
        gvrender_begin_context(gvc);
        filled = FALSE;
+       xdemitState = EMIT_DRAW;
        if (((str = agget(sg, "style")) != 0) && str[0]) {
            gvrender_set_style(gvc, (style = parse_style(str)));
            for (i = 0; style[i]; i++)
@@ -1391,6 +1409,7 @@ void emit_clusters(GVC_t * gvc, Agraph_t * g, int flags)
            gvrender_set_pencolor(gvc, str);
            gvrender_polygon(gvc, A, 4, filled);
        }
+       xdemitState = EMIT_DRAW;
        if (GD_label(sg))
            emit_label(gvc, GD_label(sg), (void *) sg);
 
index 1896b00a309b615500f2b13d88df8733c29ab097..11468d69c5cac1aca0eeadf1057c8e15dd3c6414 100644 (file)
@@ -20,8 +20,6 @@
 
 static int e_arrows;           /* graph has edges with end arrows */
 static int s_arrows;           /* graph has edges with start arrows */
-agxbuf outbuf;
-agxbuf charbuf;
 
 static void printptf(FILE * f, point pt)
 {
@@ -140,158 +138,11 @@ void write_plain_ext(GVC_t * gvc, graph_t * g, FILE * f)
     _write_plain(gvc, g, f, TRUE);
 }
 
-static attrsym_t *safe_dcl(graph_t * g, void *obj, char *name, char *def,
-                          attrsym_t * (*fun) (Agraph_t *, char *, char *))
-{
-    attrsym_t *a = agfindattr(obj, name);
-    if (a == NULL)
-       a = fun(g, name, def);
-    return a;
-}
-
-static int isInvis(char *style)
-{
-    char **styles = 0;
-    char **sp;
-    char *p;
-
-    if (style[0]) {
-       styles = parse_style(style);
-       sp = styles;
-       while ((p = *sp++)) {
-           if (streq(p, "invis"))
-               return 1;
-       }
-    }
-    return 0;
-}
-
-/* 
- * John M. suggests:
- * You might want to add four more:
- *
- * _ohdraw_ (optional head-end arrow for edges)
- * _ohldraw_ (optional head-end label for edges)
- * _otdraw_ (optional tail-end arrow for edges)
- * _otldraw_ (optional tail-end label for edges)
- * 
- * that would be generated when an additional option is supplied to 
- * dot, etc. and 
- * these would be the arrow/label positions to use if a user want to flip the 
- * direction of an edge (as sometimes is there want).
- */
-static void extend_attrs(GVC_t * gvc, graph_t *g)
-{
-    int i, j;
-    bezier bz = { 0, 0, 0, 0 };
-    bezierf bzf;
-    double scale;
-    node_t *n;
-    edge_t *e;
-    attrsym_t *n_draw = NULL;
-    attrsym_t *n_l_draw = NULL;
-    attrsym_t *e_draw = NULL;
-    attrsym_t *h_draw = NULL;
-    attrsym_t *t_draw = NULL;
-    attrsym_t *e_l_draw = NULL;
-    attrsym_t *hl_draw = NULL;
-    attrsym_t *tl_draw = NULL;
-    unsigned char buf[BUFSIZ];
-    unsigned char cbuf[BUFSIZ];
-
-    if (GD_has_labels(g) & GRAPH_LABEL)
-       g_l_draw = safe_dcl(g, g, "_ldraw_", "", agraphattr);
-    if (GD_n_cluster(g))
-       g_draw = safe_dcl(g, g, "_draw_", "", agraphattr);
-
-    n_draw = safe_dcl(g, g->proto->n, "_draw_", "", agnodeattr);
-    n_l_draw = safe_dcl(g, g->proto->n, "_ldraw_", "", agnodeattr);
-
-    e_draw = safe_dcl(g, g->proto->e, "_draw_", "", agedgeattr);
-    if (e_arrows)
-       h_draw = safe_dcl(g, g->proto->e, "_hdraw_", "", agedgeattr);
-    if (s_arrows)
-       t_draw = safe_dcl(g, g->proto->e, "_tdraw_", "", agedgeattr);
-    if (GD_has_labels(g) & EDGE_LABEL)
-       e_l_draw = safe_dcl(g, g->proto->e, "_ldraw_", "", agedgeattr);
-    if (GD_has_labels(g) & HEAD_LABEL)
-       hl_draw = safe_dcl(g, g->proto->e, "_hldraw_", "", agedgeattr);
-    if (GD_has_labels(g) & TAIL_LABEL)
-       tl_draw = safe_dcl(g, g->proto->e, "_tldraw_", "", agedgeattr);
-
-    agxbinit(&outbuf, BUFSIZ, buf);
-    agxbinit(&charbuf, BUFSIZ, cbuf);
-    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
-       if (ND_shape(n) && !isInvis(late_string(n, N_style, ""))) {
-           ND_shape(n)->fns->codefn(gvc, n);
-           agxset(n, n_draw->index, agxbuse(&outbuf));
-           agxset(n, n_l_draw->index, agxbuse(&charbuf));
-       }
-       if (State < GVSPLINES)
-           continue;
-       for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
-           if (ED_edge_type(e) == IGNORED)
-               continue;
-           if (isInvis(late_string(e, E_style, "")))
-               continue;
-           if (ED_spl(e) == NULL)
-               continue;
-
-           scale = late_double(e, E_arrowsz, 1.0, 0.0);
-           for (i = 0; i < ED_spl(e)->size; i++) {
-               bz = ED_spl(e)->list[i];
-               /* convert points to pointf for gvrender api */
-               bzf.size = bz.size;
-               bzf.list = malloc(sizeof(pointf) * bzf.size);
-               for (j = 0; j < bz.size; j++)
-                    P2PF(bz.list[j], bzf.list[j]);
-                gvrender_beziercurve(gvc, bzf.list, bz.size, FALSE, FALSE);
-               free(bzf.list);
-           }
-           agxset(e, e_draw->index, agxbuse(&outbuf));
-           for (i = 0; i < ED_spl(e)->size; i++) {
-               if (bz.sflag) {
-                   arrow_gen(gvc, bz.sp, bz.list[0], scale, bz.sflag);
-                   agxset(e, t_draw->index, agxbuse(&outbuf));
-               }
-               if (bz.eflag) {
-                   arrow_gen(gvc, bz.ep, bz.list[bz.size - 1], scale,
-                             bz.eflag);
-                   agxset(e, h_draw->index, agxbuse(&outbuf));
-               }
-           }
-           if (ED_label(e)) {
-               emit_label(gvc, ED_label(e), (void *) e);
-               if (mapbool(late_string(e, E_decorate, "false"))
-                   && ED_spl(e)) {
-                   emit_attachment(gvc, ED_label(e), ED_spl(e));
-                   agxbput(&charbuf, agxbuse(&outbuf));
-               }
-               agxset(e, e_l_draw->index, agxbuse(&charbuf));
-           }
-           if (ED_head_label(e)) {
-               emit_label(gvc, ED_head_label(e), (void *) e);
-               agxset(e, hl_draw->index, agxbuse(&charbuf));
-           }
-           if (ED_tail_label(e)) {
-               emit_label(gvc, ED_tail_label(e), (void *) e);
-               agxset(e, tl_draw->index, agxbuse(&charbuf));
-           }
-       }
-    }
-    if (GD_label(g)) {
-       emit_label(gvc, GD_label(g), (void *) g);
-       agxset(g, g_l_draw->index, agxbuse(&charbuf));
-    }
-    emit_clusters(gvc, g, 0);
-    agxbfree(&outbuf);
-    agxbfree(&charbuf);
-}
 
 void write_extended_dot(GVC_t *gvc, graph_t *g, FILE *f)
 {
        attach_attrs(g);
-       extend_attrs(gvc, g);
+       extend_attrs(gvc, g, s_arrows, e_arrows);
        agwrite(g, f);
 }
 
index 040efc71d00ec6d4944baf9c8eecd4914f168b3d..159eaecbf5565c71af11cd44afa2367d7d8c5f94 100644 (file)
@@ -71,7 +71,9 @@ extern "C" {
     extern void enqueue(queue *, Agnode_t *);
     extern void enqueue_neighbors(queue *, Agnode_t *, int);
     extern void emit_attachment(GVC_t * gvc, textlabel_t *, splines *);
+    extern void emit_background(GVC_t * gvc, graph_t *g);
     extern void emit_clusters(GVC_t * gvc, Agraph_t * g, int flags);
+    extern void emit_edge_graphics(GVC_t * gvc, edge_t * e);
     extern void emit_graph(GVC_t * gvc, graph_t * g);
     extern void emit_label(GVC_t * gvc, textlabel_t *, void *obj);
     extern int emit_once(char *message);
@@ -83,6 +85,7 @@ extern "C" {
     extern void epsf_gencode(GVC_t * gvc, node_t * n);
     extern point exch_xy(point p);
     extern pointf exch_xyf(pointf p);
+    extern void extend_attrs(GVC_t *, graph_t*, int, int);
     extern shape_desc *find_user_shape(char *);
     extern box flip_rec_box(box b, point p);
     extern point flip_pt(point p, int rankdir);
@@ -149,6 +152,8 @@ extern "C" {
     extern point *routesplines(path *, int *);
     extern void routesplinesterm(void);
     extern char *safefile(char *shapefilename);
+    extern attrsym_t* safe_dcl(graph_t*, void*, char*, char*,
+         attrsym_t * (*fun) (Agraph_t *, char *, char *));
     extern int selfRightSpace (edge_t* e);
     extern void setup_graph(GVC_t * gvc, graph_t * g);
     extern shape_kind shapeOf(node_t *);
index c13b5dd922088297c9b607d47135016102592dd5..3e0e1c051c5b36e1b0588d873c65536bb4ceaa50 100644 (file)
@@ -1149,7 +1149,9 @@ static void poly_gencode(GVC_t * gvc, node_t * n)
     static point *A;
     static int A_size;
     int filled;
+    extern int xdemitState;
 
+    xdemitState = EMIT_DRAW;
     poly = (polygon_t *) ND_shape_info(n);
     vertices = poly->vertices;
     sides = poly->sides;
@@ -1240,6 +1242,7 @@ static void poly_gencode(GVC_t * gvc, node_t * n)
        filled = 0;
     }
 
+    xdemitState = EMIT_LABEL;
     emit_label(gvc, ND_label(n), (void *) n);
 }
 
@@ -1771,7 +1774,9 @@ static void record_gencode(GVC_t * gvc, node_t * n)
     point A[4];
     int i, style;
     field_t *f;
+    extern int xdemitState;
 
+    xdemitState = EMIT_DRAW;
     f = (field_t *) ND_shape_info(n);
     A[0] = f->b.LL;
     A[2] = f->b.UR;
@@ -1791,6 +1796,7 @@ static void record_gencode(GVC_t * gvc, node_t * n)
        round_corners(gvc, n, A, 4, ROUNDED);
     else
        gvrender_polygon(gvc, A, 4, style & FILLED);
+    xdemitState = EMIT_LABEL;
     gen_fields(gvc, n, f);
 }
 
index ffd23e56771cd14dbffb195279d30f8b8a7f6df4..69124ec1a82c4fdec9b9c42b4526339c2c1a0a0a 100644 (file)
@@ -1661,3 +1661,19 @@ void undoClusterEdges(graph_t * g)
     }
     agclose(clg);
 }
+
+/* safe_dcl:
+ * Find the attribute belonging to graph g for objects like obj
+ * with given name. If one does not exist, create it with the
+ * supplied function fun with default value def.
+ */ 
+attrsym_t*
+safe_dcl(graph_t * g, void *obj, char *name, char *def,
+         attrsym_t * (*fun) (Agraph_t *, char *, char *))
+{
+    attrsym_t *a = agfindattr(obj, name);
+    if (a == NULL)
+       a = fun(g, name, def);
+    return a;
+}
+
index 88a7f3a143c1e372d27c5160de90ab41927d3225..3dfacb9555d4f67b8eaf31a330e0318f67db2617 100644 (file)
 #include       "gvc.h"
 #include       "agxbuf.h"
 
-extern agxbuf charbuf;
-extern agxbuf outbuf;
+int    xdemitState;
+static agxbuf xbuf0;
+static agxbuf xbuf1;
+static agxbuf xbuf2;
+static agxbuf xbuf3;
+static agxbuf xbuf4;
+static agxbuf xbuf5;
+static agxbuf* xbufs[6] = {
+  &xbuf0, &xbuf1,
+  &xbuf2, &xbuf3,
+  &xbuf4, &xbuf5,
+};
 static Agraph_t *cluster_g;
 
+static int isInvis(char *style)
+{
+    char **styles = 0;
+    char **sp;
+    char *p;
+
+    if (style[0]) {
+       styles = parse_style(style);
+       sp = styles;
+       while ((p = *sp++)) {
+           if (streq(p, "invis"))
+               return 1;
+       }
+    }
+    return 0;
+}
+
+/* 
+ * John M. suggests:
+ * You might want to add four more:
+ *
+ * _ohdraw_ (optional head-end arrow for edges)
+ * _ohldraw_ (optional head-end label for edges)
+ * _otdraw_ (optional tail-end arrow for edges)
+ * _otldraw_ (optional tail-end label for edges)
+ * 
+ * that would be generated when an additional option is supplied to 
+ * dot, etc. and 
+ * these would be the arrow/label positions to use if a user want to flip the 
+ * direction of an edge (as sometimes is there want).
+ */
+void extend_attrs(GVC_t * gvc, graph_t *g, int s_arrows, int e_arrows)
+{
+    node_t *n;
+    edge_t *e;
+    attrsym_t *n_draw = NULL;
+    attrsym_t *n_l_draw = NULL;
+    attrsym_t *e_draw = NULL;
+    attrsym_t *h_draw = NULL;
+    attrsym_t *t_draw = NULL;
+    attrsym_t *e_l_draw = NULL;
+    attrsym_t *hl_draw = NULL;
+    attrsym_t *tl_draw = NULL;
+    unsigned char buf0[BUFSIZ];
+    unsigned char buf1[BUFSIZ];
+    unsigned char buf2[BUFSIZ];
+    unsigned char buf3[BUFSIZ];
+    unsigned char buf4[BUFSIZ];
+    unsigned char buf5[BUFSIZ];
+
+    if (GD_has_labels(g) & GRAPH_LABEL)
+       g_l_draw = safe_dcl(g, g, "_ldraw_", "", agraphattr);
+    if (GD_n_cluster(g))
+       g_draw = safe_dcl(g, g, "_draw_", "", agraphattr);
+
+    n_draw = safe_dcl(g, g->proto->n, "_draw_", "", agnodeattr);
+    n_l_draw = safe_dcl(g, g->proto->n, "_ldraw_", "", agnodeattr);
+
+    e_draw = safe_dcl(g, g->proto->e, "_draw_", "", agedgeattr);
+    if (e_arrows)
+       h_draw = safe_dcl(g, g->proto->e, "_hdraw_", "", agedgeattr);
+    if (s_arrows)
+       t_draw = safe_dcl(g, g->proto->e, "_tdraw_", "", agedgeattr);
+    if (GD_has_labels(g) & EDGE_LABEL)
+       e_l_draw = safe_dcl(g, g->proto->e, "_ldraw_", "", agedgeattr);
+    if (GD_has_labels(g) & HEAD_LABEL)
+       hl_draw = safe_dcl(g, g->proto->e, "_hldraw_", "", agedgeattr);
+    if (GD_has_labels(g) & TAIL_LABEL)
+       tl_draw = safe_dcl(g, g->proto->e, "_tldraw_", "", agedgeattr);
+
+    agxbinit(&xbuf0, BUFSIZ, buf0);
+    agxbinit(&xbuf1, BUFSIZ, buf1);
+    agxbinit(&xbuf2, BUFSIZ, buf2);
+    agxbinit(&xbuf3, BUFSIZ, buf3);
+    agxbinit(&xbuf4, BUFSIZ, buf4);
+    agxbinit(&xbuf5, BUFSIZ, buf5);
+
+    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+       if (ND_shape(n) && !isInvis(late_string(n, N_style, ""))) {
+           ND_shape(n)->fns->codefn(gvc, n);
+           agxset(n, n_draw->index, agxbuse(xbufs[EMIT_DRAW]));
+           agxset(n, n_l_draw->index, agxbuse(xbufs[EMIT_LABEL]));
+       }
+       if (State < GVSPLINES)
+           continue;
+       for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
+           if (ED_edge_type(e) == IGNORED)
+               continue;
+           if (isInvis(late_string(e, E_style, "")))
+               continue;
+           if (ED_spl(e) == NULL)
+               continue;
+
+           emit_edge_graphics (gvc, e);
+           agxset(e, e_draw->index, agxbuse(xbufs[EMIT_DRAW]));
+           if (t_draw) agxset(e, t_draw->index, agxbuse(xbufs[EMIT_TDRAW]));
+           if (h_draw) agxset(e, h_draw->index, agxbuse(xbufs[EMIT_HDRAW]));
+           if (e_l_draw) agxset(e, e_l_draw->index,agxbuse(xbufs[EMIT_LABEL]));
+           if (tl_draw) agxset(e, tl_draw->index, agxbuse(xbufs[EMIT_TLABEL]));
+           if (hl_draw) agxset(e, hl_draw->index, agxbuse(xbufs[EMIT_HLABEL]));
+       }
+    }
+  
+    xdemitState = EMIT_DRAW;
+    emit_background(gvc, g);
+    if (agxblen(xbufs[EMIT_DRAW])) {
+       if (!g_draw)
+           g_draw = safe_dcl(g, g, "_draw_", "", agraphattr);
+       agxset(g, g_draw->index, agxbuse(xbufs[EMIT_DRAW]));
+    }
+    xdemitState = EMIT_LABEL;
+    if (GD_label(g)) {
+       emit_label(gvc, GD_label(g), (void *) g);
+       agxset(g, g_l_draw->index, agxbuse(xbufs[EMIT_LABEL]));
+    }
+    emit_clusters(gvc, g, 0);
+    agxbfree(&xbuf0);
+    agxbfree(&xbuf1);
+    agxbfree(&xbuf2);
+    agxbfree(&xbuf3);
+    agxbfree(&xbuf4);
+    agxbfree(&xbuf5);
+}
+
+static void xd_str (char* pfx, char* s)
+{
+    char buf[BUFSIZ];
+
+    sprintf (buf, "%s%d -", pfx, strlen(s));
+    agxbput(xbufs[xdemitState], buf);
+    agxbput(xbufs[xdemitState], s);
+    agxbputc(xbufs[xdemitState], ' ');
+}
+
 static void xd_textline(point p, textline_t * line)
 {
     char buf[BUFSIZ];
     int j;
 
-    agxbputc(&charbuf, 'T');
     switch (line->just) {
     case 'l':
        j = -1;
@@ -40,20 +183,18 @@ static void xd_textline(point p, textline_t * line)
        j = 0;
        break;
     }
-    sprintf(buf, " %d %d %d %d %d -", p.x, YDIR(p.y), j,
-           (int) line->width, (int) strlen(line->str));
-    agxbput(&charbuf, buf);
-    agxbput(&charbuf, line->str);
-    agxbputc(&charbuf, ' ');
+    sprintf(buf, "T %d %d %d %d ", p.x, YDIR(p.y), j, (int) line->width);
+    agxbput(xbufs[xdemitState], buf);
+    xd_str ("", line->str);
 }
 
 static void xd_ellipse(point p, int rx, int ry, int filled)
 {
     char buf[BUFSIZ];
 
-    agxbputc(&outbuf, (filled ? 'E' : 'e'));
+    agxbputc(xbufs[xdemitState], (filled ? 'E' : 'e'));
     sprintf(buf, " %d %d %d %d ", p.x, YDIR(p.y), rx, ry);
-    agxbput(&outbuf, buf);
+    agxbput(xbufs[xdemitState], buf);
 }
 
 static void xd_points(char c, point * A, int n)
@@ -62,13 +203,13 @@ static void xd_points(char c, point * A, int n)
     int i;
     point p;
 
-    agxbputc(&outbuf, c);
+    agxbputc(xbufs[xdemitState], c);
     sprintf(buf, " %d ", n);
-    agxbput(&outbuf, buf);
+    agxbput(xbufs[xdemitState], buf);
     for (i = 0; i < n; i++) {
        p = A[i];
        sprintf(buf, "%d %d ", p.x, YDIR(p.y));
-       agxbput(&outbuf, buf);
+       agxbput(xbufs[xdemitState], buf);
     }
 }
 
@@ -88,6 +229,60 @@ static void xd_polyline(point * A, int n)
     xd_points('L', A, n);
 }
 
+static void 
+xd_set_font (char *fontname, double fontsize)
+{
+    char buf[BUFSIZ];
+
+    sprintf(buf, "F %f ", fontsize);
+    agxbput(xbufs[xdemitState], buf);
+    xd_str ("", fontname);
+}
+
+static void 
+xd_set_pencolor (char *name)
+{
+    xd_str ("c ", name);
+}
+
+static void 
+xd_set_fillcolor (char *name)
+{
+    xd_str ("C ", name);
+}
+
+static void 
+xd_set_style (char **s)
+{
+    char buf[BUFSIZ];
+    agxbuf xbuf;
+    char* p;
+    int more;
+
+    agxbinit(&xbuf, BUFSIZ, buf);
+    while ((p = *s++)) {
+       agxbput(&xbuf, p);
+       while (*p)
+           p++;
+       p++;
+       if (*p) {  /* arguments */
+           agxbputc(&xbuf, '(');
+            more = 0;
+           while (*p) {
+               if (more)
+                   agxbputc(&xbuf, ',');
+               agxbput(&xbuf, p);
+               while (*p) p++;
+               p++;
+               more++;
+           }
+           agxbputc(&xbuf, ')');
+       }
+       xd_str ("S ", agxbuse(&xbuf));
+    }
+    agxbfree(&xbuf);
+}
+
 static void xd_begin_cluster(Agraph_t * sg)
 {
     cluster_g = sg;
@@ -95,9 +290,9 @@ static void xd_begin_cluster(Agraph_t * sg)
 
 static void xd_end_cluster(void)
 {
-    agxset(cluster_g, g_draw->index, agxbuse(&outbuf));
+    agxset(cluster_g, g_draw->index, agxbuse(xbufs[EMIT_DRAW]));
     if (GD_label(cluster_g))
-       agxset(cluster_g, g_l_draw->index, agxbuse(&charbuf));
+       agxset(cluster_g, g_l_draw->index, agxbuse(xbufs[EMIT_LABEL]));
 }
 
 codegen_t XDot_CodeGen = {
@@ -109,12 +304,12 @@ codegen_t XDot_CodeGen = {
     xd_begin_cluster, xd_end_cluster,
     0, /* xd_begin_nodes */ 0, /* xd_end_nodes */
     0, /* xd_begin_edges */ 0, /* xd_end_edges */
-    0, /* xd_begin_node */ 0,  /* xd_node */
-    0, /* xd_begin_edge */ 0,  /* xd_edge */
+    0, /* xd_begin_node */ 0, /* xd_end_node */
+    0, /* xd_begin_edge */ 0, /* xd_end_edge */
     0, /* xd_begin_context */ 0,       /* xd_context */
     0, /* xd_begin_anchor */ 0,        /* xd_anchor */
-    0, /* xd_set_font */ xd_textline,
-    0, /* xd_set_pencolor */ 0, /* xd_set_fillcolor */ 0,      /* xd_set_style */
+    xd_set_font, xd_textline,
+    xd_set_pencolor, xd_set_fillcolor, xd_set_style,
     xd_ellipse, xd_polygon,
     xd_bezier, xd_polyline,
     0, /* xd_has_arrows */ 0,  /* xd_comment */