File: | svgtiny.c |
Warning: | line 381, column 26 The left operand of '*' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * This file is part of Libsvgtiny | |||
3 | * Licensed under the MIT License, | |||
4 | * http://opensource.org/licenses/mit-license.php | |||
5 | * Copyright 2008-2009 James Bursa <james@semichrome.net> | |||
6 | * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org> | |||
7 | */ | |||
8 | ||||
9 | #include <assert.h> | |||
10 | #include <math.h> | |||
11 | #include <setjmp.h> | |||
12 | #include <stdbool.h> | |||
13 | #include <stdio.h> | |||
14 | #include <stdlib.h> | |||
15 | #include <string.h> | |||
16 | ||||
17 | #include <dom/dom.h> | |||
18 | #include <dom/bindings/xml/xmlparser.h> | |||
19 | ||||
20 | #include "svgtiny.h" | |||
21 | #include "svgtiny_internal.h" | |||
22 | ||||
23 | /* Source file generated by `gperf`. */ | |||
24 | #include "autogenerated_colors.c" | |||
25 | ||||
26 | #define TAU6.28318530717958647692 6.28318530717958647692 | |||
27 | ||||
28 | #ifndef M_PI3.14159265358979323846 | |||
29 | #define M_PI3.14159265358979323846 3.14159265358979323846 | |||
30 | #endif | |||
31 | ||||
32 | #ifndef M_PI_21.57079632679489661923 | |||
33 | #define M_PI_21.57079632679489661923 1.57079632679489661923 | |||
34 | #endif | |||
35 | ||||
36 | #define KAPPA0.5522847498 0.5522847498 | |||
37 | ||||
38 | #define degToRad(angleInDegrees)((angleInDegrees) * 3.14159265358979323846 / 180.0) ((angleInDegrees) * M_PI3.14159265358979323846 / 180.0) | |||
39 | #define radToDeg(angleInRadians)((angleInRadians) * 180.0 / 3.14159265358979323846) ((angleInRadians) * 180.0 / M_PI3.14159265358979323846) | |||
40 | ||||
41 | static svgtiny_code svgtiny_parse_svg(dom_element *svg, | |||
42 | struct svgtiny_parse_state state); | |||
43 | static svgtiny_code svgtiny_parse_path(dom_element *path, | |||
44 | struct svgtiny_parse_state state); | |||
45 | static svgtiny_code svgtiny_parse_rect(dom_element *rect, | |||
46 | struct svgtiny_parse_state state); | |||
47 | static svgtiny_code svgtiny_parse_circle(dom_element *circle, | |||
48 | struct svgtiny_parse_state state); | |||
49 | static svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, | |||
50 | struct svgtiny_parse_state state); | |||
51 | static svgtiny_code svgtiny_parse_line(dom_element *line, | |||
52 | struct svgtiny_parse_state state); | |||
53 | static svgtiny_code svgtiny_parse_poly(dom_element *poly, | |||
54 | struct svgtiny_parse_state state, bool_Bool polygon); | |||
55 | static svgtiny_code svgtiny_parse_text(dom_element *text, | |||
56 | struct svgtiny_parse_state state); | |||
57 | static void svgtiny_parse_position_attributes(dom_element *node, | |||
58 | const struct svgtiny_parse_state state, | |||
59 | float *x, float *y, float *width, float *height); | |||
60 | static void svgtiny_parse_paint_attributes(dom_element *node, | |||
61 | struct svgtiny_parse_state *state); | |||
62 | static void svgtiny_parse_font_attributes(dom_element *node, | |||
63 | struct svgtiny_parse_state *state); | |||
64 | static void svgtiny_parse_transform_attributes(dom_element *node, | |||
65 | struct svgtiny_parse_state *state); | |||
66 | static svgtiny_code svgtiny_add_path(float *p, unsigned int n, | |||
67 | struct svgtiny_parse_state *state); | |||
68 | static void _svgtiny_parse_color(const char *s, svgtiny_colour *c, | |||
69 | struct svgtiny_parse_state_gradient *grad, | |||
70 | struct svgtiny_parse_state *state); | |||
71 | ||||
72 | /** | |||
73 | * rotate midpoint vector | |||
74 | */ | |||
75 | static void | |||
76 | rotate_midpoint_vector(float ax, float ay, | |||
77 | float bx, float by, | |||
78 | double radangle, | |||
79 | double *x_out, double *y_out) | |||
80 | { | |||
81 | double dx2; /* midpoint x coordinate */ | |||
82 | double dy2; /* midpoint y coordinate */ | |||
83 | double cosangle; /* cosine of rotation angle */ | |||
84 | double sinangle; /* sine of rotation angle */ | |||
85 | ||||
86 | /* compute the sin and cos of the angle */ | |||
87 | cosangle = cos(radangle); | |||
88 | sinangle = sin(radangle); | |||
89 | ||||
90 | /* compute the midpoint between start and end points */ | |||
91 | dx2 = (ax - bx) / 2.0; | |||
92 | dy2 = (ay - by) / 2.0; | |||
93 | ||||
94 | /* rotate vector to remove angle */ | |||
95 | *x_out = ((cosangle * dx2) + (sinangle * dy2)); | |||
96 | *y_out = ((-sinangle * dx2) + (cosangle * dy2)); | |||
97 | } | |||
98 | ||||
99 | ||||
100 | /** | |||
101 | * ensure the arc radii are large enough and scale as appropriate | |||
102 | * | |||
103 | * the radii need to be large enough if they are not they must be | |||
104 | * adjusted. This allows for elimination of differences between | |||
105 | * implementations especialy with rounding. | |||
106 | */ | |||
107 | static void | |||
108 | ensure_radii_scale(double x1_sq, double y1_sq, | |||
109 | float *rx, float *ry, | |||
110 | double *rx_sq, double *ry_sq) | |||
111 | { | |||
112 | double radiisum; | |||
113 | double radiiscale; | |||
114 | ||||
115 | /* set radii square values */ | |||
116 | (*rx_sq) = (*rx) * (*rx); | |||
117 | (*ry_sq) = (*ry) * (*ry); | |||
118 | ||||
119 | radiisum = (x1_sq / (*rx_sq)) + (y1_sq / (*ry_sq)); | |||
120 | if (radiisum > 0.99999) { | |||
121 | /* need to scale radii */ | |||
122 | radiiscale = sqrt(radiisum) * 1.00001; | |||
123 | *rx = (float)(radiiscale * (*rx)); | |||
124 | *ry = (float)(radiiscale * (*ry)); | |||
125 | /* update squares too */ | |||
126 | (*rx_sq) = (*rx) * (*rx); | |||
127 | (*ry_sq) = (*ry) * (*ry); | |||
128 | } | |||
129 | } | |||
130 | ||||
131 | ||||
132 | /** | |||
133 | * compute the transformed centre point | |||
134 | */ | |||
135 | static void | |||
136 | compute_transformed_centre_point(double sign, float rx, float ry, | |||
137 | double rx_sq, double ry_sq, | |||
138 | double x1, double y1, | |||
139 | double x1_sq, double y1_sq, | |||
140 | double *cx1, double *cy1) | |||
141 | { | |||
142 | double sq; | |||
143 | double coef; | |||
144 | sq = ((rx_sq * ry_sq) - (rx_sq * y1_sq) - (ry_sq * x1_sq)) / | |||
145 | ((rx_sq * y1_sq) + (ry_sq * x1_sq)); | |||
146 | sq = (sq < 0) ? 0 : sq; | |||
147 | ||||
148 | coef = (sign * sqrt(sq)); | |||
149 | ||||
150 | *cx1 = coef * ((rx * y1) / ry); | |||
151 | *cy1 = coef * -((ry * x1) / rx); | |||
152 | } | |||
153 | ||||
154 | ||||
155 | /** | |||
156 | * compute untransformed centre point | |||
157 | * | |||
158 | * \param ax The first point x coordinate | |||
159 | * \param ay The first point y coordinate | |||
160 | * \param bx The second point x coordinate | |||
161 | * \param ay The second point y coordinate | |||
162 | */ | |||
163 | static void | |||
164 | compute_centre_point(float ax, float ay, | |||
165 | float bx, float by, | |||
166 | double cx1, double cy1, | |||
167 | double radangle, | |||
168 | double *x_out, double *y_out) | |||
169 | { | |||
170 | double sx2; | |||
171 | double sy2; | |||
172 | double cosangle; /* cosine of rotation angle */ | |||
173 | double sinangle; /* sine of rotation angle */ | |||
174 | ||||
175 | /* compute the sin and cos of the angle */ | |||
176 | cosangle = cos(radangle); | |||
177 | sinangle = sin(radangle); | |||
178 | ||||
179 | sx2 = (ax + bx) / 2.0; | |||
180 | sy2 = (ay + by) / 2.0; | |||
181 | ||||
182 | *x_out = sx2 + (cosangle * cx1 - sinangle * cy1); | |||
183 | *y_out = sy2 + (sinangle * cx1 + cosangle * cy1); | |||
184 | } | |||
185 | ||||
186 | ||||
187 | /** | |||
188 | * compute the angle start and extent | |||
189 | */ | |||
190 | static void | |||
191 | compute_angle_start_extent(float rx, float ry, | |||
192 | double x1, double y1, | |||
193 | double cx1, double cy1, | |||
194 | double *start, double *extent) | |||
195 | { | |||
196 | double sign; | |||
197 | double ux; | |||
198 | double uy; | |||
199 | double vx; | |||
200 | double vy; | |||
201 | double p, n; | |||
202 | double actmp; | |||
203 | ||||
204 | /* | |||
205 | * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v)) | |||
206 | * Where: | |||
207 | * '.' is the dot product. | |||
208 | * +/- is calculated from the sign of the cross product (u x v) | |||
209 | */ | |||
210 | ||||
211 | ux = (x1 - cx1) / rx; | |||
212 | uy = (y1 - cy1) / ry; | |||
213 | vx = (-x1 - cx1) / rx; | |||
214 | vy = (-y1 - cy1) / ry; | |||
215 | ||||
216 | /* compute the start angle */ | |||
217 | /* The angle between (ux, uy) and the 0 angle */ | |||
218 | ||||
219 | /* len(u) * len(1,0) == len(u) */ | |||
220 | n = sqrt((ux * ux) + (uy * uy)); | |||
221 | /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */ | |||
222 | p = ux; | |||
223 | /* u x v == (1 * uy - ux * 0) == uy */ | |||
224 | sign = (uy < 0) ? -1.0 : 1.0; | |||
225 | /* (p >= n) so safe */ | |||
226 | *start = sign * acos(p / n); | |||
227 | ||||
228 | /* compute the extent angle */ | |||
229 | n = sqrt(((ux * ux) + (uy * uy)) * ((vx * vx) + (vy * vy))); | |||
230 | p = (ux * vx) + (uy * vy); | |||
231 | sign = ((ux * vy) - (uy * vx) < 0) ? -1.0f : 1.0f; | |||
232 | ||||
233 | /* arc cos must operate between -1 and 1 */ | |||
234 | actmp = p / n; | |||
235 | if (actmp < -1.0) { | |||
236 | *extent = sign * M_PI3.14159265358979323846; | |||
237 | } else if (actmp > 1.0) { | |||
238 | *extent = 0; | |||
239 | } else { | |||
240 | *extent = sign * acos(actmp); | |||
241 | } | |||
242 | } | |||
243 | ||||
244 | ||||
245 | /** | |||
246 | * converts a circle centered unit circle arc to a series of bezier curves | |||
247 | * | |||
248 | * Each bezier is stored as six values of three pairs of coordinates | |||
249 | * | |||
250 | * The beziers are stored without their start point as that is assumed | |||
251 | * to be the preceding elements end point. | |||
252 | * | |||
253 | * \param start The start angle of the arc (in radians) | |||
254 | * \param extent The size of the arc (in radians) | |||
255 | * \param bzpt The array to store the bezier values in | |||
256 | * \return The number of bezier segments output (max 4) | |||
257 | */ | |||
258 | static int | |||
259 | circle_arc_to_bezier(double start, double extent, double *bzpt) | |||
260 | { | |||
261 | int bzsegments; | |||
262 | double increment; | |||
263 | double controllen; | |||
264 | int pos = 0; | |||
265 | int segment; | |||
266 | double angle; | |||
267 | double dx, dy; | |||
268 | ||||
269 | bzsegments = (int) ceil(fabs(extent) / M_PI_21.57079632679489661923); | |||
270 | increment = extent / bzsegments; | |||
271 | controllen = 4.0 / 3.0 * sin(increment / 2.0) / (1.0 + cos(increment / 2.0)); | |||
272 | ||||
273 | for (segment = 0; segment < bzsegments; segment++) { | |||
274 | /* first control point */ | |||
275 | angle = start + (segment * increment); | |||
276 | dx = cos(angle); | |||
277 | dy = sin(angle); | |||
278 | bzpt[pos++] = dx - controllen * dy; | |||
279 | bzpt[pos++] = dy + controllen * dx; | |||
280 | /* second control point */ | |||
281 | angle+=increment; | |||
282 | dx = cos(angle); | |||
283 | dy = sin(angle); | |||
284 | bzpt[pos++] = dx + controllen * dy; | |||
285 | bzpt[pos++] = dy - controllen * dx; | |||
286 | /* endpoint */ | |||
287 | bzpt[pos++] = dx; | |||
288 | bzpt[pos++] = dy; | |||
289 | ||||
290 | } | |||
291 | return bzsegments; | |||
292 | } | |||
293 | ||||
294 | ||||
295 | /** | |||
296 | * transform coordinate list | |||
297 | * | |||
298 | * perform a scale, rotate and translate on list of coordinates | |||
299 | * | |||
300 | * scale(rx,ry) | |||
301 | * rotate(an) | |||
302 | * translate (cx, cy) | |||
303 | * | |||
304 | * homogeneous transforms | |||
305 | * | |||
306 | * scaling | |||
307 | * | rx 0 0 | | |||
308 | * S = | 0 ry 0 | | |||
309 | * | 0 0 1 | | |||
310 | * | |||
311 | * rotate | |||
312 | * | cos(an) -sin(an) 0 | | |||
313 | * R = | sin(an) cos(an) 0 | | |||
314 | * | 0 0 1 | | |||
315 | * | |||
316 | * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}} | |||
317 | * | |||
318 | * translate | |||
319 | * | 1 0 cx | | |||
320 | * T = | 0 1 cy | | |||
321 | * | 0 0 1 | | |||
322 | * | |||
323 | * note order is significat here and the combined matrix is | |||
324 | * M = T.R.S | |||
325 | * | |||
326 | * | cos(an) -sin(an) cx | | |||
327 | * T.R = | sin(an) cos(an) cy | | |||
328 | * | 0 0 1 | | |||
329 | * | |||
330 | * | rx * cos(an) ry * -sin(an) cx | | |||
331 | * T.R.S = | rx * sin(an) ry * cos(an) cy | | |||
332 | * | 0 0 1 | | |||
333 | * | |||
334 | * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}} | |||
335 | * | |||
336 | * Each point | |||
337 | * | x1 | | |||
338 | * P = | y1 | | |||
339 | * | 1 | | |||
340 | * | |||
341 | * output | |||
342 | * | x2 | | |||
343 | * | y2 | = M . P | |||
344 | * | 1 | | |||
345 | * | |||
346 | * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a)) | |||
347 | * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a)) | |||
348 | * | |||
349 | * | |||
350 | * \param rx X scaling to apply | |||
351 | * \param ry Y scaling to apply | |||
352 | * \param radangle rotation to apply (in radians) | |||
353 | * \param cx X translation to apply | |||
354 | * \param cy Y translation to apply | |||
355 | * \param points The size of the bzpoints array | |||
356 | * \param bzpoints an array of x,y values to apply the transform to | |||
357 | */ | |||
358 | static void | |||
359 | scale_rotate_translate_points(double rx, double ry, | |||
360 | double radangle, | |||
361 | double cx, double cy, | |||
362 | int pntsize, | |||
363 | double *points) | |||
364 | { | |||
365 | int pnt; | |||
366 | double cosangle; /* cosine of rotation angle */ | |||
367 | double sinangle; /* sine of rotation angle */ | |||
368 | double rxcosangle, rxsinangle, rycosangle, rynsinangle; | |||
369 | double x2,y2; | |||
370 | ||||
371 | /* compute the sin and cos of the angle */ | |||
372 | cosangle = cos(radangle); | |||
373 | sinangle = sin(radangle); | |||
374 | ||||
375 | rxcosangle = rx * cosangle; | |||
376 | rxsinangle = rx * sinangle; | |||
377 | rycosangle = ry * cosangle; | |||
378 | rynsinangle = ry * -1 * sinangle; | |||
379 | ||||
380 | for (pnt = 0; pnt < pntsize; pnt+=2) { | |||
381 | x2 = cx + (points[pnt] * rxcosangle) + (points[pnt + 1] * rynsinangle); | |||
| ||||
382 | y2 = cy + (points[pnt + 1] * rycosangle) + (points[pnt] * rxsinangle); | |||
383 | points[pnt] = x2; | |||
384 | points[pnt + 1] = y2; | |||
385 | } | |||
386 | } | |||
387 | ||||
388 | ||||
389 | /** | |||
390 | * convert an svg path arc to a bezier curve | |||
391 | * | |||
392 | * This function perfoms a transform on the nine arc parameters | |||
393 | * (coordinate pairs for start and end together with the radii of the | |||
394 | * elipse, the rotation angle and which of the four arcs to draw) | |||
395 | * which generates the parameters (coordinate pairs for start, | |||
396 | * end and their control points) for a set of up to four bezier curves. | |||
397 | * | |||
398 | * Obviously the start and end coordinates are not altered between | |||
399 | * representations so the aim is to calculate the coordinate pairs for | |||
400 | * the bezier control points. | |||
401 | * | |||
402 | * \param bzpoints the array to fill with bezier curves | |||
403 | * \return the number of bezier segments generated or -1 for a line | |||
404 | */ | |||
405 | static int | |||
406 | svgarc_to_bezier(float start_x, | |||
407 | float start_y, | |||
408 | float end_x, | |||
409 | float end_y, | |||
410 | float rx, | |||
411 | float ry, | |||
412 | float angle, | |||
413 | bool_Bool largearc, | |||
414 | bool_Bool sweep, | |||
415 | double *bzpoints) | |||
416 | { | |||
417 | double radangle; /* normalised elipsis rotation angle in radians */ | |||
418 | double rx_sq; /* x radius squared */ | |||
419 | double ry_sq; /* y radius squared */ | |||
420 | double x1, y1; /* rotated midpoint vector */ | |||
421 | double x1_sq, y1_sq; /* x1 vector squared */ | |||
422 | double cx1,cy1; /* transformed circle center */ | |||
423 | double cx,cy; /* circle center */ | |||
424 | double start, extent; | |||
425 | int bzsegments; | |||
426 | ||||
427 | if ((start_x == end_x) && (start_y == end_y)) { | |||
428 | /* | |||
429 | * if the start and end coordinates are the same the | |||
430 | * svg spec says this is equivalent to having no segment | |||
431 | * at all | |||
432 | */ | |||
433 | return 0; | |||
434 | } | |||
435 | ||||
436 | if ((rx == 0) || (ry == 0)) { | |||
437 | /* | |||
438 | * if either radii is zero the specified behaviour is a line | |||
439 | */ | |||
440 | return -1; | |||
441 | } | |||
442 | ||||
443 | /* obtain the absolute values of the radii */ | |||
444 | rx = fabsf(rx); | |||
445 | ry = fabsf(ry); | |||
446 | ||||
447 | /* convert normalised angle to radians */ | |||
448 | radangle = degToRad(fmod(angle, 360.0))((fmod(angle, 360.0)) * 3.14159265358979323846 / 180.0); | |||
449 | ||||
450 | /* step 1 */ | |||
451 | /* x1,x2 is the midpoint vector rotated to remove the arc angle */ | |||
452 | rotate_midpoint_vector(start_x, start_y, end_x, end_y, radangle, &x1, &y1); | |||
453 | ||||
454 | /* step 2 */ | |||
455 | /* get squared x1 values */ | |||
456 | x1_sq = x1 * x1; | |||
457 | y1_sq = y1 * y1; | |||
458 | ||||
459 | /* ensure radii are correctly scaled */ | |||
460 | ensure_radii_scale(x1_sq, y1_sq, &rx, &ry, &rx_sq, &ry_sq); | |||
461 | ||||
462 | /* compute the transformed centre point */ | |||
463 | compute_transformed_centre_point(largearc == sweep?-1:1, | |||
464 | rx, ry, | |||
465 | rx_sq, ry_sq, | |||
466 | x1, y1, | |||
467 | x1_sq, y1_sq, | |||
468 | &cx1, &cy1); | |||
469 | ||||
470 | /* step 3 */ | |||
471 | /* get the untransformed centre point */ | |||
472 | compute_centre_point(start_x, start_y, | |||
473 | end_x, end_y, | |||
474 | cx1, cy1, | |||
475 | radangle, | |||
476 | &cx, &cy); | |||
477 | ||||
478 | /* step 4 */ | |||
479 | /* compute anglestart and extent */ | |||
480 | compute_angle_start_extent(rx,ry, | |||
481 | x1,y1, | |||
482 | cx1, cy1, | |||
483 | &start, &extent); | |||
484 | ||||
485 | /* extent of 0 is a straight line */ | |||
486 | if (extent == 0) { | |||
487 | return -1; | |||
488 | } | |||
489 | ||||
490 | /* take account of sweep */ | |||
491 | if (!sweep && extent > 0) { | |||
492 | extent -= TAU6.28318530717958647692; | |||
493 | } else if (sweep
| |||
494 | extent += TAU6.28318530717958647692; | |||
495 | } | |||
496 | ||||
497 | /* normalise start and extent */ | |||
498 | extent = fmod(extent, TAU6.28318530717958647692); | |||
499 | start = fmod(start, TAU6.28318530717958647692); | |||
500 | ||||
501 | /* convert the arc to unit circle bezier curves */ | |||
502 | bzsegments = circle_arc_to_bezier(start, extent, bzpoints); | |||
503 | ||||
504 | /* transform the bezier curves */ | |||
505 | scale_rotate_translate_points(rx, ry, | |||
506 | radangle, | |||
507 | cx, cy, | |||
508 | bzsegments * 6, | |||
509 | bzpoints); | |||
510 | ||||
511 | return bzsegments; | |||
512 | } | |||
513 | ||||
514 | ||||
515 | /** | |||
516 | * Call this to ref the strings in a gradient state. | |||
517 | */ | |||
518 | static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient *grad) | |||
519 | { | |||
520 | if (grad->gradient_x1 != NULL((void*)0)) { | |||
521 | dom_string_ref(grad->gradient_x1); | |||
522 | } | |||
523 | if (grad->gradient_y1 != NULL((void*)0)) { | |||
524 | dom_string_ref(grad->gradient_y1); | |||
525 | } | |||
526 | if (grad->gradient_x2 != NULL((void*)0)) { | |||
527 | dom_string_ref(grad->gradient_x2); | |||
528 | } | |||
529 | if (grad->gradient_y2 != NULL((void*)0)) { | |||
530 | dom_string_ref(grad->gradient_y2); | |||
531 | } | |||
532 | } | |||
533 | ||||
534 | /** | |||
535 | * Call this to clean up the strings in a gradient state. | |||
536 | */ | |||
537 | static void svgtiny_grad_string_cleanup( | |||
538 | struct svgtiny_parse_state_gradient *grad) | |||
539 | { | |||
540 | if (grad->gradient_x1 != NULL((void*)0)) { | |||
541 | dom_string_unref(grad->gradient_x1); | |||
542 | grad->gradient_x1 = NULL((void*)0); | |||
543 | } | |||
544 | if (grad->gradient_y1 != NULL((void*)0)) { | |||
545 | dom_string_unref(grad->gradient_y1); | |||
546 | grad->gradient_y1 = NULL((void*)0); | |||
547 | } | |||
548 | if (grad->gradient_x2 != NULL((void*)0)) { | |||
549 | dom_string_unref(grad->gradient_x2); | |||
550 | grad->gradient_x2 = NULL((void*)0); | |||
551 | } | |||
552 | if (grad->gradient_y2 != NULL((void*)0)) { | |||
553 | dom_string_unref(grad->gradient_y2); | |||
554 | grad->gradient_y2 = NULL((void*)0); | |||
555 | } | |||
556 | } | |||
557 | ||||
558 | /** | |||
559 | * Set the local externally-stored parts of a parse state. | |||
560 | * Call this in functions that made a new state on the stack. | |||
561 | * Doesn't make own copy of global state, such as the interned string list. | |||
562 | */ | |||
563 | static void svgtiny_setup_state_local(struct svgtiny_parse_state *state) | |||
564 | { | |||
565 | svgtiny_grad_string_ref(&(state->fill_grad)); | |||
566 | svgtiny_grad_string_ref(&(state->stroke_grad)); | |||
567 | } | |||
568 | ||||
569 | /** | |||
570 | * Cleanup the local externally-stored parts of a parse state. | |||
571 | * Call this in functions that made a new state on the stack. | |||
572 | * Doesn't cleanup global state, such as the interned string list. | |||
573 | */ | |||
574 | static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state) | |||
575 | { | |||
576 | svgtiny_grad_string_cleanup(&(state->fill_grad)); | |||
577 | svgtiny_grad_string_cleanup(&(state->stroke_grad)); | |||
578 | } | |||
579 | ||||
580 | ||||
581 | /** | |||
582 | * Create a new svgtiny_diagram structure. | |||
583 | */ | |||
584 | ||||
585 | struct svgtiny_diagram *svgtiny_create(void) | |||
586 | { | |||
587 | struct svgtiny_diagram *diagram; | |||
588 | ||||
589 | diagram = calloc(sizeof(*diagram), 1); | |||
590 | if (!diagram) | |||
591 | return 0; | |||
592 | ||||
593 | return diagram; | |||
594 | free(diagram); | |||
595 | return NULL((void*)0); | |||
596 | } | |||
597 | ||||
598 | static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...) | |||
599 | { | |||
600 | UNUSED(severity)((void) (severity)); | |||
601 | UNUSED(ctx)((void) (ctx)); | |||
602 | UNUSED(msg)((void) (msg)); | |||
603 | } | |||
604 | ||||
605 | /** | |||
606 | * Parse a block of memory into a svgtiny_diagram. | |||
607 | */ | |||
608 | ||||
609 | svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, | |||
610 | const char *buffer, size_t size, const char *url, | |||
611 | int viewport_width, int viewport_height) | |||
612 | { | |||
613 | dom_document *document; | |||
614 | dom_exception exc; | |||
615 | dom_xml_parser *parser; | |||
616 | dom_xml_error err; | |||
617 | dom_element *svg; | |||
618 | dom_string *svg_name; | |||
619 | lwc_string *svg_name_lwc; | |||
620 | struct svgtiny_parse_state state; | |||
621 | float x, y, width, height; | |||
622 | svgtiny_code code; | |||
623 | ||||
624 | assert(diagram)((diagram) ? (void) (0) : __assert_fail ("diagram", "src/svgtiny.c" , 624, __extension__ __PRETTY_FUNCTION__)); | |||
625 | assert(buffer)((buffer) ? (void) (0) : __assert_fail ("buffer", "src/svgtiny.c" , 625, __extension__ __PRETTY_FUNCTION__)); | |||
626 | assert(url)((url) ? (void) (0) : __assert_fail ("url", "src/svgtiny.c", 626 , __extension__ __PRETTY_FUNCTION__)); | |||
627 | ||||
628 | UNUSED(url)((void) (url)); | |||
629 | ||||
630 | parser = dom_xml_parser_create(NULL((void*)0), NULL((void*)0), | |||
631 | ignore_msg, NULL((void*)0), &document); | |||
632 | ||||
633 | if (parser == NULL((void*)0)) | |||
634 | return svgtiny_LIBDOM_ERROR; | |||
635 | ||||
636 | err = dom_xml_parser_parse_chunk(parser, (uint8_t *)buffer, size); | |||
637 | if (err != DOM_XML_OK) { | |||
638 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
639 | dom_xml_parser_destroy(parser); | |||
640 | return svgtiny_LIBDOM_ERROR; | |||
641 | } | |||
642 | ||||
643 | err = dom_xml_parser_completed(parser); | |||
644 | if (err != DOM_XML_OK) { | |||
645 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
646 | dom_xml_parser_destroy(parser); | |||
647 | return svgtiny_LIBDOM_ERROR; | |||
648 | } | |||
649 | ||||
650 | /* We're done parsing, drop the parser. | |||
651 | * We now own the document entirely. | |||
652 | */ | |||
653 | dom_xml_parser_destroy(parser); | |||
654 | ||||
655 | /* find root <svg> element */ | |||
656 | exc = dom_document_get_document_element(document, &svg)dom_document_get_document_element((dom_document *) (document) , (struct dom_element **) (&svg)); | |||
657 | if (exc != DOM_NO_ERR) { | |||
658 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
659 | return svgtiny_LIBDOM_ERROR; | |||
660 | } | |||
661 | if (svg == NULL((void*)0)) { | |||
662 | /* no root svg element */ | |||
663 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
664 | return svgtiny_SVG_ERROR; | |||
665 | } | |||
666 | ||||
667 | exc = dom_node_get_node_name(svg, &svg_name)dom_node_get_node_name((dom_node *) (svg), (&svg_name)); | |||
668 | if (exc != DOM_NO_ERR) { | |||
669 | dom_node_unref(svg)dom_node_unref((dom_node *) (svg)); | |||
670 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
671 | return svgtiny_LIBDOM_ERROR; | |||
672 | } | |||
673 | if (lwc_intern_string("svg", 3 /* SLEN("svg") */, | |||
674 | &svg_name_lwc) != lwc_error_ok) { | |||
675 | dom_string_unref(svg_name); | |||
676 | dom_node_unref(svg)dom_node_unref((dom_node *) (svg)); | |||
677 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
678 | return svgtiny_LIBDOM_ERROR; | |||
679 | } | |||
680 | if (!dom_string_caseless_lwc_isequal(svg_name, svg_name_lwc)) { | |||
681 | lwc_string_unref(svg_name_lwc){ lwc_string *__lwc_s = (svg_name_lwc); ((__lwc_s != ((void*) 0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/svgtiny.c" , 681, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
682 | dom_string_unref(svg_name); | |||
683 | dom_node_unref(svg)dom_node_unref((dom_node *) (svg)); | |||
684 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
685 | return svgtiny_NOT_SVG; | |||
686 | } | |||
687 | ||||
688 | lwc_string_unref(svg_name_lwc){ lwc_string *__lwc_s = (svg_name_lwc); ((__lwc_s != ((void*) 0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/svgtiny.c" , 688, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; | |||
689 | dom_string_unref(svg_name); | |||
690 | ||||
691 | /* get graphic dimensions */ | |||
692 | memset(&state, 0, sizeof(state)); | |||
693 | state.diagram = diagram; | |||
694 | state.document = document; | |||
695 | state.viewport_width = viewport_width; | |||
696 | state.viewport_height = viewport_height; | |||
697 | ||||
698 | #define SVGTINY_STRING_ACTION2(s,n) \ | |||
699 | if (dom_string_create_interned((const uint8_t *) #n, \ | |||
700 | strlen(#n), &state.interned_##s) \ | |||
701 | != DOM_NO_ERR) { \ | |||
702 | code = svgtiny_LIBDOM_ERROR; \ | |||
703 | goto cleanup; \ | |||
704 | } | |||
705 | #include "svgtiny_strings.h" | |||
706 | #undef SVGTINY_STRING_ACTION2 | |||
707 | ||||
708 | svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height); | |||
709 | diagram->width = width; | |||
710 | diagram->height = height; | |||
711 | ||||
712 | /* set up parsing state */ | |||
713 | state.viewport_width = width; | |||
714 | state.viewport_height = height; | |||
715 | state.ctm.a = 1; /*(float) viewport_width / (float) width;*/ | |||
716 | state.ctm.b = 0; | |||
717 | state.ctm.c = 0; | |||
718 | state.ctm.d = 1; /*(float) viewport_height / (float) height;*/ | |||
719 | state.ctm.e = 0; /*x;*/ | |||
720 | state.ctm.f = 0; /*y;*/ | |||
721 | /*state.style = css_base_style; | |||
722 | state.style.font_size.value.length.value = option_font_size * 0.1;*/ | |||
723 | state.fill = 0x000000; | |||
724 | state.stroke = svgtiny_TRANSPARENT0x1000000; | |||
725 | state.stroke_width = 1; | |||
726 | ||||
727 | /* parse tree */ | |||
728 | code = svgtiny_parse_svg(svg, state); | |||
729 | ||||
730 | dom_node_unref(svg)dom_node_unref((dom_node *) (svg)); | |||
731 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); | |||
732 | ||||
733 | cleanup: | |||
734 | svgtiny_cleanup_state_local(&state); | |||
735 | #define SVGTINY_STRING_ACTION2(s,n) \ | |||
736 | if (state.interned_##s != NULL((void*)0)) \ | |||
737 | dom_string_unref(state.interned_##s); | |||
738 | #include "svgtiny_strings.h" | |||
739 | #undef SVGTINY_STRING_ACTION2 | |||
740 | return code; | |||
741 | } | |||
742 | ||||
743 | ||||
744 | /** | |||
745 | * Parse a <svg> or <g> element node. | |||
746 | */ | |||
747 | ||||
748 | svgtiny_code svgtiny_parse_svg(dom_element *svg, | |||
749 | struct svgtiny_parse_state state) | |||
750 | { | |||
751 | float x, y, width, height; | |||
752 | dom_string *view_box; | |||
753 | dom_element *child; | |||
754 | dom_exception exc; | |||
755 | ||||
756 | svgtiny_setup_state_local(&state); | |||
757 | ||||
758 | svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height); | |||
759 | svgtiny_parse_paint_attributes(svg, &state); | |||
760 | svgtiny_parse_font_attributes(svg, &state); | |||
761 | ||||
762 | exc = dom_element_get_attribute(svg, state.interned_viewBox,dom_element_get_attribute( (dom_element *) (svg), (state.interned_viewBox ), (&view_box)) | |||
763 | &view_box)dom_element_get_attribute( (dom_element *) (svg), (state.interned_viewBox ), (&view_box)); | |||
764 | if (exc != DOM_NO_ERR) { | |||
765 | svgtiny_cleanup_state_local(&state); | |||
766 | return svgtiny_LIBDOM_ERROR; | |||
767 | } | |||
768 | ||||
769 | if (view_box) { | |||
770 | char *s = strndup(dom_string_data(view_box), | |||
771 | dom_string_byte_length(view_box)); | |||
772 | float min_x, min_y, vwidth, vheight; | |||
773 | if (sscanf(s, "%f,%f,%f,%f", | |||
774 | &min_x, &min_y, &vwidth, &vheight) == 4 || | |||
775 | sscanf(s, "%f %f %f %f", | |||
776 | &min_x, &min_y, &vwidth, &vheight) == 4) { | |||
777 | state.ctm.a = (float) state.viewport_width / vwidth; | |||
778 | state.ctm.d = (float) state.viewport_height / vheight; | |||
779 | state.ctm.e += -min_x * state.ctm.a; | |||
780 | state.ctm.f += -min_y * state.ctm.d; | |||
781 | } | |||
782 | free(s); | |||
783 | dom_string_unref(view_box); | |||
784 | } | |||
785 | ||||
786 | svgtiny_parse_transform_attributes(svg, &state); | |||
787 | ||||
788 | exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child)dom_node_get_first_child( (dom_node *) (svg), (dom_node **) ( (dom_node **) (void *) &child)); | |||
789 | if (exc != DOM_NO_ERR) { | |||
790 | svgtiny_cleanup_state_local(&state); | |||
791 | return svgtiny_LIBDOM_ERROR; | |||
792 | } | |||
793 | while (child != NULL((void*)0)) { | |||
794 | dom_element *next; | |||
795 | dom_node_type nodetype; | |||
796 | svgtiny_code code = svgtiny_OK; | |||
797 | ||||
798 | exc = dom_node_get_node_type(child, &nodetype)dom_node_get_node_type( (dom_node *) (child), (dom_node_type * ) (&nodetype)); | |||
799 | if (exc != DOM_NO_ERR) { | |||
800 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
801 | return svgtiny_LIBDOM_ERROR; | |||
802 | } | |||
803 | if (nodetype == DOM_ELEMENT_NODE) { | |||
804 | dom_string *nodename; | |||
805 | exc = dom_node_get_node_name(child, &nodename)dom_node_get_node_name((dom_node *) (child), (&nodename)); | |||
806 | if (exc != DOM_NO_ERR) { | |||
807 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
808 | svgtiny_cleanup_state_local(&state); | |||
809 | return svgtiny_LIBDOM_ERROR; | |||
810 | } | |||
811 | if (dom_string_caseless_isequal(state.interned_svg, | |||
812 | nodename)) | |||
813 | code = svgtiny_parse_svg(child, state); | |||
814 | else if (dom_string_caseless_isequal(state.interned_g, | |||
815 | nodename)) | |||
816 | code = svgtiny_parse_svg(child, state); | |||
817 | else if (dom_string_caseless_isequal(state.interned_a, | |||
818 | nodename)) | |||
819 | code = svgtiny_parse_svg(child, state); | |||
820 | else if (dom_string_caseless_isequal(state.interned_path, | |||
821 | nodename)) | |||
822 | code = svgtiny_parse_path(child, state); | |||
823 | else if (dom_string_caseless_isequal(state.interned_rect, | |||
824 | nodename)) | |||
825 | code = svgtiny_parse_rect(child, state); | |||
826 | else if (dom_string_caseless_isequal(state.interned_circle, | |||
827 | nodename)) | |||
828 | code = svgtiny_parse_circle(child, state); | |||
829 | else if (dom_string_caseless_isequal(state.interned_ellipse, | |||
830 | nodename)) | |||
831 | code = svgtiny_parse_ellipse(child, state); | |||
832 | else if (dom_string_caseless_isequal(state.interned_line, | |||
833 | nodename)) | |||
834 | code = svgtiny_parse_line(child, state); | |||
835 | else if (dom_string_caseless_isequal(state.interned_polyline, | |||
836 | nodename)) | |||
837 | code = svgtiny_parse_poly(child, state, false0); | |||
838 | else if (dom_string_caseless_isequal(state.interned_polygon, | |||
839 | nodename)) | |||
840 | code = svgtiny_parse_poly(child, state, true1); | |||
841 | else if (dom_string_caseless_isequal(state.interned_text, | |||
842 | nodename)) | |||
843 | code = svgtiny_parse_text(child, state); | |||
844 | dom_string_unref(nodename); | |||
845 | } | |||
846 | if (code != svgtiny_OK) { | |||
847 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
848 | svgtiny_cleanup_state_local(&state); | |||
849 | return code; | |||
850 | } | |||
851 | exc = dom_node_get_next_sibling(child,dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) ((dom_node **) (void *) &next)) | |||
852 | (dom_node **) (void *) &next)dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) ((dom_node **) (void *) &next)); | |||
853 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
854 | if (exc != DOM_NO_ERR) { | |||
855 | svgtiny_cleanup_state_local(&state); | |||
856 | return svgtiny_LIBDOM_ERROR; | |||
857 | } | |||
858 | child = next; | |||
859 | } | |||
860 | ||||
861 | svgtiny_cleanup_state_local(&state); | |||
862 | return svgtiny_OK; | |||
863 | } | |||
864 | ||||
865 | ||||
866 | ||||
867 | /** | |||
868 | * Parse a <path> element node. | |||
869 | * | |||
870 | * http://www.w3.org/TR/SVG11/paths#PathElement | |||
871 | */ | |||
872 | ||||
873 | svgtiny_code svgtiny_parse_path(dom_element *path, | |||
874 | struct svgtiny_parse_state state) | |||
875 | { | |||
876 | svgtiny_code err; | |||
877 | dom_string *path_d_str; | |||
878 | dom_exception exc; | |||
879 | char *s, *path_d; | |||
880 | float *p; /* path elemets */ | |||
881 | unsigned int palloc; /* number of path elements allocated */ | |||
882 | unsigned int i; | |||
883 | float last_x = 0, last_y = 0; | |||
884 | float last_cubic_x = 0, last_cubic_y = 0; | |||
885 | float last_quad_x = 0, last_quad_y = 0; | |||
886 | float subpath_first_x = 0, subpath_first_y = 0; | |||
887 | ||||
888 | svgtiny_setup_state_local(&state); | |||
889 | ||||
890 | svgtiny_parse_paint_attributes(path, &state); | |||
891 | svgtiny_parse_transform_attributes(path, &state); | |||
892 | ||||
893 | /* read d attribute */ | |||
894 | exc = dom_element_get_attribute(path, state.interned_d, &path_d_str)dom_element_get_attribute( (dom_element *) (path), (state.interned_d ), (&path_d_str)); | |||
895 | if (exc != DOM_NO_ERR) { | |||
| ||||
896 | state.diagram->error_line = -1; /* path->line; */ | |||
897 | state.diagram->error_message = "path: error retrieving d attribute"; | |||
898 | svgtiny_cleanup_state_local(&state); | |||
899 | return svgtiny_SVG_ERROR; | |||
900 | } | |||
901 | ||||
902 | if (path_d_str == NULL((void*)0)) { | |||
903 | state.diagram->error_line = -1; /* path->line; */ | |||
904 | state.diagram->error_message = "path: missing d attribute"; | |||
905 | svgtiny_cleanup_state_local(&state); | |||
906 | return svgtiny_SVG_ERROR; | |||
907 | } | |||
908 | ||||
909 | /* empty path is permitted it just disables the path */ | |||
910 | palloc = dom_string_byte_length(path_d_str); | |||
911 | if (palloc == 0) { | |||
912 | dom_string_unref(path_d_str); | |||
913 | svgtiny_cleanup_state_local(&state); | |||
914 | return svgtiny_OK; | |||
915 | } | |||
916 | ||||
917 | /* local copy of the path data allowing in-place modification */ | |||
918 | s = path_d = strndup(dom_string_data(path_d_str), palloc); | |||
919 | dom_string_unref(path_d_str); | |||
920 | if (s == NULL((void*)0)) { | |||
921 | svgtiny_cleanup_state_local(&state); | |||
922 | return svgtiny_OUT_OF_MEMORY; | |||
923 | } | |||
924 | ||||
925 | /* ensure path element allocation is sensibly bounded */ | |||
926 | if (palloc < 8) { | |||
927 | palloc = 8; | |||
928 | } else if (palloc > 64) { | |||
929 | palloc = palloc / 2; | |||
930 | } | |||
931 | ||||
932 | /* allocate initial space for path elements */ | |||
933 | p = malloc(sizeof p[0] * palloc); | |||
934 | if (p == NULL((void*)0)) { | |||
935 | free(path_d); | |||
936 | svgtiny_cleanup_state_local(&state); | |||
937 | return svgtiny_OUT_OF_MEMORY; | |||
938 | } | |||
939 | ||||
940 | /* parse d and build path */ | |||
941 | for (i = 0; s[i]; i++) | |||
942 | if (s[i] == ',') | |||
943 | s[i] = ' '; | |||
944 | i = 0; | |||
945 | while (*s) { | |||
946 | char command[2]; | |||
947 | int plot_command; | |||
948 | float x, y, x1, y1, x2, y2, rx, ry, rotation, large_arc, sweep; | |||
949 | int n; | |||
950 | ||||
951 | /* Ensure there is sufficient space for path elements */ | |||
952 | #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS)do { if ((palloc - i) < NUM_ELEMENTS) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc ); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0) \ | |||
953 | do { \ | |||
954 | if ((palloc - i) < NUM_ELEMENTS) { \ | |||
955 | float *tp; \ | |||
956 | palloc = (palloc * 2) + (palloc / 2); \ | |||
957 | tp = realloc(p, sizeof p[0] * palloc); \ | |||
958 | if (tp == NULL((void*)0)) { \ | |||
959 | free(p); \ | |||
960 | free(path_d); \ | |||
961 | svgtiny_cleanup_state_local(&state); \ | |||
962 | return svgtiny_OUT_OF_MEMORY; \ | |||
963 | } \ | |||
964 | p = tp; \ | |||
965 | } \ | |||
966 | } while(0) | |||
967 | ||||
968 | ||||
969 | /* moveto (M, m), lineto (L, l) (2 arguments) */ | |||
970 | if (sscanf(s, " %1[MmLl] %f %f %n", command, &x, &y, &n) == 3) { | |||
971 | /*LOG(("moveto or lineto"));*/ | |||
972 | if (*command == 'M' || *command == 'm') | |||
973 | plot_command = svgtiny_PATH_MOVE; | |||
974 | else | |||
975 | plot_command = svgtiny_PATH_LINE; | |||
976 | do { | |||
977 | ALLOC_PATH_ELEMENTS(3)do { if ((palloc - i) < 3) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
978 | p[i++] = plot_command; | |||
979 | if ('a' <= *command) { | |||
980 | x += last_x; | |||
981 | y += last_y; | |||
982 | } | |||
983 | if (plot_command == svgtiny_PATH_MOVE) { | |||
984 | subpath_first_x = x; | |||
985 | subpath_first_y = y; | |||
986 | } | |||
987 | p[i++] = last_cubic_x = last_quad_x = last_x | |||
988 | = x; | |||
989 | p[i++] = last_cubic_y = last_quad_y = last_y | |||
990 | = y; | |||
991 | s += n; | |||
992 | plot_command = svgtiny_PATH_LINE; | |||
993 | } while (sscanf(s, "%f %f %n", &x, &y, &n) == 2); | |||
994 | ||||
995 | /* closepath (Z, z) (no arguments) */ | |||
996 | } else if (sscanf(s, " %1[Zz] %n", command, &n) == 1) { | |||
997 | /*LOG(("closepath"));*/ | |||
998 | ALLOC_PATH_ELEMENTS(1)do { if ((palloc - i) < 1) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
999 | ||||
1000 | p[i++] = svgtiny_PATH_CLOSE; | |||
1001 | s += n; | |||
1002 | last_cubic_x = last_quad_x = last_x = subpath_first_x; | |||
1003 | last_cubic_y = last_quad_y = last_y = subpath_first_y; | |||
1004 | ||||
1005 | /* horizontal lineto (H, h) (1 argument) */ | |||
1006 | } else if (sscanf(s, " %1[Hh] %f %n", command, &x, &n) == 2) { | |||
1007 | /*LOG(("horizontal lineto"));*/ | |||
1008 | do { | |||
1009 | ALLOC_PATH_ELEMENTS(3)do { if ((palloc - i) < 3) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1010 | ||||
1011 | p[i++] = svgtiny_PATH_LINE; | |||
1012 | if (*command == 'h') | |||
1013 | x += last_x; | |||
1014 | p[i++] = last_cubic_x = last_quad_x = last_x | |||
1015 | = x; | |||
1016 | p[i++] = last_cubic_y = last_quad_y = last_y; | |||
1017 | s += n; | |||
1018 | } while (sscanf(s, "%f %n", &x, &n) == 1); | |||
1019 | ||||
1020 | /* vertical lineto (V, v) (1 argument) */ | |||
1021 | } else if (sscanf(s, " %1[Vv] %f %n", command, &y, &n) == 2) { | |||
1022 | /*LOG(("vertical lineto"));*/ | |||
1023 | do { | |||
1024 | ALLOC_PATH_ELEMENTS(3)do { if ((palloc - i) < 3) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1025 | ||||
1026 | p[i++] = svgtiny_PATH_LINE; | |||
1027 | if (*command == 'v') | |||
1028 | y += last_y; | |||
1029 | p[i++] = last_cubic_x = last_quad_x = last_x; | |||
1030 | p[i++] = last_cubic_y = last_quad_y = last_y | |||
1031 | = y; | |||
1032 | s += n; | |||
1033 | } while (sscanf(s, "%f %n", &y, &n) == 1); | |||
1034 | ||||
1035 | /* curveto (C, c) (6 arguments) */ | |||
1036 | } else if (sscanf(s, " %1[Cc] %f %f %f %f %f %f %n", command, | |||
1037 | &x1, &y1, &x2, &y2, &x, &y, &n) == 7) { | |||
1038 | /*LOG(("curveto"));*/ | |||
1039 | do { | |||
1040 | ALLOC_PATH_ELEMENTS(7)do { if ((palloc - i) < 7) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1041 | ||||
1042 | p[i++] = svgtiny_PATH_BEZIER; | |||
1043 | if (*command == 'c') { | |||
1044 | x1 += last_x; | |||
1045 | y1 += last_y; | |||
1046 | x2 += last_x; | |||
1047 | y2 += last_y; | |||
1048 | x += last_x; | |||
1049 | y += last_y; | |||
1050 | } | |||
1051 | p[i++] = x1; | |||
1052 | p[i++] = y1; | |||
1053 | p[i++] = last_cubic_x = x2; | |||
1054 | p[i++] = last_cubic_y = y2; | |||
1055 | p[i++] = last_quad_x = last_x = x; | |||
1056 | p[i++] = last_quad_y = last_y = y; | |||
1057 | s += n; | |||
1058 | } while (sscanf(s, "%f %f %f %f %f %f %n", | |||
1059 | &x1, &y1, &x2, &y2, &x, &y, &n) == 6); | |||
1060 | ||||
1061 | /* shorthand/smooth curveto (S, s) (4 arguments) */ | |||
1062 | } else if (sscanf(s, " %1[Ss] %f %f %f %f %n", command, | |||
1063 | &x2, &y2, &x, &y, &n) == 5) { | |||
1064 | /*LOG(("shorthand/smooth curveto"));*/ | |||
1065 | do { | |||
1066 | ALLOC_PATH_ELEMENTS(7)do { if ((palloc - i) < 7) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1067 | ||||
1068 | p[i++] = svgtiny_PATH_BEZIER; | |||
1069 | x1 = last_x + (last_x - last_cubic_x); | |||
1070 | y1 = last_y + (last_y - last_cubic_y); | |||
1071 | if (*command == 's') { | |||
1072 | x2 += last_x; | |||
1073 | y2 += last_y; | |||
1074 | x += last_x; | |||
1075 | y += last_y; | |||
1076 | } | |||
1077 | p[i++] = x1; | |||
1078 | p[i++] = y1; | |||
1079 | p[i++] = last_cubic_x = x2; | |||
1080 | p[i++] = last_cubic_y = y2; | |||
1081 | p[i++] = last_quad_x = last_x = x; | |||
1082 | p[i++] = last_quad_y = last_y = y; | |||
1083 | s += n; | |||
1084 | } while (sscanf(s, "%f %f %f %f %n", | |||
1085 | &x2, &y2, &x, &y, &n) == 4); | |||
1086 | ||||
1087 | /* quadratic Bezier curveto (Q, q) (4 arguments) */ | |||
1088 | } else if (sscanf(s, " %1[Qq] %f %f %f %f %n", command, | |||
1089 | &x1, &y1, &x, &y, &n) == 5) { | |||
1090 | /*LOG(("quadratic Bezier curveto"));*/ | |||
1091 | do { | |||
1092 | ALLOC_PATH_ELEMENTS(7)do { if ((palloc - i) < 7) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1093 | ||||
1094 | p[i++] = svgtiny_PATH_BEZIER; | |||
1095 | last_quad_x = x1; | |||
1096 | last_quad_y = y1; | |||
1097 | if (*command == 'q') { | |||
1098 | x1 += last_x; | |||
1099 | y1 += last_y; | |||
1100 | x += last_x; | |||
1101 | y += last_y; | |||
1102 | } | |||
1103 | p[i++] = 1./3 * last_x + 2./3 * x1; | |||
1104 | p[i++] = 1./3 * last_y + 2./3 * y1; | |||
1105 | p[i++] = 2./3 * x1 + 1./3 * x; | |||
1106 | p[i++] = 2./3 * y1 + 1./3 * y; | |||
1107 | p[i++] = last_cubic_x = last_x = x; | |||
1108 | p[i++] = last_cubic_y = last_y = y; | |||
1109 | s += n; | |||
1110 | } while (sscanf(s, "%f %f %f %f %n", | |||
1111 | &x1, &y1, &x, &y, &n) == 4); | |||
1112 | ||||
1113 | /* shorthand/smooth quadratic Bezier curveto (T, t) | |||
1114 | (2 arguments) */ | |||
1115 | } else if (sscanf(s, " %1[Tt] %f %f %n", command, | |||
1116 | &x, &y, &n) == 3) { | |||
1117 | /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/ | |||
1118 | do { | |||
1119 | ALLOC_PATH_ELEMENTS(7)do { if ((palloc - i) < 7) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1120 | ||||
1121 | p[i++] = svgtiny_PATH_BEZIER; | |||
1122 | x1 = last_x + (last_x - last_quad_x); | |||
1123 | y1 = last_y + (last_y - last_quad_y); | |||
1124 | last_quad_x = x1; | |||
1125 | last_quad_y = y1; | |||
1126 | if (*command == 't') { | |||
1127 | x1 += last_x; | |||
1128 | y1 += last_y; | |||
1129 | x += last_x; | |||
1130 | y += last_y; | |||
1131 | } | |||
1132 | p[i++] = 1./3 * last_x + 2./3 * x1; | |||
1133 | p[i++] = 1./3 * last_y + 2./3 * y1; | |||
1134 | p[i++] = 2./3 * x1 + 1./3 * x; | |||
1135 | p[i++] = 2./3 * y1 + 1./3 * y; | |||
1136 | p[i++] = last_cubic_x = last_x = x; | |||
1137 | p[i++] = last_cubic_y = last_y = y; | |||
1138 | s += n; | |||
1139 | } while (sscanf(s, "%f %f %n", | |||
1140 | &x, &y, &n) == 2); | |||
1141 | ||||
1142 | /* elliptical arc (A, a) (7 arguments) */ | |||
1143 | } else if (sscanf(s, " %1[Aa] %f %f %f %f %f %f %f %n", command, | |||
1144 | &rx, &ry, &rotation, &large_arc, &sweep, | |||
1145 | &x, &y, &n) == 8) { | |||
1146 | do { | |||
1147 | int bzsegments; | |||
1148 | double bzpoints[6*4]; /* allow for up to four bezier segments per arc */ | |||
1149 | ||||
1150 | if (*command == 'a') { | |||
1151 | x += last_x; | |||
1152 | y += last_y; | |||
1153 | } | |||
1154 | ||||
1155 | bzsegments = svgarc_to_bezier(last_x, last_y, | |||
1156 | x, y, | |||
1157 | rx, ry, | |||
1158 | rotation, | |||
1159 | large_arc, | |||
1160 | sweep, | |||
1161 | bzpoints); | |||
1162 | if (bzsegments == -1) { | |||
1163 | /* draw a line */ | |||
1164 | ALLOC_PATH_ELEMENTS(3)do { if ((palloc - i) < 3) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d); svgtiny_cleanup_state_local (&state); return svgtiny_OUT_OF_MEMORY; } p = tp; } } while (0); | |||
1165 | p[i++] = svgtiny_PATH_LINE; | |||
1166 | p[i++] = x; | |||
1167 | p[i++] = y; | |||
1168 | } else if (bzsegments > 0) { | |||
1169 | int bzpnt; | |||
1170 | ALLOC_PATH_ELEMENTS((unsigned int)bzsegments * 7)do { if ((palloc - i) < (unsigned int)bzsegments * 7) { float *tp; palloc = (palloc * 2) + (palloc / 2); tp = realloc(p, sizeof p[0] * palloc); if (tp == ((void*)0)) { free(p); free(path_d ); svgtiny_cleanup_state_local(&state); return svgtiny_OUT_OF_MEMORY ; } p = tp; } } while(0); | |||
1171 | for (bzpnt = 0;bzpnt < (bzsegments * 6); bzpnt+=6) { | |||
1172 | p[i++] = svgtiny_PATH_BEZIER; | |||
1173 | p[i++] = bzpoints[bzpnt]; | |||
1174 | p[i++] = bzpoints[bzpnt+1]; | |||
1175 | p[i++] = bzpoints[bzpnt+2]; | |||
1176 | p[i++] = bzpoints[bzpnt+3]; | |||
1177 | p[i++] = bzpoints[bzpnt+4]; | |||
1178 | p[i++] = bzpoints[bzpnt+5]; | |||
1179 | } | |||
1180 | } | |||
1181 | if (bzsegments != 0) { | |||
1182 | last_cubic_x = last_quad_x = last_x = p[i-2]; | |||
1183 | last_cubic_y = last_quad_y = last_y = p[i-1]; | |||
1184 | } | |||
1185 | ||||
1186 | ||||
1187 | s += n; | |||
1188 | } while (sscanf(s, "%f %f %f %f %f %f %f %n", | |||
1189 | &rx, &ry, &rotation, &large_arc, &sweep, | |||
1190 | &x, &y, &n) == 7); | |||
1191 | ||||
1192 | } else { | |||
1193 | /* fprintf(stderr, "parse failed at \"%s\"\n", s); */ | |||
1194 | break; | |||
1195 | } | |||
1196 | } | |||
1197 | ||||
1198 | free(path_d); | |||
1199 | ||||
1200 | if (i <= 4) { | |||
1201 | /* no real segments in path */ | |||
1202 | free(p); | |||
1203 | svgtiny_cleanup_state_local(&state); | |||
1204 | return svgtiny_OK; | |||
1205 | } | |||
1206 | ||||
1207 | /* resize path element array to not be over allocated */ | |||
1208 | if (palloc != i) { | |||
1209 | float *tp; | |||
1210 | ||||
1211 | /* try the resize, if it fails just continue to use the old | |||
1212 | * allocation | |||
1213 | */ | |||
1214 | tp = realloc(p, sizeof p[0] * i); | |||
1215 | if (tp != NULL((void*)0)) { | |||
1216 | p = tp; | |||
1217 | } | |||
1218 | } | |||
1219 | ||||
1220 | err = svgtiny_add_path(p, i, &state); | |||
1221 | ||||
1222 | svgtiny_cleanup_state_local(&state); | |||
1223 | ||||
1224 | return err; | |||
1225 | } | |||
1226 | ||||
1227 | ||||
1228 | /** | |||
1229 | * Parse a <rect> element node. | |||
1230 | * | |||
1231 | * http://www.w3.org/TR/SVG11/shapes#RectElement | |||
1232 | */ | |||
1233 | ||||
1234 | svgtiny_code svgtiny_parse_rect(dom_element *rect, | |||
1235 | struct svgtiny_parse_state state) | |||
1236 | { | |||
1237 | svgtiny_code err; | |||
1238 | float x, y, width, height; | |||
1239 | float *p; | |||
1240 | ||||
1241 | svgtiny_setup_state_local(&state); | |||
1242 | ||||
1243 | svgtiny_parse_position_attributes(rect, state, | |||
1244 | &x, &y, &width, &height); | |||
1245 | svgtiny_parse_paint_attributes(rect, &state); | |||
1246 | svgtiny_parse_transform_attributes(rect, &state); | |||
1247 | ||||
1248 | p = malloc(13 * sizeof p[0]); | |||
1249 | if (!p) { | |||
1250 | svgtiny_cleanup_state_local(&state); | |||
1251 | return svgtiny_OUT_OF_MEMORY; | |||
1252 | } | |||
1253 | ||||
1254 | p[0] = svgtiny_PATH_MOVE; | |||
1255 | p[1] = x; | |||
1256 | p[2] = y; | |||
1257 | p[3] = svgtiny_PATH_LINE; | |||
1258 | p[4] = x + width; | |||
1259 | p[5] = y; | |||
1260 | p[6] = svgtiny_PATH_LINE; | |||
1261 | p[7] = x + width; | |||
1262 | p[8] = y + height; | |||
1263 | p[9] = svgtiny_PATH_LINE; | |||
1264 | p[10] = x; | |||
1265 | p[11] = y + height; | |||
1266 | p[12] = svgtiny_PATH_CLOSE; | |||
1267 | ||||
1268 | err = svgtiny_add_path(p, 13, &state); | |||
1269 | ||||
1270 | svgtiny_cleanup_state_local(&state); | |||
1271 | ||||
1272 | return err; | |||
1273 | } | |||
1274 | ||||
1275 | ||||
1276 | /** | |||
1277 | * Parse a <circle> element node. | |||
1278 | */ | |||
1279 | ||||
1280 | svgtiny_code svgtiny_parse_circle(dom_element *circle, | |||
1281 | struct svgtiny_parse_state state) | |||
1282 | { | |||
1283 | svgtiny_code err; | |||
1284 | float x = 0, y = 0, r = -1; | |||
1285 | float *p; | |||
1286 | dom_string *attr; | |||
1287 | dom_exception exc; | |||
1288 | ||||
1289 | svgtiny_setup_state_local(&state); | |||
1290 | ||||
1291 | exc = dom_element_get_attribute(circle, state.interned_cx, &attr)dom_element_get_attribute( (dom_element *) (circle), (state.interned_cx ), (&attr)); | |||
1292 | if (exc != DOM_NO_ERR) { | |||
1293 | svgtiny_cleanup_state_local(&state); | |||
1294 | return svgtiny_LIBDOM_ERROR; | |||
1295 | } | |||
1296 | if (attr != NULL((void*)0)) { | |||
1297 | x = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1298 | } | |||
1299 | dom_string_unref(attr); | |||
1300 | ||||
1301 | exc = dom_element_get_attribute(circle, state.interned_cy, &attr)dom_element_get_attribute( (dom_element *) (circle), (state.interned_cy ), (&attr)); | |||
1302 | if (exc != DOM_NO_ERR) { | |||
1303 | svgtiny_cleanup_state_local(&state); | |||
1304 | return svgtiny_LIBDOM_ERROR; | |||
1305 | } | |||
1306 | if (attr != NULL((void*)0)) { | |||
1307 | y = svgtiny_parse_length(attr, state.viewport_height, state); | |||
1308 | } | |||
1309 | dom_string_unref(attr); | |||
1310 | ||||
1311 | exc = dom_element_get_attribute(circle, state.interned_r, &attr)dom_element_get_attribute( (dom_element *) (circle), (state.interned_r ), (&attr)); | |||
1312 | if (exc != DOM_NO_ERR) { | |||
1313 | svgtiny_cleanup_state_local(&state); | |||
1314 | return svgtiny_LIBDOM_ERROR; | |||
1315 | } | |||
1316 | if (attr != NULL((void*)0)) { | |||
1317 | r = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1318 | } | |||
1319 | dom_string_unref(attr); | |||
1320 | ||||
1321 | svgtiny_parse_paint_attributes(circle, &state); | |||
1322 | svgtiny_parse_transform_attributes(circle, &state); | |||
1323 | ||||
1324 | if (r < 0) { | |||
1325 | state.diagram->error_line = -1; /* circle->line; */ | |||
1326 | state.diagram->error_message = "circle: r missing or negative"; | |||
1327 | svgtiny_cleanup_state_local(&state); | |||
1328 | return svgtiny_SVG_ERROR; | |||
1329 | } | |||
1330 | if (r == 0) { | |||
1331 | svgtiny_cleanup_state_local(&state); | |||
1332 | return svgtiny_OK; | |||
1333 | } | |||
1334 | ||||
1335 | p = malloc(32 * sizeof p[0]); | |||
1336 | if (!p) { | |||
1337 | svgtiny_cleanup_state_local(&state); | |||
1338 | return svgtiny_OUT_OF_MEMORY; | |||
1339 | } | |||
1340 | ||||
1341 | p[0] = svgtiny_PATH_MOVE; | |||
1342 | p[1] = x + r; | |||
1343 | p[2] = y; | |||
1344 | p[3] = svgtiny_PATH_BEZIER; | |||
1345 | p[4] = x + r; | |||
1346 | p[5] = y + r * KAPPA0.5522847498; | |||
1347 | p[6] = x + r * KAPPA0.5522847498; | |||
1348 | p[7] = y + r; | |||
1349 | p[8] = x; | |||
1350 | p[9] = y + r; | |||
1351 | p[10] = svgtiny_PATH_BEZIER; | |||
1352 | p[11] = x - r * KAPPA0.5522847498; | |||
1353 | p[12] = y + r; | |||
1354 | p[13] = x - r; | |||
1355 | p[14] = y + r * KAPPA0.5522847498; | |||
1356 | p[15] = x - r; | |||
1357 | p[16] = y; | |||
1358 | p[17] = svgtiny_PATH_BEZIER; | |||
1359 | p[18] = x - r; | |||
1360 | p[19] = y - r * KAPPA0.5522847498; | |||
1361 | p[20] = x - r * KAPPA0.5522847498; | |||
1362 | p[21] = y - r; | |||
1363 | p[22] = x; | |||
1364 | p[23] = y - r; | |||
1365 | p[24] = svgtiny_PATH_BEZIER; | |||
1366 | p[25] = x + r * KAPPA0.5522847498; | |||
1367 | p[26] = y - r; | |||
1368 | p[27] = x + r; | |||
1369 | p[28] = y - r * KAPPA0.5522847498; | |||
1370 | p[29] = x + r; | |||
1371 | p[30] = y; | |||
1372 | p[31] = svgtiny_PATH_CLOSE; | |||
1373 | ||||
1374 | err = svgtiny_add_path(p, 32, &state); | |||
1375 | ||||
1376 | svgtiny_cleanup_state_local(&state); | |||
1377 | ||||
1378 | return err; | |||
1379 | } | |||
1380 | ||||
1381 | ||||
1382 | /** | |||
1383 | * Parse an <ellipse> element node. | |||
1384 | */ | |||
1385 | ||||
1386 | svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, | |||
1387 | struct svgtiny_parse_state state) | |||
1388 | { | |||
1389 | svgtiny_code err; | |||
1390 | float x = 0, y = 0, rx = -1, ry = -1; | |||
1391 | float *p; | |||
1392 | dom_string *attr; | |||
1393 | dom_exception exc; | |||
1394 | ||||
1395 | svgtiny_setup_state_local(&state); | |||
1396 | ||||
1397 | exc = dom_element_get_attribute(ellipse, state.interned_cx, &attr)dom_element_get_attribute( (dom_element *) (ellipse), (state. interned_cx), (&attr)); | |||
1398 | if (exc != DOM_NO_ERR) { | |||
1399 | svgtiny_cleanup_state_local(&state); | |||
1400 | return svgtiny_LIBDOM_ERROR; | |||
1401 | } | |||
1402 | if (attr != NULL((void*)0)) { | |||
1403 | x = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1404 | } | |||
1405 | dom_string_unref(attr); | |||
1406 | ||||
1407 | exc = dom_element_get_attribute(ellipse, state.interned_cy, &attr)dom_element_get_attribute( (dom_element *) (ellipse), (state. interned_cy), (&attr)); | |||
1408 | if (exc != DOM_NO_ERR) { | |||
1409 | svgtiny_cleanup_state_local(&state); | |||
1410 | return svgtiny_LIBDOM_ERROR; | |||
1411 | } | |||
1412 | if (attr != NULL((void*)0)) { | |||
1413 | y = svgtiny_parse_length(attr, state.viewport_height, state); | |||
1414 | } | |||
1415 | dom_string_unref(attr); | |||
1416 | ||||
1417 | exc = dom_element_get_attribute(ellipse, state.interned_rx, &attr)dom_element_get_attribute( (dom_element *) (ellipse), (state. interned_rx), (&attr)); | |||
1418 | if (exc != DOM_NO_ERR) { | |||
1419 | svgtiny_cleanup_state_local(&state); | |||
1420 | return svgtiny_LIBDOM_ERROR; | |||
1421 | } | |||
1422 | if (attr != NULL((void*)0)) { | |||
1423 | rx = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1424 | } | |||
1425 | dom_string_unref(attr); | |||
1426 | ||||
1427 | exc = dom_element_get_attribute(ellipse, state.interned_ry, &attr)dom_element_get_attribute( (dom_element *) (ellipse), (state. interned_ry), (&attr)); | |||
1428 | if (exc != DOM_NO_ERR) { | |||
1429 | svgtiny_cleanup_state_local(&state); | |||
1430 | return svgtiny_LIBDOM_ERROR; | |||
1431 | } | |||
1432 | if (attr != NULL((void*)0)) { | |||
1433 | ry = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1434 | } | |||
1435 | dom_string_unref(attr); | |||
1436 | ||||
1437 | svgtiny_parse_paint_attributes(ellipse, &state); | |||
1438 | svgtiny_parse_transform_attributes(ellipse, &state); | |||
1439 | ||||
1440 | if (rx < 0 || ry < 0) { | |||
1441 | state.diagram->error_line = -1; /* ellipse->line; */ | |||
1442 | state.diagram->error_message = "ellipse: rx or ry missing " | |||
1443 | "or negative"; | |||
1444 | svgtiny_cleanup_state_local(&state); | |||
1445 | return svgtiny_SVG_ERROR; | |||
1446 | } | |||
1447 | if (rx == 0 || ry == 0) { | |||
1448 | svgtiny_cleanup_state_local(&state); | |||
1449 | return svgtiny_OK; | |||
1450 | } | |||
1451 | ||||
1452 | p = malloc(32 * sizeof p[0]); | |||
1453 | if (!p) { | |||
1454 | svgtiny_cleanup_state_local(&state); | |||
1455 | return svgtiny_OUT_OF_MEMORY; | |||
1456 | } | |||
1457 | ||||
1458 | p[0] = svgtiny_PATH_MOVE; | |||
1459 | p[1] = x + rx; | |||
1460 | p[2] = y; | |||
1461 | p[3] = svgtiny_PATH_BEZIER; | |||
1462 | p[4] = x + rx; | |||
1463 | p[5] = y + ry * KAPPA0.5522847498; | |||
1464 | p[6] = x + rx * KAPPA0.5522847498; | |||
1465 | p[7] = y + ry; | |||
1466 | p[8] = x; | |||
1467 | p[9] = y + ry; | |||
1468 | p[10] = svgtiny_PATH_BEZIER; | |||
1469 | p[11] = x - rx * KAPPA0.5522847498; | |||
1470 | p[12] = y + ry; | |||
1471 | p[13] = x - rx; | |||
1472 | p[14] = y + ry * KAPPA0.5522847498; | |||
1473 | p[15] = x - rx; | |||
1474 | p[16] = y; | |||
1475 | p[17] = svgtiny_PATH_BEZIER; | |||
1476 | p[18] = x - rx; | |||
1477 | p[19] = y - ry * KAPPA0.5522847498; | |||
1478 | p[20] = x - rx * KAPPA0.5522847498; | |||
1479 | p[21] = y - ry; | |||
1480 | p[22] = x; | |||
1481 | p[23] = y - ry; | |||
1482 | p[24] = svgtiny_PATH_BEZIER; | |||
1483 | p[25] = x + rx * KAPPA0.5522847498; | |||
1484 | p[26] = y - ry; | |||
1485 | p[27] = x + rx; | |||
1486 | p[28] = y - ry * KAPPA0.5522847498; | |||
1487 | p[29] = x + rx; | |||
1488 | p[30] = y; | |||
1489 | p[31] = svgtiny_PATH_CLOSE; | |||
1490 | ||||
1491 | err = svgtiny_add_path(p, 32, &state); | |||
1492 | ||||
1493 | svgtiny_cleanup_state_local(&state); | |||
1494 | ||||
1495 | return err; | |||
1496 | } | |||
1497 | ||||
1498 | ||||
1499 | /** | |||
1500 | * Parse a <line> element node. | |||
1501 | */ | |||
1502 | ||||
1503 | svgtiny_code svgtiny_parse_line(dom_element *line, | |||
1504 | struct svgtiny_parse_state state) | |||
1505 | { | |||
1506 | svgtiny_code err; | |||
1507 | float x1 = 0, y1 = 0, x2 = 0, y2 = 0; | |||
1508 | float *p; | |||
1509 | dom_string *attr; | |||
1510 | dom_exception exc; | |||
1511 | ||||
1512 | svgtiny_setup_state_local(&state); | |||
1513 | ||||
1514 | exc = dom_element_get_attribute(line, state.interned_x1, &attr)dom_element_get_attribute( (dom_element *) (line), (state.interned_x1 ), (&attr)); | |||
1515 | if (exc != DOM_NO_ERR) { | |||
1516 | svgtiny_cleanup_state_local(&state); | |||
1517 | return svgtiny_LIBDOM_ERROR; | |||
1518 | } | |||
1519 | if (attr != NULL((void*)0)) { | |||
1520 | x1 = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1521 | } | |||
1522 | dom_string_unref(attr); | |||
1523 | ||||
1524 | exc = dom_element_get_attribute(line, state.interned_y1, &attr)dom_element_get_attribute( (dom_element *) (line), (state.interned_y1 ), (&attr)); | |||
1525 | if (exc != DOM_NO_ERR) { | |||
1526 | svgtiny_cleanup_state_local(&state); | |||
1527 | return svgtiny_LIBDOM_ERROR; | |||
1528 | } | |||
1529 | if (attr != NULL((void*)0)) { | |||
1530 | y1 = svgtiny_parse_length(attr, state.viewport_height, state); | |||
1531 | } | |||
1532 | dom_string_unref(attr); | |||
1533 | ||||
1534 | exc = dom_element_get_attribute(line, state.interned_x2, &attr)dom_element_get_attribute( (dom_element *) (line), (state.interned_x2 ), (&attr)); | |||
1535 | if (exc != DOM_NO_ERR) { | |||
1536 | svgtiny_cleanup_state_local(&state); | |||
1537 | return svgtiny_LIBDOM_ERROR; | |||
1538 | } | |||
1539 | if (attr != NULL((void*)0)) { | |||
1540 | x2 = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1541 | } | |||
1542 | dom_string_unref(attr); | |||
1543 | ||||
1544 | exc = dom_element_get_attribute(line, state.interned_y2, &attr)dom_element_get_attribute( (dom_element *) (line), (state.interned_y2 ), (&attr)); | |||
1545 | if (exc != DOM_NO_ERR) { | |||
1546 | svgtiny_cleanup_state_local(&state); | |||
1547 | return svgtiny_LIBDOM_ERROR; | |||
1548 | } | |||
1549 | if (attr != NULL((void*)0)) { | |||
1550 | y2 = svgtiny_parse_length(attr, state.viewport_height, state); | |||
1551 | } | |||
1552 | dom_string_unref(attr); | |||
1553 | ||||
1554 | svgtiny_parse_paint_attributes(line, &state); | |||
1555 | svgtiny_parse_transform_attributes(line, &state); | |||
1556 | ||||
1557 | p = malloc(7 * sizeof p[0]); | |||
1558 | if (!p) { | |||
1559 | svgtiny_cleanup_state_local(&state); | |||
1560 | return svgtiny_OUT_OF_MEMORY; | |||
1561 | } | |||
1562 | ||||
1563 | p[0] = svgtiny_PATH_MOVE; | |||
1564 | p[1] = x1; | |||
1565 | p[2] = y1; | |||
1566 | p[3] = svgtiny_PATH_LINE; | |||
1567 | p[4] = x2; | |||
1568 | p[5] = y2; | |||
1569 | p[6] = svgtiny_PATH_CLOSE; | |||
1570 | ||||
1571 | err = svgtiny_add_path(p, 7, &state); | |||
1572 | ||||
1573 | svgtiny_cleanup_state_local(&state); | |||
1574 | ||||
1575 | return err; | |||
1576 | } | |||
1577 | ||||
1578 | ||||
1579 | /** | |||
1580 | * Parse a <polyline> or <polygon> element node. | |||
1581 | * | |||
1582 | * http://www.w3.org/TR/SVG11/shapes#PolylineElement | |||
1583 | * http://www.w3.org/TR/SVG11/shapes#PolygonElement | |||
1584 | */ | |||
1585 | ||||
1586 | svgtiny_code svgtiny_parse_poly(dom_element *poly, | |||
1587 | struct svgtiny_parse_state state, bool_Bool polygon) | |||
1588 | { | |||
1589 | svgtiny_code err; | |||
1590 | dom_string *points_str; | |||
1591 | dom_exception exc; | |||
1592 | char *s, *points; | |||
1593 | float *p; | |||
1594 | unsigned int i; | |||
1595 | ||||
1596 | svgtiny_setup_state_local(&state); | |||
1597 | ||||
1598 | svgtiny_parse_paint_attributes(poly, &state); | |||
1599 | svgtiny_parse_transform_attributes(poly, &state); | |||
1600 | ||||
1601 | exc = dom_element_get_attribute(poly, state.interned_points,dom_element_get_attribute( (dom_element *) (poly), (state.interned_points ), (&points_str)) | |||
1602 | &points_str)dom_element_get_attribute( (dom_element *) (poly), (state.interned_points ), (&points_str)); | |||
1603 | if (exc != DOM_NO_ERR) { | |||
1604 | svgtiny_cleanup_state_local(&state); | |||
1605 | return svgtiny_LIBDOM_ERROR; | |||
1606 | } | |||
1607 | ||||
1608 | if (points_str == NULL((void*)0)) { | |||
1609 | state.diagram->error_line = -1; /* poly->line; */ | |||
1610 | state.diagram->error_message = | |||
1611 | "polyline/polygon: missing points attribute"; | |||
1612 | svgtiny_cleanup_state_local(&state); | |||
1613 | return svgtiny_SVG_ERROR; | |||
1614 | } | |||
1615 | ||||
1616 | s = points = strndup(dom_string_data(points_str), | |||
1617 | dom_string_byte_length(points_str)); | |||
1618 | dom_string_unref(points_str); | |||
1619 | /* read points attribute */ | |||
1620 | if (s == NULL((void*)0)) { | |||
1621 | svgtiny_cleanup_state_local(&state); | |||
1622 | return svgtiny_OUT_OF_MEMORY; | |||
1623 | } | |||
1624 | /* allocate space for path: it will never have more elements than s */ | |||
1625 | p = malloc(sizeof p[0] * strlen(s)); | |||
1626 | if (!p) { | |||
1627 | free(points); | |||
1628 | svgtiny_cleanup_state_local(&state); | |||
1629 | return svgtiny_OUT_OF_MEMORY; | |||
1630 | } | |||
1631 | ||||
1632 | /* parse s and build path */ | |||
1633 | for (i = 0; s[i]; i++) | |||
1634 | if (s[i] == ',') | |||
1635 | s[i] = ' '; | |||
1636 | i = 0; | |||
1637 | while (*s) { | |||
1638 | float x, y; | |||
1639 | int n; | |||
1640 | ||||
1641 | if (sscanf(s, "%f %f %n", &x, &y, &n) == 2) { | |||
1642 | if (i == 0) | |||
1643 | p[i++] = svgtiny_PATH_MOVE; | |||
1644 | else | |||
1645 | p[i++] = svgtiny_PATH_LINE; | |||
1646 | p[i++] = x; | |||
1647 | p[i++] = y; | |||
1648 | s += n; | |||
1649 | } else { | |||
1650 | break; | |||
1651 | } | |||
1652 | } | |||
1653 | if (polygon) | |||
1654 | p[i++] = svgtiny_PATH_CLOSE; | |||
1655 | ||||
1656 | free(points); | |||
1657 | ||||
1658 | err = svgtiny_add_path(p, i, &state); | |||
1659 | ||||
1660 | svgtiny_cleanup_state_local(&state); | |||
1661 | ||||
1662 | return err; | |||
1663 | } | |||
1664 | ||||
1665 | ||||
1666 | /** | |||
1667 | * Parse a <text> or <tspan> element node. | |||
1668 | */ | |||
1669 | ||||
1670 | svgtiny_code svgtiny_parse_text(dom_element *text, | |||
1671 | struct svgtiny_parse_state state) | |||
1672 | { | |||
1673 | float x, y, width, height; | |||
1674 | float px, py; | |||
1675 | dom_node *child; | |||
1676 | dom_exception exc; | |||
1677 | ||||
1678 | svgtiny_setup_state_local(&state); | |||
1679 | ||||
1680 | svgtiny_parse_position_attributes(text, state, | |||
1681 | &x, &y, &width, &height); | |||
1682 | svgtiny_parse_font_attributes(text, &state); | |||
1683 | svgtiny_parse_transform_attributes(text, &state); | |||
1684 | ||||
1685 | px = state.ctm.a * x + state.ctm.c * y + state.ctm.e; | |||
1686 | py = state.ctm.b * x + state.ctm.d * y + state.ctm.f; | |||
1687 | /* state.ctm.e = px - state.origin_x; */ | |||
1688 | /* state.ctm.f = py - state.origin_y; */ | |||
1689 | ||||
1690 | /*struct css_style style = state.style; | |||
1691 | style.font_size.value.length.value *= state.ctm.a;*/ | |||
1692 | ||||
1693 | exc = dom_node_get_first_child(text, &child)dom_node_get_first_child( (dom_node *) (text), (dom_node **) ( &child)); | |||
1694 | if (exc != DOM_NO_ERR) { | |||
1695 | return svgtiny_LIBDOM_ERROR; | |||
1696 | svgtiny_cleanup_state_local(&state); | |||
1697 | } | |||
1698 | while (child != NULL((void*)0)) { | |||
1699 | dom_node *next; | |||
1700 | dom_node_type nodetype; | |||
1701 | svgtiny_code code = svgtiny_OK; | |||
1702 | ||||
1703 | exc = dom_node_get_node_type(child, &nodetype)dom_node_get_node_type( (dom_node *) (child), (dom_node_type * ) (&nodetype)); | |||
1704 | if (exc != DOM_NO_ERR) { | |||
1705 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1706 | svgtiny_cleanup_state_local(&state); | |||
1707 | return svgtiny_LIBDOM_ERROR; | |||
1708 | } | |||
1709 | if (nodetype == DOM_ELEMENT_NODE) { | |||
1710 | dom_string *nodename; | |||
1711 | exc = dom_node_get_node_name(child, &nodename)dom_node_get_node_name((dom_node *) (child), (&nodename)); | |||
1712 | if (exc != DOM_NO_ERR) { | |||
1713 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1714 | svgtiny_cleanup_state_local(&state); | |||
1715 | return svgtiny_LIBDOM_ERROR; | |||
1716 | } | |||
1717 | if (dom_string_caseless_isequal(nodename, | |||
1718 | state.interned_tspan)) | |||
1719 | code = svgtiny_parse_text((dom_element *)child, | |||
1720 | state); | |||
1721 | dom_string_unref(nodename); | |||
1722 | } else if (nodetype == DOM_TEXT_NODE) { | |||
1723 | struct svgtiny_shape *shape = svgtiny_add_shape(&state); | |||
1724 | dom_string *content; | |||
1725 | if (shape == NULL((void*)0)) { | |||
1726 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1727 | svgtiny_cleanup_state_local(&state); | |||
1728 | return svgtiny_OUT_OF_MEMORY; | |||
1729 | } | |||
1730 | exc = dom_text_get_whole_text(child, &content)dom_text_get_whole_text((dom_text *) (child), (&content)); | |||
1731 | if (exc != DOM_NO_ERR) { | |||
1732 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1733 | svgtiny_cleanup_state_local(&state); | |||
1734 | return svgtiny_LIBDOM_ERROR; | |||
1735 | } | |||
1736 | if (content != NULL((void*)0)) { | |||
1737 | shape->text = strndup(dom_string_data(content), | |||
1738 | dom_string_byte_length(content)); | |||
1739 | dom_string_unref(content); | |||
1740 | } else { | |||
1741 | shape->text = strdup(""); | |||
1742 | } | |||
1743 | shape->text_x = px; | |||
1744 | shape->text_y = py; | |||
1745 | state.diagram->shape_count++; | |||
1746 | } | |||
1747 | ||||
1748 | if (code != svgtiny_OK) { | |||
1749 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1750 | svgtiny_cleanup_state_local(&state); | |||
1751 | return code; | |||
1752 | } | |||
1753 | exc = dom_node_get_next_sibling(child, &next)dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) (&next)); | |||
1754 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); | |||
1755 | if (exc != DOM_NO_ERR) { | |||
1756 | svgtiny_cleanup_state_local(&state); | |||
1757 | return svgtiny_LIBDOM_ERROR; | |||
1758 | } | |||
1759 | child = next; | |||
1760 | } | |||
1761 | ||||
1762 | svgtiny_cleanup_state_local(&state); | |||
1763 | ||||
1764 | return svgtiny_OK; | |||
1765 | } | |||
1766 | ||||
1767 | ||||
1768 | /** | |||
1769 | * Parse x, y, width, and height attributes, if present. | |||
1770 | */ | |||
1771 | ||||
1772 | void svgtiny_parse_position_attributes(dom_element *node, | |||
1773 | const struct svgtiny_parse_state state, | |||
1774 | float *x, float *y, float *width, float *height) | |||
1775 | { | |||
1776 | dom_string *attr; | |||
1777 | dom_exception exc; | |||
1778 | ||||
1779 | *x = 0; | |||
1780 | *y = 0; | |||
1781 | *width = state.viewport_width; | |||
1782 | *height = state.viewport_height; | |||
1783 | ||||
1784 | exc = dom_element_get_attribute(node, state.interned_x, &attr)dom_element_get_attribute( (dom_element *) (node), (state.interned_x ), (&attr)); | |||
1785 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1786 | *x = svgtiny_parse_length(attr, state.viewport_width, state); | |||
1787 | dom_string_unref(attr); | |||
1788 | } | |||
1789 | ||||
1790 | exc = dom_element_get_attribute(node, state.interned_y, &attr)dom_element_get_attribute( (dom_element *) (node), (state.interned_y ), (&attr)); | |||
1791 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1792 | *y = svgtiny_parse_length(attr, state.viewport_height, state); | |||
1793 | dom_string_unref(attr); | |||
1794 | } | |||
1795 | ||||
1796 | exc = dom_element_get_attribute(node, state.interned_width, &attr)dom_element_get_attribute( (dom_element *) (node), (state.interned_width ), (&attr)); | |||
1797 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1798 | *width = svgtiny_parse_length(attr, state.viewport_width, | |||
1799 | state); | |||
1800 | dom_string_unref(attr); | |||
1801 | } | |||
1802 | ||||
1803 | exc = dom_element_get_attribute(node, state.interned_height, &attr)dom_element_get_attribute( (dom_element *) (node), (state.interned_height ), (&attr)); | |||
1804 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1805 | *height = svgtiny_parse_length(attr, state.viewport_height, | |||
1806 | state); | |||
1807 | dom_string_unref(attr); | |||
1808 | } | |||
1809 | } | |||
1810 | ||||
1811 | ||||
1812 | /** | |||
1813 | * Parse a length as a number of pixels. | |||
1814 | */ | |||
1815 | ||||
1816 | static float _svgtiny_parse_length(const char *s, int viewport_size, | |||
1817 | const struct svgtiny_parse_state state) | |||
1818 | { | |||
1819 | int num_length = strspn(s, "0123456789+-."); | |||
1820 | const char *unit = s + num_length; | |||
1821 | float n = atof((const char *) s); | |||
1822 | float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/ | |||
1823 | ||||
1824 | UNUSED(state)((void) (state)); | |||
1825 | ||||
1826 | if (unit[0] == 0) { | |||
1827 | return n; | |||
1828 | } else if (unit[0] == '%') { | |||
1829 | return n / 100.0 * viewport_size; | |||
1830 | } else if (unit[0] == 'e' && unit[1] == 'm') { | |||
1831 | return n * font_size; | |||
1832 | } else if (unit[0] == 'e' && unit[1] == 'x') { | |||
1833 | return n / 2.0 * font_size; | |||
1834 | } else if (unit[0] == 'p' && unit[1] == 'x') { | |||
1835 | return n; | |||
1836 | } else if (unit[0] == 'p' && unit[1] == 't') { | |||
1837 | return n * 1.25; | |||
1838 | } else if (unit[0] == 'p' && unit[1] == 'c') { | |||
1839 | return n * 15.0; | |||
1840 | } else if (unit[0] == 'm' && unit[1] == 'm') { | |||
1841 | return n * 3.543307; | |||
1842 | } else if (unit[0] == 'c' && unit[1] == 'm') { | |||
1843 | return n * 35.43307; | |||
1844 | } else if (unit[0] == 'i' && unit[1] == 'n') { | |||
1845 | return n * 90; | |||
1846 | } | |||
1847 | ||||
1848 | return 0; | |||
1849 | } | |||
1850 | ||||
1851 | float svgtiny_parse_length(dom_string *s, int viewport_size, | |||
1852 | const struct svgtiny_parse_state state) | |||
1853 | { | |||
1854 | char *ss = strndup(dom_string_data(s), dom_string_byte_length(s)); | |||
1855 | float ret = _svgtiny_parse_length(ss, viewport_size, state); | |||
1856 | free(ss); | |||
1857 | return ret; | |||
1858 | } | |||
1859 | ||||
1860 | /** | |||
1861 | * Parse paint attributes, if present. | |||
1862 | */ | |||
1863 | ||||
1864 | void svgtiny_parse_paint_attributes(dom_element *node, | |||
1865 | struct svgtiny_parse_state *state) | |||
1866 | { | |||
1867 | dom_string *attr; | |||
1868 | dom_exception exc; | |||
1869 | ||||
1870 | exc = dom_element_get_attribute(node, state->interned_fill, &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_fill), (&attr)); | |||
1871 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1872 | svgtiny_parse_color(attr, &state->fill, &state->fill_grad, state); | |||
1873 | dom_string_unref(attr); | |||
1874 | } | |||
1875 | ||||
1876 | exc = dom_element_get_attribute(node, state->interned_stroke, &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_stroke), (&attr)); | |||
1877 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1878 | svgtiny_parse_color(attr, &state->stroke, &state->stroke_grad, state); | |||
1879 | dom_string_unref(attr); | |||
1880 | } | |||
1881 | ||||
1882 | exc = dom_element_get_attribute(node, state->interned_stroke_width, &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_stroke_width), (&attr)); | |||
1883 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1884 | state->stroke_width = svgtiny_parse_length(attr, | |||
1885 | state->viewport_width, *state); | |||
1886 | dom_string_unref(attr); | |||
1887 | } | |||
1888 | ||||
1889 | exc = dom_element_get_attribute(node, state->interned_style, &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_style), (&attr)); | |||
1890 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
1891 | char *style = strndup(dom_string_data(attr), | |||
1892 | dom_string_byte_length(attr)); | |||
1893 | const char *s; | |||
1894 | char *value; | |||
1895 | if ((s = strstr(style, "fill:"))) { | |||
1896 | s += 5; | |||
1897 | while (*s == ' ') | |||
1898 | s++; | |||
1899 | value = strndup(s, strcspn(s, "; ")); | |||
1900 | _svgtiny_parse_color(value, &state->fill, &state->fill_grad, state); | |||
1901 | free(value); | |||
1902 | } | |||
1903 | if ((s = strstr(style, "stroke:"))) { | |||
1904 | s += 7; | |||
1905 | while (*s == ' ') | |||
1906 | s++; | |||
1907 | value = strndup(s, strcspn(s, "; ")); | |||
1908 | _svgtiny_parse_color(value, &state->stroke, &state->stroke_grad, state); | |||
1909 | free(value); | |||
1910 | } | |||
1911 | if ((s = strstr(style, "stroke-width:"))) { | |||
1912 | s += 13; | |||
1913 | while (*s == ' ') | |||
1914 | s++; | |||
1915 | value = strndup(s, strcspn(s, "; ")); | |||
1916 | state->stroke_width = _svgtiny_parse_length(value, | |||
1917 | state->viewport_width, *state); | |||
1918 | free(value); | |||
1919 | } | |||
1920 | free(style); | |||
1921 | dom_string_unref(attr); | |||
1922 | } | |||
1923 | } | |||
1924 | ||||
1925 | ||||
1926 | /** | |||
1927 | * Parse a colour. | |||
1928 | */ | |||
1929 | ||||
1930 | static void _svgtiny_parse_color(const char *s, svgtiny_colour *c, | |||
1931 | struct svgtiny_parse_state_gradient *grad, | |||
1932 | struct svgtiny_parse_state *state) | |||
1933 | { | |||
1934 | unsigned int r, g, b; | |||
1935 | float rf, gf, bf; | |||
1936 | size_t len = strlen(s); | |||
1937 | char *id = 0, *rparen; | |||
1938 | ||||
1939 | if (len == 4 && s[0] == '#') { | |||
1940 | if (sscanf(s + 1, "%1x%1x%1x", &r, &g, &b) == 3) | |||
1941 | *c = svgtiny_RGB(r | r << 4, g | g << 4, b | b << 4)((r | r << 4) << 16 | (g | g << 4) << 8 | (b | b << 4)); | |||
1942 | ||||
1943 | } else if (len == 7 && s[0] == '#') { | |||
1944 | if (sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) | |||
1945 | *c = svgtiny_RGB(r, g, b)((r) << 16 | (g) << 8 | (b)); | |||
1946 | ||||
1947 | } else if (10 <= len && s[0] == 'r' && s[1] == 'g' && s[2] == 'b' && | |||
1948 | s[3] == '(' && s[len - 1] == ')') { | |||
1949 | if (sscanf(s + 4, "%u,%u,%u", &r, &g, &b) == 3) | |||
1950 | *c = svgtiny_RGB(r, g, b)((r) << 16 | (g) << 8 | (b)); | |||
1951 | else if (sscanf(s + 4, "%f%%,%f%%,%f%%", &rf, &gf, &bf) == 3) { | |||
1952 | b = bf * 255 / 100; | |||
1953 | g = gf * 255 / 100; | |||
1954 | r = rf * 255 / 100; | |||
1955 | *c = svgtiny_RGB(r, g, b)((r) << 16 | (g) << 8 | (b)); | |||
1956 | } | |||
1957 | ||||
1958 | } else if (len == 4 && strcmp(s, "none") == 0) { | |||
1959 | *c = svgtiny_TRANSPARENT0x1000000; | |||
1960 | ||||
1961 | } else if (5 < len && s[0] == 'u' && s[1] == 'r' && s[2] == 'l' && | |||
1962 | s[3] == '(') { | |||
1963 | if (grad == NULL((void*)0)) { | |||
1964 | *c = svgtiny_RGB(0, 0, 0)((0) << 16 | (0) << 8 | (0)); | |||
1965 | } else if (s[4] == '#') { | |||
1966 | id = strdup(s + 5); | |||
1967 | if (!id) | |||
1968 | return; | |||
1969 | rparen = strchr(id, ')'); | |||
1970 | if (rparen) | |||
1971 | *rparen = 0; | |||
1972 | svgtiny_find_gradient(id, grad, state); | |||
1973 | free(id); | |||
1974 | if (grad->linear_gradient_stop_count == 0) | |||
1975 | *c = svgtiny_TRANSPARENT0x1000000; | |||
1976 | else if (grad->linear_gradient_stop_count == 1) | |||
1977 | *c = grad->gradient_stop[0].color; | |||
1978 | else | |||
1979 | *c = svgtiny_LINEAR_GRADIENT0x2000000; | |||
1980 | } | |||
1981 | ||||
1982 | } else { | |||
1983 | const struct svgtiny_named_color *named_color; | |||
1984 | named_color = svgtiny_color_lookup(s, strlen(s)); | |||
1985 | if (named_color) | |||
1986 | *c = named_color->color; | |||
1987 | } | |||
1988 | } | |||
1989 | ||||
1990 | void svgtiny_parse_color(dom_string *s, svgtiny_colour *c, | |||
1991 | struct svgtiny_parse_state_gradient *grad, | |||
1992 | struct svgtiny_parse_state *state) | |||
1993 | { | |||
1994 | dom_string_ref(s); | |||
1995 | _svgtiny_parse_color(dom_string_data(s), c, grad, state); | |||
1996 | dom_string_unref(s); | |||
1997 | } | |||
1998 | ||||
1999 | /** | |||
2000 | * Parse font attributes, if present. | |||
2001 | */ | |||
2002 | ||||
2003 | void svgtiny_parse_font_attributes(dom_element *node, | |||
2004 | struct svgtiny_parse_state *state) | |||
2005 | { | |||
2006 | /* TODO: Implement this, it never used to be */ | |||
2007 | UNUSED(node)((void) (node)); | |||
2008 | UNUSED(state)((void) (state)); | |||
2009 | #ifdef WRITTEN_THIS_PROPERLY | |||
2010 | const xmlAttr *attr; | |||
2011 | ||||
2012 | UNUSED(state)((void) (state)); | |||
2013 | ||||
2014 | for (attr = node->properties; attr; attr = attr->next) { | |||
2015 | if (strcmp((const char *) attr->name, "font-size") == 0) { | |||
2016 | /*if (css_parse_length( | |||
2017 | (const char *) attr->children->content, | |||
2018 | &state->style.font_size.value.length, | |||
2019 | true, true)) { | |||
2020 | state->style.font_size.size = | |||
2021 | CSS_FONT_SIZE_LENGTH; | |||
2022 | }*/ | |||
2023 | } | |||
2024 | } | |||
2025 | #endif | |||
2026 | } | |||
2027 | ||||
2028 | ||||
2029 | /** | |||
2030 | * Parse transform attributes, if present. | |||
2031 | * | |||
2032 | * http://www.w3.org/TR/SVG11/coords#TransformAttribute | |||
2033 | */ | |||
2034 | ||||
2035 | void svgtiny_parse_transform_attributes(dom_element *node, | |||
2036 | struct svgtiny_parse_state *state) | |||
2037 | { | |||
2038 | char *transform; | |||
2039 | dom_string *attr; | |||
2040 | dom_exception exc; | |||
2041 | ||||
2042 | exc = dom_element_get_attribute(node, state->interned_transform,dom_element_get_attribute( (dom_element *) (node), (state-> interned_transform), (&attr)) | |||
2043 | &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_transform), (&attr)); | |||
2044 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { | |||
2045 | transform = strndup(dom_string_data(attr), | |||
2046 | dom_string_byte_length(attr)); | |||
2047 | svgtiny_parse_transform(transform, &state->ctm.a, &state->ctm.b, | |||
2048 | &state->ctm.c, &state->ctm.d, | |||
2049 | &state->ctm.e, &state->ctm.f); | |||
2050 | free(transform); | |||
2051 | dom_string_unref(attr); | |||
2052 | } | |||
2053 | } | |||
2054 | ||||
2055 | ||||
2056 | /** | |||
2057 | * Parse a transform string. | |||
2058 | */ | |||
2059 | ||||
2060 | void svgtiny_parse_transform(char *s, float *ma, float *mb, | |||
2061 | float *mc, float *md, float *me, float *mf) | |||
2062 | { | |||
2063 | float a, b, c, d, e, f; | |||
2064 | float za, zb, zc, zd, ze, zf; | |||
2065 | float angle, x, y; | |||
2066 | int n; | |||
2067 | unsigned int i; | |||
2068 | ||||
2069 | for (i = 0; s[i]; i++) | |||
2070 | if (s[i] == ',') | |||
2071 | s[i] = ' '; | |||
2072 | ||||
2073 | while (*s) { | |||
2074 | a = d = 1; | |||
2075 | b = c = 0; | |||
2076 | e = f = 0; | |||
2077 | n = 0; | |||
2078 | if ((sscanf(s, " matrix (%f %f %f %f %f %f ) %n", | |||
2079 | &a, &b, &c, &d, &e, &f, &n) == 6) && (n > 0)) | |||
2080 | ; | |||
2081 | else if ((sscanf(s, " translate (%f %f ) %n", | |||
2082 | &e, &f, &n) == 2) && (n > 0)) | |||
2083 | ; | |||
2084 | else if ((sscanf(s, " translate (%f ) %n", | |||
2085 | &e, &n) == 1) && (n > 0)) | |||
2086 | ; | |||
2087 | else if ((sscanf(s, " scale (%f %f ) %n", | |||
2088 | &a, &d, &n) == 2) && (n > 0)) | |||
2089 | ; | |||
2090 | else if ((sscanf(s, " scale (%f ) %n", | |||
2091 | &a, &n) == 1) && (n > 0)) | |||
2092 | d = a; | |||
2093 | else if ((sscanf(s, " rotate (%f %f %f ) %n", | |||
2094 | &angle, &x, &y, &n) == 3) && (n > 0)) { | |||
2095 | angle = angle / 180 * M_PI3.14159265358979323846; | |||
2096 | a = cos(angle); | |||
2097 | b = sin(angle); | |||
2098 | c = -sin(angle); | |||
2099 | d = cos(angle); | |||
2100 | e = -x * cos(angle) + y * sin(angle) + x; | |||
2101 | f = -x * sin(angle) - y * cos(angle) + y; | |||
2102 | } else if ((sscanf(s, " rotate (%f ) %n", | |||
2103 | &angle, &n) == 1) && (n > 0)) { | |||
2104 | angle = angle / 180 * M_PI3.14159265358979323846; | |||
2105 | a = cos(angle); | |||
2106 | b = sin(angle); | |||
2107 | c = -sin(angle); | |||
2108 | d = cos(angle); | |||
2109 | } else if ((sscanf(s, " skewX (%f ) %n", | |||
2110 | &angle, &n) == 1) && (n > 0)) { | |||
2111 | angle = angle / 180 * M_PI3.14159265358979323846; | |||
2112 | c = tan(angle); | |||
2113 | } else if ((sscanf(s, " skewY (%f ) %n", | |||
2114 | &angle, &n) == 1) && (n > 0)) { | |||
2115 | angle = angle / 180 * M_PI3.14159265358979323846; | |||
2116 | b = tan(angle); | |||
2117 | } else | |||
2118 | break; | |||
2119 | za = *ma * a + *mc * b; | |||
2120 | zb = *mb * a + *md * b; | |||
2121 | zc = *ma * c + *mc * d; | |||
2122 | zd = *mb * c + *md * d; | |||
2123 | ze = *ma * e + *mc * f + *me; | |||
2124 | zf = *mb * e + *md * f + *mf; | |||
2125 | *ma = za; | |||
2126 | *mb = zb; | |||
2127 | *mc = zc; | |||
2128 | *md = zd; | |||
2129 | *me = ze; | |||
2130 | *mf = zf; | |||
2131 | s += n; | |||
2132 | } | |||
2133 | } | |||
2134 | ||||
2135 | ||||
2136 | /** | |||
2137 | * Add a path to the svgtiny_diagram. | |||
2138 | */ | |||
2139 | ||||
2140 | svgtiny_code svgtiny_add_path(float *p, unsigned int n, | |||
2141 | struct svgtiny_parse_state *state) | |||
2142 | { | |||
2143 | struct svgtiny_shape *shape; | |||
2144 | ||||
2145 | if (state->fill == svgtiny_LINEAR_GRADIENT0x2000000) | |||
2146 | return svgtiny_add_path_linear_gradient(p, n, state); | |||
2147 | ||||
2148 | svgtiny_transform_path(p, n, state); | |||
2149 | ||||
2150 | shape = svgtiny_add_shape(state); | |||
2151 | if (!shape) { | |||
2152 | free(p); | |||
2153 | return svgtiny_OUT_OF_MEMORY; | |||
2154 | } | |||
2155 | shape->path = p; | |||
2156 | shape->path_length = n; | |||
2157 | state->diagram->shape_count++; | |||
2158 | ||||
2159 | return svgtiny_OK; | |||
2160 | } | |||
2161 | ||||
2162 | ||||
2163 | /** | |||
2164 | * Add a svgtiny_shape to the svgtiny_diagram. | |||
2165 | */ | |||
2166 | ||||
2167 | struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state) | |||
2168 | { | |||
2169 | struct svgtiny_shape *shape = realloc(state->diagram->shape, | |||
2170 | (state->diagram->shape_count + 1) * | |||
2171 | sizeof (state->diagram->shape[0])); | |||
2172 | if (!shape) | |||
2173 | return 0; | |||
2174 | state->diagram->shape = shape; | |||
2175 | ||||
2176 | shape += state->diagram->shape_count; | |||
2177 | shape->path = 0; | |||
2178 | shape->path_length = 0; | |||
2179 | shape->text = 0; | |||
2180 | shape->fill = state->fill; | |||
2181 | shape->stroke = state->stroke; | |||
2182 | shape->stroke_width = lroundf((float) state->stroke_width * | |||
2183 | (state->ctm.a + state->ctm.d) / 2.0); | |||
2184 | if (0 < state->stroke_width && shape->stroke_width == 0) | |||
2185 | shape->stroke_width = 1; | |||
2186 | ||||
2187 | return shape; | |||
2188 | } | |||
2189 | ||||
2190 | ||||
2191 | /** | |||
2192 | * Apply the current transformation matrix to a path. | |||
2193 | */ | |||
2194 | ||||
2195 | void svgtiny_transform_path(float *p, unsigned int n, | |||
2196 | struct svgtiny_parse_state *state) | |||
2197 | { | |||
2198 | unsigned int j; | |||
2199 | ||||
2200 | for (j = 0; j != n; ) { | |||
2201 | unsigned int points = 0; | |||
2202 | unsigned int k; | |||
2203 | switch ((int) p[j]) { | |||
2204 | case svgtiny_PATH_MOVE: | |||
2205 | case svgtiny_PATH_LINE: | |||
2206 | points = 1; | |||
2207 | break; | |||
2208 | case svgtiny_PATH_CLOSE: | |||
2209 | points = 0; | |||
2210 | break; | |||
2211 | case svgtiny_PATH_BEZIER: | |||
2212 | points = 3; | |||
2213 | break; | |||
2214 | default: | |||
2215 | assert(0)((0) ? (void) (0) : __assert_fail ("0", "src/svgtiny.c", 2215 , __extension__ __PRETTY_FUNCTION__)); | |||
2216 | } | |||
2217 | j++; | |||
2218 | for (k = 0; k != points; k++) { | |||
2219 | float x0 = p[j], y0 = p[j + 1]; | |||
2220 | float x = state->ctm.a * x0 + state->ctm.c * y0 + | |||
2221 | state->ctm.e; | |||
2222 | float y = state->ctm.b * x0 + state->ctm.d * y0 + | |||
2223 | state->ctm.f; | |||
2224 | p[j] = x; | |||
2225 | p[j + 1] = y; | |||
2226 | j += 2; | |||
2227 | } | |||
2228 | } | |||
2229 | } | |||
2230 | ||||
2231 | ||||
2232 | /** | |||
2233 | * Free all memory used by a diagram. | |||
2234 | */ | |||
2235 | ||||
2236 | void svgtiny_free(struct svgtiny_diagram *svg) | |||
2237 | { | |||
2238 | unsigned int i; | |||
2239 | assert(svg)((svg) ? (void) (0) : __assert_fail ("svg", "src/svgtiny.c", 2239 , __extension__ __PRETTY_FUNCTION__)); | |||
2240 | ||||
2241 | for (i = 0; i != svg->shape_count; i++) { | |||
2242 | free(svg->shape[i].path); | |||
2243 | free(svg->shape[i].text); | |||
2244 | } | |||
2245 | ||||
2246 | free(svg->shape); | |||
2247 | ||||
2248 | free(svg); | |||
2249 | } | |||
2250 | ||||
2251 | #ifndef HAVE_STRNDUP | |||
2252 | char *svgtiny_strndup(const char *s, size_t n) | |||
2253 | { | |||
2254 | size_t len; | |||
2255 | char *s2; | |||
2256 | ||||
2257 | for (len = 0; len != n && s[len]; len++) | |||
2258 | continue; | |||
2259 | ||||
2260 | s2 = malloc(len + 1); | |||
2261 | if (s2 == NULL((void*)0)) | |||
2262 | return NULL((void*)0); | |||
2263 | ||||
2264 | memcpy(s2, s, len); | |||
2265 | s2[len] = '\0'; | |||
2266 | ||||
2267 | return s2; | |||
2268 | } | |||
2269 | #endif |