/* @(#) Microsoft Vector Markup Language (VML) Driver: John S. Urban, Mar 1999 =============================================================================== Low level interface to VML Based on http://www.w3.org/TR/NOTE-VML http://www.microsoft.com/standards/vml/default.asp Microsoft says this format will be supported in Internet Explorer 5.0 and Microsoft Office 2000 products including Word. Quite a bit of the VML specification needs clarification. Testing against IE5. Recollecting connected line segments into polylines because VML is too verbose to do otherwise. Use negative color values to specify line thickness in raster units. =============================================================================== NEED TO DO YET: This driver subgroups according to pen attribute change inside of an entire page group. That means the entire page comes in as one grouped object into your document, but that if you ungroup it you will find most things arranged into reasonable subobjects. If you wish to force the beginning of a new VML subobject just change the pen color or thickness. All polygons are considered objects, by the way. Even if this does not make sense when you read it it is very important because otherwise your drawings will crawl away like a bunch of ants when you try to manipulate the drawing or objects in it. Need decent support of hardware fonts (size, angle, font ...). Should support center, left, right and top, bottom middle justify flags for hardware text too. Can line weight be specified as scaled to local coordinate system size? This would allow thickness to scale with a rescaled plot. See if can reduce file size. VML documentation is not very clear. A line of zero length does not print as a point; need to make into a point or make sure zero-length vectors are changed to have length =============================================================================== */ #include #include #include #ifndef MINGW #include #include #endif #include #include #include #include #include "vogle.h" extern FILE *_voutfile(); #define MAX(x, y) ((x) > (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define ABS(x) ((x) < 0 ? -(x) : (x)) #define FLIPY(y) ((vdevice.sizeSy)-(y)) #define FALSE 0 #define TRUE 1 #define VMLXSIZE 450 /* total drawing area size in x direction */ #define VMLYSIZE 650 /* total drawing area size in y direction */ /* scale factor for going from world coordinates to device coordinates. Make bigger to increase accuracy of vector data */ #define VMLTORAS 20 static int points=0; static int vml_first_time = 1, drawn = 0, pslstx = -1, pslsty = -1;/* last (x, y) drawn */ extern FILE *fp; int OLDX; int OLDY; int VML_MOVED=0; #define CMAPSIZE 256 struct rgb_color { unsigned short int red; unsigned short int green; unsigned short int blue; }; static struct rgb_color vml_carr[CMAPSIZE]; /******************************************************************************/ static int lineopen = FALSE; /* PolyLine not open */ static int shapeopen = FALSE; /* shape not open */ static int curcol = 0; /* Current pen color (black) */ static int curwid = 1; /* Current pen width */ static int curpat = 0; /* Current fill pattern*/ static int pgroup=1; /* groupid reserved for the entire page */ extern float hardwidth[128]; /* array to store hardware character widths */ char fontstyle[256]; /******************************************************************************/ static int VML_header() { time_t tod; #ifndef MINGW struct utsname unstr, *un; #endif char *username; struct passwd *pw; fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"VML graphics\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"\n"); fprintf(fp,"

 

\n"); return(0); } /******************************************************************************/ /* change index i in the color map to the appropriate rgb value. */ int VML_mapcolor(int i, int r, int g, int b) { if (i >= CMAPSIZE || i < 0 ){ return(-1); } vml_carr[i].red = (unsigned short)(r); vml_carr[i].green = (unsigned short)(g); vml_carr[i].blue = (unsigned short)(b); return(0); } /******************************************************************************/ /* VML_init set up the environment. Returns 1 on success. */ static int VML_init(void) { int prefx, prefy, prefxs, prefys; int i; int VML_header(); fp = _voutfile(); if (!vml_first_time) return(1); VML_header(); getprefposandsize(&prefx, &prefy, &prefxs, &prefys); if (prefxs != -1 ) { vdevice.sizeSy = prefys*VMLTORAS; vdevice.sizeSx = prefxs*VMLTORAS; vdevice.sizeX = vdevice.sizeY = MIN(prefys, prefxs )*VMLTORAS; } else{ vdevice.sizeSy = VMLYSIZE*VMLTORAS; /* size in resolution rasters */ vdevice.sizeSx = VMLXSIZE*VMLTORAS; /* size in resolution rasters */ vdevice.sizeX = vdevice.sizeY = MIN(VMLXSIZE,VMLYSIZE)*VMLTORAS; /* current viewport to use */ } fprintf(fp,"\n", pgroup); fprintf(fp,"\n",vdevice.sizeSx,vdevice.sizeSy); vdevice.depth = 8; for (i = 0; i < CMAPSIZE; i++) /* set up the basic colors */ { vml_carr[i].red=255; vml_carr[i].green=255; vml_carr[i].blue=255; } VML_mapcolor(0, 255, 255, 255); VML_mapcolor(1, 255, 0, 0); VML_mapcolor(2, 0, 255, 0); VML_mapcolor(3, 255, 255, 0); VML_mapcolor(4, 0, 0, 255); VML_mapcolor(5, 255, 0, 255); VML_mapcolor(6, 0, 255, 255); VML_mapcolor(7, 0, 0, 0); VML_mapcolor( 8, 155, 0, 0); VML_mapcolor( 9, 0, 155, 0); VML_mapcolor(10, 155, 255, 255); VML_mapcolor(11, 155, 155, 0); VML_mapcolor(12, 0, 0, 155); VML_mapcolor(13, 155, 0, 155); VML_mapcolor(14, 0, 155, 155); VML_mapcolor(15, 100, 100, 100); lineopen = FALSE; /* Polyline not open */ shapeopen = FALSE; /* Polyline not open */ curcol=0; curwid=1; curpat=0; drawn = 0; return (1); /* Set other line drawing parameters */ /* Move */ /* Set a default font height */ } /******************************************************************************/ static int closeline(void){ if(lineopen){ if(VML_MOVED == 0 ){ /* NULL LINES ARE NOT POINTS, BUT NO-OPS TO 2002+ WINDOWS */ /* fprintf(fp," "); */ /* fprintf(fp," %d %d ",OLDX+1,OLDY+1); */ /* draw small square to make a point */ /* TOO BIG, SHOWS UP IN SOFTWARE TEXT TOO (MAYBE JUST MAKE SMALLER? fprintf(fp," t %d %d r %d %d %d %d %d %d %d %d",-VMLTORAS/2,-VMLTORAS/2, 0,VMLTORAS, VMLTORAS,0, 0,-VMLTORAS, -VMLTORAS,0); */ fprintf(fp," t -1 -1 r 1 1 "); } fprintf(fp, " e"); /* end curve */ lineopen = FALSE; /* Polyline not open */ points = 0; } return (0); } /******************************************************************************/ static int closeshape(void){ if(shapeopen){ fprintf(fp, "\">\n"); /* end curve */ fprintf(fp, "\n"); /* end curve */ shapeopen = FALSE; /* Polyline not open */ points = 0; } return (0); } /******************************************************************************/ static int openline(void){ if(!lineopen){ fprintf(fp,"\nm"); lineopen = TRUE; /* Polyline open */ } return (0); } /******************************************************************************/ static int openshape(void){ if(!shapeopen){ fprintf(fp, "\n"); /* Page Clear, End of Page Group */ fprintf(fp,"
\n\n\n"); /* End of Document */ drawn = 0; points = 0; if (fp != stdout && fp != stderr ){ fflush(fp); if(vdevice.writestoprocess == 2){ pclose(fp); }else{ fclose(fp); } } return (0); } /******************************************************************************/ /* VML_draw draw to an x, y point. */ /* Note: (0, 0) is defined as the top left of the window in VML. */ static int VML_draw(int x, int y) { static char linefeed[2] = {' ','\n'}; if (pslstx != vdevice.cpVx || pslsty != vdevice.cpVy ){ closeline(); /* close line if required */ openshape(); /* start shape if required */ openline(); /* start line */ fprintf(fp, " %d %d l", vdevice.cpVx, FLIPY(vdevice.cpVy)); OLDX=vdevice.cpVx; OLDY=FLIPY(vdevice.cpVy); VML_MOVED=0; points = 1; } openshape(); /* start shape if required */ openline(); /* start line if required */ if(points == 0){ fprintf(fp, " %d %d l", x ,FLIPY(y)); OLDX=x; OLDY=FLIPY(y); VML_MOVED=0; }else{ if(OLDX!=x || OLDY!=FLIPY(y))VML_MOVED++; OLDX=x; OLDY=FLIPY(y); fprintf(fp, "%c%d %d", linefeed[(points % 8/7)], x ,FLIPY(y)); } points++; pslstx = x; pslsty = y; drawn = 1; return (0); } /******************************************************************************/ /* VML_clear flush the current page without resetting the graphics state */ static int VML_clear(void) { closeline(); /* close line if required */ closeshape(); /* close shape if required */ if (drawn) { pgroup++; /* increment page id */ fprintf(fp, "\n"); /* Page Clear, End of Page Group */ fprintf(fp,"\n", pgroup); fprintf(fp,"
\n"); fprintf(fp,"\n",vdevice.sizeSx,vdevice.sizeSy); } drawn = 0; points = 0; return(0); } /******************************************************************************/ /* VML_color change the color of the pen * kludged so negative value sets raster line width * if exceed allowable number of colors maybe pick a line style * or something like a gradient fill style for fun */ static int VML_color(int col) { closeline(); /* close line if required */ closeshape(); /* close shape if required */ if ( col < 0 ) { curwid = abs(col); } else { curpat = col/CMAPSIZE; curcol = col % CMAPSIZE; } return(0); } /******************************************************************************/ /* * value sets raster linewidth */ static int VML_setlw(int width) { closeline(); /* close line if required */ closeshape(); /* close shape if required */ if ( width >= 0 ) { curwid = width; } return(0); } /******************************************************************************/ /* load in small or large - could be improved. Radically KLUDGED; made SoftText extern */ static int VML_font(char *fontname) { int i; float rat; /* assuming vdevice.hwidth is the desired width of the reference character, * this is a list of percentages of the other character widths. */ static int helvetica_w[128] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0, 27,33,43,55,55,88,72,28,33,33,39,58,27,33,28,28,55,55,55,55,55,55,55,55,55,55, 34,34,58,58,58,61,97, 73,73,73,73,66,61,78,72,28,55,73,61,83,72,78,66,78,72,67,61,72,66,94,66,66,61, 33,28,33,58,55,28, 55,61,55,61,55,33,61,61,28,28,55,28,88,61,61,61,61,39,55,33,61,55,78,55,55,50, 39,28,39,58,0 }; vdevice.attr->a.softtext = SOFTHARDWARE; /* textsize will be obeyed after the font is set * maybe should read current software size and convert virtual * to device instead of resetting each time */ if(vdevice.hwidth == 0 || vdevice.hheight == 0 ){ vdevice.hwidth=11.0*VMLTORAS; vdevice.hheight=11.0*VMLTORAS; } if (strcmp(fontname, "small") == 0) { rat=0.55; /* Kludge Factor */ for (i = 0; i < 128; i++){ hardwidth[i]=1.00 * rat; /* ratio of character width to vdevice.hwidth*/ /*fprintf(stderr," font table %f %c \n",hardwidth[i],i);*/ /* VERIFY FONT TABLE*/ } strcpy(fontstyle,"Font-weight:bold; font-family:'Times New Roman'"); strcpy(fontstyle,"font-family:'Times New Roman'"); } else if (strcmp(fontname, "large") == 0) { rat=1.00; /* Kludge Factor */ for (i = 0; i < 128; i++){ hardwidth[i]=((float)helvetica_w[i])/100.0*rat; /* ratio of character width to vdevice.hwidth*/ /*fprintf(stderr," font table %f %c \n",hardwidth[i],i);*/ /* VERIFY FONT TABLE*/ } strcpy(fontstyle,"font-weight:bold; font-family:'Arial Black', Arial, sans-serif"); strcpy(fontstyle,"font-family:'Arial Black', Arial, sans-serif"); } else{ strcpy(fontstyle,"font-weight:bold; font-family:'Arial Black', Arial, sans-serif"); strcpy(fontstyle,"font-family:'Arial Black', Arial, sans-serif"); return(0); } return(1); } /******************************************************************************/ /* output a character string using current character size and rotation angle. */ static int VML_string(char *s) { char c; int i; float slen; float sheight; int uneven; int x; int y; int ijust; closeline(); /* close line if required */ closeshape(); /* close shape if required */ if(vdevice.hwidth == 0 || vdevice.hheight == 0 ){ fprintf(stderr,"*VML_string* ERROR: ZERO SIZE CHARACTERS\n"); vdevice.hwidth=11.0; vdevice.hheight=11.0; } slen=strlength(s)*vdevice.hwidth*VMLTORAS; sheight=vdevice.hheight; /* There is a bug where if the line is horizontal, the text prints with zero character height */ uneven=0; if( FLIPY(vdevice.cpVy) == FLIPY(vdevice.cpVy+slen*vdevice.attr->a.textsin) ){ uneven=1; }else{ uneven=0; } /* VML aligns thru center of text */ ijust=(int)(0.50*sheight); /* The line the text will be placed along */ x=(int)(vdevice.cpVx+slen*vdevice.attr->a.textcos); y=(int)(uneven+vdevice.cpVy+slen*vdevice.attr->a.textsin); fprintf(fp,"\n", vml_carr[curcol].red, vml_carr[curcol].green, vml_carr[curcol].blue); /* character fill color */ fprintf(fp,"\n", vml_carr[curcol].red, vml_carr[curcol].green, vml_carr[curcol].blue); fprintf(fp,"\n"); /* string */ fprintf(fp,"': fprintf(fp, ">"); break; default: fprintf(fp, "%c",c); } } fprintf(fp,"\" style=\"%s;",fontstyle); fprintf(fp,"v-text-align:stretch-justify;fontsize:%d\"/>\n",(int)sheight); fprintf(fp,"\n"); drawn = 1; pslstx = x; pslsty = y; closeshape(); /* close shape if required */ return(0); } /******************************************************************************/ /* VML_char output a character */ int VML_char(char c){ char s[2]; s[0] = c; s[1]='\0'; VML_string(s); return(0); } /******************************************************************************/ /* fill a polygon */ static int VML_fill(int n, int x[], int y[]) { int i; static char linefeed[2] = {' ','\n'}; closeline(); /* close line if required */ closeshape(); /* close line if required */ fprintf(fp, "\n"); /* end of polygon */ vdevice.cpVx = x[n - 1]; vdevice.cpVy = y[n - 1]; pslstx = pslsty = -1; /* fill destroys current path */ drawn = 1; return(0); } /******************************************************************************/ /* no operations - do nothing but return -1 */ static int noop(void) { return(-1); } static int noop2(int *x, int *y) { return(-1); } /******************************************************************************/ static DevEntry vmldev = { "vml", /* name of device */ "large", /* name of large font */ "small", /* name of small font */ noop, /* Set drawing in back buffer */ VML_char, /* Draw a hardware character */ noop, /* Check if a key was hit */ VML_clear, /* Clear the screen to current color */ VML_color, /* Set current color */ VML_draw, /* Draw a line */ VML_exit, /* Exit graphics */ VML_fill, /* Fill a polygon */ VML_font, /* Set hardware font */ noop, /* Set drawing in front buffer */ noop, /* Wait for and get the next key hit */ VML_init, /* Initialize the device */ noop2, /* Get mouse/cross hair position */ VML_mapcolor, /* Set color indices */ VML_setlw, /* Set line width */ VML_string, /* Draw a hardware string */ noop, /* Swap front and back buffers */ noop /* Syncronize the display */ }; /******************************************************************************/ /* _VML_devcpy copy the vml device into vdevice.dev. */ int _VML_devcpy() { vdevice.dev = vmldev; return(0); } /******************************************************************************/