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>
18 #include <dom/bindings/xml/xmlparser.h>
20 #include <libcss/libcss.h>
23 #include "svgtiny_internal.h"
25 /* Source file generated by `gperf`. */
26 #include "autogenerated_colors.c"
28 #define TAU 6.28318530717958647692
31 #define M_PI 3.14159265358979323846
35 #define M_PI_2 1.57079632679489661923
38 #define KAPPA 0.5522847498
40 #define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
41 #define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI)
43 static svgtiny_code
svgtiny_parse_style_element(dom_element
*style
,
44 struct svgtiny_parse_state state
);
45 static css_stylesheet
*svgtiny_parse_style_inline(const uint8_t *data
,
47 static svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
48 struct svgtiny_parse_state state
);
49 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
50 struct svgtiny_parse_state state
);
51 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
52 struct svgtiny_parse_state state
);
53 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
54 struct svgtiny_parse_state state
);
55 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
56 struct svgtiny_parse_state state
);
57 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
58 struct svgtiny_parse_state state
);
59 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
60 struct svgtiny_parse_state state
);
61 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
62 struct svgtiny_parse_state state
, bool polygon
);
63 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
64 struct svgtiny_parse_state state
);
65 static void svgtiny_parse_position_attributes(dom_element
*node
,
66 const struct svgtiny_parse_state state
,
67 float *x
, float *y
, float *width
, float *height
);
68 static void svgtiny_parse_paint_attributes(dom_element
*node
,
69 struct svgtiny_parse_state
*state
);
70 static void svgtiny_parse_font_attributes(dom_element
*node
,
71 struct svgtiny_parse_state
*state
);
72 static void svgtiny_parse_transform_attributes(dom_element
*node
,
73 struct svgtiny_parse_state
*state
);
74 static css_select_results
*svgtiny_parse_styles(dom_element
*node
,
75 struct svgtiny_parse_state
*state
);
76 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
77 struct svgtiny_parse_state
*state
);
78 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
79 struct svgtiny_parse_state_gradient
*grad
,
80 struct svgtiny_parse_state
*state
);
83 * rotate midpoint vector
86 rotate_midpoint_vector(float ax
, float ay
,
89 double *x_out
, double *y_out
)
91 double dx2
; /* midpoint x coordinate */
92 double dy2
; /* midpoint y coordinate */
93 double cosangle
; /* cosine of rotation angle */
94 double sinangle
; /* sine of rotation angle */
96 /* compute the sin and cos of the angle */
97 cosangle
= cos(radangle
);
98 sinangle
= sin(radangle
);
100 /* compute the midpoint between start and end points */
101 dx2
= (ax
- bx
) / 2.0;
102 dy2
= (ay
- by
) / 2.0;
104 /* rotate vector to remove angle */
105 *x_out
= ((cosangle
* dx2
) + (sinangle
* dy2
));
106 *y_out
= ((-sinangle
* dx2
) + (cosangle
* dy2
));
111 * ensure the arc radii are large enough and scale as appropriate
113 * the radii need to be large enough if they are not they must be
114 * adjusted. This allows for elimination of differences between
115 * implementations especialy with rounding.
118 ensure_radii_scale(double x1_sq
, double y1_sq
,
119 float *rx
, float *ry
,
120 double *rx_sq
, double *ry_sq
)
125 /* set radii square values */
126 (*rx_sq
) = (*rx
) * (*rx
);
127 (*ry_sq
) = (*ry
) * (*ry
);
129 radiisum
= (x1_sq
/ (*rx_sq
)) + (y1_sq
/ (*ry_sq
));
130 if (radiisum
> 0.99999) {
131 /* need to scale radii */
132 radiiscale
= sqrt(radiisum
) * 1.00001;
133 *rx
= (float)(radiiscale
* (*rx
));
134 *ry
= (float)(radiiscale
* (*ry
));
135 /* update squares too */
136 (*rx_sq
) = (*rx
) * (*rx
);
137 (*ry_sq
) = (*ry
) * (*ry
);
143 * compute the transformed centre point
146 compute_transformed_centre_point(double sign
, float rx
, float ry
,
147 double rx_sq
, double ry_sq
,
148 double x1
, double y1
,
149 double x1_sq
, double y1_sq
,
150 double *cx1
, double *cy1
)
154 sq
= ((rx_sq
* ry_sq
) - (rx_sq
* y1_sq
) - (ry_sq
* x1_sq
)) /
155 ((rx_sq
* y1_sq
) + (ry_sq
* x1_sq
));
156 sq
= (sq
< 0) ? 0 : sq
;
158 coef
= (sign
* sqrt(sq
));
160 *cx1
= coef
* ((rx
* y1
) / ry
);
161 *cy1
= coef
* -((ry
* x1
) / rx
);
166 * compute untransformed centre point
168 * \param ax The first point x coordinate
169 * \param ay The first point y coordinate
170 * \param bx The second point x coordinate
171 * \param ay The second point y coordinate
174 compute_centre_point(float ax
, float ay
,
176 double cx1
, double cy1
,
178 double *x_out
, double *y_out
)
182 double cosangle
; /* cosine of rotation angle */
183 double sinangle
; /* sine of rotation angle */
185 /* compute the sin and cos of the angle */
186 cosangle
= cos(radangle
);
187 sinangle
= sin(radangle
);
189 sx2
= (ax
+ bx
) / 2.0;
190 sy2
= (ay
+ by
) / 2.0;
192 *x_out
= sx2
+ (cosangle
* cx1
- sinangle
* cy1
);
193 *y_out
= sy2
+ (sinangle
* cx1
+ cosangle
* cy1
);
198 * compute the angle start and extent
201 compute_angle_start_extent(float rx
, float ry
,
202 double x1
, double y1
,
203 double cx1
, double cy1
,
204 double *start
, double *extent
)
215 * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v))
217 * '.' is the dot product.
218 * +/- is calculated from the sign of the cross product (u x v)
221 ux
= (x1
- cx1
) / rx
;
222 uy
= (y1
- cy1
) / ry
;
223 vx
= (-x1
- cx1
) / rx
;
224 vy
= (-y1
- cy1
) / ry
;
226 /* compute the start angle */
227 /* The angle between (ux, uy) and the 0 angle */
229 /* len(u) * len(1,0) == len(u) */
230 n
= sqrt((ux
* ux
) + (uy
* uy
));
231 /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */
233 /* u x v == (1 * uy - ux * 0) == uy */
234 sign
= (uy
< 0) ? -1.0 : 1.0;
235 /* (p >= n) so safe */
236 *start
= sign
* acos(p
/ n
);
238 /* compute the extent angle */
239 n
= sqrt(((ux
* ux
) + (uy
* uy
)) * ((vx
* vx
) + (vy
* vy
)));
240 p
= (ux
* vx
) + (uy
* vy
);
241 sign
= ((ux
* vy
) - (uy
* vx
) < 0) ? -1.0f
: 1.0f
;
243 /* arc cos must operate between -1 and 1 */
246 *extent
= sign
* M_PI
;
247 } else if (actmp
> 1.0) {
250 *extent
= sign
* acos(actmp
);
256 * converts a circle centered unit circle arc to a series of bezier curves
258 * Each bezier is stored as six values of three pairs of coordinates
260 * The beziers are stored without their start point as that is assumed
261 * to be the preceding elements end point.
263 * \param start The start angle of the arc (in radians)
264 * \param extent The size of the arc (in radians)
265 * \param bzpt The array to store the bezier values in
266 * \return The number of bezier segments output (max 4)
269 circle_arc_to_bezier(double start
, double extent
, double *bzpt
)
279 bzsegments
= (int) ceil(fabs(extent
) / M_PI_2
);
280 increment
= extent
/ bzsegments
;
281 controllen
= 4.0 / 3.0 * sin(increment
/ 2.0) / (1.0 + cos(increment
/ 2.0));
283 for (segment
= 0; segment
< bzsegments
; segment
++) {
284 /* first control point */
285 angle
= start
+ (segment
* increment
);
288 bzpt
[pos
++] = dx
- controllen
* dy
;
289 bzpt
[pos
++] = dy
+ controllen
* dx
;
290 /* second control point */
294 bzpt
[pos
++] = dx
+ controllen
* dy
;
295 bzpt
[pos
++] = dy
- controllen
* dx
;
306 * transform coordinate list
308 * perform a scale, rotate and translate on list of coordinates
314 * homogeneous transforms
322 * | cos(an) -sin(an) 0 |
323 * R = | sin(an) cos(an) 0 |
326 * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}}
333 * note order is significat here and the combined matrix is
336 * | cos(an) -sin(an) cx |
337 * T.R = | sin(an) cos(an) cy |
340 * | rx * cos(an) ry * -sin(an) cx |
341 * T.R.S = | rx * sin(an) ry * cos(an) cy |
344 * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}}
356 * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a))
357 * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a))
360 * \param rx X scaling to apply
361 * \param ry Y scaling to apply
362 * \param radangle rotation to apply (in radians)
363 * \param cx X translation to apply
364 * \param cy Y translation to apply
365 * \param points The size of the bzpoints array
366 * \param bzpoints an array of x,y values to apply the transform to
369 scale_rotate_translate_points(double rx
, double ry
,
371 double cx
, double cy
,
376 double cosangle
; /* cosine of rotation angle */
377 double sinangle
; /* sine of rotation angle */
378 double rxcosangle
, rxsinangle
, rycosangle
, rynsinangle
;
381 /* compute the sin and cos of the angle */
382 cosangle
= cos(radangle
);
383 sinangle
= sin(radangle
);
385 rxcosangle
= rx
* cosangle
;
386 rxsinangle
= rx
* sinangle
;
387 rycosangle
= ry
* cosangle
;
388 rynsinangle
= ry
* -1 * sinangle
;
390 for (pnt
= 0; pnt
< pntsize
; pnt
+=2) {
391 x2
= cx
+ (points
[pnt
] * rxcosangle
) + (points
[pnt
+ 1] * rynsinangle
);
392 y2
= cy
+ (points
[pnt
+ 1] * rycosangle
) + (points
[pnt
] * rxsinangle
);
394 points
[pnt
+ 1] = y2
;
400 * convert an svg path arc to a bezier curve
402 * This function perfoms a transform on the nine arc parameters
403 * (coordinate pairs for start and end together with the radii of the
404 * elipse, the rotation angle and which of the four arcs to draw)
405 * which generates the parameters (coordinate pairs for start,
406 * end and their control points) for a set of up to four bezier curves.
408 * Obviously the start and end coordinates are not altered between
409 * representations so the aim is to calculate the coordinate pairs for
410 * the bezier control points.
412 * \param bzpoints the array to fill with bezier curves
413 * \return the number of bezier segments generated or -1 for a line
416 svgarc_to_bezier(float start_x
,
427 double radangle
; /* normalised elipsis rotation angle in radians */
428 double rx_sq
; /* x radius squared */
429 double ry_sq
; /* y radius squared */
430 double x1
, y1
; /* rotated midpoint vector */
431 double x1_sq
, y1_sq
; /* x1 vector squared */
432 double cx1
,cy1
; /* transformed circle center */
433 double cx
,cy
; /* circle center */
434 double start
, extent
;
437 if ((start_x
== end_x
) && (start_y
== end_y
)) {
439 * if the start and end coordinates are the same the
440 * svg spec says this is equivalent to having no segment
446 if ((rx
== 0) || (ry
== 0)) {
448 * if either radii is zero the specified behaviour is a line
453 /* obtain the absolute values of the radii */
457 /* convert normalised angle to radians */
458 radangle
= degToRad(fmod(angle
, 360.0));
461 /* x1,x2 is the midpoint vector rotated to remove the arc angle */
462 rotate_midpoint_vector(start_x
, start_y
, end_x
, end_y
, radangle
, &x1
, &y1
);
465 /* get squared x1 values */
469 /* ensure radii are correctly scaled */
470 ensure_radii_scale(x1_sq
, y1_sq
, &rx
, &ry
, &rx_sq
, &ry_sq
);
472 /* compute the transformed centre point */
473 compute_transformed_centre_point(largearc
== sweep
?-1:1,
481 /* get the untransformed centre point */
482 compute_centre_point(start_x
, start_y
,
489 /* compute anglestart and extent */
490 compute_angle_start_extent(rx
,ry
,
495 /* extent of 0 is a straight line */
500 /* take account of sweep */
501 if (!sweep
&& extent
> 0) {
503 } else if (sweep
&& extent
< 0) {
507 /* normalise start and extent */
508 extent
= fmod(extent
, TAU
);
509 start
= fmod(start
, TAU
);
511 /* convert the arc to unit circle bezier curves */
512 bzsegments
= circle_arc_to_bezier(start
, extent
, bzpoints
);
514 /* transform the bezier curves */
515 scale_rotate_translate_points(rx
, ry
,
526 * Call this to ref the strings in a gradient state.
528 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
530 if (grad
->gradient_x1
!= NULL
) {
531 dom_string_ref(grad
->gradient_x1
);
533 if (grad
->gradient_y1
!= NULL
) {
534 dom_string_ref(grad
->gradient_y1
);
536 if (grad
->gradient_x2
!= NULL
) {
537 dom_string_ref(grad
->gradient_x2
);
539 if (grad
->gradient_y2
!= NULL
) {
540 dom_string_ref(grad
->gradient_y2
);
545 * Call this to clean up the strings in a gradient state.
547 static void svgtiny_grad_string_cleanup(
548 struct svgtiny_parse_state_gradient
*grad
)
550 if (grad
->gradient_x1
!= NULL
) {
551 dom_string_unref(grad
->gradient_x1
);
552 grad
->gradient_x1
= NULL
;
554 if (grad
->gradient_y1
!= NULL
) {
555 dom_string_unref(grad
->gradient_y1
);
556 grad
->gradient_y1
= NULL
;
558 if (grad
->gradient_x2
!= NULL
) {
559 dom_string_unref(grad
->gradient_x2
);
560 grad
->gradient_x2
= NULL
;
562 if (grad
->gradient_y2
!= NULL
) {
563 dom_string_unref(grad
->gradient_y2
);
564 grad
->gradient_y2
= NULL
;
569 * Set the local externally-stored parts of a parse state.
570 * Call this in functions that made a new state on the stack.
571 * Doesn't make own copy of global state, such as the interned string list.
573 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
575 svgtiny_grad_string_ref(&(state
->fill_grad
));
576 svgtiny_grad_string_ref(&(state
->stroke_grad
));
580 * Cleanup the local externally-stored parts of a parse state.
581 * Call this in functions that made a new state on the stack.
582 * Doesn't cleanup global state, such as the interned string list.
584 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
586 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
587 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
592 * Create a new svgtiny_diagram structure.
595 struct svgtiny_diagram
*svgtiny_create(void)
597 struct svgtiny_diagram
*diagram
;
599 diagram
= calloc(sizeof(*diagram
), 1);
608 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
616 * Parse a block of memory into a svgtiny_diagram.
619 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
620 const char *buffer
, size_t size
, const char *url
,
621 int viewport_width
, int viewport_height
)
624 dom_document
*document
;
626 dom_xml_parser
*parser
;
629 dom_string
*svg_name
;
630 lwc_string
*svg_name_lwc
;
631 struct svgtiny_parse_state state
= {
632 /* Initialize the unit context here because it has a
633 * const member and doing it any other way subverts
634 * the type system. The magic numbers below were taken
635 * from the libcss example program without much
636 * thought, because at the moment we don't support any
637 * properties with units. */
639 .font_size_default
= FLTTOFIX(16.0),
640 .font_size_minimum
= FLTTOFIX(6.0),
641 .device_dpi
= FLTTOFIX(96.0),
647 float x
, y
, width
, height
;
656 parser
= dom_xml_parser_create(NULL
, NULL
,
657 ignore_msg
, NULL
, &document
);
660 return svgtiny_LIBDOM_ERROR
;
662 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
663 if (err
!= DOM_XML_OK
) {
664 dom_node_unref(document
);
665 dom_xml_parser_destroy(parser
);
666 return svgtiny_LIBDOM_ERROR
;
669 err
= dom_xml_parser_completed(parser
);
670 if (err
!= DOM_XML_OK
) {
671 dom_node_unref(document
);
672 dom_xml_parser_destroy(parser
);
673 return svgtiny_LIBDOM_ERROR
;
676 /* We're done parsing, drop the parser.
677 * We now own the document entirely.
679 dom_xml_parser_destroy(parser
);
681 /* find root <svg> element */
682 exc
= dom_document_get_document_element(document
, &svg
);
683 if (exc
!= DOM_NO_ERR
) {
684 dom_node_unref(document
);
685 return svgtiny_LIBDOM_ERROR
;
688 /* no root svg element */
689 dom_node_unref(document
);
690 return svgtiny_SVG_ERROR
;
693 exc
= dom_node_get_node_name(svg
, &svg_name
);
694 if (exc
!= DOM_NO_ERR
) {
696 dom_node_unref(document
);
697 return svgtiny_LIBDOM_ERROR
;
699 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
700 &svg_name_lwc
) != lwc_error_ok
) {
701 dom_string_unref(svg_name
);
703 dom_node_unref(document
);
704 return svgtiny_LIBDOM_ERROR
;
706 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
707 lwc_string_unref(svg_name_lwc
);
708 dom_string_unref(svg_name
);
710 dom_node_unref(document
);
711 return svgtiny_NOT_SVG
;
714 lwc_string_unref(svg_name_lwc
);
715 dom_string_unref(svg_name
);
717 /* initialize the state struct with zeros */
718 memset(&state
, 0, sizeof(state
));
720 /* get graphic dimensions */
721 state
.diagram
= diagram
;
722 state
.document
= document
;
723 state
.viewport_width
= viewport_width
;
724 state
.viewport_height
= viewport_height
;
726 /* Initialize CSS context */
727 css_code
= css_select_ctx_create(&state
.select_ctx
);
728 if (css_code
!= CSS_OK
) {
730 dom_node_unref(document
);
731 return svgtiny_LIBCSS_ERROR
;
734 /* ...and the unit context, whose other fields were
735 * initialized along with the parser state itself */
736 state
.unit_ctx
.viewport_width
= FLTTOFIX(viewport_width
);
737 state
.unit_ctx
.viewport_height
= FLTTOFIX(viewport_height
);
739 #define SVGTINY_STRING_ACTION2(s,n) \
740 if (dom_string_create_interned((const uint8_t *) #n, \
741 strlen(#n), &state.interned_##s) \
743 code = svgtiny_LIBDOM_ERROR; \
746 #include "svgtiny_strings.h"
747 #undef SVGTINY_STRING_ACTION2
749 /* Intern SVG's xmlns separately because it's an lwc_string
750 * and not a dom_string. We initialize its pointer to NULL
751 * because the "cleanup:" test to see if it needs to be free'd
752 * looks for NULL. Returning a LIBDOM_ERROR on failure is not
753 * perfect but it's the closest of the available options. */
754 state
.interned_svg_xmlns
= NULL
;
755 if (lwc_intern_string("http://www.w3.org/2000/svg",
757 &state
.interned_svg_xmlns
) != lwc_error_ok
) {
758 code
= svgtiny_LIBDOM_ERROR
;
762 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
763 diagram
->width
= width
;
764 diagram
->height
= height
;
766 /* set up parsing state */
767 state
.viewport_width
= width
;
768 state
.viewport_height
= height
;
769 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
772 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
773 state
.ctm
.e
= 0; /*x;*/
774 state
.ctm
.f
= 0; /*y;*/
775 state
.fill
= 0x000000;
776 state
.stroke
= svgtiny_TRANSPARENT
;
777 state
.stroke_width
= 1;
780 code
= svgtiny_preparse_styles(svg
, state
);
781 if (code
== svgtiny_OK
) {
782 code
= svgtiny_parse_svg(svg
, state
);
786 dom_node_unref(document
);
787 css_code
= css_select_ctx_destroy(state
.select_ctx
);
788 if (css_code
!= CSS_OK
) {
789 code
= svgtiny_LIBCSS_ERROR
;
793 svgtiny_cleanup_state_local(&state
);
794 #define SVGTINY_STRING_ACTION2(s,n) \
795 if (state.interned_##s != NULL) \
796 dom_string_unref(state.interned_##s);
797 #include "svgtiny_strings.h"
798 #undef SVGTINY_STRING_ACTION2
800 if (state
.interned_svg_xmlns
!= NULL
) {
801 lwc_string_unref(state
.interned_svg_xmlns
);
809 * Parse a single <style> element, appending the result to the CSS
810 * select context within the given parser state.
812 svgtiny_code
svgtiny_parse_style_element(dom_element
*style
,
813 struct svgtiny_parse_state state
)
815 css_stylesheet
*sheet
;
819 code
= svgtiny_create_stylesheet(&sheet
, false);
820 if (code
!= CSS_OK
) {
821 return svgtiny_LIBCSS_ERROR
;
824 /* Parse the style element's "media" attribute if it has
825 one. We don't do anything with it right now. */
826 dom_string
*media_attr
;
827 exc
= dom_element_get_attribute(style
, state
.interned_media
,
829 if (exc
!= DOM_NO_ERR
) {
830 css_stylesheet_destroy(sheet
);
831 return svgtiny_LIBDOM_ERROR
;
835 /* Here's where we'd actually change the media type if
836 we were going to use it */
837 dom_string_unref(media_attr
);
841 dom_node_get_text_content(style
, &data
);
843 /* Empty stylesheet? That's fine. */
844 css_stylesheet_destroy(sheet
);
848 code
= css_stylesheet_append_data(sheet
,
849 (uint8_t *)dom_string_data(data
),
850 dom_string_byte_length(data
));
851 if (code
!= CSS_OK
&& code
!= CSS_NEEDDATA
) {
852 dom_string_unref(data
);
853 css_stylesheet_destroy(sheet
);
854 return svgtiny_LIBCSS_ERROR
;
857 code
= css_stylesheet_data_done(sheet
);
858 if (code
!= CSS_OK
) {
859 dom_string_unref(data
);
860 css_stylesheet_destroy(sheet
);
861 return svgtiny_LIBCSS_ERROR
;
864 code
= css_select_ctx_append_sheet(state
.select_ctx
,
868 if (code
!= CSS_OK
) {
869 dom_string_unref(data
);
870 return svgtiny_LIBCSS_ERROR
;
873 dom_string_unref(data
);
879 * Parse the contents of an inline style and return (a pointer to) the
880 * corresponding stylesheet for use with css_select_style(). Returns
881 * NULL if anything goes wrong.
883 css_stylesheet
*svgtiny_parse_style_inline(const uint8_t *data
,
886 css_stylesheet
*sheet
;
889 code
= svgtiny_create_stylesheet(&sheet
, true);
890 if (code
!= CSS_OK
) {
894 code
= css_stylesheet_append_data(sheet
, data
, len
);
895 if (code
!= CSS_OK
&& code
!= CSS_NEEDDATA
) {
896 css_stylesheet_destroy(sheet
);
900 code
= css_stylesheet_data_done(sheet
);
901 if (code
!= CSS_OK
) {
902 css_stylesheet_destroy(sheet
);
910 * Parse all <style> elements within a root <svg> element. This
911 * should be called before svgtiny_parse_svg() because that function
912 * makes a single pass through the document and we'd like all style
913 * information to be available during that pass. Specifically, we'd
914 * like a <style> sheet at the end of the document to affect the
915 * rendering of elements at its beginning.
917 * The element-parsing inner loop here is essentially the same as
918 * that within svgtiny_parse_svg().
920 svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
921 struct svgtiny_parse_state state
)
926 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
927 if (exc
!= DOM_NO_ERR
) {
928 return svgtiny_LIBDOM_ERROR
;
930 while (child
!= NULL
) {
932 dom_node_type nodetype
;
933 svgtiny_code code
= svgtiny_OK
;
935 exc
= dom_node_get_node_type(child
, &nodetype
);
936 if (exc
!= DOM_NO_ERR
) {
937 dom_node_unref(child
);
938 return svgtiny_LIBDOM_ERROR
;
940 if (nodetype
== DOM_ELEMENT_NODE
) {
941 dom_string
*nodename
;
942 exc
= dom_node_get_node_name(child
, &nodename
);
943 if (exc
!= DOM_NO_ERR
) {
944 dom_node_unref(child
);
945 return svgtiny_LIBDOM_ERROR
;
948 if (dom_string_caseless_isequal(state
.interned_style
,
950 /* We have a <style> element, parse it */
951 code
= svgtiny_parse_style_element(child
,
956 dom_string_unref(nodename
);
958 if (code
!= svgtiny_OK
) {
959 dom_node_unref(child
);
962 exc
= dom_node_get_next_sibling(child
,
963 (dom_node
**) (void *) &next
);
964 dom_node_unref(child
);
965 if (exc
!= DOM_NO_ERR
) {
966 return svgtiny_LIBDOM_ERROR
;
975 * Parse <svg>, <g>, and <a> element nodes.
978 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
979 struct svgtiny_parse_state state
)
981 float x
, y
, width
, height
;
982 dom_string
*view_box
;
985 css_select_results
*styles
;
987 svgtiny_setup_state_local(&state
);
989 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
990 svgtiny_parse_paint_attributes(svg
, &state
);
991 svgtiny_parse_font_attributes(svg
, &state
);
992 styles
= svgtiny_parse_styles(svg
, &state
);
994 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
996 if (exc
!= DOM_NO_ERR
) {
997 svgtiny_cleanup_state_local(&state
);
998 return svgtiny_LIBDOM_ERROR
;
1002 char *s
= strndup(dom_string_data(view_box
),
1003 dom_string_byte_length(view_box
));
1004 float min_x
, min_y
, vwidth
, vheight
;
1005 if (sscanf(s
, "%f,%f,%f,%f",
1006 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
1007 sscanf(s
, "%f %f %f %f",
1008 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
1009 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
1010 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
1011 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
1012 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
1015 dom_string_unref(view_box
);
1018 svgtiny_parse_transform_attributes(svg
, &state
);
1020 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
1021 if (exc
!= DOM_NO_ERR
) {
1022 svgtiny_cleanup_state_local(&state
);
1023 return svgtiny_LIBDOM_ERROR
;
1025 while (child
!= NULL
) {
1027 dom_node_type nodetype
;
1028 svgtiny_code code
= svgtiny_OK
;
1030 /* Before we descend to one of my child elements, set
1031 * the "parent style" to my style. */
1033 /* For now at least, the root element won't
1034 * have any styles; hence the null check. */
1035 state
.parent_style
= styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
];
1038 exc
= dom_node_get_node_type(child
, &nodetype
);
1039 if (exc
!= DOM_NO_ERR
) {
1040 dom_node_unref(child
);
1041 return svgtiny_LIBDOM_ERROR
;
1043 if (nodetype
== DOM_ELEMENT_NODE
) {
1044 dom_string
*nodename
;
1045 exc
= dom_node_get_node_name(child
, &nodename
);
1046 if (exc
!= DOM_NO_ERR
) {
1047 dom_node_unref(child
);
1048 svgtiny_cleanup_state_local(&state
);
1049 return svgtiny_LIBDOM_ERROR
;
1051 if (dom_string_caseless_isequal(state
.interned_svg
,
1053 code
= svgtiny_parse_svg(child
, state
);
1054 else if (dom_string_caseless_isequal(state
.interned_g
,
1056 code
= svgtiny_parse_svg(child
, state
);
1057 else if (dom_string_caseless_isequal(state
.interned_a
,
1059 code
= svgtiny_parse_svg(child
, state
);
1060 else if (dom_string_caseless_isequal(state
.interned_path
,
1062 code
= svgtiny_parse_path(child
, state
);
1063 else if (dom_string_caseless_isequal(state
.interned_rect
,
1065 code
= svgtiny_parse_rect(child
, state
);
1066 else if (dom_string_caseless_isequal(state
.interned_circle
,
1068 code
= svgtiny_parse_circle(child
, state
);
1069 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
1071 code
= svgtiny_parse_ellipse(child
, state
);
1072 else if (dom_string_caseless_isequal(state
.interned_line
,
1074 code
= svgtiny_parse_line(child
, state
);
1075 else if (dom_string_caseless_isequal(state
.interned_polyline
,
1077 code
= svgtiny_parse_poly(child
, state
, false);
1078 else if (dom_string_caseless_isequal(state
.interned_polygon
,
1080 code
= svgtiny_parse_poly(child
, state
, true);
1081 else if (dom_string_caseless_isequal(state
.interned_text
,
1083 code
= svgtiny_parse_text(child
, state
);
1084 dom_string_unref(nodename
);
1086 if (code
!= svgtiny_OK
) {
1087 dom_node_unref(child
);
1088 svgtiny_cleanup_state_local(&state
);
1091 exc
= dom_node_get_next_sibling(child
,
1092 (dom_node
**) (void *) &next
);
1093 dom_node_unref(child
);
1094 if (exc
!= DOM_NO_ERR
) {
1095 svgtiny_cleanup_state_local(&state
);
1096 return svgtiny_LIBDOM_ERROR
;
1101 /* Hoping that destroying "styles" destroys state.parent_style
1103 css_select_results_destroy(styles
);
1104 svgtiny_cleanup_state_local(&state
);
1111 * Parse a <path> element node.
1113 * http://www.w3.org/TR/SVG11/paths#PathElement
1116 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
1117 struct svgtiny_parse_state state
)
1120 dom_string
*path_d_str
;
1123 float *p
; /* path elemets */
1124 unsigned int palloc
; /* number of path elements allocated */
1126 float last_x
= 0, last_y
= 0;
1127 float last_cubic_x
= 0, last_cubic_y
= 0;
1128 float last_quad_x
= 0, last_quad_y
= 0;
1129 float subpath_first_x
= 0, subpath_first_y
= 0;
1131 svgtiny_setup_state_local(&state
);
1133 svgtiny_parse_paint_attributes(path
, &state
);
1134 svgtiny_parse_transform_attributes(path
, &state
);
1135 css_select_results_destroy(svgtiny_parse_styles(path
, &state
));
1137 /* read d attribute */
1138 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
1139 if (exc
!= DOM_NO_ERR
) {
1140 state
.diagram
->error_line
= -1; /* path->line; */
1141 state
.diagram
->error_message
= "path: error retrieving d attribute";
1142 svgtiny_cleanup_state_local(&state
);
1143 return svgtiny_SVG_ERROR
;
1146 if (path_d_str
== NULL
) {
1147 state
.diagram
->error_line
= -1; /* path->line; */
1148 state
.diagram
->error_message
= "path: missing d attribute";
1149 svgtiny_cleanup_state_local(&state
);
1150 return svgtiny_SVG_ERROR
;
1153 /* empty path is permitted it just disables the path */
1154 palloc
= dom_string_byte_length(path_d_str
);
1156 dom_string_unref(path_d_str
);
1157 svgtiny_cleanup_state_local(&state
);
1161 /* local copy of the path data allowing in-place modification */
1162 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
1163 dom_string_unref(path_d_str
);
1165 svgtiny_cleanup_state_local(&state
);
1166 return svgtiny_OUT_OF_MEMORY
;
1169 /* ensure path element allocation is sensibly bounded */
1172 } else if (palloc
> 64) {
1173 palloc
= palloc
/ 2;
1176 /* allocate initial space for path elements */
1177 p
= malloc(sizeof p
[0] * palloc
);
1180 svgtiny_cleanup_state_local(&state
);
1181 return svgtiny_OUT_OF_MEMORY
;
1184 /* parse d and build path */
1185 for (i
= 0; s
[i
]; i
++)
1192 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
1195 /* Ensure there is sufficient space for path elements */
1196 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
1198 if ((palloc - i) < NUM_ELEMENTS) { \
1200 palloc = (palloc * 2) + (palloc / 2); \
1201 tp = realloc(p, sizeof p[0] * palloc); \
1205 svgtiny_cleanup_state_local(&state); \
1206 return svgtiny_OUT_OF_MEMORY; \
1213 /* moveto (M, m), lineto (L, l) (2 arguments) */
1214 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
1215 /*LOG(("moveto or lineto"));*/
1216 if (*command
== 'M' || *command
== 'm')
1217 plot_command
= svgtiny_PATH_MOVE
;
1219 plot_command
= svgtiny_PATH_LINE
;
1221 ALLOC_PATH_ELEMENTS(3);
1222 p
[i
++] = plot_command
;
1223 if ('a' <= *command
) {
1227 if (plot_command
== svgtiny_PATH_MOVE
) {
1228 subpath_first_x
= x
;
1229 subpath_first_y
= y
;
1231 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1233 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1236 plot_command
= svgtiny_PATH_LINE
;
1237 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
1239 /* closepath (Z, z) (no arguments) */
1240 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
1241 /*LOG(("closepath"));*/
1242 ALLOC_PATH_ELEMENTS(1);
1244 p
[i
++] = svgtiny_PATH_CLOSE
;
1246 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
1247 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
1249 /* horizontal lineto (H, h) (1 argument) */
1250 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
1251 /*LOG(("horizontal lineto"));*/
1253 ALLOC_PATH_ELEMENTS(3);
1255 p
[i
++] = svgtiny_PATH_LINE
;
1256 if (*command
== 'h')
1258 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1260 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
1262 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
1264 /* vertical lineto (V, v) (1 argument) */
1265 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
1266 /*LOG(("vertical lineto"));*/
1268 ALLOC_PATH_ELEMENTS(3);
1270 p
[i
++] = svgtiny_PATH_LINE
;
1271 if (*command
== 'v')
1273 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
1274 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1277 } while (sscanf(s
, "%f %n", &y
, &n
) == 1);
1279 /* curveto (C, c) (6 arguments) */
1280 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
1281 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
1282 /*LOG(("curveto"));*/
1284 ALLOC_PATH_ELEMENTS(7);
1286 p
[i
++] = svgtiny_PATH_BEZIER
;
1287 if (*command
== 'c') {
1297 p
[i
++] = last_cubic_x
= x2
;
1298 p
[i
++] = last_cubic_y
= y2
;
1299 p
[i
++] = last_quad_x
= last_x
= x
;
1300 p
[i
++] = last_quad_y
= last_y
= y
;
1302 } while (sscanf(s
, "%f %f %f %f %f %f %n",
1303 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
1305 /* shorthand/smooth curveto (S, s) (4 arguments) */
1306 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
1307 &x2
, &y2
, &x
, &y
, &n
) == 5) {
1308 /*LOG(("shorthand/smooth curveto"));*/
1310 ALLOC_PATH_ELEMENTS(7);
1312 p
[i
++] = svgtiny_PATH_BEZIER
;
1313 x1
= last_x
+ (last_x
- last_cubic_x
);
1314 y1
= last_y
+ (last_y
- last_cubic_y
);
1315 if (*command
== 's') {
1323 p
[i
++] = last_cubic_x
= x2
;
1324 p
[i
++] = last_cubic_y
= y2
;
1325 p
[i
++] = last_quad_x
= last_x
= x
;
1326 p
[i
++] = last_quad_y
= last_y
= y
;
1328 } while (sscanf(s
, "%f %f %f %f %n",
1329 &x2
, &y2
, &x
, &y
, &n
) == 4);
1331 /* quadratic Bezier curveto (Q, q) (4 arguments) */
1332 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
1333 &x1
, &y1
, &x
, &y
, &n
) == 5) {
1334 /*LOG(("quadratic Bezier curveto"));*/
1336 ALLOC_PATH_ELEMENTS(7);
1338 p
[i
++] = svgtiny_PATH_BEZIER
;
1341 if (*command
== 'q') {
1347 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1348 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1349 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1350 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1351 p
[i
++] = last_cubic_x
= last_x
= x
;
1352 p
[i
++] = last_cubic_y
= last_y
= y
;
1354 } while (sscanf(s
, "%f %f %f %f %n",
1355 &x1
, &y1
, &x
, &y
, &n
) == 4);
1357 /* shorthand/smooth quadratic Bezier curveto (T, t)
1359 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
1361 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
1363 ALLOC_PATH_ELEMENTS(7);
1365 p
[i
++] = svgtiny_PATH_BEZIER
;
1366 x1
= last_x
+ (last_x
- last_quad_x
);
1367 y1
= last_y
+ (last_y
- last_quad_y
);
1370 if (*command
== 't') {
1376 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1377 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1378 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1379 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1380 p
[i
++] = last_cubic_x
= last_x
= x
;
1381 p
[i
++] = last_cubic_y
= last_y
= y
;
1383 } while (sscanf(s
, "%f %f %n",
1386 /* elliptical arc (A, a) (7 arguments) */
1387 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
1388 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1392 double bzpoints
[6*4]; /* allow for up to four bezier segments per arc */
1394 if (*command
== 'a') {
1399 bzsegments
= svgarc_to_bezier(last_x
, last_y
,
1406 if (bzsegments
== -1) {
1408 ALLOC_PATH_ELEMENTS(3);
1409 p
[i
++] = svgtiny_PATH_LINE
;
1412 } else if (bzsegments
> 0) {
1414 ALLOC_PATH_ELEMENTS((unsigned int)bzsegments
* 7);
1415 for (bzpnt
= 0;bzpnt
< (bzsegments
* 6); bzpnt
+=6) {
1416 p
[i
++] = svgtiny_PATH_BEZIER
;
1417 p
[i
++] = bzpoints
[bzpnt
];
1418 p
[i
++] = bzpoints
[bzpnt
+1];
1419 p
[i
++] = bzpoints
[bzpnt
+2];
1420 p
[i
++] = bzpoints
[bzpnt
+3];
1421 p
[i
++] = bzpoints
[bzpnt
+4];
1422 p
[i
++] = bzpoints
[bzpnt
+5];
1425 if (bzsegments
!= 0) {
1426 last_cubic_x
= last_quad_x
= last_x
= p
[i
-2];
1427 last_cubic_y
= last_quad_y
= last_y
= p
[i
-1];
1432 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
1433 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1437 /* fprintf(stderr, "parse failed at \"%s\"\n", s); */
1445 /* no real segments in path */
1447 svgtiny_cleanup_state_local(&state
);
1451 /* resize path element array to not be over allocated */
1455 /* try the resize, if it fails just continue to use the old
1458 tp
= realloc(p
, sizeof p
[0] * i
);
1464 err
= svgtiny_add_path(p
, i
, &state
);
1466 svgtiny_cleanup_state_local(&state
);
1473 * Parse a <rect> element node.
1475 * http://www.w3.org/TR/SVG11/shapes#RectElement
1478 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
1479 struct svgtiny_parse_state state
)
1482 float x
, y
, width
, height
;
1485 svgtiny_setup_state_local(&state
);
1487 svgtiny_parse_position_attributes(rect
, state
,
1488 &x
, &y
, &width
, &height
);
1489 svgtiny_parse_paint_attributes(rect
, &state
);
1490 svgtiny_parse_transform_attributes(rect
, &state
);
1491 css_select_results_destroy(svgtiny_parse_styles(rect
, &state
));
1493 p
= malloc(13 * sizeof p
[0]);
1495 svgtiny_cleanup_state_local(&state
);
1496 return svgtiny_OUT_OF_MEMORY
;
1499 p
[0] = svgtiny_PATH_MOVE
;
1502 p
[3] = svgtiny_PATH_LINE
;
1505 p
[6] = svgtiny_PATH_LINE
;
1508 p
[9] = svgtiny_PATH_LINE
;
1511 p
[12] = svgtiny_PATH_CLOSE
;
1513 err
= svgtiny_add_path(p
, 13, &state
);
1515 svgtiny_cleanup_state_local(&state
);
1522 * Parse a <circle> element node.
1525 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
1526 struct svgtiny_parse_state state
)
1529 float x
= 0, y
= 0, r
= -1;
1534 svgtiny_setup_state_local(&state
);
1536 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
1537 if (exc
!= DOM_NO_ERR
) {
1538 svgtiny_cleanup_state_local(&state
);
1539 return svgtiny_LIBDOM_ERROR
;
1542 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1544 dom_string_unref(attr
);
1546 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
1547 if (exc
!= DOM_NO_ERR
) {
1548 svgtiny_cleanup_state_local(&state
);
1549 return svgtiny_LIBDOM_ERROR
;
1552 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1554 dom_string_unref(attr
);
1556 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
1557 if (exc
!= DOM_NO_ERR
) {
1558 svgtiny_cleanup_state_local(&state
);
1559 return svgtiny_LIBDOM_ERROR
;
1562 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1564 dom_string_unref(attr
);
1566 svgtiny_parse_paint_attributes(circle
, &state
);
1567 svgtiny_parse_transform_attributes(circle
, &state
);
1568 css_select_results_destroy(svgtiny_parse_styles(circle
, &state
));
1571 state
.diagram
->error_line
= -1; /* circle->line; */
1572 state
.diagram
->error_message
= "circle: r missing or negative";
1573 svgtiny_cleanup_state_local(&state
);
1574 return svgtiny_SVG_ERROR
;
1577 svgtiny_cleanup_state_local(&state
);
1581 p
= malloc(32 * sizeof p
[0]);
1583 svgtiny_cleanup_state_local(&state
);
1584 return svgtiny_OUT_OF_MEMORY
;
1587 p
[0] = svgtiny_PATH_MOVE
;
1590 p
[3] = svgtiny_PATH_BEZIER
;
1592 p
[5] = y
+ r
* KAPPA
;
1593 p
[6] = x
+ r
* KAPPA
;
1597 p
[10] = svgtiny_PATH_BEZIER
;
1598 p
[11] = x
- r
* KAPPA
;
1601 p
[14] = y
+ r
* KAPPA
;
1604 p
[17] = svgtiny_PATH_BEZIER
;
1606 p
[19] = y
- r
* KAPPA
;
1607 p
[20] = x
- r
* KAPPA
;
1611 p
[24] = svgtiny_PATH_BEZIER
;
1612 p
[25] = x
+ r
* KAPPA
;
1615 p
[28] = y
- r
* KAPPA
;
1618 p
[31] = svgtiny_PATH_CLOSE
;
1620 err
= svgtiny_add_path(p
, 32, &state
);
1622 svgtiny_cleanup_state_local(&state
);
1629 * Parse an <ellipse> element node.
1632 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
1633 struct svgtiny_parse_state state
)
1636 float x
= 0, y
= 0, rx
= -1, ry
= -1;
1641 svgtiny_setup_state_local(&state
);
1643 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
1644 if (exc
!= DOM_NO_ERR
) {
1645 svgtiny_cleanup_state_local(&state
);
1646 return svgtiny_LIBDOM_ERROR
;
1649 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1651 dom_string_unref(attr
);
1653 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
1654 if (exc
!= DOM_NO_ERR
) {
1655 svgtiny_cleanup_state_local(&state
);
1656 return svgtiny_LIBDOM_ERROR
;
1659 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1661 dom_string_unref(attr
);
1663 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
1664 if (exc
!= DOM_NO_ERR
) {
1665 svgtiny_cleanup_state_local(&state
);
1666 return svgtiny_LIBDOM_ERROR
;
1669 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1671 dom_string_unref(attr
);
1673 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
1674 if (exc
!= DOM_NO_ERR
) {
1675 svgtiny_cleanup_state_local(&state
);
1676 return svgtiny_LIBDOM_ERROR
;
1679 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1681 dom_string_unref(attr
);
1683 svgtiny_parse_paint_attributes(ellipse
, &state
);
1684 svgtiny_parse_transform_attributes(ellipse
, &state
);
1685 css_select_results_destroy(svgtiny_parse_styles(ellipse
, &state
));
1687 if (rx
< 0 || ry
< 0) {
1688 state
.diagram
->error_line
= -1; /* ellipse->line; */
1689 state
.diagram
->error_message
= "ellipse: rx or ry missing "
1691 svgtiny_cleanup_state_local(&state
);
1692 return svgtiny_SVG_ERROR
;
1694 if (rx
== 0 || ry
== 0) {
1695 svgtiny_cleanup_state_local(&state
);
1699 p
= malloc(32 * sizeof p
[0]);
1701 svgtiny_cleanup_state_local(&state
);
1702 return svgtiny_OUT_OF_MEMORY
;
1705 p
[0] = svgtiny_PATH_MOVE
;
1708 p
[3] = svgtiny_PATH_BEZIER
;
1710 p
[5] = y
+ ry
* KAPPA
;
1711 p
[6] = x
+ rx
* KAPPA
;
1715 p
[10] = svgtiny_PATH_BEZIER
;
1716 p
[11] = x
- rx
* KAPPA
;
1719 p
[14] = y
+ ry
* KAPPA
;
1722 p
[17] = svgtiny_PATH_BEZIER
;
1724 p
[19] = y
- ry
* KAPPA
;
1725 p
[20] = x
- rx
* KAPPA
;
1729 p
[24] = svgtiny_PATH_BEZIER
;
1730 p
[25] = x
+ rx
* KAPPA
;
1733 p
[28] = y
- ry
* KAPPA
;
1736 p
[31] = svgtiny_PATH_CLOSE
;
1738 err
= svgtiny_add_path(p
, 32, &state
);
1740 svgtiny_cleanup_state_local(&state
);
1747 * Parse a <line> element node.
1750 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1751 struct svgtiny_parse_state state
)
1754 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1759 svgtiny_setup_state_local(&state
);
1761 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1762 if (exc
!= DOM_NO_ERR
) {
1763 svgtiny_cleanup_state_local(&state
);
1764 return svgtiny_LIBDOM_ERROR
;
1767 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1769 dom_string_unref(attr
);
1771 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1772 if (exc
!= DOM_NO_ERR
) {
1773 svgtiny_cleanup_state_local(&state
);
1774 return svgtiny_LIBDOM_ERROR
;
1777 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1779 dom_string_unref(attr
);
1781 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1782 if (exc
!= DOM_NO_ERR
) {
1783 svgtiny_cleanup_state_local(&state
);
1784 return svgtiny_LIBDOM_ERROR
;
1787 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1789 dom_string_unref(attr
);
1791 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1792 if (exc
!= DOM_NO_ERR
) {
1793 svgtiny_cleanup_state_local(&state
);
1794 return svgtiny_LIBDOM_ERROR
;
1797 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1799 dom_string_unref(attr
);
1801 svgtiny_parse_paint_attributes(line
, &state
);
1802 svgtiny_parse_transform_attributes(line
, &state
);
1803 css_select_results_destroy(svgtiny_parse_styles(line
, &state
));
1805 p
= malloc(7 * sizeof p
[0]);
1807 svgtiny_cleanup_state_local(&state
);
1808 return svgtiny_OUT_OF_MEMORY
;
1811 p
[0] = svgtiny_PATH_MOVE
;
1814 p
[3] = svgtiny_PATH_LINE
;
1817 p
[6] = svgtiny_PATH_CLOSE
;
1819 err
= svgtiny_add_path(p
, 7, &state
);
1821 svgtiny_cleanup_state_local(&state
);
1828 * Parse a <polyline> or <polygon> element node.
1830 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1831 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1834 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1835 struct svgtiny_parse_state state
, bool polygon
)
1838 dom_string
*points_str
;
1844 svgtiny_setup_state_local(&state
);
1846 svgtiny_parse_paint_attributes(poly
, &state
);
1847 svgtiny_parse_transform_attributes(poly
, &state
);
1848 css_select_results_destroy(svgtiny_parse_styles(poly
, &state
));
1850 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1852 if (exc
!= DOM_NO_ERR
) {
1853 svgtiny_cleanup_state_local(&state
);
1854 return svgtiny_LIBDOM_ERROR
;
1857 if (points_str
== NULL
) {
1858 state
.diagram
->error_line
= -1; /* poly->line; */
1859 state
.diagram
->error_message
=
1860 "polyline/polygon: missing points attribute";
1861 svgtiny_cleanup_state_local(&state
);
1862 return svgtiny_SVG_ERROR
;
1865 s
= points
= strndup(dom_string_data(points_str
),
1866 dom_string_byte_length(points_str
));
1867 dom_string_unref(points_str
);
1868 /* read points attribute */
1870 svgtiny_cleanup_state_local(&state
);
1871 return svgtiny_OUT_OF_MEMORY
;
1873 /* allocate space for path: it will never have more elements than s */
1874 p
= malloc(sizeof p
[0] * strlen(s
));
1877 svgtiny_cleanup_state_local(&state
);
1878 return svgtiny_OUT_OF_MEMORY
;
1881 /* parse s and build path */
1882 for (i
= 0; s
[i
]; i
++)
1890 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1892 p
[i
++] = svgtiny_PATH_MOVE
;
1894 p
[i
++] = svgtiny_PATH_LINE
;
1903 p
[i
++] = svgtiny_PATH_CLOSE
;
1907 err
= svgtiny_add_path(p
, i
, &state
);
1909 svgtiny_cleanup_state_local(&state
);
1916 * Parse a <text> or <tspan> element node.
1919 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1920 struct svgtiny_parse_state state
)
1922 float x
, y
, width
, height
;
1927 svgtiny_setup_state_local(&state
);
1929 svgtiny_parse_position_attributes(text
, state
,
1930 &x
, &y
, &width
, &height
);
1931 svgtiny_parse_font_attributes(text
, &state
);
1932 svgtiny_parse_transform_attributes(text
, &state
);
1934 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1935 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1936 /* state.ctm.e = px - state.origin_x; */
1937 /* state.ctm.f = py - state.origin_y; */
1939 exc
= dom_node_get_first_child(text
, &child
);
1940 if (exc
!= DOM_NO_ERR
) {
1941 return svgtiny_LIBDOM_ERROR
;
1942 svgtiny_cleanup_state_local(&state
);
1944 while (child
!= NULL
) {
1946 dom_node_type nodetype
;
1947 svgtiny_code code
= svgtiny_OK
;
1949 exc
= dom_node_get_node_type(child
, &nodetype
);
1950 if (exc
!= DOM_NO_ERR
) {
1951 dom_node_unref(child
);
1952 svgtiny_cleanup_state_local(&state
);
1953 return svgtiny_LIBDOM_ERROR
;
1955 if (nodetype
== DOM_ELEMENT_NODE
) {
1956 dom_string
*nodename
;
1957 exc
= dom_node_get_node_name(child
, &nodename
);
1958 if (exc
!= DOM_NO_ERR
) {
1959 dom_node_unref(child
);
1960 svgtiny_cleanup_state_local(&state
);
1961 return svgtiny_LIBDOM_ERROR
;
1963 if (dom_string_caseless_isequal(nodename
,
1964 state
.interned_tspan
))
1965 code
= svgtiny_parse_text((dom_element
*)child
,
1967 dom_string_unref(nodename
);
1968 } else if (nodetype
== DOM_TEXT_NODE
) {
1969 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1970 dom_string
*content
;
1971 if (shape
== NULL
) {
1972 dom_node_unref(child
);
1973 svgtiny_cleanup_state_local(&state
);
1974 return svgtiny_OUT_OF_MEMORY
;
1976 exc
= dom_text_get_whole_text(child
, &content
);
1977 if (exc
!= DOM_NO_ERR
) {
1978 dom_node_unref(child
);
1979 svgtiny_cleanup_state_local(&state
);
1980 return svgtiny_LIBDOM_ERROR
;
1982 if (content
!= NULL
) {
1983 shape
->text
= strndup(dom_string_data(content
),
1984 dom_string_byte_length(content
));
1985 dom_string_unref(content
);
1987 shape
->text
= strdup("");
1991 state
.diagram
->shape_count
++;
1994 if (code
!= svgtiny_OK
) {
1995 dom_node_unref(child
);
1996 svgtiny_cleanup_state_local(&state
);
1999 exc
= dom_node_get_next_sibling(child
, &next
);
2000 dom_node_unref(child
);
2001 if (exc
!= DOM_NO_ERR
) {
2002 svgtiny_cleanup_state_local(&state
);
2003 return svgtiny_LIBDOM_ERROR
;
2008 svgtiny_cleanup_state_local(&state
);
2015 * Parse x, y, width, and height attributes, if present.
2018 void svgtiny_parse_position_attributes(dom_element
*node
,
2019 const struct svgtiny_parse_state state
,
2020 float *x
, float *y
, float *width
, float *height
)
2027 *width
= state
.viewport_width
;
2028 *height
= state
.viewport_height
;
2030 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
2031 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2032 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
2033 dom_string_unref(attr
);
2036 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
2037 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2038 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
2039 dom_string_unref(attr
);
2042 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
2043 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2044 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
2046 dom_string_unref(attr
);
2049 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
2050 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2051 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
2053 dom_string_unref(attr
);
2059 * Parse a length as a number of pixels.
2062 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
2063 const struct svgtiny_parse_state state
)
2065 int num_length
= strspn(s
, "0123456789+-.");
2066 const char *unit
= s
+ num_length
;
2067 float n
= atof((const char *) s
);
2068 float font_size
= 20;
2074 } else if (unit
[0] == '%') {
2075 return n
/ 100.0 * viewport_size
;
2076 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
2077 return n
* font_size
;
2078 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
2079 return n
/ 2.0 * font_size
;
2080 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
2082 } else if (unit
[0] == 'p' && unit
[1] == 't') {
2084 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
2086 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
2087 return n
* 3.543307;
2088 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
2089 return n
* 35.43307;
2090 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
2097 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
2098 const struct svgtiny_parse_state state
)
2100 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
2101 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
2107 * Parse paint attributes, if present.
2110 void svgtiny_parse_paint_attributes(dom_element
*node
,
2111 struct svgtiny_parse_state
*state
)
2116 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
2117 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2118 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
2119 dom_string_unref(attr
);
2122 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
2123 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2124 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
2125 dom_string_unref(attr
);
2128 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
2129 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2130 state
->stroke_width
= svgtiny_parse_length(attr
,
2131 state
->viewport_width
, *state
);
2132 dom_string_unref(attr
);
2135 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
2136 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2137 /* Parse a few properties "by hand" until they can
2138 be supported in libcss. */
2139 char *style
= strndup(dom_string_data(attr
),
2140 dom_string_byte_length(attr
));
2143 if ((s
= strstr(style
, "fill:"))) {
2147 value
= strndup(s
, strcspn(s
, "; "));
2148 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
2151 if ((s
= strstr(style
, "stroke:"))) {
2155 value
= strndup(s
, strcspn(s
, "; "));
2156 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
2159 if ((s
= strstr(style
, "stroke-width:"))) {
2163 value
= strndup(s
, strcspn(s
, "; "));
2164 state
->stroke_width
= _svgtiny_parse_length(value
,
2165 state
->viewport_width
, *state
);
2169 dom_string_unref(attr
);
2178 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
2179 struct svgtiny_parse_state_gradient
*grad
,
2180 struct svgtiny_parse_state
*state
)
2182 unsigned int r
, g
, b
;
2184 size_t len
= strlen(s
);
2185 char *id
= 0, *rparen
;
2187 if (len
== 4 && s
[0] == '#') {
2188 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
2189 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
2191 } else if (len
== 7 && s
[0] == '#') {
2192 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
2193 *c
= svgtiny_RGB(r
, g
, b
);
2195 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
2196 s
[3] == '(' && s
[len
- 1] == ')') {
2197 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
2198 *c
= svgtiny_RGB(r
, g
, b
);
2199 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
2203 *c
= svgtiny_RGB(r
, g
, b
);
2206 } else if (len
== 4 && strcmp(s
, "none") == 0) {
2207 *c
= svgtiny_TRANSPARENT
;
2209 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
2212 *c
= svgtiny_RGB(0, 0, 0);
2213 } else if (s
[4] == '#') {
2217 rparen
= strchr(id
, ')');
2220 svgtiny_find_gradient(id
, grad
, state
);
2222 if (grad
->linear_gradient_stop_count
== 0)
2223 *c
= svgtiny_TRANSPARENT
;
2224 else if (grad
->linear_gradient_stop_count
== 1)
2225 *c
= grad
->gradient_stop
[0].color
;
2227 *c
= svgtiny_LINEAR_GRADIENT
;
2231 const struct svgtiny_named_color
*named_color
;
2232 named_color
= svgtiny_color_lookup(s
, strlen(s
));
2234 *c
= named_color
->color
;
2238 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
2239 struct svgtiny_parse_state_gradient
*grad
,
2240 struct svgtiny_parse_state
*state
)
2243 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
2244 dom_string_unref(s
);
2248 * Parse font attributes, if present.
2251 void svgtiny_parse_font_attributes(dom_element
*node
,
2252 struct svgtiny_parse_state
*state
)
2254 /* TODO: Implement this, it never used to be */
2257 #ifdef WRITTEN_THIS_PROPERLY
2258 const xmlAttr
*attr
;
2262 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
2263 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
2272 * Parse transform attributes, if present.
2274 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
2277 void svgtiny_parse_transform_attributes(dom_element
*node
,
2278 struct svgtiny_parse_state
*state
)
2284 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
2286 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2287 transform
= strndup(dom_string_data(attr
),
2288 dom_string_byte_length(attr
));
2289 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
2290 &state
->ctm
.c
, &state
->ctm
.d
,
2291 &state
->ctm
.e
, &state
->ctm
.f
);
2293 dom_string_unref(attr
);
2298 * Parse element styles.
2300 * First we parse any inline "style" attributes. We then compose the
2301 * element's style with any parent styles. Finally, we compute any
2302 * styles that we support and set the corresponding fields in the
2305 css_select_results
*svgtiny_parse_styles(dom_element
*node
,
2306 struct svgtiny_parse_state
*state
)
2309 uint8_t fill_opacity_type
;
2310 css_fixed fill_opacity
;
2311 uint8_t stroke_opacity_type
;
2312 css_fixed stroke_opacity
;
2314 /* We store the result of svgtiny_parse_style_inline() in
2315 * inline_sheet, and that function returns NULL on error; in
2316 * particular you do not need to css_stylesheet_destroy() the
2317 * result if it is NULL, and css_stylesheet_destroy() checks
2319 css_stylesheet
*inline_sheet
= NULL
;
2321 /* Initialize this to NULL for the same reason: so that we can
2322 * safely destroy it later even if we never populated it. */
2323 css_select_results
*styles
= NULL
;
2325 /* The result of composing this node's styles with its
2326 * parent's styles. */
2327 css_computed_style
*composed
= NULL
;
2332 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
2333 if (exc
!= DOM_NO_ERR
) {
2337 inline_sheet
= svgtiny_parse_style_inline(
2338 (uint8_t *)dom_string_data(attr
),
2339 dom_string_byte_length(attr
));
2340 dom_string_unref(attr
);
2343 code
= svgtiny_select_style(state
, node
, inline_sheet
, &styles
);
2344 css_stylesheet_destroy(inline_sheet
);
2345 if (code
!= CSS_OK
) {
2349 if (state
->parent_style
!= NULL
) {
2350 code
= css_computed_style_compose(
2351 state
->parent_style
,
2352 styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
],
2356 if (code
!= CSS_OK
|| composed
== NULL
) {
2357 /* This function promises to return a
2358 * fully-composed set of styles, so if
2359 * we can't do that, we should fail. */
2360 css_select_results_destroy(styles
);
2364 /* Replace my original computed styles with the
2366 css_computed_style_destroy(
2367 styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
]);
2368 styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
] = composed
;
2371 fill_opacity_type
= css_computed_fill_opacity(
2372 styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
],
2374 stroke_opacity_type
= css_computed_stroke_opacity(
2375 styles
->styles
[CSS_PSEUDO_ELEMENT_NONE
],
2378 state
->fill_opacity
= FIXTOFLT(fill_opacity
);
2379 state
->stroke_opacity
= FIXTOFLT(stroke_opacity
);
2385 * Parse a transform string.
2388 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
2389 float *mc
, float *md
, float *me
, float *mf
)
2391 float a
, b
, c
, d
, e
, f
;
2392 float za
, zb
, zc
, zd
, ze
, zf
;
2397 for (i
= 0; s
[i
]; i
++)
2406 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
2407 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
2409 else if ((sscanf(s
, " translate (%f %f ) %n",
2410 &e
, &f
, &n
) == 2) && (n
> 0))
2412 else if ((sscanf(s
, " translate (%f ) %n",
2413 &e
, &n
) == 1) && (n
> 0))
2415 else if ((sscanf(s
, " scale (%f %f ) %n",
2416 &a
, &d
, &n
) == 2) && (n
> 0))
2418 else if ((sscanf(s
, " scale (%f ) %n",
2419 &a
, &n
) == 1) && (n
> 0))
2421 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
2422 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
2423 angle
= angle
/ 180 * M_PI
;
2428 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
2429 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
2430 } else if ((sscanf(s
, " rotate (%f ) %n",
2431 &angle
, &n
) == 1) && (n
> 0)) {
2432 angle
= angle
/ 180 * M_PI
;
2437 } else if ((sscanf(s
, " skewX (%f ) %n",
2438 &angle
, &n
) == 1) && (n
> 0)) {
2439 angle
= angle
/ 180 * M_PI
;
2441 } else if ((sscanf(s
, " skewY (%f ) %n",
2442 &angle
, &n
) == 1) && (n
> 0)) {
2443 angle
= angle
/ 180 * M_PI
;
2447 za
= *ma
* a
+ *mc
* b
;
2448 zb
= *mb
* a
+ *md
* b
;
2449 zc
= *ma
* c
+ *mc
* d
;
2450 zd
= *mb
* c
+ *md
* d
;
2451 ze
= *ma
* e
+ *mc
* f
+ *me
;
2452 zf
= *mb
* e
+ *md
* f
+ *mf
;
2465 * Add a path to the svgtiny_diagram.
2468 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
2469 struct svgtiny_parse_state
*state
)
2471 struct svgtiny_shape
*shape
;
2473 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
2474 return svgtiny_add_path_linear_gradient(p
, n
, state
);
2476 svgtiny_transform_path(p
, n
, state
);
2478 shape
= svgtiny_add_shape(state
);
2481 return svgtiny_OUT_OF_MEMORY
;
2484 shape
->path_length
= n
;
2485 state
->diagram
->shape_count
++;
2492 * Add a svgtiny_shape to the svgtiny_diagram.
2495 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
2497 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
2498 (state
->diagram
->shape_count
+ 1) *
2499 sizeof (state
->diagram
->shape
[0]));
2502 state
->diagram
->shape
= shape
;
2504 shape
+= state
->diagram
->shape_count
;
2506 shape
->path_length
= 0;
2508 shape
->fill
= state
->fill
;
2509 shape
->stroke
= state
->stroke
;
2510 shape
->fill_opacity
= state
->fill_opacity
;
2511 shape
->stroke_opacity
= state
->stroke_opacity
;
2512 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
2513 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
2514 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
2515 shape
->stroke_width
= 1;
2522 * Apply the current transformation matrix to a path.
2525 void svgtiny_transform_path(float *p
, unsigned int n
,
2526 struct svgtiny_parse_state
*state
)
2530 for (j
= 0; j
!= n
; ) {
2531 unsigned int points
= 0;
2533 switch ((int) p
[j
]) {
2534 case svgtiny_PATH_MOVE
:
2535 case svgtiny_PATH_LINE
:
2538 case svgtiny_PATH_CLOSE
:
2541 case svgtiny_PATH_BEZIER
:
2548 for (k
= 0; k
!= points
; k
++) {
2549 float x0
= p
[j
], y0
= p
[j
+ 1];
2550 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
2552 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
2563 * Free all memory used by a diagram.
2566 void svgtiny_free(struct svgtiny_diagram
*svg
)
2571 for (i
= 0; i
!= svg
->shape_count
; i
++) {
2572 free(svg
->shape
[i
].path
);
2573 free(svg
->shape
[i
].text
);
2581 #ifndef HAVE_STRNDUP
2582 char *svgtiny_strndup(const char *s
, size_t n
)
2587 for (len
= 0; len
!= n
&& s
[len
]; len
++)
2590 s2
= malloc(len
+ 1);