File: | svgtiny.c |
Warning: | line 2219, column 10 Assigned value is garbage or undefined |
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 && extent < 0) { | ||||
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
| ||||
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 |
1 | /* This file is part of Libsvgtiny |
2 | * Licensed under the MIT License, |
3 | * http://opensource.org/licenses/mit-license.php |
4 | * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org> |
5 | */ |
6 | |
7 | #ifndef SVGTINY_STRING_ACTION2 |
8 | #error No action defined |
9 | #endif |
10 | |
11 | #define SVGTINY_STRING_ACTION(s) SVGTINY_STRING_ACTION2(s,s) |
12 | |
13 | SVGTINY_STRING_ACTION(svg) |
14 | SVGTINY_STRING_ACTION(viewBox) |
15 | SVGTINY_STRING_ACTION(a) |
16 | SVGTINY_STRING_ACTION(d) |
17 | SVGTINY_STRING_ACTION(g) |
18 | SVGTINY_STRING_ACTION(r) |
19 | SVGTINY_STRING_ACTION(x) |
20 | SVGTINY_STRING_ACTION(y) |
21 | SVGTINY_STRING_ACTION(cx) |
22 | SVGTINY_STRING_ACTION(cy) |
23 | SVGTINY_STRING_ACTION(rx) |
24 | SVGTINY_STRING_ACTION(ry) |
25 | SVGTINY_STRING_ACTION(x1) |
26 | SVGTINY_STRING_ACTION(y1) |
27 | SVGTINY_STRING_ACTION(x2) |
28 | SVGTINY_STRING_ACTION(y2) |
29 | SVGTINY_STRING_ACTION(path) |
30 | SVGTINY_STRING_ACTION(points) |
31 | SVGTINY_STRING_ACTION(width) |
32 | SVGTINY_STRING_ACTION(height) |
33 | SVGTINY_STRING_ACTION(rect) |
34 | SVGTINY_STRING_ACTION(circle) |
35 | SVGTINY_STRING_ACTION(ellipse) |
36 | SVGTINY_STRING_ACTION(line) |
37 | SVGTINY_STRING_ACTION(polyline) |
38 | SVGTINY_STRING_ACTION(polygon) |
39 | SVGTINY_STRING_ACTION(text) |
40 | SVGTINY_STRING_ACTION(tspan) |
41 | SVGTINY_STRING_ACTION(fill) |
42 | SVGTINY_STRING_ACTION(stroke) |
43 | SVGTINY_STRING_ACTION(style) |
44 | SVGTINY_STRING_ACTION(transform) |
45 | SVGTINY_STRING_ACTION(linearGradient) |
46 | SVGTINY_STRING_ACTION(href) |
47 | SVGTINY_STRING_ACTION(stop) |
48 | SVGTINY_STRING_ACTION(offset) |
49 | SVGTINY_STRING_ACTION(gradientUnits) |
50 | SVGTINY_STRING_ACTION(gradientTransform) |
51 | SVGTINY_STRING_ACTION(userSpaceOnUse) |
52 | SVGTINY_STRING_ACTION2(stroke_width,stroke-width) |
53 | SVGTINY_STRING_ACTION2(stop_color,stop-color) |
54 | SVGTINY_STRING_ACTION2(zero_percent,0%) |
55 | SVGTINY_STRING_ACTION2(hundred_percent,100%) |
56 | |
57 | #undef SVGTINY_STRING_ACTION |