Code

User message context in extensions
[inkscape.git] / src / svg / svg-affine.cpp
1 /*
2  * SVG data parser
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Raph Levien <raph@acm.org>
7  *
8  * Copyright (C) 1999-2002 Lauris Kaplinski
9  * Copyright (C) 1999 Raph Levien
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
18 #include <cstring>
19 #include <string>
20 #include <cstdlib>
21 #include <cstdio>
22 #include <glib/gstrfuncs.h>
23 #include <libnr/nr-matrix-fns.h>
24 #include <libnr/nr-matrix-ops.h>
25 #include <2geom/transforms.h>
26 #include <2geom/angle.h>
27 #include <libnr/nr-convert2geom.h>
28 #include "svg.h"
29 #include "preferences.h"
31 #ifndef M_PI
32 # define M_PI 3.14159265358979323846
33 #endif
35 bool
36 sp_svg_transform_read(gchar const *str, Geom::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     Geom::Matrix a(Geom::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);
83                 
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 = (*((Geom::Matrix *) &(args)[0])) * 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 = Geom::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 = Geom::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             Geom::Rotate const rot(Geom::deg_to_rad(args[0]));
126             if (n_args == 3) {
127                 a = ( Geom::Translate(-args[1], -args[2])
128                       * rot
129                       * Geom::Translate(args[1], args[2])
130                       * Geom::Matrix(a) );
131             } else {
132                 a = rot * a;
133             }
134         } else if (!strcmp (keyword, "skewX")) {
135             if (n_args != 1) return false;
136             a = ( Geom::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 = ( Geom::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;
157 #define EQ(a,b) (fabs ((a) - (b)) < 1e-9)
159 gchar *
160 sp_svg_transform_write(Geom::Matrix const &transform)
162     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
164     double e = 0.000001 * transform.descrim();
165     int prec = prefs->getInt("/options/svgoutput/numericprecision", 8);
166     int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8);
168     /* fixme: We could use t1 * t1 + t2 * t2 here instead */
169     if ( Geom::are_near(transform[1], 0.0, e) && Geom::are_near (transform[2], 0.0, e)) {
170         if (Geom::are_near (transform[4], 0.0, e) && Geom::are_near (transform[5], 0.0, e)) {
171             if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
172                 /* We are more or less identity */
173                 return NULL;
174             } else {
175                 /* We are more or less scale */
176                 gchar c[256];
177                 unsigned p = 0;
178                 strcpy (c + p, "scale(");
179                 p += 6;
180                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[0], prec, min_exp );
181                 c[p++] = ',';
182                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[3], prec, min_exp );
183                 c[p++] = ')';
184                 c[p] = '\000';
185                 g_assert( p <= sizeof(c) );
186                 return g_strdup(c);
187             }
188         } else {
189             if (Geom::are_near (transform[0], 1.0, e) && Geom::are_near (transform[3], 1.0, e)) {
190                 /* We are more or less translate */
191                 gchar c[256];
192                 unsigned p = 0;
193                 strcpy (c + p, "translate(");
194                 p += 10;
195                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[4], prec, min_exp );
196                 c[p++] = ',';
197                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[5], prec, min_exp );
198                 c[p++] = ')';
199                 c[p] = '\000';
200                 g_assert( p <= sizeof(c) );
201                 return g_strdup(c);
202             } else {
203                 gchar c[256];
204                 unsigned p = 0;
205                 strcpy (c + p, "matrix(");
206                 p += 7;
207                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[0], prec, min_exp );
208                 c[p++] = ',';
209                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[1], prec, min_exp );
210                 c[p++] = ',';
211                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[2], prec, min_exp );
212                 c[p++] = ',';
213                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[3], prec, min_exp );
214                 c[p++] = ',';
215                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[4], prec, min_exp );
216                 c[p++] = ',';
217                 p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[5], prec, min_exp );
218                 c[p++] = ')';
219                 c[p] = '\000';
220                 g_assert( p <= sizeof(c) );
221                 return g_strdup(c);
222             }
223         }
224     } else {
225         gchar c[256];
226         unsigned p = 0;
227         strcpy (c + p, "matrix(");
228         p += 7;
229         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[0], prec, min_exp );
230         c[p++] = ',';
231         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[1], prec, min_exp );
232         c[p++] = ',';
233         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[2], prec, min_exp );
234         c[p++] = ',';
235         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[3], prec, min_exp );
236         c[p++] = ',';
237         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[4], prec, min_exp );
238         c[p++] = ',';
239         p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[5], prec, min_exp );
240         c[p++] = ')';
241         c[p] = '\000';
242         g_assert( p <= sizeof(c) );
243         return g_strdup(c);
244     }
248 gchar *
249 sp_svg_transform_write(Geom::Matrix const *transform)
251     return sp_svg_transform_write(*transform);
254 /*
255   Local Variables:
256   mode:c++
257   c-file-style:"stroustrup"
258   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
259   indent-tabs-mode:nil
260   fill-column:99
261   End:
262 */
263 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :