/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/plugin/core/gvrender_core_vml.c

Go to the documentation of this file.
00001 /* $Id: gvrender_core_vml.c,v 1.11 2008/02/22 14:21:43 glenlow Exp $ $Revision: 1.11 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 #ifdef HAVE_CONFIG_H
00018 #include "config.h"
00019 #endif
00020 
00021 #include <stdarg.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 
00025 #include "macros.h"
00026 #include "const.h"
00027 
00028 #include "gvplugin_render.h"
00029 #include "graph.h"
00030 
00031 typedef enum { FORMAT_VML, FORMAT_VMLZ, } format_type;
00032 
00033 extern char *xml_string(char *str);
00034 
00035 char graphcoords[256];
00036 
00037 #if defined(WIN32) && !defined(__MINGW32_VERSION)       /* MinGW already defines snprintf */
00038 static int
00039 snprintf (char *str, int n, char *fmt, ...)
00040 {
00041 int ret;
00042 va_list a;
00043 va_start (a, fmt);
00044 ret = _vsnprintf (str, n, fmt, a);
00045 va_end (a);
00046 return ret;
00047 }
00048 #endif
00049 
00050 static void vml_bzptarray(GVJ_t * job, pointf * A, int n)
00051 {
00052     int i;
00053     char *c;
00054 
00055     c = "m ";                   /* first point */
00056     for (i = 0; i < n; i++) {
00057         gvdevice_printf(job, "%s%.0f,%.0f ", c, A[i].x, -A[i].y);
00058         if (i == 0)
00059             c = "c ";           /* second point */
00060         else
00061             c = "";             /* remaining points */
00062     }
00063 }
00064 
00065 static void vml_print_color(GVJ_t * job, gvcolor_t color)
00066 {
00067     switch (color.type) {
00068     case COLOR_STRING:
00069         gvdevice_fputs(job, color.u.string);
00070         break;
00071     case RGBA_BYTE:
00072         if (color.u.rgba[3] == 0) /* transparent */
00073             gvdevice_fputs(job, "none");
00074         else
00075             gvdevice_printf(job, "#%02x%02x%02x",
00076                 color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]);
00077         break;
00078     default:
00079         assert(0);              /* internal error */
00080     }
00081 }
00082 
00083 static void vml_grstroke(GVJ_t * job, int filled)
00084 {
00085     obj_state_t *obj = job->obj;
00086 
00087     gvdevice_fputs(job, "<v:stroke fillcolor=\"");
00088     if (filled)
00089         vml_print_color(job, obj->fillcolor);
00090     else
00091         gvdevice_fputs(job, "none");
00092     gvdevice_fputs(job, "\" strokecolor=\"");
00093     vml_print_color(job, obj->pencolor);
00094     if (obj->penwidth != PENWIDTH_NORMAL)
00095         gvdevice_printf(job, "\" stroke-weight=\"%g", obj->penwidth);
00096     if (obj->pen == PEN_DASHED) {
00097         gvdevice_fputs(job, "\" dashstyle=\"dash");
00098     } else if (obj->pen == PEN_DOTTED) {
00099         gvdevice_fputs(job, "\" dashstyle=\"dot");
00100     }
00101     gvdevice_fputs(job, "\" />");
00102 }
00103 
00104 static void vml_grstrokeattr(GVJ_t * job)
00105 {
00106     obj_state_t *obj = job->obj;
00107 
00108     gvdevice_fputs(job, " strokecolor=\"");
00109     vml_print_color(job, obj->pencolor);
00110     if (obj->penwidth != PENWIDTH_NORMAL)
00111         gvdevice_printf(job, "\" stroke-weight=\"%g", obj->penwidth);
00112     if (obj->pen == PEN_DASHED) {
00113         gvdevice_fputs(job, "\" dashstyle=\"dash");
00114     } else if (obj->pen == PEN_DOTTED) {
00115         gvdevice_fputs(job, "\" dashstyle=\"dot");
00116     }
00117     gvdevice_fputs(job, "\"");
00118 }
00119 
00120 static void vml_grfill(GVJ_t * job, int filled)
00121 {
00122     obj_state_t *obj = job->obj;
00123 
00124     gvdevice_fputs(job, "<v:fill color=\"");
00125     if (filled)
00126         vml_print_color(job, obj->fillcolor);
00127     else
00128         gvdevice_fputs(job, "none");
00129     gvdevice_fputs(job, "\" />");
00130 }
00131 
00132 static void vml_comment(GVJ_t * job, char *str)
00133 {
00134     gvdevice_fputs(job, "      <!-- ");
00135     gvdevice_fputs(job, xml_string(str));
00136     gvdevice_fputs(job, " -->\n");
00137 }
00138 
00139 static void vml_begin_job(GVJ_t * job)
00140 {
00141     gvdevice_fputs(job, "<?xml version=\"1.1\" encoding=\"UTF-8\" ?>\n");
00142 
00143     gvdevice_fputs(job, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ");
00144     gvdevice_fputs(job, "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
00145     gvdevice_fputs(job, "<html xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" ");
00146     gvdevice_fputs(job, "xmlns:v=\"urn:schemas-microsoft-com:vml\""); 
00147     gvdevice_fputs(job, ">"); 
00148 
00149     gvdevice_fputs(job, "\n<!-- Generated by ");
00150     gvdevice_fputs(job, xml_string(job->common->info[0]));
00151     gvdevice_fputs(job, " version ");
00152     gvdevice_fputs(job, xml_string(job->common->info[1]));
00153     gvdevice_fputs(job, " (");
00154     gvdevice_fputs(job, xml_string(job->common->info[2]));
00155     gvdevice_fputs(job, ")\n     For user: ");
00156     gvdevice_fputs(job, xml_string(job->common->user));
00157     gvdevice_fputs(job, " -->\n");
00158 }
00159 
00160 static void vml_begin_graph(GVJ_t * job)
00161 {
00162     obj_state_t *obj = job->obj;
00163 
00164     gvdevice_fputs(job, "<head>");
00165     if (obj->u.g->name[0]) {
00166         gvdevice_fputs(job, "<title>");
00167         gvdevice_fputs(job, xml_string(obj->u.g->name));
00168         gvdevice_fputs(job, "</title>");
00169     }
00170     gvdevice_printf(job, "<!-- Pages: %d -->\n</head>\n", job->pagesArraySize.x * job->pagesArraySize.y);
00171 
00172     snprintf(graphcoords, sizeof(graphcoords), "style=\"width: %.0fpt; height: %.0fpt\" coordsize=\"%.0f,%.0f\" coordorigin=\"-4,-%.0f\"",
00173         job->width*.75, job->height*.75,
00174         job->width*.75, job->height*.75,
00175         job->height*.75 - 4);
00176 
00177     gvdevice_printf(job, "<body>\n<div class=\"graph\" %s>\n", graphcoords);
00178     gvdevice_fputs(job, "<style type=\"text/css\">\nv\\:* {\nbehavior: url(#default#VML);display:inline-block;position: absolute; left: 0px; top: 0px;\n}\n</style>\n");
00179 /*    graphcoords[0] = '\0'; */
00180 
00181 }
00182 
00183 static void vml_end_graph(GVJ_t * job)
00184 {
00185     gvdevice_fputs(job, "</div>\n</body>\n");
00186 }
00187 
00188 static void
00189 vml_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target)
00190 {
00191     gvdevice_fputs(job, "      <a");
00192     if (href && href[0])
00193         gvdevice_printf(job, " href=\"%s\"", xml_string(href));
00194     if (tooltip && tooltip[0])
00195         gvdevice_printf(job, " title=\"%s\"", xml_string(tooltip));
00196     if (target && target[0])
00197         gvdevice_printf(job, " target=\"%s\"", xml_string(target));
00198     gvdevice_fputs(job, ">\n");
00199 }
00200 
00201 static void vml_end_anchor(GVJ_t * job)
00202 {
00203     gvdevice_fputs(job, "      </a>\n");
00204 }
00205 
00206 static void vml_textpara(GVJ_t * job, pointf p, textpara_t * para)
00207 {
00208     obj_state_t *obj = job->obj;
00209 
00210     gvdevice_fputs(job, "        <div");
00211     switch (para->just) {
00212     case 'l':
00213         gvdevice_fputs(job, " style=\"text-align: left; ");
00214         break;
00215     case 'r':
00216         gvdevice_fputs(job, " style=\"text-align: right; ");
00217         break;
00218     default:
00219     case 'n':
00220         gvdevice_fputs(job, " style=\"text-align: center; ");
00221         break;
00222     }
00223     gvdevice_printf(job, "position: absolute; left: %gpx; top: %gpx;", p.x/.75, job->height - p.y/.75 - 14);
00224     if (para->postscript_alias) {
00225         gvdevice_printf(job, " font-family: '%s';", para->postscript_alias->family);
00226         if (para->postscript_alias->weight)
00227             gvdevice_printf(job, " font-weight: %s;", para->postscript_alias->weight);
00228         if (para->postscript_alias->stretch)
00229             gvdevice_printf(job, " font-stretch: %s;", para->postscript_alias->stretch);
00230         if (para->postscript_alias->style)
00231             gvdevice_printf(job, " font-style: %s;", para->postscript_alias->style);
00232     }
00233     else {
00234         gvdevice_printf(job, " font-family: \'%s\';", para->fontname);
00235     }
00236     /* FIXME - even inkscape requires a magic correction to fontsize.  Why?  */
00237     gvdevice_printf(job, " font-size: %.2fpt;", para->fontsize * 0.81);
00238     switch (obj->pencolor.type) {
00239     case COLOR_STRING:
00240         if (strcasecmp(obj->pencolor.u.string, "black"))
00241             gvdevice_printf(job, "color:%s;", obj->pencolor.u.string);
00242         break;
00243     case RGBA_BYTE:
00244         gvdevice_printf(job, "color:#%02x%02x%02x;",
00245                 obj->pencolor.u.rgba[0], obj->pencolor.u.rgba[1], obj->pencolor.u.rgba[2]);
00246         break;
00247     default:
00248         assert(0);              /* internal error */
00249     }
00250     gvdevice_fputs(job, "\">");
00251     gvdevice_fputs(job, xml_string(para->str));
00252     gvdevice_fputs(job, "</div>\n");
00253 }
00254 
00255 static void vml_ellipse(GVJ_t * job, pointf * A, int filled)
00256 {
00257     /* A[] contains 2 points: the center and corner. */
00258     
00259     gvdevice_fputs(job, "        <v:oval");
00260 
00261     vml_grstrokeattr(job);
00262 
00263     gvdevice_fputs(job, " style=\"position: absolute;");
00264 
00265     gvdevice_printf(job, " left:  %gpt; top:    %gpt;", 2*A[0].x - A[1].x+4, job->height*.75 - A[1].y-4);
00266     gvdevice_printf(job, " width: %gpt; height: %gpt;", 2*(A[1].x - A[0].x), 2*(A[1].y - A[0].y));
00267     gvdevice_fputs(job, "\">");
00268     vml_grstroke(job, filled);
00269     vml_grfill(job, filled);
00270     gvdevice_fputs(job, "</v:oval>\n");
00271 }
00272 
00273 static void
00274 vml_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
00275               int arrow_at_end, int filled)
00276 {
00277     gvdevice_printf(job, "        <v:shape %s><!-- bezier --><v:path", graphcoords);
00278     gvdevice_fputs(job, " v=\"");
00279     vml_bzptarray(job, A, n);
00280     gvdevice_fputs(job, "\" />");
00281     vml_grstroke(job, filled);
00282     gvdevice_fputs(job, "</v:path>");
00283     vml_grfill(job, filled);
00284     gvdevice_fputs(job, "</v:shape>\n");
00285 }
00286 
00287 static void vml_polygon(GVJ_t * job, pointf * A, int n, int filled)
00288 {
00289     int i;
00290 
00291     gvdevice_fputs(job, "        <v:shape");
00292     vml_grstrokeattr(job);
00293     gvdevice_printf(job, " %s><!-- polygon --><v:path", graphcoords);
00294     gvdevice_fputs(job, " v=\"");
00295     for (i = 0; i < n; i++)
00296     {
00297         if (i==0) gvdevice_fputs(job, "m ");
00298         gvdevice_printf(job, "%.0f,%.0f ", A[i].x, -A[i].y);
00299         if (i==0) gvdevice_fputs(job, "l ");
00300         if (i==n-1) gvdevice_fputs(job, "x e ");
00301     }
00302     gvdevice_fputs(job, "\">");
00303     vml_grstroke(job, filled);
00304     gvdevice_fputs(job, "</v:path>");
00305     vml_grfill(job, filled);
00306     gvdevice_fputs(job, "</v:shape>\n");
00307 }
00308 
00309 static void vml_polyline(GVJ_t * job, pointf * A, int n)
00310 {
00311     int i;
00312 
00313     gvdevice_printf(job, "        <v:shape %s><!-- polyline --><v:path", graphcoords);
00314     gvdevice_fputs(job, " v=\"");
00315     for (i = 0; i < n; i++)
00316     {
00317         if (i==0) gvdevice_fputs(job, " m ");
00318         gvdevice_printf(job, "%.0f,%.0f ", A[i].x, -A[i].y);
00319         if (i==0) gvdevice_fputs(job, " l ");
00320         if (i==n-1) gvdevice_fputs(job, " e "); /* no x here for polyline */
00321     }
00322     gvdevice_fputs(job, "\">");
00323     vml_grstroke(job, 0);                 /* no fill here for polyline */
00324     gvdevice_fputs(job, "</v:path>");
00325     gvdevice_fputs(job, "</v:shape>\n");
00326 
00327 }
00328 
00329 /* color names from http://www.w3.org/TR/VML/types.html */
00330 /* NB.  List must be LANG_C sorted */
00331 static char *vml_knowncolors[] = {
00332     "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
00333     "beige", "bisque", "black", "blanchedalmond", "blue",
00334     "blueviolet", "brown", "burlywood",
00335     "cadetblue", "chartreuse", "chocolate", "coral",
00336     "cornflowerblue", "cornsilk", "crimson", "cyan",
00337     "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
00338     "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
00339     "darkolivegreen", "darkorange", "darkorchid", "darkred",
00340     "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
00341     "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
00342     "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
00343     "firebrick", "floralwhite", "forestgreen", "fuchsia",
00344     "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
00345     "green", "greenyellow", "grey",
00346     "honeydew", "hotpink", "indianred",
00347     "indigo", "ivory", "khaki",
00348     "lavender", "lavenderblush", "lawngreen", "lemonchiffon",
00349     "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow",
00350     "lightgray", "lightgreen", "lightgrey", "lightpink",
00351     "lightsalmon", "lightseagreen", "lightskyblue",
00352     "lightslategray", "lightslategrey", "lightsteelblue",
00353     "lightyellow", "lime", "limegreen", "linen",
00354     "magenta", "maroon", "mediumaquamarine", "mediumblue",
00355     "mediumorchid", "mediumpurple", "mediumseagreen",
00356     "mediumslateblue", "mediumspringgreen", "mediumturquoise",
00357     "mediumvioletred", "midnightblue", "mintcream",
00358     "mistyrose", "moccasin",
00359     "navajowhite", "navy", "oldlace",
00360     "olive", "olivedrab", "orange", "orangered", "orchid",
00361     "palegoldenrod", "palegreen", "paleturquoise",
00362     "palevioletred", "papayawhip", "peachpuff", "peru", "pink",
00363     "plum", "powderblue", "purple",
00364     "red", "rosybrown", "royalblue",
00365     "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell",
00366     "sienna", "silver", "skyblue", "slateblue", "slategray",
00367     "slategrey", "snow", "springgreen", "steelblue",
00368     "tan", "teal", "thistle", "tomato", "turquoise",
00369     "violet",
00370     "wheat", "white", "whitesmoke",
00371     "yellow", "yellowgreen"
00372 };
00373 
00374 gvrender_engine_t vml_engine = {
00375     vml_begin_job,
00376     0,                          /* vml_end_job */
00377     vml_begin_graph,
00378     vml_end_graph,
00379     0,                          /* vml_begin_layer */
00380     0,                          /* vml_end_layer */
00381     0,                          /* vml_begin_page */
00382     0,                          /* vml_end_page */
00383     0,                          /* vml_begin_cluster */
00384     0,                          /* vml_end_cluster */
00385     0,                          /* vml_begin_nodes */
00386     0,                          /* vml_end_nodes */
00387     0,                          /* vml_begin_edges */
00388     0,                          /* vml_end_edges */
00389     0,                          /* vml_begin_node */
00390     0,                          /* vml_end_node */
00391     0,                          /* vml_begin_edge */
00392     0,                          /* vml_end_edge */
00393     vml_begin_anchor,
00394     vml_end_anchor,
00395     vml_textpara,
00396     0,                          /* vml_resolve_color */
00397     vml_ellipse,
00398     vml_polygon,
00399     vml_bezier,
00400     vml_polyline,
00401     vml_comment,
00402     0,                          /* vml_library_shape */
00403 };
00404 
00405 gvrender_features_t render_features_vml = {
00406     GVRENDER_Y_GOES_DOWN
00407         | GVRENDER_DOES_TRANSFORM
00408         | GVRENDER_DOES_LABELS
00409         | GVRENDER_DOES_MAPS
00410         | GVRENDER_DOES_TARGETS
00411         | GVRENDER_DOES_TOOLTIPS, /* flags */
00412     4.,                         /* default pad - graph units */
00413     vml_knowncolors,            /* knowncolors */
00414     sizeof(vml_knowncolors) / sizeof(char *),   /* sizeof knowncolors */
00415     RGBA_BYTE,                  /* color_type */
00416 };
00417 
00418 gvdevice_features_t device_features_vml = {
00419     GVDEVICE_DOES_TRUECOLOR,    /* flags */
00420     {0.,0.},                    /* default margin - points */
00421     {0.,0.},                    /* default page width, height - points */
00422     {96.,96.},                  /* default dpi */
00423 };
00424 
00425 gvdevice_features_t device_features_vmlz = {
00426     GVDEVICE_DOES_TRUECOLOR
00427       | GVDEVICE_COMPRESSED_FORMAT,     /* flags */
00428     {0.,0.},                    /* default margin - points */
00429     {0.,0.},                    /* default page width, height - points */
00430     {96.,96.},                  /* default dpi */
00431 };
00432 
00433 gvplugin_installed_t gvrender_vml_types[] = {
00434     {FORMAT_VML, "vml", 1, &vml_engine, &render_features_vml},
00435     {0, NULL, 0, NULL, NULL}
00436 };
00437 
00438 gvplugin_installed_t gvdevice_vml_types[] = {
00439     {FORMAT_VML, "vml:vml", 1, NULL, &device_features_vml},
00440 #if HAVE_LIBZ
00441     {FORMAT_VMLZ, "vmlz:vml", 1, NULL, &device_features_vmlz},
00442 #endif
00443     {0, NULL, 0, NULL, NULL}
00444 };

Generated on Mon Mar 31 19:03:28 2008 for Graphviz by  doxygen 1.5.1