Graphviz 12.0.1~dev.20240715.2254
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 <cgraph/alloc.h>
26#include <cgraph/startswith.h>
27#include <cgraph/streq.h>
28#include <cgraph/unreachable.h>
29#include <common/utils.h>
30#include <gvc/gvc.h>
31#include <gvc/gvio.h>
32#include <gvc/gvcint.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
74#define LOCALNAMEPREFIX '%'
75
83static void stoj(char *ins, state_t *sp, GVJ_t *job) {
84 char* s;
85 char* input;
86 char c;
87
88 if (sp->isLatin)
89 input = latin1ToUTF8 (ins);
90 else
91 input = ins;
92
93 gvputc(job, '"');
94 for (s = input; (c = *s); s++) {
95 switch (c) {
96 case '"' :
97 gvputs(job, "\\\"");
98 break;
99 case '\\' :
100 gvputs(job, "\\\\");
101 break;
102 case '/' :
103 gvputs(job, "\\/");
104 break;
105 case '\b' :
106 gvputs(job, "\\b");
107 break;
108 case '\f' :
109 gvputs(job, "\\f");
110 break;
111 case '\n' :
112 gvputs(job, "\\n");
113 break;
114 case '\r' :
115 gvputs(job, "\\r");
116 break;
117 case '\t' :
118 gvputs(job, "\\t");
119 break;
120 default :
121 gvputc(job, c);
122 break;
123 }
124 }
125 gvputc(job, '"');
126
127 if (sp->isLatin)
128 free (input);
129}
130
131static void indent(GVJ_t * job, int level)
132{
133 int i;
134 for (i = level; i > 0; i--)
135 gvputs(job, " ");
136}
137
138static void set_attrwf(Agraph_t * g, bool toplevel, bool value)
139{
140 Agraph_t *subg;
141 Agnode_t *n;
142 Agedge_t *e;
143
144 AGATTRWF(g) = value;
145 for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
146 set_attrwf(subg, false, value);
147 }
148 if (toplevel) {
149 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
150 AGATTRWF(n) = value;
151 for (e = agfstout(g, n); e; e = agnxtout(g, e))
152 AGATTRWF(e) = value;
153 }
154 }
155}
156
157static void write_polyline (GVJ_t * job, xdot_polyline* polyline)
158{
159 const size_t cnt = polyline->cnt;
160 xdot_point* pts = polyline->pts;
161
162 gvprintf(job, "\"points\": [");
163 for (size_t i = 0; i < cnt; i++) {
164 if (i > 0) gvprintf(job, ",");
165 gvprintf(job, "[%.03f,%.03f]", pts[i].x, pts[i].y);
166 }
167 gvprintf(job, "]\n");
168}
169
170static void write_stops (GVJ_t * job, int n_stops, xdot_color_stop* stp, state_t* sp)
171{
172 int i;
173
174 gvprintf(job, "\"stops\": [");
175 for (i = 0; i < n_stops; i++) {
176 if (i > 0) gvprintf(job, ",");
177 gvprintf(job, "{\"frac\": %.03f, \"color\": ", stp[i].frac);
178 stoj(stp[i].color, sp, job);
179 gvputc(job, '}');
180 }
181 gvprintf(job, "]\n");
182}
183
184static void write_radial_grad (GVJ_t * job, xdot_radial_grad* rg, state_t* sp)
185{
186 indent(job, sp->Level);
187 gvprintf(job, "\"p0\": [%.03f,%.03f,%.03f],\n", rg->x0, rg->y0, rg->r0);
188 indent(job, sp->Level);
189 gvprintf(job, "\"p1\": [%.03f,%.03f,%.03f],\n", rg->x1, rg->y1, rg->r1);
190 indent(job, sp->Level);
191 write_stops (job, rg->n_stops, rg->stops, sp);
192}
193
194static void write_linear_grad (GVJ_t * job, xdot_linear_grad* lg, state_t* sp)
195{
196 indent(job, sp->Level);
197 gvprintf(job, "\"p0\": [%.03f,%.03f],\n", lg->x0, lg->y0);
198 indent(job, sp->Level);
199 gvprintf(job, "\"p1\": [%.03f,%.03f],\n", lg->x1, lg->y1);
200 indent(job, sp->Level);
201 write_stops (job, lg->n_stops, lg->stops, sp);
202}
203
204static void write_xdot (xdot_op * op, GVJ_t * job, state_t* sp)
205{
206 indent(job, sp->Level++);
207 gvputs(job, "{\n");
208 indent(job, sp->Level);
209
210 switch (op->kind) {
211 case xd_filled_ellipse :
213 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_ellipse ? 'E' : 'e');
214 indent(job, sp->Level);
215 gvprintf(job, "\"rect\": [%.03f,%.03f,%.03f,%.03f]\n",
216 op->u.ellipse.x, op->u.ellipse.y, op->u.ellipse.w, op->u.ellipse.h);
217 break;
218 case xd_filled_polygon :
220 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_polygon ? 'P' : 'p');
221 indent(job, sp->Level);
222 write_polyline (job, &op->u.polygon);
223 break;
224 case xd_filled_bezier :
225 case xd_unfilled_bezier :
226 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_filled_bezier ? 'B' : 'b');
227 indent(job, sp->Level);
228 write_polyline (job, &op->u.bezier);
229 break;
230 case xd_polyline :
231 gvprintf(job, "\"op\": \"L\",\n");
232 indent(job, sp->Level);
233 write_polyline (job, &op->u.polyline);
234 break;
235 case xd_text :
236 gvprintf(job, "\"op\": \"T\",\n");
237 indent(job, sp->Level);
238 gvprintf(job, "\"pt\": [%.03f,%.03f],\n", op->u.text.x, op->u.text.y);
239 indent(job, sp->Level);
240 gvprintf(job, "\"align\": \"%c\",\n",
241 op->u.text.align == xd_left? 'l' :
242 (op->u.text.align == xd_center ? 'c' : 'r'));
243 indent(job, sp->Level);
244 gvprintf(job, "\"width\": %.03f,\n", op->u.text.width);
245 indent(job, sp->Level);
246 gvputs(job, "\"text\": ");
247 stoj(op->u.text.text, sp, job);
248 gvputc(job, '\n');
249 break;
250 case xd_fill_color :
251 case xd_pen_color :
252 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_fill_color ? 'C' : 'c');
253 indent(job, sp->Level);
254 gvprintf(job, "\"grad\": \"none\",\n");
255 indent(job, sp->Level);
256 gvputs(job, "\"color\": ");
257 stoj(op->u.color, sp, job);
258 gvputc(job, '\n');
259 break;
260 case xd_grad_pen_color :
261 case xd_grad_fill_color :
262 gvprintf(job, "\"op\": \"%c\",\n", op->kind == xd_grad_fill_color ? 'C' : 'c');
263 indent(job, sp->Level);
264 if (op->u.grad_color.type == xd_none) {
265 gvprintf(job, "\"grad\": \"none\",\n");
266 indent(job, sp->Level);
267 gvputs(job, "\"color\": ");
268 stoj(op->u.grad_color.u.clr, sp, job);
269 gvputc(job, '\n');
270 }
271 else {
272 if (op->u.grad_color.type == xd_linear) {
273 gvprintf(job, "\"grad\": \"linear\",\n");
274 indent(job, sp->Level);
275 write_linear_grad (job, &op->u.grad_color.u.ling, sp);
276 }
277 else {
278 gvprintf(job, "\"grad\": \"radial\",\n");
279 indent(job, sp->Level);
280 write_radial_grad (job, &op->u.grad_color.u.ring, sp);
281 }
282 }
283 break;
284 case xd_font :
285 gvprintf(job, "\"op\": \"F\",\n");
286 indent(job, sp->Level);
287 gvprintf(job, "\"size\": %.03f,\n", op->u.font.size);
288 indent(job, sp->Level);
289 gvputs(job, "\"face\": ");
290 stoj(op->u.font.name, sp, job);
291 gvputc(job, '\n');
292 break;
293 case xd_style :
294 gvprintf(job, "\"op\": \"S\",\n");
295 indent(job, sp->Level);
296 gvputs(job, "\"style\": ");
297 stoj(op->u.style, sp, job);
298 gvputc(job, '\n');
299 break;
300 case xd_image :
301 break;
302 case xd_fontchar :
303 gvprintf(job, "\"op\": \"t\",\n");
304 indent(job, sp->Level);
305 gvprintf(job, "\"fontchar\": %d\n", op->u.fontchar);
306 break;
307 default:
308 UNREACHABLE();
309 }
310 sp->Level--;
311 indent(job, sp->Level);
312 gvputs(job, "}");
313}
314
315static void write_xdots (char * val, GVJ_t * job, state_t* sp)
316{
317 xdot* cmds;
318
319 if (!val || *val == '\0') return;
320
321 cmds = parseXDot(val);
322 if (!cmds) {
323 agwarningf("Could not parse xdot \"%s\"\n", val);
324 return;
325 }
326
327 gvputs(job, "\n");
328 indent(job, sp->Level++);
329 gvputs(job, "[\n");
330 for (size_t i = 0; i < cmds->cnt; i++) {
331 if (i > 0)
332 gvputs(job, ",\n");
333 write_xdot (cmds->ops+i, job, sp);
334 }
335 sp->Level--;
336 gvputs(job, "\n");
337 indent(job, sp->Level);
338 gvputs(job, "]");
339 freeXDot(cmds);
340}
341
342static bool isXDot(const char* name) {
343 return streq(name, "_draw_") || streq(name, "_ldraw_") ||
344 streq(name, "_hdraw_") || streq(name, "_tdraw_") ||
345 streq(name, "_hldraw_") || streq(name, "_tldraw_");
346}
347
348static void write_attrs(Agobj_t * obj, GVJ_t * job, state_t* sp)
349{
350 Agraph_t* g = agroot(obj);
351 int type = AGTYPE(obj);
352 char* attrval;
353 Agsym_t* sym = agnxtattr(g, type, NULL);
354 if (!sym) return;
355
356 for (; sym; sym = agnxtattr(g, type, sym)) {
357 if (!(attrval = agxget(obj, sym))) continue;
358 if (*attrval == '\0' && !streq(sym->name, "label")) continue;
359 gvputs(job, ",\n");
360 indent(job, sp->Level);
361 stoj(sym->name, sp, job);
362 gvputs(job, ": ");
363 if (sp->doXDot && isXDot(sym->name))
364 write_xdots(agxget(obj, sym), job, sp);
365 else
366 stoj(agxget(obj, sym), sp, job);
367 }
368}
369
370static void write_hdr(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
371 char *name;
372
373 name = agnameof(g);
374 indent(job, sp->Level);
375 gvputs(job, "\"name\": ");
376 stoj(name, sp, job);
377
378 if (top) {
379 gvputs(job, ",\n");
380 indent(job, sp->Level);
381 gvprintf(job, "\"directed\": %s,\n", agisdirected(g)?"true":"false");
382 indent(job, sp->Level);
383 gvprintf(job, "\"strict\": %s", agisstrict(g)?"true":"false");
384 }
385}
386
387static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp);
388
389static void write_subg(Agraph_t * g, GVJ_t * job, state_t* sp)
390{
391 Agraph_t* sg;
392
393 write_graph (g, job, false, sp);
394 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
395 gvputs(job, ",\n");
396 write_subg(sg, job, sp);
397 }
398}
399
403static bool write_subgs(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
404 Agraph_t* sg;
405
406 sg = agfstsubg(g);
407 if (!sg) return false;
408
409 gvputs(job, ",\n");
410 indent(job, sp->Level++);
411 if (top)
412 gvputs(job, "\"objects\": [\n");
413 else {
414 gvputs(job, "\"subgraphs\": [\n");
415 indent(job, sp->Level);
416 }
417 const char *separator = "";
418 for (; sg; sg = agnxtsubg(sg)) {
419 gvputs(job, separator);
420 if (top)
421 write_subg (sg, job, sp);
422 else
423 gvprintf(job, "%d", GD_gid(sg));
424 separator = ",\n";
425 }
426 if (!top) {
427 sp->Level--;
428 gvputs(job, "\n");
429 indent(job, sp->Level);
430 gvputs(job, "]");
431 }
432
433 return true;
434}
435
436static int agseqasc(const void *x, const void *y) {
437// Suppress Clang/GCC -Wcast-qual warning. Casting away const here is acceptable
438// as the later usage is const. We need the cast because the macros use
439// non-const pointers for genericity.
440#ifdef __GNUC__
441#pragma GCC diagnostic push
442#pragma GCC diagnostic ignored "-Wcast-qual"
443#endif
444 Agedge_t **lhs = (Agedge_t **)x;
445 Agedge_t **rhs = (Agedge_t **)y;
446#ifdef __GNUC__
447#pragma GCC diagnostic pop
448#endif
449 Agedge_t *e1 = *lhs;
450 Agedge_t *e2 = *rhs;
451
452 if (AGSEQ(e1) < AGSEQ(e2)) {
453 return -1;
454 }
455 else if (AGSEQ(e1) > AGSEQ(e2)) {
456 return 1;
457 }
458 else {
459 return 0;
460 }
461}
462
463static void write_edge(Agedge_t *e, GVJ_t *job, bool top, state_t *sp) {
464 if (top) {
465 indent(job, sp->Level++);
466 gvputs(job, "{\n");
467 indent(job, sp->Level);
468 gvprintf(job, "\"_gvid\": %d,\n", ED_gid(e));
469 indent(job, sp->Level);
470 gvprintf(job, "\"tail\": %d,\n", ND_gid(agtail(e)));
471 indent(job, sp->Level);
472 gvprintf(job, "\"head\": %d", ND_gid(aghead(e)));
473 write_attrs((Agobj_t*)e, job, sp);
474 gvputs(job, "\n");
475 sp->Level--;
476 indent(job, sp->Level);
477 gvputs(job, "}");
478 }
479 else {
480 gvprintf(job, "%d", ED_gid(e));
481 }
482}
483
484static int write_edges(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
485 size_t count = 0;
486
487 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
488 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
489 ++count;
490 }
491 }
492
493 if (count == 0) {
494 return 0;
495 }
496
497 Agedge_t **edges = gv_calloc(count, sizeof(Agedge_t *));
498
499 size_t i = 0;
500 for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) {
501 for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) {
502 edges[i] = ep;
503 ++i;
504 }
505 }
506
507 qsort(edges, count, sizeof(Agedge_t *), agseqasc);
508
509 gvputs(job, ",\n");
510 indent(job, sp->Level++);
511 gvputs(job, "\"edges\": [\n");
512 if (!top)
513 indent(job, sp->Level);
514 for (size_t j = 0; j < count; ++j) {
515 if (j > 0) {
516 if (top)
517 gvputs(job, ",\n");
518 else
519 gvputs(job, ",");
520 }
521 write_edge(edges[j], job, top, sp);
522 }
523
524 free(edges);
525
526 sp->Level--;
527 gvputs(job, "\n");
528 indent(job, sp->Level);
529 gvputs(job, "]");
530 return 1;
531}
532
533static void write_node(Agnode_t *n, GVJ_t *job, bool top, state_t *sp) {
534 if (top) {
535 indent(job, sp->Level++);
536 gvputs(job, "{\n");
537 indent(job, sp->Level);
538 gvprintf(job, "\"_gvid\": %d,\n", ND_gid(n));
539 indent(job, sp->Level);
540 gvputs(job, "\"name\": ");
541 stoj(agnameof(n), sp, job);
542 write_attrs((Agobj_t*)n, job, sp);
543 gvputs(job, "\n");
544 sp->Level--;
545 indent(job, sp->Level);
546 gvputs(job, "}");
547 }
548 else {
549 gvprintf(job, "%d", ND_gid(n));
550 }
551}
552
553static int write_nodes(Agraph_t *g, GVJ_t *job, bool top, bool has_subgs, state_t *sp) {
554
555 // is every subcomponent of this graph a cluster?
556 bool only_clusters = true;
557 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
558 if (!IS_CLUST_NODE(n)) {
559 only_clusters = false;
560 break;
561 }
562 }
563
564 if (only_clusters) {
565 if (has_subgs && top) {
566 sp->Level--;
567 gvputs(job, "\n");
568 indent(job, sp->Level);
569 gvputs(job, "]");
570 }
571 return 0;
572 }
573 gvputs(job, ",\n");
574 if (top) {
575 if (!has_subgs) {
576 indent(job, sp->Level++);
577 gvputs(job, "\"objects\": [\n");
578 }
579 }
580 else {
581 indent(job, sp->Level++);
582 gvputs(job, "\"nodes\": [\n");
583 indent(job, sp->Level);
584 }
585 const char *separator = "";
586 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
587 if (IS_CLUST_NODE(n)) continue;
588 gvputs(job, separator);
589 write_node (n, job, top, sp);
590 separator = top ? ",\n" : ",";
591 }
592 sp->Level--;
593 gvputs(job, "\n");
594 indent(job, sp->Level);
595 gvputs(job, "]");
596 return 1;
597}
598
599typedef struct {
601 char* id;
602 int v;
603} intm;
604
605static void freef(void *ident) {
606 intm *obj = ident;
607 free(obj->id);
608 free(obj);
609}
610
612 offsetof(intm, id),
613 -1,
614 offsetof(intm, link),
615 (Dtmake_f) NULL,
616 freef,
618};
619
620static int lookup (Dt_t* map, char* name)
621{
622 intm* ip = dtmatch(map, name);
623 if (ip) return ip->v;
624 else return -1;
625}
626
627static void insert (Dt_t* map, char* name, int v)
628{
629 intm* ip = dtmatch(map, name);
630
631 if (ip) {
632 if (ip->v != v)
633 agwarningf("Duplicate cluster name \"%s\"\n", name);
634 return;
635 }
636 ip = gv_alloc(sizeof(intm));
637 ip->id = gv_strdup(name);
638 ip->v = v;
639 dtinsert (map, ip);
640}
641
642static int label_subgs(Agraph_t* g, int lbl, Dt_t* map)
643{
644 Agraph_t* sg;
645
646 if (g != agroot(g)) {
647 GD_gid(g) = lbl++;
648 if (IS_CLUSTER(g))
649 insert (map, agnameof(g), GD_gid(g));
650 }
651 for (sg = agfstsubg(g); sg; sg = agnxtsubg(sg)) {
652 lbl = label_subgs(sg, lbl, map);
653 }
654 return lbl;
655}
656
657
658static void write_graph(Agraph_t *g, GVJ_t *job, bool top, state_t *sp) {
659 Agnode_t* np;
660 Agedge_t* ep;
661 int ncnt = 0;
662 int ecnt = 0;
663 int sgcnt = 0;
664 Dt_t* map;
665
666 if (top) {
667 map = dtopen (&intDisc, Dtoset);
668 aginit(g, AGNODE, ID, sizeof(gvid_t), false);
669 aginit(g, AGEDGE, ID, sizeof(gvid_t), false);
670 aginit(g, AGRAPH, ID, -((int)sizeof(gvid_t)), false);
671 sgcnt = label_subgs(g, sgcnt, map);
672 for (np = agfstnode(g); np; np = agnxtnode(g,np)) {
673 if (IS_CLUST_NODE(np)) {
674 ND_gid(np) = lookup(map, agnameof(np));
675 }
676 else {
677 ND_gid(np) = sgcnt + ncnt++;
678 }
679 for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) {
680 ED_gid(ep) = ecnt++;
681 }
682 }
683 dtclose(map);
684 }
685
686 indent(job, sp->Level++);
687 gvputs(job, "{\n");
688 write_hdr(g, job, top, sp);
689 write_attrs((Agobj_t*)g, job, sp);
690 if (top) {
691 gvputs(job, ",\n");
692 indent(job, sp->Level);
693 gvprintf(job, "\"_subgraph_cnt\": %d", sgcnt);
694 } else {
695 gvputs(job, ",\n");
696 indent(job, sp->Level);
697 gvprintf(job, "\"_gvid\": %d", GD_gid(g));
698 }
699 bool has_subgs = write_subgs(g, job, top, sp);
700 write_nodes (g, job, top, has_subgs, sp);
701 write_edges (g, job, top, sp);
702 gvputs(job, "\n");
703 sp->Level--;
704 indent(job, sp->Level);
705 if (top)
706 gvputs(job, "}\n");
707 else
708 gvputs(job, "}");
709}
710
711typedef int (*putstrfn) (void *chan, const char *str);
712typedef int (*flushfn) (void *chan);
713
714static void json_end_graph(GVJ_t *job)
715{
716 graph_t *g = job->obj->u.g;
717 state_t sp;
718 static Agiodisc_t io;
719
720 if (io.afread == NULL) {
722 io.putstr = (putstrfn)gvputs;
723 io.flush = (flushfn)gvflush;
724 }
725
726 g->clos->disc.io = &io;
727
728 set_attrwf(g, true, false);
729 sp.Level = 0;
730 sp.isLatin = GD_charset(g) == CHAR_LATIN1;
731 sp.doXDot = job->render.id == FORMAT_JSON || job->render.id == FORMAT_XDOT_JSON;
732 write_graph(g, job, true, &sp);
733}
734
736 0, /* json_begin_job */
737 0, /* json_end_job */
740 0, /* json_begin_layer */
741 0, /* json_end_layer */
742 0, /* json_begin_page */
743 0, /* json_end_page */
744 0, /* json_begin_cluster */
745 0, /* json_end_cluster */
746 0, /* json_begin_nodes */
747 0, /* json_end_nodes */
748 0, /* json_begin_edges */
749 0, /* json_end_edges */
750 0, /* json_begin_node */
751 0, /* json_end_node */
752 0, /* json_begin_edge */
753 0, /* json_end_edge */
754 0, /* json_begin_anchor */
755 0, /* json_end_anchor */
756 0, /* json_begin_label */
757 0, /* json_end_label */
758 0, /* json_textspan */
759 0, /* json_resolve_color */
760 0, /* json_ellipse */
761 0, /* json_polygon */
762 0, /* json_bezier */
763 0, /* json_polyline */
764 0, /* json_comment */
765 0, /* json_library_shape */
766};
767
769 GVRENDER_DOES_TRANSFORM, /* not really - uses raw graph coords */ /* flags */
770 0., /* default pad - graph units */
771 NULL, /* knowncolors */
772 0, /* sizeof knowncolors */
773 COLOR_STRING, /* color_type */
774};
775
777 GVRENDER_DOES_TRANSFORM /* not really - uses raw graph coords */
780 | GVRENDER_DOES_TOOLTIPS, /* flags */
781 0., /* default pad - graph units */
782 NULL, /* knowncolors */
783 0, /* sizeof knowncolors */
784 COLOR_STRING, /* color_type */
785};
786
788 LAYOUT_NOT_REQUIRED, /* flags */
789 {0.,0.}, /* default margin - points */
790 {0.,0.}, /* default page width, height - points */
791 {72.,72.}, /* default dpi */
792};
793
795 0, /* flags */
796 {0.,0.}, /* default margin - points */
797 {0.,0.}, /* default page width, height - points */
798 {72.,72.}, /* default dpi */
799};
800
808
810 {FORMAT_JSON, "json:json", 1, NULL, &device_features_json},
811 {FORMAT_JSON0, "json0:json", 1, NULL, &device_features_json},
812 {FORMAT_DOT_JSON, "dot_json:json", 1, NULL, &device_features_json_nop},
813 {FORMAT_XDOT_JSON, "xdot_json:json", 1, NULL, &device_features_json_nop},
814 {0, NULL, 0, NULL, NULL}
815};
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
void *(* Dtmake_f)(void *, Dtdisc_t *)
Definition cdt.h:50
int(* Dtcompar_f)(void *, void *)
Definition cdt.h:52
#define dtmatch(d, o)
Definition cdt.h:192
#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
@ COLOR_STRING
Definition color.h:27
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1278
#define CHAR_LATIN1
Definition const.h:197
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:149
expr procedure type
Definition exparse.y:211
void free(void *)
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:356
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
Agiodisc_t AgIoDisc
Definition io.c:39
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define agtail(e)
Definition cgraph.h:889
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
void agwarningf(const char *fmt,...)
Definition agerror.c:173
int agisdirected(Agraph_t *g)
Definition graph.c:179
int agisstrict(Agraph_t *g)
Definition graph.c:189
#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:167
#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:169
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:77
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:82
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:383
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:128
int gvputc(GVJ_t *job, int c)
Definition gvdevice.c:290
int gvflush(GVJ_t *job)
Definition gvdevice.c:300
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:263
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:394
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)
GVC_t * gvc
Definition htmlparse.c:99
agxbuf * str
Definition htmlparse.c:97
htmllabel_t * lbl
Definition htmlparse.c:93
static Agedge_t * top(gv_stack_t *sp)
Definition tred.c:71
#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:639
char * name
Definition cgraph.h:641
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
Definition cdt.h:104
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
ingroup plugin_api
Definition gvplugin.h:35
Dtlink_t link
union obj_state_s::@92 u
graph_t * g
Definition gvcjob.h:186
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
xdot_radial_grad ring
Definition xdot.h:73
union xdot_color::@125 u
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