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/* FIXME - incomplete replacement for codegen */
15
16#include "config.h"
17
18#include <stdarg.h>
19#include <stdlib.h>
20#include <string.h>
21#include <ctype.h>
22
23#ifdef _WIN32
24#include <io.h>
25#include "compat.h"
26#endif
27
28#include "macros.h"
29#include "const.h"
30
31#include "gvplugin_render.h"
32#include "gvplugin_device.h"
33#include "gvio.h"
34#include "agxbuf.h"
35#include "utils.h"
36#include "color.h"
37
38/* Number of points to split splines into */
39#define BEZIERSUBDIVISION 6
40
41typedef enum { FORMAT_MP, } format_type;
42
43static int Depth;
44
45static void mpptarray(GVJ_t *job, pointf * A, int n, int close)
46{
47 int i;
48 point p;
49
50 for (i = 0; i < n; i++) {
51 PF2P(A[i],p);
52 gvprintf(job, " %d %d", p.x, p.y);
53 }
54 if (close) {
55 PF2P(A[0],p);
56 gvprintf(job, " %d %d", p.x, p.y);
57 }
58 gvputs(job, "\n");
59}
60
61static char *mp_string(char *s)
62{
63 static char *buf = NULL;
64 static int bufsize = 0;
65 int pos = 0;
66 char *p;
67 unsigned char c;
68
69 if (!buf) {
70 bufsize = 64;
71 buf = malloc(bufsize * sizeof(char));
72 }
73
74 p = buf;
75 while ((c = *s++)) {
76 if (pos > (bufsize - 8)) {
77 bufsize *= 2;
78 buf = realloc(buf, bufsize * sizeof(char));
79 p = buf + pos;
80 }
81 if (isascii(c)) {
82 if (c == '\\') {
83 *p++ = '\\';
84 pos++;
85 }
86 *p++ = c;
87 pos++;
88 } else {
89 *p++ = '\\';
90 sprintf(p, "%03o", c);
91 p += 3;
92 pos += 4;
93 }
94 }
95 *p = '\0';
96 return buf;
97}
98
99static int mpColorResolve(int *new, int r, int g, int b)
100{
101#define maxColors 256
102 static int top = 0;
103 static short red[maxColors], green[maxColors], blue[maxColors];
104 int c;
105 int ct = -1;
106 long rd, gd, bd, dist;
107 long mindist = 3 * 255 * 255; /* init to max poss dist */
108
109 *new = 0; /* in case it is not a new color */
110 for (c = 0; c < top; c++) {
111 rd = (long) (red[c] - r);
112 gd = (long) (green[c] - g);
113 bd = (long) (blue[c] - b);
114 dist = rd * rd + gd * gd + bd * bd;
115 if (dist < mindist) {
116 if (dist == 0)
117 return c; /* Return exact match color */
118 mindist = dist;
119 ct = c;
120 }
121 }
122 /* no exact match. We now know closest, but first try to allocate exact */
123 if (top++ == maxColors)
124 return ct; /* Return closest available color */
125 red[c] = r;
126 green[c] = g;
127 blue[c] = b;
128 *new = 1; /* flag new color */
129 return c; /* Return newly allocated color */
130}
131
132/* this table is in xfig color index order */
133static char *mpcolor[] = {
134 "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL
135};
136
137static void mp_resolve_color(GVJ_t *job, gvcolor_t * color)
138{
139 int object_code = 0; /* always 0 for color */
140 int i, new;
141
142 switch (color->type) {
143 case COLOR_STRING:
144 for (i = 0; mpcolor[i]; i++) {
145 if (streq(mpcolor[i], color->u.string)) {
146 color->u.index = i;
147 break;
148 }
149 }
150 break;
151 case RGBA_BYTE:
152 i = 32 + mpColorResolve(&new,
153 color->u.rgba[0],
154 color->u.rgba[1],
155 color->u.rgba[2]);
156 if (new)
157 gvprintf(job, "%d %d #%02x%02x%02x\n",
158 object_code, i,
159 color->u.rgba[0],
160 color->u.rgba[1],
161 color->u.rgba[2]);
162 color->u.index = i;
163 break;
164 case HSVA_DOUBLE: /* TODO: implement color conversion */
165 color->u.index = 0;
166 break;
167 default:
168 assert(0); /* internal error */
169 }
170
171 color->type = COLOR_INDEX;
172}
173
174static void mp_line_style(obj_state_t *obj, int *line_style, double *style_val)
175{
176 switch (obj->pen) {
177 case PEN_DASHED:
178 *line_style = 1;
179 *style_val = 10.;
180 break;
181 case PEN_DOTTED:
182 *line_style = 2;
183 *style_val = 10.;
184 break;
185 case PEN_SOLID:
186 default:
187 *line_style = 0;
188 *style_val = 0.;
189 break;
190 }
191}
192
193static void mp_comment(GVJ_t *job, char *str)
194{
195 gvprintf(job, "# %s\n", str);
196}
197
198static void mp_begin_graph(GVJ_t * job)
199{
200 obj_state_t *obj = job->obj;
201
202 gvputs(job, "#FIG 3.2\n");
203 gvprintf(job, "# Generated by %s version %s (%s)\n",
204 job->common->info[0], job->common->info[1], job->common->info[2]);
205 gvprintf(job, "# Title: %s\n", agnameof(obj->u.g));
206 gvprintf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
207 gvputs(job, "Portrait\n"); /* orientation */
208 gvputs(job, "Center\n"); /* justification */
209 gvputs(job, "Inches\n"); /* units */
210 gvputs(job, "Letter\n"); /* papersize */
211 gvputs(job, "100.00\n"); /* magnification % */
212 gvputs(job, "Single\n"); /* multiple-page */
213 gvputs(job, "-2\n"); /* transparent color (none) */
214 gvputs(job, "1200"); /* resolution */
215 gvputs(job, " 2\n"); /* coordinate system (upper left) */
216}
217
218static void mp_end_graph(GVJ_t * job)
219{
220 gvputs(job, "# end of FIG file\n");
221}
222
223static void mp_begin_page(GVJ_t * job)
224{
225 Depth = 2;
226}
227
228static void mp_begin_node(GVJ_t * job)
229{
230 Depth = 1;
231}
232
233static void mp_end_node(GVJ_t * job)
234{
235 Depth = 2;
236}
237
238static void mp_begin_edge(GVJ_t * job)
239{
240 Depth = 0;
241}
242
243static void mp_end_edge(GVJ_t * job)
244{
245 Depth = 2;
246}
247
248static void mp_textspan(GVJ_t * job, pointf p, textspan_t * span)
249{
250 obj_state_t *obj = job->obj;
251
252 int object_code = 4; /* always 4 for text */
253 int sub_type = 0; /* text justification */
254 int color = obj->pencolor.u.index;
255 int depth = Depth;
256 int pen_style = 0; /* not used */
257 int font = -1; /* init to xfig's default font */
258 double font_size = span->font->size * job->zoom;
259 double angle = job->rotation ? (M_PI / 2.0) : 0.0;
260 int font_flags = 4; /* PostScript font */
261 double height = 0.0;
262 double length = 0.0;
263
264 if (span->font->postscript_alias) /* if it is a standard postscript font */
265 font = span->font->postscript_alias->xfig_code;
266
267 switch (span->just) {
268 case 'l':
269 sub_type = 0;
270 break;
271 case 'r':
272 sub_type = 2;
273 break;
274 default:
275 case 'n':
276 sub_type = 1;
277 break;
278 }
279
280 gvprintf(job,
281 "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %d %d %s\\001\n",
282 object_code, sub_type, color, depth, pen_style, font,
283 font_size, angle, font_flags, height, length, ROUND(p.x), ROUND(p.y),
284 mp_string(span->str));
285}
286
287static void mp_ellipse(GVJ_t * job, pointf * A, int filled)
288{
289 obj_state_t *obj = job->obj;
290
291 int object_code = 1; /* always 1 for ellipse */
292 int sub_type = 1; /* ellipse defined by radii */
293 int line_style; /* solid, dotted, dashed */
294 int thickness = obj->penwidth;
295 int pen_color = obj->pencolor.u.index;
296 int fill_color = obj->fillcolor.u.index;
297 int depth = Depth;
298 int pen_style = 0; /* not used */
299 int area_fill = filled ? 20 : -1;
300 double style_val;
301 int direction = 0;
302 double angle = 0.0;
303 int center_x, center_y, radius_x, radius_y;
304 int start_x, start_y, end_x, end_y;
305
306 mp_line_style(obj, &line_style, &style_val);
307
308 start_x = center_x = ROUND(A[0].x);
309 start_y = center_y = ROUND(A[0].y);
310 radius_x = ROUND(A[1].x - A[0].x);
311 radius_y = ROUND(A[1].y - A[0].y);
312 end_x = ROUND(A[1].x);
313 end_y = ROUND(A[1].y);
314
315 gvprintf(job,
316 "%d %d %d %d %d %d %d %d %d %.3f %d %.4f %d %d %d %d %d %d %d %d\n",
317 object_code, sub_type, line_style, thickness, pen_color,
318 fill_color, depth, pen_style, area_fill, style_val, direction,
319 angle, center_x, center_y, radius_x, radius_y, start_x,
320 start_y, end_x, end_y);
321}
322
323static void mp_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
324 int arrow_at_end, int filled)
325{
326 obj_state_t *obj = job->obj;
327
328 int object_code = 3; /* always 3 for spline */
329 int sub_type;
330 int line_style; /* solid, dotted, dashed */
331 int thickness = obj->penwidth;
332 int pen_color = obj->pencolor.u.index;
333 int fill_color = obj->fillcolor.u.index;
334 int depth = Depth;
335 int pen_style = 0; /* not used */
336 int area_fill;
337 double style_val;
338 int cap_style = 0;
339 int forward_arrow = 0;
340 int backward_arrow = 0;
341 int npoints = n;
342 int i;
343
344 pointf pf, V[4];
345 point p;
346 int j, step;
347 int count = 0;
348 int size;
349
350 char *buffer;
351 char *buf;
352 buffer =
353 malloc((npoints + 1) * (BEZIERSUBDIVISION +
354 1) * 20 * sizeof(char));
355 buf = buffer;
356
357 mp_line_style(obj, &line_style, &style_val);
358
359 if (filled) {
360 sub_type = 5; /* closed X-spline */
361 area_fill = 20; /* fully saturated color */
362 fill_color = job->obj->fillcolor.u.index;
363 }
364 else {
365 sub_type = 4; /* opened X-spline */
366 area_fill = -1;
367 fill_color = 0;
368 }
369 V[3].x = A[0].x;
370 V[3].y = A[0].y;
371 /* Write first point in line */
372 count++;
373 PF2P(A[0], p);
374 size = sprintf(buf, " %d %d", p.x, p.y);
375 buf += size;
376 /* write subsequent points */
377 for (i = 0; i + 3 < n; i += 3) {
378 V[0] = V[3];
379 for (j = 1; j <= 3; j++) {
380 V[j].x = A[i + j].x;
381 V[j].y = A[i + j].y;
382 }
383 for (step = 1; step <= BEZIERSUBDIVISION; step++) {
384 count++;
385 pf = Bezier (V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL);
386 PF2P(pf, p);
387 size = sprintf(buf, " %d %d", p.x, p.y);
388 buf += size;
389 }
390 }
391
392 gvprintf(job, "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d\n",
393 object_code,
394 sub_type,
395 line_style,
396 thickness,
397 pen_color,
398 fill_color,
399 depth,
400 pen_style,
401 area_fill,
402 style_val, cap_style, forward_arrow, backward_arrow, count);
403
404 gvprintf(job, " %s\n", buffer); /* print points */
405 free(buffer);
406 for (i = 0; i < count; i++) {
407 gvprintf(job, " %d", i % (count + 1) ? 1 : 0); /* -1 on all */
408 }
409 gvputs(job, "\n");
410}
411
412static void mp_polygon(GVJ_t * job, pointf * A, int n, int filled)
413{
414 obj_state_t *obj = job->obj;
415
416 int object_code = 2; /* always 2 for polyline */
417 int sub_type = 3; /* always 3 for polygon */
418 int line_style; /* solid, dotted, dashed */
419 int thickness = obj->penwidth;
420 int pen_color = obj->pencolor.u.index;
421 int fill_color = obj->fillcolor.u.index;
422 int depth = Depth;
423 int pen_style = 0; /* not used */
424 int area_fill = filled ? 20 : -1;
425 double style_val;
426 int join_style = 0;
427 int cap_style = 0;
428 int radius = 0;
429 int forward_arrow = 0;
430 int backward_arrow = 0;
431 int npoints = n + 1;
432
433 mp_line_style(obj, &line_style, &style_val);
434
435 gvprintf(job,
436 "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
437 object_code, sub_type, line_style, thickness, pen_color,
438 fill_color, depth, pen_style, area_fill, style_val, join_style,
439 cap_style, radius, forward_arrow, backward_arrow, npoints);
440 mpptarray(job, A, n, 1); /* closed shape */
441}
442
443static void mp_polyline(GVJ_t * job, pointf * A, int n)
444{
445 obj_state_t *obj = job->obj;
446
447 int object_code = 2; /* always 2 for polyline */
448 int sub_type = 1; /* always 1 for polyline */
449 int line_style; /* solid, dotted, dashed */
450 int thickness = obj->penwidth;
451 int pen_color = obj->pencolor.u.index;
452 int fill_color = 0;
453 int depth = Depth;
454 int pen_style = 0; /* not used */
455 int area_fill = 0;
456 double style_val;
457 int join_style = 0;
458 int cap_style = 0;
459 int radius = 0;
460 int forward_arrow = 0;
461 int backward_arrow = 0;
462 int npoints = n;
463
464 mp_line_style(obj, &line_style, &style_val);
465
466 gvprintf(job,
467 "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
468 object_code, sub_type, line_style, thickness, pen_color,
469 fill_color, depth, pen_style, area_fill, style_val, join_style,
470 cap_style, radius, forward_arrow, backward_arrow, npoints);
471 mpptarray(job, A, n, 0); /* open shape */
472}
473
474gvrender_engine_t mp_engine = {
475 0, /* mp_begin_job */
476 0, /* mp_end_job */
477 mp_begin_graph,
478 mp_end_graph,
479 0, /* mp_begin_layer */
480 0, /* mp_end_layer */
481 mp_begin_page,
482 0, /* mp_end_page */
483 0, /* mp_begin_cluster */
484 0, /* mp_end_cluster */
485 0, /* mp_begin_nodes */
486 0, /* mp_end_nodes */
487 0, /* mp_begin_edges */
488 0, /* mp_end_edges */
489 mp_begin_node,
490 mp_end_node,
491 mp_begin_edge,
492 mp_end_edge,
493 0, /* mp_begin_anchor */
494 0, /* mp_end_anchor */
495 0, /* mp_begin_label */
496 0, /* mp_end_label */
497 mp_textspan,
498 mp_resolve_color,
499 mp_ellipse,
500 mp_polygon,
501 mp_bezier,
502 mp_polyline,
503 mp_comment,
504 0, /* mp_library_shape */
505};
506
507static gvrender_features_t render_features_mp = {
508 0, /* flags */
509 4., /* default pad - graph units */
510 NULL, /* knowncolors */
511 0, /* sizeof knowncolors */
512 HSVA_DOUBLE, /* color_type */
513};
514
515static gvdevice_features_t device_features_mp = {
516 0, /* flags */
517 {0.,0.}, /* default margin - points */
518 {0.,0.}, /* default page width, height - points */
519 {72.,72.}, /* default dpi */
520};
521
522gvplugin_installed_t gvrender_mp_types[] = {
523 {FORMAT_MP, "mp", -1, &mp_engine, &render_features_mp},
524 {0, NULL, 0, NULL, NULL}
525};
526
527gvplugin_installed_t gvdevice_mp_types[] = {
528 {FORMAT_MP, "mp:mp", -1, NULL, &device_features_mp},
529 {0, NULL, 0, NULL, NULL}
530};
531
532