Graphviz 13.0.0~dev.20241220.2304
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
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// Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
436// as the later usage is const. We need the cast because the macros use
437// non-const pointers for genericity.
438#ifdef __GNUC__
439#pragma GCC diagnostic push
440#pragma GCC diagnostic ignored "-Wcast-qual"
441#endif
442 Agedge_t **lhs = (Agedge_t **)x;
443 Agedge_t **rhs = (Agedge_t **)y;
444#ifdef __GNUC__
445#pragma GCC diagnostic pop
446#endif
447 Agedge_t *e1 = *lhs;
448 Agedge_t *e2 = *rhs;
449
450 if (AGSEQ(e1) < AGSEQ(e2)) {
451 return -1;
452 }
453 else if (AGSEQ(e1) > AGSEQ(e2)) {
454 return 1;
455 }
456 else {
457 return 0;
458 }
459}
460
461static void write_edge(Agedge_t *e, GVJ_t *job, bool top, state_t *sp) {
462 if (top) {
463 indent(job, sp->Level++);
464 gvputs(job, "{\n");
465 indent(job, sp->Level);
466 gvprintf(job, "\"_gvid\": %d,\n", ED_gid(e));
467 indent(job, sp->Level);
468 gvprintf(job, "\"tail\": %d,\n", ND_gid(agtail(e)));
469 indent(job, sp->Level);
470 gvprintf(job, "\"head\": %d", ND_gid(aghead(e)));
471 write_attrs((Agobj_t*)e, job, sp);
472 gvputs(job, "\n");
473 sp->Level--;
474 indent(job, sp->Level);
475 gvputs(job, "}");
476 }
477 else {
478 gvprintf(job, "%d", ED_gid(e));
479 }
480}
481
482static int write_edges(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
483 size_t count = 0;
484
485 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
486 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
487 ++count;
488 }
489 }
490
491 if (count == 0) {
492 return 0;
493 }
494
495 Agedge_t **edges = gv_calloc(count, sizeof(Agedge_t *));
496
497 size_t i = 0;
498 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
499 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
500 edges[i] = ep;
501 ++i;
502 }
503 }
504
505 qsort(edges, count, sizeof(Agedge_t *), agseqasc);
506
507 gvputs(job, ",\n");
508 indent(job, sp->Level++);
509 gvputs(job, "\"edges\": [\n");
510 if (!top)
511 indent(job, sp->Level);
512 for (size_t j = 0; j < count; ++j) {
513 if (j > 0) {
514 if (top)
515 gvputs(job, ",\n");
516 else
517 gvputs(job, ",");
518 }
519 write_edge(edges[j], job, top, sp);
520 }
521
522 free(edges);
523
524 sp->Level--;
525 gvputs(job, "\n");
526 indent(job, sp->Level);
527 gvputs(job, "]");
528 return 1;
529}
530
531static void write_node(Agnode_t *n, GVJ_t *job, bool top, state_t *sp) {
532 if (top) {
533 indent(job, sp->Level++);
534 gvputs(job, "{\n");
535 indent(job, sp->Level);
536 gvprintf(job, "\"_gvid\": %d,\n", ND_gid(n));
537 indent(job, sp->Level);
538 gvputs(job, "\"name\": ");
539 stoj(agnameof(n), sp, job);
540 write_attrs((Agobj_t*)n, job, sp);
541 gvputs(job, "\n");
542 sp->Level--;
543 indent(job, sp->Level);
544 gvputs(job, "}");
545 }
546 else {
547 gvprintf(job, "%d", ND_gid(n));
548 }
549}
550
551static int write_nodes(Agraph_t *g, GVJ_t *job, bool top, bool has_subgs, state_t *sp) {
552
553 // is every subcomponent of this graph a cluster?
554 bool only_clusters = true;
555 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
556 if (!IS_CLUST_NODE(n)) {
557 only_clusters = false;
558 break;
559 }
560 }
561
562 if (only_clusters) {
563 if (has_subgs && top) {
564 sp->Level--;
565 gvputs(job, "\n");
566 indent(job, sp->Level);
567 gvputs(job, "]");
568 }
569 return 0;
570 }
571 gvputs(job, ",\n");
572 if (top) {
573 if (!has_subgs) {
574 indent(job, sp->Level++);
575 gvputs(job, "\"objects\": [\n");
576 }
577 }
578 else {
579 indent(job, sp->Level++);
580 gvputs(job, "\"nodes\": [\n");
581 indent(job, sp->Level);
582 }
583 const char *separator = "";
584 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
585 if (IS_CLUST_NODE(n)) continue;
586 gvputs(job, separator);
587 write_node (n, job, top, sp);
588 separator = top ? ",\n" : ",";
589 }
590 sp->Level--;
591 gvputs(job, "\n");
592 indent(job, sp->Level);
593 gvputs(job, "]");
594 return 1;
595}
596
597typedef struct {
599 char* id;
600 int v;
601} intm;
602
603static void freef(void *ident) {
604 intm *obj = ident;
605 free(obj->id);
606 free(obj);
607}
608
610 offsetof(intm, id),
611 -1,
612 offsetof(intm, link),
613 NULL,
614 freef,
615 NULL,
616};
617
618static int lookup (Dt_t* map, char* name)
619{
620 intm* ip = dtmatch(map, name);
621 if (ip) return ip->v;
622 else return -1;
623}
624
625static void insert (Dt_t* map, char* name, int v)
626{
627 intm* ip = dtmatch(map, name);
628
629 if (ip) {
630 if (ip->v != v)
631 agwarningf("Duplicate cluster name \"%s\"\n", name);
632 return;
633 }
634 ip = gv_alloc(sizeof(intm));
635 ip->id = gv_strdup(name);
636 ip->v = v;
637 dtinsert (map, ip);
638}
639
640static int label_subgs(Agraph_t* g, int lbl, Dt_t* map)
641{
642 Agraph_t* sg;
643
644 if (g != agroot(g)) {
645 GD_gid(g) = lbl++;
646 if (IS_CLUSTER(g))
647 insert (map, agnameof(g), GD_gid(g));
648 }
649 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
650 lbl = label_subgs(sg, lbl, map);
651 }
652 return lbl;
653}
654
655
656static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
657 Agnode_t* np;
658 Agedge_t* ep;
659 int ncnt = 0;
660 int ecnt = 0;
661 int sgcnt = 0;
662 Dt_t* map;
663
664 if (top) {
665 map = dtopen (&intDisc, Dtoset);
666 aginit(g, AGNODE, ID, sizeof(gvid_t), false);
667 aginit(g, AGEDGE, ID, sizeof(gvid_t), false);
668 aginit(g, AGRAPH, ID, -((int)sizeof(gvid_t)), false);
669 sgcnt = label_subgs(g, sgcnt, map);
670 for (np = agfstnode(g); np; np = agnxtnode(g,np)) {
671 if (IS_CLUST_NODE(np)) {
672 ND_gid(np) = lookup(map, agnameof(np));
673 }
674 else {
675 ND_gid(np) = sgcnt + ncnt++;
676 }
677 for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) {
678 ED_gid(ep) = ecnt++;
679 }
680 }
681 dtclose(map);
682 }
683
684 indent(job, sp->Level++);
685 gvputs(job, "{\n");
686 write_hdr(g, job, top, sp);
687 write_attrs((Agobj_t*)g, job, sp);
688 if (top) {
689 gvputs(job, ",\n");
690 indent(job, sp->Level);
691 gvprintf(job, "\"_subgraph_cnt\": %d", sgcnt);
692 } else {
693 gvputs(job, ",\n");
694 indent(job, sp->Level);
695 gvprintf(job, "\"_gvid\": %d", GD_gid(g));
696 }
697 bool has_subgs = write_subgs(g, job, top, sp);
698 write_nodes (g, job, top, has_subgs, sp);
699 write_edges (g, job, top, sp);
700 gvputs(job, "\n");
701 sp->Level--;
702 indent(job, sp->Level);
703 if (top)
704 gvputs(job, "}\n");
705 else
706 gvputs(job, "}");
707}
708
709typedef int (*putstrfn) (void *chan, const char *str);
710typedef int (*flushfn) (void *chan);
711
712static void json_end_graph(GVJ_t *job)
713{
714 graph_t *g = job->obj->u.g;
715 state_t sp;
716 static Agiodisc_t io;
717
718 if (io.afread == NULL) {
720 io.putstr = (putstrfn)gvputs;
721 io.flush = (flushfn)gvflush;
722 }
723
724 g->clos->disc.io = &io;
725
726 set_attrwf(g, true, false);
727 sp.Level = 0;
728 sp.isLatin = GD_charset(g) == CHAR_LATIN1;
729 sp.doXDot = job->render.id == FORMAT_JSON || job->render.id == FORMAT_XDOT_JSON;
730 write_graph(g, job, true, &sp);
731}
732
734 0, /* json_begin_job */
735 0, /* json_end_job */
738 0, /* json_begin_layer */
739 0, /* json_end_layer */
740 0, /* json_begin_page */
741 0, /* json_end_page */
742 0, /* json_begin_cluster */
743 0, /* json_end_cluster */
744 0, /* json_begin_nodes */
745 0, /* json_end_nodes */
746 0, /* json_begin_edges */
747 0, /* json_end_edges */
748 0, /* json_begin_node */
749 0, /* json_end_node */
750 0, /* json_begin_edge */
751 0, /* json_end_edge */
752 0, /* json_begin_anchor */
753 0, /* json_end_anchor */
754 0, /* json_begin_label */
755 0, /* json_end_label */
756 0, /* json_textspan */
757 0, /* json_resolve_color */
758 0, /* json_ellipse */
759 0, /* json_polygon */
760 0, /* json_bezier */
761 0, /* json_polyline */
762 0, /* json_comment */
763 0, /* json_library_shape */
764};
765
767 GVRENDER_DOES_TRANSFORM, /* not really - uses raw graph coords */ /* flags */
768 0., /* default pad - graph units */
769 NULL, /* knowncolors */
770 0, /* sizeof knowncolors */
771 COLOR_STRING, /* color_type */
772};
773
775 GVRENDER_DOES_TRANSFORM /* not really - uses raw graph coords */
778 | GVRENDER_DOES_TOOLTIPS, /* flags */
779 0., /* default pad - graph units */
780 NULL, /* knowncolors */
781 0, /* sizeof knowncolors */
782 COLOR_STRING, /* color_type */
783};
784
786 LAYOUT_NOT_REQUIRED, /* flags */
787 {0.,0.}, /* default margin - points */
788 {0.,0.}, /* default page width, height - points */
789 {72.,72.}, /* default dpi */
790};
791
793 0, /* flags */
794 {0.,0.}, /* default margin - points */
795 {0.,0.}, /* default page width, height - points */
796 {72.,72.}, /* default dpi */
797};
798
806
808 {FORMAT_JSON, "json:json", 1, NULL, &device_features_json},
809 {FORMAT_JSON0, "json0:json", 1, NULL, &device_features_json},
810 {FORMAT_DOT_JSON, "dot_json:json", 1, NULL, &device_features_json_nop},
811 {FORMAT_XDOT_JSON, "xdot_json:json", 1, NULL, &device_features_json_nop},
812 {0, NULL, 0, NULL, NULL}
813};
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:184
#define dtinsert(d, o)
Definition cdt.h:185
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
@ COLOR_STRING
Definition color.h:27
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1271
#define CHAR_LATIN1
Definition const.h:197
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:150
expr procedure type
Definition exparse.y:208
void free(void *)
node NULL
Definition grammar.y:163
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:210
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:353
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:455
Agiodisc_t AgIoDisc
Definition io.c:39
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
#define agtail(e)
Definition cgraph.h:880
#define aghead(e)
Definition cgraph.h:881
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
void agwarningf(const char *fmt,...)
Definition agerror.c:173
int agisdirected(Agraph_t *g)
Definition graph.c:190
int agisstrict(Agraph_t *g)
Definition graph.c:200
#define GD_charset(g)
Definition types.h:367
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
#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:168
#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:170
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:78
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:83
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:394
static GVC_t * gvc
Definition gv.cpp:23
Graphviz context library.
GVCINT_API void gvFreeCloneGVC(GVC_t *)
Definition gvcontext.c:110
GVCINT_API GVC_t * gvCloneGVC(GVC_t *)
Definition gvcontext.c:98
#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:129
int gvputc(GVJ_t *job, int c)
Definition gvdevice.c:291
int gvflush(GVJ_t *job)
Definition gvdevice.c:301
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:264
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:395
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)
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)
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[]
gvrender_features_t render_features_json1
@ FORMAT_JSON0
@ FORMAT_JSON
@ FORMAT_DOT_JSON
@ FORMAT_XDOT_JSON
gvdevice_features_t device_features_json
#define GD_gid(n)
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:224
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
#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:412
Agiodisc_t * io
Definition cgraph.h:339
IO services.
Definition cgraph.h:327
int(* afread)(void *chan, char *buf, int bufsize)
Definition cgraph.h:328
int(* flush)(void *chan)
Definition cgraph.h:330
int(* putstr)(void *chan, const char *str)
Definition cgraph.h:329
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:425
Agclos_t * clos
shared resources
Definition cgraph.h:435
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:637
char * name
Definition cgraph.h:639
Definition gvcint.h:80
graph_t * g
Definition gvcint.h:117
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: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
union _xdot_op::@125 u
xdot_color grad_color
Definition xdot.h:145
Definition cdt.h:100
ingroup plugin_api
Definition gvplugin.h:35
Dtlink_t link
graph_t * g
Definition gvcjob.h:186
union obj_state_s::@89 u
information the ID allocator needs to do its job
Definition id.c:27
xdot_grad_type type
Definition xdot.h:69
xdot_linear_grad ling
Definition xdot.h:72
union xdot_color::@124 u
xdot_radial_grad ring
Definition xdot.h:73
char * clr
Definition xdot.h:71
double size
Definition xdot.h:107
char * name
Definition xdot.h:108
double y1
Definition xdot.h:56
xdot_color_stop * stops
Definition xdot.h:58
double y0
Definition xdot.h:55
double x0
Definition xdot.h:55
double x1
Definition xdot.h:56
size_t cnt
Definition xdot.h:90
xdot_point * pts
Definition xdot.h:91
double r0
Definition xdot.h:62
double x0
Definition xdot.h:62
double y0
Definition xdot.h:62
double x1
Definition xdot.h:63
double y1
Definition xdot.h:63
double r1
Definition xdot.h:63
xdot_color_stop * stops
Definition xdot.h:65
double x
Definition xdot.h:86
double w
Definition xdot.h:86
double y
Definition xdot.h:86
double h
Definition xdot.h:86
double width
Definition xdot.h:97
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
xdot_op * ops
Definition xdot.h:158
size_t cnt
Definition xdot.h:156
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30
void freeXDot(xdot *x)
Definition xdot.c:782
xdot * parseXDot(char *s)
Definition xdot.c:400
parsing and deparsing of xdot operations
@ xd_none
Definition xdot.h:44
@ xd_linear
Definition xdot.h:45
@ xd_left
Definition xdot.h:78
@ xd_center
Definition xdot.h:78
@ 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