1 | /* vim:set shiftwidth=4 ts=8: */ |
2 | |
3 | /************************************************************************* |
4 | * Copyright (c) 2011 AT&T Intellectual Property |
5 | * All rights reserved. This program and the accompanying materials |
6 | * are made available under the terms of the Eclipse Public License v1.0 |
7 | * which accompanies this distribution, and is available at |
8 | * http://www.eclipse.org/legal/epl-v10.html |
9 | * |
10 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
11 | *************************************************************************/ |
12 | |
13 | #include <xdot.h> |
14 | #include <stdlib.h> |
15 | #include <string.h> |
16 | #include <ctype.h> |
17 | |
18 | #define NEW(t) (t*)calloc(1, sizeof(t)) |
19 | #define N_NEW(n,t) (t*)calloc((n), sizeof(t)) |
20 | |
21 | typedef struct { |
22 | unsigned char *buf; /* start of buffer */ |
23 | unsigned char *ptr; /* next place to write */ |
24 | unsigned char *eptr; /* end of buffer */ |
25 | int dyna; /* true if buffer is malloc'ed */ |
26 | } agxbuf; |
27 | |
28 | #define agxbputc(X,C) ((((X)->ptr >= (X)->eptr) ? agxbmore(X,1) : 0), \ |
29 | (void)(*(X)->ptr++ = ((unsigned char)C))) |
30 | #define agxbuse(X) (agxbputc(X,'\0'),(char*)((X)->ptr = (X)->buf)) |
31 | |
32 | static void agxbinit(agxbuf * xb, unsigned int hint, unsigned char *init) |
33 | { |
34 | if (init) { |
35 | xb->buf = init; |
36 | xb->dyna = 0; |
37 | } else { |
38 | if (hint == 0) |
39 | hint = BUFSIZ; |
40 | xb->dyna = 1; |
41 | xb->buf = N_NEW(hint, unsigned char); |
42 | } |
43 | xb->eptr = xb->buf + hint; |
44 | xb->ptr = xb->buf; |
45 | *xb->ptr = '\0'; |
46 | } |
47 | static int agxbmore(agxbuf * xb, unsigned int ssz) |
48 | { |
49 | int cnt; /* current no. of characters in buffer */ |
50 | int size; /* current buffer size */ |
51 | int nsize; /* new buffer size */ |
52 | unsigned char *nbuf; /* new buffer */ |
53 | |
54 | size = xb->eptr - xb->buf; |
55 | nsize = 2 * size; |
56 | if (size + ssz > nsize) |
57 | nsize = size + ssz; |
58 | cnt = xb->ptr - xb->buf; |
59 | if (xb->dyna) { |
60 | nbuf = realloc(xb->buf, nsize); |
61 | } else { |
62 | nbuf = N_NEW(nsize, unsigned char); |
63 | memcpy(nbuf, xb->buf, cnt); |
64 | xb->dyna = 1; |
65 | } |
66 | xb->buf = nbuf; |
67 | xb->ptr = xb->buf + cnt; |
68 | xb->eptr = xb->buf + nsize; |
69 | return 0; |
70 | } |
71 | |
72 | static int agxbput(char *s, agxbuf * xb) |
73 | { |
74 | unsigned int ssz = strlen(s); |
75 | if (xb->ptr + ssz > xb->eptr) |
76 | agxbmore(xb, ssz); |
77 | memcpy(xb->ptr, s, ssz); |
78 | xb->ptr += ssz; |
79 | return ssz; |
80 | } |
81 | |
82 | /* agxbfree: |
83 | * Free any malloced resources. |
84 | */ |
85 | static void agxbfree(agxbuf * xb) |
86 | { |
87 | if (xb->dyna) |
88 | free(xb->buf); |
89 | } |
90 | |
91 | /* the parse functions should return NULL on error */ |
92 | static char *parseReal(char *s, double *fp) |
93 | { |
94 | char *p; |
95 | double d; |
96 | |
97 | d = strtod(s, &p); |
98 | if (p == s) return 0; |
99 | |
100 | *fp = d; |
101 | return (p); |
102 | } |
103 | |
104 | |
105 | static char *parseInt(char *s, int *ip) |
106 | { |
107 | char* endp; |
108 | |
109 | #ifdef UNUSED |
110 | r = sscanf(s, "%d%n" , ip, &sz); |
111 | if (r != 1) return 0; |
112 | else return (s + sz); |
113 | #endif |
114 | |
115 | *ip = (int)strtol (s, &endp, 10); |
116 | if (s == endp) |
117 | return 0; |
118 | else |
119 | return endp; |
120 | } |
121 | |
122 | static char *parseUInt(char *s, unsigned int *ip) |
123 | { |
124 | char* endp; |
125 | |
126 | *ip = (unsigned int)strtoul (s, &endp, 10); |
127 | if (s == endp) |
128 | return 0; |
129 | else |
130 | return endp; |
131 | } |
132 | |
133 | #ifdef UNUSED |
134 | static char *parsePoint(char *s, xdot_point * pp) |
135 | { |
136 | int r, sz; |
137 | r = sscanf(s, "%lf %lf%n" , &(pp->x), &(pp->y), &sz); |
138 | if (r != 2) return 0; |
139 | pp->z = 0; |
140 | return (s + sz); |
141 | } |
142 | #endif |
143 | |
144 | static char *parseRect(char *s, xdot_rect * rp) |
145 | { |
146 | char* endp; |
147 | #ifdef UNUSED |
148 | int r, sz; |
149 | r = sscanf(s, "%lf %lf %lf %lf%n" , &(rp->x), &(rp->y), &(rp->w), |
150 | &(rp->h), &sz); |
151 | if (r != 4) return 0; |
152 | else return (s + sz); |
153 | #endif |
154 | |
155 | rp->x = strtod (s, &endp); |
156 | if (s == endp) |
157 | return 0; |
158 | else |
159 | s = endp; |
160 | |
161 | rp->y = strtod (s, &endp); |
162 | if (s == endp) |
163 | return 0; |
164 | else |
165 | s = endp; |
166 | |
167 | rp->w = strtod (s, &endp); |
168 | if (s == endp) |
169 | return 0; |
170 | else |
171 | s = endp; |
172 | |
173 | rp->h = strtod (s, &endp); |
174 | if (s == endp) |
175 | return 0; |
176 | else |
177 | s = endp; |
178 | |
179 | return s; |
180 | } |
181 | |
182 | static char *parsePolyline(char *s, xdot_polyline * pp) |
183 | { |
184 | int i; |
185 | xdot_point *pts; |
186 | xdot_point *ps; |
187 | char* endp; |
188 | |
189 | s = parseInt(s, &i); |
190 | if (!s) return s; |
191 | pts = ps = N_NEW(i, xdot_point); |
192 | pp->cnt = i; |
193 | for (i = 0; i < pp->cnt; i++) { |
194 | ps->x = strtod (s, &endp); |
195 | if (s == endp) { |
196 | free (pts); |
197 | return 0; |
198 | } |
199 | else |
200 | s = endp; |
201 | ps->y = strtod (s, &endp); |
202 | if (s == endp) { |
203 | free (pts); |
204 | return 0; |
205 | } |
206 | else |
207 | s = endp; |
208 | ps->z = 0; |
209 | ps++; |
210 | } |
211 | pp->pts = pts; |
212 | return s; |
213 | } |
214 | |
215 | static char *parseString(char *s, char **sp) |
216 | { |
217 | int i; |
218 | char *c; |
219 | char *p; |
220 | s = parseInt(s, &i); |
221 | if (!s || (i <= 0)) return 0; |
222 | while (*s && (*s != '-')) s++; |
223 | if (*s) s++; |
224 | else { |
225 | return 0; |
226 | } |
227 | c = N_NEW(i + 1, char); |
228 | p = c; |
229 | while ((i > 0) && *s) { |
230 | *p++ = *s++; |
231 | i--; |
232 | } |
233 | if (i > 0) { |
234 | free (c); |
235 | return 0; |
236 | } |
237 | |
238 | *p = '\0'; |
239 | *sp = c; |
240 | return s; |
241 | } |
242 | |
243 | static char *parseAlign(char *s, xdot_align * ap) |
244 | { |
245 | int i; |
246 | s = parseInt(s, &i); |
247 | |
248 | if (i < 0) |
249 | *ap = xd_left; |
250 | else if (i > 0) |
251 | *ap = xd_right; |
252 | else |
253 | *ap = xd_center; |
254 | return s; |
255 | } |
256 | |
257 | #define CHK(s) if(!s){*error=1;return 0;} |
258 | |
259 | static char *parseOp(xdot_op * op, char *s, drawfunc_t ops[], int* error) |
260 | { |
261 | char* cs; |
262 | xdot_color clr; |
263 | |
264 | *error = 0; |
265 | while (isspace(*s)) |
266 | s++; |
267 | switch (*s++) { |
268 | case 'E': |
269 | op->kind = xd_filled_ellipse; |
270 | s = parseRect(s, &op->u.ellipse); |
271 | CHK(s); |
272 | if (ops) |
273 | op->drawfunc = ops[xop_ellipse]; |
274 | break; |
275 | |
276 | case 'e': |
277 | op->kind = xd_unfilled_ellipse; |
278 | s = parseRect(s, &op->u.ellipse); |
279 | CHK(s); |
280 | if (ops) |
281 | op->drawfunc = ops[xop_ellipse]; |
282 | break; |
283 | |
284 | case 'P': |
285 | op->kind = xd_filled_polygon; |
286 | s = parsePolyline(s, &op->u.polygon); |
287 | CHK(s); |
288 | if (ops) |
289 | op->drawfunc = ops[xop_polygon]; |
290 | break; |
291 | |
292 | case 'p': |
293 | op->kind = xd_unfilled_polygon; |
294 | s = parsePolyline(s, &op->u.polygon); |
295 | CHK(s); |
296 | if (ops) |
297 | op->drawfunc = ops[xop_polygon]; |
298 | break; |
299 | |
300 | case 'b': |
301 | op->kind = xd_filled_bezier; |
302 | s = parsePolyline(s, &op->u.bezier); |
303 | CHK(s); |
304 | if (ops) |
305 | op->drawfunc = ops[xop_bezier]; |
306 | break; |
307 | |
308 | case 'B': |
309 | op->kind = xd_unfilled_bezier; |
310 | s = parsePolyline(s, &op->u.bezier); |
311 | CHK(s); |
312 | if (ops) |
313 | op->drawfunc = ops[xop_bezier]; |
314 | break; |
315 | |
316 | case 'c': |
317 | s = parseString(s, &cs); |
318 | CHK(s); |
319 | cs = parseXDotColor (cs, &clr); |
320 | CHK(cs); |
321 | if (clr.type == xd_none) { |
322 | op->kind = xd_pen_color; |
323 | op->u.color = clr.u.clr; |
324 | if (ops) |
325 | op->drawfunc = ops[xop_pen_color]; |
326 | } |
327 | else { |
328 | op->kind = xd_grad_pen_color; |
329 | op->u.grad_color = clr; |
330 | if (ops) |
331 | op->drawfunc = ops[xop_grad_color]; |
332 | } |
333 | break; |
334 | |
335 | case 'C': |
336 | s = parseString(s, &cs); |
337 | CHK(s); |
338 | cs = parseXDotColor (cs, &clr); |
339 | CHK(cs); |
340 | if (clr.type == xd_none) { |
341 | op->kind = xd_fill_color; |
342 | op->u.color = clr.u.clr; |
343 | if (ops) |
344 | op->drawfunc = ops[xop_fill_color]; |
345 | } |
346 | else { |
347 | op->kind = xd_grad_fill_color; |
348 | op->u.grad_color = clr; |
349 | if (ops) |
350 | op->drawfunc = ops[xop_grad_color]; |
351 | } |
352 | break; |
353 | |
354 | case 'L': |
355 | op->kind = xd_polyline; |
356 | s = parsePolyline(s, &op->u.polyline); |
357 | CHK(s); |
358 | if (ops) |
359 | op->drawfunc = ops[xop_polyline]; |
360 | break; |
361 | |
362 | case 'T': |
363 | op->kind = xd_text; |
364 | s = parseReal(s, &op->u.text.x); |
365 | CHK(s); |
366 | s = parseReal(s, &op->u.text.y); |
367 | CHK(s); |
368 | s = parseAlign(s, &op->u.text.align); |
369 | CHK(s); |
370 | s = parseReal(s, &op->u.text.width); |
371 | CHK(s); |
372 | s = parseString(s, &op->u.text.text); |
373 | CHK(s); |
374 | if (ops) |
375 | op->drawfunc = ops[xop_text]; |
376 | break; |
377 | |
378 | case 'F': |
379 | op->kind = xd_font; |
380 | s = parseReal(s, &op->u.font.size); |
381 | CHK(s); |
382 | s = parseString(s, &op->u.font.name); |
383 | CHK(s); |
384 | if (ops) |
385 | op->drawfunc = ops[xop_font]; |
386 | break; |
387 | |
388 | case 'S': |
389 | op->kind = xd_style; |
390 | s = parseString(s, &op->u.style); |
391 | CHK(s); |
392 | if (ops) |
393 | op->drawfunc = ops[xop_style]; |
394 | break; |
395 | |
396 | case 'I': |
397 | op->kind = xd_image; |
398 | s = parseRect(s, &op->u.image.pos); |
399 | CHK(s); |
400 | s = parseString(s, &op->u.image.name); |
401 | CHK(s); |
402 | if (ops) |
403 | op->drawfunc = ops[xop_image]; |
404 | break; |
405 | |
406 | case 't': |
407 | op->kind = xd_fontchar; |
408 | s = parseUInt(s, &op->u.fontchar); |
409 | CHK(s); |
410 | if (ops) |
411 | op->drawfunc = ops[xop_fontchar]; |
412 | break; |
413 | |
414 | |
415 | case '\0': |
416 | s = 0; |
417 | break; |
418 | |
419 | default: |
420 | *error = 1; |
421 | s = 0; |
422 | break; |
423 | } |
424 | return s; |
425 | } |
426 | |
427 | #define XDBSIZE 100 |
428 | |
429 | /* parseXDotFOn: |
430 | * Parse and append additional xops onto a given xdot object. |
431 | * Return x. |
432 | */ |
433 | xdot *parseXDotFOn (char *s, drawfunc_t fns[], int sz, xdot* x) |
434 | { |
435 | xdot_op op; |
436 | char *ops; |
437 | int oldsz, bufsz; |
438 | int error; |
439 | int initcnt; |
440 | |
441 | if (!s) |
442 | return x; |
443 | |
444 | if (!x) { |
445 | x = NEW(xdot); |
446 | if (sz <= sizeof(xdot_op)) |
447 | sz = sizeof(xdot_op); |
448 | |
449 | /* cnt, freefunc, ops, flags zeroed by NEW */ |
450 | x->sz = sz; |
451 | } |
452 | initcnt = x->cnt; |
453 | sz = x->sz; |
454 | |
455 | if (initcnt == 0) { |
456 | bufsz = XDBSIZE; |
457 | ops = (char *) calloc(XDBSIZE, sz); |
458 | } |
459 | else { |
460 | ops = (char*)(x->ops); |
461 | bufsz = initcnt + XDBSIZE; |
462 | ops = (char *) realloc(ops, bufsz * sz); |
463 | memset(ops + (initcnt*sz), '\0', (bufsz - initcnt)*sz); |
464 | } |
465 | |
466 | while ((s = parseOp(&op, s, fns, &error))) { |
467 | if (x->cnt == bufsz) { |
468 | oldsz = bufsz; |
469 | bufsz *= 2; |
470 | ops = (char *) realloc(ops, bufsz * sz); |
471 | memset(ops + (oldsz*sz), '\0', (bufsz - oldsz)*sz); |
472 | } |
473 | *(xdot_op *) (ops + (x->cnt * sz)) = op; |
474 | x->cnt++; |
475 | } |
476 | if (error) |
477 | x->flags |= XDOT_PARSE_ERROR; |
478 | if (x->cnt) { |
479 | x->ops = (xdot_op *) realloc(ops, x->cnt * sz); |
480 | } |
481 | else { |
482 | free (ops); |
483 | free (x); |
484 | x = NULL; |
485 | } |
486 | |
487 | return x; |
488 | |
489 | } |
490 | |
491 | xdot *parseXDotF(char *s, drawfunc_t fns[], int sz) |
492 | { |
493 | return parseXDotFOn (s, fns, sz, NULL); |
494 | } |
495 | |
496 | xdot *parseXDot(char *s) |
497 | { |
498 | return parseXDotF(s, 0, 0); |
499 | } |
500 | |
501 | typedef void (*pf) (char *, void *); |
502 | |
503 | /* trim: |
504 | * Trailing zeros are removed and decimal point, if possible. |
505 | */ |
506 | static void trim (char* buf) |
507 | { |
508 | char* dotp; |
509 | char* p; |
510 | |
511 | if ((dotp = strchr (buf,'.'))) { |
512 | p = dotp+1; |
513 | while (*p) p++; // find end of string |
514 | p--; |
515 | while (*p == '0') *p-- = '\0'; |
516 | if (*p == '.') // If all decimals were zeros, remove ".". |
517 | *p = '\0'; |
518 | else |
519 | p++; |
520 | } |
521 | } |
522 | |
523 | static void printRect(xdot_rect * r, pf print, void *info) |
524 | { |
525 | char buf[128]; |
526 | |
527 | sprintf(buf, " %.02f" , r->x); |
528 | trim(buf); |
529 | print(buf, info); |
530 | sprintf(buf, " %.02f" , r->y); |
531 | trim(buf); |
532 | print(buf, info); |
533 | sprintf(buf, " %.02f" , r->w); |
534 | trim(buf); |
535 | print(buf, info); |
536 | sprintf(buf, " %.02f" , r->h); |
537 | trim(buf); |
538 | print(buf, info); |
539 | } |
540 | |
541 | static void printPolyline(xdot_polyline * p, pf print, void *info) |
542 | { |
543 | int i; |
544 | char buf[512]; |
545 | |
546 | sprintf(buf, " %d" , p->cnt); |
547 | print(buf, info); |
548 | for (i = 0; i < p->cnt; i++) { |
549 | sprintf(buf, " %.02f" , p->pts[i].x); |
550 | trim(buf); |
551 | print(buf, info); |
552 | sprintf(buf, " %.02f" , p->pts[i].y); |
553 | trim(buf); |
554 | print(buf, info); |
555 | } |
556 | } |
557 | |
558 | static void printString(char *p, pf print, void *info) |
559 | { |
560 | char buf[30]; |
561 | |
562 | sprintf(buf, " %d -" , (int) strlen(p)); |
563 | print(buf, info); |
564 | print(p, info); |
565 | } |
566 | |
567 | static void printInt(int i, pf print, void *info) |
568 | { |
569 | char buf[30]; |
570 | |
571 | sprintf(buf, " %d" , i); |
572 | print(buf, info); |
573 | } |
574 | |
575 | static void printFloat(float f, pf print, void *info, int space) |
576 | { |
577 | char buf[128]; |
578 | |
579 | if (space) |
580 | sprintf(buf, " %.02f" , f); |
581 | else |
582 | sprintf(buf, "%.02f" , f); |
583 | trim (buf); |
584 | print(buf, info); |
585 | } |
586 | |
587 | static void printAlign(xdot_align a, pf print, void *info) |
588 | { |
589 | switch (a) { |
590 | case xd_left: |
591 | print(" -1" , info); |
592 | break; |
593 | case xd_right: |
594 | print(" 1" , info); |
595 | break; |
596 | case xd_center: |
597 | print(" 0" , info); |
598 | break; |
599 | } |
600 | } |
601 | |
602 | static void |
603 | gradprint (char* s, void* v) |
604 | { |
605 | agxbput(s, (agxbuf*)v); |
606 | } |
607 | |
608 | static void |
609 | toGradString (agxbuf* xb, xdot_color* cp) |
610 | { |
611 | int i, n_stops; |
612 | xdot_color_stop* stops; |
613 | |
614 | if (cp->type == xd_linear) { |
615 | agxbputc (xb, '['); |
616 | printFloat (cp->u.ling.x0, gradprint, xb, 0); |
617 | printFloat (cp->u.ling.y0, gradprint, xb, 1); |
618 | printFloat (cp->u.ling.x1, gradprint, xb, 1); |
619 | printFloat (cp->u.ling.y1, gradprint, xb, 1); |
620 | n_stops = cp->u.ling.n_stops; |
621 | stops = cp->u.ling.stops; |
622 | } |
623 | else { |
624 | agxbputc (xb, '('); |
625 | printFloat (cp->u.ring.x0, gradprint, xb, 0); |
626 | printFloat (cp->u.ring.y0, gradprint, xb, 1); |
627 | printFloat (cp->u.ring.r0, gradprint, xb, 1); |
628 | printFloat (cp->u.ring.x1, gradprint, xb, 1); |
629 | printFloat (cp->u.ring.y1, gradprint, xb, 1); |
630 | printFloat (cp->u.ring.r1, gradprint, xb, 1); |
631 | n_stops = cp->u.ring.n_stops; |
632 | stops = cp->u.ring.stops; |
633 | } |
634 | printInt (n_stops, gradprint, xb); |
635 | for (i = 0; i < n_stops; i++) { |
636 | printFloat (stops[i].frac, gradprint, xb, 1); |
637 | printString (stops[i].color, gradprint, xb); |
638 | } |
639 | |
640 | if (cp->type == xd_linear) |
641 | agxbputc (xb, ']'); |
642 | else |
643 | agxbputc (xb, ')'); |
644 | } |
645 | |
646 | typedef void (*print_op)(xdot_op * op, pf print, void *info, int more); |
647 | |
648 | static void printXDot_Op(xdot_op * op, pf print, void *info, int more) |
649 | { |
650 | agxbuf xb; |
651 | unsigned char buf[BUFSIZ]; |
652 | |
653 | agxbinit (&xb, BUFSIZ, buf); |
654 | switch (op->kind) { |
655 | case xd_filled_ellipse: |
656 | print("E" , info); |
657 | printRect(&op->u.ellipse, print, info); |
658 | break; |
659 | case xd_unfilled_ellipse: |
660 | print("e" , info); |
661 | printRect(&op->u.ellipse, print, info); |
662 | break; |
663 | case xd_filled_polygon: |
664 | print("P" , info); |
665 | printPolyline(&op->u.polygon, print, info); |
666 | break; |
667 | case xd_unfilled_polygon: |
668 | print("p" , info); |
669 | printPolyline(&op->u.polygon, print, info); |
670 | break; |
671 | case xd_filled_bezier: |
672 | print("b" , info); |
673 | printPolyline(&op->u.bezier, print, info); |
674 | break; |
675 | case xd_unfilled_bezier: |
676 | print("B" , info); |
677 | printPolyline(&op->u.bezier, print, info); |
678 | break; |
679 | case xd_pen_color: |
680 | print("c" , info); |
681 | printString(op->u.color, print, info); |
682 | break; |
683 | case xd_grad_pen_color: |
684 | print("c" , info); |
685 | toGradString (&xb, &op->u.grad_color); |
686 | printString(agxbuse(&xb), print, info); |
687 | break; |
688 | case xd_fill_color: |
689 | print("C" , info); |
690 | printString(op->u.color, print, info); |
691 | break; |
692 | case xd_grad_fill_color: |
693 | print("C" , info); |
694 | toGradString (&xb, &op->u.grad_color); |
695 | printString(agxbuse(&xb), print, info); |
696 | break; |
697 | case xd_polyline: |
698 | print("L" , info); |
699 | printPolyline(&op->u.polyline, print, info); |
700 | break; |
701 | case xd_text: |
702 | print("T" , info); |
703 | printInt(op->u.text.x, print, info); |
704 | printInt(op->u.text.y, print, info); |
705 | printAlign(op->u.text.align, print, info); |
706 | printInt(op->u.text.width, print, info); |
707 | printString(op->u.text.text, print, info); |
708 | break; |
709 | case xd_font: |
710 | print("F" , info); |
711 | printFloat(op->u.font.size, print, info, 1); |
712 | printString(op->u.font.name, print, info); |
713 | break; |
714 | case xd_fontchar: |
715 | print("t" , info); |
716 | printInt(op->u.fontchar, print, info); |
717 | break; |
718 | case xd_style: |
719 | print("S" , info); |
720 | printString(op->u.style, print, info); |
721 | break; |
722 | case xd_image: |
723 | print("I" , info); |
724 | printRect(&op->u.image.pos, print, info); |
725 | printString(op->u.image.name, print, info); |
726 | break; |
727 | } |
728 | if (more) |
729 | print(" " , info); |
730 | agxbfree (&xb); |
731 | } |
732 | |
733 | static void jsonRect(xdot_rect * r, pf print, void *info) |
734 | { |
735 | char buf[128]; |
736 | |
737 | sprintf(buf, "[%.06f,%.06f,%.06f,%.06f]" , r->x, r->y, r->w, r->h); |
738 | print(buf, info); |
739 | } |
740 | |
741 | static void jsonPolyline(xdot_polyline * p, pf print, void *info) |
742 | { |
743 | int i; |
744 | char buf[128]; |
745 | |
746 | print("[" , info); |
747 | for (i = 0; i < p->cnt; i++) { |
748 | sprintf(buf, "%.06f,%.06f" , p->pts[i].x, p->pts[i].y); |
749 | print(buf, info); |
750 | if (i < p->cnt-1) print("," , info); |
751 | } |
752 | print("]" , info); |
753 | } |
754 | |
755 | static void jsonString(char *p, pf print, void *info) |
756 | { |
757 | unsigned char c, buf[BUFSIZ]; |
758 | agxbuf xb; |
759 | |
760 | agxbinit(&xb, BUFSIZ, buf); |
761 | agxbputc(&xb, '"'); |
762 | while ((c = *p++)) { |
763 | if (c == '"') agxbput("\\\"" , &xb); |
764 | else if (c == '\\') agxbput("\\\\" , &xb); |
765 | /* else if (c > 127) handle UTF-8 */ |
766 | else agxbputc(&xb, c); |
767 | } |
768 | agxbputc(&xb, '"'); |
769 | print(agxbuse(&xb), info); |
770 | agxbfree(&xb); |
771 | } |
772 | |
773 | static void jsonXDot_Op(xdot_op * op, pf print, void *info, int more) |
774 | { |
775 | agxbuf xb; |
776 | unsigned char buf[BUFSIZ]; |
777 | |
778 | agxbinit (&xb, BUFSIZ, buf); |
779 | switch (op->kind) { |
780 | case xd_filled_ellipse: |
781 | print("{E : " , info); |
782 | jsonRect(&op->u.ellipse, print, info); |
783 | break; |
784 | case xd_unfilled_ellipse: |
785 | print("{e : " , info); |
786 | jsonRect(&op->u.ellipse, print, info); |
787 | break; |
788 | case xd_filled_polygon: |
789 | print("{P : " , info); |
790 | jsonPolyline(&op->u.polygon, print, info); |
791 | break; |
792 | case xd_unfilled_polygon: |
793 | print("{p : " , info); |
794 | jsonPolyline(&op->u.polygon, print, info); |
795 | break; |
796 | case xd_filled_bezier: |
797 | print("{b : " , info); |
798 | jsonPolyline(&op->u.bezier, print, info); |
799 | break; |
800 | case xd_unfilled_bezier: |
801 | print("{B : " , info); |
802 | jsonPolyline(&op->u.bezier, print, info); |
803 | break; |
804 | case xd_pen_color: |
805 | print("{c : " , info); |
806 | jsonString(op->u.color, print, info); |
807 | break; |
808 | case xd_grad_pen_color: |
809 | print("{c : " , info); |
810 | toGradString (&xb, &op->u.grad_color); |
811 | jsonString(agxbuse(&xb), print, info); |
812 | break; |
813 | case xd_fill_color: |
814 | print("{C : " , info); |
815 | jsonString(op->u.color, print, info); |
816 | break; |
817 | case xd_grad_fill_color: |
818 | print("{C : " , info); |
819 | toGradString (&xb, &op->u.grad_color); |
820 | jsonString(agxbuse(&xb), print, info); |
821 | break; |
822 | case xd_polyline: |
823 | print("{L :" , info); |
824 | jsonPolyline(&op->u.polyline, print, info); |
825 | break; |
826 | case xd_text: |
827 | print("{T : [" , info); |
828 | printInt(op->u.text.x, print, info); |
829 | print("," , info); |
830 | printInt(op->u.text.y, print, info); |
831 | print("," , info); |
832 | printAlign(op->u.text.align, print, info); |
833 | print("," , info); |
834 | printInt(op->u.text.width, print, info); |
835 | print("," , info); |
836 | jsonString(op->u.text.text, print, info); |
837 | print("]" , info); |
838 | break; |
839 | case xd_font: |
840 | print("{F : [" , info); |
841 | op->kind = xd_font; |
842 | printFloat(op->u.font.size, print, info, 1); |
843 | print("," , info); |
844 | jsonString(op->u.font.name, print, info); |
845 | print("]" , info); |
846 | break; |
847 | case xd_fontchar: |
848 | print("{t : " , info); |
849 | printInt(op->u.fontchar, print, info); |
850 | break; |
851 | case xd_style: |
852 | print("{S : " , info); |
853 | jsonString(op->u.style, print, info); |
854 | break; |
855 | case xd_image: |
856 | print("{I : [" , info); |
857 | jsonRect(&op->u.image.pos, print, info); |
858 | print("," , info); |
859 | jsonString(op->u.image.name, print, info); |
860 | print("]" , info); |
861 | break; |
862 | } |
863 | if (more) |
864 | print("},\n" , info); |
865 | else |
866 | print("}\n" , info); |
867 | agxbfree (&xb); |
868 | } |
869 | |
870 | static void _printXDot(xdot * x, pf print, void *info, print_op ofn) |
871 | { |
872 | int i; |
873 | xdot_op *op; |
874 | char *base = (char *) (x->ops); |
875 | for (i = 0; i < x->cnt; i++) { |
876 | op = (xdot_op *) (base + i * x->sz); |
877 | ofn(op, print, info, (i < x->cnt - 1)); |
878 | } |
879 | } |
880 | |
881 | char *sprintXDot(xdot * x) |
882 | { |
883 | char *s; |
884 | unsigned char buf[BUFSIZ]; |
885 | agxbuf xb; |
886 | agxbinit(&xb, BUFSIZ, buf); |
887 | _printXDot(x, (pf) agxbput, &xb, printXDot_Op); |
888 | s = strdup(agxbuse(&xb)); |
889 | agxbfree(&xb); |
890 | |
891 | return s; |
892 | } |
893 | |
894 | void fprintXDot(FILE * fp, xdot * x) |
895 | { |
896 | _printXDot(x, (pf) fputs, fp, printXDot_Op); |
897 | } |
898 | |
899 | void jsonXDot(FILE * fp, xdot * x) |
900 | { |
901 | fputs ("[\n" , fp); |
902 | _printXDot(x, (pf) fputs, fp, jsonXDot_Op); |
903 | fputs ("]\n" , fp); |
904 | } |
905 | |
906 | static void freeXOpData(xdot_op * x) |
907 | { |
908 | switch (x->kind) { |
909 | case xd_filled_polygon: |
910 | case xd_unfilled_polygon: |
911 | free(x->u.polyline.pts); |
912 | break; |
913 | case xd_filled_bezier: |
914 | case xd_unfilled_bezier: |
915 | free(x->u.polyline.pts); |
916 | break; |
917 | case xd_polyline: |
918 | free(x->u.polyline.pts); |
919 | break; |
920 | case xd_text: |
921 | free(x->u.text.text); |
922 | break; |
923 | case xd_fill_color: |
924 | case xd_pen_color: |
925 | free(x->u.color); |
926 | break; |
927 | case xd_grad_fill_color: |
928 | case xd_grad_pen_color: |
929 | freeXDotColor (&x->u.grad_color); |
930 | break; |
931 | case xd_font: |
932 | free(x->u.font.name); |
933 | break; |
934 | case xd_style: |
935 | free(x->u.style); |
936 | break; |
937 | case xd_image: |
938 | free(x->u.image.name); |
939 | break; |
940 | default: |
941 | break; |
942 | } |
943 | } |
944 | |
945 | void freeXDot (xdot * x) |
946 | { |
947 | int i; |
948 | xdot_op *op; |
949 | char *base; |
950 | freefunc_t ff = x->freefunc; |
951 | |
952 | if (!x) return; |
953 | base = (char *) (x->ops); |
954 | for (i = 0; i < x->cnt; i++) { |
955 | op = (xdot_op *) (base + i * x->sz); |
956 | if (ff) ff (op); |
957 | freeXOpData(op); |
958 | } |
959 | free(base); |
960 | free(x); |
961 | } |
962 | |
963 | int statXDot (xdot* x, xdot_stats* sp) |
964 | { |
965 | int i; |
966 | xdot_op *op; |
967 | char *base; |
968 | |
969 | if (!x || !sp) return 1; |
970 | memset(sp, 0, sizeof(xdot_stats)); |
971 | sp->cnt = x->cnt; |
972 | base = (char *) (x->ops); |
973 | for (i = 0; i < x->cnt; i++) { |
974 | op = (xdot_op *) (base + i * x->sz); |
975 | switch (op->kind) { |
976 | case xd_filled_ellipse: |
977 | case xd_unfilled_ellipse: |
978 | sp->n_ellipse++; |
979 | break; |
980 | case xd_filled_polygon: |
981 | case xd_unfilled_polygon: |
982 | sp->n_polygon++; |
983 | sp->n_polygon_pts += op->u.polygon.cnt; |
984 | break; |
985 | case xd_filled_bezier: |
986 | case xd_unfilled_bezier: |
987 | sp->n_bezier++; |
988 | sp->n_bezier_pts += op->u.bezier.cnt; |
989 | break; |
990 | case xd_polyline: |
991 | sp->n_polyline++; |
992 | sp->n_polyline_pts += op->u.polyline.cnt; |
993 | break; |
994 | case xd_text: |
995 | sp->n_text++; |
996 | break; |
997 | case xd_image: |
998 | sp->n_image++; |
999 | break; |
1000 | case xd_fill_color: |
1001 | case xd_pen_color: |
1002 | sp->n_color++; |
1003 | break; |
1004 | case xd_grad_fill_color: |
1005 | case xd_grad_pen_color: |
1006 | sp->n_gradcolor++; |
1007 | break; |
1008 | case xd_font: |
1009 | sp->n_font++; |
1010 | break; |
1011 | case xd_fontchar: |
1012 | sp->n_fontchar++; |
1013 | break; |
1014 | case xd_style: |
1015 | sp->n_style++; |
1016 | break; |
1017 | default : |
1018 | break; |
1019 | } |
1020 | } |
1021 | |
1022 | return 0; |
1023 | } |
1024 | |
1025 | xdot_grad_type |
1026 | colorType (char* cp) |
1027 | { |
1028 | xdot_grad_type rv; |
1029 | |
1030 | switch (*cp) { |
1031 | case '[' : |
1032 | rv = xd_linear; |
1033 | break; |
1034 | case '(' : |
1035 | rv = xd_radial; |
1036 | break; |
1037 | default : |
1038 | rv = xd_none; |
1039 | break; |
1040 | } |
1041 | return rv; |
1042 | } |
1043 | |
1044 | #define CHK1(s) if(!s){free(stops);return NULL;} |
1045 | |
1046 | /* radGradient: |
1047 | * Parse radial gradient spec |
1048 | * Return NULL on failure. |
1049 | */ |
1050 | static char* |
1051 | radGradient (char* cp, xdot_color* clr) |
1052 | { |
1053 | char* s = cp; |
1054 | int i; |
1055 | double d; |
1056 | xdot_color_stop* stops = NULL; |
1057 | |
1058 | clr->type = xd_radial; |
1059 | s = parseReal(s, &clr->u.ring.x0); |
1060 | CHK1(s); |
1061 | s = parseReal(s, &clr->u.ring.y0); |
1062 | CHK1(s); |
1063 | s = parseReal(s, &clr->u.ring.r0); |
1064 | CHK1(s); |
1065 | s = parseReal(s, &clr->u.ring.x1); |
1066 | CHK1(s); |
1067 | s = parseReal(s, &clr->u.ring.y1); |
1068 | CHK1(s); |
1069 | s = parseReal(s, &clr->u.ring.r1); |
1070 | CHK1(s); |
1071 | s = parseInt(s, &clr->u.ring.n_stops); |
1072 | CHK1(s); |
1073 | |
1074 | stops = N_NEW(clr->u.ring.n_stops,xdot_color_stop); |
1075 | for (i = 0; i < clr->u.ring.n_stops; i++) { |
1076 | s = parseReal(s, &d); |
1077 | CHK1(s); |
1078 | stops[i].frac = d; |
1079 | s = parseString(s, &stops[i].color); |
1080 | CHK1(s); |
1081 | } |
1082 | clr->u.ring.stops = stops; |
1083 | |
1084 | return cp; |
1085 | } |
1086 | |
1087 | /* linGradient: |
1088 | * Parse linear gradient spec |
1089 | * Return NULL on failure. |
1090 | */ |
1091 | static char* |
1092 | linGradient (char* cp, xdot_color* clr) |
1093 | { |
1094 | char* s = cp; |
1095 | int i; |
1096 | double d; |
1097 | xdot_color_stop* stops = NULL; |
1098 | |
1099 | clr->type = xd_linear; |
1100 | s = parseReal(s, &clr->u.ling.x0); |
1101 | CHK1(s); |
1102 | s = parseReal(s, &clr->u.ling.y0); |
1103 | CHK1(s); |
1104 | s = parseReal(s, &clr->u.ling.x1); |
1105 | CHK1(s); |
1106 | s = parseReal(s, &clr->u.ling.y1); |
1107 | CHK1(s); |
1108 | s = parseInt(s, &clr->u.ling.n_stops); |
1109 | CHK1(s); |
1110 | |
1111 | stops = N_NEW(clr->u.ling.n_stops,xdot_color_stop); |
1112 | for (i = 0; i < clr->u.ling.n_stops; i++) { |
1113 | s = parseReal(s, &d); |
1114 | CHK1(s); |
1115 | stops[i].frac = d; |
1116 | s = parseString(s, &stops[i].color); |
1117 | CHK1(s); |
1118 | } |
1119 | clr->u.ling.stops = stops; |
1120 | |
1121 | return cp; |
1122 | } |
1123 | |
1124 | /* parseXDotColor: |
1125 | * Parse xdot color spec: ordinary or gradient |
1126 | * The result is stored in clr. |
1127 | * Return NULL on failure. |
1128 | */ |
1129 | char* |
1130 | parseXDotColor (char* cp, xdot_color* clr) |
1131 | { |
1132 | char c = *cp; |
1133 | |
1134 | switch (c) { |
1135 | case '[' : |
1136 | return linGradient (cp+1, clr); |
1137 | break; |
1138 | case '(' : |
1139 | return radGradient (cp+1, clr); |
1140 | break; |
1141 | case '#' : |
1142 | case '/' : |
1143 | clr->type = xd_none; |
1144 | clr->u.clr = cp; |
1145 | return cp; |
1146 | break; |
1147 | default : |
1148 | if (isalnum(c)) { |
1149 | clr->type = xd_none; |
1150 | clr->u.clr = cp; |
1151 | return cp; |
1152 | } |
1153 | else |
1154 | return NULL; |
1155 | } |
1156 | } |
1157 | |
1158 | void freeXDotColor (xdot_color* cp) |
1159 | { |
1160 | int i; |
1161 | |
1162 | if (cp->type == xd_linear) { |
1163 | for (i = 0; i < cp->u.ling.n_stops; i++) { |
1164 | free (cp->u.ling.stops[i].color); |
1165 | } |
1166 | free (cp->u.ling.stops); |
1167 | } |
1168 | else if (cp->type == xd_radial) { |
1169 | for (i = 0; i < cp->u.ring.n_stops; i++) { |
1170 | free (cp->u.ring.stops[i].color); |
1171 | } |
1172 | free (cp->u.ring.stops); |
1173 | } |
1174 | } |
1175 | |
1176 | #if 0 |
1177 | static void execOp(xdot_op * op, int param) |
1178 | { |
1179 | op->drawfunc(op, param); |
1180 | } |
1181 | #endif |
1182 | |