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