]> granicus.if.org Git - graphviz/commitdiff
first cut at a -Tfig plugin
authorellson <devnull@localhost>
Wed, 26 Jul 2006 19:23:31 +0000 (19:23 +0000)
committerellson <devnull@localhost>
Wed, 26 Jul 2006 19:23:31 +0000 (19:23 +0000)
plugin/core/Makefile.am
plugin/core/gvplugin_core.c
plugin/core/gvrender_core_fig.c [new file with mode: 0644]

index cbee80317b267f7821deeab6c316b3274d6a77bb..ad125fb3305d1aa09f6cbea4f03fc44008eb43f1 100644 (file)
@@ -17,6 +17,7 @@ pkglib_LTLIBRARIES = libgvplugin_core.la
 libgvplugin_core_C_la_SOURCES = \
        gvplugin_core.c \
        gvrender_core_svg.c \
+       gvrender_core_fig.c \
        gvrender_core_ps.c \
        gvrender_core_map.c \
        gvloadimage_core.c
index 3958c593b5a3fa3e136bc359f38bbf782c7d325b..3a548fa5c9b65353655e120fa85b92d4fceaff19 100644 (file)
 #include "gvplugin.h"
 
 extern gvplugin_installed_t gvrender_core_ps_types;
+extern gvplugin_installed_t gvrender_core_fig_types;
 extern gvplugin_installed_t gvrender_core_svg_types;
 extern gvplugin_installed_t gvrender_core_map_types;
 extern gvplugin_installed_t gvloadimage_core_types;
 
 static gvplugin_api_t apis[] = {
     {API_render, &gvrender_core_ps_types},
+    {API_render, &gvrender_core_fig_types},
     {API_render, &gvrender_core_svg_types},
     {API_render, &gvrender_core_map_types},
     {API_loadimage, &gvloadimage_core_types},
diff --git a/plugin/core/gvrender_core_fig.c b/plugin/core/gvrender_core_fig.c
new file mode 100644 (file)
index 0000000..1cf7078
--- /dev/null
@@ -0,0 +1,593 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+*      This software is part of the graphviz package      *
+*                http://www.graphviz.org/                 *
+*                                                         *
+*            Copyright (c) 1994-2004 AT&T Corp.           *
+*                and is licensed under the                *
+*            Common Public License, Version 1.0           *
+*                      by AT&T Corp.                      *
+*                                                         *
+*        Information and Software Systems Research        *
+*              AT&T Research, Florham Park NJ             *
+**********************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef MSWIN32
+#include <io.h>
+#endif
+
+#include "macros.h"
+#include "const.h"
+
+#include "gvplugin_render.h"
+#include "graph.h"
+#include "agxbuf.h"
+#include "utils.h"
+#include "color.h"
+
+/* Number of points to split splines into */
+#define BEZIERSUBDIVISION 6
+
+typedef enum { FORMAT_FIG, } format_type;
+
+static int Depth;
+
+void figgen_fputs(GVJ_t * job, char *s)
+{
+    int len;
+
+    len = strlen(s);
+    fwrite(s, sizeof(char), (unsigned) len, job->output_file);
+}
+
+/* figgen_printf:
+ * Note that this function is unsafe due to the fixed buffer size.
+ * It should only be used when the caller is sure the input will not
+ * overflow the buffer. In particular, it should be avoided for
+ * input coming from users. Also, if vsnprintf is available, the
+ * code should check for return values to use it safely.
+ */
+void figgen_printf(GVJ_t * job, const char *format, ...)
+{
+    char buf[BUFSIZ];
+    va_list argp;
+
+    va_start(argp, format);
+#ifdef HAVE_VSNPRINTF
+    (void) vsnprintf(buf, sizeof(buf), format, argp);
+#else
+    (void) vsprintf(buf, format, argp);
+#endif
+    va_end(argp);
+
+    figgen_fputs(job, buf);
+}
+
+static void figptarray(GVJ_t *job, pointf * A, int n, int close)
+{
+    int i;
+
+    for (i = 0; i < n; i++)
+        figgen_printf(job, " %d %d", A[i].x, A[i].y);
+    if (close)
+        figgen_printf(job, " %d %d", A[i].x, A[i].y);
+    figgen_fputs(job, "\n");
+}
+
+static char *fig_string(char *s)
+{
+    static char *buf = NULL;
+    static int bufsize = 0;
+    int pos = 0;
+    char *p;
+    unsigned char c;
+
+    if (!buf) {
+        bufsize = 64;
+        buf = malloc(bufsize * sizeof(char));
+    }
+
+    p = buf;
+    while ((c = *s++)) {
+        if (pos > (bufsize - 8)) {
+            bufsize *= 2;
+            buf = realloc(buf, bufsize * sizeof(char));
+            p = buf + pos;
+        }
+        if (isascii(c)) {
+            if (c == '\\') {
+                *p++ = '\\';
+                pos++;
+            }
+            *p++ = c;
+            pos++;
+        } else {
+            *p++ = '\\';
+            sprintf(p, "%03o", c);
+            p += 3;
+            pos += 4;
+        }
+    }
+    *p = '\0';
+    return buf;
+}
+
+static int figColorResolve(int *new, int r, int g, int b)
+{
+#define maxColors 256
+    static int top = 0;
+    static short red[maxColors], green[maxColors], blue[maxColors];
+    int c;
+    int ct = -1;
+    long rd, gd, bd, dist;
+    long mindist = 3 * 255 * 255;       /* init to max poss dist */
+
+    *new = 0;                   /* in case it is not a new color */
+    for (c = 0; c < top; c++) {
+        rd = (long) (red[c] - r);
+        gd = (long) (green[c] - g);
+        bd = (long) (blue[c] - b);
+        dist = rd * rd + gd * gd + bd * bd;
+        if (dist < mindist) {
+            if (dist == 0)
+                return c;       /* Return exact match color */
+            mindist = dist;
+            ct = c;
+        }
+    }
+    /* no exact match.  We now know closest, but first try to allocate exact */
+    if (top++ == maxColors)
+        return ct;              /* Return closest available color */
+    red[c] = r;
+    green[c] = g;
+    blue[c] = b;
+    *new = 1;                   /* flag new color */
+    return c;                   /* Return newly allocated color */
+}
+
+/* this table is in xfig color index order */
+static char *figcolor[] = {
+    "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL
+};
+
+static void figgen_resolve_color(GVJ_t *job, gvcolor_t * color)
+{
+    int object_code = 0;        /* always 0 for color */
+    int i, new;
+
+    switch (color->type) {
+       case COLOR_STRING:
+           for (i = 0; figcolor[i]; i++)
+               if (streq(figcolor[i], color->u.string))
+                       color->u.index = i;
+           break;
+       case RGBA_BYTE:
+           color->u.index = 32 + figColorResolve(&new,
+                       color->u.rgba[0],
+                       color->u.rgba[1],
+                       color->u.rgba[2]);
+           if (new)
+               figgen_printf(job, "%d %d #%02x%02x%02x\n",
+                       object_code,
+                       color->u.index,
+                       color->u.rgba[0],
+                       color->u.rgba[1],
+                       color->u.rgba[2]);
+           break;
+       default:
+           assert(0);  /* internal error */
+    }
+
+    color->type = COLOR_INDEX;
+}
+
+static void figgen_line_style(gvstyle_t *style, int *line_style, double *style_val)
+{
+    switch (style->pen) {
+       case PEN_DASHED: 
+           *line_style = 1;
+           *style_val = 10.;
+           break;
+       case PEN_DOTTED:
+           *line_style = 2;
+           *style_val = 10.;
+           break;
+       case PEN_SOLID:
+       default:
+           *line_style = 0;
+           *style_val = 10.;
+           break;
+    }
+}
+
+static void figgen_begin_job(GVJ_t * job)
+{
+    figgen_fputs(job, "#FIG 3.2\n");
+    figgen_printf(job, "# Generated by %s version %s (%s)\n",
+       job->common->info[0], job->common->info[1], job->common->info[2]);
+    figgen_printf(job, "# For: %s\n", job->common->user);
+}
+
+static void figgen_end_job(GVJ_t * job)
+{
+    figgen_fputs(job, "# end of FIG file\n");
+}
+
+
+static void figgen_begin_graph(GVJ_t * job)
+{
+    obj_state_t *obj = job->obj;
+
+    figgen_printf(job, "# Title: %s\n", obj->g->name);
+    figgen_printf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
+}
+
+static void figgen_begin_page(GVJ_t * job)
+{
+    Depth = 2;
+
+    figgen_fputs(job, "Portrait\n"); /* orientation */
+    figgen_fputs(job, "Center\n");   /* justification */
+    figgen_fputs(job, "Inches\n");   /* units */
+    figgen_fputs(job, "Letter\n");   /* papersize */
+    figgen_fputs(job, "100.00\n");   /* magnification % */
+    figgen_fputs(job, "Single\n");   /* multiple-page */
+    figgen_fputs(job, "-2\n");       /* transparent color (none) */
+    figgen_fputs(job, "1200 ");      /* resolution */
+    figgen_fputs(job, "2\n");        /* coordinate system (upper left) */
+}
+
+static void figgen_begin_node(GVJ_t * job)
+{
+    Depth = 1;
+}
+
+static void figgen_end_node(GVJ_t * job)
+{
+    Depth = 2;
+}
+
+static void
+figgen_begin_edge(GVJ_t * job)
+{
+    Depth = 0;
+}
+
+static void figgen_end_edge(GVJ_t * job)
+{
+    Depth = 2;
+}
+
+static void figgen_textpara(GVJ_t * job, pointf p, textpara_t * para)
+{
+    gvstyle_t *style = job->style;
+
+    int object_code = 4;        /* always 4 for text */
+    int sub_type = 0;           /* text justification */
+    int color = style->pencolor.u.index;
+    int depth = Depth;
+    int pen_style = 0;          /* not used */
+    int font = 0;
+    double font_size = para->fontsize * job->zoom;
+    double angle = job->rotation ? (PI / 2.0) : 0.0;
+    int font_flags = 2;
+    double height = 0.0;
+    double length = 0.0;
+
+    switch (para->just) {
+    case 'l':
+        sub_type = 0;
+        break;
+    case 'r':
+        sub_type = 2;
+        break;
+    default:
+    case 'n':
+        sub_type = 1;
+        break;
+    }
+
+    figgen_printf(job,
+            "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %g %g %s\\001\n",
+            object_code, sub_type, color, depth, pen_style, font,
+            font_size, angle, font_flags, height, length, p.x, p.y,
+            fig_string(para->str));
+
+#if 0
+    figgen_fputs(job, "<text");
+    switch (para->just) {
+    case 'l':
+       figgen_fputs(job, " text-anchor=\"start\"");
+       break;
+    case 'r':
+       figgen_fputs(job, " text-anchor=\"end\"");
+       break;
+    default:
+    case 'n':
+       figgen_fputs(job, " text-anchor=\"middle\"");
+       break;
+    }
+    figgen_printf(job, " x=\"%g\" y=\"%g\"", p.x, -p.y);
+    figgen_fputs(job, " style=\"");
+    if (para->postscript_alias) {
+        figgen_printf(job, "font-family:%s;", para->postscript_alias->family);
+        if (para->postscript_alias->weight)
+           figgen_printf(job, "font-weight:%s;", para->postscript_alias->weight);
+        if (para->postscript_alias->stretch)
+           figgen_printf(job, "font-stretch:%s;", para->postscript_alias->stretch);
+        if (para->postscript_alias->style)
+           figgen_printf(job, "font-style:%s;", para->postscript_alias->style);
+    }
+    else {
+        figgen_printf(job, "font:%s;", para->fontname);
+    }
+    figgen_printf(job, "font-size:%.2f;", para->fontsize);
+    switch (penstyle->pencolor.type) {
+    case COLOR_STRING:
+       if (strcasecmp(penstyle->pencolor.u.string, "black"))
+           figgen_printf(job, "fill:%s;", penstyle->pencolor.u.string);
+       break;
+    case RGBA_BYTE:
+       figgen_printf(job, "fill:#%02x%02x%02x;",
+               penstyle->pencolor.u.rgba[0],
+               penstyle->pencolor.u.rgba[1], penstyle->pencolor.u.rgba[2]);
+       break;
+    default:
+       assert(0);              /* internal error */
+    }
+    figgen_fputs(job, "\">");
+    figgen_fputs(job, xml_string(para->str));
+    figgen_fputs(job, "</text>\n");
+#endif
+}
+
+static void figgen_ellipse(GVJ_t * job, pointf * A, int filled)
+{
+    gvstyle_t *style = job->style;
+
+    int object_code = 1;        /* always 1 for ellipse */
+    int sub_type = 1;           /* ellipse defined by radii */
+    int line_style;            /* solid, dotted, dashed */
+    int thickness = style->penwidth;
+    int pen_color = style->pencolor.u.index;
+    int fill_color = style->fillcolor.u.index;
+    int depth = Depth;
+    int pen_style = 0;          /* not used */
+    int area_fill = filled ? 20 : -1;
+    double style_val;
+    int direction = 0;
+    double angle = 0.0;
+    int center_x, center_y, radius_x, radius_y;
+    int start_x, start_y, end_x, end_y;
+
+    figgen_line_style(style, &line_style, &style_val);
+
+    start_x = center_x = A[0].x;
+    start_y = center_y = -A[0].y;
+    radius_x = A[1].x - A[0].x;
+    radius_y = A[1].y - A[0].y;
+    end_x = A[1].x;
+    end_y = -A[1].y;
+
+    figgen_printf(job,
+            "%d %d %d %d %d %d %d %d %d %.3f %d %.4f %d %d %d %d %d %d %d %d\n",
+            object_code, sub_type, line_style, thickness, pen_color,
+            fill_color, depth, pen_style, area_fill, style_val, direction,
+            angle, center_x, center_y, radius_x, radius_y, start_x,
+            start_y, end_x, end_y);
+}
+
+static void
+figgen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
+             int arrow_at_end, int filled)
+{
+    gvstyle_t *style = job->style;
+
+    int object_code = 3;        /* always 3 for spline */
+    int sub_type;
+    int line_style;            /* solid, dotted, dashed */
+    int thickness = style->penwidth;
+    int pen_color = style->pencolor.u.index;
+    int fill_color = style->fillcolor.u.index;;
+    int depth = Depth;
+    int pen_style = 0;          /* not used */
+    int area_fill;
+    double style_val;
+    int cap_style = 0;
+    int forward_arrow = 0;
+    int backward_arrow = 0;
+    int npoints = n;
+    int i;
+
+    pointf p, V[4];
+    int j, step;
+    int count = 0;
+    int size;
+
+    char *buffer;
+    char *buf;
+    buffer =
+        malloc((npoints + 1) * (BEZIERSUBDIVISION +
+                                1) * 20 * sizeof(char));
+    buf = buffer;
+
+    figgen_line_style(style, &line_style, &style_val);
+
+    if (filled) {
+        sub_type = 5;     /* closed X-spline */
+        area_fill = 20;   /* fully saturated color */
+        fill_color = job->style->fillcolor.u.index;
+    }
+    else {
+        sub_type = 4;     /* opened X-spline */
+        area_fill = -1;
+        fill_color = 0;
+    }
+    V[3].x = A[0].x;
+    V[3].y = A[0].y;
+    /* Write first point in line */
+    count++;
+    size = sprintf(buf, " %g %g", A[0].x, A[0].y);
+    buf += size;
+    /* write subsequent points */
+    for (i = 0; i + 3 < n; i += 3) {
+        V[0] = V[3];
+        for (j = 1; j <= 3; j++) {
+            V[j].x = A[i + j].x;
+            V[j].y = A[i + j].y;
+        }
+        for (step = 1; step <= BEZIERSUBDIVISION; step++) {
+            count++;
+            p = Bezier (V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL);
+            size = sprintf(buf, " %g %g", p.x, p.y);
+            buf += size;
+        }
+    }
+
+    figgen_printf(job, "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d\n",
+            object_code,
+            sub_type,
+            line_style,
+            thickness,
+            pen_color,
+            fill_color,
+            depth,
+            pen_style,
+            area_fill,
+            style_val, cap_style, forward_arrow, backward_arrow, count);
+
+    figgen_printf(job, " %s\n", buffer);      /* print points */
+    free(buffer);
+    for (i = 0; i < count; i++) {
+        figgen_printf(job, " %d", i % (count - 1) ? 1 : 0);   /* -1 on all */
+    }
+    figgen_fputs(job, "\n");
+}
+
+static void figgen_polygon(GVJ_t * job, pointf * A, int n, int filled)
+{
+    gvstyle_t *style = job->style;
+
+    int object_code = 2;        /* always 2 for polyline */
+    int sub_type = 3;           /* always 3 for polygon */
+    int line_style;            /* solid, dotted, dashed */
+    int thickness = style->penwidth;
+    int pen_color = style->pencolor.u.index;
+    int fill_color = style->fillcolor.u.index;
+    int depth = Depth;
+    int pen_style = 0;          /* not used */
+    int area_fill = filled ? 20 : -1;
+    double style_val;
+    int join_style = 0;
+    int cap_style = 0;
+    int radius = 0;
+    int forward_arrow = 0;
+    int backward_arrow = 0;
+    int npoints = n + 1;
+
+    figgen_line_style(style, &line_style, &style_val);
+
+    figgen_printf(job,
+            "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
+            object_code, sub_type, line_style, thickness, pen_color,
+            fill_color, depth, pen_style, area_fill, style_val, join_style,
+            cap_style, radius, forward_arrow, backward_arrow, npoints);
+    figptarray(job, A, n, 1);        /* closed shape */
+}
+
+static void figgen_polyline(GVJ_t * job, pointf * A, int n)
+{
+    gvstyle_t *style = job->style;
+
+    int object_code = 2;        /* always 2 for polyline */
+    int sub_type = 1;           /* always 1 for polyline */
+    int line_style;            /* solid, dotted, dashed */
+    int thickness = style->penwidth;
+    int pen_color = style->pencolor.u.index;
+    int fill_color = 0;
+    int depth = Depth;
+    int pen_style = 0;          /* not used */
+    int area_fill = 0;
+    double style_val;
+    int join_style = 0;
+    int cap_style = 0;
+    int radius = 0;
+    int forward_arrow = 0;
+    int backward_arrow = 0;
+    int npoints = n;
+
+    figgen_line_style(style, &line_style, &style_val);
+
+    figgen_printf(job,
+            "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n",
+            object_code, sub_type, line_style, thickness, pen_color,
+            fill_color, depth, pen_style, area_fill, style_val, join_style,
+            cap_style, radius, forward_arrow, backward_arrow, npoints);
+    figptarray(job, A, n, 0);        /* open shape */
+}
+
+gvrender_engine_t figgen_engine = {
+    figgen_begin_job,
+    figgen_end_job,
+    figgen_begin_graph,
+    0,                         /* figgen_end_graph */
+    0,                         /* figgen_begin_layer */
+    0,                         /* figgen_end_layer */
+    figgen_begin_page,
+    0,                         /* figgen_end_page */
+    0,                         /* figgen_begin_cluster */
+    0,                         /* figgen_end_cluster */
+    0,                         /* figgen_begin_nodes */
+    0,                         /* figgen_end_nodes */
+    0,                         /* figgen_begin_edges */
+    0,                         /* figgen_end_edges */
+    figgen_begin_node,
+    figgen_end_node,
+    figgen_begin_edge,
+    figgen_end_edge,
+    0,                         /* figgen_begin_anchor */
+    0,                         /* figgen_end_anchor */
+    figgen_textpara,
+    figgen_resolve_color,
+    figgen_ellipse,
+    figgen_polygon,
+    figgen_bezier,
+    figgen_polyline,
+    0,                         /* figgen_comment */
+};
+
+
+/* NB.  List must be LANG_C sorted */
+static char *figgen_knowncolors[] = {
+    "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
+};
+
+
+gvrender_features_t figgen_features = {
+    GVRENDER_DOES_TRUECOLOR
+       | GVRENDER_Y_GOES_DOWN
+        | GVRENDER_DOES_TRANSFORM, /* flags */
+    DEFAULT_EMBED_MARGIN,      /* default margin - points */
+    {96.,96.},                 /* default dpi */
+    figgen_knowncolors,                /* knowncolors */
+    sizeof(figgen_knowncolors) / sizeof(char *), /* sizeof knowncolors */
+    RGBA_BYTE,                 /* color_type */
+    NULL,                       /* device */
+    "fig",                      /* gvloadimage target for usershapes */
+};
+
+gvplugin_installed_t gvrender_core_fig_types[] = {
+    {FORMAT_FIG, "fig", -1, &figgen_engine, &figgen_features},
+    {0, NULL, 0, NULL, NULL}
+};