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