1077f7e2fc928fc9e719a3614837904207c60732
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 int idx;
41 char keyword[32];
42 double args[6];
43 int n_args;
44 size_t key_len;
46 if (str == NULL) return false;
48 Geom::Matrix a(Geom::identity());
50 idx = 0;
51 while (str[idx]) {
52 /* skip initial whitespace */
53 while (g_ascii_isspace (str[idx])) idx++;
55 /* parse keyword */
56 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
57 char c;
59 c = str[idx];
60 if (g_ascii_isalpha (c) || c == '-') {
61 keyword[key_len] = str[idx++];
62 } else {
63 break;
64 }
65 }
66 if (key_len >= sizeof (keyword)) return false;
67 keyword[key_len] = '\0';
69 /* skip whitespace */
70 while (g_ascii_isspace (str[idx])) idx++;
72 if (str[idx] != '(') return false;
73 idx++;
75 for (n_args = 0; ; n_args++) {
76 char c;
77 char *end_ptr;
79 /* skip whitespace */
80 while (g_ascii_isspace (str[idx])) idx++;
81 c = str[idx];
82 if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
83 if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */
84 args[n_args] = g_ascii_strtod (str + idx, &end_ptr);
86 //printf("took %d chars from '%s' to make %f\n",
87 // end_ptr-(str+idx),
88 // str+idx,
89 // args[n_args]);
91 idx = end_ptr - (char *) str;
93 while (g_ascii_isspace (str[idx])) idx++;
95 /* skip optional comma */
96 if (str[idx] == ',') idx++;
97 } else if (c == ')') {
98 break;
99 } else {
100 return false;
101 }
102 }
103 idx++;
105 /* ok, have parsed keyword and args, now modify the transform */
106 if (!strcmp (keyword, "matrix")) {
107 if (n_args != 6) return false;
108 a = (*((Geom::Matrix *) &(args)[0])) * a;
109 } else if (!strcmp (keyword, "translate")) {
110 if (n_args == 1) {
111 args[1] = 0;
112 } else if (n_args != 2) {
113 return false;
114 }
115 a = Geom::Translate(args[0], args[1]) * a;
116 } else if (!strcmp (keyword, "scale")) {
117 if (n_args == 1) {
118 args[1] = args[0];
119 } else if (n_args != 2) {
120 return false;
121 }
122 a = Geom::Scale(args[0], args[1]) * a;
123 } else if (!strcmp (keyword, "rotate")) {
124 if (n_args != 1 && n_args != 3) {
125 return false;
126 }
127 Geom::Rotate const rot(Geom::deg_to_rad(args[0]));
128 if (n_args == 3) {
129 a = ( Geom::Translate(-args[1], -args[2])
130 * rot
131 * Geom::Translate(args[1], args[2])
132 * Geom::Matrix(a) );
133 } else {
134 a = rot * a;
135 }
136 } else if (!strcmp (keyword, "skewX")) {
137 if (n_args != 1) return false;
138 a = ( Geom::Matrix(1, 0,
139 tan(args[0] * M_PI / 180.0), 1,
140 0, 0)
141 * a );
142 } else if (!strcmp (keyword, "skewY")) {
143 if (n_args != 1) return false;
144 a = ( Geom::Matrix(1, tan(args[0] * M_PI / 180.0),
145 0, 1,
146 0, 0)
147 * a );
148 } else {
149 return false; /* unknown keyword */
150 }
151 /* Skip trailing whitespace */
152 while (g_ascii_isspace (str[idx])) idx++;
153 }
155 *transform = a;
156 return true;
157 }
159 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
161 gchar *
162 sp_svg_transform_write(Geom::Matrix const &transform)
163 {
164 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
166 double e = 0.000001 * transform.descrim();
167 int prec = prefs->getInt("/options/svgoutput/numericprecision", 8);
168 int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8);
170 /* fixme: We could use t1 * t1 + t2 * t2 here instead */
171 if ( Geom::are_near(transform[1], 0.0, e) && Geom::are_near (transform[2], 0.0, e)) {
172 if (Geom::are_near (transform[4], 0.0, e) && Geom::are_near (transform[5], 0.0, e)) {
173 if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
174 /* We are more or less identity */
175 return NULL;
176 } else {
177 /* We are more or less scale */
178 gchar c[256];
179 unsigned p = 0;
180 strcpy (c + p, "scale(");
181 p += 6;
182 p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
183 c[p++] = ',';
184 p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
185 c[p++] = ')';
186 c[p] = '\000';
187 g_assert( p <= sizeof(c) );
188 return g_strdup(c);
189 }
190 } else {
191 if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
192 /* We are more or less translate */
193 gchar c[256];
194 unsigned p = 0;
195 strcpy (c + p, "translate(");
196 p += 10;
197 p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
198 c[p++] = ',';
199 p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
200 c[p++] = ')';
201 c[p] = '\000';
202 g_assert( p <= sizeof(c) );
203 return g_strdup(c);
204 } else {
205 gchar c[256];
206 unsigned p = 0;
207 strcpy (c + p, "matrix(");
208 p += 7;
209 p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
210 c[p++] = ',';
211 p += sp_svg_number_write_de (c + p, transform[1], prec, min_exp);
212 c[p++] = ',';
213 p += sp_svg_number_write_de (c + p, transform[2], prec, min_exp);
214 c[p++] = ',';
215 p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
216 c[p++] = ',';
217 p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
218 c[p++] = ',';
219 p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
220 c[p++] = ')';
221 c[p] = '\000';
222 g_assert( p <= sizeof(c) );
223 return g_strdup(c);
224 }
225 }
226 } else {
227 gchar c[256];
228 unsigned p = 0;
229 strcpy (c + p, "matrix(");
230 p += 7;
231 p += sp_svg_number_write_de (c + p, transform[0], prec, min_exp);
232 c[p++] = ',';
233 p += sp_svg_number_write_de (c + p, transform[1], prec, min_exp);
234 c[p++] = ',';
235 p += sp_svg_number_write_de (c + p, transform[2], prec, min_exp);
236 c[p++] = ',';
237 p += sp_svg_number_write_de (c + p, transform[3], prec, min_exp);
238 c[p++] = ',';
239 p += sp_svg_number_write_de (c + p, transform[4], prec, min_exp);
240 c[p++] = ',';
241 p += sp_svg_number_write_de (c + p, transform[5], prec, min_exp);
242 c[p++] = ')';
243 c[p] = '\000';
244 g_assert( p <= sizeof(c) );
245 return g_strdup(c);
246 }
247 }
250 gchar *
251 sp_svg_transform_write(Geom::Matrix const *transform)
252 {
253 return sp_svg_transform_write(*transform);
254 }