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 <libnr/nr-matrix-translate-ops.h>
28 #include <libnr/nr-rotate-fns.h>
29 #include <libnr/nr-rotate-matrix-ops.h>
30 #include <libnr/nr-scale-matrix-ops.h>
31 #include <libnr/nr-translate-matrix-ops.h>
32 #include <libnr/nr-translate-rotate-ops.h>
33 #include <libnr/nr-convert2geom.h>
34 #include "svg.h"
35 #include "prefs-utils.h"
37 #ifndef M_PI
38 # define M_PI 3.14159265358979323846
39 #endif
41 bool
42 sp_svg_transform_read(gchar const *str, Geom::Matrix *transform)
43 {
44 NR::Matrix mat;
45 if (sp_svg_transform_read(str, &mat)) {
46 *transform = to_2geom(mat);
47 return true;
48 } else {
49 return false;
50 }
51 }
53 bool
54 sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
55 {
56 int idx;
57 char keyword[32];
58 double args[6];
59 int n_args;
60 size_t key_len;
62 if (str == NULL) return false;
64 NR::Matrix a(NR::identity());
66 idx = 0;
67 while (str[idx]) {
68 /* skip initial whitespace */
69 while (g_ascii_isspace (str[idx])) idx++;
71 /* parse keyword */
72 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
73 char c;
75 c = str[idx];
76 if (g_ascii_isalpha (c) || c == '-') {
77 keyword[key_len] = str[idx++];
78 } else {
79 break;
80 }
81 }
82 if (key_len >= sizeof (keyword)) return false;
83 keyword[key_len] = '\0';
85 /* skip whitespace */
86 while (g_ascii_isspace (str[idx])) idx++;
88 if (str[idx] != '(') return false;
89 idx++;
91 for (n_args = 0; ; n_args++) {
92 char c;
93 char *end_ptr;
95 /* skip whitespace */
96 while (g_ascii_isspace (str[idx])) idx++;
97 c = str[idx];
98 if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
99 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
100 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
102 //printf("took %d chars from '%s' to make %f\n",
103 // end_ptr-(str+idx),
104 // str+idx,
105 // args[n_args]);
107 idx = end_ptr - (char *) str;
109 while (g_ascii_isspace (str[idx])) idx++;
111 /* skip optional comma */
112 if (str[idx] == ',') idx++;
113 } else if (c == ')') {
114 break;
115 } else {
116 return false;
117 }
118 }
119 idx++;
121 /* ok, have parsed keyword and args, now modify the transform */
122 if (!strcmp (keyword, "matrix")) {
123 if (n_args != 6) return false;
124 a = (*NR_MATRIX_D_FROM_DOUBLE(args)) * a;
125 } else if (!strcmp (keyword, "translate")) {
126 if (n_args == 1) {
127 args[1] = 0;
128 } else if (n_args != 2) {
129 return false;
130 }
131 a = NR::translate(args[0], args[1]) * a;
132 } else if (!strcmp (keyword, "scale")) {
133 if (n_args == 1) {
134 args[1] = args[0];
135 } else if (n_args != 2) {
136 return false;
137 }
138 a = NR::scale(args[0], args[1]) * a;
139 } else if (!strcmp (keyword, "rotate")) {
140 if (n_args != 1 && n_args != 3) {
141 return false;
142 }
143 NR::rotate const rot(rotate_degrees(args[0]));
144 if (n_args == 3) {
145 a = ( NR::translate(-args[1], -args[2])
146 * rot
147 * NR::translate(args[1], args[2])
148 * a );
149 } else {
150 a = rot * a;
151 }
152 } else if (!strcmp (keyword, "skewX")) {
153 if (n_args != 1) return false;
154 a = ( NR::Matrix(1, 0,
155 tan(args[0] * M_PI / 180.0), 1,
156 0, 0)
157 * a );
158 } else if (!strcmp (keyword, "skewY")) {
159 if (n_args != 1) return false;
160 a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
161 0, 1,
162 0, 0)
163 * a );
164 } else {
165 return false; /* unknown keyword */
166 }
167 /* Skip trailing whitespace */
168 while (g_ascii_isspace (str[idx])) idx++;
169 }
171 *transform = a;
172 return true;
173 }
175 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
177 gchar *
178 sp_svg_transform_write(Geom::Matrix const &transform)
179 {
180 return sp_svg_transform_write(&transform);
181 }
184 gchar *
185 sp_svg_transform_write(Geom::Matrix const *transform)
186 {
187 NR::Matrix const t(from_2geom(*transform));
188 return sp_svg_transform_write(&t);
189 }
191 gchar *
192 sp_svg_transform_write(NR::Matrix const &transform)
193 {
194 NR::Matrix const t(transform);
195 return sp_svg_transform_write(&t);
196 }
198 gchar *
199 sp_svg_transform_write(NR::Matrix const *transform)
200 {
201 double e;
203 if (!transform) {
204 return NULL;
205 }
207 e = 0.000001 * NR::expansion(*transform);
208 int prec = prefs_get_int_attribute("options.svgoutput", "numericprecision", 8);
209 int min_exp = prefs_get_int_attribute("options.svgoutput", "minimumexponent", -8);
211 /* fixme: We could use t1 * t1 + t2 * t2 here instead */
212 if (NR_DF_TEST_CLOSE ((*transform)[1], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[2], 0.0, e)) {
213 if (NR_DF_TEST_CLOSE ((*transform)[4], 0.0, e) && NR_DF_TEST_CLOSE ((*transform)[5], 0.0, e)) {
214 if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
215 /* We are more or less identity */
216 return NULL;
217 } else {
218 /* We are more or less scale */
219 gchar c[256];
220 unsigned p = 0;
221 strcpy (c + p, "scale(");
222 p += 6;
223 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp, FALSE);
224 c[p++] = ',';
225 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp, FALSE);
226 c[p++] = ')';
227 c[p] = '\000';
228 g_assert( p <= sizeof(c) );
229 return g_strdup(c);
230 }
231 } else {
232 if (NR_DF_TEST_CLOSE ((*transform)[0], 1.0, e) && NR_DF_TEST_CLOSE ((*transform)[3], 1.0, e)) {
233 /* We are more or less translate */
234 gchar c[256];
235 unsigned p = 0;
236 strcpy (c + p, "translate(");
237 p += 10;
238 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp, FALSE);
239 c[p++] = ',';
240 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp, FALSE);
241 c[p++] = ')';
242 c[p] = '\000';
243 g_assert( p <= sizeof(c) );
244 return g_strdup(c);
245 } else {
246 gchar c[256];
247 unsigned p = 0;
248 strcpy (c + p, "matrix(");
249 p += 7;
250 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp, FALSE);
251 c[p++] = ',';
252 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp, FALSE);
253 c[p++] = ',';
254 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp, FALSE);
255 c[p++] = ',';
256 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp, FALSE);
257 c[p++] = ',';
258 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp, FALSE);
259 c[p++] = ',';
260 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp, FALSE);
261 c[p++] = ')';
262 c[p] = '\000';
263 g_assert( p <= sizeof(c) );
264 return g_strdup(c);
265 }
266 }
267 } else {
268 gchar c[256];
269 unsigned p = 0;
270 strcpy (c + p, "matrix(");
271 p += 7;
272 p += sp_svg_number_write_de (c + p, (*transform)[0], prec, min_exp, FALSE);
273 c[p++] = ',';
274 p += sp_svg_number_write_de (c + p, (*transform)[1], prec, min_exp, FALSE);
275 c[p++] = ',';
276 p += sp_svg_number_write_de (c + p, (*transform)[2], prec, min_exp, FALSE);
277 c[p++] = ',';
278 p += sp_svg_number_write_de (c + p, (*transform)[3], prec, min_exp, FALSE);
279 c[p++] = ',';
280 p += sp_svg_number_write_de (c + p, (*transform)[4], prec, min_exp, FALSE);
281 c[p++] = ',';
282 p += sp_svg_number_write_de (c + p, (*transform)[5], prec, min_exp, FALSE);
283 c[p++] = ')';
284 c[p] = '\000';
285 g_assert( p <= sizeof(c) );
286 return g_strdup(c);
287 }
288 }