#ident "@(#)VOGLE:source - driver/svg.c - VOGLE driver SVG (Scalable Vector Graphics) files"
#ident "@(#)VOGLE:author - John S. Urban"
#ident "@(#)VOGLE:version - 1.0, Mar 2005"
/*
Low level interface to SVG
based on version SVG 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.
layout02
Vector-based Small Images
Percent-based Small Images
A simpler way will probably evolve as SVG develops; the 1.2 proposal includes
a 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   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
#include
#include
#ifndef MINGW
#include
#include
#endif
#include
#include
#include
#include
#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,"\n");
fprintf(fp,"\n");
fprintf(fp,"\n");
time(&tod);
fprintf(fp,"\n",ctime(&tod));
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();
vogle_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,"\n"); /* Page Clear, End of Page Group */
fprintf(fp,"\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,"\n",pgroup); /* Page Clear, End of Page Group */
pgroup++; /* increment page id */
fprintf(fp,"\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, "\n",(int)slength);
for(i=0; (c=s[i]) != '\0' ;i++) {
switch(c) { /* Do I need to expand strings like or ; to & or something? */
case '&' : fprintf(fp, "&"); break;
case '<' : fprintf(fp, "<"); break;
case '>' : fprintf(fp, ">"); break;
case '"' : fprintf(fp, """); break;
case '\'': fprintf(fp, "'"); break;
default : fprintf(fp, "%c",c);
}
}
fprintf(fp,"\n\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,"\n",vdevice.cpVx,FLIPY(vdevice.cpVy),LAST_X,FLIPY(LAST_Y));*/
return(0);
}
/*
fprintf(fp, "\n");
fprintf(fp,"' \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,"\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);
}
/******************************************************************************/