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