1 /*
2    svg_transform.c: Data structure for SVG transformation matrix.
3 
4    Copyright � 2002 USC/Information Sciences Institute
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public
17    License along with this program; if not, write to the
18    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.
20 
21    Author: Carl Worth <cworth@isi.edu>
22 */
23 
24 #include <math.h>
25 #include <string.h>
26 
27 #include "svgint.h"
28 
29 static svg_status_t
30 _svg_transform_multiply (svg_transform_t	*result,
31 			 const svg_transform_t	*t1,
32 			 const svg_transform_t	*t2);
33 
34 static svg_transform_t SVG_TRANSFORM_IDENTITY = {
35     {
36 	{1, 0},
37 	{0, 1},
38 	{0, 0}
39     }
40 };
41 
42 svg_status_t
_svg_transform_init(svg_transform_t * transform)43 _svg_transform_init (svg_transform_t *transform)
44 {
45     *transform = SVG_TRANSFORM_IDENTITY;
46 
47     return SVG_STATUS_SUCCESS;
48 }
49 
50 svg_status_t
_svg_transform_init_matrix(svg_transform_t * transform,double a,double b,double c,double d,double e,double f)51 _svg_transform_init_matrix (svg_transform_t *transform,
52 			    double a, double b,
53 			    double c, double d,
54 			    double e, double f)
55 {
56     transform->m[0][0] = a; transform->m[0][1] = b;
57     transform->m[1][0] = c; transform->m[1][1] = d;
58     transform->m[2][0] = e; transform->m[2][1] = f;
59 
60     return SVG_STATUS_SUCCESS;
61 }
62 
63 svg_status_t
_svg_transform_init_translate(svg_transform_t * transform,double tx,double ty)64 _svg_transform_init_translate (svg_transform_t *transform,
65 			       double tx, double ty)
66 {
67     return _svg_transform_init_matrix (transform,
68 				       1,  0,
69 				       0,  1,
70 				       tx, ty);
71 }
72 
73 svg_status_t
_svg_transform_init_scale(svg_transform_t * transform,double sx,double sy)74 _svg_transform_init_scale (svg_transform_t *transform,
75 			   double sx, double sy)
76 {
77     return _svg_transform_init_matrix (transform,
78 				       sx, 0,
79 				       0, sy,
80 				       0,  0);
81 }
82 
83 /* silly SVG spec. uses degrees */
84 svg_status_t
_svg_transform_init_rotate(svg_transform_t * transform,double angle_degrees)85 _svg_transform_init_rotate (svg_transform_t *transform,
86 			    double angle_degrees)
87 {
88     double rad = M_PI * angle_degrees / 180.0;
89     return _svg_transform_init_matrix (transform,
90 				       cos(rad),  sin(rad),
91 				       -sin(rad), cos(rad),
92 				       0,         0);
93 }
94 
95 /* silly SVG spec. uses degrees */
96 svg_status_t
_svg_transform_init_skew_x(svg_transform_t * transform,double angle_degrees)97 _svg_transform_init_skew_x (svg_transform_t *transform,
98 			    double angle_degrees)
99 {
100     double rad = M_PI * angle_degrees / 180.0;
101     return _svg_transform_init_matrix (transform,
102 				       1,        0,
103 				       tan(rad), 1,
104 				       0,        0);
105 }
106 
107 /* silly SVG spec. uses degrees */
108 svg_status_t
_svg_transform_init_skew_y(svg_transform_t * transform,double angle_degrees)109 _svg_transform_init_skew_y (svg_transform_t *transform,
110 			    double angle_degrees)
111 {
112     double rad = M_PI * angle_degrees / 180.0;
113     return _svg_transform_init_matrix (transform,
114 				       1, tan(rad),
115 				       0,        1,
116 				       0,        0);
117 }
118 
119 svg_status_t
_svg_transform_deinit(svg_transform_t * transform)120 _svg_transform_deinit (svg_transform_t *transform)
121 {
122     return _svg_transform_init (transform);
123 }
124 
125 svg_status_t
_svg_transform_add_translate(svg_transform_t * transform,double tx,double ty)126 _svg_transform_add_translate (svg_transform_t *transform,
127 			      double tx, double ty)
128 {
129     svg_transform_t translate;
130 
131     _svg_transform_init_translate (&translate, tx, ty);
132     return _svg_transform_multiply_into_right (&translate, transform);
133 }
134 
135 svg_status_t
_svg_transform_add_scale_uniform(svg_transform_t * transform,double s)136 _svg_transform_add_scale_uniform (svg_transform_t *transform, double s)
137 {
138     return _svg_transform_add_scale (transform, s, s);
139 }
140 
141 svg_status_t
_svg_transform_add_scale(svg_transform_t * transform,double sx,double sy)142 _svg_transform_add_scale (svg_transform_t *transform,
143 			  double sx, double sy)
144 {
145     svg_transform_t scale;
146 
147     _svg_transform_init_scale (&scale, sx, sy);
148     return _svg_transform_multiply_into_right (&scale, transform);
149 }
150 
151 /* silly SVG spec. uses degrees */
152 svg_status_t
_svg_transform_add_rotate(svg_transform_t * transform,double angle_degrees)153 _svg_transform_add_rotate (svg_transform_t *transform,
154 			   double angle_degrees)
155 {
156     svg_transform_t rotate;
157 
158     _svg_transform_init_rotate (&rotate, angle_degrees);
159     return _svg_transform_multiply_into_right (&rotate, transform);
160 }
161 
162 /* silly SVG spec. uses degrees */
163 svg_status_t
_svg_transform_add_skew_x(svg_transform_t * transform,double angle_degrees)164 _svg_transform_add_skew_x (svg_transform_t *transform,
165 			   double angle_degrees)
166 {
167     svg_transform_t skew;
168 
169     _svg_transform_init_skew_x (&skew, angle_degrees);
170     return _svg_transform_multiply_into_right (&skew, transform);
171 }
172 
173 /* silly SVG spec. uses degrees */
174 svg_status_t
_svg_transform_add_skew_y(svg_transform_t * transform,double angle_degrees)175 _svg_transform_add_skew_y (svg_transform_t *transform,
176 			   double angle_degrees)
177 {
178     svg_transform_t skew;
179 
180     _svg_transform_init_skew_y (&skew, angle_degrees);
181     return _svg_transform_multiply_into_right (&skew, transform);
182 }
183 
184 
185 svg_status_t
_svg_transform_multiply_into_left(svg_transform_t * t1,const svg_transform_t * t2)186 _svg_transform_multiply_into_left (svg_transform_t *t1, const svg_transform_t *t2)
187 {
188     svg_status_t status;
189     svg_transform_t t1_new;
190 
191     status = _svg_transform_multiply (&t1_new, t1, t2);
192 
193     *t1 = t1_new;
194 
195     return status;
196 }
197 
198 svg_status_t
_svg_transform_multiply_into_right(const svg_transform_t * t1,svg_transform_t * t2)199 _svg_transform_multiply_into_right (const svg_transform_t *t1, svg_transform_t *t2)
200 {
201     svg_status_t status;
202     svg_transform_t t2_new;
203 
204     status = _svg_transform_multiply (&t2_new, t1, t2);
205 
206     *t2 = t2_new;
207 
208     return status;
209 }
210 
211 static svg_status_t
_svg_transform_multiply(svg_transform_t * result,const svg_transform_t * t1,const svg_transform_t * t2)212 _svg_transform_multiply (svg_transform_t	*result,
213 			 const svg_transform_t	*t1,
214 			 const svg_transform_t	*t2)
215 {
216     int	    row, col, n;
217     double  t;
218 
219     for (row = 0; row < 3; row++) {
220 	for (col = 0; col < 2; col++) {
221 	    if (row == 2)
222 		t = t2->m[2][col];
223 	    else
224 		t = 0;
225 	    for (n = 0; n < 2; n++) {
226 		t += t1->m[row][n] * t2->m[n][col];
227 	    }
228 	    result->m[row][col] = t;
229 	}
230     }
231 
232     return SVG_STATUS_SUCCESS;
233 }
234 
235 svg_status_t
_svg_transform_render(svg_transform_t * transform,svg_render_engine_t * engine,void * closure)236 _svg_transform_render (svg_transform_t		*transform,
237 		       svg_render_engine_t	*engine,
238 		       void			*closure)
239 {
240     return (engine->transform) (closure,
241 				transform->m[0][0], transform->m[0][1],
242 				transform->m[1][0], transform->m[1][1],
243 				transform->m[2][0], transform->m[2][1]);
244 }
245 
246 
247 /* The following parse function is:
248 
249    Copyright (C) 2000 Eazel, Inc.
250    Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
251 
252    Author: Raph Levien <raph@artofcode.com>
253 
254    Parse an SVG transform string into an affine matrix. Reference: SVG
255    working draft dated 1999-07-06, section 8.5.
256 */
257 extern svg_status_t
_svg_transform_parse_str(svg_transform_t * transform,const char * str)258 _svg_transform_parse_str (svg_transform_t *transform, const char *str)
259 {
260     int idx;
261     svg_status_t status;
262     char keyword[32];
263     double args[6];
264     int n_args;
265     unsigned int key_len;
266     svg_transform_t tmp_transform;
267 
268     status = _svg_transform_init (transform);
269     if (status)
270 	return status;
271 
272     idx = 0;
273     while (str[idx]) {
274 	/* skip initial whitespace */
275 	while (_svg_ascii_isspace (str[idx]) || str[idx] == ',')
276 	    idx++;
277 
278 	/* parse keyword */
279 	for (key_len = 0; key_len < sizeof (keyword); key_len++) {
280 	    char c;
281 
282 	    c = str[idx];
283 	    if (_svg_ascii_isalpha (c) || c == '-')
284 		keyword[key_len] = str[idx++];
285 	    else
286 		break;
287 	}
288 	/* XXX: This size limitation looks bogus */
289 	if (key_len >= sizeof (keyword))
290 	    return SVG_STATUS_PARSE_ERROR;
291 	keyword[key_len] = '\0';
292 
293 	/* skip whitespace */
294 	while (_svg_ascii_isspace (str[idx]))
295 	    idx++;
296 
297 	if (str[idx] != '(')
298 	    return SVG_STATUS_PARSE_ERROR;
299 	idx++;
300 
301 	for (n_args = 0; ; n_args++) {
302 	    char c;
303 	    const char *end_ptr;
304 
305 	    /* skip whitespace */
306 	    while (_svg_ascii_isspace (str[idx]))
307 		idx++;
308 	    c = str[idx];
309 	    if (_svg_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
310 		if (n_args == SVG_ARRAY_SIZE (args))
311 		    return SVG_STATUS_PARSE_ERROR;
312 		args[n_args] = _svg_ascii_strtod (str + idx, &end_ptr);
313 		idx = end_ptr - str;
314 
315 		while (_svg_ascii_isspace (str[idx]))
316 		    idx++;
317 
318 		/* skip optional comma */
319 		if (str[idx] == ',')
320 		    idx++;
321 	    } else if (c == ')')
322 		break;
323 	    else
324 		return SVG_STATUS_PARSE_ERROR;
325 	}
326 	idx++;
327 
328 	/* ok, have parsed keyword and args, now modify the transform */
329 	if (strcmp (keyword, "matrix") == 0) {
330 	    if (n_args != 6)
331 		return SVG_STATUS_PARSE_ERROR;
332 	    _svg_transform_init_matrix (&tmp_transform,
333 					args[0], args[1],
334 					args[2], args[3],
335 					args[4], args[5]);
336 	    _svg_transform_multiply_into_right (&tmp_transform, transform);
337 	} else if (strcmp (keyword, "translate") == 0) {
338 	    if (n_args == 1)
339 		args[1] = 0;
340 	    else if (n_args != 2)
341 		return SVG_STATUS_PARSE_ERROR;
342 	    _svg_transform_add_translate (transform, args[0], args[1]);
343 	} else if (strcmp (keyword, "scale") == 0) {
344 	    if (n_args == 1)
345 		args[1] = args[0];
346 	    else if (n_args != 2)
347 		return SVG_STATUS_PARSE_ERROR;
348 	    _svg_transform_add_scale (transform, args[0], args[1]);
349 	} else if (strcmp (keyword, "rotate") == 0) {
350 	    if (n_args != 1)
351 		return SVG_STATUS_PARSE_ERROR;
352 	    _svg_transform_add_rotate (transform, args[0]);
353 	} else if (strcmp (keyword, "skewX") == 0) {
354 	    if (n_args != 1)
355 		return SVG_STATUS_PARSE_ERROR;
356 	    _svg_transform_add_skew_x (transform, args[0]);
357 	} else if (strcmp (keyword, "skewY") == 0) {
358 	    if (n_args != 1)
359 		return SVG_STATUS_PARSE_ERROR;
360 	    _svg_transform_add_skew_y (transform, args[0]);
361 	} else
362 	    return SVG_STATUS_PARSE_ERROR;
363     }
364 
365     return SVG_STATUS_SUCCESS;
366 }
367 
368 svg_status_t
_svg_transform_apply_attributes(svg_transform_t * transform,const char ** attributes)369 _svg_transform_apply_attributes (svg_transform_t	*transform,
370 				 const char		**attributes)
371 {
372     const char *transform_str;
373 
374     _svg_attribute_get_string (attributes, "transform", &transform_str, NULL);
375 
376     if (transform_str)
377 	return _svg_transform_parse_str (transform, transform_str);
378 
379     return SVG_STATUS_SUCCESS;
380 }
381 
382