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 | |
39 | typedef 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 | */ |
56 | static agxbuf xbuf[NUMXBUFS]; |
57 | static 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 | }; |
62 | static double penwidth [] = { |
63 | 1, 1, 1, 1, |
64 | 1, 1, 1, 1, |
65 | 1, 1, 1, 1, |
66 | }; |
67 | static unsigned int textflags[EMIT_ELABEL+1]; |
68 | |
69 | typedef 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; |
84 | static xdot_state_t* xd; |
85 | |
86 | static 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 | |
96 | static 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 | */ |
106 | static 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 | */ |
134 | static 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 | |
145 | static 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 | |
154 | static void xdot_num(agxbuf *xbuf, double v) |
155 | { |
156 | char buf[BUFSIZ]; |
157 | xdot_fmt_num (buf, v); |
158 | agxbput(xbuf, buf); |
159 | } |
160 | |
161 | static 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 | |
174 | static char* |
175 | color2str (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 | |
186 | static void xdot_pencolor (GVJ_t *job) |
187 | { |
188 | xdot_str (job, "c " , color2str (job->obj->pencolor.u.rgba)); |
189 | } |
190 | |
191 | static void xdot_fillcolor (GVJ_t *job) |
192 | { |
193 | xdot_str (job, "C " , color2str (job->obj->fillcolor.u.rgba)); |
194 | } |
195 | |
196 | static 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 | |
248 | static 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 | |
261 | static 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 | */ |
296 | static 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 | |
319 | static 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 | |
327 | static 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 | |
340 | static unsigned short |
341 | versionStr2Version (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 | */ |
385 | static void |
386 | xdot_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 | |
448 | static 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 | |
474 | static 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 | |
496 | typedef int (*putstrfn) (void *chan, const char *str); |
497 | typedef int (*flushfn) (void *chan); |
498 | static 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 | |
535 | static unsigned int flag_masks[] = { 0x1F, 0x3F, 0x7F }; |
536 | |
537 | static 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 | |
586 | static 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 | |
595 | static 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 | |
657 | static 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 | |
682 | static 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 | |
698 | static 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 | |
714 | static 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 | |
721 | void 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 | |
735 | gvrender_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 | |
768 | gvrender_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 | |
806 | gvrender_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 | |
814 | gvrender_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 | |
825 | gvdevice_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 | |
832 | gvdevice_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 | |
839 | gvplugin_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 | |
845 | gvplugin_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 | |