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