1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "config.h"
15
16#ifdef _WIN32
17#include <io.h>
18#include "compat.h"
19#endif
20
21#include <stdarg.h>
22#include <stdlib.h>
23#include <string.h>
24#include <ctype.h>
25
26#include "macros.h"
27#include "const.h"
28
29#include "gvplugin_render.h"
30#include "gvplugin_device.h"
31#include "agxbuf.h"
32#include "utils.h"
33#include "gvio.h"
34
35#define GNEW(t) (t*)malloc(sizeof(t))
36
37/* #define NEW_XDOT */
38
39typedef enum {
40 FORMAT_DOT,
41 FORMAT_CANON,
42 FORMAT_PLAIN,
43 FORMAT_PLAIN_EXT,
44 FORMAT_XDOT,
45 FORMAT_XDOT12,
46 FORMAT_XDOT14,
47} format_type;
48
49#define XDOTVERSION "1.7"
50
51#define NUMXBUFS (EMIT_HLABEL+1)
52/* There are as many xbufs as there are values of emit_state_t.
53 * However, only the first NUMXBUFS are distinct. Nodes, clusters, and
54 * edges are drawn atomically, so they share the DRAW and LABEL buffers
55 */
56static agxbuf xbuf[NUMXBUFS];
57static agxbuf* xbufs[] = {
58 xbuf+EMIT_GDRAW, xbuf+EMIT_CDRAW, xbuf+EMIT_TDRAW, xbuf+EMIT_HDRAW,
59 xbuf+EMIT_GLABEL, xbuf+EMIT_CLABEL, xbuf+EMIT_TLABEL, xbuf+EMIT_HLABEL,
60 xbuf+EMIT_CDRAW, xbuf+EMIT_CDRAW, xbuf+EMIT_CLABEL, xbuf+EMIT_CLABEL,
61};
62static double penwidth [] = {
63 1, 1, 1, 1,
64 1, 1, 1, 1,
65 1, 1, 1, 1,
66};
67static unsigned int textflags[EMIT_ELABEL+1];
68
69typedef struct {
70 attrsym_t *g_draw;
71 attrsym_t *g_l_draw;
72 attrsym_t *n_draw;
73 attrsym_t *n_l_draw;
74 attrsym_t *e_draw;
75 attrsym_t *h_draw;
76 attrsym_t *t_draw;
77 attrsym_t *e_l_draw;
78 attrsym_t *hl_draw;
79 attrsym_t *tl_draw;
80 unsigned char buf[NUMXBUFS][BUFSIZ];
81 unsigned short version;
82 char* version_s;
83} xdot_state_t;
84static xdot_state_t* xd;
85
86static void xdot_str_xbuf (agxbuf* xb, char* pfx, char* s)
87{
88 char buf[BUFSIZ];
89
90 sprintf (buf, "%s%d -", pfx, (int)strlen(s));
91 agxbput(xb, buf);
92 agxbput(xb, s);
93 agxbputc(xb, ' ');
94}
95
96static void xdot_str (GVJ_t *job, char* pfx, char* s)
97{
98 emit_state_t emit_state = job->obj->emit_state;
99 xdot_str_xbuf (xbufs[emit_state], pfx, s);
100}
101
102/* xdot_trim_zeros
103 * Trailing zeros are removed and decimal point, if possible.
104 * Add trailing space if addSpace is non-zero.
105 */
106static void xdot_trim_zeros (char* buf, int addSpace)
107{
108 char* dotp;
109 char* p;
110
111 if ((dotp = strchr (buf,'.'))) {
112 p = dotp+1;
113 while (*p) p++; // find end of string
114 p--;
115 while (*p == '0') *p-- = '\0';
116 if (*p == '.') // If all decimals were zeros, remove ".".
117 *p = '\0';
118 else
119 p++;
120 }
121 else if (addSpace)
122 p = buf + strlen(buf);
123
124 if (addSpace) { /* p points to null byte */
125 *p++ = ' ';
126 *p = '\0';
127 }
128}
129
130/* xdot_fmt_num:
131 * Convert double to string with space at end.
132 * Trailing zeros are removed and decimal point, if possible.
133 */
134static void xdot_fmt_num (char* buf, double v)
135{
136 // Prevents values like -0
137 if (v > -0.00000001 && v < 0.00000001)
138 {
139 v = 0;
140 }
141 sprintf(buf, "%.02f", v);
142 xdot_trim_zeros (buf, 1);
143}
144
145static void xdot_point(agxbuf *xbuf, pointf p)
146{
147 char buf[BUFSIZ];
148 xdot_fmt_num (buf, p.x);
149 agxbput(xbuf, buf);
150 xdot_fmt_num (buf, yDir(p.y));
151 agxbput(xbuf, buf);
152}
153
154static void xdot_num(agxbuf *xbuf, double v)
155{
156 char buf[BUFSIZ];
157 xdot_fmt_num (buf, v);
158 agxbput(xbuf, buf);
159}
160
161static void xdot_points(GVJ_t *job, char c, pointf * A, int n)
162{
163 emit_state_t emit_state = job->obj->emit_state;
164 char buf[BUFSIZ];
165 int i;
166
167 agxbputc(xbufs[emit_state], c);
168 sprintf(buf, " %d ", n);
169 agxbput(xbufs[emit_state], buf);
170 for (i = 0; i < n; i++)
171 xdot_point(xbufs[emit_state], A[i]);
172}
173
174static char*
175color2str (unsigned char rgba[4])
176{
177 static char buf [10];
178
179 if (rgba[3] == 0xFF)
180 sprintf (buf, "#%02x%02x%02x", rgba[0], rgba[1], rgba[2]);
181 else
182 sprintf (buf, "#%02x%02x%02x%02x", rgba[0], rgba[1], rgba[2], rgba[3]);
183 return buf;
184}
185
186static void xdot_pencolor (GVJ_t *job)
187{
188 xdot_str (job, "c ", color2str (job->obj->pencolor.u.rgba));
189}
190
191static void xdot_fillcolor (GVJ_t *job)
192{
193 xdot_str (job, "C ", color2str (job->obj->fillcolor.u.rgba));
194}
195
196static void xdot_style (GVJ_t *job)
197{
198 unsigned char buf0[BUFSIZ];
199 char buf [128]; /* enough to hold a double */
200 agxbuf xbuf;
201 char* p, **s;
202 int more;
203
204 agxbinit(&xbuf, BUFSIZ, buf0);
205
206 /* First, check if penwidth state is correct */
207 if (job->obj->penwidth != penwidth[job->obj->emit_state]) {
208 penwidth[job->obj->emit_state] = job->obj->penwidth;
209 agxbput (&xbuf, "setlinewidth(");
210 sprintf (buf, "%.3f", job->obj->penwidth);
211 xdot_trim_zeros (buf, 0);
212 agxbput(&xbuf, buf);
213 agxbputc (&xbuf, ')');
214 xdot_str (job, "S ", agxbuse(&xbuf));
215 }
216
217 /* now process raw style, if any */
218 s = job->obj->rawstyle;
219 if (!s)
220 return;
221
222 while ((p = *s++)) {
223 if (streq(p, "filled") || streq(p, "bold") || streq(p, "setlinewidth")) continue;
224 agxbput(&xbuf, p);
225 while (*p)
226 p++;
227 p++;
228 if (*p) { /* arguments */
229 agxbputc(&xbuf, '(');
230 more = 0;
231 while (*p) {
232 if (more)
233 agxbputc(&xbuf, ',');
234 agxbput(&xbuf, p);
235 while (*p) p++;
236 p++;
237 more++;
238 }
239 agxbputc(&xbuf, ')');
240 }
241 xdot_str (job, "S ", agxbuse(&xbuf));
242 }
243
244 agxbfree(&xbuf);
245
246}
247
248static void xdot_end_node(GVJ_t* job)
249{
250 Agnode_t* n = job->obj->u.n;
251 if (agxblen(xbufs[EMIT_NDRAW]))
252 agxset(n, xd->n_draw, agxbuse(xbufs[EMIT_NDRAW]));
253 if (agxblen(xbufs[EMIT_NLABEL]))
254 agxset(n, xd->n_l_draw, agxbuse(xbufs[EMIT_NLABEL]));
255 penwidth[EMIT_NDRAW] = 1;
256 penwidth[EMIT_NLABEL] = 1;
257 textflags[EMIT_NDRAW] = 0;
258 textflags[EMIT_NLABEL] = 0;
259}
260
261static void xdot_end_edge(GVJ_t* job)
262{
263 Agedge_t* e = job->obj->u.e;
264
265 if (agxblen(xbufs[EMIT_EDRAW]))
266 agxset(e, xd->e_draw, agxbuse(xbufs[EMIT_EDRAW]));
267 if (agxblen(xbufs[EMIT_TDRAW]))
268 agxset(e, xd->t_draw, agxbuse(xbufs[EMIT_TDRAW]));
269 if (agxblen(xbufs[EMIT_HDRAW]))
270 agxset(e, xd->h_draw, agxbuse(xbufs[EMIT_HDRAW]));
271 if (agxblen(xbufs[EMIT_ELABEL]))
272 agxset(e, xd->e_l_draw,agxbuse(xbufs[EMIT_ELABEL]));
273 if (agxblen(xbufs[EMIT_TLABEL]))
274 agxset(e, xd->tl_draw, agxbuse(xbufs[EMIT_TLABEL]));
275 if (agxblen(xbufs[EMIT_HLABEL]))
276 agxset(e, xd->hl_draw, agxbuse(xbufs[EMIT_HLABEL]));
277 penwidth[EMIT_EDRAW] = 1;
278 penwidth[EMIT_ELABEL] = 1;
279 penwidth[EMIT_TDRAW] = 1;
280 penwidth[EMIT_HDRAW] = 1;
281 penwidth[EMIT_TLABEL] = 1;
282 penwidth[EMIT_HLABEL] = 1;
283 textflags[EMIT_EDRAW] = 0;
284 textflags[EMIT_ELABEL] = 0;
285 textflags[EMIT_TDRAW] = 0;
286 textflags[EMIT_HDRAW] = 0;
287 textflags[EMIT_TLABEL] = 0;
288 textflags[EMIT_HLABEL] = 0;
289}
290
291#ifdef NEW_XDOT
292/* xdot_begin_anchor:
293 * The encoding of which fields are present assumes that one of the fields is present,
294 * so there is never a 0 after the H.
295 */
296static void xdot_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id)
297{
298 emit_state_t emit_state = job->obj->emit_state;
299 char buf[3]; /* very small integer */
300 unsigned int flags = 0;
301
302 agxbput(xbufs[emit_state], "H ");
303 if (href)
304 flags |= 1;
305 if (tooltip)
306 flags |= 2;
307 if (target)
308 flags |= 4;
309 sprintf (buf, "%d ", flags);
310 agxbput(xbufs[emit_state], buf);
311 if (href)
312 xdot_str (job, "", href);
313 if (tooltip)
314 xdot_str (job, "", tooltip);
315 if (target)
316 xdot_str (job, "", target);
317}
318
319static void xdot_end_anchor(GVJ_t * job)
320{
321 emit_state_t emit_state = job->obj->emit_state;
322
323 agxbput(xbufs[emit_state], "H 0 ");
324}
325#endif
326
327static void xdot_end_cluster(GVJ_t * job)
328{
329 Agraph_t* cluster_g = job->obj->u.sg;
330
331 agxset(cluster_g, xd->g_draw, agxbuse(xbufs[EMIT_CDRAW]));
332 if (GD_label(cluster_g))
333 agxset(cluster_g, xd->g_l_draw, agxbuse(xbufs[EMIT_CLABEL]));
334 penwidth[EMIT_CDRAW] = 1;
335 penwidth[EMIT_CLABEL] = 1;
336 textflags[EMIT_CDRAW] = 0;
337 textflags[EMIT_CLABEL] = 0;
338}
339
340static unsigned short
341versionStr2Version (char* str)
342{
343 char c, buf[BUFSIZ];
344 int n = 0;
345 char* s = str;
346 unsigned short us;
347
348 while ((c = *s++)) {
349 if (isdigit(c)) {
350 if (n < BUFSIZ-1) buf[n++] = c;
351 else {
352 agerr(AGWARN, "xdot version \"%s\" too long", str);
353 break;
354 }
355 }
356 }
357 buf[n] = '\0';
358
359 us = atoi(buf);
360 return us;
361}
362
363/*
364 * John M. suggests:
365 * You might want to add four more:
366 *
367 * _ohdraw_ (optional head-end arrow for edges)
368 * _ohldraw_ (optional head-end label for edges)
369 * _otdraw_ (optional tail-end arrow for edges)
370 * _otldraw_ (optional tail-end label for edges)
371 *
372 * that would be generated when an additional option is supplied to
373 * dot, etc. and
374 * these would be the arrow/label positions to use if a user want to flip the
375 * direction of an edge (as sometimes is there want).
376 *
377 * N.B. John M. asks:
378 * By the way, I don't know if you ever plan to add other letters for
379 * the xdot spec, but could you reserve "a" and also "A" (for attribute),
380 * "n" and also "N" (for numeric), "w" (for sWitch), "s" (for string)
381 * and "t" (for tooltip) and "x" (for position). We use those letters in
382 * our drawing spec (and also "<" and ">"), so if you start generating
383 * output with them, it could break what we have.
384 */
385static void
386xdot_begin_graph (graph_t *g, int s_arrows, int e_arrows, format_type id)
387{
388 int i, us;
389 char* s;
390
391 xd = GNEW(xdot_state_t);
392
393 if (id == FORMAT_XDOT14) {
394 xd->version = 14;
395 xd->version_s = "1.4";
396 }
397 else if (id == FORMAT_XDOT12) {
398 xd->version = 12;
399 xd->version_s = "1.2";
400 }
401 else if ((s = agget(g, "xdotversion")) && s[0] && ((us = versionStr2Version(s)) > 10)) {
402 xd->version = us;
403 xd->version_s = s;
404 }
405 else {
406 xd->version = versionStr2Version(XDOTVERSION);
407 xd->version_s = XDOTVERSION;
408 }
409
410 if (GD_n_cluster(g))
411 xd->g_draw = safe_dcl(g, AGRAPH, "_draw_", "");
412 else
413 xd->g_draw = NULL;
414 if (GD_has_labels(g) & GRAPH_LABEL)
415 xd->g_l_draw = safe_dcl(g, AGRAPH, "_ldraw_", "");
416 else
417 xd->g_l_draw = NULL;
418
419 xd->n_draw = safe_dcl(g, AGNODE, "_draw_", "");
420 xd->n_l_draw = safe_dcl(g, AGNODE, "_ldraw_", "");
421
422 xd->e_draw = safe_dcl(g, AGEDGE, "_draw_", "");
423 if (e_arrows)
424 xd->h_draw = safe_dcl(g, AGEDGE, "_hdraw_", "");
425 else
426 xd->h_draw = NULL;
427 if (s_arrows)
428 xd->t_draw = safe_dcl(g, AGEDGE, "_tdraw_", "");
429 else
430 xd->t_draw = NULL;
431 if (GD_has_labels(g) & (EDGE_LABEL|EDGE_XLABEL))
432 xd->e_l_draw = safe_dcl(g, AGEDGE, "_ldraw_", "");
433 else
434 xd->e_l_draw = NULL;
435 if (GD_has_labels(g) & HEAD_LABEL)
436 xd->hl_draw = safe_dcl(g, AGEDGE, "_hldraw_", "");
437 else
438 xd->hl_draw = NULL;
439 if (GD_has_labels(g) & TAIL_LABEL)
440 xd->tl_draw = safe_dcl(g, AGEDGE, "_tldraw_", "");
441 else
442 xd->tl_draw = NULL;
443
444 for (i = 0; i < NUMXBUFS; i++)
445 agxbinit(xbuf+i, BUFSIZ, xd->buf[i]);
446}
447
448static void dot_begin_graph(GVJ_t *job)
449{
450 int e_arrows; /* graph has edges with end arrows */
451 int s_arrows; /* graph has edges with start arrows */
452 graph_t *g = job->obj->u.g;
453
454 switch (job->render.id) {
455 case FORMAT_DOT:
456 attach_attrs(g);
457 break;
458 case FORMAT_CANON:
459 if (HAS_CLUST_EDGE(g))
460 undoClusterEdges(g);
461 break;
462 case FORMAT_PLAIN:
463 case FORMAT_PLAIN_EXT:
464 break;
465 case FORMAT_XDOT:
466 case FORMAT_XDOT12:
467 case FORMAT_XDOT14:
468 attach_attrs_and_arrows(g, &s_arrows, &e_arrows);
469 xdot_begin_graph(g, s_arrows, e_arrows, job->render.id);
470 break;
471 }
472}
473
474static void xdot_end_graph(graph_t* g)
475{
476 int i;
477
478 if (agxblen(xbufs[EMIT_GDRAW])) {
479 if (!xd->g_draw)
480 xd->g_draw = safe_dcl(g, AGRAPH, "_draw_", "");
481 agxset(g, xd->g_draw, agxbuse(xbufs[EMIT_GDRAW]));
482 }
483 if (GD_label(g))
484 agxset(g, xd->g_l_draw, agxbuse(xbufs[EMIT_GLABEL]));
485 agsafeset (g, "xdotversion", xd->version_s, "");
486
487 for (i = 0; i < NUMXBUFS; i++)
488 agxbfree(xbuf+i);
489 free (xd);
490 penwidth[EMIT_GDRAW] = 1;
491 penwidth[EMIT_GLABEL] = 1;
492 textflags[EMIT_GDRAW] = 0;
493 textflags[EMIT_GLABEL] = 0;
494}
495
496typedef int (*putstrfn) (void *chan, const char *str);
497typedef int (*flushfn) (void *chan);
498static void dot_end_graph(GVJ_t *job)
499{
500 graph_t *g = job->obj->u.g;
501 Agiodisc_t* io_save;
502 static Agiodisc_t io;
503
504 if (io.afread == NULL) {
505 io.afread = AgIoDisc.afread;
506 io.putstr = (putstrfn)gvputs;
507 io.flush = (flushfn)gvflush;
508 }
509
510 io_save = g->clos->disc.io;
511 g->clos->disc.io = &io;
512 switch (job->render.id) {
513 case FORMAT_PLAIN:
514 write_plain(job, g, (FILE*)job, FALSE);
515 break;
516 case FORMAT_PLAIN_EXT:
517 write_plain(job, g, (FILE*)job, TRUE);
518 break;
519 case FORMAT_DOT:
520 case FORMAT_CANON:
521 if (!(job->flags & OUTPUT_NOT_REQUIRED))
522 agwrite(g, (FILE*)job);
523 break;
524 case FORMAT_XDOT:
525 case FORMAT_XDOT12:
526 case FORMAT_XDOT14:
527 xdot_end_graph(g);
528 if (!(job->flags & OUTPUT_NOT_REQUIRED))
529 agwrite(g, (FILE*)job);
530 break;
531 }
532 g->clos->disc.io = io_save;
533}
534
535static unsigned int flag_masks[] = { 0x1F, 0x3F, 0x7F };
536
537static void xdot_textspan(GVJ_t * job, pointf p, textspan_t * span)
538{
539 emit_state_t emit_state = job->obj->emit_state;
540 int flags;
541 char buf[BUFSIZ];
542 int j;
543
544 agxbput(xbufs[emit_state], "F ");
545 xdot_fmt_num (buf, span->font->size);
546 agxbput(xbufs[emit_state], buf);
547 xdot_str (job, "", span->font->name);
548 xdot_pencolor(job);
549
550 switch (span->just) {
551 case 'l':
552 j = -1;
553 break;
554 case 'r':
555 j = 1;
556 break;
557 default:
558 case 'n':
559 j = 0;
560 break;
561 }
562 if (span->font)
563 flags = span->font->flags;
564 else
565 flags = 0;
566 if (xd->version >= 15) {
567 unsigned int mask = flag_masks[xd->version-15];
568 unsigned int bits = flags & mask;
569 if (textflags[emit_state] != bits) {
570 sprintf (buf, "t %u ", bits);
571 agxbput(xbufs[emit_state], buf);
572 textflags[emit_state] = bits;
573 }
574 }
575
576 p.y += span->yoffset_centerline;
577 agxbput(xbufs[emit_state], "T ");
578 xdot_point(xbufs[emit_state], p);
579 sprintf(buf, "%d ", j);
580 agxbput(xbufs[emit_state], buf);
581 xdot_fmt_num (buf, span->size.x);
582 agxbput(xbufs[emit_state], buf);
583 xdot_str (job, "", span->str);
584}
585
586static void xdot_color_stop (agxbuf* xb, float v, gvcolor_t* clr)
587{
588 char buf[BUFSIZ];
589
590 sprintf (buf, "%.03f", v);
591 xdot_trim_zeros (buf, 1);
592 xdot_str_xbuf (xb, buf, color2str (clr->u.rgba));
593}
594
595static void xdot_gradient_fillcolor (GVJ_t* job, int filled, pointf* A, int n)
596{
597 unsigned char buf0[BUFSIZ];
598 agxbuf xbuf;
599 obj_state_t* obj = job->obj;
600 float angle = obj->gradient_angle * M_PI / 180;
601 float r1,r2;
602 pointf G[2],c1,c2;
603
604 if (xd->version < 14) {
605 xdot_fillcolor (job);
606 return;
607 }
608
609 agxbinit(&xbuf, BUFSIZ, buf0);
610 if (filled == GRADIENT) {
611 get_gradient_points(A, G, n, angle, 2);
612 agxbputc (&xbuf, '[');
613 xdot_point (&xbuf, G[0]);
614 xdot_point (&xbuf, G[1]);
615 }
616 else {
617 get_gradient_points(A, G, n, 0, 3);
618 //r1 is inner radius, r2 is outer radius
619 r1 = G[1].x;
620 r2 = G[1].y;
621 if (angle == 0) {
622 c1.x = G[0].x;
623 c1.y = G[0].y;
624 }
625 else {
626 c1.x = G[0].x + (r2/4) * cos(angle);
627 c1.y = G[0].y + (r2/4) * sin(angle);
628 }
629 c2.x = G[0].x;
630 c2.y = G[0].y;
631 r1 = r2/4;
632 agxbputc(&xbuf, '(');
633 xdot_point (&xbuf, c1);
634 xdot_num (&xbuf, r1);
635 xdot_point (&xbuf, c2);
636 xdot_num (&xbuf, r2);
637 }
638
639 agxbput(&xbuf, "2 ");
640 if (obj->gradient_frac > 0) {
641 xdot_color_stop (&xbuf, obj->gradient_frac, &obj->fillcolor);
642 xdot_color_stop (&xbuf, obj->gradient_frac, &obj->stopcolor);
643 }
644 else {
645 xdot_color_stop (&xbuf, 0, &obj->fillcolor);
646 xdot_color_stop (&xbuf, 1, &obj->stopcolor);
647 }
648 agxbpop(&xbuf);
649 if (filled == GRADIENT)
650 agxbputc(&xbuf, ']');
651 else
652 agxbputc(&xbuf, ')');
653 xdot_str (job, "C ", agxbuse(&xbuf));
654 agxbfree(&xbuf);
655}
656
657static void xdot_ellipse(GVJ_t * job, pointf * A, int filled)
658{
659 emit_state_t emit_state = job->obj->emit_state;
660
661 char buf[BUFSIZ];
662
663 xdot_style (job);
664 xdot_pencolor (job);
665 if (filled) {
666 if ((filled == GRADIENT) || (filled == RGRADIENT)) {
667 xdot_gradient_fillcolor (job, filled, A, 2);
668 }
669 else
670 xdot_fillcolor (job);
671 agxbput(xbufs[emit_state], "E ");
672 }
673 else
674 agxbput(xbufs[emit_state], "e ");
675 xdot_point(xbufs[emit_state], A[0]);
676 xdot_fmt_num (buf, A[1].x - A[0].x);
677 agxbput(xbufs[emit_state], buf);
678 xdot_fmt_num (buf, A[1].y - A[0].y);
679 agxbput(xbufs[emit_state], buf);
680}
681
682static void xdot_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, int arrow_at_end, int filled)
683{
684 xdot_style (job);
685 xdot_pencolor (job);
686 if (filled) {
687 if ((filled == GRADIENT) || (filled == RGRADIENT)) {
688 xdot_gradient_fillcolor (job, filled, A, n);
689 }
690 else
691 xdot_fillcolor (job);
692 xdot_points(job, 'b', A, n); /* NB - 'B' & 'b' are reversed in comparison to the other items */
693 }
694 else
695 xdot_points(job, 'B', A, n);
696}
697
698static void xdot_polygon(GVJ_t * job, pointf * A, int n, int filled)
699{
700 xdot_style (job);
701 xdot_pencolor (job);
702 if (filled) {
703 if ((filled == GRADIENT) || (filled == RGRADIENT)) {
704 xdot_gradient_fillcolor (job, filled, A, n);
705 }
706 else
707 xdot_fillcolor (job);
708 xdot_points(job, 'P', A, n);
709 }
710 else
711 xdot_points(job, 'p', A, n);
712}
713
714static void xdot_polyline(GVJ_t * job, pointf * A, int n)
715{
716 xdot_style (job);
717 xdot_pencolor (job);
718 xdot_points(job, 'L', A, n);
719}
720
721void core_loadimage_xdot(GVJ_t * job, usershape_t *us, boxf b, boolean filled)
722{
723 emit_state_t emit_state = job->obj->emit_state;
724 char buf[BUFSIZ];
725
726 agxbput(xbufs[emit_state], "I ");
727 xdot_point(xbufs[emit_state], b.LL);
728 xdot_fmt_num (buf, b.UR.x - b.LL.x);
729 agxbput(xbufs[emit_state], buf);
730 xdot_fmt_num (buf, b.UR.y - b.LL.y);
731 agxbput(xbufs[emit_state], buf);
732 xdot_str (job, "", (char*)(us->name));
733}
734
735gvrender_engine_t dot_engine = {
736 0, /* dot_begin_job */
737 0, /* dot_end_job */
738 dot_begin_graph,
739 dot_end_graph,
740 0, /* dot_begin_layer */
741 0, /* dot_end_layer */
742 0, /* dot_begin_page */
743 0, /* dot_end_page */
744 0, /* dot_begin_cluster */
745 0, /* dot_end_cluster */
746 0, /* dot_begin_nodes */
747 0, /* dot_end_nodes */
748 0, /* dot_begin_edges */
749 0, /* dot_end_edges */
750 0, /* dot_begin_node */
751 0, /* dot_end_node */
752 0, /* dot_begin_edge */
753 0, /* dot_end_edge */
754 0, /* dot_begin_anchor */
755 0, /* dot_end_anchor */
756 0, /* dot_begin_label */
757 0, /* dot_end_label */
758 0, /* dot_textspan */
759 0, /* dot_resolve_color */
760 0, /* dot_ellipse */
761 0, /* dot_polygon */
762 0, /* dot_bezier */
763 0, /* dot_polyline */
764 0, /* dot_comment */
765 0, /* dot_library_shape */
766};
767
768gvrender_engine_t xdot_engine = {
769 0, /* xdot_begin_job */
770 0, /* xdot_end_job */
771 dot_begin_graph,
772 dot_end_graph,
773 0, /* xdot_begin_layer */
774 0, /* xdot_end_layer */
775 0, /* xdot_begin_page */
776 0, /* xdot_end_page */
777 0, /* xdot_begin_cluster */
778 xdot_end_cluster,
779 0, /* xdot_begin_nodes */
780 0, /* xdot_end_nodes */
781 0, /* xdot_begin_edges */
782 0, /* xdot_end_edges */
783 0, /* xdot_begin_node */
784 xdot_end_node,
785 0, /* xdot_begin_edge */
786 xdot_end_edge,
787#ifdef NEW_XDOT
788 xdot_begin_anchor,
789 xdot_end_anchor,
790#else
791 0, /* xdot_begin_anchor */
792 0, /* xdot_end_anchor */
793#endif
794 0, /* xdot_begin_label */
795 0, /* xdot_end_label */
796 xdot_textspan,
797 0, /* xdot_resolve_color */
798 xdot_ellipse,
799 xdot_polygon,
800 xdot_bezier,
801 xdot_polyline,
802 0, /* xdot_comment */
803 0, /* xdot_library_shape */
804};
805
806gvrender_features_t render_features_dot = {
807 GVRENDER_DOES_TRANSFORM, /* not really - uses raw graph coords */ /* flags */
808 0., /* default pad - graph units */
809 NULL, /* knowncolors */
810 0, /* sizeof knowncolors */
811 COLOR_STRING, /* color_type */
812};
813
814gvrender_features_t render_features_xdot = {
815 GVRENDER_DOES_TRANSFORM /* not really - uses raw graph coords */
816 | GVRENDER_DOES_MAPS
817 | GVRENDER_DOES_TARGETS
818 | GVRENDER_DOES_TOOLTIPS, /* flags */
819 0., /* default pad - graph units */
820 NULL, /* knowncolors */
821 0, /* sizeof knowncolors */
822 RGBA_BYTE, /* color_type */
823};
824
825gvdevice_features_t device_features_canon = {
826 LAYOUT_NOT_REQUIRED, /* flags */
827 {0.,0.}, /* default margin - points */
828 {0.,0.}, /* default height, width - device units */
829 {72.,72.}, /* default dpi */
830};
831
832gvdevice_features_t device_features_dot = {
833 0, /* flags */
834 {0.,0.}, /* default margin - points */
835 {0.,0.}, /* default page width, height - points */
836 {72.,72.}, /* default dpi */
837};
838
839gvplugin_installed_t gvrender_dot_types[] = {
840 {FORMAT_DOT, "dot", 1, &dot_engine, &render_features_dot},
841 {FORMAT_XDOT, "xdot", 1, &xdot_engine, &render_features_xdot},
842 {0, NULL, 0, NULL, NULL}
843};
844
845gvplugin_installed_t gvdevice_dot_types[] = {
846 {FORMAT_DOT, "dot:dot", 1, NULL, &device_features_dot},
847 {FORMAT_DOT, "gv:dot", 1, NULL, &device_features_dot},
848 {FORMAT_CANON, "canon:dot", 1, NULL, &device_features_canon},
849 {FORMAT_PLAIN, "plain:dot", 1, NULL, &device_features_dot},
850 {FORMAT_PLAIN_EXT, "plain-ext:dot", 1, NULL, &device_features_dot},
851 {FORMAT_XDOT, "xdot:xdot", 1, NULL, &device_features_dot},
852 {FORMAT_XDOT12, "xdot1.2:xdot", 1, NULL, &device_features_dot},
853 {FORMAT_XDOT14, "xdot1.4:xdot", 1, NULL, &device_features_dot},
854 {0, NULL, 0, NULL, NULL}
855};
856