Graphviz 14.1.3~dev.20260207.0611
Loading...
Searching...
No Matches
gvrender_core_json.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2015 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <stdarg.h>
14#include <stdbool.h>
15#include <stdlib.h>
16#include <string.h>
17#include <ctype.h>
18
19#include <common/macros.h>
20#include <common/const.h>
21#include <xdot/xdot.h>
22
23#include <gvc/gvplugin_render.h>
24#include <gvc/gvplugin_device.h>
25#include <common/utils.h>
26#include <gvc/gvc.h>
27#include <gvc/gvio.h>
28#include <gvc/gvcint.h>
29#include <util/alloc.h>
30#include <util/startswith.h>
31#include <util/streq.h>
32#include <util/unreachable.h>
33
34enum {
39};
40
41typedef struct {
42 int Level;
43 bool isLatin;
44 bool doXDot;
45} state_t;
46
47typedef struct {
49 int id;
50} gvid_t;
51
52#define ID "id"
53#define ND_gid(n) (((gvid_t*)aggetrec(n, ID, 0))->id)
54#define ED_gid(n) (((gvid_t*)aggetrec(n, ID, 0))->id)
55#define GD_gid(n) (((gvid_t*)aggetrec(n, ID, 0))->id)
56
57static bool IS_CLUSTER(Agraph_t *s) {
58 return startswith(agnameof(s), "cluster");
59}
60
61static void json_begin_graph(GVJ_t *job)
62{
63 if (job->render.id == FORMAT_JSON) {
64 GVC_t* gvc = gvCloneGVC (job->gvc);
65 graph_t *g = job->obj->u.g;
66 gvRender (gvc, g, "xdot", NULL);
68 }
69 else if (job->render.id == FORMAT_JSON0) {
70 attach_attrs(job->gvc->g);
71 }
72}
73
81static void stoj(char *ins, state_t *sp, GVJ_t *job) {
82 char* s;
83 char* input;
84 char c;
85
86 if (sp->isLatin)
87 input = latin1ToUTF8 (ins);
88 else
89 input = ins;
90
91 gvputc(job, '"');
92 for (s = input; (c = *s); s++) {
93 switch (c) {
94 case '"' :
95 gvputs(job, "\\\"");
96 break;
97 case '\\' :
98 gvputs(job, "\\\\");
99 break;
100 case '/' :
101 gvputs(job, "\\/");
102 break;
103 case '\b' :
104 gvputs(job, "\\b");
105 break;
106 case '\f' :
107 gvputs(job, "\\f");
108 break;
109 case '\n' :
110 gvputs(job, "\\n");
111 break;
112 case '\r' :
113 gvputs(job, "\\r");
114 break;
115 case '\t' :
116 gvputs(job, "\\t");
117 break;
118 default :
119 gvputc(job, c);
120 break;
121 }
122 }
123 gvputc(job, '"');
124
125 if (sp->isLatin)
126 free (input);
127}
128
129static void indent(GVJ_t * job, int level)
130{
131 int i;
132 for (i = level; i > 0; i--)
133 gvputs(job, " ");
134}
135
136static void set_attrwf(Agraph_t * g, bool toplevel, bool value)
137{
138 Agraph_t *subg;
139 Agnode_t *n;
140 Agedge_t *e;
141
142 AGATTRWF(g) = value;
143 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
144 set_attrwf(subg, false, value);
145 }
146 if (toplevel) {
147 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
148 AGATTRWF(n) = value;
149 for (e = agfstout(g, n); e; e = agnxtout(g, e))
150 AGATTRWF(e) = value;
151 }
152 }
153}
154
155static void write_polyline (GVJ_t * job, xdot_polyline* polyline)
156{
157 const size_t cnt = polyline->cnt;
158 xdot_point* pts = polyline->pts;
159
160 gvprintf(job, "\"points\": [");
161 for (size_t i = 0; i < cnt; i++) {
162 if (i > 0) gvprintf(job, ",");
163 gvprintf(job, "[%.03f,%.03f]", pts[i].x, pts[i].y);
164 }
165 gvprintf(job, "]\n");
166}
167
168static void write_stops (GVJ_t * job, int n_stops, xdot_color_stop* stp, state_t* sp)
169{
170 int i;
171
172 gvprintf(job, "\"stops\": [");
173 for (i = 0; i < n_stops; i++) {
174 if (i > 0) gvprintf(job, ",");
175 gvprintf(job, "{\"frac\": %.03f, \"color\": ", stp[i].frac);
176 stoj(stp[i].color, sp, job);
177 gvputc(job, '}');
178 }
179 gvprintf(job, "]\n");
180}
181
182static void write_radial_grad (GVJ_t * job, xdot_radial_grad* rg, state_t* sp)
183{
184 indent(job, sp->Level);
185 gvprintf(job, "\"p0\": [%.03f,%.03f,%.03f],\n", rg->x0, rg->y0, rg->r0);
186 indent(job, sp->Level);
187 gvprintf(job, "\"p1\": [%.03f,%.03f,%.03f],\n", rg->x1, rg->y1, rg->r1);
188 indent(job, sp->Level);
189 write_stops (job, rg->n_stops, rg->stops, sp);
190}
191
192static void write_linear_grad (GVJ_t * job, xdot_linear_grad* lg, state_t* sp)
193{
194 indent(job, sp->Level);
195 gvprintf(job, "\"p0\": [%.03f,%.03f],\n", lg->x0, lg->y0);
196 indent(job, sp->Level);
197 gvprintf(job, "\"p1\": [%.03f,%.03f],\n", lg->x1, lg->y1);
198 indent(job, sp->Level);
199 write_stops (job, lg->n_stops, lg->stops, sp);
200}
201
202static void write_xdot (xdot_op * op, GVJ_t * job, state_t* sp)
203{
204 indent(job, sp->Level++);
205 gvputs(job, "{\n");
206 indent(job, sp->Level);
207
208 switch (op->kind) {
209 case xd_filled_ellipse :
211 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_ellipse ? 'E' : 'e');
212 indent(job, sp->Level);
213 gvprintf(job, "\"rect\": [%.03f,%.03f,%.03f,%.03f]\n",
214 op->u.ellipse.x, op->u.ellipse.y, op->u.ellipse.w, op->u.ellipse.h);
215 break;
216 case xd_filled_polygon :
218 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_polygon ? 'P' : 'p');
219 indent(job, sp->Level);
220 write_polyline (job, &op->u.polygon);
221 break;
222 case xd_filled_bezier :
223 case xd_unfilled_bezier :
224 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_bezier ? 'B' : 'b');
225 indent(job, sp->Level);
226 write_polyline (job, &op->u.bezier);
227 break;
228 case xd_polyline :
229 gvprintf(job, "\"op\": \"L\",\n");
230 indent(job, sp->Level);
231 write_polyline (job, &op->u.polyline);
232 break;
233 case xd_text :
234 gvprintf(job, "\"op\": \"T\",\n");
235 indent(job, sp->Level);
236 gvprintf(job, "\"pt\": [%.03f,%.03f],\n", op->u.text.x, op->u.text.y);
237 indent(job, sp->Level);
238 gvprintf(job, "\"align\": \"%c\",\n",
239 op->u.text.align == xd_left? 'l' :
240 (op->u.text.align == xd_center ? 'c' : 'r'));
241 indent(job, sp->Level);
242 gvprintf(job, "\"width\": %.03f,\n", op->u.text.width);
243 indent(job, sp->Level);
244 gvputs(job, "\"text\": ");
245 stoj(op->u.text.text, sp, job);
246 gvputc(job, '\n');
247 break;
248 case xd_fill_color :
249 case xd_pen_color :
250 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_fill_color ? 'C' : 'c');
251 indent(job, sp->Level);
252 gvprintf(job, "\"grad\": \"none\",\n");
253 indent(job, sp->Level);
254 gvputs(job, "\"color\": ");
255 stoj(op->u.color, sp, job);
256 gvputc(job, '\n');
257 break;
258 case xd_grad_pen_color :
259 case xd_grad_fill_color :
260 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_grad_fill_color ? 'C' : 'c');
261 indent(job, sp->Level);
262 if (op->u.grad_color.type == xd_none) {
263 gvprintf(job, "\"grad\": \"none\",\n");
264 indent(job, sp->Level);
265 gvputs(job, "\"color\": ");
266 stoj(op->u.grad_color.u.clr, sp, job);
267 gvputc(job, '\n');
268 }
269 else {
270 if (op->u.grad_color.type == xd_linear) {
271 gvprintf(job, "\"grad\": \"linear\",\n");
272 indent(job, sp->Level);
273 write_linear_grad (job, &op->u.grad_color.u.ling, sp);
274 }
275 else {
276 gvprintf(job, "\"grad\": \"radial\",\n");
277 indent(job, sp->Level);
278 write_radial_grad (job, &op->u.grad_color.u.ring, sp);
279 }
280 }
281 break;
282 case xd_font :
283 gvprintf(job, "\"op\": \"F\",\n");
284 indent(job, sp->Level);
285 gvprintf(job, "\"size\": %.03f,\n", op->u.font.size);
286 indent(job, sp->Level);
287 gvputs(job, "\"face\": ");
288 stoj(op->u.font.name, sp, job);
289 gvputc(job, '\n');
290 break;
291 case xd_style :
292 gvprintf(job, "\"op\": \"S\",\n");
293 indent(job, sp->Level);
294 gvputs(job, "\"style\": ");
295 stoj(op->u.style, sp, job);
296 gvputc(job, '\n');
297 break;
298 case xd_image :
299 break;
300 case xd_fontchar :
301 gvprintf(job, "\"op\": \"t\",\n");
302 indent(job, sp->Level);
303 gvprintf(job, "\"fontchar\": %d\n", op->u.fontchar);
304 break;
305 default:
306 UNREACHABLE();
307 }
308 sp->Level--;
309 indent(job, sp->Level);
310 gvputs(job, "}");
311}
312
313static void write_xdots (char * val, GVJ_t * job, state_t* sp)
314{
315 xdot* cmds;
316
317 if (!val || *val == '\0') return;
318
319 cmds = parseXDot(val);
320 if (!cmds) {
321 agwarningf("Could not parse xdot \"%s\"\n", val);
322 return;
323 }
324
325 gvputs(job, "\n");
326 indent(job, sp->Level++);
327 gvputs(job, "[\n");
328 for (size_t i = 0; i < cmds->cnt; i++) {
329 if (i > 0)
330 gvputs(job, ",\n");
331 write_xdot (cmds->ops+i, job, sp);
332 }
333 sp->Level--;
334 gvputs(job, "\n");
335 indent(job, sp->Level);
336 gvputs(job, "]");
337 freeXDot(cmds);
338}
339
340static bool isXDot(const char* name) {
341 return streq(name, "_draw_") || streq(name, "_ldraw_") ||
342 streq(name, "_hdraw_") || streq(name, "_tdraw_") ||
343 streq(name, "_hldraw_") || streq(name, "_tldraw_");
344}
345
346static void write_attrs(Agobj_t * obj, GVJ_t * job, state_t* sp)
347{
348 Agraph_t* g = agroot(obj);
349 int type = AGTYPE(obj);
350 char* attrval;
351 Agsym_t* sym = agnxtattr(g, type, NULL);
352 if (!sym) return;
353
354 for (; sym; sym = agnxtattr(g, type, sym)) {
355 if (!(attrval = agxget(obj, sym))) continue;
356 if (*attrval == '\0' && !streq(sym->name, "label")) continue;
357 gvputs(job, ",\n");
358 indent(job, sp->Level);
359 stoj(sym->name, sp, job);
360 gvputs(job, ": ");
361 if (sp->doXDot && isXDot(sym->name))
362 write_xdots(agxget(obj, sym), job, sp);
363 else
364 stoj(agxget(obj, sym), sp, job);
365 }
366}
367
368static void write_hdr(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
369 char *name;
370
371 name = agnameof(g);
372 indent(job, sp->Level);
373 gvputs(job, "\"name\": ");
374 stoj(name, sp, job);
375
376 if (top) {
377 gvputs(job, ",\n");
378 indent(job, sp->Level);
379 gvprintf(job, "\"directed\": %s,\n", agisdirected(g)?"true":"false");
380 indent(job, sp->Level);
381 gvprintf(job, "\"strict\": %s", agisstrict(g)?"true":"false");
382 }
383}
384
385static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp);
386
387static void write_subg(Agraph_t * g, GVJ_t * job, state_t* sp)
388{
389 Agraph_t* sg;
390
391 write_graph (g, job, false, sp);
392 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
393 gvputs(job, ",\n");
394 write_subg(sg, job, sp);
395 }
396}
397
401static bool write_subgs(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
402 Agraph_t* sg;
403
404 sg = agfstsubg(g);
405 if (!sg) return false;
406
407 gvputs(job, ",\n");
408 indent(job, sp->Level++);
409 if (top)
410 gvputs(job, "\"objects\": [\n");
411 else {
412 gvputs(job, "\"subgraphs\": [\n");
413 indent(job, sp->Level);
414 }
415 const char *separator = "";
416 for (; sg; sg = agnxtsubg(sg)) {
417 gvputs(job, separator);
418 if (top)
419 write_subg (sg, job, sp);
420 else
421 gvprintf(job, "%d", GD_gid(sg));
422 separator = ",\n";
423 }
424 if (!top) {
425 sp->Level--;
426 gvputs(job, "\n");
427 indent(job, sp->Level);
428 gvputs(job, "]");
429 }
430
431 return true;
432}
433
434static int agseqasc(const void *x, const void *y) {
435 Agedge_t *const *const lhs = x;
436 Agedge_t *const *const rhs = y;
437 Agedge_t *e1 = *lhs;
438 Agedge_t *e2 = *rhs;
439
440 if (AGSEQ(e1) < AGSEQ(e2)) {
441 return -1;
442 }
443 else if (AGSEQ(e1) > AGSEQ(e2)) {
444 return 1;
445 }
446 else {
447 return 0;
448 }
449}
450
451static void write_edge(Agedge_t *e, GVJ_t *job, bool top, state_t *sp) {
452 if (top) {
453 indent(job, sp->Level++);
454 gvputs(job, "{\n");
455 indent(job, sp->Level);
456 gvprintf(job, "\"_gvid\": %d,\n", ED_gid(e));
457 indent(job, sp->Level);
458 gvprintf(job, "\"tail\": %d,\n", ND_gid(agtail(e)));
459 indent(job, sp->Level);
460 gvprintf(job, "\"head\": %d", ND_gid(aghead(e)));
461 write_attrs(&e->base, job, sp);
462 gvputs(job, "\n");
463 sp->Level--;
464 indent(job, sp->Level);
465 gvputs(job, "}");
466 }
467 else {
468 gvprintf(job, "%d", ED_gid(e));
469 }
470}
471
472static int write_edges(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
473 size_t count = 0;
474
475 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
476 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
477 ++count;
478 }
479 }
480
481 if (count == 0) {
482 return 0;
483 }
484
485 Agedge_t **edges = gv_calloc(count, sizeof(Agedge_t *));
486
487 size_t i = 0;
488 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
489 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
490 edges[i] = ep;
491 ++i;
492 }
493 }
494
495 qsort(edges, count, sizeof(Agedge_t *), agseqasc);
496
497 gvputs(job, ",\n");
498 indent(job, sp->Level++);
499 gvputs(job, "\"edges\": [\n");
500 if (!top)
501 indent(job, sp->Level);
502 for (size_t j = 0; j < count; ++j) {
503 if (j > 0) {
504 if (top)
505 gvputs(job, ",\n");
506 else
507 gvputs(job, ",");
508 }
509 write_edge(edges[j], job, top, sp);
510 }
511
512 free(edges);
513
514 sp->Level--;
515 gvputs(job, "\n");
516 indent(job, sp->Level);
517 gvputs(job, "]");
518 return 1;
519}
520
521static void write_node(Agnode_t *n, GVJ_t *job, bool top, state_t *sp) {
522 if (top) {
523 indent(job, sp->Level++);
524 gvputs(job, "{\n");
525 indent(job, sp->Level);
526 gvprintf(job, "\"_gvid\": %d,\n", ND_gid(n));
527 indent(job, sp->Level);
528 gvputs(job, "\"name\": ");
529 stoj(agnameof(n), sp, job);
530 write_attrs(&n->base, job, sp);
531 gvputs(job, "\n");
532 sp->Level--;
533 indent(job, sp->Level);
534 gvputs(job, "}");
535 }
536 else {
537 gvprintf(job, "%d", ND_gid(n));
538 }
539}
540
541static int write_nodes(Agraph_t *g, GVJ_t *job, bool top, bool has_subgs, state_t *sp) {
542
543 // is every subcomponent of this graph a cluster?
544 bool only_clusters = true;
545 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
546 if (!IS_CLUST_NODE(n)) {
547 only_clusters = false;
548 break;
549 }
550 }
551
552 if (only_clusters) {
553 if (has_subgs && top) {
554 sp->Level--;
555 gvputs(job, "\n");
556 indent(job, sp->Level);
557 gvputs(job, "]");
558 }
559 return 0;
560 }
561 gvputs(job, ",\n");
562 if (top) {
563 if (!has_subgs) {
564 indent(job, sp->Level++);
565 gvputs(job, "\"objects\": [\n");
566 }
567 }
568 else {
569 indent(job, sp->Level++);
570 gvputs(job, "\"nodes\": [\n");
571 indent(job, sp->Level);
572 }
573 const char *separator = "";
574 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
575 if (IS_CLUST_NODE(n)) continue;
576 gvputs(job, separator);
577 write_node (n, job, top, sp);
578 separator = top ? ",\n" : ",";
579 }
580 sp->Level--;
581 gvputs(job, "\n");
582 indent(job, sp->Level);
583 gvputs(job, "]");
584 return 1;
585}
586
587typedef struct {
589 char* id;
590 int v;
591} intm;
592
593static void freef(void *ident) {
594 intm *obj = ident;
595 free(obj->id);
596 free(obj);
597}
598
600 offsetof(intm, id),
601 -1,
602 offsetof(intm, link),
603 NULL,
604 freef,
605 NULL,
606};
607
608static int lookup (Dt_t* map, char* name)
609{
610 intm* ip = dtmatch(map, name);
611 if (ip) return ip->v;
612 else return -1;
613}
614
615static void insert (Dt_t* map, char* name, int v)
616{
617 intm* ip = dtmatch(map, name);
618
619 if (ip) {
620 if (ip->v != v)
621 agwarningf("Duplicate cluster name \"%s\"\n", name);
622 return;
623 }
624 ip = gv_alloc(sizeof(intm));
625 ip->id = gv_strdup(name);
626 ip->v = v;
627 dtinsert (map, ip);
628}
629
630static int label_subgs(Agraph_t* g, int lbl, Dt_t* map)
631{
632 Agraph_t* sg;
633
634 if (g != agroot(g)) {
635 GD_gid(g) = lbl++;
636 if (IS_CLUSTER(g))
637 insert (map, agnameof(g), GD_gid(g));
638 }
639 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
640 lbl = label_subgs(sg, lbl, map);
641 }
642 return lbl;
643}
644
645
646static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
647 Agnode_t* np;
648 Agedge_t* ep;
649 int ncnt = 0;
650 int ecnt = 0;
651 int sgcnt = 0;
652 Dt_t* map;
653
654 if (top) {
655 map = dtopen (&intDisc, Dtoset);
656 aginit(g, AGNODE, ID, sizeof(gvid_t), false);
657 aginit(g, AGEDGE, ID, sizeof(gvid_t), false);
658 aginit(g, AGRAPH, ID, -((int)sizeof(gvid_t)), false);
659 sgcnt = label_subgs(g, sgcnt, map);
660 for (np = agfstnode(g); np; np = agnxtnode(g,np)) {
661 if (IS_CLUST_NODE(np)) {
662 ND_gid(np) = lookup(map, agnameof(np));
663 }
664 else {
665 ND_gid(np) = sgcnt + ncnt++;
666 }
667 for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) {
668 ED_gid(ep) = ecnt++;
669 }
670 }
671 dtclose(map);
672 }
673
674 indent(job, sp->Level++);
675 gvputs(job, "{\n");
676 write_hdr(g, job, top, sp);
677 write_attrs(&g->base, job, sp);
678 if (top) {
679 gvputs(job, ",\n");
680 indent(job, sp->Level);
681 gvprintf(job, "\"_subgraph_cnt\": %d", sgcnt);
682 } else {
683 gvputs(job, ",\n");
684 indent(job, sp->Level);
685 gvprintf(job, "\"_gvid\": %d", GD_gid(g));
686 }
687 bool has_subgs = write_subgs(g, job, top, sp);
688 write_nodes (g, job, top, has_subgs, sp);
689 write_edges (g, job, top, sp);
690 gvputs(job, "\n");
691 sp->Level--;
692 indent(job, sp->Level);
693 if (top)
694 gvputs(job, "}\n");
695 else
696 gvputs(job, "}");
697}
698
699typedef int (*putstrfn) (void *chan, const char *str);
700typedef int (*flushfn) (void *chan);
701
702static void json_end_graph(GVJ_t *job)
703{
704 graph_t *g = job->obj->u.g;
705 state_t sp;
706 static Agiodisc_t io;
707
708 if (io.afread == NULL) {
710 io.putstr = (putstrfn)gvputs;
711 io.flush = (flushfn)gvflush;
712 }
713
714 g->clos->disc.io = &io;
715
716 set_attrwf(g, true, false);
717 sp.Level = 0;
718 sp.isLatin = GD_charset(g) == CHAR_LATIN1;
719 sp.doXDot = job->render.id == FORMAT_JSON || job->render.id == FORMAT_XDOT_JSON;
720 write_graph(g, job, true, &sp);
721}
722
724 0, /* json_begin_job */
725 0, /* json_end_job */
728 0, /* json_begin_layer */
729 0, /* json_end_layer */
730 0, /* json_begin_page */
731 0, /* json_end_page */
732 0, /* json_begin_cluster */
733 0, /* json_end_cluster */
734 0, /* json_begin_nodes */
735 0, /* json_end_nodes */
736 0, /* json_begin_edges */
737 0, /* json_end_edges */
738 0, /* json_begin_node */
739 0, /* json_end_node */
740 0, /* json_begin_edge */
741 0, /* json_end_edge */
742 0, /* json_begin_anchor */
743 0, /* json_end_anchor */
744 0, /* json_begin_label */
745 0, /* json_end_label */
746 0, /* json_textspan */
747 0, /* json_resolve_color */
748 0, /* json_ellipse */
749 0, /* json_polygon */
750 0, /* json_bezier */
751 0, /* json_polyline */
752 0, /* json_comment */
753 0, /* json_library_shape */
754};
755
757 GVRENDER_DOES_TRANSFORM /* not really - uses raw graph coords */
760 | GVRENDER_DOES_TOOLTIPS, /* flags */
761 0., /* default pad - graph units */
762 NULL, /* knowncolors */
763 0, /* sizeof knowncolors */
764 COLOR_STRING, /* color_type */
765};
766
768 LAYOUT_NOT_REQUIRED, /* flags */
769 {0.,0.}, /* default margin - points */
770 {0.,0.}, /* default page width, height - points */
771 {72.,72.}, /* default dpi */
772};
773
775 0, /* flags */
776 {0.,0.}, /* default margin - points */
777 {0.,0.}, /* default page width, height - points */
778 {72.,72.}, /* default dpi */
779};
780
788
790 {FORMAT_JSON, "json:json", 1, NULL, &device_features_json},
791 {FORMAT_JSON0, "json0:json", 1, NULL, &device_features_json},
792 {FORMAT_DOT_JSON, "dot_json:json", 1, NULL, &device_features_json_nop},
793 {FORMAT_XDOT_JSON, "xdot_json:json", 1, NULL, &device_features_json_nop},
794 {0, NULL, 0, NULL, NULL}
795};
Memory allocation wrappers that exit on failure.
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 dtmatch(d, o)
Definition cdt.h:185
#define dtinsert(d, o)
Definition cdt.h:186
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:10
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:306
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:11
@ COLOR_STRING
Definition color.h:27
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1259
#define CHAR_LATIN1
Definition const.h:188
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:151
expr procedure type
Definition exparse.y:208
void free(void *)
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:198
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:365
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:460
Agiodisc_t AgIoDisc
Definition io.c:41
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:977
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
void agwarningf(const char *fmt,...)
Definition agerror.c:175
int agisdirected(Agraph_t *g)
Definition graph.c:178
int agisstrict(Agraph_t *g)
Definition graph.c:188
#define GD_charset(g)
Definition types.h:367
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
#define AGATTRWF(obj)
Definition cgraph.h:226
Agraph_t * agroot(void *obj)
Definition obj.c:170
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void aginit(Agraph_t *g, int kind, const char *rec_name, int rec_size, int move_to_front)
attach new records to objects of specified kind
Definition rec.c:172
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:75
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:80
int gvRender(GVC_t *gvc, graph_t *g, const char *format, FILE *out)
Definition gvc.c:84
void attach_attrs(graph_t *g)
Definition output.c:416
static GVC_t * gvc
Definition gv.cpp:27
Graphviz context library.
GVCINT_API void gvFreeCloneGVC(GVC_t *)
Definition gvcontext.c:111
GVCINT_API GVC_t * gvCloneGVC(GVC_t *)
Definition gvcontext.c:99
#define GVRENDER_DOES_TOOLTIPS
Definition gvcjob.h:103
#define LAYOUT_NOT_REQUIRED
Definition gvcjob.h:107
#define GVRENDER_DOES_MAPS
Definition gvcjob.h:97
#define GVRENDER_DOES_TARGETS
Definition gvcjob.h:104
#define GVRENDER_DOES_TRANSFORM
Definition gvcjob.h:95
static void color(Agraph_t *g)
Definition gvcolor.c:118
int gvputc(GVJ_t *job, int c)
Definition gvdevice.c:298
int gvflush(GVJ_t *job)
Definition gvdevice.c:308
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:266
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:402
static void indent(GVJ_t *job, int level)
int(* putstrfn)(void *chan, const char *str)
static int lookup(Dt_t *map, char *name)
static void write_radial_grad(GVJ_t *job, xdot_radial_grad *rg, state_t *sp)
static void write_xdot(xdot_op *op, GVJ_t *job, state_t *sp)
static void write_linear_grad(GVJ_t *job, xdot_linear_grad *lg, state_t *sp)
static bool IS_CLUSTER(Agraph_t *s)
static void write_subg(Agraph_t *g, GVJ_t *job, state_t *sp)
static void write_attrs(Agobj_t *obj, GVJ_t *job, state_t *sp)
@ FORMAT_JSON0
@ FORMAT_JSON
@ FORMAT_DOT_JSON
@ FORMAT_XDOT_JSON
static gvrender_engine_t json_engine
static int write_edges(Agraph_t *g, GVJ_t *job, bool top, state_t *sp)
static void write_edge(Agedge_t *e, GVJ_t *job, bool top, state_t *sp)
#define ED_gid(n)
static bool write_subgs(Agraph_t *g, GVJ_t *job, bool top, state_t *sp)
static int write_nodes(Agraph_t *g, GVJ_t *job, bool top, bool has_subgs, state_t *sp)
static gvdevice_features_t device_features_json_nop
static void set_attrwf(Agraph_t *g, bool toplevel, bool value)
static void write_polyline(GVJ_t *job, xdot_polyline *polyline)
gvplugin_installed_t gvdevice_json_types[]
static int label_subgs(Agraph_t *g, int lbl, Dt_t *map)
static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp)
#define ID
static void write_hdr(Agraph_t *g, GVJ_t *job, bool top, state_t *sp)
static Dtdisc_t intDisc
static void freef(void *ident)
int(* flushfn)(void *chan)
static void stoj(char *ins, state_t *sp, GVJ_t *job)
gvplugin_installed_t gvrender_json_types[]
static gvdevice_features_t device_features_json
#define GD_gid(n)
static gvrender_features_t render_features_json
static void write_xdots(char *val, GVJ_t *job, state_t *sp)
static int agseqasc(const void *x, const void *y)
static void write_node(Agnode_t *n, GVJ_t *job, bool top, state_t *sp)
static void json_end_graph(GVJ_t *job)
static void insert(Dt_t *map, char *name, int v)
static void write_stops(GVJ_t *job, int n_stops, xdot_color_stop *stp, state_t *sp)
#define ND_gid(n)
static bool isXDot(const char *name)
static void json_begin_graph(GVJ_t *job)
textitem scanner parser str
Definition htmlparse.y:218
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:75
#define IS_CLUST_NODE(n)
Definition macros.h:23
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
Agdisc_t disc
Definition cgraph.h:411
Agiodisc_t * io
Definition cgraph.h:338
Agobj_t base
Definition cgraph.h:269
IO services.
Definition cgraph.h:326
int(* afread)(void *chan, char *buf, int bufsize)
Definition cgraph.h:327
int(* flush)(void *chan)
Definition cgraph.h:329
int(* putstr)(void *chan, const char *str)
Definition cgraph.h:328
Agobj_t base
Definition cgraph.h:260
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:424
Agobj_t base
Definition cgraph.h:425
Agclos_t * clos
shared resources
Definition cgraph.h:434
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:640
char * name
Definition cgraph.h:642
Definition gvcint.h:81
graph_t * g
Definition gvcint.h:118
obj_state_t * obj
Definition gvcjob.h:269
gvplugin_active_render_t render
Definition gvcjob.h:285
GVC_t * gvc
Definition gvcjob.h:263
char * style
Definition xdot.h:158
union _xdot_op::@106 u
xdot_font font
Definition xdot.h:157
xdot_polyline polygon
Definition xdot.h:150
xdot_rect ellipse
Definition xdot.h:149
char * color
Definition xdot.h:155
unsigned int fontchar
Definition xdot.h:159
xdot_kind kind
Definition xdot.h:147
xdot_text text
Definition xdot.h:153
xdot_polyline bezier
Definition xdot.h:152
xdot_polyline polyline
Definition xdot.h:151
xdot_color grad_color
Definition xdot.h:156
Definition cdt.h:98
ingroup plugin_api
Definition gvplugin.h:35
Dtlink_t link
graph_t * g
Definition gvcjob.h:186
union obj_state_s::@66 u
information the ID allocator needs to do its job
Definition id.c:29
xdot_grad_type type
Definition xdot.h:68
xdot_linear_grad ling
Definition xdot.h:71
xdot_radial_grad ring
Definition xdot.h:72
char * clr
Definition xdot.h:70
union xdot_color::@105 u
double size
Definition xdot.h:104
char * name
Definition xdot.h:105
double y1
Definition xdot.h:55
xdot_color_stop * stops
Definition xdot.h:57
double y0
Definition xdot.h:54
double x0
Definition xdot.h:54
double x1
Definition xdot.h:55
size_t cnt
Definition xdot.h:87
xdot_point * pts
Definition xdot.h:88
double r0
Definition xdot.h:61
double x0
Definition xdot.h:61
double y0
Definition xdot.h:61
double x1
Definition xdot.h:62
double y1
Definition xdot.h:62
double r1
Definition xdot.h:62
xdot_color_stop * stops
Definition xdot.h:64
double x
Definition xdot.h:83
double w
Definition xdot.h:83
double y
Definition xdot.h:83
double h
Definition xdot.h:83
double width
Definition xdot.h:94
char * text
Definition xdot.h:95
double x
Definition xdot.h:92
xdot_align align
Definition xdot.h:93
double y
Definition xdot.h:92
Definition xdot.h:166
xdot_op * ops
Definition xdot.h:169
size_t cnt
Definition xdot.h:167
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30
void freeXDot(xdot *x)
Definition xdot.c:762
xdot * parseXDot(char *s)
Definition xdot.c:396
parsing and deparsing of xdot operations
@ xd_none
Definition xdot.h:46
@ xd_linear
Definition xdot.h:46
@ xd_left
Definition xdot.h:76
@ xd_center
Definition xdot.h:76
@ xd_filled_polygon
Definition xdot.h:111
@ xd_pen_color
Definition xdot.h:118
@ xd_unfilled_ellipse
Definition xdot.h:110
@ xd_fontchar
Definition xdot.h:124
@ xd_font
Definition xdot.h:119
@ xd_fill_color
Definition xdot.h:117
@ xd_unfilled_bezier
Definition xdot.h:114
@ xd_grad_fill_color
Definition xdot.h:122
@ xd_polyline
Definition xdot.h:115
@ xd_text
Definition xdot.h:116
@ xd_filled_ellipse
Definition xdot.h:109
@ xd_image
Definition xdot.h:121
@ xd_unfilled_polygon
Definition xdot.h:112
@ xd_grad_pen_color
Definition xdot.h:123
@ xd_filled_bezier
Definition xdot.h:113
@ xd_style
Definition xdot.h:120