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 svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
46 struct svgtiny_parse_state state
);
47 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
48 struct svgtiny_parse_state state
);
49 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
50 struct svgtiny_parse_state state
);
51 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
52 struct svgtiny_parse_state state
);
53 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
54 struct svgtiny_parse_state state
);
55 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
56 struct svgtiny_parse_state state
);
57 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
58 struct svgtiny_parse_state state
);
59 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
60 struct svgtiny_parse_state state
, bool polygon
);
61 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
62 struct svgtiny_parse_state state
);
63 static void svgtiny_parse_position_attributes(dom_element
*node
,
64 const struct svgtiny_parse_state state
,
65 float *x
, float *y
, float *width
, float *height
);
66 static void svgtiny_parse_paint_attributes(dom_element
*node
,
67 struct svgtiny_parse_state
*state
);
68 static void svgtiny_parse_font_attributes(dom_element
*node
,
69 struct svgtiny_parse_state
*state
);
70 static void svgtiny_parse_transform_attributes(dom_element
*node
,
71 struct svgtiny_parse_state
*state
);
72 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
73 struct svgtiny_parse_state
*state
);
74 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
75 struct svgtiny_parse_state_gradient
*grad
,
76 struct svgtiny_parse_state
*state
);
79 * rotate midpoint vector
82 rotate_midpoint_vector(float ax
, float ay
,
85 double *x_out
, double *y_out
)
87 double dx2
; /* midpoint x coordinate */
88 double dy2
; /* midpoint y coordinate */
89 double cosangle
; /* cosine of rotation angle */
90 double sinangle
; /* sine of rotation angle */
92 /* compute the sin and cos of the angle */
93 cosangle
= cos(radangle
);
94 sinangle
= sin(radangle
);
96 /* compute the midpoint between start and end points */
97 dx2
= (ax
- bx
) / 2.0;
98 dy2
= (ay
- by
) / 2.0;
100 /* rotate vector to remove angle */
101 *x_out
= ((cosangle
* dx2
) + (sinangle
* dy2
));
102 *y_out
= ((-sinangle
* dx2
) + (cosangle
* dy2
));
107 * ensure the arc radii are large enough and scale as appropriate
109 * the radii need to be large enough if they are not they must be
110 * adjusted. This allows for elimination of differences between
111 * implementations especialy with rounding.
114 ensure_radii_scale(double x1_sq
, double y1_sq
,
115 float *rx
, float *ry
,
116 double *rx_sq
, double *ry_sq
)
121 /* set radii square values */
122 (*rx_sq
) = (*rx
) * (*rx
);
123 (*ry_sq
) = (*ry
) * (*ry
);
125 radiisum
= (x1_sq
/ (*rx_sq
)) + (y1_sq
/ (*ry_sq
));
126 if (radiisum
> 0.99999) {
127 /* need to scale radii */
128 radiiscale
= sqrt(radiisum
) * 1.00001;
129 *rx
= (float)(radiiscale
* (*rx
));
130 *ry
= (float)(radiiscale
* (*ry
));
131 /* update squares too */
132 (*rx_sq
) = (*rx
) * (*rx
);
133 (*ry_sq
) = (*ry
) * (*ry
);
139 * compute the transformed centre point
142 compute_transformed_centre_point(double sign
, float rx
, float ry
,
143 double rx_sq
, double ry_sq
,
144 double x1
, double y1
,
145 double x1_sq
, double y1_sq
,
146 double *cx1
, double *cy1
)
150 sq
= ((rx_sq
* ry_sq
) - (rx_sq
* y1_sq
) - (ry_sq
* x1_sq
)) /
151 ((rx_sq
* y1_sq
) + (ry_sq
* x1_sq
));
152 sq
= (sq
< 0) ? 0 : sq
;
154 coef
= (sign
* sqrt(sq
));
156 *cx1
= coef
* ((rx
* y1
) / ry
);
157 *cy1
= coef
* -((ry
* x1
) / rx
);
162 * compute untransformed centre point
164 * \param ax The first point x coordinate
165 * \param ay The first point y coordinate
166 * \param bx The second point x coordinate
167 * \param ay The second point y coordinate
170 compute_centre_point(float ax
, float ay
,
172 double cx1
, double cy1
,
174 double *x_out
, double *y_out
)
178 double cosangle
; /* cosine of rotation angle */
179 double sinangle
; /* sine of rotation angle */
181 /* compute the sin and cos of the angle */
182 cosangle
= cos(radangle
);
183 sinangle
= sin(radangle
);
185 sx2
= (ax
+ bx
) / 2.0;
186 sy2
= (ay
+ by
) / 2.0;
188 *x_out
= sx2
+ (cosangle
* cx1
- sinangle
* cy1
);
189 *y_out
= sy2
+ (sinangle
* cx1
+ cosangle
* cy1
);
194 * compute the angle start and extent
197 compute_angle_start_extent(float rx
, float ry
,
198 double x1
, double y1
,
199 double cx1
, double cy1
,
200 double *start
, double *extent
)
211 * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v))
213 * '.' is the dot product.
214 * +/- is calculated from the sign of the cross product (u x v)
217 ux
= (x1
- cx1
) / rx
;
218 uy
= (y1
- cy1
) / ry
;
219 vx
= (-x1
- cx1
) / rx
;
220 vy
= (-y1
- cy1
) / ry
;
222 /* compute the start angle */
223 /* The angle between (ux, uy) and the 0 angle */
225 /* len(u) * len(1,0) == len(u) */
226 n
= sqrt((ux
* ux
) + (uy
* uy
));
227 /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */
229 /* u x v == (1 * uy - ux * 0) == uy */
230 sign
= (uy
< 0) ? -1.0 : 1.0;
231 /* (p >= n) so safe */
232 *start
= sign
* acos(p
/ n
);
234 /* compute the extent angle */
235 n
= sqrt(((ux
* ux
) + (uy
* uy
)) * ((vx
* vx
) + (vy
* vy
)));
236 p
= (ux
* vx
) + (uy
* vy
);
237 sign
= ((ux
* vy
) - (uy
* vx
) < 0) ? -1.0f
: 1.0f
;
239 /* arc cos must operate between -1 and 1 */
242 *extent
= sign
* M_PI
;
243 } else if (actmp
> 1.0) {
246 *extent
= sign
* acos(actmp
);
252 * converts a circle centered unit circle arc to a series of bezier curves
254 * Each bezier is stored as six values of three pairs of coordinates
256 * The beziers are stored without their start point as that is assumed
257 * to be the preceding elements end point.
259 * \param start The start angle of the arc (in radians)
260 * \param extent The size of the arc (in radians)
261 * \param bzpt The array to store the bezier values in
262 * \return The number of bezier segments output (max 4)
265 circle_arc_to_bezier(double start
, double extent
, double *bzpt
)
275 bzsegments
= (int) ceil(fabs(extent
) / M_PI_2
);
276 increment
= extent
/ bzsegments
;
277 controllen
= 4.0 / 3.0 * sin(increment
/ 2.0) / (1.0 + cos(increment
/ 2.0));
279 for (segment
= 0; segment
< bzsegments
; segment
++) {
280 /* first control point */
281 angle
= start
+ (segment
* increment
);
284 bzpt
[pos
++] = dx
- controllen
* dy
;
285 bzpt
[pos
++] = dy
+ controllen
* dx
;
286 /* second control point */
290 bzpt
[pos
++] = dx
+ controllen
* dy
;
291 bzpt
[pos
++] = dy
- controllen
* dx
;
302 * transform coordinate list
304 * perform a scale, rotate and translate on list of coordinates
310 * homogeneous transforms
318 * | cos(an) -sin(an) 0 |
319 * R = | sin(an) cos(an) 0 |
322 * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}}
329 * note order is significat here and the combined matrix is
332 * | cos(an) -sin(an) cx |
333 * T.R = | sin(an) cos(an) cy |
336 * | rx * cos(an) ry * -sin(an) cx |
337 * T.R.S = | rx * sin(an) ry * cos(an) cy |
340 * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}}
352 * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a))
353 * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a))
356 * \param rx X scaling to apply
357 * \param ry Y scaling to apply
358 * \param radangle rotation to apply (in radians)
359 * \param cx X translation to apply
360 * \param cy Y translation to apply
361 * \param points The size of the bzpoints array
362 * \param bzpoints an array of x,y values to apply the transform to
365 scale_rotate_translate_points(double rx
, double ry
,
367 double cx
, double cy
,
372 double cosangle
; /* cosine of rotation angle */
373 double sinangle
; /* sine of rotation angle */
374 double rxcosangle
, rxsinangle
, rycosangle
, rynsinangle
;
377 /* compute the sin and cos of the angle */
378 cosangle
= cos(radangle
);
379 sinangle
= sin(radangle
);
381 rxcosangle
= rx
* cosangle
;
382 rxsinangle
= rx
* sinangle
;
383 rycosangle
= ry
* cosangle
;
384 rynsinangle
= ry
* -1 * sinangle
;
386 for (pnt
= 0; pnt
< pntsize
; pnt
+=2) {
387 x2
= cx
+ (points
[pnt
] * rxcosangle
) + (points
[pnt
+ 1] * rynsinangle
);
388 y2
= cy
+ (points
[pnt
+ 1] * rycosangle
) + (points
[pnt
] * rxsinangle
);
390 points
[pnt
+ 1] = y2
;
396 * convert an svg path arc to a bezier curve
398 * This function perfoms a transform on the nine arc parameters
399 * (coordinate pairs for start and end together with the radii of the
400 * elipse, the rotation angle and which of the four arcs to draw)
401 * which generates the parameters (coordinate pairs for start,
402 * end and their control points) for a set of up to four bezier curves.
404 * Obviously the start and end coordinates are not altered between
405 * representations so the aim is to calculate the coordinate pairs for
406 * the bezier control points.
408 * \param bzpoints the array to fill with bezier curves
409 * \return the number of bezier segments generated or -1 for a line
412 svgarc_to_bezier(float start_x
,
423 double radangle
; /* normalised elipsis rotation angle in radians */
424 double rx_sq
; /* x radius squared */
425 double ry_sq
; /* y radius squared */
426 double x1
, y1
; /* rotated midpoint vector */
427 double x1_sq
, y1_sq
; /* x1 vector squared */
428 double cx1
,cy1
; /* transformed circle center */
429 double cx
,cy
; /* circle center */
430 double start
, extent
;
433 if ((start_x
== end_x
) && (start_y
== end_y
)) {
435 * if the start and end coordinates are the same the
436 * svg spec says this is equivalent to having no segment
442 if ((rx
== 0) || (ry
== 0)) {
444 * if either radii is zero the specified behaviour is a line
449 /* obtain the absolute values of the radii */
453 /* convert normalised angle to radians */
454 radangle
= degToRad(fmod(angle
, 360.0));
457 /* x1,x2 is the midpoint vector rotated to remove the arc angle */
458 rotate_midpoint_vector(start_x
, start_y
, end_x
, end_y
, radangle
, &x1
, &y1
);
461 /* get squared x1 values */
465 /* ensure radii are correctly scaled */
466 ensure_radii_scale(x1_sq
, y1_sq
, &rx
, &ry
, &rx_sq
, &ry_sq
);
468 /* compute the transformed centre point */
469 compute_transformed_centre_point(largearc
== sweep
?-1:1,
477 /* get the untransformed centre point */
478 compute_centre_point(start_x
, start_y
,
485 /* compute anglestart and extent */
486 compute_angle_start_extent(rx
,ry
,
491 /* extent of 0 is a straight line */
496 /* take account of sweep */
497 if (!sweep
&& extent
> 0) {
499 } else if (sweep
&& extent
< 0) {
503 /* normalise start and extent */
504 extent
= fmod(extent
, TAU
);
505 start
= fmod(start
, TAU
);
507 /* convert the arc to unit circle bezier curves */
508 bzsegments
= circle_arc_to_bezier(start
, extent
, bzpoints
);
510 /* transform the bezier curves */
511 scale_rotate_translate_points(rx
, ry
,
522 * Call this to ref the strings in a gradient state.
524 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
526 if (grad
->gradient_x1
!= NULL
) {
527 dom_string_ref(grad
->gradient_x1
);
529 if (grad
->gradient_y1
!= NULL
) {
530 dom_string_ref(grad
->gradient_y1
);
532 if (grad
->gradient_x2
!= NULL
) {
533 dom_string_ref(grad
->gradient_x2
);
535 if (grad
->gradient_y2
!= NULL
) {
536 dom_string_ref(grad
->gradient_y2
);
541 * Call this to clean up the strings in a gradient state.
543 static void svgtiny_grad_string_cleanup(
544 struct svgtiny_parse_state_gradient
*grad
)
546 if (grad
->gradient_x1
!= NULL
) {
547 dom_string_unref(grad
->gradient_x1
);
548 grad
->gradient_x1
= NULL
;
550 if (grad
->gradient_y1
!= NULL
) {
551 dom_string_unref(grad
->gradient_y1
);
552 grad
->gradient_y1
= NULL
;
554 if (grad
->gradient_x2
!= NULL
) {
555 dom_string_unref(grad
->gradient_x2
);
556 grad
->gradient_x2
= NULL
;
558 if (grad
->gradient_y2
!= NULL
) {
559 dom_string_unref(grad
->gradient_y2
);
560 grad
->gradient_y2
= NULL
;
565 * Set the local externally-stored parts of a parse state.
566 * Call this in functions that made a new state on the stack.
567 * Doesn't make own copy of global state, such as the interned string list.
569 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
571 svgtiny_grad_string_ref(&(state
->fill_grad
));
572 svgtiny_grad_string_ref(&(state
->stroke_grad
));
576 * Cleanup the local externally-stored parts of a parse state.
577 * Call this in functions that made a new state on the stack.
578 * Doesn't cleanup global state, such as the interned string list.
580 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
582 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
583 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
588 * Create a new svgtiny_diagram structure.
591 struct svgtiny_diagram
*svgtiny_create(void)
593 struct svgtiny_diagram
*diagram
;
595 diagram
= calloc(sizeof(*diagram
), 1);
604 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
612 * Parse a block of memory into a svgtiny_diagram.
615 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
616 const char *buffer
, size_t size
, const char *url
,
617 int viewport_width
, int viewport_height
)
620 dom_document
*document
;
622 dom_xml_parser
*parser
;
625 dom_string
*svg_name
;
626 lwc_string
*svg_name_lwc
;
627 struct svgtiny_parse_state state
;
628 float x
, y
, width
, height
;
637 parser
= dom_xml_parser_create(NULL
, NULL
,
638 ignore_msg
, NULL
, &document
);
641 return svgtiny_LIBDOM_ERROR
;
643 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
644 if (err
!= DOM_XML_OK
) {
645 dom_node_unref(document
);
646 dom_xml_parser_destroy(parser
);
647 return svgtiny_LIBDOM_ERROR
;
650 err
= dom_xml_parser_completed(parser
);
651 if (err
!= DOM_XML_OK
) {
652 dom_node_unref(document
);
653 dom_xml_parser_destroy(parser
);
654 return svgtiny_LIBDOM_ERROR
;
657 /* We're done parsing, drop the parser.
658 * We now own the document entirely.
660 dom_xml_parser_destroy(parser
);
662 /* find root <svg> element */
663 exc
= dom_document_get_document_element(document
, &svg
);
664 if (exc
!= DOM_NO_ERR
) {
665 dom_node_unref(document
);
666 return svgtiny_LIBDOM_ERROR
;
669 /* no root svg element */
670 dom_node_unref(document
);
671 return svgtiny_SVG_ERROR
;
674 exc
= dom_node_get_node_name(svg
, &svg_name
);
675 if (exc
!= DOM_NO_ERR
) {
677 dom_node_unref(document
);
678 return svgtiny_LIBDOM_ERROR
;
680 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
681 &svg_name_lwc
) != lwc_error_ok
) {
682 dom_string_unref(svg_name
);
684 dom_node_unref(document
);
685 return svgtiny_LIBDOM_ERROR
;
687 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
688 lwc_string_unref(svg_name_lwc
);
689 dom_string_unref(svg_name
);
691 dom_node_unref(document
);
692 return svgtiny_NOT_SVG
;
695 lwc_string_unref(svg_name_lwc
);
696 dom_string_unref(svg_name
);
698 /* get graphic dimensions */
699 memset(&state
, 0, sizeof(state
));
700 state
.diagram
= diagram
;
701 state
.document
= document
;
702 state
.viewport_width
= viewport_width
;
703 state
.viewport_height
= viewport_height
;
706 /* Initialize CSS context */
707 if (state
.select_ctx
== NULL
) {
708 css_code
= css_select_ctx_create(&state
.select_ctx
);
709 if (css_code
!= CSS_OK
) {
711 dom_node_unref(document
);
712 return svgtiny_LIBCSS_ERROR
;
716 #define SVGTINY_STRING_ACTION2(s,n) \
717 if (dom_string_create_interned((const uint8_t *) #n, \
718 strlen(#n), &state.interned_##s) \
720 code = svgtiny_LIBDOM_ERROR; \
723 #include "svgtiny_strings.h"
724 #undef SVGTINY_STRING_ACTION2
726 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
727 diagram
->width
= width
;
728 diagram
->height
= height
;
730 /* set up parsing state */
731 state
.viewport_width
= width
;
732 state
.viewport_height
= height
;
733 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
736 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
737 state
.ctm
.e
= 0; /*x;*/
738 state
.ctm
.f
= 0; /*y;*/
739 state
.fill
= 0x000000;
740 state
.stroke
= svgtiny_TRANSPARENT
;
741 state
.stroke_width
= 1;
744 code
= svgtiny_preparse_styles(svg
, state
);
745 if (code
== svgtiny_OK
) {
746 code
= svgtiny_parse_svg(svg
, state
);
750 dom_node_unref(document
);
751 css_code
= css_select_ctx_destroy(state
.select_ctx
);
752 if (css_code
!= CSS_OK
) {
753 code
= svgtiny_LIBCSS_ERROR
;
757 svgtiny_cleanup_state_local(&state
);
758 #define SVGTINY_STRING_ACTION2(s,n) \
759 if (state.interned_##s != NULL) \
760 dom_string_unref(state.interned_##s);
761 #include "svgtiny_strings.h"
762 #undef SVGTINY_STRING_ACTION2
768 * Parse a single <style> element, appending the result to the CSS
769 * select context within the given parser state.
771 svgtiny_code
svgtiny_parse_style_element(dom_element
*style
,
772 struct svgtiny_parse_state state
)
774 css_stylesheet
*sheet
;
775 css_stylesheet_params params
;
779 params
.params_version
= CSS_STYLESHEET_PARAMS_VERSION_1
;
780 params
.level
= CSS_LEVEL_DEFAULT
;
781 params
.charset
= NULL
;
784 params
.allow_quirks
= false;
785 params
.inline_style
= false;
786 params
.resolve
= svgtiny_resolve_url
;
787 params
.resolve_pw
= NULL
;
788 params
.import = NULL
;
789 params
.import_pw
= NULL
;
791 params
.color_pw
= NULL
;
793 params
.font_pw
= NULL
;
795 code
= css_stylesheet_create(¶ms
, &sheet
);
796 if (code
!= CSS_OK
) {
797 return svgtiny_LIBCSS_ERROR
;
800 /* Parse the style element's "media" attribute if it has
801 one. We don't do anything with it right now. */
802 dom_string
*media_attr
;
803 exc
= dom_element_get_attribute(style
, state
.interned_media
,
805 if (exc
!= DOM_NO_ERR
) {
806 css_stylesheet_destroy(sheet
);
807 return svgtiny_LIBDOM_ERROR
;
811 /* Here's where we'd actually change the media type if
812 we were going to use it */
813 dom_string_unref(media_attr
);
817 dom_node_get_text_content(style
, &data
);
819 /* Empty stylesheet? That's fine. */
820 css_stylesheet_destroy(sheet
);
824 code
= css_stylesheet_append_data(sheet
,
825 (uint8_t *)dom_string_data(data
),
826 dom_string_byte_length(data
));
827 if (code
!= CSS_OK
&& code
!= CSS_NEEDDATA
) {
828 dom_string_unref(data
);
829 css_stylesheet_destroy(sheet
);
830 return svgtiny_LIBCSS_ERROR
;
833 code
= css_stylesheet_data_done(sheet
);
834 if (code
!= CSS_OK
) {
835 dom_string_unref(data
);
836 css_stylesheet_destroy(sheet
);
837 return svgtiny_LIBCSS_ERROR
;
840 code
= css_select_ctx_append_sheet(state
.select_ctx
,
844 if (code
!= CSS_OK
) {
845 dom_string_unref(data
);
846 return svgtiny_LIBCSS_ERROR
;
849 dom_string_unref(data
);
855 * Parse all <style> elements within a root <svg> element. This
856 * should be called before svgtiny_parse_svg() because that function
857 * makes a single pass through the document and we'd like all style
858 * information to be available during that pass. Specifically, we'd
859 * like a <style> sheet at the end of the document to affect the
860 * rendering of elements at its beginning.
862 * The element-parsing inner loop here is essentially the same as
863 * that within svgtiny_parse_svg().
865 svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
866 struct svgtiny_parse_state state
)
871 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
872 if (exc
!= DOM_NO_ERR
) {
873 return svgtiny_LIBDOM_ERROR
;
875 while (child
!= NULL
) {
877 dom_node_type nodetype
;
878 svgtiny_code code
= svgtiny_OK
;
880 exc
= dom_node_get_node_type(child
, &nodetype
);
881 if (exc
!= DOM_NO_ERR
) {
882 dom_node_unref(child
);
883 return svgtiny_LIBDOM_ERROR
;
885 if (nodetype
== DOM_ELEMENT_NODE
) {
886 dom_string
*nodename
;
887 exc
= dom_node_get_node_name(child
, &nodename
);
888 if (exc
!= DOM_NO_ERR
) {
889 dom_node_unref(child
);
890 return svgtiny_LIBDOM_ERROR
;
893 if (dom_string_caseless_isequal(state
.interned_style
,
895 /* We have a <style> element, parse it */
896 code
= svgtiny_parse_style_element(child
,
901 dom_string_unref(nodename
);
903 if (code
!= svgtiny_OK
) {
904 dom_node_unref(child
);
907 exc
= dom_node_get_next_sibling(child
,
908 (dom_node
**) (void *) &next
);
909 dom_node_unref(child
);
910 if (exc
!= DOM_NO_ERR
) {
911 return svgtiny_LIBDOM_ERROR
;
920 * Parse a <svg> or <g> element node.
923 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
924 struct svgtiny_parse_state state
)
926 float x
, y
, width
, height
;
927 dom_string
*view_box
;
931 svgtiny_setup_state_local(&state
);
933 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
934 svgtiny_parse_paint_attributes(svg
, &state
);
935 svgtiny_parse_font_attributes(svg
, &state
);
937 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
939 if (exc
!= DOM_NO_ERR
) {
940 svgtiny_cleanup_state_local(&state
);
941 return svgtiny_LIBDOM_ERROR
;
945 char *s
= strndup(dom_string_data(view_box
),
946 dom_string_byte_length(view_box
));
947 float min_x
, min_y
, vwidth
, vheight
;
948 if (sscanf(s
, "%f,%f,%f,%f",
949 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
950 sscanf(s
, "%f %f %f %f",
951 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
952 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
953 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
954 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
955 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
958 dom_string_unref(view_box
);
961 svgtiny_parse_transform_attributes(svg
, &state
);
963 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
964 if (exc
!= DOM_NO_ERR
) {
965 svgtiny_cleanup_state_local(&state
);
966 return svgtiny_LIBDOM_ERROR
;
968 while (child
!= NULL
) {
970 dom_node_type nodetype
;
971 svgtiny_code code
= svgtiny_OK
;
973 exc
= dom_node_get_node_type(child
, &nodetype
);
974 if (exc
!= DOM_NO_ERR
) {
975 dom_node_unref(child
);
976 return svgtiny_LIBDOM_ERROR
;
978 if (nodetype
== DOM_ELEMENT_NODE
) {
979 dom_string
*nodename
;
980 exc
= dom_node_get_node_name(child
, &nodename
);
981 if (exc
!= DOM_NO_ERR
) {
982 dom_node_unref(child
);
983 svgtiny_cleanup_state_local(&state
);
984 return svgtiny_LIBDOM_ERROR
;
986 if (dom_string_caseless_isequal(state
.interned_svg
,
988 code
= svgtiny_parse_svg(child
, state
);
989 else if (dom_string_caseless_isequal(state
.interned_g
,
991 code
= svgtiny_parse_svg(child
, state
);
992 else if (dom_string_caseless_isequal(state
.interned_a
,
994 code
= svgtiny_parse_svg(child
, state
);
995 else if (dom_string_caseless_isequal(state
.interned_path
,
997 code
= svgtiny_parse_path(child
, state
);
998 else if (dom_string_caseless_isequal(state
.interned_rect
,
1000 code
= svgtiny_parse_rect(child
, state
);
1001 else if (dom_string_caseless_isequal(state
.interned_circle
,
1003 code
= svgtiny_parse_circle(child
, state
);
1004 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
1006 code
= svgtiny_parse_ellipse(child
, state
);
1007 else if (dom_string_caseless_isequal(state
.interned_line
,
1009 code
= svgtiny_parse_line(child
, state
);
1010 else if (dom_string_caseless_isequal(state
.interned_polyline
,
1012 code
= svgtiny_parse_poly(child
, state
, false);
1013 else if (dom_string_caseless_isequal(state
.interned_polygon
,
1015 code
= svgtiny_parse_poly(child
, state
, true);
1016 else if (dom_string_caseless_isequal(state
.interned_text
,
1018 code
= svgtiny_parse_text(child
, state
);
1019 dom_string_unref(nodename
);
1021 if (code
!= svgtiny_OK
) {
1022 dom_node_unref(child
);
1023 svgtiny_cleanup_state_local(&state
);
1026 exc
= dom_node_get_next_sibling(child
,
1027 (dom_node
**) (void *) &next
);
1028 dom_node_unref(child
);
1029 if (exc
!= DOM_NO_ERR
) {
1030 svgtiny_cleanup_state_local(&state
);
1031 return svgtiny_LIBDOM_ERROR
;
1036 svgtiny_cleanup_state_local(&state
);
1043 * Parse a <path> element node.
1045 * http://www.w3.org/TR/SVG11/paths#PathElement
1048 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
1049 struct svgtiny_parse_state state
)
1052 dom_string
*path_d_str
;
1055 float *p
; /* path elemets */
1056 unsigned int palloc
; /* number of path elements allocated */
1058 float last_x
= 0, last_y
= 0;
1059 float last_cubic_x
= 0, last_cubic_y
= 0;
1060 float last_quad_x
= 0, last_quad_y
= 0;
1061 float subpath_first_x
= 0, subpath_first_y
= 0;
1063 svgtiny_setup_state_local(&state
);
1065 svgtiny_parse_paint_attributes(path
, &state
);
1066 svgtiny_parse_transform_attributes(path
, &state
);
1068 /* read d attribute */
1069 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
1070 if (exc
!= DOM_NO_ERR
) {
1071 state
.diagram
->error_line
= -1; /* path->line; */
1072 state
.diagram
->error_message
= "path: error retrieving d attribute";
1073 svgtiny_cleanup_state_local(&state
);
1074 return svgtiny_SVG_ERROR
;
1077 if (path_d_str
== NULL
) {
1078 state
.diagram
->error_line
= -1; /* path->line; */
1079 state
.diagram
->error_message
= "path: missing d attribute";
1080 svgtiny_cleanup_state_local(&state
);
1081 return svgtiny_SVG_ERROR
;
1084 /* empty path is permitted it just disables the path */
1085 palloc
= dom_string_byte_length(path_d_str
);
1087 dom_string_unref(path_d_str
);
1088 svgtiny_cleanup_state_local(&state
);
1092 /* local copy of the path data allowing in-place modification */
1093 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
1094 dom_string_unref(path_d_str
);
1096 svgtiny_cleanup_state_local(&state
);
1097 return svgtiny_OUT_OF_MEMORY
;
1100 /* ensure path element allocation is sensibly bounded */
1103 } else if (palloc
> 64) {
1104 palloc
= palloc
/ 2;
1107 /* allocate initial space for path elements */
1108 p
= malloc(sizeof p
[0] * palloc
);
1111 svgtiny_cleanup_state_local(&state
);
1112 return svgtiny_OUT_OF_MEMORY
;
1115 /* parse d and build path */
1116 for (i
= 0; s
[i
]; i
++)
1123 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
1126 /* Ensure there is sufficient space for path elements */
1127 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
1129 if ((palloc - i) < NUM_ELEMENTS) { \
1131 palloc = (palloc * 2) + (palloc / 2); \
1132 tp = realloc(p, sizeof p[0] * palloc); \
1136 svgtiny_cleanup_state_local(&state); \
1137 return svgtiny_OUT_OF_MEMORY; \
1144 /* moveto (M, m), lineto (L, l) (2 arguments) */
1145 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
1146 /*LOG(("moveto or lineto"));*/
1147 if (*command
== 'M' || *command
== 'm')
1148 plot_command
= svgtiny_PATH_MOVE
;
1150 plot_command
= svgtiny_PATH_LINE
;
1152 ALLOC_PATH_ELEMENTS(3);
1153 p
[i
++] = plot_command
;
1154 if ('a' <= *command
) {
1158 if (plot_command
== svgtiny_PATH_MOVE
) {
1159 subpath_first_x
= x
;
1160 subpath_first_y
= y
;
1162 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1164 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1167 plot_command
= svgtiny_PATH_LINE
;
1168 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
1170 /* closepath (Z, z) (no arguments) */
1171 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
1172 /*LOG(("closepath"));*/
1173 ALLOC_PATH_ELEMENTS(1);
1175 p
[i
++] = svgtiny_PATH_CLOSE
;
1177 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
1178 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
1180 /* horizontal lineto (H, h) (1 argument) */
1181 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
1182 /*LOG(("horizontal lineto"));*/
1184 ALLOC_PATH_ELEMENTS(3);
1186 p
[i
++] = svgtiny_PATH_LINE
;
1187 if (*command
== 'h')
1189 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1191 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
1193 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
1195 /* vertical lineto (V, v) (1 argument) */
1196 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
1197 /*LOG(("vertical lineto"));*/
1199 ALLOC_PATH_ELEMENTS(3);
1201 p
[i
++] = svgtiny_PATH_LINE
;
1202 if (*command
== 'v')
1204 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
1205 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1208 } while (sscanf(s
, "%f %n", &y
, &n
) == 1);
1210 /* curveto (C, c) (6 arguments) */
1211 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
1212 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
1213 /*LOG(("curveto"));*/
1215 ALLOC_PATH_ELEMENTS(7);
1217 p
[i
++] = svgtiny_PATH_BEZIER
;
1218 if (*command
== 'c') {
1228 p
[i
++] = last_cubic_x
= x2
;
1229 p
[i
++] = last_cubic_y
= y2
;
1230 p
[i
++] = last_quad_x
= last_x
= x
;
1231 p
[i
++] = last_quad_y
= last_y
= y
;
1233 } while (sscanf(s
, "%f %f %f %f %f %f %n",
1234 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
1236 /* shorthand/smooth curveto (S, s) (4 arguments) */
1237 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
1238 &x2
, &y2
, &x
, &y
, &n
) == 5) {
1239 /*LOG(("shorthand/smooth curveto"));*/
1241 ALLOC_PATH_ELEMENTS(7);
1243 p
[i
++] = svgtiny_PATH_BEZIER
;
1244 x1
= last_x
+ (last_x
- last_cubic_x
);
1245 y1
= last_y
+ (last_y
- last_cubic_y
);
1246 if (*command
== 's') {
1254 p
[i
++] = last_cubic_x
= x2
;
1255 p
[i
++] = last_cubic_y
= y2
;
1256 p
[i
++] = last_quad_x
= last_x
= x
;
1257 p
[i
++] = last_quad_y
= last_y
= y
;
1259 } while (sscanf(s
, "%f %f %f %f %n",
1260 &x2
, &y2
, &x
, &y
, &n
) == 4);
1262 /* quadratic Bezier curveto (Q, q) (4 arguments) */
1263 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
1264 &x1
, &y1
, &x
, &y
, &n
) == 5) {
1265 /*LOG(("quadratic Bezier curveto"));*/
1267 ALLOC_PATH_ELEMENTS(7);
1269 p
[i
++] = svgtiny_PATH_BEZIER
;
1272 if (*command
== 'q') {
1278 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1279 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1280 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1281 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1282 p
[i
++] = last_cubic_x
= last_x
= x
;
1283 p
[i
++] = last_cubic_y
= last_y
= y
;
1285 } while (sscanf(s
, "%f %f %f %f %n",
1286 &x1
, &y1
, &x
, &y
, &n
) == 4);
1288 /* shorthand/smooth quadratic Bezier curveto (T, t)
1290 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
1292 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
1294 ALLOC_PATH_ELEMENTS(7);
1296 p
[i
++] = svgtiny_PATH_BEZIER
;
1297 x1
= last_x
+ (last_x
- last_quad_x
);
1298 y1
= last_y
+ (last_y
- last_quad_y
);
1301 if (*command
== 't') {
1307 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1308 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1309 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1310 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1311 p
[i
++] = last_cubic_x
= last_x
= x
;
1312 p
[i
++] = last_cubic_y
= last_y
= y
;
1314 } while (sscanf(s
, "%f %f %n",
1317 /* elliptical arc (A, a) (7 arguments) */
1318 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
1319 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1323 double bzpoints
[6*4]; /* allow for up to four bezier segments per arc */
1325 if (*command
== 'a') {
1330 bzsegments
= svgarc_to_bezier(last_x
, last_y
,
1337 if (bzsegments
== -1) {
1339 ALLOC_PATH_ELEMENTS(3);
1340 p
[i
++] = svgtiny_PATH_LINE
;
1343 } else if (bzsegments
> 0) {
1345 ALLOC_PATH_ELEMENTS((unsigned int)bzsegments
* 7);
1346 for (bzpnt
= 0;bzpnt
< (bzsegments
* 6); bzpnt
+=6) {
1347 p
[i
++] = svgtiny_PATH_BEZIER
;
1348 p
[i
++] = bzpoints
[bzpnt
];
1349 p
[i
++] = bzpoints
[bzpnt
+1];
1350 p
[i
++] = bzpoints
[bzpnt
+2];
1351 p
[i
++] = bzpoints
[bzpnt
+3];
1352 p
[i
++] = bzpoints
[bzpnt
+4];
1353 p
[i
++] = bzpoints
[bzpnt
+5];
1356 if (bzsegments
!= 0) {
1357 last_cubic_x
= last_quad_x
= last_x
= p
[i
-2];
1358 last_cubic_y
= last_quad_y
= last_y
= p
[i
-1];
1363 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
1364 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1368 /* fprintf(stderr, "parse failed at \"%s\"\n", s); */
1376 /* no real segments in path */
1378 svgtiny_cleanup_state_local(&state
);
1382 /* resize path element array to not be over allocated */
1386 /* try the resize, if it fails just continue to use the old
1389 tp
= realloc(p
, sizeof p
[0] * i
);
1395 err
= svgtiny_add_path(p
, i
, &state
);
1397 svgtiny_cleanup_state_local(&state
);
1404 * Parse a <rect> element node.
1406 * http://www.w3.org/TR/SVG11/shapes#RectElement
1409 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
1410 struct svgtiny_parse_state state
)
1413 float x
, y
, width
, height
;
1416 svgtiny_setup_state_local(&state
);
1418 svgtiny_parse_position_attributes(rect
, state
,
1419 &x
, &y
, &width
, &height
);
1420 svgtiny_parse_paint_attributes(rect
, &state
);
1421 svgtiny_parse_transform_attributes(rect
, &state
);
1423 p
= malloc(13 * sizeof p
[0]);
1425 svgtiny_cleanup_state_local(&state
);
1426 return svgtiny_OUT_OF_MEMORY
;
1429 p
[0] = svgtiny_PATH_MOVE
;
1432 p
[3] = svgtiny_PATH_LINE
;
1435 p
[6] = svgtiny_PATH_LINE
;
1438 p
[9] = svgtiny_PATH_LINE
;
1441 p
[12] = svgtiny_PATH_CLOSE
;
1443 err
= svgtiny_add_path(p
, 13, &state
);
1445 svgtiny_cleanup_state_local(&state
);
1452 * Parse a <circle> element node.
1455 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
1456 struct svgtiny_parse_state state
)
1459 float x
= 0, y
= 0, r
= -1;
1464 svgtiny_setup_state_local(&state
);
1466 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
1467 if (exc
!= DOM_NO_ERR
) {
1468 svgtiny_cleanup_state_local(&state
);
1469 return svgtiny_LIBDOM_ERROR
;
1472 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1474 dom_string_unref(attr
);
1476 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
1477 if (exc
!= DOM_NO_ERR
) {
1478 svgtiny_cleanup_state_local(&state
);
1479 return svgtiny_LIBDOM_ERROR
;
1482 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1484 dom_string_unref(attr
);
1486 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
1487 if (exc
!= DOM_NO_ERR
) {
1488 svgtiny_cleanup_state_local(&state
);
1489 return svgtiny_LIBDOM_ERROR
;
1492 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1494 dom_string_unref(attr
);
1496 svgtiny_parse_paint_attributes(circle
, &state
);
1497 svgtiny_parse_transform_attributes(circle
, &state
);
1500 state
.diagram
->error_line
= -1; /* circle->line; */
1501 state
.diagram
->error_message
= "circle: r missing or negative";
1502 svgtiny_cleanup_state_local(&state
);
1503 return svgtiny_SVG_ERROR
;
1506 svgtiny_cleanup_state_local(&state
);
1510 p
= malloc(32 * sizeof p
[0]);
1512 svgtiny_cleanup_state_local(&state
);
1513 return svgtiny_OUT_OF_MEMORY
;
1516 p
[0] = svgtiny_PATH_MOVE
;
1519 p
[3] = svgtiny_PATH_BEZIER
;
1521 p
[5] = y
+ r
* KAPPA
;
1522 p
[6] = x
+ r
* KAPPA
;
1526 p
[10] = svgtiny_PATH_BEZIER
;
1527 p
[11] = x
- r
* KAPPA
;
1530 p
[14] = y
+ r
* KAPPA
;
1533 p
[17] = svgtiny_PATH_BEZIER
;
1535 p
[19] = y
- r
* KAPPA
;
1536 p
[20] = x
- r
* KAPPA
;
1540 p
[24] = svgtiny_PATH_BEZIER
;
1541 p
[25] = x
+ r
* KAPPA
;
1544 p
[28] = y
- r
* KAPPA
;
1547 p
[31] = svgtiny_PATH_CLOSE
;
1549 err
= svgtiny_add_path(p
, 32, &state
);
1551 svgtiny_cleanup_state_local(&state
);
1558 * Parse an <ellipse> element node.
1561 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
1562 struct svgtiny_parse_state state
)
1565 float x
= 0, y
= 0, rx
= -1, ry
= -1;
1570 svgtiny_setup_state_local(&state
);
1572 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
1573 if (exc
!= DOM_NO_ERR
) {
1574 svgtiny_cleanup_state_local(&state
);
1575 return svgtiny_LIBDOM_ERROR
;
1578 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1580 dom_string_unref(attr
);
1582 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
1583 if (exc
!= DOM_NO_ERR
) {
1584 svgtiny_cleanup_state_local(&state
);
1585 return svgtiny_LIBDOM_ERROR
;
1588 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1590 dom_string_unref(attr
);
1592 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
1593 if (exc
!= DOM_NO_ERR
) {
1594 svgtiny_cleanup_state_local(&state
);
1595 return svgtiny_LIBDOM_ERROR
;
1598 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1600 dom_string_unref(attr
);
1602 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
1603 if (exc
!= DOM_NO_ERR
) {
1604 svgtiny_cleanup_state_local(&state
);
1605 return svgtiny_LIBDOM_ERROR
;
1608 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1610 dom_string_unref(attr
);
1612 svgtiny_parse_paint_attributes(ellipse
, &state
);
1613 svgtiny_parse_transform_attributes(ellipse
, &state
);
1615 if (rx
< 0 || ry
< 0) {
1616 state
.diagram
->error_line
= -1; /* ellipse->line; */
1617 state
.diagram
->error_message
= "ellipse: rx or ry missing "
1619 svgtiny_cleanup_state_local(&state
);
1620 return svgtiny_SVG_ERROR
;
1622 if (rx
== 0 || ry
== 0) {
1623 svgtiny_cleanup_state_local(&state
);
1627 p
= malloc(32 * sizeof p
[0]);
1629 svgtiny_cleanup_state_local(&state
);
1630 return svgtiny_OUT_OF_MEMORY
;
1633 p
[0] = svgtiny_PATH_MOVE
;
1636 p
[3] = svgtiny_PATH_BEZIER
;
1638 p
[5] = y
+ ry
* KAPPA
;
1639 p
[6] = x
+ rx
* KAPPA
;
1643 p
[10] = svgtiny_PATH_BEZIER
;
1644 p
[11] = x
- rx
* KAPPA
;
1647 p
[14] = y
+ ry
* KAPPA
;
1650 p
[17] = svgtiny_PATH_BEZIER
;
1652 p
[19] = y
- ry
* KAPPA
;
1653 p
[20] = x
- rx
* KAPPA
;
1657 p
[24] = svgtiny_PATH_BEZIER
;
1658 p
[25] = x
+ rx
* KAPPA
;
1661 p
[28] = y
- ry
* KAPPA
;
1664 p
[31] = svgtiny_PATH_CLOSE
;
1666 err
= svgtiny_add_path(p
, 32, &state
);
1668 svgtiny_cleanup_state_local(&state
);
1675 * Parse a <line> element node.
1678 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1679 struct svgtiny_parse_state state
)
1682 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1687 svgtiny_setup_state_local(&state
);
1689 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1690 if (exc
!= DOM_NO_ERR
) {
1691 svgtiny_cleanup_state_local(&state
);
1692 return svgtiny_LIBDOM_ERROR
;
1695 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1697 dom_string_unref(attr
);
1699 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1700 if (exc
!= DOM_NO_ERR
) {
1701 svgtiny_cleanup_state_local(&state
);
1702 return svgtiny_LIBDOM_ERROR
;
1705 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1707 dom_string_unref(attr
);
1709 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1710 if (exc
!= DOM_NO_ERR
) {
1711 svgtiny_cleanup_state_local(&state
);
1712 return svgtiny_LIBDOM_ERROR
;
1715 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1717 dom_string_unref(attr
);
1719 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1720 if (exc
!= DOM_NO_ERR
) {
1721 svgtiny_cleanup_state_local(&state
);
1722 return svgtiny_LIBDOM_ERROR
;
1725 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1727 dom_string_unref(attr
);
1729 svgtiny_parse_paint_attributes(line
, &state
);
1730 svgtiny_parse_transform_attributes(line
, &state
);
1732 p
= malloc(7 * sizeof p
[0]);
1734 svgtiny_cleanup_state_local(&state
);
1735 return svgtiny_OUT_OF_MEMORY
;
1738 p
[0] = svgtiny_PATH_MOVE
;
1741 p
[3] = svgtiny_PATH_LINE
;
1744 p
[6] = svgtiny_PATH_CLOSE
;
1746 err
= svgtiny_add_path(p
, 7, &state
);
1748 svgtiny_cleanup_state_local(&state
);
1755 * Parse a <polyline> or <polygon> element node.
1757 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1758 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1761 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1762 struct svgtiny_parse_state state
, bool polygon
)
1765 dom_string
*points_str
;
1771 svgtiny_setup_state_local(&state
);
1773 svgtiny_parse_paint_attributes(poly
, &state
);
1774 svgtiny_parse_transform_attributes(poly
, &state
);
1776 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1778 if (exc
!= DOM_NO_ERR
) {
1779 svgtiny_cleanup_state_local(&state
);
1780 return svgtiny_LIBDOM_ERROR
;
1783 if (points_str
== NULL
) {
1784 state
.diagram
->error_line
= -1; /* poly->line; */
1785 state
.diagram
->error_message
=
1786 "polyline/polygon: missing points attribute";
1787 svgtiny_cleanup_state_local(&state
);
1788 return svgtiny_SVG_ERROR
;
1791 s
= points
= strndup(dom_string_data(points_str
),
1792 dom_string_byte_length(points_str
));
1793 dom_string_unref(points_str
);
1794 /* read points attribute */
1796 svgtiny_cleanup_state_local(&state
);
1797 return svgtiny_OUT_OF_MEMORY
;
1799 /* allocate space for path: it will never have more elements than s */
1800 p
= malloc(sizeof p
[0] * strlen(s
));
1803 svgtiny_cleanup_state_local(&state
);
1804 return svgtiny_OUT_OF_MEMORY
;
1807 /* parse s and build path */
1808 for (i
= 0; s
[i
]; i
++)
1816 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1818 p
[i
++] = svgtiny_PATH_MOVE
;
1820 p
[i
++] = svgtiny_PATH_LINE
;
1829 p
[i
++] = svgtiny_PATH_CLOSE
;
1833 err
= svgtiny_add_path(p
, i
, &state
);
1835 svgtiny_cleanup_state_local(&state
);
1842 * Parse a <text> or <tspan> element node.
1845 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1846 struct svgtiny_parse_state state
)
1848 float x
, y
, width
, height
;
1853 svgtiny_setup_state_local(&state
);
1855 svgtiny_parse_position_attributes(text
, state
,
1856 &x
, &y
, &width
, &height
);
1857 svgtiny_parse_font_attributes(text
, &state
);
1858 svgtiny_parse_transform_attributes(text
, &state
);
1860 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1861 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1862 /* state.ctm.e = px - state.origin_x; */
1863 /* state.ctm.f = py - state.origin_y; */
1865 exc
= dom_node_get_first_child(text
, &child
);
1866 if (exc
!= DOM_NO_ERR
) {
1867 return svgtiny_LIBDOM_ERROR
;
1868 svgtiny_cleanup_state_local(&state
);
1870 while (child
!= NULL
) {
1872 dom_node_type nodetype
;
1873 svgtiny_code code
= svgtiny_OK
;
1875 exc
= dom_node_get_node_type(child
, &nodetype
);
1876 if (exc
!= DOM_NO_ERR
) {
1877 dom_node_unref(child
);
1878 svgtiny_cleanup_state_local(&state
);
1879 return svgtiny_LIBDOM_ERROR
;
1881 if (nodetype
== DOM_ELEMENT_NODE
) {
1882 dom_string
*nodename
;
1883 exc
= dom_node_get_node_name(child
, &nodename
);
1884 if (exc
!= DOM_NO_ERR
) {
1885 dom_node_unref(child
);
1886 svgtiny_cleanup_state_local(&state
);
1887 return svgtiny_LIBDOM_ERROR
;
1889 if (dom_string_caseless_isequal(nodename
,
1890 state
.interned_tspan
))
1891 code
= svgtiny_parse_text((dom_element
*)child
,
1893 dom_string_unref(nodename
);
1894 } else if (nodetype
== DOM_TEXT_NODE
) {
1895 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1896 dom_string
*content
;
1897 if (shape
== NULL
) {
1898 dom_node_unref(child
);
1899 svgtiny_cleanup_state_local(&state
);
1900 return svgtiny_OUT_OF_MEMORY
;
1902 exc
= dom_text_get_whole_text(child
, &content
);
1903 if (exc
!= DOM_NO_ERR
) {
1904 dom_node_unref(child
);
1905 svgtiny_cleanup_state_local(&state
);
1906 return svgtiny_LIBDOM_ERROR
;
1908 if (content
!= NULL
) {
1909 shape
->text
= strndup(dom_string_data(content
),
1910 dom_string_byte_length(content
));
1911 dom_string_unref(content
);
1913 shape
->text
= strdup("");
1917 state
.diagram
->shape_count
++;
1920 if (code
!= svgtiny_OK
) {
1921 dom_node_unref(child
);
1922 svgtiny_cleanup_state_local(&state
);
1925 exc
= dom_node_get_next_sibling(child
, &next
);
1926 dom_node_unref(child
);
1927 if (exc
!= DOM_NO_ERR
) {
1928 svgtiny_cleanup_state_local(&state
);
1929 return svgtiny_LIBDOM_ERROR
;
1934 svgtiny_cleanup_state_local(&state
);
1941 * Parse x, y, width, and height attributes, if present.
1944 void svgtiny_parse_position_attributes(dom_element
*node
,
1945 const struct svgtiny_parse_state state
,
1946 float *x
, float *y
, float *width
, float *height
)
1953 *width
= state
.viewport_width
;
1954 *height
= state
.viewport_height
;
1956 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1957 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1958 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1959 dom_string_unref(attr
);
1962 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1963 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1964 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1965 dom_string_unref(attr
);
1968 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1969 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1970 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1972 dom_string_unref(attr
);
1975 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1976 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1977 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1979 dom_string_unref(attr
);
1985 * Parse a length as a number of pixels.
1988 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1989 const struct svgtiny_parse_state state
)
1991 int num_length
= strspn(s
, "0123456789+-.");
1992 const char *unit
= s
+ num_length
;
1993 float n
= atof((const char *) s
);
1994 float font_size
= 20;
2000 } else if (unit
[0] == '%') {
2001 return n
/ 100.0 * viewport_size
;
2002 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
2003 return n
* font_size
;
2004 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
2005 return n
/ 2.0 * font_size
;
2006 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
2008 } else if (unit
[0] == 'p' && unit
[1] == 't') {
2010 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
2012 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
2013 return n
* 3.543307;
2014 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
2015 return n
* 35.43307;
2016 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
2023 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
2024 const struct svgtiny_parse_state state
)
2026 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
2027 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
2033 * Parse paint attributes, if present.
2036 void svgtiny_parse_paint_attributes(dom_element
*node
,
2037 struct svgtiny_parse_state
*state
)
2042 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
2043 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2044 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
2045 dom_string_unref(attr
);
2048 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
2049 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2050 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
2051 dom_string_unref(attr
);
2054 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
2055 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2056 state
->stroke_width
= svgtiny_parse_length(attr
,
2057 state
->viewport_width
, *state
);
2058 dom_string_unref(attr
);
2061 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
2062 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2063 char *style
= strndup(dom_string_data(attr
),
2064 dom_string_byte_length(attr
));
2067 if ((s
= strstr(style
, "fill:"))) {
2071 value
= strndup(s
, strcspn(s
, "; "));
2072 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
2075 if ((s
= strstr(style
, "stroke:"))) {
2079 value
= strndup(s
, strcspn(s
, "; "));
2080 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
2083 if ((s
= strstr(style
, "stroke-width:"))) {
2087 value
= strndup(s
, strcspn(s
, "; "));
2088 state
->stroke_width
= _svgtiny_parse_length(value
,
2089 state
->viewport_width
, *state
);
2093 dom_string_unref(attr
);
2102 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
2103 struct svgtiny_parse_state_gradient
*grad
,
2104 struct svgtiny_parse_state
*state
)
2106 unsigned int r
, g
, b
;
2108 size_t len
= strlen(s
);
2109 char *id
= 0, *rparen
;
2111 if (len
== 4 && s
[0] == '#') {
2112 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
2113 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
2115 } else if (len
== 7 && s
[0] == '#') {
2116 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
2117 *c
= svgtiny_RGB(r
, g
, b
);
2119 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
2120 s
[3] == '(' && s
[len
- 1] == ')') {
2121 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
2122 *c
= svgtiny_RGB(r
, g
, b
);
2123 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
2127 *c
= svgtiny_RGB(r
, g
, b
);
2130 } else if (len
== 4 && strcmp(s
, "none") == 0) {
2131 *c
= svgtiny_TRANSPARENT
;
2133 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
2136 *c
= svgtiny_RGB(0, 0, 0);
2137 } else if (s
[4] == '#') {
2141 rparen
= strchr(id
, ')');
2144 svgtiny_find_gradient(id
, grad
, state
);
2146 if (grad
->linear_gradient_stop_count
== 0)
2147 *c
= svgtiny_TRANSPARENT
;
2148 else if (grad
->linear_gradient_stop_count
== 1)
2149 *c
= grad
->gradient_stop
[0].color
;
2151 *c
= svgtiny_LINEAR_GRADIENT
;
2155 const struct svgtiny_named_color
*named_color
;
2156 named_color
= svgtiny_color_lookup(s
, strlen(s
));
2158 *c
= named_color
->color
;
2162 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
2163 struct svgtiny_parse_state_gradient
*grad
,
2164 struct svgtiny_parse_state
*state
)
2167 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
2168 dom_string_unref(s
);
2172 * Parse font attributes, if present.
2175 void svgtiny_parse_font_attributes(dom_element
*node
,
2176 struct svgtiny_parse_state
*state
)
2178 /* TODO: Implement this, it never used to be */
2181 #ifdef WRITTEN_THIS_PROPERLY
2182 const xmlAttr
*attr
;
2186 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
2187 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
2196 * Parse transform attributes, if present.
2198 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
2201 void svgtiny_parse_transform_attributes(dom_element
*node
,
2202 struct svgtiny_parse_state
*state
)
2208 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
2210 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2211 transform
= strndup(dom_string_data(attr
),
2212 dom_string_byte_length(attr
));
2213 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
2214 &state
->ctm
.c
, &state
->ctm
.d
,
2215 &state
->ctm
.e
, &state
->ctm
.f
);
2217 dom_string_unref(attr
);
2223 * Parse a transform string.
2226 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
2227 float *mc
, float *md
, float *me
, float *mf
)
2229 float a
, b
, c
, d
, e
, f
;
2230 float za
, zb
, zc
, zd
, ze
, zf
;
2235 for (i
= 0; s
[i
]; i
++)
2244 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
2245 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
2247 else if ((sscanf(s
, " translate (%f %f ) %n",
2248 &e
, &f
, &n
) == 2) && (n
> 0))
2250 else if ((sscanf(s
, " translate (%f ) %n",
2251 &e
, &n
) == 1) && (n
> 0))
2253 else if ((sscanf(s
, " scale (%f %f ) %n",
2254 &a
, &d
, &n
) == 2) && (n
> 0))
2256 else if ((sscanf(s
, " scale (%f ) %n",
2257 &a
, &n
) == 1) && (n
> 0))
2259 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
2260 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
2261 angle
= angle
/ 180 * M_PI
;
2266 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
2267 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
2268 } else if ((sscanf(s
, " rotate (%f ) %n",
2269 &angle
, &n
) == 1) && (n
> 0)) {
2270 angle
= angle
/ 180 * M_PI
;
2275 } else if ((sscanf(s
, " skewX (%f ) %n",
2276 &angle
, &n
) == 1) && (n
> 0)) {
2277 angle
= angle
/ 180 * M_PI
;
2279 } else if ((sscanf(s
, " skewY (%f ) %n",
2280 &angle
, &n
) == 1) && (n
> 0)) {
2281 angle
= angle
/ 180 * M_PI
;
2285 za
= *ma
* a
+ *mc
* b
;
2286 zb
= *mb
* a
+ *md
* b
;
2287 zc
= *ma
* c
+ *mc
* d
;
2288 zd
= *mb
* c
+ *md
* d
;
2289 ze
= *ma
* e
+ *mc
* f
+ *me
;
2290 zf
= *mb
* e
+ *md
* f
+ *mf
;
2303 * Add a path to the svgtiny_diagram.
2306 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
2307 struct svgtiny_parse_state
*state
)
2309 struct svgtiny_shape
*shape
;
2311 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
2312 return svgtiny_add_path_linear_gradient(p
, n
, state
);
2314 svgtiny_transform_path(p
, n
, state
);
2316 shape
= svgtiny_add_shape(state
);
2319 return svgtiny_OUT_OF_MEMORY
;
2322 shape
->path_length
= n
;
2323 state
->diagram
->shape_count
++;
2330 * Add a svgtiny_shape to the svgtiny_diagram.
2333 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
2335 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
2336 (state
->diagram
->shape_count
+ 1) *
2337 sizeof (state
->diagram
->shape
[0]));
2340 state
->diagram
->shape
= shape
;
2342 shape
+= state
->diagram
->shape_count
;
2344 shape
->path_length
= 0;
2346 shape
->fill
= state
->fill
;
2347 shape
->stroke
= state
->stroke
;
2348 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
2349 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
2350 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
2351 shape
->stroke_width
= 1;
2358 * Apply the current transformation matrix to a path.
2361 void svgtiny_transform_path(float *p
, unsigned int n
,
2362 struct svgtiny_parse_state
*state
)
2366 for (j
= 0; j
!= n
; ) {
2367 unsigned int points
= 0;
2369 switch ((int) p
[j
]) {
2370 case svgtiny_PATH_MOVE
:
2371 case svgtiny_PATH_LINE
:
2374 case svgtiny_PATH_CLOSE
:
2377 case svgtiny_PATH_BEZIER
:
2384 for (k
= 0; k
!= points
; k
++) {
2385 float x0
= p
[j
], y0
= p
[j
+ 1];
2386 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
2388 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
2399 * Free all memory used by a diagram.
2402 void svgtiny_free(struct svgtiny_diagram
*svg
)
2407 for (i
= 0; i
!= svg
->shape_count
; i
++) {
2408 free(svg
->shape
[i
].path
);
2409 free(svg
->shape
[i
].text
);
2417 #ifndef HAVE_STRNDUP
2418 char *svgtiny_strndup(const char *s
, size_t n
)
2423 for (len
= 0; len
!= n
&& s
[len
]; len
++)
2426 s2
= malloc(len
+ 1);