Graphviz  2.29.20120524.0446
plugin/core/gvrender_core_vml.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 
00022 #include "macros.h"
00023 #include "const.h"
00024 
00025 #include "gvplugin_render.h"
00026 #include "gvplugin_device.h"
00027 #include "gvio.h"
00028 #include "memory.h"
00029 
00030 typedef enum { FORMAT_VML, FORMAT_VMLZ, } format_type;
00031 
00032 unsigned int  graphHeight,graphWidth;
00033 
00034 #ifndef HAVE_STRCASECMP
00035 extern int strcasecmp(const char *s1, const char *s2);
00036 #endif
00037 
00038 /*  this is a direct copy fromlib/common/labels.c  */
00039 static int xml_isentity(char *s)
00040 {
00041     s++;                        /* already known to be '&' */
00042     if (*s == '#') {
00043         s++;
00044         if (*s == 'x' || *s == 'X') {
00045             s++;
00046             while ((*s >= '0' && *s <= '9')
00047                    || (*s >= 'a' && *s <= 'f')
00048                    || (*s >= 'A' && *s <= 'F'))
00049                 s++;
00050         } else {
00051             while (*s >= '0' && *s <= '9')
00052                 s++;
00053         }
00054     } else {
00055         while ((*s >= 'a' && *s <= 'z')
00056                || (*s >= 'A' && *s <= 'Z'))
00057             s++;
00058     }
00059     if (*s == ';')
00060         return 1;
00061     return 0;
00062 }
00063 
00064 static void vml_bzptarray(GVJ_t * job, pointf * A, int n)
00065 {
00066     int i;
00067     char *c;
00068 
00069     c = "m ";                   /* first point */
00070     for (i = 0; i < n; i++) {
00071         /* integers only in path! */
00072         gvprintf(job, "%s%.0f,%.0f ", c, A[i].x, graphHeight-A[i].y);
00073         if (i == 0)
00074             c = "c ";           /* second point */
00075         else
00076             c = "";             /* remaining points */
00077     }
00078     gvputs(job, "\"");
00079 }
00080 
00081 static void vml_print_color(GVJ_t * job, gvcolor_t color)
00082 {
00083     switch (color.type) {
00084     case COLOR_STRING:
00085         gvputs(job, color.u.string);
00086         break;
00087     case RGBA_BYTE:
00088         if (color.u.rgba[3] == 0) /* transparent */
00089             gvputs(job, "none");
00090         else
00091             gvprintf(job, "#%02x%02x%02x",
00092                 color.u.rgba[0], color.u.rgba[1], color.u.rgba[2]);
00093         break;
00094     default:
00095         assert(0);              /* internal error */
00096     }
00097 }
00098 
00099 static void vml_grstroke(GVJ_t * job, int filled)
00100 {
00101     obj_state_t *obj = job->obj;
00102 
00103     gvputs(job, "<v:stroke color=\"");
00104     vml_print_color(job, obj->pencolor);
00105     if (obj->penwidth != PENWIDTH_NORMAL)
00106         gvprintf(job, "\" weight=\"%.0fpt", obj->penwidth);
00107     if (obj->pen == PEN_DASHED) {
00108         gvputs(job, "\" dashstyle=\"dash");
00109     } else if (obj->pen == PEN_DOTTED) {
00110         gvputs(job, "\" dashstyle=\"dot");
00111     }
00112     gvputs(job, "\" />");
00113 }
00114 
00115 
00116 static void vml_grfill(GVJ_t * job, int filled)
00117 {
00118     obj_state_t *obj = job->obj;
00119 
00120     if (filled){
00121         gvputs(job, " filled=\"true\" fillcolor=\"");
00122         vml_print_color(job, obj->fillcolor);
00123         gvputs(job, "\" ");
00124     }else{
00125         gvputs(job, " filled=\"false\" ");
00126     }
00127 }
00128 
00129 /*  html_string is a modified version of xml_string  */
00130 char *html_string(char *s)
00131 {
00132     static char *buf = NULL;
00133     static int bufsize = 0;
00134     char *p, *sub, *prev = NULL;
00135     int len, pos = 0;
00136     int temp,cnt,remaining=0;
00137     char workstr[16];
00138     long unsigned int charnum=0;
00139     unsigned char byte;
00140     unsigned char mask;
00141 
00142 
00143     if (!buf) {
00144         bufsize = 64;
00145         buf = gmalloc(bufsize);
00146     }
00147     p = buf;
00148     while (s && *s) {
00149         if (pos > (bufsize - 8)) {
00150             bufsize *= 2;
00151             buf = grealloc(buf, bufsize);
00152             p = buf + pos;
00153         }
00154         /* escape '&' only if not part of a legal entity sequence */
00155         if (*s == '&' && !(xml_isentity(s))) {
00156             sub = "&amp;";
00157             len = 5;
00158         }
00159         /* '<' '>' are safe to substitute even if string is already UTF-8 coded
00160          * since UTF-8 strings won't contain '<' or '>' */
00161         else if (*s == '<') {
00162             sub = "&lt;";
00163             len = 4;
00164         }
00165         else if (*s == '>') {
00166             sub = "&gt;";
00167             len = 4;
00168         }
00169         else if (*s == '-') {   /* can't be used in xml comment strings */
00170             sub = "&#45;";
00171             len = 5;
00172         }
00173         else if (*s == ' ' && prev && *prev == ' ') {
00174             /* substitute 2nd and subsequent spaces with required_spaces */
00175             sub = "&#160;";  /* inkscape doesn't recognise &nbsp; */
00176             len = 6;
00177         }
00178         else if (*s == '"') {
00179             sub = "&quot;";
00180             len = 6;
00181         }
00182         else if (*s == '\'') {
00183             sub = "&#39;";
00184             len = 5;
00185         }
00186         else if ((unsigned char)*s > 127) {
00187             byte=(unsigned char)*s;
00188             cnt=0;
00189             for (mask=127; mask < byte; mask=mask >>1){
00190               cnt++;
00191               byte=byte & mask;
00192             }
00193             if (cnt>1){
00194               charnum=byte;
00195               remaining=cnt-1;
00196             }else{
00197               charnum=charnum<<6;
00198               charnum+=byte;
00199               remaining--;
00200             }
00201             if (remaining>0){
00202               s++;
00203               continue;
00204             }           
00205             /* we will build the html value right-to-left
00206              * (least significant-to-most)  */
00207             workstr[15]=';';
00208             sub=&workstr[14];
00209             len=3; /*  &#  + ;  */
00210             do {
00211               temp=charnum%10;
00212               *(sub--)=(char)((int)'0'+ temp);
00213               charnum/=10;
00214               len++;
00215               if (len>12){      /* 12 is arbitrary, but clearly in error  */
00216                 fprintf(stderr, "Error during conversion to \"UTF-8\".  Quiting.\n");
00217                 exit(1);
00218               }
00219             } while (charnum>0);
00220             *(sub--)='#';
00221             *(sub)='&';
00222         }
00223         else {
00224             sub = s;
00225             len = 1;
00226         }
00227         while (len--) {
00228             *p++ = *sub++;
00229             pos++;
00230         }
00231         prev = s;
00232         s++;
00233     }
00234     *p = '\0';
00235     return buf;
00236 }
00237 static void vml_comment(GVJ_t * job, char *str)
00238 {
00239     gvputs(job, "      <!-- ");
00240     gvputs(job, html_string(str));
00241     gvputs(job, " -->\n");
00242 }
00243 static void vml_begin_job(GVJ_t * job)
00244 {
00245     gvputs(job, "<HTML>\n");
00246     gvputs(job, "\n<!-- Generated by ");
00247     gvputs(job, html_string(job->common->info[0]));
00248     gvputs(job, " version ");
00249     gvputs(job, html_string(job->common->info[1]));
00250     gvputs(job, " (");
00251     gvputs(job, html_string(job->common->info[2]));
00252     gvputs(job, ")\n-->\n");
00253 }
00254 
00255 static void vml_begin_graph(GVJ_t * job)
00256 {
00257     obj_state_t *obj = job->obj;
00258     char *name;
00259 
00260     graphHeight =(int)(job->bb.UR.y - job->bb.LL.y);
00261     graphWidth  =(int)(job->bb.UR.x - job->bb.LL.x);
00262 
00263     gvputs(job, "<HEAD>");
00264     gvputs(job, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
00265  
00266 
00267 #ifndef WITH_CGRAPH
00268     name = obj->u.g->name;
00269 #else
00270     name = agnameof(obj->u.g);
00271 #endif
00272     if (name[0]) {
00273         gvputs(job, "<TITLE>");
00274         gvputs(job, html_string(name));
00275         gvputs(job, "</TITLE>");
00276     }
00277     gvprintf(job, "<!-- Pages: %d -->\n", job->pagesArraySize.x * job->pagesArraySize.y);
00278 
00279 /*  the next chunk and all the "DIV" stuff is not required,
00280  *  but it helps with non-IE browsers  */
00281     gvputs(job, "   <SCRIPT LANGUAGE='Javascript'>\n");
00282     gvputs(job, "   function browsercheck()\n");
00283     gvputs(job, "   {\n");
00284     gvputs(job, "      var ua = window.navigator.userAgent\n");
00285     gvputs(job, "      var msie = ua.indexOf ( 'MSIE ' )\n");
00286     gvputs(job, "      var ievers;\n");
00287     gvputs(job, "      var item;\n");
00288     gvputs(job, "      var VMLyes=new Array('_VML1_','_VML2_');\n");
00289     gvputs(job, "      var VMLno=new Array('_notVML1_','_notVML2_');\n");
00290     gvputs(job, "      if ( msie > 0 ){      // If Internet Explorer, return version number\n");
00291     gvputs(job, "         ievers= parseInt (ua.substring (msie+5, ua.indexOf ('.', msie )))\n");
00292     gvputs(job, "      }\n");
00293     gvputs(job, "      if (ievers>=5){\n");
00294     gvputs(job, "       for (x in VMLyes){\n");
00295     gvputs(job, "         item = document.getElementById(VMLyes[x]);\n");
00296     gvputs(job, "         if (item) {\n");
00297     gvputs(job, "           item.style.visibility='visible';\n");
00298     gvputs(job, "         }\n");
00299     gvputs(job, "       }\n");
00300     gvputs(job, "       for (x in VMLno){\n");
00301     gvputs(job, "         item = document.getElementById(VMLno[x]);\n");
00302     gvputs(job, "         if (item) {\n");
00303     gvputs(job, "           item.style.visibility='hidden';\n");
00304     gvputs(job, "         }\n");
00305     gvputs(job, "       }\n");
00306     gvputs(job, "     }else{\n");
00307     gvputs(job, "       for (x in VMLyes){\n");
00308     gvputs(job, "         item = document.getElementById(VMLyes[x]);\n");
00309     gvputs(job, "         if (item) {\n");
00310     gvputs(job, "           item.style.visibility='hidden';\n");
00311     gvputs(job, "         }\n");
00312     gvputs(job, "       }\n");
00313     gvputs(job, "       for (x in VMLno){\n");
00314     gvputs(job, "         item = document.getElementById(VMLno[x]);\n");
00315     gvputs(job, "         if (item) {\n");
00316     gvputs(job, "           item.style.visibility='visible';\n");
00317     gvputs(job, "         }\n");
00318     gvputs(job, "       }\n");
00319     gvputs(job, "     }\n");
00320     gvputs(job, "   }\n");
00321     gvputs(job, "   </SCRIPT>\n");
00322 
00323     gvputs(job, "</HEAD>");
00324     gvputs(job, "<BODY onload='browsercheck();'>\n");
00325     /* add 10pt pad to the bottom of the graph */
00326     gvputs(job, "<DIV id='_VML1_' style=\"position:relative; display:inline; visibility:hidden");
00327     gvprintf(job, " width: %dpt; height: %dpt\">\n", graphWidth, 10+graphHeight);
00328     gvputs(job, "<STYLE>\n");
00329     gvputs(job, "v\\:* { behavior: url(#default#VML);display:inline-block}\n"); 
00330     gvputs(job, "</STYLE>\n"); 
00331     gvputs(job, "<xml:namespace ns=\"urn:schemas-microsoft-com:vml\" prefix=\"v\" />\n"); 
00332 
00333     gvputs(job, " <v:group style=\"position:relative; ");
00334     gvprintf(job, " width: %dpt; height: %dpt\"", graphWidth, graphHeight);
00335     gvprintf(job, " coordorigin=\"0,0\" coordsize=\"%d,%d\" >", graphWidth, graphHeight);
00336 }
00337 
00338 static void vml_end_graph(GVJ_t * job)
00339 {
00340    gvputs(job, "</v:group>\n");
00341    gvputs(job, "</DIV>\n");
00342    /* add 10pt pad to the bottom of the graph */
00343    gvputs(job, "<DIV id='_VML2_' style=\"position:relative;visibility:hidden\">\n");
00344    gvputs(job, "<!-- insert any other html content here -->\n");
00345    gvputs(job, "</DIV>\n");
00346    gvputs(job, "<DIV id='_notVML1_' style=\"position:relative;\">\n");
00347    gvputs(job, "<!-- this should only display on NON-IE browsers -->\n");
00348    gvputs(job, "<H2>Sorry, this diagram will only display correctly on Internet Explorer 5 (and up) browsers.</H2>\n");
00349    gvputs(job, "</DIV>\n");
00350    gvputs(job, "<DIV id='_notVML2_' style=\"position:relative;\">\n");
00351    gvputs(job, "<!-- insert any other NON-IE html content here -->\n");
00352    gvputs(job, "</DIV>\n");
00353 
00354    gvputs(job, "</BODY>\n</HTML>\n");
00355 }
00356 
00357 static void
00358 vml_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id)
00359 {
00360     gvputs(job, "<a");
00361     if (href && href[0])
00362         gvprintf(job, " href=\"%s\"", html_string(href));
00363     if (tooltip && tooltip[0])
00364         gvprintf(job, " title=\"%s\"", html_string(tooltip));
00365     if (target && target[0])
00366         gvprintf(job, " target=\"%s\"", html_string(target));
00367     gvputs(job, ">\n");
00368 }
00369 
00370 static void vml_end_anchor(GVJ_t * job)
00371 {
00372     gvputs(job, "</a>\n");
00373 }
00374 
00375 static void vml_textpara(GVJ_t * job, pointf p, textpara_t * para)
00376 {
00377     pointf p1,p2;
00378     obj_state_t *obj = job->obj;
00379 
00380     switch (para->just) {
00381     case 'l':
00382         p1.x=p.x;
00383         break;
00384     case 'r':
00385         p1.x=p.x-para->width;
00386         break;
00387     default:
00388     case 'n':
00389         p1.x=p.x-(para->width/2);
00390         break;
00391     }
00392     p2.x=p1.x+para->width;
00393     if (para->height <  para->fontsize){
00394       para->height = 1 + (1.1*para->fontsize);
00395     }
00396 
00397     p1.x-=8; /* vml textbox margin fudge factor */
00398     p2.x+=8; /* vml textbox margin fudge factor */
00399     p2.y=graphHeight-(p.y);
00400     p1.y=(p2.y-para->height);
00401     /* text "y" was too high
00402      * Graphviz uses "baseline", VML seems to use bottom of descenders - so we fudge a little
00403      * (heuristics - based on eyeballs)  */
00404     if (para->fontsize <12.){ /*     see graphs/directed/arrows.gv  */
00405       p1.y+=1.4+para->fontsize/5; /* adjust by approx. descender */
00406       p2.y+=1.4+para->fontsize/5; /* adjust by approx. descender */
00407     }else{
00408       p1.y+=2+para->fontsize/5; /* adjust by approx. descender */
00409       p2.y+=2+para->fontsize/5; /* adjust by approx. descender */
00410     }
00411 
00412     gvprintf(job, "<v:rect style=\"position:absolute; ");
00413     gvprintf(job, " left: %.2f; top: %.2f;", p1.x, p1.y);
00414     gvprintf(job, " width: %.2f; height: %.2f\"", p2.x-p1.x, p2.y-p1.y);
00415     gvputs(job, " stroked=\"false\" filled=\"false\">\n");
00416     gvputs(job, "<v:textbox inset=\"0,0,0,0\" style=\"position:absolute; v-text-wrapping:'false';padding:'0';");
00417 
00418     if (para->postscript_alias) {
00419         gvprintf(job, "font-family: '%s';", para->postscript_alias->family);
00420         if (para->postscript_alias->weight)
00421             gvprintf(job, "font-weight: %s;", para->postscript_alias->weight);
00422         if (para->postscript_alias->stretch)
00423             gvprintf(job, "font-stretch: %s;", para->postscript_alias->stretch);
00424         if (para->postscript_alias->style)
00425             gvprintf(job, "font-style: %s;", para->postscript_alias->style);
00426     }
00427     else {
00428         gvprintf(job, "font-family: \'%s\';", para->fontname);
00429     }
00430     gvprintf(job, " font-size: %.2fpt;", para->fontsize);
00431     switch (obj->pencolor.type) {
00432     case COLOR_STRING:
00433         if (strcasecmp(obj->pencolor.u.string, "black"))
00434             gvprintf(job, "color:%s;", obj->pencolor.u.string);
00435         break;
00436     case RGBA_BYTE:
00437         gvprintf(job, "color:#%02x%02x%02x;",
00438                 obj->pencolor.u.rgba[0], obj->pencolor.u.rgba[1], obj->pencolor.u.rgba[2]);
00439         break;
00440     default:
00441         assert(0);              /* internal error */
00442     }
00443     gvputs(job, "\"><center>");
00444     gvputs(job, html_string(para->str));
00445     gvputs(job, "</center></v:textbox>\n"); 
00446     gvputs(job, "</v:rect>\n");
00447 }
00448 
00449 static void vml_ellipse(GVJ_t * job, pointf * A, int filled)
00450 {   
00451     double dx, dy, left, right, top, bottom;
00452 
00453     /* A[] contains 2 points: the center and corner. */
00454     gvputs(job, "  <v:oval style=\"position:absolute;");
00455 
00456     dx=A[1].x-A[0].x;
00457     dy=A[1].y-A[0].y;
00458 
00459     top=graphHeight-(A[0].y+dy);
00460     bottom=top+dy+dy;
00461     left=A[0].x - dx;
00462     right=A[1].x;
00463     gvprintf(job, " left: %.2f; top: %.2f;",left, top);
00464     gvprintf(job, " width: %.2f; height: %.2f\"", 2*dx, 2*dy);
00465 
00466     vml_grfill(job, filled);
00467     gvputs(job, " >");
00468     vml_grstroke(job, filled);
00469     gvputs(job, "</v:oval>\n");
00470 }
00471 
00472 static void
00473 vml_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
00474               int arrow_at_end, int filled)
00475 {
00476     gvputs(job, " <v:shape style=\"position:absolute; ");
00477     gvprintf(job, " width: %d; height: %d\"", graphWidth, graphHeight);
00478 
00479     vml_grfill(job, filled);
00480     gvputs(job, " >");
00481     vml_grstroke(job, filled);
00482     gvputs(job, "<v:path  v=\"");
00483     vml_bzptarray(job, A, n);
00484     gvputs(job, "/></v:shape>\n");
00485 }
00486 
00487 static void vml_polygon(GVJ_t * job, pointf * A, int n, int filled)
00488 {
00489     int i;
00490     double px,py;
00491 
00492     gvputs(job, " <v:shape style=\"position:absolute; ");
00493     gvprintf(job, " width: %d; height: %d\"", graphWidth, graphHeight);
00494     vml_grfill(job, filled);
00495     gvputs(job, " >");
00496     vml_grstroke(job, filled);
00497 
00498     gvputs(job, "<v:path  v=\"");
00499     for (i = 0; i < n; i++)
00500     {
00501         px=A[i].x;
00502         py= (graphHeight-A[i].y);
00503         if (i==0){
00504           gvputs(job, "m ");
00505         }
00506         /* integers only in path */
00507         gvprintf(job, "%.0f %.0f ", px, py);
00508         if (i==0) gvputs(job, "l ");
00509         if (i==n-1) gvputs(job, "x e \"/>");
00510     }
00511     gvputs(job, "</v:shape>\n");
00512 }
00513 
00514 static void vml_polyline(GVJ_t * job, pointf * A, int n)
00515 {
00516     int i;
00517 
00518     gvputs(job, " <v:shape style=\"position:absolute; ");
00519     gvprintf(job, " width: %d; height: %d\" filled=\"false\">", graphWidth, graphHeight);
00520     gvputs(job, "<v:path v=\"");
00521     for (i = 0; i < n; i++)
00522     {
00523         if (i==0) gvputs(job, " m ");
00524         gvprintf(job, "%.0f,%.0f ", A[i].x, graphHeight-A[i].y);
00525         if (i==0) gvputs(job, " l ");
00526         if (i==n-1) gvputs(job, " e "); /* no x here for polyline */
00527     }
00528     gvputs(job, "\"/>");
00529     vml_grstroke(job, 0);                 /* no fill here for polyline */
00530     gvputs(job, "</v:shape>\n");
00531 }
00532 
00533 /* color names from 
00534   http://msdn.microsoft.com/en-us/library/bb250525(VS.85).aspx#t.color
00535 */
00536 /* NB.  List must be LANG_C sorted */
00537 static char *vml_knowncolors[] = {
00538      "aqua",  "black",  "blue", "fuchsia", 
00539      "gray",  "green", "lime",  "maroon", 
00540      "navy",  "olive",  "purple",  "red",
00541      "silver",  "teal",  "white",  "yellow"
00542 };
00543 
00544 gvrender_engine_t vml_engine = {
00545     vml_begin_job,
00546     0,                          /* vml_end_job */
00547     vml_begin_graph,
00548     vml_end_graph,
00549     0,                          /* vml_begin_layer */
00550     0,                          /* vml_end_layer */
00551     0,                          /* vml_begin_page */
00552     0,                          /* vml_end_page */
00553     0,                          /* vml_begin_cluster */
00554     0,                          /* vml_end_cluster */
00555     0,                          /* vml_begin_nodes */
00556     0,                          /* vml_end_nodes */
00557     0,                          /* vml_begin_edges */
00558     0,                          /* vml_end_edges */
00559     0,                          /* vml_begin_node */
00560     0,                          /* vml_end_node */
00561     0,                          /* vml_begin_edge */
00562     0,                          /* vml_end_edge */
00563     vml_begin_anchor,
00564     vml_end_anchor,
00565     0,                          /* vml_begin_label */
00566     0,                          /* vml_end_label */
00567     vml_textpara,
00568     0,                          /* vml_resolve_color */
00569     vml_ellipse,
00570     vml_polygon,
00571     vml_bezier,
00572     vml_polyline,
00573     vml_comment,
00574     0,                          /* vml_library_shape */
00575 };
00576 
00577 gvrender_features_t render_features_vml = {
00578     GVRENDER_Y_GOES_DOWN
00579         | GVRENDER_DOES_TRANSFORM
00580         | GVRENDER_DOES_LABELS
00581         | GVRENDER_DOES_MAPS
00582         | GVRENDER_DOES_TARGETS
00583         | GVRENDER_DOES_TOOLTIPS, /* flags */
00584     0.,                         /* default pad - graph units */
00585     vml_knowncolors,            /* knowncolors */
00586     sizeof(vml_knowncolors) / sizeof(char *),   /* sizeof knowncolors */
00587     RGBA_BYTE,                  /* color_type */
00588 };
00589 
00590 gvdevice_features_t device_features_vml = {
00591     GVDEVICE_DOES_TRUECOLOR,    /* flags */
00592     {0.,0.},                    /* default margin - points */
00593     {0.,0.},                    /* default page width, height - points */
00594     {96.,96.},                  /* default dpi */
00595 };
00596 
00597 gvdevice_features_t device_features_vmlz = {
00598     GVDEVICE_DOES_TRUECOLOR
00599       | GVDEVICE_COMPRESSED_FORMAT,     /* flags */
00600     {0.,0.},                    /* default margin - points */
00601     {0.,0.},                    /* default page width, height - points */
00602     {96.,96.},                  /* default dpi */
00603 };
00604 
00605 gvplugin_installed_t gvrender_vml_types[] = {
00606     {FORMAT_VML, "vml", 1, &vml_engine, &render_features_vml},
00607     {0, NULL, 0, NULL, NULL}
00608 };
00609 
00610 gvplugin_installed_t gvdevice_vml_types[] = {
00611     {FORMAT_VML, "vml:vml", 1, NULL, &device_features_vml},
00612 #if HAVE_LIBZ
00613     {FORMAT_VMLZ, "vmlz:vml", 1, NULL, &device_features_vmlz},
00614 #endif
00615     {0, NULL, 0, NULL, NULL}
00616 };
00617