1 /*
2  * Fig2dev: Translate Fig code to various Devices
3  * Parts Copyright (c) 2002 by Anthony Starks
4  * Parts Copyright (c) 2002-2006 by Martin Kroeker
5  * Parts Copyright (c) 2002-2015 by Brian V. Smith
6  * Parts Copyright (c) 2015-2020 by Thomas Loimer
7  *
8  * Any party obtaining a copy of these files is granted, free of charge, a
9  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
10  * nonexclusive right and license to deal in this software and documentation
11  * files (the "Software"), including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense and/or sell copies
13  * of the Software, and to permit persons who receive copies from any such
14  * party to do so, with the only requirement being that the above copyright
15  * and this permission notice remain intact.
16  *
17  */
18 
19 /*
20  *
21  * gensvg.c: convert fig to SVG
22  *
23  *  from fig2svg -- convert FIG 3.2 to SVG
24  *
25  *  Original author:  Anthony Starks (ajstarks@home.com)
26  *  Created: 17 May 2000
27  *  Converted to gensvg by Brian Smith
28  *  Further modified by Martin Kroeker (martin@ruby.chemie.uni-freiburg.de)
29  *  incorporating changes by Philipp Hahn and Justus Piater
30  *  Modified by Thomas Loimer
31  *
32  *  Changes:
33  *
34  *  by Thomas Loimer <thomas.loimer@tuwien.ac.at>
35  *
36  *  2019-05-11
37  *	- Output utf8-encoded text
38  *	- Parse and replace characters <, > and & in comments by &lt; &gt; &amp;
39  *
40  *  2017-01-04
41  *	- Fix pattern definitions. Use clip paths when painting objects with
42  *	  arrows instead of retracting the line. Let the viewer do any
43  *	  magnification. Use properties instead of attributes (e.g.,
44  *	  stroke="red" instead of style="stroke:red;"). Implement pie-wedge
45  *	  arcs. Add a todo list, see below.
46  *
47  * Changes before 2006
48  *
49  *  PH: Philipp Hahn
50  *  JP: Justus Piater
51  *  MK: Martin Kroeker
52  *  BS: Brian Smith
53  *  RE: Russell Edwards
54  *
55  *  MK 04-Dec-02: partial support for the symbol font, bigger fontscale, text alignment,
56  *  dashed and dotted lines, bugfix for missing % in stroke-color statement of arcs
57  *  FIXME: lacks support for arrowheads; fill patterns; percent grayscale fills
58  *  MK 08-Dec-02: rotated text; shades and tints of fill colors; filled circles
59  *  MK 11-Dec-02: scaling;proper font/slant/weight support; changed arc code
60  *  12-Dec-02: fixes by Brian Smith: scale factor, orientation, ellipse fills
61  *  MK 14-Dec-02: arc code rewrite, simplified line style handling,
62  *  arrowheads on arcs and lines (FIXME: not clipped), stroke->color command
63  *  is simply 'stroke'
64  *  MK 15-Dec-02: catch pattern fill flags, convert to tinted fills for now
65  *  MK 18-Dec-02: fill patterns; fixes by BS: arrowhead scale & position,
66  *  circle by diameter
67  *  PH 03-Feb-03: Fix CIRCLE_BY_DIA, color/fill styles, update SVG DTD
68  *  MK 10-Feb-03: do not encode space characters when in symbol font;
69  *		  always encode characters '&', '<' and '>'. Leave non-
70  *		  alphabetic characters in the lower half of the symbol
71  *		  font unchanged.
72  *  MK 12-Feb-03: Added complete character conversion tables for the symbol
73  *		  and dingbat fonts (based on the information in Unicode
74  *		  Inc.'s symbol.txt and zdingbat.txt tables, version 0.2)
75  *  MK 18-Feb-03: Added cap and join style fields for line and arc
76  *  MK 24-Feb-03: Symbol and Dingbat fonts are no longer translated to
77  *		  font-family="Times" with both bold and italic flags set.
78  *  MK 17-Jun-03: Fix for rotation angle bug. Correct rendering of 'tinted'
79  *		  colors using code from www.cs.rit.edu. Added forgotten
80  *		  pattern fill option for ellipses (circles).
81  *  JP 21-Jan-04: Calculate proper bounding box instead of current paper
82  *		  dimensions. Added missing semicolons in some property
83  *		  strings, and proper linebreak characters in multi-line
84  *		  format strings.
85  *  MK 23-Jan-04: Pattern-filled objects are now drawn twice - painting the
86  *		  pattern over the fill color (if any). This solves the problem
87  *		  of missing color support in pattern fills (as reported by JP)
88  *		  Corrected filling of ellipses, which was still B/W only.
89  *		  Fixed bad tiling of diagonal patterns 1 - 3 (the old formula
90  *		  favoured exact angles over seamless tiling). Updated DTD.
91  *  MK 25-Jan-04: Endpoints of polylines are now truncated when arrowheads
92  *		  are drawn. Corrected rendering of type 0 (stick) arrowheads.
93  *  MK 28-Jan-04: Fix for arc arrowhead orientation.
94  *  MK 31-Jan-04: Corrected arc angle calculation (this time for good ?)
95  *  MK 22-Feb-04: Picture support
96  *  JP	1-Mar-04: Closed arrowheads should use polygons instead of polylines
97  *  JP	3-Mar-04: Corrected font family selection
98  *  JP 26-Mar-04: Corrected (and simplified) calculation of white-tinted
99  *		  fill colors (and removed the HSV/RGB conversion code)
100  *  MK 29-Mar-04: Added code for rounded boxes (polyline subtype 4)
101  *  MK 30-Mar-04: Added code for boxes, explicit support for polygons
102  *  MK 10-Apr-04: Added xml-space:preserve qualifier on texts to preserve
103  *		  whitespace. Rewrote fill pattern handling to generate
104  *		  patterns as needed - adding support for penwidth and color.
105  *		  Corrected tiling of all shingle patterns and reversal
106  *		  of horizontal shingles.
107  *  RE	6-May-04: Changed degrees() to double for more precision
108  *		  Added linewidth() to transform all line widths in the
109  *		   same way as genps.c : thin lines get thinner
110  *		  Changed circle radius to use F_ellipse::radiuses.x instead
111  *		   of start and end (which seemed not to work correctly)
112  *		   Query: Is this broken for byradius or bydiameter??
113  *		  Added rotation to ellipses
114  *		  Changed back to mapping Symbol to Times, greeks look a bit
115  *		   better. Ultimately embedding PS fonts would be better.
116  *		  Removed newlines inside <text> printf, otherwise they get
117  *		   rendered as spaces due to xml:space="preserve"
118  *		  Removed extraneous comma between two halves of format
119  *		   string in gensvg_arc, fixes Seg fault.
120  *  MK	3-Aug-04  Split the multi-line format string in gensvg_arc in two to
121  *		  get rid of (compiler version-dependant) segfaults for good.
122  *  MK 11-Sep-05: Added explicit stroke color to text to prevent black outline
123  *		  on colored text.
124  *		  Added support for latex-special formatted text, converting
125  *		  sub- and superscripts to either baseline-shift=sub/super
126  *		  (the intended way of doing this in SVG) or "dy" offsets
127  *		  (less elegant, but more likely to be supported by browsers
128  *		  and editors) depending on the NOSUPER define below.
129  *		  Tested with Batik-1.6, konqueror-3.4, firefox-1.5b1,
130  *		  inkscape-0.41
131  *  MK 15-Sep-05: Use a font-family list of "Times,Symbol" for symbol
132  *		  characters - the Times fontface does not contain all
133  *		  elements of the Symbol font on all platforms.
134  *  MK	4-Nov-05: Corrected length and appearance of stick-type arrows.
135  *  MK	2-Jan-06: Added support for filled arcs.
136  *  MK 26-Feb-06: Added support for dashed circles, ellipses and arcs.
137  *		  Dash/gap lengths are now drawn according to style_val.
138  *		  Fixed several glitches uncovered by splint.
139  *  MK 22-Apr-06: Corrected blue component of shaded colors (was always
140  *		  zero due to missing parentheses around typecast). Corrected
141  *		  arrowheads of large arrows by adding an increased miterlimit.
142  *		  Corrected position of backward arrowheads on polylines with
143  *		  both forward and backward arrows.
144  *  MK	2-Jul-06: Patterns do not inherit their line width from the parent object
145  *		  (which may be zero if no visible boundary is desired), so always
146  *		  use linewidth:1
147  *  MK 22-Oct-06: Changed unicode variant of lowercase phi to match its X11 Symbol
148  *		  counterpart.
149  *  *********************************************************************************
150  *
151  *  An excerpt from http://www.w3.org/TR/2011/REC-SVG11-20110816/:
152  *  °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
153  *  W3 recommendations for
154  *  1.3 SVG Namespace, Public Identifier and System Identifier
155  *
156  *  The following are the SVG 1.1 namespace, public identifier and system identifier:
157  *
158  *  SVG Namespace:
159  *	http://www.w3.org/2000/svg
160  *	xmlns:xlink="http://www.w3.org/1999/xlink"
161  *  Public Identifier for SVG 1.1:
162  *	PUBLIC "-//W3C//DTD SVG 1.1//EN"
163  *  System Identifier for the SVG 1.1 Recommendation:
164  *	http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd
165  *
166  *  The following is an example document type declaration for an SVG document:
167  *
168  *  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
169  *	     "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
170  *
171  *  Note that DTD listed in the System Identifier is a modularized DTD (ie. its
172  *  contents are spread over multiple files), which means that a validator may have
173  *  to fetch the multiple modules in order to validate. For that reason, there is
174  *  a single flattened DTD available that corresponds to the SVG 1.1 modularized DTD.
175  *  It can be found at http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-flat.dtd.
176  *
177  *  While a DTD is provided in this specification, the use of DTDs for
178  *  validating XML documents is known to be problematic.  In particular, DTDs
179  *  do not handle namespaces gracefully.  It is not recommended that
180  *  a DOCTYPE declaration be included in SVG documents.
181  *  *********************************************************************************
182  */
183 
184 /* TODO
185  *	* identify pattern by number and color (int pattern[22], an
186  *	  int has 32 bits, need 32 standard colors plus 512 user colors)
187  *	* create nicer color commands ("white", "blue", etc. )
188  *	  and "currentColor" for default
189  *	* get put_precision from gentikz.c, prec 0 or 1
190  *	  for, e.g., center of image, for rotation
191  *	* ellipses: for dashed lines, put cap style "round"
192  *	* need char *fig_color_names, or use int array?
193  *	* image rotation - around center necessary?
194  *	* check line stipples, compare with genps.c
195  *	* see, if BLACK_FILL, WHITE_FILL can be replaced
196  *	* use pdftocairo, where available?
197  *      * defs until now: tile%d, p%d, cp%d
198  *	* probably change to pattern%d, c%d (color)
199  *
200  *	* Correct color values;
201  *	  hex codes see Fig_color_names in fig2dev.c, or, identical,
202 	  colorNames in xfig/src/resources.c
203  *		colorNames	Fig_color_names	svg name
204  *	0	"black"		"#000000"	black
205  *	1	"blue"		"#0000ff"	blue
206  *	2	"green"		"#00ff00"	lime
207  *	3	"cyan"		"#00ffff"	cyan
208  *	4	"red"		"#ff0000"	red
209  *	5	"magenta"	"#ff00ff"	magenta
210  *	6	"yellow"	"#ffff00"	yellow
211  *	7	"white"		"#ffffff"	white
212  *	8	"#000090"	"#000090" 144
213  *	9	"#0000b0"	"#0000b0" 176
214  *	10	"#0000d0"	"#0000d0" 208
215  *	11	"#87ceff"	"#87ceff" 135 206
216  *	12	"#009000"	"#009000"
217  *	13	"#00b000"	"#00b000"
218  *	14	"#00d000"	"#00d000"
219  *	15	"#009090"	"#009090"
220  *	16	"#00b0b0"	"#00b0b0"
221  *	17	"#00d0d0"	"#00d0d0"
222  *	18	"#900000"	"#900000"
223  *	19	"#b00000"	"#b00000"
224  *	20	"#d00000"	"#d00000"
225  *	21	"#900090"	"#900090"
226  *	22	"#b000b0"	"#b000b0"
227  *	23	"#d000d0"	"#d000d0"
228  *	24	"#803000"	"#803000" 128 48
229  *	25	"#a04000"	"#a04000" 160 64
230  *	26	"#c06000"	"#c06000" 192 96
231  *	27	"#ff8080"	"#ff8080"
232  *	28	"#ffa0a0"	"#ffa0a0"
233  *	29	"#ffc0c0"	"#ffc0c0"
234  *	30	"#ffe0e0"	"#ffe0e0"
235  *	31	"gold"	        "#ffd700"	gold	(255, 215, 0)
236  *
237  */
238 
239 #ifdef HAVE_CONFIG_H
240 #include "config.h"
241 #endif
242 
243 #include <stdio.h>
244 #include <stdlib.h>
245 #include <string.h>
246 #ifdef	HAVE_STRINGS_H
247 #include <strings.h>
248 #endif
249 #include <math.h>
250 
251 #include "fig2dev.h"	/* includes bool.h and object.h */
252 //#include "object.h"
253 #include "bound.h"
254 #include "creationdate.h"
255 #include "messages.h"
256 #include "pi.h"
257 
258 static bool svg_arrows(int line_thickness, F_arrow *for_arrow, F_arrow *back_arrow,
259 	F_pos *forw1, F_pos *forw2, F_pos *back1, F_pos *back2, int pen_color);
260 static void generate_tile(int number, int colorIndex);
261 static void svg_dash(int, double);
262 
263 #define PREAMBLE "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
264 #define	SVG_LINEWIDTH	76
265 
266 static unsigned int symbolchar[256]=
267 {0,0,0,0,0,0,0,0,0,0,
268 0,0,0,0,0,0,0,0,0,0,
269 0,0,0,0,0,0,0,0,0,0,
270 0,0,0x0020,0x0021,0x2200,0x0023,0x2203,0x0025,
271 0x0026,0x220B,0x0028,0x0029,0x2217,0x002B,0x002C,0x2212,0x002E,0x002F,0x0030,
272 0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,
273 0x003C,0x003D,0x003E,0x003F,0x2245,0x0391,0x0392,0x03A7,0x0394,0x0395,
274 0x03A6,0x0393,0x0397,0x0399,0x03D1,0x039A,0x039B,0x039C,0x039D,0x039F,0x03A0,
275 0x0398,0x03A1,0x03A3,0x03A4,0x03A5,0x03C2,0x03A9,0x039E,0x03A8,0x0396,
276 0x005B,0x2234,0x005D,0x22A5,0x005F,0xF8E5,0x03B1,0x03B2,0x03C7,0x03B4,0x03B5,
277 0x03D5 /*0x03C6*/,0x03B3,0x03B7,0x03B9,0x03D5,0x03BA,0x03BB,0x03BC,0x03BD,0x03BF,
278 0x03C0,0x03B8,0x03C1,0x03C3,0x03C4,0x03C5,0x03D6,0x03C9,0x03BE,0x03C8,0x03B6,
279 0x007B,0x007C,0x007D,0x223C,0,0,0,0,0,0,0,0,0,
280 0,0,0,0,0,0,0,0,0,0,
281 0,0,0,0,0,0,0,0,0,0, 0,0,
282 0,0,0x20AC,0x03D2,0x2032,0x2264,0x2044,0x221E,
283 0x0192,0x2663,0x2666,0x2665,0x2660,0x2194,0x2190,0x2191,0x2192,0x2193,0x00B0,
284 0x00B1,0x2033,0x2265,0x00D7,0x221D,0x2202,0x2022,0x00F7,0x2260,0x2261,0x2248,
285 0x2026,0xF8E6,0xF8E7,0x21B5,0x2135,0x2111,0x211C,0x2118,0x2297,0x2295,0x2205,
286 0x2229,0x222A,0x2283,0x2287,0x2284,0x2282,0x2286,0x2208,0x2209,0x2220,0x2207,
287 0xF6DA,0xF6D9,0xF6DB,0x220F,0x221A,0x22C5,0x00AC,0x2227,0x2228,0x21D4,0x21D0,
288 0x21D1,0x21D2,0x21D3,0x25CA,0x2329,0xF8E8,0xF8E9,0xF8EA,0x2211,0xF8EB,0xF8EC,
289 0xF8ED,0xF8EE,0xF8EF,0xF8F0,0xF8F1,0xF8F2,0xF8F3,0xF8F4,0,0x232A,0x222B,0x2320,
290 0xF8F5,0x2321,0xF8F6,0xF8F7,0xF8F8,0xF8F9,0xF8FA,0xF8FB,0xF8FC,0xF8FD,0xF8FE,0
291 };
292 
293 static unsigned int dingbatchar[256]=
294 {0,0,0,0,0,0,0,0,0,0,
295 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0x0020,
296 0x2701,0x2702,0x2703,0x2704,0x260E,0x2706,0x2707,0x2708,0x2709,0x261B,
297 0x261E,0x270C,0x270D,0x270E,0x270F,0x2710,0x2711,0x2712,0x2713,0x2714,
298 0x2715,0x2716,0x2717,0x2718,0x2719,0x271A,0x271B,0x271C,0x271D,0x271E,
299 0x271F,0x2720,0x2721,0x2722,0x2723,0x2724,0x2725,0x2726,0x2727,0x2605,
300 0x2729,0x272A,0x272B,0x272C,0x272D,0x272E,0x272F,0x2730,0x2731,0x2732,
301 0x2733,0x2734,0x2735,0x2736,0x2737,0x2738,0x2739,0x273A,0x273B,0x273C,
302 0x273D,0x273E,0x273F,0x2740,0x2741,0x2742,0x2743,0x2744,0x2745,0x2746,
303 0x2747,0x2748,0x2749,0x274A,0x274B,0x25CF,0x274D,0x25A0,0x274F,0x2750,
304 0x2751,0x2752,0x25B2,0x25BC,0x25C6,0x2756,0x25D7,0x2758,0x2759,0x275A,
305 0x275B,0x275C,0x275D,0x275E,0,0xF8D7,0xF8D8,0xF8D9,0xF8DA,0xF8DB,
306 0xF8DC,0xF8DD,0xF8DE,0xF8DF,0xF8E0,0xF8E1,0xF8E2,0xF8E3,0xF8E4,0,0,
307 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
308 0,0x2761,0x2762,0x2763,
309 0x2764,0x2765,0x2766,0x2767,0x2663,0x2666,0x2665,0x2660,0x2460,0x2461,
310 0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,0x2776,0x2777,
311 0x2778,0x2779,0x277A,0x277B,0x277C,0x277D,0x277E,0x277F,0x2780,0x2781,
312 0x2782,0x2783,0x2784,0x2785,0x2786,0x2787,0x2788,0x2789,0x278A,0x278B,
313 0x278C,0x278D,0x278E,0x278F,0x2790,0x2791,0x2792,0x2793,0x2794,0x2192,
314 0x2194,0x2195,0x2798,0x2799,0x279A,0x279B,0x279C,0x279D,0x279E,0x279F,
315 0x27A0,0x27A1,0x27A2,0x27A3,0x27A4,0x27A5,0x27A6,0x27A7,0x27A8,0x27A9,
316 0x27AA,0x27AB,0x27AC,0x27AD,0x27AE,0x27AF,0,0x27B1,0x27B2,0x27B3,0x27B4,
317 0x27B5,0x27B6,0x27B7,0x27B8,0x27B9,0x27BA,0x27BB,0x27BC,0x27BD,0x27BE,0
318 };
319 
320 static int	tileno = -1;	/* number of current tile */
321 static int	pathno = -1;	/* number of current path */
322 static int	clipno = -1;	/* number of current clip path */
323 
324 static void
put_capstyle(int c)325 put_capstyle(int c)
326 {
327 	if (c == 1)
328 	    fputs(" stroke-linecap=\"round\"", tfp);
329 	else if (c == 2)
330 	    fputs(" stroke-linecap=\"square\"", tfp);
331 }
332 
333 static void
put_joinstyle(int j)334 put_joinstyle(int j)
335 {
336 	if (j == 1)
337 	    fputs(" stroke-linejoin=\"round\"", tfp);
338 	else if (j == 2)
339 	    fputs(" stroke-linejoin=\"bevel\"", tfp);
340 }
341 
342 static unsigned int
rgbColorVal(int colorIndex)343 rgbColorVal(int colorIndex)
344 {				/* taken from genptk.c */
345     unsigned int rgb;
346     static unsigned int rgbColors[NUM_STD_COLS] = {
347 	0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff,
348 	0xffff00, 0xffffff, 0x00008f, 0x0000b0, 0x0000d1, 0x87cfff,
349 	0x008f00, 0x00b000, 0x00d100, 0x008f8f, 0x00b0b0, 0x00d1d1,
350 	0x8f0000, 0xb00000, 0xd10000, 0x8f008f, 0xb000b0, 0xd100d1,
351 	0x803000, 0xa14000, 0xb46100, 0xff8080, 0xffa1a1, 0xffbfbf,
352 	0xffe0e0, 0xffd600
353     };
354 
355     if (colorIndex == DEFAULT)
356 	rgb = rgbColors[0];
357     else if (colorIndex < NUM_STD_COLS)
358 	rgb = rgbColors[colorIndex];
359     else
360 	rgb = ((user_colors[colorIndex - NUM_STD_COLS].r & 0xff) << 16)
361 	    | ((user_colors[colorIndex - NUM_STD_COLS].g & 0xff) << 8)
362 	    | (user_colors[colorIndex - NUM_STD_COLS].b & 0xff);
363     return rgb;
364 }
365 
366 static unsigned int
rgbFillVal(int colorIndex,int area_fill)367 rgbFillVal(int colorIndex, int area_fill)
368 {
369     unsigned int	rgb, r, g, b;
370     double	t;
371     short	tintflag = 0;
372 
373     if (colorIndex == BLACK_COLOR || colorIndex == DEFAULT) {
374 	if (area_fill > NUMSHADES - 1)
375 	    area_fill = NUMSHADES - 1;
376 	area_fill = NUMSHADES - 1 - area_fill;
377 	colorIndex = WHITE_COLOR;
378     }
379 
380     rgb = rgbColorVal(colorIndex);
381 
382     if (area_fill > NUMSHADES - 1) {
383 	tintflag = 1;
384 	area_fill -= NUMSHADES - 1;
385     }
386 
387     t = (double) area_fill / (NUMSHADES - 1);
388     if (tintflag) {
389 	r = ((rgb & ~0xFFFF) >> 16);
390 	g = ((rgb & 0xFF00) >> 8);
391 	b = (rgb & ~0xFFFF00) ;
392 
393 	r += t * (0xFF-r);
394 	g += t * (0xff-g);
395 	b += t * (0xff-b);
396 
397 	rgb = ((r &0xff) << 16) + ((g&0xff) << 8) + (b&0xff);
398     } else {
399 	rgb = (((int) (t * ((rgb & ~0xFFFF) >> 16)) << 16) +
400 		((int) (t * ((rgb & 0xFF00) >> 8)) << 8) +
401 		((int) (t * (rgb & ~0xFFFF00))) );
402     }
403 
404     return rgb;
405 }
406 
407 static double
degrees(double angle)408 degrees(double angle)
409 {
410    return -angle / M_PI * 180.0;
411 }
412 
413 static double
linewidth_adj(int linewidth)414 linewidth_adj(int linewidth)
415 {
416    /* Adjustment as in genps.c */
417    return linewidth <= THICK_SCALE ?
418 		linewidth / 2. : (double)(linewidth-THICK_SCALE);
419 }
420 
421 void
put_sanitized_char(int c)422 put_sanitized_char(int c)
423 {
424 	switch (c) {
425 	case '<':
426 		fputs("&lt;", tfp);
427 		break;
428 	case '>':
429 		fputs("&gt;", tfp);
430 		break;
431 	case '&':
432 		fputs("&amp;", tfp);
433 		break;
434 	default:
435 		fputc(c, tfp);
436 	}
437 }
438 
439 void
print_svgcomments(char * s1,F_comment * comments,char * s2)440 print_svgcomments(char *s1, F_comment *comments, char *s2)
441 {
442 	unsigned char	*c;
443 	while (comments) {
444 		fputs(s1, tfp);
445 		for (c = (unsigned char *)comments->comment; *c; ++c)
446 			put_sanitized_char((int)*c);
447 		fputs(s2, tfp);
448 		comments = comments->next;
449 	}
450 }
451 
452 void
gensvg_option(char opt,char * optarg)453 gensvg_option(char opt, char *optarg)
454 {
455     switch (opt) {
456 	case 'G':		/* ignore language and grid */
457 	case 'L':
458 	    break;
459 	case 'z':
460 	    (void) strcpy (papersize, optarg);
461 	    paperspec = true;
462 	    break;
463 	default:
464 	    put_msg (Err_badarg, opt, "svg");
465 	    exit (1);
466     }
467 }
468 
469 void
gensvg_start(F_compound * objects)470 gensvg_start(F_compound *objects)
471 {
472     const struct paperdef	*pd;
473     int     pagewidth = -1, pageheight = -1;
474     int     vw, vh;
475     char    date_buf[CREATION_TIME_LEN];
476 
477     fprintf(tfp, "%s\n", PREAMBLE);
478     fprintf(tfp, "<!-- Creator: %s Version %s -->\n",
479 		  prog, PACKAGE_VERSION);
480 
481     if (creation_date(date_buf))
482 	fprintf(tfp, "<!-- CreationDate: %s -->\n", date_buf);
483     fprintf(tfp, "<!-- Magnification: %.3g -->\n", mag);
484 
485 
486     if (paperspec) {
487 	/* convert paper size from ppi to inches */
488 	for (pd = paperdef; pd->name != NULL; ++pd)
489 	    if (strcasecmp(papersize, pd->name) == 0) {
490 		pagewidth = pd->width;
491 		pageheight = pd->height;
492 		strcpy(papersize, pd->name);	/* use the "nice" form */
493 		break;
494 	    }
495 	if (pagewidth < 0 || pageheight < 0) {
496 	    (void) fprintf(stderr, "Unknown paper size `%s'\n", papersize);
497 	    exit(1);
498 	}
499 	if (landscape) {
500 	    vh = pagewidth;
501 	    vw = pageheight;
502 	} else {
503 	    vw = pagewidth;
504 	    vh = pageheight;
505 	}
506     } else {
507 	vw = ceil((urx - llx) * 72. * mag / ppi);
508 	vh = ceil((ury - lly) * 72. * mag / ppi);
509     }
510     fputs("<svg\txmlns=\"http://www.w3.org/2000/svg\"\n", tfp);
511     fputs("\txmlns:xlink=\"http://www.w3.org/1999/xlink\"\n", tfp);
512     fprintf(tfp,
513 	"\twidth=\"%dpt\" height=\"%dpt\"\n\tviewBox=\"%d %d %d %d\">\n",
514 	vw, vh, llx, lly, urx - llx , ury - lly);
515 
516     if (objects->comments)
517 	print_svgcomments("<desc>", objects->comments, "</desc>\n");
518     fputs("<g fill=\"none\">\n", tfp);
519 
520 }
521 
522 int
gensvg_end(void)523 gensvg_end(void)
524 {
525     fprintf(tfp, "</g>\n</svg>\n");
526     return 0;
527 }
528 
529 /*
530  *	paint objects without arrows
531  *	****************************
532  *
533  *	PATTERN					FILL		UNFILLED
534  * a|	<defs>
535  *
536  *	<path d="..				<path d="..	<path d="..
537  *
538  * b|	id="p%d"/>				fill="#fillcol"
539  * b|	generate_tile(pen_color)
540  * b|	</defs>
541  * b|	<use xlink:href="#p%d" fill="#col"/>
542  * b|	<use xlink:href="#p%d" fill="url(#tile%d)"
543  *
544  *	    -----------   ...continue with "stroke=..." etc.   -----------
545  *	/>				/>				/>
546  *
547  *	a| INIT_PAINT,  b| continue_paint
548  *
549  *	paint objects with arrows
550  *	*************************
551  *
552  *	has_clip = svg_arrows(..., INIT)
553  *	if (UNFILLED && thickness == 0) {svg_arrows(..); return;}
554  *
555  *	PATTERN				FILL				UNFILLED
556  * c|	<defs>				<defs>				<defs>
557  * c|	    -----------------   svg_arrows(..., CLIP)    ---------------
558  *									</defs>
559  *	    -----------------   <path polyline points="...  ------------
560  * d|	id="p%d"/>			id="p%d"/>
561  * d|	generate_tile(pen_color)
562  * d|	</defs>				</defs>
563  * d|	<use ..#p%d fill="#col"/>	<use ..."#p%d" fill="#fillcol"/>
564  * d|	<use ..#p%d fill="url(#tile%d)"/>
565  * d|	<use ..#p%d			<use ..#p%d
566  * d|	    -----------------   clip-path="#cp%d"   --------------------
567  *	    -------------   ...continue with "stroke=..." etc.   -------
568  *	/>				/>				/>
569  *	    -----------------   svg_arrows(..., pen_color)   ------------
570  *
571  *	c| INIT_PAINT_W_CLIP,  d| continue_paint_w_clip
572  */
573 
574 #define	INIT	-9	/* Change this, if pen_color may be negative. */
575 #define	CLIP	-8
576 
577 #define	INIT_PAINT(fill_style) \
578 		if (fill_style > NUMFILLS) fputs("<defs>\n", tfp)
579 
580 #define	INIT_PAINT_W_CLIP(fill_style, thickness, for_arrow, back_arrow,	\
581 			  forw1, forw2, back1, back2)			\
582 	fputs("<defs>\n", tfp);					\
583 	(void) svg_arrows(thickness, for_arrow, back_arrow,	\
584 		forw1, forw2, back1, back2, CLIP);		\
585 	if (fill_style == UNFILLED)				\
586 		fputs("</defs>\n", tfp)
587 
588 void
continue_paint(int fill_style,int pen_color,int fill_color)589 continue_paint(int fill_style, int pen_color, int fill_color)
590 {
591     if (fill_style > NUMFILLS) {
592 	fprintf(tfp, " id=\"p%d\"/>\n", ++pathno);
593 	generate_tile(fill_style - NUMFILLS, pen_color);
594 	fputs("</defs>\n", tfp);
595 	fprintf(tfp, "<use xlink:href=\"#p%d\" fill=\"#%6.6x\"/>\n",
596 		pathno, rgbColorVal(fill_color));
597 	fprintf(tfp, "<use xlink:href=\"#p%d\" fill=\"url(#tile%d)\"",
598 		pathno, tileno);
599     } else if (fill_style > UNFILLED) {	/* && fill_style <= NUMFILLS */
600 	fprintf(tfp, " fill=\"#%6.6x\"", rgbFillVal(fill_color, fill_style));
601     }
602 }
603 
604 void
continue_paint_w_clip(int fill_style,int pen_color,int fill_color)605 continue_paint_w_clip(int fill_style, int pen_color, int fill_color)
606 {
607     if (fill_style > UNFILLED) {
608 	fprintf(tfp, " id=\"p%d\"/>\n", ++pathno);
609 	if (fill_style > NUMFILLS) {
610 	    generate_tile(fill_style - NUMFILLS, pen_color);
611 	}
612 	fputs("</defs>\n", tfp);
613 	fprintf(tfp, "<use xlink:href=\"#p%d\" ", pathno);
614 	if (fill_style > NUMFILLS) {
615 	    fprintf(tfp, "fill=\"#%6.6x\"/>\n", rgbColorVal(fill_color));
616 	    fprintf(tfp, "<use xlink:href=\"#p%d\" fill=\"url(#tile%d)\"/> ",
617 		    pathno, tileno);
618 	} else {
619 	    fprintf(tfp, "fill=\"#%6.6x\"/>\n",
620 			rgbFillVal(fill_color, fill_style));
621 	}
622 	fprintf(tfp, "<use xlink:href=\"#p%d\"", pathno);
623     }
624     fprintf(tfp, " clip-path=\"url(#cp%d)\"", clipno);
625 }
626 
627 void
gensvg_line(F_line * l)628 gensvg_line(F_line *l)
629 {
630     char	chars;
631     int		px,py;
632     int		px2,py2,width,height,rotation;
633     F_point	*p;
634 
635 
636     if (l->type == T_PIC_BOX ) {
637 	fprintf(tfp,"<!-- Image -->\n");
638 	fprintf(tfp,
639 	    "<image xlink:href=\"file://%s\" preserveAspectRatio=\"none\"\n",
640 	    l->pic->file);
641 	p = l->points;
642 	px = p->x;
643 	py = p->y;
644 	px2 = p->next->next->x;
645 	py2 = p->next->next->y;
646 	width = px2 - px;
647 	height = py2 - py;
648 	rotation = 0;
649 	if (width<0 && height < 0)
650 		rotation = 180;
651 	else if (width < 0 && height >= 0)
652 		rotation = 90;
653 	else if (width >= 0 && height <0)
654 		rotation = 270;
655 	if (l->pic->flipped) rotation -= 90;
656 	if (width < 0) {
657 	    px = px2;
658 	    width = -width;
659 	}
660 	if (height < 0) {
661 	    py = py2;
662 	    height = -height;
663 	}
664 	px2 = px + width/2;
665 	py2 = py + height/2;
666 	if (l->pic->flipped) {
667 	    fprintf(tfp,
668 		"transform=\"rotate(%d %d %d) scale(-1,1) translate(%d,%d)\"\n",
669 		rotation, px2, py2, -2*px2, 0);
670 	} else if (rotation !=0) {
671 	fprintf(tfp,"transform=\"rotate(%d %d %d)\"\n",rotation,px2,py2);
672 	}
673 
674 	fprintf(tfp,"x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"/>\n",
675 	px, py, width, height);
676     return;
677     }
678 
679     if (l->thickness <= 0 && l->fill_style == UNFILLED &&
680 			!l->for_arrow && !l->back_arrow)
681 	return;
682 
683     /* l->type == T_BOX, T_ARC_BOX, T_POLYGON or T_POLYLINE */
684     fprintf(tfp, "<!-- Line -->\n");
685     print_svgcomments("<!-- ", l->comments, " -->\n");
686 
687     if (l->type == T_BOX || l->type == T_ARC_BOX || l->type == T_POLYGON) {
688 
689 	INIT_PAINT(l->fill_style);
690 
691 	if (l->type == T_POLYGON) {
692 	    chars = fputs("<polygon points=\"", tfp);
693 	    for (p = l->points; p->next; p = p->next) {
694 		chars += fprintf(tfp, " %d,%d", p->x , p->y);
695 		if (chars > SVG_LINEWIDTH) {
696 		    fputc('\n', tfp);
697 		    chars = 0;
698 		}
699 	    }
700 	    fputc('\"', tfp);
701 	} else {	/* T_BOX || T_ARC_BOX */
702 	    px = l->points->next->next->x;
703 	    py = l->points->next->next->y;
704 	    width = l->points->x - px;
705 	    height = l->points->y - py;
706 	    if (width < 0) {
707 		px = l->points->x;
708 		width = -width;
709 	    }
710 	    if (height < 0) {
711 		py = l->points->y;
712 		height = -height;
713 	    }
714 
715 	    fprintf(tfp, "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\"",
716 			px, py, width, height);
717 	    if (l->type == T_ARC_BOX)
718 		fprintf(tfp, " rx=\"%d\"", l->radius);
719 	}
720 
721     continue_paint(l->fill_style, l->pen_color, l->fill_color);
722 
723     /* http://jwatt.org/SVG Authoring Guidelines.html recommends to
724        use px unit for stroke width */
725     if (l->thickness) {
726 	fprintf(tfp, "\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\"",
727 		rgbColorVal(l->pen_color),
728 		(int) ceil(linewidth_adj(l->thickness)));
729 	put_joinstyle(l->join_style);
730 	put_capstyle(l->cap_style);
731 	if (l->style > SOLID_LINE)
732 	    svg_dash(l->style, l->style_val);
733     }
734     fputs("/>\n", tfp);
735 
736     return;
737     }
738 
739     if (l->type == T_POLYLINE) {
740 	bool	has_clip = false;
741 
742 	if (l->for_arrow || l->back_arrow) {
743 	    has_clip = svg_arrows(l->thickness, l->for_arrow, l->back_arrow,
744 			    &(l->last[1]), l->last, (F_pos *)l->points->next,
745 			    (F_pos *)l->points, INIT);
746 	    if (l->fill_style == UNFILLED && l->thickness <= 0) {
747 		(void) svg_arrows(l->thickness, l->for_arrow, l->back_arrow,
748 			    &(l->last[1]), l->last, (F_pos *)l->points->next,
749 			    (F_pos *)l->points, l->pen_color);
750 		return;
751 	    }
752 	}
753 
754 	if (has_clip) {
755 	    INIT_PAINT_W_CLIP(l->fill_style, l->thickness, l->for_arrow,
756 		    l->back_arrow, &(l->last[1]), l->last,
757 		    (F_pos *)l->points->next, (F_pos *)l->points);
758 	} else {
759 	    INIT_PAINT(l->fill_style);
760 	}
761 
762 	chars = fputs("<polyline points=\"", tfp);
763 	for (p = l->points; p; p = p->next) {
764 	    chars += fprintf(tfp, " %d,%d", p->x , p->y);
765 	    if (chars > SVG_LINEWIDTH) {
766 		fputc('\n', tfp);
767 		chars = 0;
768 	    }
769 	}
770 	fputc('\"', tfp);
771 
772 	if (has_clip)
773 	    continue_paint_w_clip(l->fill_style, l->pen_color, l->fill_color);
774 	else
775 	    continue_paint(l->fill_style, l->pen_color, l->fill_color);
776 
777 	if (l->thickness) {
778 	    fprintf(tfp, "\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\"",
779 		    rgbColorVal(l->pen_color),
780 		    (int) ceil(linewidth_adj(l->thickness)));
781 	    put_joinstyle(l->join_style);
782 	    put_capstyle(l->cap_style);
783 	    if (l->style > SOLID_LINE)
784 		svg_dash(l->style, l->style_val);
785 	}
786 
787 	fputs("/>\n", tfp);
788 	if (l->for_arrow || l->back_arrow)
789 	    (void) svg_arrows(l->thickness, l->for_arrow, l->back_arrow,
790 			&(l->last[1]), l->last, (F_pos *)l->points->next,
791 			(F_pos *)l->points, l->pen_color);
792     }	/* l->type == T_POLYLINE */
793 }
794 
795 void
gensvg_spline(F_spline * s)796 gensvg_spline( /* not used by fig2dev */
797 	F_spline *s)
798 {
799     F_point *p;
800     fprintf(tfp, "<!-- Spline -->\n");
801     print_svgcomments("<!-- ", s->comments, " -->\n");
802 
803     fprintf(tfp, "<path style=\"stroke:#%6.6x;stroke-width:%d\" d=\"",
804 	     rgbColorVal(s->pen_color), (int) ceil (linewidth_adj(s->thickness)));
805     fprintf(tfp, "M %d,%d\n C", s->points->x , s->points->y );
806     for (p = s->points++; p; p = p->next) {
807 	fprintf(tfp, "%d,%d\n", p->x , p->y );
808     }
809     fprintf(tfp, "\"/>\n");
810 }
811 
812 void
gensvg_arc(F_arc * a)813 gensvg_arc(F_arc *a)
814 {
815     bool    has_clip = false;
816     double  radius;
817     double  x, y, angle, dx, dy;
818     F_pos   forw1, forw2, back1, back2;
819 
820     if (a->fill_style == UNFILLED && a->thickness <= 0 &&
821 	    !a->for_arrow && !a->back_arrow)
822 	return;
823 
824     fputs("<!-- Arc -->\n", tfp);
825     print_svgcomments("<!-- ", a->comments, " -->\n");
826 
827     if (a->for_arrow || a->back_arrow) {
828 	if (a->for_arrow) {
829 	    forw2.x = a->point[2].x;
830 	    forw2.y = a->point[2].y;
831 	    compute_arcarrow_angle(a->center.x, a->center.y,
832 		    (double) forw2.x, (double) forw2.y, a->direction,
833 		    a->for_arrow, &(forw1.x), &(forw1.y));
834 	}
835 	if (a->back_arrow) {
836 	    back2.x = a->point[0].x;
837 	    back2.y = a->point[0].y;
838 	    compute_arcarrow_angle(a->center.x, a->center.y,
839 		    (double) back2.x, (double) back2.y, a->direction ^ 1,
840 		    a->back_arrow, &(back1.x), &(back1.y));
841 	}
842 	has_clip = svg_arrows(a->thickness, a->for_arrow, a->back_arrow,
843 				&forw1, &forw2, &back1, &back2, INIT);
844 	if (a->fill_style == UNFILLED && a->thickness <= 0) {
845 	    (void) svg_arrows(a->thickness, a->for_arrow, a->back_arrow,
846 				&forw1, &forw2, &back1, &back2, a->pen_color);
847 	    return;
848 	}
849     }
850 
851     dx = a->point[0].x - a->center.x;
852     dy = a->point[0].y - a->center.y;
853     radius = sqrt(dx * dx + dy * dy);
854 
855     x = (a->point[0].x-a->center.x) * (a->point[2].x-a->center.x) +
856 		(a->point[0].y-a->center.y) * (a->point[2].y-a->center.y);
857     y = (a->point[0].x-a->center.x) * (a->point[2].y-a->center.y) -
858 		(a->point[0].y-a->center.y) * (a->point[2].x-a->center.x);
859 
860     if (x == 0.0 && y == 0.0)
861 	angle=0.0;
862     else
863 	angle = atan2(y,x);
864     if (angle < 0.0) angle += 2.*M_PI;
865     angle *= 180./M_PI;
866     if (a->direction == 1)
867 	angle = 360. - angle;
868 
869     if (has_clip) {
870 	INIT_PAINT_W_CLIP(a->fill_style, a->thickness, a->for_arrow,
871 		a->back_arrow, &forw1, &forw2, &back1, &back2);
872     } else {
873 	INIT_PAINT(a->fill_style);
874     }
875 
876     /* paint the object */
877     fputs("<path d=\"M", tfp);
878     if (a->type == T_PIE_WEDGE_ARC)
879 		fprintf(tfp, " %ld,%ld L",
880 			lround(a->center.x), lround(a->center.y));
881     fprintf(tfp, " %d,%d A %ld %ld %d %d %d %d %d",
882 	     a->point[0].x , a->point[0].y ,
883 	     lround(radius), lround(radius), 0,
884 	     (fabs(angle) > 180.) ? 1 : 0,
885 	     (fabs(angle) > 0. && a->direction == 0) ? 1 : 0,
886 	     a->point[2].x , a->point[2].y );
887     if (a->type == T_PIE_WEDGE_ARC)
888 	fputs(" z", tfp);
889     fputc('\"', tfp);
890 
891     if (has_clip)
892 	continue_paint_w_clip(a->fill_style, a->pen_color, a->fill_color);
893     else
894 	continue_paint(a->fill_style, a->pen_color, a->fill_color);
895 
896     if (a->thickness) {
897 	fprintf(tfp, "\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\"",
898 		rgbColorVal(a->pen_color),
899 		(int) ceil(linewidth_adj(a->thickness)));
900 	put_capstyle(a->cap_style);
901 	if (a->style > SOLID_LINE)
902 	    svg_dash(a->style, a->style_val);
903     }
904 
905     fputs("/>\n", tfp);
906     if (a->for_arrow || a->back_arrow)
907 	(void) svg_arrows(a->thickness, a->for_arrow, a->back_arrow,
908 			&forw1, &forw2, &back1, &back2, a->pen_color);
909 }
910 
911 void
gensvg_ellipse(F_ellipse * e)912 gensvg_ellipse(F_ellipse *e)
913 {
914     int cx = e->center.x ;
915     int cy = e->center.y ;
916 
917     if (e->type == T_CIRCLE_BY_RAD || e->type == T_CIRCLE_BY_DIA) {
918 	int r = e->radiuses.x ;
919 	fputs("<!-- Circle -->\n", tfp);
920 	print_svgcomments("<!-- ", e->comments, " -->\n");
921 
922 	INIT_PAINT(e->fill_style);
923 
924 	/* paint the object */
925 	fprintf(tfp, "<circle cx=\"%d\" cy=\"%d\" r=\"%d\"", cx, cy, r);
926 
927     } else { /* T_ELLIPSE_BY_RAD or T_ELLIPSE_BY_DIA */
928 	int rx = e->radiuses.x ;
929 	int ry = e->radiuses.y ;
930 	fputs("<!-- Ellipse -->\n", tfp);
931 	print_svgcomments("<!-- ", e->comments, " -->\n");
932 
933 	INIT_PAINT(e->fill_style);
934 
935 	/* now paint object */
936 	if (e->angle == 0.0)
937 	    fprintf(tfp, "<ellipse cx=\"%d\" cy=\"%d\"", cx, cy);
938 	else
939 	    fprintf(tfp,
940 		    "<ellipse transform=\"translate(%d,%d) rotate(%.0f)\"",
941 		    cx, cy, degrees(e->angle));
942 	fprintf(tfp, " rx=\"%d\" ry=\"%d\"", rx, ry);
943     } /* end T_CIRCLE... or T_ELLIPSE... */
944 
945     continue_paint(e->fill_style, e->pen_color, e->fill_color);
946 
947     if (e->thickness) {
948 	fprintf(tfp, "\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\"",
949 		rgbColorVal(e->pen_color),
950 		(int) ceil(linewidth_adj(e->thickness)));
951 	if (e->style > SOLID_LINE)
952 	    svg_dash(e->style, e->style_val);
953     }
954     fputs("/>\n", tfp);
955 }
956 
957 void
gensvg_text(F_text * t)958 gensvg_text(F_text *t)
959 {
960 	unsigned char *cp;
961 	int ch;
962 	const char *anchor[3] = { "start", "middle", "end" };
963 	const char *family[9] = { "Times", "AvantGarde",
964 		"Bookman", "Courier", "Helvetica", "Helvetica Narrow",
965 		"New Century Schoolbook", "Palatino", "Times,Symbol"
966 	};
967 	int x = t->base_x ;
968 	int y = t->base_y ;
969 #ifdef NOSUPER
970 	int dy = 0;
971 #endif
972 
973 	fprintf(tfp, "<!-- Text -->\n");
974 	print_svgcomments("<!-- ", t->comments, " -->\n");
975 
976 	if (t->angle != 0.) {
977 		fprintf(tfp,
978 			"<g transform=\"translate(%d,%d) rotate(%.0f)\" >\n",
979 			x, y, degrees(t->angle));
980 	x = y = 0;
981 	}
982 	fputs("<text xml:space=\"preserve\" ", tfp);
983 	fprintf(tfp, "x=\"%d\" y=\"%d\" fill=\"#%6.6x\" font-family=\"%s\" ",
984 			x, y, rgbColorVal(t->color), family[t->font / 4]);
985 	fprintf(tfp,
986 		"font-style=\"%s\" font-weight=\"%s\" font-size=\"%d\" text-anchor=\"%s\">",
987 		((t->font % 2 == 0 || t->font > 31) ? "normal" : "italic"),
988 		((t->font % 4 < 2 || t->font > 31) ? "normal" : "bold"),
989 		(int)ceil(t->size * 12), anchor[t->type]);
990 
991 	if (t->font == 32) {
992 		for (cp = (unsigned char *)t->cstring; *cp; ++cp) {
993 			ch = *cp;
994 			fprintf(tfp, "&#%d;", symbolchar[ch]);
995 		}
996 	} else if (t->font == 34) {
997 		for (cp = (unsigned char *)t->cstring; *cp; ++cp) {
998 			ch = *cp;
999 			fprintf(tfp, "&#%d;", dingbatchar[ch]);
1000 		}
1001 	} else if (special_text(t)) {
1002 		int supsub = 0;
1003 #ifdef NOSUPER
1004 		int old_dy=0;
1005 #endif
1006 		for (cp = (unsigned char *)t->cstring; *cp; cp++) {
1007 			ch = *cp;
1008 			if (( supsub == 2 &&ch == '}' ) || supsub==1) {
1009 #ifdef NOSUPER
1010 				fprintf(tfp,"</tspan><tspan dy=\"%d\">",-dy);
1011 				old_dy=-dy;
1012 #else
1013 				fprintf(tfp,"</tspan>");
1014 #endif
1015 				supsub = 0;
1016 				if (ch == '}') {
1017 					++cp;
1018 					ch = *cp;
1019 				}
1020 			}
1021 			if (ch == '_' || ch == '^') {
1022 				supsub=1;
1023 #ifdef NOSUPER
1024 				if (dy != 0)
1025 					fprintf(tfp,"</tspan>");
1026 				if (ch == '_')
1027 					dy = 35.;
1028 				if (ch == '^')
1029 					dy = -50.;
1030 				fprintf(tfp,
1031 					"<tspan font-size=\"%d\" dy=\"%d\">",
1032 					(int)ceil(t->size * 8), dy + old_dy);
1033 				old_dy = 0;
1034 #else
1035 				fprintf(tfp,
1036 					"<tspan font-size=\"%d\" baseline-shift=\"",
1037 					(int)ceil(t->size * 8));
1038 				if (ch == '_')
1039 					fprintf(tfp,"sub\">");
1040 				if (ch == '^')
1041 					fprintf(tfp,"super\">");
1042 #endif
1043 				++cp;
1044 				ch = *cp;
1045 				if (ch == '{' ) {
1046 					supsub=2;
1047 					++cp;
1048 					ch = *cp;
1049 				}
1050 			}
1051 #ifdef NOSUPER
1052 			else old_dy=0;
1053 #endif
1054 			if (ch != '$')
1055 				put_sanitized_char(ch);
1056 		}
1057 	} else {
1058 		for (cp = (unsigned char *)t->cstring; *cp; ++cp)
1059 			put_sanitized_char((int)*cp);
1060 	}
1061 #ifdef NOSUPER
1062 	if (dy != 0)
1063 		fprintf(tfp,"</tspan>");
1064 #endif
1065 	fprintf(tfp, "</text>\n");
1066 	if (t->angle != 0)
1067 		fprintf(tfp, "</g>");
1068 }
1069 
1070 static void
arrow_path(F_arrow * arrow,F_pos * arrow2,int pen_color,int npoints,F_pos points[],int nfillpoints,F_pos * fillpoints,int nclippoints,F_pos clippoints[])1071 arrow_path(F_arrow *arrow, F_pos *arrow2, int pen_color, int npoints,
1072 	F_pos points[], int nfillpoints, F_pos *fillpoints
1073 #ifdef DEBUG
1074 	, int nclippoints, F_pos clippoints[]
1075 #endif
1076 	)
1077 {
1078     int	    i, chars;
1079 
1080     fprintf(tfp, " to point %d,%d -->\n", arrow2->x, arrow2->y);
1081     chars = fprintf(tfp, "<%s points=\"",
1082 	    (points[0].x == points[npoints-1].x &&
1083 	     points[0].y == points[npoints-1].y ? "polygon" : "polyline"));
1084     for (i = 0; i < npoints; ++i) {
1085 	chars += fprintf(tfp, " %d,%d", points[i].x ,
1086 		points[i].y );
1087 	if (chars > SVG_LINEWIDTH) {
1088 	    fputc('\n', tfp);
1089 	    chars = 0;
1090 	}
1091     }
1092     fprintf(tfp,
1093 	"\"\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\" stroke-miterlimit=\"8\"",
1094 	rgbColorVal(pen_color),
1095 	(int) ceil(linewidth_adj((int)arrow->thickness)));
1096     if (arrow->type < 13 && (arrow->style != 0 || nfillpoints != 0)) {
1097 	if (nfillpoints == 0)
1098 	    fprintf(tfp, " fill=\"#%6.6x\"/>\n", rgbColorVal(pen_color));
1099 	else { /* fill the special area */
1100 	    fprintf(tfp, "/>\n<path d=\"M ");
1101 	    for (i = 0; i < nfillpoints; ++i) {
1102 		fprintf(tfp, "%d,%d ", fillpoints[i].x ,
1103 			fillpoints[i].y );
1104 	    }
1105 	    fprintf(tfp, "z\"\n\tstroke=\"#%6.6x\" stroke-width=\"%dpx\"",
1106 		    rgbColorVal(pen_color),
1107 		    (int) ceil(linewidth_adj((int)arrow->thickness)));
1108 	    fprintf(tfp, " stroke-miterlimit=\"8\" fill=\"#%6.6x\"/>\n",
1109 		    rgbColorVal(pen_color));
1110 	}
1111     } else
1112       fprintf(tfp, "/>\n");
1113 #ifdef DEBUG
1114     /* paint the clip path */
1115     if (nclippoints) {
1116 	fputs("<!-- clip path -->\n<path d=\"M", tfp);
1117 	for (i = 0; i < nclippoints; ++i)
1118 	    fprintf(tfp,"%d,%d ", clippoints[i].x, clippoints[i].y);
1119 	fputs("z\"\n\tstroke=\"red\" opacity=\"0.5\" stroke-width=\"10px\" stroke-miterlimit=\"8\"/>\n", tfp);
1120     }
1121 #endif
1122 }
1123 
1124 static bool
svg_arrows(int line_thickness,F_arrow * for_arrow,F_arrow * back_arrow,F_pos * forw1,F_pos * forw2,F_pos * back1,F_pos * back2,int pen_color)1125 svg_arrows(int line_thickness, F_arrow *for_arrow, F_arrow *back_arrow,
1126 	F_pos *forw1, F_pos *forw2, F_pos *back1, F_pos *back2, int pen_color)
1127 {
1128     static int		fnpoints, fnfillpoints, fnclippoints;
1129     static int		bnpoints, bnfillpoints, bnclippoints;
1130     static F_pos	fpoints[50], ffillpoints[50], fclippoints[50];
1131     static F_pos	bpoints[50], bfillpoints[50], bclippoints[50];
1132     int			i;
1133 
1134     if (pen_color == INIT) {
1135 	if (for_arrow) {
1136 	    calc_arrow(forw1->x, forw1->y, forw2->x, forw2->y,
1137 		    line_thickness, for_arrow, fpoints, &fnpoints,
1138 		    ffillpoints, &fnfillpoints, fclippoints, &fnclippoints);
1139 	}
1140 	if (back_arrow) {
1141 	    calc_arrow(back1->x, back1->y, back2->x, back2->y,
1142 		    line_thickness, back_arrow, bpoints, &bnpoints,
1143 		    bfillpoints, &bnfillpoints, bclippoints, &bnclippoints);
1144 	}
1145 	if (fnclippoints || bnclippoints)
1146 	    return true;
1147 	else
1148 	    return false;
1149     }
1150 
1151     if (pen_color == CLIP) {
1152 	fprintf(tfp, "<clipPath id=\"cp%d\">\n", ++clipno);
1153 	fprintf(tfp,
1154 		"\t<path clip-rule=\"evenodd\" d=\"M %d,%d H %d V %d H %d z",
1155 		llx, lly, urx, ury, llx);
1156 	if (fnclippoints) {
1157 	    fprintf(tfp, "\n\t\tM %d,%d", fclippoints[0].x,fclippoints[0].y);
1158 	    for (i = 1; i < fnclippoints; ++i)
1159 		fprintf(tfp, " %d,%d", fclippoints[i].x, fclippoints[i].y);
1160 	    fputc('z', tfp);
1161 	}
1162 	if (bnclippoints) {
1163 	    fprintf(tfp, "\n\t\tM %d,%d", bclippoints[0].x,bclippoints[0].y);
1164 	    for (i = 1; i < bnclippoints; ++i)
1165 		fprintf(tfp, " %d,%d", bclippoints[i].x, bclippoints[i].y);
1166 	    fputc('z', tfp);
1167 	}
1168 	fputs("\"/>\n</clipPath>\n", tfp);
1169 	return true;
1170     }
1171 
1172     if (for_arrow) {
1173 	fputs("<!-- Forward arrow", tfp);
1174 	arrow_path(for_arrow, forw2, pen_color, fnpoints, fpoints,
1175 		fnfillpoints, ffillpoints
1176 #ifdef DEBUG
1177 		, fnclippoints, fclippoints
1178 #endif
1179 		);
1180     }
1181     if (back_arrow) {
1182 	fputs("<!-- Backward arrow", tfp);
1183 	arrow_path(back_arrow, back2, pen_color, bnpoints, bpoints,
1184 		bnfillpoints, bfillpoints
1185 #ifdef DEBUG
1186 		, bnclippoints, bclippoints
1187 #endif
1188 		);
1189     }
1190     return true;
1191 }
1192 
1193 static void
generate_tile(int number,int colorIndex)1194 generate_tile(int number, int colorIndex)
1195 {
1196     static const struct pattern {
1197 	char*	size;
1198 	char*	code;
1199     }	pattern[NUMPATTERNS] = {
1200 	/* 0	30 degrees left diagonal */
1201 	{"width=\"134\" height=\"67\">",
1202 	 "\"M -7,30 73,70 M 61,-3 141,37\""},
1203 	/* 1 	30 degrees right diagonal */
1204 	{"width=\"134\" height=\"67\">",
1205 	 /* M 0 33.5 67 0 M 67 67 134 33.5 */
1206 	 "\"M -7,37 73,-3 M 61,70 141,30\""},
1207 	 /* 2	30 degrees crosshatch */
1208 	{"width=\"134\" height=\"67\">",
1209 	 "\"M -7,30 73,70 M 61,-3 141,37 M -7,37 73,-3 M 61,70 141,30\""},
1210 	 /* 3	45 degrees left diagonal */
1211 	{"width=\"134\" height=\"134\">",
1212 	 "\"M -4,63 71,138 M 63,-4 138,71\""},
1213 	 /* 4	45 degrees right diagonal */
1214 	{"width=\"134\" height=\"134\">",
1215 	 "\"M -4,71 71,-4 M 63,138 138,63\""},
1216 	 /* 5	45 degrees crosshatch */
1217 	{"width=\"134\" height=\"134\">",
1218 	 "\"M-4,63 71,138 M63,-4 138,71 M-4,71 71,-4 M63,138 138,63\""},
1219 	 /* 6	horizontal bricks */
1220 	{"width=\"268\" height=\"268\">",
1221 	 "\"M-1,67 H269 M-1,201 H269 M67,-1 V67 M67,201 V269 M201,67 V201\""},
1222 	 /* 7	vertical bricks */
1223 	{"width=\"268\" height=\"268\">",
1224 	 "\"M67,-1 V269 M201,-1 V269 M-1,67 H67 M201,67 H269 M67,201 H201\""},
1225 	 /* 8	horizontal lines */
1226 	{"width=\"268\" height=\"67\">",
1227 	 "\"M -1,30 H 269\""},
1228 	 /* 9	vertical lines */
1229 	{"width=\"67\" height=\"268\">",
1230 	 "\"M 30,-1 V 269\""},
1231 	 /* 10	crosshatch */
1232 	{"width=\"67\" height=\"67\">",
1233 	 "\"M -1,30 H 68 M 30,-1 V 68\""},
1234 	 /* 11	left-pointing shingles */
1235 	{"width=\"402\" height=\"402\">",
1236 	 "\"M-1,30 H403 M-1,164 H403 M-1,298 H403 M238,30 l-67,134 M372,164 l-67,134 M104,298 l-60,120 M37,30 l20,-40\""},
1237 	 /* 12	right-pointing shingles */
1238 	{"width=\"402\" height=\"402\">",
1239 	 "\"M-1,30 H403 M-1,164 H403 M-1,298 H403 M164,30 l67,134 M30,164 l67,134 M298,298 l60,120 M365,30 l-20,-40\""},
1240 	 /* 13  vertical left-pointing shingles */
1241 	{"width=\"402\" height=\"402\">",
1242 	 "\"M30,-1 V403 M164,-1 V403 M298,-1 V403 M30,164 l134,67 M164,30 l134,67 M298,298 l120,60 M30,365 l-40,-20\""},
1243 	 /* 14	vertical right-pointing shingles */
1244 	{"width=\"402\" height=\"402\">",
1245 	 "\"M30,-1 V403 M164,-1 V403 M298,-1 V403 M30,238 l134,-67 M164,372 l134,-67 M298,104 l120,-60 M30,37 l-40,20\""},
1246 	 /* 15	fish scales */
1247 	{"width=\"268\" height=\"140\">",
1248 	 "\"M-104,-30 a167.5,167.5 0 0,0 268,0 a167.5,167.5 0 0,0 134,67 m0,3 a167.5,167.5 0 0,1 -268,0 a167.5,167.5 0 0,1 -134,67 m134,70 a167.5,167.5 0 0,0 134,-67 a167.5,167.5 0 0,0 134,67\""},
1249 	 /* 16	small fish scales */
1250 	{"width=\"134\" height=\"134\">",
1251 	 "\"M164,-30 a67,67 0 0,1 -134,0 a67,67 0 0,1 -67,67 a67,67 0 0,0 134,0 a67,67 0 0,0 67,67 a67,67 0 0,1 -134,0 a67,67 0 0,1 -67,67\""},
1252 	 /* 17	circles */
1253 	{"width=\"268\" height=\"268\">",
1254 	 "\"M0,134 a134,134 0 0,0 134,-134 a134,134 0 0,0 134,134 a134,134 0 0,0 -134,134 a134,134 0 0,0 -134,-134\""},
1255 	 /* 18	hexagons */
1256 	{"width=\"402\" height=\"232\">",
1257 	 "\"m97,-86 -67,116 67,116 -67,116 M231,-86 l67,116 l-67,116 l67,116 M-1,30 h31 m268,0 h105 M97,146 h134\""},
1258 	 /* 19	octagons */
1259 	{"width=\"280\" height=\"280\">",
1260 	 "\"m-1,140 59,0 82,-82 82,82 -82,82 -82,-82 m82,82 v59 m0,-282 v59 m82,82 h59\""},
1261 	 /* 20	horizontal sawtooth */
1262 	{"width=\"134\" height=\"134\">",
1263 	 "\"m-4,63 67,67 67,-67 20,20\""},
1264 	 /* 21	vertical sawtooth */
1265 	{"width=\"134\" height=\"134\">",
1266 	 "\"m63,-4 67,67 -67,67 20,20\""},
1267     };
1268 
1269     fprintf(tfp,
1270 	    "<pattern id=\"tile%d\" patternUnits=\"userSpaceOnUse\"\n",
1271 	    ++tileno);
1272     fputs("\tx=\"0\" y=\"0\" ", tfp);
1273     fputs(pattern[number - 1].size, tfp);
1274     /* Draw pattern lines with a width of .45 bp ( = 7.5 Fig units at
1275        ppi = 1200), consistent with line widths in gentikz.c.
1276        In genps.c, patterns are drawn with a linewidth of 1 bp
1277        ( = 16.6 Fig units, at 1200 ppi), or .7 bp ( = 11.7 Fig units). */
1278     fprintf(tfp,
1279 	    "\n<g stroke-width=\"%.2g\" stroke=\"#%6.6x\" fill=\"none\">\n",
1280 	    0.5*ppi/80., rgbColorVal(colorIndex));
1281     fputs("<path d=", tfp);
1282     fputs(pattern[number - 1].code, tfp);
1283     fputs("/>\n</g>\n</pattern>\n", tfp);
1284 }
1285 
1286 static void
svg_dash(int style,double val)1287 svg_dash(int style, double val)
1288 {
1289 	fprintf(tfp, " stroke-dasharray=\"");
1290 	switch(style) {
1291 	case 1:
1292 	default:
1293 		fprintf(tfp,"%ld %ld\"", lround(val*10), lround(val*10));
1294 		break;
1295 	case 2:
1296 		fprintf(tfp,"10 %ld\"", lround(val*10));
1297 		break;
1298 	case 3:
1299 		fprintf(tfp,"%ld %ld 10 %ld\"", lround(val*10),
1300 			lround(val*5), lround(val*5));
1301 		break;
1302 	case 4:
1303 		fprintf(tfp,"%ld %ld 10 %ld 10 %ld\"", lround(val*10),
1304 			lround(val*3), lround(val*3), lround(val*3));
1305 		break;
1306 	case 5:
1307 		fprintf(tfp,"%ld %ld 10 %ld 10 %ld 10 %ld\"", lround(val*10),
1308 			lround(val*3), lround(val*3), lround(val*3),
1309 			lround(val*3));
1310 		break;
1311 	}
1312 }
1313 
1314 /* driver defs */
1315 
1316 struct driver dev_svg = {
1317 	gensvg_option,
1318 	gensvg_start,
1319 	gendev_nogrid,
1320 	gensvg_arc,
1321 	gensvg_ellipse,
1322 	gensvg_line,
1323 	gensvg_spline,
1324 	gensvg_text,
1325 	gensvg_end,
1326 	INCLUDE_TEXT
1327 };
1328