]> granicus.if.org Git - graphviz/commitdiff
Steve Roush's update to the -Tvml driver
authorellson <devnull@localhost>
Tue, 24 Feb 2009 15:41:11 +0000 (15:41 +0000)
committerellson <devnull@localhost>
Tue, 24 Feb 2009 15:41:11 +0000 (15:41 +0000)
plugin/core/gvrender_core_vml.c

index 7b88ae4b8614e173e873c76f7af9b10b75775ca5..2d1305f9ca68ee381a9139df682a7177e3cdae4e 100644 (file)
 #include "gvplugin_render.h"
 #include "gvplugin_device.h"
 #include "gvio.h"
+#include "memory.h"
 
 typedef enum { FORMAT_VML, FORMAT_VMLZ, } format_type;
 
-extern char *xml_string(char *str);
-
-char graphcoords[256];
-
-#if defined(WIN32) && !defined(__MINGW32_VERSION)      /* MinGW already defines snprintf */
-static int
-snprintf (char *str, int n, char *fmt, ...)
+unsigned int  graphHeight,graphWidth;
+/*  this is a direct copy fromlib/common/labels.c  */
+static int xml_isentity(char *s)
 {
-int ret;
-va_list a;
-va_start (a, fmt);
-ret = _vsnprintf (str, n, fmt, a);
-va_end (a);
-return ret;
+    s++;                       /* already known to be '&' */
+    if (*s == '#') {
+       s++;
+       if (*s == 'x' || *s == 'X') {
+           s++;
+           while ((*s >= '0' && *s <= '9')
+                  || (*s >= 'a' && *s <= 'f')
+                  || (*s >= 'A' && *s <= 'F'))
+               s++;
+       } else {
+           while (*s >= '0' && *s <= '9')
+               s++;
+       }
+    } else {
+       while ((*s >= 'a' && *s <= 'z')
+              || (*s >= 'A' && *s <= 'Z'))
+           s++;
+    }
+    if (*s == ';')
+       return 1;
+    return 0;
 }
-#endif
 
 static void vml_bzptarray(GVJ_t * job, pointf * A, int n)
 {
@@ -55,12 +66,14 @@ static void vml_bzptarray(GVJ_t * job, pointf * A, int n)
 
     c = "m ";                  /* first point */
     for (i = 0; i < n; i++) {
-       gvprintf(job, "%s%.0f,%.0f ", c, A[i].x, -A[i].y);
+        /* integers only in path! */
+       gvprintf(job, "%s%.0f,%.0f ", c, A[i].x, graphHeight-A[i].y);
        if (i == 0)
            c = "c ";           /* second point */
        else
            c = "";             /* remaining points */
     }
+    gvputs(job, "\"");
 }
 
 static void vml_print_color(GVJ_t * job, gvcolor_t color)
@@ -85,15 +98,10 @@ static void vml_grstroke(GVJ_t * job, int filled)
 {
     obj_state_t *obj = job->obj;
 
-    gvputs(job, "<v:stroke fillcolor=\"");
-    if (filled)
-       vml_print_color(job, obj->fillcolor);
-    else
-       gvputs(job, "none");
-    gvputs(job, "\" strokecolor=\"");
+    gvputs(job, "<v:stroke color=\"");
     vml_print_color(job, obj->pencolor);
     if (obj->penwidth != PENWIDTH_NORMAL)
-       gvprintf(job, "\" stroke-weight=\"%g", obj->penwidth);
+       gvprintf(job, "\" weight=\"%.0fpt", obj->penwidth);
     if (obj->pen == PEN_DASHED) {
        gvputs(job, "\" dashstyle=\"dash");
     } else if (obj->pen == PEN_DOTTED) {
@@ -102,139 +110,315 @@ static void vml_grstroke(GVJ_t * job, int filled)
     gvputs(job, "\" />");
 }
 
-static void vml_grstrokeattr(GVJ_t * job)
-{
-    obj_state_t *obj = job->obj;
-
-    gvputs(job, " strokecolor=\"");
-    vml_print_color(job, obj->pencolor);
-    if (obj->penwidth != PENWIDTH_NORMAL)
-       gvprintf(job, "\" stroke-weight=\"%g", obj->penwidth);
-    if (obj->pen == PEN_DASHED) {
-       gvputs(job, "\" dashstyle=\"dash");
-    } else if (obj->pen == PEN_DOTTED) {
-       gvputs(job, "\" dashstyle=\"dot");
-    }
-    gvputs(job, "\"");
-}
 
 static void vml_grfill(GVJ_t * job, int filled)
 {
     obj_state_t *obj = job->obj;
 
-    gvputs(job, "<v:fill color=\"");
-    if (filled)
+    if (filled){
+        gvputs(job, " filled=\"true\" fillcolor=\"");
        vml_print_color(job, obj->fillcolor);
-    else
-       gvputs(job, "none");
-    gvputs(job, "\" />");
+        gvputs(job, "\" ");
+    }else{
+       gvputs(job, " filled=\"false\" ");
+    }
 }
 
+/*  html_string is a modified version of xml_string  */
+char *html_string(char *s)
+{
+    static char *buf = NULL;
+    static int bufsize = 0;
+    char *p, *sub, *prev = NULL;
+    int len, pos = 0;
+    int temp,cnt,remaining=0;
+    char workstr[16];
+    long unsigned int charnum=0;
+    unsigned char byte;
+    unsigned char mask;
+
+
+    if (!buf) {
+       bufsize = 64;
+       buf = gmalloc(bufsize);
+    }
+    p = buf;
+    while (s && *s) {
+       if (pos > (bufsize - 8)) {
+           bufsize *= 2;
+           buf = grealloc(buf, bufsize);
+           p = buf + pos;
+       }
+       /* escape '&' only if not part of a legal entity sequence */
+       if (*s == '&' && !(xml_isentity(s))) {
+           sub = "&amp;";
+           len = 5;
+       }
+       /* '<' '>' are safe to substitute even if string is already UTF-8 coded
+        * since UTF-8 strings won't contain '<' or '>' */
+       else if (*s == '<') {
+           sub = "&lt;";
+           len = 4;
+       }
+       else if (*s == '>') {
+           sub = "&gt;";
+           len = 4;
+       }
+       else if (*s == '-') {   /* can't be used in xml comment strings */
+           sub = "&#45;";
+           len = 5;
+       }
+       else if (*s == ' ' && prev && *prev == ' ') {
+           /* substitute 2nd and subsequent spaces with required_spaces */
+           sub = "&#160;";  /* inkscape doesn't recognise &nbsp; */
+           len = 6;
+       }
+       else if (*s == '"') {
+           sub = "&quot;";
+           len = 6;
+       }
+       else if (*s == '\'') {
+           sub = "&#39;";
+           len = 5;
+       }
+       else if ((unsigned char)*s > 127) {
+            byte=(unsigned char)*s;
+            cnt=0;
+            for (mask=127; mask < byte; mask=mask >>1){
+              cnt++;
+              byte=byte & mask;
+            }
+            if (cnt>1){
+              charnum=byte;
+              remaining=cnt-1;
+            }else{
+              charnum=charnum<<6;
+              charnum+=byte;
+              remaining--;
+            }
+            if (remaining>0){
+              s++;
+              continue;
+            }           
+            /* we will build the html value right-to-left
+             * (least significant-to-most)  */
+            workstr[15]=';';
+            sub=&workstr[14];
+            len=3; /*  &#  + ;  */
+            do {
+              temp=charnum%10;
+              *(sub--)=(char)((int)'0'+ temp);
+              charnum/=10;
+              len++;
+              if (len>12){      /* 12 is arbitrary, but clearly in error  */
+                fprintf(stderr, "Error during conversion to \"UTF-8\".  Quiting.\n");
+                exit(1);
+              }
+            } while (charnum>0);
+            *(sub--)='#';
+            *(sub)='&';
+       }
+       else {
+           sub = s;
+           len = 1;
+       }
+       while (len--) {
+           *p++ = *sub++;
+           pos++;
+       }
+       prev = s;
+       s++;
+    }
+    *p = '\0';
+    return buf;
+}
 static void vml_comment(GVJ_t * job, char *str)
 {
     gvputs(job, "      <!-- ");
-    gvputs(job, xml_string(str));
+    gvputs(job, html_string(str));
     gvputs(job, " -->\n");
 }
-
 static void vml_begin_job(GVJ_t * job)
 {
-    gvputs(job, "<?xml version=\"1.1\" encoding=\"UTF-8\" ?>\n");
-
-    gvputs(job, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ");
-    gvputs(job, "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
-    gvputs(job, "<html xml:lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\" ");
-    gvputs(job, "xmlns:v=\"urn:schemas-microsoft-com:vml\""); 
-    gvputs(job, ">"); 
-
+    gvputs(job, "<HTML>\n");
     gvputs(job, "\n<!-- Generated by ");
-    gvputs(job, xml_string(job->common->info[0]));
+    gvputs(job, html_string(job->common->info[0]));
     gvputs(job, " version ");
-    gvputs(job, xml_string(job->common->info[1]));
+    gvputs(job, html_string(job->common->info[1]));
     gvputs(job, " (");
-    gvputs(job, xml_string(job->common->info[2]));
-    gvputs(job, ")\n");
-    gvputs(job, " -->\n");
+    gvputs(job, html_string(job->common->info[2]));
+    gvputs(job, ")\n-->\n");
 }
 
 static void vml_begin_graph(GVJ_t * job)
 {
     obj_state_t *obj = job->obj;
 
-    gvputs(job, "<head>");
-    if (agnameof(obj->u.g)[0]) {
-        gvputs(job, "<title>");
-       gvputs(job, xml_string(agnameof(obj->u.g)));
-        gvputs(job, "</title>");
-    }
-    gvprintf(job, "<!-- Pages: %d -->\n</head>\n", job->pagesArraySize.x * job->pagesArraySize.y);
-
-    snprintf(graphcoords, sizeof(graphcoords), "style=\"width: %.0fpt; height: %.0fpt\" coordsize=\"%.0f,%.0f\" coordorigin=\"-4,-%.0f\"",
-       job->width*.75, job->height*.75,
-        job->width*.75, job->height*.75,
-        job->height*.75 - 4);
-
-    gvprintf(job, "<body>\n<div class=\"graph\" %s>\n", graphcoords);
-    gvputs(job, "<style type=\"text/css\">\nv\\:* {\nbehavior: url(#default#VML);display:inline-block;position: absolute; left: 0px; top: 0px;\n}\n</style>\n");
-/*    graphcoords[0] = '\0'; */
+    graphHeight =(int)(job->bb.UR.y - job->bb.LL.y);
+    graphWidth  =(int)(job->bb.UR.x - job->bb.LL.x);
 
+    gvputs(job, "<HEAD>");
+    gvputs(job, "<META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
+    if (obj->u.g->name[0]) {
+        gvputs(job, "<TITLE>");
+       gvputs(job, html_string(obj->u.g->name));
+        gvputs(job, "</TITLE>");
+    }
+    gvprintf(job, "<!-- Pages: %d -->\n", job->pagesArraySize.x * job->pagesArraySize.y);
+
+/*  the next chunk and all the "DIV" stuff is not required,
+ *  but it helps with non-IE browsers  */
+    gvputs(job, "   <SCRIPT LANGUAGE='Javascript'>\n");
+    gvputs(job, "   function browsercheck()\n");
+    gvputs(job, "   {\n");
+    gvputs(job, "      var ua = window.navigator.userAgent\n");
+    gvputs(job, "      var msie = ua.indexOf ( 'MSIE ' )\n");
+    gvputs(job, "      var ievers;\n");
+    gvputs(job, "      var item;\n");
+    gvputs(job, "      var VMLyes=new Array('_VML1_','_VML2_');\n");
+    gvputs(job, "      var VMLno=new Array('_notVML1_','_notVML2_');\n");
+    gvputs(job, "      if ( msie > 0 ){      // If Internet Explorer, return version number\n");
+    gvputs(job, "         ievers= parseInt (ua.substring (msie+5, ua.indexOf ('.', msie )))\n");
+    gvputs(job, "      }\n");
+    gvputs(job, "      if (ievers>=5){\n");
+    gvputs(job, "       for (x in VMLyes){\n");
+    gvputs(job, "         item = document.getElementById(VMLyes[x]);\n");
+    gvputs(job, "         if (item) {\n");
+    gvputs(job, "           item.style.visibility='visible';\n");
+    gvputs(job, "         }\n");
+    gvputs(job, "       }\n");
+    gvputs(job, "       for (x in VMLno){\n");
+    gvputs(job, "         item = document.getElementById(VMLno[x]);\n");
+    gvputs(job, "         if (item) {\n");
+    gvputs(job, "           item.style.visibility='hidden';\n");
+    gvputs(job, "         }\n");
+    gvputs(job, "       }\n");
+    gvputs(job, "     }else{\n");
+    gvputs(job, "       for (x in VMLyes){\n");
+    gvputs(job, "         item = document.getElementById(VMLyes[x]);\n");
+    gvputs(job, "         if (item) {\n");
+    gvputs(job, "           item.style.visibility='hidden';\n");
+    gvputs(job, "         }\n");
+    gvputs(job, "       }\n");
+    gvputs(job, "       for (x in VMLno){\n");
+    gvputs(job, "         item = document.getElementById(VMLno[x]);\n");
+    gvputs(job, "         if (item) {\n");
+    gvputs(job, "           item.style.visibility='visible';\n");
+    gvputs(job, "         }\n");
+    gvputs(job, "       }\n");
+    gvputs(job, "     }\n");
+    gvputs(job, "   }\n");
+    gvputs(job, "   </SCRIPT>\n");
+
+    gvputs(job, "</HEAD>");
+    gvputs(job, "<BODY onload='browsercheck();'>\n");
+    /* add 10pt pad to the bottom of the graph */
+    gvputs(job, "<DIV id='_VML1_' style=\"position:relative; display:inline; visibility:hidden");
+    gvprintf(job, " width: %dpt; height: %dpt\">\n", graphWidth, 10+graphHeight);
+    gvputs(job, "<STYLE>\n");
+    gvputs(job, "v\\:* { behavior: url(#default#VML);display:inline-block}\n"); 
+    gvputs(job, "</STYLE>\n"); 
+    gvputs(job, "<xml:namespace ns=\"urn:schemas-microsoft-com:vml\" prefix=\"v\" />\n"); 
+
+    gvputs(job, " <v:group style=\"position:relative; ");
+    gvprintf(job, " width: %dpt; height: %dpt\"", graphWidth, graphHeight);
+    gvprintf(job, " coordorigin=\"0,0\" coordsize=\"%d,%d\" >", graphWidth, graphHeight);
 }
 
 static void vml_end_graph(GVJ_t * job)
 {
-    gvputs(job, "</div>\n</body>\n");
+   gvputs(job, "</v:group>\n");
+   gvputs(job, "</DIV>\n");
+   /* add 10pt pad to the bottom of the graph */
+   gvputs(job, "<DIV id='_VML2_' style=\"position:relative;visibility:hidden\">\n");
+   gvputs(job, "<!-- insert any other html content here -->\n");
+   gvputs(job, "</DIV>\n");
+   gvputs(job, "<DIV id='_notVML1_' style=\"position:relative;\">\n");
+   gvputs(job, "<!-- this should only display on NON-IE browsers -->\n");
+   gvputs(job, "<H2>Sorry, this diagram will only display correctly on Internet Explorer 5 (and up) browsers.</H2>\n");
+   gvputs(job, "</DIV>\n");
+   gvputs(job, "<DIV id='_notVML2_' style=\"position:relative;\">\n");
+   gvputs(job, "<!-- insert any other NON-IE html content here -->\n");
+   gvputs(job, "</DIV>\n");
+
+   gvputs(job, "</BODY>\n</HTML>");
 }
 
 static void
 vml_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id)
 {
-    gvputs(job, "      <a");
+    gvputs(job, "<a");
     if (href && href[0])
-       gvprintf(job, " href=\"%s\"", xml_string(href));
+       gvprintf(job, " href=\"%s\"", html_string(href));
     if (tooltip && tooltip[0])
-       gvprintf(job, " title=\"%s\"", xml_string(tooltip));
+       gvprintf(job, " title=\"%s\"", html_string(tooltip));
     if (target && target[0])
-       gvprintf(job, " target=\"%s\"", xml_string(target));
+       gvprintf(job, " target=\"%s\"", html_string(target));
     gvputs(job, ">\n");
 }
 
 static void vml_end_anchor(GVJ_t * job)
 {
-    gvputs(job, "      </a>\n");
+    gvputs(job, "</a>\n");
 }
 
 static void vml_textpara(GVJ_t * job, pointf p, textpara_t * para)
 {
+    pointf p1,p2;
     obj_state_t *obj = job->obj;
 
-    gvputs(job, "        <div");
     switch (para->just) {
     case 'l':
-       gvputs(job, " style=\"text-align: left; ");
+       p1.x=p.x;
        break;
     case 'r':
-       gvputs(job, " style=\"text-align: right; ");
+       p1.x=p.x-para->width;
        break;
     default:
     case 'n':
-       gvputs(job, " style=\"text-align: center; ");
+       p1.x=p.x-(para->width/2);
        break;
     }
-    gvprintf(job, "position: absolute; left: %gpx; top: %gpx;", p.x/.75, job->height - p.y/.75 - 14);
+    p2.x=p1.x+para->width;
+    if (para->height <  para->fontsize){
+      para->height = 1 + (1.1*para->fontsize);
+    }
+
+    p1.x-=8; /* vml textbox margin fudge factor */
+    p2.x+=8; /* vml textbox margin fudge factor */
+    p2.y=graphHeight-(p.y);
+    p1.y=(p2.y-para->height);
+    /* text "y" was too high
+     * Graphviz uses "baseline", VML seems to use bottom of descenders - so we fudge a little
+     * (heuristics - based on eyeballs)  */
+    if (para->fontsize <12.){ /*     see graphs/directed/arrows.dot  */
+      p1.y+=1.4+para->fontsize/5; /* adjust by approx. descender */
+      p2.y+=1.4+para->fontsize/5; /* adjust by approx. descender */
+    }else{
+      p1.y+=2+para->fontsize/5; /* adjust by approx. descender */
+      p2.y+=2+para->fontsize/5; /* adjust by approx. descender */
+    }
+
+    gvprintf(job, "<v:rect style=\"position:absolute; ");
+    gvprintf(job, " left: %.2f; top: %.2f;", p1.x, p1.y);
+    gvprintf(job, " width: %.2f; height: %.2f\"", p2.x-p1.x, p2.y-p1.y);
+    gvputs(job, " stroked=\"false\" filled=\"false\">\n");
+    gvputs(job, "<v:textbox inset=\"0,0,0,0\" style=\"position:absolute; v-text-wrapping:'false';padding:'0';");
+
     if (para->postscript_alias) {
-        gvprintf(job, " font-family: '%s';", para->postscript_alias->family);
+        gvprintf(job, "font-family: '%s';", para->postscript_alias->family);
         if (para->postscript_alias->weight)
-           gvprintf(job, " font-weight: %s;", para->postscript_alias->weight);
+           gvprintf(job, "font-weight: %s;", para->postscript_alias->weight);
         if (para->postscript_alias->stretch)
-           gvprintf(job, " font-stretch: %s;", para->postscript_alias->stretch);
+           gvprintf(job, "font-stretch: %s;", para->postscript_alias->stretch);
         if (para->postscript_alias->style)
-           gvprintf(job, " font-style: %s;", para->postscript_alias->style);
+           gvprintf(job, "font-style: %s;", para->postscript_alias->style);
     }
     else {
-        gvprintf(job, " font-family: \'%s\';", para->fontname);
+        gvprintf(job, "font-family: \'%s\';", para->fontname);
     }
-    /* FIXME - even inkscape requires a magic correction to fontsize.  Why?  */
-    gvprintf(job, " font-size: %.2fpt;", para->fontsize * 0.81);
+    gvprintf(job, " font-size: %.2fpt;", para->fontsize);
     switch (obj->pencolor.type) {
     case COLOR_STRING:
        if (strcasecmp(obj->pencolor.u.string, "black"))
@@ -247,26 +431,32 @@ static void vml_textpara(GVJ_t * job, pointf p, textpara_t * para)
     default:
        assert(0);              /* internal error */
     }
-    gvputs(job, "\">");
-    gvputs(job, xml_string(para->str));
-    gvputs(job, "</div>\n");
+    gvputs(job, "\"><center>");
+    gvputs(job, html_string(para->str));
+    gvputs(job, "</center></v:textbox>\n"); 
+    gvputs(job, "</v:rect>\n");
 }
 
 static void vml_ellipse(GVJ_t * job, pointf * A, int filled)
-{
+{   
+    double dx, dy, left, right, top, bottom;
+
     /* A[] contains 2 points: the center and corner. */
-    
-    gvputs(job, "        <v:oval");
+    gvputs(job, "  <v:oval style=\"position:absolute;");
 
-    vml_grstrokeattr(job);
+    dx=A[1].x-A[0].x;
+    dy=A[1].y-A[0].y;
 
-    gvputs(job, " style=\"position: absolute;");
+    top=graphHeight-(A[0].y+dy);
+    bottom=top+dy+dy;
+    left=A[0].x - dx;
+    right=A[1].x;
+    gvprintf(job, " left: %.2f; top: %.2f;",left, top);
+    gvprintf(job, " width: %.2f; height: %.2f\"", 2*dx, 2*dy);
 
-    gvprintf(job, " left:  %gpt; top:    %gpt;", 2*A[0].x - A[1].x+4, job->height*.75 - A[1].y-4);
-    gvprintf(job, " width: %gpt; height: %gpt;", 2*(A[1].x - A[0].x), 2*(A[1].y - A[0].y));
-    gvputs(job, "\">");
-    vml_grstroke(job, filled);
     vml_grfill(job, filled);
+    gvputs(job, " >");
+    vml_grstroke(job, filled);
     gvputs(job, "</v:oval>\n");
 }
 
@@ -274,35 +464,41 @@ static void
 vml_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
              int arrow_at_end, int filled)
 {
-    gvprintf(job, "        <v:shape %s><!-- bezier --><v:path", graphcoords);
-    gvputs(job, " v=\"");
-    vml_bzptarray(job, A, n);
-    gvputs(job, "\" />");
-    vml_grstroke(job, filled);
-    gvputs(job, "</v:path>");
+    gvputs(job, " <v:shape style=\"position:absolute; ");
+    gvprintf(job, " width: %d; height: %d\"", graphWidth, graphHeight);
+
     vml_grfill(job, filled);
-    gvputs(job, "</v:shape>\n");
+    gvputs(job, " >");
+    vml_grstroke(job, filled);
+    gvputs(job, "<v:path  v=\"");
+    vml_bzptarray(job, A, n);
+    gvputs(job, "/></v:shape>\n");
 }
 
 static void vml_polygon(GVJ_t * job, pointf * A, int n, int filled)
 {
     int i;
+    double px,py;
 
-    gvputs(job, "        <v:shape");
-    vml_grstrokeattr(job);
-    gvprintf(job, " %s><!-- polygon --><v:path", graphcoords);
-    gvputs(job, " v=\"");
+    gvputs(job, " <v:shape style=\"position:absolute; ");
+    gvprintf(job, " width: %d; height: %d\"", graphWidth, graphHeight);
+    vml_grfill(job, filled);
+    gvputs(job, " >");
+    vml_grstroke(job, filled);
+
+    gvputs(job, "<v:path  v=\"");
     for (i = 0; i < n; i++)
     {
-        if (i==0) gvputs(job, "m ");
-       gvprintf(job, "%.0f,%.0f ", A[i].x, -A[i].y);
+        px=A[i].x;
+        py= (graphHeight-A[i].y);
+        if (i==0){
+          gvputs(job, "m ");
+        }
+        /* integers only in path */
+       gvprintf(job, "%.0f %.0f ", px, py);
         if (i==0) gvputs(job, "l ");
-        if (i==n-1) gvputs(job, "x e ");
+        if (i==n-1) gvputs(job, "x e \"/>");
     }
-    gvputs(job, "\">");
-    vml_grstroke(job, filled);
-    gvputs(job, "</v:path>");
-    vml_grfill(job, filled);
     gvputs(job, "</v:shape>\n");
 }
 
@@ -310,65 +506,30 @@ static void vml_polyline(GVJ_t * job, pointf * A, int n)
 {
     int i;
 
-    gvprintf(job, "        <v:shape %s><!-- polyline --><v:path", graphcoords);
-    gvputs(job, " v=\"");
+    gvputs(job, " <v:shape style=\"position:absolute; ");
+    gvprintf(job, " width: %d; height: %d\" filled=\"false\">", graphWidth, graphHeight);
+    gvputs(job, "<v:path v=\"");
     for (i = 0; i < n; i++)
     {
         if (i==0) gvputs(job, " m ");
-       gvprintf(job, "%.0f,%.0f ", A[i].x, -A[i].y);
+       gvprintf(job, "%.0f,%.0f ", A[i].x, graphHeight-A[i].y);
         if (i==0) gvputs(job, " l ");
         if (i==n-1) gvputs(job, " e "); /* no x here for polyline */
     }
-    gvputs(job, "\">");
+    gvputs(job, "\"/>");
     vml_grstroke(job, 0);                 /* no fill here for polyline */
-    gvputs(job, "</v:path>");
     gvputs(job, "</v:shape>\n");
-
 }
 
-/* color names from http://www.w3.org/TR/VML/types.html */
+/* color names from 
+  http://msdn.microsoft.com/en-us/library/bb250525(VS.85).aspx#t.color
+*/
 /* NB.  List must be LANG_C sorted */
 static char *vml_knowncolors[] = {
-    "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
-    "beige", "bisque", "black", "blanchedalmond", "blue",
-    "blueviolet", "brown", "burlywood",
-    "cadetblue", "chartreuse", "chocolate", "coral",
-    "cornflowerblue", "cornsilk", "crimson", "cyan",
-    "darkblue", "darkcyan", "darkgoldenrod", "darkgray",
-    "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
-    "darkolivegreen", "darkorange", "darkorchid", "darkred",
-    "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
-    "darkslategrey", "darkturquoise", "darkviolet", "deeppink",
-    "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
-    "firebrick", "floralwhite", "forestgreen", "fuchsia",
-    "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
-    "green", "greenyellow", "grey",
-    "honeydew", "hotpink", "indianred",
-    "indigo", "ivory", "khaki",
-    "lavender", "lavenderblush", "lawngreen", "lemonchiffon",
-    "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow",
-    "lightgray", "lightgreen", "lightgrey", "lightpink",
-    "lightsalmon", "lightseagreen", "lightskyblue",
-    "lightslategray", "lightslategrey", "lightsteelblue",
-    "lightyellow", "lime", "limegreen", "linen",
-    "magenta", "maroon", "mediumaquamarine", "mediumblue",
-    "mediumorchid", "mediumpurple", "mediumseagreen",
-    "mediumslateblue", "mediumspringgreen", "mediumturquoise",
-    "mediumvioletred", "midnightblue", "mintcream",
-    "mistyrose", "moccasin",
-    "navajowhite", "navy", "oldlace",
-    "olive", "olivedrab", "orange", "orangered", "orchid",
-    "palegoldenrod", "palegreen", "paleturquoise",
-    "palevioletred", "papayawhip", "peachpuff", "peru", "pink",
-    "plum", "powderblue", "purple",
-    "red", "rosybrown", "royalblue",
-    "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell",
-    "sienna", "silver", "skyblue", "slateblue", "slategray",
-    "slategrey", "snow", "springgreen", "steelblue",
-    "tan", "teal", "thistle", "tomato", "turquoise",
-    "violet",
-    "wheat", "white", "whitesmoke",
-    "yellow", "yellowgreen"
+     "aqua",  "black",  "blue", "fuchsia", 
+     "gray",  "green", "lime",  "maroon", 
+     "navy",  "olive",  "purple",  "red",
+     "silver",  "teal",  "white",  "yellow"
 };
 
 gvrender_engine_t vml_engine = {
@@ -392,8 +553,8 @@ gvrender_engine_t vml_engine = {
     0,                          /* vml_end_edge */
     vml_begin_anchor,
     vml_end_anchor,
-    0,                         /* vml_begin_label */
-    0,                         /* vml_end_label */
+    0,                          /* vml_begin_label */
+    0,                          /* vml_end_label */
     vml_textpara,
     0,                         /* vml_resolve_color */
     vml_ellipse,
@@ -411,7 +572,7 @@ gvrender_features_t render_features_vml = {
        | GVRENDER_DOES_MAPS
        | GVRENDER_DOES_TARGETS
        | GVRENDER_DOES_TOOLTIPS, /* flags */
-    4.,                         /* default pad - graph units */
+    0.,                         /* default pad - graph units */
     vml_knowncolors,           /* knowncolors */
     sizeof(vml_knowncolors) / sizeof(char *),  /* sizeof knowncolors */
     RGBA_BYTE,                 /* color_type */
@@ -444,3 +605,4 @@ gvplugin_installed_t gvdevice_vml_types[] = {
 #endif
     {0, NULL, 0, NULL, NULL}
 };
+