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 < > &
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("<", tfp);
427 break;
428 case '>':
429 fputs(">", tfp);
430 break;
431 case '&':
432 fputs("&", 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