2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2008-2009 James Bursa <james@semichrome.net>
6 * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org>
18 #include <dom/bindings/xml/xmlparser.h>
20 #include <libcss/libcss.h>
23 #include "svgtiny_internal.h"
25 /* Source file generated by `gperf`. */
26 #include "autogenerated_colors.c"
28 #define TAU 6.28318530717958647692
31 #define M_PI 3.14159265358979323846
35 #define M_PI_2 1.57079632679489661923
38 #define KAPPA 0.5522847498
40 #define degToRad(angleInDegrees) ((angleInDegrees) * M_PI / 180.0)
41 #define radToDeg(angleInRadians) ((angleInRadians) * 180.0 / M_PI)
43 static svgtiny_code
svgtiny_parse_style_element(dom_element
*style
,
44 struct svgtiny_parse_state state
);
45 static css_stylesheet
*svgtiny_parse_style_inline(const uint8_t *data
,
47 static svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
48 struct svgtiny_parse_state state
);
49 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
50 struct svgtiny_parse_state state
);
51 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
52 struct svgtiny_parse_state state
);
53 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
54 struct svgtiny_parse_state state
);
55 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
56 struct svgtiny_parse_state state
);
57 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
58 struct svgtiny_parse_state state
);
59 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
60 struct svgtiny_parse_state state
);
61 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
62 struct svgtiny_parse_state state
, bool polygon
);
63 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
64 struct svgtiny_parse_state state
);
65 static void svgtiny_parse_position_attributes(dom_element
*node
,
66 const struct svgtiny_parse_state state
,
67 float *x
, float *y
, float *width
, float *height
);
68 static void svgtiny_parse_paint_attributes(dom_element
*node
,
69 struct svgtiny_parse_state
*state
);
70 static void svgtiny_parse_font_attributes(dom_element
*node
,
71 struct svgtiny_parse_state
*state
);
72 static void svgtiny_parse_transform_attributes(dom_element
*node
,
73 struct svgtiny_parse_state
*state
);
74 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
75 struct svgtiny_parse_state
*state
);
76 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
77 struct svgtiny_parse_state_gradient
*grad
,
78 struct svgtiny_parse_state
*state
);
81 * rotate midpoint vector
84 rotate_midpoint_vector(float ax
, float ay
,
87 double *x_out
, double *y_out
)
89 double dx2
; /* midpoint x coordinate */
90 double dy2
; /* midpoint y coordinate */
91 double cosangle
; /* cosine of rotation angle */
92 double sinangle
; /* sine of rotation angle */
94 /* compute the sin and cos of the angle */
95 cosangle
= cos(radangle
);
96 sinangle
= sin(radangle
);
98 /* compute the midpoint between start and end points */
99 dx2
= (ax
- bx
) / 2.0;
100 dy2
= (ay
- by
) / 2.0;
102 /* rotate vector to remove angle */
103 *x_out
= ((cosangle
* dx2
) + (sinangle
* dy2
));
104 *y_out
= ((-sinangle
* dx2
) + (cosangle
* dy2
));
109 * ensure the arc radii are large enough and scale as appropriate
111 * the radii need to be large enough if they are not they must be
112 * adjusted. This allows for elimination of differences between
113 * implementations especialy with rounding.
116 ensure_radii_scale(double x1_sq
, double y1_sq
,
117 float *rx
, float *ry
,
118 double *rx_sq
, double *ry_sq
)
123 /* set radii square values */
124 (*rx_sq
) = (*rx
) * (*rx
);
125 (*ry_sq
) = (*ry
) * (*ry
);
127 radiisum
= (x1_sq
/ (*rx_sq
)) + (y1_sq
/ (*ry_sq
));
128 if (radiisum
> 0.99999) {
129 /* need to scale radii */
130 radiiscale
= sqrt(radiisum
) * 1.00001;
131 *rx
= (float)(radiiscale
* (*rx
));
132 *ry
= (float)(radiiscale
* (*ry
));
133 /* update squares too */
134 (*rx_sq
) = (*rx
) * (*rx
);
135 (*ry_sq
) = (*ry
) * (*ry
);
141 * compute the transformed centre point
144 compute_transformed_centre_point(double sign
, float rx
, float ry
,
145 double rx_sq
, double ry_sq
,
146 double x1
, double y1
,
147 double x1_sq
, double y1_sq
,
148 double *cx1
, double *cy1
)
152 sq
= ((rx_sq
* ry_sq
) - (rx_sq
* y1_sq
) - (ry_sq
* x1_sq
)) /
153 ((rx_sq
* y1_sq
) + (ry_sq
* x1_sq
));
154 sq
= (sq
< 0) ? 0 : sq
;
156 coef
= (sign
* sqrt(sq
));
158 *cx1
= coef
* ((rx
* y1
) / ry
);
159 *cy1
= coef
* -((ry
* x1
) / rx
);
164 * compute untransformed centre point
166 * \param ax The first point x coordinate
167 * \param ay The first point y coordinate
168 * \param bx The second point x coordinate
169 * \param ay The second point y coordinate
172 compute_centre_point(float ax
, float ay
,
174 double cx1
, double cy1
,
176 double *x_out
, double *y_out
)
180 double cosangle
; /* cosine of rotation angle */
181 double sinangle
; /* sine of rotation angle */
183 /* compute the sin and cos of the angle */
184 cosangle
= cos(radangle
);
185 sinangle
= sin(radangle
);
187 sx2
= (ax
+ bx
) / 2.0;
188 sy2
= (ay
+ by
) / 2.0;
190 *x_out
= sx2
+ (cosangle
* cx1
- sinangle
* cy1
);
191 *y_out
= sy2
+ (sinangle
* cx1
+ cosangle
* cy1
);
196 * compute the angle start and extent
199 compute_angle_start_extent(float rx
, float ry
,
200 double x1
, double y1
,
201 double cx1
, double cy1
,
202 double *start
, double *extent
)
213 * Angle betwen two vectors is +/- acos( u.v / len(u) * len(v))
215 * '.' is the dot product.
216 * +/- is calculated from the sign of the cross product (u x v)
219 ux
= (x1
- cx1
) / rx
;
220 uy
= (y1
- cy1
) / ry
;
221 vx
= (-x1
- cx1
) / rx
;
222 vy
= (-y1
- cy1
) / ry
;
224 /* compute the start angle */
225 /* The angle between (ux, uy) and the 0 angle */
227 /* len(u) * len(1,0) == len(u) */
228 n
= sqrt((ux
* ux
) + (uy
* uy
));
229 /* u.v == (ux,uy).(1,0) == (1 * ux) + (0 * uy) == ux */
231 /* u x v == (1 * uy - ux * 0) == uy */
232 sign
= (uy
< 0) ? -1.0 : 1.0;
233 /* (p >= n) so safe */
234 *start
= sign
* acos(p
/ n
);
236 /* compute the extent angle */
237 n
= sqrt(((ux
* ux
) + (uy
* uy
)) * ((vx
* vx
) + (vy
* vy
)));
238 p
= (ux
* vx
) + (uy
* vy
);
239 sign
= ((ux
* vy
) - (uy
* vx
) < 0) ? -1.0f
: 1.0f
;
241 /* arc cos must operate between -1 and 1 */
244 *extent
= sign
* M_PI
;
245 } else if (actmp
> 1.0) {
248 *extent
= sign
* acos(actmp
);
254 * converts a circle centered unit circle arc to a series of bezier curves
256 * Each bezier is stored as six values of three pairs of coordinates
258 * The beziers are stored without their start point as that is assumed
259 * to be the preceding elements end point.
261 * \param start The start angle of the arc (in radians)
262 * \param extent The size of the arc (in radians)
263 * \param bzpt The array to store the bezier values in
264 * \return The number of bezier segments output (max 4)
267 circle_arc_to_bezier(double start
, double extent
, double *bzpt
)
277 bzsegments
= (int) ceil(fabs(extent
) / M_PI_2
);
278 increment
= extent
/ bzsegments
;
279 controllen
= 4.0 / 3.0 * sin(increment
/ 2.0) / (1.0 + cos(increment
/ 2.0));
281 for (segment
= 0; segment
< bzsegments
; segment
++) {
282 /* first control point */
283 angle
= start
+ (segment
* increment
);
286 bzpt
[pos
++] = dx
- controllen
* dy
;
287 bzpt
[pos
++] = dy
+ controllen
* dx
;
288 /* second control point */
292 bzpt
[pos
++] = dx
+ controllen
* dy
;
293 bzpt
[pos
++] = dy
- controllen
* dx
;
304 * transform coordinate list
306 * perform a scale, rotate and translate on list of coordinates
312 * homogeneous transforms
320 * | cos(an) -sin(an) 0 |
321 * R = | sin(an) cos(an) 0 |
324 * {{cos(a), -sin(a) 0}, {sin(a), cos(a),0}, {0,0,1}}
331 * note order is significat here and the combined matrix is
334 * | cos(an) -sin(an) cx |
335 * T.R = | sin(an) cos(an) cy |
338 * | rx * cos(an) ry * -sin(an) cx |
339 * T.R.S = | rx * sin(an) ry * cos(an) cy |
342 * {{Cos[a], -Sin[a], c}, {Sin[a], Cos[a], d}, {0, 0, 1}} . {{r, 0, 0}, {0, s, 0}, {0, 0, 1}}
354 * x2 = cx + (rx * x1 * cos(a)) + (ry * y1 * -1 * sin(a))
355 * y2 = cy + (ry * y1 * cos(a)) + (rx * x1 * sin(a))
358 * \param rx X scaling to apply
359 * \param ry Y scaling to apply
360 * \param radangle rotation to apply (in radians)
361 * \param cx X translation to apply
362 * \param cy Y translation to apply
363 * \param points The size of the bzpoints array
364 * \param bzpoints an array of x,y values to apply the transform to
367 scale_rotate_translate_points(double rx
, double ry
,
369 double cx
, double cy
,
374 double cosangle
; /* cosine of rotation angle */
375 double sinangle
; /* sine of rotation angle */
376 double rxcosangle
, rxsinangle
, rycosangle
, rynsinangle
;
379 /* compute the sin and cos of the angle */
380 cosangle
= cos(radangle
);
381 sinangle
= sin(radangle
);
383 rxcosangle
= rx
* cosangle
;
384 rxsinangle
= rx
* sinangle
;
385 rycosangle
= ry
* cosangle
;
386 rynsinangle
= ry
* -1 * sinangle
;
388 for (pnt
= 0; pnt
< pntsize
; pnt
+=2) {
389 x2
= cx
+ (points
[pnt
] * rxcosangle
) + (points
[pnt
+ 1] * rynsinangle
);
390 y2
= cy
+ (points
[pnt
+ 1] * rycosangle
) + (points
[pnt
] * rxsinangle
);
392 points
[pnt
+ 1] = y2
;
398 * convert an svg path arc to a bezier curve
400 * This function perfoms a transform on the nine arc parameters
401 * (coordinate pairs for start and end together with the radii of the
402 * elipse, the rotation angle and which of the four arcs to draw)
403 * which generates the parameters (coordinate pairs for start,
404 * end and their control points) for a set of up to four bezier curves.
406 * Obviously the start and end coordinates are not altered between
407 * representations so the aim is to calculate the coordinate pairs for
408 * the bezier control points.
410 * \param bzpoints the array to fill with bezier curves
411 * \return the number of bezier segments generated or -1 for a line
414 svgarc_to_bezier(float start_x
,
425 double radangle
; /* normalised elipsis rotation angle in radians */
426 double rx_sq
; /* x radius squared */
427 double ry_sq
; /* y radius squared */
428 double x1
, y1
; /* rotated midpoint vector */
429 double x1_sq
, y1_sq
; /* x1 vector squared */
430 double cx1
,cy1
; /* transformed circle center */
431 double cx
,cy
; /* circle center */
432 double start
, extent
;
435 if ((start_x
== end_x
) && (start_y
== end_y
)) {
437 * if the start and end coordinates are the same the
438 * svg spec says this is equivalent to having no segment
444 if ((rx
== 0) || (ry
== 0)) {
446 * if either radii is zero the specified behaviour is a line
451 /* obtain the absolute values of the radii */
455 /* convert normalised angle to radians */
456 radangle
= degToRad(fmod(angle
, 360.0));
459 /* x1,x2 is the midpoint vector rotated to remove the arc angle */
460 rotate_midpoint_vector(start_x
, start_y
, end_x
, end_y
, radangle
, &x1
, &y1
);
463 /* get squared x1 values */
467 /* ensure radii are correctly scaled */
468 ensure_radii_scale(x1_sq
, y1_sq
, &rx
, &ry
, &rx_sq
, &ry_sq
);
470 /* compute the transformed centre point */
471 compute_transformed_centre_point(largearc
== sweep
?-1:1,
479 /* get the untransformed centre point */
480 compute_centre_point(start_x
, start_y
,
487 /* compute anglestart and extent */
488 compute_angle_start_extent(rx
,ry
,
493 /* extent of 0 is a straight line */
498 /* take account of sweep */
499 if (!sweep
&& extent
> 0) {
501 } else if (sweep
&& extent
< 0) {
505 /* normalise start and extent */
506 extent
= fmod(extent
, TAU
);
507 start
= fmod(start
, TAU
);
509 /* convert the arc to unit circle bezier curves */
510 bzsegments
= circle_arc_to_bezier(start
, extent
, bzpoints
);
512 /* transform the bezier curves */
513 scale_rotate_translate_points(rx
, ry
,
524 * Call this to ref the strings in a gradient state.
526 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
528 if (grad
->gradient_x1
!= NULL
) {
529 dom_string_ref(grad
->gradient_x1
);
531 if (grad
->gradient_y1
!= NULL
) {
532 dom_string_ref(grad
->gradient_y1
);
534 if (grad
->gradient_x2
!= NULL
) {
535 dom_string_ref(grad
->gradient_x2
);
537 if (grad
->gradient_y2
!= NULL
) {
538 dom_string_ref(grad
->gradient_y2
);
543 * Call this to clean up the strings in a gradient state.
545 static void svgtiny_grad_string_cleanup(
546 struct svgtiny_parse_state_gradient
*grad
)
548 if (grad
->gradient_x1
!= NULL
) {
549 dom_string_unref(grad
->gradient_x1
);
550 grad
->gradient_x1
= NULL
;
552 if (grad
->gradient_y1
!= NULL
) {
553 dom_string_unref(grad
->gradient_y1
);
554 grad
->gradient_y1
= NULL
;
556 if (grad
->gradient_x2
!= NULL
) {
557 dom_string_unref(grad
->gradient_x2
);
558 grad
->gradient_x2
= NULL
;
560 if (grad
->gradient_y2
!= NULL
) {
561 dom_string_unref(grad
->gradient_y2
);
562 grad
->gradient_y2
= NULL
;
567 * Set the local externally-stored parts of a parse state.
568 * Call this in functions that made a new state on the stack.
569 * Doesn't make own copy of global state, such as the interned string list.
571 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
573 svgtiny_grad_string_ref(&(state
->fill_grad
));
574 svgtiny_grad_string_ref(&(state
->stroke_grad
));
578 * Cleanup the local externally-stored parts of a parse state.
579 * Call this in functions that made a new state on the stack.
580 * Doesn't cleanup global state, such as the interned string list.
582 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
584 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
585 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
590 * Create a new svgtiny_diagram structure.
593 struct svgtiny_diagram
*svgtiny_create(void)
595 struct svgtiny_diagram
*diagram
;
597 diagram
= calloc(sizeof(*diagram
), 1);
606 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
614 * Parse a block of memory into a svgtiny_diagram.
617 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
618 const char *buffer
, size_t size
, const char *url
,
619 int viewport_width
, int viewport_height
)
622 dom_document
*document
;
624 dom_xml_parser
*parser
;
627 dom_string
*svg_name
;
628 lwc_string
*svg_name_lwc
;
629 struct svgtiny_parse_state state
;
630 float x
, y
, width
, height
;
639 parser
= dom_xml_parser_create(NULL
, NULL
,
640 ignore_msg
, NULL
, &document
);
643 return svgtiny_LIBDOM_ERROR
;
645 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
646 if (err
!= DOM_XML_OK
) {
647 dom_node_unref(document
);
648 dom_xml_parser_destroy(parser
);
649 return svgtiny_LIBDOM_ERROR
;
652 err
= dom_xml_parser_completed(parser
);
653 if (err
!= DOM_XML_OK
) {
654 dom_node_unref(document
);
655 dom_xml_parser_destroy(parser
);
656 return svgtiny_LIBDOM_ERROR
;
659 /* We're done parsing, drop the parser.
660 * We now own the document entirely.
662 dom_xml_parser_destroy(parser
);
664 /* find root <svg> element */
665 exc
= dom_document_get_document_element(document
, &svg
);
666 if (exc
!= DOM_NO_ERR
) {
667 dom_node_unref(document
);
668 return svgtiny_LIBDOM_ERROR
;
671 /* no root svg element */
672 dom_node_unref(document
);
673 return svgtiny_SVG_ERROR
;
676 exc
= dom_node_get_node_name(svg
, &svg_name
);
677 if (exc
!= DOM_NO_ERR
) {
679 dom_node_unref(document
);
680 return svgtiny_LIBDOM_ERROR
;
682 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
683 &svg_name_lwc
) != lwc_error_ok
) {
684 dom_string_unref(svg_name
);
686 dom_node_unref(document
);
687 return svgtiny_LIBDOM_ERROR
;
689 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
690 lwc_string_unref(svg_name_lwc
);
691 dom_string_unref(svg_name
);
693 dom_node_unref(document
);
694 return svgtiny_NOT_SVG
;
697 lwc_string_unref(svg_name_lwc
);
698 dom_string_unref(svg_name
);
700 /* initialize the state struct with zeros */
701 memset(&state
, 0, sizeof(state
));
703 /* get graphic dimensions */
704 state
.diagram
= diagram
;
705 state
.document
= document
;
706 state
.viewport_width
= viewport_width
;
707 state
.viewport_height
= viewport_height
;
709 /* Initialize CSS context */
710 css_code
= css_select_ctx_create(&state
.select_ctx
);
711 if (css_code
!= CSS_OK
) {
713 dom_node_unref(document
);
714 return svgtiny_LIBCSS_ERROR
;
717 #define SVGTINY_STRING_ACTION2(s,n) \
718 if (dom_string_create_interned((const uint8_t *) #n, \
719 strlen(#n), &state.interned_##s) \
721 code = svgtiny_LIBDOM_ERROR; \
724 #include "svgtiny_strings.h"
725 #undef SVGTINY_STRING_ACTION2
727 /* Intern SVG's xmlns separately because it's an lwc_string
728 * and not a dom_string. We initialize its pointer to NULL
729 * because the "cleanup:" test to see if it needs to be free'd
730 * looks for NULL. Returning a LIBDOM_ERROR on failure is not
731 * perfect but it's the closest of the available options. */
732 state
.interned_svg_xmlns
= NULL
;
733 if (lwc_intern_string("http://www.w3.org/2000/svg",
735 &state
.interned_svg_xmlns
) != lwc_error_ok
) {
736 code
= svgtiny_LIBDOM_ERROR
;
740 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
741 diagram
->width
= width
;
742 diagram
->height
= height
;
744 /* set up parsing state */
745 state
.viewport_width
= width
;
746 state
.viewport_height
= height
;
747 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
750 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
751 state
.ctm
.e
= 0; /*x;*/
752 state
.ctm
.f
= 0; /*y;*/
753 state
.fill
= 0x000000;
754 state
.stroke
= svgtiny_TRANSPARENT
;
755 state
.stroke_width
= 1;
758 code
= svgtiny_preparse_styles(svg
, state
);
759 if (code
== svgtiny_OK
) {
760 code
= svgtiny_parse_svg(svg
, state
);
764 dom_node_unref(document
);
765 css_code
= css_select_ctx_destroy(state
.select_ctx
);
766 if (css_code
!= CSS_OK
) {
767 code
= svgtiny_LIBCSS_ERROR
;
771 svgtiny_cleanup_state_local(&state
);
772 #define SVGTINY_STRING_ACTION2(s,n) \
773 if (state.interned_##s != NULL) \
774 dom_string_unref(state.interned_##s);
775 #include "svgtiny_strings.h"
776 #undef SVGTINY_STRING_ACTION2
778 if (state
.interned_svg_xmlns
!= NULL
) {
779 lwc_string_unref(state
.interned_svg_xmlns
);
787 * Parse a single <style> element, appending the result to the CSS
788 * select context within the given parser state.
790 svgtiny_code
svgtiny_parse_style_element(dom_element
*style
,
791 struct svgtiny_parse_state state
)
793 css_stylesheet
*sheet
;
797 code
= svgtiny_create_stylesheet(&sheet
, false);
798 if (code
!= CSS_OK
) {
799 return svgtiny_LIBCSS_ERROR
;
802 /* Parse the style element's "media" attribute if it has
803 one. We don't do anything with it right now. */
804 dom_string
*media_attr
;
805 exc
= dom_element_get_attribute(style
, state
.interned_media
,
807 if (exc
!= DOM_NO_ERR
) {
808 css_stylesheet_destroy(sheet
);
809 return svgtiny_LIBDOM_ERROR
;
813 /* Here's where we'd actually change the media type if
814 we were going to use it */
815 dom_string_unref(media_attr
);
819 dom_node_get_text_content(style
, &data
);
821 /* Empty stylesheet? That's fine. */
822 css_stylesheet_destroy(sheet
);
826 code
= css_stylesheet_append_data(sheet
,
827 (uint8_t *)dom_string_data(data
),
828 dom_string_byte_length(data
));
829 if (code
!= CSS_OK
&& code
!= CSS_NEEDDATA
) {
830 dom_string_unref(data
);
831 css_stylesheet_destroy(sheet
);
832 return svgtiny_LIBCSS_ERROR
;
835 code
= css_stylesheet_data_done(sheet
);
836 if (code
!= CSS_OK
) {
837 dom_string_unref(data
);
838 css_stylesheet_destroy(sheet
);
839 return svgtiny_LIBCSS_ERROR
;
842 code
= css_select_ctx_append_sheet(state
.select_ctx
,
846 if (code
!= CSS_OK
) {
847 dom_string_unref(data
);
848 return svgtiny_LIBCSS_ERROR
;
851 dom_string_unref(data
);
857 * Parse the contents of an inline style and return (a pointer to) the
858 * corresponding stylesheet for use with css_select_style(). Returns
859 * NULL if anything goes wrong.
861 css_stylesheet
*svgtiny_parse_style_inline(const uint8_t *data
,
864 css_stylesheet
*sheet
;
867 code
= svgtiny_create_stylesheet(&sheet
, true);
868 if (code
!= CSS_OK
) {
872 code
= css_stylesheet_append_data(sheet
, data
, len
);
873 if (code
!= CSS_OK
&& code
!= CSS_NEEDDATA
) {
874 css_stylesheet_destroy(sheet
);
878 code
= css_stylesheet_data_done(sheet
);
879 if (code
!= CSS_OK
) {
880 css_stylesheet_destroy(sheet
);
888 * Parse all <style> elements within a root <svg> element. This
889 * should be called before svgtiny_parse_svg() because that function
890 * makes a single pass through the document and we'd like all style
891 * information to be available during that pass. Specifically, we'd
892 * like a <style> sheet at the end of the document to affect the
893 * rendering of elements at its beginning.
895 * The element-parsing inner loop here is essentially the same as
896 * that within svgtiny_parse_svg().
898 svgtiny_code
svgtiny_preparse_styles(dom_element
*svg
,
899 struct svgtiny_parse_state state
)
904 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
905 if (exc
!= DOM_NO_ERR
) {
906 return svgtiny_LIBDOM_ERROR
;
908 while (child
!= NULL
) {
910 dom_node_type nodetype
;
911 svgtiny_code code
= svgtiny_OK
;
913 exc
= dom_node_get_node_type(child
, &nodetype
);
914 if (exc
!= DOM_NO_ERR
) {
915 dom_node_unref(child
);
916 return svgtiny_LIBDOM_ERROR
;
918 if (nodetype
== DOM_ELEMENT_NODE
) {
919 dom_string
*nodename
;
920 exc
= dom_node_get_node_name(child
, &nodename
);
921 if (exc
!= DOM_NO_ERR
) {
922 dom_node_unref(child
);
923 return svgtiny_LIBDOM_ERROR
;
926 if (dom_string_caseless_isequal(state
.interned_style
,
928 /* We have a <style> element, parse it */
929 code
= svgtiny_parse_style_element(child
,
934 dom_string_unref(nodename
);
936 if (code
!= svgtiny_OK
) {
937 dom_node_unref(child
);
940 exc
= dom_node_get_next_sibling(child
,
941 (dom_node
**) (void *) &next
);
942 dom_node_unref(child
);
943 if (exc
!= DOM_NO_ERR
) {
944 return svgtiny_LIBDOM_ERROR
;
953 * Parse a <svg> or <g> element node.
956 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
957 struct svgtiny_parse_state state
)
959 float x
, y
, width
, height
;
960 dom_string
*view_box
;
964 svgtiny_setup_state_local(&state
);
966 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
967 svgtiny_parse_paint_attributes(svg
, &state
);
968 svgtiny_parse_font_attributes(svg
, &state
);
970 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
972 if (exc
!= DOM_NO_ERR
) {
973 svgtiny_cleanup_state_local(&state
);
974 return svgtiny_LIBDOM_ERROR
;
978 char *s
= strndup(dom_string_data(view_box
),
979 dom_string_byte_length(view_box
));
980 float min_x
, min_y
, vwidth
, vheight
;
981 if (sscanf(s
, "%f,%f,%f,%f",
982 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
983 sscanf(s
, "%f %f %f %f",
984 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
985 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
986 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
987 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
988 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
991 dom_string_unref(view_box
);
994 svgtiny_parse_transform_attributes(svg
, &state
);
996 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
997 if (exc
!= DOM_NO_ERR
) {
998 svgtiny_cleanup_state_local(&state
);
999 return svgtiny_LIBDOM_ERROR
;
1001 while (child
!= NULL
) {
1003 dom_node_type nodetype
;
1004 svgtiny_code code
= svgtiny_OK
;
1006 exc
= dom_node_get_node_type(child
, &nodetype
);
1007 if (exc
!= DOM_NO_ERR
) {
1008 dom_node_unref(child
);
1009 return svgtiny_LIBDOM_ERROR
;
1011 if (nodetype
== DOM_ELEMENT_NODE
) {
1012 dom_string
*nodename
;
1013 exc
= dom_node_get_node_name(child
, &nodename
);
1014 if (exc
!= DOM_NO_ERR
) {
1015 dom_node_unref(child
);
1016 svgtiny_cleanup_state_local(&state
);
1017 return svgtiny_LIBDOM_ERROR
;
1019 if (dom_string_caseless_isequal(state
.interned_svg
,
1021 code
= svgtiny_parse_svg(child
, state
);
1022 else if (dom_string_caseless_isequal(state
.interned_g
,
1024 code
= svgtiny_parse_svg(child
, state
);
1025 else if (dom_string_caseless_isequal(state
.interned_a
,
1027 code
= svgtiny_parse_svg(child
, state
);
1028 else if (dom_string_caseless_isequal(state
.interned_path
,
1030 code
= svgtiny_parse_path(child
, state
);
1031 else if (dom_string_caseless_isequal(state
.interned_rect
,
1033 code
= svgtiny_parse_rect(child
, state
);
1034 else if (dom_string_caseless_isequal(state
.interned_circle
,
1036 code
= svgtiny_parse_circle(child
, state
);
1037 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
1039 code
= svgtiny_parse_ellipse(child
, state
);
1040 else if (dom_string_caseless_isequal(state
.interned_line
,
1042 code
= svgtiny_parse_line(child
, state
);
1043 else if (dom_string_caseless_isequal(state
.interned_polyline
,
1045 code
= svgtiny_parse_poly(child
, state
, false);
1046 else if (dom_string_caseless_isequal(state
.interned_polygon
,
1048 code
= svgtiny_parse_poly(child
, state
, true);
1049 else if (dom_string_caseless_isequal(state
.interned_text
,
1051 code
= svgtiny_parse_text(child
, state
);
1052 dom_string_unref(nodename
);
1054 if (code
!= svgtiny_OK
) {
1055 dom_node_unref(child
);
1056 svgtiny_cleanup_state_local(&state
);
1059 exc
= dom_node_get_next_sibling(child
,
1060 (dom_node
**) (void *) &next
);
1061 dom_node_unref(child
);
1062 if (exc
!= DOM_NO_ERR
) {
1063 svgtiny_cleanup_state_local(&state
);
1064 return svgtiny_LIBDOM_ERROR
;
1069 svgtiny_cleanup_state_local(&state
);
1076 * Parse a <path> element node.
1078 * http://www.w3.org/TR/SVG11/paths#PathElement
1081 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
1082 struct svgtiny_parse_state state
)
1085 dom_string
*path_d_str
;
1088 float *p
; /* path elemets */
1089 unsigned int palloc
; /* number of path elements allocated */
1091 float last_x
= 0, last_y
= 0;
1092 float last_cubic_x
= 0, last_cubic_y
= 0;
1093 float last_quad_x
= 0, last_quad_y
= 0;
1094 float subpath_first_x
= 0, subpath_first_y
= 0;
1096 svgtiny_setup_state_local(&state
);
1098 svgtiny_parse_paint_attributes(path
, &state
);
1099 svgtiny_parse_transform_attributes(path
, &state
);
1101 /* read d attribute */
1102 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
1103 if (exc
!= DOM_NO_ERR
) {
1104 state
.diagram
->error_line
= -1; /* path->line; */
1105 state
.diagram
->error_message
= "path: error retrieving d attribute";
1106 svgtiny_cleanup_state_local(&state
);
1107 return svgtiny_SVG_ERROR
;
1110 if (path_d_str
== NULL
) {
1111 state
.diagram
->error_line
= -1; /* path->line; */
1112 state
.diagram
->error_message
= "path: missing d attribute";
1113 svgtiny_cleanup_state_local(&state
);
1114 return svgtiny_SVG_ERROR
;
1117 /* empty path is permitted it just disables the path */
1118 palloc
= dom_string_byte_length(path_d_str
);
1120 dom_string_unref(path_d_str
);
1121 svgtiny_cleanup_state_local(&state
);
1125 /* local copy of the path data allowing in-place modification */
1126 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
1127 dom_string_unref(path_d_str
);
1129 svgtiny_cleanup_state_local(&state
);
1130 return svgtiny_OUT_OF_MEMORY
;
1133 /* ensure path element allocation is sensibly bounded */
1136 } else if (palloc
> 64) {
1137 palloc
= palloc
/ 2;
1140 /* allocate initial space for path elements */
1141 p
= malloc(sizeof p
[0] * palloc
);
1144 svgtiny_cleanup_state_local(&state
);
1145 return svgtiny_OUT_OF_MEMORY
;
1148 /* parse d and build path */
1149 for (i
= 0; s
[i
]; i
++)
1156 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
1159 /* Ensure there is sufficient space for path elements */
1160 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
1162 if ((palloc - i) < NUM_ELEMENTS) { \
1164 palloc = (palloc * 2) + (palloc / 2); \
1165 tp = realloc(p, sizeof p[0] * palloc); \
1169 svgtiny_cleanup_state_local(&state); \
1170 return svgtiny_OUT_OF_MEMORY; \
1177 /* moveto (M, m), lineto (L, l) (2 arguments) */
1178 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
1179 /*LOG(("moveto or lineto"));*/
1180 if (*command
== 'M' || *command
== 'm')
1181 plot_command
= svgtiny_PATH_MOVE
;
1183 plot_command
= svgtiny_PATH_LINE
;
1185 ALLOC_PATH_ELEMENTS(3);
1186 p
[i
++] = plot_command
;
1187 if ('a' <= *command
) {
1191 if (plot_command
== svgtiny_PATH_MOVE
) {
1192 subpath_first_x
= x
;
1193 subpath_first_y
= y
;
1195 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1197 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1200 plot_command
= svgtiny_PATH_LINE
;
1201 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
1203 /* closepath (Z, z) (no arguments) */
1204 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
1205 /*LOG(("closepath"));*/
1206 ALLOC_PATH_ELEMENTS(1);
1208 p
[i
++] = svgtiny_PATH_CLOSE
;
1210 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
1211 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
1213 /* horizontal lineto (H, h) (1 argument) */
1214 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
1215 /*LOG(("horizontal lineto"));*/
1217 ALLOC_PATH_ELEMENTS(3);
1219 p
[i
++] = svgtiny_PATH_LINE
;
1220 if (*command
== 'h')
1222 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
1224 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
1226 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
1228 /* vertical lineto (V, v) (1 argument) */
1229 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
1230 /*LOG(("vertical lineto"));*/
1232 ALLOC_PATH_ELEMENTS(3);
1234 p
[i
++] = svgtiny_PATH_LINE
;
1235 if (*command
== 'v')
1237 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
1238 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
1241 } while (sscanf(s
, "%f %n", &y
, &n
) == 1);
1243 /* curveto (C, c) (6 arguments) */
1244 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
1245 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
1246 /*LOG(("curveto"));*/
1248 ALLOC_PATH_ELEMENTS(7);
1250 p
[i
++] = svgtiny_PATH_BEZIER
;
1251 if (*command
== 'c') {
1261 p
[i
++] = last_cubic_x
= x2
;
1262 p
[i
++] = last_cubic_y
= y2
;
1263 p
[i
++] = last_quad_x
= last_x
= x
;
1264 p
[i
++] = last_quad_y
= last_y
= y
;
1266 } while (sscanf(s
, "%f %f %f %f %f %f %n",
1267 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
1269 /* shorthand/smooth curveto (S, s) (4 arguments) */
1270 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
1271 &x2
, &y2
, &x
, &y
, &n
) == 5) {
1272 /*LOG(("shorthand/smooth curveto"));*/
1274 ALLOC_PATH_ELEMENTS(7);
1276 p
[i
++] = svgtiny_PATH_BEZIER
;
1277 x1
= last_x
+ (last_x
- last_cubic_x
);
1278 y1
= last_y
+ (last_y
- last_cubic_y
);
1279 if (*command
== 's') {
1287 p
[i
++] = last_cubic_x
= x2
;
1288 p
[i
++] = last_cubic_y
= y2
;
1289 p
[i
++] = last_quad_x
= last_x
= x
;
1290 p
[i
++] = last_quad_y
= last_y
= y
;
1292 } while (sscanf(s
, "%f %f %f %f %n",
1293 &x2
, &y2
, &x
, &y
, &n
) == 4);
1295 /* quadratic Bezier curveto (Q, q) (4 arguments) */
1296 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
1297 &x1
, &y1
, &x
, &y
, &n
) == 5) {
1298 /*LOG(("quadratic Bezier curveto"));*/
1300 ALLOC_PATH_ELEMENTS(7);
1302 p
[i
++] = svgtiny_PATH_BEZIER
;
1305 if (*command
== 'q') {
1311 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1312 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1313 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1314 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1315 p
[i
++] = last_cubic_x
= last_x
= x
;
1316 p
[i
++] = last_cubic_y
= last_y
= y
;
1318 } while (sscanf(s
, "%f %f %f %f %n",
1319 &x1
, &y1
, &x
, &y
, &n
) == 4);
1321 /* shorthand/smooth quadratic Bezier curveto (T, t)
1323 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
1325 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
1327 ALLOC_PATH_ELEMENTS(7);
1329 p
[i
++] = svgtiny_PATH_BEZIER
;
1330 x1
= last_x
+ (last_x
- last_quad_x
);
1331 y1
= last_y
+ (last_y
- last_quad_y
);
1334 if (*command
== 't') {
1340 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
1341 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
1342 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
1343 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
1344 p
[i
++] = last_cubic_x
= last_x
= x
;
1345 p
[i
++] = last_cubic_y
= last_y
= y
;
1347 } while (sscanf(s
, "%f %f %n",
1350 /* elliptical arc (A, a) (7 arguments) */
1351 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
1352 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1356 double bzpoints
[6*4]; /* allow for up to four bezier segments per arc */
1358 if (*command
== 'a') {
1363 bzsegments
= svgarc_to_bezier(last_x
, last_y
,
1370 if (bzsegments
== -1) {
1372 ALLOC_PATH_ELEMENTS(3);
1373 p
[i
++] = svgtiny_PATH_LINE
;
1376 } else if (bzsegments
> 0) {
1378 ALLOC_PATH_ELEMENTS((unsigned int)bzsegments
* 7);
1379 for (bzpnt
= 0;bzpnt
< (bzsegments
* 6); bzpnt
+=6) {
1380 p
[i
++] = svgtiny_PATH_BEZIER
;
1381 p
[i
++] = bzpoints
[bzpnt
];
1382 p
[i
++] = bzpoints
[bzpnt
+1];
1383 p
[i
++] = bzpoints
[bzpnt
+2];
1384 p
[i
++] = bzpoints
[bzpnt
+3];
1385 p
[i
++] = bzpoints
[bzpnt
+4];
1386 p
[i
++] = bzpoints
[bzpnt
+5];
1389 if (bzsegments
!= 0) {
1390 last_cubic_x
= last_quad_x
= last_x
= p
[i
-2];
1391 last_cubic_y
= last_quad_y
= last_y
= p
[i
-1];
1396 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
1397 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
1401 /* fprintf(stderr, "parse failed at \"%s\"\n", s); */
1409 /* no real segments in path */
1411 svgtiny_cleanup_state_local(&state
);
1415 /* resize path element array to not be over allocated */
1419 /* try the resize, if it fails just continue to use the old
1422 tp
= realloc(p
, sizeof p
[0] * i
);
1428 err
= svgtiny_add_path(p
, i
, &state
);
1430 svgtiny_cleanup_state_local(&state
);
1437 * Parse a <rect> element node.
1439 * http://www.w3.org/TR/SVG11/shapes#RectElement
1442 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
1443 struct svgtiny_parse_state state
)
1446 float x
, y
, width
, height
;
1449 svgtiny_setup_state_local(&state
);
1451 svgtiny_parse_position_attributes(rect
, state
,
1452 &x
, &y
, &width
, &height
);
1453 svgtiny_parse_paint_attributes(rect
, &state
);
1454 svgtiny_parse_transform_attributes(rect
, &state
);
1456 p
= malloc(13 * sizeof p
[0]);
1458 svgtiny_cleanup_state_local(&state
);
1459 return svgtiny_OUT_OF_MEMORY
;
1462 p
[0] = svgtiny_PATH_MOVE
;
1465 p
[3] = svgtiny_PATH_LINE
;
1468 p
[6] = svgtiny_PATH_LINE
;
1471 p
[9] = svgtiny_PATH_LINE
;
1474 p
[12] = svgtiny_PATH_CLOSE
;
1476 err
= svgtiny_add_path(p
, 13, &state
);
1478 svgtiny_cleanup_state_local(&state
);
1485 * Parse a <circle> element node.
1488 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
1489 struct svgtiny_parse_state state
)
1492 float x
= 0, y
= 0, r
= -1;
1497 svgtiny_setup_state_local(&state
);
1499 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
1500 if (exc
!= DOM_NO_ERR
) {
1501 svgtiny_cleanup_state_local(&state
);
1502 return svgtiny_LIBDOM_ERROR
;
1505 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1507 dom_string_unref(attr
);
1509 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
1510 if (exc
!= DOM_NO_ERR
) {
1511 svgtiny_cleanup_state_local(&state
);
1512 return svgtiny_LIBDOM_ERROR
;
1515 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1517 dom_string_unref(attr
);
1519 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
1520 if (exc
!= DOM_NO_ERR
) {
1521 svgtiny_cleanup_state_local(&state
);
1522 return svgtiny_LIBDOM_ERROR
;
1525 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1527 dom_string_unref(attr
);
1529 svgtiny_parse_paint_attributes(circle
, &state
);
1530 svgtiny_parse_transform_attributes(circle
, &state
);
1533 state
.diagram
->error_line
= -1; /* circle->line; */
1534 state
.diagram
->error_message
= "circle: r missing or negative";
1535 svgtiny_cleanup_state_local(&state
);
1536 return svgtiny_SVG_ERROR
;
1539 svgtiny_cleanup_state_local(&state
);
1543 p
= malloc(32 * sizeof p
[0]);
1545 svgtiny_cleanup_state_local(&state
);
1546 return svgtiny_OUT_OF_MEMORY
;
1549 p
[0] = svgtiny_PATH_MOVE
;
1552 p
[3] = svgtiny_PATH_BEZIER
;
1554 p
[5] = y
+ r
* KAPPA
;
1555 p
[6] = x
+ r
* KAPPA
;
1559 p
[10] = svgtiny_PATH_BEZIER
;
1560 p
[11] = x
- r
* KAPPA
;
1563 p
[14] = y
+ r
* KAPPA
;
1566 p
[17] = svgtiny_PATH_BEZIER
;
1568 p
[19] = y
- r
* KAPPA
;
1569 p
[20] = x
- r
* KAPPA
;
1573 p
[24] = svgtiny_PATH_BEZIER
;
1574 p
[25] = x
+ r
* KAPPA
;
1577 p
[28] = y
- r
* KAPPA
;
1580 p
[31] = svgtiny_PATH_CLOSE
;
1582 err
= svgtiny_add_path(p
, 32, &state
);
1584 svgtiny_cleanup_state_local(&state
);
1591 * Parse an <ellipse> element node.
1594 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
1595 struct svgtiny_parse_state state
)
1598 float x
= 0, y
= 0, rx
= -1, ry
= -1;
1603 svgtiny_setup_state_local(&state
);
1605 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
1606 if (exc
!= DOM_NO_ERR
) {
1607 svgtiny_cleanup_state_local(&state
);
1608 return svgtiny_LIBDOM_ERROR
;
1611 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1613 dom_string_unref(attr
);
1615 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
1616 if (exc
!= DOM_NO_ERR
) {
1617 svgtiny_cleanup_state_local(&state
);
1618 return svgtiny_LIBDOM_ERROR
;
1621 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1623 dom_string_unref(attr
);
1625 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
1626 if (exc
!= DOM_NO_ERR
) {
1627 svgtiny_cleanup_state_local(&state
);
1628 return svgtiny_LIBDOM_ERROR
;
1631 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1633 dom_string_unref(attr
);
1635 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
1636 if (exc
!= DOM_NO_ERR
) {
1637 svgtiny_cleanup_state_local(&state
);
1638 return svgtiny_LIBDOM_ERROR
;
1641 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1643 dom_string_unref(attr
);
1645 svgtiny_parse_paint_attributes(ellipse
, &state
);
1646 svgtiny_parse_transform_attributes(ellipse
, &state
);
1648 if (rx
< 0 || ry
< 0) {
1649 state
.diagram
->error_line
= -1; /* ellipse->line; */
1650 state
.diagram
->error_message
= "ellipse: rx or ry missing "
1652 svgtiny_cleanup_state_local(&state
);
1653 return svgtiny_SVG_ERROR
;
1655 if (rx
== 0 || ry
== 0) {
1656 svgtiny_cleanup_state_local(&state
);
1660 p
= malloc(32 * sizeof p
[0]);
1662 svgtiny_cleanup_state_local(&state
);
1663 return svgtiny_OUT_OF_MEMORY
;
1666 p
[0] = svgtiny_PATH_MOVE
;
1669 p
[3] = svgtiny_PATH_BEZIER
;
1671 p
[5] = y
+ ry
* KAPPA
;
1672 p
[6] = x
+ rx
* KAPPA
;
1676 p
[10] = svgtiny_PATH_BEZIER
;
1677 p
[11] = x
- rx
* KAPPA
;
1680 p
[14] = y
+ ry
* KAPPA
;
1683 p
[17] = svgtiny_PATH_BEZIER
;
1685 p
[19] = y
- ry
* KAPPA
;
1686 p
[20] = x
- rx
* KAPPA
;
1690 p
[24] = svgtiny_PATH_BEZIER
;
1691 p
[25] = x
+ rx
* KAPPA
;
1694 p
[28] = y
- ry
* KAPPA
;
1697 p
[31] = svgtiny_PATH_CLOSE
;
1699 err
= svgtiny_add_path(p
, 32, &state
);
1701 svgtiny_cleanup_state_local(&state
);
1708 * Parse a <line> element node.
1711 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1712 struct svgtiny_parse_state state
)
1715 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1720 svgtiny_setup_state_local(&state
);
1722 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1723 if (exc
!= DOM_NO_ERR
) {
1724 svgtiny_cleanup_state_local(&state
);
1725 return svgtiny_LIBDOM_ERROR
;
1728 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1730 dom_string_unref(attr
);
1732 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1733 if (exc
!= DOM_NO_ERR
) {
1734 svgtiny_cleanup_state_local(&state
);
1735 return svgtiny_LIBDOM_ERROR
;
1738 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1740 dom_string_unref(attr
);
1742 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1743 if (exc
!= DOM_NO_ERR
) {
1744 svgtiny_cleanup_state_local(&state
);
1745 return svgtiny_LIBDOM_ERROR
;
1748 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1750 dom_string_unref(attr
);
1752 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1753 if (exc
!= DOM_NO_ERR
) {
1754 svgtiny_cleanup_state_local(&state
);
1755 return svgtiny_LIBDOM_ERROR
;
1758 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1760 dom_string_unref(attr
);
1762 svgtiny_parse_paint_attributes(line
, &state
);
1763 svgtiny_parse_transform_attributes(line
, &state
);
1765 p
= malloc(7 * sizeof p
[0]);
1767 svgtiny_cleanup_state_local(&state
);
1768 return svgtiny_OUT_OF_MEMORY
;
1771 p
[0] = svgtiny_PATH_MOVE
;
1774 p
[3] = svgtiny_PATH_LINE
;
1777 p
[6] = svgtiny_PATH_CLOSE
;
1779 err
= svgtiny_add_path(p
, 7, &state
);
1781 svgtiny_cleanup_state_local(&state
);
1788 * Parse a <polyline> or <polygon> element node.
1790 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1791 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1794 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1795 struct svgtiny_parse_state state
, bool polygon
)
1798 dom_string
*points_str
;
1804 svgtiny_setup_state_local(&state
);
1806 svgtiny_parse_paint_attributes(poly
, &state
);
1807 svgtiny_parse_transform_attributes(poly
, &state
);
1809 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1811 if (exc
!= DOM_NO_ERR
) {
1812 svgtiny_cleanup_state_local(&state
);
1813 return svgtiny_LIBDOM_ERROR
;
1816 if (points_str
== NULL
) {
1817 state
.diagram
->error_line
= -1; /* poly->line; */
1818 state
.diagram
->error_message
=
1819 "polyline/polygon: missing points attribute";
1820 svgtiny_cleanup_state_local(&state
);
1821 return svgtiny_SVG_ERROR
;
1824 s
= points
= strndup(dom_string_data(points_str
),
1825 dom_string_byte_length(points_str
));
1826 dom_string_unref(points_str
);
1827 /* read points attribute */
1829 svgtiny_cleanup_state_local(&state
);
1830 return svgtiny_OUT_OF_MEMORY
;
1832 /* allocate space for path: it will never have more elements than s */
1833 p
= malloc(sizeof p
[0] * strlen(s
));
1836 svgtiny_cleanup_state_local(&state
);
1837 return svgtiny_OUT_OF_MEMORY
;
1840 /* parse s and build path */
1841 for (i
= 0; s
[i
]; i
++)
1849 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1851 p
[i
++] = svgtiny_PATH_MOVE
;
1853 p
[i
++] = svgtiny_PATH_LINE
;
1862 p
[i
++] = svgtiny_PATH_CLOSE
;
1866 err
= svgtiny_add_path(p
, i
, &state
);
1868 svgtiny_cleanup_state_local(&state
);
1875 * Parse a <text> or <tspan> element node.
1878 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1879 struct svgtiny_parse_state state
)
1881 float x
, y
, width
, height
;
1886 svgtiny_setup_state_local(&state
);
1888 svgtiny_parse_position_attributes(text
, state
,
1889 &x
, &y
, &width
, &height
);
1890 svgtiny_parse_font_attributes(text
, &state
);
1891 svgtiny_parse_transform_attributes(text
, &state
);
1893 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1894 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1895 /* state.ctm.e = px - state.origin_x; */
1896 /* state.ctm.f = py - state.origin_y; */
1898 exc
= dom_node_get_first_child(text
, &child
);
1899 if (exc
!= DOM_NO_ERR
) {
1900 return svgtiny_LIBDOM_ERROR
;
1901 svgtiny_cleanup_state_local(&state
);
1903 while (child
!= NULL
) {
1905 dom_node_type nodetype
;
1906 svgtiny_code code
= svgtiny_OK
;
1908 exc
= dom_node_get_node_type(child
, &nodetype
);
1909 if (exc
!= DOM_NO_ERR
) {
1910 dom_node_unref(child
);
1911 svgtiny_cleanup_state_local(&state
);
1912 return svgtiny_LIBDOM_ERROR
;
1914 if (nodetype
== DOM_ELEMENT_NODE
) {
1915 dom_string
*nodename
;
1916 exc
= dom_node_get_node_name(child
, &nodename
);
1917 if (exc
!= DOM_NO_ERR
) {
1918 dom_node_unref(child
);
1919 svgtiny_cleanup_state_local(&state
);
1920 return svgtiny_LIBDOM_ERROR
;
1922 if (dom_string_caseless_isequal(nodename
,
1923 state
.interned_tspan
))
1924 code
= svgtiny_parse_text((dom_element
*)child
,
1926 dom_string_unref(nodename
);
1927 } else if (nodetype
== DOM_TEXT_NODE
) {
1928 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1929 dom_string
*content
;
1930 if (shape
== NULL
) {
1931 dom_node_unref(child
);
1932 svgtiny_cleanup_state_local(&state
);
1933 return svgtiny_OUT_OF_MEMORY
;
1935 exc
= dom_text_get_whole_text(child
, &content
);
1936 if (exc
!= DOM_NO_ERR
) {
1937 dom_node_unref(child
);
1938 svgtiny_cleanup_state_local(&state
);
1939 return svgtiny_LIBDOM_ERROR
;
1941 if (content
!= NULL
) {
1942 shape
->text
= strndup(dom_string_data(content
),
1943 dom_string_byte_length(content
));
1944 dom_string_unref(content
);
1946 shape
->text
= strdup("");
1950 state
.diagram
->shape_count
++;
1953 if (code
!= svgtiny_OK
) {
1954 dom_node_unref(child
);
1955 svgtiny_cleanup_state_local(&state
);
1958 exc
= dom_node_get_next_sibling(child
, &next
);
1959 dom_node_unref(child
);
1960 if (exc
!= DOM_NO_ERR
) {
1961 svgtiny_cleanup_state_local(&state
);
1962 return svgtiny_LIBDOM_ERROR
;
1967 svgtiny_cleanup_state_local(&state
);
1974 * Parse x, y, width, and height attributes, if present.
1977 void svgtiny_parse_position_attributes(dom_element
*node
,
1978 const struct svgtiny_parse_state state
,
1979 float *x
, float *y
, float *width
, float *height
)
1986 *width
= state
.viewport_width
;
1987 *height
= state
.viewport_height
;
1989 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1990 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1991 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1992 dom_string_unref(attr
);
1995 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1996 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1997 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1998 dom_string_unref(attr
);
2001 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
2002 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2003 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
2005 dom_string_unref(attr
);
2008 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
2009 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2010 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
2012 dom_string_unref(attr
);
2018 * Parse a length as a number of pixels.
2021 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
2022 const struct svgtiny_parse_state state
)
2024 int num_length
= strspn(s
, "0123456789+-.");
2025 const char *unit
= s
+ num_length
;
2026 float n
= atof((const char *) s
);
2027 float font_size
= 20;
2033 } else if (unit
[0] == '%') {
2034 return n
/ 100.0 * viewport_size
;
2035 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
2036 return n
* font_size
;
2037 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
2038 return n
/ 2.0 * font_size
;
2039 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
2041 } else if (unit
[0] == 'p' && unit
[1] == 't') {
2043 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
2045 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
2046 return n
* 3.543307;
2047 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
2048 return n
* 35.43307;
2049 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
2056 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
2057 const struct svgtiny_parse_state state
)
2059 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
2060 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
2066 * Parse paint attributes, if present.
2069 void svgtiny_parse_paint_attributes(dom_element
*node
,
2070 struct svgtiny_parse_state
*state
)
2075 /* We store the result of svgtiny_parse_style_inline() in
2076 * inline_sheet, and that function returns NULL on error; in
2077 * particular you do not need to css_stylesheet_destroy() the
2078 * result if it is NULL. We initialize inline_sheet to NULL to
2079 * retain the same semantics. */
2080 css_stylesheet
*inline_sheet
= NULL
;
2082 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
2083 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2084 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
2085 dom_string_unref(attr
);
2088 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
2089 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2090 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
2091 dom_string_unref(attr
);
2094 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
2095 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2096 state
->stroke_width
= svgtiny_parse_length(attr
,
2097 state
->viewport_width
, *state
);
2098 dom_string_unref(attr
);
2101 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
2102 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2103 /* First parse the style attribute into a libcss stylesheet
2104 in case any of its properties are known to libcss. */
2105 inline_sheet
= svgtiny_parse_style_inline(
2106 (uint8_t *)dom_string_data(attr
),
2107 dom_string_byte_length(attr
));
2109 /* Parse any other properties "by hand" until they can
2110 be supported in libcss. */
2111 char *style
= strndup(dom_string_data(attr
),
2112 dom_string_byte_length(attr
));
2115 if ((s
= strstr(style
, "fill:"))) {
2119 value
= strndup(s
, strcspn(s
, "; "));
2120 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
2123 if ((s
= strstr(style
, "stroke:"))) {
2127 value
= strndup(s
, strcspn(s
, "; "));
2128 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
2131 if ((s
= strstr(style
, "stroke-width:"))) {
2135 value
= strndup(s
, strcspn(s
, "; "));
2136 state
->stroke_width
= _svgtiny_parse_length(value
,
2137 state
->viewport_width
, *state
);
2141 dom_string_unref(attr
);
2144 if (inline_sheet
!= NULL
) {
2145 css_stylesheet_destroy(inline_sheet
);
2154 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
2155 struct svgtiny_parse_state_gradient
*grad
,
2156 struct svgtiny_parse_state
*state
)
2158 unsigned int r
, g
, b
;
2160 size_t len
= strlen(s
);
2161 char *id
= 0, *rparen
;
2163 if (len
== 4 && s
[0] == '#') {
2164 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
2165 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
2167 } else if (len
== 7 && s
[0] == '#') {
2168 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
2169 *c
= svgtiny_RGB(r
, g
, b
);
2171 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
2172 s
[3] == '(' && s
[len
- 1] == ')') {
2173 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
2174 *c
= svgtiny_RGB(r
, g
, b
);
2175 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
2179 *c
= svgtiny_RGB(r
, g
, b
);
2182 } else if (len
== 4 && strcmp(s
, "none") == 0) {
2183 *c
= svgtiny_TRANSPARENT
;
2185 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
2188 *c
= svgtiny_RGB(0, 0, 0);
2189 } else if (s
[4] == '#') {
2193 rparen
= strchr(id
, ')');
2196 svgtiny_find_gradient(id
, grad
, state
);
2198 if (grad
->linear_gradient_stop_count
== 0)
2199 *c
= svgtiny_TRANSPARENT
;
2200 else if (grad
->linear_gradient_stop_count
== 1)
2201 *c
= grad
->gradient_stop
[0].color
;
2203 *c
= svgtiny_LINEAR_GRADIENT
;
2207 const struct svgtiny_named_color
*named_color
;
2208 named_color
= svgtiny_color_lookup(s
, strlen(s
));
2210 *c
= named_color
->color
;
2214 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
2215 struct svgtiny_parse_state_gradient
*grad
,
2216 struct svgtiny_parse_state
*state
)
2219 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
2220 dom_string_unref(s
);
2224 * Parse font attributes, if present.
2227 void svgtiny_parse_font_attributes(dom_element
*node
,
2228 struct svgtiny_parse_state
*state
)
2230 /* TODO: Implement this, it never used to be */
2233 #ifdef WRITTEN_THIS_PROPERLY
2234 const xmlAttr
*attr
;
2238 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
2239 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
2248 * Parse transform attributes, if present.
2250 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
2253 void svgtiny_parse_transform_attributes(dom_element
*node
,
2254 struct svgtiny_parse_state
*state
)
2260 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
2262 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
2263 transform
= strndup(dom_string_data(attr
),
2264 dom_string_byte_length(attr
));
2265 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
2266 &state
->ctm
.c
, &state
->ctm
.d
,
2267 &state
->ctm
.e
, &state
->ctm
.f
);
2269 dom_string_unref(attr
);
2275 * Parse a transform string.
2278 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
2279 float *mc
, float *md
, float *me
, float *mf
)
2281 float a
, b
, c
, d
, e
, f
;
2282 float za
, zb
, zc
, zd
, ze
, zf
;
2287 for (i
= 0; s
[i
]; i
++)
2296 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
2297 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
2299 else if ((sscanf(s
, " translate (%f %f ) %n",
2300 &e
, &f
, &n
) == 2) && (n
> 0))
2302 else if ((sscanf(s
, " translate (%f ) %n",
2303 &e
, &n
) == 1) && (n
> 0))
2305 else if ((sscanf(s
, " scale (%f %f ) %n",
2306 &a
, &d
, &n
) == 2) && (n
> 0))
2308 else if ((sscanf(s
, " scale (%f ) %n",
2309 &a
, &n
) == 1) && (n
> 0))
2311 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
2312 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
2313 angle
= angle
/ 180 * M_PI
;
2318 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
2319 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
2320 } else if ((sscanf(s
, " rotate (%f ) %n",
2321 &angle
, &n
) == 1) && (n
> 0)) {
2322 angle
= angle
/ 180 * M_PI
;
2327 } else if ((sscanf(s
, " skewX (%f ) %n",
2328 &angle
, &n
) == 1) && (n
> 0)) {
2329 angle
= angle
/ 180 * M_PI
;
2331 } else if ((sscanf(s
, " skewY (%f ) %n",
2332 &angle
, &n
) == 1) && (n
> 0)) {
2333 angle
= angle
/ 180 * M_PI
;
2337 za
= *ma
* a
+ *mc
* b
;
2338 zb
= *mb
* a
+ *md
* b
;
2339 zc
= *ma
* c
+ *mc
* d
;
2340 zd
= *mb
* c
+ *md
* d
;
2341 ze
= *ma
* e
+ *mc
* f
+ *me
;
2342 zf
= *mb
* e
+ *md
* f
+ *mf
;
2355 * Add a path to the svgtiny_diagram.
2358 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
2359 struct svgtiny_parse_state
*state
)
2361 struct svgtiny_shape
*shape
;
2363 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
2364 return svgtiny_add_path_linear_gradient(p
, n
, state
);
2366 svgtiny_transform_path(p
, n
, state
);
2368 shape
= svgtiny_add_shape(state
);
2371 return svgtiny_OUT_OF_MEMORY
;
2374 shape
->path_length
= n
;
2375 state
->diagram
->shape_count
++;
2382 * Add a svgtiny_shape to the svgtiny_diagram.
2385 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
2387 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
2388 (state
->diagram
->shape_count
+ 1) *
2389 sizeof (state
->diagram
->shape
[0]));
2392 state
->diagram
->shape
= shape
;
2394 shape
+= state
->diagram
->shape_count
;
2396 shape
->path_length
= 0;
2398 shape
->fill
= state
->fill
;
2399 shape
->stroke
= state
->stroke
;
2400 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
2401 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
2402 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
2403 shape
->stroke_width
= 1;
2410 * Apply the current transformation matrix to a path.
2413 void svgtiny_transform_path(float *p
, unsigned int n
,
2414 struct svgtiny_parse_state
*state
)
2418 for (j
= 0; j
!= n
; ) {
2419 unsigned int points
= 0;
2421 switch ((int) p
[j
]) {
2422 case svgtiny_PATH_MOVE
:
2423 case svgtiny_PATH_LINE
:
2426 case svgtiny_PATH_CLOSE
:
2429 case svgtiny_PATH_BEZIER
:
2436 for (k
= 0; k
!= points
; k
++) {
2437 float x0
= p
[j
], y0
= p
[j
+ 1];
2438 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
2440 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
2451 * Free all memory used by a diagram.
2454 void svgtiny_free(struct svgtiny_diagram
*svg
)
2459 for (i
= 0; i
!= svg
->shape_count
; i
++) {
2460 free(svg
->shape
[i
].path
);
2461 free(svg
->shape
[i
].text
);
2469 #ifndef HAVE_STRNDUP
2470 char *svgtiny_strndup(const char *s
, size_t n
)
2475 for (len
= 0; len
!= n
&& s
[len
]; len
++)
2478 s2
= malloc(len
+ 1);