Initial release of Maemo 5 port of gnuplot
[gnuplot] / term / svg.trm
1 /* Hello, Emacs, this is -*-C-*-
2  * $Id: svg.trm,v 1.63.2.13 2009/01/22 20:27:38 sfeam Exp $
3  */
4
5 /*------------------------------------------------------------------------------------------------------------------------------------
6         GNUPLOT - svg.trm
7
8         This file is included by ../term.c.
9
10         This terminal driver supports:
11                 W3C Scalable Vector Graphics
12
13         AUTHOR
14
15                 Amedeo Farello
16                 afarello@libero.it
17
18         HEAVILY MODIFIED by
19
20                 Hans-Bernhard Br"oker
21                 broeker@physik.rwth-aachen.de
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  *
33  *   Tweaked code for compatibility with Sodipodi svg viewer/editor.
34  *   Added enhanced text support.
35  *   Additional line properties.
36  *   Increase resolution by adding a coordinate scale factor.
37  *   CODDLE_NONCOMPLIANT_VIEWERS
38  *   Support dashed lines, TC_* color model.
39  *   Change path markup from    style='attribute: foo'  to   attribute='foo'
40  *
41  * Ethan Merritt  <merritt@u.washington.edu>
42  */
43
44 #include "driver.h"
45
46 #ifdef TERM_REGISTER
47 register_term(svg)
48 #endif
49
50 #ifdef TERM_PROTO
51 TERM_PUBLIC void SVG_options __PROTO ((void));
52 TERM_PUBLIC void SVG_init __PROTO ((void));
53 TERM_PUBLIC void SVG_graphics __PROTO ((void));
54 TERM_PUBLIC void SVG_text __PROTO ((void));
55 TERM_PUBLIC void SVG_linetype __PROTO ((int linetype));
56 TERM_PUBLIC void SVG_move __PROTO ((unsigned int x, unsigned int y));
57 TERM_PUBLIC void SVG_vector __PROTO ((unsigned int x, unsigned int y));
58 TERM_PUBLIC void SVG_put_text __PROTO ((unsigned int x, unsigned int y, const char *str));
59 TERM_PUBLIC void SVG_reset __PROTO ((void));
60 TERM_PUBLIC int SVG_justify_text __PROTO ((enum JUSTIFY mode));
61 TERM_PUBLIC int SVG_text_angle __PROTO ((int ang));
62 TERM_PUBLIC void SVG_point __PROTO ((unsigned int x, unsigned int y, int pointstyle));
63 TERM_PUBLIC int SVG_set_font __PROTO ((const char *font));
64 /* TERM_PUBLIC void SVG_pointsize __PROTO((double pointsize)); */
65 TERM_PUBLIC void SVG_fillbox __PROTO((int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height));
66 TERM_PUBLIC void SVG_linewidth __PROTO ((double linewidth));
67 TERM_PUBLIC int SVG_make_palette __PROTO((t_sm_palette *));
68 TERM_PUBLIC void SVG_previous_palette __PROTO((void));
69 TERM_PUBLIC void SVG_set_color __PROTO((t_colorspec *));
70 TERM_PUBLIC void SVG_filled_polygon __PROTO((int, gpiPoint *));
71
72 TERM_PUBLIC void ENHsvg_OPEN __PROTO((char *, double, double, TBOOLEAN, TBOOLEAN, int));
73 TERM_PUBLIC void ENHsvg_FLUSH __PROTO((void));
74 TERM_PUBLIC void ENHsvg_put_text __PROTO((unsigned int, unsigned int, const char *));
75 TERM_PUBLIC void ENHsvg_writec __PROTO((int));
76
77 TERM_PUBLIC void SVG_path __PROTO((int p));
78
79 #define SVG_SCALE       10.
80 #define Y(y) ((float)((int)term->ymax - (int)y) / SVG_SCALE)
81 #define X(x) ((float)(x) / SVG_SCALE)
82
83 #define SVG_XMAX        (600 * SVG_SCALE)
84 #define SVG_YMAX        (480 * SVG_SCALE)
85
86 #endif /* TERM_PROTO */
87
88 #ifndef TERM_PROTO_ONLY
89 #ifdef TERM_BODY
90
91 static t_sm_palette SVG_palette;
92 static unsigned char SVG_red = 0;
93 static unsigned char SVG_green = 0;
94 static unsigned char SVG_blue = 0;
95 static unsigned char SVG_color_mode = TC_DEFAULT;
96 static char *SVG_linecolor = NULL;
97
98 static TBOOLEAN SVG_groupFilledIsOpen = FALSE; /* open pm3d group flag*/
99
100 struct SVG_PEN
101 {
102     double width;
103     char color[8];
104 };
105
106 static unsigned int SVG_xSize = SVG_XMAX; /* plot horizontal size */
107 static unsigned int SVG_ySize = SVG_YMAX; /* plot vertical size*/
108 static TBOOLEAN SVG_fixed_size = TRUE;  /* make SVG viewer size fixed */
109
110 static unsigned int SVG_xLast = UINT_MAX; /* current pen horizontal position*/
111 static unsigned int SVG_yLast = UINT_MAX;       /* current pen vertical position*/
112
113 static int SVG_LineType = LT_NODRAW;    /* current line type*/
114 static double SVG_LineWidth = 1.0; /* current line width*/
115 static double SVG_linewidth_factor = 1.0; /* Multiplier for linewidths */
116 static TBOOLEAN SVG_rounded = FALSE; /* linejoin and linecap */
117 static int SVG_TextAngle = 0;   /* current text orientation*/
118 static enum JUSTIFY SVG_TextJust = LEFT; /* current text justification*/
119
120 /* default text font family: */
121 static char SVG_fontNameDef[MAX_ID_LEN + 1] = "Arial";
122 static double SVG_fontSizeDef = 12;     /* default text size*/
123 /* current text font family: */
124 static char SVG_fontNameCur[MAX_ID_LEN + 1] = "Arial";
125 static double SVG_fontSizeCur = 12;     /* current text size*/
126 static TBOOLEAN SVG_groupIsOpen = FALSE; /* open group flag*/
127 static TBOOLEAN SVG_pathIsOpen = FALSE; /* open path flag*/
128 static unsigned int SVG_path_count = 0; /* size of current path*/
129 static struct SVG_PEN SVG_pens[16];     /* pen descriptors*/
130
131 static int SVG_fillPattern = -1; /* active fill pattern (-1 == undefined) */
132 static unsigned int SVG_fillPatternIndex = 0;
133
134 static int SVG_fontAscent = 0;  /* estimated current font ascent*/
135 static int SVG_fontDescent = 0; /* estimated current font descent*/
136 static int SVG_fontLeading = 0; /* estimated current font leading*/
137 static int SVG_fontAvWidth = 0; /* estimated current font char average width*/
138
139 static short SVG_Pen_RealID __PROTO ((int));
140 static void SVG_PathOpen __PROTO ((void));
141 static void SVG_PathClose __PROTO ((void));
142 static void SVG_PathLimit __PROTO ((void));
143 static void SVG_GroupOpen __PROTO ((void));
144 static void SVG_GroupClose __PROTO ((void));
145 static void SVG_SetFont __PROTO ((const char *name, double size));
146 static void SVG_GroupFilledOpen __PROTO ((void));
147 static void SVG_GroupFilledClose __PROTO ((void));
148 static void SVG_StyleColor __PROTO((const char*));
149 static void SVG_StyleFillColor __PROTO((void));
150 static void SVG_local_reset __PROTO((void));
151 static void SVG_DefineFillPattern __PROTO((int fillpat));
152 static void SVG_MoveForced __PROTO((unsigned int x, unsigned int y));
153
154 /* Points to source of requested embedded font */
155 static char *SVG_embedded_font = NULL;
156 static void SVG_load_fontfile __PROTO((char *fontfile));
157
158 /* Stuff for enhanced text mode */
159 static int ENHsvg_string_state = 0;
160 static double ENHsvg_x_offset = 0;
161 static TBOOLEAN ENHsvg_preserve_spaces = FALSE;
162 #define CODDLE_NONCOMPLIANT_VIEWERS 1   /* Use pt rather than em spacing */
163
164 /* Support for dashed lines */
165 #define SVG_dashtypes 5
166 static TBOOLEAN SVG_dashed = FALSE;
167 static char *SVG_dashpattern[SVG_dashtypes] = {
168     "", " 5,8", " 1,4", " 8,4,2,4", " 9,4,1,4,1,4"
169     };
170
171 /*------------------------------------------------------------------------------------------------------------------------------------
172         SVG_Pen_RealID
173 ------------------------------------------------------------------------------------------------------------------------------------*/
174 static short
175 SVG_Pen_RealID (int inPenCode)
176 {
177     if (inPenCode >= 13)
178         inPenCode %= 13;        /* normalize pen code*/
179     inPenCode += 3;
180     if (inPenCode < 0)
181         inPenCode = 0;          /* LT_NODRAW or LT_BACKGROUND should use background color */
182
183     return (inPenCode);
184 }
185
186 /*------------------------------------------------------------------------------------------------------------------------------------
187         SVG_GroupOpen
188 ------------------------------------------------------------------------------------------------------------------------------------*/
189 static void
190 SVG_GroupOpen ()
191 {
192     SVG_GroupFilledClose();
193     if (!SVG_groupIsOpen) {
194
195         fprintf (gpoutfile, "<g style=\"fill:none; color:%s; stroke:",
196                  SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
197
198         if (SVG_color_mode == TC_RGB)
199             fprintf(gpoutfile, "rgb(%3d, %3d, %3d)", SVG_red, SVG_green, SVG_blue);
200         else if (SVG_color_mode == TC_LT)
201             fprintf(gpoutfile, "%s", SVG_linecolor);
202         else
203             fprintf(gpoutfile, "currentColor");
204
205         fprintf (gpoutfile, "; stroke-width:%.2f; stroke-linecap:%s; stroke-linejoin:%s",
206                  SVG_pens[SVG_Pen_RealID (SVG_LineType)].width * SVG_linewidth_factor,
207                  SVG_rounded ? "round" : "butt",
208                  SVG_rounded ? "round" : "miter");
209
210         fprintf (gpoutfile, "\">\n");
211
212         SVG_groupIsOpen = TRUE;
213     }
214 }
215
216 /*------------------------------------------------------------------------------------------------------------------------------------
217         SVG_GroupClose
218 ------------------------------------------------------------------------------------------------------------------------------------*/
219 static void
220 SVG_GroupClose ()
221 {
222     SVG_GroupFilledClose();
223     if (SVG_groupIsOpen) {
224           fputs ("</g>\n", gpoutfile);
225           SVG_groupIsOpen = FALSE;
226           SVG_fillPattern = -1;
227       }
228 }
229
230 /*------------------------------------------------------------------------------------------------------------------------------------
231         SVG_PathOpen
232 ------------------------------------------------------------------------------------------------------------------------------------*/
233 static void
234 SVG_PathOpen ()
235 {
236     if (!SVG_pathIsOpen) {
237         SVG_GroupFilledClose();
238             
239         fputs ("\t<path ", gpoutfile);
240
241         /* Line color */
242         if (SVG_color_mode == TC_RGB)
243             fprintf(gpoutfile, "stroke='rgb(%3d, %3d, %3d)' ",
244                     SVG_red, SVG_green, SVG_blue);
245         else if (SVG_color_mode == TC_LT)
246             fprintf(gpoutfile, "stroke='%s' ", SVG_linecolor);
247         
248         /* Dash patterns */
249         if (SVG_dashed && SVG_LineType % SVG_dashtypes > 0)
250             fprintf(gpoutfile, "stroke-dasharray='%s' ",
251                     SVG_dashpattern[SVG_LineType % SVG_dashtypes]);
252         
253         fputs (" d='", gpoutfile);
254
255         SVG_pathIsOpen = TRUE;
256     }
257 }
258
259 /*------------------------------------------------------------------------------------------------------------------------------------
260         SVG_PathClose
261 ------------------------------------------------------------------------------------------------------------------------------------*/
262 static void
263 SVG_PathClose ()
264 {
265     if (SVG_pathIsOpen) {
266         SVG_GroupFilledClose();
267         fputs ("'></path>\n", gpoutfile);
268         SVG_path_count = 0;
269         SVG_pathIsOpen = FALSE;
270     }
271 }
272
273 /*------------------------------------------------------------------------------------------------------------------------------------
274         SVG_PathLimit
275 ------------------------------------------------------------------------------------------------------------------------------------*/
276 static void
277 SVG_PathLimit ()
278 {
279     if (SVG_path_count % 8 == 0)        /* avoid excessive line length*/
280         fputs ("\n\t\t", gpoutfile);
281 }
282
283 /*------------------------------------------------------------------------------------------------------------------------------------
284         SVG_SetFont
285 ------------------------------------------------------------------------------------------------------------------------------------*/
286 static void
287 SVG_SetFont (const char *name, double size)
288 {
289     if (name && name != SVG_fontNameCur)
290         strncpy (SVG_fontNameCur, name, sizeof(SVG_fontNameCur)-1);
291     SVG_fontSizeCur = size;
292
293 /* since we cannot interrogate SVG about text properties and according
294  * to SVG 1.0 W3C Candidate Recommendation 2 August 2000 the
295  * "line-height" of the 'text' element is defined to be equal to the
296  * 'font-size' (!), we have to to define font properties in a less
297  * than optimal way */
298
299     SVG_fontAscent = (int) (SVG_fontSizeCur * 1.00 * SVG_SCALE); /* estimated current font ascent*/
300     SVG_fontDescent = (int) (SVG_fontSizeCur * 0.25 * SVG_SCALE); /* estimated current font descent*/
301     SVG_fontLeading = (int) (SVG_fontSizeCur * 0.25 * SVG_SCALE); /* estimated current font leading*/
302     SVG_fontAvWidth = (int) (SVG_fontSizeCur * 0.70 * SVG_SCALE); /* estimated current font char average width*/
303 }
304
305 static void
306 SVG_GroupFilledOpen()
307 {
308     if (!SVG_groupFilledIsOpen) {
309         SVG_PathClose();
310         fputs("\t<g style = 'stroke:none; shape-rendering:crispEdges'>\n",
311               gpoutfile);
312         SVG_groupFilledIsOpen = TRUE;
313     }
314 }
315
316 static void
317 SVG_GroupFilledClose()
318 {
319     if (SVG_groupFilledIsOpen) {
320         fputs("\t</g>\n", gpoutfile);
321         SVG_groupFilledIsOpen = FALSE;
322     }
323 }
324
325 static void
326 SVG_StyleColor(const char* paint)
327 {
328     if (SVG_color_mode == TC_RGB)
329         fprintf(gpoutfile, "%s = 'rgb(%3d, %3d, %3d)'", paint, SVG_red, SVG_green, SVG_blue);
330     else if (SVG_color_mode == TC_LT)
331         fprintf(gpoutfile, "%s = '%s'", paint, SVG_linecolor);
332     else
333         fprintf(gpoutfile, "%s = 'currentColor'", paint);
334 }
335
336 static void
337 SVG_StyleFillColor()
338 {
339     SVG_StyleColor("fill");
340 }
341
342 static void
343 SVG_DefineFillPattern(int fillpat)
344 {
345     char *path;
346     char *style="stroke";
347
348     fillpat %= 8;
349     if (fillpat != SVG_fillPattern) {
350         SVG_fillPattern = fillpat;
351         SVG_PathClose();
352         SVG_fillPatternIndex++;
353
354         fprintf(gpoutfile,
355             "\t<defs>\n"
356             "\t\t<pattern id='gpPat%d' patternUnits='userSpaceOnUse' x='0' y='0' width='8' height='8'>\n",
357             SVG_fillPatternIndex);
358         switch (fillpat) {
359             default:
360             case 0:
361                     path="";
362                     break;
363             case 1:
364                     path="M0,0 L8,8 M0,8 L8,0";
365                     break;
366             case 2:
367                     path="M0,0 L8,8 M0,8 L8,0 M0,4 L4,8 L8,4 L4,0 L0,4";
368                     break;
369             case 3:
370                     path="M0,0 L0,8 L8,8 L8,0 L0,0";
371                     style="fill";
372                     break;
373             case 4:
374                     path="M-4,0 L8,12 M0,-4 L12,8";
375                     break;
376             case 5:
377                     path="M-4,8 L8,-4 M0,12 L12,0";
378                     break;
379             case 6:
380                     path="M-2,8 L4,-4 M0,12 L8,-4 M4,12 L10,0";
381                     break;
382             case 7:
383                     path="M-2,0 L4,12 M0,-4 L8,12 M4,-4 L10,8";
384                     break;
385         }
386         if (*path) {
387             if (SVG_color_mode == TC_RGB)
388                 fprintf(gpoutfile,"\t\t\t<path style='fill:none; %s:rgb(%d,%d,%d)' d='%s'/>\n",
389                         style, SVG_red, SVG_green, SVG_blue, path);
390             else if (SVG_color_mode == TC_LT)
391                 fprintf(gpoutfile, "\t\t\t<path style = '%s:%s' d= '%s'/>\n",
392                         style, SVG_linecolor, path);
393             else
394                 fprintf(gpoutfile, "\t\t\t<path style = '%s:currentColor' d='%s'/>\n",
395                         style, path);
396         }
397         fputs("\t\t</pattern>\n" "\t</defs>\n", gpoutfile);
398     }
399 }
400
401 static void
402 SVG_MoveForced(unsigned int x, unsigned int y)
403 {
404     if (SVG_path_count > 512)
405         SVG_PathClose();
406
407     SVG_PathOpen ();
408
409     fprintf (gpoutfile, "M%.1f,%.1f ", X(x), Y(y));
410     SVG_path_count++;
411
412     SVG_PathLimit ();
413
414     SVG_xLast = x;
415     SVG_yLast = y;
416 }
417
418 /*------------------------------------------------------------------------------------------------------------------------------------
419         SVG_options
420 ------------------------------------------------------------------------------------------------------------------------------------*/
421 TERM_PUBLIC void
422 SVG_options ()
423 {
424     struct value a;
425
426     /* Annoying hack to handle the case of 'set termoption' after */
427     /* we have already initialized the terminal settings.         */
428     if (c_token != 2)
429         SVG_local_reset();
430
431     while (!END_OF_COMMAND) {
432         if (almost_equals(c_token, "s$ize")) {
433             double value;
434
435             c_token++;
436
437             if (END_OF_COMMAND)
438                 int_error(c_token,"expecting x size");
439             value = real(const_express(&a));
440             if (value < 2 || value > 8192)
441                 int_error(c_token,"x size out of range");
442             SVG_xSize = value * SVG_SCALE;
443
444             if (equals(c_token,","))
445                 c_token++;
446             if (END_OF_COMMAND)
447                 int_error(c_token,"expecting y size");
448             value = real(const_express(&a));
449             if (value < 2 || value > 8192)
450                 int_error(c_token,"y size out of range");
451             SVG_ySize = value * SVG_SCALE;
452             continue;
453         }
454
455         if (almost_equals(c_token, "d$ynamic")) {
456             c_token++;
457             SVG_fixed_size = FALSE;
458             continue;
459         }
460
461         if (almost_equals(c_token, "fi$xed")){
462             c_token++;
463             SVG_fixed_size = TRUE;
464             continue;
465         }
466
467         if (almost_equals(c_token, "enh$anced")) {
468             c_token++;
469             term->put_text = ENHsvg_put_text;
470             term->flags |= TERM_ENHANCED_TEXT;
471             continue;
472         }
473
474         if (almost_equals(c_token, "noenh$anced")) {
475             c_token++;
476             term->put_text = SVG_put_text;
477             term->flags &= ~TERM_ENHANCED_TEXT;
478             continue;
479         }
480
481         if (almost_equals(c_token, "fn$ame") || almost_equals(c_token, "font"))  {
482             char *s, *comma;
483             c_token++;
484
485             if (!(s = try_to_get_string()))
486                 int_error(c_token,"expecting font name");
487             comma = strrchr(s,',');
488             if (comma && (1 == sscanf(comma + 1, "%lf", &SVG_fontSizeDef)))
489                 *comma = '\0';
490             if (*s)
491                 strncpy(SVG_fontNameDef, s, sizeof(SVG_fontNameDef));
492             free(s);
493             continue;
494         }
495
496         if (almost_equals(c_token, "fs$ize")) {
497             c_token++;
498
499             if (END_OF_COMMAND)
500                 int_error(c_token,"fsize: expecting font size");
501             SVG_fontSizeDef = real(const_express(&a));
502             continue;
503         }
504
505         if (almost_equals(c_token, "fontfile")) {
506             char *fontfile_name;
507             c_token++;
508
509             fontfile_name = try_to_get_string();
510             if (!fontfile_name)
511                 int_error(c_token, "Font filename expected");
512             gp_expand_tilde(&fontfile_name);
513 #if defined(PIPES)
514             if ( *fontfile_name == '<' ) {
515                 SVG_embedded_font = fontfile_name;
516             } else
517 #endif
518                 SVG_embedded_font = fontpath_fullname(fontfile_name);
519             if (!SVG_embedded_font)
520                 int_error(c_token, "Font file '%s' not found", fontfile_name);
521
522             continue;
523         }
524
525         if (almost_equals(c_token, "linew$idth") || equals(c_token, "lw")) {
526             c_token++;
527             SVG_linewidth_factor = real(const_express(&a));
528             if (SVG_linewidth_factor <= 0.0)
529                 SVG_linewidth_factor = 1.0;
530             continue;
531         }
532                                                                         
533         if (almost_equals (c_token, "round$ed")) {
534             c_token++;
535             SVG_rounded = TRUE;
536             continue;
537         }
538                                                                         
539         if (equals (c_token, "butt")) {
540             c_token++;
541             SVG_rounded = FALSE;
542             continue;
543         }
544
545         if (equals(c_token, "solid")) {
546             c_token++;
547             SVG_dashed = FALSE;
548             continue;
549         }
550                                                                         
551         if (almost_equals(c_token, "dash$ed")) {
552             c_token++;
553             SVG_dashed = TRUE;
554             continue;
555         }
556                                                                         
557         int_error(c_token, "unrecognized terminal option");
558     }
559
560     /* I don't think any error checks on font name are possible; just set it */
561     SVG_set_font("");
562
563     /* Save options back into options string in normalized format */
564     sprintf(term_options, "size %d,%d%s %s fname '%s'  fsize %g ",
565             (int)(SVG_xSize/SVG_SCALE), (int)(SVG_ySize/SVG_SCALE),
566         SVG_fixed_size ? " fixed": " dynamic",
567         term->put_text == ENHsvg_put_text ? "enhanced" : "",
568         SVG_fontNameCur, SVG_fontSizeCur);
569
570     if (SVG_embedded_font) {
571         sprintf(term_options + strlen(term_options),
572             "fontfile \"%s\" ", SVG_embedded_font);
573     }
574
575     sprintf(term_options + strlen(term_options),
576         SVG_rounded ? "rounded " : "butt ");
577
578     sprintf(term_options + strlen(term_options),
579         SVG_dashed ? "dashed " : "solid ");
580
581     if (SVG_linewidth_factor != 1.0) {
582         sprintf(term_options + strlen(term_options),
583             "linewidth %3.1f ", SVG_linewidth_factor);
584     }
585
586 }
587
588 static void
589 SVG_local_reset()
590 {
591     SVG_xSize      = SVG_XMAX;
592     SVG_ySize      = SVG_YMAX;
593     SVG_fixed_size = TRUE;
594     strcpy(SVG_fontNameDef,"Arial");
595     SVG_fontSizeDef  = 12;
596     if (SVG_embedded_font)
597         free(SVG_embedded_font);
598     SVG_embedded_font = NULL;
599 }
600
601 /*------------------------------------------------------------------------------------------------------------------------------------
602         SVG_init
603 ------------------------------------------------------------------------------------------------------------------------------------*/
604 TERM_PUBLIC void
605 SVG_init ()
606 {
607     double stroke_width;
608     char *svg_encoding = "";
609
610     /* setup pens*/
611     SVG_pens[0].width = SVG_LineWidth;
612     strcpy (SVG_pens[0].color, "white"); /* should really be background */
613     SVG_pens[1].width = SVG_LineWidth;
614     strcpy(SVG_pens[1].color, "black");
615     SVG_pens[2].width = SVG_LineWidth;
616     strcpy(SVG_pens[2].color, "gray");
617     SVG_pens[3].width = SVG_LineWidth;
618     strcpy(SVG_pens[3].color, "red");
619     SVG_pens[4].width = SVG_LineWidth;
620     strcpy(SVG_pens[4].color, "green");
621     SVG_pens[5].width = SVG_LineWidth;
622     strcpy(SVG_pens[5].color, "blue");
623     SVG_pens[6].width = SVG_LineWidth;
624     strcpy(SVG_pens[6].color, "cyan");
625     SVG_pens[7].width = SVG_LineWidth;
626     sprintf(SVG_pens[7].color, "#%2.2X%2.2X%2.2X", 21, 117, 69); /* pine green*/
627     SVG_pens[8].width = SVG_LineWidth;
628     sprintf (SVG_pens[8].color, "#%2.2X%2.2X%2.2X", 0, 0, 148); /* navy*/
629     SVG_pens[9].width = SVG_LineWidth;
630     sprintf (SVG_pens[9].color, "#%2.2X%2.2X%2.2X", 255, 153, 0); /* orange*/
631     SVG_pens[10].width = SVG_LineWidth;
632     sprintf (SVG_pens[10].color, "#%2.2X%2.2X%2.2X", 0, 153, 161); /* green blue*/
633     SVG_pens[11].width = SVG_LineWidth;
634     sprintf (SVG_pens[11].color, "#%2.2X%2.2X%2.2X", 214, 214, 69); /* olive*/
635     SVG_pens[12].width = SVG_LineWidth;
636     sprintf (SVG_pens[12].color, "#%2.2X%2.2X%2.2X", 163, 145, 255); /* cornflower*/
637     SVG_pens[13].width = SVG_LineWidth;
638     sprintf (SVG_pens[13].color, "#%2.2X%2.2X%2.2X", 255, 204, 0); /* gold*/
639     SVG_pens[14].width = SVG_LineWidth;
640     sprintf (SVG_pens[14].color, "#%2.2X%2.2X%2.2X", 214, 0, 120); /* mulberry*/
641     SVG_pens[15].width = SVG_LineWidth;
642     sprintf (SVG_pens[15].color, "#%2.2X%2.2X%2.2X", 171, 214, 0); /* green yellow*/
643
644     SVG_LineType = LT_NODRAW;
645
646 /* set xmax, ymax*/
647
648     term->xmax = SVG_xSize;
649     term->ymax = SVG_ySize;
650
651 /* set current font*/
652
653     SVG_SetFont (SVG_fontNameCur, SVG_fontSizeCur);
654
655 /* set h_char, v_char*/
656
657     term->h_char = SVG_fontAvWidth;
658     term->v_char = (SVG_fontAscent + SVG_fontDescent + SVG_fontLeading);
659
660 /* set h_tic, v_tic*/
661
662     term->h_tic = term->v_char / 2;
663     term->v_tic = term->v_char / 2;
664
665 /* write file header*/
666
667     switch (encoding) {
668         case S_ENC_ISO8859_1:   svg_encoding = "encoding=\"iso-8859-1\" "; break;
669         case S_ENC_ISO8859_2:   svg_encoding = "encoding=\"iso-8859-2\" "; break;
670         case S_ENC_ISO8859_15:  svg_encoding = "encoding=\"iso-8859-15\" "; break;
671         case S_ENC_CP850:       svg_encoding = "encoding=\"ibm-850\" "; break;
672         case S_ENC_CP852:       svg_encoding = "encoding=\"ibm-852\" "; break;
673         case S_ENC_CP1250:      svg_encoding = "encoding=\"windows-1250\" "; break;
674         case S_ENC_KOI8_R:      svg_encoding = "encoding=\"koi8-r\" "; break;
675         case S_ENC_KOI8_U:      svg_encoding = "encoding=\"koi8-u\" "; break;
676         case S_ENC_CP437:       svg_encoding = ""; break;
677         default: /* UTF-8 */
678                                 svg_encoding = "encoding=\"utf-8\" ";
679                                 break;
680     }
681
682     fprintf (gpoutfile,
683              "<?xml version=\"1.0\" %s standalone=\"no\"?>\n"
684              "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \n"
685              " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
686              "<svg ", svg_encoding);
687
688     if (SVG_fixed_size)
689         fprintf (gpoutfile, "width=\"%u\" height=\"%u\" ",
690                  (unsigned int) (term->xmax / SVG_SCALE),
691                  (unsigned int) (term->ymax / SVG_SCALE));
692
693     fprintf (gpoutfile, "viewBox=\"0 0 %u %u\"\n",
694              (unsigned int) (term->xmax / SVG_SCALE),
695              (unsigned int) (term->ymax / SVG_SCALE));
696     fprintf (gpoutfile, " xmlns=\"http://www.w3.org/2000/svg\"\n");
697     fprintf (gpoutfile, " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n");
698     fprintf (gpoutfile,
699              "<desc>Produced by GNUPLOT %s patchlevel %s </desc>\n\n",
700              gnuplot_version, gnuplot_patchlevel);
701
702     /* Start prologue section of output file, and load fonts if requested */
703     fprintf(gpoutfile,"<defs>\n");
704     if (SVG_embedded_font)
705         SVG_load_fontfile(SVG_embedded_font);
706
707     /* definitions of point symbols */
708     /* FIXME: SVG scales linewidth along with the marker itself, and
709      * there seems to be no way to avoid that without copying the
710      * marker definition into the file, rather than referencing a
711      * defined one :-( That would make for much larger files */
712     /* "\t<path id='gpPt3' stroke-width='%.3f' d='M-1,-1 h2 v2 h-2 z'/>\n" */
713
714     stroke_width = 2.0 *SVG_SCALE / term->h_tic;
715     fprintf (gpoutfile,
716              "\n"
717              /* dot: */
718              "\t<circle id='gpDot' r='0.5' stroke-width='0.5'/>\n"
719              /*  0 plus */
720              "\t<path id='gpPt0' stroke-width='%.3f' stroke='currentColor' d='M-1,0 h2 M0,-1 v2'/>\n"
721              /*  1 X */
722              "\t<path id='gpPt1' stroke-width='%.3f' stroke='currentColor' d='M-1,-1 L1,1 M1,-1 L-1,1'/>\n"
723              /*  2 star */
724              "\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"
725              /*  3 box */
726              "\t<rect id='gpPt3' stroke-width='%.3f' stroke='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
727              /*  4 box                   filled */
728              "\t<rect id='gpPt4' stroke-width='%.3f' stroke='currentColor' fill='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
729              /*  5 circle */
730              "\t<circle id='gpPt5' stroke-width='%.3f' stroke='currentColor' cx='0' cy='0' r='1'/>\n"
731              /*  6 circle (disk)         filled */
732              "\t<use xlink:href='#gpPt5' id='gpPt6' fill='currentColor' stroke='none'/>\n"
733              /*  7 triangle */
734              "\t<path id='gpPt7' stroke-width='%.3f' stroke='currentColor' d='M0,-1.33 L-1.33,0.67 L1.33,0.67 z'/>\n"
735              /*  8 triangle  filled */
736              "\t<use xlink:href='#gpPt7' id='gpPt8' fill='currentColor' stroke='none'/>\n"
737              /*  9 upside down triangle */
738              "\t<use xlink:href='#gpPt7' id='gpPt9' stroke='currentColor' transform='rotate(180)'/>\n"
739              /*  10 upside down triangle filled */
740              "\t<use xlink:href='#gpPt9' id='gpPt10' fill='currentColor' stroke='none'/>\n"
741              /* 11 diamond */
742              "\t<use xlink:href='#gpPt3' id='gpPt11' stroke='currentColor' transform='rotate(45)'/>\n"
743              /* 12 diamond               filled */
744              "\t<use xlink:href='#gpPt11' id='gpPt12' fill='currentColor' stroke='none'/>\n"
745
746              /* NOTE: Fill patterns must be defined after the stroke color has been
747               * set to use the correct (current) stroke color. Therefore we can't
748               * define fill patterns here. */
749
750              "</defs>\n"
751              , stroke_width
752              , stroke_width
753              , stroke_width
754              , stroke_width
755              , stroke_width
756              , stroke_width
757              , stroke_width
758         );
759 }
760
761 /*------------------------------------------------------------------------------------------------------------------------------------
762         SVG_graphics
763 ------------------------------------------------------------------------------------------------------------------------------------*/
764 TERM_PUBLIC void
765 SVG_graphics ()
766 {
767     /* EAM 5-May-2004 We must force a new group with fill:none in order for  */
768     /* multiple plots per page to work. Otherwise new plots are black-filled */
769     SVG_GroupOpen();
770
771     SVG_fillPattern = -1;
772     SVG_fillPatternIndex = 0;
773     SVG_groupFilledIsOpen = FALSE;
774     SVG_color_mode = TC_DEFAULT;
775     SVG_pathIsOpen = FALSE;
776
777 /* reset position*/
778
779     SVG_xLast = SVG_yLast = UINT_MAX;
780 }
781
782 /*------------------------------------------------------------------------------------------------------------------------------------
783         SVG_text
784 ------------------------------------------------------------------------------------------------------------------------------------*/
785 TERM_PUBLIC void
786 SVG_text ()
787 {
788     SVG_PathClose ();
789     SVG_GroupClose ();
790 }
791
792 /*------------------------------------------------------------------------------------------------------------------------------------
793         SVG_reset
794 ------------------------------------------------------------------------------------------------------------------------------------*/
795 TERM_PUBLIC void
796 SVG_reset ()
797 {
798     fputs("</svg>\n\n", gpoutfile);
799 }
800
801 /*------------------------------------------------------------------------------------------------------------------------------------
802         SVG_linetype
803 ------------------------------------------------------------------------------------------------------------------------------------*/
804 TERM_PUBLIC void
805 SVG_linetype (int linetype)
806 {
807     SVG_color_mode = TC_DEFAULT;
808     if (linetype != SVG_LineType) {
809         SVG_PathClose ();
810         SVG_GroupClose ();
811         SVG_LineType = linetype;
812         SVG_GroupOpen ();
813     }
814 }
815
816 TERM_PUBLIC void
817 SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height)
818 {
819     gpiPoint corner[4];
820
821         corner[0].x = x1;        corner[0].y = y1;
822         corner[1].x = x1+width;  corner[1].y = y1;
823         corner[2].x = x1+width;  corner[2].y = y1+height;
824         corner[3].x = x1;        corner[3].y = y1+height;
825         corner->style = style;
826
827         SVG_filled_polygon(4, corner);
828 }
829
830 /*------------------------------------------------------------------------------------------------------------------------------------
831         SVG_linewidth - verificare
832 ------------------------------------------------------------------------------------------------------------------------------------*/
833 TERM_PUBLIC void
834 SVG_linewidth (double linewidth)
835 {
836     if (linewidth != SVG_LineWidth) {
837         short k;
838
839         SVG_LineWidth = linewidth;
840
841         for (k = 0; k < 16; k++)
842             SVG_pens[k].width = SVG_LineWidth;
843
844         SVG_PathClose ();
845         SVG_GroupClose ();
846         SVG_GroupOpen ();
847     }
848 }
849
850 /*------------------------------------------------------------------------------------------------------------------------------------
851         SVG_move
852 ------------------------------------------------------------------------------------------------------------------------------------*/
853 TERM_PUBLIC void
854 SVG_move (unsigned int x, unsigned int y)
855 {
856     if (x != SVG_xLast || y != SVG_yLast)  {
857         SVG_MoveForced(x, y);
858     }
859 }
860
861 /*------------------------------------------------------------------------------------------------------------------------------------
862         SVG_vector
863 ------------------------------------------------------------------------------------------------------------------------------------*/
864 TERM_PUBLIC void
865 SVG_vector (unsigned int x, unsigned int y)
866 {
867     if (x != SVG_xLast || y != SVG_yLast) {
868
869         if (!SVG_pathIsOpen) {
870             /* The SVG 'path' MUST have a 'moveto' as first command. */
871             SVG_MoveForced(SVG_xLast, SVG_yLast);
872         }
873
874         fprintf (gpoutfile, "L%.1f,%.1f ", X(x), Y(y));
875         SVG_path_count++;
876
877         SVG_PathLimit ();
878
879         SVG_xLast = x;
880         SVG_yLast = y;
881     }
882 }
883
884 /*------------------------------------------------------------------------------------------------------------------------------------
885         SVG_point
886 ------------------------------------------------------------------------------------------------------------------------------------*/
887 TERM_PUBLIC void
888 SVG_point (unsigned int x, unsigned int y, int number)
889 {
890     char color_spec[0x40];
891     if (SVG_color_mode == TC_RGB)
892         sprintf(color_spec, " color='rgb(%3d, %3d, %3d)'",
893                 SVG_red, SVG_green, SVG_blue);
894     else if (SVG_color_mode == TC_LT)
895         sprintf(color_spec, " color='%s'", SVG_linecolor);
896     else
897         *color_spec = '\0';
898
899     SVG_PathClose ();
900
901     if (number < 0) {           /* do dot */
902         fprintf (gpoutfile, "\
903 \t<use xlink:href='#gpDot' x='%.1f' y='%.1f'%s/>\n",
904                  X(x), Y(y), color_spec);
905     } else {                    /* draw a point symbol */
906         fprintf (gpoutfile, "\
907 \t<use xlink:href='#gpPt%u' transform='translate(%.1f,%.1f) scale(%.2f)'%s/>\
908 \n",
909                  number % 13, X(x), Y(y),
910                  term_pointsize * term->h_tic / (2 * SVG_SCALE),
911                  color_spec);
912     }
913     SVG_xLast = x;
914     SVG_yLast = y;
915 }
916
917 /*------------------------------------------------------------------------------------------------------------------------------------
918         SVG_justify_text
919 ------------------------------------------------------------------------------------------------------------------------------------*/
920 TERM_PUBLIC int
921 SVG_justify_text (enum JUSTIFY mode)
922 {
923     SVG_TextJust = mode;
924     return (TRUE);
925 }
926
927 /*------------------------------------------------------------------------------------------------------------------------------------
928         SVG_text_angle
929 ------------------------------------------------------------------------------------------------------------------------------------*/
930 TERM_PUBLIC int
931 SVG_text_angle (int ang)
932 {
933     /* Can only do pure horizontal or vertical */
934     SVG_TextAngle = ang;
935     return (TRUE);
936 }
937
938 /*------------------------------------------------------------------------------------------------------------------------------------
939         SVG_put_text
940 ------------------------------------------------------------------------------------------------------------------------------------*/
941 TERM_PUBLIC void
942 SVG_put_text (unsigned int x, unsigned int y, const char *str)
943 {
944     char *alignment;
945     int h = x, v = y;
946
947     SVG_PathClose ();
948
949 /* horizontal justification*/
950
951     switch (SVG_TextJust) {
952     case LEFT:
953         alignment = "start";
954         break;
955     case CENTRE:
956         alignment = "middle";
957         break;
958     case RIGHT:
959     default:   /* can't happen, just to make gcc happy */
960         alignment = "end";
961         break;
962     }
963
964 /* vertical justification*/
965
966     if (SVG_TextAngle % 180) {
967         /* vertical text */
968         h += (SVG_fontAscent - SVG_fontDescent) / 2;
969     } else {
970         /* horizontal text */
971         v -= (SVG_fontAscent - SVG_fontDescent) / 2;
972     }
973
974 /* define text position and attributes */
975
976     fprintf (gpoutfile, "\t<g transform=\"translate(%.1f,%.1f)", X(h), Y(v));
977     if (SVG_TextAngle)
978         fprintf (gpoutfile, " rotate(%i)", -SVG_TextAngle);
979     fprintf (gpoutfile, "\" style=\"stroke:none; fill:");
980
981     if (SVG_color_mode == TC_RGB)
982         fprintf (gpoutfile, "rgb(%d,%d,%d)", SVG_red, SVG_green, SVG_blue);
983     else if (SVG_color_mode == TC_LT)
984         fprintf (gpoutfile, "%s", SVG_linecolor);
985     else
986         fprintf (gpoutfile, "%s", SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
987
988     fprintf (gpoutfile,
989              "; font-family:%s; font-size:%.2fpt; text-anchor:%s\">\n",
990              SVG_fontNameCur, SVG_fontSizeCur, alignment);
991
992 /* output text (unless the enhanced_text processing is in action) */
993
994     if (strstr(str,"  "))
995         fputs ("\t\t<text xml:space=\"preserve\">", gpoutfile);
996     else
997         fputs ("\t\t<text>", gpoutfile);
998
999     if (!ENHsvg_string_state) {
1000
1001         while (*str) {
1002             /* Escape SVG reserved characters */
1003             switch (*str) {
1004             case '<':
1005                 fputs("&lt;", gpoutfile);
1006                 break;
1007             case '&':
1008                 if (str[1] == '#' && str[2] == 'x')
1009                     fputc(*str, gpoutfile);
1010                 else
1011                     fputs("&amp;", gpoutfile);
1012                 break;
1013             default:
1014                 fputc(*str, gpoutfile);
1015                 break;
1016             }
1017
1018             str++;
1019         }
1020         fputs("</text>\n\t</g>\n", gpoutfile);
1021     }
1022 }
1023
1024 /*------------------------------------------------------------------------------------------------------------------------------------
1025         SVG_set_font
1026 ------------------------------------------------------------------------------------------------------------------------------------*/
1027 TERM_PUBLIC int
1028 SVG_set_font (const char *font)
1029 {
1030
1031     if (!font || !(*font)) {
1032         strcpy (SVG_fontNameCur, SVG_fontNameDef);
1033         SVG_fontSizeCur = SVG_fontSizeDef;
1034     } else {
1035         int sep = strcspn(font,",");
1036         if (sep > 0) {
1037             strncpy(SVG_fontNameCur, font, sep);
1038             SVG_fontNameCur[sep] = NUL;
1039         }
1040         if (font[sep] == ',')
1041             sscanf(font + sep + 1, "%lf", &SVG_fontSizeCur);
1042     }
1043
1044     return (TRUE);
1045 }
1046
1047
1048 /*------------------------------------------------------------------------------------------------------------------------------------
1049         SVG_make_palette
1050 ------------------------------------------------------------------------------------------------------------------------------------*/
1051 TERM_PUBLIC int
1052 SVG_make_palette(t_sm_palette *palette)
1053 {
1054     SVG_GroupFilledClose();
1055     if (palette == NULL) {
1056         /* svg can do continuous colors */
1057         return 0;
1058     }
1059
1060     /* save mapping formulae needed if SMPAL_COLOR_MODE_RGB */
1061     SVG_palette.colorMode = palette->colorMode;
1062     SVG_palette.formulaR = palette->formulaR;
1063     SVG_palette.formulaG = palette->formulaG;
1064     SVG_palette.formulaB = palette->formulaB;
1065     SVG_palette.positive = palette->positive;
1066
1067     return 0;
1068 }
1069
1070
1071 /*------------------------------------------------------------------------------------------------------------------------------------
1072         SVG_set_color
1073 ------------------------------------------------------------------------------------------------------------------------------------*/
1074 TERM_PUBLIC void
1075 SVG_set_color(t_colorspec *colorspec)
1076 {
1077     rgb255_color rgb255;
1078
1079     if (colorspec->type == TC_LT) {
1080         SVG_linecolor = SVG_pens[SVG_Pen_RealID (colorspec->lt)].color;
1081         SVG_color_mode = TC_LT;
1082         return;
1083     } else if (colorspec->type == TC_FRAC)
1084         rgb255maxcolors_from_gray( colorspec->value, &rgb255 );
1085     else if (colorspec->type == TC_RGB) {
1086         rgb255.r = colorspec->lt >> 16;
1087         rgb255.g = colorspec->lt >> 8 & 0xff;
1088         rgb255.b = colorspec->lt & 0xff;
1089     } else
1090         return;
1091
1092     SVG_color_mode = TC_RGB;
1093
1094     if (rgb255.r != SVG_red || rgb255.g != SVG_green || rgb255.b != SVG_blue) {
1095         /* pm3d color has changed. We've to start a new path
1096          * with a different line color. This is necessary when
1097          * using "linetype palette". */
1098         SVG_PathClose();
1099         SVG_red = rgb255.r;
1100         SVG_green = rgb255.g;
1101         SVG_blue = rgb255.b;
1102     }
1103
1104     return;
1105 }
1106
1107 /*------------------------------------------------------------------------------------------------------------------------------------
1108         SVG_previous_palette
1109 ------------------------------------------------------------------------------------------------------------------------------------*/
1110 TERM_PUBLIC void
1111 SVG_previous_palette()
1112 {
1113     SVG_GroupFilledClose();
1114 }
1115
1116
1117 /*------------------------------------------------------------------------------------------------------------------------------------
1118         SVG_filled_polygon
1119 ------------------------------------------------------------------------------------------------------------------------------------*/
1120 TERM_PUBLIC void
1121 SVG_filled_polygon(int points, gpiPoint* corners)
1122 {
1123     int i;
1124     int fillpar = corners->style >> 4;
1125     int style = corners->style &= 0xf;
1126
1127     if (style == FS_PATTERN) {
1128         /* make sure the pattern is defined (with the current stroke color)
1129          * must be defined AFTER the current group is opened with the color
1130          * attribute set, as the patterns use 'currentColor' */
1131         SVG_DefineFillPattern(fillpar);
1132     }
1133
1134     SVG_GroupFilledOpen();
1135     fputs("\t\t<polygon ", gpoutfile);
1136
1137     switch (style) {
1138         case FS_EMPTY: /* fill with background color */
1139             /* TODO: and what if the background color is not white ? */
1140             fputs(" fill = 'white'", gpoutfile);
1141             break;
1142         case FS_SOLID: /* solid fill */
1143             SVG_StyleFillColor();
1144             if (fillpar >= 0 && fillpar < 100)
1145                 fprintf(gpoutfile, " fill-opacity = '%f'", fillpar * 0.01);
1146             break;
1147         case FS_PATTERN: /* pattern fill */
1148             fprintf(gpoutfile, " fill = 'url(#gpPat%d)'",
1149                     SVG_fillPatternIndex);
1150             break;
1151         default:
1152             SVG_StyleFillColor();
1153             break;
1154     }
1155
1156     fputs(" points = '", gpoutfile);
1157     for (i = 0; i < points; i++)
1158         fprintf(gpoutfile, "%.1f,%.1f%s",
1159                 X(corners[i].x), Y(corners[i].y),
1160                 i % 16 == 15 ? "\n" : " ");
1161     fputs("'/>\n", gpoutfile);
1162 }
1163
1164 /* Enhanced text mode support starts here */
1165
1166 static double ENHsvg_base = 0.0;
1167 static TBOOLEAN ENHsvg_opened_string = FALSE;
1168 static int ENHsvg_charcount = 0;
1169
1170 TERM_PUBLIC void
1171 ENHsvg_OPEN(
1172     char *fontname,
1173     double fontsize, double base,
1174     TBOOLEAN widthflag, TBOOLEAN showflag,
1175     int overprint)
1176 {
1177     /* overprint = 1 means print the base text (leave position in center)
1178      * overprint = 2 means print the overlying text
1179      * overprint = 3 means save current position
1180      * overprint = 4 means restore saved position
1181      * EAM FIXME - Unfortunately I can find no way in the svg spec to do this.
1182      * The best I can come up with is to count characters from here and then
1183      * try to back up over them.
1184      */
1185     switch (overprint) {
1186     case 2:
1187 #ifdef CODDLE_NONCOMPLIANT_VIEWERS
1188         fprintf(gpoutfile, "<tspan dx=\"-%.1fpt\" dy=\"%.1fpt\">", 
1189                 0.3 * ENHsvg_charcount * 1.1*SVG_fontSizeCur, ENHsvg_base-base);
1190 #else
1191         fprintf(gpoutfile, "<tspan dx=\"-%.1fem\" dy=\"%.1fpt\">", 
1192                 0.3 * ENHsvg_charcount, ENHsvg_base-base);
1193 #endif
1194         ENHsvg_base = base;
1195         ENHsvg_x_offset = 0.0;
1196         enhanced_cur_text = enhanced_text;
1197         ENHsvg_charcount = 0;
1198         ENHsvg_opened_string = TRUE;
1199         break;
1200     case 3:
1201         ENHsvg_charcount = 0;
1202         return;
1203     case 4:
1204         /* Defer setting the offsets until the text arrives */
1205         ENHsvg_x_offset = -0.6 * ENHsvg_charcount;
1206         ENHsvg_base -= base;
1207         ENHsvg_charcount = 0;
1208         return;
1209     default:
1210         break;
1211     }
1212
1213     if (!ENHsvg_opened_string) {
1214         ENHsvg_opened_string = TRUE;
1215         enhanced_cur_text = enhanced_text;
1216
1217         /* Start a new textspan fragment */
1218         fputs("<tspan", gpoutfile);
1219         if (strcmp(SVG_fontNameCur, fontname)) {
1220             strncpy(SVG_fontNameCur, fontname, sizeof(SVG_fontNameCur));
1221             fprintf(gpoutfile, " style=\"font-family:%s\" ", SVG_fontNameCur);
1222         }
1223         if (SVG_fontSizeCur != fontsize) {
1224             SVG_fontSizeCur = fontsize;
1225             fprintf(gpoutfile, " font-size=\"%.1fpt\"", SVG_fontSizeCur);
1226         }
1227         if (ENHsvg_x_offset != 0) {
1228 #ifdef CODDLE_NONCOMPLIANT_VIEWERS
1229             fprintf(gpoutfile, " dx=\"%.2fpt\"",
1230                                 ENHsvg_x_offset * 1.1*SVG_fontSizeCur);
1231 #else
1232             fprintf(gpoutfile, " dx=\"%.2fem\"", ENHsvg_x_offset);
1233 #endif
1234             ENHsvg_x_offset = 0.0;
1235         }
1236         if (ENHsvg_base != base) {
1237             fprintf(gpoutfile, " dy=\"%.2fpt\"", ENHsvg_base-base);
1238             ENHsvg_base = base;
1239         }
1240         if (!showflag) {
1241             fprintf(gpoutfile, " fill=\"none\"");
1242         }
1243         if (ENHsvg_preserve_spaces) {
1244             fprintf(gpoutfile, " xml:space=\"preserve\"");
1245         }
1246         fputs(">", gpoutfile);
1247     }
1248
1249 }
1250
1251 TERM_PUBLIC void
1252 ENHsvg_FLUSH()
1253 {
1254     if (ENHsvg_opened_string) {
1255         ENHsvg_opened_string = FALSE;
1256         *enhanced_cur_text = '\0';
1257         fprintf(gpoutfile, "%s</tspan>\n\t\t", enhanced_text);
1258     }
1259 }
1260
1261 TERM_PUBLIC void
1262 ENHsvg_put_text(unsigned int x, unsigned int y, const char *str)
1263 {
1264
1265     /* We need local copies of the starting font properties */
1266     char fontname[MAX_ID_LEN + 1];
1267     double fontsize = SVG_fontSizeCur;
1268     strncpy(fontname,SVG_fontNameCur,sizeof(fontname));
1269
1270     /* We need the full set of tags for text, just as normal. But in */
1271     /* the case of enhanced text ENHsvg_string_state == 1 tells the  */
1272     /* SVG_put_text() to return without actually putting the text.   */
1273     if (ignore_enhanced_text) {
1274         ENHsvg_string_state = 0;
1275         SVG_put_text(x, y, str);
1276         return;
1277     } else {
1278         ENHsvg_string_state = 1;
1279         SVG_put_text(x, y, str);
1280     }
1281
1282     /* EAM FIXME - This is a total hack, to make up for the fact that all  */
1283     /* svg viewers I have tried fail to pick up the xml:space setting from */
1284     /* the environment. So it has to be set all over again for each text   */
1285     /* fragment. Without this, all whitespace is collapsed to a single ' '.*/
1286     if (strstr(str,"  "))
1287         ENHsvg_preserve_spaces = TRUE;
1288
1289     /* Set up global variables needed by enhanced_recursion() */
1290     ENHsvg_charcount = 0;
1291     enhanced_fontscale = 1.0;
1292     strncpy(enhanced_escape_format,"%c",sizeof(enhanced_escape_format));
1293
1294     while (*(str = enhanced_recursion((char *)str, TRUE,
1295                         fontname, fontsize, 0.0, TRUE, TRUE, 0))) {
1296         (term->enhanced_flush)();
1297         enh_err_check(str);
1298         if (!*++str)
1299             break; /* end of string */
1300     }
1301
1302     /* Make sure we leave with the same font properties as on entry */
1303     strncpy(SVG_fontNameCur,fontname,sizeof(fontname));
1304     if (SVG_fontSizeCur != fontsize || ENHsvg_base != 0) {
1305         fprintf(gpoutfile, "<tspan font-size=\"%.1fpt\" dy=\"%.2fpt\"></tspan>",
1306                 fontsize, ENHsvg_base);
1307         SVG_fontSizeCur = fontsize;
1308         ENHsvg_base = 0;
1309     }
1310     ENHsvg_preserve_spaces = FALSE;
1311
1312     /* Close the text section */
1313     fputs("</text>\n\t</g>\n", gpoutfile);
1314
1315     return;
1316 }
1317
1318 TERM_PUBLIC void
1319 ENHsvg_writec(int c)
1320 {
1321     /* Kludge for phantom box accounting */
1322     ENHsvg_charcount++;
1323
1324     /* Escape SVG reserved characters. Are there any besides '<' and '&' ? */
1325     switch (c) {
1326     case '<':
1327                 *enhanced_cur_text++ = '&';
1328                 *enhanced_cur_text++ = 'l';
1329                 *enhanced_cur_text++ = 't';
1330                 *enhanced_cur_text++ = ';';
1331                 break;
1332     case '&':
1333                 *enhanced_cur_text++ = '&';
1334                 *enhanced_cur_text++ = 'a';
1335                 *enhanced_cur_text++ = 'm';
1336                 *enhanced_cur_text++ = 'p';
1337                 *enhanced_cur_text++ = ';';
1338                 break;
1339     case '\376':
1340                 /* This is an illegal UTF-8 byte; we use it to escape the reserved '&' */
1341                 if (encoding == S_ENC_DEFAULT) {
1342                     *enhanced_cur_text++ = '&';
1343                     break;
1344                 } /* else fall through */
1345     default:
1346                 *enhanced_cur_text++ = c;
1347                 break;
1348     }
1349 }
1350
1351 static void
1352 SVG_load_fontfile(char *fontfile)
1353 {
1354     if (fontfile) {
1355         unsigned int linesread = 0;
1356         FILE *ffont = NULL;
1357         char line[256];
1358         char *fontname = NULL;
1359 #if defined(PIPES)
1360         TBOOLEAN ispipe = FALSE;
1361 #endif
1362
1363 #if defined(PIPES)
1364         if ( *fontfile == '<' ) {
1365             ispipe = TRUE;
1366             ffont = popen(fontfile + 1, "r" );
1367             if ( !ffont )
1368                 int_error(NO_CARET, "Could not execute pipe '%s'",
1369                           fontfile + 1 );
1370         } else
1371 #endif
1372         {
1373             ffont = fopen(fontfile, "r");
1374             if (!ffont)
1375                 int_error(NO_CARET, "Font file '%s' not found", fontfile);
1376         }
1377
1378         /* read the file */
1379         while (fgets(line,255,ffont)) {
1380             /* Echo fontname to terminal */
1381             if ((fontname = strstr(line,"font-family"))) {
1382                 fprintf(stderr, "Font file '%s' contains the font '%s'\n",
1383                                 fontfile, fontname);
1384             }
1385
1386             /* Copy contents into output file */
1387             fputs(line,gpoutfile);
1388
1389             ++linesread;
1390         }
1391 #if defined(PIPES)
1392         if ( ispipe ) {
1393             int exitcode;
1394             if ( (exitcode = pclose(ffont)) != 0 )
1395                 int_error(NO_CARET, "Command '%s' generated error exitcode %d",
1396                           fontfile + 1, exitcode);
1397         } else
1398 #endif
1399             fclose(ffont);
1400
1401         if (linesread == 0) {
1402 #if defined(PIPES)
1403             if ( ispipe )
1404                 int_error(NO_CARET,
1405                     "Command '%s' generates empty output", fontfile + 1);
1406             else
1407 #endif
1408                 int_error(NO_CARET, "Font file '%s' is empty", fontfile);
1409         }
1410
1411     }
1412 }
1413
1414 TERM_PUBLIC void
1415 SVG_path(int p)
1416 {
1417     switch (p) {
1418         case 1: /* Close path */
1419                 fputs("Z ", gpoutfile);
1420                 SVG_PathClose();
1421                 break;
1422         case 0:
1423                 break;
1424     }
1425 }
1426
1427
1428 #undef Y
1429 #undef X
1430 #undef CODDLE_NONCOMPLIANT_VIEWERS
1431
1432 #endif /* TERM_BODY */
1433
1434 #ifdef TERM_TABLE
1435 TERM_TABLE_START (svg_driver)
1436     "svg", "W3C Scalable Vector Graphics driver",
1437     0 /* xmax */ , 0 /* ymax */ , 0 /* vchar */ , 0 /* hchar */ ,
1438     0 /* vtic */ , 0 /* htic */ ,
1439     SVG_options, SVG_init, SVG_reset, SVG_text, null_scale, SVG_graphics,
1440     SVG_move, SVG_vector, SVG_linetype, SVG_put_text, SVG_text_angle,
1441     SVG_justify_text, SVG_point, do_arrow, SVG_set_font, do_pointsize,
1442     TERM_CAN_MULTIPLOT | TERM_BINARY,
1443     0 /* suspend */, 0 /* resume */ , SVG_fillbox, SVG_linewidth
1444 #ifdef USE_MOUSE
1445    , 0, 0, 0, 0, 0 /* no mouse support for svg */
1446 #endif
1447    , SVG_make_palette,
1448    SVG_previous_palette,
1449    SVG_set_color,
1450    SVG_filled_polygon
1451 #ifdef WITH_IMAGE
1452     , NULL
1453 #endif
1454    , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec
1455    , NULL       /* layer */
1456    , SVG_path   /* path */
1457 TERM_TABLE_END (svg_driver)
1458
1459 #undef LAST_TERM
1460 #define LAST_TERM svg_driver
1461
1462 #endif /* TERM_TABLE */
1463 #endif /* TERM_PROTO_ONLY */
1464
1465 #ifdef TERM_HELP
1466 START_HELP(svg)
1467 "1 svg",
1468 "?commands set terminal svg",
1469 "?set terminal svg",
1470 "?set term svg",
1471 "?terminal svg",
1472 "?term svg",
1473 "?svg",
1474 " This terminal produces files in the W3C Scalable Vector Graphics format.",
1475 "",
1476 " Syntax:",
1477 "       set terminal svg {size <x>,<y> {|fixed|dynamic}}",
1478 "                        {{no}enhanced}",
1479 "                        {fname \"<font>\"} {fsize <fontsize>}",
1480 "                        {font \"<fontname>{,<fontsize>}\"}",
1481 "                        {fontfile <filename>}",
1482 "                        {rounded|butt} {solid|dashed} {linewidth <lw>}",
1483 "",
1484 " where <x> and <y> are the size of the SVG plot to generate,",
1485 " `dynamic` allows a svg-viewer to resize plot, whereas the default",
1486 " setting, `fixed`, will request an absolute size.",
1487 "",
1488 " `linewidth <w>` increases the width of all lines used in the figure",
1489 " by a factor of <w>.",
1490 "",
1491 " <font> is the name of the default font to use (default Arial) and",
1492 " <fontsize> is the font size (in points, default 12). SVG viewing",
1493 " programs may substitute other fonts when the file is displayed.",
1494 "",
1495 " The svg terminal supports an enhanced text mode, which allows font",
1496 " and other formatting commands to be embedded in labels and other text",
1497 " strings. The enhanced text mode syntax is shared with other gnuplot",
1498 " terminal types. See `enhanced` for more details.",
1499 "",
1500 " SVG allows you to embed fonts directly into an SVG document, or to",
1501 " provide a hypertext link to the desired font. The `fontfile` option",
1502 " specifies a local file which is copied into the <defs> section of the",
1503 " resulting SVG output file.  This file may either itself contain a font,",
1504 " or may contain the records necessary to create a hypertext reference to",
1505 " the desired font. Gnuplot will look for the requested file using the",
1506 " directory list in the GNUPLOT_FONTPATH environmental variable.",
1507 " NB: You must embed an svg font, not a TrueType or PostScript font."
1508 END_HELP(svg)
1509 #endif