2b9a79d3d106b497b72688c68b145bae62db4aea
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 "preferences.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);
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;
169 }
171 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
173 gchar *
174 sp_svg_transform_write(Geom::Matrix const &transform)
175 {
176 return sp_svg_transform_write(&transform);
177 }
180 gchar *
181 sp_svg_transform_write(Geom::Matrix const *transform)
182 {
183 NR::Matrix const t(*transform);
184 return sp_svg_transform_write(&t);
185 }
187 gchar *
188 sp_svg_transform_write(NR::Matrix const &transform)
189 {
190 NR::Matrix const t(transform);
191 return sp_svg_transform_write(&t);
192 }
194 gchar *
195 sp_svg_transform_write(NR::Matrix const *transform)
196 {
197 if (!transform) {
198 return NULL;
199 }
200 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
202 double e = 0.000001 * NR::expansion(*transform);
203 int prec = prefs->getInt("/options/svgoutput/numericprecision", 8);
204 int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8);
206 /* fixme: We could use t1 * t1 + t2 * t2 here instead */
207 if (NR_DF_TEST_CLOSE ((*transform)[1], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[2], 0.0, e)) {
208 if (NR_DF_TEST_CLOSE ((*transform)[4], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[5], 0.0, e)) {
209 if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
210 /* We are more or less identity */
211 return NULL;
212 } else {
213 /* We are more or less scale */
214 gchar c[256];
215 unsigned p = 0;
216 strcpy (c + p, "scale(");
217 p += 6;
218 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
219 c[p++] = ',';
220 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
221 c[p++] = ')';
222 c[p] = '\000';
223 g_assert( p <= sizeof(c) );
224 return g_strdup(c);
225 }
226 } else {
227 if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
228 /* We are more or less translate */
229 gchar c[256];
230 unsigned p = 0;
231 strcpy (c + p, "translate(");
232 p += 10;
233 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
234 c[p++] = ',';
235 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
236 c[p++] = ')';
237 c[p] = '\000';
238 g_assert( p <= sizeof(c) );
239 return g_strdup(c);
240 } else {
241 gchar c[256];
242 unsigned p = 0;
243 strcpy (c + p, "matrix(");
244 p += 7;
245 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
246 c[p++] = ',';
247 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp);
248 c[p++] = ',';
249 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp);
250 c[p++] = ',';
251 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
252 c[p++] = ',';
253 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
254 c[p++] = ',';
255 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
256 c[p++] = ')';
257 c[p] = '\000';
258 g_assert( p <= sizeof(c) );
259 return g_strdup(c);
260 }
261 }
262 } else {
263 gchar c[256];
264 unsigned p = 0;
265 strcpy (c + p, "matrix(");
266 p += 7;
267 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp);
268 c[p++] = ',';
269 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp);
270 c[p++] = ',';
271 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp);
272 c[p++] = ',';
273 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp);
274 c[p++] = ',';
275 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp);
276 c[p++] = ',';
277 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp);
278 c[p++] = ')';
279 c[p] = '\000';
280 g_assert( p <= sizeof(c) );
281 return g_strdup(c);
282 }
283 }