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 | |
28 | typedef enum { FORMAT_TK, } format_type; |
29 | |
30 | static char *tkgen_string(char *s) |
31 | { |
32 | return s; |
33 | } |
34 | |
35 | static 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 | |
53 | static 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 | |
112 | static 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 | |
120 | static void (GVJ_t * job, char *str) |
121 | { |
122 | gvputs(job, "# " ); |
123 | gvputs(job, tkgen_string(str)); |
124 | gvputs(job, "\n" ); |
125 | } |
126 | |
127 | static 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 | |
138 | static int first_periphery; |
139 | |
140 | static 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 | |
154 | static void tkgen_begin_node(GVJ_t * job) |
155 | { |
156 | first_periphery = 1; /* FIXME - this is an ugly hack! */ |
157 | } |
158 | |
159 | static 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 | |
164 | static 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 | |
216 | static 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 | |
255 | static void |
256 | tkgen_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 | |
279 | static 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 | |
312 | static 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 | |
331 | gvrender_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 | |
364 | gvrender_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 | |
373 | gvdevice_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 | |
380 | gvplugin_installed_t gvrender_tk_types[] = { |
381 | {FORMAT_TK, "tk" , 1, &tkgen_engine, &render_features_tk}, |
382 | {0, NULL, 0, NULL, NULL} |
383 | }; |
384 | |
385 | gvplugin_installed_t gvdevice_tk_types[] = { |
386 | {FORMAT_TK, "tk:tk" , 1, NULL, &device_features_tk}, |
387 | {0, NULL, 0, NULL, NULL} |
388 | }; |
389 | |