/* Hello, Emacs, this is -*-C-*- */
/*------------------------------------------------------------------------------------------------------------------------------------
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
DomTerm support
Per Bothner
------------------------------------------------------------------------------------------------------------------------------------*/
/* 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
* Ethan Merritt
*
* 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 to attribute='foo'
*
* Additional code for gnuplot versions 4.5
* Ethan Merritt
*
* Wrap each plot in a named group
* 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
* Javascript code to toggle plots on/off
*
* Revised font sizing Oct 2012
* specify font-size without "pt" units.
*
* Inline image data in Base64 encoding
* Daniel Sebald May 2016
*
* Transition to SVG 2.0
* remove DTD
* remove option "fontfile" and references to SVG fonts
*/
/*
* Code for gnuplot version 4.5
* Bold -> font-weight:bold
* Italic -> font-style:italic
* Rich Seymour
*/
#include "driver.h"
#ifdef TERM_REGISTER
register_term(svg)
#endif
#ifdef TERM_PROTO
TERM_PUBLIC void SVG_options(void);
TERM_PUBLIC void SVG_init(void);
TERM_PUBLIC void SVG_graphics(void);
TERM_PUBLIC void SVG_text(void);
TERM_PUBLIC void SVG_linetype(int linetype);
TERM_PUBLIC void SVG_dashtype(int type, t_dashtype *custom_dash_type);
TERM_PUBLIC void SVG_move(unsigned int x, unsigned int y);
TERM_PUBLIC void SVG_vector(unsigned int x, unsigned int y);
TERM_PUBLIC void SVG_put_text(unsigned int x, unsigned int y, const char *str);
TERM_PUBLIC void SVG_reset(void);
TERM_PUBLIC int SVG_justify_text(enum JUSTIFY mode);
TERM_PUBLIC int SVG_text_angle(float ang);
TERM_PUBLIC void SVG_point(unsigned int x, unsigned int y, int pointstyle);
TERM_PUBLIC int SVG_set_font(const char *font);
/* TERM_PUBLIC void SVG_pointsize(double pointsize); */
TERM_PUBLIC void SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height);
TERM_PUBLIC void SVG_linewidth(double linewidth);
TERM_PUBLIC int SVG_make_palette(t_sm_palette *);
TERM_PUBLIC void SVG_previous_palette(void);
TERM_PUBLIC void SVG_set_color(t_colorspec *);
TERM_PUBLIC void SVG_filled_polygon(int, gpiPoint *);
TERM_PUBLIC void SVG_layer(t_termlayer syncpoint);
TERM_PUBLIC void ENHsvg_OPEN(char *, double, double, TBOOLEAN, TBOOLEAN, int);
TERM_PUBLIC void ENHsvg_FLUSH(void);
TERM_PUBLIC void ENHsvg_put_text(unsigned int, unsigned int, const char *);
TERM_PUBLIC void ENHsvg_writec(int);
TERM_PUBLIC void SVG_path(int p);
TERM_PUBLIC void SVG_hypertext(int, const char *);
#ifdef WRITE_PNG_IMAGE
TERM_PUBLIC void SVG_image(unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode);
#endif
#define SVG_SCALE 100. /* Coordinate accuracy is 1/SVG_SCALE pixel */
#define PREC 2 /* 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 unsigned int SVG_rgb = 0;
static unsigned int SVG_patterncolor[8];
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_domterm = FALSE;
static TBOOLEAN SVG_emit_doctype = TRUE;
static TBOOLEAN SVG_animate = 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 float 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_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(int);
static void SVG_PathOpen(void);
static void SVG_PathClose(void);
static void SVG_AddSpaceOrNewline(void);
static void SVG_GroupOpen(void);
static void SVG_GroupClose(void);
static void SVG_SetFont(const char *name, double size);
static void SVG_GroupFilledOpen(void);
static void SVG_GroupFilledClose(void);
static void SVG_StyleColor(const char*);
static void SVG_StyleFillColor(void);
static void SVG_local_reset(void);
static void SVG_DefineFillPattern(int fillpat);
static void SVG_MoveForced(unsigned int x, unsigned int y);
static void SVG_write_preamble(void);
/* 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];
/* Hypertext support */
static double SVG_hypertext_fontSize = 0;
static char *SVG_hypertext_fontName = NULL;
static char *SVG_hypertext_fontStyle = NULL;
static char *SVG_hypertext_fontWeight = NULL;
/* 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_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");
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\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 *;
unsigned int newcolor;
fillpat %= 8;
if (SVG_color_mode == TC_RGB || SVG_color_mode == TC_LT)
newcolor = SVG_rgb;
else
newcolor = SVG_patterncolor[fillpat];
if (fillpat != SVG_fillPattern || newcolor != SVG_patterncolor[fillpat]) {
SVG_fillPattern = fillpat;
SVG_patterncolor[fillpat] = newcolor;
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";
;
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 || SVG_color_mode == TC_LT)
fprintf(gpoutfile,"\t\t\t\n",
figure, style,
(newcolor >> 16) & 0xff, (newcolor >> 8) & 0xff, newcolor & 0xff,
path);
else
fprintf(gpoutfile, "\t\t\t\n",
figure, 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%.*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();
if (strcmp(term->name, "domterm") == 0) {
SVG_emit_doctype = FALSE;
SVG_domterm = TRUE;
} else {
SVG_emit_doctype = TRUE;
SVG_domterm = FALSE;
}
/* Minimal initialization in case we error out of options parsing */
SVG_set_font("");
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 (equals(c_token, "fontscale")) {
c_token++;
SVG_fontscale = real_expression();
if (SVG_fontscale