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_svg(dom_element
*svg
,
44 struct svgtiny_parse_state state
);
45 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
46 struct svgtiny_parse_state state
);
47 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
48 struct svgtiny_parse_state state
);
49 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
50 struct svgtiny_parse_state state
);
51 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
52 struct svgtiny_parse_state state
);
53 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
54 struct svgtiny_parse_state state
);
55 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
56 struct svgtiny_parse_state state
, bool polygon
);
57 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
58 struct svgtiny_parse_state state
);
59 static void svgtiny_parse_position_attributes(dom_element
*node
,
60 const struct svgtiny_parse_state state
,
61 float *x
, float *y
, float *width
, float *height
);
62 static void svgtiny_parse_paint_attributes(dom_element
*node
,
63 struct svgtiny_parse_state
*state
);
64 static void svgtiny_parse_font_attributes(dom_element
*node
,
65 struct svgtiny_parse_state
*state
);
66 static void svgtiny_parse_transform_attributes(dom_element
*node
,
67 struct svgtiny_parse_state
*state
);
68 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
69 struct svgtiny_parse_state
*state
);
70 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
71 struct svgtiny_parse_state_gradient
*grad
,
72 struct svgtiny_parse_state
*state
);
75 * rotate midpoint vector
78 rotate_midpoint_vector(float ax
, float ay
,
81 double *x_out
, double *y_out
)
83 double dx2
; /* midpoint x coordinate */
84 double dy2
; /* midpoint y coordinate */
85 double cosangle
; /* cosine of rotation angle */
86 double sinangle
; /* sine of rotation angle */
88 /* compute the sin and cos of the angle */
89 cosangle
= cos(radangle
);
90 sinangle
= sin(radangle
);
92 /* compute the midpoint between start and end points */
93 dx2
= (ax
- bx
) / 2.0;
94 dy2
= (ay
- by
) / 2.0;
96 /* rotate vector to remove angle */
97 *x_out
= ((cosangle
* dx2
) + (sinangle
* dy2
));
98 *y_out
= ((-sinangle
* dx2
) + (cosangle
* dy2
));
103 * ensure the arc radii are large enough and scale as appropriate
105 * the radii need to be large enough if they are not they must be
106 * adjusted. This allows for elimination of differences between
107 * implementations especialy with rounding.
110 ensure_radii_scale(double x1_sq
, double y1_sq
,
111 float *rx
, float *ry
,
112 double *rx_sq
, double *ry_sq
)
117 /* set radii square values */
118 (*rx_sq
) = (*rx
) * (*rx
);
119 (*ry_sq
) = (*ry
) * (*ry
);
121 radiisum
= (x1_sq
/ (*rx_sq
)) + (y1_sq
/ (*ry_sq
));
122 if (radiisum
> 0.99999) {
123 /* need to scale radii */
124 radiiscale
= sqrt(radiisum
) * 1.00001;
125 *rx
= (float)(radiiscale
* (*rx
));
126 *ry
= (float)(radiiscale
* (*ry
));
127 /* update squares too */
128 (*rx_sq
) = (*rx
) * (*rx
);
129 (*ry_sq
) = (*ry
) * (*ry
);
135 * compute the transformed centre point
138 compute_transformed_centre_point(double sign
, float rx
, float ry
,
139 double rx_sq
, double ry_sq
,
140 double x1
, double y1
,
141 double x1_sq
, double y1_sq
,
142 double *cx1
, double *cy1
)
146 sq
= ((rx_sq
* ry_sq
) - (rx_sq
* y1_sq
) - (ry_sq
* x1_sq
)) /
147 ((rx_sq
* y1_sq
) + (ry_sq
* x1_sq
));
148 sq
= (sq
< 0) ? 0 : sq
;
150 coef
= (sign
* sqrt(sq
));
152 *cx1
= coef
* ((rx
* y1
) / ry
);
153 *cy1
= coef
* -((ry
* x1
) / rx
);
158 * compute untransformed centre point
160 * \param ax The first point x coordinate
161 * \param ay The first point y coordinate
162 * \param bx The second point x coordinate
163 * \param ay The second point y coordinate
166 compute_centre_point(float ax
, float ay
,
168 double cx1
, double cy1
,
170 double *x_out
, double *y_out
)
174 double cosangle
; /* cosine of rotation angle */
175 double sinangle
; /* sine of rotation angle */
177 /* compute the sin and cos of the angle */
178 cosangle
= cos(radangle
);
179 sinangle
= sin(radangle
);
181 sx2
= (ax
+ bx
) / 2.0;
182 sy2
= (ay
+ by
) / 2.0;
184 *x_out
= sx2
+ (cosangle
* cx1
- sinangle
* cy1
);
185 *y_out
= sy2
+ (sinangle
* cx1
+ cosangle
* cy1
);
190 * compute the angle start and extent
193 compute_angle_start_extent(float rx
, float ry
,
194 double x1
, double y1
,
195 double cx1
, double cy1
,
196 double *start
, double *extent
)
207 * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v))
209 * '.' is the dot product.
210 * +/- is calculated from the sign of the cross product (u x v)
213 ux
= (x1
- cx1
) / rx
;
214 uy
= (y1
- cy1
) / ry
;
215 vx
= (-x1
- cx1
) / rx
;
216 vy
= (-y1
- cy1
) / ry
;
218 /* compute the start angle */
219 /* The angle between (ux, uy) and the 0 angle */
221 /* len(u) * len(1,0) == len(u) */
222 n
= sqrt((ux
* ux
) + (uy
* uy
));
223 /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */
225 /* u x v == (1 * uy - ux * 0) == uy */
226 sign
= (uy
< 0) ? -1.0 : 1.0;
227 /* (p >= n) so safe */
228 *start
= sign
* acos(p
/ n
);
230 /* compute the extent angle */
231 n
= sqrt(((ux
* ux
) + (uy
* uy
)) * ((vx
* vx
) + (vy
* vy
)));
232 p
= (ux
* vx
) + (uy
* vy
);
233 sign
= ((ux
* vy
) - (uy
* vx
) < 0) ? -1.0f
: 1.0f
;
235 /* arc cos must operate between -1 and 1 */
238 *extent
= sign
* M_PI
;
239 } else if (actmp
> 1.0) {
242 *extent
= sign
* acos(actmp
);
248 * converts a circle centered unit circle arc to a series of bezier curves
250 * Each bezier is stored as six values of three pairs of coordinates
252 * The beziers are stored without their start point as that is assumed
253 * to be the preceding elements end point.
255 * \param start The start angle of the arc (in radians)
256 * \param extent The size of the arc (in radians)
257 * \param bzpt The array to store the bezier values in
258 * \return The number of bezier segments output (max 4)
261 circle_arc_to_bezier(double start
, double extent
, double *bzpt
)
271 bzsegments
= (int) ceil(fabs(extent
) / M_PI_2
);
272 increment
= extent
/ bzsegments
;
273 controllen
= 4.0 / 3.0 * sin(increment
/ 2.0) / (1.0 + cos(increment
/ 2.0));
275 for (segment
= 0; segment
< bzsegments
; segment
++) {
276 /* first control point */
277 angle
= start
+ (segment
* increment
);
280 bzpt
[pos
++] = dx
- controllen
* dy
;
281 bzpt
[pos
++] = dy
+ controllen
* dx
;
282 /* second control point */
286 bzpt
[pos
++] = dx
+ controllen
* dy
;
287 bzpt
[pos
++] = dy
- controllen
* dx
;
298 * transform coordinate list
300 * perform a scale, rotate and translate on list of coordinates
306 * homogeneous transforms
314 * | cos(an) -sin(an) 0 |
315 * R = | sin(an) cos(an) 0 |
318 * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}}
325 * note order is significat here and the combined matrix is
328 * | cos(an) -sin(an) cx |
329 * T.R = | sin(an) cos(an) cy |
332 * | rx * cos(an) ry * -sin(an) cx |
333 * T.R.S = | rx * sin(an) ry * cos(an) cy |
336 * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}}
348 * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a))
349 * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a))
352 * \param rx X scaling to apply
353 * \param ry Y scaling to apply
354 * \param radangle rotation to apply (in radians)
355 * \param cx X translation to apply
356 * \param cy Y translation to apply
357 * \param points The size of the bzpoints array
358 * \param bzpoints an array of x,y values to apply the transform to
361 scale_rotate_translate_points(double rx
, double ry
,
363 double cx
, double cy
,
368 double cosangle
; /* cosine of rotation angle */
369 double sinangle
; /* sine of rotation angle */
370 double rxcosangle
, rxsinangle
, rycosangle
, rynsinangle
;
373 /* compute the sin and cos of the angle */
374 cosangle
= cos(radangle
);
375 sinangle
= sin(radangle
);
377 rxcosangle
= rx
* cosangle
;
378 rxsinangle
= rx
* sinangle
;
379 rycosangle
= ry
* cosangle
;
380 rynsinangle
= ry
* -1 * sinangle
;
382 for (pnt
= 0; pnt
< pntsize
; pnt
+=2) {
383 x2
= cx
+ (points
[pnt
] * rxcosangle
) + (points
[pnt
+ 1] * rynsinangle
);
384 y2
= cy
+ (points
[pnt
+ 1] * rycosangle
) + (points
[pnt
] * rxsinangle
);
386 points
[pnt
+ 1] = y2
;
392 * convert an svg path arc to a bezier curve
394 * This function perfoms a transform on the nine arc parameters
395 * (coordinate pairs for start and end together with the radii of the
396 * elipse, the rotation angle and which of the four arcs to draw)
397 * which generates the parameters (coordinate pairs for start,
398 * end and their control points) for a set of up to four bezier curves.
400 * Obviously the start and end coordinates are not altered between
401 * representations so the aim is to calculate the coordinate pairs for
402 * the bezier control points.
404 * \param bzpoints the array to fill with bezier curves
405 * \return the number of bezier segments generated or -1 for a line
408 svgarc_to_bezier(float start_x
,
419 double radangle
; /* normalised elipsis rotation angle in radians */
420 double rx_sq
; /* x radius squared */
421 double ry_sq
; /* y radius squared */
422 double x1
, y1
; /* rotated midpoint vector */
423 double x1_sq
, y1_sq
; /* x1 vector squared */
424 double cx1
,cy1
; /* transformed circle center */
425 double cx
,cy
; /* circle center */
426 double start
, extent
;
429 if ((start_x
== end_x
) && (start_y
== end_y
)) {
431 * if the start and end coordinates are the same the
432 * svg spec says this is equivalent to having no segment
438 if ((rx
== 0) || (ry
== 0)) {
440 * if either radii is zero the specified behaviour is a line
445 /* obtain the absolute values of the radii */
449 /* convert normalised angle to radians */
450 radangle
= degToRad(fmod(angle
, 360.0));
453 /* x1,x2 is the midpoint vector rotated to remove the arc angle */
454 rotate_midpoint_vector(start_x
, start_y
, end_x
, end_y
, radangle
, &x1
, &y1
);
457 /* get squared x1 values */
461 /* ensure radii are correctly scaled */
462 ensure_radii_scale(x1_sq
, y1_sq
, &rx
, &ry
, &rx_sq
, &ry_sq
);
464 /* compute the transformed centre point */
465 compute_transformed_centre_point(largearc
== sweep
?-1:1,
473 /* get the untransformed centre point */
474 compute_centre_point(start_x
, start_y
,
481 /* compute anglestart and extent */
482 compute_angle_start_extent(rx
,ry
,
487 /* extent of 0 is a straight line */
492 /* take account of sweep */
493 if (!sweep
&& extent
> 0) {
495 } else if (sweep
&& extent
< 0) {
499 /* normalise start and extent */
500 extent
= fmod(extent
, TAU
);
501 start
= fmod(start
, TAU
);
503 /* convert the arc to unit circle bezier curves */
504 bzsegments
= circle_arc_to_bezier(start
, extent
, bzpoints
);
506 /* transform the bezier curves */
507 scale_rotate_translate_points(rx
, ry
,
518 * Call this to ref the strings in a gradient state.
520 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
522 if (grad
->gradient_x1
!= NULL
) {
523 dom_string_ref(grad
->gradient_x1
);
525 if (grad
->gradient_y1
!= NULL
) {
526 dom_string_ref(grad
->gradient_y1
);
528 if (grad
->gradient_x2
!= NULL
) {
529 dom_string_ref(grad
->gradient_x2
);
531 if (grad
->gradient_y2
!= NULL
) {
532 dom_string_ref(grad
->gradient_y2
);
537 * Call this to clean up the strings in a gradient state.
539 static void svgtiny_grad_string_cleanup(
540 struct svgtiny_parse_state_gradient
*grad
)
542 if (grad
->gradient_x1
!= NULL
) {
543 dom_string_unref(grad
->gradient_x1
);
544 grad
->gradient_x1
= NULL
;
546 if (grad
->gradient_y1
!= NULL
) {
547 dom_string_unref(grad
->gradient_y1
);
548 grad
->gradient_y1
= NULL
;
550 if (grad
->gradient_x2
!= NULL
) {
551 dom_string_unref(grad
->gradient_x2
);
552 grad
->gradient_x2
= NULL
;
554 if (grad
->gradient_y2
!= NULL
) {
555 dom_string_unref(grad
->gradient_y2
);
556 grad
->gradient_y2
= NULL
;
561 * Set the local externally-stored parts of a parse state.
562 * Call this in functions that made a new state on the stack.
563 * Doesn't make own copy of global state, such as the interned string list.
565 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
567 svgtiny_grad_string_ref(&(state
->fill_grad
));
568 svgtiny_grad_string_ref(&(state
->stroke_grad
));
572 * Cleanup the local externally-stored parts of a parse state.
573 * Call this in functions that made a new state on the stack.
574 * Doesn't cleanup global state, such as the interned string list.
576 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
578 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
579 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
584 * Create a new svgtiny_diagram structure.
587 struct svgtiny_diagram
*svgtiny_create(void)
589 struct svgtiny_diagram
*diagram
;
591 diagram
= calloc(sizeof(*diagram
), 1);
600 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
608 * Parse a block of memory into a svgtiny_diagram.
611 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
612 const char *buffer
, size_t size
, const char *url
,
613 int viewport_width
, int viewport_height
)
616 dom_document
*document
;
618 dom_xml_parser
*parser
;
621 dom_string
*svg_name
;
622 lwc_string
*svg_name_lwc
;
623 struct svgtiny_parse_state state
;
624 float x
, y
, width
, height
;
633 parser
= dom_xml_parser_create(NULL
, NULL
,
634 ignore_msg
, NULL
, &document
);
637 return svgtiny_LIBDOM_ERROR
;
639 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
640 if (err
!= DOM_XML_OK
) {
641 dom_node_unref(document
);
642 dom_xml_parser_destroy(parser
);
643 return svgtiny_LIBDOM_ERROR
;
646 err
= dom_xml_parser_completed(parser
);
647 if (err
!= DOM_XML_OK
) {
648 dom_node_unref(document
);
649 dom_xml_parser_destroy(parser
);
650 return svgtiny_LIBDOM_ERROR
;
653 /* We're done parsing, drop the parser.
654 * We now own the document entirely.
656 dom_xml_parser_destroy(parser
);
658 /* find root <svg> element */
659 exc
= dom_document_get_document_element(document
, &svg
);
660 if (exc
!= DOM_NO_ERR
) {
661 dom_node_unref(document
);
662 return svgtiny_LIBDOM_ERROR
;
665 /* no root svg element */
666 dom_node_unref(document
);
667 return svgtiny_SVG_ERROR
;
670 exc
= dom_node_get_node_name(svg
, &svg_name
);
671 if (exc
!= DOM_NO_ERR
) {
673 dom_node_unref(document
);
674 return svgtiny_LIBDOM_ERROR
;
676 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
677 &svg_name_lwc
) != lwc_error_ok
) {
678 dom_string_unref(svg_name
);
680 dom_node_unref(document
);
681 return svgtiny_LIBDOM_ERROR
;
683 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
684 lwc_string_unref(svg_name_lwc
);
685 dom_string_unref(svg_name
);
687 dom_node_unref(document
);
688 return svgtiny_NOT_SVG
;
691 lwc_string_unref(svg_name_lwc
);
692 dom_string_unref(svg_name
);
694 /* get graphic dimensions */
695 memset(&state
, 0, sizeof(state
));
696 state
.diagram
= diagram
;
697 state
.document
= document
;
698 state
.viewport_width
= viewport_width
;
699 state
.viewport_height
= viewport_height
;
702 /* Initialize CSS context */
703 if (state
.select_ctx
== NULL
) {
704 css_code
= css_select_ctx_create(&state
.select_ctx
);
705 if (css_code
!= CSS_OK
) {
707 dom_node_unref(document
);
708 return svgtiny_LIBCSS_ERROR
;
712 #define SVGTINY_STRING_ACTION2(s,n) \
713 if (dom_string_create_interned((const uint8_t *) #n, \
714 strlen(#n), &state.interned_##s) \
716 code = svgtiny_LIBDOM_ERROR; \
719 #include "svgtiny_strings.h"
720 #undef SVGTINY_STRING_ACTION2
722 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
723 diagram
->width
= width
;
724 diagram
->height
= height
;
726 /* set up parsing state */
727 state
.viewport_width
= width
;
728 state
.viewport_height
= height
;
729 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
732 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
733 state
.ctm
.e
= 0; /*x;*/
734 state
.ctm
.f
= 0; /*y;*/
735 state
.fill
= 0x000000;
736 state
.stroke
= svgtiny_TRANSPARENT
;
737 state
.stroke_width
= 1;
740 code
= svgtiny_parse_svg(svg
, state
);
743 dom_node_unref(document
);
744 css_code
= css_select_ctx_destroy(state
.select_ctx
);
745 if (css_code
!= CSS_OK
) {
746 code
= svgtiny_LIBCSS_ERROR
;
750 svgtiny_cleanup_state_local(&state
);
751 #define SVGTINY_STRING_ACTION2(s,n) \
752 if (state.interned_##s != NULL) \
753 dom_string_unref(state.interned_##s);
754 #include "svgtiny_strings.h"
755 #undef SVGTINY_STRING_ACTION2
761 * Parse a <svg> or <g> element node.
764 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
765 struct svgtiny_parse_state state
)
767 float x
, y
, width
, height
;
768 dom_string
*view_box
;
772 svgtiny_setup_state_local(&state
);
774 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
775 svgtiny_parse_paint_attributes(svg
, &state
);
776 svgtiny_parse_font_attributes(svg
, &state
);
778 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
780 if (exc
!= DOM_NO_ERR
) {
781 svgtiny_cleanup_state_local(&state
);
782 return svgtiny_LIBDOM_ERROR
;
786 char *s
= strndup(dom_string_data(view_box
),
787 dom_string_byte_length(view_box
));
788 float min_x
, min_y
, vwidth
, vheight
;
789 if (sscanf(s
, "%f,%f,%f,%f",
790 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
791 sscanf(s
, "%f %f %f %f",
792 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
793 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
794 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
795 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
796 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
799 dom_string_unref(view_box
);
802 svgtiny_parse_transform_attributes(svg
, &state
);
804 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
805 if (exc
!= DOM_NO_ERR
) {
806 svgtiny_cleanup_state_local(&state
);
807 return svgtiny_LIBDOM_ERROR
;
809 while (child
!= NULL
) {
811 dom_node_type nodetype
;
812 svgtiny_code code
= svgtiny_OK
;
814 exc
= dom_node_get_node_type(child
, &nodetype
);
815 if (exc
!= DOM_NO_ERR
) {
816 dom_node_unref(child
);
817 return svgtiny_LIBDOM_ERROR
;
819 if (nodetype
== DOM_ELEMENT_NODE
) {
820 dom_string
*nodename
;
821 exc
= dom_node_get_node_name(child
, &nodename
);
822 if (exc
!= DOM_NO_ERR
) {
823 dom_node_unref(child
);
824 svgtiny_cleanup_state_local(&state
);
825 return svgtiny_LIBDOM_ERROR
;
827 if (dom_string_caseless_isequal(state
.interned_svg
,
829 code
= svgtiny_parse_svg(child
, state
);
830 else if (dom_string_caseless_isequal(state
.interned_g
,
832 code
= svgtiny_parse_svg(child
, state
);
833 else if (dom_string_caseless_isequal(state
.interned_a
,
835 code
= svgtiny_parse_svg(child
, state
);
836 else if (dom_string_caseless_isequal(state
.interned_path
,
838 code
= svgtiny_parse_path(child
, state
);
839 else if (dom_string_caseless_isequal(state
.interned_rect
,
841 code
= svgtiny_parse_rect(child
, state
);
842 else if (dom_string_caseless_isequal(state
.interned_circle
,
844 code
= svgtiny_parse_circle(child
, state
);
845 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
847 code
= svgtiny_parse_ellipse(child
, state
);
848 else if (dom_string_caseless_isequal(state
.interned_line
,
850 code
= svgtiny_parse_line(child
, state
);
851 else if (dom_string_caseless_isequal(state
.interned_polyline
,
853 code
= svgtiny_parse_poly(child
, state
, false);
854 else if (dom_string_caseless_isequal(state
.interned_polygon
,
856 code
= svgtiny_parse_poly(child
, state
, true);
857 else if (dom_string_caseless_isequal(state
.interned_text
,
859 code
= svgtiny_parse_text(child
, state
);
860 dom_string_unref(nodename
);
862 if (code
!= svgtiny_OK
) {
863 dom_node_unref(child
);
864 svgtiny_cleanup_state_local(&state
);
867 exc
= dom_node_get_next_sibling(child
,
868 (dom_node
**) (void *) &next
);
869 dom_node_unref(child
);
870 if (exc
!= DOM_NO_ERR
) {
871 svgtiny_cleanup_state_local(&state
);
872 return svgtiny_LIBDOM_ERROR
;
877 svgtiny_cleanup_state_local(&state
);
884 * Parse a <path> element node.
886 * http://www.w3.org/TR/SVG11/paths#PathElement
889 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
890 struct svgtiny_parse_state state
)
893 dom_string
*path_d_str
;
896 float *p
; /* path elemets */
897 unsigned int palloc
; /* number of path elements allocated */
899 float last_x
= 0, last_y
= 0;
900 float last_cubic_x
= 0, last_cubic_y
= 0;
901 float last_quad_x
= 0, last_quad_y
= 0;
902 float subpath_first_x
= 0, subpath_first_y
= 0;
904 svgtiny_setup_state_local(&state
);
906 svgtiny_parse_paint_attributes(path
, &state
);
907 svgtiny_parse_transform_attributes(path
, &state
);
909 /* read d attribute */
910 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
911 if (exc
!= DOM_NO_ERR
) {
912 state
.diagram
->error_line
= -1; /* path->line; */
913 state
.diagram
->error_message
= "path: error retrieving d attribute";
914 svgtiny_cleanup_state_local(&state
);
915 return svgtiny_SVG_ERROR
;
918 if (path_d_str
== NULL
) {
919 state
.diagram
->error_line
= -1; /* path->line; */
920 state
.diagram
->error_message
= "path: missing d attribute";
921 svgtiny_cleanup_state_local(&state
);
922 return svgtiny_SVG_ERROR
;
925 /* empty path is permitted it just disables the path */
926 palloc
= dom_string_byte_length(path_d_str
);
928 dom_string_unref(path_d_str
);
929 svgtiny_cleanup_state_local(&state
);
933 /* local copy of the path data allowing in-place modification */
934 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
935 dom_string_unref(path_d_str
);
937 svgtiny_cleanup_state_local(&state
);
938 return svgtiny_OUT_OF_MEMORY
;
941 /* ensure path element allocation is sensibly bounded */
944 } else if (palloc
> 64) {
948 /* allocate initial space for path elements */
949 p
= malloc(sizeof p
[0] * palloc
);
952 svgtiny_cleanup_state_local(&state
);
953 return svgtiny_OUT_OF_MEMORY
;
956 /* parse d and build path */
957 for (i
= 0; s
[i
]; i
++)
964 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
967 /* Ensure there is sufficient space for path elements */
968 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
970 if ((palloc - i) < NUM_ELEMENTS) { \
972 palloc = (palloc * 2) + (palloc / 2); \
973 tp = realloc(p, sizeof p[0] * palloc); \
977 svgtiny_cleanup_state_local(&state); \
978 return svgtiny_OUT_OF_MEMORY; \
985 /* moveto (M, m), lineto (L, l) (2 arguments) */
986 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
987 /*LOG(("moveto or lineto"));*/
988 if (*command
== 'M' || *command
== 'm')
989 plot_command
= svgtiny_PATH_MOVE
;
991 plot_command
= svgtiny_PATH_LINE
;
993 ALLOC_PATH_ELEMENTS(3);
994 p
[i
++] = plot_command
;
995 if ('a' <= *command
) {
999 if (plot_command
== svgtiny_PATH_MOVE
) {
1000 subpath_first_x
= x
;
1001 subpath_first_y
= y
;
1003 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1005 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1008 plot_command
= svgtiny_PATH_LINE
;
1009 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
1011 /* closepath (Z, z) (no arguments) */
1012 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
1013 /*LOG(("closepath"));*/
1014 ALLOC_PATH_ELEMENTS(1);
1016 p
[i
++] = svgtiny_PATH_CLOSE
;
1018 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
1019 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
1021 /* horizontal lineto (H, h) (1 argument) */
1022 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
1023 /*LOG(("horizontal lineto"));*/
1025 ALLOC_PATH_ELEMENTS(3);
1027 p
[i
++] = svgtiny_PATH_LINE
;
1028 if (*command
== 'h')
1030 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1032 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
1034 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
1036 /* vertical lineto (V, v) (1 argument) */
1037 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
1038 /*LOG(("vertical lineto"));*/
1040 ALLOC_PATH_ELEMENTS(3);
1042 p
[i
++] = svgtiny_PATH_LINE
;
1043 if (*command
== 'v')
1045 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
1046 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1049 } while (sscanf(s
, "%f %n", &y
, &n
) == 1);
1051 /* curveto (C, c) (6 arguments) */
1052 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
1053 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
1054 /*LOG(("curveto"));*/
1056 ALLOC_PATH_ELEMENTS(7);
1058 p
[i
++] = svgtiny_PATH_BEZIER
;
1059 if (*command
== 'c') {
1069 p
[i
++] = last_cubic_x
= x2
;
1070 p
[i
++] = last_cubic_y
= y2
;
1071 p
[i
++] = last_quad_x
= last_x
= x
;
1072 p
[i
++] = last_quad_y
= last_y
= y
;
1074 } while (sscanf(s
, "%f %f %f %f %f %f %n",
1075 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
1077 /* shorthand/smooth curveto (S, s) (4 arguments) */
1078 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
1079 &x2
, &y2
, &x
, &y
, &n
) == 5) {
1080 /*LOG(("shorthand/smooth curveto"));*/
1082 ALLOC_PATH_ELEMENTS(7);
1084 p
[i
++] = svgtiny_PATH_BEZIER
;
1085 x1
= last_x
+ (last_x
- last_cubic_x
);
1086 y1
= last_y
+ (last_y
- last_cubic_y
);
1087 if (*command
== 's') {
1095 p
[i
++] = last_cubic_x
= x2
;
1096 p
[i
++] = last_cubic_y
= y2
;
1097 p
[i
++] = last_quad_x
= last_x
= x
;
1098 p
[i
++] = last_quad_y
= last_y
= y
;
1100 } while (sscanf(s
, "%f %f %f %f %n",
1101 &x2
, &y2
, &x
, &y
, &n
) == 4);
1103 /* quadratic Bezier curveto (Q, q) (4 arguments) */
1104 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
1105 &x1
, &y1
, &x
, &y
, &n
) == 5) {
1106 /*LOG(("quadratic Bezier curveto"));*/
1108 ALLOC_PATH_ELEMENTS(7);
1110 p
[i
++] = svgtiny_PATH_BEZIER
;
1113 if (*command
== 'q') {
1119 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1120 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1121 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1122 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1123 p
[i
++] = last_cubic_x
= last_x
= x
;
1124 p
[i
++] = last_cubic_y
= last_y
= y
;
1126 } while (sscanf(s
, "%f %f %f %f %n",
1127 &x1
, &y1
, &x
, &y
, &n
) == 4);
1129 /* shorthand/smooth quadratic Bezier curveto (T, t)
1131 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
1133 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
1135 ALLOC_PATH_ELEMENTS(7);
1137 p
[i
++] = svgtiny_PATH_BEZIER
;
1138 x1
= last_x
+ (last_x
- last_quad_x
);
1139 y1
= last_y
+ (last_y
- last_quad_y
);
1142 if (*command
== 't') {
1148 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1149 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1150 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1151 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1152 p
[i
++] = last_cubic_x
= last_x
= x
;
1153 p
[i
++] = last_cubic_y
= last_y
= y
;
1155 } while (sscanf(s
, "%f %f %n",
1158 /* elliptical arc (A, a) (7 arguments) */
1159 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
1160 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1164 double bzpoints
[6*4]; /* allow for up to four bezier segments per arc */
1166 if (*command
== 'a') {
1171 bzsegments
= svgarc_to_bezier(last_x
, last_y
,
1178 if (bzsegments
== -1) {
1180 ALLOC_PATH_ELEMENTS(3);
1181 p
[i
++] = svgtiny_PATH_LINE
;
1184 } else if (bzsegments
> 0) {
1186 ALLOC_PATH_ELEMENTS((unsigned int)bzsegments
* 7);
1187 for (bzpnt
= 0;bzpnt
< (bzsegments
* 6); bzpnt
+=6) {
1188 p
[i
++] = svgtiny_PATH_BEZIER
;
1189 p
[i
++] = bzpoints
[bzpnt
];
1190 p
[i
++] = bzpoints
[bzpnt
+1];
1191 p
[i
++] = bzpoints
[bzpnt
+2];
1192 p
[i
++] = bzpoints
[bzpnt
+3];
1193 p
[i
++] = bzpoints
[bzpnt
+4];
1194 p
[i
++] = bzpoints
[bzpnt
+5];
1197 if (bzsegments
!= 0) {
1198 last_cubic_x
= last_quad_x
= last_x
= p
[i
-2];
1199 last_cubic_y
= last_quad_y
= last_y
= p
[i
-1];
1204 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
1205 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1209 /* fprintf(stderr, "parse failed at \"%s\"\n", s); */
1217 /* no real segments in path */
1219 svgtiny_cleanup_state_local(&state
);
1223 /* resize path element array to not be over allocated */
1227 /* try the resize, if it fails just continue to use the old
1230 tp
= realloc(p
, sizeof p
[0] * i
);
1236 err
= svgtiny_add_path(p
, i
, &state
);
1238 svgtiny_cleanup_state_local(&state
);
1245 * Parse a <rect> element node.
1247 * http://www.w3.org/TR/SVG11/shapes#RectElement
1250 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
1251 struct svgtiny_parse_state state
)
1254 float x
, y
, width
, height
;
1257 svgtiny_setup_state_local(&state
);
1259 svgtiny_parse_position_attributes(rect
, state
,
1260 &x
, &y
, &width
, &height
);
1261 svgtiny_parse_paint_attributes(rect
, &state
);
1262 svgtiny_parse_transform_attributes(rect
, &state
);
1264 p
= malloc(13 * sizeof p
[0]);
1266 svgtiny_cleanup_state_local(&state
);
1267 return svgtiny_OUT_OF_MEMORY
;
1270 p
[0] = svgtiny_PATH_MOVE
;
1273 p
[3] = svgtiny_PATH_LINE
;
1276 p
[6] = svgtiny_PATH_LINE
;
1279 p
[9] = svgtiny_PATH_LINE
;
1282 p
[12] = svgtiny_PATH_CLOSE
;
1284 err
= svgtiny_add_path(p
, 13, &state
);
1286 svgtiny_cleanup_state_local(&state
);
1293 * Parse a <circle> element node.
1296 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
1297 struct svgtiny_parse_state state
)
1300 float x
= 0, y
= 0, r
= -1;
1305 svgtiny_setup_state_local(&state
);
1307 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
1308 if (exc
!= DOM_NO_ERR
) {
1309 svgtiny_cleanup_state_local(&state
);
1310 return svgtiny_LIBDOM_ERROR
;
1313 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1315 dom_string_unref(attr
);
1317 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
1318 if (exc
!= DOM_NO_ERR
) {
1319 svgtiny_cleanup_state_local(&state
);
1320 return svgtiny_LIBDOM_ERROR
;
1323 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1325 dom_string_unref(attr
);
1327 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
1328 if (exc
!= DOM_NO_ERR
) {
1329 svgtiny_cleanup_state_local(&state
);
1330 return svgtiny_LIBDOM_ERROR
;
1333 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1335 dom_string_unref(attr
);
1337 svgtiny_parse_paint_attributes(circle
, &state
);
1338 svgtiny_parse_transform_attributes(circle
, &state
);
1341 state
.diagram
->error_line
= -1; /* circle->line; */
1342 state
.diagram
->error_message
= "circle: r missing or negative";
1343 svgtiny_cleanup_state_local(&state
);
1344 return svgtiny_SVG_ERROR
;
1347 svgtiny_cleanup_state_local(&state
);
1351 p
= malloc(32 * sizeof p
[0]);
1353 svgtiny_cleanup_state_local(&state
);
1354 return svgtiny_OUT_OF_MEMORY
;
1357 p
[0] = svgtiny_PATH_MOVE
;
1360 p
[3] = svgtiny_PATH_BEZIER
;
1362 p
[5] = y
+ r
* KAPPA
;
1363 p
[6] = x
+ r
* KAPPA
;
1367 p
[10] = svgtiny_PATH_BEZIER
;
1368 p
[11] = x
- r
* KAPPA
;
1371 p
[14] = y
+ r
* KAPPA
;
1374 p
[17] = svgtiny_PATH_BEZIER
;
1376 p
[19] = y
- r
* KAPPA
;
1377 p
[20] = x
- r
* KAPPA
;
1381 p
[24] = svgtiny_PATH_BEZIER
;
1382 p
[25] = x
+ r
* KAPPA
;
1385 p
[28] = y
- r
* KAPPA
;
1388 p
[31] = svgtiny_PATH_CLOSE
;
1390 err
= svgtiny_add_path(p
, 32, &state
);
1392 svgtiny_cleanup_state_local(&state
);
1399 * Parse an <ellipse> element node.
1402 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
1403 struct svgtiny_parse_state state
)
1406 float x
= 0, y
= 0, rx
= -1, ry
= -1;
1411 svgtiny_setup_state_local(&state
);
1413 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
1414 if (exc
!= DOM_NO_ERR
) {
1415 svgtiny_cleanup_state_local(&state
);
1416 return svgtiny_LIBDOM_ERROR
;
1419 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1421 dom_string_unref(attr
);
1423 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
1424 if (exc
!= DOM_NO_ERR
) {
1425 svgtiny_cleanup_state_local(&state
);
1426 return svgtiny_LIBDOM_ERROR
;
1429 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1431 dom_string_unref(attr
);
1433 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
1434 if (exc
!= DOM_NO_ERR
) {
1435 svgtiny_cleanup_state_local(&state
);
1436 return svgtiny_LIBDOM_ERROR
;
1439 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1441 dom_string_unref(attr
);
1443 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
1444 if (exc
!= DOM_NO_ERR
) {
1445 svgtiny_cleanup_state_local(&state
);
1446 return svgtiny_LIBDOM_ERROR
;
1449 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1451 dom_string_unref(attr
);
1453 svgtiny_parse_paint_attributes(ellipse
, &state
);
1454 svgtiny_parse_transform_attributes(ellipse
, &state
);
1456 if (rx
< 0 || ry
< 0) {
1457 state
.diagram
->error_line
= -1; /* ellipse->line; */
1458 state
.diagram
->error_message
= "ellipse: rx or ry missing "
1460 svgtiny_cleanup_state_local(&state
);
1461 return svgtiny_SVG_ERROR
;
1463 if (rx
== 0 || ry
== 0) {
1464 svgtiny_cleanup_state_local(&state
);
1468 p
= malloc(32 * sizeof p
[0]);
1470 svgtiny_cleanup_state_local(&state
);
1471 return svgtiny_OUT_OF_MEMORY
;
1474 p
[0] = svgtiny_PATH_MOVE
;
1477 p
[3] = svgtiny_PATH_BEZIER
;
1479 p
[5] = y
+ ry
* KAPPA
;
1480 p
[6] = x
+ rx
* KAPPA
;
1484 p
[10] = svgtiny_PATH_BEZIER
;
1485 p
[11] = x
- rx
* KAPPA
;
1488 p
[14] = y
+ ry
* KAPPA
;
1491 p
[17] = svgtiny_PATH_BEZIER
;
1493 p
[19] = y
- ry
* KAPPA
;
1494 p
[20] = x
- rx
* KAPPA
;
1498 p
[24] = svgtiny_PATH_BEZIER
;
1499 p
[25] = x
+ rx
* KAPPA
;
1502 p
[28] = y
- ry
* KAPPA
;
1505 p
[31] = svgtiny_PATH_CLOSE
;
1507 err
= svgtiny_add_path(p
, 32, &state
);
1509 svgtiny_cleanup_state_local(&state
);
1516 * Parse a <line> element node.
1519 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1520 struct svgtiny_parse_state state
)
1523 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1528 svgtiny_setup_state_local(&state
);
1530 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1531 if (exc
!= DOM_NO_ERR
) {
1532 svgtiny_cleanup_state_local(&state
);
1533 return svgtiny_LIBDOM_ERROR
;
1536 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1538 dom_string_unref(attr
);
1540 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1541 if (exc
!= DOM_NO_ERR
) {
1542 svgtiny_cleanup_state_local(&state
);
1543 return svgtiny_LIBDOM_ERROR
;
1546 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1548 dom_string_unref(attr
);
1550 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1551 if (exc
!= DOM_NO_ERR
) {
1552 svgtiny_cleanup_state_local(&state
);
1553 return svgtiny_LIBDOM_ERROR
;
1556 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1558 dom_string_unref(attr
);
1560 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1561 if (exc
!= DOM_NO_ERR
) {
1562 svgtiny_cleanup_state_local(&state
);
1563 return svgtiny_LIBDOM_ERROR
;
1566 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1568 dom_string_unref(attr
);
1570 svgtiny_parse_paint_attributes(line
, &state
);
1571 svgtiny_parse_transform_attributes(line
, &state
);
1573 p
= malloc(7 * sizeof p
[0]);
1575 svgtiny_cleanup_state_local(&state
);
1576 return svgtiny_OUT_OF_MEMORY
;
1579 p
[0] = svgtiny_PATH_MOVE
;
1582 p
[3] = svgtiny_PATH_LINE
;
1585 p
[6] = svgtiny_PATH_CLOSE
;
1587 err
= svgtiny_add_path(p
, 7, &state
);
1589 svgtiny_cleanup_state_local(&state
);
1596 * Parse a <polyline> or <polygon> element node.
1598 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1599 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1602 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1603 struct svgtiny_parse_state state
, bool polygon
)
1606 dom_string
*points_str
;
1612 svgtiny_setup_state_local(&state
);
1614 svgtiny_parse_paint_attributes(poly
, &state
);
1615 svgtiny_parse_transform_attributes(poly
, &state
);
1617 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1619 if (exc
!= DOM_NO_ERR
) {
1620 svgtiny_cleanup_state_local(&state
);
1621 return svgtiny_LIBDOM_ERROR
;
1624 if (points_str
== NULL
) {
1625 state
.diagram
->error_line
= -1; /* poly->line; */
1626 state
.diagram
->error_message
=
1627 "polyline/polygon: missing points attribute";
1628 svgtiny_cleanup_state_local(&state
);
1629 return svgtiny_SVG_ERROR
;
1632 s
= points
= strndup(dom_string_data(points_str
),
1633 dom_string_byte_length(points_str
));
1634 dom_string_unref(points_str
);
1635 /* read points attribute */
1637 svgtiny_cleanup_state_local(&state
);
1638 return svgtiny_OUT_OF_MEMORY
;
1640 /* allocate space for path: it will never have more elements than s */
1641 p
= malloc(sizeof p
[0] * strlen(s
));
1644 svgtiny_cleanup_state_local(&state
);
1645 return svgtiny_OUT_OF_MEMORY
;
1648 /* parse s and build path */
1649 for (i
= 0; s
[i
]; i
++)
1657 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1659 p
[i
++] = svgtiny_PATH_MOVE
;
1661 p
[i
++] = svgtiny_PATH_LINE
;
1670 p
[i
++] = svgtiny_PATH_CLOSE
;
1674 err
= svgtiny_add_path(p
, i
, &state
);
1676 svgtiny_cleanup_state_local(&state
);
1683 * Parse a <text> or <tspan> element node.
1686 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1687 struct svgtiny_parse_state state
)
1689 float x
, y
, width
, height
;
1694 svgtiny_setup_state_local(&state
);
1696 svgtiny_parse_position_attributes(text
, state
,
1697 &x
, &y
, &width
, &height
);
1698 svgtiny_parse_font_attributes(text
, &state
);
1699 svgtiny_parse_transform_attributes(text
, &state
);
1701 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1702 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1703 /* state.ctm.e = px - state.origin_x; */
1704 /* state.ctm.f = py - state.origin_y; */
1706 exc
= dom_node_get_first_child(text
, &child
);
1707 if (exc
!= DOM_NO_ERR
) {
1708 return svgtiny_LIBDOM_ERROR
;
1709 svgtiny_cleanup_state_local(&state
);
1711 while (child
!= NULL
) {
1713 dom_node_type nodetype
;
1714 svgtiny_code code
= svgtiny_OK
;
1716 exc
= dom_node_get_node_type(child
, &nodetype
);
1717 if (exc
!= DOM_NO_ERR
) {
1718 dom_node_unref(child
);
1719 svgtiny_cleanup_state_local(&state
);
1720 return svgtiny_LIBDOM_ERROR
;
1722 if (nodetype
== DOM_ELEMENT_NODE
) {
1723 dom_string
*nodename
;
1724 exc
= dom_node_get_node_name(child
, &nodename
);
1725 if (exc
!= DOM_NO_ERR
) {
1726 dom_node_unref(child
);
1727 svgtiny_cleanup_state_local(&state
);
1728 return svgtiny_LIBDOM_ERROR
;
1730 if (dom_string_caseless_isequal(nodename
,
1731 state
.interned_tspan
))
1732 code
= svgtiny_parse_text((dom_element
*)child
,
1734 dom_string_unref(nodename
);
1735 } else if (nodetype
== DOM_TEXT_NODE
) {
1736 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1737 dom_string
*content
;
1738 if (shape
== NULL
) {
1739 dom_node_unref(child
);
1740 svgtiny_cleanup_state_local(&state
);
1741 return svgtiny_OUT_OF_MEMORY
;
1743 exc
= dom_text_get_whole_text(child
, &content
);
1744 if (exc
!= DOM_NO_ERR
) {
1745 dom_node_unref(child
);
1746 svgtiny_cleanup_state_local(&state
);
1747 return svgtiny_LIBDOM_ERROR
;
1749 if (content
!= NULL
) {
1750 shape
->text
= strndup(dom_string_data(content
),
1751 dom_string_byte_length(content
));
1752 dom_string_unref(content
);
1754 shape
->text
= strdup("");
1758 state
.diagram
->shape_count
++;
1761 if (code
!= svgtiny_OK
) {
1762 dom_node_unref(child
);
1763 svgtiny_cleanup_state_local(&state
);
1766 exc
= dom_node_get_next_sibling(child
, &next
);
1767 dom_node_unref(child
);
1768 if (exc
!= DOM_NO_ERR
) {
1769 svgtiny_cleanup_state_local(&state
);
1770 return svgtiny_LIBDOM_ERROR
;
1775 svgtiny_cleanup_state_local(&state
);
1782 * Parse x, y, width, and height attributes, if present.
1785 void svgtiny_parse_position_attributes(dom_element
*node
,
1786 const struct svgtiny_parse_state state
,
1787 float *x
, float *y
, float *width
, float *height
)
1794 *width
= state
.viewport_width
;
1795 *height
= state
.viewport_height
;
1797 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1798 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1799 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1800 dom_string_unref(attr
);
1803 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1804 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1805 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1806 dom_string_unref(attr
);
1809 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1810 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1811 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1813 dom_string_unref(attr
);
1816 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1817 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1818 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1820 dom_string_unref(attr
);
1826 * Parse a length as a number of pixels.
1829 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1830 const struct svgtiny_parse_state state
)
1832 int num_length
= strspn(s
, "0123456789+-.");
1833 const char *unit
= s
+ num_length
;
1834 float n
= atof((const char *) s
);
1835 float font_size
= 20;
1841 } else if (unit
[0] == '%') {
1842 return n
/ 100.0 * viewport_size
;
1843 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1844 return n
* font_size
;
1845 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1846 return n
/ 2.0 * font_size
;
1847 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1849 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1851 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1853 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1854 return n
* 3.543307;
1855 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1856 return n
* 35.43307;
1857 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1864 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1865 const struct svgtiny_parse_state state
)
1867 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1868 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1874 * Parse paint attributes, if present.
1877 void svgtiny_parse_paint_attributes(dom_element
*node
,
1878 struct svgtiny_parse_state
*state
)
1883 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1884 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1885 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
1886 dom_string_unref(attr
);
1889 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1890 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1891 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
1892 dom_string_unref(attr
);
1895 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1896 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1897 state
->stroke_width
= svgtiny_parse_length(attr
,
1898 state
->viewport_width
, *state
);
1899 dom_string_unref(attr
);
1902 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1903 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1904 char *style
= strndup(dom_string_data(attr
),
1905 dom_string_byte_length(attr
));
1908 if ((s
= strstr(style
, "fill:"))) {
1912 value
= strndup(s
, strcspn(s
, "; "));
1913 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
1916 if ((s
= strstr(style
, "stroke:"))) {
1920 value
= strndup(s
, strcspn(s
, "; "));
1921 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
1924 if ((s
= strstr(style
, "stroke-width:"))) {
1928 value
= strndup(s
, strcspn(s
, "; "));
1929 state
->stroke_width
= _svgtiny_parse_length(value
,
1930 state
->viewport_width
, *state
);
1934 dom_string_unref(attr
);
1943 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1944 struct svgtiny_parse_state_gradient
*grad
,
1945 struct svgtiny_parse_state
*state
)
1947 unsigned int r
, g
, b
;
1949 size_t len
= strlen(s
);
1950 char *id
= 0, *rparen
;
1952 if (len
== 4 && s
[0] == '#') {
1953 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1954 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1956 } else if (len
== 7 && s
[0] == '#') {
1957 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1958 *c
= svgtiny_RGB(r
, g
, b
);
1960 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1961 s
[3] == '(' && s
[len
- 1] == ')') {
1962 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1963 *c
= svgtiny_RGB(r
, g
, b
);
1964 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1968 *c
= svgtiny_RGB(r
, g
, b
);
1971 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1972 *c
= svgtiny_TRANSPARENT
;
1974 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1977 *c
= svgtiny_RGB(0, 0, 0);
1978 } else if (s
[4] == '#') {
1982 rparen
= strchr(id
, ')');
1985 svgtiny_find_gradient(id
, grad
, state
);
1987 if (grad
->linear_gradient_stop_count
== 0)
1988 *c
= svgtiny_TRANSPARENT
;
1989 else if (grad
->linear_gradient_stop_count
== 1)
1990 *c
= grad
->gradient_stop
[0].color
;
1992 *c
= svgtiny_LINEAR_GRADIENT
;
1996 const struct svgtiny_named_color
*named_color
;
1997 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1999 *c
= named_color
->color
;
2003 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
2004 struct svgtiny_parse_state_gradient
*grad
,
2005 struct svgtiny_parse_state
*state
)
2008 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
2009 dom_string_unref(s
);
2013 * Parse font attributes, if present.
2016 void svgtiny_parse_font_attributes(dom_element
*node
,
2017 struct svgtiny_parse_state
*state
)
2019 /* TODO: Implement this, it never used to be */
2022 #ifdef WRITTEN_THIS_PROPERLY
2023 const xmlAttr
*attr
;
2027 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
2028 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
2037 * Parse transform attributes, if present.
2039 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
2042 void svgtiny_parse_transform_attributes(dom_element
*node
,
2043 struct svgtiny_parse_state
*state
)
2049 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
2051 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2052 transform
= strndup(dom_string_data(attr
),
2053 dom_string_byte_length(attr
));
2054 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
2055 &state
->ctm
.c
, &state
->ctm
.d
,
2056 &state
->ctm
.e
, &state
->ctm
.f
);
2058 dom_string_unref(attr
);
2064 * Parse a transform string.
2067 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
2068 float *mc
, float *md
, float *me
, float *mf
)
2070 float a
, b
, c
, d
, e
, f
;
2071 float za
, zb
, zc
, zd
, ze
, zf
;
2076 for (i
= 0; s
[i
]; i
++)
2085 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
2086 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
2088 else if ((sscanf(s
, " translate (%f %f ) %n",
2089 &e
, &f
, &n
) == 2) && (n
> 0))
2091 else if ((sscanf(s
, " translate (%f ) %n",
2092 &e
, &n
) == 1) && (n
> 0))
2094 else if ((sscanf(s
, " scale (%f %f ) %n",
2095 &a
, &d
, &n
) == 2) && (n
> 0))
2097 else if ((sscanf(s
, " scale (%f ) %n",
2098 &a
, &n
) == 1) && (n
> 0))
2100 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
2101 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
2102 angle
= angle
/ 180 * M_PI
;
2107 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
2108 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
2109 } else if ((sscanf(s
, " rotate (%f ) %n",
2110 &angle
, &n
) == 1) && (n
> 0)) {
2111 angle
= angle
/ 180 * M_PI
;
2116 } else if ((sscanf(s
, " skewX (%f ) %n",
2117 &angle
, &n
) == 1) && (n
> 0)) {
2118 angle
= angle
/ 180 * M_PI
;
2120 } else if ((sscanf(s
, " skewY (%f ) %n",
2121 &angle
, &n
) == 1) && (n
> 0)) {
2122 angle
= angle
/ 180 * M_PI
;
2126 za
= *ma
* a
+ *mc
* b
;
2127 zb
= *mb
* a
+ *md
* b
;
2128 zc
= *ma
* c
+ *mc
* d
;
2129 zd
= *mb
* c
+ *md
* d
;
2130 ze
= *ma
* e
+ *mc
* f
+ *me
;
2131 zf
= *mb
* e
+ *md
* f
+ *mf
;
2144 * Add a path to the svgtiny_diagram.
2147 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
2148 struct svgtiny_parse_state
*state
)
2150 struct svgtiny_shape
*shape
;
2152 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
2153 return svgtiny_add_path_linear_gradient(p
, n
, state
);
2155 svgtiny_transform_path(p
, n
, state
);
2157 shape
= svgtiny_add_shape(state
);
2160 return svgtiny_OUT_OF_MEMORY
;
2163 shape
->path_length
= n
;
2164 state
->diagram
->shape_count
++;
2171 * Add a svgtiny_shape to the svgtiny_diagram.
2174 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
2176 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
2177 (state
->diagram
->shape_count
+ 1) *
2178 sizeof (state
->diagram
->shape
[0]));
2181 state
->diagram
->shape
= shape
;
2183 shape
+= state
->diagram
->shape_count
;
2185 shape
->path_length
= 0;
2187 shape
->fill
= state
->fill
;
2188 shape
->stroke
= state
->stroke
;
2189 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
2190 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
2191 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
2192 shape
->stroke_width
= 1;
2199 * Apply the current transformation matrix to a path.
2202 void svgtiny_transform_path(float *p
, unsigned int n
,
2203 struct svgtiny_parse_state
*state
)
2207 for (j
= 0; j
!= n
; ) {
2208 unsigned int points
= 0;
2210 switch ((int) p
[j
]) {
2211 case svgtiny_PATH_MOVE
:
2212 case svgtiny_PATH_LINE
:
2215 case svgtiny_PATH_CLOSE
:
2218 case svgtiny_PATH_BEZIER
:
2225 for (k
= 0; k
!= points
; k
++) {
2226 float x0
= p
[j
], y0
= p
[j
+ 1];
2227 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
2229 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
2240 * Free all memory used by a diagram.
2243 void svgtiny_free(struct svgtiny_diagram
*svg
)
2248 for (i
= 0; i
!= svg
->shape_count
; i
++) {
2249 free(svg
->shape
[i
].path
);
2250 free(svg
->shape
[i
].text
);
2258 #ifndef HAVE_STRNDUP
2259 char *svgtiny_strndup(const char *s
, size_t n
)
2264 for (len
= 0; len
!= n
&& s
[len
]; len
++)
2267 s2
= malloc(len
+ 1);