/* Hello, Emacs, this is -*-C-*-
 * $Id: svg.trm,v 1.170.2.2 2016/08/04 23:01:12 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 <johannes@zellner.org>, May-16-2002 */
/* set_color fixes by Petr Mikulik <mikulik@physics.muni.cz>, June-10-2002 */
/* ISO-Latin encoding, Font selection fixes, option "fixed|dynamic" by
 * Wilhelm Braunschober <Wilhelm.Braunschober@t-online.de>, Feb-21-2002 */

/*
 * Additional code for gnuplot versions 4.2 and 4.3
 * Ethan Merritt  <merritt@u.washington.edu>
 *
 *   Tweaked code for compatibility with Sodipodi svg viewer/editor.
 *   Added enhanced text support.
 *   Additional line properties.
 *   Increase resolution by adding a coordinate scale factor.
 *   Support dashed lines, TC_* color model.
 *   Change path markup from    style='attribute: foo'  to   attribute='foo'
 *
 * Additional code for gnuplot versions 4.5
 * Ethan Merritt  <merritt@u.washington.edu>
 *
 *   Wrap each plot in a named group <g id="name_plot_%02d">
 *   Set the name using 'set term svg name "foo"'
 *   Background option
 *   Bitmap image support by creating and linking to external png files
 *   Mouse-tracking with coordinate readout.
 *   Version 4.7 (April 2012) hypertext support
 *
 * Contributed by <plotter@piments.com>
 *   Javascript code to toggle plots on/off
 *
 * Revised font sizing Oct 2012
 *   specify font-size without "pt" units.
 */

/*
 * Code for gnuplot version 4.5
 *   Bold -> font-weight:bold
 *   Italic -> font-style:italic
 *   Trying to match well with PNG output.
 * Rich Seymour <rseymour@usc.edu>
 */

#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_dashtype __PROTO ((int type, t_dashtype *custom_dash_type));
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 SVG_layer __PROTO((t_termlayer syncpoint));

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));
TERM_PUBLIC void SVG_hypertext __PROTO((int, const char *));

#ifdef WRITE_PNG_IMAGE
TERM_PUBLIC void SVG_image __PROTO((unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode));
#endif

#define SVG_SCALE	10.	/* Coordinates accuracy is 1/SVG_SCALE pixel */
#define PREC		1	/* Decimal places needed for SVG_SCALEd values */
#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 double SVG_alpha = 0.0;
static unsigned char SVG_color_mode = TC_DEFAULT;
static char *SVG_linecolor = NULL;
static char *SVG_name = NULL;
static char *SVG_scriptdir = NULL;
static TBOOLEAN SVG_mouseable = FALSE;
static TBOOLEAN SVG_standalone = FALSE;

static TBOOLEAN SVG_groupFilledIsOpen = FALSE; /* open pm3d group flag*/
static TBOOLEAN SVG_inTextBox = FALSE;

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 double SVG_dashlength = 1.0; /* Multiplier for dash patterns */
static t_linecap SVG_linecap = BUTT; /* 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 = NULL;
static char *SVG_fontStyleDef = NULL; /* default font style */
static char *SVG_fontWeightDef = NULL; /* default font weight */
static double SVG_fontSizeDef = 12;	/* default text size*/
/* current text font family: */
static char *SVG_fontNameCur = NULL;
static char *SVG_fontStyleCur = NULL; /* current font style */
static char *SVG_fontWeightCur = NULL; /* current font weight */
static double SVG_fontSizeCur = 12;	/* current text size*/
static double SVG_fontscale = 1.0;	/* multiplier for nominal font 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_background = -1;
static int SVG_imageno = 0;
static int SVG_plotno = 0;
static TBOOLEAN SVG_gridline = FALSE;
static TBOOLEAN SVG_hasgrid = FALSE;

static double SVG_fontAscent  = 0;	/* estimated current font ascent*/
static double SVG_fontDescent = 0;	/* estimated current font descent*/
static double SVG_fontLeading = 0;	/* estimated current font leading*/
static double 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_AddSpaceOrNewline __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;

/* Support for dashed lines */
#define SVG_dashtypes 5
static char *SVG_defaultdashpattern[SVG_dashtypes] = {
    "", " 5,8", " 2,4", " 8,4,2,4", " 9,4,1,4,1,4"
};
static char *SVG_axis_dashpattern = "2,4";
static int SVG_dasharray[SVG_dashtypes][7] = {
	{ 0,0,0,0,0,0,0},
	{5,8, 0,0,0,0,0},
	{2,4, 0,0,0,0,0},
	{8,4,2,4, 0,0,0},
	{9,4,1,4,1,4, 0}
};
static char *SVG_dashpattern = NULL;
static char  SVG_custom_dash_pattern[64];

/* Support for embedded hypertext */
static char *SVG_hypertext_text = NULL;

/*------------------------------------------------------------------------------------------------------------------------------------
	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, "<g fill=\"none\" color=\"%s\" stroke=\"",
		 SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);

	if (SVG_color_mode == TC_RGB)
	    fprintf(gpoutfile, "rgb(%3d, %3d, %3d)", SVG_red, SVG_green, SVG_blue);
	else if (SVG_color_mode == TC_LT)
	    fprintf(gpoutfile, "%s", SVG_linecolor);
	else
	    fprintf(gpoutfile, "currentColor");

	fprintf (gpoutfile, "\" ");
	fprintf (gpoutfile, "stroke-width=\"%.2f\" stroke-linecap=\"%s\" stroke-linejoin=\"%s\"",
		 SVG_pens[SVG_Pen_RealID (SVG_LineType)].width * SVG_linewidth_factor,
		 SVG_linecap == ROUNDED ? "round" : SVG_linecap == SQUARE ? "square" : "butt",
		 SVG_linecap == ROUNDED ? "round" : "miter");

	fprintf (gpoutfile, ">\n");

	SVG_groupIsOpen = TRUE;
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_GroupClose
------------------------------------------------------------------------------------------------------------------------------------*/
static void
SVG_GroupClose ()
{
    SVG_GroupFilledClose();
    if (SVG_groupIsOpen) {
	  fputs ("</g>\n", gpoutfile);
	  SVG_groupIsOpen = FALSE;
	  SVG_fillPattern = -1;
      }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_PathOpen
------------------------------------------------------------------------------------------------------------------------------------*/
static void
SVG_PathOpen ()
{
    if (!SVG_pathIsOpen) {
	SVG_GroupFilledClose();
	    
	fputs ("\t<path ", gpoutfile);

	/* Line color */
	if (SVG_color_mode == TC_RGB)
	    fprintf(gpoutfile, "stroke='rgb(%3d, %3d, %3d)' ",
		    SVG_red, SVG_green, SVG_blue);
	else if (SVG_color_mode == TC_LT)
	    fprintf(gpoutfile, "stroke='%s' ", SVG_linecolor);

	/* Axis is always dotted */
	if (SVG_LineType == LT_AXIS)
	    fprintf(gpoutfile, "stroke-dasharray='2,4' ");

	/* Other patterns were selected by a previous call to SVG_dashtype */
	else if (SVG_dashpattern)
	    fprintf(gpoutfile, "stroke-dasharray='%s' ", SVG_dashpattern);
	
	/* RGBA */
	if (SVG_alpha != 0.0)
	    fprintf(gpoutfile, "opacity='%4.2f' ", 1.0 - SVG_alpha);

	/* Mark grid lines so that we can toggle them on/off */
	if (SVG_gridline)
	    fprintf(gpoutfile, "class=\"gridline\" ");

	fputs (" d='", gpoutfile);

	SVG_pathIsOpen = TRUE;
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_PathClose
------------------------------------------------------------------------------------------------------------------------------------*/
static void
SVG_PathClose ()
{
    if (SVG_pathIsOpen) {
	SVG_GroupFilledClose();
	fprintf(gpoutfile," '/>");
	SVG_path_count = 0;
	SVG_pathIsOpen = FALSE;
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_AddSpaceOrNewline
------------------------------------------------------------------------------------------------------------------------------------*/
static void
SVG_AddSpaceOrNewline ()
{
    if (SVG_path_count % 8 == 0)	/* avoid excessive line length*/
	fputs ("\n\t\t", gpoutfile);
    else
      fputs (" ", gpoutfile);
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_SetFont
------------------------------------------------------------------------------------------------------------------------------------*/
static void
SVG_SetFont (const char *name, double size)
{
    if (name != SVG_fontNameCur) {
	free(SVG_fontNameCur);
	SVG_fontNameCur = gp_strdup(name);
    }
    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  = (SVG_fontSizeCur * 0.90 * SVG_SCALE);
    SVG_fontDescent = (SVG_fontSizeCur * 0.25 * SVG_SCALE);
    SVG_fontLeading = (SVG_fontSizeCur * 0.35 * SVG_SCALE);
    SVG_fontAvWidth = (SVG_fontSizeCur * 0.70 * SVG_SCALE);

    term->h_char = SVG_fontAvWidth;
    term->v_char = (SVG_fontAscent + SVG_fontDescent + SVG_fontLeading);
}

static void
SVG_GroupFilledOpen()
{
    if (!SVG_groupFilledIsOpen) {
	SVG_PathClose();
	fputs("\t<g stroke='none' shape-rendering='crispEdges'>\n",
	      gpoutfile);
	SVG_groupFilledIsOpen = TRUE;
    }
}

static void
SVG_GroupFilledClose()
{
    if (SVG_groupFilledIsOpen) {
	fputs("\t</g>\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<defs>\n"
	    "\t\t<pattern id='gpPat%d' patternUnits='userSpaceOnUse' x='0' y='0' width='8' height='8'>\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) {
	    char *figure = "fill:none;";
	    if (!strcmp(style,"fill")) figure = "stroke:none;";
	    if (SVG_color_mode == TC_RGB)
		fprintf(gpoutfile,"\t\t\t<path style='%s %s:rgb(%d,%d,%d)' d='%s'/>\n",
			figure, style, SVG_red, SVG_green, SVG_blue, path);
	    else if (SVG_color_mode == TC_LT)
		fprintf(gpoutfile, "\t\t\t<path style = '%s %s:%s' d= '%s'/>\n",
			figure, style, SVG_linecolor, path);
	    else
		fprintf(gpoutfile, "\t\t\t<path style = '%s %s:currentColor' d='%s'/>\n",
			figure, style, path);
	}
	fputs("\t\t</pattern>\n" "\t</defs>\n", gpoutfile);
    }
}

static void
SVG_MoveForced(unsigned int x, unsigned int y)
{
    if (SVG_path_count > 512)
	SVG_PathClose();

    SVG_PathOpen ();

    fprintf (gpoutfile, "M%.*f,%.*f", PREC, X(x), PREC, Y(y));
    SVG_path_count++;

    SVG_AddSpaceOrNewline ();

    SVG_xLast = x;
    SVG_yLast = y;
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_options
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_options ()
{
    /* Annoying hack to handle the case of 'set termoption' after */
    /* we have already initialized the terminal settings.         */
    if (!almost_equals(c_token-1, "termopt$ion"))
	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_expression();
	    if (value < 2)
		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_expression();
	    if (value < 2)
		int_error(c_token,"y size out of range");
	    SVG_ySize = value * SVG_SCALE;
	    continue;
	}

	if (equals(c_token, "mouse") || almost_equals(c_token, "mous$ing")) {
	    c_token++;
	    SVG_mouseable = TRUE;
	    continue;
	}

	if (almost_equals(c_token, "stand$alone")) {
	    c_token++;
	    SVG_standalone = TRUE;
	    continue;
	}

	if (equals(c_token, "name")) {
	    c_token++;
	    SVG_name = try_to_get_string();
	    if (!SVG_name)
		    int_error(c_token,"expecting a plot name");
	    if (SVG_name[strspn(SVG_name,
		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_1234567890")])
		    int_error(c_token-1,"name must contain only alphanumerics or _");
	    continue;
	}

	if (equals(c_token, "jsdir")) {
	    c_token++;
	    SVG_scriptdir = try_to_get_string();
	    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) {
		char *bold, *italic;

		if (!((bold = strstr(s," bold"))))
		    bold = strstr(s," Bold");

		if (!((italic = strstr(s," italic"))))
		    italic = strstr(s," Italic");

		free(SVG_fontNameDef);
		SVG_fontNameDef = s;
		if (italic) {
		    SVG_fontStyleDef = "italic";
		    SVG_fontNameDef[strlen(s) - strlen(italic)] = NUL;
		} else  {
		    SVG_fontStyleDef = "normal";
		}
		if (bold) {
		    SVG_fontWeightDef="bold";
		    SVG_fontNameDef[strlen(s) - strlen(bold)] = NUL;
		} else {
		    SVG_fontWeightDef = "normal";
		}
	    } else
		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_expression();
	    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);
	    SVG_embedded_font = fontfile_name;
	    continue;
	}

	if (almost_equals(c_token, "linew$idth") || equals(c_token, "lw")) {
	    c_token++;
	    SVG_linewidth_factor = real_expression();
	    if (SVG_linewidth_factor <= 0.0)
		SVG_linewidth_factor = 1.0;
	    continue;
	}
									
	if (almost_equals(c_token, "dashl$ength") || equals(c_token, "dl")) {
	    c_token++;
	    SVG_dashlength = real_expression();
	    if (SVG_dashlength < 0.5)
		SVG_dashlength = 1.0;
	    continue;
	}
									
	if (almost_equals (c_token, "round$ed")) {
	    c_token++;
	    SVG_linecap = ROUNDED;
	    continue;
	}
									
	if (equals (c_token, "square")) {
	    c_token++;
	    SVG_linecap = SQUARE;
	    continue;
	}

	if (equals (c_token, "butt")) {
	    c_token++;
	    SVG_linecap = BUTT;
	    continue;
	}

	/* Not used in version 5 */
	if (equals(c_token, "solid") || almost_equals(c_token, "dash$ed")) {
	    c_token++;
	    continue;
	}

	if (almost_equals(c_token, "backg$round")) {
	    c_token++;
	    SVG_background = parse_color_name();
	    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_mouseable) {
	sprintf(term_options + strlen(term_options),
	    "mousing ");
    }

    if (SVG_standalone) {
	sprintf(term_options + strlen(term_options),
	    "standalone ");
    }

    if (SVG_name) {
	sprintf(term_options + strlen(term_options),
	    "name \"%s\" ", SVG_name);
    }

    if (SVG_embedded_font) {
	sprintf(term_options + strlen(term_options),
	    "fontfile \"%s\" ", SVG_embedded_font);
    }

    sprintf(term_options + strlen(term_options),
	SVG_linecap == ROUNDED ? "rounded " : SVG_linecap == SQUARE ? "square " : "butt ");

    sprintf(term_options + strlen(term_options),
	    "dashlength %.1f ", SVG_dashlength);

    if (SVG_linewidth_factor != 1.0) {
	sprintf(term_options + strlen(term_options),
	    "linewidth %3.1f ", SVG_linewidth_factor);
    }

    if (SVG_background >= 0) {
	sprintf(term_options + strlen(term_options),
	    "background \"#%06x\" ", SVG_background);
    }

}

static void
SVG_local_reset()
{
    SVG_xSize      = SVG_XMAX;
    SVG_ySize      = SVG_YMAX;
    SVG_fixed_size = TRUE;
    free(SVG_fontNameDef);
    SVG_fontNameDef = gp_strdup("Arial");
    SVG_fontSizeDef  = 12;
    if (SVG_embedded_font)
	free(SVG_embedded_font);
    SVG_embedded_font = NULL;
    SVG_mouseable = FALSE;
    SVG_standalone = FALSE;
    free(SVG_name);
    SVG_name = NULL;
    free(SVG_scriptdir);
    SVG_scriptdir = NULL;
    SVG_gridline = FALSE;
    SVG_hasgrid = FALSE;
    /* Default to enhanced text */
    term->put_text = ENHsvg_put_text;
    term->flags |= TERM_ENHANCED_TEXT;
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_init
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_init ()
{
    int len;
    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*/

    if (SVG_background >= 0)
	sprintf(SVG_pens[0].color, "#%2.2X%2.2X%2.2X",
		(SVG_background >> 16)&0xff,
		(SVG_background >> 8)&0xff,
		(SVG_background)&0xff);

    SVG_LineType = LT_NODRAW;

/* set xmax, ymax*/

    term->xmax = SVG_xSize;
    term->ymax = SVG_ySize;

/* set current font, including h_char and v_char */

    SVG_SetFont (SVG_fontNameCur, SVG_fontSizeCur);

/* 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_9:	svg_encoding = "encoding=\"iso-8859-9\" "; 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_CP950:	svg_encoding = "encoding=\"cp950\" "; break;
	case S_ENC_CP1250:	svg_encoding = "encoding=\"windows-1250\" "; break;
	case S_ENC_CP1251:	svg_encoding = "encoding=\"windows-1251\" "; break;
	case S_ENC_CP1252:	svg_encoding = "encoding=\"windows-1252\" "; 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_SJIS:	svg_encoding = "encoding=\"Shift_JIS\" "; break;
	case S_ENC_CP437:	svg_encoding = ""; break;
	default: /* UTF-8 */
				svg_encoding = "encoding=\"utf-8\" ";
				break;
    }

    fprintf (gpoutfile,
	     "<?xml version=\"1.0\" %s standalone=\"no\"?>\n"
	     "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"
	     " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
	     "<svg ", svg_encoding);

    if (SVG_mouseable)
	fprintf (gpoutfile, " onload=\"if (typeof(gnuplot_svg)!='undefined') gnuplot_svg.Init(evt)\" ");

    if (SVG_fixed_size)
	fprintf (gpoutfile, "\n width=\"%u\" height=\"%u\"",
		 (unsigned int) (term->xmax / SVG_SCALE),
		 (unsigned int) (term->ymax / SVG_SCALE));

    fprintf (gpoutfile, "\n 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");
#if (0)
    /* This should be required, but Firefox gets it totally wrong */
    fprintf (gpoutfile, " xml:space=\"preserve\"\n");
#endif
    fprintf (gpoutfile, ">\n\n");

    /* TODO: It would be nice to print the actual plot title here */
    fprintf (gpoutfile, "<title>%s</title>\n", SVG_name ? SVG_name : "Gnuplot");
    fprintf (gpoutfile,
	     "<desc>Produced by GNUPLOT %s patchlevel %s </desc>\n\n",
	     gnuplot_version, gnuplot_patchlevel);

/*
 * FIXME: This code could be shared with canvas.trm
 *	  Figure out the full URL to use for xlink:href="gnuplot_svg.js"
 */
    if (SVG_scriptdir == NULL) {
	char *svg_default_jsdir = "";
#ifdef GNUPLOT_JS_DIR
# if defined(_Windows)
	/* retrieve path relative to the gnuplot executable,
	 * whose path is in szModuleName (winmain.c) */
	/* EAM FIXME: memory leak */
	svg_default_jsdir = gp_alloc(strlen((char*) szPackageDir)
			+ strlen(GNUPLOT_JS_DIR) + 2, "jsdir");
	strcpy(svg_default_jsdir, (char*) szPackageDir);
	len = strlen(svg_default_jsdir);
	if (*svg_default_jsdir && svg_default_jsdir[len-1] != '\\' && svg_default_jsdir[len-1] != '/')
	    strcat(svg_default_jsdir, "\\");
	/* GNUPLOT_JS_DIR is _relative_ path */
	strcat(svg_default_jsdir, GNUPLOT_JS_DIR);
# else /* !_Windows */
	/* use hardcoded _absolute_ path */
	svg_default_jsdir = GNUPLOT_JS_DIR;
# endif
#endif /* GNUPLOT_JS_DIR */
	SVG_scriptdir = gp_strdup(svg_default_jsdir);
    }

#if !defined(VMS)
    len = strlen(SVG_scriptdir);
# if defined(_Windows)
    if (*SVG_scriptdir && SVG_scriptdir[len-1] != '\\' && SVG_scriptdir[len-1] != '/') {
	SVG_scriptdir = gp_realloc(SVG_scriptdir, len+2, "jsdir");
	if (SVG_scriptdir[len-1] == '\\') /* use backslash if used in jsdir, otherwise slash */
	    strcat(SVG_scriptdir,"\\");
	else
	    strcat(SVG_scriptdir,"/");
    }
# else
    if (*SVG_scriptdir && SVG_scriptdir[len-1] != '/') {
	SVG_scriptdir = gp_realloc(SVG_scriptdir, len+2, "jsdir");
	strcat(SVG_scriptdir,"/");
    }
# endif
#endif

    if (SVG_mouseable) {
	/* Inclusion of gnuplot_svg.js is sufficient to support toggling plots on/off */
	if (!SVG_standalone) {
 	    fprintf(gpoutfile,
		"<script type=\"text/javascript\" xlink:href=\"%sgnuplot_svg.js\"/>\n",
		SVG_scriptdir);
	} else {
	    /* "standalone" option includes the mousing code in the file itself */
	    char *fullname = NULL;
	    char *name ="gnuplot_svg.js";
	    char buf[256];
	    FILE *svg_js_fd;

	    fullname = gp_alloc(strlen(SVG_scriptdir) + strlen(name) + 4,"javascript name");
	    strcpy(fullname, SVG_scriptdir);
	    PATH_CONCAT(fullname, name);

	    svg_js_fd=fopen(fullname, "r");
	    if (!svg_js_fd)
		int_warn(NO_CARET, "Failed to insert javascript file %s\n", fullname);
 	    else {
		fprintf(gpoutfile,
		    "<script language=\"javaScript\" TYPE=\"text/javascript\" > <![CDATA[\n");
		while (fgets(buf, sizeof(buf), svg_js_fd))
		    fputs(buf, gpoutfile);
		fprintf(gpoutfile,"]]>\n</script>\n");
		fclose(svg_js_fd);
	    }
	    free(fullname);
	}
    }

    if (SVG_mouseable) {   /* FIXME: Should only do this for 2D plots */
	/* This is extra code to support tracking the mouse coordinates */
	fprintf(gpoutfile,"\n<!-- Tie mousing to entire bounding box of the plot -->\n");
	fprintf(gpoutfile,"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"",
		0, 0, (int)(term->xmax/SVG_SCALE), (int)(term->ymax/SVG_SCALE));
	fprintf(gpoutfile," fill=\"#%06x\" stroke=\"black\" stroke-width=\"1\"\n",
		SVG_background>0 ? SVG_background : 0xffffff);
	fprintf(gpoutfile,"onclick=\"gnuplot_svg.toggleCoordBox(evt)\"  onmousemove=\"gnuplot_svg.moveCoordBox(evt)\"/>\n");
	fprintf(gpoutfile,"\n<!-- Also track mouse when it is on a plot element -->\n");
	fprintf(gpoutfile,"<g id=\"gnuplot_canvas\" onclick=\"gnuplot_svg.toggleCoordBox(evt)\" onmousemove=\"gnuplot_svg.moveCoordBox(evt)\">\n\n");
    } else {
	fprintf(gpoutfile,"<g id=\"gnuplot_canvas\">\n\n");
	fprintf(gpoutfile,"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"",
		0, 0, (int)(term->xmax/SVG_SCALE), (int)(term->ymax/SVG_SCALE));
	if (SVG_background >= 0)
		fprintf(gpoutfile," fill=\"#%06x\"", SVG_background);
	else
		fprintf(gpoutfile," fill=\"none\"");
	fprintf(gpoutfile,"/>\n");
    }

    /* Start prologue section of output file, and load fonts if requested */

    fprintf(gpoutfile,"<defs>\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<path id='gpPt3' stroke-width='%.3f' d='M-1,-1 h2 v2 h-2 z'/>\n" */

    stroke_width = 2.0 *SVG_SCALE / term->h_tic;
    fprintf (gpoutfile,
	     "\n"
	     /* dot: */
	     "\t<circle id='gpDot' r='0.5' stroke-width='0.5'/>\n"
	     /*  0 plus */
	     "\t<path id='gpPt0' stroke-width='%.3f' stroke='currentColor' d='M-1,0 h2 M0,-1 v2'/>\n"
	     /*  1 X */
	     "\t<path id='gpPt1' stroke-width='%.3f' stroke='currentColor' d='M-1,-1 L1,1 M1,-1 L-1,1'/>\n"
	     /*  2 star */
	     "\t<path id='gpPt2' stroke-width='%.3f' stroke='currentColor' d='M-1,0 L1,0 M0,-1 L0,1 M-1,-1 L1,1 M-1,1 L1,-1'/>\n"
	     /*  3 box */
	     "\t<rect id='gpPt3' stroke-width='%.3f' stroke='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
	     /*  4 box                   filled */
	     "\t<rect id='gpPt4' stroke-width='%.3f' stroke='currentColor' fill='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
	     /*  5 circle */
	     "\t<circle id='gpPt5' stroke-width='%.3f' stroke='currentColor' cx='0' cy='0' r='1'/>\n"
	     /*  6 circle (disk)         filled */
	     "\t<use xlink:href='#gpPt5' id='gpPt6' fill='currentColor' stroke='none'/>\n"
	     /*  7 triangle */
	     "\t<path id='gpPt7' stroke-width='%.3f' stroke='currentColor' d='M0,-1.33 L-1.33,0.67 L1.33,0.67 z'/>\n"
	     /*  8 triangle  filled */
	     "\t<use xlink:href='#gpPt7' id='gpPt8' fill='currentColor' stroke='none'/>\n"
	     /*  9 upside down triangle */
	     "\t<use xlink:href='#gpPt7' id='gpPt9' stroke='currentColor' transform='rotate(180)'/>\n"
	     /*  10 upside down triangle filled */
	     "\t<use xlink:href='#gpPt9' id='gpPt10' fill='currentColor' stroke='none'/>\n"
	     /* 11 diamond */
	     "\t<use xlink:href='#gpPt3' id='gpPt11' stroke='currentColor' transform='rotate(45)'/>\n"
	     /* 12 diamond               filled */
	     "\t<use xlink:href='#gpPt11' id='gpPt12' fill='currentColor' stroke='none'/>\n"
	     /* 13 pentagon */
	     "\t<path id='gpPt13' stroke-width='%.3f' stroke='currentColor' d='M0,1.330 L1.265,0.411 L0.782,-1.067 L-0.782,-1.076 L-1.265,0.411 z'/>\n"
	     /* 14 pentagon              filled */
	     "\t<use xlink:href='#gpPt13' id='gpPt14' fill='currentColor' stroke='none'/>\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. */
#ifdef EAM_BOXED_TEXT
	     "\t<filter id='textbox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>\n"
	     "\t  <feFlood flood-color='%s' flood-opacity='1' result='bgnd'/>\n"
	     "\t  <feComposite in='SourceGraphic' in2='bgnd' operator='atop'/>\n"
	     "\t</filter>\n"
#endif
	     "\t<filter id='greybox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>\n"
	     "\t  <feFlood flood-color='lightgrey' flood-opacity='1' result='grey'/>\n"
	     "\t  <feComposite in='SourceGraphic' in2='grey' operator='atop'/>\n"
	     "\t</filter>\n"

	     "</defs>\n"
	     , stroke_width
	     , stroke_width
	     , stroke_width
	     , stroke_width
	     , stroke_width
	     , stroke_width
	     , stroke_width
	     , stroke_width
#ifdef EAM_BOXED_TEXT
	     , SVG_pens[0].color
#endif
	);
}

/*------------------------------------------------------------------------------------------------------------------------------------
	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 ();

    if (SVG_mouseable) {
	struct udvt_entry *udv;

	fprintf(gpoutfile, "\n<script type=\"text/javascript\"><![CDATA[\n");
	fprintf(gpoutfile, "// plot boundaries and axis scaling information for mousing \n");
	fprintf(gpoutfile, "gnuplot_svg.plot_term_xmax = %d;\n", (int)(term->xmax / SVG_SCALE));
	fprintf(gpoutfile, "gnuplot_svg.plot_term_ymax = %d;\n", (int)(term->ymax / SVG_SCALE));
	fprintf(gpoutfile, "gnuplot_svg.plot_xmin = %.1f;\n", (double)plot_bounds.xleft / SVG_SCALE);
	fprintf(gpoutfile, "gnuplot_svg.plot_xmax = %.1f;\n", (double)plot_bounds.xright / SVG_SCALE);
	fprintf(gpoutfile, "gnuplot_svg.plot_ybot = %.1f;\n", (double)(term->ymax-plot_bounds.ybot) / SVG_SCALE);
	fprintf(gpoutfile, "gnuplot_svg.plot_ytop = %.1f;\n", (double)(term->ymax-plot_bounds.ytop) / SVG_SCALE);
	fprintf(gpoutfile, "gnuplot_svg.plot_width = %.1f;\n", (double)(plot_bounds.xright - plot_bounds.xleft) / SVG_SCALE);
	fprintf(gpoutfile, "gnuplot_svg.plot_height = %.1f;\n", (double)(plot_bounds.ytop - plot_bounds.ybot) / SVG_SCALE);

	/* Get true axis ranges as used in the plot */
	update_gpval_variables(1);

#define	MOUSE_PARAM( GP_NAME, js_NAME ) \
	if ((udv = add_udv_by_name(GP_NAME))) { \
	    if (udv->udv_value.type == INTGR)   \
		fprintf(gpoutfile, "gnuplot_svg.%s = %d;\n", \
			js_NAME, udv->udv_value.v.int_val); \
	    else if (udv->udv_value.type == CMPLX) \
		fprintf(gpoutfile, "gnuplot_svg.%s = %g;\n", \
			js_NAME, udv->udv_value.v.cmplx_val.real); \
	}
	if (axis_array[FIRST_X_AXIS].datatype != DT_TIMEDATE) {
	    MOUSE_PARAM("GPVAL_X_MIN", "plot_axis_xmin");
	    MOUSE_PARAM("GPVAL_X_MAX", "plot_axis_xmax");
	}
	/* FIXME: Should this inversion be done at a higher level? */
	if (is_3d_plot && splot_map) {
	    MOUSE_PARAM("GPVAL_Y_MAX", "plot_axis_ymin");
	    MOUSE_PARAM("GPVAL_Y_MIN", "plot_axis_ymax");
	} else {
	    MOUSE_PARAM("GPVAL_Y_MIN", "plot_axis_ymin");
	    MOUSE_PARAM("GPVAL_Y_MAX", "plot_axis_ymax");
	}

	fprintf(gpoutfile, "gnuplot_svg.polar_mode = %s;\n",
		polar ? "true" : "false");
	if (polar)
	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_rmin = %g;\n",
		(R_AXIS.autoscale & AUTOSCALE_MIN) ? 0.0 : R_AXIS.set_min);

	if ((axis_array[SECOND_X_AXIS].ticmode & TICS_MASK) != NO_TICS) {
	    MOUSE_PARAM("GPVAL_X2_MIN", "plot_axis_x2min");
	    MOUSE_PARAM("GPVAL_X2_MAX", "plot_axis_x2max");
	} else
	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_x2min = \"none\"\n");
	if ((axis_array[SECOND_Y_AXIS].ticmode & TICS_MASK) != NO_TICS) {
	    MOUSE_PARAM("GPVAL_Y2_MIN", "plot_axis_y2min");
	    MOUSE_PARAM("GPVAL_Y2_MAX", "plot_axis_y2max");
	} else
	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_y2min = \"none\"\n");
#undef MOUSE_PARAM

	fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_x = %d;\n",
		axis_array[FIRST_X_AXIS].log ? 1: 0);
	fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_y = %d;\n",
		axis_array[FIRST_Y_AXIS].log ? 1: 0);
	if (polar)
	    fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_r = %d;\n",
		axis_array[POLAR_AXIS].log ? 1: 0);

	/*
	 * Note: mouse_alt_string is set by 'set mouse mouseformat "foo"'
	 * The only strings currently recognized by gnuplot_svg.js are
	 * "Time", "Date", and "DateTime" but the user could modify
	 * gnuplot_svg.js to recognize additional formats.
	 * FIXME: This all needs to be documented somewhere!
	 */
	if (axis_array[FIRST_X_AXIS].datatype == DT_TIMEDATE) {
	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_xmin = %.3f;\n",
		axis_array[FIRST_X_AXIS].min);
	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_xmax = %.3f;\n",
		axis_array[FIRST_X_AXIS].max);
	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"%s\";\n",
		(mouse_alt_string) ? mouse_alt_string
		: (mouse_mode == 4) ? "Date"
		: (mouse_mode == 5) ? "Time"
		: "DateTime"
		);
	} else if (axis_array[FIRST_X_AXIS].datatype == DT_DMS) {
	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"DMS\";\n");
	} else
	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"\";\n");

	if (axis_array[FIRST_Y_AXIS].datatype == DT_DMS)
	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_y = \"DMS\";\n");
	else
	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_y = \"\";\n");

	fprintf(gpoutfile,"]]>\n</script>\n");
    } /* End of section writing out variables for mousing */

    /* Close off the group with id=gnuplot_canvas that wraps the entire plot */
    fprintf(gpoutfile,"</g>\n");

    /* Now create a text element to hold the mouse-tracking text. */
    /* It comes _after_ the plot group so that it floats on top.  */
    if (SVG_mouseable) {
	fprintf(gpoutfile,"\n  <text id=\"coord_text\" text-anchor=\"start\" pointer-events=\"none\"\n");
	fprintf(gpoutfile,"  font-size=\"12\" font-family=\"Arial\"\n");
	fprintf(gpoutfile,"  visibility=\"hidden\"> </text>\n");
    }

    /* And a box and a text element to hold mouseover hypertext */
    if (SVG_mouseable) {
	fprintf(gpoutfile,"\n  <rect id=\"hypertextbox\" class=\"hypertextbox\" pointer-events=\"none\"\n");
	fprintf(gpoutfile,"  fill=\"white\" stroke=\"black\" opacity=\"0.8\"\n");
	fprintf(gpoutfile,"  height=\"16\" visibility=\"hidden\" />\n");
	fprintf(gpoutfile,"\n  <text id=\"hypertext\" class=\"hypertext\" pointer-events=\"none\"\n");
	fprintf(gpoutfile,"  font-size=\"12\" font-family=\"Arial\"\n");
	fprintf(gpoutfile,"  visibility=\"hidden\"> </text>\n");
    }

    /* If there were any grid lines in this plot, add a button to toggle them */
    if (SVG_mouseable && SVG_hasgrid) {
	fprintf(gpoutfile,"\n  <image x='10' y='%d' width='16' height='16' xlink:href='grid.png'\n",
	(int)(term->ymax/SVG_SCALE)-26);
	fprintf(gpoutfile,"    onclick='gnuplot_svg.toggleGrid();'/>\n");
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_reset
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_reset ()
{
    fputs("</svg>\n\n", gpoutfile);
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_linetype
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_linetype (int linetype)
{
    SVG_color_mode = TC_DEFAULT;
    if (TRUE || linetype != SVG_LineType) {
	SVG_PathClose ();
	SVG_GroupClose ();
	SVG_LineType = linetype;
	SVG_GroupOpen ();
    }
    if (linetype == LT_AXIS)
	SVG_dashpattern = SVG_axis_dashpattern;
    if (linetype == LT_SOLID)
	SVG_dashpattern = NULL;
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_dashtype
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_dashtype (int type, t_dashtype *custom_dash_type)
{
    int d, j;
    double empirical_scale = 0.50;
    /* The dash pattern should depend on the `linewidth` and the terminal options `dashlength` and `linewidth`. */
    double dash_scale = SVG_pens[SVG_Pen_RealID (SVG_LineType)].width * SVG_linewidth_factor * SVG_dashlength * empirical_scale;

    SVG_dashpattern = NULL;	/* Assume solid line */

    switch(type) {

	case DASHTYPE_SOLID:
	    break;

	case DASHTYPE_AXIS:
	    /* Currently handled elsewhere via LT_AXIS */
	    break;

	case DASHTYPE_CUSTOM:
	    if (custom_dash_type) {
		SVG_dashpattern = SVG_custom_dash_pattern;
		*SVG_dashpattern = '\0';
		for (j = 0; j < 8 && custom_dash_type->pattern[j] > 0; j++) {
		    char *p = &SVG_dashpattern[strlen(SVG_dashpattern)];
		    snprintf(p, 8, "%.1f", custom_dash_type->pattern[j] * dash_scale);
		    if (j < 7 && custom_dash_type->pattern[j+1])
			strcat(p,",");
		}
	    }
	    break;

	default:
	    /* Fall back to whatever version 4 would have provided */
	    d = type % SVG_dashtypes;
	    if (d <= 0)
		break;

	    /* Default dash length and sequence */
	    if (dash_scale == 1.0)
		SVG_dashpattern = SVG_defaultdashpattern[d];

	    /* Dash patterns scaled up by dashlength and linewidth */
	    else {
		SVG_dashpattern = SVG_custom_dash_pattern;
		*SVG_dashpattern = '\0';
		j = 0;
		do {
		    char *p = &SVG_dashpattern[strlen(SVG_dashpattern)];
		    snprintf(p, 8, "%.1f", SVG_dasharray[d][j] * dash_scale);
		    if (SVG_dasharray[d][++j])
			strcat(p,",");
		} while (SVG_dasharray[d][j] > 0);
	    }
	    break;
    }
}

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%.*f,%.*f", PREC, X(x), PREC, Y(y));
	SVG_path_count++;

	SVG_AddSpaceOrNewline ();

	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);
	if (SVG_alpha != 0.0)
		sprintf(&color_spec[27], " opacity='%4.2f'", 1.0 - SVG_alpha);
    } else if (SVG_color_mode == TC_LT)
	sprintf(color_spec, " color='%s'", SVG_linecolor);
    else
	*color_spec = '\0';

    SVG_PathClose ();

    if (SVG_hypertext_text) {
	fprintf(gpoutfile,"\
\t<g onmousemove=\"gnuplot_svg.showHypertext(evt,'%s')\" \
onmouseout=\"gnuplot_svg.hideHypertext()\"><title> </title>\n",
		SVG_hypertext_text);
    }

    if (number < 0) {		/* do dot */
	fprintf (gpoutfile, "\
\t<use xlink:href='#gpDot' x='%.*f' y='%.*f'%s/>\n",
		 PREC, X(x), PREC, Y(y), color_spec);
    } else {			/* draw a point symbol */
	fprintf (gpoutfile, "\
\t<use xlink:href='#gpPt%u' transform='translate(%.*f,%.*f) scale(%.2f)'%s/>",
		 number % 15, PREC, X(x), PREC, Y(y),
		 term_pointsize * term->h_tic / (2 * SVG_SCALE),
		 color_spec);
    }
    SVG_xLast = x;
    SVG_yLast = y;

    if (SVG_hypertext_text) {
	fprintf(gpoutfile,"</g>\n");
	free(SVG_hypertext_text);
	SVG_hypertext_text = NULL;
    } else {
	fprintf(gpoutfile,"\n");
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	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;
    double vertical_offset;
    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*/
    vertical_offset = (SVG_fontAscent - SVG_fontDescent) / 2.;
    h += vertical_offset * sin(SVG_TextAngle * DEG2RAD);
    v -= vertical_offset * cos(SVG_TextAngle * DEG2RAD);

/* define text position and attributes */

    fprintf (gpoutfile, "\t<g transform=\"translate(%.*f,%.*f)", PREC, X(h), PREC, Y(v));
    if (SVG_TextAngle)
	fprintf (gpoutfile, " rotate(%i)", -SVG_TextAngle);
    fprintf (gpoutfile, "\" stroke=\"none\" fill=\"");

    if (SVG_color_mode == TC_RGB)
	fprintf (gpoutfile, "rgb(%d,%d,%d)", SVG_red, SVG_green, SVG_blue);
    else if (SVG_color_mode == TC_LT)
	fprintf (gpoutfile, "%s", SVG_linecolor);
    else
	fprintf (gpoutfile, "%s", SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
    fprintf (gpoutfile, "\" font-family=\"%s\" font-size=\"%.2f\" ",
	     SVG_fontNameCur, SVG_fontSizeCur * SVG_fontscale);
    if (SVG_fontWeightCur && strcmp(SVG_fontWeightCur,"normal"))
	    fprintf(gpoutfile, " font-weight=\"%s\" ", SVG_fontWeightCur);
    if (SVG_fontStyleCur && strcmp(SVG_fontStyleCur,"normal"))
	    fprintf(gpoutfile, " font-style=\"%s\" ", SVG_fontStyleCur);
    fprintf(gpoutfile, " text-anchor=\"%s\"", alignment);
#ifdef EAM_BOXED_TEXT
    if (SVG_inTextBox)
	fprintf(gpoutfile, " style='filter:url(#textbox)'");
#endif
    fprintf(gpoutfile, ">\n");

/* output text (unless the enhanced_text processing is in action) */

    if (strstr(str,"  "))
	fputs ("\t\t<text xml:space=\"preserve\">", gpoutfile);
    else
	fputs ("\t\t<text>", gpoutfile);

    if (!ENHsvg_string_state) {

	while (*str) {
	    /* Escape SVG reserved characters */
	    switch (*str) {
	    case '<':
		fputs("&lt;", gpoutfile);
		break;
	    case '&':
		if (str[1] == '#' && str[2] == 'x')
		    fputc(*str, gpoutfile);
		else
		    fputs("&amp;", gpoutfile);
		break;
	    default:
		fputc(*str, gpoutfile);
		break;
	    }

	    str++;
	}
	fputs("</text>\n\t</g>\n", gpoutfile);
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_set_font
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC int
SVG_set_font (const char *font)
{

    if (!font || !(*font)) {
	free(SVG_fontNameCur);
	SVG_fontNameCur = gp_strdup(SVG_fontNameDef);
	SVG_fontSizeCur = SVG_fontSizeDef;
	SVG_fontStyleCur = SVG_fontStyleDef;
	SVG_fontWeightCur = SVG_fontWeightDef;
    } else {
	char *bold, *italic; 
	int sep;

	if (!((bold = strstr(font," bold"))))
	    bold = strstr(font," Bold");

	if (!((italic = strstr(font," italic"))))
	    italic = strstr(font," Italic");

	sep = strcspn(font,",");
	if (sep > 0) {
	    free(SVG_fontNameCur);
	    SVG_fontNameCur = gp_strdup(font);
	    if (italic) {
		SVG_fontStyleCur="italic";
		SVG_fontNameCur[strlen(font) - strlen(italic)] = NUL;
	    } else {
		SVG_fontStyleCur="normal";
	    }

	    if (bold) {
        	SVG_fontWeightCur="bold";
		SVG_fontNameCur[strlen(font) - strlen(bold)] = NUL;
            } else {
		SVG_fontWeightCur="normal";
	    }
	    SVG_fontNameCur[sep] = NUL;
	}

	if (font[sep] == ',')
	    sscanf(font + sep + 1, "%lf", &SVG_fontSizeCur);
    }

    /* Set other font properties */
    SVG_SetFont(SVG_fontNameCur, 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;
    SVG_alpha = 0.0;

    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 & 0xff;
	rgb255.g = colorspec->lt >> 8 & 0xff;
	rgb255.b = colorspec->lt & 0xff;
	SVG_alpha = (double)(colorspec->lt >> 24 & 0xff) / 255.;
    } 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 || style == FS_TRANSPARENT_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<polygon ", gpoutfile);

    switch (style) {
	case FS_EMPTY: /* fill with background color */
	    fprintf(gpoutfile," fill = '%s'", SVG_pens[0].color);
	    break;
	case FS_SOLID: /* solid fill */
	case FS_TRANSPARENT_SOLID:
	    SVG_StyleFillColor();
	    if (SVG_alpha != 0.0)
		fprintf(gpoutfile, " fill-opacity='%4.2f' ", 1.0 - SVG_alpha);
	    else if (fillpar >= 0 && fillpar < 100)
		fprintf(gpoutfile, " fill-opacity = '%f'", fillpar * 0.01);
	    break;
	case FS_PATTERN: /* pattern fill */
	case FS_TRANSPARENT_PATTERN:
	    fprintf(gpoutfile, " fill = 'url(#gpPat%d)'",
		    SVG_fillPatternIndex);
	    break;
	default:
	    SVG_StyleFillColor();
	    break;
    }

    fputs(" points = '", gpoutfile);
    for (i = 0; i < points; i++)
	fprintf(gpoutfile, "%.*f,%.*f%s",
		PREC, X(corners[i].x), PREC, Y(corners[i].y),
		i % 16 == 15 ? "\n" : " ");
    fputs("'/>\n", gpoutfile);
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_layer
------------------------------------------------------------------------------------------------------------------------------------*/
TERM_PUBLIC void
SVG_layer(t_termlayer syncpoint)
{
    char *name = NULL;
    char panel[2] = {'\0','\0'};

    /* We must ignore all syncpoints that we don't recognize */
    switch (syncpoint) {

    	default:
		break;

	case TERM_LAYER_BEFORE_PLOT:
		SVG_PathClose();
		SVG_GroupClose();
		++SVG_plotno;
		name = (SVG_name) ? SVG_name : "gnuplot";
		if (multiplot && multiplot_current_panel() < 26)
		    panel[0] = 'a' + multiplot_current_panel();

		fprintf(gpoutfile, "\t<g id=\"%s_plot_%d%s\" ", name,SVG_plotno,panel);
		if (SVG_hypertext_text && *SVG_hypertext_text) {
		    fprintf(gpoutfile, "><title>%s</title>\n", SVG_hypertext_text);
		    free(SVG_hypertext_text);
		    SVG_hypertext_text = NULL;
		} else {
		    fprintf(gpoutfile, "><title>%s_plot_%d%s</title>\n", name,SVG_plotno,panel);
		}
		SVG_LineType = LT_UNDEFINED; /* Force a new group on next stroke */
		break;

	case TERM_LAYER_AFTER_PLOT:
		SVG_PathClose();
		SVG_GroupClose();
		fprintf(gpoutfile, "\t</g>\n");
		SVG_LineType = LT_UNDEFINED; /* Force a new group on next stroke */
		break;

	case TERM_LAYER_BEGIN_GRID:
		SVG_gridline = TRUE;
		SVG_hasgrid = TRUE;
		break;

	case TERM_LAYER_END_GRID:
		SVG_gridline = FALSE;
		break;

	case TERM_LAYER_BEGIN_KEYSAMPLE:
		if (SVG_mouseable) {
		    SVG_PathClose();
		    SVG_GroupFilledClose();
		    name = (SVG_name) ? SVG_name : "gnuplot";
		    if (multiplot && multiplot_current_panel() < 26)
			panel[0] = 'a' + multiplot_current_panel();

		    fprintf(gpoutfile, "\t<g id=\"%s_plot_%d%s_keyentry\" visibility=\"visible\" ",
			name,SVG_plotno,panel);
		    fprintf(gpoutfile,
			"onclick=\"gnuplot_svg.toggleVisibility(evt,'%s_plot_%d%s')\"",
			name,SVG_plotno,panel);
		    fprintf(gpoutfile, ">\n");
		}
		break;

	case TERM_LAYER_END_KEYSAMPLE:
		if (SVG_mouseable) {
		    SVG_PathClose();
		    SVG_GroupFilledClose();
		    fprintf(gpoutfile, "\t</g>\n");
		}
		break;

	case TERM_LAYER_RESET:
	case TERM_LAYER_RESET_PLOTNO:
		SVG_plotno = 0;
		break;
    }
}

/*------------------------------------------------------------------------------------------------------------------------------------
	SVG_image
------------------------------------------------------------------------------------------------------------------------------------*/
#ifdef WRITE_PNG_IMAGE
TERM_PUBLIC void
SVG_image (unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode) 
{
    char *image_file;
    char *base_name = SVG_name ? SVG_name : "gp";
   
    /* Write the image to a png file */
    image_file = gp_alloc(strlen(base_name)+16, "SVG_image");
    sprintf(image_file, "%s_image_%02d.png", base_name, ++SVG_imageno);
    write_png_image (m, n, image, color_mode, image_file);

    /* Map it onto the terminals coordinate system. */
    fprintf(gpoutfile, "<image x='%.*f' y='%.*f' width='%.*f' height='%.*f' preserveAspectRatio='none' ",
	PREC, X(corner[0].x), PREC, Y(corner[0].y), 
	PREC, X(corner[1].x) - X(corner[0].x), PREC, Y(corner[1].y) - Y(corner[0].y));

    fprintf(gpoutfile, "xlink:href='%s_image_%02d.png'/>;\n",
	base_name, SVG_imageno);

}
#endif


/* 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:
	/* FIXME: If there are multiple overprint characters, 
         *        they all get piled on top of one another.
	 */
	ENHsvg_FLUSH();
	fprintf(gpoutfile, "<tspan dx=\"-%.1fem\" dy=\"%.1fpx\">", 
		0.5 * ENHsvg_charcount, ENHsvg_base-base);
	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.5 * 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("<tspan", gpoutfile);
#if (0)
	if (strcmp(SVG_fontNameCur, fontname)) {
	    free(SVG_fontNameCur);
	    SVG_fontNameCur = gp_strdup(fontname);
	    fprintf(gpoutfile, " font-family=\"%s\" ", SVG_fontNameCur);
	}
	if (SVG_fontWeightCur && strcmp(SVG_fontWeightCur,"normal"))
	    fprintf(gpoutfile, " font-weight=\"%s\" ", SVG_fontWeightCur);
	if (SVG_fontStyleCur && strcmp(SVG_fontStyleCur,"normal"))
	    fprintf(gpoutfile, " font-style=\"%s\" ", SVG_fontStyleCur);
#else
	if (!fontname)
	    fprintf(stderr,"ENHsvg_OPEN: null fontname\n");
	else {
	    char *family = strdup(fontname);
	    char *sep = strchr(family, ':');
	    if (sep)
		*sep = '\0';
	    if (strcmp(SVG_fontNameCur, family)) {
		free(SVG_fontNameCur);
		SVG_fontNameCur = family;
	    } else {
		free(family);
	    }
	    fprintf(gpoutfile, " font-family=\"%s\" ", SVG_fontNameCur);
	    if (strstr(fontname,":Bold"))
		fprintf(gpoutfile, " font-weight=\"bold\" ");
	    if (strstr(fontname,":Italic"))
		fprintf(gpoutfile, " font-style=\"italic\" ");
	}
#endif
	if (SVG_fontSizeCur != fontsize) {
	    SVG_fontSizeCur = fontsize;
	    fprintf(gpoutfile, " font-size=\"%.1f\"", SVG_fontSizeCur * SVG_fontscale);
	}
	if (ENHsvg_x_offset != 0) {
	    fprintf(gpoutfile, " dx=\"%.2fem\"", ENHsvg_x_offset);
	    ENHsvg_x_offset = 0.0;
	}
	if (ENHsvg_base != base) {
	    fprintf(gpoutfile, " dy=\"%.2fpx\"", ENHsvg_base-base);
	    ENHsvg_base = base;
	}
	if (!showflag) {
	    fprintf(gpoutfile, " fill=\"none\"");
	}
	if (ENHsvg_preserve_spaces) {
	    fprintf(gpoutfile, " xml:space=\"preserve\"");
	}
	fputs(">", gpoutfile);
    }

}

TERM_PUBLIC void
ENHsvg_FLUSH()
{
    if (ENHsvg_opened_string) {
	ENHsvg_opened_string = FALSE;
	*enhanced_cur_text = '\0';
	fprintf(gpoutfile, "%s</tspan>", 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 */
    double fontsize = SVG_fontSizeCur;
    static char *fontname = NULL;

    free(fontname);
    fontname = gp_strdup(SVG_fontNameCur);

    /* 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);
	ENHsvg_string_state = 0;
    }

    /* 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 */
    free(SVG_fontNameCur);
    SVG_fontNameCur = fontname;
    fontname = NULL;
    if (SVG_fontSizeCur != fontsize || ENHsvg_base != 0) {
	fprintf(gpoutfile, "<tspan font-size=\"%.1f\" dy=\"%.2f\"></tspan>",
		fontsize * SVG_fontscale, ENHsvg_base);
	SVG_fontSizeCur = fontsize;
	ENHsvg_base = 0;
    }
    ENHsvg_preserve_spaces = FALSE;

    /* Close the text section */
    fputs("</text>\n\t</g>\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 '\n':
		*enhanced_cur_text++ = '\\';
		*enhanced_cur_text++ = 'n';
		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 == '<' ) {
	    restrict_popen();
	    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;
    }
}

TERM_PUBLIC void
SVG_hypertext( int type, const char *text )
{
    if (type != TERM_HYPERTEXT_TOOLTIP && type != TERM_HYPERTEXT_TITLE)
	return;

    free(SVG_hypertext_text);
    if (text) {
	char *buffer = gp_alloc(5*strlen(text),"escape");
	enhanced_cur_text = buffer;
	do { ENHsvg_writec(*text); }
	    while (*text++);
	SVG_hypertext_text = gp_strdup(buffer);
	enhanced_cur_text = NULL;
	free(buffer);
    } else {
	SVG_hypertext_text = NULL;
    }
}

#ifdef EAM_BOXED_TEXT
TERM_PUBLIC void
SVG_boxed_text(unsigned int x, unsigned int y, int option)
{
    switch (option) {
    case TEXTBOX_INIT:
	/* Mark group containing next text item */
	SVG_inTextBox = TRUE;
	break;
    case TEXTBOX_OUTLINE:
	/* Stroke the outline of the bounding box (FIXME: how???) */
    case TEXTBOX_BACKGROUNDFILL:
	/* Close the group, which will trigger application of the filter */
	SVG_inTextBox = FALSE;
	break;
    case TEXTBOX_MARGINS:
	/* Adjust the size of the bounding box */
	break;
    }
}
#endif
	
#undef Y
#undef X
#undef PREC

#endif /* TERM_BODY */

#ifdef TERM_TABLE
TERM_TABLE_START (svg_driver)
    "svg", "W3C Scalable Vector Graphics",
    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_CAN_DASH | TERM_ALPHA_CHANNEL|TERM_LINEWIDTH,
    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 WRITE_PNG_IMAGE
    , SVG_image
#else
    , NULL	/* image */
#endif
   , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec
   , SVG_layer	/* layer */
   , SVG_path 	/* path */
   , SVG_SCALE	/* pixel oversampling scale */
   , SVG_hypertext	/* hypertext support */
#ifdef EAM_BOXED_TEXT
   , SVG_boxed_text	/* boxed text labels */
#endif
   , NULL		/* modify_plots */
   , SVG_dashtype	/* Version 5 dashtype support */
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 <x>,<y> {|fixed|dynamic}}",
"                        {{no}enhanced}",
"                        {fname \"<font>\"} {fsize <fontsize>}",
"                        {mouse} {standalone | jsdir <dirname>}",
"                        {name <plotname>}",
"                        {font \"<fontname>{,<fontsize>}\"}",
"                        {fontfile <filename>}",
"                        {rounded|butt|square} {solid|dashed} {linewidth <lw>}",
"                        {background <rgb_color>}",
"",
" where <x> and <y> 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 <w>` increases the width of all lines used in the figure",
" by a factor of <w>.",
"",
" <font> is the name of the default font to use (default Arial) and",
" <fontsize> 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.",
"",
" The `mouse` option tells gnuplot to add support for mouse tracking and for",
" toggling individual plots on/off by clicking on the corresponding key entry.",
" By default this is done by including a link that points to a script in a",
" local directory, usually /usr/local/share/gnuplot/<version>/js.",
" You can change this by using the `jsdir` option to specify either a",
" different local directory or a general URL. The latter is usually",
" appropriate if you are embedding the svg into a web page.",
" Alternatively, the `standalone` option embeds the mousing code in the",
" svg document itself rather than linking to an external resource.",
"",
" When an SVG file will be used in conjunction with external files,",
" e.g. if it embeds a PNG image or is referenced by javascript code",
" in a web page or embedding document, then a unique name is required",
" to avoid potential conflicting references to other SVG plots.",
" Use the `name` option to ensure uniqueness.",
"",
" 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 <defs> 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
