Code

Merging from trunk
[inkscape.git] / src / svg / svg-affine.cpp
1 #define __SP_SVG_AFFINE_C__
3 /*
4  * SVG data parser
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Raph Levien <raph@acm.org>
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 1999 Raph Levien
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <cstring>
21 #include <string>
22 #include <cstdlib>
23 #include <cstdio>
24 #include <glib/gstrfuncs.h>
25 #include <libnr/nr-matrix-fns.h>
26 #include <libnr/nr-matrix-ops.h>
27 #include <2geom/transforms.h>
28 #include <2geom/angle.h>
29 #include <libnr/nr-convert2geom.h>
30 #include "svg.h"
31 #include "prefs-utils.h"
33 #ifndef M_PI
34 # define M_PI 3.14159265358979323846
35 #endif
37 bool
38 sp_svg_transform_read(gchar const *str, Geom::Matrix *transform)
39 {
40     NR::Matrix mat;
41     if (sp_svg_transform_read(str, &mat)) {
42         *transform = mat;
43         return true;
44     } else {
45         return false;
46     }
47 }
49 bool
50 sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
51 {
52         int idx;
53         char keyword[32];
54         double args[6];
55         int n_args;
56         size_t key_len;
58         if (str == NULL) return false;
60         NR::Matrix a(NR::identity());
62         idx = 0;
63         while (str[idx]) {
64                 /* skip initial whitespace */
65                 while (g_ascii_isspace (str[idx])) idx++;
67                 /* parse keyword */
68                 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
69                         char c;
71                         c = str[idx];
72                         if (g_ascii_isalpha (c) || c == '-') {
73                                 keyword[key_len] = str[idx++];
74                         } else {
75                                 break;
76                         }
77                 }
78                 if (key_len >= sizeof (keyword)) return false;
79                 keyword[key_len] = '\0';
81                 /* skip whitespace */
82                 while (g_ascii_isspace (str[idx])) idx++;
84                 if (str[idx] != '(') return false;
85                 idx++;
87                 for (n_args = 0; ; n_args++) {
88                         char c;
89                         char *end_ptr;
91                         /* skip whitespace */
92                         while (g_ascii_isspace (str[idx])) idx++;
93                         c = str[idx];
94                         if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
95                                 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
96                                 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
97                                 
98                                 //printf("took %d chars from '%s' to make %f\n",
99                                 //              end_ptr-(str+idx),
100                                 //              str+idx,
101                                 //              args[n_args]);
103                                 idx = end_ptr - (char *) str;
105                                 while (g_ascii_isspace (str[idx])) idx++;
107                                 /* skip optional comma */
108                                 if (str[idx] == ',') idx++;
109                         } else if (c == ')') {
110                                 break;
111                         } else {
112                                 return false;
113                         }
114                 }
115                 idx++;
117                 /* ok, have parsed keyword and args, now modify the transform */
118                 if (!strcmp (keyword, "matrix")) {
119                         if (n_args != 6) return false;
120                         a = (*NR_MATRIX_D_FROM_DOUBLE(args)) * a;
121                 } else if (!strcmp (keyword, "translate")) {
122                         if (n_args == 1) {
123                                 args[1] = 0;
124                         } else if (n_args != 2) {
125                                 return false;
126                         }
127                         a = Geom::Translate(args[0], args[1]) * a;
128                 } else if (!strcmp (keyword, "scale")) {
129                         if (n_args == 1) {
130                                 args[1] = args[0];
131                         } else if (n_args != 2) {
132                                 return false;
133                         }
134                         a = Geom::Scale(args[0], args[1]) * a;
135                 } else if (!strcmp (keyword, "rotate")) {
136                         if (n_args != 1 && n_args != 3) {
137                                 return false;
138                         }
139                         Geom::Rotate const rot(Geom::deg_to_rad(args[0]));
140                         if (n_args == 3) {
141                                 a = ( Geom::Translate(-args[1], -args[2])
142                                       * rot
143                                       * Geom::Translate(args[1], args[2])
144                                       * Geom::Matrix(a) );
145                         } else {
146                                 a = rot * a;
147                         }
148                 } else if (!strcmp (keyword, "skewX")) {
149                         if (n_args != 1) return false;
150                         a = ( NR::Matrix(1, 0,
151                                          tan(args[0] * M_PI / 180.0), 1,
152                                          0, 0)
153                               * a );
154                 } else if (!strcmp (keyword, "skewY")) {
155                         if (n_args != 1) return false;
156                         a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
157                                          0, 1,
158                                          0, 0)
159                               * a );
160                 } else {
161                         return false; /* unknown keyword */
162                 }
163                 /* Skip trailing whitespace */
164              while (g_ascii_isspace (str[idx])) idx++;
165         }
167         *transform = a;
168         return true;
171 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
173 gchar *
174 sp_svg_transform_write(Geom::Matrix const &transform)
176     return sp_svg_transform_write(&transform);
180 gchar *
181 sp_svg_transform_write(Geom::Matrix const *transform)
183     NR::Matrix const t(*transform);
184     return sp_svg_transform_write(&t);
187 gchar *
188 sp_svg_transform_write(NR::Matrix const &transform)
190         NR::Matrix const t(transform);
191         return sp_svg_transform_write(&t);
194 gchar *
195 sp_svg_transform_write(NR::Matrix const *transform)
197         double e;
199         if (!transform) {
200                 return NULL;
201         }
203         e = 0.000001 * NR::expansion(*transform);
204         int prec = prefs_get_int_attribute("options.svgoutput", "numericprecision", 8);
205         int min_exp = prefs_get_int_attribute("options.svgoutput", "minimumexponent", -8);
207         /* fixme: We could use t1 * t1 + t2 * t2 here instead */
208         if (NR_DF_TEST_CLOSE ((*transform)[1], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[2], 0.0, e)) {
209                 if (NR_DF_TEST_CLOSE ((*transform)[4], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[5], 0.0, e)) {
210                         if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
211                                 /* We are more or less identity */
212                                 return NULL;
213                         } else {
214                                 /* We are more or less scale */
215                                 gchar c[256];
216                                 unsigned p = 0;
217                                 strcpy (c + p, "scale(");
218                                 p += 6;
219                                 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
220                                 c[p++] = ',';
221                                 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
222                                 c[p++] = ')';
223                                 c[p] = '\000';
224                                 g_assert( p <= sizeof(c) );
225                                 return g_strdup(c);
226                         }
227                 } else {
228                         if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
229                                 /* We are more or less translate */
230                                 gchar c[256];
231                                 unsigned p = 0;
232                                 strcpy (c + p, "translate(");
233                                 p += 10;
234                                 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
235                                 c[p++] = ',';
236                                 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
237                                 c[p++] = ')';
238                                 c[p] = '\000';
239                                 g_assert( p <= sizeof(c) );
240                                 return g_strdup(c);
241                         } else {
242                                 gchar c[256];
243                                 unsigned p = 0;
244                                 strcpy (c + p, "matrix(");
245                                 p += 7;
246                                 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
247                                 c[p++] = ',';
248                                 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp);
249                                 c[p++] = ',';
250                                 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp);
251                                 c[p++] = ',';
252                                 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
253                                 c[p++] = ',';
254                                 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
255                                 c[p++] = ',';
256                                 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
257                                 c[p++] = ')';
258                                 c[p] = '\000';
259                                 g_assert( p <= sizeof(c) );
260                                 return g_strdup(c);
261                         }
262                 }
263         } else {
264                 gchar c[256];
265                 unsigned p = 0;
266                 strcpy (c + p, "matrix(");
267                 p += 7;
268                 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
269                 c[p++] = ',';
270                 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp);
271                 c[p++] = ',';
272                 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp);
273                 c[p++] = ',';
274                 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
275                 c[p++] = ',';
276                 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
277                 c[p++] = ',';
278                 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
279                 c[p++] = ')';
280                 c[p] = '\000';
281                 g_assert( p <= sizeof(c) );
282                 return g_strdup(c);
283         }