Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
emit.c
Go to the documentation of this file.
1
7/*************************************************************************
8 * Copyright (c) 2011 AT&T Intellectual Property
9 * All rights reserved. This program and the accompanying materials
10 * are made available under the terms of the Eclipse Public License v1.0
11 * which accompanies this distribution, and is available at
12 * https://www.eclipse.org/legal/epl-v10.html
13 *
14 * Contributors: Details at https://graphviz.org
15 *************************************************************************/
16
17#include "config.h"
18#include <assert.h>
19#include <float.h>
20#include <stdbool.h>
21#include <stdlib.h>
22#include <string.h>
23#include <limits.h>
24#include <locale.h>
25#include <math.h>
26#include <common/geomprocs.h>
27#include <common/render.h>
28#include <cgraph/agxbuf.h>
29#include <cgraph/alloc.h>
30#include <cgraph/gv_ctype.h>
31#include <cgraph/gv_math.h>
32#include <cgraph/list.h>
33#include <cgraph/streq.h>
34#include <cgraph/unreachable.h>
35#include <common/htmltable.h>
36#include <gvc/gvc.h>
37#include <cdt/cdt.h>
38#include <pathplan/pathgeom.h>
39#include <xdot/xdot.h>
40
41#ifdef _WIN32
42#define strtok_r strtok_s
43#endif
44
45#define P2RECT(p, pr, sx, sy) (pr[0].x = p.x - sx, pr[0].y = p.y - sy, pr[1].x = p.x + sx, pr[1].y = p.y + sy)
46#define FUZZ 3
47#define EPSILON .0001
48
49typedef struct {
53} exdot_op;
54
56{
57 char* p;
58 xdot* xd = NULL;
59
60 if (!((p = agget(g, "_background")) && p[0])) {
61 if (!((p = agget(g, "_draw_")) && p[0])) {
62 return NULL;
63 }
64 }
65#ifdef DEBUG
66 if (Verbose) {
68 }
69#endif
70 xd = parseXDotF (p, NULL, sizeof (exdot_op));
71
72 if (!xd) {
73 agwarningf("Could not parse \"_background\" attribute in graph %s\n", agnameof(g));
74 agerr(AGPREV, " \"%s\"\n", p);
75 }
76#ifdef DEBUG
77 if (Verbose) {
78 xdot_stats stats;
79 double et = elapsed_sec();
80 statXDot (xd, &stats);
81 fprintf (stderr, "%d ops %.2f sec\n", stats.cnt, et);
82 fprintf (stderr, "%d polygons %d points\n", stats.n_polygon, stats.n_polygon_pts);
83 fprintf (stderr, "%d polylines %d points\n", stats.n_polyline, stats.n_polyline_pts);
84 fprintf (stderr, "%d beziers %d points\n", stats.n_bezier, stats.n_bezier_pts);
85 fprintf (stderr, "%d ellipses\n", stats.n_ellipse);
86 fprintf (stderr, "%d texts\n", stats.n_text);
87 }
88#endif
89 return xd;
90}
91
92static char *defaultlinestyle[3] = { "solid\0", "setlinewidth\0001\0", 0 };
93
94/* push empty graphic state for current object */
96{
97 obj_state_t *obj = gv_alloc(sizeof(obj_state_t));
98
99 obj_state_t *parent = obj->parent = job->obj;
100 job->obj = obj;
101 if (parent) {
102 obj->pencolor = parent->pencolor; /* default styles to parent's style */
103 obj->fillcolor = parent->fillcolor;
104 obj->pen = parent->pen;
105 obj->fill = parent->fill;
106 obj->penwidth = parent->penwidth;
107 obj->gradient_angle = parent->gradient_angle;
108 obj->stopcolor = parent->stopcolor;
109 }
110 else {
111 obj->pen = PEN_SOLID;
112 obj->fill = FILL_NONE;
114 }
115 return obj;
116}
117
118/* pop graphic state of current object */
120{
121 obj_state_t *obj = job->obj;
122
123 assert(obj);
124
125 free(obj->id);
126 free(obj->url);
127 free(obj->labelurl);
128 free(obj->tailurl);
129 free(obj->headurl);
130 free(obj->tooltip);
131 free(obj->labeltooltip);
132 free(obj->tailtooltip);
133 free(obj->headtooltip);
134 free(obj->target);
135 free(obj->labeltarget);
136 free(obj->tailtarget);
137 free(obj->headtarget);
138 free(obj->url_map_p);
141
142 job->obj = obj->parent;
143 free(obj);
144}
145
146/* Store image map data into job, substituting for node, edge, etc.
147 * names.
148 * Return 1 if an assignment was made for url or tooltip or target.
149 */
150int
151initMapData (GVJ_t* job, char* lbl, char* url, char* tooltip, char* target, char *id,
152 void* gobj)
153{
154 obj_state_t *obj = job->obj;
155 int flags = job->flags;
156 int assigned = 0;
157
158 if ((flags & GVRENDER_DOES_LABELS) && lbl)
159 obj->label = lbl;
161 obj->id = strdup_and_subst_obj(id, gobj);
162 if (url && url[0]) {
163 obj->url = strdup_and_subst_obj(url, gobj);
164 assigned = 1;
165 }
166 }
168 if (tooltip && tooltip[0]) {
169 obj->tooltip = strdup_and_subst_obj(tooltip, gobj);
170 obj->explicit_tooltip = true;
171 assigned = 1;
172 }
173 else if (obj->label) {
174 obj->tooltip = gv_strdup(obj->label);
175 assigned = 1;
176 }
177 }
178 if ((flags & GVRENDER_DOES_TARGETS) && target && target[0]) {
179 obj->target = strdup_and_subst_obj(target, gobj);
180 assigned = 1;
181 }
182 return assigned;
183}
184
185static void
187{
188 if (job->layerNum > 1 && (job->flags & GVDEVICE_DOES_LAYERS)) {
189 agxbprint (xb, "%s_", job->gvc->layerIDs[job->layerNum]);
190 }
191 if (job->pagesArrayElem.x > 0 || job->pagesArrayElem.y > 0) {
192 agxbprint (xb, "page%d,%d_", job->pagesArrayElem.x, job->pagesArrayElem.y);
193 }
194}
195
197char*
198getObjId (GVJ_t* job, void* obj, agxbuf* xb)
199{
200 char* id;
201 graph_t* root = job->gvc->g;
202 char* gid = GD_drawing(root)->id;
203 long idnum = 0;
204 char* pfx = NULL;
205
206 layerPagePrefix (job, xb);
207
208 id = agget(obj, "id");
209 if (id && *id != '\0') {
210 agxbput (xb, id);
211 return agxbuse(xb);
212 }
213
214 if (obj != root && gid) {
215 agxbprint (xb, "%s_", gid);
216 }
217
218 switch (agobjkind(obj)) {
219 case AGRAPH:
220 idnum = AGSEQ(obj);
221 if (root == obj)
222 pfx = "graph";
223 else
224 pfx = "clust";
225 break;
226 case AGNODE:
227 idnum = AGSEQ((Agnode_t*)obj);
228 pfx = "node";
229 break;
230 case AGEDGE:
231 idnum = AGSEQ((Agedge_t*)obj);
232 pfx = "edge";
233 break;
234 }
235
236 agxbprint (xb, "%s%ld", pfx, idnum);
237
238 return agxbuse(xb);
239}
240
241/* Map "\n" to ^J, "\r" to ^M and "\l" to ^J.
242 * Map "\\" to backslash.
243 * Map "\x" to x.
244 * Mapping is done in place.
245 * Return input string.
246 */
247static char*
249{
250 char* rets = ins;
251 char* outs = ins;
252 char c;
253 bool backslash_seen = false;
254
255 while ((c = *ins++)) {
256 if (backslash_seen) {
257 switch (c) {
258 case 'n' :
259 case 'l' :
260 *outs++ = '\n';
261 break;
262 case 'r' :
263 *outs++ = '\r';
264 break;
265 default :
266 *outs++ = c;
267 break;
268 }
269 backslash_seen = false;
270 }
271 else {
272 if (c == '\\')
273 backslash_seen = true;
274 else
275 *outs++ = c;
276 }
277 }
278 *outs = '\0';
279 return rets;
280}
281
282/* Tooltips are a weak form of escString, so we expect object substitution
283 * and newlines to be handled. The former occurs in initMapData. Here we
284 * map "\r", "\l" and "\n" to newlines. (We don't try to handle alignment
285 * as in real labels.) To make things uniform when the
286 * tooltip is emitted latter as visible text, we also convert HTML escape
287 * sequences into UTF8. This is already occurring when tooltips are input
288 * via HTML-like tables.
289 */
290static char*
291preprocessTooltip(char* s, void* gobj)
292{
293 Agraph_t* g = agroot(gobj);
294 int charset = GD_charset(g);
295 char* news;
296 switch (charset) {
297 case CHAR_LATIN1:
298 news = latin1ToUTF8(s);
299 break;
300 default: /* UTF8 */
301 news = htmlEntityUTF8(s, g);
302 break;
303 }
304
305 return interpretCRNL (news);
306}
307
308static void
309initObjMapData (GVJ_t* job, textlabel_t *lab, void* gobj)
310{
311 char* lbl;
312 char* url = agget(gobj, "href");
313 char* tooltip = agget(gobj, "tooltip");
314 char* target = agget(gobj, "target");
315 char* id;
316 agxbuf xb = {0};
317
318 if (lab) lbl = lab->text;
319 else lbl = NULL;
320 if (!url || !*url) /* try URL as an alias for href */
321 url = agget(gobj, "URL");
322 id = getObjId (job, gobj, &xb);
323 if (tooltip)
324 tooltip = preprocessTooltip (tooltip, gobj);
325 initMapData (job, lbl, url, tooltip, target, id, gobj);
326
327 free (tooltip);
328 agxbfree(&xb);
329}
330
331static void map_point(GVJ_t *job, pointf pf)
332{
333 obj_state_t *obj = job->obj;
334 int flags = job->flags;
335 pointf *p;
336
340 obj->url_map_n = 2;
341 }
342 else {
344 obj->url_map_n = 4;
345 }
346 free(obj->url_map_p);
347 obj->url_map_p = p = gv_calloc(obj->url_map_n, sizeof(pointf));
348 P2RECT(pf, p, FUZZ, FUZZ);
350 gvrender_ptf_A(job, p, p, 2);
352 rect2poly(p);
353 }
354}
355
357 char *style;
358 char **pstyle = NULL;
359 graphviz_polygon_style_t istyle = {0};
360
361 if ((style = agget(sg, "style")) != 0 && style[0]) {
362 char **pp;
363 char **qp;
364 char *p;
365 pp = pstyle = parse_style(style);
366 while ((p = *pp)) {
367 if (strcmp(p, "filled") == 0) {
368 istyle.filled = true;
369 pp++;
370 }else if (strcmp(p, "radial") == 0) {
371 istyle.filled = true;
372 istyle.radial = true;
373 qp = pp; /* remove rounded from list passed to renderer */
374 do {
375 qp++;
376 *(qp-1) = *qp;
377 } while (*qp);
378 }else if (strcmp(p, "striped") == 0) {
379 istyle.striped = true;
380 qp = pp; /* remove rounded from list passed to renderer */
381 do {
382 qp++;
383 *(qp-1) = *qp;
384 } while (*qp);
385 }else if (strcmp(p, "rounded") == 0) {
386 istyle.rounded = true;
387 qp = pp; /* remove rounded from list passed to renderer */
388 do {
389 qp++;
390 *(qp-1) = *qp;
391 } while (*qp);
392 } else pp++;
393 }
394 }
395
396 *flagp = istyle;
397 return pstyle;
398}
399
400typedef struct {
401 char* color; /* segment color */
402 double t;
403 bool hasFraction; /* true if color explicitly specifies its fraction */
404} colorseg_t;
405/* Sum of segment sizes should add to 1 */
406typedef struct {
407 int numc; /* number of used segments in segs; may include segs with t == 0 */
408 char* base; /* storage of color names */
409 colorseg_t* segs; /* array of segments; real segments always followed by a sentinel */
411
412static void
414{
415 free (segs->base);
416 free (segs->segs);
417}
418
419/* Find semicolon in s, replace with '\0'.
420 * Convert remainder to float v.
421 * Return 0 if no float given
422 * Return -1 on failure
423 */
424static double getSegLen (char* s)
425{
426 char* p = strchr (s, ';');
427 char* endp;
428 double v;
429
430 if (!p) {
431 return 0;
432 }
433 *p++ = '\0';
434 v = strtod (p, &endp);
435 if (endp != p) { /* scanned something */
436 if (v >= 0)
437 return v;
438 }
439 return -1;
440}
441
442#define EPS 1E-5
443#define AEQ0(x) (((x) < EPS) && ((x) > -EPS))
444
445/* Parse string of form color;float:color;float:...:color;float:color
446 * where the semicolon-floats are optional, nonnegative, sum to <= 1.
447 * Store the values in an array of colorseg_t's and return the array in psegs.
448 * If nseg == 0, count the number of colors.
449 * If the sum of the floats does not equal 1, the remainder is equally distributed
450 * to all colors without an explicit float. If no such colors exist, the remainder
451 * is added to the last color.
452 * 0 => okay
453 * 1 => error without message
454 * 2 => error with message
455 * 3 => warning message
456 * There is a last sentinel segment with color == NULL; it will always follow
457 * the last segment with t > 0.
458 *
459 * Note that psegs is only assigned to if the return value is 0 or 3.
460 * Otherwise, psegs is left unchanged and the allocated memory is
461 * freed before returning.
462 */
463static int parseSegs(char *clrs, size_t nseg, colorsegs_t *psegs) {
464 colorsegs_t segs = {0};
465 colorseg_t* s;
466 char* colors = gv_strdup(clrs);
467 char* color;
468 int cnum = 0;
469 double v, left = 1;
470 static int doWarn = 1;
471 int i, rval = 0;
472 char* p;
473
474 if (nseg == 0) {
475 nseg = 1;
476 /* need to know how many colors separated by ':' */
477 for (p = colors; *p; p++) {
478 if (*p == ':') nseg++;
479 }
480 }
481
482 segs.base = colors;
483 segs.segs = s = gv_calloc(nseg + 1, sizeof(colorseg_t));
484 for (color = strtok(colors, ":"); color; color = strtok(0, ":")) {
485 if ((v = getSegLen (color)) >= 0) {
486 double del = v - left;
487 if (del > 0) {
488 if (doWarn && !AEQ0(del)) {
489 agwarningf("Total size > 1 in \"%s\" color spec ", clrs);
490 doWarn = 0;
491 rval = 3;
492 }
493 v = left;
494 }
495 left -= v;
496 if (v > 0) s[cnum].hasFraction = true;
497 if (*color) s[cnum].color = color;
498 s[cnum++].t = v;
499 }
500 else {
501 if (doWarn) {
502 agerrorf("Illegal value in \"%s\" color attribute; float expected after ';'\n",
503 clrs);
504 doWarn = 0;
505 rval = 2;
506 }
507 else rval = 1;
508 freeSegs(&segs);
509 return rval;
510 }
511 if (AEQ0(left)) {
512 left = 0;
513 break;
514 }
515 }
516
517 /* distribute remaining into slot with t == 0; if none, add to last */
518 if (left > 0) {
519 /* count zero segments */
520 nseg = 0;
521 for (i = 0; i < cnum; i++) {
522 if (s[i].t <= 0) nseg++;
523 }
524 if (nseg > 0) {
525 double delta = left / (double)nseg;
526 for (i = 0; i < cnum; i++) {
527 if (s[i].t <= 0) s[i].t = delta;
528 }
529 }
530 else {
531 s[cnum-1].t += left;
532 }
533 }
534
535 /* Make sure last positive segment is followed by a sentinel. */
536 for (i = cnum-1; i >= 0; i--) {
537 if (s[i].t > 0) break;
538 }
539 s[i+1].color = NULL;
540 segs.numc = i+1;
541
542 *psegs = segs;
543 return rval;
544}
545
546#define THIN_LINE 0.5
547
548/* Fill an ellipse whose bounding box is given by 2 points in pf
549 * with multiple wedges determined by the color spec in clrs.
550 * clrs is a list of colon separated colors, with possible quantities.
551 * Thin boundaries are drawn.
552 * 0 => okay
553 * 1 => error without message
554 * 2 => error with message
555 * 3 => warning message
556 */
557int
558wedgedEllipse (GVJ_t* job, pointf * pf, char* clrs)
559{
560 colorsegs_t segs;
561 int rv;
562 double save_penwidth = job->obj->penwidth;
563 Ppolyline_t* pp;
564 double angle0, angle1;
565
566 rv = parseSegs (clrs, 0, &segs);
567 if (rv == 1 || rv == 2) return rv;
568 const pointf ctr = mid_pointf(pf[0], pf[1]);
569 const pointf semi = sub_pointf(pf[1], ctr);
570 if (save_penwidth > THIN_LINE)
572
573 angle0 = 0;
574 for (colorseg_t *s = segs.segs; s->color; s++) {
575 if (s->t <= 0) continue;
576 gvrender_set_fillcolor(job, s->color);
577
578 if (s[1].color == NULL)
579 angle1 = 2*M_PI;
580 else
581 angle1 = angle0 + 2 * M_PI * s->t;
582 pp = ellipticWedge (ctr, semi.x, semi.y, angle0, angle1);
583 gvrender_beziercurve(job, pp->ps, pp->pn, 1);
584 angle0 = angle1;
585 freePath (pp);
586 }
587
588 if (save_penwidth > THIN_LINE)
589 gvrender_set_penwidth(job, save_penwidth);
590 freeSegs(&segs);
591 return rv;
592}
593
594/* Fill a rectangular box with vertical stripes of colors.
595 * AF gives 4 corner points, with AF[0] the LL corner and the points ordered CCW.
596 * clrs is a list of colon separated colors, with possible quantities.
597 * Thin boundaries are drawn.
598 * 0 => okay
599 * 1 => error without message
600 * 2 => error with message
601 * 3 => warning message
602 */
603int
604stripedBox (GVJ_t * job, pointf* AF, char* clrs, int rotate)
605{
606 colorsegs_t segs;
607 int rv;
608 double xdelta;
609 pointf pts[4];
610 double lastx;
611 double save_penwidth = job->obj->penwidth;
612
613 rv = parseSegs (clrs, 0, &segs);
614 if (rv == 1 || rv == 2) return rv;
615 if (rotate) {
616 pts[0] = AF[2];
617 pts[1] = AF[3];
618 pts[2] = AF[0];
619 pts[3] = AF[1];
620 } else {
621 pts[0] = AF[0];
622 pts[1] = AF[1];
623 pts[2] = AF[2];
624 pts[3] = AF[3];
625 }
626 lastx = pts[1].x;
627 xdelta = (pts[1].x - pts[0].x);
628 pts[1].x = pts[2].x = pts[0].x;
629
630 if (save_penwidth > THIN_LINE)
632 for (colorseg_t *s = segs.segs; s->color; s++) {
633 if (s->t <= 0) continue;
634 gvrender_set_fillcolor (job, (s->color?s->color:DEFAULT_COLOR));
635 if (s[1].color == NULL)
636 pts[1].x = pts[2].x = lastx;
637 else
638 pts[1].x = pts[2].x = pts[0].x + xdelta*(s->t);
639 gvrender_polygon(job, pts, 4, FILL);
640 pts[0].x = pts[3].x = pts[1].x;
641 }
642 if (save_penwidth > THIN_LINE)
643 gvrender_set_penwidth(job, save_penwidth);
644 freeSegs(&segs);
645 return rv;
646}
647
649{
650 obj_state_t *obj = job->obj;
651 int flags = job->flags;
652 pointf *p;
653
657 obj->url_map_n = 2;
658 }
659 else {
661 obj->url_map_n = 4;
662 }
663 free(obj->url_map_p);
664 obj->url_map_p = p = gv_calloc(obj->url_map_n, sizeof(pointf));
665 p[0] = b.LL;
666 p[1] = b.UR;
668 gvrender_ptf_A(job, p, p, 2);
670 rect2poly(p);
671 }
672}
673
674static void map_label(GVJ_t *job, textlabel_t *lab)
675{
676 obj_state_t *obj = job->obj;
677 int flags = job->flags;
678 pointf *p;
679
683 obj->url_map_n = 2;
684 }
685 else {
687 obj->url_map_n = 4;
688 }
689 free(obj->url_map_p);
690 obj->url_map_p = p = gv_calloc(obj->url_map_n, sizeof(pointf));
691 P2RECT(lab->pos, p, lab->dimen.x / 2., lab->dimen.y / 2.);
693 gvrender_ptf_A(job, p, p, 2);
695 rect2poly(p);
696 }
697}
698
699/* isRect function returns true when polygon has
700 * regular rectangular shape. Rectangle is regular when
701 * it is not skewed and distorted and orientation is almost zero
702 */
703static bool isRect(polygon_t * p)
704{
705 return p->sides == 4 && fabs(fmod(p->orientation, 90)) < 0.5
707}
708
709/*
710 * isFilled function returns true if filled style has been set for node 'n'
711 * otherwise returns false. it accepts pointer to node_t as an argument
712 */
713static bool isFilled(node_t * n)
714{
715 char *style, *p, **pp;
716 bool r = false;
717 style = late_nnstring(n, N_style, "");
718 if (style[0]) {
719 pp = parse_style(style);
720 while ((p = *pp)) {
721 if (strcmp(p, "filled") == 0)
722 r = true;
723 pp++;
724 }
725 }
726 return r;
727}
728
729/* pEllipse function returns 'np' points from the circumference
730 * of ellipse described by radii 'a' and 'b'.
731 * Assumes 'np' is greater than zero.
732 * 'np' should be at least 4 to sample polygon from ellipse
733 */
734static pointf *pEllipse(double a, double b, size_t np) {
735 double theta = 0.0;
736 double deltheta = 2 * M_PI / (double)np;
737
738 pointf *ps = gv_calloc(np, sizeof(pointf));
739 for (size_t i = 0; i < np; i++) {
740 ps[i].x = a * cos(theta);
741 ps[i].y = b * sin(theta);
742 theta += deltheta;
743 }
744 return ps;
745}
746
747#define HW 2.0 /* maximum distance away from line, in points */
748
749/* check_control_points function checks the size of quadrilateral
750 * formed by four control points
751 * returns true if four points are in line (or close to line)
752 * else return false
753 */
755{
756 double dis1 = ptToLine2 (cp[0], cp[3], cp[1]);
757 double dis2 = ptToLine2 (cp[0], cp[3], cp[2]);
758 return dis1 < HW * HW && dis2 < HW * HW;
759}
760
761/* update bounding box to contain a bezier segment */
763{
764
765 /* if any control point of the segment is outside the bounding box */
766 if (cp[0].x > bb->UR.x || cp[0].x < bb->LL.x ||
767 cp[0].y > bb->UR.y || cp[0].y < bb->LL.y ||
768 cp[1].x > bb->UR.x || cp[1].x < bb->LL.x ||
769 cp[1].y > bb->UR.y || cp[1].y < bb->LL.y ||
770 cp[2].x > bb->UR.x || cp[2].x < bb->LL.x ||
771 cp[2].y > bb->UR.y || cp[2].y < bb->LL.y ||
772 cp[3].x > bb->UR.x || cp[3].x < bb->LL.x ||
773 cp[3].y > bb->UR.y || cp[3].y < bb->LL.y) {
774
775 /* if the segment is sufficiently refined */
776 if (check_control_points(cp)) {
777 int i;
778 /* expand the bounding box */
779 for (i = 0; i < 4; i++) {
780 if (cp[i].x > bb->UR.x)
781 bb->UR.x = cp[i].x;
782 else if (cp[i].x < bb->LL.x)
783 bb->LL.x = cp[i].x;
784 if (cp[i].y > bb->UR.y)
785 bb->UR.y = cp[i].y;
786 else if (cp[i].y < bb->LL.y)
787 bb->LL.y = cp[i].y;
788 }
789 }
790 else { /* else refine the segment */
791 pointf left[4], right[4];
792 Bezier (cp, 0.5, left, right);
793 update_bb_bz(bb, left);
794 update_bb_bz(bb, right);
795 }
796 }
797}
798
799#if defined(DEBUG) && DEBUG == 2
800static void psmapOutput(pointf* ps, size_t n) {
801 fprintf (stdout, "newpath %f %f moveto\n", ps[0].x, ps[0].y);
802 for (size_t i = 1; i < n; i++)
803 fprintf (stdout, "%f %f lineto\n", ps[i].x, ps[i].y);
804 fprintf (stdout, "closepath stroke\n");
805}
806#endif
807
812
813#define MARK_FIRST_SEG(L) ((L)->next = (segitem_t*)1)
814#define FIRST_SEG(L) ((L)->next == (segitem_t*)1)
815#define INIT_SEG(P,L) {(L)->next = 0; (L)->p = P;}
816
818{
819 segitem_t* s = gv_alloc(sizeof(segitem_t));
820 INIT_SEG (p, s);
821 lp->next = s;
822 return s;
823}
824
825/* Output the polygon determined by the n points in p1, followed
826 * by the n points in p2 in reverse order. Assumes n <= 50.
827 */
828static void map_bspline_poly(pointf **pbs_p, size_t **pbs_n, size_t *pbs_poly_n,
829 size_t n, pointf *p1, pointf *p2) {
830 size_t i = 0, nump = 0, last = 2 * n - 1;
831
832 for ( ; i < *pbs_poly_n; i++)
833 nump += (*pbs_n)[i];
834
835 (*pbs_poly_n)++;
836 *pbs_n = gv_recalloc(*pbs_n, *pbs_poly_n - 1, *pbs_poly_n, sizeof(size_t));
837 (*pbs_n)[i] = 2*n;
838 *pbs_p = gv_recalloc(*pbs_p, nump, nump + 2 * n, sizeof(pointf));
839
840 for (i = 0; i < n; i++) {
841 (*pbs_p)[nump+i] = p1[i];
842 (*pbs_p)[nump+last-i] = p2[i];
843 }
844#if defined(DEBUG) && DEBUG == 2
845 psmapOutput (*pbs_p + nump, last+1);
846#endif
847}
848
849/* Approximate Bezier by line segments. If the four points are
850 * almost colinear, as determined by check_control_points, we store
851 * the segment cp[0]-cp[3]. Otherwise we split the Bezier into 2 and recurse.
852 * Since 2 contiguous segments share an endpoint, we actually store
853 * the segments as a list of points.
854 * New points are appended to the list given by lp. The tail of the
855 * list is returned.
856 */
858{
859 pointf left[4], right[4];
860
861 if (check_control_points(cp)) {
862 if (FIRST_SEG (lp)) INIT_SEG (cp[0], lp);
863 lp = appendSeg (cp[3], lp);
864 }
865 else {
866 Bezier (cp, 0.5, left, right);
867 lp = approx_bezier (left, lp);
868 lp = approx_bezier (right, lp);
869 }
870 return lp;
871}
872
873/* Return the angle of the bisector between the two rays
874 * pp-cp and cp-np. The bisector returned is always to the
875 * left of pp-cp-np.
876 */
877static double bisect (pointf pp, pointf cp, pointf np)
878{
879 double ang, theta, phi;
880 theta = atan2(np.y - cp.y,np.x - cp.x);
881 phi = atan2(pp.y - cp.y,pp.x - cp.x);
882 ang = theta - phi;
883 if (ang > 0) ang -= 2*M_PI;
884
885 return phi + ang / 2.0;
886}
887
888/* Determine polygon points related to 2 segments prv-cur and cur-nxt.
889 * The points lie on the bisector of the 2 segments, passing through cur,
890 * and distance w2 from cur. The points are stored in p1 and p2.
891 * If p1 is NULL, we use the normal to cur-nxt.
892 * If p2 is NULL, we use the normal to prv-cur.
893 * Assume at least one of prv or nxt is non-NULL.
894 */
895static void mkSegPts (segitem_t* prv, segitem_t* cur, segitem_t* nxt,
896 pointf* p1, pointf* p2, double w2)
897{
898 pointf cp, pp, np;
899 double theta, delx, dely;
900 pointf p;
901
902 cp = cur->p;
903 /* if prv or nxt are NULL, use the one given to create a collinear
904 * prv or nxt. This could be more efficiently done with special case code,
905 * but this way is more uniform.
906 */
907 if (prv) {
908 pp = prv->p;
909 if (nxt)
910 np = nxt->p;
911 else {
912 np.x = 2*cp.x - pp.x;
913 np.y = 2*cp.y - pp.y;
914 }
915 }
916 else {
917 np = nxt->p;
918 pp.x = 2*cp.x - np.x;
919 pp.y = 2*cp.y - np.y;
920 }
921 theta = bisect(pp,cp,np);
922 delx = w2*cos(theta);
923 dely = w2*sin(theta);
924 p.x = cp.x + delx;
925 p.y = cp.y + dely;
926 *p1 = p;
927 p.x = cp.x - delx;
928 p.y = cp.y - dely;
929 *p2 = p;
930}
931
932/* Construct and output a closed polygon approximating the input
933 * B-spline bp. We do this by first approximating bp by a sequence
934 * of line segments. We then use the sequence of segments to determine
935 * the polygon.
936 * In cmapx, polygons are limited to 100 points, so we output polygons
937 * in chunks of 100.
938 */
939static void map_output_bspline(pointf **pbs, size_t **pbs_n, size_t *pbs_poly_n,
940 bezier *bp, double w2) {
941 segitem_t* segl = gv_alloc(sizeof(segitem_t));
942 segitem_t* segp = segl;
943 segitem_t* segprev;
944 segitem_t* segnext;
945 pointf pts[4], pt1[50], pt2[50];
946
947 MARK_FIRST_SEG(segl);
948 const size_t nc = (bp->size - 1) / 3; // nc is number of bezier curves
949 for (size_t j = 0; j < nc; j++) {
950 for (size_t k = 0; k < 4; k++) {
951 pts[k] = bp->list[3*j + k];
952 }
953 segp = approx_bezier (pts, segp);
954 }
955
956 segp = segl;
957 segprev = 0;
958 size_t cnt = 0;
959 while (segp) {
960 segnext = segp->next;
961 mkSegPts (segprev, segp, segnext, pt1+cnt, pt2+cnt, w2);
962 cnt++;
963 if (segnext == NULL || cnt == 50) {
964 map_bspline_poly (pbs, pbs_n, pbs_poly_n, cnt, pt1, pt2);
965 pt1[0] = pt1[cnt-1];
966 pt2[0] = pt2[cnt-1];
967 cnt = 1;
968 }
969 segprev = segp;
970 segp = segnext;
971 }
972
973 /* free segl */
974 while (segl) {
975 segp = segl->next;
976 free (segl);
977 segl = segp;
978 }
979}
980
981static bool is_natural_number(const char *sstr)
982{
983 const char *str = sstr;
984
985 while (*str)
986 if (!gv_isdigit(*str++))
987 return false;
988 return true;
989}
990
991static int layer_index(GVC_t *gvc, char *str, int all)
992{
993 int i;
994
995 if (streq(str, "all"))
996 return all;
998 return atoi(str);
999 if (gvc->layerIDs)
1000 for (i = 1; i <= gvc->numLayers; i++)
1001 if (streq(str, gvc->layerIDs[i]))
1002 return i;
1003 return -1;
1004}
1005
1006static bool selectedLayer(GVC_t *gvc, int layerNum, int numLayers, char *spec)
1007{
1008 int n0, n1;
1009 char *w0, *w1;
1010 char *buf_part_p = NULL, *buf_p = NULL, *cur, *part_in_p;
1011 bool rval = false;
1012
1013 // copy `spec` so we can `strtok_r` it
1014 char *spec_copy = gv_strdup(spec);
1015 part_in_p = spec_copy;
1016
1017 while (!rval && (cur = strtok_r(part_in_p, gvc->layerListDelims, &buf_part_p))) {
1018 w1 = w0 = strtok_r (cur, gvc->layerDelims, &buf_p);
1019 if (w0)
1020 w1 = strtok_r (NULL, gvc->layerDelims, &buf_p);
1021 if (w1 != NULL) {
1022 assert(w0 != NULL);
1023 n0 = layer_index(gvc, w0, 0);
1024 n1 = layer_index(gvc, w1, numLayers);
1025 if (n0 >= 0 || n1 >= 0) {
1026 if (n0 > n1) {
1027 int t = n0;
1028 n0 = n1;
1029 n1 = t;
1030 }
1031 rval = BETWEEN(n0, layerNum, n1);
1032 }
1033 } else if (w0 != NULL) {
1034 n0 = layer_index(gvc, w0, layerNum);
1035 rval = (n0 == layerNum);
1036 } else {
1037 rval = false;
1038 }
1039 part_in_p = NULL;
1040 }
1041 free(spec_copy);
1042 return rval;
1043}
1044
1045static bool selectedlayer(GVJ_t *job, char *spec)
1046{
1047 return selectedLayer (job->gvc, job->layerNum, job->numLayers, spec);
1048}
1049
1050/* Parse the graph's layerselect attribute, which determines
1051 * which layers are emitted. The specification is the same used
1052 * by the layer attribute.
1053 *
1054 * If we find n layers, we return an array arr of n+2 ints. arr[0]=n.
1055 * arr[n+1]=numLayers+1, acting as a sentinel. The other entries give
1056 * the desired layer indices.
1057 *
1058 * If no layers are detected, NULL is returned.
1059 *
1060 * This implementation does a linear walk through each layer index and
1061 * uses selectedLayer to match it against p. There is probably a more
1062 * efficient way to do this, but this is simple and until we find people
1063 * using huge numbers of layers, it should be adequate.
1064 */
1065static int *parse_layerselect(GVC_t *gvc, char *p) {
1066 int* laylist = gv_calloc(gvc->numLayers + 2, sizeof(int));
1067 int i, cnt = 0;
1068 for (i = 1; i <=gvc->numLayers; i++) {
1069 if (selectedLayer (gvc, i, gvc->numLayers, p)) {
1070 laylist[++cnt] = i;
1071 }
1072 }
1073 if (cnt) {
1074 laylist[0] = cnt;
1075 laylist[cnt+1] = gvc->numLayers+1;
1076 }
1077 else {
1078 agwarningf("The layerselect attribute \"%s\" does not match any layer specifed by the layers attribute - ignored.\n", p);
1079 free (laylist);
1080 laylist = NULL;
1081 }
1082 return laylist;
1083}
1084
1085DEFINE_LIST(layer_names, char*)
1086
1087/* Split input string into tokens, with separators specified by
1088 * the layersep attribute. Store the values in the gvc->layerIDs array,
1089 * starting at index 1, and return the count.
1090 * Note that there is no mechanism
1091 * to free the memory before exit.
1092 */
1093static int parse_layers(GVC_t *gvc, graph_t * g, char *p)
1094{
1095 char *tok;
1096
1097 gvc->layerDelims = agget(g, "layersep");
1098 if (!gvc->layerDelims)
1100 gvc->layerListDelims = agget(g, "layerlistsep");
1101 if (!gvc->layerListDelims)
1103 if ((tok = strpbrk (gvc->layerDelims, gvc->layerListDelims))) { /* conflict in delimiter strings */
1104 agwarningf("The character \'%c\' appears in both the layersep and layerlistsep attributes - layerlistsep ignored.\n", *tok);
1105 gvc->layerListDelims = "";
1106 }
1107
1108 gvc->layers = gv_strdup(p);
1109 layer_names_t layerIDs = {0};
1110
1111 // inferred entry for the first (unnamed) layer
1112 layer_names_append(&layerIDs, NULL);
1113
1114 for (tok = strtok(gvc->layers, gvc->layerDelims); tok;
1115 tok = strtok(NULL, gvc->layerDelims)) {
1116 layer_names_append(&layerIDs, tok);
1117 }
1118
1119 assert(layer_names_size(&layerIDs) - 1 <= INT_MAX);
1120 int ntok = (int)(layer_names_size(&layerIDs) - 1);
1121
1122 // if we found layers, save them for later reference
1123 if (layer_names_size(&layerIDs) > 1) {
1124 layer_names_append(&layerIDs, NULL); // add a terminating entry
1125 gvc->layerIDs = layer_names_detach(&layerIDs);
1126 }
1127 layer_names_free(&layerIDs);
1128
1129 return ntok;
1130}
1131
1132/* Determine order of output.
1133 * Output usually in breadth first graph walk order
1134 */
1135static int chkOrder(graph_t * g)
1136{
1137 char *p = agget(g, "outputorder");
1138 if (p) {
1139 if (!strcmp(p, "nodesfirst"))
1140 return EMIT_SORTED;
1141 if (!strcmp(p, "edgesfirst"))
1142 return EMIT_EDGE_SORTED;
1143 }
1144 return 0;
1145}
1146
1147static void init_layering(GVC_t * gvc, graph_t * g)
1148{
1149 char *str;
1150
1151 /* free layer strings and pointers from previous graph */
1152 free(gvc->layers);
1153 gvc->layers = NULL;
1154 free(gvc->layerIDs);
1155 gvc->layerIDs = NULL;
1156 free(gvc->layerlist);
1157 gvc->layerlist = NULL;
1158 if ((str = agget(g, "layers")) != 0) {
1160 if ((str = agget(g, "layerselect")) != 0 && *str) {
1162 }
1163 } else {
1164 gvc->numLayers = 1;
1165 }
1166}
1167
1169static int numPhysicalLayers (GVJ_t *job)
1170{
1171 if (job->gvc->layerlist) {
1172 return job->gvc->layerlist[0];
1173 }
1174 else
1175 return job->numLayers;
1176
1177}
1178
1179static void firstlayer(GVJ_t *job, int** listp)
1180{
1181 job->numLayers = job->gvc->numLayers;
1182 if (job->gvc->layerlist) {
1183 int *list = job->gvc->layerlist;
1184 int cnt = *list++;
1185 if (cnt > 1 && !(job->flags & GVDEVICE_DOES_LAYERS)) {
1186 agwarningf("layers not supported in %s output\n",
1187 job->output_langname);
1188 list[1] = job->numLayers + 1; /* only one layer printed */
1189 }
1190 job->layerNum = *list++;
1191 *listp = list;
1192 }
1193 else {
1194 if (job->numLayers > 1 && !(job->flags & GVDEVICE_DOES_LAYERS)) {
1195 agwarningf("layers not supported in %s output\n",
1196 job->output_langname);
1197 job->numLayers = 1;
1198 }
1199 job->layerNum = 1;
1200 *listp = NULL;
1201 }
1202}
1203
1204static bool validlayer(GVJ_t *job)
1205{
1206 return job->layerNum <= job->numLayers;
1207}
1208
1209static void nextlayer(GVJ_t *job, int** listp)
1210{
1211 int *list = *listp;
1212 if (list) {
1213 job->layerNum = *list++;
1214 *listp = list;
1215 }
1216 else
1217 job->layerNum++;
1218}
1219
1220static point pagecode(GVJ_t *job, char c)
1221{
1222 point rv = {0};
1223 switch (c) {
1224 case 'T':
1225 job->pagesArrayFirst.y = job->pagesArraySize.y - 1;
1226 rv.y = -1;
1227 break;
1228 case 'B':
1229 rv.y = 1;
1230 break;
1231 case 'L':
1232 rv.x = 1;
1233 break;
1234 case 'R':
1235 job->pagesArrayFirst.x = job->pagesArraySize.x - 1;
1236 rv.x = -1;
1237 break;
1238 default:
1239 // ignore; will trigger a warning later in our caller
1240 break;
1241 }
1242 return rv;
1243}
1244
1245static void init_job_pagination(GVJ_t * job, graph_t *g)
1246{
1247 GVC_t *gvc = job->gvc;
1248 pointf pageSize; /* page size for the graph - points*/
1249 pointf centering = {0}; // centering offset - points
1250
1251 /* unpaginated image size - in points - in graph orientation */
1252 pointf imageSize = job->view; // image size on one page of the graph - points
1253
1254 /* rotate imageSize to page orientation */
1255 if (job->rotation)
1256 imageSize = exch_xyf(imageSize);
1257
1258 /* margin - in points - in page orientation */
1259 pointf margin = job->margin; // margin for a page of the graph - points
1260
1261 /* determine pagination */
1263 /* page was set by user */
1264
1265 /* determine size of page for image */
1266 pageSize.x = gvc->pageSize.x - 2 * margin.x;
1267 pageSize.y = gvc->pageSize.y - 2 * margin.y;
1268
1269 if (pageSize.x < EPSILON)
1270 job->pagesArraySize.x = 1;
1271 else {
1272 job->pagesArraySize.x = (int)(imageSize.x / pageSize.x);
1273 if (imageSize.x - job->pagesArraySize.x * pageSize.x > EPSILON)
1274 job->pagesArraySize.x++;
1275 }
1276 if (pageSize.y < EPSILON)
1277 job->pagesArraySize.y = 1;
1278 else {
1279 job->pagesArraySize.y = (int)(imageSize.y / pageSize.y);
1280 if (imageSize.y - job->pagesArraySize.y * pageSize.y > EPSILON)
1281 job->pagesArraySize.y++;
1282 }
1283 job->numPages = job->pagesArraySize.x * job->pagesArraySize.y;
1284
1285 /* find the drawable size in points */
1286 imageSize.x = fmin(imageSize.x, pageSize.x);
1287 imageSize.y = fmin(imageSize.y, pageSize.y);
1288 } else {
1289 /* page not set by user, use default from renderer */
1290 if (job->render.features) {
1291 pageSize.x = job->device.features->default_pagesize.x - 2*margin.x;
1292 pageSize.x = fmax(pageSize.x, 0);
1293 pageSize.y = job->device.features->default_pagesize.y - 2*margin.y;
1294 pageSize.y = fmax(pageSize.y, 0);
1295 }
1296 else
1297 pageSize.x = pageSize.y = 0.;
1298 job->pagesArraySize.x = job->pagesArraySize.y = job->numPages = 1;
1299
1300 pageSize.x = fmax(pageSize.x, imageSize.x);
1301 pageSize.y = fmax(pageSize.y, imageSize.y);
1302 }
1303
1304 /* initial window size */
1305 job->width = ROUND((pageSize.x + 2*margin.x) * job->dpi.x / POINTS_PER_INCH);
1306 job->height = ROUND((pageSize.y + 2*margin.y) * job->dpi.y / POINTS_PER_INCH);
1307
1308 /* set up pagedir */
1309 job->pagesArrayMajor = (point){0};
1310 job->pagesArrayMinor = (point){0};
1311 job->pagesArrayFirst = (point){0};
1312 job->pagesArrayMajor = pagecode(job, gvc->pagedir[0]);
1313 job->pagesArrayMinor = pagecode(job, gvc->pagedir[1]);
1314 if (abs(job->pagesArrayMajor.x + job->pagesArrayMinor.x) != 1
1315 || abs(job->pagesArrayMajor.y + job->pagesArrayMinor.y) != 1) {
1316 job->pagesArrayMajor = pagecode(job, 'B');
1317 job->pagesArrayMinor = pagecode(job, 'L');
1318 agwarningf("pagedir=%s ignored\n", gvc->pagedir);
1319 }
1320
1321 /* determine page box including centering */
1322 if (GD_drawing(g)->centered) {
1323 if (pageSize.x > imageSize.x)
1324 centering.x = (pageSize.x - imageSize.x) / 2;
1325 if (pageSize.y > imageSize.y)
1326 centering.y = (pageSize.y - imageSize.y) / 2;
1327 }
1328
1329 /* rotate back into graph orientation */
1330 if (job->rotation) {
1331 imageSize = exch_xyf(imageSize);
1332 pageSize = exch_xyf(pageSize);
1333 margin = exch_xyf(margin);
1334 centering = exch_xyf(centering);
1335 }
1336
1337 /* canvas area, centered if necessary */
1338 job->canvasBox.LL.x = margin.x + centering.x;
1339 job->canvasBox.LL.y = margin.y + centering.y;
1340 job->canvasBox.UR.x = margin.x + centering.x + imageSize.x;
1341 job->canvasBox.UR.y = margin.y + centering.y + imageSize.y;
1342
1343 /* size of one page in graph units */
1344 job->pageSize.x = imageSize.x / job->zoom;
1345 job->pageSize.y = imageSize.y / job->zoom;
1346
1347 /* pageBoundingBox in device units and page orientation */
1348 job->pageBoundingBox.LL.x = ROUND(job->canvasBox.LL.x * job->dpi.x / POINTS_PER_INCH);
1349 job->pageBoundingBox.LL.y = ROUND(job->canvasBox.LL.y * job->dpi.y / POINTS_PER_INCH);
1350 job->pageBoundingBox.UR.x = ROUND(job->canvasBox.UR.x * job->dpi.x / POINTS_PER_INCH);
1351 job->pageBoundingBox.UR.y = ROUND(job->canvasBox.UR.y * job->dpi.y / POINTS_PER_INCH);
1352 if (job->rotation) {
1355 job->canvasBox.LL = exch_xyf(job->canvasBox.LL);
1356 job->canvasBox.UR = exch_xyf(job->canvasBox.UR);
1357 }
1358}
1359
1360static void firstpage(GVJ_t *job)
1361{
1362 job->pagesArrayElem = job->pagesArrayFirst;
1363}
1364
1365static bool validpage(GVJ_t *job)
1366{
1367 return job->pagesArrayElem.x >= 0
1368 && job->pagesArrayElem.x < job->pagesArraySize.x
1369 && job->pagesArrayElem.y >= 0
1370 && job->pagesArrayElem.y < job->pagesArraySize.y;
1371}
1372
1373static void nextpage(GVJ_t *job)
1374{
1376 if (!validpage(job)) {
1377 if (job->pagesArrayMajor.y)
1378 job->pagesArrayElem.x = job->pagesArrayFirst.x;
1379 else
1380 job->pagesArrayElem.y = job->pagesArrayFirst.y;
1382 }
1383}
1384
1385static bool write_edge_test(Agraph_t * g, Agedge_t * e)
1386{
1387 Agraph_t *sg;
1388 int c;
1389
1390 for (c = 1; c <= GD_n_cluster(g); c++) {
1391 sg = GD_clust(g)[c];
1392 if (agcontains(sg, e))
1393 return false;
1394 }
1395 return true;
1396}
1397
1398static bool write_node_test(Agraph_t * g, Agnode_t * n)
1399{
1400 Agraph_t *sg;
1401 int c;
1402
1403 for (c = 1; c <= GD_n_cluster(g); c++) {
1404 sg = GD_clust(g)[c];
1405 if (agcontains(sg, n))
1406 return false;
1407 }
1408 return true;
1409}
1410
1411static pointf *copyPts(xdot_point *inpts, size_t numpts) {
1412 pointf *pts = gv_calloc(numpts, sizeof(pointf));
1413 for (size_t i = 0; i < numpts; i++) {
1414 pts[i].x = inpts[i].x;
1415 pts[i].y = inpts[i].y;
1416 }
1417 return pts;
1418}
1419
1420static void emit_xdot (GVJ_t * job, xdot* xd)
1421{
1422 int image_warn = 1;
1423 exdot_op* op;
1424 int angle;
1425 char** styles = NULL;
1426 int filled = FILL;
1427
1428 op = (exdot_op*)(xd->ops);
1429 for (size_t i = 0; i < xd->cnt; i++) {
1430 switch (op->op.kind) {
1431 case xd_filled_ellipse :
1432 case xd_unfilled_ellipse :
1433 if (boxf_overlap(op->bb, job->clip)) {
1434 pointf pts[] = {{.x = op->op.u.ellipse.x - op->op.u.ellipse.w,
1435 .y = op->op.u.ellipse.y - op->op.u.ellipse.h},
1436 {.x = op->op.u.ellipse.x + op->op.u.ellipse.w,
1437 .y = op->op.u.ellipse.y + op->op.u.ellipse.h}};
1438 gvrender_ellipse(job, pts, op->op.kind == xd_filled_ellipse ? filled : 0);
1439 }
1440 break;
1441 case xd_filled_polygon :
1442 case xd_unfilled_polygon :
1443 if (boxf_overlap(op->bb, job->clip)) {
1444 pointf *pts = copyPts(op->op.u.polygon.pts, op->op.u.polygon.cnt);
1445 assert(op->op.u.polygon.cnt <= INT_MAX &&
1446 "polygon count exceeds gvrender_polygon support");
1447 gvrender_polygon(job, pts, op->op.u.polygon.cnt,
1448 op->op.kind == xd_filled_polygon ? filled : 0);
1449 free(pts);
1450 }
1451 break;
1452 case xd_filled_bezier :
1453 case xd_unfilled_bezier :
1454 if (boxf_overlap(op->bb, job->clip)) {
1455 pointf *pts = copyPts(op->op.u.bezier.pts, op->op.u.bezier.cnt);
1456 gvrender_beziercurve(job, pts, op->op.u.bezier.cnt,
1457 op->op.kind == xd_filled_bezier ? filled : 0);
1458 free(pts);
1459 }
1460 break;
1461 case xd_polyline :
1462 if (boxf_overlap(op->bb, job->clip)) {
1463 pointf *pts = copyPts(op->op.u.polyline.pts, op->op.u.polyline.cnt);
1464 gvrender_polyline(job, pts, op->op.u.polyline.cnt);
1465 free(pts);
1466 }
1467 break;
1468 case xd_text :
1469 if (boxf_overlap(op->bb, job->clip)) {
1470 pointf pt = {.x = op->op.u.text.x, .y = op->op.u.text.y};
1471 gvrender_textspan(job, pt, op->span);
1472 }
1473 break;
1474 case xd_fill_color :
1475 gvrender_set_fillcolor(job, op->op.u.color);
1476 filled = FILL;
1477 break;
1478 case xd_pen_color :
1479 gvrender_set_pencolor(job, op->op.u.color);
1480 filled = FILL;
1481 break;
1482 case xd_grad_fill_color :
1483 {
1484 char* clr0;
1485 char* clr1;
1486 float frac;
1487 if (op->op.u.grad_color.type == xd_radial) {
1489 clr0 = p->stops[0].color;
1490 clr1 = p->stops[1].color;
1491 frac = p->stops[1].frac;
1492 if (p->x1 == p->x0 && p->y1 == p->y0)
1493 angle = 0;
1494 else
1495 angle = (int)(180.0*acos((p->x0 - p->x1)/p->r0)/M_PI);
1496 gvrender_set_fillcolor(job, clr0);
1497 gvrender_set_gradient_vals(job, clr1, angle, frac);
1498 filled = RGRADIENT;
1499 }
1500 else {
1502 clr0 = p->stops[0].color;
1503 clr1 = p->stops[1].color;
1504 frac = p->stops[1].frac;
1505 angle = (int)(180.0*atan2(p->y1-p->y0,p->x1-p->x0)/M_PI);
1506 gvrender_set_fillcolor(job, clr0);
1507 gvrender_set_gradient_vals(job, clr1, angle, frac);
1508 filled = GRADIENT;
1509 }
1510 }
1511 break;
1512 case xd_grad_pen_color :
1513 agwarningf("gradient pen colors not yet supported.\n");
1514 break;
1515 case xd_font :
1516 /* fontsize and fontname already encoded via xdotBB */
1517 break;
1518 case xd_style :
1519 styles = parse_style (op->op.u.style);
1520 gvrender_set_style (job, styles);
1521 break;
1522 case xd_fontchar :
1523 /* font characteristics already encoded via xdotBB */
1524 break;
1525 case xd_image :
1526 if (image_warn) {
1527 agwarningf("Images unsupported in \"background\" attribute\n");
1528 image_warn = 0;
1529 }
1530 break;
1531 default:
1532 UNREACHABLE();
1533 }
1534 op++;
1535 }
1536 if (styles)
1538}
1539
1540static void emit_background(GVJ_t * job, graph_t *g)
1541{
1542 xdot* xd;
1543 char *str;
1544 int dfltColor;
1545
1546 /* if no bgcolor specified - first assume default of "white" */
1547 if (! ((str = agget(g, "bgcolor")) && str[0])) {
1548 str = "white";
1549 dfltColor = 1;
1550 }
1551 else
1552 dfltColor = 0;
1553
1554
1555 /* if device has no truecolor support, change "transparent" to "white" */
1556 if (! (job->flags & GVDEVICE_DOES_TRUECOLOR) && (streq(str, "transparent"))) {
1557 str = "white";
1558 dfltColor = 1;
1559 }
1560
1561 /* except for "transparent" on truecolor, or default "white" on (assumed) white paper, paint background */
1562 if (!( ((job->flags & GVDEVICE_DOES_TRUECOLOR) && streq(str, "transparent"))
1563 || ((job->flags & GVRENDER_NO_WHITE_BG) && dfltColor))) {
1564 char* clrs[2];
1565 double frac;
1566
1567 if ((findStopColor (str, clrs, &frac))) {
1568 int filled;
1569 graphviz_polygon_style_t istyle = {0};
1570 gvrender_set_fillcolor(job, clrs[0]);
1571 gvrender_set_pencolor(job, "transparent");
1572 checkClusterStyle(g, &istyle);
1573 if (clrs[1])
1574 gvrender_set_gradient_vals(job,clrs[1],late_int(g,G_gradientangle,0,0), frac);
1575 else
1577 if (istyle.radial)
1578 filled = RGRADIENT;
1579 else
1580 filled = GRADIENT;
1581 gvrender_box(job, job->clip, filled);
1582 free (clrs[0]);
1583 }
1584 else {
1586 gvrender_set_pencolor(job, "transparent");
1587 gvrender_box(job, job->clip, FILL); /* filled */
1588 }
1589 }
1590
1591 if ((xd = GD_drawing(g)->xdots))
1592 emit_xdot (job, xd);
1593}
1594
1595static void setup_page(GVJ_t * job)
1596{
1597 point pagesArrayElem = job->pagesArrayElem, pagesArraySize = job->pagesArraySize;
1598
1599 if (job->rotation) {
1600 pagesArrayElem = exch_xy(pagesArrayElem);
1601 pagesArraySize = exch_xy(pagesArraySize);
1602 }
1603
1604 /* establish current box in graph units */
1605 job->pageBox.LL.x = pagesArrayElem.x * job->pageSize.x - job->pad.x;
1606 job->pageBox.LL.y = pagesArrayElem.y * job->pageSize.y - job->pad.y;
1607 job->pageBox.UR.x = job->pageBox.LL.x + job->pageSize.x;
1608 job->pageBox.UR.y = job->pageBox.LL.y + job->pageSize.y;
1609
1610 /* maximum boundingBox in device units and page orientation */
1611 if (job->common->viewNum == 0)
1612 job->boundingBox = job->pageBoundingBox;
1613 else
1615
1616 if (job->flags & GVDEVICE_EVENTS) {
1617 job->clip.LL.x = job->focus.x - job->view.x / 2.;
1618 job->clip.LL.y = job->focus.y - job->view.y / 2.;
1619 job->clip.UR.x = job->focus.x + job->view.x / 2.;
1620 job->clip.UR.y = job->focus.y + job->view.y / 2.;
1621 }
1622 else {
1623 job->clip.LL.x = job->focus.x + job->pageSize.x * (pagesArrayElem.x - pagesArraySize.x / 2.);
1624 job->clip.LL.y = job->focus.y + job->pageSize.y * (pagesArrayElem.y - pagesArraySize.y / 2.);
1625 job->clip.UR.x = job->clip.LL.x + job->pageSize.x;
1626 job->clip.UR.y = job->clip.LL.y + job->pageSize.y;
1627 }
1628
1629 /* CAUTION - job->translation was difficult to get right. */
1630 // Test with and without asymmetric margins, e.g: -Gmargin="1,0"
1631 if (job->rotation) {
1632 job->translation.y = - job->clip.UR.y - job->canvasBox.LL.y / job->zoom;
1633 if ((job->flags & GVRENDER_Y_GOES_DOWN) || Y_invert)
1634 job->translation.x = - job->clip.UR.x - job->canvasBox.LL.x / job->zoom;
1635 else
1636 job->translation.x = - job->clip.LL.x + job->canvasBox.LL.x / job->zoom;
1637 }
1638 else {
1639 /* pre unscale margins to keep them constant under scaling */
1640 job->translation.x = - job->clip.LL.x + job->canvasBox.LL.x / job->zoom;
1641 if ((job->flags & GVRENDER_Y_GOES_DOWN) || Y_invert)
1642 job->translation.y = - job->clip.UR.y - job->canvasBox.LL.y / job->zoom;
1643 else
1644 job->translation.y = - job->clip.LL.y + job->canvasBox.LL.y / job->zoom;
1645 }
1646}
1647
1648static bool node_in_layer(GVJ_t *job, graph_t * g, node_t * n)
1649{
1650 char *pn, *pe;
1651 edge_t *e;
1652
1653 if (job->numLayers <= 1)
1654 return true;
1655 pn = late_string(n, N_layer, "");
1656 if (selectedlayer(job, pn))
1657 return true;
1658 if (pn[0])
1659 return false; /* Only check edges if pn = "" */
1660 if ((e = agfstedge(g, n)) == NULL)
1661 return true;
1662 for (e = agfstedge(g, n); e; e = agnxtedge(g, e, n)) {
1663 pe = late_string(e, E_layer, "");
1664 if (pe[0] == '\0' || selectedlayer(job, pe))
1665 return true;
1666 }
1667 return false;
1668}
1669
1670static bool edge_in_layer(GVJ_t *job, edge_t * e)
1671{
1672 char *pe, *pn;
1673 int cnt;
1674
1675 if (job->numLayers <= 1)
1676 return true;
1677 pe = late_string(e, E_layer, "");
1678 if (selectedlayer(job, pe))
1679 return true;
1680 if (pe[0])
1681 return false;
1682 for (cnt = 0; cnt < 2; cnt++) {
1683 pn = late_string(cnt < 1 ? agtail(e) : aghead(e), N_layer, "");
1684 if (pn[0] == '\0' || selectedlayer(job, pn))
1685 return true;
1686 }
1687 return false;
1688}
1689
1690static bool clust_in_layer(GVJ_t *job, graph_t * sg)
1691{
1692 char *pg;
1693 node_t *n;
1694
1695 if (job->numLayers <= 1)
1696 return true;
1697 pg = late_string(sg, agattr(sg, AGRAPH, "layer", 0), "");
1698 if (selectedlayer(job, pg))
1699 return true;
1700 if (pg[0])
1701 return false;
1702 for (n = agfstnode(sg); n; n = agnxtnode(sg, n))
1703 if (node_in_layer(job, sg, n))
1704 return true;
1705 return false;
1706}
1707
1708static bool node_in_box(node_t *n, boxf b)
1709{
1710 return boxf_overlap(ND_bb(n), b);
1711}
1712
1714
1715static void emit_begin_node(GVJ_t * job, node_t * n)
1716{
1717 obj_state_t *obj;
1718 int flags = job->flags;
1719 int shape;
1720 size_t nump = 0;
1721 polygon_t *poly = NULL;
1722 pointf *vertices, *p = NULL;
1723 pointf coord;
1724 char *s;
1725
1726 obj = push_obj_state(job);
1727 obj->type = NODE_OBJTYPE;
1728 obj->u.n = n;
1729 obj->emit_state = EMIT_NDRAW;
1730
1731 if (flags & GVRENDER_DOES_Z) {
1732 if (GD_odim(agraphof(n)) >=3)
1733 obj->z = POINTS(ND_pos(n)[2]);
1734 else
1735 obj->z = 0.0;
1736 }
1737 initObjMapData (job, ND_label(n), n);
1739 && (obj->url || obj->explicit_tooltip)) {
1740
1741 /* checking shape of node */
1742 shape = shapeOf(n);
1743 /* node coordinate */
1744 coord = ND_coord(n);
1745 /* checking if filled style has been set for node */
1746 bool filled = isFilled(n);
1747
1748 bool is_rect = false;
1749 if (shape == SH_POLY || shape == SH_POINT) {
1750 poly = ND_shape_info(n);
1751
1752 /* checking if polygon is regular rectangle */
1753 if (isRect(poly) && (poly->peripheries || filled))
1754 is_rect = true;
1755 }
1756
1757 /* When node has polygon shape and requested output supports polygons
1758 * we use a polygon to map the clickable region that is a:
1759 * circle, ellipse, polygon with n side, or point.
1760 * For regular rectangular shape we have use node's bounding box to map clickable region
1761 */
1762 if (poly && !is_rect && (flags & GVRENDER_DOES_MAP_POLYGON)) {
1763
1764 const size_t sides = poly->sides < 3 ? 1 : poly->sides;
1765 const size_t peripheries = poly->peripheries < 2 ? 1 : poly->peripheries;
1766
1767 vertices = poly->vertices;
1768
1769 int nump_int = 0;
1770 if ((s = agget(n, "samplepoints")))
1771 nump_int = atoi(s);
1772 /* We want at least 4 points. For server-side maps, at most 100
1773 * points are allowed. To simplify things to fit with the 120 points
1774 * used for skewed ellipses, we set the bound at 60.
1775 */
1776 nump = (nump_int < 4 || nump_int > 60) ? DFLT_SAMPLE : (size_t)nump_int;
1777 /* use bounding box of text label or node image for mapping
1778 * when polygon has no peripheries and node is not filled
1779 */
1780 if (poly->peripheries == 0 && !filled) {
1782 nump = 2;
1783 p = gv_calloc(nump, sizeof(pointf));
1784 P2RECT(coord, p, ND_lw(n), ND_ht(n) / 2.0 );
1785 }
1786 /* circle or ellipse */
1787 else if (poly->sides < 3 && is_exactly_zero(poly->skew) &&
1788 is_exactly_zero(poly->distortion)) {
1789 if (poly->regular) {
1791 nump = 2; /* center of circle and top right corner of bb */
1792 p = gv_calloc(nump, sizeof(pointf));
1793 p[0].x = coord.x;
1794 p[0].y = coord.y;
1795 /* even vertices contain LL corner of bb */
1796 /* odd vertices contain UR corner of bb */
1797 p[1].x = coord.x + vertices[2*peripheries - 1].x;
1798 p[1].y = coord.y + vertices[2*peripheries - 1].y;
1799 }
1800 else { /* ellipse is treated as polygon */
1802 p = pEllipse(vertices[2 * peripheries - 1].x,
1803 vertices[2 * peripheries - 1].y, nump);
1804 for (size_t i = 0; i < nump; i++) {
1805 p[i].x += coord.x;
1806 p[i].y += coord.y;
1807 }
1808 }
1809 }
1810 /* all other polygonal shape */
1811 else {
1812 assert(peripheries >= 1);
1813 size_t offset = (peripheries - 1) * poly->sides;
1815 /* distorted or skewed ellipses and circles are polygons with 120
1816 * sides. For mapping we convert them into polygon with sample sides
1817 */
1818 if (poly->sides >= nump) {
1819 size_t delta = poly->sides / nump;
1820 p = gv_calloc(nump, sizeof(pointf));
1821 for (size_t i = 0, j = 0; j < nump; i += delta, j++) {
1822 p[j].x = coord.x + vertices[i + offset].x;
1823 p[j].y = coord.y + vertices[i + offset].y;
1824 }
1825 } else {
1826 nump = sides;
1827 p = gv_calloc(nump, sizeof(pointf));
1828 for (size_t i = 0; i < nump; i++) {
1829 p[i].x = coord.x + vertices[i + offset].x;
1830 p[i].y = coord.y + vertices[i + offset].y;
1831 }
1832 }
1833 }
1834 }
1835 else {
1836 /* we have to use the node's bounding box to map clickable region
1837 * when requested output format is not capable of polygons.
1838 */
1840 nump = 2;
1841 p = gv_calloc(nump, sizeof(pointf));
1842 p[0].x = coord.x - ND_lw(n);
1843 p[0].y = coord.y - (ND_ht(n) / 2);
1844 p[1].x = coord.x + ND_rw(n);
1845 p[1].y = coord.y + (ND_ht(n) / 2);
1846 }
1848 gvrender_ptf_A(job, p, p, nump);
1849 obj->url_map_p = p;
1850 obj->url_map_n = nump;
1851 }
1852
1853 saved_color_scheme = setColorScheme(agget(n, "colorscheme"));
1855}
1856
1857static void emit_end_node(GVJ_t * job)
1858{
1859 gvrender_end_node(job);
1860
1861 char *color_scheme = setColorScheme(saved_color_scheme);
1862 free(color_scheme);
1865
1866 pop_obj_state(job);
1867}
1868
1869static void emit_node(GVJ_t * job, node_t * n)
1870{
1871 GVC_t *gvc = job->gvc;
1872 char *s;
1873 char *style;
1874 char **styles = NULL;
1875 char **sp;
1876 char *p;
1877
1878 if (ND_shape(n) /* node has a shape */
1879 && node_in_layer(job, agraphof(n), n) /* and is in layer */
1880 && node_in_box(n, job->clip) /* and is in page/view */
1881 && ND_state(n) != gvc->common.viewNum) /* and not already drawn */
1882 {
1883 ND_state(n) = gvc->common.viewNum; /* mark node as drawn */
1884
1885 gvrender_comment(job, agnameof(n));
1886 s = late_string(n, N_comment, "");
1887 if (s[0])
1888 gvrender_comment(job, s);
1889
1890 style = late_string(n, N_style, "");
1891 if (style[0]) {
1892 styles = parse_style(style);
1893 sp = styles;
1894 while ((p = *sp++)) {
1895 if (streq(p, "invis")) return;
1896 }
1897 }
1898
1899 emit_begin_node(job, n);
1900 ND_shape(n)->fns->codefn(job, n);
1901 if (ND_xlabel(n) && ND_xlabel(n)->set)
1903 emit_end_node(job);
1904 }
1905}
1906
1907/* calculate an offset vector, length d, perpendicular to line p,q */
1909{
1910 pointf res;
1911 double x = p.x - q.x, y = p.y - q.y;
1912
1913 /* keep d finite as line length approaches 0 */
1914 d /= sqrt(x * x + y * y + EPSILON);
1915 res.x = y * d;
1916 res.y = -x * d;
1917 return res;
1918}
1919
1920/* calculate offset vector, length d, perpendicular to spline p,q,r,s at q&r */
1922 double d)
1923{
1924 pointf res;
1925 double len;
1926 double x = q.x - r.x, y = q.y - r.y;
1927
1928 len = hypot(x, y);
1929 if (len < EPSILON) {
1930 /* control points are on top of each other
1931 use slope between endpoints instead */
1932 x = p.x - s.x, y = p.y - s.y;
1933 /* keep d finite as line length approaches 0 */
1934 len = sqrt(x * x + y * y + EPSILON);
1935 }
1936 d /= len;
1937 res.x = y * d;
1938 res.y = -x * d;
1939 return res;
1940}
1941
1942static void emit_attachment(GVJ_t * job, textlabel_t * lp, splines * spl)
1943{
1944 pointf sz, AF[3];
1945 const char *s;
1946
1947 for (s = lp->text; *s; s++) {
1948 if (!gv_isspace(*s))
1949 break;
1950 }
1951 if (*s == '\0')
1952 return;
1953
1954 sz = lp->dimen;
1955 AF[0] = (pointf){lp->pos.x + sz.x / 2., lp->pos.y - sz.y / 2.};
1956 AF[1] = (pointf){AF[0].x - sz.x, AF[0].y};
1957 AF[2] = dotneato_closest(spl, lp->pos);
1958 /* Don't use edge style to draw attachment */
1960 /* Use font color to draw attachment
1961 - need something unambiguous in case of multicolored parallel edges
1962 - defaults to black for html-like labels
1963 */
1965 gvrender_polyline(job, AF, 3);
1966}
1967
1968/* edges colors can be multiple colors separated by ":"
1969 * so we commpute a default pencolor with the same number of colors. */
1970static char* default_pencolor(char *pencolor, char *deflt)
1971{
1972 static agxbuf buf;
1973 char *p;
1974 size_t ncol = 1;
1975 for (p = pencolor; *p; p++) {
1976 if (*p == ':')
1977 ncol++;
1978 }
1979 agxbput(&buf, deflt);
1980 while(--ncol) {
1981 agxbprint(&buf, ":%s", deflt);
1982 }
1983 return agxbuse(&buf);
1984}
1985
1986static double approxLen (pointf* pts)
1987{
1988 double d = DIST(pts[0],pts[1]);
1989 d += DIST(pts[1],pts[2]);
1990 d += DIST(pts[2],pts[3]);
1991 return d;
1992}
1993
1994/* Given B-spline bz and 0 < t < 1, split bz so that left corresponds to
1995 * the fraction t of the arc length. The new parts are store in left and right.
1996 * The caller needs to free the allocated points.
1997 *
1998 * In the current implementation, we find the Bezier that should contain t by
1999 * treating the control points as a polyline.
2000 * We then split that Bezier.
2001 */
2002static void splitBSpline(bezier *bz, double t, bezier *left, bezier *right) {
2003 const size_t cnt = (bz->size - 1) / 3;
2004 double last, len, sum;
2005 pointf* pts;
2006
2007 if (cnt == 1) {
2008 left->size = 4;
2009 left->list = gv_calloc(4, sizeof(pointf));
2010 right->size = 4;
2011 right->list = gv_calloc(4, sizeof(pointf));
2012 Bezier (bz->list, t, left->list, right->list);
2013 return;
2014 }
2015
2016 double* lens = gv_calloc(cnt, sizeof(double));
2017 sum = 0;
2018 pts = bz->list;
2019 for (size_t i = 0; i < cnt; i++) {
2020 lens[i] = approxLen (pts);
2021 sum += lens[i];
2022 pts += 3;
2023 }
2024 len = t*sum;
2025 sum = 0;
2026 size_t i;
2027 for (i = 0; i < cnt; i++) {
2028 sum += lens[i];
2029 if (sum >= len)
2030 break;
2031 }
2032
2033 left->size = 3*(i+1) + 1;
2034 left->list = gv_calloc(left->size, sizeof(pointf));
2035 right->size = 3*(cnt-i) + 1;
2036 right->list = gv_calloc(right->size, sizeof(pointf));
2037 size_t j;
2038 for (j = 0; j < left->size; j++)
2039 left->list[j] = bz->list[j];
2040 size_t k = j - 4;
2041 for (j = 0; j < right->size; j++)
2042 right->list[j] = bz->list[k++];
2043
2044 last = lens[i];
2045 const double r = (len - (sum - last)) / last;
2046 Bezier (bz->list + 3*i, r, left->list + 3*i, right->list);
2047
2048 free (lens);
2049}
2050
2051/* Draw an edge as a sequence of colors.
2052 * Not sure how to handle multiple B-splines, so do a naive
2053 * implementation.
2054 * Return non-zero if color spec is incorrect
2055 */
2056static int multicolor(GVJ_t *job, edge_t *e, char **styles, char *colors,
2057 size_t num, double arrowsize, double penwidth) {
2058 bezier bz;
2059 bezier bz0, bz_l, bz_r;
2060 int rv;
2061 colorsegs_t segs;
2062 char* endcolor = NULL;
2063 double left;
2064 int first; /* first segment with t > 0 */
2065
2066 rv = parseSegs (colors, num, &segs);
2067 if (rv > 1) {
2068 Agraph_t* g = agraphof(agtail(e));
2069 agerr (AGPREV, "in edge %s%s%s\n", agnameof(agtail(e)), (agisdirected(g)?" -> ":" -- "), agnameof(aghead(e)));
2070
2071 if (rv == 2)
2072 return 1;
2073 }
2074 else if (rv == 1)
2075 return 1;
2076
2077
2078 for (size_t i = 0; i < ED_spl(e)->size; i++) {
2079 left = 1;
2080 bz = ED_spl(e)->list[i];
2081 first = 1;
2082 for (colorseg_t *s = segs.segs; s->color; s++) {
2083 if (AEQ0(s->t)) continue;
2084 gvrender_set_pencolor(job, s->color);
2085 left -= s->t;
2086 endcolor = s->color;
2087 if (first) {
2088 first = 0;
2089 splitBSpline (&bz, s->t, &bz_l, &bz_r);
2090 gvrender_beziercurve(job, bz_l.list, bz_l.size, 0);
2091 free (bz_l.list);
2092 if (AEQ0(left)) {
2093 free (bz_r.list);
2094 break;
2095 }
2096 }
2097 else if (AEQ0(left)) {
2098 gvrender_beziercurve(job, bz_r.list, bz_r.size, 0);
2099 free (bz_r.list);
2100 break;
2101 }
2102 else {
2103 bz0 = bz_r;
2104 splitBSpline(&bz0, s->t / (left + s->t), &bz_l, &bz_r);
2105 free (bz0.list);
2106 gvrender_beziercurve(job, bz_l.list, bz_l.size, 0);
2107 free (bz_l.list);
2108 }
2109
2110 }
2111 /* arrow_gen resets the job style (How? FIXME)
2112 * If we have more splines to do, restore the old one.
2113 * Use local copy of penwidth to work around reset.
2114 */
2115 if (bz.sflag) {
2116 gvrender_set_pencolor(job, segs.segs->color);
2117 gvrender_set_fillcolor(job, segs.segs->color);
2118 arrow_gen(job, EMIT_TDRAW, bz.sp, bz.list[0], arrowsize, penwidth, bz.sflag);
2119 }
2120 if (bz.eflag) {
2121 gvrender_set_pencolor(job, endcolor);
2122 gvrender_set_fillcolor(job, endcolor);
2123 arrow_gen(job, EMIT_HDRAW, bz.ep, bz.list[bz.size - 1], arrowsize, penwidth, bz.eflag);
2124 }
2125 if (ED_spl(e)->size > 1 && (bz.sflag || bz.eflag) && styles)
2126 gvrender_set_style(job, styles);
2127 }
2128 freeSegs(&segs);
2129 return 0;
2130}
2131
2132static void free_stroke(stroke_t sp) {
2133 free(sp.vertices);
2134}
2135
2136typedef double (*radfunc_t)(double,double,double);
2137
2138static double forfunc (double curlen, double totallen, double initwid)
2139{
2140 return (1 - curlen / totallen) * initwid / 2.0;
2141}
2142
2143static double revfunc (double curlen, double totallen, double initwid)
2144{
2145 return curlen / totallen * initwid / 2.0;
2146}
2147
2148static double nonefunc (double curlen, double totallen, double initwid)
2149{
2150 (void)curlen;
2151 (void)totallen;
2152
2153 return initwid / 2.0;
2154}
2155
2156static double bothfunc (double curlen, double totallen, double initwid)
2157{
2158 double fr = curlen/totallen;
2159 if (fr <= 0.5) return fr * initwid;
2160 return (1 - fr) * initwid;
2161}
2162
2163static radfunc_t
2165{
2166 char* attr;
2167 if (E_dir && ((attr = agxget(e, E_dir)))[0]) {
2168 if (streq(attr, "forward")) return forfunc;
2169 if (streq(attr, "back")) return revfunc;
2170 if (streq(attr, "both")) return bothfunc;
2171 if (streq(attr, "none")) return nonefunc;
2172 }
2174}
2175
2176static void emit_edge_graphics(GVJ_t * job, edge_t * e, char** styles)
2177{
2178 int cnum, numsemi = 0;
2179 char *color, *pencolor, *fillcolor;
2180 char *headcolor, *tailcolor, *lastcolor;
2181 char *colors = NULL;
2182 bezier bz;
2183 splines offspl, tmpspl;
2184 pointf pf0, pf1, pf2 = { 0, 0 }, pf3, *offlist, *tmplist;
2185 double arrowsize, numc2, penwidth=job->obj->penwidth;
2186 char* p;
2187 bool tapered = false;
2188
2189#define SEP 2.0
2190
2191 char *previous_color_scheme = setColorScheme(agget(e, "colorscheme"));
2192 if (ED_spl(e)) {
2193 arrowsize = late_double(e, E_arrowsz, 1.0, 0.0);
2194 color = late_string(e, E_color, "");
2195
2196 if (styles) {
2197 char** sp = styles;
2198 while ((p = *sp++)) {
2199 if (streq(p, "tapered")) {
2200 tapered = true;
2201 break;
2202 }
2203 }
2204 }
2205
2206 /* need to know how many colors separated by ':' */
2207 size_t numc = 0;
2208 for (p = color; *p; p++) {
2209 if (*p == ':')
2210 numc++;
2211 else if (*p == ';')
2212 numsemi++;
2213 }
2214
2215 if (numsemi && numc) {
2216 if (multicolor (job, e, styles, color, numc+1, arrowsize, penwidth)) {
2218 }
2219 else
2220 goto done;
2221 }
2222
2223 fillcolor = pencolor = color;
2224 if (ED_gui_state(e) & GUI_STATE_ACTIVE) {
2225 pencolor = late_nnstring(e, E_activepencolor,
2228 }
2229 else if (ED_gui_state(e) & GUI_STATE_SELECTED) {
2230 pencolor = late_nnstring(e, E_selectedpencolor,
2233 }
2234 else if (ED_gui_state(e) & GUI_STATE_DELETED) {
2235 pencolor = late_nnstring(e, E_deletedpencolor,
2238 }
2239 else if (ED_gui_state(e) & GUI_STATE_VISITED) {
2240 pencolor = late_nnstring(e, E_visitedpencolor,
2243 }
2244 else
2245 fillcolor = late_nnstring(e, E_fillcolor, color);
2246 if (pencolor != color)
2247 gvrender_set_pencolor(job, pencolor);
2248 if (fillcolor != color)
2249 gvrender_set_fillcolor(job, fillcolor);
2250 color = pencolor;
2251
2252 if (tapered) {
2253 if (*color == '\0') color = DEFAULT_COLOR;
2254 if (*fillcolor == '\0') fillcolor = DEFAULT_COLOR;
2255 gvrender_set_pencolor(job, "transparent");
2257 bz = ED_spl(e)->list[0];
2258 stroke_t stp = taper(&bz, taperfun (e), penwidth);
2259 assert(stp.nvertices <= INT_MAX);
2260 gvrender_polygon(job, stp.vertices, stp.nvertices, 1);
2261 free_stroke(stp);
2263 if (fillcolor != color)
2264 gvrender_set_fillcolor(job, fillcolor);
2265 if (bz.sflag) {
2266 arrow_gen(job, EMIT_TDRAW, bz.sp, bz.list[0], arrowsize, penwidth, bz.sflag);
2267 }
2268 if (bz.eflag) {
2269 arrow_gen(job, EMIT_HDRAW, bz.ep, bz.list[bz.size - 1], arrowsize, penwidth, bz.eflag);
2270 }
2271 }
2272 /* if more than one color - then generate parallel beziers, one per color */
2273 else if (numc) {
2274 /* calculate and save offset vector spline and initialize first offset spline */
2275 tmpspl.size = offspl.size = ED_spl(e)->size;
2276 offspl.list = gv_calloc(offspl.size, sizeof(bezier));
2277 tmpspl.list = gv_calloc(tmpspl.size, sizeof(bezier));
2278 numc2 = (2 + (double)numc) / 2.0;
2279 for (size_t i = 0; i < offspl.size; i++) {
2280 bz = ED_spl(e)->list[i];
2281 tmpspl.list[i].size = offspl.list[i].size = bz.size;
2282 offlist = offspl.list[i].list = gv_calloc(bz.size, sizeof(pointf));
2283 tmplist = tmpspl.list[i].list = gv_calloc(bz.size, sizeof(pointf));
2284 pf3 = bz.list[0];
2285 size_t j;
2286 for (j = 0; j < bz.size - 1; j += 3) {
2287 pf0 = pf3;
2288 pf1 = bz.list[j + 1];
2289 /* calculate perpendicular vectors for each bezier point */
2290 if (j == 0) /* first segment, no previous pf2 */
2291 offlist[j] = computeoffset_p(pf0, pf1, SEP);
2292 else /* i.e. pf2 is available from previous segment */
2293 offlist[j] = computeoffset_p(pf2, pf1, SEP);
2294 pf2 = bz.list[j + 2];
2295 pf3 = bz.list[j + 3];
2296 offlist[j + 1] = offlist[j + 2] =
2297 computeoffset_qr(pf0, pf1, pf2, pf3, SEP);
2298 /* initialize tmpspl to outermost position */
2299 tmplist[j].x = pf0.x - numc2 * offlist[j].x;
2300 tmplist[j].y = pf0.y - numc2 * offlist[j].y;
2301 tmplist[j + 1].x = pf1.x - numc2 * offlist[j + 1].x;
2302 tmplist[j + 1].y = pf1.y - numc2 * offlist[j + 1].y;
2303 tmplist[j + 2].x = pf2.x - numc2 * offlist[j + 2].x;
2304 tmplist[j + 2].y = pf2.y - numc2 * offlist[j + 2].y;
2305 }
2306 /* last segment, no next pf1 */
2307 offlist[j] = computeoffset_p(pf2, pf3, SEP);
2308 tmplist[j].x = pf3.x - numc2 * offlist[j].x;
2309 tmplist[j].y = pf3.y - numc2 * offlist[j].y;
2310 }
2311 lastcolor = headcolor = tailcolor = color;
2312 colors = gv_strdup(color);
2313 for (cnum = 0, color = strtok(colors, ":"); color;
2314 cnum++, color = strtok(0, ":")) {
2315 if (!color[0])
2317 if (color != lastcolor) {
2321 }
2322 lastcolor = color;
2323 }
2324 if (cnum == 0)
2325 headcolor = tailcolor = color;
2326 if (cnum == 1)
2327 tailcolor = color;
2328 for (size_t i = 0; i < tmpspl.size; i++) {
2329 tmplist = tmpspl.list[i].list;
2330 offlist = offspl.list[i].list;
2331 for (size_t j = 0; j < tmpspl.list[i].size; j++) {
2332 tmplist[j].x += offlist[j].x;
2333 tmplist[j].y += offlist[j].y;
2334 }
2335 gvrender_beziercurve(job, tmplist, tmpspl.list[i].size, 0);
2336 }
2337 }
2338 if (bz.sflag) {
2339 if (color != tailcolor) {
2340 color = tailcolor;
2344 }
2345 }
2346 arrow_gen(job, EMIT_TDRAW, bz.sp, bz.list[0],
2347 arrowsize, penwidth, bz.sflag);
2348 }
2349 if (bz.eflag) {
2350 if (color != headcolor) {
2351 color = headcolor;
2355 }
2356 }
2357 arrow_gen(job, EMIT_HDRAW, bz.ep, bz.list[bz.size - 1],
2358 arrowsize, penwidth, bz.eflag);
2359 }
2360 free(colors);
2361 for (size_t i = 0; i < offspl.size; i++) {
2362 free(offspl.list[i].list);
2363 free(tmpspl.list[i].list);
2364 }
2365 free(offspl.list);
2366 free(tmpspl.list);
2367 } else {
2369 if (color[0]) {
2371 gvrender_set_fillcolor(job, fillcolor);
2372 } else {
2374 if (fillcolor[0])
2375 gvrender_set_fillcolor(job, fillcolor);
2376 else
2378 }
2379 }
2380 for (size_t i = 0; i < ED_spl(e)->size; i++) {
2381 bz = ED_spl(e)->list[i];
2382 gvrender_beziercurve(job, bz.list, bz.size, 0);
2383 if (bz.sflag) {
2384 arrow_gen(job, EMIT_TDRAW, bz.sp, bz.list[0],
2385 arrowsize, penwidth, bz.sflag);
2386 }
2387 if (bz.eflag) {
2388 arrow_gen(job, EMIT_HDRAW, bz.ep, bz.list[bz.size - 1],
2389 arrowsize, penwidth, bz.eflag);
2390 }
2391 if (ED_spl(e)->size > 1 && (bz.sflag || bz.eflag) && styles)
2392 gvrender_set_style(job, styles);
2393 }
2394 }
2395 }
2396
2397done:;
2398 char *color_scheme = setColorScheme(previous_color_scheme);
2399 free(color_scheme);
2400 free(previous_color_scheme);
2401}
2402
2403static bool edge_in_box(edge_t *e, boxf b)
2404{
2405 splines *spl;
2406 textlabel_t *lp;
2407
2408 spl = ED_spl(e);
2409 if (spl && boxf_overlap(spl->bb, b))
2410 return true;
2411
2412 lp = ED_label(e);
2413 if (lp && overlap_label(lp, b))
2414 return true;
2415
2416 lp = ED_xlabel(e);
2417 if (lp && lp->set && overlap_label(lp, b))
2418 return true;
2419
2420 return false;
2421}
2422
2423static void emit_begin_edge(GVJ_t *job, edge_t *e, char **styles) {
2424 obj_state_t *obj;
2425 int flags = job->flags;
2426 char *s;
2427 textlabel_t *lab = NULL, *tlab = NULL, *hlab = NULL;
2428 pointf *pbs = NULL;
2429 size_t *pbs_n = NULL, pbs_poly_n = 0;
2430 char *dflt_url = NULL;
2431 char *dflt_target = NULL;
2432 double penwidth;
2433
2434 obj = push_obj_state(job);
2435 obj->type = EDGE_OBJTYPE;
2436 obj->u.e = e;
2437 obj->emit_state = EMIT_EDRAW;
2438 if (ED_label(e) && !ED_label(e)->html && mapbool(agget(e, "labelaligned")))
2439 obj->labeledgealigned = true;
2440
2441 /* We handle the edge style and penwidth here because the width
2442 * is needed below for calculating polygonal image maps
2443 */
2444 if (styles && ED_spl(e))
2445 gvrender_set_style(job, styles);
2446
2447 if (E_penwidth && (s = agxget(e, E_penwidth)) && s[0]) {
2448 penwidth = late_double(e, E_penwidth, 1.0, 0.0);
2450 }
2451
2452 if (flags & GVRENDER_DOES_Z) {
2453 if (GD_odim(agraphof(agtail(e))) >= 3) {
2454 obj->tail_z = POINTS(ND_pos(agtail(e))[2]);
2455 obj->head_z = POINTS(ND_pos(aghead(e))[2]);
2456 } else {
2457 obj->tail_z = obj->head_z = 0.0;
2458 }
2459 }
2460
2462 if ((lab = ED_label(e)))
2463 obj->label = lab->text;
2464 obj->taillabel = obj->headlabel = obj->xlabel = obj->label;
2465 if ((tlab = ED_xlabel(e)))
2466 obj->xlabel = tlab->text;
2467 if ((tlab = ED_tail_label(e)))
2468 obj->taillabel = tlab->text;
2469 if ((hlab = ED_head_label(e)))
2470 obj->headlabel = hlab->text;
2471 }
2472
2473 if (flags & GVRENDER_DOES_MAPS) {
2474 agxbuf xb = {0};
2475
2476 s = getObjId(job, e, &xb);
2477 obj->id = strdup_and_subst_obj(s, e);
2478 agxbfree(&xb);
2479
2480 if (((s = agget(e, "href")) && s[0]) || ((s = agget(e, "URL")) && s[0]))
2481 dflt_url = strdup_and_subst_obj(s, e);
2482 if (((s = agget(e, "edgehref")) && s[0]) ||
2483 ((s = agget(e, "edgeURL")) && s[0]))
2484 obj->url = strdup_and_subst_obj(s, e);
2485 else if (dflt_url)
2486 obj->url = gv_strdup(dflt_url);
2487 if (((s = agget(e, "labelhref")) && s[0]) ||
2488 ((s = agget(e, "labelURL")) && s[0]))
2489 obj->labelurl = strdup_and_subst_obj(s, e);
2490 else if (dflt_url)
2491 obj->labelurl = gv_strdup(dflt_url);
2492 if (((s = agget(e, "tailhref")) && s[0]) ||
2493 ((s = agget(e, "tailURL")) && s[0])) {
2494 obj->tailurl = strdup_and_subst_obj(s, e);
2495 obj->explicit_tailurl = true;
2496 } else if (dflt_url)
2497 obj->tailurl = gv_strdup(dflt_url);
2498 if (((s = agget(e, "headhref")) && s[0]) ||
2499 ((s = agget(e, "headURL")) && s[0])) {
2500 obj->headurl = strdup_and_subst_obj(s, e);
2501 obj->explicit_headurl = true;
2502 } else if (dflt_url)
2503 obj->headurl = gv_strdup(dflt_url);
2504 }
2505
2507 if ((s = agget(e, "target")) && s[0])
2508 dflt_target = strdup_and_subst_obj(s, e);
2509 if ((s = agget(e, "edgetarget")) && s[0]) {
2510 obj->explicit_edgetarget = true;
2511 obj->target = strdup_and_subst_obj(s, e);
2512 } else if (dflt_target)
2513 obj->target = gv_strdup(dflt_target);
2514 if ((s = agget(e, "labeltarget")) && s[0])
2516 else if (dflt_target)
2517 obj->labeltarget = gv_strdup(dflt_target);
2518 if ((s = agget(e, "tailtarget")) && s[0]) {
2520 obj->explicit_tailtarget = true;
2521 } else if (dflt_target)
2522 obj->tailtarget = gv_strdup(dflt_target);
2523 if ((s = agget(e, "headtarget")) && s[0]) {
2524 obj->explicit_headtarget = true;
2526 } else if (dflt_target)
2527 obj->headtarget = gv_strdup(dflt_target);
2528 }
2529
2531 if (((s = agget(e, "tooltip")) && s[0]) ||
2532 ((s = agget(e, "edgetooltip")) && s[0])) {
2533 char *tooltip = preprocessTooltip(s, e);
2534 obj->tooltip = strdup_and_subst_obj(tooltip, e);
2535 free(tooltip);
2536 obj->explicit_tooltip = true;
2537 } else if (obj->label)
2538 obj->tooltip = gv_strdup(obj->label);
2539
2540 if ((s = agget(e, "labeltooltip")) && s[0]) {
2541 char *tooltip = preprocessTooltip(s, e);
2542 obj->labeltooltip = strdup_and_subst_obj(tooltip, e);
2543 free(tooltip);
2544 obj->explicit_labeltooltip = true;
2545 } else if (obj->label)
2546 obj->labeltooltip = gv_strdup(obj->label);
2547
2548 if ((s = agget(e, "tailtooltip")) && s[0]) {
2549 char *tooltip = preprocessTooltip(s, e);
2550 obj->tailtooltip = strdup_and_subst_obj(tooltip, e);
2551 free(tooltip);
2552 obj->explicit_tailtooltip = true;
2553 } else if (obj->taillabel)
2554 obj->tailtooltip = gv_strdup(obj->taillabel);
2555
2556 if ((s = agget(e, "headtooltip")) && s[0]) {
2557 char *tooltip = preprocessTooltip(s, e);
2558 obj->headtooltip = strdup_and_subst_obj(tooltip, e);
2559 free(tooltip);
2560 obj->explicit_headtooltip = true;
2561 } else if (obj->headlabel)
2562 obj->headtooltip = gv_strdup(obj->headlabel);
2563 }
2564
2565 free(dflt_url);
2566 free(dflt_target);
2567
2569 if (ED_spl(e) && (obj->url || obj->tooltip) &&
2571 splines *spl;
2572 double w2 = fmax(job->obj->penwidth / 2.0, 2.0);
2573
2574 spl = ED_spl(e);
2575 const size_t ns = spl->size; /* number of splines */
2576 for (size_t i = 0; i < ns; i++)
2577 map_output_bspline(&pbs, &pbs_n, &pbs_poly_n, spl->list + i, w2);
2578 obj->url_bsplinemap_poly_n = pbs_poly_n;
2579 obj->url_bsplinemap_n = pbs_n;
2580 if (!(flags & GVRENDER_DOES_TRANSFORM)) {
2581 size_t nump = 0;
2582 for (size_t i = 0; i < pbs_poly_n; i++) {
2583 nump += pbs_n[i];
2584 }
2585 gvrender_ptf_A(job, pbs, pbs, nump);
2586 }
2587 obj->url_bsplinemap_p = pbs;
2589 obj->url_map_p = pbs;
2590 obj->url_map_n = pbs_n[0];
2591 }
2592 }
2593
2595 if (obj->url || obj->explicit_tooltip)
2596 gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
2597}
2598
2599static void
2601 char* url, char* tooltip, char* target, char *id, splines* spl)
2602{
2603 int flags = job->flags;
2604 emit_state_t old_emit_state;
2605 char* newid;
2606 agxbuf xb = {0};
2607 char* type;
2608
2609 if (lbl == NULL || !lbl->set) return;
2610 if (id) { /* non-NULL if needed */
2611 switch (lkind) {
2612 case EMIT_ELABEL :
2613 type = "label";
2614 break;
2615 case EMIT_HLABEL :
2616 type = "headlabel";
2617 break;
2618 case EMIT_TLABEL :
2619 type = "taillabel";
2620 break;
2621 default :
2622 UNREACHABLE();
2623 }
2624 agxbprint(&xb, "%s-%s", id, type);
2625 newid = agxbuse(&xb);
2626 }
2627 else
2628 newid = NULL;
2629 old_emit_state = job->obj->emit_state;
2630 job->obj->emit_state = lkind;
2631 if ((url || explicit) && !(flags & EMIT_CLUSTERS_LAST)) {
2632 map_label(job, lbl);
2633 gvrender_begin_anchor(job, url, tooltip, target, newid);
2634 }
2635 emit_label(job, lkind, lbl);
2636 if (spl) emit_attachment(job, lbl, spl);
2637 if (url || explicit) {
2638 if (flags & EMIT_CLUSTERS_LAST) {
2639 map_label(job, lbl);
2640 gvrender_begin_anchor(job, url, tooltip, target, newid);
2641 }
2643 }
2644 agxbfree(&xb);
2645 job->obj->emit_state = old_emit_state;
2646}
2647
2648/* Common logic for setting hot spots at the beginning and end of
2649 * an edge.
2650 * If we are given a value (url, tooltip, target) explicitly set for
2651 * the head/tail, we use that.
2652 * Otherwise, if we are given a value explicitly set for the edge,
2653 * we use that.
2654 * Otherwise, we use whatever the argument value is.
2655 * We also note whether or not the tooltip was explicitly set.
2656 * If the url is non-NULL or the tooltip was explicit, we set
2657 * a hot spot around point p.
2658 */
2659static void nodeIntersect(GVJ_t *job, pointf p, bool explicit_iurl, char *iurl,
2660 bool explicit_itooltip) {
2661 obj_state_t *obj = job->obj;
2662 char* url;
2663 bool explicit;
2664
2665 if (explicit_iurl) url = iurl;
2666 else url = obj->url;
2667 if (explicit_itooltip) {
2668 explicit = true;
2669 }
2670 else if (obj->explicit_tooltip) {
2671 explicit = true;
2672 }
2673 else {
2674 explicit = false;
2675 }
2676
2677 if (url || explicit) {
2678 map_point(job, p);
2679 }
2680}
2681
2682static void emit_end_edge(GVJ_t * job)
2683{
2684 obj_state_t *obj = job->obj;
2685 edge_t *e = obj->u.e;
2686
2687 if (obj->url || obj->explicit_tooltip) {
2689 if (obj->url_bsplinemap_poly_n) {
2690 for (size_t nump = obj->url_bsplinemap_n[0], i = 1;
2691 i < obj->url_bsplinemap_poly_n; i++) {
2692 /* additional polygon maps around remaining bezier pieces */
2693 obj->url_map_n = obj->url_bsplinemap_n[i];
2694 obj->url_map_p = &(obj->url_bsplinemap_p[nump]);
2696 obj->url, obj->tooltip, obj->target, obj->id);
2698 nump += obj->url_bsplinemap_n[i];
2699 }
2700 }
2701 }
2702 obj->url_map_n = 0; /* null out copy so that it doesn't get freed twice */
2703 obj->url_map_p = NULL;
2704
2705 if (ED_spl(e)) {
2706 pointf p;
2707 bezier bz;
2708
2709 /* process intersection with tail node */
2710 bz = ED_spl(e)->list[0];
2711 if (bz.sflag) /* Arrow at start of splines */
2712 p = bz.sp;
2713 else /* No arrow at start of splines */
2714 p = bz.list[0];
2715 nodeIntersect(job, p, obj->explicit_tailurl != 0, obj->tailurl,
2716 obj->explicit_tailtooltip != 0);
2717
2718 /* process intersection with head node */
2719 bz = ED_spl(e)->list[ED_spl(e)->size - 1];
2720 if (bz.eflag) /* Arrow at end of splines */
2721 p = bz.ep;
2722 else /* No arrow at end of splines */
2723 p = bz.list[bz.size - 1];
2724 nodeIntersect(job, p, obj->explicit_headurl != 0, obj->headurl,
2725 obj->explicit_headtooltip != 0);
2726 }
2727
2730 obj->labelurl, obj->labeltooltip, obj->labeltarget, obj->id,
2731 ((mapbool(late_string(e, E_decorate, "false")) && ED_spl(e)) ? ED_spl(e) : 0));
2734 obj->labelurl, obj->labeltooltip, obj->labeltarget, obj->id,
2735 ((mapbool(late_string(e, E_decorate, "false")) && ED_spl(e)) ? ED_spl(e) : 0));
2738 obj->headurl, obj->headtooltip, obj->headtarget, obj->id,
2739 0);
2742 obj->tailurl, obj->tailtooltip, obj->tailtarget, obj->id,
2743 0);
2744
2745 gvrender_end_edge(job);
2746 pop_obj_state(job);
2747}
2748
2749static void emit_edge(GVJ_t * job, edge_t * e)
2750{
2751 char *s;
2752 char *style;
2753 char **styles = NULL;
2754 char **sp;
2755 char *p;
2756
2757 if (edge_in_box(e, job->clip) && edge_in_layer(job, e) ) {
2758
2759 agxbuf edge = {0};
2760 agxbput(&edge, agnameof(agtail(e)));
2761 if (agisdirected(agraphof(aghead(e))))
2762 agxbput(&edge, "->");
2763 else
2764 agxbput(&edge, "--");
2765 agxbput(&edge, agnameof(aghead(e)));
2767 agxbfree(&edge);
2768
2769 s = late_string(e, E_comment, "");
2770 if (s[0])
2771 gvrender_comment(job, s);
2772
2773 style = late_string(e, E_style, "");
2774 /* We shortcircuit drawing an invisible edge because the arrowhead
2775 * code resets the style to solid, and most of the code generators
2776 * (except PostScript) won't honor a previous style of invis.
2777 */
2778 if (style[0]) {
2779 styles = parse_style(style);
2780 sp = styles;
2781 while ((p = *sp++)) {
2782 if (streq(p, "invis")) return;
2783 }
2784 }
2785
2786 emit_begin_edge(job, e, styles);
2787 emit_edge_graphics (job, e, styles);
2788 emit_end_edge(job);
2789 }
2790}
2791
2792static char adjust[] = {'l', 'n', 'r'};
2793
2794static void
2796{
2797 bb->UR.x = fmax(bb->UR.x, p.x);
2798 bb->LL.x = fmin(bb->LL.x, p.x);
2799 bb->UR.y = fmax(bb->UR.y, p.y);
2800 bb->LL.y = fmin(bb->LL.y, p.y);
2801}
2802
2803static boxf ptsBB(xdot_point *inpts, size_t numpts, boxf *bb) {
2804 boxf opbb;
2805
2806 opbb.LL.x = opbb.UR.x = inpts->x;
2807 opbb.LL.y = opbb.UR.y = inpts->y;
2808 for (size_t i = 1; i < numpts; i++) {
2809 inpts++;
2810 if (inpts->x < opbb.LL.x)
2811 opbb.LL.x = inpts->x;
2812 else if (inpts->x > opbb.UR.x)
2813 opbb.UR.x = inpts->x;
2814 if (inpts->y < opbb.LL.y)
2815 opbb.LL.y = inpts->y;
2816 else if (inpts->y > opbb.UR.y)
2817 opbb.UR.y = inpts->y;
2818
2819 }
2820 expandBB (bb, opbb.LL);
2821 expandBB (bb, opbb.UR);
2822 return opbb;
2823}
2824
2825static boxf
2826textBB (double x, double y, textspan_t* span)
2827{
2828 boxf bb;
2829 pointf sz = span->size;
2830
2831 switch (span->just) {
2832 case 'l':
2833 bb.LL.x = x;
2834 bb.UR.x = bb.LL.x + sz.x;
2835 break;
2836 case 'n':
2837 bb.LL.x = x - sz.x / 2.0;
2838 bb.UR.x = x + sz.x / 2.0;
2839 break;
2840 case 'r':
2841 bb.UR.x = x;
2842 bb.LL.x = bb.UR.x - sz.x;
2843 break;
2844 }
2845 bb.UR.y = y + span->yoffset_layout;
2846 bb.LL.y = bb.UR.y - sz.y;
2847 return bb;
2848}
2849
2850static void
2852{
2853 if (op->op.kind == xd_text)
2854 free_textspan (op->span, 1);
2855}
2856
2858{
2859 GVC_t *gvc = GD_gvc(g);
2860 exdot_op* op;
2861 double fontsize = 0.0;
2862 char* fontname = NULL;
2863 pointf pts[2];
2864 boxf bb0;
2865 boxf bb = GD_bb(g);
2866 xdot* xd = GD_drawing(g)->xdots;
2867 textfont_t tf, null_tf = {0};
2868 int fontflags = 0;
2869
2870 if (!xd) return bb;
2871
2872 if (bb.LL.x == bb.UR.x && bb.LL.y == bb.UR.y) {
2873 bb.LL.x = bb.LL.y = DBL_MAX;
2874 bb.UR.x = bb.UR.y = -DBL_MAX;
2875 }
2876
2877 op = (exdot_op*)xd->ops;
2878 for (size_t i = 0; i < xd->cnt; i++) {
2879 tf = null_tf;
2880 switch (op->op.kind) {
2881 case xd_filled_ellipse :
2882 case xd_unfilled_ellipse :
2883 pts[0].x = op->op.u.ellipse.x - op->op.u.ellipse.w;
2884 pts[0].y = op->op.u.ellipse.y - op->op.u.ellipse.h;
2885 pts[1].x = op->op.u.ellipse.x + op->op.u.ellipse.w;
2886 pts[1].y = op->op.u.ellipse.y + op->op.u.ellipse.h;
2887 op->bb.LL = pts[0];
2888 op->bb.UR = pts[1];
2889 expandBB (&bb, pts[0]);
2890 expandBB (&bb, pts[1]);
2891 break;
2892 case xd_filled_polygon :
2893 case xd_unfilled_polygon :
2894 op->bb = ptsBB (op->op.u.polygon.pts, op->op.u.polygon.cnt, &bb);
2895 break;
2896 case xd_filled_bezier :
2897 case xd_unfilled_bezier :
2898 op->bb = ptsBB (op->op.u.polygon.pts, op->op.u.polygon.cnt, &bb);
2899 break;
2900 case xd_polyline :
2901 op->bb = ptsBB (op->op.u.polygon.pts, op->op.u.polygon.cnt, &bb);
2902 break;
2903 case xd_text :
2904 op->span = gv_alloc(sizeof(textspan_t));
2905 op->span->str = gv_strdup (op->op.u.text.text);
2906 op->span->just = adjust [op->op.u.text.align];
2907 tf.name = fontname;
2908 tf.size = fontsize;
2909 tf.flags = fontflags;
2910 op->span->font = dtinsert(gvc->textfont_dt, &tf);
2911 textspan_size (gvc, op->span);
2912 bb0 = textBB (op->op.u.text.x, op->op.u.text.y, op->span);
2913 op->bb = bb0;
2914 expandBB (&bb, bb0.LL);
2915 expandBB (&bb, bb0.UR);
2916 if (!xd->freefunc)
2917 xd->freefunc = (freefunc_t)freePara;
2918 break;
2919 case xd_font :
2920 fontsize = op->op.u.font.size;
2921 fontname = op->op.u.font.name;
2922 break;
2923 case xd_fontchar :
2924 fontflags = op->op.u.fontchar;
2925 break;
2926 default :
2927 break;
2928 }
2929 op++;
2930 }
2931 return bb;
2932}
2933
2934static void init_gvc(GVC_t * gvc, graph_t * g)
2935{
2936 double xf, yf;
2937 char *p;
2938 int i;
2939
2940 gvc->g = g;
2941
2942 /* margins */
2943 gvc->graph_sets_margin = false;
2944 if ((p = agget(g, "margin"))) {
2945 i = sscanf(p, "%lf,%lf", &xf, &yf);
2946 if (i > 0) {
2947 gvc->margin.x = gvc->margin.y = xf * POINTS_PER_INCH;
2948 if (i > 1)
2949 gvc->margin.y = yf * POINTS_PER_INCH;
2950 gvc->graph_sets_margin = true;
2951 }
2952 }
2953
2954 /* pad */
2955 gvc->graph_sets_pad = false;
2956 if ((p = agget(g, "pad"))) {
2957 i = sscanf(p, "%lf,%lf", &xf, &yf);
2958 if (i > 0) {
2959 gvc->pad.x = gvc->pad.y = xf * POINTS_PER_INCH;
2960 if (i > 1)
2961 gvc->pad.y = yf * POINTS_PER_INCH;
2962 gvc->graph_sets_pad = true;
2963 }
2964 }
2965
2966 /* pagesize */
2967 gvc->graph_sets_pageSize = false;
2968 gvc->pageSize = GD_drawing(g)->page;
2969 if (GD_drawing(g)->page.x > 0.001 && GD_drawing(g)->page.y > 0.001)
2970 gvc->graph_sets_pageSize = true;
2971
2972 /* rotation */
2973 if (GD_drawing(g)->landscape)
2974 gvc->rotation = 90;
2975 else
2976 gvc->rotation = 0;
2977
2978 /* pagedir */
2979 gvc->pagedir = "BL";
2980 if ((p = agget(g, "pagedir")) && p[0])
2981 gvc->pagedir = p;
2982
2983
2984 /* bounding box */
2985 gvc->bb = GD_bb(g);
2986
2987 /* clusters have peripheries */
2988 G_peripheries = agfindgraphattr(g, "peripheries");
2989 G_penwidth = agfindgraphattr(g, "penwidth");
2990
2991 /* default font */
2996
2997 /* default line style */
2999
3000 gvc->graphname = agnameof(g);
3001}
3002
3003static void init_job_pad(GVJ_t *job)
3004{
3005 GVC_t *gvc = job->gvc;
3006
3007 if (gvc->graph_sets_pad) {
3008 job->pad = gvc->pad;
3009 }
3010 else {
3011 switch (job->output_lang) {
3012 case GVRENDER_PLUGIN:
3013 job->pad.x = job->pad.y = job->render.features->default_pad;
3014 break;
3015 default:
3016 job->pad.x = job->pad.y = DEFAULT_GRAPH_PAD;
3017 break;
3018 }
3019 }
3020}
3021
3022static void init_job_margin(GVJ_t *job)
3023{
3024 GVC_t *gvc = job->gvc;
3025
3026 if (gvc->graph_sets_margin) {
3027 job->margin = gvc->margin;
3028 }
3029 else {
3030 /* set default margins depending on format */
3031 switch (job->output_lang) {
3032 case GVRENDER_PLUGIN:
3033 job->margin = job->device.features->default_margin;
3034 break;
3035 case PCL: case MIF: case METAPOST: case VTX: case QPDF:
3036 job->margin.x = job->margin.y = DEFAULT_PRINT_MARGIN;
3037 break;
3038 default:
3039 job->margin.x = job->margin.y = DEFAULT_EMBED_MARGIN;
3040 break;
3041 }
3042 }
3043
3044}
3045
3046static void init_job_dpi(GVJ_t *job, graph_t *g)
3047{
3048 GVJ_t *firstjob = job->gvc->active_jobs;
3049
3050 if (GD_drawing(g)->dpi != 0) {
3051 job->dpi.x = job->dpi.y = (double)GD_drawing(g)->dpi;
3052 }
3053 else if (firstjob && firstjob->device_sets_dpi) {
3054 job->dpi = firstjob->device_dpi; /* some devices set dpi in initialize() */
3055 }
3056 else {
3057 /* set default margins depending on format */
3058 switch (job->output_lang) {
3059 case GVRENDER_PLUGIN:
3060 job->dpi = job->device.features->default_dpi;
3061 break;
3062 default:
3063 job->dpi.x = job->dpi.y = (double)DEFAULT_DPI;
3064 break;
3065 }
3066 }
3067}
3068
3069static void init_job_viewport(GVJ_t * job, graph_t * g)
3070{
3071 GVC_t *gvc = job->gvc;
3072 pointf LL, UR, size, sz;
3073 double X, Y, Z, x, y;
3074 int rv;
3075 Agnode_t *n;
3076 char *str, *nodename = NULL;
3077
3078 UR = gvc->bb.UR;
3079 LL = gvc->bb.LL;
3080 job->bb.LL.x = LL.x - job->pad.x; /* job->bb is bb of graph and padding - graph units */
3081 job->bb.LL.y = LL.y - job->pad.y;
3082 job->bb.UR.x = UR.x + job->pad.x;
3083 job->bb.UR.y = UR.y + job->pad.y;
3084 sz.x = job->bb.UR.x - job->bb.LL.x; /* size, including padding - graph units */
3085 sz.y = job->bb.UR.y - job->bb.LL.y;
3086
3087 /* determine final drawing size and scale to apply. */
3088 /* N.B. size given by user is not rotated by landscape mode */
3089 /* start with "natural" size of layout */
3090
3091 Z = 1.0;
3092 if (GD_drawing(g)->size.x > 0.001 && GD_drawing(g)->size.y > 0.001) { /* graph size was given by user... */
3093 size = GD_drawing(g)->size;
3094 if (sz.x <= 0.001) sz.x = size.x;
3095 if (sz.y <= 0.001) sz.y = size.y;
3096 if (size.x < sz.x || size.y < sz.y /* drawing is too big (in either axis) ... */
3097 || (GD_drawing(g)->filled /* or ratio=filled requested and ... */
3098 && size.x > sz.x && size.y > sz.y)) /* drawing is too small (in both axes) ... */
3099 Z = fmin(size.x / sz.x, size.y / sz.y);
3100 }
3101
3102 /* default focus, in graph units = center of bb */
3103 x = (LL.x + UR.x) / 2.;
3104 y = (LL.y + UR.y) / 2.;
3105
3106 /* rotate and scale bb to give default absolute size in points*/
3107 job->rotation = job->gvc->rotation;
3108 X = sz.x * Z;
3109 Y = sz.y * Z;
3110
3111 /* user can override */
3112 if ((str = agget(g, "viewport"))) {
3113 nodename = gv_alloc(strlen(str) + 1);
3114 rv = sscanf(str, "%lf,%lf,%lf,\'%[^\']\'", &X, &Y, &Z, nodename);
3115 if (rv == 4) {
3116 n = agfindnode(g->root, nodename);
3117 if (n) {
3118 x = ND_coord(n).x;
3119 y = ND_coord(n).y;
3120 }
3121 }
3122 else {
3123 char junk;
3124 rv = sscanf(str, "%lf,%lf,%lf,%[^,]%c", &X, &Y, &Z, nodename, &junk);
3125 if (rv == 4) {
3126 n = agfindnode(g->root, nodename);
3127 if (n) {
3128 x = ND_coord(n).x;
3129 y = ND_coord(n).y;
3130 }
3131 }
3132 else {
3133 rv = sscanf(str, "%lf,%lf,%lf,%lf,%lf", &X, &Y, &Z, &x, &y);
3134 }
3135 }
3136 free (nodename);
3137 }
3138 /* rv is ignored since args retain previous values if not scanned */
3139
3140 /* job->view gives port size in graph units, unscaled or rotated
3141 * job->zoom gives scaling factor.
3142 * job->focus gives the position in the graph of the center of the port
3143 */
3144 job->view.x = X;
3145 job->view.y = Y;
3146 job->zoom = Z; /* scaling factor */
3147 job->focus.x = x;
3148 job->focus.y = y;
3149}
3150
3151static void emit_cluster_colors(GVJ_t * job, graph_t * g)
3152{
3153 graph_t *sg;
3154 int c;
3155 char *str;
3156
3157 for (c = 1; c <= GD_n_cluster(g); c++) {
3158 sg = GD_clust(g)[c];
3159 emit_cluster_colors(job, sg);
3160 if (((str = agget(sg, "color")) != 0) && str[0])
3162 if (((str = agget(sg, "pencolor")) != 0) && str[0])
3164 if (((str = agget(sg, "bgcolor")) != 0) && str[0])
3166 if (((str = agget(sg, "fillcolor")) != 0) && str[0])
3168 if (((str = agget(sg, "fontcolor")) != 0) && str[0])
3170 }
3171}
3172
3173static void emit_colors(GVJ_t * job, graph_t * g)
3174{
3175 node_t *n;
3176 edge_t *e;
3177 char *str, *colors;
3178
3180 if (((str = agget(g, "bgcolor")) != 0) && str[0])
3182 if (((str = agget(g, "fontcolor")) != 0) && str[0])
3184
3185 emit_cluster_colors(job, g);
3186 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
3187 if (((str = agget(n, "color")) != 0) && str[0])
3189 if (((str = agget(n, "pencolor")) != 0) && str[0])
3191 if (((str = agget(n, "fillcolor")) != 0) && str[0]) {
3192 if (strchr(str, ':')) {
3193 colors = gv_strdup(str);
3194 for (str = strtok(colors, ":"); str;
3195 str = strtok(0, ":")) {
3196 if (str[0])
3198 }
3199 free(colors);
3200 }
3201 else {
3203 }
3204 }
3205 if (((str = agget(n, "fontcolor")) != 0) && str[0])
3207 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
3208 if (((str = agget(e, "color")) != 0) && str[0]) {
3209 if (strchr(str, ':')) {
3210 colors = gv_strdup(str);
3211 for (str = strtok(colors, ":"); str;
3212 str = strtok(0, ":")) {
3213 if (str[0])
3215 }
3216 free(colors);
3217 }
3218 else {
3220 }
3221 }
3222 if (((str = agget(e, "fontcolor")) != 0) && str[0])
3224 }
3225 }
3226}
3227
3228static void emit_view(GVJ_t * job, graph_t * g, int flags)
3229{
3230 GVC_t * gvc = job->gvc;
3231 node_t *n;
3232 edge_t *e;
3233
3234 gvc->common.viewNum++;
3235 /* when drawing, lay clusters down before nodes and edges */
3236 if (!(flags & EMIT_CLUSTERS_LAST))
3237 emit_clusters(job, g, flags);
3238 if (flags & EMIT_SORTED) {
3239 /* output all nodes, then all edges */
3241 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3242 emit_node(job, n);
3243 gvrender_end_nodes(job);
3245 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
3246 for (e = agfstout(g, n); e; e = agnxtout(g, e))
3247 emit_edge(job, e);
3248 }
3249 gvrender_end_edges(job);
3250 } else if (flags & EMIT_EDGE_SORTED) {
3251 /* output all edges, then all nodes */
3253 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3254 for (e = agfstout(g, n); e; e = agnxtout(g, e))
3255 emit_edge(job, e);
3256 gvrender_end_edges(job);
3258 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3259 emit_node(job, n);
3260 gvrender_end_nodes(job);
3261 } else if (flags & EMIT_PREORDER) {
3263 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3264 if (write_node_test(g, n))
3265 emit_node(job, n);
3266 gvrender_end_nodes(job);
3268
3269 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
3270 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
3271 if (write_edge_test(g, e))
3272 emit_edge(job, e);
3273 }
3274 }
3275 gvrender_end_edges(job);
3276 } else {
3277 /* output in breadth first graph walk order */
3278 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
3279 emit_node(job, n);
3280 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
3281 emit_node(job, aghead(e));
3282 emit_edge(job, e);
3283 }
3284 }
3285 }
3286 /* when mapping, detect events on clusters after nodes and edges */
3288 emit_clusters(job, g, flags);
3289}
3290
3291static void emit_begin_graph(GVJ_t * job, graph_t * g)
3292{
3293 obj_state_t *obj;
3294
3295 obj = push_obj_state(job);
3296 obj->type = ROOTGRAPH_OBJTYPE;
3297 obj->u.g = g;
3298 obj->emit_state = EMIT_GDRAW;
3299
3300 initObjMapData (job, GD_label(g), g);
3301
3303}
3304
3305static void emit_end_graph(GVJ_t * job)
3306{
3307 gvrender_end_graph(job);
3308 pop_obj_state(job);
3309}
3310
3311#define NotFirstPage(j) (((j)->layerNum>1)||((j)->pagesArrayElem.x > 0)||((j)->pagesArrayElem.x > 0))
3312
3313static void emit_page(GVJ_t * job, graph_t * g)
3314{
3315 obj_state_t *obj = job->obj;
3316 int flags = job->flags;
3317 size_t nump = 0;
3318 textlabel_t *lab;
3319 pointf *p = NULL;
3320 char* saveid;
3321 agxbuf xb = {0};
3322
3323 /* For the first page, we can use the values generated in emit_begin_graph.
3324 * For multiple pages, we need to generate a new id.
3325 */
3326 if (NotFirstPage(job)) {
3327 saveid = obj->id;
3328 layerPagePrefix (job, &xb);
3329 agxbput (&xb, saveid);
3330 obj->id = agxbuse(&xb);
3331 }
3332 else
3333 saveid = NULL;
3334
3335 char *previous_color_scheme = setColorScheme(agget(g, "colorscheme"));
3336 setup_page(job);
3341 && (obj->url || obj->explicit_tooltip)) {
3345 nump = 2;
3346 }
3347 else {
3349 nump = 4;
3350 }
3351 p = gv_calloc(nump, sizeof(pointf));
3352 p[0] = job->pageBox.LL;
3353 p[1] = job->pageBox.UR;
3355 rect2poly(p);
3356 }
3358 gvrender_ptf_A(job, p, p, nump);
3359 obj->url_map_p = p;
3360 obj->url_map_n = nump;
3361 }
3362 if ((flags & GVRENDER_DOES_LABELS) && ((lab = GD_label(g))))
3363 /* do graph label on every page and rely on clipping to show it on the right one(s) */
3364 obj->label = lab->text;
3365 /* If EMIT_CLUSTERS_LAST is set, we assume any URL or tooltip
3366 * attached to the root graph is emitted either in begin_page
3367 * or end_page of renderer.
3368 */
3369 if (!(flags & EMIT_CLUSTERS_LAST) && (obj->url || obj->explicit_tooltip)) {
3370 emit_map_rect(job, job->clip);
3371 gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
3372 }
3373 emit_background(job, g);
3374 if (GD_label(g))
3376 if (!(flags & EMIT_CLUSTERS_LAST) && (obj->url || obj->explicit_tooltip))
3378 emit_view(job,g,flags);
3379 gvrender_end_page(job);
3380 if (saveid) {
3381 obj->id = saveid;
3382 }
3383 agxbfree(&xb);
3384
3385 char *color_scheme = setColorScheme(previous_color_scheme);
3386 free(color_scheme);
3387 free(previous_color_scheme);
3388}
3389
3390void emit_graph(GVJ_t * job, graph_t * g)
3391{
3392 node_t *n;
3393 char *s;
3394 int flags = job->flags;
3395 int* lp;
3396
3397 /* device dpi is now known */
3398 job->scale.x = job->zoom * job->dpi.x / POINTS_PER_INCH;
3399 job->scale.y = job->zoom * job->dpi.y / POINTS_PER_INCH;
3400
3401 job->devscale.x = job->dpi.x / POINTS_PER_INCH;
3402 job->devscale.y = job->dpi.y / POINTS_PER_INCH;
3403 if ((job->flags & GVRENDER_Y_GOES_DOWN) || (Y_invert))
3404 job->devscale.y *= -1;
3405
3406 /* compute current view in graph units */
3407 if (job->rotation) {
3408 job->view.y = job->width / job->scale.y;
3409 job->view.x = job->height / job->scale.x;
3410 }
3411 else {
3412 job->view.x = job->width / job->scale.x;
3413 job->view.y = job->height / job->scale.y;
3414 }
3415
3416 s = late_string(g, agattr(g, AGRAPH, "comment", 0), "");
3417 gvrender_comment(job, s);
3418
3419 job->layerNum = 0;
3420 emit_begin_graph(job, g);
3421
3422 if (flags & EMIT_COLORS)
3423 emit_colors(job,g);
3424
3425 /* reset node state */
3426 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3427 ND_state(n) = 0;
3428 /* iterate layers */
3429 for (firstlayer(job,&lp); validlayer(job); nextlayer(job,&lp)) {
3430 if (numPhysicalLayers (job) > 1)
3432
3433 /* iterate pages */
3434 for (firstpage(job); validpage(job); nextpage(job))
3435 emit_page(job, g);
3436
3437 if (numPhysicalLayers (job) > 1)
3438 gvrender_end_layer(job);
3439 }
3440 emit_end_graph(job);
3441}
3442
3445 .link = -1, // link - allocate separate holder objects
3446 .freef = free,
3447};
3448
3449bool emit_once(char *str) {
3450 if (strings == 0)
3452 if (!dtsearch(strings, str)) {
3454 return true;
3455 }
3456 return false;
3457}
3458
3460{
3461 if (strings) {
3463 strings = 0;
3464 }
3465}
3466
3467static void emit_begin_cluster(GVJ_t * job, Agraph_t * sg)
3468{
3469 obj_state_t *obj;
3470
3471 obj = push_obj_state(job);
3472 obj->type = CLUSTER_OBJTYPE;
3473 obj->u.sg = sg;
3474 obj->emit_state = EMIT_CDRAW;
3475
3476 initObjMapData (job, GD_label(sg), sg);
3477
3479}
3480
3481static void emit_end_cluster(GVJ_t *job) {
3483 pop_obj_state(job);
3484}
3485
3486void emit_clusters(GVJ_t * job, Agraph_t * g, int flags)
3487{
3488 int doPerim, c, filled;
3489 pointf AF[4];
3490 char *color, *fillcolor, *pencolor, **style, *s;
3491 graph_t *sg;
3492 node_t *n;
3493 edge_t *e;
3494 obj_state_t *obj;
3495 textlabel_t *lab;
3496 int doAnchor;
3497 double penwidth;
3498 char* clrs[2];
3499
3500 for (c = 1; c <= GD_n_cluster(g); c++) {
3501 sg = GD_clust(g)[c];
3502 if (!clust_in_layer(job, sg))
3503 continue;
3504 /* when mapping, detect events on clusters after sub_clusters */
3506 emit_clusters(job, sg, flags);
3507 emit_begin_cluster(job, sg);
3508 obj = job->obj;
3509 doAnchor = obj->url || obj->explicit_tooltip;
3510 char *previous_color_scheme = setColorScheme(agget(sg, "colorscheme"));
3511 if (doAnchor && !(flags & EMIT_CLUSTERS_LAST)) {
3512 emit_map_rect(job, GD_bb(sg));
3513 gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
3514 }
3515 filled = 0;
3516 graphviz_polygon_style_t istyle = {0};
3517 if ((style = checkClusterStyle(sg, &istyle))) {
3518 gvrender_set_style(job, style);
3519 if (istyle.filled)
3520 filled = FILL;
3521 }
3522 fillcolor = pencolor = 0;
3523
3524 if (GD_gui_state(sg) & GUI_STATE_ACTIVE) {
3527 filled = FILL;
3528 }
3529 else if (GD_gui_state(sg) & GUI_STATE_SELECTED) {
3532 filled = FILL;
3533 }
3534 else if (GD_gui_state(sg) & GUI_STATE_DELETED) {
3537 filled = FILL;
3538 }
3539 else if (GD_gui_state(sg) & GUI_STATE_VISITED) {
3542 filled = FILL;
3543 }
3544 else {
3545 if ((color = agget(sg, "color")) != 0 && color[0])
3546 fillcolor = pencolor = color;
3547 if ((color = agget(sg, "pencolor")) != 0 && color[0])
3548 pencolor = color;
3549 if ((color = agget(sg, "fillcolor")) != 0 && color[0])
3550 fillcolor = color;
3551 /* bgcolor is supported for backward compatibility
3552 if fill is set, fillcolor trumps bgcolor, so
3553 don't bother checking.
3554 if gradient is set fillcolor trumps bgcolor
3555 */
3556 if ((filled == 0 || !fillcolor) && (color = agget(sg, "bgcolor")) != 0 && color[0]) {
3557 fillcolor = color;
3558 filled = FILL;
3559 }
3560
3561 }
3562 if (!pencolor) pencolor = DEFAULT_COLOR;
3563 if (!fillcolor) fillcolor = DEFAULT_FILL;
3564 clrs[0] = NULL;
3565 if (filled != 0) {
3566 double frac;
3567 if (findStopColor (fillcolor, clrs, &frac)) {
3568 gvrender_set_fillcolor(job, clrs[0]);
3569 if (clrs[1])
3570 gvrender_set_gradient_vals(job,clrs[1],late_int(sg,G_gradientangle,0,0), frac);
3571 else
3573 if (istyle.radial)
3574 filled = RGRADIENT;
3575 else
3576 filled = GRADIENT;
3577 }
3578 else
3579 gvrender_set_fillcolor(job, fillcolor);
3580 }
3581
3582 if (G_penwidth && ((s=ag_xget(sg,G_penwidth)) && s[0])) {
3583 penwidth = late_double(sg, G_penwidth, 1.0, 0.0);
3585 }
3586
3587 if (istyle.rounded) {
3588 if ((doPerim = late_int(sg, G_peripheries, 1, 0)) || filled != 0) {
3589 AF[0] = GD_bb(sg).LL;
3590 AF[2] = GD_bb(sg).UR;
3591 AF[1].x = AF[2].x;
3592 AF[1].y = AF[0].y;
3593 AF[3].x = AF[0].x;
3594 AF[3].y = AF[2].y;
3595 if (doPerim)
3596 gvrender_set_pencolor(job, pencolor);
3597 else
3598 gvrender_set_pencolor(job, "transparent");
3599 round_corners(job, AF, 4, istyle, filled);
3600 }
3601 }
3602 else if (istyle.striped) {
3603 int rv;
3604 AF[0] = GD_bb(sg).LL;
3605 AF[2] = GD_bb(sg).UR;
3606 AF[1].x = AF[2].x;
3607 AF[1].y = AF[0].y;
3608 AF[3].x = AF[0].x;
3609 AF[3].y = AF[2].y;
3610 if (late_int(sg, G_peripheries, 1, 0) == 0)
3611 gvrender_set_pencolor(job, "transparent");
3612 else
3613 gvrender_set_pencolor(job, pencolor);
3614 rv = stripedBox (job, AF, fillcolor, 0);
3615 if (rv > 1)
3616 agerr (AGPREV, "in cluster %s\n", agnameof(sg));
3617 gvrender_box(job, GD_bb(sg), 0);
3618 }
3619 else {
3620 if (late_int(sg, G_peripheries, 1, 0)) {
3621 gvrender_set_pencolor(job, pencolor);
3622 gvrender_box(job, GD_bb(sg), filled);
3623 }
3624 else if (filled != 0) {
3625 gvrender_set_pencolor(job, "transparent");
3626 gvrender_box(job, GD_bb(sg), filled);
3627 }
3628 }
3629
3630 free (clrs[0]);
3631 if ((lab = GD_label(sg)))
3632 emit_label(job, EMIT_CLABEL, lab);
3633
3634 if (doAnchor) {
3635 if (flags & EMIT_CLUSTERS_LAST) {
3636 emit_map_rect(job, GD_bb(sg));
3637 gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
3638 }
3640 }
3641
3642 if (flags & EMIT_PREORDER) {
3643 for (n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
3644 emit_node(job, n);
3645 for (e = agfstout(sg, n); e; e = agnxtout(sg, e))
3646 emit_edge(job, e);
3647 }
3648 }
3649 emit_end_cluster(job);
3650 /* when drawing, lay down clusters before sub_clusters */
3651 if (!(flags & EMIT_CLUSTERS_LAST))
3652 emit_clusters(job, sg, flags);
3653
3654 char *color_scheme = setColorScheme(previous_color_scheme);
3655 free(color_scheme);
3656 free(previous_color_scheme);
3657 }
3658}
3659
3660static bool is_style_delim(int c)
3661{
3662 switch (c) {
3663 case '(':
3664 case ')':
3665 case ',':
3666 case '\0':
3667 return true;
3668 default:
3669 return false;
3670 }
3671}
3672
3673#define SID 1
3674
3679typedef struct {
3680 int type;
3681 const char *start;
3682 size_t size;
3683} token_t;
3684
3685static token_t style_token(char **s) {
3686 char *p = *s;
3687 int token;
3688
3689 while (gv_isspace(*p) || *p == ',')
3690 p++;
3691 const char *start = p;
3692 switch (*p) {
3693 case '\0':
3694 token = 0;
3695 break;
3696 case '(':
3697 case ')':
3698 token = *p++;
3699 break;
3700 default:
3701 token = SID;
3702 while (!is_style_delim(*p)) {
3703 p++;
3704 }
3705 }
3706 *s = p;
3707 assert(start <= p);
3708 size_t size = (size_t)(p - start);
3709 return (token_t){.type = token, .start = start, .size = size};
3710}
3711
3712#define FUNLIMIT 64
3713
3714/* This is one of the worst internal designs in graphviz.
3715 * The use of '\0' characters within strings seems cute but it
3716 * makes all of the standard functions useless if not dangerous.
3717 * Plus the function uses static memory for both the array and
3718 * the character buffer. One hopes all of the values are used
3719 * before the function is called again.
3720 */
3721char **parse_style(char *s)
3722{
3723 static char *parse[FUNLIMIT];
3724 size_t parse_offsets[sizeof(parse) / sizeof(parse[0])];
3725 size_t fun = 0;
3726 bool in_parens = false;
3727 char *p;
3728 static agxbuf ps_xb;
3729
3730 p = s;
3731 while (true) {
3732 token_t c = style_token(&p);
3733 if (c.type == 0) {
3734 break;
3735 }
3736 switch (c.type) {
3737 case '(':
3738 if (in_parens) {
3739 agerrorf("nesting not allowed in style: %s\n", s);
3740 parse[0] = NULL;
3741 return parse;
3742 }
3743 in_parens = true;
3744 break;
3745
3746 case ')':
3747 if (!in_parens) {
3748 agerrorf("unmatched ')' in style: %s\n", s);
3749 parse[0] = NULL;
3750 return parse;
3751 }
3752 in_parens = false;
3753 break;
3754
3755 default:
3756 if (!in_parens) {
3757 if (fun == FUNLIMIT - 1) {
3758 agwarningf("truncating style '%s'\n", s);
3759 parse[fun] = NULL;
3760 return parse;
3761 }
3762 agxbputc(&ps_xb, '\0'); /* terminate previous */
3763 parse_offsets[fun++] = agxblen(&ps_xb);
3764 }
3765 agxbput_n(&ps_xb, c.start, c.size);
3766 agxbputc(&ps_xb, '\0');
3767 }
3768 }
3769
3770 if (in_parens) {
3771 agerrorf("unmatched '(' in style: %s\n", s);
3772 parse[0] = NULL;
3773 return parse;
3774 }
3775
3776 char *base = agxbuse(&ps_xb); // add final '\0' to buffer
3777
3778 // construct list of style strings
3779 for (size_t i = 0; i < fun; ++i) {
3780 parse[i] = base + parse_offsets[i];
3781 }
3782 parse[fun] = NULL;
3783
3784 return parse;
3785}
3786
3788{
3789 pointf p, p1, p2;
3790 boxf bb;
3791
3792 assert(bz.size > 0);
3793 assert(bz.size % 3 == 1);
3794 bb.LL = bb.UR = bz.list[0];
3795 for (size_t i = 1; i < bz.size;) {
3796 /* take mid-point between two control points for bb calculation */
3797 p1=bz.list[i];
3798 i++;
3799 p2=bz.list[i];
3800 i++;
3801 p.x = ( p1.x + p2.x ) / 2;
3802 p.y = ( p1.y + p2.y ) / 2;
3803 EXPANDBP(bb,p);
3804
3805 p=bz.list[i];
3806 EXPANDBP(bb,p);
3807 i++;
3808 }
3809 return bb;
3810}
3811
3812static void init_splines_bb(splines *spl)
3813{
3814 bezier bz;
3815 boxf bb, b;
3816
3817 assert(spl->size > 0);
3818 bz = spl->list[0];
3819 bb = bezier_bb(bz);
3820 for (size_t i = 0; i < spl->size; i++) {
3821 if (i > 0) {
3822 bz = spl->list[i];
3823 b = bezier_bb(bz);
3824 EXPANDBB(bb, b);
3825 }
3826 if (bz.sflag) {
3827 b = arrow_bb(bz.sp, bz.list[0], 1);
3828 EXPANDBB(bb, b);
3829 }
3830 if (bz.eflag) {
3831 b = arrow_bb(bz.ep, bz.list[bz.size - 1], 1);
3832 EXPANDBB(bb, b);
3833 }
3834 }
3835 spl->bb = bb;
3836}
3837
3838static void init_bb_edge(edge_t *e)
3839{
3840 splines *spl;
3841
3842 spl = ED_spl(e);
3843 if (spl)
3844 init_splines_bb(spl);
3845}
3846
3847static void init_bb_node(graph_t *g, node_t *n)
3848{
3849 edge_t *e;
3850
3851 ND_bb(n).LL.x = ND_coord(n).x - ND_lw(n);
3852 ND_bb(n).LL.y = ND_coord(n).y - ND_ht(n) / 2.;
3853 ND_bb(n).UR.x = ND_coord(n).x + ND_rw(n);
3854 ND_bb(n).UR.y = ND_coord(n).y + ND_ht(n) / 2.;
3855
3856 for (e = agfstout(g, n); e; e = agnxtout(g, e))
3857 init_bb_edge(e);
3858
3859 /* IDEA - could also save in the node the bb of the node and
3860 all of its outedges, then the scan time would be proportional
3861 to just the number of nodes for many graphs.
3862 Wouldn't work so well if the edges are sprawling all over the place
3863 because then the boxes would overlap a lot and require more tests,
3864 but perhaps that wouldn't add much to the cost before trying individual
3865 nodes and edges. */
3866}
3867
3868static void init_bb(graph_t *g)
3869{
3870 node_t *n;
3871
3872 for (n = agfstnode(g); n; n = agnxtnode(g, n))
3873 init_bb_node(g, n);
3874}
3875
3877extern const size_t gvevent_key_binding_size;
3879
3880/* Set LC_NUMERIC to "C" to get expected interpretation of %f
3881 * in printf functions. Languages like postscript and dot expect
3882 * floating point numbers to use a decimal point.
3883 *
3884 * If set is non-zero, the "C" locale set;
3885 * if set is zero, the original locale is reset.
3886 * Calls to the function can nest.
3887 */
3888void gv_fixLocale (int set)
3889{
3890 static char* save_locale;
3891 static int cnt;
3892
3893 if (set) {
3894 cnt++;
3895 if (cnt == 1) {
3896 save_locale = gv_strdup(setlocale (LC_NUMERIC, NULL));
3897 setlocale (LC_NUMERIC, "C");
3898 }
3899 }
3900 else if (cnt > 0) {
3901 cnt--;
3902 if (cnt == 0) {
3903 setlocale (LC_NUMERIC, save_locale);
3904 free (save_locale);
3905 }
3906 }
3907}
3908
3909
3910#define FINISH() if (Verbose) fprintf(stderr,"gvRenderJobs %s: %.2f secs.\n", agnameof(g), elapsed_sec())
3911
3913{
3914 static GVJ_t *prevjob;
3915 GVJ_t *job, *firstjob;
3916
3917 if (Verbose)
3918 start_timer();
3919
3920 if (!LAYOUT_DONE(g)) {
3921 agerrorf("Layout was not done. Missing layout plugins? \n");
3922 FINISH();
3923 return -1;
3924 }
3925
3926 init_bb(g);
3927 init_gvc(gvc, g);
3928 init_layering(gvc, g);
3929
3930 gv_fixLocale (1);
3931 for (job = gvjobs_first(gvc); job; job = gvjobs_next(gvc)) {
3932 if (gvc->gvg) {
3934 job->graph_index = gvc->gvg->graph_index;
3935 }
3936 else {
3937 job->input_filename = NULL;
3938 job->graph_index = 0;
3939 }
3940 job->common = &(gvc->common);
3941 job->layout_type = gvc->layout.type;
3944 if (!GD_drawing(g)) {
3945 agerrorf("layout was not done\n");
3946 gv_fixLocale (0);
3947 FINISH();
3948 return -1;
3949 }
3950
3952 if (job->output_lang == NO_SUPPORT) {
3953 agerrorf("renderer for %s is unavailable\n", job->output_langname);
3954 gv_fixLocale (0);
3955 FINISH();
3956 return -1;
3957 }
3958
3959 switch (job->output_lang) {
3960 case VTX:
3961 /* output sorted, i.e. all nodes then all edges */
3962 job->flags |= EMIT_SORTED;
3963 break;
3964 default:
3965 job->flags |= chkOrder(g);
3966 break;
3967 }
3968
3969 // if we already have an active job list and the device doesn't support
3970 // multiple output files, or we are about to write to a different output
3971 // device
3972 firstjob = gvc->active_jobs;
3973 if (firstjob) {
3974 if (! (firstjob->flags & GVDEVICE_DOES_PAGES)
3975 || strcmp(job->output_langname, firstjob->output_langname)) {
3976
3977 gvrender_end_job(firstjob);
3978
3979 gvc->active_jobs = NULL; /* clear active list */
3980 gvc->common.viewNum = 0;
3981 prevjob = NULL;
3982 }
3983 }
3984 else {
3985 prevjob = NULL;
3986 }
3987
3988 if (prevjob) {
3989 prevjob->next_active = job; /* insert job in active list */
3990 job->output_file = prevjob->output_file; /* FIXME - this is dumb ! */
3991 }
3992 else {
3993 if (gvrender_begin_job(job))
3994 continue;
3995 gvc->active_jobs = job; /* first job of new list */
3996 }
3997 job->next_active = NULL; /* terminate active list */
3999
4000 init_job_pad(job);
4001 init_job_margin(job);
4002 init_job_dpi(job, g);
4003 init_job_viewport(job, g);
4004 init_job_pagination(job, g);
4005
4006 if (! (job->flags & GVDEVICE_EVENTS)) {
4007#ifdef DEBUG
4008 /* Show_boxes is not defined, if at all,
4009 * until splines are generated in dot
4010 */
4011 show_boxes_append(&Show_boxes, NULL);
4012 job->common->show_boxes = show_boxes_at(&Show_boxes, 0);
4013#endif
4014 emit_graph(job, g);
4015 }
4016
4017 /* the last job, after all input graphs are processed,
4018 * is finalized from gvFinalize()
4019 */
4020 prevjob = job;
4021 }
4022 gv_fixLocale (0);
4023 FINISH();
4024 return 0;
4025}
4026
4027/* Check for colon in colorlist. If one exists, and not the first
4028 * character, store the characters before the colon in clrs[0] and
4029 * the characters after the colon (and before the next or end-of-string)
4030 * in clrs[1]. If there are no characters after the first colon, clrs[1]
4031 * is NULL. Return TRUE.
4032 * If there is no non-trivial string before a first colon, set clrs[0] to
4033 * NULL and return FALSE.
4034 *
4035 * Note that memory is allocated as a single block stored in clrs[0] and
4036 * must be freed by calling function.
4037 */
4038bool findStopColor(char *colorlist, char *clrs[2], double *frac) {
4039 colorsegs_t segs = {0};
4040 int rv;
4041
4042 rv = parseSegs (colorlist, 0, &segs);
4043 if (rv || segs.numc < 2 || segs.segs[0].color == NULL) {
4044 clrs[0] = NULL;
4045 freeSegs(&segs);
4046 return false;
4047 }
4048
4049 if (segs.numc > 2)
4050 agwarningf("More than 2 colors specified for a gradient - ignoring remaining\n");
4051
4052 clrs[0] = gv_calloc(strlen(colorlist) + 1, sizeof(char));
4053 strcpy(clrs[0], segs.segs[0].color);
4054 if (segs.segs[1].color) {
4055 clrs[1] = clrs[0] + (strlen(clrs[0])+1);
4056 strcpy(clrs[1], segs.segs[1].color);
4057 }
4058 else
4059 clrs[1] = NULL;
4060
4061 if (segs.segs[0].hasFraction)
4062 *frac = segs.segs[0].t;
4063 else if (segs.segs[1].hasFraction)
4064 *frac = 1 - segs.segs[1].t;
4065 else
4066 *frac = 0;
4067
4068 freeSegs(&segs);
4069 return true;
4070}
4071
static agxbuf last
last message
Definition agerror.c:29
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:249
static size_t agxbput_n(agxbuf *xb, const char *s, size_t ssz)
append string s of length ssz into xb
Definition agxbuf.h:229
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:88
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define BETWEEN(a, b, c)
Definition arith.h:38
#define ROUND(f)
Definition arith.h:48
#define M_PI
Definition arith.h:41
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1117
void arrow_gen(GVJ_t *job, emit_state_t emit_state, pointf p, pointf u, double arrowsize, double penwidth, uint32_t flag)
Definition arrows.c:1155
container data types API
#define dtsearch(d, o)
Definition cdt.h:191
#define dtinsert(d, o)
Definition cdt.h:193
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
#define parent(i)
Definition closest.c:78
#define right(i)
Definition closest.c:77
COLORPROCS_API char * setColorScheme(const char *s)
Definition colxlate.c:414
pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:169
char * late_nnstring(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:84
bool mapbool(const char *p)
Definition utils.c:336
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:78
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:33
bool overlap_label(textlabel_t *lp, boxf b)
Definition utils.c:1341
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:48
pointf dotneato_closest(splines *spl, pointf pt)
Definition utils.c:341
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1278
char * htmlEntityUTF8(char *s, graph_t *g)
Definition utils.c:1199
#define DEFAULT_SELECTEDFILLCOLOR
Definition const.h:53
#define VTX
Definition const.h:137
#define CHAR_LATIN1
Definition const.h:197
#define DEFAULT_LAYERLISTSEP
Definition const.h:83
#define NO_SUPPORT
Definition const.h:147
#define METAPOST
Definition const.h:138
#define PCL
Definition const.h:132
#define DEFAULT_COLOR
Definition const.h:48
#define QPDF
Definition const.h:140
#define DEFAULT_EMBED_MARGIN
Definition const.h:94
#define DEFAULT_ACTIVEFILLCOLOR
Definition const.h:50
#define MIF
Definition const.h:133
#define DEFAULT_FONTSIZE
Definition const.h:61
#define MIN_FONTSIZE
Definition const.h:63
#define DEFAULT_PRINT_MARGIN
Definition const.h:92
#define DEFAULT_FONTNAME
Definition const.h:67
#define GVRENDER_PLUGIN
Definition const.h:146
#define DEFAULT_FILL
Definition const.h:69
#define DEFAULT_DELETEDFILLCOLOR
Definition const.h:56
#define DFLT_SAMPLE
Definition const.h:105
#define DEFAULT_SELECTEDPENCOLOR
Definition const.h:52
#define DEFAULT_LAYERSEP
Definition const.h:82
#define GRADIENT
Definition const.h:232
#define DEFAULT_VISITEDFILLCOLOR
Definition const.h:59
#define DEFAULT_DELETEDPENCOLOR
Definition const.h:55
#define DEFAULT_GRAPH_PAD
Definition const.h:96
#define RGRADIENT
Definition const.h:233
#define DEFAULT_VISITEDPENCOLOR
Definition const.h:58
#define DEFAULT_ACTIVEPENCOLOR
Definition const.h:49
#define left
Definition dthdr.h:12
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:149
static void del(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:156
Ppolyline_t * ellipticWedge(pointf ctr, double xsemi, double ysemi, double angle0, double angle1)
Definition ellipse.c:298
#define EPSILON
Definition emit.c:47
bool emit_once(char *str)
Definition emit.c:3449
int wedgedEllipse(GVJ_t *job, pointf *pf, char *clrs)
Definition emit.c:558
static bool node_in_box(node_t *n, boxf b)
Definition emit.c:1708
static bool write_edge_test(Agraph_t *g, Agedge_t *e)
Definition emit.c:1385
static void free_stroke(stroke_t sp)
Definition emit.c:2132
static void map_point(GVJ_t *job, pointf pf)
Definition emit.c:331
static radfunc_t taperfun(edge_t *e)
Definition emit.c:2164
static void emit_xdot(GVJ_t *job, xdot *xd)
Definition emit.c:1420
static void emit_end_graph(GVJ_t *job)
Definition emit.c:3305
static void emit_end_cluster(GVJ_t *job)
Definition emit.c:3481
static int * parse_layerselect(GVC_t *gvc, char *p)
Definition emit.c:1065
static void emit_edge_graphics(GVJ_t *job, edge_t *e, char **styles)
Definition emit.c:2176
void emit_clusters(GVJ_t *job, Agraph_t *g, int flags)
Definition emit.c:3486
static Dict_t * strings
Definition emit.c:3443
static void emit_cluster_colors(GVJ_t *job, graph_t *g)
Definition emit.c:3151
static double revfunc(double curlen, double totallen, double initwid)
Definition emit.c:2143
static void nodeIntersect(GVJ_t *job, pointf p, bool explicit_iurl, char *iurl, bool explicit_itooltip)
Definition emit.c:2659
static char * defaultlinestyle[3]
Definition emit.c:92
static void init_splines_bb(splines *spl)
Definition emit.c:3812
static bool selectedLayer(GVC_t *gvc, int layerNum, int numLayers, char *spec)
Definition emit.c:1006
void gv_fixLocale(int set)
Definition emit.c:3888
static segitem_t * approx_bezier(pointf *cp, segitem_t *lp)
Definition emit.c:857
static void nextlayer(GVJ_t *job, int **listp)
Definition emit.c:1209
static void layerPagePrefix(GVJ_t *job, agxbuf *xb)
Definition emit.c:186
static void init_bb(graph_t *g)
Definition emit.c:3868
static void emit_end_node(GVJ_t *job)
Definition emit.c:1857
static pointf computeoffset_qr(pointf p, pointf q, pointf r, pointf s, double d)
Definition emit.c:1921
struct segitem_s segitem_t
static bool edge_in_layer(GVJ_t *job, edge_t *e)
Definition emit.c:1670
static double forfunc(double curlen, double totallen, double initwid)
Definition emit.c:2138
#define FUZZ
Definition emit.c:46
static char ** checkClusterStyle(graph_t *sg, graphviz_polygon_style_t *flagp)
Definition emit.c:356
static bool isFilled(node_t *n)
Definition emit.c:713
static void emit_background(GVJ_t *job, graph_t *g)
Definition emit.c:1540
static void init_bb_edge(edge_t *e)
Definition emit.c:3838
int initMapData(GVJ_t *job, char *lbl, char *url, char *tooltip, char *target, char *id, void *gobj)
Definition emit.c:151
static char * preprocessTooltip(char *s, void *gobj)
Definition emit.c:291
static int multicolor(GVJ_t *job, edge_t *e, char **styles, char *colors, size_t num, double arrowsize, double penwidth)
Definition emit.c:2056
static int parse_layers(GVC_t *gvc, graph_t *g, char *p)
Definition emit.c:1093
static char * interpretCRNL(char *ins)
Definition emit.c:248
static boxf bezier_bb(bezier bz)
Definition emit.c:3787
static bool is_style_delim(int c)
Definition emit.c:3660
static void expandBB(boxf *bb, pointf p)
Definition emit.c:2795
static char adjust[]
Definition emit.c:2792
static bool clust_in_layer(GVJ_t *job, graph_t *sg)
Definition emit.c:1690
static bool isRect(polygon_t *p)
Definition emit.c:703
static void emit_edge(GVJ_t *job, edge_t *e)
Definition emit.c:2749
static pointf * pEllipse(double a, double b, size_t np)
Definition emit.c:734
static Dtdisc_t stringdict
Definition emit.c:3444
static void init_bb_node(graph_t *g, node_t *n)
Definition emit.c:3847
#define NotFirstPage(j)
Definition emit.c:3311
static double bothfunc(double curlen, double totallen, double initwid)
Definition emit.c:2156
#define AEQ0(x)
Definition emit.c:443
#define FUNLIMIT
Definition emit.c:3712
static void emit_view(GVJ_t *job, graph_t *g, int flags)
Definition emit.c:3228
#define SID
Definition emit.c:3673
#define THIN_LINE
Definition emit.c:546
#define FIRST_SEG(L)
Definition emit.c:814
static boxf textBB(double x, double y, textspan_t *span)
Definition emit.c:2826
static void emit_begin_node(GVJ_t *job, node_t *n)
Definition emit.c:1715
static double approxLen(pointf *pts)
Definition emit.c:1986
static int numPhysicalLayers(GVJ_t *job)
Return number of physical layers to be emitted.
Definition emit.c:1169
static void init_job_dpi(GVJ_t *job, graph_t *g)
Definition emit.c:3046
static void map_label(GVJ_t *job, textlabel_t *lab)
Definition emit.c:674
static bool node_in_layer(GVJ_t *job, graph_t *g, node_t *n)
Definition emit.c:1648
static void firstpage(GVJ_t *job)
Definition emit.c:1360
static void splitBSpline(bezier *bz, double t, bezier *left, bezier *right)
Definition emit.c:2002
static char * saved_color_scheme
Definition emit.c:1713
void * init_xdot(Agraph_t *g)
Definition emit.c:55
#define INIT_SEG(P, L)
Definition emit.c:815
static void emit_edge_label(GVJ_t *job, textlabel_t *lbl, emit_state_t lkind, int explicit, char *url, char *tooltip, char *target, char *id, splines *spl)
Definition emit.c:2600
static void mkSegPts(segitem_t *prv, segitem_t *cur, segitem_t *nxt, pointf *p1, pointf *p2, double w2)
Definition emit.c:895
static void initObjMapData(GVJ_t *job, textlabel_t *lab, void *gobj)
Definition emit.c:309
static void init_job_margin(GVJ_t *job)
Definition emit.c:3022
static void freePara(exdot_op *op)
Definition emit.c:2851
static bool validpage(GVJ_t *job)
Definition emit.c:1365
static int chkOrder(graph_t *g)
Definition emit.c:1135
static void init_job_viewport(GVJ_t *job, graph_t *g)
Definition emit.c:3069
static void emit_colors(GVJ_t *job, graph_t *g)
Definition emit.c:3173
#define SEP
static void emit_begin_cluster(GVJ_t *job, Agraph_t *sg)
Definition emit.c:3467
static void setup_page(GVJ_t *job)
Definition emit.c:1595
static int layer_index(GVC_t *gvc, char *str, int all)
Definition emit.c:991
static bool validlayer(GVJ_t *job)
Definition emit.c:1204
static void init_gvc(GVC_t *gvc, graph_t *g)
Definition emit.c:2934
static int parseSegs(char *clrs, size_t nseg, colorsegs_t *psegs)
Definition emit.c:463
static bool is_natural_number(const char *sstr)
Definition emit.c:981
int stripedBox(GVJ_t *job, pointf *AF, char *clrs, int rotate)
Definition emit.c:604
obj_state_t * push_obj_state(GVJ_t *job)
Definition emit.c:95
static void emit_attachment(GVJ_t *job, textlabel_t *lp, splines *spl)
Definition emit.c:1942
#define FINISH()
Definition emit.c:3910
static void emit_page(GVJ_t *job, graph_t *g)
Definition emit.c:3313
gvevent_key_binding_t gvevent_key_binding[]
Definition gvevent.c:520
char ** parse_style(char *s)
Definition emit.c:3721
static void firstlayer(GVJ_t *job, int **listp)
Definition emit.c:1179
void emit_once_reset(void)
Definition emit.c:3459
static void map_output_bspline(pointf **pbs, size_t **pbs_n, size_t *pbs_poly_n, bezier *bp, double w2)
Definition emit.c:939
void emit_map_rect(GVJ_t *job, boxf b)
Definition emit.c:648
static void emit_end_edge(GVJ_t *job)
Definition emit.c:2682
static void freeSegs(colorsegs_t *segs)
Definition emit.c:413
static void init_layering(GVC_t *gvc, graph_t *g)
Definition emit.c:1147
static void init_job_pagination(GVJ_t *job, graph_t *g)
Definition emit.c:1245
static double nonefunc(double curlen, double totallen, double initwid)
Definition emit.c:2148
static boxf ptsBB(xdot_point *inpts, size_t numpts, boxf *bb)
Definition emit.c:2803
void pop_obj_state(GVJ_t *job)
Definition emit.c:119
static void nextpage(GVJ_t *job)
Definition emit.c:1373
static pointf * copyPts(xdot_point *inpts, size_t numpts)
Definition emit.c:1411
static point pagecode(GVJ_t *job, char c)
Definition emit.c:1220
#define MARK_FIRST_SEG(L)
Definition emit.c:813
static bool edge_in_box(edge_t *e, boxf b)
Definition emit.c:2403
gvdevice_callbacks_t gvdevice_callbacks
Definition gvevent.c:540
#define HW
Definition emit.c:747
char * getObjId(GVJ_t *job, void *obj, agxbuf *xb)
Use id of root graph if any, plus kind and internal id of object.
Definition emit.c:198
void update_bb_bz(boxf *bb, pointf *cp)
Definition emit.c:762
static void map_bspline_poly(pointf **pbs_p, size_t **pbs_n, size_t *pbs_poly_n, size_t n, pointf *p1, pointf *p2)
Definition emit.c:828
const size_t gvevent_key_binding_size
Definition gvevent.c:537
static void emit_begin_edge(GVJ_t *job, edge_t *e, char **styles)
Definition emit.c:2423
static token_t style_token(char **s)
Definition emit.c:3685
static bool check_control_points(pointf *cp)
Definition emit.c:754
static void emit_begin_graph(GVJ_t *job, graph_t *g)
Definition emit.c:3291
static bool selectedlayer(GVJ_t *job, char *spec)
Definition emit.c:1045
static segitem_t * appendSeg(pointf p, segitem_t *lp)
Definition emit.c:817
static bool write_node_test(Agraph_t *g, Agnode_t *n)
Definition emit.c:1398
static void emit_node(GVJ_t *job, node_t *n)
Definition emit.c:1869
#define P2RECT(p, pr, sx, sy)
Definition emit.c:45
void emit_graph(GVJ_t *job, graph_t *g)
Definition emit.c:3390
static char * default_pencolor(char *pencolor, char *deflt)
Definition emit.c:1970
boxf xdotBB(Agraph_t *g)
Definition emit.c:2857
bool findStopColor(char *colorlist, char *clrs[2], double *frac)
Definition emit.c:4038
double(* radfunc_t)(double, double, double)
Definition emit.c:2136
static double getSegLen(char *s)
Definition emit.c:424
static void init_job_pad(GVJ_t *job)
Definition emit.c:3003
static double bisect(pointf pp, pointf cp, pointf np)
Definition emit.c:877
static pointf computeoffset_p(pointf p, pointf q, double d)
Definition emit.c:1908
expr procedure type
Definition exparse.y:211
static int flags
Definition gc.c:61
#define Y(i)
Definition gdefs.h:3
#define X(prefix, name, str, type, subtype,...)
Definition gdefs.h:14
void rect2poly(pointf *p)
Definition geom.c:139
double ptToLine2(pointf a, pointf b, pointf p)
Definition geom.c:218
#define POINTS(a_inches)
Definition geom.h:68
#define EXPANDBP(b, p)
Definition geom.h:54
struct pointf_s pointf
#define EXPANDBB(b0, b1)
Definition geom.h:57
#define DIST(p, q)
Definition geom.h:62
#define POINTS_PER_INCH
Definition geom.h:64
geometric functions (e.g. on points and boxes)
static pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:81
static pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:72
static point exch_xy(point p)
Definition geomprocs.h:99
static pointf exch_xyf(pointf p)
Definition geomprocs.h:108
static bool boxf_overlap(boxf b0, boxf b1)
Definition geomprocs.h:117
Agsym_t * N_fontsize
Definition globals.h:84
Agsym_t * E_visitedpencolor
Definition globals.h:94
Agsym_t * N_layer
Definition globals.h:87
Agsym_t * G_deletedpencolor
Definition globals.h:75
Agsym_t * G_deletedfillcolor
Definition globals.h:75
Agsym_t * E_comment
Definition globals.h:99
Agsym_t * G_peripheries
Definition globals.h:76
Agsym_t * E_deletedfillcolor
Definition globals.h:95
Agsym_t * E_decorate
Definition globals.h:97
Agsym_t * E_style
Definition globals.h:97
Agsym_t * G_activepencolor
Definition globals.h:73
Agsym_t * N_fontname
Definition globals.h:84
Agsym_t * G_activefillcolor
Definition globals.h:73
Agsym_t * N_comment
Definition globals.h:88
Agsym_t * E_activefillcolor
Definition globals.h:92
Agsym_t * N_style
Definition globals.h:85
Agsym_t * E_fillcolor
Definition globals.h:91
Agsym_t * E_dir
Definition globals.h:97
Agsym_t * G_visitedfillcolor
Definition globals.h:74
show_boxes_t Show_boxes
Definition globals.h:59
Agsym_t * E_selectedpencolor
Definition globals.h:93
Agsym_t * E_selectedfillcolor
Definition globals.h:93
Agsym_t * E_deletedpencolor
Definition globals.h:95
Agsym_t * E_color
Definition globals.h:91
Agsym_t * E_activepencolor
Definition globals.h:92
Agsym_t * E_arrowsz
Definition globals.h:98
Agsym_t * G_gradientangle
Definition globals.h:77
Agsym_t * E_visitedfillcolor
Definition globals.h:94
Agsym_t * G_penwidth
Definition globals.h:76
bool Y_invert
invert y in dot & plain output
Definition globals.h:69
Agsym_t * E_penwidth
Definition globals.h:106
Agsym_t * E_layer
Definition globals.h:98
Agsym_t * G_visitedpencolor
Definition globals.h:74
static double len(glCompPoint p)
Definition glutils.c:150
static int Verbose
Definition gml2gv.c:22
#define FILL
Definition gmlparse.c:400
void free(void *)
edge
Definition gmlparse.y:279
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:341
char * agget(void *obj, char *name)
Definition attr.c:442
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
#define ED_xlabel(e)
Definition types.h:590
#define ED_head_label(e)
Definition types.h:587
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define ED_gui_state(e)
Definition types.h:586
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:889
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:93
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:84
#define ED_label(e)
Definition types.h:589
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:155
@ AGPREV
Definition cgraph.h:858
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_drawing(g)
Definition types.h:353
int agisdirected(Agraph_t *g)
Definition graph.c:179
#define GD_clust(g)
Definition types.h:360
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_label(g)
Definition types.h:374
#define GD_charset(g)
Definition types.h:367
#define GD_gvc(g)
Definition types.h:355
#define GD_odim(g)
Definition types.h:391
#define GD_gui_state(g)
Definition types.h:366
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
#define ND_bb(n)
Definition types.h:488
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_state(n)
Definition types.h:531
#define ND_label(n)
Definition types.h:502
#define ND_rw(n)
Definition types.h:525
#define ND_lw(n)
Definition types.h:506
#define ND_xlabel(n)
Definition types.h:503
#define ND_shape_info(n)
Definition types.h:529
#define ND_pos(n)
Definition types.h:520
#define agfindnode(g, n)
Definition types.h:611
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
Agraph_t * agraphof(void *obj)
Definition obj.c:184
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:234
int agobjkind(void *obj)
Definition obj.c:253
Agraph_t * agroot(void *obj)
Definition obj.c:167
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
#define LAYOUT_DONE(g)
Definition gvc.h:51
int gvRenderJobs(GVC_t *gvc, graph_t *g)
Definition emit.c:3912
static uint64_t id
Definition gv2gml.c:42
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static bool gv_isspace(int c)
Definition gv_ctype.h:55
Arithmetic helper functions.
static bool is_exactly_zero(double v)
is a value precisely 0.0?
Definition gv_math.h:62
swig_ptr_object_handlers offset
Definition gv_php.cpp:5915
Graphviz context library.
#define EMIT_CLUSTERS_LAST
Definition gvcjob.h:84
@ PEN_SOLID
Definition gvcjob.h:35
#define EMIT_SORTED
Definition gvcjob.h:82
#define GVRENDER_DOES_TOOLTIPS
Definition gvcjob.h:103
#define GVRENDER_DOES_Z
Definition gvcjob.h:105
#define EMIT_EDGE_SORTED
Definition gvcjob.h:86
#define GVRENDER_DOES_LABELS
Definition gvcjob.h:96
#define GVRENDER_NO_WHITE_BG
Definition gvcjob.h:106
@ MAP_CIRCLE
Definition gvcjob.h:166
@ MAP_POLYGON
Definition gvcjob.h:166
@ MAP_RECTANGLE
Definition gvcjob.h:166
#define GVDEVICE_DOES_PAGES
Definition gvcjob.h:87
#define GVDEVICE_DOES_LAYERS
Definition gvcjob.h:88
#define GVDEVICE_DOES_TRUECOLOR
Definition gvcjob.h:90
@ FILL_NONE
Definition gvcjob.h:36
#define EMIT_PREORDER
Definition gvcjob.h:85
emit_state_t
Definition gvcjob.h:173
@ EMIT_CDRAW
Definition gvcjob.h:174
@ EMIT_NDRAW
Definition gvcjob.h:176
@ EMIT_TDRAW
Definition gvcjob.h:174
@ EMIT_HDRAW
Definition gvcjob.h:174
@ EMIT_HLABEL
Definition gvcjob.h:175
@ EMIT_GDRAW
Definition gvcjob.h:174
@ EMIT_NLABEL
Definition gvcjob.h:176
@ EMIT_GLABEL
Definition gvcjob.h:175
@ EMIT_ELABEL
Definition gvcjob.h:176
@ EMIT_EDRAW
Definition gvcjob.h:176
@ EMIT_CLABEL
Definition gvcjob.h:175
@ EMIT_TLABEL
Definition gvcjob.h:175
#define GVDEVICE_EVENTS
Definition gvcjob.h:89
#define GVRENDER_DOES_MAPS
Definition gvcjob.h:97
#define GVRENDER_DOES_TARGETS
Definition gvcjob.h:104
@ CLUSTER_OBJTYPE
Definition gvcjob.h:168
@ EDGE_OBJTYPE
Definition gvcjob.h:168
@ ROOTGRAPH_OBJTYPE
Definition gvcjob.h:168
@ NODE_OBJTYPE
Definition gvcjob.h:168
#define PENWIDTH_NORMAL
Definition gvcjob.h:40
#define GVRENDER_DOES_TRANSFORM
Definition gvcjob.h:95
#define GVRENDER_Y_GOES_DOWN
Definition gvcjob.h:94
#define EMIT_COLORS
Definition gvcjob.h:83
#define GVRENDER_DOES_MAP_POLYGON
Definition gvcjob.h:100
#define GVRENDER_DOES_MAP_RECTANGLE
Definition gvcjob.h:98
static void color(Agraph_t *g)
Definition gvcolor.c:128
void gvrender_end_nodes(GVJ_t *job)
Definition gvrender.c:308
void gvrender_begin_nodes(GVJ_t *job)
Definition gvrender.c:298
void gvrender_end_job(GVJ_t *job)
Definition gvrender.c:116
void gvrender_beziercurve(GVJ_t *job, pointf *AF, size_t n, int filled)
Definition gvrender.c:582
void gvrender_comment(GVJ_t *job, char *str)
Definition gvrender.c:616
void gvrender_end_graph(GVJ_t *job)
Definition gvrender.c:228
void gvrender_set_style(GVJ_t *job, char **s)
Definition gvrender.c:484
void gvrender_set_fillcolor(GVJ_t *job, char *name)
Definition gvrender.c:453
void gvrender_polyline(GVJ_t *job, pointf *AF, size_t n)
Definition gvrender.c:599
void gvrender_begin_edges(GVJ_t *job)
Definition gvrender.c:318
pointf * gvrender_ptf_A(GVJ_t *job, pointf *af, pointf *AF, size_t n)
Definition gvrender.c:154
void gvrender_polygon(GVJ_t *job, pointf *af, size_t n, int filled)
Definition gvrender.c:540
void gvrender_end_edges(GVJ_t *job)
Definition gvrender.c:328
void gvrender_begin_graph(GVJ_t *job)
Definition gvrender.c:218
void gvrender_end_page(GVJ_t *job)
Definition gvrender.c:249
void gvrender_end_layer(GVJ_t *job)
Definition gvrender.c:270
void gvrender_box(GVJ_t *job, boxf BF, int filled)
Definition gvrender.c:568
void gvrender_begin_cluster(GVJ_t *job)
Definition gvrender.c:280
void gvrender_set_gradient_vals(GVJ_t *job, char *stopcolor, int angle, double frac)
Definition gvrender.c:470
void gvrender_ellipse(GVJ_t *job, pointf *AF, int filled)
Definition gvrender.c:523
void gvrender_end_edge(GVJ_t *job)
Definition gvrender.c:366
void gvrender_begin_anchor(GVJ_t *job, char *href, char *tooltip, char *target, char *id)
Definition gvrender.c:376
void gvrender_end_anchor(GVJ_t *job)
Definition gvrender.c:387
void gvrender_begin_layer(GVJ_t *job)
Definition gvrender.c:259
void gvrender_begin_page(GVJ_t *job)
Definition gvrender.c:239
void gvrender_begin_edge(GVJ_t *job)
Definition gvrender.c:357
GVJ_t * gvjobs_first(GVC_t *gvc)
Definition gvjobs.c:86
void gvrender_textspan(GVJ_t *job, pointf p, textspan_t *span)
Definition gvrender.c:417
void gvrender_begin_node(GVJ_t *job)
Definition gvrender.c:338
int gvrender_begin_job(GVJ_t *job)
Definition gvrender.c:103
void gvrender_end_cluster(GVJ_t *job)
Definition gvrender.c:289
GVJ_t * gvjobs_next(GVC_t *gvc)
Definition gvjobs.c:91
void gvrender_set_penwidth(GVJ_t *job, double penwidth)
Definition gvrender.c:801
int gvrender_select(GVJ_t *job, const char *lang)
Definition gvrender.c:40
void gvrender_end_node(GVJ_t *job)
Definition gvrender.c:347
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition gvrender.c:436
static xdot_state_t * xd
static double penwidth[]
GVC_t * gvc
Definition htmlparse.c:99
agxbuf * str
Definition htmlparse.c:97
htmllabel_t * lbl
Definition htmlparse.c:93
char * strdup_and_subst_obj(char *str, void *obj)
Definition labels.c:385
void free_textspan(textspan_t *tl, size_t cnt)
Definition labels.c:186
void emit_label(GVJ_t *job, emit_state_t emit_state, textlabel_t *lp)
Definition labels.c:212
#define DEFINE_LIST(name, type)
Definition list.h:26
static int * ps
Definition lu.c:51
static void add_point(int *n, int igrp, double **x, int *nmax, double point[], int **groups)
Definition make_map.c:1197
#define delta
Definition maze.c:133
void freePath(Ppolyline_t *p)
Definition util.c:18
void round_corners(GVJ_t *job, pointf *AF, size_t sides, graphviz_polygon_style_t style, int filled)
Handle some special graphical cases, such as rounding the shape, adding diagonals at corners,...
Definition shapes.c:706
pointf textspan_size(GVC_t *gvc, textspan_t *span)
Estimates size of a textspan, in points.
Definition textspan.c:79
shape_kind shapeOf(node_t *)
Definition shapes.c:1902
pointf coord(node_t *n)
Definition utils.c:151
stroke_t taper(bezier *, double(*radfunc_t)(double, double, double), double initwid)
Definition taper.c:181
static void rotate(int n, int dim, double *x, double angle)
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:425
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
const char ** show_boxes
emit code for correct box coordinates
Definition gvcommon.h:25
int viewNum
rendering state
Definition gvcommon.h:29
Definition gvcint.h:80
char * graphname
Definition gvcint.h:122
bool graph_sets_pageSize
Definition gvcint.h:133
char ** defaultlinestyle
Definition gvcint.h:148
bool graph_sets_margin
Definition gvcint.h:133
GVJ_t * active_jobs
Definition gvcint.h:123
pointf pad
Definition gvcint.h:128
GVCOMMON_t common
Definition gvcint.h:81
double defaultfontsize
Definition gvcint.h:145
char * defaultfontname
Definition gvcint.h:144
GVG_t * gvg
Definition gvcint.h:92
bool graph_sets_pad
Definition gvcint.h:133
char * layerListDelims
Definition gvcint.h:137
pointf pageSize
Definition gvcint.h:129
char * layerDelims
Definition gvcint.h:136
int numLayers
Definition gvcint.h:140
char * pagedir
Definition gvcint.h:126
pointf margin
Definition gvcint.h:127
int rotation
Definition gvcint.h:132
gvplugin_active_layout_t layout
Definition gvcint.h:120
Dt_t * textfont_dt
Definition gvcint.h:107
boxf bb
Definition gvcint.h:131
graph_t * g
Definition gvcint.h:117
char * layers
Definition gvcint.h:138
char ** layerIDs
Definition gvcint.h:139
int * layerlist
Definition gvcint.h:141
char * input_filename
Definition gvcint.h:73
int graph_index
Definition gvcint.h:74
int rotation
Definition gvcjob.h:319
int flags
Definition gvcjob.h:299
boxf clip
Definition gvcjob.h:313
pointf margin
Definition gvcjob.h:323
pointf pageSize
Definition gvcjob.h:315
point pagesArraySize
Definition gvcjob.h:304
pointf dpi
Definition gvcjob.h:325
gvdevice_callbacks_t * callbacks
Definition gvcjob.h:288
int output_lang
Definition gvcjob.h:283
obj_state_t * obj
Definition gvcjob.h:269
boxf bb
Definition gvcjob.h:311
gvplugin_active_device_t device
Definition gvcjob.h:286
gvevent_key_binding_t * keybindings
Definition gvcjob.h:356
point pagesArrayElem
Definition gvcjob.h:308
gvplugin_active_render_t render
Definition gvcjob.h:285
point pagesArrayMinor
Definition gvcjob.h:307
pointf view
Definition gvcjob.h:321
pointf devscale
Definition gvcjob.h:334
point pagesArrayMajor
Definition gvcjob.h:306
pointf focus
Definition gvcjob.h:316
GVCOMMON_t * common
Definition gvcjob.h:267
point pagesArrayFirst
Definition gvcjob.h:305
FILE * output_file
Definition gvcjob.h:277
pointf device_dpi
Definition gvcjob.h:289
box pageBoundingBox
Definition gvcjob.h:329
double zoom
Definition gvcjob.h:318
box boundingBox
Definition gvcjob.h:330
pointf scale
Definition gvcjob.h:332
const char * layout_type
Definition gvcjob.h:274
boxf canvasBox
Definition gvcjob.h:322
GVC_t * gvc
Definition gvcjob.h:263
int layerNum
Definition gvcjob.h:302
unsigned int width
Definition gvcjob.h:327
int numLayers
Definition gvcjob.h:301
int numPages
Definition gvcjob.h:309
boxf pageBox
Definition gvcjob.h:314
bool device_sets_dpi
Definition gvcjob.h:290
char * input_filename
Definition gvcjob.h:271
const char * output_langname
Definition gvcjob.h:282
int graph_index
Definition gvcjob.h:272
pointf pad
Definition gvcjob.h:312
size_t numkeys
Definition gvcjob.h:357
pointf translation
Definition gvcjob.h:333
GVJ_t * next_active
Definition gvcjob.h:265
unsigned int height
Definition gvcjob.h:328
size_t pn
Definition pathgeom.h:47
Ppoint_t * ps
Definition pathgeom.h:46
Definition cdt.h:104
int link
Definition cdt.h:91
char * style
Definition xdot.h:147
xdot_font font
Definition xdot.h:146
xdot_polyline polygon
Definition xdot.h:139
xdot_rect ellipse
Definition xdot.h:138
char * color
Definition xdot.h:144
unsigned int fontchar
Definition xdot.h:148
xdot_kind kind
Definition xdot.h:136
xdot_text text
Definition xdot.h:142
xdot_polyline bezier
Definition xdot.h:141
xdot_polyline polyline
Definition xdot.h:140
xdot_color grad_color
Definition xdot.h:145
union _xdot_op::@126 u
Definition types.h:89
size_t size
Definition types.h:91
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
point LL
Definition geom.h:39
point UR
Definition geom.h:39
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
double t
segment size >= 0
Definition emit.c:402
char * color
Definition emit.c:401
bool hasFraction
Definition emit.c:403
char * base
Definition emit.c:408
colorseg_t * segs
Definition emit.c:409
int numc
Definition emit.c:407
boxf bb
Definition emit.c:51
xdot_op op
Definition emit.c:50
textspan_t * span
Definition emit.c:52
pointf default_margin
Definition gvcjob.h:120
pointf default_pagesize
Definition gvcjob.h:121
gvdevice_features_t * features
Definition gvcjob.h:130
const char * type
Definition gvcint.h:42
gvrender_features_t * features
Definition gvcjob.h:137
union obj_state_s::@92 u
char * headlabel
Definition gvcjob.h:208
graph_t * g
Definition gvcjob.h:186
unsigned explicit_tailtarget
Definition gvcjob.h:230
edge_t * e
Definition gvcjob.h:189
pointf * url_map_p
Definition gvcjob.h:240
gvcolor_t fillcolor
Definition gvcjob.h:194
char * tooltip
Definition gvcjob.h:216
unsigned explicit_edgetarget
Definition gvcjob.h:232
char * taillabel
Definition gvcjob.h:207
size_t url_bsplinemap_poly_n
Definition gvcjob.h:243
double z
Definition gvcjob.h:202
char * url
Definition gvcjob.h:210
unsigned explicit_tooltip
Definition gvcjob.h:226
char * labelurl
Definition gvcjob.h:212
char * tailurl
Definition gvcjob.h:213
char * xlabel
Definition gvcjob.h:206
char * labeltooltip
Definition gvcjob.h:217
char * headtarget
Definition gvcjob.h:224
char * target
Definition gvcjob.h:221
obj_type type
Definition gvcjob.h:184
size_t * url_bsplinemap_n
Definition gvcjob.h:245
char * tailtooltip
Definition gvcjob.h:218
gvcolor_t stopcolor
Definition gvcjob.h:194
pen_type pen
Definition gvcjob.h:197
char * headtooltip
Definition gvcjob.h:219
unsigned explicit_labeltooltip
Definition gvcjob.h:229
map_shape_t url_map_shape
Definition gvcjob.h:238
unsigned explicit_tailurl
Definition gvcjob.h:233
pointf * url_bsplinemap_p
Definition gvcjob.h:247
unsigned explicit_tailtooltip
Definition gvcjob.h:227
node_t * n
Definition gvcjob.h:188
unsigned explicit_headurl
Definition gvcjob.h:234
unsigned explicit_headtarget
Definition gvcjob.h:231
graph_t * sg
Definition gvcjob.h:187
char * headurl
Definition gvcjob.h:214
gvcolor_t pencolor
Definition gvcjob.h:194
char * id
Definition gvcjob.h:211
int gradient_angle
Definition gvcjob.h:195
emit_state_t emit_state
Definition gvcjob.h:192
char * labeltarget
Definition gvcjob.h:222
obj_state_t * parent
Definition gvcjob.h:182
double tail_z
Definition gvcjob.h:202
char * tailtarget
Definition gvcjob.h:223
double head_z
Definition gvcjob.h:202
unsigned explicit_headtooltip
Definition gvcjob.h:228
unsigned labeledgealigned
Definition gvcjob.h:235
char * label
Definition gvcjob.h:205
size_t url_map_n
Definition gvcjob.h:239
fill_type fill
Definition gvcjob.h:198
double penwidth
Definition gvcjob.h:199
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
size_t sides
number of sides
Definition types.h:146
double skew
Definition types.h:149
double orientation
Definition types.h:147
double distortion
Definition types.h:148
struct segitem_s * next
Definition emit.c:810
pointf p
Definition emit.c:809
bezier * list
Definition types.h:99
boxf bb
Definition types.h:101
size_t size
Definition types.h:100
pointf * vertices
Definition types.h:175
size_t nvertices
number of points in the stroke
Definition types.h:174
char * name
Definition textspan.h:54
unsigned int flags
Definition textspan.h:58
double size
Definition textspan.h:57
pointf pos
Definition types.h:114
char * fontcolor
Definition types.h:107
char * text
Definition types.h:105
bool set
Definition types.h:123
pointf dimen
Definition types.h:110
double yoffset_layout
Definition textspan.h:69
char * str
Definition textspan.h:65
char just
'l' 'n' 'r'
Definition textspan.h:71
pointf size
Definition textspan.h:70
textfont_t * font
Definition textspan.h:66
const char * start
Beginning of the token content.
Definition emit.c:3681
int type
Token category.
Definition emit.c:3680
size_t size
Number of bytes in the token content.
Definition emit.c:3682
xdot_grad_type type
Definition xdot.h:69
xdot_linear_grad ling
Definition xdot.h:72
xdot_radial_grad ring
Definition xdot.h:73
union xdot_color::@125 u
double size
Definition xdot.h:107
char * name
Definition xdot.h:108
double x
Definition xdot.h:82
double y
Definition xdot.h:82
size_t cnt
Definition xdot.h:90
xdot_point * pts
Definition xdot.h:91
double x
Definition xdot.h:86
double w
Definition xdot.h:86
double y
Definition xdot.h:86
double h
Definition xdot.h:86
size_t n_polygon_pts
Definition xdot.h:167
size_t cnt
Definition xdot.h:164
size_t n_text
Definition xdot.h:172
size_t n_polyline_pts
Definition xdot.h:169
size_t n_bezier
Definition xdot.h:170
size_t n_polygon
Definition xdot.h:166
size_t n_polyline
Definition xdot.h:168
size_t n_ellipse
Definition xdot.h:165
size_t n_bezier_pts
Definition xdot.h:171
char * text
Definition xdot.h:98
double x
Definition xdot.h:95
xdot_align align
Definition xdot.h:96
double y
Definition xdot.h:95
Definition xdot.h:155
double elapsed_sec(void)
Definition timing.c:48
void start_timer(void)
Definition timing.c:43
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
#define ag_xget(x, a)
Definition types.h:606
#define GUI_STATE_ACTIVE
Definition types.h:256
@ SH_POINT
Definition types.h:187
@ SH_POLY
Definition types.h:187
#define GUI_STATE_SELECTED
Definition types.h:257
#define GUI_STATE_DELETED
Definition types.h:259
#define GUI_STATE_VISITED
Definition types.h:258
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30
int(* pf)(void *, char *,...)
Definition xdot.c:405
int statXDot(xdot *x, xdot_stats *sp)
Definition xdot.c:799
xdot * parseXDotF(char *s, drawfunc_t fns[], size_t sz)
Definition xdot.c:396
parsing and deparsing of xdot operations
@ xd_radial
Definition xdot.h:46
void(* freefunc_t)(xdot_op *)
Definition xdot.h:133
@ xd_filled_polygon
Definition xdot.h:113
@ xd_pen_color
Definition xdot.h:116
@ xd_unfilled_ellipse
Definition xdot.h:112
@ xd_fontchar
Definition xdot.h:118
@ xd_font
Definition xdot.h:116
@ xd_fill_color
Definition xdot.h:116
@ xd_unfilled_bezier
Definition xdot.h:114
@ xd_grad_fill_color
Definition xdot.h:117
@ xd_polyline
Definition xdot.h:115
@ xd_text
Definition xdot.h:115
@ xd_filled_ellipse
Definition xdot.h:112
@ xd_image
Definition xdot.h:116
@ xd_unfilled_polygon
Definition xdot.h:113
@ xd_grad_pen_color
Definition xdot.h:117
@ xd_filled_bezier
Definition xdot.h:114
@ xd_style
Definition xdot.h:116