]> granicus.if.org Git - graphviz/commitdiff
Update xdot library to handle gradient fill
authorEmden R. Gansner <erg@research.att.com>
Tue, 6 Aug 2013 19:44:42 +0000 (15:44 -0400)
committerEmden R. Gansner <erg@research.att.com>
Tue, 6 Aug 2013 19:44:42 +0000 (15:44 -0400)
lib/xdot/xdot.3
lib/xdot/xdot.c
lib/xdot/xdot.h

index 56b79899522ae841dc2354a8936bb9e4e14a1cac..fb2668375e2effe75f4a421af8bef6216784e1c9 100644 (file)
@@ -8,6 +8,40 @@
 \f5
 #include <graphviz/xdot.h>
 
+typedef enum {
+    xd_none,
+    xd_linear,
+    xd_radial
+} xdot_grad_type;
+
+typedef struct {
+    float frac;
+    char* color;
+} xdot_color_stop;
+
+typedef struct {
+    double x0, y0;
+    double x1, y1;
+    int n_stops;
+    xdot_color_stop* stops;
+} xdot_linear_grad;
+
+typedef struct {
+    double x0, y0, r0;
+    double x1, y1, r1;
+    int n_stops;
+    xdot_color_stop* stops;
+} xdot_radial_grad;
+
+typedef struct {
+    xdot_grad_type type;
+    union {
+       char* clr;
+       xdot_linear_grad ling;
+       xdot_radial_grad ring;
+    } u;
+} xdot_color;
+
 typedef enum {
     xd_left, xd_center, xd_right
 } xdot_align;
@@ -47,7 +81,8 @@ typedef enum {
     xd_filled_polygon, xd_unfilled_polygon,
     xd_filled_bezier,  xd_unfilled_bezier,
     xd_polyline,       xd_text,
-    xd_fill_color,     xd_pen_color, xd_font, xd_style, xd_image
+    xd_fill_color,     xd_pen_color, xd_font, xd_style, xd_image,
+    xd_grad_fill_color,     xd_grad_pen_color
 } xdot_kind; 
     
 typedef enum {
@@ -55,7 +90,8 @@ typedef enum {
     xop_polygon,
     xop_bezier,
     xop_polyline,       xop_text,
-    xop_fill_color,     xop_pen_color, xop_font, xop_style, xop_image
+    xop_fill_color,     xop_pen_color, xop_font, xop_style, xop_image,
+    xop_grad_fill_color,     xop_grad_pen_color
 } xop_kind; 
     
 typedef struct _xdot_op xdot_op;
@@ -72,6 +108,7 @@ struct _xdot_op {
       xdot_text text;          /* xd_text */
       xdot_image image;        /* xd_image */
       char* color;             /* xd_fill_color, xd_pen_color */
+      xdot_color grad_color;   /* xd_grad_fill_color, xd_grad_pen_color */
       xdot_font font;          /* xd_font */
       char* style;             /* xd_style */
     } u;
@@ -93,6 +130,10 @@ xdot* parseXDot (char*);
 char* sprintXDot (xdot*);
 void fprintXDot (FILE*, xdot*);
 void freeXDot (xdot*);
+
+xdot_grad_type colorType (char*);
+xdot_color* parseXDotColor (char*);
+void freeXDotColor (xdot_color*);
 \fP
 .fi
 .SH DESCRIPTION
@@ -119,7 +160,7 @@ a given \fIkind\fP indicated by the comments.
 The \fIdrawfunc\fP field allows the user to attach a drawing-specific
 function to the operation, providing an object-based interface. These
 functions can be automatically attached during parsing by providing a
-non-NULL second argument to \fIparseXDotF\fP.
+non-NULL second argument to \fBparseXDotF\fP.
 .PP
 .SS "  xop_kind"
 This type provides an enumeration of the allowed xdot operations.
@@ -193,6 +234,25 @@ These two functions deparse the argument xdot structure, producing
 a string representation. \fIfprintXDot\fP writes the output onto
 the open stream \fIfp\fP; \fIsprintXDot\fP returns a heap-allocated
 string.
+.PP
+The color string with fill and draw operations can encode linear
+and radial gradients. These values are parsed automatically by
+\fBparseXDotF\fP or \fBparseXDot\fP, 
+with \fIxdot_op\fP having kind \fIxd_grad_pen_color\fP or
+\fIxd_grad_fill_color\fP and the value is stored in \fIgrad_color\fP.
+.PP
+For an application that handles its own parsing of xdot, the library
+provides three helper functions.
+.PP
+.SS "  xdot_grad_type colorTypeXDot (char *str)"
+returns the color type described by the input string.
+.PP
+.SS "  char* parseXDotColor (char *str, xdot_color* clr)"
+attempts to parse the string \fIstr\fP as a color value, storing
+the result in \fIclr\fP. It returns NULL on failure.
+.PP
+.SS "  void freeXDotColor (xdot_color* cp)"
+This frees the resources associated with a value of type \fIxdot_color\fP.
 
 .SH BUGS
 Although some small checking is done on the \fIsz\fP argument to
index e0217943bed64e99c5ef37020353d496e7f35ed8..f7f7a5f10caf5a372494ca805fa63e7605d008f2 100755 (executable)
@@ -1,4 +1,3 @@
-/* $Id$Revision: */
 /* vim:set shiftwidth=4 ts=8: */
 
 /*************************************************************************
@@ -247,6 +246,9 @@ static char *parseAlign(char *s, xdot_align * ap)
 
 static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
 {
+    char* cs;
+    xdot_color clr;
+
     *error = 0;
     while (isspace(*s))
        s++;
@@ -300,19 +302,41 @@ static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
        break;
 
     case 'c':
-       op->kind = xd_pen_color;
-       s = parseString(s, &op->u.color);
+       s = parseString(s, &cs);
        CHK(s);
-       if (ops)
-           op->drawfunc = ops[xop_pen_color];
+       cs = parseXDotColor (cs, &clr);
+       CHK(cs);
+       if (clr.type == xd_none) {
+           op->kind = xd_pen_color;
+           op->u.color = clr.u.clr;
+           if (ops)
+               op->drawfunc = ops[xop_pen_color];
+       }
+       else {
+           op->kind = xd_grad_pen_color;
+           op->u.grad_color = clr;
+           if (ops)
+               op->drawfunc = ops[xop_grad_pen_color];
+       }
        break;
 
     case 'C':
-       op->kind = xd_fill_color;
-       s = parseString(s, &op->u.color);
+       s = parseString(s, &cs);
        CHK(s);
-       if (ops)
-           op->drawfunc = ops[xop_fill_color];
+       cs = parseXDotColor (cs, &clr);
+       CHK(cs);
+       if (clr.type == xd_none) {
+           op->kind = xd_fill_color;
+           op->u.color = clr.u.clr;
+           if (ops)
+               op->drawfunc = ops[xop_fill_color];
+       }
+       else {
+           op->kind = xd_grad_fill_color;
+           op->u.grad_color = clr;
+           if (ops)
+               op->drawfunc = ops[xop_grad_fill_color];
+       }
        break;
 
     case 'L':
@@ -456,11 +480,41 @@ xdot *parseXDot(char *s)
 
 typedef void (*pf) (char *, void *);
 
+/* trim:
+ * Trailing zeros are removed and decimal point, if possible.
+ */
+static void trim (char* buf)
+{
+    char* dotp;
+    char* p;
+
+    if ((dotp = strchr (buf,'.'))) {
+        p = dotp+1;
+        while (*p) p++;  // find end of string
+        p--;
+        while (*p == '0') *p-- = '\0';
+        if (*p == '.')        // If all decimals were zeros, remove ".".
+            *p = '\0';
+        else
+            p++;
+    }
+}
+
 static void printRect(xdot_rect * r, pf print, void *info)
 {
     char buf[128];
 
-    sprintf(buf, " %.06f %.06f %.06f %.06f", r->x, r->y, r->w, r->h);
+    sprintf(buf, " %.02f", r->x);
+    trim(buf);
+    print(buf, info);
+    sprintf(buf, " %.02f", r->y);
+    trim(buf);
+    print(buf, info);
+    sprintf(buf, " %.02f", r->w);
+    trim(buf);
+    print(buf, info);
+    sprintf(buf, " %.02f", r->h);
+    trim(buf);
     print(buf, info);
 }
 
@@ -472,7 +526,11 @@ static void printPolyline(xdot_polyline * p, pf print, void *info)
     sprintf(buf, " %d", p->cnt);
     print(buf, info);
     for (i = 0; i < p->cnt; i++) {
-       sprintf(buf, " %.06f %.06f", p->pts[i].x, p->pts[i].y);
+       sprintf(buf, " %.02f", p->pts[i].x);
+       trim(buf);
+       print(buf, info);
+       sprintf(buf, " %.02f", p->pts[i].y);
+       trim(buf);
        print(buf, info);
     }
 }
@@ -494,11 +552,15 @@ static void printInt(int i, pf print, void *info)
     print(buf, info);
 }
 
-static void printFloat(float f, pf print, void *info)
+static void printFloat(float f, pf print, void *info, int space)
 {
     char buf[128];
 
-    sprintf(buf, " %f", f);
+    if (space)
+       sprintf(buf, " %.02f", f);
+    else
+       sprintf(buf, "%.02f", f);
+    trim (buf);
     print(buf, info);
 }
 
@@ -517,10 +579,58 @@ static void printAlign(xdot_align a, pf print, void *info)
     }
 }
 
+static void
+gradprint (char* s, void* v)
+{
+    agxbput(s, (agxbuf*)v);
+}
+
+static void
+toGradString (agxbuf* xb, xdot_color* cp)
+{
+    int i, n_stops;
+    xdot_color_stop* stops;
+
+    if (cp->type == xd_linear) {
+       agxbputc (xb, '[');
+       printFloat (cp->u.ling.x0, gradprint, xb, 0);
+       printFloat (cp->u.ling.y0, gradprint, xb, 1);
+       printFloat (cp->u.ling.x1, gradprint, xb, 1);
+       printFloat (cp->u.ling.y1, gradprint, xb, 1);
+       n_stops = cp->u.ling.n_stops;
+       stops = cp->u.ling.stops;
+    }
+    else {
+       agxbputc (xb, '(');
+       printFloat (cp->u.ring.x0, gradprint, xb, 0);
+       printFloat (cp->u.ring.y0, gradprint, xb, 1);
+       printFloat (cp->u.ring.r0, gradprint, xb, 1);
+       printFloat (cp->u.ring.x1, gradprint, xb, 1);
+       printFloat (cp->u.ring.y1, gradprint, xb, 1);
+       printFloat (cp->u.ring.r1, gradprint, xb, 1);
+       n_stops = cp->u.ring.n_stops;
+       stops = cp->u.ring.stops;
+    }
+    printInt (n_stops, gradprint, xb);
+    for (i = 0; i < n_stops; i++) {
+       printFloat (stops[i].frac, gradprint, xb, 1);
+       printString (stops[i].color, gradprint, xb);
+    }
+
+    if (cp->type == xd_linear)
+       agxbputc (xb, ']');
+    else
+       agxbputc (xb, ')');
+}
+
 typedef void (*print_op)(xdot_op * op, pf print, void *info, int more);
 
 static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
 {
+    agxbuf xb;
+    unsigned char buf[BUFSIZ];
+
+    agxbinit (&xb, BUFSIZ, buf);
     switch (op->kind) {
     case xd_filled_ellipse:
        print("E", info);
@@ -551,10 +661,20 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
        print("c", info);
        printString(op->u.color, print, info);
        break;
+    case xd_grad_pen_color:
+       print("c", info);
+       toGradString (&xb, &op->u.grad_color);
+       printString(agxbuse(&xb), print, info);
+       break;
     case xd_fill_color:
        print("C", info);
        printString(op->u.color, print, info);
        break;
+    case xd_grad_fill_color:
+       print("C", info);
+       toGradString (&xb, &op->u.grad_color);
+       printString(agxbuse(&xb), print, info);
+       break;
     case xd_polyline:
        print("L", info);
        printPolyline(&op->u.polyline, print, info);
@@ -570,7 +690,7 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
     case xd_font:
        print("F", info);
        op->kind = xd_font;
-       printFloat(op->u.font.size, print, info);
+       printFloat(op->u.font.size, print, info, 1);
        printString(op->u.font.name, print, info);
        break;
     case xd_style:
@@ -585,6 +705,7 @@ static void printXDot_Op(xdot_op * op, pf print, void *info, int more)
     }
     if (more)
        print(" ", info);
+    agxbfree (&xb);
 }
 
 static void jsonRect(xdot_rect * r, pf print, void *info)
@@ -629,6 +750,10 @@ static void jsonString(char *p, pf print, void *info)
 
 static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
 {
+    agxbuf xb;
+    unsigned char buf[BUFSIZ];
+
+    agxbinit (&xb, BUFSIZ, buf);
     switch (op->kind) {
     case xd_filled_ellipse:
        print("{E : ", info);
@@ -658,10 +783,20 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
        print("{c : ", info);
        jsonString(op->u.color, print, info);
        break;
+    case xd_grad_pen_color:
+       print("{c : ", info);
+       toGradString (&xb, &op->u.grad_color);
+       jsonString(agxbuse(&xb), print, info);
+       break;
     case xd_fill_color:
        print("{C : ", info);
        jsonString(op->u.color, print, info);
        break;
+    case xd_grad_fill_color:
+       print("{C : ", info);
+       toGradString (&xb, &op->u.grad_color);
+       jsonString(agxbuse(&xb), print, info);
+       break;
     case xd_polyline:
        print("{L :", info);
        jsonPolyline(&op->u.polyline, print, info);
@@ -682,7 +817,7 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
     case xd_font:
        print("{F : [", info);
        op->kind = xd_font;
-       printFloat(op->u.font.size, print, info);
+       printFloat(op->u.font.size, print, info, 1);
        print(",", info);
        jsonString(op->u.font.name, print, info);
        print("]", info);
@@ -703,6 +838,7 @@ static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
        print("},\n", info);
     else
        print("}\n", info);
+    agxbfree (&xb);
 }
 
 static void _printXDot(xdot * x, pf print, void *info, print_op ofn)
@@ -762,6 +898,10 @@ static void freeXOpData(xdot_op * x)
     case xd_pen_color:
        free(x->u.color);
        break;
+    case xd_grad_fill_color:
+    case xd_grad_pen_color:
+       freeXDotColor (&x->u.grad_color);
+       break;
     case xd_font:
        free(x->u.font.name);
        break;
@@ -849,6 +989,157 @@ int statXDot (xdot* x, xdot_stats* sp)
     return 0;
 }
 
+xdot_grad_type 
+colorType (char* cp)
+{
+    xdot_grad_type rv;
+
+    switch (*cp) {
+    case '[' :
+       rv = xd_linear;
+       break;
+    case '(' :
+       rv = xd_radial;
+       break;
+    default :
+       rv = xd_none;
+       break;
+    }
+    return rv;
+}
+
+#define CHK1(s) if(!s){free(stops);return NULL;}
+
+/* radGradient:
+ * Parse radial gradient spec
+ * Return NULL on failure.
+ */
+static char*
+radGradient (char* cp, xdot_color* clr)
+{
+    char* s = cp;
+    int i;
+    double d;
+    xdot_color_stop* stops = NULL;
+
+    clr->type = xd_radial;
+    s = parseReal(s, &clr->u.ring.x0);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ring.y0);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ring.r0);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ring.x1);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ring.y1);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ring.r1);
+    CHK1(s);
+    s = parseInt(s, &clr->u.ring.n_stops);
+    CHK1(s);
+
+    stops = N_NEW(clr->u.ring.n_stops,xdot_color_stop);
+    for (i = 0; i < clr->u.ring.n_stops; i++) {
+       s = parseReal(s, &d);
+       CHK1(s);
+       stops[i].frac = d;
+       s = parseString(s, &stops[i].color);
+       CHK1(s);
+    }
+    clr->u.ring.stops = stops;
+
+    return cp;
+}
+
+/* linGradient:
+ * Parse linear gradient spec
+ * Return NULL on failure.
+ */
+static char*
+linGradient (char* cp, xdot_color* clr)
+{
+    char* s = cp;
+    int i;
+    double d;
+    xdot_color_stop* stops = NULL;
+
+    clr->type = xd_linear;
+    s = parseReal(s, &clr->u.ling.x0);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ling.y0);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ling.x1);
+    CHK1(s);
+    s = parseReal(s, &clr->u.ling.y1);
+    CHK1(s);
+    s = parseInt(s, &clr->u.ling.n_stops);
+    CHK1(s);
+
+    stops = N_NEW(clr->u.ling.n_stops,xdot_color_stop);
+    for (i = 0; i < clr->u.ling.n_stops; i++) {
+       s = parseReal(s, &d);
+       CHK1(s);
+       stops[i].frac = d;
+       s = parseString(s, &stops[i].color);
+       CHK1(s);
+    }
+    clr->u.ling.stops = stops;
+
+    return cp;
+}
+
+/* parseXDotColor:
+ * Parse xdot color spec: ordinary or gradient
+ * The result is stored in clr.
+ * Return NULL on failure.
+ */
+char*
+parseXDotColor (char* cp, xdot_color* clr)
+{
+    char c = *cp;
+
+    switch (c) {
+    case '[' :
+       return linGradient (cp+1, clr);
+       break;
+    case '(' :
+       return radGradient (cp+1, clr);
+       break;
+    case '#' :
+    case '/' :
+       clr->type = xd_none; 
+       clr->u.clr = cp;
+       return cp;
+       break;
+    default :
+       if (isalnum(c)) {
+           clr->type = xd_none; 
+           clr->u.clr = cp;
+           return cp;
+       }
+       else
+           return NULL;
+    }
+}
+
+void freeXDotColor (xdot_color* cp)
+{
+    int i;
+
+    if (cp->type == xd_linear) {
+       for (i = 0; i < cp->u.ling.n_stops; i++) {
+           free (cp->u.ling.stops[i].color);
+       }
+       free (cp->u.ling.stops);
+    }
+    else if (cp->type == xd_radial) {
+       for (i = 0; i < cp->u.ring.n_stops; i++) {
+           free (cp->u.ring.stops[i].color);
+       }
+       free (cp->u.ring.stops);
+    }
+}
+
 #if 0
 static void execOp(xdot_op * op, int param)
 {
index dec2c64a438b624a0f12c9476ec10b4b52320174..e9e89c2b9c91435465e61bb9412e32dec7f60b57 100755 (executable)
 #endif
 #define INITIAL_XDOT_CAPACITY 512
 
+typedef enum {
+    xd_none,
+    xd_linear,
+    xd_radial
+} xdot_grad_type;
+
+typedef struct {
+    float frac;
+    char* color;
+} xdot_color_stop;
+
+typedef struct {
+    double x0, y0;
+    double x1, y1;
+    int n_stops;
+    xdot_color_stop* stops;
+} xdot_linear_grad;
+
+typedef struct {
+    double x0, y0, r0;
+    double x1, y1, r1;
+    int n_stops;
+    xdot_color_stop* stops;
+} xdot_radial_grad;
+
+typedef struct {
+    xdot_grad_type type;
+    union {
+       char* clr;
+       xdot_linear_grad ling;
+       xdot_radial_grad ring;
+    } u;
+} xdot_color;
+
 typedef enum {
     xd_left, xd_center, xd_right
 } xdot_align;
@@ -57,7 +91,8 @@ typedef enum {
     xd_filled_polygon, xd_unfilled_polygon,
     xd_filled_bezier,  xd_unfilled_bezier,
     xd_polyline,       xd_text,
-    xd_fill_color,     xd_pen_color, xd_font, xd_style, xd_image
+    xd_fill_color,     xd_pen_color, xd_font, xd_style, xd_image,
+    xd_grad_fill_color,     xd_grad_pen_color
 } xdot_kind; 
     
 typedef enum {
@@ -65,7 +100,8 @@ typedef enum {
     xop_polygon,
     xop_bezier,
     xop_polyline,       xop_text,
-    xop_fill_color,     xop_pen_color, xop_font, xop_style, xop_image
+    xop_fill_color,     xop_pen_color, xop_font, xop_style, xop_image,
+    xop_grad_fill_color,     xop_grad_pen_color
 } xop_kind; 
     
 typedef struct _xdot_op xdot_op;
@@ -82,6 +118,7 @@ struct _xdot_op {
       xdot_text text;          /* xd_text */
       xdot_image image;        /* xd_image */
       char* color;             /* xd_fill_color, xd_pen_color */
+      xdot_color grad_color;   /* xd_grad_fill_color, xd_grad_pen_color */
       xdot_font font;          /* xd_font */
       char* style;             /* xd_style */
     } u;
@@ -123,5 +160,8 @@ extern void fprintXDot (FILE*, xdot*);
 extern void jsonXDot (FILE*, xdot*);
 extern void freeXDot (xdot*);
 extern int statXDot (xdot*, xdot_stats*);
+extern xdot_grad_type colorTypeXDot (char*);
+extern char* parseXDotColor (char* cp, xdot_color* clr);
+extern void freeXDotColor (xdot_color*);
 
 #endif