123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- #include <cgraph/gv_ctype.h>
- #include <xdot/xdot.h>
- #include <stdlib.h>
- #include <string.h>
- #include <util/agxbuf.h>
- #include <util/alloc.h>
- #include <util/prisize_t.h>
- #include <util/unreachable.h>
- /* the parse functions should return NULL on error */
- static char *parseReal(char *s, double *fp)
- {
- char *p;
- double d;
- d = strtod(s, &p);
- if (p == s) return 0;
-
- *fp = d;
- return (p);
- }
- static char *parseInt(char *s, int *ip)
- {
- char* endp;
- *ip = (int)strtol (s, &endp, 10);
- if (s == endp)
- return 0;
- else
- return endp;
- }
- static char *parseUInt(char *s, unsigned int *ip)
- {
- char* endp;
- *ip = (unsigned int)strtoul (s, &endp, 10);
- if (s == endp)
- return 0;
- else
- return endp;
- }
- static char *parseRect(char *s, xdot_rect * rp)
- {
- char* endp;
- rp->x = strtod (s, &endp);
- if (s == endp)
- return 0;
- else
- s = endp;
- rp->y = strtod (s, &endp);
- if (s == endp)
- return 0;
- else
- s = endp;
- rp->w = strtod (s, &endp);
- if (s == endp)
- return 0;
- else
- s = endp;
- rp->h = strtod (s, &endp);
- if (s == endp)
- return 0;
- else
- s = endp;
- return s;
- }
- static char *parsePolyline(char *s, xdot_polyline * pp)
- {
- unsigned i;
- xdot_point *pts;
- xdot_point *ps;
- char* endp;
- s = parseUInt(s, &i);
- if (!s) return NULL;
- pts = ps = gv_calloc(i, sizeof(ps[0]));
- pp->cnt = i;
- for (i = 0; i < pp->cnt; i++) {
- ps->x = strtod (s, &endp);
- if (s == endp) {
- free (pts);
- return NULL;
- }
- else
- s = endp;
- ps->y = strtod (s, &endp);
- if (s == endp) {
- free (pts);
- return NULL;
- }
- else
- s = endp;
- ps->z = 0;
- ps++;
- }
- pp->pts = pts;
- return s;
- }
- static char *parseString(char *s, char **sp)
- {
- int i;
- s = parseInt(s, &i);
- if (!s || i <= 0) return 0;
- while (*s && *s != '-') s++;
- if (*s) s++;
- else {
- return 0;
- }
- // The leading number we just read indicates the count of originating
- // characters in the upcoming string. But the string may contain \-escaped
- // characters. So the count alone does not tell us how many bytes we need to
- // now read.
- agxbuf c = {0};
- int j = 0;
- for (int accounted = 0; accounted < i; ++j) {
- if (s[j] == '\0') {
- agxbfree(&c);
- return 0;
- }
- agxbputc(&c, s[j]);
- // only count this character if it was not an escape prefix
- if (s[j] != '\\' || (j > 0 && s[j - 1] == '\\')) {
- ++accounted;
- }
- }
- *sp = agxbdisown(&c);
- return s + j;
- }
- static char *parseAlign(char *s, xdot_align * ap)
- {
- int i;
- s = parseInt(s, &i);
- if (i < 0)
- *ap = xd_left;
- else if (i > 0)
- *ap = xd_right;
- else
- *ap = xd_center;
- return s;
- }
- #define CHK(s) if(!s){*error=1;return 0;}
- static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error)
- {
- char* cs;
- xdot_color clr;
- *error = 0;
- while (gv_isspace(*s))
- s++;
- switch (*s++) {
- case 'E':
- op->kind = xd_filled_ellipse;
- s = parseRect(s, &op->u.ellipse);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_ellipse];
- break;
- case 'e':
- op->kind = xd_unfilled_ellipse;
- s = parseRect(s, &op->u.ellipse);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_ellipse];
- break;
- case 'P':
- op->kind = xd_filled_polygon;
- s = parsePolyline(s, &op->u.polygon);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_polygon];
- break;
- case 'p':
- op->kind = xd_unfilled_polygon;
- s = parsePolyline(s, &op->u.polygon);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_polygon];
- break;
- case 'b':
- op->kind = xd_filled_bezier;
- s = parsePolyline(s, &op->u.bezier);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_bezier];
- break;
- case 'B':
- op->kind = xd_unfilled_bezier;
- s = parsePolyline(s, &op->u.bezier);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_bezier];
- break;
- case 'c':
- s = parseString(s, &cs);
- CHK(s);
- 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_color];
- }
- break;
- case 'C':
- s = parseString(s, &cs);
- CHK(s);
- 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_color];
- }
- break;
- case 'L':
- op->kind = xd_polyline;
- s = parsePolyline(s, &op->u.polyline);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_polyline];
- break;
- case 'T':
- op->kind = xd_text;
- s = parseReal(s, &op->u.text.x);
- CHK(s);
- s = parseReal(s, &op->u.text.y);
- CHK(s);
- s = parseAlign(s, &op->u.text.align);
- CHK(s);
- s = parseReal(s, &op->u.text.width);
- CHK(s);
- s = parseString(s, &op->u.text.text);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_text];
- break;
- case 'F':
- op->kind = xd_font;
- s = parseReal(s, &op->u.font.size);
- CHK(s);
- s = parseString(s, &op->u.font.name);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_font];
- break;
- case 'S':
- op->kind = xd_style;
- s = parseString(s, &op->u.style);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_style];
- break;
- case 'I':
- op->kind = xd_image;
- s = parseRect(s, &op->u.image.pos);
- CHK(s);
- s = parseString(s, &op->u.image.name);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_image];
- break;
- case 't':
- op->kind = xd_fontchar;
- s = parseUInt(s, &op->u.fontchar);
- CHK(s);
- if (ops)
- op->drawfunc = ops[xop_fontchar];
- break;
- case '\0':
- s = 0;
- break;
- default:
- *error = 1;
- s = 0;
- break;
- }
- return s;
- }
- #define XDBSIZE 100
- /* parseXDotFOn:
- * Parse and append additional xops onto a given xdot object.
- * Return x.
- */
- xdot *parseXDotFOn(char *s, drawfunc_t fns[], size_t sz, xdot *x) {
- xdot_op op;
- char *ops;
- size_t oldsz, bufsz;
- int error;
- if (!s)
- return x;
- if (!x) {
- x = gv_alloc(sizeof(*x));
- if (sz <= sizeof(xdot_op))
- sz = sizeof(xdot_op);
- /* cnt, freefunc, ops, flags zeroed by gv_alloc */
- x->sz = sz;
- }
- size_t initcnt = x->cnt;
- sz = x->sz;
- if (initcnt == 0) {
- bufsz = XDBSIZE;
- ops = gv_calloc(XDBSIZE, sz);
- }
- else {
- bufsz = initcnt + XDBSIZE;
- ops = gv_recalloc(x->ops, initcnt, bufsz, sz);
- }
- while ((s = parseOp(&op, s, fns, &error))) {
- if (x->cnt == bufsz) {
- oldsz = bufsz;
- bufsz *= 2;
- ops = gv_recalloc(ops, oldsz, bufsz, sz);
- }
- *(xdot_op *) (ops + x->cnt * sz) = op;
- x->cnt++;
- }
- if (error)
- x->flags |= XDOT_PARSE_ERROR;
- if (x->cnt) {
- x->ops = gv_recalloc(ops, bufsz, x->cnt, sz);
- }
- else {
- free (ops);
- free (x);
- x = NULL;
- }
- return x;
- }
- xdot *parseXDotF(char *s, drawfunc_t fns[], size_t sz) {
- return parseXDotFOn (s, fns, sz, NULL);
- }
- xdot *parseXDot(char *s)
- {
- return parseXDotF(s, 0, 0);
- }
- typedef int (*pf)(void*, char*, ...);
- static void printRect(xdot_rect * r, pf print, void *info)
- {
- agxbuf buf = {0};
- agxbprint(&buf, " %.02f", r->x);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbprint(&buf, " %.02f", r->y);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbprint(&buf, " %.02f", r->w);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbprint(&buf, " %.02f", r->h);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbfree(&buf);
- }
- static void printPolyline(xdot_polyline * p, pf print, void *info)
- {
- agxbuf buf = {0};
- print(info, " %" PRISIZE_T, p->cnt);
- for (size_t i = 0; i < p->cnt; i++) {
- agxbprint(&buf, " %.02f", p->pts[i].x);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbprint(&buf, " %.02f", p->pts[i].y);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- }
- agxbfree(&buf);
- }
- static void printString(char *p, pf print, void *info)
- {
- print(info, " %" PRISIZE_T " -%s", strlen(p), p);
- }
- static void printFloat(double f, pf print, void *info, int space) {
- agxbuf buf = {0};
- if (space)
- agxbprint(&buf, " %.02f", f);
- else
- agxbprint(&buf, "%.02f", f);
- agxbuf_trim_zeros(&buf);
- print(info, "%s", agxbuse(&buf));
- agxbfree(&buf);
- }
- static void printAlign(xdot_align a, pf print, void *info)
- {
- switch (a) {
- case xd_left:
- print(info, " -1");
- break;
- case xd_right:
- print(info, " 1");
- break;
- case xd_center:
- print(info, " 0");
- break;
- default:
- UNREACHABLE();
- }
- }
- 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, (pf)agxbprint, xb, 0);
- printFloat (cp->u.ling.y0, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ling.x1, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ling.y1, (pf)agxbprint, xb, 1);
- n_stops = cp->u.ling.n_stops;
- stops = cp->u.ling.stops;
- }
- else {
- agxbputc (xb, '(');
- printFloat (cp->u.ring.x0, (pf)agxbprint, xb, 0);
- printFloat (cp->u.ring.y0, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ring.r0, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ring.x1, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ring.y1, (pf)agxbprint, xb, 1);
- printFloat (cp->u.ring.r1, (pf)agxbprint, xb, 1);
- n_stops = cp->u.ring.n_stops;
- stops = cp->u.ring.stops;
- }
- agxbprint(xb, " %d", n_stops);
- for (i = 0; i < n_stops; i++) {
- printFloat (stops[i].frac, (pf)agxbprint, xb, 1);
- printString (stops[i].color, (pf)agxbprint, 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 = {0};
- switch (op->kind) {
- case xd_filled_ellipse:
- print(info, "E");
- printRect(&op->u.ellipse, print, info);
- break;
- case xd_unfilled_ellipse:
- print(info, "e");
- printRect(&op->u.ellipse, print, info);
- break;
- case xd_filled_polygon:
- print(info, "P");
- printPolyline(&op->u.polygon, print, info);
- break;
- case xd_unfilled_polygon:
- print(info, "p");
- printPolyline(&op->u.polygon, print, info);
- break;
- case xd_filled_bezier:
- print(info, "b");
- printPolyline(&op->u.bezier, print, info);
- break;
- case xd_unfilled_bezier:
- print(info, "B");
- printPolyline(&op->u.bezier, print, info);
- break;
- case xd_pen_color:
- print(info, "c");
- printString(op->u.color, print, info);
- break;
- case xd_grad_pen_color:
- print(info, "c");
- toGradString (&xb, &op->u.grad_color);
- printString(agxbuse(&xb), print, info);
- break;
- case xd_fill_color:
- print(info, "C");
- printString(op->u.color, print, info);
- break;
- case xd_grad_fill_color:
- print(info, "C");
- toGradString (&xb, &op->u.grad_color);
- printString(agxbuse(&xb), print, info);
- break;
- case xd_polyline:
- print(info, "L");
- printPolyline(&op->u.polyline, print, info);
- break;
- case xd_text:
- print(info, "T %.f %.f", op->u.text.x, op->u.text.y);
- printAlign(op->u.text.align, print, info);
- print(info, " %.f", op->u.text.width);
- printString(op->u.text.text, print, info);
- break;
- case xd_font:
- print(info, "F");
- printFloat(op->u.font.size, print, info, 1);
- printString(op->u.font.name, print, info);
- break;
- case xd_fontchar:
- print(info, "t %u", op->u.fontchar);
- break;
- case xd_style:
- print(info, "S");
- printString(op->u.style, print, info);
- break;
- case xd_image:
- print(info, "I");
- printRect(&op->u.image.pos, print, info);
- printString(op->u.image.name, print, info);
- break;
- default: // invalid type; ignore
- break;
- }
- if (more)
- print(info, " ");
- agxbfree (&xb);
- }
- static void jsonRect(xdot_rect * r, pf print, void *info)
- {
- print(info, "[%.06f,%.06f,%.06f,%.06f]", r->x, r->y, r->w, r->h);
- }
- static void jsonPolyline(xdot_polyline * p, pf print, void *info)
- {
- print(info, "[");
- for (size_t i = 0; i < p->cnt; i++) {
- print(info, "%.06f,%.06f", p->pts[i].x, p->pts[i].y);
- if (i < p->cnt-1) print(info, ",");
- }
- print(info, "]");
- }
- static void jsonString(char *p, pf print, void *info)
- {
- char c;
- print(info, "\"");
- while ((c = *p++)) {
- if (c == '"') print(info, "\\\"");
- else if (c == '\\') print(info, "\\\\");
- else print(info, "%c", c);
- }
- print(info, "\"");
- }
- static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more)
- {
- agxbuf xb = {0};
- switch (op->kind) {
- case xd_filled_ellipse:
- print(info, "{\"E\" : ");
- jsonRect(&op->u.ellipse, print, info);
- break;
- case xd_unfilled_ellipse:
- print(info, "{\"e\" : ");
- jsonRect(&op->u.ellipse, print, info);
- break;
- case xd_filled_polygon:
- print(info, "{\"P\" : ");
- jsonPolyline(&op->u.polygon, print, info);
- break;
- case xd_unfilled_polygon:
- print(info, "{\"p\" : ");
- jsonPolyline(&op->u.polygon, print, info);
- break;
- case xd_filled_bezier:
- print(info, "{\"b\" : ");
- jsonPolyline(&op->u.bezier, print, info);
- break;
- case xd_unfilled_bezier:
- print(info, "{\"B\" : ");
- jsonPolyline(&op->u.bezier, print, info);
- break;
- case xd_pen_color:
- print(info, "{\"c\" : ");
- jsonString(op->u.color, print, info);
- break;
- case xd_grad_pen_color:
- print(info, "{\"c\" : ");
- toGradString (&xb, &op->u.grad_color);
- jsonString(agxbuse(&xb), print, info);
- break;
- case xd_fill_color:
- print(info, "{\"C\" : ");
- jsonString(op->u.color, print, info);
- break;
- case xd_grad_fill_color:
- print(info, "{\"C\" : ");
- toGradString (&xb, &op->u.grad_color);
- jsonString(agxbuse(&xb), print, info);
- break;
- case xd_polyline:
- print(info, "{\"L\" :");
- jsonPolyline(&op->u.polyline, print, info);
- break;
- case xd_text:
- print(info, "{\"T\" : [ %.f, %.f,", op->u.text.x, op->u.text.y);
- printAlign(op->u.text.align, print, info);
- print(info, ", %.f,", op->u.text.width);
- jsonString(op->u.text.text, print, info);
- print(info, "]");
- break;
- case xd_font:
- print(info, "{\"F\" : [");
- op->kind = xd_font;
- printFloat(op->u.font.size, print, info, 1);
- print(info, ",");
- jsonString(op->u.font.name, print, info);
- print(info, "]");
- break;
- case xd_fontchar:
- print(info, "{\"t\" : %u", op->u.fontchar);
- break;
- case xd_style:
- print(info, "{\"S\" : ");
- jsonString(op->u.style, print, info);
- break;
- case xd_image:
- print(info, "{\"I\" : [");
- jsonRect(&op->u.image.pos, print, info);
- print(info, ",");
- jsonString(op->u.image.name, print, info);
- print(info, "]");
- break;
- default: // invalid type; ignore
- break;
- }
- if (more)
- print(info, "},\n");
- else
- print(info, "}\n");
- agxbfree (&xb);
- }
- static void _printXDot(xdot * x, pf print, void *info, print_op ofn)
- {
- xdot_op *op;
- char *base = (char *) (x->ops);
- for (size_t i = 0; i < x->cnt; i++) {
- op = (xdot_op *) (base + i * x->sz);
- ofn(op, print, info, i < x->cnt - 1);
- }
- }
- char *sprintXDot(xdot * x)
- {
- agxbuf xb = {0};
- _printXDot(x, (pf)agxbprint, &xb, printXDot_Op);
- return agxbdisown(&xb);
- }
- void fprintXDot(FILE * fp, xdot * x)
- {
- _printXDot(x, (pf)fprintf, fp, printXDot_Op);
- }
- void jsonXDot(FILE * fp, xdot * x)
- {
- fputs ("[\n", fp);
- _printXDot(x, (pf)fprintf, fp, jsonXDot_Op);
- fputs ("]\n", fp);
- }
- static void freeXOpData(xdot_op * x)
- {
- switch (x->kind) {
- case xd_filled_polygon:
- case xd_unfilled_polygon:
- free(x->u.polyline.pts);
- break;
- case xd_filled_bezier:
- case xd_unfilled_bezier:
- free(x->u.polyline.pts);
- break;
- case xd_polyline:
- free(x->u.polyline.pts);
- break;
- case xd_text:
- free(x->u.text.text);
- break;
- case xd_fill_color:
- 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;
- case xd_style:
- free(x->u.style);
- break;
- case xd_image:
- free(x->u.image.name);
- break;
- default:
- break;
- }
- }
- void freeXDot (xdot * x)
- {
- xdot_op *op;
- char *base;
- freefunc_t ff = x->freefunc;
- if (!x) return;
- base = (char *) (x->ops);
- for (size_t i = 0; i < x->cnt; i++) {
- op = (xdot_op *) (base + i * x->sz);
- if (ff) ff (op);
- freeXOpData(op);
- }
- free(base);
- free(x);
- }
- int statXDot (xdot* x, xdot_stats* sp)
- {
- xdot_op *op;
- char *base;
- if (!x || !sp) return 1;
- memset(sp, 0, sizeof(xdot_stats));
- sp->cnt = x->cnt;
- base = (char *) (x->ops);
- for (size_t i = 0; i < x->cnt; i++) {
- op = (xdot_op *) (base + i * x->sz);
- switch (op->kind) {
- case xd_filled_ellipse:
- case xd_unfilled_ellipse:
- sp->n_ellipse++;
- break;
- case xd_filled_polygon:
- case xd_unfilled_polygon:
- sp->n_polygon++;
- sp->n_polygon_pts += op->u.polygon.cnt;
- break;
- case xd_filled_bezier:
- case xd_unfilled_bezier:
- sp->n_bezier++;
- sp->n_bezier_pts += op->u.bezier.cnt;
- break;
- case xd_polyline:
- sp->n_polyline++;
- sp->n_polyline_pts += op->u.polyline.cnt;
- break;
- case xd_text:
- sp->n_text++;
- break;
- case xd_image:
- sp->n_image++;
- break;
- case xd_fill_color:
- case xd_pen_color:
- sp->n_color++;
- break;
- case xd_grad_fill_color:
- case xd_grad_pen_color:
- sp->n_gradcolor++;
- break;
- case xd_font:
- sp->n_font++;
- break;
- case xd_fontchar:
- sp->n_fontchar++;
- break;
- case xd_style:
- sp->n_style++;
- break;
- default :
- break;
- }
- }
-
- return 0;
- }
- #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 = gv_calloc(clr->u.ring.n_stops, sizeof(stops[0]));
- 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 = gv_calloc(clr->u.ling.n_stops, sizeof(stops[0]));
- 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 (gv_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);
- }
- }
- /**
- * @dir lib/xdot
- * @brief parsing and deparsing of xdot operations, API xdot.h
- *
- * [man 3 xdot](https://graphviz.org/pdf/xdot.3.pdf)
- *
- */
|