SVG documenation /* @(#) Scalable Vector Graphics (SVG) Driver: John S. Urban, Mar 2005 Low level interface to SVG based on version 1.1 described at http://www.w3.org/Graphics/SVG/ =============================================================================== JUST STARTING -- NEED TO FINISH HARDWARE TEXT, USE SCALABLE/ZOOMABLE NEED TO HANDLE MULTI-PAGE, ZOOMABLE PANNABLE STUFF Usage Notes: o assuming 1000 rasters to the inch; so a call for size 4000,4000 would create a 4inx4in display surface. Default is 4.25inx5.5in page. o Since the standard supports compression with gzip, you can compress the files and change the suffix to .svgz instead of .svg if you desire to save space. o Since SVG is scalable, if each output is in a seperate file you can lay them out any way you want; one plot per page; many small plots per page; and so on. Assuming you have a file called a.svg here is an example html file to lay out frames for printing or viewing from an HTML/SVG browser/: So far, am assuming one plot per SVG plot file. Split file with Unix csplit command if that is not the case and then make a document similar to the following (this file can easily be generated by a script). Basically, treating SVG files much like a PNG bitmap file as far as use in a document. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>layout02</title> <link rel="stylesheet" type="text/css" href="containers.css" /> </head> <body> <p> <object type="image/svg+xml" data="a.svg" width="100%" height="129%"> </object><br /> <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> <object type="image/svg+xml" data="a.svg" width="100%" height="129%"> </object><br /> </p> <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> <object type="image/svg+xml" data="a.svg" width="500" height="800"> </object><br /> </p> <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> <object type="image/svg+xml" data="a.svg" width="500" height="800"> </object><br /> </p> <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> <object type="image/svg+xml" data="a.svg" width="500" height="800"> </object><br /> </p> <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> Vector-based Small Images <object type="image/svg+xml" data="a.svg" width="150" height="250"> </object> <object type="image/svg+xml" data="a.svg" width="150" height="250"> </object> <object type="image/svg+xml" data="a.svg" width="150" height="250"> </object> <object type="image/svg+xml" data="a.svg" width="150" height="250"> </object> <object type="image/svg+xml" data="a.svg" width="150" height="250"> </object> </p> Percent-based Small Images <p style=" margin-bottom: 0.17in; page-break-before: always; page-break-after: avoid"> <object type="image/svg+xml" data="a.svg" width="50%" height="64%"> </object> <object type="image/svg+xml" data="a.svg" width="50%" height="64%"> </object> <object type="image/svg+xml" data="a.svg" width="50%" height="64%"> </object> <object type="image/svg+xml" data="a.svg" width="50%" height="64%"> </object> <a href="../">back</a> </p> </body> </html> A simpler way will probably evolve as SVG develops; the 1.2 proposal includes a <page> element Quite a bit of the SVG specification needs clarification. Testing against Adobe SVG interpreter Recollecting connected line segments into polylines because SVG is too verbose to do otherwise. Use negative color values to specify line thickness in raster units in this version of vogle A line of zero length does not always print as a point even when line terminators on; drawing a circle when a zero length line is encountered. The issue about whitespace in a text line means you have to be very careful about reformatting the SVG file; I thought it would be better not to turn on xml:space="preserve" and use &nbsp for an explicit space character but it seems to get changed to a space before space is consolidated so it has no effect. Using a "missing glyph" seems too unreliable and pushing it too far; so using preserve mode for white space. =============================================================================== NEED TO DO YET: If change pen color and width after started a line what should be effect? 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 SVG 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. SVG documentation is not very clear. Verdana =============================================================================== */ #include <stdlib.h> #include <stdio.h> #include <time.h> #ifndef MINGW #include <sys/utsname.h> #include <pwd.h> #endif #include <unistd.h> #include <string.h> #include <math.h> #include <sys/types.h> #include "vogle.h" extern FILE *_voutfile(); /* How to convert degrees to radians */ #ifndef PI #define PI 3.14159265358979323844 #endif #define d2r(x) ((x) * PI / 180.0) #define r2d(x) ((x) * 180.0 / PI) #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) ((int)((vdevice.sizeSy)-(y))) #define FALSE 0 #define TRUE 1 extern float hardwidth[128]; static char hardfont[30]={'m','o','n','o','s','p','a','c','e','\0'}; /* Assume 1000 units per inch, and default size of 4.25inx5.5in*/ /* total drawing area size in x direction */ #define SVGYSIZE 5500 /* total drawing area size in y direction */ #define SVGXSIZE 4250 /* scale factor for going from world coordinates to device coordinates. Make bigger to increase accuracy of vector data */ #define SVGTORAS 1000 static int points=0; static int svg_first_time = 1, drawn = 0, LAST_X = -1, LAST_Y = -1;/* last (x, y) drawn */ extern FILE *fp; int SVG_MOVED=0; #define CMAPSIZE 256 struct rgb_color { unsigned short int red; unsigned short int green; unsigned short int blue; }; static struct rgb_color svg_carr[CMAPSIZE]; /******************************************************************************/ static int PolyLineOpen = FALSE; /* PolyLine not open */ static int ObjectOpen = FALSE; /* Object not open */ static int curcol = 0; /* Current pen color (black) */ static int curwid = 1; /* Current pen color width */ static int curpat = 0; /* Current fill pattern*/ static int pgroup=1; /* groupid reserved for the entire page */ /******************************************************************************/ static int SVG_header() { time_t tod; #ifndef MINGW struct utsname unstr, *un; #endif char *username; struct passwd *pw; fprintf(fp,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>\n"); fprintf(fp,"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"); fprintf(fp,"\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); fprintf(fp,"<!-- Creator: VOGLE SVG driver 1.0 2005-03-25 -->\n"); time(&tod); fprintf(fp,"<!-- CreationDate: %s -->\n",ctime(&tod)); fprintf(fp,"<!--\n"); time(&tod); #ifndef MINGW un = &unstr; /* initialize the pointer to an address with enough room to store the returned value in */ uname(un); if ((username = getlogin()) == NULL ){ pw = getpwuid(getuid()); username = pw->pw_name; } fprintf(fp," For: %s on OS=%.*s\n NETWORK_NAME=%.*s\n RELEASE=%.*s\n VERSION=%.*s\n MACHINE=%.*s\n", username, (int)sizeof(un->sysname), un->sysname, (int)sizeof(un->nodename), un->nodename, (int)sizeof(un->release), un->release, (int)sizeof(un->version), un->version, (int)sizeof(un->machine), un->machine); fprintf(fp,"-->\n"); #endif return(0); } /******************************************************************************/ /* change index i in the color map to the appropriate rgb value. */ int SVG_mapcolor(int i, int r, int g, int b) { if (i >= CMAPSIZE || i < 0 ){ return(-1); } svg_carr[i].red = (unsigned short)(r); svg_carr[i].green = (unsigned short)(g); svg_carr[i].blue = (unsigned short)(b); return(0); } /******************************************************************************/ /* SVG_init set up the environment. Returns 1 on success. */ static int SVG_init(void) { int prefx, prefy, prefxs, prefys; int i; int SVG_header(); fp = _voutfile(); if (!svg_first_time) return(1); SVG_header(); getprefposandsize(&prefx, &prefy, &prefxs, &prefys); if (prefxs != -1 ) { vdevice.sizeSy = prefys; vdevice.sizeSx = prefxs; vdevice.sizeX = vdevice.sizeY = MIN(prefys, prefxs ); } else{ vdevice.sizeSy = SVGYSIZE; /* size in resolution rasters */ vdevice.sizeSx = SVGXSIZE; /* size in resolution rasters */ vdevice.sizeX = vdevice.sizeY = MIN(SVGXSIZE,SVGYSIZE); /* current viewport to use */ } fprintf(fp,"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"%fin\" height=\"%fin\" viewBox=\"0 0 %d %d\">\n", (float)vdevice.sizeSx/SVGTORAS,(float)vdevice.sizeSy/SVGTORAS, vdevice.sizeSx,vdevice.sizeSy); fprintf(fp,"<desc> VOGLE SVG pages</desc>\n"); fprintf(fp,"<!-- LINECAP: butt round square JOIN: round miter bevel inherit -->\n"); fprintf(fp,"<g style=\" stroke-width:%dpx;stroke:black;stroke-linecap:round;stroke-linejoin:round;fill:none;\n\ fill-rule:even-odd;fill-opacity:1;font-style:normal;font-variant:normal;\n\ font-weight:normal;font-stretch:normal;font-size-adjust:none;letter-spacing:normal;\n\ word-spacing:normal;text-anchor:start\" xml:space=\"preserve\">\n", MAX(1,vdevice.sizeX*curwid/10000)); fprintf(fp,"<defs>\n"); /* Horizontal Line from left to right to transform as a text path */ fprintf(fp,"<path id=\"baseline\" d=\"M0,0L2000000,0\" style=\"fill:none\"/>\n"); fprintf(fp,"<marker id=\"Triangle\"\n"); fprintf(fp," viewBox=\"0 0 10 10\" refX=\"0\" refY=\"5\"\n"); fprintf(fp," markerUnits=\"strokeWidth\"\n"); fprintf(fp," markerWidth=\"4\" markerHeight=\"3\"\n"); fprintf(fp," orient=\"auto\">\n"); fprintf(fp," <path d=\"M 0 0 L 10 5 L 0 10 z\"/>\n"); fprintf(fp," </marker>\n"); fprintf(fp," <marker id=\"Dot\"\n"); fprintf(fp," viewBox=\"0 0 10 10\" refX=\"0\" refY=\"5\"\n"); fprintf(fp," markerUnits=\"strokeWidth\"\n"); fprintf(fp," markerWidth=\"4\" markerHeight=\"3\"\n"); fprintf(fp," orient=\"auto\">\n"); fprintf(fp," <path d=\"M 0 0 L 10 5 L 0 10 z\" />\n"); fprintf(fp,"</marker>\n"); fprintf(fp,"</defs>\n"); fprintf(fp,"<!-- id=\"Page%d\" -->\n", pgroup); vdevice.depth = 8; for (i = 0; i < CMAPSIZE; i++) /* set up the basic colors */ { svg_carr[i].red=255; svg_carr[i].green=255; svg_carr[i].blue=255; } SVG_mapcolor(0, 255, 255, 255); SVG_mapcolor(1, 255, 0, 0); SVG_mapcolor(2, 0, 255, 0); SVG_mapcolor(3, 255, 255, 0); SVG_mapcolor(4, 0, 0, 255); SVG_mapcolor(5, 255, 0, 255); SVG_mapcolor(6, 0, 255, 255); SVG_mapcolor(7, 0, 0, 0); SVG_mapcolor( 8, 155, 0, 0); SVG_mapcolor( 9, 0, 155, 0); SVG_mapcolor(10, 155, 255, 255); SVG_mapcolor(11, 155, 155, 0); SVG_mapcolor(12, 0, 0, 155); SVG_mapcolor(13, 155, 0, 155); SVG_mapcolor(14, 0, 155, 155); SVG_mapcolor(15, 100, 100, 100); PolyLineOpen = FALSE; /* Polyline not open */ ObjectOpen = FALSE; /* Object 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){ /* No "point" object in SVG; null lines do not show up at all with default line terminators. If terminator is round or square points do show up, at least in one interpreter; but I expect SVG to be read by other products and I have repeatedly encountered ones that throw away a line with zero length so should probably avoid zero-length lines. So keep track of whether drew any vector since last move in SVG_MOVED So with SVG I could o change top-level <g> to have all lines have round terminators or square terminators o ensure line terminator if line is null to ensure get circle or square even if current default style does not o Draw circle or square with radius of current line thickness of at least N rasters, be careful of Current Position at end o use SVG marker o draw a line of one "raster" when get a zero-length line o Some stuff showing up in Hershey fonts too -- stick to dot. */ /*int half_box;*/ if(PolyLineOpen){ if(SVG_MOVED == 0 ){ /* ASSUME NULL LINES ARE DOTS OR POINTS */ fprintf(fp,"\"/>\n<circle cx=\"%d\" cy=\"%d\" r=\"%d\" stroke=\"none\"/>\n", LAST_X, FLIPY(LAST_Y), MAX(1,vdevice.sizeX*curwid/10000/2)); /* 1-unit line round terminator fprintf(fp,"L%d,%d style=\" stroke-linecap:round; stroke-linejoin:round\"\>\n",LAST_X+1,FLIPY(LAST_Y+1)); */ /* null line line round terminator fprintf(fp,"L%d,%d style=\" stroke-linecap:round; stroke-linejoin:round\"\>\n",LAST_X+1,FLIPY(LAST_Y+1)); */ /* Place marker at end of a path. fprintf(fp,"\" stroke=\"black\" stroke-width=\"1%\" marker-end=\"url(#Dot)\"/>\n"); */ /* draw small square to make a point half_box=MAX(1,vdevice.sizeX*curwid/10000); fprintf(fp,"\"/><path d=\"m %d %d l %d %d %d %d %d %d %d %d\" />", -half_box/2,-half_box/2, 0,half_box, half_box,0, 0,-half_box, -half_box,0); */ } else{ fprintf(fp, "\"/>\n"); /* end curve */ } PolyLineOpen = FALSE; /* Polyline not open */ points = 0; } return (0); } /******************************************************************************/ static int closeObject(void){ if(ObjectOpen){ fprintf(fp,"</g>\n"); ObjectOpen = FALSE; /* object not open */ points = 0; } return (0); } /******************************************************************************/ static int openline(void){ if(!PolyLineOpen){ fprintf(fp,"<path d=\"M"); PolyLineOpen = TRUE; /* Polyline open */ } return (0); } /******************************************************************************/ static int openObject(void){ if(!ObjectOpen){ fprintf(fp, "\n<g style=\"stroke-width:%dpx; stroke:#%2.2x%2.2x%2.2x; fill:none\">\n", MAX(1,vdevice.sizeX*curwid/10000), svg_carr[curcol].red, svg_carr[curcol].green, svg_carr[curcol].blue); ObjectOpen = TRUE; /* Object open */ } return (0); } /******************************************************************************/ /* SVG_exit do a flush and close the output file if necessary. */ static int SVG_exit(void) { closeline(); /* close Polyline line if it is open */ closeObject(); /* close object if it is open */ fprintf(fp, "</g>\n"); /* Page Clear, End of Page Group */ fprintf(fp, "</svg>\n"); /* Page Clear, End of Page Group */ fprintf(fp,"<!--- End of Document -->\n"); drawn = 0; points = 0; if (fp != stdout && fp != stderr ){ fflush(fp); if(vdevice.writestoprocess == 2){ pclose(fp); }else{ fclose(fp); } } return (0); } /******************************************************************************/ /* SVG_draw draw to an x, y point. */ /* Note: (0, 0) is defined as the top left of the window in SVG. */ static int SVG_draw(int x, int y) { static char linefeed[2] = {' ','\n'}; if (LAST_X != vdevice.cpVx || LAST_Y != vdevice.cpVy ){ closeline(); /* close line if required */ openObject(); /* start Object if required */ openline(); /* start line */ fprintf(fp, "%d,%d", vdevice.cpVx, FLIPY(vdevice.cpVy)); LAST_X=vdevice.cpVx; LAST_Y=vdevice.cpVy; SVG_MOVED=0; points = 1; } openline(); /* start line if required */ if(points == 0){ fprintf(fp, "%d,%d", x ,FLIPY(y)); SVG_MOVED=0; }else{ if(LAST_X!=x || LAST_Y!=y)SVG_MOVED=SVG_MOVED+1; fprintf(fp, "%cL%d,%d", linefeed[(points % 8/7)], x ,FLIPY(y)); } points++; LAST_X = x; LAST_Y = y; drawn = 1; return (0); } /******************************************************************************/ /* SVG_clear flush the current page without resetting the graphics state */ static int SVG_clear(void) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if (drawn) { fprintf(fp,"</g><!-- End Page%d -->\n",pgroup); /* Page Clear, End of Page Group */ pgroup++; /* increment page id */ fprintf(fp,"<g id=\"Page%d\">\n", pgroup); } drawn = 0; points = 0; SVG_MOVED=0; return(0); } /******************************************************************************/ /* SVG_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 SVG_color(int col) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if ( col < 0 ) { curwid = abs(col); } else { curpat = col/CMAPSIZE; curcol = col % CMAPSIZE; } return(0); } /******************************************************************************/ /* * value sets raster line width */ static int SVG_setlw(int width) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if ( width >= 0 ) { curwid = width; } return(0); } /******************************************************************************/ /* load in small or large - could be improved. Radically KLUDGED; made SoftText extern */ static int SVG_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. | 00 nul| 01 soh| 02 stx| 03 etx| 04 eot| 05 enq| 06 ack| 07 bel| | 08 bs | 09 ht | 10 nl | 11 vt | 12 np | 13 cr | 14 so | 15 si | | 16 dle| 17 dc1| 18 dc2| 19 dc3| 20 dc4| 21 nak| 22 syn| 23 etb| | 24 can| 25 em | 26 sub| 27 esc| 28 fs | 29 gs | 30 rs | 31 us | | 32 sp | 33 ! | 34 " | 35 # | 36 $ | 37 % | 38 & | 39 ' | | 40 ( | 41 ) | 42 * | 43 + | 44 , | 45 - | 46 . | 47 / | | 48 0 | 49 1 | 50 2 | 51 3 | 52 4 | 53 5 | 54 6 | 55 7 | | 56 8 | 57 9 | 58 : | 59 ; | 60 < | 61 = | 62 > | 63 ? | | 64 @ | 65 A | 66 B | 67 C | 68 D | 69 E | 70 F | 71 G | | 72 H | 73 I | 74 J | 75 K | 76 L | 77 M | 78 N | 79 O | | 80 P | 81 Q | 82 R | 83 S | 84 T | 85 U | 86 V | 87 W | | 88 X | 89 Y | 90 Z | 91 [ | 92 \ | 93 ] | 94 ^ | 95 _ | | 96 ` | 97 a | 98 b | 99 c |100 d |101 e |102 f |103 g | |104 h |105 i |106 j |107 k |108 l |109 m |110 n |111 o | |112 p |113 q |114 r |115 s |116 t |117 u |118 v |119 w | |120 x |121 y |122 z |123 { |124 | |125 } |126 ~ |127 del| */ 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, 53, /* 33 ! */ 68, /* 34 " */ 107, /* 35 # */ 107, /* 36 $ */ 171, /* 37 % */ 128, /* 38 & */ 37, /* 39 ' */ 64, /* 40 ( */ 64, /* 41 ) */ 75, /* 42 * */ 112, /* 43 + */ 53, /* 44 , */ 64, /* 45 - */ 53, /* 46 . */ 53, /* 47 / */ 107, /* 48 0 */ 93, /* 49 1 */ 107, /* 50 2 */ 107, /* 51 3 */ 107, /* 52 4 */ 107, /* 53 5 */ 107, /* 54 6 */ 107, /* 55 7 */ 107, /* 56 8 */ 107, /* 57 9 */ 53, /* 58 : */ 53, /* 59 ; */ 112, /* 60 < */ 112, /* 61 = */ 112, /* 62 > */ 107, /* 63 ? */ 195, /* 64 @ */ 128, /* 65 A */ 128, /* 66 B */ 139, /* 67 C */ 139, /* 68 D */ 128, /* 69 E */ 117, /* 70 F */ 149, /* 71 G */ 139, /* 72 H */ 53, /* 73 I */ 96, /* 74 J */ 128, /* 75 K */ 107, /* 76 L */ 160, /* 77 M */ 139, /* 78 N */ 149, /* 79 O */ 128, /* 80 P */ 149, /* 81 Q */ 139, /* 82 R */ 128, /* 83 S */ 117, /* 84 T */ 139, /* 85 U */ 128, /* 86 V */ 181, /* 87 W */ 128, /* 88 X */ 128, /* 89 Y */ 117, /* 90 Z */ 53, /* 91 [ */ 53, /* 92 \ */ 53, /* 93 ] */ 90, /* 94 ^ */ 107, /* 95 _ */ 64, /* 96 ` */ 107, /* 97 a */ 107, /* 98 b */ 96, /* 99 c */ 107, /* 100 d */ 107, /* 101 e */ 50, /* 102 f */ 107, /* 103 g */ 107, /* 104 h */ 43, /* 105 i */ 43, /* 106 j */ 96, /* 107 k */ 43, /* 108 l */ 160, /* 109 m */ 107, /* 110 n */ 107, /* 111 o */ 107, /* 112 p */ 107, /* 113 q */ 64, /* 114 r */ 96, /* 115 s */ 53, /* 116 t */ 107, /* 117 u */ 96, /* 118 v */ 139, /* 119 w */ 96, /* 120 x */ 96, /* 121 y */ 96, /* 122 z */ 64, /* 123 { */ 50, /* 124 | */ 64, /* 125 } */ 112, /* 126 ~ */ 144 /* 127 del */ }; /* select Courier for small and Helvetica for big */ 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*300.0/72.0; vdevice.hheight=11.0*300.0/72.0; } /* fprintf(stderr,"*SVG_font* vdevice.hwidth=%f",vdevice.hwidth); */ /* fprintf(stderr," vdevice.hheight=%f\n",vdevice.hheight); */ if (strcmp(fontname, "small") == 0) { strncpy(hardfont,"monospace",30); rat=0.60; /* Kludge Factor */ rat=1.00; /* 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*/ } } else if (strcmp(fontname, "large") == 0) { strncpy(hardfont,"Helvetica,serif",30); rat=1.00; /* Kludge Factor */ rat=1.18; rat=0.795; 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*/ } } else{ return(0); } return(1); } /******************************************************************************/ /* SVG_string output a string. */ static int SVG_string(char *s) { char c; int i; float slength; float rot; /* rot angle of path of text in degrees */ closeline(); /* close line if required */ rot=r2d(atan2((double)vdevice.attr->a.textsin,(double)vdevice.attr->a.textcos)); fprintf(fp, "<text stroke=\"none\" lengthAdjust=\"spacingAndGlyphs\""); fprintf(fp," fill=\"#%2.2x%2.2x%2.2x\"\n", svg_carr[curcol].red, svg_carr[curcol].green, svg_carr[curcol].blue); /* color */ if(rot == 0){ fprintf(fp, " x=\"%d\" y=\"%d\"", (int)vdevice.cpVx, FLIPY(vdevice.cpVy)); fprintf(fp," font-family=\"%s\" font-size=\"%d\" ",hardfont,(int)vdevice.hheight); }else{ fprintf(fp," transform=\"translate(%d,%d) rotate(%f)\" x=\"0\" y=\"0\" ", (int)vdevice.cpVx, FLIPY(vdevice.cpVy),-rot); fprintf(fp," font-family=\"%s\" font-size=\"%d\" ",hardfont,(int)vdevice.hheight); } for(i=0,slength=0.0; (c=s[i]) != '\0' ;i++) { slength=slength+hardwidth[(int)c]; /* fprintf(stderr,"hardwidth=%f\n",hardwidth[(int)c]); */ } /* fprintf(stderr,"slength=%f\n",slength); */ slength=slength*vdevice.hwidth; /* fprintf(stderr,"slength 2=%f\n",slength); */ if(slength<=0.0) slength=vdevice.hwidth*strlen(s); fprintf(fp,"textLength=\"%d\">\n",(int)slength); for(i=0; (c=s[i]) != '\0' ;i++) { switch(c) { /* Do I need to expand strings like </text> or ; to &amp; or something? */ case '&' : fprintf(fp, "&amp;"); break; case '<' : fprintf(fp, "&lt;"); break; case '>' : fprintf(fp, "&gt;"); break; case '"' : fprintf(fp, "&quot;"); break; case '\'': fprintf(fp, "&apos;"); break; default : fprintf(fp, "%c",c); } } fprintf(fp,"\n</text>\n"); drawn = 1; LAST_X=vdevice.cpVx+slength*vdevice.attr->a.textcos; LAST_Y=vdevice.cpVy+slength*vdevice.attr->a.textsin; /* debug: draw a line under text */ /*fprintf(fp,"<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\"/>\n",vdevice.cpVx,FLIPY(vdevice.cpVy),LAST_X,FLIPY(LAST_Y));*/ return(0); } /* fprintf(fp, "<textPath xline:href=\"#baseline\">\n"); fprintf(fp,"'</textPath></text>\n"); */ /******************************************************************************/ int SVG_char(char c){ /* SVG_char output a character */ char s[2]; s[0] = c; s[1]='\0'; SVG_string(s); return(0); } /******************************************************************************/ static int SVG_fill(int n, int x[], int y[]) { /* fill a polygon */ /* Maybe should leave edge off stroke:none */ int i; static char linefeed[2] = {' ','\n'}; closeline(); /* close line if required */ closeObject(); /* close line if required */ fprintf(fp,"<!-- Polygon --><path d=\"M %d,%d L\n", x[0],FLIPY(y[0])); for (i = 1; i < n; i++) { fprintf(fp, "%d,%d%c", x[i], FLIPY(y[i]),linefeed[(i % 8/7)]); } /* close path */ fprintf(fp," z\" style=\"stroke:#%2.2x%2.2x%2.2x;stroke-width:%dpx;fill:#%2.2x%2.2x%2.2x\"/>\n", svg_carr[curcol].red, svg_carr[curcol].green, svg_carr[curcol].blue, /* edge color */ MAX(1,vdevice.sizeX*curwid/10000), /* edge width */ svg_carr[curcol].red, svg_carr[curcol].green, svg_carr[curcol].blue); /* fill color*/ vdevice.cpVx = x[n - 1]; /* update current position */ vdevice.cpVy = y[n - 1]; LAST_X = vdevice.cpVx; LAST_Y = vdevice.cpVy; 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 svgdev = { "svg", /* name of device */ "large", /* name of large font */ "small", /* name of small font */ noop, /* Set drawing in back buffer */ SVG_char, /* Draw a hardware character */ noop, /* Check if a key was hit */ SVG_clear, /* Clear the screen to current color */ SVG_color, /* Set current color */ SVG_draw, /* Draw a line */ SVG_exit, /* Exit graphics */ SVG_fill, /* Fill a polygon */ SVG_font, /* Set hardware font */ noop, /* Set drawing in front buffer */ noop, /* Wait for and get the next key hit */ SVG_init, /* Initialize the device */ noop2, /* Get mouse/cross hair position */ SVG_mapcolor, /* Set color indices */ SVG_setlw, /* Set line width */ SVG_string, /* Draw a hardware string */ noop, /* Swap front and back buffers */ noop /* Syncronize the display */ }; /******************************************************************************/ /* _SVG_devcpy copy the svg device into vdevice.dev. */ int _SVG_devcpy() { vdevice.dev = svgdev; return(0); } /******************************************************************************/