Graphviz  2.41.20170721.1726
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 #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 
39 typedef enum { FORMAT_FIG, } format_type;
40 
41 static int Depth;
42 
43 static 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 
59 static 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 
97 static 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 */
131 static char *figcolor[] = {
132  "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL
133 };
134 
135 static 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 
169 static 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 
188 static void fig_comment(GVJ_t *job, char *str)
189 {
190  gvprintf(job, "# %s\n", str);
191 }
192 
193 static 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 
213 static void fig_end_graph(GVJ_t * job)
214 {
215  gvputs(job, "# end of FIG file\n");
216 }
217 
218 static void fig_begin_page(GVJ_t * job)
219 {
220  Depth = 2;
221 }
222 
223 static void fig_begin_node(GVJ_t * job)
224 {
225  Depth = 1;
226 }
227 
228 static void fig_end_node(GVJ_t * job)
229 {
230  Depth = 2;
231 }
232 
233 static void fig_begin_edge(GVJ_t * job)
234 {
235  Depth = 0;
236 }
237 
238 static void fig_end_edge(GVJ_t * job)
239 {
240  Depth = 2;
241 }
242 
243 static 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 
294 static 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 
330 static 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 
422 static 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 
453 static 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 
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 */
519 static char *fig_knowncolors[] = {
520  "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
521 };
522 
523 
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 
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 
551  {FORMAT_FIG, "fig", 1, &fig_engine, &render_features_fig},
552  {0, NULL, 0, NULL, NULL}
553 };
554 
556  {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig},
557  {0, NULL, 0, NULL, NULL}
558 };
gvdevice_features_t device_features_fig
int rotation
Definition: gvcjob.h:328
union color_s::@10 u
gvrender_features_t render_features_fig
pen_type pen
Definition: gvcjob.h:206
int index
Definition: color.h:42
double size
Definition: textspan.h:52
point pagesArraySize
Definition: gvcjob.h:313
#define ROUND(f)
Definition: arith.h:84
#define assert(x)
Definition: cghdr.h:47
Definition: geom.h:28
#define PF2P(pf, p)
Definition: geom.h:72
Definition: color.h:34
#define maxColors
gvcolor_t pencolor
Definition: gvcjob.h:203
Definition: gvcjob.h:271
int x
Definition: geom.h:26
obj_state_t * obj
Definition: gvcjob.h:278
#define GVRENDER_Y_GOES_DOWN
Definition: gvcjob.h:96
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
char * str
Definition: textspan.h:59
color_type_t type
Definition: color.h:44
double y
Definition: geom.h:28
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
gvrender_engine_t fig_engine
#define BEZIERSUBDIVISION
char * string
Definition: color.h:41
GVCOMMON_t * common
Definition: gvcjob.h:276
char ** info
Definition: gvcommon.h:22
#define EMIT_COLORS
Definition: gvcjob.h:85
PostscriptAlias * postscript_alias
Definition: textspan.h:51
Definition: grammar.c:79
graph_t * g
Definition: gvcjob.h:195
format_type
pointf Bezier(pointf *V, int degree, double t, pointf *Left, pointf *Right)
Definition: utils.c:221
#define NULL
Definition: logic.h:39
Definition: geom.h:26
double x
Definition: geom.h:28
#define streq(s, t)
Definition: cghdr.h:52
#define top(sp)
Definition: stack.h:35
char just
Definition: textspan.h:65
gvplugin_installed_t gvdevice_fig_types[]
union obj_state_s::@23 u
gvplugin_installed_t gvrender_fig_types[]
void(* pf)(char *, void *)
Definition: xdot.c:501
#define M_PI
Definition: arith.h:77
agxbuf * str
Definition: htmlparse.c:85
gvcolor_t fillcolor
Definition: gvcjob.h:203
double dist(Site *s, Site *t)
Definition: site.c:41
double penwidth
Definition: gvcjob.h:208
int y
Definition: geom.h:26
unsigned char rgba[4]
Definition: color.h:38
double zoom
Definition: gvcjob.h:327
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
textfont_t * font
Definition: textspan.h:60