1/* Hello, Emacs, this is -*-C-*- */
2
3/*------------------------------------------------------------------------------------------------------------------------------------
4	GNUPLOT - svg.trm
5
6	This file is included by ../term.c.
7
8	This terminal driver supports:
9		W3C Scalable Vector Graphics
10
11	AUTHOR
12
13		Amedeo Farello
14		afarello@libero.it
15
16	HEAVILY MODIFIED by
17
18		Hans-Bernhard Br"oker
19		broeker@physik.rwth-aachen.de
20
21	DomTerm support
22
23		Per Bothner  <per@bothner.com>
24
25------------------------------------------------------------------------------------------------------------------------------------*/
26
27/* PM3D support by Johannes Zellner <johannes@zellner.org>, May-16-2002 */
28/* set_color fixes by Petr Mikulik <mikulik@physics.muni.cz>, June-10-2002 */
29/* ISO-Latin encoding, Font selection fixes, option "fixed|dynamic" by
30 * Wilhelm Braunschober <Wilhelm.Braunschober@t-online.de>, Feb-21-2002 */
31
32/*
33 * Additional code for gnuplot versions 4.2 and 4.3
34 * Ethan Merritt  <merritt@u.washington.edu>
35 *
36 *   Tweaked code for compatibility with Sodipodi svg viewer/editor.
37 *   Added enhanced text support.
38 *   Additional line properties.
39 *   Increase resolution by adding a coordinate scale factor.
40 *   Support dashed lines, TC_* color model.
41 *   Change path markup from    style='attribute: foo'  to   attribute='foo'
42 *
43 * Additional code for gnuplot versions 4.5
44 * Ethan Merritt  <merritt@u.washington.edu>
45 *
46 *   Wrap each plot in a named group <g id="name_plot_%02d">
47 *   Set the name using 'set term svg name "foo"'
48 *   Background option
49 *   Bitmap image support by creating and linking to external png files
50 *   Mouse-tracking with coordinate readout.
51 *   Version 4.7 (April 2012) hypertext support
52 *
53 * Contributed by <plotter@piments.com>
54 *   Javascript code to toggle plots on/off
55 *
56 * Revised font sizing Oct 2012
57 *   specify font-size without "pt" units.
58 *
59 * Inline image data in Base64 encoding
60 *   Daniel Sebald May 2016
61 *
62 * Transition to SVG 2.0
63 *   remove DTD
64 *   remove option "fontfile" and references to SVG fonts
65 */
66
67/*
68 * Code for gnuplot version 4.5
69 *   Bold -> font-weight:bold
70 *   Italic -> font-style:italic
71 * Rich Seymour <rseymour@usc.edu>
72 */
73
74#include "driver.h"
75
76#ifdef TERM_REGISTER
77register_term(svg)
78#endif
79
80#ifdef TERM_PROTO
81TERM_PUBLIC void SVG_options(void);
82TERM_PUBLIC void SVG_init(void);
83TERM_PUBLIC void SVG_graphics(void);
84TERM_PUBLIC void SVG_text(void);
85TERM_PUBLIC void SVG_linetype(int linetype);
86TERM_PUBLIC void SVG_dashtype(int type, t_dashtype *custom_dash_type);
87TERM_PUBLIC void SVG_move(unsigned int x, unsigned int y);
88TERM_PUBLIC void SVG_vector(unsigned int x, unsigned int y);
89TERM_PUBLIC void SVG_put_text(unsigned int x, unsigned int y, const char *str);
90TERM_PUBLIC void SVG_reset(void);
91TERM_PUBLIC int SVG_justify_text(enum JUSTIFY mode);
92TERM_PUBLIC int SVG_text_angle(int ang);
93TERM_PUBLIC void SVG_point(unsigned int x, unsigned int y, int pointstyle);
94TERM_PUBLIC int SVG_set_font(const char *font);
95/* TERM_PUBLIC void SVG_pointsize(double pointsize); */
96TERM_PUBLIC void SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height);
97TERM_PUBLIC void SVG_linewidth(double linewidth);
98TERM_PUBLIC int SVG_make_palette(t_sm_palette *);
99TERM_PUBLIC void SVG_previous_palette(void);
100TERM_PUBLIC void SVG_set_color(t_colorspec *);
101TERM_PUBLIC void SVG_filled_polygon(int, gpiPoint *);
102TERM_PUBLIC void SVG_layer(t_termlayer syncpoint);
103
104TERM_PUBLIC void ENHsvg_OPEN(char *, double, double, TBOOLEAN, TBOOLEAN, int);
105TERM_PUBLIC void ENHsvg_FLUSH(void);
106TERM_PUBLIC void ENHsvg_put_text(unsigned int, unsigned int, const char *);
107TERM_PUBLIC void ENHsvg_writec(int);
108
109TERM_PUBLIC void SVG_path(int p);
110TERM_PUBLIC void SVG_hypertext(int, const char *);
111
112#ifdef WRITE_PNG_IMAGE
113TERM_PUBLIC void SVG_image(unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode);
114static int SVG_imageno = 0;
115#endif
116
117#define SVG_SCALE	100.	/* Coordinate accuracy is 1/SVG_SCALE pixel */
118#define PREC		2	/* Decimal places needed for SVG_SCALEd values */
119#define Y(y) ((float)((int)term->ymax - (int)y) / SVG_SCALE)
120#define X(x) ((float)(x) / SVG_SCALE)
121
122#define SVG_XMAX	(600 * SVG_SCALE)
123#define SVG_YMAX	(480 * SVG_SCALE)
124
125#endif /* TERM_PROTO */
126
127#ifndef TERM_PROTO_ONLY
128#ifdef TERM_BODY
129
130static t_sm_palette SVG_palette;
131static unsigned char SVG_red = 0;
132static unsigned char SVG_green = 0;
133static unsigned char SVG_blue = 0;
134static double SVG_alpha = 0.0;
135static unsigned char SVG_color_mode = TC_DEFAULT;
136static char *SVG_linecolor = NULL;
137static char *SVG_name = NULL;
138static char *SVG_scriptdir = NULL;
139static TBOOLEAN SVG_mouseable = FALSE;
140static TBOOLEAN SVG_standalone = FALSE;
141static TBOOLEAN SVG_domterm = FALSE;
142static TBOOLEAN SVG_emit_doctype = TRUE;
143
144static TBOOLEAN SVG_groupFilledIsOpen = FALSE; /* open pm3d group flag*/
145static TBOOLEAN SVG_inTextBox = FALSE;
146
147struct SVG_PEN
148{
149    double width;
150    char color[8];
151};
152
153static unsigned int SVG_xSize = SVG_XMAX; /* plot horizontal size */
154static unsigned int SVG_ySize = SVG_YMAX; /* plot vertical size*/
155static TBOOLEAN SVG_fixed_size = TRUE;  /* make SVG viewer size fixed */
156
157static unsigned int SVG_xLast = UINT_MAX; /* current pen horizontal position*/
158static unsigned int SVG_yLast = UINT_MAX;	/* current pen vertical position*/
159
160static int SVG_LineType = LT_NODRAW;	/* current line type*/
161static double SVG_LineWidth = 1.0; /* current line width*/
162static double SVG_linewidth_factor = 1.0; /* Multiplier for linewidths */
163static double SVG_dashlength = 1.0; /* Multiplier for dash patterns */
164static t_linecap SVG_linecap = BUTT; /* linejoin and linecap */
165static int SVG_TextAngle = 0;	/* current text orientation*/
166static enum JUSTIFY SVG_TextJust = LEFT; /* current text justification*/
167
168/* default text font family: */
169static char *SVG_fontNameDef = NULL;
170static char *SVG_fontStyleDef = NULL; /* default font style */
171static char *SVG_fontWeightDef = NULL; /* default font weight */
172static double SVG_fontSizeDef = 12;	/* default text size*/
173/* current text font family: */
174static char *SVG_fontNameCur = NULL;
175static char *SVG_fontStyleCur = NULL; /* current font style */
176static char *SVG_fontWeightCur = NULL; /* current font weight */
177static double SVG_fontSizeCur = 12;	/* current text size*/
178static double SVG_fontscale = 1.0;	/* multiplier for nominal font size */
179static TBOOLEAN SVG_groupIsOpen = FALSE; /* open group flag*/
180static TBOOLEAN SVG_pathIsOpen = FALSE; /* open path flag*/
181static unsigned int SVG_path_count = 0;	/* size of current path*/
182static struct SVG_PEN SVG_pens[16];	/* pen descriptors*/
183
184static int SVG_fillPattern = -1; /* active fill pattern (-1 == undefined) */
185static unsigned int SVG_fillPatternIndex = 0;
186static int SVG_background = -1;
187static int SVG_plotno = 0;
188static TBOOLEAN SVG_gridline = FALSE;
189static TBOOLEAN SVG_hasgrid = FALSE;
190
191static double SVG_fontAscent  = 0;	/* estimated current font ascent*/
192static double SVG_fontDescent = 0;	/* estimated current font descent*/
193static double SVG_fontLeading = 0;	/* estimated current font leading*/
194static double SVG_fontAvWidth = 0;	/* estimated current font char average width*/
195
196static short SVG_Pen_RealID(int);
197static void SVG_PathOpen(void);
198static void SVG_PathClose(void);
199static void SVG_AddSpaceOrNewline(void);
200static void SVG_GroupOpen(void);
201static void SVG_GroupClose(void);
202static void SVG_SetFont(const char *name, double size);
203static void SVG_GroupFilledOpen(void);
204static void SVG_GroupFilledClose(void);
205static void SVG_StyleColor(const char*);
206static void SVG_StyleFillColor(void);
207static void SVG_local_reset(void);
208static void SVG_DefineFillPattern(int fillpat);
209static void SVG_MoveForced(unsigned int x, unsigned int y);
210static void SVG_write_preamble(void);
211
212/* Stuff for enhanced text mode */
213static int ENHsvg_string_state = 0;
214static double ENHsvg_x_offset = 0;
215static TBOOLEAN ENHsvg_preserve_spaces = FALSE;
216
217/* Support for dashed lines */
218#define SVG_dashtypes 5
219static char *SVG_defaultdashpattern[SVG_dashtypes] = {
220    "", " 5,8", " 2,4", " 8,4,2,4", " 9,4,1,4,1,4"
221};
222static char *SVG_axis_dashpattern = "2,4";
223static int SVG_dasharray[SVG_dashtypes][7] = {
224	{ 0,0,0,0,0,0,0},
225	{5,8, 0,0,0,0,0},
226	{2,4, 0,0,0,0,0},
227	{8,4,2,4, 0,0,0},
228	{9,4,1,4,1,4, 0}
229};
230static char *SVG_dashpattern = NULL;
231static char  SVG_custom_dash_pattern[64];
232
233/* Hypertext support */
234static double SVG_hypertext_fontSize = 0;
235static char *SVG_hypertext_fontName = NULL;
236static char *SVG_hypertext_fontStyle = NULL;
237static char *SVG_hypertext_fontWeight = NULL;
238
239/* Support for embedded hypertext */
240static char *SVG_hypertext_text = NULL;
241
242/*------------------------------------------------------------------------------------------------------------------------------------
243	SVG_Pen_RealID
244------------------------------------------------------------------------------------------------------------------------------------*/
245static short
246SVG_Pen_RealID (int inPenCode)
247{
248    if (inPenCode >= 13)
249	inPenCode %= 13;	/* normalize pen code*/
250    inPenCode += 3;
251    if (inPenCode < 0)
252	inPenCode = 0;		/* LT_BACKGROUND should use background color */
253
254    return (inPenCode);
255}
256
257/*------------------------------------------------------------------------------------------------------------------------------------
258	SVG_GroupOpen
259------------------------------------------------------------------------------------------------------------------------------------*/
260static void
261SVG_GroupOpen ()
262{
263    SVG_GroupFilledClose();
264    if (!SVG_groupIsOpen) {
265
266	fprintf (gpoutfile, "<g fill=\"none\" color=\"%s\" stroke=\"",
267		 SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
268
269	if (SVG_color_mode == TC_RGB)
270	    fprintf(gpoutfile, "rgb(%3d, %3d, %3d)", SVG_red, SVG_green, SVG_blue);
271	else if (SVG_color_mode == TC_LT)
272	    fprintf(gpoutfile, "%s", SVG_linecolor);
273	else
274	    fprintf(gpoutfile, "currentColor");
275
276	fprintf (gpoutfile, "\" ");
277	fprintf (gpoutfile, "stroke-width=\"%.2f\" stroke-linecap=\"%s\" stroke-linejoin=\"%s\"",
278		 SVG_pens[SVG_Pen_RealID (SVG_LineType)].width * SVG_linewidth_factor,
279		 SVG_linecap == ROUNDED ? "round" : SVG_linecap == SQUARE ? "square" : "butt",
280		 SVG_linecap == ROUNDED ? "round" : "miter");
281
282	fprintf (gpoutfile, ">\n");
283
284	SVG_groupIsOpen = TRUE;
285    }
286}
287
288/*------------------------------------------------------------------------------------------------------------------------------------
289	SVG_GroupClose
290------------------------------------------------------------------------------------------------------------------------------------*/
291static void
292SVG_GroupClose ()
293{
294    SVG_GroupFilledClose();
295    if (SVG_groupIsOpen) {
296	  fputs ("</g>\n", gpoutfile);
297	  SVG_groupIsOpen = FALSE;
298	  SVG_fillPattern = -1;
299      }
300}
301
302/*------------------------------------------------------------------------------------------------------------------------------------
303	SVG_PathOpen
304------------------------------------------------------------------------------------------------------------------------------------*/
305static void
306SVG_PathOpen ()
307{
308    if (!SVG_pathIsOpen) {
309	SVG_GroupFilledClose();
310
311	fputs ("\t<path ", gpoutfile);
312
313	/* Line color */
314	if (SVG_LineType == LT_NODRAW)
315	    fprintf(gpoutfile, "stroke='none' ");
316	else if (SVG_color_mode == TC_RGB)
317	    fprintf(gpoutfile, "stroke='rgb(%3d, %3d, %3d)' ",
318		    SVG_red, SVG_green, SVG_blue);
319	else if (SVG_color_mode == TC_LT)
320	    fprintf(gpoutfile, "stroke='%s' ", SVG_linecolor);
321
322	/* Axis is always dotted */
323	if (SVG_LineType == LT_AXIS)
324	    fprintf(gpoutfile, "stroke-dasharray='2,4' ");
325
326	/* Other patterns were selected by a previous call to SVG_dashtype */
327	else if (SVG_dashpattern)
328	    fprintf(gpoutfile, "stroke-dasharray='%s' ", SVG_dashpattern);
329
330	/* RGBA */
331	if (SVG_alpha != 0.0)
332	    fprintf(gpoutfile, "opacity='%4.2f' ", 1.0 - SVG_alpha);
333
334	/* Mark grid lines so that we can toggle them on/off */
335	if (SVG_gridline)
336	    fprintf(gpoutfile, "class=\"gridline\" ");
337
338	fputs (" d='", gpoutfile);
339
340	SVG_pathIsOpen = TRUE;
341    }
342}
343
344/*------------------------------------------------------------------------------------------------------------------------------------
345	SVG_PathClose
346------------------------------------------------------------------------------------------------------------------------------------*/
347static void
348SVG_PathClose ()
349{
350    if (SVG_pathIsOpen) {
351	SVG_GroupFilledClose();
352	fprintf(gpoutfile," '/>");
353	SVG_path_count = 0;
354	SVG_pathIsOpen = FALSE;
355    }
356}
357
358/*------------------------------------------------------------------------------------------------------------------------------------
359	SVG_AddSpaceOrNewline
360------------------------------------------------------------------------------------------------------------------------------------*/
361static void
362SVG_AddSpaceOrNewline ()
363{
364    if (SVG_path_count % 8 == 0)	/* avoid excessive line length*/
365	fputs ("\n\t\t", gpoutfile);
366    else
367      fputs (" ", gpoutfile);
368}
369
370/*------------------------------------------------------------------------------------------------------------------------------------
371	SVG_SetFont
372------------------------------------------------------------------------------------------------------------------------------------*/
373static void
374SVG_SetFont (const char *name, double size)
375{
376    if (name != SVG_fontNameCur) {
377	free(SVG_fontNameCur);
378	SVG_fontNameCur = gp_strdup(name);
379    }
380    SVG_fontSizeCur = size;
381
382/* since we cannot interrogate SVG about text properties and according
383 * to SVG 1.0 W3C Candidate Recommendation 2 August 2000 the
384 * "line-height" of the 'text' element is defined to be equal to the
385 * 'font-size' (!), we have to to define font properties in a less
386 * than optimal way */
387
388    SVG_fontAscent  = (SVG_fontSizeCur * 0.90 * SVG_SCALE);
389    SVG_fontDescent = (SVG_fontSizeCur * 0.25 * SVG_SCALE);
390    SVG_fontLeading = (SVG_fontSizeCur * 0.35 * SVG_SCALE);
391    SVG_fontAvWidth = (SVG_fontSizeCur * 0.70 * SVG_SCALE);
392
393    term->h_char = SVG_fontAvWidth;
394    term->v_char = (SVG_fontAscent + SVG_fontDescent + SVG_fontLeading);
395}
396
397static void
398SVG_GroupFilledOpen()
399{
400    if (!SVG_groupFilledIsOpen) {
401	SVG_PathClose();
402	fputs("\t<g stroke='none' shape-rendering='crispEdges'>\n",
403	      gpoutfile);
404	SVG_groupFilledIsOpen = TRUE;
405    }
406}
407
408static void
409SVG_GroupFilledClose()
410{
411    if (SVG_groupFilledIsOpen) {
412	fputs("\t</g>\n", gpoutfile);
413	SVG_groupFilledIsOpen = FALSE;
414    }
415}
416
417static void
418SVG_StyleColor(const char* paint)
419{
420    if (SVG_color_mode == TC_RGB)
421	fprintf(gpoutfile, "%s = 'rgb(%3d, %3d, %3d)'", paint, SVG_red, SVG_green, SVG_blue);
422    else if (SVG_color_mode == TC_LT)
423	fprintf(gpoutfile, "%s = '%s'", paint, SVG_linecolor);
424    else
425	fprintf(gpoutfile, "%s = 'currentColor'", paint);
426}
427
428static void
429SVG_StyleFillColor()
430{
431    SVG_StyleColor("fill");
432}
433
434static void
435SVG_DefineFillPattern(int fillpat)
436{
437    char *path;
438    char *style="stroke";
439
440    fillpat %= 8;
441    if (fillpat != SVG_fillPattern) {
442	SVG_fillPattern = fillpat;
443	SVG_PathClose();
444	SVG_fillPatternIndex++;
445
446	fprintf(gpoutfile,
447	    "\t<defs>\n"
448	    "\t\t<pattern id='gpPat%d' patternUnits='userSpaceOnUse' x='0' y='0' width='8' height='8'>\n",
449	    SVG_fillPatternIndex);
450	switch (fillpat) {
451	    default:
452	    case 0:
453		    path="";
454		    break;
455	    case 1:
456		    path="M0,0 L8,8 M0,8 L8,0";
457		    break;
458	    case 2:
459		    path="M0,0 L8,8 M0,8 L8,0 M0,4 L4,8 L8,4 L4,0 L0,4";
460		    break;
461	    case 3:
462		    path="M0,0 L0,8 L8,8 L8,0 L0,0";
463		    style="fill";
464		    break;
465	    case 4:
466		    path="M-4,0 L8,12 M0,-4 L12,8";
467		    break;
468	    case 5:
469		    path="M-4,8 L8,-4 M0,12 L12,0";
470		    break;
471	    case 6:
472		    path="M-2,8 L4,-4 M0,12 L8,-4 M4,12 L10,0";
473		    break;
474	    case 7:
475		    path="M-2,0 L4,12 M0,-4 L8,12 M4,-4 L10,8";
476		    break;
477	}
478	if (*path) {
479	    char *figure = "fill:none;";
480	    if (!strcmp(style,"fill")) figure = "stroke:none;";
481	    if (SVG_color_mode == TC_RGB)
482		fprintf(gpoutfile,"\t\t\t<path style='%s %s:rgb(%d,%d,%d)' d='%s'/>\n",
483			figure, style, SVG_red, SVG_green, SVG_blue, path);
484	    else if (SVG_color_mode == TC_LT)
485		fprintf(gpoutfile, "\t\t\t<path style = '%s %s:%s' d= '%s'/>\n",
486			figure, style, SVG_linecolor, path);
487	    else
488		fprintf(gpoutfile, "\t\t\t<path style = '%s %s:currentColor' d='%s'/>\n",
489			figure, style, path);
490	}
491	fputs("\t\t</pattern>\n" "\t</defs>\n", gpoutfile);
492    }
493}
494
495static void
496SVG_MoveForced(unsigned int x, unsigned int y)
497{
498    if (SVG_path_count > 512)
499	SVG_PathClose();
500
501    SVG_PathOpen ();
502
503    fprintf (gpoutfile, "M%.*f,%.*f", PREC, X(x), PREC, Y(y));
504    SVG_path_count++;
505
506    SVG_AddSpaceOrNewline ();
507
508    SVG_xLast = x;
509    SVG_yLast = y;
510}
511
512/*------------------------------------------------------------------------------------------------------------------------------------
513	SVG_options
514------------------------------------------------------------------------------------------------------------------------------------*/
515TERM_PUBLIC void
516SVG_options ()
517{
518    /* Annoying hack to handle the case of 'set termoption' after */
519    /* we have already initialized the terminal settings.         */
520    if (!almost_equals(c_token-1, "termopt$ion"))
521	SVG_local_reset();
522
523    if (strcmp(term->name, "domterm") == 0) {
524        SVG_emit_doctype = FALSE;
525        SVG_domterm = TRUE;
526    } else {
527        SVG_emit_doctype = TRUE;
528        SVG_domterm = FALSE;
529    }
530
531    /* Minimal initialization in case we error out of options parsing */
532    SVG_set_font("");
533
534    while (!END_OF_COMMAND) {
535	if (almost_equals(c_token, "s$ize")) {
536	    double value;
537
538	    c_token++;
539
540	    if (END_OF_COMMAND)
541		int_error(c_token,"expecting x size");
542	    value = real_expression();
543	    if (value < 2)
544		int_error(c_token,"x size out of range");
545	    SVG_xSize = value * SVG_SCALE;
546
547	    if (equals(c_token,","))
548		c_token++;
549	    if (END_OF_COMMAND)
550		int_error(c_token,"expecting y size");
551	    value = real_expression();
552	    if (value < 2)
553		int_error(c_token,"y size out of range");
554	    SVG_ySize = value * SVG_SCALE;
555	    continue;
556	}
557
558	if (equals(c_token, "mouse") || almost_equals(c_token, "mous$ing")) {
559	    c_token++;
560	    SVG_mouseable = TRUE;
561	    continue;
562	}
563
564	if (almost_equals(c_token, "stand$alone")) {
565	    c_token++;
566	    SVG_standalone = TRUE;
567	    continue;
568	}
569
570	if (equals(c_token, "name")) {
571	    c_token++;
572	    SVG_name = try_to_get_string();
573	    if (!SVG_name)
574		    int_error(c_token,"expecting a plot name");
575	    if (SVG_name[strspn(SVG_name,
576		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_1234567890")])
577		    int_error(c_token-1,"name must contain only alphanumerics or _");
578	    continue;
579	}
580
581	if (equals(c_token, "jsdir")) {
582	    c_token++;
583	    SVG_scriptdir = try_to_get_string();
584	    continue;
585	}
586
587	if (almost_equals(c_token, "d$ynamic")) {
588	    c_token++;
589	    SVG_fixed_size = FALSE;
590	    continue;
591	}
592
593	if (almost_equals(c_token, "fi$xed")){
594	    c_token++;
595	    SVG_fixed_size = TRUE;
596	    continue;
597	}
598
599	if (almost_equals(c_token, "enh$anced")) {
600	    c_token++;
601	    term->put_text = ENHsvg_put_text;
602	    term->flags |= TERM_ENHANCED_TEXT;
603	    continue;
604	}
605
606	if (almost_equals(c_token, "noenh$anced")) {
607	    c_token++;
608	    term->put_text = SVG_put_text;
609	    term->flags &= ~TERM_ENHANCED_TEXT;
610	    continue;
611	}
612
613	if (almost_equals(c_token, "fn$ame") || almost_equals(c_token, "font"))  {
614	    char *s, *comma;
615	    c_token++;
616
617	    if (!(s = try_to_get_string()))
618		int_error(c_token,"expecting font name");
619	    comma = strrchr(s,',');
620	    if (comma && (1 == sscanf(comma + 1, "%lf", &SVG_fontSizeDef)))
621		*comma = '\0';
622
623	    if (*s) {
624		char *bold, *italic;
625
626		if (!((bold = strstr(s," bold"))))
627		    bold = strstr(s," Bold");
628
629		if (!((italic = strstr(s," italic"))))
630		    italic = strstr(s," Italic");
631
632		free(SVG_fontNameDef);
633		SVG_fontNameDef = s;
634		if (italic) {
635		    SVG_fontStyleDef = "italic";
636		    SVG_fontNameDef[strlen(s) - strlen(italic)] = NUL;
637		} else  {
638		    SVG_fontStyleDef = "normal";
639		}
640		if (bold) {
641		    SVG_fontWeightDef="bold";
642		    SVG_fontNameDef[strlen(s) - strlen(bold)] = NUL;
643		} else {
644		    SVG_fontWeightDef = "normal";
645		}
646	    } else
647		free(s);
648	    continue;
649	}
650
651	if (equals(c_token, "fontscale")) {
652	    c_token++;
653	    SVG_fontscale = real_expression();
654	    if (SVG_fontscale <= 0)
655		SVG_fontscale = 1.0;
656	    continue;
657	}
658
659	if (almost_equals(c_token, "linew$idth") || equals(c_token, "lw")) {
660	    c_token++;
661	    SVG_linewidth_factor = real_expression();
662	    if (SVG_linewidth_factor <= 0.0)
663		SVG_linewidth_factor = 1.0;
664	    continue;
665	}
666
667	if (almost_equals(c_token, "dashl$ength") || equals(c_token, "dl")) {
668	    c_token++;
669	    SVG_dashlength = real_expression();
670	    if (SVG_dashlength < 0.5)
671		SVG_dashlength = 1.0;
672	    continue;
673	}
674
675	if (almost_equals (c_token, "round$ed")) {
676	    c_token++;
677	    SVG_linecap = ROUNDED;
678	    continue;
679	}
680
681	if (equals (c_token, "square")) {
682	    c_token++;
683	    SVG_linecap = SQUARE;
684	    continue;
685	}
686
687	if (equals (c_token, "butt")) {
688	    c_token++;
689	    SVG_linecap = BUTT;
690	    continue;
691	}
692
693	/* Not used in version 5 */
694	if (equals(c_token, "solid") || almost_equals(c_token, "dash$ed")) {
695	    c_token++;
696	    continue;
697	}
698
699	if (almost_equals(c_token, "backg$round")) {
700	    c_token++;
701	    SVG_background = parse_color_name();
702	    continue;
703	}
704
705	int_error(c_token, "unrecognized terminal option");
706    }
707
708    /* I don't think any error checks on font name are possible; just set it */
709    SVG_set_font("");
710
711    /* Save options back into options string in normalized format */
712    sprintf(term_options, "size %d,%d%s %s font '%s,%g' ",
713	    (int)(SVG_xSize/SVG_SCALE), (int)(SVG_ySize/SVG_SCALE),
714	SVG_fixed_size ? " fixed": " dynamic",
715	term->put_text == ENHsvg_put_text ? "enhanced" : "",
716	SVG_fontNameCur, SVG_fontSizeCur);
717
718    if (SVG_mouseable) {
719	sprintf(term_options + strlen(term_options),
720	    "mousing ");
721    }
722
723    if (SVG_standalone) {
724	sprintf(term_options + strlen(term_options),
725	    "standalone ");
726    }
727
728    if (SVG_name) {
729	sprintf(term_options + strlen(term_options),
730	    "name \"%s\" ", SVG_name);
731    }
732
733    sprintf(term_options + strlen(term_options),
734	SVG_linecap == ROUNDED ? "rounded " : SVG_linecap == SQUARE ? "square " : "butt ");
735
736    sprintf(term_options + strlen(term_options),
737	    "dashlength %.1f ", SVG_dashlength);
738
739    if (SVG_linewidth_factor != 1.0) {
740	sprintf(term_options + strlen(term_options),
741	    "linewidth %3.1f ", SVG_linewidth_factor);
742    }
743
744    if (SVG_background >= 0) {
745	sprintf(term_options + strlen(term_options),
746	    "background \"#%06x\" ", SVG_background);
747    }
748
749}
750
751static void
752SVG_local_reset()
753{
754    SVG_xSize      = SVG_XMAX;
755    SVG_ySize      = SVG_YMAX;
756    SVG_fixed_size = TRUE;
757    free(SVG_fontNameDef);
758    SVG_fontNameDef = gp_strdup("Arial");
759    SVG_fontSizeDef  = 12;
760    SVG_mouseable = FALSE;
761    SVG_standalone = FALSE;
762    free(SVG_name);
763    SVG_name = NULL;
764    free(SVG_scriptdir);
765    SVG_scriptdir = NULL;
766    SVG_gridline = FALSE;
767    SVG_hasgrid = FALSE;
768    /* Default to enhanced text */
769    term->put_text = ENHsvg_put_text;
770    term->flags |= TERM_ENHANCED_TEXT;
771}
772
773/*------------------------------------------------------------------------------------------------------------------------------------
774	SVG_init
775------------------------------------------------------------------------------------------------------------------------------------*/
776TERM_PUBLIC void
777SVG_init ()
778{
779    /* setup pens*/
780    SVG_pens[0].width = SVG_LineWidth;
781    strcpy (SVG_pens[0].color, "white"); /* should really be background */
782    SVG_pens[1].width = SVG_LineWidth;
783    strcpy(SVG_pens[1].color, "black");
784    SVG_pens[2].width = SVG_LineWidth;
785    strcpy(SVG_pens[2].color, "gray");
786    SVG_pens[3].width = SVG_LineWidth;
787    strcpy(SVG_pens[3].color, "red");
788    SVG_pens[4].width = SVG_LineWidth;
789    strcpy(SVG_pens[4].color, "green");
790    SVG_pens[5].width = SVG_LineWidth;
791    strcpy(SVG_pens[5].color, "blue");
792    SVG_pens[6].width = SVG_LineWidth;
793    strcpy(SVG_pens[6].color, "cyan");
794    SVG_pens[7].width = SVG_LineWidth;
795    sprintf(SVG_pens[7].color, "#%2.2X%2.2X%2.2X", 21, 117, 69); /* pine green*/
796    SVG_pens[8].width = SVG_LineWidth;
797    sprintf (SVG_pens[8].color, "#%2.2X%2.2X%2.2X", 0, 0, 148);	/* navy*/
798    SVG_pens[9].width = SVG_LineWidth;
799    sprintf (SVG_pens[9].color, "#%2.2X%2.2X%2.2X", 255, 153, 0); /* orange*/
800    SVG_pens[10].width = SVG_LineWidth;
801    sprintf (SVG_pens[10].color, "#%2.2X%2.2X%2.2X", 0, 153, 161); /* green blue*/
802    SVG_pens[11].width = SVG_LineWidth;
803    sprintf (SVG_pens[11].color, "#%2.2X%2.2X%2.2X", 214, 214, 69); /* olive*/
804    SVG_pens[12].width = SVG_LineWidth;
805    sprintf (SVG_pens[12].color, "#%2.2X%2.2X%2.2X", 163, 145, 255); /* cornflower*/
806    SVG_pens[13].width = SVG_LineWidth;
807    sprintf (SVG_pens[13].color, "#%2.2X%2.2X%2.2X", 255, 204, 0); /* gold*/
808    SVG_pens[14].width = SVG_LineWidth;
809    sprintf (SVG_pens[14].color, "#%2.2X%2.2X%2.2X", 214, 0, 120); /* mulberry*/
810    SVG_pens[15].width = SVG_LineWidth;
811    sprintf (SVG_pens[15].color, "#%2.2X%2.2X%2.2X", 171, 214, 0); /* green yellow*/
812
813    if (SVG_background >= 0)
814	sprintf(SVG_pens[0].color, "#%2.2X%2.2X%2.2X",
815		(SVG_background >> 16)&0xff,
816		(SVG_background >> 8)&0xff,
817		(SVG_background)&0xff);
818
819    SVG_LineType = LT_NODRAW;
820
821/* set xmax, ymax*/
822
823    term->xmax = SVG_xSize;
824    term->ymax = SVG_ySize;
825
826/* set current font, including h_char and v_char */
827
828    SVG_SetFont (SVG_fontNameCur, SVG_fontSizeCur);
829
830/* set h_tic, v_tic*/
831
832    term->h_tic = term->v_char / 2;
833    term->v_tic = term->v_char / 2;
834}
835
836/* write file header*/
837static void
838SVG_write_preamble ()
839{
840    int len;
841    double stroke_width;
842    char *svg_encoding = "";
843
844    switch (encoding) {
845	case S_ENC_ISO8859_1:	svg_encoding = "encoding=\"iso-8859-1\" "; break;
846	case S_ENC_ISO8859_2:	svg_encoding = "encoding=\"iso-8859-2\" "; break;
847	case S_ENC_ISO8859_9:	svg_encoding = "encoding=\"iso-8859-9\" "; break;
848	case S_ENC_ISO8859_15:	svg_encoding = "encoding=\"iso-8859-15\" "; break;
849	case S_ENC_CP850:	svg_encoding = "encoding=\"ibm-850\" "; break;
850	case S_ENC_CP852:	svg_encoding = "encoding=\"ibm-852\" "; break;
851	case S_ENC_CP950:	svg_encoding = "encoding=\"cp950\" "; break;
852	case S_ENC_CP1250:	svg_encoding = "encoding=\"windows-1250\" "; break;
853	case S_ENC_CP1251:	svg_encoding = "encoding=\"windows-1251\" "; break;
854	case S_ENC_CP1252:	svg_encoding = "encoding=\"windows-1252\" "; break;
855	case S_ENC_KOI8_R:	svg_encoding = "encoding=\"koi8-r\" "; break;
856	case S_ENC_KOI8_U:	svg_encoding = "encoding=\"koi8-u\" "; break;
857	case S_ENC_SJIS:	svg_encoding = "encoding=\"Shift_JIS\" "; break;
858	case S_ENC_CP437:	svg_encoding = ""; break;
859	default: /* UTF-8 */
860				svg_encoding = "encoding=\"utf-8\" ";
861				break;
862    }
863
864    if (SVG_domterm)
865      fprintf(gpoutfile, "\033]72;");
866    if (SVG_emit_doctype)
867        fprintf (gpoutfile,
868                 "<?xml version=\"1.0\" %s standalone=\"no\"?>\n",
869                 svg_encoding);
870    fprintf(gpoutfile, "<svg ");
871
872    if (SVG_mouseable)
873	fprintf (gpoutfile, " onload=\"if (typeof(gnuplot_svg)!='undefined') gnuplot_svg.Init(evt)\" ");
874
875    if (SVG_fixed_size)
876	fprintf (gpoutfile, "\n width=\"%u\" height=\"%u\"",
877		 (unsigned int) (term->xmax / SVG_SCALE),
878		 (unsigned int) (term->ymax / SVG_SCALE));
879
880    fprintf (gpoutfile, "\n viewBox=\"0 0 %u %u\"\n",
881	     (unsigned int) (term->xmax / SVG_SCALE),
882	     (unsigned int) (term->ymax / SVG_SCALE));
883    fprintf (gpoutfile, " xmlns=\"http://www.w3.org/2000/svg\"\n");
884    fprintf (gpoutfile, " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
885#if (0)
886    /* This should be required, but Firefox gets it totally wrong */
887    fprintf (gpoutfile, " xml:space=\"preserve\"\n");
888#endif
889    fprintf (gpoutfile, ">\n\n");
890
891    /* TODO: It would be nice to print the actual plot title here */
892    fprintf (gpoutfile, "<title>%s</title>\n", SVG_name ? SVG_name : "Gnuplot");
893    fprintf (gpoutfile,
894	     "<desc>Produced by GNUPLOT %s patchlevel %s </desc>\n\n",
895	     gnuplot_version, gnuplot_patchlevel);
896
897/*
898 * FIXME: This code could be shared with canvas.trm
899 *	  Figure out the full URL to use for xlink:href="gnuplot_svg.js"
900 */
901    if (SVG_scriptdir == NULL) {
902#ifdef GNUPLOT_JS_DIR
903# if defined(_WIN32) || defined(MSDOS) || defined(OS2)
904	SVG_scriptdir = RelativePathToGnuplot(GNUPLOT_JS_DIR);
905# else
906	/* use hardcoded _absolute_ path */
907	SVG_scriptdir = gp_strdup(GNUPLOT_JS_DIR);
908# endif
909#else
910	SVG_scriptdir = gp_strdup("");
911#endif /* GNUPLOT_JS_DIR */
912    }
913
914    len = strlen(SVG_scriptdir);
915# if defined(_WIN32) || defined(MSDOS) || defined(OS2)
916    if (*SVG_scriptdir && SVG_scriptdir[len-1] != '\\' && SVG_scriptdir[len-1] != '/') {
917	SVG_scriptdir = gp_realloc(SVG_scriptdir, len+2, "jsdir");
918	if (SVG_scriptdir[len-1] == '\\') /* use backslash if used in jsdir, otherwise slash */
919	    strcat(SVG_scriptdir,"\\");
920	else
921	    strcat(SVG_scriptdir,"/");
922    }
923# else
924    if (*SVG_scriptdir && SVG_scriptdir[len-1] != '/') {
925	SVG_scriptdir = gp_realloc(SVG_scriptdir, len+2, "jsdir");
926	strcat(SVG_scriptdir,"/");
927    }
928# endif
929
930    if (SVG_mouseable) {
931	/* Inclusion of gnuplot_svg.js is sufficient to support toggling plots on/off */
932	if (!SVG_standalone) {
933 	    fprintf(gpoutfile,
934		"<script type=\"text/javascript\" xlink:href=\"%sgnuplot_svg.js\"/>\n",
935		SVG_scriptdir);
936	} else {
937	    /* "standalone" option includes the mousing code in the file itself */
938	    char *fullname = NULL;
939	    char *name ="gnuplot_svg.js";
940	    char buf[256];
941	    FILE *svg_js_fd;
942
943	    fullname = gp_alloc(strlen(SVG_scriptdir) + strlen(name) + 4,"javascript name");
944	    strcpy(fullname, SVG_scriptdir);
945	    PATH_CONCAT(fullname, name);
946
947	    svg_js_fd=fopen(fullname, "r");
948	    if (!svg_js_fd)
949		int_warn(NO_CARET, "Failed to insert javascript file %s\n", fullname);
950 	    else {
951		fprintf(gpoutfile,
952		    "<script type=\"text/javascript\" > <![CDATA[\n");
953		while (fgets(buf, sizeof(buf), svg_js_fd))
954		    fputs(buf, gpoutfile);
955		fprintf(gpoutfile,"]]>\n</script>\n");
956		fclose(svg_js_fd);
957	    }
958	    free(fullname);
959	}
960    }
961
962    if (SVG_mouseable) {   /* FIXME: Should only do this for 2D plots */
963	/* This is extra code to support tracking the mouse coordinates */
964	fprintf(gpoutfile,"\n<!-- Tie mousing to entire bounding box of the plot -->\n");
965	fprintf(gpoutfile,"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"",
966		0, 0, (int)(term->xmax/SVG_SCALE), (int)(term->ymax/SVG_SCALE));
967	fprintf(gpoutfile," fill=\"#%06x\" stroke=\"black\" stroke-width=\"1\"\n",
968		SVG_background >= 0 ? SVG_background : 0xffffff);
969	fprintf(gpoutfile,"onclick=\"gnuplot_svg.toggleCoordBox(evt)\"  onmousemove=\"gnuplot_svg.moveCoordBox(evt)\"/>\n");
970	fprintf(gpoutfile,"\n<!-- Also track mouse when it is on a plot element -->\n");
971	fprintf(gpoutfile,"<g id=\"gnuplot_canvas\" onclick=\"gnuplot_svg.toggleCoordBox(evt)\" onmousemove=\"gnuplot_svg.moveCoordBox(evt)\">\n\n");
972    } else {
973	fprintf(gpoutfile,"<g id=\"gnuplot_canvas\">\n\n");
974	fprintf(gpoutfile,"<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"",
975		0, 0, (int)(term->xmax/SVG_SCALE), (int)(term->ymax/SVG_SCALE));
976	if (SVG_background >= 0)
977		fprintf(gpoutfile," fill=\"#%06x\"", SVG_background);
978	else
979		fprintf(gpoutfile," fill=\"none\"");
980	fprintf(gpoutfile,"/>\n");
981    }
982
983    /* Start prologue section of output file, and load fonts if requested */
984
985    fprintf(gpoutfile,"<defs>\n");
986
987    /* definitions of point symbols */
988    /* FIXME: SVG scales linewidth along with the marker itself, and
989     * there seems to be no way to avoid that without copying the
990     * marker definition into the file, rather than referencing a
991     * defined one :-( That would make for much larger files */
992    /* "\t<path id='gpPt3' stroke-width='%.3f' d='M-1,-1 h2 v2 h-2 z'/>\n" */
993
994    stroke_width = 2.0 *SVG_SCALE / term->h_tic;
995    fprintf (gpoutfile,
996	     "\n"
997	     /* dot: */
998	     "\t<circle id='gpDot' r='0.5' stroke-width='0.5' stroke='currentColor'/>\n"
999	     /*  0 plus */
1000	     "\t<path id='gpPt0' stroke-width='%.3f' stroke='currentColor' d='M-1,0 h2 M0,-1 v2'/>\n"
1001	     /*  1 X */
1002	     "\t<path id='gpPt1' stroke-width='%.3f' stroke='currentColor' d='M-1,-1 L1,1 M1,-1 L-1,1'/>\n"
1003	     /*  2 star */
1004	     "\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"
1005	     /*  3 box */
1006	     "\t<rect id='gpPt3' stroke-width='%.3f' stroke='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
1007	     /*  4 box                   filled */
1008	     "\t<rect id='gpPt4' stroke-width='%.3f' stroke='currentColor' fill='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
1009	     /*  5 circle */
1010	     "\t<circle id='gpPt5' stroke-width='%.3f' stroke='currentColor' cx='0' cy='0' r='1'/>\n"
1011	     /*  6 circle (disk)         filled */
1012	     "\t<use xlink:href='#gpPt5' id='gpPt6' fill='currentColor' stroke='none'/>\n"
1013	     /*  7 triangle */
1014	     "\t<path id='gpPt7' stroke-width='%.3f' stroke='currentColor' d='M0,-1.33 L-1.33,0.67 L1.33,0.67 z'/>\n"
1015	     /*  8 triangle  filled */
1016	     "\t<use xlink:href='#gpPt7' id='gpPt8' fill='currentColor' stroke='none'/>\n"
1017	     /*  9 upside down triangle */
1018	     "\t<use xlink:href='#gpPt7' id='gpPt9' stroke='currentColor' transform='rotate(180)'/>\n"
1019	     /*  10 upside down triangle filled */
1020	     "\t<use xlink:href='#gpPt9' id='gpPt10' fill='currentColor' stroke='none'/>\n"
1021	     /* 11 diamond */
1022	     "\t<use xlink:href='#gpPt3' id='gpPt11' stroke='currentColor' transform='rotate(45)'/>\n"
1023	     /* 12 diamond               filled */
1024	     "\t<use xlink:href='#gpPt11' id='gpPt12' fill='currentColor' stroke='none'/>\n"
1025	     /* 13 pentagon */
1026	     "\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"
1027	     /* 14 pentagon              filled */
1028	     "\t<use xlink:href='#gpPt13' id='gpPt14' fill='currentColor' stroke='none'/>\n"
1029
1030	     /* NOTE: Fill patterns must be defined after the stroke color has been
1031	      * set to use the correct (current) stroke color. Therefore we can't
1032	      * define fill patterns here. */
1033	     "\t<filter id='textbox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>\n"
1034	     "\t  <feFlood flood-color='%s' flood-opacity='1' result='bgnd'/>\n"
1035	     "\t  <feComposite in='SourceGraphic' in2='bgnd' operator='atop'/>\n"
1036	     "\t</filter>\n"
1037
1038	     "\t<filter id='greybox' filterUnits='objectBoundingBox' x='0' y='0' height='1' width='1'>\n"
1039	     "\t  <feFlood flood-color='lightgrey' flood-opacity='1' result='grey'/>\n"
1040	     "\t  <feComposite in='SourceGraphic' in2='grey' operator='atop'/>\n"
1041	     "\t</filter>\n"
1042
1043	     "</defs>\n"
1044	     , stroke_width
1045	     , stroke_width
1046	     , stroke_width
1047	     , stroke_width
1048	     , stroke_width
1049	     , stroke_width
1050	     , stroke_width
1051	     , stroke_width
1052	     , SVG_pens[0].color
1053	);
1054}
1055
1056/*------------------------------------------------------------------------------------------------------------------------------------
1057	SVG_graphics
1058------------------------------------------------------------------------------------------------------------------------------------*/
1059TERM_PUBLIC void
1060SVG_graphics ()
1061{
1062    SVG_write_preamble();
1063
1064    /* We must force a new group with fill:none in order for multiple */
1065    /* plots per page to work. Otherwise new plots are black-filled   */
1066    SVG_GroupOpen();
1067
1068    SVG_fillPattern = -1;
1069    SVG_fillPatternIndex = 0;
1070    SVG_groupFilledIsOpen = FALSE;
1071    SVG_color_mode = TC_DEFAULT;
1072    SVG_pathIsOpen = FALSE;
1073
1074/* reset position*/
1075
1076    SVG_xLast = SVG_yLast = UINT_MAX;
1077}
1078
1079static void
1080svg_mouse_param( char *gp_name, const char *js_name)
1081{
1082    struct udvt_entry *udv;
1083    if ((udv = add_udv_by_name(gp_name))) {
1084	if (udv->udv_value.type == INTGR) {
1085	    fprintf(gpoutfile, "gnuplot_svg.%s = ", js_name);
1086	    fprintf(gpoutfile, PLD, udv->udv_value.v.int_val);
1087	    fprintf(gpoutfile, "\n");
1088	} else if (udv->udv_value.type == CMPLX) {
1089	    fprintf(gpoutfile, "gnuplot_svg.%s = %g;\n",
1090		    js_name, udv->udv_value.v.cmplx_val.real);
1091	}
1092    }
1093}
1094
1095/*------------------------------------------------------------------------------------------------------------------------------------
1096	SVG_text
1097------------------------------------------------------------------------------------------------------------------------------------*/
1098TERM_PUBLIC void
1099SVG_text ()
1100{
1101    SVG_PathClose ();
1102    SVG_GroupClose ();
1103
1104    if (SVG_mouseable) {
1105	struct axis *this_axis;
1106
1107	fprintf(gpoutfile, "\n<script type=\"text/javascript\"><![CDATA[\n");
1108	fprintf(gpoutfile, "// plot boundaries and axis scaling information for mousing \n");
1109	fprintf(gpoutfile, "gnuplot_svg.plot_term_xmax = %d;\n", (int)(term->xmax / SVG_SCALE));
1110	fprintf(gpoutfile, "gnuplot_svg.plot_term_ymax = %d;\n", (int)(term->ymax / SVG_SCALE));
1111	fprintf(gpoutfile, "gnuplot_svg.plot_xmin = %.1f;\n", (double)plot_bounds.xleft / SVG_SCALE);
1112	fprintf(gpoutfile, "gnuplot_svg.plot_xmax = %.1f;\n", (double)plot_bounds.xright / SVG_SCALE);
1113	fprintf(gpoutfile, "gnuplot_svg.plot_ybot = %.1f;\n", (double)(term->ymax-plot_bounds.ybot) / SVG_SCALE);
1114	fprintf(gpoutfile, "gnuplot_svg.plot_ytop = %.1f;\n", (double)(term->ymax-plot_bounds.ytop) / SVG_SCALE);
1115	fprintf(gpoutfile, "gnuplot_svg.plot_width = %.1f;\n", (double)(plot_bounds.xright - plot_bounds.xleft) / SVG_SCALE);
1116	fprintf(gpoutfile, "gnuplot_svg.plot_height = %.1f;\n", (double)(plot_bounds.ytop - plot_bounds.ybot) / SVG_SCALE);
1117
1118	/* Get true axis ranges as used in the plot */
1119	update_gpval_variables(1);
1120
1121#define	MOUSE_PARAM( GP_NAME, js_NAME ) svg_mouse_param(GP_NAME, js_NAME)
1122
1123	if (axis_array[FIRST_X_AXIS].datatype != DT_TIMEDATE) {
1124	    MOUSE_PARAM("GPVAL_X_MIN", "plot_axis_xmin");
1125	    MOUSE_PARAM("GPVAL_X_MAX", "plot_axis_xmax");
1126	}
1127	/* FIXME: Should this inversion be done at a higher level? */
1128	if (is_3d_plot && splot_map) {
1129	    MOUSE_PARAM("GPVAL_Y_MAX", "plot_axis_ymin");
1130	    MOUSE_PARAM("GPVAL_Y_MIN", "plot_axis_ymax");
1131	} else {
1132	    MOUSE_PARAM("GPVAL_Y_MIN", "plot_axis_ymin");
1133	    MOUSE_PARAM("GPVAL_Y_MAX", "plot_axis_ymax");
1134	}
1135
1136	fprintf(gpoutfile, "gnuplot_svg.polar_mode = %s;\n",
1137		polar ? "true" : "false");
1138	if (polar) {
1139	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_rmin = %g;\n",
1140		(R_AXIS.autoscale & AUTOSCALE_MIN) ? 0.0 : R_AXIS.set_min);
1141	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_rmax = %g;\n", R_AXIS.set_max);
1142	    fprintf(gpoutfile, "gnuplot_svg.polar_theta0 = %d;\n", (int)theta_origin);
1143	    fprintf(gpoutfile, "gnuplot_svg.polar_sense = %d;\n", (int)theta_direction);
1144	}
1145
1146	if ((axis_array[SECOND_X_AXIS].ticmode & TICS_MASK) != NO_TICS) {
1147	    MOUSE_PARAM("GPVAL_X2_MIN", "plot_axis_x2min");
1148	    MOUSE_PARAM("GPVAL_X2_MAX", "plot_axis_x2max");
1149	} else
1150	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_x2min = \"none\";\n");
1151	if ((axis_array[SECOND_Y_AXIS].ticmode & TICS_MASK) != NO_TICS) {
1152	    MOUSE_PARAM("GPVAL_Y2_MIN", "plot_axis_y2min");
1153	    MOUSE_PARAM("GPVAL_Y2_MAX", "plot_axis_y2max");
1154	} else
1155	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_y2min = \"none\";\n");
1156#undef MOUSE_PARAM
1157
1158	/*
1159	 * Note:
1160	 * Offline mousing cannot automatically deal with
1161	 * (1) nonlinear axes other than logscale
1162	 * (2) [x,y]->plot_coordinates as specified by mouse_mode 8
1163	 *		'set mouse mouseformat function <foo>'
1164	 *     Both of these states are noted by gnuplot_svg.plot_logaxis_* < 0
1165	 *     Linked axes that happen to be nonlinear are incorrectly treated as linear
1166	 * (3) generic user-specified coordinate format (mouse_alt_string)
1167	 *      	'set mouse mouseformat "foo"'
1168	 *     Special case mouse_alt_string formats recognized by gnuplot_svg.js are
1169	 *     "Time", "Date", and "DateTime".
1170	 * FIXME: This all needs to be documented somewhere!
1171	 */
1172#       define is_nonlinear(axis) ((axis)->linked_to_primary != NULL \
1173                         && (axis)->link_udf->at != NULL \
1174			 && (axis)->index == -((axis)->linked_to_primary->index))
1175
1176	this_axis = &axis_array[FIRST_X_AXIS];
1177	fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_x = %d;\n",
1178		this_axis->log ? 1
1179		: (mouse_mode == MOUSE_COORDINATES_FUNCTION || is_nonlinear(this_axis)) ? -1
1180		: 0);
1181	this_axis = &axis_array[FIRST_Y_AXIS];
1182	fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_y = %d;\n",
1183		this_axis->log ? 1
1184		: (mouse_mode == MOUSE_COORDINATES_FUNCTION || is_nonlinear(this_axis)) ? -1
1185		: 0);
1186	if (polar)
1187	    fprintf(gpoutfile, "gnuplot_svg.plot_logaxis_r = %d;\n",
1188		axis_array[POLAR_AXIS].log ? 1: 0);
1189
1190	if (axis_array[FIRST_X_AXIS].datatype == DT_TIMEDATE) {
1191	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_xmin = %.3f;\n",
1192		axis_array[FIRST_X_AXIS].min);
1193	    fprintf(gpoutfile, "gnuplot_svg.plot_axis_xmax = %.3f;\n",
1194		axis_array[FIRST_X_AXIS].max);
1195	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"%s\";\n",
1196		(mouse_alt_string) ? mouse_alt_string
1197		: (mouse_mode == 4) ? "Date"
1198		: (mouse_mode == 5) ? "Time"
1199		: "DateTime"
1200		);
1201	} else if (axis_array[FIRST_X_AXIS].datatype == DT_DMS) {
1202	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"DMS\";\n");
1203	} else
1204	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_x = \"\";\n");
1205
1206	if (axis_array[FIRST_Y_AXIS].datatype == DT_DMS)
1207	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_y = \"DMS\";\n");
1208	else
1209	    fprintf(gpoutfile, "gnuplot_svg.plot_timeaxis_y = \"\";\n");
1210
1211	/* Hypertext font properties
1212	 * NB: These will apply to all hypertext in the plot
1213	 *     separate font for individual labels would require additional code
1214	 */
1215	fprintf(gpoutfile, "gnuplot_svg.hypertext_fontSize = %.1g;\n", SVG_hypertext_fontSize);
1216	if (SVG_hypertext_fontName)
1217	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontName = \"%s\";\n", SVG_hypertext_fontName);
1218	else
1219	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontName = null;\n");
1220	if (SVG_hypertext_fontStyle)
1221	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontStyle = \"%s\";\n", SVG_hypertext_fontStyle);
1222	else
1223	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontStyle = null;\n");
1224	if (SVG_hypertext_fontWeight)
1225	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontWeight = \"%s\";\n", SVG_hypertext_fontWeight);
1226	else
1227	    fprintf(gpoutfile, "gnuplot_svg.hypertext_fontWeight = null;\n");
1228
1229	fprintf(gpoutfile,"]]>\n</script>\n");
1230    } /* End of section writing out variables for mousing */
1231
1232    /* Close off the group with id=gnuplot_canvas that wraps the entire plot */
1233    fprintf(gpoutfile,"</g>\n");
1234
1235    /* Now create a text element to hold the mouse-tracking text. */
1236    /* It comes _after_ the plot group so that it floats on top.  */
1237    if (SVG_mouseable) {
1238	fprintf(gpoutfile,"\n  <text id=\"coord_text\" text-anchor=\"start\" pointer-events=\"none\"\n");
1239	fprintf(gpoutfile,"  font-size=\"12\" font-family=\"Arial\"\n");
1240	fprintf(gpoutfile,"  visibility=\"hidden\"> </text>\n");
1241    }
1242
1243    /* Add a box and a text element to hold mouseover hypertext */
1244    if (SVG_mouseable) {
1245	fprintf(gpoutfile,"\n  <rect id=\"hypertextbox\" class=\"hypertextbox\" pointer-events=\"none\"\n");
1246	fprintf(gpoutfile,"  fill=\"white\" stroke=\"black\" opacity=\"0.8\"\n");
1247	fprintf(gpoutfile,"  height=\"16\" visibility=\"hidden\" />\n");
1248	fprintf(gpoutfile,"\n  <text id=\"hypertext\" class=\"hypertext\" pointer-events=\"none\"\n");
1249	fprintf(gpoutfile,"  font-size=\"12\" font-family=\"Arial\"\n");
1250	fprintf(gpoutfile,"  visibility=\"hidden\"> </text>\n");
1251    }
1252
1253    /* Add a placeholder for an image linked to mouseover hypertext */
1254    if (SVG_mouseable) {
1255	fprintf(gpoutfile,"\n  <image id=\"hyperimage\" class=\"hyperimage\" pointer-events=\"none\"\n");
1256	fprintf(gpoutfile,"  fill=\"white\" stroke=\"black\" opacity=\"0.8\"\n");
1257	fprintf(gpoutfile,"  height=\"200\" width=\"300\" visibility=\"hidden\" />\n");
1258    }
1259
1260    /* If there were any grid lines in this plot, add a button to toggle them */
1261    if (SVG_mouseable && SVG_hasgrid) {
1262	fprintf(gpoutfile,"\n  <image x='10' y='%d' width='16' height='16' ",
1263		(int)(term->ymax/SVG_SCALE)-26);
1264	fprintf(gpoutfile, "\n    xlink:href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABmJLR0QA/wD/AP+gvaeTAAAAM0lEQVQokWP8//8/AymACc5iZGQkyEDRQCwgyUn///9nhGtgZISy8TBGnTSCnMRIavIGAGPTWfVV7DcfAAAAAElFTkSuQmCC'");
1265	fprintf(gpoutfile, "\n    onclick='gnuplot_svg.toggleGrid();'/>\n");
1266    }
1267
1268    fputs("</svg>\n\n", gpoutfile);
1269
1270    if (SVG_domterm) {
1271        fprintf(gpoutfile, "\007");
1272        fflush(gpoutfile);
1273    }
1274}
1275
1276/*------------------------------------------------------------------------------------------------------------------------------------
1277	SVG_reset
1278------------------------------------------------------------------------------------------------------------------------------------*/
1279TERM_PUBLIC void
1280SVG_reset ()
1281{
1282}
1283
1284/*------------------------------------------------------------------------------------------------------------------------------------
1285	SVG_linetype
1286------------------------------------------------------------------------------------------------------------------------------------*/
1287TERM_PUBLIC void
1288SVG_linetype (int linetype)
1289{
1290    SVG_color_mode = TC_DEFAULT;
1291    if (TRUE || linetype != SVG_LineType) {
1292	SVG_PathClose ();
1293	SVG_GroupClose ();
1294	SVG_LineType = linetype;
1295	SVG_GroupOpen ();
1296    }
1297    if (linetype == LT_AXIS)
1298	SVG_dashpattern = SVG_axis_dashpattern;
1299    if (linetype == LT_SOLID)
1300	SVG_dashpattern = NULL;
1301}
1302
1303/*------------------------------------------------------------------------------------------------------------------------------------
1304	SVG_dashtype
1305------------------------------------------------------------------------------------------------------------------------------------*/
1306TERM_PUBLIC void
1307SVG_dashtype (int type, t_dashtype *custom_dash_type)
1308{
1309    int d, j;
1310    double empirical_scale = 0.50;
1311    /* The dash pattern should depend on the `linewidth` and the terminal options `dashlength` and `linewidth`. */
1312    double dash_scale = SVG_pens[SVG_Pen_RealID (SVG_LineType)].width * SVG_linewidth_factor * SVG_dashlength * empirical_scale;
1313
1314    SVG_dashpattern = NULL;	/* Assume solid line */
1315
1316    switch(type) {
1317
1318	case DASHTYPE_SOLID:
1319	    break;
1320
1321	case DASHTYPE_AXIS:
1322	    /* Currently handled elsewhere via LT_AXIS */
1323	    break;
1324
1325	case DASHTYPE_CUSTOM:
1326	    if (custom_dash_type) {
1327		SVG_dashpattern = SVG_custom_dash_pattern;
1328		*SVG_dashpattern = '\0';
1329		for (j = 0; j < 8 && custom_dash_type->pattern[j] > 0; j++) {
1330		    char *p = &SVG_dashpattern[strlen(SVG_dashpattern)];
1331		    snprintf(p, 8, "%.1f", custom_dash_type->pattern[j] * dash_scale);
1332		    if (j < 7 && custom_dash_type->pattern[j+1])
1333			strcat(p,",");
1334		}
1335	    }
1336	    break;
1337
1338	default:
1339	    /* Fall back to whatever version 4 would have provided */
1340	    d = type % SVG_dashtypes;
1341	    if (d <= 0)
1342		break;
1343
1344	    /* Default dash length and sequence */
1345	    if (dash_scale == 1.0)
1346		SVG_dashpattern = SVG_defaultdashpattern[d];
1347
1348	    /* Dash patterns scaled up by dashlength and linewidth */
1349	    else {
1350		SVG_dashpattern = SVG_custom_dash_pattern;
1351		*SVG_dashpattern = '\0';
1352		j = 0;
1353		do {
1354		    char *p = &SVG_dashpattern[strlen(SVG_dashpattern)];
1355		    snprintf(p, 8, "%.1f", SVG_dasharray[d][j] * dash_scale);
1356		    if (SVG_dasharray[d][++j])
1357			strcat(p,",");
1358		} while (SVG_dasharray[d][j] > 0);
1359	    }
1360	    break;
1361    }
1362}
1363
1364TERM_PUBLIC void
1365SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height)
1366{
1367    gpiPoint corner[4];
1368
1369	corner[0].x = x1;        corner[0].y = y1;
1370	corner[1].x = x1+width;  corner[1].y = y1;
1371	corner[2].x = x1+width;  corner[2].y = y1+height;
1372	corner[3].x = x1;        corner[3].y = y1+height;
1373	corner->style = style;
1374
1375	SVG_filled_polygon(4, corner);
1376}
1377
1378/*------------------------------------------------------------------------------------------------------------------------------------
1379	SVG_linewidth - verificare
1380------------------------------------------------------------------------------------------------------------------------------------*/
1381TERM_PUBLIC void
1382SVG_linewidth (double linewidth)
1383{
1384    if (linewidth != SVG_LineWidth) {
1385	short k;
1386
1387	SVG_LineWidth = linewidth;
1388
1389	for (k = 0; k < 16; k++)
1390	    SVG_pens[k].width = SVG_LineWidth;
1391
1392	SVG_PathClose ();
1393	SVG_GroupClose ();
1394	SVG_GroupOpen ();
1395    }
1396}
1397
1398/*------------------------------------------------------------------------------------------------------------------------------------
1399	SVG_move
1400------------------------------------------------------------------------------------------------------------------------------------*/
1401TERM_PUBLIC void
1402SVG_move (unsigned int x, unsigned int y)
1403{
1404    if (x != SVG_xLast || y != SVG_yLast)  {
1405	SVG_MoveForced(x, y);
1406    }
1407}
1408
1409/*------------------------------------------------------------------------------------------------------------------------------------
1410	SVG_vector
1411------------------------------------------------------------------------------------------------------------------------------------*/
1412TERM_PUBLIC void
1413SVG_vector (unsigned int x, unsigned int y)
1414{
1415    if (x != SVG_xLast || y != SVG_yLast) {
1416
1417	if (!SVG_pathIsOpen) {
1418	    /* The SVG 'path' MUST have a 'moveto' as first command. */
1419	    SVG_MoveForced(SVG_xLast, SVG_yLast);
1420	}
1421
1422	fprintf (gpoutfile, "L%.*f,%.*f", PREC, X(x), PREC, Y(y));
1423	SVG_path_count++;
1424
1425	SVG_AddSpaceOrNewline ();
1426
1427	SVG_xLast = x;
1428	SVG_yLast = y;
1429    }
1430}
1431
1432/*------------------------------------------------------------------------------------------------------------------------------------
1433	SVG_point
1434------------------------------------------------------------------------------------------------------------------------------------*/
1435TERM_PUBLIC void
1436SVG_point (unsigned int x, unsigned int y, int number)
1437{
1438    char color_spec[0x40];
1439    if (SVG_color_mode == TC_RGB) {
1440   	sprintf(color_spec, " color='rgb(%3d, %3d, %3d)'",
1441		SVG_red, SVG_green, SVG_blue);
1442	if (SVG_alpha != 0.0)
1443		sprintf(&color_spec[27], " opacity='%4.2f'", 1.0 - SVG_alpha);
1444    } else if (SVG_color_mode == TC_LT)
1445	sprintf(color_spec, " color='%s'", SVG_linecolor);
1446    else
1447	*color_spec = '\0';
1448
1449    SVG_PathClose ();
1450
1451    if (SVG_hypertext_text) {
1452	fprintf(gpoutfile,"\
1453\t<g onmousemove=\"gnuplot_svg.showHypertext(evt,'%s')\" \
1454onmouseout=\"gnuplot_svg.hideHypertext()\"><title> </title>\n",
1455		SVG_hypertext_text);
1456    }
1457
1458    if (number < 0) {		/* do dot */
1459	fprintf (gpoutfile, "\
1460\t<use xlink:href='#gpDot' x='%.*f' y='%.*f'%s/>\n",
1461		 PREC, X(x), PREC, Y(y), color_spec);
1462    } else {			/* draw a point symbol */
1463	fprintf (gpoutfile, "\
1464\t<use xlink:href='#gpPt%u' transform='translate(%.*f,%.*f) scale(%.2f)'%s/>",
1465		 number % 15, PREC, X(x), PREC, Y(y),
1466		 term_pointsize * term->h_tic / (2 * SVG_SCALE),
1467		 color_spec);
1468    }
1469    SVG_xLast = x;
1470    SVG_yLast = y;
1471
1472    if (SVG_hypertext_text) {
1473	fprintf(gpoutfile,"</g>\n");
1474	free(SVG_hypertext_text);
1475	SVG_hypertext_text = NULL;
1476    } else {
1477	fprintf(gpoutfile,"\n");
1478    }
1479}
1480
1481/*------------------------------------------------------------------------------------------------------------------------------------
1482	SVG_justify_text
1483------------------------------------------------------------------------------------------------------------------------------------*/
1484TERM_PUBLIC int
1485SVG_justify_text (enum JUSTIFY mode)
1486{
1487    SVG_TextJust = mode;
1488    return (TRUE);
1489}
1490
1491/*------------------------------------------------------------------------------------------------------------------------------------
1492	SVG_text_angle
1493------------------------------------------------------------------------------------------------------------------------------------*/
1494TERM_PUBLIC int
1495SVG_text_angle (int ang)
1496{
1497    /* Can only do pure horizontal or vertical */
1498    SVG_TextAngle = ang;
1499    return (TRUE);
1500}
1501
1502/*------------------------------------------------------------------------------------------------------------------------------------
1503	SVG_put_text
1504------------------------------------------------------------------------------------------------------------------------------------*/
1505TERM_PUBLIC void
1506SVG_put_text (unsigned int x, unsigned int y, const char *str)
1507{
1508    char *alignment;
1509    double vertical_offset;
1510    int h = x, v = y;
1511
1512    SVG_PathClose ();
1513
1514/* horizontal justification*/
1515
1516    switch (SVG_TextJust) {
1517    case LEFT:
1518	alignment = "start";
1519	break;
1520    case CENTRE:
1521	alignment = "middle";
1522	break;
1523    case RIGHT:
1524    default:   /* can't happen, just to make gcc happy */
1525	alignment = "end";
1526	break;
1527    }
1528
1529/* vertical justification*/
1530    vertical_offset = (SVG_fontAscent - SVG_fontDescent) / 2.;
1531    h += vertical_offset * sin(SVG_TextAngle * DEG2RAD);
1532    v -= vertical_offset * cos(SVG_TextAngle * DEG2RAD);
1533
1534/* define text position and attributes */
1535
1536    fprintf (gpoutfile, "\t<g transform=\"translate(%.*f,%.*f)", PREC, X(h), PREC, Y(v));
1537    if (SVG_TextAngle)
1538	fprintf (gpoutfile, " rotate(%i)", -SVG_TextAngle);
1539    fprintf (gpoutfile, "\" stroke=\"none\" fill=\"");
1540
1541    if (SVG_color_mode == TC_RGB)
1542	fprintf (gpoutfile, "rgb(%d,%d,%d)", SVG_red, SVG_green, SVG_blue);
1543    else if (SVG_color_mode == TC_LT)
1544	fprintf (gpoutfile, "%s", SVG_linecolor);
1545    else
1546	fprintf (gpoutfile, "%s", SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
1547    fprintf (gpoutfile, "\" font-family=\"%s\" font-size=\"%.2f\" ",
1548	     SVG_fontNameCur, SVG_fontSizeCur * SVG_fontscale);
1549    if (SVG_fontWeightCur && strcmp(SVG_fontWeightCur,"normal"))
1550	    fprintf(gpoutfile, " font-weight=\"%s\" ", SVG_fontWeightCur);
1551    if (SVG_fontStyleCur && strcmp(SVG_fontStyleCur,"normal"))
1552	    fprintf(gpoutfile, " font-style=\"%s\" ", SVG_fontStyleCur);
1553    fprintf(gpoutfile, " text-anchor=\"%s\"", alignment);
1554    if (SVG_inTextBox)
1555	fprintf(gpoutfile, " style='filter:url(#textbox)'");
1556    fprintf(gpoutfile, ">\n");
1557
1558/* output text (unless the enhanced_text processing is in action) */
1559
1560    if (strstr(str,"  "))
1561	fputs ("\t\t<text xml:space=\"preserve\">", gpoutfile);
1562    else
1563	fputs ("\t\t<text>", gpoutfile);
1564
1565    if (!ENHsvg_string_state) {
1566
1567	while (*str) {
1568	    /* Escape SVG reserved characters */
1569	    switch (*str) {
1570	    case '<':
1571		fputs("&lt;", gpoutfile);
1572		break;
1573	    case '&':
1574		if (str[1] == '#' && str[2] == 'x')
1575		    fputc(*str, gpoutfile);
1576		else
1577		    fputs("&amp;", gpoutfile);
1578		break;
1579	    default:
1580		fputc(*str, gpoutfile);
1581		break;
1582	    }
1583
1584	    str++;
1585	}
1586	fputs("</text>\n\t</g>\n", gpoutfile);
1587    }
1588}
1589
1590/*------------------------------------------------------------------------------------------------------------------------------------
1591	SVG_set_font
1592------------------------------------------------------------------------------------------------------------------------------------*/
1593TERM_PUBLIC int
1594SVG_set_font (const char *font)
1595{
1596
1597    if (!font || !(*font)) {
1598	free(SVG_fontNameCur);
1599	SVG_fontNameCur = gp_strdup(SVG_fontNameDef);
1600	SVG_fontSizeCur = SVG_fontSizeDef;
1601	SVG_fontStyleCur = SVG_fontStyleDef;
1602	SVG_fontWeightCur = SVG_fontWeightDef;
1603    } else {
1604	char *bold, *italic;
1605	int sep;
1606
1607	if (!((bold = strstr(font," bold"))))
1608	    bold = strstr(font," Bold");
1609
1610	if (!((italic = strstr(font," italic"))))
1611	    italic = strstr(font," Italic");
1612
1613	sep = strcspn(font,",");
1614	if (sep > 0) {
1615	    free(SVG_fontNameCur);
1616	    SVG_fontNameCur = gp_strdup(font);
1617	    if (italic) {
1618		SVG_fontStyleCur="italic";
1619		SVG_fontNameCur[strlen(font) - strlen(italic)] = NUL;
1620	    } else {
1621		SVG_fontStyleCur="normal";
1622	    }
1623
1624	    if (bold) {
1625        	SVG_fontWeightCur="bold";
1626		SVG_fontNameCur[strlen(font) - strlen(bold)] = NUL;
1627            } else {
1628		SVG_fontWeightCur="normal";
1629	    }
1630	    SVG_fontNameCur[sep] = NUL;
1631	}
1632
1633	if (font[sep] == ',')
1634	    sscanf(font + sep + 1, "%lf", &SVG_fontSizeCur);
1635    }
1636
1637    /* Set other font properties */
1638    SVG_SetFont(SVG_fontNameCur, SVG_fontSizeCur);
1639
1640    return (TRUE);
1641}
1642
1643
1644/*------------------------------------------------------------------------------------------------------------------------------------
1645	SVG_make_palette
1646------------------------------------------------------------------------------------------------------------------------------------*/
1647TERM_PUBLIC int
1648SVG_make_palette(t_sm_palette *palette)
1649{
1650    SVG_GroupFilledClose();
1651    if (palette == NULL) {
1652	/* svg can do continuous colors */
1653	return 0;
1654    }
1655
1656    /* save mapping formulae needed if SMPAL_COLOR_MODE_RGB */
1657    SVG_palette.colorMode = palette->colorMode;
1658    SVG_palette.formulaR = palette->formulaR;
1659    SVG_palette.formulaG = palette->formulaG;
1660    SVG_palette.formulaB = palette->formulaB;
1661    SVG_palette.positive = palette->positive;
1662
1663    return 0;
1664}
1665
1666
1667/*------------------------------------------------------------------------------------------------------------------------------------
1668	SVG_set_color
1669------------------------------------------------------------------------------------------------------------------------------------*/
1670TERM_PUBLIC void
1671SVG_set_color(t_colorspec *colorspec)
1672{
1673    rgb255_color rgb255;
1674    SVG_alpha = 0.0;
1675
1676    if (colorspec->type == TC_LT) {
1677        if (SVG_linecolor != SVG_pens[SVG_Pen_RealID (colorspec->lt)].color) {
1678	    SVG_linecolor = SVG_pens[SVG_Pen_RealID (colorspec->lt)].color;
1679	    SVG_PathClose();
1680	}
1681	SVG_color_mode = TC_LT;
1682	return;
1683    } else if (colorspec->type == TC_FRAC) {
1684	rgb255maxcolors_from_gray( colorspec->value, &rgb255 );
1685    } else if (colorspec->type == TC_RGB) {
1686	rgb255.r = colorspec->lt >> 16 & 0xff;
1687	rgb255.g = colorspec->lt >> 8 & 0xff;
1688	rgb255.b = colorspec->lt & 0xff;
1689	SVG_alpha = (double)(colorspec->lt >> 24 & 0xff) / 255.;
1690    } else {
1691	return;
1692    }
1693
1694    SVG_color_mode = TC_RGB;
1695
1696    if (rgb255.r != SVG_red || rgb255.g != SVG_green || rgb255.b != SVG_blue) {
1697	/* pm3d color has changed. We must start a new path
1698	 * with a different line color. This is necessary when
1699	 * using "linetype palette". */
1700	SVG_PathClose();
1701	SVG_red = rgb255.r;
1702	SVG_green = rgb255.g;
1703	SVG_blue = rgb255.b;
1704    }
1705
1706    return;
1707}
1708
1709/*------------------------------------------------------------------------------------------------------------------------------------
1710	SVG_previous_palette
1711------------------------------------------------------------------------------------------------------------------------------------*/
1712TERM_PUBLIC void
1713SVG_previous_palette()
1714{
1715    SVG_GroupFilledClose();
1716}
1717
1718
1719/*------------------------------------------------------------------------------------------------------------------------------------
1720	SVG_filled_polygon
1721------------------------------------------------------------------------------------------------------------------------------------*/
1722TERM_PUBLIC void
1723SVG_filled_polygon(int points, gpiPoint* corners)
1724{
1725    int i;
1726    int fillpar = corners->style >> 4;
1727    int style = corners->style &= 0xf;
1728
1729    if (style == FS_PATTERN || style == FS_TRANSPARENT_PATTERN) {
1730	/* make sure the pattern is defined (with the current stroke color)
1731	 * must be defined AFTER the current group is opened with the color
1732	 * attribute set, as the patterns use 'currentColor' */
1733	SVG_DefineFillPattern(fillpar);
1734    }
1735
1736    SVG_GroupFilledOpen();
1737    fputs("\t\t<polygon ", gpoutfile);
1738
1739    switch (style) {
1740	case FS_EMPTY: /* fill with background color */
1741	    fprintf(gpoutfile," fill = '%s'", SVG_pens[0].color);
1742	    break;
1743	case FS_SOLID: /* solid fill */
1744	case FS_TRANSPARENT_SOLID:
1745	    SVG_StyleFillColor();
1746	    if (SVG_alpha != 0.0)
1747		fprintf(gpoutfile, " fill-opacity='%4.2f' ", 1.0 - SVG_alpha);
1748	    else if (fillpar >= 0 && fillpar < 100)
1749		fprintf(gpoutfile, " fill-opacity = '%f'", fillpar * 0.01);
1750	    break;
1751	case FS_PATTERN: /* pattern fill */
1752	case FS_TRANSPARENT_PATTERN:
1753	    fprintf(gpoutfile, " fill = 'url(#gpPat%d)'",
1754		    SVG_fillPatternIndex);
1755	    break;
1756	default:
1757	    SVG_StyleFillColor();
1758	    break;
1759    }
1760
1761    fputs(" points = '", gpoutfile);
1762    for (i = 0; i < points; i++)
1763	fprintf(gpoutfile, "%.*f,%.*f%s",
1764		PREC, X(corners[i].x), PREC, Y(corners[i].y),
1765		i % 16 == 15 ? "\n" : " ");
1766    fputs("'/>\n", gpoutfile);
1767}
1768
1769/*------------------------------------------------------------------------------------------------------------------------------------
1770	SVG_layer
1771------------------------------------------------------------------------------------------------------------------------------------*/
1772TERM_PUBLIC void
1773SVG_layer(t_termlayer syncpoint)
1774{
1775    char *name = NULL;
1776    char panel[2] = {'\0','\0'};
1777
1778    /* We must ignore all syncpoints that we don't recognize */
1779    switch (syncpoint) {
1780
1781    	default:
1782		break;
1783
1784	case TERM_LAYER_BEFORE_PLOT:
1785		SVG_PathClose();
1786		SVG_GroupClose();
1787		++SVG_plotno;
1788		name = (SVG_name) ? SVG_name : "gnuplot";
1789		if (multiplot && multiplot_current_panel() < 26)
1790		    panel[0] = 'a' + multiplot_current_panel();
1791
1792		fprintf(gpoutfile, "\t<g id=\"%s_plot_%d%s\" ", name,SVG_plotno,panel);
1793		if (SVG_hypertext_text && *SVG_hypertext_text)
1794		    fprintf(gpoutfile, "><title>%s</title>\n", SVG_hypertext_text);
1795		else
1796		    fprintf(gpoutfile, "><title>%s_plot_%d%s</title>\n", name,SVG_plotno,panel);
1797		free(SVG_hypertext_text);
1798		SVG_hypertext_text = NULL;
1799		SVG_LineType = LT_UNDEFINED; /* Force a new group on next stroke */
1800		break;
1801
1802	case TERM_LAYER_AFTER_PLOT:
1803		SVG_PathClose();
1804		SVG_GroupClose();
1805		fprintf(gpoutfile, "\t</g>\n");
1806		SVG_LineType = LT_UNDEFINED; /* Force a new group on next stroke */
1807		break;
1808
1809	case TERM_LAYER_BEGIN_GRID:
1810		SVG_gridline = TRUE;
1811		SVG_hasgrid = TRUE;
1812		break;
1813
1814	case TERM_LAYER_END_GRID:
1815		SVG_gridline = FALSE;
1816		break;
1817
1818	case TERM_LAYER_BEGIN_KEYSAMPLE:
1819		if (SVG_mouseable) {
1820		    SVG_PathClose();
1821		    SVG_GroupFilledClose();
1822		    name = (SVG_name) ? SVG_name : "gnuplot";
1823		    if (multiplot && multiplot_current_panel() < 26)
1824			panel[0] = 'a' + multiplot_current_panel();
1825
1826		    fprintf(gpoutfile, "\t<g id=\"%s_plot_%d%s_keyentry\" visibility=\"visible\" ",
1827			name,SVG_plotno,panel);
1828		    fprintf(gpoutfile,
1829			"onclick=\"gnuplot_svg.toggleVisibility(evt,'%s_plot_%d%s')\"",
1830			name,SVG_plotno,panel);
1831		    fprintf(gpoutfile, ">\n");
1832		}
1833		break;
1834
1835	case TERM_LAYER_END_KEYSAMPLE:
1836		if (SVG_mouseable) {
1837		    SVG_PathClose();
1838		    SVG_GroupFilledClose();
1839		    fprintf(gpoutfile, "\t</g>\n");
1840		}
1841		break;
1842
1843	case TERM_LAYER_RESET:
1844	case TERM_LAYER_RESET_PLOTNO:
1845		SVG_plotno = 0;
1846		break;
1847    }
1848}
1849
1850/*------------------------------------------------------------------------------------------------------------------------------------
1851	SVG_image
1852------------------------------------------------------------------------------------------------------------------------------------*/
1853#ifdef WRITE_PNG_IMAGE
1854TERM_PUBLIC void
1855SVG_image (unsigned m, unsigned n, coordval *image, gpiPoint *corner, t_imagecolor color_mode)
1856{
1857    SVG_PathClose();
1858
1859    /* Map image onto the terminal's coordinate system. */
1860    fprintf(gpoutfile, "<image x='%.*f' y='%.*f' width='%.*f' height='%.*f' preserveAspectRatio='none' ",
1861	    PREC, X(corner[0].x), PREC, Y(corner[0].y),
1862	    PREC, X(corner[1].x) - X(corner[0].x), PREC, Y(corner[1].y) - Y(corner[0].y));
1863
1864    /* Feb 2017 - always embed images */
1865    if (TRUE || SVG_standalone || SVG_domterm) {
1866	/* Embed the PNG file in SVG by converting to base64 */
1867	fprintf(gpoutfile, "xlink:href='data:image/png;base64,");
1868	if (write_png_base64_image(m, n, image, color_mode, gpoutfile))
1869	    os_error(NO_CARET, "SVG_image: could not write to gnuplot output file.");
1870	fprintf(gpoutfile, "'/>\n");
1871
1872    } else {
1873        /* Write the image to a png file */
1874	char *image_file;
1875        char *base_name = SVG_name ? SVG_name : "gp";
1876	int wpiresult;
1877
1878        image_file = gp_alloc(strlen(base_name)+16, "SVG_image");
1879        sprintf(image_file, "%s_image_%02d.png", base_name, ++SVG_imageno);
1880        wpiresult = write_png_image(m, n, image, color_mode, image_file);
1881
1882	/* Reference the png image file */
1883	fprintf(gpoutfile, "xlink:href='%s_image_%02d.png'/>\n",
1884		base_name, SVG_imageno);
1885
1886	free(image_file);
1887
1888	if (wpiresult != 0)
1889	    os_error(NO_CARET, "SVG_image: could not write to PNG reference file.");
1890    }
1891}
1892#endif
1893
1894
1895/* Enhanced text mode support starts here */
1896
1897static double ENHsvg_base = 0.0;
1898static TBOOLEAN ENHsvg_opened_string = FALSE;
1899static int ENHsvg_charcount = 0;
1900
1901TERM_PUBLIC void
1902ENHsvg_OPEN(
1903    char *fontname,
1904    double fontsize, double base,
1905    TBOOLEAN widthflag, TBOOLEAN showflag,
1906    int overprint)
1907{
1908    /* overprint = 1 means print the base text (leave position in center)
1909     * overprint = 2 means print the overlying text
1910     * overprint = 3 means save current position
1911     * overprint = 4 means restore saved position
1912     * EAM FIXME - Unfortunately I can find no way in the svg spec to do this.
1913     * The best I can come up with is to count characters from here and then
1914     * try to back up over them.
1915     */
1916    switch (overprint) {
1917    case 2:
1918	/* FIXME: If there are multiple overprint characters,
1919         *        they all get piled on top of one another.
1920	 */
1921	ENHsvg_FLUSH();
1922	fprintf(gpoutfile, "<tspan dx=\"-%.1fem\" dy=\"%.1fpx\">",
1923		0.5 * ENHsvg_charcount, ENHsvg_base-base);
1924	ENHsvg_base = base;
1925	ENHsvg_x_offset = 0.0;
1926	enhanced_cur_text = enhanced_text;
1927	ENHsvg_charcount = 0;
1928	ENHsvg_opened_string = TRUE;
1929	break;
1930    case 3:
1931	ENHsvg_charcount = 0;
1932	return;
1933    case 4:
1934	/* Defer setting the offsets until the text arrives */
1935	ENHsvg_x_offset = -0.5 * ENHsvg_charcount;
1936	ENHsvg_base -= base;
1937	ENHsvg_charcount = 0;
1938	return;
1939    default:
1940	break;
1941    }
1942
1943    if (!ENHsvg_opened_string) {
1944	ENHsvg_opened_string = TRUE;
1945	enhanced_cur_text = enhanced_text;
1946
1947	/* Start a new textspan fragment */
1948	fputs("<tspan", gpoutfile);
1949	if (!fontname)
1950	    fprintf(stderr,"ENHsvg_OPEN: null fontname\n");
1951	else {
1952	    char *family = strdup(fontname);
1953	    char *sep = strchr(family, ':');
1954	    if (sep)
1955		*sep = '\0';
1956	    if (strcmp(SVG_fontNameCur, family)) {
1957		free(SVG_fontNameCur);
1958		SVG_fontNameCur = family;
1959	    } else {
1960		free(family);
1961	    }
1962	    fprintf(gpoutfile, " font-family=\"%s\" ", SVG_fontNameCur);
1963	    if (strstr(fontname,":Bold"))
1964		fprintf(gpoutfile, " font-weight=\"bold\" ");
1965	    if (strstr(fontname,":Italic"))
1966		fprintf(gpoutfile, " font-style=\"italic\" ");
1967	}
1968	if (SVG_fontSizeCur != fontsize) {
1969	    SVG_fontSizeCur = fontsize;
1970	    fprintf(gpoutfile, " font-size=\"%.1f\"", SVG_fontSizeCur * SVG_fontscale);
1971	}
1972	if (ENHsvg_x_offset != 0) {
1973	    fprintf(gpoutfile, " dx=\"%.2fem\"", ENHsvg_x_offset);
1974	    ENHsvg_x_offset = 0.0;
1975	}
1976	if (ENHsvg_base != base) {
1977	    fprintf(gpoutfile, " dy=\"%.2fpx\"", ENHsvg_base-base);
1978	    ENHsvg_base = base;
1979	}
1980	if (!showflag) {
1981	    fprintf(gpoutfile, " fill=\"none\"");
1982	}
1983	if (ENHsvg_preserve_spaces) {
1984	    fprintf(gpoutfile, " xml:space=\"preserve\"");
1985	}
1986	fputs(">", gpoutfile);
1987    }
1988
1989}
1990
1991TERM_PUBLIC void
1992ENHsvg_FLUSH()
1993{
1994    char *s = enhanced_text;
1995    int i;
1996
1997    if (!ENHsvg_opened_string)
1998	return;
1999
2000    ENHsvg_opened_string = FALSE;
2001    *enhanced_cur_text = '\0';
2002    enhanced_cur_text = enhanced_text;
2003
2004    /* DEBUG - expand unicode escape sequences \U+ABCD into &#xABCD;
2005     * Triggers in two cases that I know of
2006     * 1) encoding is not UTF-8  (probably should not happen for svg)
2007     * 2) one too many backslashes in a single-quoted string
2008     * We can't just substitute &#x for \U+ in place because the
2009     * xml convention requires a trailing semicolon also.
2010     * FIXME: this incorrectly handles the case where a legal hex character
2011     * immediately follows a 4-char hex unicode entry point
2012     * (e.g. the ab ligature in the unicode.dem).
2013     */
2014    while ((s = strstr(enhanced_cur_text, "\\U+")) != NULL) {
2015	*s = '\0';
2016	fputs(enhanced_cur_text, gpoutfile);	/* everything up to the escape */
2017	fputs("&#x", gpoutfile);		/* xml escape sequence */
2018	s += 3;					/* start of hex codepoint */
2019	for (i=0; i<5; i++, s++) {		/* copy up to 5 hex characters */
2020	    if (isxdigit(*s))
2021		fputc(*s, gpoutfile);
2022	    else
2023		break;
2024	}
2025	fputs(";", gpoutfile);			/* end of xml escape sequence */
2026	enhanced_cur_text = s;
2027    }
2028
2029    fputs(enhanced_cur_text, gpoutfile);	/* everything after the escape[s] */
2030    fputs("</tspan>", gpoutfile);
2031}
2032
2033TERM_PUBLIC void
2034ENHsvg_put_text(unsigned int x, unsigned int y, const char *str)
2035{
2036
2037    /* We need local copies of the starting font properties */
2038    double fontsize = SVG_fontSizeCur;
2039    static char *fontname = NULL;
2040
2041    free(fontname);
2042    fontname = gp_strdup(SVG_fontNameCur);
2043
2044    /* We need the full set of tags for text, just as normal. But in */
2045    /* the case of enhanced text ENHsvg_string_state == 1 tells the  */
2046    /* SVG_put_text() to return without actually putting the text.   */
2047    if (ignore_enhanced_text) {
2048	ENHsvg_string_state = 0;
2049	SVG_put_text(x, y, str);
2050	return;
2051    } else {
2052	ENHsvg_string_state = 1;
2053	SVG_put_text(x, y, str);
2054	ENHsvg_string_state = 0;
2055    }
2056
2057    /* EAM FIXME - This is a total hack, to make up for the fact that all  */
2058    /* svg viewers I have tried fail to pick up the xml:space setting from */
2059    /* the environment. So it has to be set all over again for each text   */
2060    /* fragment. Without this, all whitespace is collapsed to a single ' '.*/
2061    if (strstr(str,"  "))
2062	ENHsvg_preserve_spaces = TRUE;
2063
2064    /* Set up global variables needed by enhanced_recursion() */
2065    ENHsvg_charcount = 0;
2066    enhanced_fontscale = 1.0;
2067    strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format));
2068
2069    while (*(str = enhanced_recursion((char *)str, TRUE,
2070			fontname, fontsize, 0.0, TRUE, TRUE, 0))) {
2071	(term->enhanced_flush)();
2072	enh_err_check(str);
2073	if (!*++str)
2074	    break; /* end of string */
2075    }
2076
2077    /* Make sure we leave with the same font properties as on entry */
2078    free(SVG_fontNameCur);
2079    SVG_fontNameCur = fontname;
2080    fontname = NULL;
2081    if (SVG_fontSizeCur != fontsize || ENHsvg_base != 0) {
2082	fprintf(gpoutfile, "<tspan font-size=\"%.1f\" dy=\"%.2f\"></tspan>",
2083		fontsize * SVG_fontscale, ENHsvg_base);
2084	SVG_fontSizeCur = fontsize;
2085	ENHsvg_base = 0;
2086    }
2087    ENHsvg_preserve_spaces = FALSE;
2088
2089    /* Close the text section */
2090    fputs("</text>\n\t</g>\n", gpoutfile);
2091
2092    return;
2093}
2094
2095TERM_PUBLIC void
2096ENHsvg_writec(int c)
2097{
2098    /* Kludge for phantom box accounting */
2099    ENHsvg_charcount++;
2100
2101    /* Escape SVG reserved characters. Are there any besides '<' and '&' ? */
2102    switch (c) {
2103    case '<':
2104		*enhanced_cur_text++ = '&';
2105		*enhanced_cur_text++ = 'l';
2106		*enhanced_cur_text++ = 't';
2107		*enhanced_cur_text++ = ';';
2108		break;
2109    case '&':
2110		*enhanced_cur_text++ = '&';
2111		*enhanced_cur_text++ = 'a';
2112		*enhanced_cur_text++ = 'm';
2113		*enhanced_cur_text++ = 'p';
2114		*enhanced_cur_text++ = ';';
2115		break;
2116    case '\n':
2117		*enhanced_cur_text++ = '\\';
2118		*enhanced_cur_text++ = 'n';
2119		break;
2120    case '\376':
2121		/* This is an illegal UTF-8 byte; we use it to escape the reserved '&' */
2122		if (encoding == S_ENC_DEFAULT) {
2123		    *enhanced_cur_text++ = '&';
2124		    break;
2125		} /* else fall through */
2126    default:
2127		*enhanced_cur_text++ = c;
2128		break;
2129    }
2130
2131    /* Never overflow the output buffer */
2132    if ((enhanced_cur_text - enhanced_text) >= sizeof(enhanced_text)-1)
2133	ENHsvg_FLUSH();
2134}
2135
2136TERM_PUBLIC void
2137SVG_path(int p)
2138{
2139    switch (p) {
2140	case 1: /* Close path */
2141		fputs("Z ", gpoutfile);
2142		SVG_PathClose();
2143		break;
2144	case 0:
2145		break;
2146    }
2147}
2148
2149TERM_PUBLIC void
2150SVG_hypertext( int type, const char *text )
2151{
2152    switch(type) {
2153
2154    case TERM_HYPERTEXT_TOOLTIP:
2155    case TERM_HYPERTEXT_TITLE:
2156	free(SVG_hypertext_text);
2157	if (text) {
2158	    char *buffer = gp_alloc(2+5*strlen(text),"escape");
2159	    enhanced_cur_text = buffer;
2160	    do { ENHsvg_writec(*text); }
2161		while (*text++);
2162	    SVG_hypertext_text = gp_strdup(buffer);
2163	    enhanced_cur_text = NULL;
2164	    free(buffer);
2165	} else {
2166	    SVG_hypertext_text = NULL;
2167	}
2168	break;
2169
2170    case TERM_HYPERTEXT_FONT:
2171	{
2172	int sep;
2173
2174	free(SVG_hypertext_fontName); SVG_hypertext_fontName = NULL;
2175	free(SVG_hypertext_fontStyle); SVG_hypertext_fontStyle = NULL;
2176	free(SVG_hypertext_fontWeight); SVG_hypertext_fontWeight = NULL;
2177	SVG_hypertext_fontSize = 0;
2178	if (!text || !(*text))
2179	    break;
2180
2181	sep = strcspn(text, ",: ");
2182	if (sep > 0) {
2183	    SVG_hypertext_fontName = gp_strdup(text);
2184	    SVG_hypertext_fontName[sep] = '\0';
2185	}
2186	sep = strcspn(text, ",");
2187	if (sep > 0)
2188	    sscanf(text + sep + 1, "%lf", &SVG_hypertext_fontSize);
2189	if (strstr(text, "italic") || strstr(text, "Italic"))
2190	    SVG_hypertext_fontStyle = gp_strdup("italic");
2191	if (strstr(text, "bold") || strstr(text, "Bold"))
2192	    SVG_hypertext_fontWeight = gp_strdup("bold");
2193	}
2194	break;
2195
2196    default:
2197	break;
2198    }
2199}
2200
2201TERM_PUBLIC void
2202SVG_boxed_text(unsigned int x, unsigned int y, int option)
2203{
2204    switch (option) {
2205    case TEXTBOX_INIT:
2206	/* Mark group containing next text item */
2207	SVG_inTextBox = TRUE;
2208	break;
2209    case TEXTBOX_OUTLINE:
2210	/* Stroke the outline of the bounding box (FIXME: how???) */
2211    case TEXTBOX_BACKGROUNDFILL:
2212	/* Close the group, which will trigger application of the filter */
2213	SVG_inTextBox = FALSE;
2214	break;
2215    case TEXTBOX_MARGINS:
2216	/* Adjust the size of the bounding box */
2217	break;
2218    }
2219}
2220
2221#undef Y
2222#undef X
2223#undef PREC
2224
2225#endif /* TERM_BODY */
2226
2227#ifdef TERM_TABLE
2228TERM_TABLE_START (svg_driver)
2229    "svg", "W3C Scalable Vector Graphics",
2230    0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ ,
2231    0 /* vtic */ , 0 /* htic */ ,
2232    SVG_options, SVG_init, SVG_reset, SVG_text, null_scale, SVG_graphics,
2233    SVG_move, SVG_vector, SVG_linetype, SVG_put_text, SVG_text_angle,
2234    SVG_justify_text, SVG_point, do_arrow, SVG_set_font, do_pointsize,
2235    TERM_CAN_DASH | TERM_ALPHA_CHANNEL|TERM_LINEWIDTH,
2236    0 /* suspend */, 0 /* resume */ , SVG_fillbox, SVG_linewidth
2237#ifdef USE_MOUSE
2238   , 0, 0, 0, 0, 0 /* no mouse support for svg */
2239#endif
2240   , SVG_make_palette,
2241   SVG_previous_palette,
2242   SVG_set_color,
2243   SVG_filled_polygon
2244#ifdef WRITE_PNG_IMAGE
2245    , SVG_image
2246#else
2247    , NULL	/* image */
2248#endif
2249   , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec
2250   , SVG_layer	/* layer */
2251   , SVG_path 	/* path */
2252   , SVG_SCALE	/* pixel oversampling scale */
2253   , SVG_hypertext	/* hypertext support */
2254   , SVG_boxed_text	/* boxed text labels */
2255   , NULL		/* modify_plots */
2256   , SVG_dashtype	/* Version 5 dashtype support */
2257TERM_TABLE_END (svg_driver)
2258
2259#undef LAST_TERM
2260#define LAST_TERM svg_driver
2261
2262TERM_TABLE_START (domterm_driver)
2263    "domterm", "DomTerm terminal emulator with embedded SVG",
2264    0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ ,
2265    0 /* vtic */ , 0 /* htic */ ,
2266    SVG_options, SVG_init, SVG_reset, SVG_text, null_scale, SVG_graphics,
2267    SVG_move, SVG_vector, SVG_linetype, SVG_put_text, SVG_text_angle,
2268    SVG_justify_text, SVG_point, do_arrow, SVG_set_font, do_pointsize,
2269    TERM_CAN_DASH | TERM_ALPHA_CHANNEL|TERM_LINEWIDTH,
2270    0 /* suspend */, 0 /* resume */ , SVG_fillbox, SVG_linewidth
2271#ifdef USE_MOUSE
2272   , 0, 0, 0, 0, 0 /* no mouse support for svg */
2273#endif
2274   , SVG_make_palette,
2275   SVG_previous_palette,
2276   SVG_set_color,
2277   SVG_filled_polygon
2278#ifdef WRITE_PNG_IMAGE
2279    , SVG_image
2280#else
2281    , NULL	/* image */
2282#endif
2283   , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec
2284   , SVG_layer	/* layer */
2285   , SVG_path 	/* path */
2286   , SVG_SCALE	/* pixel oversampling scale */
2287   , SVG_hypertext	/* hypertext support */
2288   , SVG_boxed_text	/* boxed text labels */
2289   , NULL		/* modify_plots */
2290   , SVG_dashtype	/* Version 5 dashtype support */
2291TERM_TABLE_END (domterm_driver)
2292
2293#undef LAST_TERM
2294#define LAST_TERM domterm_driver
2295
2296#endif /* TERM_TABLE */
2297#endif /* TERM_PROTO_ONLY */
2298
2299#ifdef TERM_HELP
2300START_HELP(svg)
2301"1 svg",
2302"?commands set terminal svg",
2303"?set terminal svg",
2304"?set term svg",
2305"?terminal svg",
2306"?term svg",
2307"?svg",
2308" This terminal produces files in the W3C Scalable Vector Graphics format.",
2309"",
2310" Syntax:",
2311"       set terminal svg {size <x>,<y> {|fixed|dynamic}}",
2312"                        {mouse} {standalone | jsdir <dirname>}",
2313"                        {name <plotname>}",
2314"                        {font \"<fontname>{,<fontsize>}\"} {{no}enhanced}",
2315"                        {fontscale <multiplier>}",
2316"                        {rounded|butt|square} {solid|dashed} {linewidth <lw>}",
2317"                        {background <rgb_color>}",
2318"",
2319" where <x> and <y> are the size of the SVG plot to generate,",
2320" `dynamic` allows a svg-viewer to resize plot, whereas the default",
2321" setting, `fixed`, will request an absolute size.",
2322"",
2323" `linewidth <w>` increases the width of all lines used in the figure",
2324" by a factor of <w>.",
2325"",
2326" <font> is the name of the default font to use (default Arial) and",
2327" <fontsize> is the font size (in points, default 12). SVG viewing",
2328" programs may substitute other fonts when the file is displayed.",
2329"",
2330" The enhanced text mode syntax is shared with other gnuplot terminal types.",
2331" See `enhanced` for more details.",
2332"",
2333" The `mouse` option tells gnuplot to add support for mouse tracking and for",
2334" toggling individual plots on/off by clicking on the corresponding key entry.",
2335" By default this is done by including a link that points to a script in a",
2336" local directory, usually /usr/local/share/gnuplot/<version>/js.",
2337" You can change this by using the `jsdir` option to specify either a",
2338" different local directory or a general URL. The latter is usually",
2339" appropriate if you are embedding the svg into a web page.",
2340" Alternatively, the `standalone` option embeds the mousing code in the svg",
2341" document itself rather than linking to an external resource.",
2342"",
2343" When an SVG file will be used in conjunction with external files,",
2344" e.g. if it is referenced by javascript code in a web page or parent document,",
2345" then a unique name is required to avoid potential conflicting references",
2346" to other SVG plots.  Use the `name` option to ensure uniqueness.",
2347""
2348END_HELP(svg)
2349
2350START_HELP(domterm)
2351"1 domterm",
2352"?set terminal domterm",
2353"?terminal domterm",
2354"?set term domterm",
2355"?term domterm",
2356"?domterm",
2357" The `domterm` terminal device runs on the DomTerm terminal emulator",
2358" including the domterm and qtdomterm programs.",
2359" It supports SVG graphics embedded directly in the terminal output.",
2360" See http://domterm.org .",
2361"",
2362" Please read the help for the `svg` terminal.",
2363""
2364END_HELP(domterm)
2365#endif /* TERM_HELP */
2366