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#include <stdarg.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include "macros.h"
21#include "const.h"
22
23#include "gvplugin_render.h"
24#include "gvplugin_device.h"
25#include "gvio.h"
26#include "gvcint.h"
27
28typedef enum { FORMAT_TK, } format_type;
29
30static char *tkgen_string(char *s)
31{
32 return s;
33}
34
35static void tkgen_print_color(GVJ_t * job, gvcolor_t color)
36{
37 switch (color.type) {
38 case COLOR_STRING:
39 gvputs(job, color.u.string);
40 break;
41 case RGBA_BYTE:
42 if (color.u.rgba[3] == 0) /* transparent */
43 gvputs(job, "\"\"");
44 else
45 gvprintf(job, "#%02x%02x%02x",
46 color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]);
47 break;
48 default:
49 assert(0); /* internal error */
50 }
51}
52
53static void tkgen_print_tags(GVJ_t *job)
54{
55 char *ObjType;
56 unsigned int ObjId;
57 obj_state_t *obj = job->obj;
58 int ObjFlag;
59
60 switch (obj->emit_state) {
61 case EMIT_NDRAW:
62 ObjType = "node";
63 ObjFlag = 1;
64 ObjId = AGID(obj->u.n);
65 break;
66 case EMIT_NLABEL:
67 ObjType = "node";
68 ObjFlag = 0;
69 ObjId = AGID(obj->u.n);
70 break;
71 case EMIT_EDRAW:
72 case EMIT_TDRAW:
73 case EMIT_HDRAW:
74 ObjType = "edge";
75 ObjFlag = 1;
76 ObjId = AGID(obj->u.e);
77 break;
78 case EMIT_ELABEL:
79 case EMIT_TLABEL:
80 case EMIT_HLABEL:
81 ObjType = "edge";
82 ObjFlag = 0;
83 ObjId = AGID(obj->u.e);
84 break;
85 case EMIT_GDRAW:
86 ObjType = "graph";
87 ObjFlag = 1;
88 ObjId = AGID(obj->u.g);
89 break;
90 case EMIT_GLABEL:
91 ObjFlag = 0;
92 ObjType = "graph label";
93 ObjId = AGID(obj->u.g);
94 break;
95 case EMIT_CDRAW:
96 ObjType = "graph";
97 ObjFlag = 1;
98 ObjId = AGID(obj->u.sg);
99 break;
100 case EMIT_CLABEL:
101 ObjType = "graph";
102 ObjFlag = 0;
103 ObjId = AGID(obj->u.sg);
104 break;
105 default:
106 assert (0);
107 break;
108 }
109 gvprintf(job, " -tags {%d%s%p}", ObjFlag, ObjType, ObjId);
110}
111
112static void tkgen_canvas(GVJ_t * job)
113{
114 if (job->external_context)
115 gvputs(job, job->imagedata);
116 else
117 gvputs(job, "$c");
118}
119
120static void tkgen_comment(GVJ_t * job, char *str)
121{
122 gvputs(job, "# ");
123 gvputs(job, tkgen_string(str));
124 gvputs(job, "\n");
125}
126
127static void tkgen_begin_job(GVJ_t * job)
128{
129 gvputs(job, "# Generated by ");
130 gvputs(job, tkgen_string(job->common->info[0]));
131 gvputs(job, " version ");
132 gvputs(job, tkgen_string(job->common->info[1]));
133 gvputs(job, " (");
134 gvputs(job, tkgen_string(job->common->info[2]));
135 gvputs(job, ")\n");
136}
137
138static int first_periphery;
139
140static void tkgen_begin_graph(GVJ_t * job)
141{
142 obj_state_t *obj = job->obj;
143
144 gvputs(job, "#");
145 if (agnameof(obj->u.g)[0]) {
146 gvputs(job, " Title: ");
147 gvputs(job, tkgen_string(agnameof(obj->u.g)));
148 }
149 gvprintf(job, " Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
150
151 first_periphery = 0;
152}
153
154static void tkgen_begin_node(GVJ_t * job)
155{
156 first_periphery = 1; /* FIXME - this is an ugly hack! */
157}
158
159static void tkgen_begin_edge(GVJ_t * job)
160{
161 first_periphery = -1; /* FIXME - this is an ugly ugly hack! Need this one for arrowheads. */
162}
163
164static void tkgen_textspan(GVJ_t * job, pointf p, textspan_t * span)
165{
166 obj_state_t *obj = job->obj;
167 const char *font;
168 PostscriptAlias *pA;
169 int size;
170
171 if (obj->pen != PEN_NONE) {
172 /* determine font size */
173 /* round fontsize down, better too small than too big */
174 size = (int)(span->font->size * job->zoom);
175 /* don't even bother if fontsize < 1 point */
176 if (size) {
177 tkgen_canvas(job);
178 gvputs(job, " create text ");
179 p.y -= size * 0.55; /* cl correction */
180 gvprintpointf(job, p);
181 gvputs(job, " -text {");
182 gvputs(job, span->str);
183 gvputs(job, "}");
184 gvputs(job, " -fill ");
185 tkgen_print_color(job, obj->pencolor);
186 gvputs(job, " -font {");
187 /* tk doesn't like PostScript font names like "Times-Roman" */
188 /* so use family names */
189 pA = span->font->postscript_alias;
190 if (pA)
191 font = pA->family;
192 else
193 font = span->font->name;
194 gvputs(job, "\"");
195 gvputs(job, font);
196 gvputs(job, "\"");
197 /* use -ve fontsize to indicate pixels - see "man n font" */
198 gvprintf(job, " %d}", size);
199 switch (span->just) {
200 case 'l':
201 gvputs(job, " -anchor w");
202 break;
203 case 'r':
204 gvputs(job, " -anchor e");
205 break;
206 default:
207 case 'n':
208 break;
209 }
210 tkgen_print_tags(job);
211 gvputs(job, "\n");
212 }
213 }
214}
215
216static void tkgen_ellipse(GVJ_t * job, pointf * A, int filled)
217{
218 obj_state_t *obj = job->obj;
219 pointf r;
220
221 if (obj->pen != PEN_NONE) {
222 /* A[] contains 2 points: the center and top right corner. */
223 r.x = A[1].x - A[0].x;
224 r.y = A[1].y - A[0].y;
225 A[0].x -= r.x;
226 A[0].y -= r.y;
227 tkgen_canvas(job);
228 gvputs(job, " create oval ");
229 gvprintpointflist(job, A, 2);
230 gvputs(job, " -fill ");
231 if (filled)
232 tkgen_print_color(job, obj->fillcolor);
233 else if (first_periphery)
234 /* tk ovals default to no fill, some fill
235 * is necessary else "canvas find overlapping" doesn't
236 * work as expected, use white instead */
237 gvputs(job, "white");
238 else
239 gvputs(job, "\"\"");
240 if (first_periphery == 1)
241 first_periphery = 0;
242 gvputs(job, " -width ");
243 gvprintdouble(job, obj->penwidth);
244 gvputs(job, " -outline ");
245 tkgen_print_color(job, obj->pencolor);
246 if (obj->pen == PEN_DASHED)
247 gvputs(job, " -dash 5");
248 if (obj->pen == PEN_DOTTED)
249 gvputs(job, " -dash 2");
250 tkgen_print_tags(job);
251 gvputs(job, "\n");
252 }
253}
254
255static void
256tkgen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
257 int arrow_at_end, int filled)
258{
259 obj_state_t *obj = job->obj;
260
261 if (obj->pen != PEN_NONE) {
262 tkgen_canvas(job);
263 gvputs(job, " create line ");
264 gvprintpointflist(job, A, n);
265 gvputs(job, " -fill ");
266 tkgen_print_color(job, obj->pencolor);
267 gvputs(job, " -width ");
268 gvprintdouble(job, obj->penwidth);
269 if (obj->pen == PEN_DASHED)
270 gvputs(job, " -dash 5");
271 if (obj->pen == PEN_DOTTED)
272 gvputs(job, " -dash 2");
273 gvputs(job, " -smooth bezier ");
274 tkgen_print_tags(job);
275 gvputs(job, "\n");
276 }
277}
278
279static void tkgen_polygon(GVJ_t * job, pointf * A, int n, int filled)
280{
281 obj_state_t *obj = job->obj;
282
283 if (obj->pen != PEN_NONE) {
284 tkgen_canvas(job);
285 gvputs(job, " create polygon ");
286 gvprintpointflist(job, A, n);
287 gvputs(job, " -fill ");
288 if (filled)
289 tkgen_print_color(job, obj->fillcolor);
290 else if (first_periphery)
291 /* tk polygons default to black fill, some fill
292 * is necessary else "canvas find overlapping" doesn't
293 * work as expected, use white instead */
294 gvputs(job, "white");
295 else
296 gvputs(job, "\"\"");
297 if (first_periphery == 1)
298 first_periphery = 0;
299 gvputs(job, " -width ");
300 gvprintdouble(job, obj->penwidth);
301 gvputs(job, " -outline ");
302 tkgen_print_color(job, obj->pencolor);
303 if (obj->pen == PEN_DASHED)
304 gvputs(job, " -dash 5");
305 if (obj->pen == PEN_DOTTED)
306 gvputs(job, " -dash 2");
307 tkgen_print_tags(job);
308 gvputs(job, "\n");
309 }
310}
311
312static void tkgen_polyline(GVJ_t * job, pointf * A, int n)
313{
314 obj_state_t *obj = job->obj;
315
316 if (obj->pen != PEN_NONE) {
317 tkgen_canvas(job);
318 gvputs(job, " create line ");
319 gvprintpointflist(job, A, n);
320 gvputs(job, " -fill ");
321 tkgen_print_color(job, obj->pencolor);
322 if (obj->pen == PEN_DASHED)
323 gvputs(job, " -dash 5");
324 if (obj->pen == PEN_DOTTED)
325 gvputs(job, " -dash 2");
326 tkgen_print_tags(job);
327 gvputs(job, "\n");
328 }
329}
330
331gvrender_engine_t tkgen_engine = {
332 tkgen_begin_job,
333 0, /* tkgen_end_job */
334 tkgen_begin_graph,
335 0, /* tkgen_end_graph */
336 0, /* tkgen_begin_layer */
337 0, /* tkgen_end_layer */
338 0, /* tkgen_begin_page */
339 0, /* tkgen_end_page */
340 0, /* tkgen_begin_cluster */
341 0, /* tkgen_end_cluster */
342 0, /* tkgen_begin_nodes */
343 0, /* tkgen_end_nodes */
344 0, /* tkgen_begin_edges */
345 0, /* tkgen_end_edges */
346 tkgen_begin_node,
347 0, /* tkgen_end_node */
348 tkgen_begin_edge,
349 0, /* tkgen_end_edge */
350 0, /* tkgen_begin_anchor */
351 0, /* tkgen_end_anchor */
352 0, /* tkgen_begin_label */
353 0, /* tkgen_end_label */
354 tkgen_textspan,
355 0, /* tkgen_resolve_color */
356 tkgen_ellipse,
357 tkgen_polygon,
358 tkgen_bezier,
359 tkgen_polyline,
360 tkgen_comment,
361 0, /* tkgen_library_shape */
362};
363
364gvrender_features_t render_features_tk = {
365 GVRENDER_Y_GOES_DOWN
366 | GVRENDER_NO_WHITE_BG, /* flags */
367 4., /* default pad - graph units */
368 NULL, /* knowncolors */
369 0, /* sizeof knowncolors */
370 COLOR_STRING, /* color_type */
371};
372
373gvdevice_features_t device_features_tk = {
374 0, /* flags */
375 {0.,0.}, /* default margin - points */
376 {0.,0.}, /* default page width, height - points */
377 {96.,96.}, /* default dpi */
378};
379
380gvplugin_installed_t gvrender_tk_types[] = {
381 {FORMAT_TK, "tk", 1, &tkgen_engine, &render_features_tk},
382 {0, NULL, 0, NULL, NULL}
383};
384
385gvplugin_installed_t gvdevice_tk_types[] = {
386 {FORMAT_TK, "tk:tk", 1, NULL, &device_features_tk},
387 {0, NULL, 0, NULL, NULL}
388};
389