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 <glib/gstrfuncs.h>
21 #include <libnr/nr-matrix-fns.h>
22 #include <libnr/nr-matrix-ops.h>
23 #include <libnr/nr-matrix-translate-ops.h>
24 #include <libnr/nr-rotate-fns.h>
25 #include <libnr/nr-rotate-matrix-ops.h>
26 #include <libnr/nr-scale-matrix-ops.h>
27 #include <libnr/nr-translate-matrix-ops.h>
28 #include <libnr/nr-translate-rotate-ops.h>
29 #include "svg.h"
31 #ifndef M_PI
32 # define M_PI 3.14159265358979323846
33 #endif
35 bool
36 sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
37 {
38 int idx;
39 char keyword[32];
40 double args[6];
41 int n_args;
42 size_t key_len;
44 if (str == NULL) return false;
46 NR::Matrix a(NR::identity());
48 idx = 0;
49 while (str[idx]) {
50 /* skip initial whitespace */
51 while (g_ascii_isspace (str[idx])) idx++;
53 /* parse keyword */
54 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
55 char c;
57 c = str[idx];
58 if (g_ascii_isalpha (c) || c == '-') {
59 keyword[key_len] = str[idx++];
60 } else {
61 break;
62 }
63 }
64 if (key_len >= sizeof (keyword)) return false;
65 keyword[key_len] = '\0';
67 /* skip whitespace */
68 while (g_ascii_isspace (str[idx])) idx++;
70 if (str[idx] != '(') return false;
71 idx++;
73 for (n_args = 0; ; n_args++) {
74 char c;
75 char *end_ptr;
77 /* skip whitespace */
78 while (g_ascii_isspace (str[idx])) idx++;
79 c = str[idx];
80 if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
81 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
82 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
84 //printf("took %d chars from '%s' to make %f\n",
85 // end_ptr-(str+idx),
86 // str+idx,
87 // args[n_args]);
89 idx = end_ptr - (char *) str;
91 while (g_ascii_isspace (str[idx])) idx++;
93 /* skip optional comma */
94 if (str[idx] == ',') idx++;
95 } else if (c == ')') {
96 break;
97 } else {
98 return false;
99 }
100 }
101 idx++;
103 /* ok, have parsed keyword and args, now modify the transform */
104 if (!strcmp (keyword, "matrix")) {
105 if (n_args != 6) return false;
106 a = NR_MATRIX_D_FROM_DOUBLE(args) * a;
107 } else if (!strcmp (keyword, "translate")) {
108 if (n_args == 1) {
109 args[1] = 0;
110 } else if (n_args != 2) {
111 return false;
112 }
113 a = NR::translate(args[0], args[1]) * a;
114 } else if (!strcmp (keyword, "scale")) {
115 if (n_args == 1) {
116 args[1] = args[0];
117 } else if (n_args != 2) {
118 return false;
119 }
120 a = NR::scale(args[0], args[1]) * a;
121 } else if (!strcmp (keyword, "rotate")) {
122 if (n_args != 1 && n_args != 3) {
123 return false;
124 }
125 NR::rotate const rot(rotate_degrees(args[0]));
126 if (n_args == 3) {
127 a = ( NR::translate(-args[1], -args[2])
128 * rot
129 * NR::translate(args[1], args[2])
130 * a );
131 } else {
132 a = rot * a;
133 }
134 } else if (!strcmp (keyword, "skewX")) {
135 if (n_args != 1) return false;
136 a = ( NR::Matrix(1, 0,
137 tan(args[0] * M_PI / 180.0), 1,
138 0, 0)
139 * a );
140 } else if (!strcmp (keyword, "skewY")) {
141 if (n_args != 1) return false;
142 a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
143 0, 1,
144 0, 0)
145 * a );
146 } else {
147 return false; /* unknown keyword */
148 }
149 /* Skip trailing whitespace */
150 while (g_ascii_isspace (str[idx])) idx++;
151 }
153 *transform = a;
154 return true;
155 }
157 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
159 unsigned
160 sp_svg_transform_write(gchar str[], unsigned const size, NR::Matrix const &transform)
161 {
162 NRMatrix const t(transform);
163 return sp_svg_transform_write(str, size, &t);
164 }
166 unsigned
167 sp_svg_transform_write(gchar str[], unsigned const size, NRMatrix const *transform)
168 {
169 double e;
171 if (!transform) {
172 *str = 0;
173 return 0;
174 }
176 e = 0.000001 * NR_MATRIX_DF_EXPANSION (transform);
178 /* fixme: We could use t1 * t1 + t2 * t2 here instead */
179 if (NR_DF_TEST_CLOSE (transform->c[1], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[2], 0.0, e)) {
180 if (NR_DF_TEST_CLOSE (transform->c[4], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[5], 0.0, e)) {
181 if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
182 /* We are more or less identity */
183 *str = 0;
184 return 0;
185 } else {
186 /* We are more or less scale */
187 gchar c[256];
188 unsigned p = 0;
189 strcpy (c + p, "scale(");
190 p += 6;
191 p += sp_svg_number_write_de (c + p, transform->c[0], 6, -8, FALSE);
192 c[p++] = ',';
193 p += sp_svg_number_write_de (c + p, transform->c[3], 6, -8, FALSE);
194 c[p++] = ')';
195 g_assert( p <= sizeof(c) );
196 p = MIN (p, size - 1 );
197 memcpy (str, c, p);
198 str[p] = 0;
199 return p;
200 }
201 } else {
202 if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
203 /* We are more or less translate */
204 gchar c[256];
205 unsigned p = 0;
206 strcpy (c + p, "translate(");
207 p += 10;
208 p += sp_svg_number_write_de (c + p, transform->c[4], 6, -8, FALSE);
209 c[p++] = ',';
210 p += sp_svg_number_write_de (c + p, transform->c[5], 6, -8, FALSE);
211 c[p++] = ')';
212 g_assert( p <= sizeof(c) );
213 p = MIN(p, size - 1);
214 memcpy (str, c, p);
215 str[p] = 0;
216 return p;
217 } else {
218 gchar c[256];
219 unsigned p = 0;
220 strcpy (c + p, "matrix(");
221 p += 7;
222 p += sp_svg_number_write_de (c + p, transform->c[0], 6, -8, FALSE);
223 c[p++] = ',';
224 p += sp_svg_number_write_de (c + p, transform->c[1], 6, -8, FALSE);
225 c[p++] = ',';
226 p += sp_svg_number_write_de (c + p, transform->c[2], 6, -8, FALSE);
227 c[p++] = ',';
228 p += sp_svg_number_write_de (c + p, transform->c[3], 6, -8, FALSE);
229 c[p++] = ',';
230 p += sp_svg_number_write_de (c + p, transform->c[4], 6, -8, FALSE);
231 c[p++] = ',';
232 p += sp_svg_number_write_de (c + p, transform->c[5], 6, -8, FALSE);
233 c[p++] = ')';
234 g_assert( p <= sizeof(c) );
235 p = MIN(p, size - 1);
236 memcpy (str, c, p);
237 str[p] = 0;
238 return p;
239 }
240 }
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->c[0], 6, -8, FALSE);
247 c[p++] = ',';
248 p += sp_svg_number_write_de (c + p, transform->c[1], 6, -8, FALSE);
249 c[p++] = ',';
250 p += sp_svg_number_write_de (c + p, transform->c[2], 6, -8, FALSE);
251 c[p++] = ',';
252 p += sp_svg_number_write_de (c + p, transform->c[3], 6, -8, FALSE);
253 c[p++] = ',';
254 p += sp_svg_number_write_de (c + p, transform->c[4], 6, -8, FALSE);
255 c[p++] = ',';
256 p += sp_svg_number_write_de (c + p, transform->c[5], 6, -8, FALSE);
257 c[p++] = ')';
258 g_assert( p <= sizeof(c) );
259 p = MIN(p, size - 1);
260 memcpy (str, c, p);
261 str[p] = 0;
262 return p;
263 }
264 }