\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;
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 {
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;
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;
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
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.
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
-/* $Id$Revision: */
/* vim:set shiftwidth=4 ts=8: */
/*************************************************************************
static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
{
+ char* cs;
+ xdot_color clr;
+
*error = 0;
while (isspace(*s))
s++;
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':
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);
}
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);
}
}
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);
}
}
}
+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);
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);
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:
}
if (more)
print(" ", info);
+ agxbfree (&xb);
}
static void jsonRect(xdot_rect * r, 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);
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);
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);
print("},\n", info);
else
print("}\n", info);
+ agxbfree (&xb);
}
static void _printXDot(xdot * x, pf print, void *info, print_op ofn)
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;
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)
{