Graphviz  2.31.20130617.0446
plugin/core/gvrender_core_fig.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #include <stdarg.h>
00019 #include <stdlib.h>
00020 #include <string.h>
00021 #include <ctype.h>
00022 
00023 #ifdef WIN32
00024 #include <io.h>
00025 #include "compat.h"
00026 #endif
00027 
00028 #include "macros.h"
00029 #include "const.h"
00030 
00031 #include "gvplugin_render.h"
00032 #include "gvplugin_device.h"
00033 #include "gvio.h"
00034 #include "agxbuf.h"
00035 #include "utils.h"
00036 #include "color.h"
00037 
00038 /* Number of points to split splines into */
00039 #define BEZIERSUBDIVISION 6
00040 
00041 typedef enum { FORMAT_FIG, } format_type;
00042 
00043 static int Depth;
00044 
00045 static void figptarray(GVJ_t *job, pointf * A, int n, int close)
00046 {
00047     int i;
00048     point p;
00049 
00050     for (i = 0; i < n; i++) {
00051         PF2P(A[i],p);
00052         gvprintf(job, " %d %d", p.x, p.y);
00053     }
00054     if (close) {
00055         PF2P(A[0],p);
00056         gvprintf(job, " %d %d", p.x, p.y);
00057     }
00058     gvputs(job, "\n");
00059 }
00060 
00061 static char *fig_string(char *s)
00062 {
00063     static char *buf = NULL;
00064     static int bufsize = 0;
00065     int pos = 0;
00066     char *p;
00067     unsigned char c;
00068 
00069     if (!buf) {
00070         bufsize = 64;
00071         buf = malloc(bufsize * sizeof(char));
00072     }
00073 
00074     p = buf;
00075     while ((c = *s++)) {
00076         if (pos > (bufsize - 8)) {
00077             bufsize *= 2;
00078             buf = realloc(buf, bufsize * sizeof(char));
00079             p = buf + pos;
00080         }
00081         if (isascii(c)) {
00082             if (c == '\\') {
00083                 *p++ = '\\';
00084                 pos++;
00085             }
00086             *p++ = c;
00087             pos++;
00088         } else {
00089             *p++ = '\\';
00090             sprintf(p, "%03o", c);
00091             p += 3;
00092             pos += 4;
00093         }
00094     }
00095     *p = '\0';
00096     return buf;
00097 }
00098 
00099 static int figColorResolve(int *new, int r, int g, int b)
00100 {
00101 #define maxColors 256
00102     static int top = 0;
00103     static short red[maxColors], green[maxColors], blue[maxColors];
00104     int c;
00105     int ct = -1;
00106     long rd, gd, bd, dist;
00107     long mindist = 3 * 255 * 255;       /* init to max poss dist */
00108 
00109     *new = 0;                   /* in case it is not a new color */
00110     for (c = 0; c < top; c++) {
00111         rd = (long) (red[c] - r);
00112         gd = (long) (green[c] - g);
00113         bd = (long) (blue[c] - b);
00114         dist = rd * rd + gd * gd + bd * bd;
00115         if (dist < mindist) {
00116             if (dist == 0)
00117                 return c;       /* Return exact match color */
00118             mindist = dist;
00119             ct = c;
00120         }
00121     }
00122     /* no exact match.  We now know closest, but first try to allocate exact */
00123     if (top++ == maxColors)
00124         return ct;              /* Return closest available color */
00125     red[c] = r;
00126     green[c] = g;
00127     blue[c] = b;
00128     *new = 1;                   /* flag new color */
00129     return c;                   /* Return newly allocated color */
00130 }
00131 
00132 /* this table is in xfig color index order */
00133 static char *figcolor[] = {
00134     "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL
00135 };
00136 
00137 static void fig_resolve_color(GVJ_t *job, gvcolor_t * color)
00138 {
00139     int object_code = 0;        /* always 0 for color */
00140     int i, new;
00141 
00142     switch (color->type) {
00143         case COLOR_STRING:
00144             for (i = 0; figcolor[i]; i++) {
00145                 if (streq(figcolor[i], color->u.string)) {
00146                     color->u.index = i;
00147                     break;
00148                 }
00149             }
00150             break;
00151         case RGBA_BYTE:
00152             i = 32 + figColorResolve(&new,
00153                         color->u.rgba[0],
00154                         color->u.rgba[1],
00155                         color->u.rgba[2]);
00156             if (new)
00157                 gvprintf(job, "%d %d #%02x%02x%02x\n",
00158                         object_code, i,
00159                         color->u.rgba[0],
00160                         color->u.rgba[1],
00161                         color->u.rgba[2]);
00162             color->u.index = i;
00163             break;
00164         default:
00165             assert(0);  /* internal error */
00166     }
00167 
00168     color->type = COLOR_INDEX;
00169 }
00170 
00171 static void fig_line_style(obj_state_t *obj, int *line_style, double *style_val)
00172 {
00173     switch (obj->pen) {
00174         case PEN_DASHED: 
00175             *line_style = 1;
00176             *style_val = 10.;
00177             break;
00178         case PEN_DOTTED:
00179             *line_style = 2;
00180             *style_val = 10.;
00181             break;
00182         case PEN_SOLID:
00183         default:
00184             *line_style = 0;
00185             *style_val = 0.;
00186             break;
00187     }
00188 }
00189 
00190 static void fig_comment(GVJ_t *job, char *str)
00191 {
00192     gvprintf(job, "# %s\n", str);
00193 }
00194 
00195 static void fig_begin_graph(GVJ_t * job)
00196 {
00197     obj_state_t *obj = job->obj;
00198 
00199     gvputs(job, "#FIG 3.2\n");
00200     gvprintf(job, "# Generated by %s version %s (%s)\n",
00201         job->common->info[0], job->common->info[1], job->common->info[2]);
00202     gvprintf(job, "# Title: %s\n", agnameof(obj->u.g));
00203     gvprintf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
00204     gvputs(job, "Portrait\n"); /* orientation */
00205     gvputs(job, "Center\n");   /* justification */
00206     gvputs(job, "Inches\n");   /* units */
00207     gvputs(job, "Letter\n");   /* papersize */
00208     gvputs(job, "100.00\n");   /* magnification % */
00209     gvputs(job, "Single\n");   /* multiple-page */
00210     gvputs(job, "-2\n");       /* transparent color (none) */
00211     gvputs(job, "1200");             /* resolution */
00212     gvputs(job, " 2\n");       /* coordinate system (upper left) */
00213 }
00214 
00215 static void fig_end_graph(GVJ_t * job)
00216 {
00217     gvputs(job, "# end of FIG file\n");
00218 }
00219 
00220 static void fig_begin_page(GVJ_t * job)
00221 {
00222     Depth = 2;
00223 }
00224 
00225 static void fig_begin_node(GVJ_t * job)
00226 {
00227     Depth = 1;
00228 }
00229 
00230 static void fig_end_node(GVJ_t * job)
00231 {
00232     Depth = 2;
00233 }
00234 
00235 static void fig_begin_edge(GVJ_t * job)
00236 {
00237     Depth = 0;
00238 }
00239 
00240 static void fig_end_edge(GVJ_t * job)
00241 {
00242     Depth = 2;
00243 }
00244 
00245 static void fig_textpara(GVJ_t * job, pointf p, textpara_t * para)
00246 {
00247     obj_state_t *obj = job->obj;
00248 
00249     int object_code = 4;        /* always 4 for text */
00250     int sub_type = 0;           /* text justification */
00251     int color = obj->pencolor.u.index;
00252     int depth = Depth;
00253     int pen_style = 0;          /* not used */
00254     int font = -1;              /* init to xfig's default font */
00255     double font_size = para->fontsize * job->zoom;
00256     double angle = job->rotation ? (M_PI / 2.0) : 0.0;
00257     int font_flags = 6;         /* PostScript font + Special text */
00258 /* Special text indicates that latex markup may exist
00259  * in the output - but note that dot knows nothing about latex,
00260  * so the node sizes may be wrong.
00261  */
00262     double height = 0.0;
00263     double length = 0.0;
00264 
00265     if (para->postscript_alias) /* if it is a standard postscript font */
00266         font = para->postscript_alias->xfig_code; 
00267 
00268     switch (para->just) {
00269     case 'l':
00270         sub_type = 0;
00271         break;
00272     case 'r':
00273         sub_type = 2;
00274         break;
00275     default:
00276     case 'n':
00277         sub_type = 1;
00278         break;
00279     }
00280 
00281     gvprintf(job,
00282             "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %d %d %s\\001\n",
00283             object_code, sub_type, color, depth, pen_style, font,
00284             font_size, angle, font_flags, height, length, ROUND(p.x), ROUND(p.y),
00285             fig_string(para->str));
00286 }
00287 
00288 static void fig_ellipse(GVJ_t * job, pointf * A, int filled)
00289 {
00290     obj_state_t *obj = job->obj;
00291 
00292     int object_code = 1;        /* always 1 for ellipse */
00293     int sub_type = 1;           /* ellipse defined by radii */
00294     int line_style;             /* solid, dotted, dashed */
00295     int thickness = obj->penwidth;
00296     int pen_color = obj->pencolor.u.index;
00297     int fill_color = obj->fillcolor.u.index;
00298     int depth = Depth;
00299     int pen_style = 0;          /* not used */
00300     int area_fill = filled ? 20 : -1;
00301     double style_val;
00302     int direction = 0;
00303     double angle = 0.0;
00304     int center_x, center_y, radius_x, radius_y;
00305     int start_x, start_y, end_x, end_y;
00306 
00307     fig_line_style(obj, &line_style, &style_val);
00308 
00309     start_x = center_x = ROUND(A[0].x);
00310     start_y = center_y = ROUND(A[0].y);
00311     radius_x = ROUND(A[1].x - A[0].x);
00312     radius_y = ROUND(A[1].y - A[0].y);
00313     end_x = ROUND(A[1].x);
00314     end_y = ROUND(A[1].y);
00315 
00316     gvprintf(job,
00317             "%d %d %d %d %d %d %d %d %d %.3f %d %.4f %d %d %d %d %d %d %d %d\n",
00318             object_code, sub_type, line_style, thickness, pen_color,
00319             fill_color, depth, pen_style, area_fill, style_val, direction,
00320             angle, center_x, center_y, radius_x, radius_y, start_x,
00321             start_y, end_x, end_y);
00322 }
00323 
00324 static void fig_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
00325               int arrow_at_end, int filled)
00326 {
00327     obj_state_t *obj = job->obj;
00328 
00329     int object_code = 3;        /* always 3 for spline */
00330     int sub_type;
00331     int line_style;             /* solid, dotted, dashed */
00332     int thickness = obj->penwidth;
00333     int pen_color = obj->pencolor.u.index;
00334     int fill_color = obj->fillcolor.u.index;
00335     int depth = Depth;
00336     int pen_style = 0;          /* not used */
00337     int area_fill;
00338     double style_val;
00339     int cap_style = 0;
00340     int forward_arrow = 0;
00341     int backward_arrow = 0;
00342     int npoints = n;
00343     int i;
00344 
00345 
00346     pointf pf, V[4];
00347     point p;
00348     int j, step;
00349     int count = 0;
00350     int size;
00351 
00352     char *buffer;
00353     char *buf;
00354     assert (n >= 4);
00355 
00356         buffer =
00357         malloc((npoints + 1) * (BEZIERSUBDIVISION +
00358                                 1) * 20 * sizeof(char));
00359     buf = buffer;
00360 
00361     fig_line_style(obj, &line_style, &style_val);
00362 
00363     if (filled) {
00364         sub_type = 5;     /* closed X-spline */
00365         area_fill = 20;   /* fully saturated color */
00366         fill_color = job->obj->fillcolor.u.index;
00367     }
00368     else {
00369         sub_type = 4;     /* opened X-spline */
00370         area_fill = -1;
00371         fill_color = 0;
00372     }
00373     V[3].x = A[0].x;
00374     V[3].y = A[0].y;
00375     /* Write first point in line */
00376     count++;
00377     PF2P(A[0], p);
00378     size = sprintf(buf, " %d %d", p.x, p.y);
00379     buf += size;
00380     /* write subsequent points */
00381     for (i = 0; i + 3 < n; i += 3) {
00382         V[0] = V[3];
00383         for (j = 1; j <= 3; j++) {
00384             V[j].x = A[i + j].x;
00385             V[j].y = A[i + j].y;
00386         }
00387         for (step = 1; step <= BEZIERSUBDIVISION; step++) {
00388             count++;
00389             pf = Bezier (V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL);
00390             PF2P(pf, p);
00391             size = sprintf(buf, " %d %d", p.x, p.y);
00392             buf += size;
00393         }
00394     }
00395 
00396     gvprintf(job, "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d\n",
00397             object_code,
00398             sub_type,
00399             line_style,
00400             thickness,
00401             pen_color,
00402             fill_color,
00403             depth,
00404             pen_style,
00405             area_fill,
00406             style_val, cap_style, forward_arrow, backward_arrow, count);
00407 
00408     gvprintf(job, " %s\n", buffer);      /* print points */
00409     free(buffer);
00410     for (i = 0; i < count; i++) {
00411         gvprintf(job, " %d", i % (count - 1) ? 1 : 0);   /* -1 on all */
00412     }
00413     gvputs(job, "\n");
00414 }
00415 
00416 static void fig_polygon(GVJ_t * job, pointf * A, int n, int filled)
00417 {
00418     obj_state_t *obj = job->obj;
00419 
00420     int object_code = 2;        /* always 2 for polyline */
00421     int sub_type = 3;           /* always 3 for polygon */
00422     int line_style;             /* solid, dotted, dashed */
00423     int thickness = obj->penwidth;
00424     int pen_color = obj->pencolor.u.index;
00425     int fill_color = obj->fillcolor.u.index;
00426     int depth = Depth;
00427     int pen_style = 0;          /* not used */
00428     int area_fill = filled ? 20 : -1;
00429     double style_val;
00430     int join_style = 0;
00431     int cap_style = 0;
00432     int radius = 0;
00433     int forward_arrow = 0;
00434     int backward_arrow = 0;
00435     int npoints = n + 1;
00436 
00437     fig_line_style(obj, &line_style, &style_val);
00438 
00439     gvprintf(job,
00440             "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
00441             object_code, sub_type, line_style, thickness, pen_color,
00442             fill_color, depth, pen_style, area_fill, style_val, join_style,
00443             cap_style, radius, forward_arrow, backward_arrow, npoints);
00444     figptarray(job, A, n, 1);        /* closed shape */
00445 }
00446 
00447 static void fig_polyline(GVJ_t * job, pointf * A, int n)
00448 {
00449     obj_state_t *obj = job->obj;
00450 
00451     int object_code = 2;        /* always 2 for polyline */
00452     int sub_type = 1;           /* always 1 for polyline */
00453     int line_style;             /* solid, dotted, dashed */
00454     int thickness = obj->penwidth;
00455     int pen_color = obj->pencolor.u.index;
00456     int fill_color = 0;
00457     int depth = Depth;
00458     int pen_style = 0;          /* not used */
00459     int area_fill = 0;
00460     double style_val;
00461     int join_style = 0;
00462     int cap_style = 0;
00463     int radius = 0;
00464     int forward_arrow = 0;
00465     int backward_arrow = 0;
00466     int npoints = n;
00467 
00468     fig_line_style(obj, &line_style, &style_val);
00469 
00470     gvprintf(job,
00471             "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
00472             object_code, sub_type, line_style, thickness, pen_color,
00473             fill_color, depth, pen_style, area_fill, style_val, join_style,
00474             cap_style, radius, forward_arrow, backward_arrow, npoints);
00475     figptarray(job, A, n, 0);        /* open shape */
00476 }
00477 
00478 gvrender_engine_t fig_engine = {
00479     0,                          /* fig_begin_job */
00480     0,                          /* fig_end_job */
00481     fig_begin_graph,
00482     fig_end_graph,
00483     0,                          /* fig_begin_layer */
00484     0,                          /* fig_end_layer */
00485     fig_begin_page,
00486     0,                          /* fig_end_page */
00487     0,                          /* fig_begin_cluster */
00488     0,                          /* fig_end_cluster */
00489     0,                          /* fig_begin_nodes */
00490     0,                          /* fig_end_nodes */
00491     0,                          /* fig_begin_edges */
00492     0,                          /* fig_end_edges */
00493     fig_begin_node,
00494     fig_end_node,
00495     fig_begin_edge,
00496     fig_end_edge,
00497     0,                          /* fig_begin_anchor */
00498     0,                          /* fig_end_anchor */
00499     0,                          /* fig_begin_label */
00500     0,                          /* fig_end_label */
00501     fig_textpara,
00502     fig_resolve_color,
00503     fig_ellipse,
00504     fig_polygon,
00505     fig_bezier,
00506     fig_polyline,
00507     fig_comment,
00508     0,                          /* fig_library_shape */
00509 };
00510 
00511 
00512 /* NB.  List must be LANG_C sorted */
00513 static char *fig_knowncolors[] = {
00514     "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
00515 };
00516 
00517 
00518 gvrender_features_t render_features_fig = {
00519     EMIT_COLORS
00520         | GVRENDER_Y_GOES_DOWN, /* flags */
00521     4.,                         /* default pad - graph units */
00522     fig_knowncolors,            /* knowncolors */
00523     sizeof(fig_knowncolors) / sizeof(char *), /* sizeof knowncolors */
00524     RGBA_BYTE,                  /* color_type */
00525 };
00526 
00527 gvdevice_features_t device_features_fig = {
00528     EMIT_COLORS
00529         | GVRENDER_Y_GOES_DOWN, /* flags */
00530     {0.,0.},                    /* default margin - points */
00531     {0.,0.},                    /* default page width, height - points */
00532     {1440.,1440.},              /* default dpi */
00533          /* FIXME - this default dpi is a very strange number!!!
00534           * It was picked to make .png usershapes the right size on my screen.
00535           * It happens to be 1.2 * 1200, but I can't explain the 1.2.
00536           * (I was expecting 1.3333 which is 96/72, but thats too big.)
00537           * Also 1200 is hardcoded in fig_begin_graph() instead of using job->dpi 
00538           */
00539 
00540          /* It may be TWIPS, i.e. 20 * POINT_PER_INCH 
00541           *    but that doesn't explain what the 1200 is? */
00542 };
00543 
00544 gvplugin_installed_t gvrender_fig_types[] = {
00545     {FORMAT_FIG, "fig", 1, &fig_engine, &render_features_fig},
00546     {0, NULL, 0, NULL, NULL}
00547 };
00548 
00549 gvplugin_installed_t gvdevice_fig_types[] = {
00550     {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig},
00551     {0, NULL, 0, NULL, NULL}
00552 };