/* * Grace - GRaphing, Advanced Computation and Exploration of data * * Home page: http://plasma-gate.weizmann.ac.il/Grace/ * * Copyright (c) 1996-2002 Grace Development Team * * Maintained by Evgeny Stambulchik * * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Driver for the Scalable Vector Graphics Format from W3C */ #include #include #include #include #include "defines.h" #include "utils.h" #include "cmath.h" #include "draw.h" #include "graphs.h" #include "device.h" #include "devlist.h" #include "patterns.h" #include "svgdrv.h" extern FILE *prstream; static Device_entry dev_svg = { DEVICE_FILE, "SVG", svginitgraphics, NULL, NULL, "svg", TRUE, FALSE, {DEFAULT_PAGE_WIDTH, DEFAULT_PAGE_HEIGHT, 72.0}, NULL }; typedef struct { double side; int *pattern_defined; int *pattern_empty; int *pattern_full; int *colorfilter_defined; int group_is_open; double line_width; Pen pen; int fillrule; int linecap; int linejoin; int linestyle; int draw; int fill; } Svg_data; static int init_svg_data(void) { Svg_data *data; int i; data = (Svg_data *) get_curdevice_data(); if (data == NULL) { /* we need to perform the allocations */ data = (Svg_data *) xrealloc(NULL, sizeof(Svg_data)); if (data == NULL) { return RETURN_FAILURE; } data->pattern_defined = NULL; data->pattern_empty = NULL; data->pattern_full = NULL; data->colorfilter_defined = NULL; set_curdevice_data((void *) data); } data->side = MIN2(page_width_pp, page_height_pp); data->pattern_defined = xrealloc(data->pattern_defined, number_of_patterns()*SIZEOF_INT); data->pattern_empty = xrealloc(data->pattern_empty, number_of_patterns()*SIZEOF_INT); data->pattern_full = xrealloc(data->pattern_full, number_of_patterns()*SIZEOF_INT); for (i = 0; i < number_of_patterns(); i++) { data->pattern_defined[i] = FALSE; data->pattern_empty[i] = FALSE; data->pattern_full[i] = FALSE; } svg_updatecmap(); data->group_is_open = FALSE; data->line_width = 0.0; data->pen.color = 0; data->pen.pattern = 0; data->fillrule = 0; data->linecap = 0; data->linejoin = 0; data->linestyle = 0; data->draw = FALSE; data->fill = FALSE; return RETURN_SUCCESS; } /* * SVG conventions : * Y coordinates increase downwards * angles increase clockwise */ /* * scale coordinates, using a SVG-viewer to do this gives rounding-problems */ static double scaleval (double val) { Svg_data *data; data = get_curdevice_data(); return val*data->side; } int register_svg_drv(void) { return register_device(dev_svg); } void svg_updatecmap(void) { int i; Svg_data *data; data = (Svg_data *) get_curdevice_data(); if (data == NULL) { return; } else { data->colorfilter_defined = xrealloc(data->colorfilter_defined, number_of_colors()*SIZEOF_INT); for (i = 0; i < number_of_colors(); i++) { data->colorfilter_defined[i] = FALSE; } } } static void define_pattern(int i, int c, Svg_data *data) { int j, k, l; fRGB *frgb; double bg_red, bg_green, bg_blue; if (data->pattern_defined[i] == TRUE && c < number_of_colors() && data->colorfilter_defined[c] == TRUE) { return; } if (data->pattern_defined[i] != TRUE) { /* testing if the pattern is either empty or full */ data->pattern_empty[i] = TRUE; data->pattern_full[i] = TRUE; for (j = 0; j < 32; j++) { if (pat_bits[i][j] != 0x00) { data->pattern_empty[i] = FALSE; } if (pat_bits[i][j] != 0xff) { data->pattern_full[i] = FALSE; } } } if (data->pattern_empty[i] != TRUE && data->pattern_full[i] != TRUE) { fprintf(prstream, " \n"); /* test if the pattern is already defined. */ if (data->pattern_defined[i] != TRUE) { /* this is an horrible hack ! */ /* we define pixels as squares in vector graphics */ /* first fill the whole pattern */ fprintf(prstream, " \n", i, 16, 16); fprintf(prstream," \n"); for (j = 0; j < 256; j++) { k = j/16; l = j%16; if ((pat_bits[i][j/8] >> (j%8)) & 0x01) { /* the bit is set */ fprintf(prstream, " \n", l, 15-k); } } fprintf(prstream, " \n"); data->pattern_defined[i] = TRUE; } /* test if the needed colorfilter is already defined. */ /* color-patterns can be drawn with black patterns and then applying a colorfilter to change white to the background-color and black to the patterncolor. */ if (c < number_of_colors() && data->colorfilter_defined[c] != TRUE) { frgb = get_frgb(getbgcolor()); bg_red=frgb->red; bg_green=frgb->green; bg_blue=frgb->blue; frgb = get_frgb(c); fprintf(prstream, " \n"); fprintf(prstream, " \n"); fprintf(prstream, " \n", frgb->red, bg_red); fprintf(prstream, " \n", frgb->green, bg_green); fprintf(prstream, " \n", frgb->blue, bg_blue); fprintf(prstream, " \n"); fprintf(prstream, " \n"); data->colorfilter_defined[c] = TRUE; } fprintf(prstream, " \n"); } } /* * escape special characters */ static char *escape_specials(unsigned char *s, int len) { static char *es = NULL; int i, elen = 0; elen = 0; for (i = 0; i < len; i++) { if (s[i] == '&') { elen += 4; } else if (s[i] == '<' || s[i] == '>') { elen += 3; } elen++; } es = xrealloc(es, (elen + 1)*SIZEOF_CHAR); elen = 0; for (i = 0; i < len; i++) { if (s[i] == '&') { es[elen++] = '&'; es[elen++] = 'a'; es[elen++] = 'm'; es[elen++] = 'p'; es[elen++] = ';'; } else if (s[i] == '<') { es[elen++] = '&'; es[elen++] = 'l'; es[elen++] = 't'; es[elen++] = ';'; } else if (s[i] == '>') { es[elen++] = '&'; es[elen++] = 'g'; es[elen++] = 't'; es[elen++] = ';'; } else { es[elen++] = (char) s[i]; } } es[elen] = '\0'; return (es); } int svginitgraphics(void) { /* device-dependent routines */ devupdatecmap = svg_updatecmap; devdrawpixel = svg_drawpixel; devdrawpolyline = svg_drawpolyline; devfillpolygon = svg_fillpolygon; devdrawarc = svg_drawarc; devfillarc = svg_fillarc; devputpixmap = svg_putpixmap; devputtext = svg_puttext; devleavegraphics = svg_leavegraphics; if (init_svg_data() != RETURN_SUCCESS) { return RETURN_FAILURE; } fprintf(prstream, "\n"); fprintf(prstream, "\n"); fprintf(prstream, "\n", bi_version_string()); /* Let do the SVG-Viewer the conversion of coordinates. */ fprintf(prstream, "\n", page_width_in, page_height_in, 0.0, 0.0, page_width_pp, page_height_pp); fprintf(prstream, " \n", page_height_pp); /* project description */ if (get_project_description() != NULL) { fprintf(prstream, " %s\n", get_project_description()); } return RETURN_SUCCESS; } static void svg_group_props (int draw, int fill) { int i, needs_group; double lw; Pen pen; int fillrule, linecap, linejoin, linestyle; RGB *prgb; int red, green, blue; Svg_data *data; data = (Svg_data *) get_curdevice_data(); /* do we need to redefine a group with new properties ? */ needs_group = (data->group_is_open == TRUE) ? FALSE : TRUE; lw = scaleval(getlinewidth()); fillrule = getfillrule(); linecap = getlinecap(); linejoin = getlinejoin(); linestyle = getlinestyle(); if (fabs(lw - data->line_width) >= 1.0e-6*(1.0 + fabs(data->line_width))) { needs_group = TRUE; } pen = getpen(); if ((pen.color != data->pen.color) || (pen.pattern != data->pen.pattern)) { needs_group = TRUE; } if (fillrule != data->fillrule) { needs_group = TRUE; } if (linecap != data->linecap) { needs_group = TRUE; } if (linejoin != data->linejoin) { needs_group = TRUE; } if (linestyle != data->linestyle) { needs_group = TRUE; } if ((draw != data->draw) || (fill != data->fill)) { needs_group = TRUE; } if (needs_group == TRUE) { /* we need to write the characteristics of the group */ if (data->group_is_open == TRUE) { /* first, we should close the preceding group */ fprintf(prstream, " \n"); data->group_is_open = FALSE; } define_pattern(pen.pattern, pen.color, data); prgb = get_rgb(pen.color); if (prgb != NULL) { red = prgb->red >> (GRACE_BPP - 8); green = prgb->green >> (GRACE_BPP - 8); blue = prgb->blue >> (GRACE_BPP - 8); } else { red = 0; green = 0; blue = 0; } if (fill && data->pattern_empty[pen.pattern] != TRUE) { if (data->pattern_full[pen.pattern] == TRUE) { fprintf(prstream, " \n"); data->group_is_open = TRUE; data->line_width = lw; data->pen = pen; data->fillrule = fillrule; data->linecap = linecap; data->linejoin = linejoin; data->linestyle = linestyle; data->draw = draw; data->fill = fill; } } void svg_drawpixel(VPoint vp) { svg_group_props(FALSE, TRUE); fprintf(prstream, " \n", scaleval(vp.x), scaleval(vp.y), scaleval(1.0), scaleval(1.0)); } void svg_drawpolyline(VPoint *vps, int n, int mode) { int i; if (n <= 0) { return; } svg_group_props(TRUE, FALSE); fprintf(prstream, " \n"); } else { fprintf(prstream, "\"/>\n"); } } void svg_fillpolygon(VPoint *vps, int nc) { int i; if (nc <= 0) { return; } svg_group_props(FALSE, TRUE); fprintf(prstream, " \n"); } void svg_drawarc(VPoint vp1, VPoint vp2, int a1, int a2) { VPoint center; double rx, ry; if (a1 == a2) { return; } center.x = 0.5*(vp1.x + vp2.x); center.y = 0.5*(vp1.y + vp2.y); rx = 0.5*fabs(vp2.x - vp1.x); ry = 0.5*fabs(vp2.y - vp1.y); svg_group_props(TRUE, FALSE); if ((a1 - a2)%360 == 0) { fprintf(prstream, " \n", scaleval(rx), scaleval(ry), scaleval(center.x), scaleval(center.y)); } else { VPoint start, end; start.x = center.x + rx*cos((M_PI/180.0)*a1); start.y = center.y + ry*sin((M_PI/180.0)*a1); end.x = center.x + rx*cos((M_PI/180.0)*a2); end.y = center.y + ry*sin((M_PI/180.0)*a2); fprintf(prstream, " \n", scaleval(start.x), scaleval(start.y), scaleval(rx), scaleval(ry), 0, (abs(a2 - a1) > 180) ? 1 : 0, (a2 > a1) ? 1 : 0, scaleval(end.x), scaleval(end.y)); } } void svg_fillarc(VPoint vp1, VPoint vp2, int a1, int a2, int mode) { VPoint center; double rx, ry; if (a1 == a2) { return; } center.x = 0.5*(vp1.x + vp2.x); center.y = 0.5*(vp1.y + vp2.y); rx = 0.5*fabs(vp2.x - vp1.x); ry = 0.5*fabs(vp2.y - vp1.y); svg_group_props(FALSE, TRUE); if ((a1 - a2)%360 == 0) { fprintf(prstream, " \n", scaleval(rx), scaleval(ry), scaleval(center.x), scaleval(center.y)); } else { VPoint start, end; start.x = center.x + rx*cos((M_PI/180.0)*a1); start.y = center.y + ry*sin((M_PI/180.0)*a1); end.x = center.x + rx*cos((M_PI/180.0)*a2); end.y = center.y + ry*sin((M_PI/180.0)*a2); if (mode == ARCFILL_CHORD) { fprintf(prstream, " \n", scaleval(start.x), scaleval(start.y), scaleval(rx), scaleval(ry), 0, (abs(a2 - a1) > 180) ? 1 : 0, (a2 > a1) ? 1 : 0, scaleval(end.x), scaleval(end.y)); } else { fprintf(prstream, " \n", scaleval(center.x), scaleval(center.y), scaleval(start.x), scaleval(start.y), scaleval(rx), scaleval(ry), 0, (abs(a2 - a1) > 180) ? 1 : 0, (a2 > a1) ? 1 : 0, scaleval(end.x), scaleval(end.y)); } } } void svg_putpixmap(VPoint vp, int width, int height, char *databits, int pixmap_bpp, int bitmap_pad, int pixmap_type) { /* not implemented yet */ } void svg_puttext(VPoint vp, char *s, int len, int font, TextMatrix *tm, int underline, int overline, int kerning) { char *fontalias, *fontfullname, *fontweight; char *dash, *family, *familyff; double fsize = scaleval(1); svg_group_props(FALSE, TRUE); fprintf(prstream, " ", tm->cxx, tm->cyx, -tm->cxy, -tm->cyy, scaleval(vp.x), scaleval(vp.y)); fprintf(prstream, escape_specials((unsigned char *) s, len)); fprintf(prstream, "\n"); } void svg_leavegraphics(void) { Svg_data *data; data = (Svg_data *) get_curdevice_data(); if (data->group_is_open == TRUE) { fprintf(prstream, " \n"); data->group_is_open = FALSE; } fprintf(prstream, " \n"); fprintf(prstream, "\n"); }