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"
30 #include "prefs-utils.h"
32 #ifndef M_PI
33 # define M_PI 3.14159265358979323846
34 #endif
36 bool
37 sp_svg_transform_read(gchar const *str, NR::Matrix *transform)
38 {
39 int idx;
40 char keyword[32];
41 double args[6];
42 int n_args;
43 size_t key_len;
45 if (str == NULL) return false;
47 NR::Matrix a(NR::identity());
49 idx = 0;
50 while (str[idx]) {
51 /* skip initial whitespace */
52 while (g_ascii_isspace (str[idx])) idx++;
54 /* parse keyword */
55 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
56 char c;
58 c = str[idx];
59 if (g_ascii_isalpha (c) || c == '-') {
60 keyword[key_len] = str[idx++];
61 } else {
62 break;
63 }
64 }
65 if (key_len >= sizeof (keyword)) return false;
66 keyword[key_len] = '\0';
68 /* skip whitespace */
69 while (g_ascii_isspace (str[idx])) idx++;
71 if (str[idx] != '(') return false;
72 idx++;
74 for (n_args = 0; ; n_args++) {
75 char c;
76 char *end_ptr;
78 /* skip whitespace */
79 while (g_ascii_isspace (str[idx])) idx++;
80 c = str[idx];
81 if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
82 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
83 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
85 //printf("took %d chars from '%s' to make %f\n",
86 // end_ptr-(str+idx),
87 // str+idx,
88 // args[n_args]);
90 idx = end_ptr - (char *) str;
92 while (g_ascii_isspace (str[idx])) idx++;
94 /* skip optional comma */
95 if (str[idx] == ',') idx++;
96 } else if (c == ')') {
97 break;
98 } else {
99 return false;
100 }
101 }
102 idx++;
104 /* ok, have parsed keyword and args, now modify the transform */
105 if (!strcmp (keyword, "matrix")) {
106 if (n_args != 6) return false;
107 a = NR_MATRIX_D_FROM_DOUBLE(args) * a;
108 } else if (!strcmp (keyword, "translate")) {
109 if (n_args == 1) {
110 args[1] = 0;
111 } else if (n_args != 2) {
112 return false;
113 }
114 a = NR::translate(args[0], args[1]) * a;
115 } else if (!strcmp (keyword, "scale")) {
116 if (n_args == 1) {
117 args[1] = args[0];
118 } else if (n_args != 2) {
119 return false;
120 }
121 a = NR::scale(args[0], args[1]) * a;
122 } else if (!strcmp (keyword, "rotate")) {
123 if (n_args != 1 && n_args != 3) {
124 return false;
125 }
126 NR::rotate const rot(rotate_degrees(args[0]));
127 if (n_args == 3) {
128 a = ( NR::translate(-args[1], -args[2])
129 * rot
130 * NR::translate(args[1], args[2])
131 * a );
132 } else {
133 a = rot * a;
134 }
135 } else if (!strcmp (keyword, "skewX")) {
136 if (n_args != 1) return false;
137 a = ( NR::Matrix(1, 0,
138 tan(args[0] * M_PI / 180.0), 1,
139 0, 0)
140 * a );
141 } else if (!strcmp (keyword, "skewY")) {
142 if (n_args != 1) return false;
143 a = ( NR::Matrix(1, tan(args[0] * M_PI / 180.0),
144 0, 1,
145 0, 0)
146 * a );
147 } else {
148 return false; /* unknown keyword */
149 }
150 /* Skip trailing whitespace */
151 while (g_ascii_isspace (str[idx])) idx++;
152 }
154 *transform = a;
155 return true;
156 }
158 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
160 gchar *
161 sp_svg_transform_write(NR::Matrix const &transform)
162 {
163 NRMatrix const t(transform);
164 return sp_svg_transform_write(&t);
165 }
167 gchar *
168 sp_svg_transform_write(NRMatrix const *transform)
169 {
170 double e;
172 if (!transform) {
173 return NULL;
174 }
176 e = 0.000001 * NR_MATRIX_DF_EXPANSION (transform);
177 int prec = prefs_get_int_attribute("options.svgoutput", "numericprecision", 8);
178 int min_exp = prefs_get_int_attribute("options.svgoutput", "minimumexponent", -8);
180 /* fixme: We could use t1 * t1 + t2 * t2 here instead */
181 if (NR_DF_TEST_CLOSE (transform->c[1], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[2], 0.0, e)) {
182 if (NR_DF_TEST_CLOSE (transform->c[4], 0.0, e) && NR_DF_TEST_CLOSE (transform->c[5], 0.0, e)) {
183 if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
184 /* We are more or less identity */
185 return NULL;
186 } else {
187 /* We are more or less scale */
188 gchar c[256];
189 unsigned p = 0;
190 strcpy (c + p, "scale(");
191 p += 6;
192 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
193 c[p++] = ',';
194 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
195 c[p++] = ')';
196 c[p] = '\000';
197 g_assert( p <= sizeof(c) );
198 return g_strdup(c);
199 }
200 } else {
201 if (NR_DF_TEST_CLOSE (transform->c[0], 1.0, e) && NR_DF_TEST_CLOSE (transform->c[3], 1.0, e)) {
202 /* We are more or less translate */
203 gchar c[256];
204 unsigned p = 0;
205 strcpy (c + p, "translate(");
206 p += 10;
207 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
208 c[p++] = ',';
209 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
210 c[p++] = ')';
211 c[p] = '\000';
212 g_assert( p <= sizeof(c) );
213 return g_strdup(c);
214 } else {
215 gchar c[256];
216 unsigned p = 0;
217 strcpy (c + p, "matrix(");
218 p += 7;
219 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
220 c[p++] = ',';
221 p += sp_svg_number_write_de (c + p, transform->c[1], prec, min_exp, FALSE);
222 c[p++] = ',';
223 p += sp_svg_number_write_de (c + p, transform->c[2], prec, min_exp, FALSE);
224 c[p++] = ',';
225 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
226 c[p++] = ',';
227 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
228 c[p++] = ',';
229 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
230 c[p++] = ')';
231 c[p] = '\000';
232 g_assert( p <= sizeof(c) );
233 return g_strdup(c);
234 }
235 }
236 } else {
237 gchar c[256];
238 unsigned p = 0;
239 strcpy (c + p, "matrix(");
240 p += 7;
241 p += sp_svg_number_write_de (c + p, transform->c[0], prec, min_exp, FALSE);
242 c[p++] = ',';
243 p += sp_svg_number_write_de (c + p, transform->c[1], prec, min_exp, FALSE);
244 c[p++] = ',';
245 p += sp_svg_number_write_de (c + p, transform->c[2], prec, min_exp, FALSE);
246 c[p++] = ',';
247 p += sp_svg_number_write_de (c + p, transform->c[3], prec, min_exp, FALSE);
248 c[p++] = ',';
249 p += sp_svg_number_write_de (c + p, transform->c[4], prec, min_exp, FALSE);
250 c[p++] = ',';
251 p += sp_svg_number_write_de (c + p, transform->c[5], prec, min_exp, FALSE);
252 c[p++] = ')';
253 c[p] = '\000';
254 g_assert( p <= sizeof(c) );
255 return g_strdup(c);
256 }
257 }