Graphviz  2.35.20130930.0449
gvrender_core_fig.c
Go to the documentation of this file.
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 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
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 
41 typedef enum { FORMAT_FIG, } format_type;
42 
43 static int Depth;
44 
45 static void figptarray(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 
61 static char *fig_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 
99 static int figColorResolve(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 */
133 static char *figcolor[] = {
134  "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL
135 };
136 
137 static void fig_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; figcolor[i]; i++) {
145  if (streq(figcolor[i], color->u.string)) {
146  color->u.index = i;
147  break;
148  }
149  }
150  break;
151  case RGBA_BYTE:
152  i = 32 + figColorResolve(&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  default:
165  assert(0); /* internal error */
166  }
167 
168  color->type = COLOR_INDEX;
169 }
170 
171 static void fig_line_style(obj_state_t *obj, int *line_style, double *style_val)
172 {
173  switch (obj->pen) {
174  case PEN_DASHED:
175  *line_style = 1;
176  *style_val = 10.;
177  break;
178  case PEN_DOTTED:
179  *line_style = 2;
180  *style_val = 10.;
181  break;
182  case PEN_SOLID:
183  default:
184  *line_style = 0;
185  *style_val = 0.;
186  break;
187  }
188 }
189 
190 static void fig_comment(GVJ_t *job, char *str)
191 {
192  gvprintf(job, "# %s\n", str);
193 }
194 
195 static void fig_begin_graph(GVJ_t * job)
196 {
197  obj_state_t *obj = job->obj;
198 
199  gvputs(job, "#FIG 3.2\n");
200  gvprintf(job, "# Generated by %s version %s (%s)\n",
201  job->common->info[0], job->common->info[1], job->common->info[2]);
202  gvprintf(job, "# Title: %s\n", agnameof(obj->u.g));
203  gvprintf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
204  gvputs(job, "Portrait\n"); /* orientation */
205  gvputs(job, "Center\n"); /* justification */
206  gvputs(job, "Inches\n"); /* units */
207  gvputs(job, "Letter\n"); /* papersize */
208  gvputs(job, "100.00\n"); /* magnification % */
209  gvputs(job, "Single\n"); /* multiple-page */
210  gvputs(job, "-2\n"); /* transparent color (none) */
211  gvputs(job, "1200"); /* resolution */
212  gvputs(job, " 2\n"); /* coordinate system (upper left) */
213 }
214 
215 static void fig_end_graph(GVJ_t * job)
216 {
217  gvputs(job, "# end of FIG file\n");
218 }
219 
220 static void fig_begin_page(GVJ_t * job)
221 {
222  Depth = 2;
223 }
224 
225 static void fig_begin_node(GVJ_t * job)
226 {
227  Depth = 1;
228 }
229 
230 static void fig_end_node(GVJ_t * job)
231 {
232  Depth = 2;
233 }
234 
235 static void fig_begin_edge(GVJ_t * job)
236 {
237  Depth = 0;
238 }
239 
240 static void fig_end_edge(GVJ_t * job)
241 {
242  Depth = 2;
243 }
244 
245 static void fig_textpara(GVJ_t * job, pointf p, textpara_t * para)
246 {
247  obj_state_t *obj = job->obj;
248 
249  int object_code = 4; /* always 4 for text */
250  int sub_type = 0; /* text justification */
251  int color = obj->pencolor.u.index;
252  int depth = Depth;
253  int pen_style = 0; /* not used */
254  int font = -1; /* init to xfig's default font */
255  double font_size = para->fontsize * job->zoom;
256  double angle = job->rotation ? (M_PI / 2.0) : 0.0;
257  int font_flags = 6; /* PostScript font + Special text */
258 /* Special text indicates that latex markup may exist
259  * in the output - but note that dot knows nothing about latex,
260  * so the node sizes may be wrong.
261  */
262  double height = 0.0;
263  double length = 0.0;
264 
265  if (para->postscript_alias) /* if it is a standard postscript font */
266  font = para->postscript_alias->xfig_code;
267 
268  switch (para->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  gvprintf(job,
282  "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %d %d %s\\001\n",
283  object_code, sub_type, color, depth, pen_style, font,
284  font_size, angle, font_flags, height, length, ROUND(p.x), ROUND(p.y),
285  fig_string(para->str));
286 }
287 
288 static void fig_ellipse(GVJ_t * job, pointf * A, int filled)
289 {
290  obj_state_t *obj = job->obj;
291 
292  int object_code = 1; /* always 1 for ellipse */
293  int sub_type = 1; /* ellipse defined by radii */
294  int line_style; /* solid, dotted, dashed */
295  int thickness = obj->penwidth;
296  int pen_color = obj->pencolor.u.index;
297  int fill_color = obj->fillcolor.u.index;
298  int depth = Depth;
299  int pen_style = 0; /* not used */
300  int area_fill = filled ? 20 : -1;
301  double style_val;
302  int direction = 0;
303  double angle = 0.0;
304  int center_x, center_y, radius_x, radius_y;
305  int start_x, start_y, end_x, end_y;
306 
307  fig_line_style(obj, &line_style, &style_val);
308 
309  start_x = center_x = ROUND(A[0].x);
310  start_y = center_y = ROUND(A[0].y);
311  radius_x = ROUND(A[1].x - A[0].x);
312  radius_y = ROUND(A[1].y - A[0].y);
313  end_x = ROUND(A[1].x);
314  end_y = ROUND(A[1].y);
315 
316  gvprintf(job,
317  "%d %d %d %d %d %d %d %d %d %.3f %d %.4f %d %d %d %d %d %d %d %d\n",
318  object_code, sub_type, line_style, thickness, pen_color,
319  fill_color, depth, pen_style, area_fill, style_val, direction,
320  angle, center_x, center_y, radius_x, radius_y, start_x,
321  start_y, end_x, end_y);
322 }
323 
324 static void fig_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
325  int arrow_at_end, int filled)
326 {
327  obj_state_t *obj = job->obj;
328 
329  int object_code = 3; /* always 3 for spline */
330  int sub_type;
331  int line_style; /* solid, dotted, dashed */
332  int thickness = obj->penwidth;
333  int pen_color = obj->pencolor.u.index;
334  int fill_color = obj->fillcolor.u.index;
335  int depth = Depth;
336  int pen_style = 0; /* not used */
337  int area_fill;
338  double style_val;
339  int cap_style = 0;
340  int forward_arrow = 0;
341  int backward_arrow = 0;
342  int npoints = n;
343  int i;
344 
345 
346  pointf pf, V[4];
347  point p;
348  int j, step;
349  int count = 0;
350  int size;
351 
352  char *buffer;
353  char *buf;
354  assert (n >= 4);
355 
356  buffer =
357  malloc((npoints + 1) * (BEZIERSUBDIVISION +
358  1) * 20 * sizeof(char));
359  buf = buffer;
360 
361  fig_line_style(obj, &line_style, &style_val);
362 
363  if (filled) {
364  sub_type = 5; /* closed X-spline */
365  area_fill = 20; /* fully saturated color */
366  fill_color = job->obj->fillcolor.u.index;
367  }
368  else {
369  sub_type = 4; /* opened X-spline */
370  area_fill = -1;
371  fill_color = 0;
372  }
373  V[3].x = A[0].x;
374  V[3].y = A[0].y;
375  /* Write first point in line */
376  count++;
377  PF2P(A[0], p);
378  size = sprintf(buf, " %d %d", p.x, p.y);
379  buf += size;
380  /* write subsequent points */
381  for (i = 0; i + 3 < n; i += 3) {
382  V[0] = V[3];
383  for (j = 1; j <= 3; j++) {
384  V[j].x = A[i + j].x;
385  V[j].y = A[i + j].y;
386  }
387  for (step = 1; step <= BEZIERSUBDIVISION; step++) {
388  count++;
389  pf = Bezier (V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL);
390  PF2P(pf, p);
391  size = sprintf(buf, " %d %d", p.x, p.y);
392  buf += size;
393  }
394  }
395 
396  gvprintf(job, "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d\n",
397  object_code,
398  sub_type,
399  line_style,
400  thickness,
401  pen_color,
402  fill_color,
403  depth,
404  pen_style,
405  area_fill,
406  style_val, cap_style, forward_arrow, backward_arrow, count);
407 
408  gvprintf(job, " %s\n", buffer); /* print points */
409  free(buffer);
410  for (i = 0; i < count; i++) {
411  gvprintf(job, " %d", i % (count - 1) ? 1 : 0); /* -1 on all */
412  }
413  gvputs(job, "\n");
414 }
415 
416 static void fig_polygon(GVJ_t * job, pointf * A, int n, int filled)
417 {
418  obj_state_t *obj = job->obj;
419 
420  int object_code = 2; /* always 2 for polyline */
421  int sub_type = 3; /* always 3 for polygon */
422  int line_style; /* solid, dotted, dashed */
423  int thickness = obj->penwidth;
424  int pen_color = obj->pencolor.u.index;
425  int fill_color = obj->fillcolor.u.index;
426  int depth = Depth;
427  int pen_style = 0; /* not used */
428  int area_fill = filled ? 20 : -1;
429  double style_val;
430  int join_style = 0;
431  int cap_style = 0;
432  int radius = 0;
433  int forward_arrow = 0;
434  int backward_arrow = 0;
435  int npoints = n + 1;
436 
437  fig_line_style(obj, &line_style, &style_val);
438 
439  gvprintf(job,
440  "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
441  object_code, sub_type, line_style, thickness, pen_color,
442  fill_color, depth, pen_style, area_fill, style_val, join_style,
443  cap_style, radius, forward_arrow, backward_arrow, npoints);
444  figptarray(job, A, n, 1); /* closed shape */
445 }
446 
447 static void fig_polyline(GVJ_t * job, pointf * A, int n)
448 {
449  obj_state_t *obj = job->obj;
450 
451  int object_code = 2; /* always 2 for polyline */
452  int sub_type = 1; /* always 1 for polyline */
453  int line_style; /* solid, dotted, dashed */
454  int thickness = obj->penwidth;
455  int pen_color = obj->pencolor.u.index;
456  int fill_color = 0;
457  int depth = Depth;
458  int pen_style = 0; /* not used */
459  int area_fill = 0;
460  double style_val;
461  int join_style = 0;
462  int cap_style = 0;
463  int radius = 0;
464  int forward_arrow = 0;
465  int backward_arrow = 0;
466  int npoints = n;
467 
468  fig_line_style(obj, &line_style, &style_val);
469 
470  gvprintf(job,
471  "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
472  object_code, sub_type, line_style, thickness, pen_color,
473  fill_color, depth, pen_style, area_fill, style_val, join_style,
474  cap_style, radius, forward_arrow, backward_arrow, npoints);
475  figptarray(job, A, n, 0); /* open shape */
476 }
477 
479  0, /* fig_begin_job */
480  0, /* fig_end_job */
481  fig_begin_graph,
482  fig_end_graph,
483  0, /* fig_begin_layer */
484  0, /* fig_end_layer */
485  fig_begin_page,
486  0, /* fig_end_page */
487  0, /* fig_begin_cluster */
488  0, /* fig_end_cluster */
489  0, /* fig_begin_nodes */
490  0, /* fig_end_nodes */
491  0, /* fig_begin_edges */
492  0, /* fig_end_edges */
493  fig_begin_node,
494  fig_end_node,
495  fig_begin_edge,
496  fig_end_edge,
497  0, /* fig_begin_anchor */
498  0, /* fig_end_anchor */
499  0, /* fig_begin_label */
500  0, /* fig_end_label */
501  fig_textpara,
502  fig_resolve_color,
503  fig_ellipse,
504  fig_polygon,
505  fig_bezier,
506  fig_polyline,
507  fig_comment,
508  0, /* fig_library_shape */
509 };
510 
511 
512 /* NB. List must be LANG_C sorted */
513 static char *fig_knowncolors[] = {
514  "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
515 };
516 
517 
520  | GVRENDER_Y_GOES_DOWN, /* flags */
521  4., /* default pad - graph units */
522  fig_knowncolors, /* knowncolors */
523  sizeof(fig_knowncolors) / sizeof(char *), /* sizeof knowncolors */
524  RGBA_BYTE, /* color_type */
525 };
526 
529  | GVRENDER_Y_GOES_DOWN, /* flags */
530  {0.,0.}, /* default margin - points */
531  {0.,0.}, /* default page width, height - points */
532  {1440.,1440.}, /* default dpi */
533  /* FIXME - this default dpi is a very strange number!!!
534  * It was picked to make .png usershapes the right size on my screen.
535  * It happens to be 1.2 * 1200, but I can't explain the 1.2.
536  * (I was expecting 1.3333 which is 96/72, but thats too big.)
537  * Also 1200 is hardcoded in fig_begin_graph() instead of using job->dpi
538  */
539 
540  /* It may be TWIPS, i.e. 20 * POINT_PER_INCH
541  * but that doesn't explain what the 1200 is? */
542 };
543 
545  {FORMAT_FIG, "fig", 1, &fig_engine, &render_features_fig},
546  {0, NULL, 0, NULL, NULL}
547 };
548 
550  {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig},
551  {0, NULL, 0, NULL, NULL}
552 };