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