/* Hello, Emacs, this is -*-C-*- * $Id: svg.trm,v 1.63.2.13 2009/01/22 20:27:38 sfeam Exp $ */ /*------------------------------------------------------------------------------------------------------------------------------------ GNUPLOT - svg.trm This file is included by ../term.c. This terminal driver supports: W3C Scalable Vector Graphics AUTHOR Amedeo Farello afarello@libero.it HEAVILY MODIFIED by Hans-Bernhard Br"oker broeker@physik.rwth-aachen.de ------------------------------------------------------------------------------------------------------------------------------------*/ /* PM3D support by Johannes Zellner , May-16-2002 */ /* set_color fixes by Petr Mikulik , June-10-2002 */ /* ISO-Latin encoding, Font selection fixes, option "fixed|dynamic" by * Wilhelm Braunschober , Feb-21-2002 */ /* * Additional code for gnuplot versions 4.2 and 4.3 * * Tweaked code for compatibility with Sodipodi svg viewer/editor. * Added enhanced text support. * Additional line properties. * Increase resolution by adding a coordinate scale factor. * CODDLE_NONCOMPLIANT_VIEWERS * Support dashed lines, TC_* color model. * Change path markup from style='attribute: foo' to attribute='foo' * * Ethan Merritt */ #include "driver.h" #ifdef TERM_REGISTER register_term(svg) #endif #ifdef TERM_PROTO TERM_PUBLIC void SVG_options __PROTO ((void)); TERM_PUBLIC void SVG_init __PROTO ((void)); TERM_PUBLIC void SVG_graphics __PROTO ((void)); TERM_PUBLIC void SVG_text __PROTO ((void)); TERM_PUBLIC void SVG_linetype __PROTO ((int linetype)); TERM_PUBLIC void SVG_move __PROTO ((unsigned int x, unsigned int y)); TERM_PUBLIC void SVG_vector __PROTO ((unsigned int x, unsigned int y)); TERM_PUBLIC void SVG_put_text __PROTO ((unsigned int x, unsigned int y, const char *str)); TERM_PUBLIC void SVG_reset __PROTO ((void)); TERM_PUBLIC int SVG_justify_text __PROTO ((enum JUSTIFY mode)); TERM_PUBLIC int SVG_text_angle __PROTO ((int ang)); TERM_PUBLIC void SVG_point __PROTO ((unsigned int x, unsigned int y, int pointstyle)); TERM_PUBLIC int SVG_set_font __PROTO ((const char *font)); /* TERM_PUBLIC void SVG_pointsize __PROTO((double pointsize)); */ TERM_PUBLIC void SVG_fillbox __PROTO((int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height)); TERM_PUBLIC void SVG_linewidth __PROTO ((double linewidth)); TERM_PUBLIC int SVG_make_palette __PROTO((t_sm_palette *)); TERM_PUBLIC void SVG_previous_palette __PROTO((void)); TERM_PUBLIC void SVG_set_color __PROTO((t_colorspec *)); TERM_PUBLIC void SVG_filled_polygon __PROTO((int, gpiPoint *)); TERM_PUBLIC void ENHsvg_OPEN __PROTO((char *, double, double, TBOOLEAN, TBOOLEAN, int)); TERM_PUBLIC void ENHsvg_FLUSH __PROTO((void)); TERM_PUBLIC void ENHsvg_put_text __PROTO((unsigned int, unsigned int, const char *)); TERM_PUBLIC void ENHsvg_writec __PROTO((int)); TERM_PUBLIC void SVG_path __PROTO((int p)); #define SVG_SCALE 10. #define Y(y) ((float)((int)term->ymax - (int)y) / SVG_SCALE) #define X(x) ((float)(x) / SVG_SCALE) #define SVG_XMAX (600 * SVG_SCALE) #define SVG_YMAX (480 * SVG_SCALE) #endif /* TERM_PROTO */ #ifndef TERM_PROTO_ONLY #ifdef TERM_BODY static t_sm_palette SVG_palette; static unsigned char SVG_red = 0; static unsigned char SVG_green = 0; static unsigned char SVG_blue = 0; static unsigned char SVG_color_mode = TC_DEFAULT; static char *SVG_linecolor = NULL; static TBOOLEAN SVG_groupFilledIsOpen = FALSE; /* open pm3d group flag*/ struct SVG_PEN { double width; char color[8]; }; static unsigned int SVG_xSize = SVG_XMAX; /* plot horizontal size */ static unsigned int SVG_ySize = SVG_YMAX; /* plot vertical size*/ static TBOOLEAN SVG_fixed_size = TRUE; /* make SVG viewer size fixed */ static unsigned int SVG_xLast = UINT_MAX; /* current pen horizontal position*/ static unsigned int SVG_yLast = UINT_MAX; /* current pen vertical position*/ static int SVG_LineType = LT_NODRAW; /* current line type*/ static double SVG_LineWidth = 1.0; /* current line width*/ static double SVG_linewidth_factor = 1.0; /* Multiplier for linewidths */ static TBOOLEAN SVG_rounded = FALSE; /* linejoin and linecap */ static int SVG_TextAngle = 0; /* current text orientation*/ static enum JUSTIFY SVG_TextJust = LEFT; /* current text justification*/ /* default text font family: */ static char SVG_fontNameDef[MAX_ID_LEN + 1] = "Arial"; static double SVG_fontSizeDef = 12; /* default text size*/ /* current text font family: */ static char SVG_fontNameCur[MAX_ID_LEN + 1] = "Arial"; static double SVG_fontSizeCur = 12; /* current text size*/ static TBOOLEAN SVG_groupIsOpen = FALSE; /* open group flag*/ static TBOOLEAN SVG_pathIsOpen = FALSE; /* open path flag*/ static unsigned int SVG_path_count = 0; /* size of current path*/ static struct SVG_PEN SVG_pens[16]; /* pen descriptors*/ static int SVG_fillPattern = -1; /* active fill pattern (-1 == undefined) */ static unsigned int SVG_fillPatternIndex = 0; static int SVG_fontAscent = 0; /* estimated current font ascent*/ static int SVG_fontDescent = 0; /* estimated current font descent*/ static int SVG_fontLeading = 0; /* estimated current font leading*/ static int SVG_fontAvWidth = 0; /* estimated current font char average width*/ static short SVG_Pen_RealID __PROTO ((int)); static void SVG_PathOpen __PROTO ((void)); static void SVG_PathClose __PROTO ((void)); static void SVG_PathLimit __PROTO ((void)); static void SVG_GroupOpen __PROTO ((void)); static void SVG_GroupClose __PROTO ((void)); static void SVG_SetFont __PROTO ((const char *name, double size)); static void SVG_GroupFilledOpen __PROTO ((void)); static void SVG_GroupFilledClose __PROTO ((void)); static void SVG_StyleColor __PROTO((const char*)); static void SVG_StyleFillColor __PROTO((void)); static void SVG_local_reset __PROTO((void)); static void SVG_DefineFillPattern __PROTO((int fillpat)); static void SVG_MoveForced __PROTO((unsigned int x, unsigned int y)); /* Points to source of requested embedded font */ static char *SVG_embedded_font = NULL; static void SVG_load_fontfile __PROTO((char *fontfile)); /* Stuff for enhanced text mode */ static int ENHsvg_string_state = 0; static double ENHsvg_x_offset = 0; static TBOOLEAN ENHsvg_preserve_spaces = FALSE; #define CODDLE_NONCOMPLIANT_VIEWERS 1 /* Use pt rather than em spacing */ /* Support for dashed lines */ #define SVG_dashtypes 5 static TBOOLEAN SVG_dashed = FALSE; static char *SVG_dashpattern[SVG_dashtypes] = { "", " 5,8", " 1,4", " 8,4,2,4", " 9,4,1,4,1,4" }; /*------------------------------------------------------------------------------------------------------------------------------------ SVG_Pen_RealID ------------------------------------------------------------------------------------------------------------------------------------*/ static short SVG_Pen_RealID (int inPenCode) { if (inPenCode >= 13) inPenCode %= 13; /* normalize pen code*/ inPenCode += 3; if (inPenCode < 0) inPenCode = 0; /* LT_NODRAW or LT_BACKGROUND should use background color */ return (inPenCode); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_GroupOpen ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_GroupOpen () { SVG_GroupFilledClose(); if (!SVG_groupIsOpen) { fprintf (gpoutfile, "\n"); SVG_groupIsOpen = TRUE; } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_GroupClose ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_GroupClose () { SVG_GroupFilledClose(); if (SVG_groupIsOpen) { fputs ("\n", gpoutfile); SVG_groupIsOpen = FALSE; SVG_fillPattern = -1; } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_PathOpen ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_PathOpen () { if (!SVG_pathIsOpen) { SVG_GroupFilledClose(); fputs ("\t 0) fprintf(gpoutfile, "stroke-dasharray='%s' ", SVG_dashpattern[SVG_LineType % SVG_dashtypes]); fputs (" d='", gpoutfile); SVG_pathIsOpen = TRUE; } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_PathClose ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_PathClose () { if (SVG_pathIsOpen) { SVG_GroupFilledClose(); fputs ("'>\n", gpoutfile); SVG_path_count = 0; SVG_pathIsOpen = FALSE; } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_PathLimit ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_PathLimit () { if (SVG_path_count % 8 == 0) /* avoid excessive line length*/ fputs ("\n\t\t", gpoutfile); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_SetFont ------------------------------------------------------------------------------------------------------------------------------------*/ static void SVG_SetFont (const char *name, double size) { if (name && name != SVG_fontNameCur) strncpy (SVG_fontNameCur, name, sizeof(SVG_fontNameCur)-1); SVG_fontSizeCur = size; /* since we cannot interrogate SVG about text properties and according * to SVG 1.0 W3C Candidate Recommendation 2 August 2000 the * "line-height" of the 'text' element is defined to be equal to the * 'font-size' (!), we have to to define font properties in a less * than optimal way */ SVG_fontAscent = (int) (SVG_fontSizeCur * 1.00 * SVG_SCALE); /* estimated current font ascent*/ SVG_fontDescent = (int) (SVG_fontSizeCur * 0.25 * SVG_SCALE); /* estimated current font descent*/ SVG_fontLeading = (int) (SVG_fontSizeCur * 0.25 * SVG_SCALE); /* estimated current font leading*/ SVG_fontAvWidth = (int) (SVG_fontSizeCur * 0.70 * SVG_SCALE); /* estimated current font char average width*/ } static void SVG_GroupFilledOpen() { if (!SVG_groupFilledIsOpen) { SVG_PathClose(); fputs("\t\n", gpoutfile); SVG_groupFilledIsOpen = TRUE; } } static void SVG_GroupFilledClose() { if (SVG_groupFilledIsOpen) { fputs("\t\n", gpoutfile); SVG_groupFilledIsOpen = FALSE; } } static void SVG_StyleColor(const char* paint) { if (SVG_color_mode == TC_RGB) fprintf(gpoutfile, "%s = 'rgb(%3d, %3d, %3d)'", paint, SVG_red, SVG_green, SVG_blue); else if (SVG_color_mode == TC_LT) fprintf(gpoutfile, "%s = '%s'", paint, SVG_linecolor); else fprintf(gpoutfile, "%s = 'currentColor'", paint); } static void SVG_StyleFillColor() { SVG_StyleColor("fill"); } static void SVG_DefineFillPattern(int fillpat) { char *path; char *style="stroke"; fillpat %= 8; if (fillpat != SVG_fillPattern) { SVG_fillPattern = fillpat; SVG_PathClose(); SVG_fillPatternIndex++; fprintf(gpoutfile, "\t\n" "\t\t\n", SVG_fillPatternIndex); switch (fillpat) { default: case 0: path=""; break; case 1: path="M0,0 L8,8 M0,8 L8,0"; break; case 2: path="M0,0 L8,8 M0,8 L8,0 M0,4 L4,8 L8,4 L4,0 L0,4"; break; case 3: path="M0,0 L0,8 L8,8 L8,0 L0,0"; style="fill"; break; case 4: path="M-4,0 L8,12 M0,-4 L12,8"; break; case 5: path="M-4,8 L8,-4 M0,12 L12,0"; break; case 6: path="M-2,8 L4,-4 M0,12 L8,-4 M4,12 L10,0"; break; case 7: path="M-2,0 L4,12 M0,-4 L8,12 M4,-4 L10,8"; break; } if (*path) { if (SVG_color_mode == TC_RGB) fprintf(gpoutfile,"\t\t\t\n", style, SVG_red, SVG_green, SVG_blue, path); else if (SVG_color_mode == TC_LT) fprintf(gpoutfile, "\t\t\t\n", style, SVG_linecolor, path); else fprintf(gpoutfile, "\t\t\t\n", style, path); } fputs("\t\t\n" "\t\n", gpoutfile); } } static void SVG_MoveForced(unsigned int x, unsigned int y) { if (SVG_path_count > 512) SVG_PathClose(); SVG_PathOpen (); fprintf (gpoutfile, "M%.1f,%.1f ", X(x), Y(y)); SVG_path_count++; SVG_PathLimit (); SVG_xLast = x; SVG_yLast = y; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_options ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_options () { struct value a; /* Annoying hack to handle the case of 'set termoption' after */ /* we have already initialized the terminal settings. */ if (c_token != 2) SVG_local_reset(); while (!END_OF_COMMAND) { if (almost_equals(c_token, "s$ize")) { double value; c_token++; if (END_OF_COMMAND) int_error(c_token,"expecting x size"); value = real(const_express(&a)); if (value < 2 || value > 8192) int_error(c_token,"x size out of range"); SVG_xSize = value * SVG_SCALE; if (equals(c_token,",")) c_token++; if (END_OF_COMMAND) int_error(c_token,"expecting y size"); value = real(const_express(&a)); if (value < 2 || value > 8192) int_error(c_token,"y size out of range"); SVG_ySize = value * SVG_SCALE; continue; } if (almost_equals(c_token, "d$ynamic")) { c_token++; SVG_fixed_size = FALSE; continue; } if (almost_equals(c_token, "fi$xed")){ c_token++; SVG_fixed_size = TRUE; continue; } if (almost_equals(c_token, "enh$anced")) { c_token++; term->put_text = ENHsvg_put_text; term->flags |= TERM_ENHANCED_TEXT; continue; } if (almost_equals(c_token, "noenh$anced")) { c_token++; term->put_text = SVG_put_text; term->flags &= ~TERM_ENHANCED_TEXT; continue; } if (almost_equals(c_token, "fn$ame") || almost_equals(c_token, "font")) { char *s, *comma; c_token++; if (!(s = try_to_get_string())) int_error(c_token,"expecting font name"); comma = strrchr(s,','); if (comma && (1 == sscanf(comma + 1, "%lf", &SVG_fontSizeDef))) *comma = '\0'; if (*s) strncpy(SVG_fontNameDef, s, sizeof(SVG_fontNameDef)); free(s); continue; } if (almost_equals(c_token, "fs$ize")) { c_token++; if (END_OF_COMMAND) int_error(c_token,"fsize: expecting font size"); SVG_fontSizeDef = real(const_express(&a)); continue; } if (almost_equals(c_token, "fontfile")) { char *fontfile_name; c_token++; fontfile_name = try_to_get_string(); if (!fontfile_name) int_error(c_token, "Font filename expected"); gp_expand_tilde(&fontfile_name); #if defined(PIPES) if ( *fontfile_name == '<' ) { SVG_embedded_font = fontfile_name; } else #endif SVG_embedded_font = fontpath_fullname(fontfile_name); if (!SVG_embedded_font) int_error(c_token, "Font file '%s' not found", fontfile_name); continue; } if (almost_equals(c_token, "linew$idth") || equals(c_token, "lw")) { c_token++; SVG_linewidth_factor = real(const_express(&a)); if (SVG_linewidth_factor <= 0.0) SVG_linewidth_factor = 1.0; continue; } if (almost_equals (c_token, "round$ed")) { c_token++; SVG_rounded = TRUE; continue; } if (equals (c_token, "butt")) { c_token++; SVG_rounded = FALSE; continue; } if (equals(c_token, "solid")) { c_token++; SVG_dashed = FALSE; continue; } if (almost_equals(c_token, "dash$ed")) { c_token++; SVG_dashed = TRUE; continue; } int_error(c_token, "unrecognized terminal option"); } /* I don't think any error checks on font name are possible; just set it */ SVG_set_font(""); /* Save options back into options string in normalized format */ sprintf(term_options, "size %d,%d%s %s fname '%s' fsize %g ", (int)(SVG_xSize/SVG_SCALE), (int)(SVG_ySize/SVG_SCALE), SVG_fixed_size ? " fixed": " dynamic", term->put_text == ENHsvg_put_text ? "enhanced" : "", SVG_fontNameCur, SVG_fontSizeCur); if (SVG_embedded_font) { sprintf(term_options + strlen(term_options), "fontfile \"%s\" ", SVG_embedded_font); } sprintf(term_options + strlen(term_options), SVG_rounded ? "rounded " : "butt "); sprintf(term_options + strlen(term_options), SVG_dashed ? "dashed " : "solid "); if (SVG_linewidth_factor != 1.0) { sprintf(term_options + strlen(term_options), "linewidth %3.1f ", SVG_linewidth_factor); } } static void SVG_local_reset() { SVG_xSize = SVG_XMAX; SVG_ySize = SVG_YMAX; SVG_fixed_size = TRUE; strcpy(SVG_fontNameDef,"Arial"); SVG_fontSizeDef = 12; if (SVG_embedded_font) free(SVG_embedded_font); SVG_embedded_font = NULL; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_init ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_init () { double stroke_width; char *svg_encoding = ""; /* setup pens*/ SVG_pens[0].width = SVG_LineWidth; strcpy (SVG_pens[0].color, "white"); /* should really be background */ SVG_pens[1].width = SVG_LineWidth; strcpy(SVG_pens[1].color, "black"); SVG_pens[2].width = SVG_LineWidth; strcpy(SVG_pens[2].color, "gray"); SVG_pens[3].width = SVG_LineWidth; strcpy(SVG_pens[3].color, "red"); SVG_pens[4].width = SVG_LineWidth; strcpy(SVG_pens[4].color, "green"); SVG_pens[5].width = SVG_LineWidth; strcpy(SVG_pens[5].color, "blue"); SVG_pens[6].width = SVG_LineWidth; strcpy(SVG_pens[6].color, "cyan"); SVG_pens[7].width = SVG_LineWidth; sprintf(SVG_pens[7].color, "#%2.2X%2.2X%2.2X", 21, 117, 69); /* pine green*/ SVG_pens[8].width = SVG_LineWidth; sprintf (SVG_pens[8].color, "#%2.2X%2.2X%2.2X", 0, 0, 148); /* navy*/ SVG_pens[9].width = SVG_LineWidth; sprintf (SVG_pens[9].color, "#%2.2X%2.2X%2.2X", 255, 153, 0); /* orange*/ SVG_pens[10].width = SVG_LineWidth; sprintf (SVG_pens[10].color, "#%2.2X%2.2X%2.2X", 0, 153, 161); /* green blue*/ SVG_pens[11].width = SVG_LineWidth; sprintf (SVG_pens[11].color, "#%2.2X%2.2X%2.2X", 214, 214, 69); /* olive*/ SVG_pens[12].width = SVG_LineWidth; sprintf (SVG_pens[12].color, "#%2.2X%2.2X%2.2X", 163, 145, 255); /* cornflower*/ SVG_pens[13].width = SVG_LineWidth; sprintf (SVG_pens[13].color, "#%2.2X%2.2X%2.2X", 255, 204, 0); /* gold*/ SVG_pens[14].width = SVG_LineWidth; sprintf (SVG_pens[14].color, "#%2.2X%2.2X%2.2X", 214, 0, 120); /* mulberry*/ SVG_pens[15].width = SVG_LineWidth; sprintf (SVG_pens[15].color, "#%2.2X%2.2X%2.2X", 171, 214, 0); /* green yellow*/ SVG_LineType = LT_NODRAW; /* set xmax, ymax*/ term->xmax = SVG_xSize; term->ymax = SVG_ySize; /* set current font*/ SVG_SetFont (SVG_fontNameCur, SVG_fontSizeCur); /* set h_char, v_char*/ term->h_char = SVG_fontAvWidth; term->v_char = (SVG_fontAscent + SVG_fontDescent + SVG_fontLeading); /* set h_tic, v_tic*/ term->h_tic = term->v_char / 2; term->v_tic = term->v_char / 2; /* write file header*/ switch (encoding) { case S_ENC_ISO8859_1: svg_encoding = "encoding=\"iso-8859-1\" "; break; case S_ENC_ISO8859_2: svg_encoding = "encoding=\"iso-8859-2\" "; break; case S_ENC_ISO8859_15: svg_encoding = "encoding=\"iso-8859-15\" "; break; case S_ENC_CP850: svg_encoding = "encoding=\"ibm-850\" "; break; case S_ENC_CP852: svg_encoding = "encoding=\"ibm-852\" "; break; case S_ENC_CP1250: svg_encoding = "encoding=\"windows-1250\" "; break; case S_ENC_KOI8_R: svg_encoding = "encoding=\"koi8-r\" "; break; case S_ENC_KOI8_U: svg_encoding = "encoding=\"koi8-u\" "; break; case S_ENC_CP437: svg_encoding = ""; break; default: /* UTF-8 */ svg_encoding = "encoding=\"utf-8\" "; break; } fprintf (gpoutfile, "\n" "\n" "xmax / SVG_SCALE), (unsigned int) (term->ymax / SVG_SCALE)); fprintf (gpoutfile, "viewBox=\"0 0 %u %u\"\n", (unsigned int) (term->xmax / SVG_SCALE), (unsigned int) (term->ymax / SVG_SCALE)); fprintf (gpoutfile, " xmlns=\"http://www.w3.org/2000/svg\"\n"); fprintf (gpoutfile, " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"); fprintf (gpoutfile, "Produced by GNUPLOT %s patchlevel %s \n\n", gnuplot_version, gnuplot_patchlevel); /* Start prologue section of output file, and load fonts if requested */ fprintf(gpoutfile,"\n"); if (SVG_embedded_font) SVG_load_fontfile(SVG_embedded_font); /* definitions of point symbols */ /* FIXME: SVG scales linewidth along with the marker itself, and * there seems to be no way to avoid that without copying the * marker definition into the file, rather than referencing a * defined one :-( That would make for much larger files */ /* "\t\n" */ stroke_width = 2.0 *SVG_SCALE / term->h_tic; fprintf (gpoutfile, "\n" /* dot: */ "\t\n" /* 0 plus */ "\t\n" /* 1 X */ "\t\n" /* 2 star */ "\t\n" /* 3 box */ "\t\n" /* 4 box filled */ "\t\n" /* 5 circle */ "\t\n" /* 6 circle (disk) filled */ "\t\n" /* 7 triangle */ "\t\n" /* 8 triangle filled */ "\t\n" /* 9 upside down triangle */ "\t\n" /* 10 upside down triangle filled */ "\t\n" /* 11 diamond */ "\t\n" /* 12 diamond filled */ "\t\n" /* NOTE: Fill patterns must be defined after the stroke color has been * set to use the correct (current) stroke color. Therefore we can't * define fill patterns here. */ "\n" , stroke_width , stroke_width , stroke_width , stroke_width , stroke_width , stroke_width , stroke_width ); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_graphics ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_graphics () { /* EAM 5-May-2004 We must force a new group with fill:none in order for */ /* multiple plots per page to work. Otherwise new plots are black-filled */ SVG_GroupOpen(); SVG_fillPattern = -1; SVG_fillPatternIndex = 0; SVG_groupFilledIsOpen = FALSE; SVG_color_mode = TC_DEFAULT; SVG_pathIsOpen = FALSE; /* reset position*/ SVG_xLast = SVG_yLast = UINT_MAX; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_text ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_text () { SVG_PathClose (); SVG_GroupClose (); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_reset ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_reset () { fputs("\n\n", gpoutfile); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_linetype ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_linetype (int linetype) { SVG_color_mode = TC_DEFAULT; if (linetype != SVG_LineType) { SVG_PathClose (); SVG_GroupClose (); SVG_LineType = linetype; SVG_GroupOpen (); } } TERM_PUBLIC void SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height) { gpiPoint corner[4]; corner[0].x = x1; corner[0].y = y1; corner[1].x = x1+width; corner[1].y = y1; corner[2].x = x1+width; corner[2].y = y1+height; corner[3].x = x1; corner[3].y = y1+height; corner->style = style; SVG_filled_polygon(4, corner); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_linewidth - verificare ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_linewidth (double linewidth) { if (linewidth != SVG_LineWidth) { short k; SVG_LineWidth = linewidth; for (k = 0; k < 16; k++) SVG_pens[k].width = SVG_LineWidth; SVG_PathClose (); SVG_GroupClose (); SVG_GroupOpen (); } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_move ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_move (unsigned int x, unsigned int y) { if (x != SVG_xLast || y != SVG_yLast) { SVG_MoveForced(x, y); } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_vector ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_vector (unsigned int x, unsigned int y) { if (x != SVG_xLast || y != SVG_yLast) { if (!SVG_pathIsOpen) { /* The SVG 'path' MUST have a 'moveto' as first command. */ SVG_MoveForced(SVG_xLast, SVG_yLast); } fprintf (gpoutfile, "L%.1f,%.1f ", X(x), Y(y)); SVG_path_count++; SVG_PathLimit (); SVG_xLast = x; SVG_yLast = y; } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_point ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_point (unsigned int x, unsigned int y, int number) { char color_spec[0x40]; if (SVG_color_mode == TC_RGB) sprintf(color_spec, " color='rgb(%3d, %3d, %3d)'", SVG_red, SVG_green, SVG_blue); else if (SVG_color_mode == TC_LT) sprintf(color_spec, " color='%s'", SVG_linecolor); else *color_spec = '\0'; SVG_PathClose (); if (number < 0) { /* do dot */ fprintf (gpoutfile, "\ \t\n", X(x), Y(y), color_spec); } else { /* draw a point symbol */ fprintf (gpoutfile, "\ \t\ \n", number % 13, X(x), Y(y), term_pointsize * term->h_tic / (2 * SVG_SCALE), color_spec); } SVG_xLast = x; SVG_yLast = y; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_justify_text ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC int SVG_justify_text (enum JUSTIFY mode) { SVG_TextJust = mode; return (TRUE); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_text_angle ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC int SVG_text_angle (int ang) { /* Can only do pure horizontal or vertical */ SVG_TextAngle = ang; return (TRUE); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_put_text ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_put_text (unsigned int x, unsigned int y, const char *str) { char *alignment; int h = x, v = y; SVG_PathClose (); /* horizontal justification*/ switch (SVG_TextJust) { case LEFT: alignment = "start"; break; case CENTRE: alignment = "middle"; break; case RIGHT: default: /* can't happen, just to make gcc happy */ alignment = "end"; break; } /* vertical justification*/ if (SVG_TextAngle % 180) { /* vertical text */ h += (SVG_fontAscent - SVG_fontDescent) / 2; } else { /* horizontal text */ v -= (SVG_fontAscent - SVG_fontDescent) / 2; } /* define text position and attributes */ fprintf (gpoutfile, "\t\n", SVG_fontNameCur, SVG_fontSizeCur, alignment); /* output text (unless the enhanced_text processing is in action) */ if (strstr(str," ")) fputs ("\t\t", gpoutfile); else fputs ("\t\t", gpoutfile); if (!ENHsvg_string_state) { while (*str) { /* Escape SVG reserved characters */ switch (*str) { case '<': fputs("<", gpoutfile); break; case '&': if (str[1] == '#' && str[2] == 'x') fputc(*str, gpoutfile); else fputs("&", gpoutfile); break; default: fputc(*str, gpoutfile); break; } str++; } fputs("\n\t\n", gpoutfile); } } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_set_font ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC int SVG_set_font (const char *font) { if (!font || !(*font)) { strcpy (SVG_fontNameCur, SVG_fontNameDef); SVG_fontSizeCur = SVG_fontSizeDef; } else { int sep = strcspn(font,","); if (sep > 0) { strncpy(SVG_fontNameCur, font, sep); SVG_fontNameCur[sep] = NUL; } if (font[sep] == ',') sscanf(font + sep + 1, "%lf", &SVG_fontSizeCur); } return (TRUE); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_make_palette ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC int SVG_make_palette(t_sm_palette *palette) { SVG_GroupFilledClose(); if (palette == NULL) { /* svg can do continuous colors */ return 0; } /* save mapping formulae needed if SMPAL_COLOR_MODE_RGB */ SVG_palette.colorMode = palette->colorMode; SVG_palette.formulaR = palette->formulaR; SVG_palette.formulaG = palette->formulaG; SVG_palette.formulaB = palette->formulaB; SVG_palette.positive = palette->positive; return 0; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_set_color ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_set_color(t_colorspec *colorspec) { rgb255_color rgb255; if (colorspec->type == TC_LT) { SVG_linecolor = SVG_pens[SVG_Pen_RealID (colorspec->lt)].color; SVG_color_mode = TC_LT; return; } else if (colorspec->type == TC_FRAC) rgb255maxcolors_from_gray( colorspec->value, &rgb255 ); else if (colorspec->type == TC_RGB) { rgb255.r = colorspec->lt >> 16; rgb255.g = colorspec->lt >> 8 & 0xff; rgb255.b = colorspec->lt & 0xff; } else return; SVG_color_mode = TC_RGB; if (rgb255.r != SVG_red || rgb255.g != SVG_green || rgb255.b != SVG_blue) { /* pm3d color has changed. We've to start a new path * with a different line color. This is necessary when * using "linetype palette". */ SVG_PathClose(); SVG_red = rgb255.r; SVG_green = rgb255.g; SVG_blue = rgb255.b; } return; } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_previous_palette ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_previous_palette() { SVG_GroupFilledClose(); } /*------------------------------------------------------------------------------------------------------------------------------------ SVG_filled_polygon ------------------------------------------------------------------------------------------------------------------------------------*/ TERM_PUBLIC void SVG_filled_polygon(int points, gpiPoint* corners) { int i; int fillpar = corners->style >> 4; int style = corners->style &= 0xf; if (style == FS_PATTERN) { /* make sure the pattern is defined (with the current stroke color) * must be defined AFTER the current group is opened with the color * attribute set, as the patterns use 'currentColor' */ SVG_DefineFillPattern(fillpar); } SVG_GroupFilledOpen(); fputs("\t\t= 0 && fillpar < 100) fprintf(gpoutfile, " fill-opacity = '%f'", fillpar * 0.01); break; case FS_PATTERN: /* pattern fill */ fprintf(gpoutfile, " fill = 'url(#gpPat%d)'", SVG_fillPatternIndex); break; default: SVG_StyleFillColor(); break; } fputs(" points = '", gpoutfile); for (i = 0; i < points; i++) fprintf(gpoutfile, "%.1f,%.1f%s", X(corners[i].x), Y(corners[i].y), i % 16 == 15 ? "\n" : " "); fputs("'/>\n", gpoutfile); } /* Enhanced text mode support starts here */ static double ENHsvg_base = 0.0; static TBOOLEAN ENHsvg_opened_string = FALSE; static int ENHsvg_charcount = 0; TERM_PUBLIC void ENHsvg_OPEN( char *fontname, double fontsize, double base, TBOOLEAN widthflag, TBOOLEAN showflag, int overprint) { /* overprint = 1 means print the base text (leave position in center) * overprint = 2 means print the overlying text * overprint = 3 means save current position * overprint = 4 means restore saved position * EAM FIXME - Unfortunately I can find no way in the svg spec to do this. * The best I can come up with is to count characters from here and then * try to back up over them. */ switch (overprint) { case 2: #ifdef CODDLE_NONCOMPLIANT_VIEWERS fprintf(gpoutfile, "", 0.3 * ENHsvg_charcount * 1.1*SVG_fontSizeCur, ENHsvg_base-base); #else fprintf(gpoutfile, "", 0.3 * ENHsvg_charcount, ENHsvg_base-base); #endif ENHsvg_base = base; ENHsvg_x_offset = 0.0; enhanced_cur_text = enhanced_text; ENHsvg_charcount = 0; ENHsvg_opened_string = TRUE; break; case 3: ENHsvg_charcount = 0; return; case 4: /* Defer setting the offsets until the text arrives */ ENHsvg_x_offset = -0.6 * ENHsvg_charcount; ENHsvg_base -= base; ENHsvg_charcount = 0; return; default: break; } if (!ENHsvg_opened_string) { ENHsvg_opened_string = TRUE; enhanced_cur_text = enhanced_text; /* Start a new textspan fragment */ fputs("", gpoutfile); } } TERM_PUBLIC void ENHsvg_FLUSH() { if (ENHsvg_opened_string) { ENHsvg_opened_string = FALSE; *enhanced_cur_text = '\0'; fprintf(gpoutfile, "%s\n\t\t", enhanced_text); } } TERM_PUBLIC void ENHsvg_put_text(unsigned int x, unsigned int y, const char *str) { /* We need local copies of the starting font properties */ char fontname[MAX_ID_LEN + 1]; double fontsize = SVG_fontSizeCur; strncpy(fontname,SVG_fontNameCur,sizeof(fontname)); /* We need the full set of tags for text, just as normal. But in */ /* the case of enhanced text ENHsvg_string_state == 1 tells the */ /* SVG_put_text() to return without actually putting the text. */ if (ignore_enhanced_text) { ENHsvg_string_state = 0; SVG_put_text(x, y, str); return; } else { ENHsvg_string_state = 1; SVG_put_text(x, y, str); } /* EAM FIXME - This is a total hack, to make up for the fact that all */ /* svg viewers I have tried fail to pick up the xml:space setting from */ /* the environment. So it has to be set all over again for each text */ /* fragment. Without this, all whitespace is collapsed to a single ' '.*/ if (strstr(str," ")) ENHsvg_preserve_spaces = TRUE; /* Set up global variables needed by enhanced_recursion() */ ENHsvg_charcount = 0; enhanced_fontscale = 1.0; strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format)); while (*(str = enhanced_recursion((char *)str, TRUE, fontname, fontsize, 0.0, TRUE, TRUE, 0))) { (term->enhanced_flush)(); enh_err_check(str); if (!*++str) break; /* end of string */ } /* Make sure we leave with the same font properties as on entry */ strncpy(SVG_fontNameCur,fontname,sizeof(fontname)); if (SVG_fontSizeCur != fontsize || ENHsvg_base != 0) { fprintf(gpoutfile, "", fontsize, ENHsvg_base); SVG_fontSizeCur = fontsize; ENHsvg_base = 0; } ENHsvg_preserve_spaces = FALSE; /* Close the text section */ fputs("\n\t\n", gpoutfile); return; } TERM_PUBLIC void ENHsvg_writec(int c) { /* Kludge for phantom box accounting */ ENHsvg_charcount++; /* Escape SVG reserved characters. Are there any besides '<' and '&' ? */ switch (c) { case '<': *enhanced_cur_text++ = '&'; *enhanced_cur_text++ = 'l'; *enhanced_cur_text++ = 't'; *enhanced_cur_text++ = ';'; break; case '&': *enhanced_cur_text++ = '&'; *enhanced_cur_text++ = 'a'; *enhanced_cur_text++ = 'm'; *enhanced_cur_text++ = 'p'; *enhanced_cur_text++ = ';'; break; case '\376': /* This is an illegal UTF-8 byte; we use it to escape the reserved '&' */ if (encoding == S_ENC_DEFAULT) { *enhanced_cur_text++ = '&'; break; } /* else fall through */ default: *enhanced_cur_text++ = c; break; } } static void SVG_load_fontfile(char *fontfile) { if (fontfile) { unsigned int linesread = 0; FILE *ffont = NULL; char line[256]; char *fontname = NULL; #if defined(PIPES) TBOOLEAN ispipe = FALSE; #endif #if defined(PIPES) if ( *fontfile == '<' ) { ispipe = TRUE; ffont = popen(fontfile + 1, "r" ); if ( !ffont ) int_error(NO_CARET, "Could not execute pipe '%s'", fontfile + 1 ); } else #endif { ffont = fopen(fontfile, "r"); if (!ffont) int_error(NO_CARET, "Font file '%s' not found", fontfile); } /* read the file */ while (fgets(line,255,ffont)) { /* Echo fontname to terminal */ if ((fontname = strstr(line,"font-family"))) { fprintf(stderr, "Font file '%s' contains the font '%s'\n", fontfile, fontname); } /* Copy contents into output file */ fputs(line,gpoutfile); ++linesread; } #if defined(PIPES) if ( ispipe ) { int exitcode; if ( (exitcode = pclose(ffont)) != 0 ) int_error(NO_CARET, "Command '%s' generated error exitcode %d", fontfile + 1, exitcode); } else #endif fclose(ffont); if (linesread == 0) { #if defined(PIPES) if ( ispipe ) int_error(NO_CARET, "Command '%s' generates empty output", fontfile + 1); else #endif int_error(NO_CARET, "Font file '%s' is empty", fontfile); } } } TERM_PUBLIC void SVG_path(int p) { switch (p) { case 1: /* Close path */ fputs("Z ", gpoutfile); SVG_PathClose(); break; case 0: break; } } #undef Y #undef X #undef CODDLE_NONCOMPLIANT_VIEWERS #endif /* TERM_BODY */ #ifdef TERM_TABLE TERM_TABLE_START (svg_driver) "svg", "W3C Scalable Vector Graphics driver", 0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ , 0 /* vtic */ , 0 /* htic */ , SVG_options, SVG_init, SVG_reset, SVG_text, null_scale, SVG_graphics, SVG_move, SVG_vector, SVG_linetype, SVG_put_text, SVG_text_angle, SVG_justify_text, SVG_point, do_arrow, SVG_set_font, do_pointsize, TERM_CAN_MULTIPLOT | TERM_BINARY, 0 /* suspend */, 0 /* resume */ , SVG_fillbox, SVG_linewidth #ifdef USE_MOUSE , 0, 0, 0, 0, 0 /* no mouse support for svg */ #endif , SVG_make_palette, SVG_previous_palette, SVG_set_color, SVG_filled_polygon #ifdef WITH_IMAGE , NULL #endif , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec , NULL /* layer */ , SVG_path /* path */ TERM_TABLE_END (svg_driver) #undef LAST_TERM #define LAST_TERM svg_driver #endif /* TERM_TABLE */ #endif /* TERM_PROTO_ONLY */ #ifdef TERM_HELP START_HELP(svg) "1 svg", "?commands set terminal svg", "?set terminal svg", "?set term svg", "?terminal svg", "?term svg", "?svg", " This terminal produces files in the W3C Scalable Vector Graphics format.", "", " Syntax:", " set terminal svg {size , {|fixed|dynamic}}", " {{no}enhanced}", " {fname \"\"} {fsize }", " {font \"{,}\"}", " {fontfile }", " {rounded|butt} {solid|dashed} {linewidth }", "", " where and are the size of the SVG plot to generate,", " `dynamic` allows a svg-viewer to resize plot, whereas the default", " setting, `fixed`, will request an absolute size.", "", " `linewidth ` increases the width of all lines used in the figure", " by a factor of .", "", " is the name of the default font to use (default Arial) and", " is the font size (in points, default 12). SVG viewing", " programs may substitute other fonts when the file is displayed.", "", " The svg terminal supports an enhanced text mode, which allows font", " and other formatting commands to be embedded in labels and other text", " strings. The enhanced text mode syntax is shared with other gnuplot", " terminal types. See `enhanced` for more details.", "", " SVG allows you to embed fonts directly into an SVG document, or to", " provide a hypertext link to the desired font. The `fontfile` option", " specifies a local file which is copied into the section of the", " resulting SVG output file. This file may either itself contain a font,", " or may contain the records necessary to create a hypertext reference to", " the desired font. Gnuplot will look for the requested file using the", " directory list in the GNUPLOT_FONTPATH environmental variable.", " NB: You must embed an svg font, not a TrueType or PostScript font." END_HELP(svg) #endif