1 /* Hello, Emacs, this is -*-C-*-
2 * $Id: svg.trm,v 1.63.2.13 2009/01/22 20:27:38 sfeam Exp $
5 /*------------------------------------------------------------------------------------------------------------------------------------
8 This file is included by ../term.c.
10 This terminal driver supports:
11 W3C Scalable Vector Graphics
21 broeker@physik.rwth-aachen.de
23 ------------------------------------------------------------------------------------------------------------------------------------*/
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 */
31 * Additional code for gnuplot versions 4.2 and 4.3
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'
41 * Ethan Merritt <merritt@u.washington.edu>
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 *));
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));
77 TERM_PUBLIC void SVG_path __PROTO((int p));
80 #define Y(y) ((float)((int)term->ymax - (int)y) / SVG_SCALE)
81 #define X(x) ((float)(x) / SVG_SCALE)
83 #define SVG_XMAX (600 * SVG_SCALE)
84 #define SVG_YMAX (480 * SVG_SCALE)
86 #endif /* TERM_PROTO */
88 #ifndef TERM_PROTO_ONLY
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;
98 static TBOOLEAN SVG_groupFilledIsOpen = FALSE; /* open pm3d group flag*/
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 */
110 static unsigned int SVG_xLast = UINT_MAX; /* current pen horizontal position*/
111 static unsigned int SVG_yLast = UINT_MAX; /* current pen vertical position*/
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*/
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*/
131 static int SVG_fillPattern = -1; /* active fill pattern (-1 == undefined) */
132 static unsigned int SVG_fillPatternIndex = 0;
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*/
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));
154 /* Points to source of requested embedded font */
155 static char *SVG_embedded_font = NULL;
156 static void SVG_load_fontfile __PROTO((char *fontfile));
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 */
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"
171 /*------------------------------------------------------------------------------------------------------------------------------------
173 ------------------------------------------------------------------------------------------------------------------------------------*/
175 SVG_Pen_RealID (int inPenCode)
178 inPenCode %= 13; /* normalize pen code*/
181 inPenCode = 0; /* LT_NODRAW or LT_BACKGROUND should use background color */
186 /*------------------------------------------------------------------------------------------------------------------------------------
188 ------------------------------------------------------------------------------------------------------------------------------------*/
192 SVG_GroupFilledClose();
193 if (!SVG_groupIsOpen) {
195 fprintf (gpoutfile, "<g style=\"fill:none; color:%s; stroke:",
196 SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
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);
203 fprintf(gpoutfile, "currentColor");
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");
210 fprintf (gpoutfile, "\">\n");
212 SVG_groupIsOpen = TRUE;
216 /*------------------------------------------------------------------------------------------------------------------------------------
218 ------------------------------------------------------------------------------------------------------------------------------------*/
222 SVG_GroupFilledClose();
223 if (SVG_groupIsOpen) {
224 fputs ("</g>\n", gpoutfile);
225 SVG_groupIsOpen = FALSE;
226 SVG_fillPattern = -1;
230 /*------------------------------------------------------------------------------------------------------------------------------------
232 ------------------------------------------------------------------------------------------------------------------------------------*/
236 if (!SVG_pathIsOpen) {
237 SVG_GroupFilledClose();
239 fputs ("\t<path ", gpoutfile);
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);
249 if (SVG_dashed && SVG_LineType % SVG_dashtypes > 0)
250 fprintf(gpoutfile, "stroke-dasharray='%s' ",
251 SVG_dashpattern[SVG_LineType % SVG_dashtypes]);
253 fputs (" d='", gpoutfile);
255 SVG_pathIsOpen = TRUE;
259 /*------------------------------------------------------------------------------------------------------------------------------------
261 ------------------------------------------------------------------------------------------------------------------------------------*/
265 if (SVG_pathIsOpen) {
266 SVG_GroupFilledClose();
267 fputs ("'></path>\n", gpoutfile);
269 SVG_pathIsOpen = FALSE;
273 /*------------------------------------------------------------------------------------------------------------------------------------
275 ------------------------------------------------------------------------------------------------------------------------------------*/
279 if (SVG_path_count % 8 == 0) /* avoid excessive line length*/
280 fputs ("\n\t\t", gpoutfile);
283 /*------------------------------------------------------------------------------------------------------------------------------------
285 ------------------------------------------------------------------------------------------------------------------------------------*/
287 SVG_SetFont (const char *name, double size)
289 if (name && name != SVG_fontNameCur)
290 strncpy (SVG_fontNameCur, name, sizeof(SVG_fontNameCur)-1);
291 SVG_fontSizeCur = size;
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 */
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*/
306 SVG_GroupFilledOpen()
308 if (!SVG_groupFilledIsOpen) {
310 fputs("\t<g style = 'stroke:none; shape-rendering:crispEdges'>\n",
312 SVG_groupFilledIsOpen = TRUE;
317 SVG_GroupFilledClose()
319 if (SVG_groupFilledIsOpen) {
320 fputs("\t</g>\n", gpoutfile);
321 SVG_groupFilledIsOpen = FALSE;
326 SVG_StyleColor(const char* paint)
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);
333 fprintf(gpoutfile, "%s = 'currentColor'", paint);
339 SVG_StyleColor("fill");
343 SVG_DefineFillPattern(int fillpat)
346 char *style="stroke";
349 if (fillpat != SVG_fillPattern) {
350 SVG_fillPattern = fillpat;
352 SVG_fillPatternIndex++;
356 "\t\t<pattern id='gpPat%d' patternUnits='userSpaceOnUse' x='0' y='0' width='8' height='8'>\n",
357 SVG_fillPatternIndex);
364 path="M0,0 L8,8 M0,8 L8,0";
367 path="M0,0 L8,8 M0,8 L8,0 M0,4 L4,8 L8,4 L4,0 L0,4";
370 path="M0,0 L0,8 L8,8 L8,0 L0,0";
374 path="M-4,0 L8,12 M0,-4 L12,8";
377 path="M-4,8 L8,-4 M0,12 L12,0";
380 path="M-2,8 L4,-4 M0,12 L8,-4 M4,12 L10,0";
383 path="M-2,0 L4,12 M0,-4 L8,12 M4,-4 L10,8";
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);
394 fprintf(gpoutfile, "\t\t\t<path style = '%s:currentColor' d='%s'/>\n",
397 fputs("\t\t</pattern>\n" "\t</defs>\n", gpoutfile);
402 SVG_MoveForced(unsigned int x, unsigned int y)
404 if (SVG_path_count > 512)
409 fprintf (gpoutfile, "M%.1f,%.1f ", X(x), Y(y));
418 /*------------------------------------------------------------------------------------------------------------------------------------
420 ------------------------------------------------------------------------------------------------------------------------------------*/
426 /* Annoying hack to handle the case of 'set termoption' after */
427 /* we have already initialized the terminal settings. */
431 while (!END_OF_COMMAND) {
432 if (almost_equals(c_token, "s$ize")) {
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;
444 if (equals(c_token,","))
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;
455 if (almost_equals(c_token, "d$ynamic")) {
457 SVG_fixed_size = FALSE;
461 if (almost_equals(c_token, "fi$xed")){
463 SVG_fixed_size = TRUE;
467 if (almost_equals(c_token, "enh$anced")) {
469 term->put_text = ENHsvg_put_text;
470 term->flags |= TERM_ENHANCED_TEXT;
474 if (almost_equals(c_token, "noenh$anced")) {
476 term->put_text = SVG_put_text;
477 term->flags &= ~TERM_ENHANCED_TEXT;
481 if (almost_equals(c_token, "fn$ame") || almost_equals(c_token, "font")) {
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)))
491 strncpy(SVG_fontNameDef, s, sizeof(SVG_fontNameDef));
496 if (almost_equals(c_token, "fs$ize")) {
500 int_error(c_token,"fsize: expecting font size");
501 SVG_fontSizeDef = real(const_express(&a));
505 if (almost_equals(c_token, "fontfile")) {
509 fontfile_name = try_to_get_string();
511 int_error(c_token, "Font filename expected");
512 gp_expand_tilde(&fontfile_name);
514 if ( *fontfile_name == '<' ) {
515 SVG_embedded_font = fontfile_name;
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);
525 if (almost_equals(c_token, "linew$idth") || equals(c_token, "lw")) {
527 SVG_linewidth_factor = real(const_express(&a));
528 if (SVG_linewidth_factor <= 0.0)
529 SVG_linewidth_factor = 1.0;
533 if (almost_equals (c_token, "round$ed")) {
539 if (equals (c_token, "butt")) {
545 if (equals(c_token, "solid")) {
551 if (almost_equals(c_token, "dash$ed")) {
557 int_error(c_token, "unrecognized terminal option");
560 /* I don't think any error checks on font name are possible; just set it */
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);
570 if (SVG_embedded_font) {
571 sprintf(term_options + strlen(term_options),
572 "fontfile \"%s\" ", SVG_embedded_font);
575 sprintf(term_options + strlen(term_options),
576 SVG_rounded ? "rounded " : "butt ");
578 sprintf(term_options + strlen(term_options),
579 SVG_dashed ? "dashed " : "solid ");
581 if (SVG_linewidth_factor != 1.0) {
582 sprintf(term_options + strlen(term_options),
583 "linewidth %3.1f ", SVG_linewidth_factor);
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;
601 /*------------------------------------------------------------------------------------------------------------------------------------
603 ------------------------------------------------------------------------------------------------------------------------------------*/
608 char *svg_encoding = "";
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*/
644 SVG_LineType = LT_NODRAW;
648 term->xmax = SVG_xSize;
649 term->ymax = SVG_ySize;
651 /* set current font*/
653 SVG_SetFont (SVG_fontNameCur, SVG_fontSizeCur);
655 /* set h_char, v_char*/
657 term->h_char = SVG_fontAvWidth;
658 term->v_char = (SVG_fontAscent + SVG_fontDescent + SVG_fontLeading);
660 /* set h_tic, v_tic*/
662 term->h_tic = term->v_char / 2;
663 term->v_tic = term->v_char / 2;
665 /* write file header*/
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;
678 svg_encoding = "encoding=\"utf-8\" ";
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);
689 fprintf (gpoutfile, "width=\"%u\" height=\"%u\" ",
690 (unsigned int) (term->xmax / SVG_SCALE),
691 (unsigned int) (term->ymax / SVG_SCALE));
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");
699 "<desc>Produced by GNUPLOT %s patchlevel %s </desc>\n\n",
700 gnuplot_version, gnuplot_patchlevel);
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);
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" */
714 stroke_width = 2.0 *SVG_SCALE / term->h_tic;
718 "\t<circle id='gpDot' r='0.5' stroke-width='0.5'/>\n"
720 "\t<path id='gpPt0' stroke-width='%.3f' stroke='currentColor' d='M-1,0 h2 M0,-1 v2'/>\n"
722 "\t<path id='gpPt1' stroke-width='%.3f' stroke='currentColor' d='M-1,-1 L1,1 M1,-1 L-1,1'/>\n"
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"
726 "\t<rect id='gpPt3' stroke-width='%.3f' stroke='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
728 "\t<rect id='gpPt4' stroke-width='%.3f' stroke='currentColor' fill='currentColor' x='-1' y='-1' width='2' height='2'/>\n"
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"
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"
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"
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. */
761 /*------------------------------------------------------------------------------------------------------------------------------------
763 ------------------------------------------------------------------------------------------------------------------------------------*/
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 */
771 SVG_fillPattern = -1;
772 SVG_fillPatternIndex = 0;
773 SVG_groupFilledIsOpen = FALSE;
774 SVG_color_mode = TC_DEFAULT;
775 SVG_pathIsOpen = FALSE;
779 SVG_xLast = SVG_yLast = UINT_MAX;
782 /*------------------------------------------------------------------------------------------------------------------------------------
784 ------------------------------------------------------------------------------------------------------------------------------------*/
792 /*------------------------------------------------------------------------------------------------------------------------------------
794 ------------------------------------------------------------------------------------------------------------------------------------*/
798 fputs("</svg>\n\n", gpoutfile);
801 /*------------------------------------------------------------------------------------------------------------------------------------
803 ------------------------------------------------------------------------------------------------------------------------------------*/
805 SVG_linetype (int linetype)
807 SVG_color_mode = TC_DEFAULT;
808 if (linetype != SVG_LineType) {
811 SVG_LineType = linetype;
817 SVG_fillbox(int style, unsigned int x1, unsigned int y1, unsigned int width, unsigned int height)
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;
827 SVG_filled_polygon(4, corner);
830 /*------------------------------------------------------------------------------------------------------------------------------------
831 SVG_linewidth - verificare
832 ------------------------------------------------------------------------------------------------------------------------------------*/
834 SVG_linewidth (double linewidth)
836 if (linewidth != SVG_LineWidth) {
839 SVG_LineWidth = linewidth;
841 for (k = 0; k < 16; k++)
842 SVG_pens[k].width = SVG_LineWidth;
850 /*------------------------------------------------------------------------------------------------------------------------------------
852 ------------------------------------------------------------------------------------------------------------------------------------*/
854 SVG_move (unsigned int x, unsigned int y)
856 if (x != SVG_xLast || y != SVG_yLast) {
857 SVG_MoveForced(x, y);
861 /*------------------------------------------------------------------------------------------------------------------------------------
863 ------------------------------------------------------------------------------------------------------------------------------------*/
865 SVG_vector (unsigned int x, unsigned int y)
867 if (x != SVG_xLast || y != SVG_yLast) {
869 if (!SVG_pathIsOpen) {
870 /* The SVG 'path' MUST have a 'moveto' as first command. */
871 SVG_MoveForced(SVG_xLast, SVG_yLast);
874 fprintf (gpoutfile, "L%.1f,%.1f ", X(x), Y(y));
884 /*------------------------------------------------------------------------------------------------------------------------------------
886 ------------------------------------------------------------------------------------------------------------------------------------*/
888 SVG_point (unsigned int x, unsigned int y, int number)
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);
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/>\
909 number % 13, X(x), Y(y),
910 term_pointsize * term->h_tic / (2 * SVG_SCALE),
917 /*------------------------------------------------------------------------------------------------------------------------------------
919 ------------------------------------------------------------------------------------------------------------------------------------*/
921 SVG_justify_text (enum JUSTIFY mode)
927 /*------------------------------------------------------------------------------------------------------------------------------------
929 ------------------------------------------------------------------------------------------------------------------------------------*/
931 SVG_text_angle (int ang)
933 /* Can only do pure horizontal or vertical */
938 /*------------------------------------------------------------------------------------------------------------------------------------
940 ------------------------------------------------------------------------------------------------------------------------------------*/
942 SVG_put_text (unsigned int x, unsigned int y, const char *str)
949 /* horizontal justification*/
951 switch (SVG_TextJust) {
956 alignment = "middle";
959 default: /* can't happen, just to make gcc happy */
964 /* vertical justification*/
966 if (SVG_TextAngle % 180) {
968 h += (SVG_fontAscent - SVG_fontDescent) / 2;
970 /* horizontal text */
971 v -= (SVG_fontAscent - SVG_fontDescent) / 2;
974 /* define text position and attributes */
976 fprintf (gpoutfile, "\t<g transform=\"translate(%.1f,%.1f)", X(h), Y(v));
978 fprintf (gpoutfile, " rotate(%i)", -SVG_TextAngle);
979 fprintf (gpoutfile, "\" style=\"stroke:none; fill:");
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);
986 fprintf (gpoutfile, "%s", SVG_pens[SVG_Pen_RealID (SVG_LineType)].color);
989 "; font-family:%s; font-size:%.2fpt; text-anchor:%s\">\n",
990 SVG_fontNameCur, SVG_fontSizeCur, alignment);
992 /* output text (unless the enhanced_text processing is in action) */
995 fputs ("\t\t<text xml:space=\"preserve\">", gpoutfile);
997 fputs ("\t\t<text>", gpoutfile);
999 if (!ENHsvg_string_state) {
1002 /* Escape SVG reserved characters */
1005 fputs("<", gpoutfile);
1008 if (str[1] == '#' && str[2] == 'x')
1009 fputc(*str, gpoutfile);
1011 fputs("&", gpoutfile);
1014 fputc(*str, gpoutfile);
1020 fputs("</text>\n\t</g>\n", gpoutfile);
1024 /*------------------------------------------------------------------------------------------------------------------------------------
1026 ------------------------------------------------------------------------------------------------------------------------------------*/
1028 SVG_set_font (const char *font)
1031 if (!font || !(*font)) {
1032 strcpy (SVG_fontNameCur, SVG_fontNameDef);
1033 SVG_fontSizeCur = SVG_fontSizeDef;
1035 int sep = strcspn(font,",");
1037 strncpy(SVG_fontNameCur, font, sep);
1038 SVG_fontNameCur[sep] = NUL;
1040 if (font[sep] == ',')
1041 sscanf(font + sep + 1, "%lf", &SVG_fontSizeCur);
1048 /*------------------------------------------------------------------------------------------------------------------------------------
1050 ------------------------------------------------------------------------------------------------------------------------------------*/
1052 SVG_make_palette(t_sm_palette *palette)
1054 SVG_GroupFilledClose();
1055 if (palette == NULL) {
1056 /* svg can do continuous colors */
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;
1071 /*------------------------------------------------------------------------------------------------------------------------------------
1073 ------------------------------------------------------------------------------------------------------------------------------------*/
1075 SVG_set_color(t_colorspec *colorspec)
1077 rgb255_color rgb255;
1079 if (colorspec->type == TC_LT) {
1080 SVG_linecolor = SVG_pens[SVG_Pen_RealID (colorspec->lt)].color;
1081 SVG_color_mode = TC_LT;
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;
1092 SVG_color_mode = TC_RGB;
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". */
1100 SVG_green = rgb255.g;
1101 SVG_blue = rgb255.b;
1107 /*------------------------------------------------------------------------------------------------------------------------------------
1108 SVG_previous_palette
1109 ------------------------------------------------------------------------------------------------------------------------------------*/
1111 SVG_previous_palette()
1113 SVG_GroupFilledClose();
1117 /*------------------------------------------------------------------------------------------------------------------------------------
1119 ------------------------------------------------------------------------------------------------------------------------------------*/
1121 SVG_filled_polygon(int points, gpiPoint* corners)
1124 int fillpar = corners->style >> 4;
1125 int style = corners->style &= 0xf;
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);
1134 SVG_GroupFilledOpen();
1135 fputs("\t\t<polygon ", gpoutfile);
1138 case FS_EMPTY: /* fill with background color */
1139 /* TODO: and what if the background color is not white ? */
1140 fputs(" fill = 'white'", gpoutfile);
1142 case FS_SOLID: /* solid fill */
1143 SVG_StyleFillColor();
1144 if (fillpar >= 0 && fillpar < 100)
1145 fprintf(gpoutfile, " fill-opacity = '%f'", fillpar * 0.01);
1147 case FS_PATTERN: /* pattern fill */
1148 fprintf(gpoutfile, " fill = 'url(#gpPat%d)'",
1149 SVG_fillPatternIndex);
1152 SVG_StyleFillColor();
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);
1164 /* Enhanced text mode support starts here */
1166 static double ENHsvg_base = 0.0;
1167 static TBOOLEAN ENHsvg_opened_string = FALSE;
1168 static int ENHsvg_charcount = 0;
1173 double fontsize, double base,
1174 TBOOLEAN widthflag, TBOOLEAN showflag,
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.
1185 switch (overprint) {
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);
1191 fprintf(gpoutfile, "<tspan dx=\"-%.1fem\" dy=\"%.1fpt\">",
1192 0.3 * ENHsvg_charcount, ENHsvg_base-base);
1195 ENHsvg_x_offset = 0.0;
1196 enhanced_cur_text = enhanced_text;
1197 ENHsvg_charcount = 0;
1198 ENHsvg_opened_string = TRUE;
1201 ENHsvg_charcount = 0;
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;
1213 if (!ENHsvg_opened_string) {
1214 ENHsvg_opened_string = TRUE;
1215 enhanced_cur_text = enhanced_text;
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);
1223 if (SVG_fontSizeCur != fontsize) {
1224 SVG_fontSizeCur = fontsize;
1225 fprintf(gpoutfile, " font-size=\"%.1fpt\"", SVG_fontSizeCur);
1227 if (ENHsvg_x_offset != 0) {
1228 #ifdef CODDLE_NONCOMPLIANT_VIEWERS
1229 fprintf(gpoutfile, " dx=\"%.2fpt\"",
1230 ENHsvg_x_offset * 1.1*SVG_fontSizeCur);
1232 fprintf(gpoutfile, " dx=\"%.2fem\"", ENHsvg_x_offset);
1234 ENHsvg_x_offset = 0.0;
1236 if (ENHsvg_base != base) {
1237 fprintf(gpoutfile, " dy=\"%.2fpt\"", ENHsvg_base-base);
1241 fprintf(gpoutfile, " fill=\"none\"");
1243 if (ENHsvg_preserve_spaces) {
1244 fprintf(gpoutfile, " xml:space=\"preserve\"");
1246 fputs(">", gpoutfile);
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);
1262 ENHsvg_put_text(unsigned int x, unsigned int y, const char *str)
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));
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);
1278 ENHsvg_string_state = 1;
1279 SVG_put_text(x, y, str);
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;
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));
1294 while (*(str = enhanced_recursion((char *)str, TRUE,
1295 fontname, fontsize, 0.0, TRUE, TRUE, 0))) {
1296 (term->enhanced_flush)();
1299 break; /* end of string */
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;
1310 ENHsvg_preserve_spaces = FALSE;
1312 /* Close the text section */
1313 fputs("</text>\n\t</g>\n", gpoutfile);
1319 ENHsvg_writec(int c)
1321 /* Kludge for phantom box accounting */
1324 /* Escape SVG reserved characters. Are there any besides '<' and '&' ? */
1327 *enhanced_cur_text++ = '&';
1328 *enhanced_cur_text++ = 'l';
1329 *enhanced_cur_text++ = 't';
1330 *enhanced_cur_text++ = ';';
1333 *enhanced_cur_text++ = '&';
1334 *enhanced_cur_text++ = 'a';
1335 *enhanced_cur_text++ = 'm';
1336 *enhanced_cur_text++ = 'p';
1337 *enhanced_cur_text++ = ';';
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++ = '&';
1344 } /* else fall through */
1346 *enhanced_cur_text++ = c;
1352 SVG_load_fontfile(char *fontfile)
1355 unsigned int linesread = 0;
1358 char *fontname = NULL;
1360 TBOOLEAN ispipe = FALSE;
1364 if ( *fontfile == '<' ) {
1366 ffont = popen(fontfile + 1, "r" );
1368 int_error(NO_CARET, "Could not execute pipe '%s'",
1373 ffont = fopen(fontfile, "r");
1375 int_error(NO_CARET, "Font file '%s' not found", fontfile);
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);
1386 /* Copy contents into output file */
1387 fputs(line,gpoutfile);
1394 if ( (exitcode = pclose(ffont)) != 0 )
1395 int_error(NO_CARET, "Command '%s' generated error exitcode %d",
1396 fontfile + 1, exitcode);
1401 if (linesread == 0) {
1405 "Command '%s' generates empty output", fontfile + 1);
1408 int_error(NO_CARET, "Font file '%s' is empty", fontfile);
1418 case 1: /* Close path */
1419 fputs("Z ", gpoutfile);
1430 #undef CODDLE_NONCOMPLIANT_VIEWERS
1432 #endif /* TERM_BODY */
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
1445 , 0, 0, 0, 0, 0 /* no mouse support for svg */
1448 SVG_previous_palette,
1454 , ENHsvg_OPEN, ENHsvg_FLUSH, ENHsvg_writec
1456 , SVG_path /* path */
1457 TERM_TABLE_END (svg_driver)
1460 #define LAST_TERM svg_driver
1462 #endif /* TERM_TABLE */
1463 #endif /* TERM_PROTO_ONLY */
1468 "?commands set terminal svg",
1469 "?set terminal svg",
1474 " This terminal produces files in the W3C Scalable Vector Graphics format.",
1477 " set terminal svg {size <x>,<y> {|fixed|dynamic}}",
1479 " {fname \"<font>\"} {fsize <fontsize>}",
1480 " {font \"<fontname>{,<fontsize>}\"}",
1481 " {fontfile <filename>}",
1482 " {rounded|butt} {solid|dashed} {linewidth <lw>}",
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.",
1488 " `linewidth <w>` increases the width of all lines used in the figure",
1489 " by a factor of <w>.",
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.",
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.",
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."