1 /*
2  * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 /*
27  * Copyright notice for the EFL:
28 
29  * Copyright (C) EFL developers (see AUTHORS)
30 
31  * All rights reserved.
32 
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions are met:
35 
36  *   1. Redistributions of source code must retain the above copyright
37  *      notice, this list of conditions and the following disclaimer.
38  *   2. Redistributions in binary form must reproduce the above copyright
39  *      notice, this list of conditions and the following disclaimer in the
40  *      documentation and/or other materials provided with the distribution.
41 
42  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
43  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
44  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
45  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
46  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
48  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
49  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
50  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
51  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 */
53 
54 #include <cstring>
55 #include <fstream>
56 #include <float.h>
57 #include "tvgLoader.h"
58 #include "tvgXmlParser.h"
59 #include "tvgSvgLoader.h"
60 #include "tvgSvgSceneBuilder.h"
61 #include "tvgStr.h"
62 #include "tvgSvgCssStyle.h"
63 #include "tvgMath.h"
64 
65 /************************************************************************/
66 /* Internal Class Implementation                                        */
67 /************************************************************************/
68 
69 /*
70  * According to: https://www.w3.org/TR/SVG2/coords.html#Units
71  * and: https://www.w3.org/TR/css-values-4/#absolute-lengths
72  */
73 #define PX_PER_IN 96        //1 in = 96 px
74 #define PX_PER_PC 16        //1 pc = 1/6 in  -> PX_PER_IN/6
75 #define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72
76 #define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4
77 #define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54
78 
79 typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
80 typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func);
81 typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
82 
_skipSpace(const char * str,const char * end)83 static char* _skipSpace(const char* str, const char* end)
84 {
85     while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
86         ++str;
87     }
88     return (char*) str;
89 }
90 
91 
_copyId(const char * str)92 static char* _copyId(const char* str)
93 {
94     if (!str) return nullptr;
95     if (strlen(str) == 0) return nullptr;
96 
97     return strdup(str);
98 }
99 
100 
_skipComma(const char * content)101 static const char* _skipComma(const char* content)
102 {
103     content = _skipSpace(content, nullptr);
104     if (*content == ',') return content + 1;
105     return content;
106 }
107 
108 
_parseNumber(const char ** content,const char ** end,float * number)109 static bool _parseNumber(const char** content, const char** end, float* number)
110 {
111     const char* _end = end ? *end : nullptr;
112 
113     *number = strToFloat(*content, (char**)&_end);
114     //If the start of string is not number
115     if ((*content) == _end) {
116         if (end) *end = _end;
117         return false;
118     }
119     //Skip comma if any
120     *content = _skipComma(_end);
121     if (end) *end = _end;
122 
123     return true;
124 }
125 
126 
127 static constexpr struct
128 {
129     AspectRatioAlign align;
130     const char* tag;
131 } alignTags[] = {
132     { AspectRatioAlign::XMinYMin, "xMinYMin" },
133     { AspectRatioAlign::XMidYMin, "xMidYMin" },
134     { AspectRatioAlign::XMaxYMin, "xMaxYMin" },
135     { AspectRatioAlign::XMinYMid, "xMinYMid" },
136     { AspectRatioAlign::XMidYMid, "xMidYMid" },
137     { AspectRatioAlign::XMaxYMid, "xMaxYMid" },
138     { AspectRatioAlign::XMinYMax, "xMinYMax" },
139     { AspectRatioAlign::XMidYMax, "xMidYMax" },
140     { AspectRatioAlign::XMaxYMax, "xMaxYMax" },
141 };
142 
143 
_parseAspectRatio(const char ** content,AspectRatioAlign * align,AspectRatioMeetOrSlice * meetOrSlice)144 static void _parseAspectRatio(const char** content, AspectRatioAlign* align, AspectRatioMeetOrSlice* meetOrSlice)
145 {
146     if (!strcmp(*content, "none")) {
147         *align = AspectRatioAlign::None;
148         return;
149     }
150 
151     for (unsigned int i = 0; i < sizeof(alignTags) / sizeof(alignTags[0]); i++) {
152         if (!strncmp(*content, alignTags[i].tag, 8)) {
153             *align = alignTags[i].align;
154             *content += 8;
155             *content = _skipSpace(*content, nullptr);
156             break;
157         }
158     }
159 
160     if (!strcmp(*content, "meet")) {
161         *meetOrSlice = AspectRatioMeetOrSlice::Meet;
162     } else if (!strcmp(*content, "slice")) {
163         *meetOrSlice = AspectRatioMeetOrSlice::Slice;
164     }
165 }
166 
167 
168 /**
169  * According to https://www.w3.org/TR/SVG/coords.html#Units
170  */
_toFloat(const SvgParser * svgParse,const char * str,SvgParserLengthType type)171 static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
172 {
173     float parsedValue = strToFloat(str, nullptr);
174 
175     if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
176     else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
177     else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
178     else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
179     else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
180     else if (strstr(str, "%")) {
181         if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0f) * svgParse->global.h;
182         else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0f) * svgParse->global.w;
183         else if (type == SvgParserLengthType::Diagonal) parsedValue = (sqrtf(powf(svgParse->global.w, 2) + powf(svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
184         else //if other than it's radius
185         {
186             float max = svgParse->global.w;
187             if (max < svgParse->global.h)
188                 max = svgParse->global.h;
189             parsedValue = (parsedValue / 100.0f) * max;
190         }
191     }
192     //TODO: Implement 'em', 'ex' attributes
193 
194     return parsedValue;
195 }
196 
197 
_gradientToFloat(const SvgParser * svgParse,const char * str,bool & isPercentage)198 static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage)
199 {
200     char* end = nullptr;
201 
202     float parsedValue = strToFloat(str, &end);
203     isPercentage = false;
204 
205     if (strstr(str, "%")) {
206         parsedValue = parsedValue / 100.0;
207         isPercentage = true;
208     }
209     else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
210     else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
211     else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
212     else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
213     else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
214     //TODO: Implement 'em', 'ex' attributes
215 
216     return parsedValue;
217 }
218 
219 
_toOffset(const char * str)220 static float _toOffset(const char* str)
221 {
222     char* end = nullptr;
223     auto strEnd = str + strlen(str);
224 
225     float parsedValue = strToFloat(str, &end);
226 
227     end = _skipSpace(end, nullptr);
228     auto ptr = strstr(str, "%");
229 
230     if (ptr) {
231         parsedValue = parsedValue / 100.0;
232         if (end != ptr || (end + 1) != strEnd) return 0;
233     } else if (end != strEnd) return 0;
234 
235     return parsedValue;
236 }
237 
238 
_toOpacity(const char * str)239 static int _toOpacity(const char* str)
240 {
241     char* end = nullptr;
242     float opacity = strToFloat(str, &end);
243 
244     if (end) {
245         if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
246         else if (*end == '\0') return lrint(opacity * 255);
247     }
248     return 255;
249 }
250 
251 
_toMaskType(const char * str)252 static SvgMaskType _toMaskType(const char* str)
253 {
254     if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha;
255 
256     return SvgMaskType::Luminance;
257 }
258 
259 
260 //The default rendering order: fill, stroke, markers
261 //If any is omitted, will be rendered in its default order after the specified ones.
_toPaintOrder(const char * str)262 static bool _toPaintOrder(const char* str)
263 {
264     uint8_t position = 1;
265     uint8_t strokePosition = 0;
266     uint8_t fillPosition = 0;
267 
268     while (*str != '\0') {
269         str = _skipSpace(str, nullptr);
270         if (!strncmp(str, "fill", 4)) {
271             fillPosition = position++;
272             str += 4;
273         } else if (!strncmp(str, "stroke", 6)) {
274             strokePosition = position++;
275             str += 6;
276         } else if (!strncmp(str, "markers", 7)) {
277             str += 7;
278         } else {
279             return _toPaintOrder("fill stroke");
280         }
281     }
282 
283     if (fillPosition == 0) fillPosition = position++;
284     if (strokePosition == 0) strokePosition = position++;
285 
286     return fillPosition < strokePosition;
287 }
288 
289 
290 #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default)                        \
291     static Type _to##Name1(const char* str)                                       \
292     {                                                                             \
293         unsigned int i;                                                           \
294                                                                                   \
295         for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) {        \
296             if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name;       \
297         }                                                                         \
298         return Default;                                                           \
299     }
300 
301 
302 /* parse the line cap used during stroking a path.
303  * Value:    butt | round | square | inherit
304  * Initial:    butt
305  * https://www.w3.org/TR/SVG/painting.html
306  */
307 static constexpr struct
308 {
309     StrokeCap lineCap;
310     const char* tag;
311 } lineCapTags[] = {
312     { StrokeCap::Butt, "butt" },
313     { StrokeCap::Round, "round" },
314     { StrokeCap::Square, "square" }
315 };
316 
317 
318 _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
319 
320 
321 /* parse the line join used during stroking a path.
322  * Value:   miter | round | bevel | inherit
323  * Initial:    miter
324  * https://www.w3.org/TR/SVG/painting.html
325  */
326 static constexpr struct
327 {
328     StrokeJoin lineJoin;
329     const char* tag;
330 } lineJoinTags[] = {
331     { StrokeJoin::Miter, "miter" },
332     { StrokeJoin::Round, "round" },
333     { StrokeJoin::Bevel, "bevel" }
334 };
335 
336 
337 _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
338 
339 
340 /* parse the fill rule used during filling a path.
341  * Value:   nonzero | evenodd | inherit
342  * Initial:    nonzero
343  * https://www.w3.org/TR/SVG/painting.html
344  */
345 static constexpr struct
346 {
347     FillRule fillRule;
348     const char* tag;
349 } fillRuleTags[] = {
350     { FillRule::EvenOdd, "evenodd" }
351 };
352 
353 
_PARSE_TAG(FillRule,fillRule,FillRule,fillRuleTags,FillRule::Winding)354 _PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding)
355 
356 
357 /* parse the dash pattern used during stroking a path.
358  * Value:   none | <dasharray> | inherit
359  * Initial:    none
360  * https://www.w3.org/TR/SVG/painting.html
361  */
362 static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash)
363 {
364     if (!strncmp(str, "none", 4)) return;
365 
366     char *end = nullptr;
367 
368     while (*str) {
369         str = _skipComma(str);
370         float parsedValue = strToFloat(str, &end);
371         if (str == end) break;
372         if (parsedValue <= 0.0f) break;
373         if (*end == '%') {
374             ++end;
375             //Refers to the diagonal length of the viewport.
376             //https://www.w3.org/TR/SVG2/coords.html#Units
377             parsedValue = (sqrtf(powf(loader->svgParse->global.w, 2) + powf(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
378         }
379         (*dash).array.push(parsedValue);
380         str = end;
381     }
382     //If dash array size is 1, it means that dash and gap size are the same.
383     if ((*dash).array.count == 1) (*dash).array.push((*dash).array[0]);
384 }
385 
386 
_idFromUrl(const char * url)387 static char* _idFromUrl(const char* url)
388 {
389     auto open = strchr(url, '(');
390     auto close = strchr(url, ')');
391     if (!open || !close || open >= close) return nullptr;
392 
393     open = strchr(url, '#');
394     if (!open || open >= close) return nullptr;
395 
396     ++open;
397     --close;
398 
399     //trim the rest of the spaces if any
400     while (open < close && *close == ' ') --close;
401 
402     //quick verification
403     for (auto id = open; id < close; id++) {
404         if (*id == ' ' || *id == '\'') return nullptr;
405     }
406 
407     return strDuplicate(open, (close - open + 1));
408 }
409 
410 
_parseColor(const char * value,char ** end)411 static unsigned char _parseColor(const char* value, char** end)
412 {
413     float r;
414 
415     r = strToFloat(value, end);
416     *end = _skipSpace(*end, nullptr);
417     if (**end == '%') {
418         r = 255 * r / 100;
419         (*end)++;
420     }
421     *end = _skipSpace(*end, nullptr);
422 
423     if (r < 0 || r > 255) {
424         *end = nullptr;
425         return 0;
426     }
427 
428     return lrint(r);
429 }
430 
431 
432 static constexpr struct
433 {
434     const char* name;
435     unsigned int value;
436 } colors[] = {
437     { "aliceblue", 0xfff0f8ff },
438     { "antiquewhite", 0xfffaebd7 },
439     { "aqua", 0xff00ffff },
440     { "aquamarine", 0xff7fffd4 },
441     { "azure", 0xfff0ffff },
442     { "beige", 0xfff5f5dc },
443     { "bisque", 0xffffe4c4 },
444     { "black", 0xff000000 },
445     { "blanchedalmond", 0xffffebcd },
446     { "blue", 0xff0000ff },
447     { "blueviolet", 0xff8a2be2 },
448     { "brown", 0xffa52a2a },
449     { "burlywood", 0xffdeb887 },
450     { "cadetblue", 0xff5f9ea0 },
451     { "chartreuse", 0xff7fff00 },
452     { "chocolate", 0xffd2691e },
453     { "coral", 0xffff7f50 },
454     { "cornflowerblue", 0xff6495ed },
455     { "cornsilk", 0xfffff8dc },
456     { "crimson", 0xffdc143c },
457     { "cyan", 0xff00ffff },
458     { "darkblue", 0xff00008b },
459     { "darkcyan", 0xff008b8b },
460     { "darkgoldenrod", 0xffb8860b },
461     { "darkgray", 0xffa9a9a9 },
462     { "darkgrey", 0xffa9a9a9 },
463     { "darkgreen", 0xff006400 },
464     { "darkkhaki", 0xffbdb76b },
465     { "darkmagenta", 0xff8b008b },
466     { "darkolivegreen", 0xff556b2f },
467     { "darkorange", 0xffff8c00 },
468     { "darkorchid", 0xff9932cc },
469     { "darkred", 0xff8b0000 },
470     { "darksalmon", 0xffe9967a },
471     { "darkseagreen", 0xff8fbc8f },
472     { "darkslateblue", 0xff483d8b },
473     { "darkslategray", 0xff2f4f4f },
474     { "darkslategrey", 0xff2f4f4f },
475     { "darkturquoise", 0xff00ced1 },
476     { "darkviolet", 0xff9400d3 },
477     { "deeppink", 0xffff1493 },
478     { "deepskyblue", 0xff00bfff },
479     { "dimgray", 0xff696969 },
480     { "dimgrey", 0xff696969 },
481     { "dodgerblue", 0xff1e90ff },
482     { "firebrick", 0xffb22222 },
483     { "floralwhite", 0xfffffaf0 },
484     { "forestgreen", 0xff228b22 },
485     { "fuchsia", 0xffff00ff },
486     { "gainsboro", 0xffdcdcdc },
487     { "ghostwhite", 0xfff8f8ff },
488     { "gold", 0xffffd700 },
489     { "goldenrod", 0xffdaa520 },
490     { "gray", 0xff808080 },
491     { "grey", 0xff808080 },
492     { "green", 0xff008000 },
493     { "greenyellow", 0xffadff2f },
494     { "honeydew", 0xfff0fff0 },
495     { "hotpink", 0xffff69b4 },
496     { "indianred", 0xffcd5c5c },
497     { "indigo", 0xff4b0082 },
498     { "ivory", 0xfffffff0 },
499     { "khaki", 0xfff0e68c },
500     { "lavender", 0xffe6e6fa },
501     { "lavenderblush", 0xfffff0f5 },
502     { "lawngreen", 0xff7cfc00 },
503     { "lemonchiffon", 0xfffffacd },
504     { "lightblue", 0xffadd8e6 },
505     { "lightcoral", 0xfff08080 },
506     { "lightcyan", 0xffe0ffff },
507     { "lightgoldenrodyellow", 0xfffafad2 },
508     { "lightgray", 0xffd3d3d3 },
509     { "lightgrey", 0xffd3d3d3 },
510     { "lightgreen", 0xff90ee90 },
511     { "lightpink", 0xffffb6c1 },
512     { "lightsalmon", 0xffffa07a },
513     { "lightseagreen", 0xff20b2aa },
514     { "lightskyblue", 0xff87cefa },
515     { "lightslategray", 0xff778899 },
516     { "lightslategrey", 0xff778899 },
517     { "lightsteelblue", 0xffb0c4de },
518     { "lightyellow", 0xffffffe0 },
519     { "lime", 0xff00ff00 },
520     { "limegreen", 0xff32cd32 },
521     { "linen", 0xfffaf0e6 },
522     { "magenta", 0xffff00ff },
523     { "maroon", 0xff800000 },
524     { "mediumaquamarine", 0xff66cdaa },
525     { "mediumblue", 0xff0000cd },
526     { "mediumorchid", 0xffba55d3 },
527     { "mediumpurple", 0xff9370d8 },
528     { "mediumseagreen", 0xff3cb371 },
529     { "mediumslateblue", 0xff7b68ee },
530     { "mediumspringgreen", 0xff00fa9a },
531     { "mediumturquoise", 0xff48d1cc },
532     { "mediumvioletred", 0xffc71585 },
533     { "midnightblue", 0xff191970 },
534     { "mintcream", 0xfff5fffa },
535     { "mistyrose", 0xffffe4e1 },
536     { "moccasin", 0xffffe4b5 },
537     { "navajowhite", 0xffffdead },
538     { "navy", 0xff000080 },
539     { "oldlace", 0xfffdf5e6 },
540     { "olive", 0xff808000 },
541     { "olivedrab", 0xff6b8e23 },
542     { "orange", 0xffffa500 },
543     { "orangered", 0xffff4500 },
544     { "orchid", 0xffda70d6 },
545     { "palegoldenrod", 0xffeee8aa },
546     { "palegreen", 0xff98fb98 },
547     { "paleturquoise", 0xffafeeee },
548     { "palevioletred", 0xffd87093 },
549     { "papayawhip", 0xffffefd5 },
550     { "peachpuff", 0xffffdab9 },
551     { "peru", 0xffcd853f },
552     { "pink", 0xffffc0cb },
553     { "plum", 0xffdda0dd },
554     { "powderblue", 0xffb0e0e6 },
555     { "purple", 0xff800080 },
556     { "red", 0xffff0000 },
557     { "rosybrown", 0xffbc8f8f },
558     { "royalblue", 0xff4169e1 },
559     { "saddlebrown", 0xff8b4513 },
560     { "salmon", 0xfffa8072 },
561     { "sandybrown", 0xfff4a460 },
562     { "seagreen", 0xff2e8b57 },
563     { "seashell", 0xfffff5ee },
564     { "sienna", 0xffa0522d },
565     { "silver", 0xffc0c0c0 },
566     { "skyblue", 0xff87ceeb },
567     { "slateblue", 0xff6a5acd },
568     { "slategray", 0xff708090 },
569     { "slategrey", 0xff708090 },
570     { "snow", 0xfffffafa },
571     { "springgreen", 0xff00ff7f },
572     { "steelblue", 0xff4682b4 },
573     { "tan", 0xffd2b48c },
574     { "teal", 0xff008080 },
575     { "thistle", 0xffd8bfd8 },
576     { "tomato", 0xffff6347 },
577     { "turquoise", 0xff40e0d0 },
578     { "violet", 0xffee82ee },
579     { "wheat", 0xfff5deb3 },
580     { "white", 0xffffffff },
581     { "whitesmoke", 0xfff5f5f5 },
582     { "yellow", 0xffffff00 },
583     { "yellowgreen", 0xff9acd32 }
584 };
585 
586 
_hslToRgb(float hue,float saturation,float brightness,uint8_t * red,uint8_t * green,uint8_t * blue)587 static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue)
588 {
589     if (!red || !green || !blue) return false;
590 
591     float sv, vsf, f, p, q, t, v;
592     float _red = 0, _green = 0, _blue = 0;
593     uint32_t i = 0;
594 
595     while (hue < 0) hue += 360.0f;
596     hue = fmod(hue, 360.0f);
597     saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
598     brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
599 
600     if (tvg::zero(saturation))  _red = _green = _blue = brightness;
601     else {
602         if (tvg::equal(hue, 360.0)) hue = 0.0f;
603         hue /= 60.0f;
604 
605         v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
606         p = brightness + brightness - v;
607 
608         if (!tvg::zero(v)) sv = (v - p) / v;
609         else sv = 0;
610 
611         i = static_cast<uint8_t>(hue);
612         f = hue - i;
613 
614         vsf = v * sv * f;
615 
616         t = p + vsf;
617         q = v - vsf;
618 
619         switch (i) {
620             case 0: {
621                 _red = v;
622                 _green = t;
623                 _blue = p;
624                 break;
625             }
626             case 1: {
627                 _red = q;
628                 _green = v;
629                 _blue = p;
630                 break;
631             }
632             case 2: {
633                 _red = p;
634                 _green = v;
635                 _blue = t;
636                 break;
637             }
638             case 3: {
639                 _red = p;
640                 _green = q;
641                 _blue = v;
642                 break;
643             }
644             case 4: {
645                 _red = t;
646                 _green = p;
647                 _blue = v;
648                 break;
649             }
650             case 5: {
651                 _red = v;
652                 _green = p;
653                 _blue = q;
654                 break;
655             }
656         }
657     }
658 
659     *red = (uint8_t)nearbyint(_red * 255.0f);
660     *green = (uint8_t)nearbyint(_green * 255.0f);
661     *blue = (uint8_t)nearbyint(_blue * 255.0f);
662 
663     return true;
664 }
665 
666 
_toColor(const char * str,uint8_t * r,uint8_t * g,uint8_t * b,char ** ref)667 static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
668 {
669     unsigned int len = strlen(str);
670     char *red, *green, *blue;
671     unsigned char tr, tg, tb;
672 
673     if (len == 4 && str[0] == '#') {
674         //Case for "#456" should be interpreted as "#445566"
675         if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
676             char tmp[3] = { '\0', '\0', '\0' };
677             tmp[0] = str[1];
678             tmp[1] = str[1];
679             *r = strtol(tmp, nullptr, 16);
680             tmp[0] = str[2];
681             tmp[1] = str[2];
682             *g = strtol(tmp, nullptr, 16);
683             tmp[0] = str[3];
684             tmp[1] = str[3];
685             *b = strtol(tmp, nullptr, 16);
686         }
687         return true;
688     } else if (len == 7 && str[0] == '#') {
689         if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
690             char tmp[3] = { '\0', '\0', '\0' };
691             tmp[0] = str[1];
692             tmp[1] = str[2];
693             *r = strtol(tmp, nullptr, 16);
694             tmp[0] = str[3];
695             tmp[1] = str[4];
696             *g = strtol(tmp, nullptr, 16);
697             tmp[0] = str[5];
698             tmp[1] = str[6];
699             *b = strtol(tmp, nullptr, 16);
700         }
701         return true;
702     } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
703         tr = _parseColor(str + 4, &red);
704         if (red && *red == ',') {
705             tg = _parseColor(red + 1, &green);
706             if (green && *green == ',') {
707                 tb = _parseColor(green + 1, &blue);
708                 if (blue && blue[0] == ')' && blue[1] == '\0') {
709                     *r = tr;
710                     *g = tg;
711                     *b = tb;
712                 }
713             }
714         }
715         return true;
716     } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
717         if (*ref) free(*ref);
718         *ref = _idFromUrl((const char*)(str + 3));
719         return true;
720     } else if (len >= 10 && (str[0] == 'h' || str[0] == 'H') && (str[1] == 's' || str[1] == 'S') && (str[2] == 'l' || str[2] == 'L') && str[3] == '(' && str[len - 1] == ')') {
721         float th, ts, tb;
722         const char* content = _skipSpace(str + 4, nullptr);
723         const char* hue = nullptr;
724         if (_parseNumber(&content, &hue, &th) && hue) {
725             const char* saturation = nullptr;
726             hue = _skipSpace(hue, nullptr);
727             hue = (char*)_skipComma(hue);
728             hue = _skipSpace(hue, nullptr);
729             if (_parseNumber(&hue, &saturation, &ts) && saturation && *saturation == '%') {
730                 const char* brightness = nullptr;
731                 ts /= 100.0f;
732                 saturation = _skipSpace(saturation + 1, nullptr);
733                 saturation = (char*)_skipComma(saturation);
734                 saturation = _skipSpace(saturation, nullptr);
735                 if (_parseNumber(&saturation, &brightness, &tb) && brightness && *brightness == '%') {
736                     tb /= 100.0f;
737                     brightness = _skipSpace(brightness + 1, nullptr);
738                     if (brightness && brightness[0] == ')' && brightness[1] == '\0') {
739                        return _hslToRgb(th, ts, tb, r, g, b);
740                     }
741                 }
742             }
743         }
744     } else {
745         //Handle named color
746         for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
747             if (!strcasecmp(colors[i].name, str)) {
748                 *r = (((uint8_t*)(&(colors[i].value)))[2]);
749                 *g = (((uint8_t*)(&(colors[i].value)))[1]);
750                 *b = (((uint8_t*)(&(colors[i].value)))[0]);
751                 return true;
752             }
753         }
754     }
755     return false;
756 }
757 
758 
_parseNumbersArray(char * str,float * points,int * ptCount,int len)759 static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
760 {
761     int count = 0;
762     char* end = nullptr;
763 
764     str = _skipSpace(str, nullptr);
765     while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
766         points[count++] = strToFloat(str, &end);
767         str = end;
768         str = _skipSpace(str, nullptr);
769         if (*str == ',') ++str;
770         //Eat the rest of space
771         str = _skipSpace(str, nullptr);
772     }
773     *ptCount = count;
774     return str;
775 }
776 
777 
778 enum class MatrixState {
779     Unknown,
780     Matrix,
781     Translate,
782     Rotate,
783     Scale,
784     SkewX,
785     SkewY
786 };
787 
788 
789 #define MATRIX_DEF(Name, Value)     \
790     {                               \
791 #Name, sizeof(#Name), Value \
792     }
793 
794 
795 static constexpr struct
796 {
797     const char* tag;
798     int sz;
799     MatrixState state;
800 } matrixTags[] = {
801     MATRIX_DEF(matrix, MatrixState::Matrix),
802     MATRIX_DEF(translate, MatrixState::Translate),
803     MATRIX_DEF(rotate, MatrixState::Rotate),
804     MATRIX_DEF(scale, MatrixState::Scale),
805     MATRIX_DEF(skewX, MatrixState::SkewX),
806     MATRIX_DEF(skewY, MatrixState::SkewY)
807 };
808 
809 
810 /* parse transform attribute
811  * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
812  */
_parseTransformationMatrix(const char * value)813 static Matrix* _parseTransformationMatrix(const char* value)
814 {
815     const int POINT_CNT = 8;
816 
817     auto matrix = (Matrix*)malloc(sizeof(Matrix));
818     if (!matrix) return nullptr;
819     *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
820 
821     float points[POINT_CNT];
822     int ptCount = 0;
823     char* str = (char*)value;
824     char* end = str + strlen(str);
825 
826     while (str < end) {
827         auto state = MatrixState::Unknown;
828 
829         if (isspace(*str) || (*str == ',')) {
830             ++str;
831             continue;
832         }
833         for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
834             if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
835                 state = matrixTags[i].state;
836                 str += (matrixTags[i].sz - 1);
837                 break;
838             }
839         }
840         if (state == MatrixState::Unknown) goto error;
841 
842         str = _skipSpace(str, end);
843         if (*str != '(') goto error;
844         ++str;
845         str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
846         if (*str != ')') goto error;
847         ++str;
848 
849         if (state == MatrixState::Matrix) {
850             if (ptCount != 6) goto error;
851             Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
852             *matrix *= tmp;
853         } else if (state == MatrixState::Translate) {
854             if (ptCount == 1) {
855                 Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
856                 *matrix *= tmp;
857             } else if (ptCount == 2) {
858                 Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
859                 *matrix *= tmp;
860             } else goto error;
861         } else if (state == MatrixState::Rotate) {
862             //Transform to signed.
863             points[0] = fmodf(points[0], 360.0f);
864             if (points[0] < 0) points[0] += 360.0f;
865             auto c = cosf(deg2rad(points[0]));
866             auto s = sinf(deg2rad(points[0]));
867             if (ptCount == 1) {
868                 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
869                 *matrix *= tmp;
870             } else if (ptCount == 3) {
871                 Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
872                 *matrix *= tmp;
873                 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
874                 *matrix *= tmp;
875                 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
876                 *matrix *= tmp;
877             } else {
878                 goto error;
879             }
880         } else if (state == MatrixState::Scale) {
881             if (ptCount < 1 || ptCount > 2) goto error;
882             auto sx = points[0];
883             auto sy = sx;
884             if (ptCount == 2) sy = points[1];
885             Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
886             *matrix *= tmp;
887         } else if (state == MatrixState::SkewX) {
888             if (ptCount != 1) goto error;
889             auto deg = tanf(deg2rad(points[0]));
890             Matrix tmp = { 1, deg, 0, 0, 1, 0, 0, 0, 1 };
891             *matrix *= tmp;
892         } else if (state == MatrixState::SkewY) {
893             if (ptCount != 1) goto error;
894             auto deg = tanf(deg2rad(points[0]));
895             Matrix tmp = { 1, 0, 0, deg, 1, 0, 0, 0, 1 };
896             *matrix *= tmp;
897         }
898     }
899     return matrix;
900 error:
901     if (matrix) free(matrix);
902     return nullptr;
903 }
904 
905 
906 #define LENGTH_DEF(Name, Value)     \
907     {                               \
908 #Name, sizeof(#Name), Value \
909     }
910 
911 
_postpone(Array<SvgNodeIdPair> & nodes,SvgNode * node,char * id)912 static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
913 {
914     nodes.push({node, id});
915 }
916 
917 
918 /*
919 // TODO - remove?
920 static constexpr struct
921 {
922     const char* tag;
923     int sz;
924     SvgLengthType type;
925 } lengthTags[] = {
926     LENGTH_DEF(%, SvgLengthType::Percent),
927     LENGTH_DEF(px, SvgLengthType::Px),
928     LENGTH_DEF(pc, SvgLengthType::Pc),
929     LENGTH_DEF(pt, SvgLengthType::Pt),
930     LENGTH_DEF(mm, SvgLengthType::Mm),
931     LENGTH_DEF(cm, SvgLengthType::Cm),
932     LENGTH_DEF(in, SvgLengthType::In)
933 };
934 
935 static float _parseLength(const char* str, SvgLengthType* type)
936 {
937     float value;
938     int sz = strlen(str);
939 
940     *type = SvgLengthType::Px;
941     for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
942         if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
943     }
944     value = svgUtilStrtof(str, nullptr);
945     return value;
946 }
947 */
948 
949 static bool _parseStyleAttr(void* data, const char* key, const char* value);
950 static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
951 
952 
_attrParseSvgNode(void * data,const char * key,const char * value)953 static bool _attrParseSvgNode(void* data, const char* key, const char* value)
954 {
955     SvgLoaderData* loader = (SvgLoaderData*)data;
956     SvgNode* node = loader->svgParse->node;
957     SvgDocNode* doc = &(node->node.doc);
958 
959     if (!strcmp(key, "width")) {
960         doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
961         if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
962             doc->viewFlag = (doc->viewFlag | SvgViewFlag::WidthInPercent);
963         } else {
964             doc->viewFlag = (doc->viewFlag | SvgViewFlag::Width);
965         }
966     } else if (!strcmp(key, "height")) {
967         doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
968         if (strstr(value, "%") && !(doc->viewFlag & SvgViewFlag::Viewbox)) {
969             doc->viewFlag = (doc->viewFlag | SvgViewFlag::HeightInPercent);
970         } else {
971             doc->viewFlag = (doc->viewFlag | SvgViewFlag::Height);
972         }
973     } else if (!strcmp(key, "viewBox")) {
974         if (_parseNumber(&value, nullptr, &doc->vx)) {
975             if (_parseNumber(&value, nullptr, &doc->vy)) {
976                 if (_parseNumber(&value, nullptr, &doc->vw)) {
977                     if (_parseNumber(&value, nullptr, &doc->vh)) {
978                         doc->viewFlag = (doc->viewFlag | SvgViewFlag::Viewbox);
979                         loader->svgParse->global.h = doc->vh;
980                     }
981                     loader->svgParse->global.w = doc->vw;
982                 }
983                 loader->svgParse->global.y = doc->vy;
984             }
985             loader->svgParse->global.x = doc->vx;
986         }
987         if ((doc->viewFlag & SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
988             doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
989             TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
990         }
991         if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
992             loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
993             loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
994         }
995     } else if (!strcmp(key, "preserveAspectRatio")) {
996         _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
997     } else if (!strcmp(key, "style")) {
998         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
999 #ifdef THORVG_LOG_ENABLED
1000     } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(strToFloat(value, nullptr)) > FLOAT_EPSILON) {
1001         TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
1002 #endif
1003     } else {
1004         return _parseStyleAttr(loader, key, value, false);
1005     }
1006     return true;
1007 }
1008 
1009 
1010 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
_handlePaintAttr(SvgPaint * paint,const char * value)1011 static void _handlePaintAttr(SvgPaint* paint, const char* value)
1012 {
1013     if (!strcmp(value, "none")) {
1014         //No paint property
1015         paint->none = true;
1016         return;
1017     }
1018     if (!strcmp(value, "currentColor")) {
1019         paint->curColor = true;
1020         paint->none = false;
1021         return;
1022     }
1023     if (_toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url)) paint->none = false;
1024 }
1025 
1026 
_handleColorAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1027 static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1028 {
1029     SvgStyleProperty* style = node->style;
1030     if (_toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr)) {
1031         style->curColorSet = true;
1032     }
1033 }
1034 
1035 
_handleFillAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1036 static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1037 {
1038     SvgStyleProperty* style = node->style;
1039     style->fill.flags = (style->fill.flags | SvgFillFlags::Paint);
1040     _handlePaintAttr(&style->fill.paint, value);
1041 }
1042 
1043 
_handleStrokeAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1044 static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1045 {
1046     SvgStyleProperty* style = node->style;
1047     style->stroke.flags = (style->stroke.flags | SvgStrokeFlags::Paint);
1048     _handlePaintAttr(&style->stroke.paint, value);
1049 }
1050 
1051 
_handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1052 static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1053 {
1054     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Opacity);
1055     node->style->stroke.opacity = _toOpacity(value);
1056 }
1057 
_handleStrokeDashArrayAttr(SvgLoaderData * loader,SvgNode * node,const char * value)1058 static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
1059 {
1060     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Dash);
1061     _parseDashArray(loader, value, &node->style->stroke.dash);
1062 }
1063 
_handleStrokeDashOffsetAttr(SvgLoaderData * loader,SvgNode * node,const char * value)1064 static void _handleStrokeDashOffsetAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
1065 {
1066     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::DashOffset);
1067     node->style->stroke.dash.offset = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1068 }
1069 
_handleStrokeWidthAttr(SvgLoaderData * loader,SvgNode * node,const char * value)1070 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
1071 {
1072     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Width);
1073     node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Diagonal);
1074 }
1075 
1076 
_handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1077 static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1078 {
1079     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Cap);
1080     node->style->stroke.cap = _toLineCap(value);
1081 }
1082 
1083 
_handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1084 static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1085 {
1086     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Join);
1087     node->style->stroke.join = _toLineJoin(value);
1088 }
1089 
_handleStrokeMiterlimitAttr(SvgLoaderData * loader,SvgNode * node,const char * value)1090 static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
1091 {
1092     char* end = nullptr;
1093     const float miterlimit = strToFloat(value, &end);
1094 
1095     // https://www.w3.org/TR/SVG2/painting.html#LineJoin
1096     // - A negative value for stroke-miterlimit must be treated as an illegal value.
1097     if (miterlimit < 0.0f) {
1098         TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.",
1099             node->style->stroke.miterlimit, miterlimit);
1100         return;
1101     }
1102 
1103     node->style->stroke.flags = (node->style->stroke.flags | SvgStrokeFlags::Miterlimit);
1104     node->style->stroke.miterlimit = miterlimit;
1105 }
1106 
_handleFillRuleAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1107 static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1108 {
1109     node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::FillRule);
1110     node->style->fill.fillRule = _toFillRule(value);
1111 }
1112 
1113 
_handleOpacityAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1114 static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1115 {
1116     node->style->flags = (node->style->flags | SvgStyleFlags::Opacity);
1117     node->style->opacity = _toOpacity(value);
1118 }
1119 
1120 
_handleFillOpacityAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1121 static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1122 {
1123     node->style->fill.flags = (node->style->fill.flags | SvgFillFlags::Opacity);
1124     node->style->fill.opacity = _toOpacity(value);
1125 }
1126 
1127 
_handleTransformAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1128 static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1129 {
1130     node->transform = _parseTransformationMatrix(value);
1131 }
1132 
1133 
_handleClipPathAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1134 static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1135 {
1136     SvgStyleProperty* style = node->style;
1137     int len = strlen(value);
1138     if (len >= 3 && !strncmp(value, "url", 3)) {
1139         if (style->clipPath.url) free(style->clipPath.url);
1140         style->clipPath.url = _idFromUrl((const char*)(value + 3));
1141     }
1142 }
1143 
1144 
_handleMaskAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1145 static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1146 {
1147     SvgStyleProperty* style = node->style;
1148     int len = strlen(value);
1149     if (len >= 3 && !strncmp(value, "url", 3)) {
1150         if (style->mask.url) free(style->mask.url);
1151         style->mask.url = _idFromUrl((const char*)(value + 3));
1152     }
1153 }
1154 
1155 
_handleMaskTypeAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1156 static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1157 {
1158     node->node.mask.type = _toMaskType(value);
1159 }
1160 
1161 
_handleDisplayAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1162 static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1163 {
1164     //TODO : The display attribute can have various values as well as "none".
1165     //       The default is "inline" which means visible and "none" means invisible.
1166     //       Depending on the type of node, additional functionality may be required.
1167     //       refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
1168     node->style->flags = (node->style->flags | SvgStyleFlags::Display);
1169     if (!strcmp(value, "none")) node->style->display = false;
1170     else node->style->display = true;
1171 }
1172 
1173 
_handlePaintOrderAttr(TVG_UNUSED SvgLoaderData * loader,SvgNode * node,const char * value)1174 static void _handlePaintOrderAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
1175 {
1176     node->style->flags = (node->style->flags | SvgStyleFlags::PaintOrder);
1177     node->style->paintOrder = _toPaintOrder(value);
1178 }
1179 
1180 
_handleCssClassAttr(SvgLoaderData * loader,SvgNode * node,const char * value)1181 static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
1182 {
1183     auto cssClass = &node->style->cssClass;
1184 
1185     if (*cssClass && value) free(*cssClass);
1186     *cssClass = _copyId(value);
1187 
1188     bool cssClassFound = false;
1189 
1190     //css styling: tag.name has higher priority than .name
1191     if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) {
1192         cssClassFound = true;
1193         cssCopyStyleAttr(node, cssNode);
1194     }
1195     if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) {
1196         cssClassFound = true;
1197         cssCopyStyleAttr(node, cssNode);
1198     }
1199 
1200     if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass);
1201 }
1202 
1203 
1204 typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
1205 
1206 #define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
1207 
1208 
1209 static constexpr struct
1210 {
1211     const char* tag;
1212     int sz;
1213     styleMethod tagHandler;
1214     SvgStyleFlags flag;
1215 } styleTags[] = {
1216     STYLE_DEF(color, Color, SvgStyleFlags::Color),
1217     STYLE_DEF(fill, Fill, SvgStyleFlags::Fill),
1218     STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule),
1219     STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity),
1220     STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity),
1221     STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
1222     STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
1223     STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
1224     STYLE_DEF(stroke-miterlimit, StrokeMiterlimit, SvgStyleFlags::StrokeMiterlimit),
1225     STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
1226     STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
1227     STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
1228     STYLE_DEF(stroke-dashoffset, StrokeDashOffset, SvgStyleFlags::StrokeDashOffset),
1229     STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
1230     STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
1231     STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
1232     STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
1233     STYLE_DEF(display, Display, SvgStyleFlags::Display),
1234     STYLE_DEF(paint-order, PaintOrder, SvgStyleFlags::PaintOrder)
1235 };
1236 
1237 
_parseStyleAttr(void * data,const char * key,const char * value,bool style)1238 static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style)
1239 {
1240     SvgLoaderData* loader = (SvgLoaderData*)data;
1241     SvgNode* node = loader->svgParse->node;
1242     int sz;
1243     if (!key || !value) return false;
1244 
1245     //Trim the white space
1246     key = _skipSpace(key, nullptr);
1247     value = _skipSpace(value, nullptr);
1248 
1249     sz = strlen(key);
1250     for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
1251         if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
1252             bool importance = false;
1253             if (auto ptr = strstr(value, "!important")) {
1254                 size_t size = ptr - value;
1255                 while (size > 0 && isspace(value[size - 1])) {
1256                     size--;
1257                 }
1258                 value = strDuplicate(value, size);
1259                 importance = true;
1260             }
1261             if (style) {
1262                 if (importance || !(node->style->flagsImportance & styleTags[i].flag)) {
1263                     styleTags[i].tagHandler(loader, node, value);
1264                     node->style->flags = (node->style->flags | styleTags[i].flag);
1265                 }
1266             } else if (!(node->style->flags & styleTags[i].flag)) {
1267                 styleTags[i].tagHandler(loader, node, value);
1268             }
1269             if (importance) {
1270                 node->style->flagsImportance = (node->style->flags | styleTags[i].flag);
1271                 free(const_cast<char*>(value));
1272             }
1273             return true;
1274         }
1275     }
1276 
1277     return false;
1278 }
1279 
1280 
_parseStyleAttr(void * data,const char * key,const char * value)1281 static bool _parseStyleAttr(void* data, const char* key, const char* value)
1282 {
1283     return _parseStyleAttr(data, key, value, true);
1284 }
1285 
1286 
1287 /* parse g node
1288  * https://www.w3.org/TR/SVG/struct.html#Groups
1289  */
_attrParseGNode(void * data,const char * key,const char * value)1290 static bool _attrParseGNode(void* data, const char* key, const char* value)
1291 {
1292     SvgLoaderData* loader = (SvgLoaderData*)data;
1293     SvgNode* node = loader->svgParse->node;
1294 
1295     if (!strcmp(key, "style")) {
1296         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1297     } else if (!strcmp(key, "transform")) {
1298         node->transform = _parseTransformationMatrix(value);
1299     } else if (!strcmp(key, "id")) {
1300         if (node->id && value) free(node->id);
1301         node->id = _copyId(value);
1302     } else if (!strcmp(key, "class")) {
1303         _handleCssClassAttr(loader, node, value);
1304     } else if (!strcmp(key, "clip-path")) {
1305         _handleClipPathAttr(loader, node, value);
1306     } else if (!strcmp(key, "mask")) {
1307         _handleMaskAttr(loader, node, value);
1308     } else {
1309         return _parseStyleAttr(loader, key, value, false);
1310     }
1311     return true;
1312 }
1313 
1314 
1315 /* parse clipPath node
1316  * https://www.w3.org/TR/SVG/struct.html#Groups
1317  */
_attrParseClipPathNode(void * data,const char * key,const char * value)1318 static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
1319 {
1320     SvgLoaderData* loader = (SvgLoaderData*)data;
1321     SvgNode* node = loader->svgParse->node;
1322     SvgClipNode* clip = &(node->node.clip);
1323 
1324     if (!strcmp(key, "style")) {
1325         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1326     } else if (!strcmp(key, "transform")) {
1327         node->transform = _parseTransformationMatrix(value);
1328     } else if (!strcmp(key, "id")) {
1329         if (node->id && value) free(node->id);
1330         node->id = _copyId(value);
1331     } else if (!strcmp(key, "class")) {
1332         _handleCssClassAttr(loader, node, value);
1333     } else if (!strcmp(key, "clipPathUnits")) {
1334         if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false;
1335     } else {
1336         return _parseStyleAttr(loader, key, value, false);
1337     }
1338     return true;
1339 }
1340 
1341 
_attrParseMaskNode(void * data,const char * key,const char * value)1342 static bool _attrParseMaskNode(void* data, const char* key, const char* value)
1343 {
1344     SvgLoaderData* loader = (SvgLoaderData*)data;
1345     SvgNode* node = loader->svgParse->node;
1346     SvgMaskNode* mask = &(node->node.mask);
1347 
1348     if (!strcmp(key, "style")) {
1349         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1350     } else if (!strcmp(key, "transform")) {
1351         node->transform = _parseTransformationMatrix(value);
1352     } else if (!strcmp(key, "id")) {
1353         if (node->id && value) free(node->id);
1354         node->id = _copyId(value);
1355     } else if (!strcmp(key, "class")) {
1356         _handleCssClassAttr(loader, node, value);
1357     } else if (!strcmp(key, "maskContentUnits")) {
1358         if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false;
1359     } else if (!strcmp(key, "mask-type")) {
1360         mask->type = _toMaskType(value);
1361     } else {
1362         return _parseStyleAttr(loader, key, value, false);
1363     }
1364     return true;
1365 }
1366 
1367 
_attrParseCssStyleNode(void * data,const char * key,const char * value)1368 static bool _attrParseCssStyleNode(void* data, const char* key, const char* value)
1369 {
1370     SvgLoaderData* loader = (SvgLoaderData*)data;
1371     SvgNode* node = loader->svgParse->node;
1372 
1373     if (!strcmp(key, "id")) {
1374         if (node->id && value) free(node->id);
1375         node->id = _copyId(value);
1376     } else {
1377         return _parseStyleAttr(loader, key, value, false);
1378     }
1379     return true;
1380 }
1381 
1382 
_attrParseSymbolNode(void * data,const char * key,const char * value)1383 static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
1384 {
1385     SvgLoaderData* loader = (SvgLoaderData*)data;
1386     SvgNode* node = loader->svgParse->node;
1387     SvgSymbolNode* symbol = &(node->node.symbol);
1388 
1389     if (!strcmp(key, "viewBox")) {
1390         if (!_parseNumber(&value, nullptr, &symbol->vx) || !_parseNumber(&value, nullptr, &symbol->vy)) return false;
1391         if (!_parseNumber(&value, nullptr, &symbol->vw) || !_parseNumber(&value, nullptr, &symbol->vh)) return false;
1392         symbol->hasViewBox = true;
1393     } else if (!strcmp(key, "width")) {
1394         symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1395         symbol->hasWidth = true;
1396     } else if (!strcmp(key, "height")) {
1397         symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1398         symbol->hasHeight = true;
1399     } else if (!strcmp(key, "preserveAspectRatio")) {
1400         _parseAspectRatio(&value, &symbol->align, &symbol->meetOrSlice);
1401     } else if (!strcmp(key, "overflow")) {
1402         if (!strcmp(value, "visible")) symbol->overflowVisible = true;
1403     } else {
1404         return _attrParseGNode(data, key, value);
1405     }
1406 
1407     return true;
1408 }
1409 
1410 
_createNode(SvgNode * parent,SvgNodeType type)1411 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
1412 {
1413     SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
1414 
1415     if (!node) return nullptr;
1416 
1417     //Default fill property
1418     node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
1419 
1420     if (!node->style) {
1421         free(node);
1422         return nullptr;
1423     }
1424 
1425     //Update the default value of stroke and fill
1426     //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
1427     node->style->fill.paint.none = false;
1428     //Default fill opacity is 1
1429     node->style->fill.opacity = 255;
1430     node->style->opacity = 255;
1431     //Default current color is not set
1432     node->style->fill.paint.curColor = false;
1433     node->style->curColorSet = false;
1434     //Default fill rule is nonzero
1435     node->style->fill.fillRule = FillRule::Winding;
1436 
1437     //Default stroke is none
1438     node->style->stroke.paint.none = true;
1439     //Default stroke opacity is 1
1440     node->style->stroke.opacity = 255;
1441     //Default stroke current color is not set
1442     node->style->stroke.paint.curColor = false;
1443     //Default stroke width is 1
1444     node->style->stroke.width = 1;
1445     //Default line cap is butt
1446     node->style->stroke.cap = StrokeCap::Butt;
1447     //Default line join is miter
1448     node->style->stroke.join = StrokeJoin::Miter;
1449     node->style->stroke.miterlimit = 4.0f;
1450     node->style->stroke.scale = 1.0;
1451 
1452     node->style->paintOrder = _toPaintOrder("fill stroke");
1453 
1454     //Default display is true("inline").
1455     node->style->display = true;
1456 
1457     node->parent = parent;
1458     node->type = type;
1459 
1460     if (parent) parent->child.push(node);
1461     return node;
1462 }
1463 
1464 
_createDefsNode(TVG_UNUSED SvgLoaderData * loader,TVG_UNUSED SvgNode * parent,const char * buf,unsigned bufLength,TVG_UNUSED parseAttributes func)1465 static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func)
1466 {
1467     if (loader->def && loader->doc->node.doc.defs) return loader->def;
1468     SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
1469 
1470     loader->def = node;
1471     loader->doc->node.doc.defs = node;
1472     return node;
1473 }
1474 
1475 
_createGNode(TVG_UNUSED SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1476 static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1477 {
1478     loader->svgParse->node = _createNode(parent, SvgNodeType::G);
1479     if (!loader->svgParse->node) return nullptr;
1480 
1481     func(buf, bufLength, _attrParseGNode, loader);
1482     return loader->svgParse->node;
1483 }
1484 
1485 
_createSvgNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1486 static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1487 {
1488     loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
1489     if (!loader->svgParse->node) return nullptr;
1490     SvgDocNode* doc = &(loader->svgParse->node->node.doc);
1491 
1492     loader->svgParse->global.w = 1.0f;
1493     loader->svgParse->global.h = 1.0f;
1494 
1495     doc->align = AspectRatioAlign::XMidYMid;
1496     doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
1497     doc->viewFlag = SvgViewFlag::None;
1498     func(buf, bufLength, _attrParseSvgNode, loader);
1499 
1500     if (!(doc->viewFlag & SvgViewFlag::Viewbox)) {
1501         if (doc->viewFlag & SvgViewFlag::Width) {
1502             loader->svgParse->global.w = doc->w;
1503         }
1504         if (doc->viewFlag & SvgViewFlag::Height) {
1505             loader->svgParse->global.h = doc->h;
1506         }
1507     }
1508     return loader->svgParse->node;
1509 }
1510 
1511 
_createMaskNode(SvgLoaderData * loader,SvgNode * parent,TVG_UNUSED const char * buf,TVG_UNUSED unsigned bufLength,parseAttributes func)1512 static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func)
1513 {
1514     loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
1515     if (!loader->svgParse->node) return nullptr;
1516 
1517     loader->svgParse->node->node.mask.userSpace = true;
1518     loader->svgParse->node->node.mask.type = SvgMaskType::Luminance;
1519 
1520     func(buf, bufLength, _attrParseMaskNode, loader);
1521 
1522     return loader->svgParse->node;
1523 }
1524 
1525 
_createClipPathNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1526 static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1527 {
1528     loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
1529     if (!loader->svgParse->node) return nullptr;
1530 
1531     loader->svgParse->node->style->display = false;
1532     loader->svgParse->node->node.clip.userSpace = true;
1533 
1534     func(buf, bufLength, _attrParseClipPathNode, loader);
1535 
1536     return loader->svgParse->node;
1537 }
1538 
1539 
_createCssStyleNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1540 static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1541 {
1542     loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle);
1543     if (!loader->svgParse->node) return nullptr;
1544 
1545     func(buf, bufLength, _attrParseCssStyleNode, loader);
1546 
1547     return loader->svgParse->node;
1548 }
1549 
1550 
_createSymbolNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1551 static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1552 {
1553     loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
1554     if (!loader->svgParse->node) return nullptr;
1555 
1556     loader->svgParse->node->node.symbol.align = AspectRatioAlign::XMidYMid;
1557     loader->svgParse->node->node.symbol.meetOrSlice = AspectRatioMeetOrSlice::Meet;
1558     loader->svgParse->node->node.symbol.overflowVisible = false;
1559 
1560     loader->svgParse->node->node.symbol.hasViewBox = false;
1561     loader->svgParse->node->node.symbol.hasWidth = false;
1562     loader->svgParse->node->node.symbol.hasHeight = false;
1563     loader->svgParse->node->node.symbol.vx = 0.0f;
1564     loader->svgParse->node->node.symbol.vy = 0.0f;
1565 
1566     func(buf, bufLength, _attrParseSymbolNode, loader);
1567 
1568     return loader->svgParse->node;
1569 }
1570 
1571 
_attrParsePathNode(void * data,const char * key,const char * value)1572 static bool _attrParsePathNode(void* data, const char* key, const char* value)
1573 {
1574     SvgLoaderData* loader = (SvgLoaderData*)data;
1575     SvgNode* node = loader->svgParse->node;
1576     SvgPathNode* path = &(node->node.path);
1577 
1578     if (!strcmp(key, "d")) {
1579         if (path->path) free(path->path);
1580         //Temporary: need to copy
1581         path->path = _copyId(value);
1582     } else if (!strcmp(key, "style")) {
1583         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1584     } else if (!strcmp(key, "clip-path")) {
1585         _handleClipPathAttr(loader, node, value);
1586     } else if (!strcmp(key, "mask")) {
1587         _handleMaskAttr(loader, node, value);
1588     } else if (!strcmp(key, "id")) {
1589         if (node->id && value) free(node->id);
1590         node->id = _copyId(value);
1591     } else if (!strcmp(key, "class")) {
1592         _handleCssClassAttr(loader, node, value);
1593     } else {
1594         return _parseStyleAttr(loader, key, value, false);
1595     }
1596     return true;
1597 }
1598 
1599 
_createPathNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1600 static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1601 {
1602     loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
1603 
1604     if (!loader->svgParse->node) return nullptr;
1605 
1606     func(buf, bufLength, _attrParsePathNode, loader);
1607 
1608     return loader->svgParse->node;
1609 }
1610 
1611 
1612 static constexpr struct
1613 {
1614     const char* tag;
1615     SvgParserLengthType type;
1616     int sz;
1617     size_t offset;
1618 } circleTags[] = {
1619     {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
1620     {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
1621     {"r", SvgParserLengthType::Diagonal, sizeof("r"), offsetof(SvgCircleNode, r)}
1622 };
1623 
1624 
1625 /* parse the attributes for a circle element.
1626  * https://www.w3.org/TR/SVG/shapes.html#CircleElement
1627  */
_attrParseCircleNode(void * data,const char * key,const char * value)1628 static bool _attrParseCircleNode(void* data, const char* key, const char* value)
1629 {
1630     SvgLoaderData* loader = (SvgLoaderData*)data;
1631     SvgNode* node = loader->svgParse->node;
1632     SvgCircleNode* circle = &(node->node.circle);
1633     unsigned char* array;
1634     int sz = strlen(key);
1635 
1636     array = (unsigned char*)circle;
1637     for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
1638         if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
1639             *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
1640             return true;
1641         }
1642     }
1643 
1644     if (!strcmp(key, "style")) {
1645         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1646     } else if (!strcmp(key, "clip-path")) {
1647         _handleClipPathAttr(loader, node, value);
1648     } else if (!strcmp(key, "mask")) {
1649         _handleMaskAttr(loader, node, value);
1650     } else if (!strcmp(key, "id")) {
1651         if (node->id && value) free(node->id);
1652         node->id = _copyId(value);
1653     } else if (!strcmp(key, "class")) {
1654         _handleCssClassAttr(loader, node, value);
1655     } else {
1656         return _parseStyleAttr(loader, key, value, false);
1657     }
1658     return true;
1659 }
1660 
1661 
_createCircleNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1662 static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1663 {
1664     loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
1665 
1666     if (!loader->svgParse->node) return nullptr;
1667 
1668     func(buf, bufLength, _attrParseCircleNode, loader);
1669     return loader->svgParse->node;
1670 }
1671 
1672 
1673 static constexpr struct
1674 {
1675     const char* tag;
1676     SvgParserLengthType type;
1677     int sz;
1678     size_t offset;
1679 } ellipseTags[] = {
1680     {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
1681     {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
1682     {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
1683     {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
1684 };
1685 
1686 
1687 /* parse the attributes for an ellipse element.
1688  * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
1689  */
_attrParseEllipseNode(void * data,const char * key,const char * value)1690 static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
1691 {
1692     SvgLoaderData* loader = (SvgLoaderData*)data;
1693     SvgNode* node = loader->svgParse->node;
1694     SvgEllipseNode* ellipse = &(node->node.ellipse);
1695     unsigned char* array;
1696     int sz = strlen(key);
1697 
1698     array = (unsigned char*)ellipse;
1699     for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
1700         if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
1701             *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
1702             return true;
1703         }
1704     }
1705 
1706     if (!strcmp(key, "id")) {
1707         if (node->id && value) free(node->id);
1708         node->id = _copyId(value);
1709     } else if (!strcmp(key, "class")) {
1710         _handleCssClassAttr(loader, node, value);
1711     } else if (!strcmp(key, "style")) {
1712         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1713     } else if (!strcmp(key, "clip-path")) {
1714         _handleClipPathAttr(loader, node, value);
1715     } else if (!strcmp(key, "mask")) {
1716         _handleMaskAttr(loader, node, value);
1717     } else {
1718         return _parseStyleAttr(loader, key, value, false);
1719     }
1720     return true;
1721 }
1722 
1723 
_createEllipseNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1724 static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1725 {
1726     loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
1727 
1728     if (!loader->svgParse->node) return nullptr;
1729 
1730     func(buf, bufLength, _attrParseEllipseNode, loader);
1731     return loader->svgParse->node;
1732 }
1733 
1734 
_attrParsePolygonPoints(const char * str,SvgPolygonNode * polygon)1735 static bool _attrParsePolygonPoints(const char* str, SvgPolygonNode* polygon)
1736 {
1737     float num_x, num_y;
1738     while (_parseNumber(&str, nullptr, &num_x) && _parseNumber(&str, nullptr, &num_y)) {
1739         polygon->pts.push(num_x);
1740         polygon->pts.push(num_y);
1741     }
1742     return true;
1743 }
1744 
1745 
1746 /* parse the attributes for a polygon element.
1747  * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
1748  */
_attrParsePolygonNode(void * data,const char * key,const char * value)1749 static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
1750 {
1751     SvgLoaderData* loader = (SvgLoaderData*)data;
1752     SvgNode* node = loader->svgParse->node;
1753     SvgPolygonNode* polygon = nullptr;
1754 
1755     if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
1756     else polygon = &(node->node.polyline);
1757 
1758     if (!strcmp(key, "points")) {
1759         return _attrParsePolygonPoints(value, polygon);
1760     } else if (!strcmp(key, "style")) {
1761         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1762     } else if (!strcmp(key, "clip-path")) {
1763         _handleClipPathAttr(loader, node, value);
1764     } else if (!strcmp(key, "mask")) {
1765         _handleMaskAttr(loader, node, value);
1766     } else if (!strcmp(key, "id")) {
1767         if (node->id && value) free(node->id);
1768         node->id = _copyId(value);
1769     } else if (!strcmp(key, "class")) {
1770         _handleCssClassAttr(loader, node, value);
1771     } else {
1772         return _parseStyleAttr(loader, key, value, false);
1773     }
1774     return true;
1775 }
1776 
1777 
_createPolygonNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1778 static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1779 {
1780     loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
1781 
1782     if (!loader->svgParse->node) return nullptr;
1783 
1784     func(buf, bufLength, _attrParsePolygonNode, loader);
1785     return loader->svgParse->node;
1786 }
1787 
1788 
_createPolylineNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1789 static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1790 {
1791     loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
1792 
1793     if (!loader->svgParse->node) return nullptr;
1794 
1795     func(buf, bufLength, _attrParsePolygonNode, loader);
1796     return loader->svgParse->node;
1797 }
1798 
1799 static constexpr struct
1800 {
1801     const char* tag;
1802     SvgParserLengthType type;
1803     int sz;
1804     size_t offset;
1805 } rectTags[] = {
1806     {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1807     {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1808     {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1809     {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1810     {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
1811     {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
1812 };
1813 
1814 
1815 /* parse the attributes for a rect element.
1816  * https://www.w3.org/TR/SVG/shapes.html#RectElement
1817  */
_attrParseRectNode(void * data,const char * key,const char * value)1818 static bool _attrParseRectNode(void* data, const char* key, const char* value)
1819 {
1820     SvgLoaderData* loader = (SvgLoaderData*)data;
1821     SvgNode* node = loader->svgParse->node;
1822     SvgRectNode* rect = &(node->node.rect);
1823     unsigned char* array;
1824     bool ret = true;
1825     int sz = strlen(key);
1826 
1827     array = (unsigned char*)rect;
1828     for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
1829         if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
1830             *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
1831 
1832             //Case if only rx or ry is declared
1833             if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
1834             if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
1835 
1836             if ((rect->rx >= FLOAT_EPSILON) && (rect->ry < FLOAT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
1837             if ((rect->ry >= FLOAT_EPSILON) && (rect->rx < FLOAT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
1838             return ret;
1839         }
1840     }
1841 
1842     if (!strcmp(key, "id")) {
1843         if (node->id && value) free(node->id);
1844         node->id = _copyId(value);
1845     } else if (!strcmp(key, "class")) {
1846         _handleCssClassAttr(loader, node, value);
1847     } else if (!strcmp(key, "style")) {
1848         ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1849     } else if (!strcmp(key, "clip-path")) {
1850         _handleClipPathAttr(loader, node, value);
1851     } else if (!strcmp(key, "mask")) {
1852         _handleMaskAttr(loader, node, value);
1853     } else {
1854         ret = _parseStyleAttr(loader, key, value, false);
1855     }
1856 
1857     return ret;
1858 }
1859 
1860 
_createRectNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1861 static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1862 {
1863     loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
1864 
1865     if (!loader->svgParse->node) return nullptr;
1866 
1867     loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
1868 
1869     func(buf, bufLength, _attrParseRectNode, loader);
1870     return loader->svgParse->node;
1871 }
1872 
1873 
1874 static constexpr struct
1875 {
1876     const char* tag;
1877     SvgParserLengthType type;
1878     int sz;
1879     size_t offset;
1880 } lineTags[] = {
1881     {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
1882     {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
1883     {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
1884     {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
1885 };
1886 
1887 
1888 /* parse the attributes for a line element.
1889  * https://www.w3.org/TR/SVG/shapes.html#LineElement
1890  */
_attrParseLineNode(void * data,const char * key,const char * value)1891 static bool _attrParseLineNode(void* data, const char* key, const char* value)
1892 {
1893     SvgLoaderData* loader = (SvgLoaderData*)data;
1894     SvgNode* node = loader->svgParse->node;
1895     SvgLineNode* line = &(node->node.line);
1896     unsigned char* array;
1897     int sz = strlen(key);
1898 
1899     array = (unsigned char*)line;
1900     for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
1901         if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
1902             *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
1903             return true;
1904         }
1905     }
1906 
1907     if (!strcmp(key, "id")) {
1908         if (node->id && value) free(node->id);
1909         node->id = _copyId(value);
1910     } else if (!strcmp(key, "class")) {
1911         _handleCssClassAttr(loader, node, value);
1912     } else if (!strcmp(key, "style")) {
1913         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1914     } else if (!strcmp(key, "clip-path")) {
1915         _handleClipPathAttr(loader, node, value);
1916     } else if (!strcmp(key, "mask")) {
1917         _handleMaskAttr(loader, node, value);
1918     } else {
1919         return _parseStyleAttr(loader, key, value, false);
1920     }
1921     return true;
1922 }
1923 
1924 
_createLineNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)1925 static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
1926 {
1927     loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
1928 
1929     if (!loader->svgParse->node) return nullptr;
1930 
1931     func(buf, bufLength, _attrParseLineNode, loader);
1932     return loader->svgParse->node;
1933 }
1934 
1935 
_idFromHref(const char * href)1936 static char* _idFromHref(const char* href)
1937 {
1938     href = _skipSpace(href, nullptr);
1939     if ((*href) == '#') href++;
1940     return strdup(href);
1941 }
1942 
1943 
1944 static constexpr struct
1945 {
1946     const char* tag;
1947     SvgParserLengthType type;
1948     int sz;
1949     size_t offset;
1950 } imageTags[] = {
1951     {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1952     {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1953     {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1954     {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1955 };
1956 
1957 
1958 /* parse the attributes for a image element.
1959  * https://www.w3.org/TR/SVG/embedded.html#ImageElement
1960  */
_attrParseImageNode(void * data,const char * key,const char * value)1961 static bool _attrParseImageNode(void* data, const char* key, const char* value)
1962 {
1963     SvgLoaderData* loader = (SvgLoaderData*)data;
1964     SvgNode* node = loader->svgParse->node;
1965     SvgImageNode* image = &(node->node.image);
1966     unsigned char* array;
1967     int sz = strlen(key);
1968 
1969     array = (unsigned char*)image;
1970     for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) {
1971         if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) {
1972             *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type);
1973             return true;
1974         }
1975     }
1976 
1977     if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
1978         if (image->href && value) free(image->href);
1979         image->href = _idFromHref(value);
1980     } else if (!strcmp(key, "id")) {
1981         if (node->id && value) free(node->id);
1982         node->id = _copyId(value);
1983     } else if (!strcmp(key, "class")) {
1984         _handleCssClassAttr(loader, node, value);
1985     } else if (!strcmp(key, "style")) {
1986         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
1987     } else if (!strcmp(key, "clip-path")) {
1988         _handleClipPathAttr(loader, node, value);
1989     } else if (!strcmp(key, "mask")) {
1990         _handleMaskAttr(loader, node, value);
1991     } else if (!strcmp(key, "transform")) {
1992         node->transform = _parseTransformationMatrix(value);
1993     } else {
1994         return _parseStyleAttr(loader, key, value);
1995     }
1996     return true;
1997 }
1998 
1999 
_createImageNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)2000 static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
2001 {
2002     loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
2003 
2004     if (!loader->svgParse->node) return nullptr;
2005 
2006     func(buf, bufLength, _attrParseImageNode, loader);
2007     return loader->svgParse->node;
2008 }
2009 
2010 
_getDefsNode(SvgNode * node)2011 static SvgNode* _getDefsNode(SvgNode* node)
2012 {
2013     if (!node) return nullptr;
2014 
2015     while (node->parent != nullptr) {
2016         node = node->parent;
2017     }
2018 
2019     if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
2020     if (node->type == SvgNodeType::Defs) return node;
2021 
2022     return nullptr;
2023 }
2024 
2025 
_findNodeById(SvgNode * node,const char * id)2026 static SvgNode* _findNodeById(SvgNode *node, const char* id)
2027 {
2028     if (!node) return nullptr;
2029 
2030     SvgNode* result = nullptr;
2031     if (node->id && !strcmp(node->id, id)) return node;
2032 
2033     if (node->child.count > 0) {
2034         auto child = node->child.data;
2035         for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2036             result = _findNodeById(*child, id);
2037             if (result) break;
2038         }
2039     }
2040     return result;
2041 }
2042 
2043 
_findParentById(SvgNode * node,char * id,SvgNode * doc)2044 static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc)
2045 {
2046     SvgNode *parent = node->parent;
2047     while (parent != nullptr && parent != doc) {
2048         if (parent->id && !strcmp(parent->id, id)) {
2049             return parent;
2050         }
2051         parent = parent->parent;
2052     }
2053     return nullptr;
2054 }
2055 
2056 
2057 static constexpr struct
2058 {
2059     const char* tag;
2060     SvgParserLengthType type;
2061     int sz;
2062     size_t offset;
2063 } useTags[] = {
2064     {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)},
2065     {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)},
2066     {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)},
2067     {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)}
2068 };
2069 
2070 
2071 static void _cloneNode(SvgNode* from, SvgNode* parent, int depth);
_attrParseUseNode(void * data,const char * key,const char * value)2072 static bool _attrParseUseNode(void* data, const char* key, const char* value)
2073 {
2074     SvgLoaderData* loader = (SvgLoaderData*)data;
2075     SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
2076     char* id;
2077 
2078     SvgUseNode* use = &(node->node.use);
2079     int sz = strlen(key);
2080     unsigned char* array = (unsigned char*)use;
2081     for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) {
2082         if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) {
2083             *((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type);
2084 
2085             if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true;
2086             else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true;
2087 
2088             return true;
2089         }
2090     }
2091 
2092     if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
2093         id = _idFromHref(value);
2094         defs = _getDefsNode(node);
2095         nodeFrom = _findNodeById(defs, id);
2096         if (nodeFrom) {
2097             if (!_findParentById(node, id, loader->doc)) {
2098                 _cloneNode(nodeFrom, node, 0);
2099                 if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
2100             } else {
2101                 TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id);
2102             }
2103             free(id);
2104         } else {
2105             //some svg export software include <defs> element at the end of the file
2106             //if so the 'from' element won't be found now and we have to repeat finding
2107             //after the whole file is parsed
2108             _postpone(loader->cloneNodes, node, id);
2109         }
2110     } else {
2111         return _attrParseGNode(data, key, value);
2112     }
2113     return true;
2114 }
2115 
2116 
_createUseNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)2117 static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
2118 {
2119     loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
2120 
2121     if (!loader->svgParse->node) return nullptr;
2122 
2123     loader->svgParse->node->node.use.isWidthSet = false;
2124     loader->svgParse->node->node.use.isHeightSet = false;
2125 
2126     func(buf, bufLength, _attrParseUseNode, loader);
2127     return loader->svgParse->node;
2128 }
2129 
2130 
2131 static constexpr struct
2132 {
2133     const char* tag;
2134     SvgParserLengthType type;
2135     int sz;
2136     size_t offset;
2137 } textTags[] = {
2138         {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgTextNode, x)},
2139         {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgTextNode, y)},
2140         {"font-size", SvgParserLengthType::Vertical, sizeof("font-size"), offsetof(SvgTextNode, fontSize)}
2141 };
2142 
2143 
_attrParseTextNode(void * data,const char * key,const char * value)2144 static bool _attrParseTextNode(void* data, const char* key, const char* value)
2145 {
2146     SvgLoaderData* loader = (SvgLoaderData*)data;
2147     SvgNode* node = loader->svgParse->node;
2148     SvgTextNode* text = &(node->node.text);
2149 
2150     unsigned char* array;
2151     int sz = strlen(key);
2152 
2153     array = (unsigned char*)text;
2154     for (unsigned int i = 0; i < sizeof(textTags) / sizeof(textTags[0]); i++) {
2155         if (textTags[i].sz - 1 == sz && !strncmp(textTags[i].tag, key, sz)) {
2156             *((float*)(array + textTags[i].offset)) = _toFloat(loader->svgParse, value, textTags[i].type);
2157             return true;
2158         }
2159     }
2160 
2161     if (!strcmp(key, "font-family")) {
2162         if (text->fontFamily && value) free(text->fontFamily);
2163         text->fontFamily = strdup(value);
2164     } else if (!strcmp(key, "style")) {
2165         return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
2166     } else if (!strcmp(key, "clip-path")) {
2167         _handleClipPathAttr(loader, node, value);
2168     } else if (!strcmp(key, "mask")) {
2169         _handleMaskAttr(loader, node, value);
2170     } else if (!strcmp(key, "id")) {
2171         if (node->id && value) free(node->id);
2172         node->id = _copyId(value);
2173     } else if (!strcmp(key, "class")) {
2174         _handleCssClassAttr(loader, node, value);
2175     } else {
2176         return _parseStyleAttr(loader, key, value, false);
2177     }
2178     return true;
2179 }
2180 
2181 
_createTextNode(SvgLoaderData * loader,SvgNode * parent,const char * buf,unsigned bufLength,parseAttributes func)2182 static SvgNode* _createTextNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
2183 {
2184     loader->svgParse->node = _createNode(parent, SvgNodeType::Text);
2185     if (!loader->svgParse->node) return nullptr;
2186 
2187     //TODO: support the def font and size as used in a system?
2188     loader->svgParse->node->node.text.fontSize = 10.0f;
2189     loader->svgParse->node->node.text.fontFamily = nullptr;
2190     loader->svgParse->node->node.text.text = nullptr;
2191 
2192     func(buf, bufLength, _attrParseTextNode, loader);
2193 
2194     return loader->svgParse->node;
2195 }
2196 
2197 
2198 static constexpr struct
2199 {
2200     const char* tag;
2201     int sz;
2202     FactoryMethod tagHandler;
2203 } graphicsTags[] = {
2204     {"use", sizeof("use"), _createUseNode},
2205     {"circle", sizeof("circle"), _createCircleNode},
2206     {"ellipse", sizeof("ellipse"), _createEllipseNode},
2207     {"path", sizeof("path"), _createPathNode},
2208     {"polygon", sizeof("polygon"), _createPolygonNode},
2209     {"rect", sizeof("rect"), _createRectNode},
2210     {"polyline", sizeof("polyline"), _createPolylineNode},
2211     {"line", sizeof("line"), _createLineNode},
2212     {"image", sizeof("image"), _createImageNode},
2213     {"text", sizeof("text"), _createTextNode}
2214 };
2215 
2216 
2217 static constexpr struct
2218 {
2219     const char* tag;
2220     int sz;
2221     FactoryMethod tagHandler;
2222 } groupTags[] = {
2223     {"defs", sizeof("defs"), _createDefsNode},
2224     {"g", sizeof("g"), _createGNode},
2225     {"svg", sizeof("svg"), _createSvgNode},
2226     {"mask", sizeof("mask"), _createMaskNode},
2227     {"clipPath", sizeof("clipPath"), _createClipPathNode},
2228     {"style", sizeof("style"), _createCssStyleNode},
2229     {"symbol", sizeof("symbol"), _createSymbolNode}
2230 };
2231 
2232 
2233 #define FIND_FACTORY(Short_Name, Tags_Array)                                           \
2234     static FactoryMethod                                                               \
2235         _find##Short_Name##Factory(const char* name)                                   \
2236     {                                                                                  \
2237         unsigned int i;                                                                \
2238         int sz = strlen(name);                                                         \
2239                                                                                        \
2240         for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) {             \
2241             if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
2242                 return Tags_Array[i].tagHandler;                                       \
2243             }                                                                          \
2244         }                                                                              \
2245         return nullptr;                                                                \
2246     }
2247 
FIND_FACTORY(Group,groupTags)2248 FIND_FACTORY(Group, groupTags)
2249 FIND_FACTORY(Graphics, graphicsTags)
2250 
2251 
2252 FillSpread _parseSpreadValue(const char* value)
2253 {
2254     auto spread = FillSpread::Pad;
2255 
2256     if (!strcmp(value, "reflect")) {
2257         spread = FillSpread::Reflect;
2258     } else if (!strcmp(value, "repeat")) {
2259         spread = FillSpread::Repeat;
2260     }
2261 
2262     return spread;
2263 }
2264 
2265 
_handleRadialCxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2266 static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2267 {
2268     radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage);
2269     if (!loader->svgParse->gradient.parsedFx) {
2270         radial->fx = radial->cx;
2271         radial->isFxPercentage = radial->isCxPercentage;
2272     }
2273 }
2274 
2275 
_handleRadialCyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2276 static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2277 {
2278     radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage);
2279     if (!loader->svgParse->gradient.parsedFy) {
2280         radial->fy = radial->cy;
2281         radial->isFyPercentage = radial->isCyPercentage;
2282     }
2283 }
2284 
2285 
_handleRadialFxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2286 static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2287 {
2288     radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage);
2289     loader->svgParse->gradient.parsedFx = true;
2290 }
2291 
2292 
_handleRadialFyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2293 static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2294 {
2295     radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage);
2296     loader->svgParse->gradient.parsedFy = true;
2297 }
2298 
2299 
_handleRadialFrAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2300 static void _handleRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2301 {
2302     radial->fr = _gradientToFloat(loader->svgParse, value, radial->isFrPercentage);
2303 }
2304 
2305 
_handleRadialRAttr(SvgLoaderData * loader,SvgRadialGradient * radial,const char * value)2306 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
2307 {
2308     radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
2309 }
2310 
2311 
_recalcRadialCxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2312 static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2313 {
2314     if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w;
2315 }
2316 
2317 
_recalcRadialCyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2318 static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2319 {
2320     if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h;
2321 }
2322 
2323 
_recalcRadialFxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2324 static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2325 {
2326     if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w;
2327 }
2328 
2329 
_recalcRadialFyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2330 static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2331 {
2332     if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h;
2333 }
2334 
2335 
_recalcRadialFrAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2336 static void _recalcRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2337 {
2338     // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
2339     if (userSpace && !radial->isFrPercentage) radial->fr = radial->fr / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
2340 }
2341 
2342 
_recalcRadialRAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2343 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2344 {
2345     // scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
2346     if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0));
2347 }
2348 
2349 
_recalcInheritedRadialCxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2350 static void _recalcInheritedRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2351 {
2352     if (!radial->isCxPercentage) {
2353         if (userSpace) radial->cx /= loader->svgParse->global.w;
2354         else radial->cx *= loader->svgParse->global.w;
2355     }
2356 }
2357 
2358 
_recalcInheritedRadialCyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2359 static void _recalcInheritedRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2360 {
2361     if (!radial->isCyPercentage) {
2362         if (userSpace) radial->cy /= loader->svgParse->global.h;
2363         else radial->cy *= loader->svgParse->global.h;
2364     }
2365 }
2366 
2367 
_recalcInheritedRadialFxAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2368 static void _recalcInheritedRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2369 {
2370     if (!radial->isFxPercentage) {
2371         if (userSpace) radial->fx /= loader->svgParse->global.w;
2372         else radial->fx *= loader->svgParse->global.w;
2373     }
2374 }
2375 
2376 
_recalcInheritedRadialFyAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2377 static void _recalcInheritedRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2378 {
2379     if (!radial->isFyPercentage) {
2380         if (userSpace) radial->fy /= loader->svgParse->global.h;
2381         else radial->fy *= loader->svgParse->global.h;
2382     }
2383 }
2384 
2385 
_recalcInheritedRadialFrAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2386 static void _recalcInheritedRadialFrAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2387 {
2388     if (!radial->isFrPercentage) {
2389         if (userSpace) radial->fr /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
2390         else radial->fr *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
2391     }
2392 }
2393 
2394 
_recalcInheritedRadialRAttr(SvgLoaderData * loader,SvgRadialGradient * radial,bool userSpace)2395 static void _recalcInheritedRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
2396 {
2397     if (!radial->isRPercentage) {
2398         if (userSpace) radial->r /= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
2399         else radial->r *= sqrtf(powf(loader->svgParse->global.h, 2) + powf(loader->svgParse->global.w, 2)) / sqrtf(2.0);
2400     }
2401 }
2402 
2403 
_inheritRadialCxAttr(SvgStyleGradient * to,SvgStyleGradient * from)2404 static void _inheritRadialCxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2405 {
2406     to->radial->cx = from->radial->cx;
2407     to->radial->isCxPercentage = from->radial->isCxPercentage;
2408     to->flags = (to->flags | SvgGradientFlags::Cx);
2409 }
2410 
2411 
_inheritRadialCyAttr(SvgStyleGradient * to,SvgStyleGradient * from)2412 static void _inheritRadialCyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2413 {
2414     to->radial->cy = from->radial->cy;
2415     to->radial->isCyPercentage = from->radial->isCyPercentage;
2416     to->flags = (to->flags | SvgGradientFlags::Cy);
2417 }
2418 
2419 
_inheritRadialFxAttr(SvgStyleGradient * to,SvgStyleGradient * from)2420 static void _inheritRadialFxAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2421 {
2422     to->radial->fx = from->radial->fx;
2423     to->radial->isFxPercentage = from->radial->isFxPercentage;
2424     to->flags = (to->flags | SvgGradientFlags::Fx);
2425 }
2426 
2427 
_inheritRadialFyAttr(SvgStyleGradient * to,SvgStyleGradient * from)2428 static void _inheritRadialFyAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2429 {
2430     to->radial->fy = from->radial->fy;
2431     to->radial->isFyPercentage = from->radial->isFyPercentage;
2432     to->flags = (to->flags | SvgGradientFlags::Fy);
2433 }
2434 
2435 
_inheritRadialFrAttr(SvgStyleGradient * to,SvgStyleGradient * from)2436 static void _inheritRadialFrAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2437 {
2438     to->radial->fr = from->radial->fr;
2439     to->radial->isFrPercentage = from->radial->isFrPercentage;
2440     to->flags = (to->flags | SvgGradientFlags::Fr);
2441 }
2442 
2443 
_inheritRadialRAttr(SvgStyleGradient * to,SvgStyleGradient * from)2444 static void _inheritRadialRAttr(SvgStyleGradient* to, SvgStyleGradient* from)
2445 {
2446     to->radial->r = from->radial->r;
2447     to->radial->isRPercentage = from->radial->isRPercentage;
2448     to->flags = (to->flags | SvgGradientFlags::R);
2449 }
2450 
2451 
2452 typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
2453 typedef void (*radialInheritMethod)(SvgStyleGradient* to, SvgStyleGradient* from);
2454 typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
2455 
2456 
2457 #define RADIAL_DEF(Name, Name1, Flag)                                                    \
2458     {                                                                                    \
2459 #Name, sizeof(#Name), _handleRadial##Name1##Attr, _inheritRadial##Name1##Attr, _recalcRadial##Name1##Attr, _recalcInheritedRadial##Name1##Attr, Flag \
2460     }
2461 
2462 
2463 static constexpr struct
2464 {
2465     const char* tag;
2466     int sz;
2467     radialMethod tagHandler;
2468     radialInheritMethod tagInheritHandler;
2469     radialMethodRecalc tagRecalc;
2470     radialMethodRecalc tagInheritedRecalc;
2471     SvgGradientFlags flag;
2472 } radialTags[] = {
2473     RADIAL_DEF(cx, Cx, SvgGradientFlags::Cx),
2474     RADIAL_DEF(cy, Cy, SvgGradientFlags::Cy),
2475     RADIAL_DEF(fx, Fx, SvgGradientFlags::Fx),
2476     RADIAL_DEF(fy, Fy, SvgGradientFlags::Fy),
2477     RADIAL_DEF(r, R, SvgGradientFlags::R),
2478     RADIAL_DEF(fr, Fr, SvgGradientFlags::Fr)
2479 };
2480 
2481 
_attrParseRadialGradientNode(void * data,const char * key,const char * value)2482 static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
2483 {
2484     SvgLoaderData* loader = (SvgLoaderData*)data;
2485     SvgStyleGradient* grad = loader->svgParse->styleGrad;
2486     SvgRadialGradient* radial = grad->radial;
2487     int sz = strlen(key);
2488 
2489     for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
2490         if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
2491             radialTags[i].tagHandler(loader, radial, value);
2492             grad->flags = (grad->flags | radialTags[i].flag);
2493             return true;
2494         }
2495     }
2496 
2497     if (!strcmp(key, "id")) {
2498         if (grad->id && value) free(grad->id);
2499         grad->id = _copyId(value);
2500     } else if (!strcmp(key, "spreadMethod")) {
2501         grad->spread = _parseSpreadValue(value);
2502         grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
2503     } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
2504         if (grad->ref && value) free(grad->ref);
2505         grad->ref = _idFromHref(value);
2506     } else if (!strcmp(key, "gradientUnits")) {
2507         if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
2508         grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
2509     } else if (!strcmp(key, "gradientTransform")) {
2510         grad->transform = _parseTransformationMatrix(value);
2511     } else {
2512         return false;
2513     }
2514 
2515     return true;
2516 }
2517 
2518 
_createRadialGradient(SvgLoaderData * loader,const char * buf,unsigned bufLength)2519 static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2520 {
2521     auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
2522     loader->svgParse->styleGrad = grad;
2523 
2524     grad->flags = SvgGradientFlags::None;
2525     grad->type = SvgGradientType::Radial;
2526     grad->userSpace = false;
2527     grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
2528     if (!grad->radial) {
2529         grad->clear();
2530         free(grad);
2531         return nullptr;
2532     }
2533     /**
2534     * Default values of gradient transformed into global percentage
2535     */
2536     grad->radial->cx = 0.5f;
2537     grad->radial->cy = 0.5f;
2538     grad->radial->fx = 0.5f;
2539     grad->radial->fy = 0.5f;
2540     grad->radial->r = 0.5f;
2541     grad->radial->isCxPercentage = true;
2542     grad->radial->isCyPercentage = true;
2543     grad->radial->isFxPercentage = true;
2544     grad->radial->isFyPercentage = true;
2545     grad->radial->isRPercentage = true;
2546     grad->radial->isFrPercentage = true;
2547 
2548     loader->svgParse->gradient.parsedFx = false;
2549     loader->svgParse->gradient.parsedFy = false;
2550     simpleXmlParseAttributes(buf, bufLength,
2551         _attrParseRadialGradientNode, loader);
2552 
2553     for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
2554         radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
2555     }
2556 
2557     return loader->svgParse->styleGrad;
2558 }
2559 
2560 
_attrParseStopsStyle(void * data,const char * key,const char * value)2561 static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
2562 {
2563     SvgLoaderData* loader = (SvgLoaderData*)data;
2564     auto stop = &loader->svgParse->gradStop;
2565 
2566     if (!strcmp(key, "stop-opacity")) {
2567         stop->a = _toOpacity(value);
2568         loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopOpacity);
2569     } else if (!strcmp(key, "stop-color")) {
2570         if (_toColor(value, &stop->r, &stop->g, &stop->b, nullptr)) {
2571             loader->svgParse->flags = (loader->svgParse->flags | SvgStopStyleFlags::StopColor);
2572         }
2573     } else {
2574         return false;
2575     }
2576 
2577     return true;
2578 }
2579 
2580 
_attrParseStops(void * data,const char * key,const char * value)2581 static bool _attrParseStops(void* data, const char* key, const char* value)
2582 {
2583     SvgLoaderData* loader = (SvgLoaderData*)data;
2584     auto stop = &loader->svgParse->gradStop;
2585 
2586     if (!strcmp(key, "offset")) {
2587         stop->offset = _toOffset(value);
2588     } else if (!strcmp(key, "stop-opacity")) {
2589         if (!(loader->svgParse->flags & SvgStopStyleFlags::StopOpacity)) {
2590             stop->a = _toOpacity(value);
2591         }
2592     } else if (!strcmp(key, "stop-color")) {
2593         if (!(loader->svgParse->flags & SvgStopStyleFlags::StopColor)) {
2594             _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
2595         }
2596     } else if (!strcmp(key, "style")) {
2597         simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
2598     } else {
2599         return false;
2600     }
2601 
2602     return true;
2603 }
2604 
2605 
_handleLinearX1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,const char * value)2606 static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2607 {
2608     linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage);
2609 }
2610 
2611 
_handleLinearY1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,const char * value)2612 static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2613 {
2614     linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage);
2615 }
2616 
2617 
_handleLinearX2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,const char * value)2618 static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2619 {
2620     linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage);
2621 }
2622 
2623 
_handleLinearY2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,const char * value)2624 static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2625 {
2626     linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage);
2627 }
2628 
2629 
_recalcLinearX1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2630 static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2631 {
2632     if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w;
2633 }
2634 
2635 
_recalcLinearY1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2636 static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2637 {
2638     if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h;
2639 }
2640 
2641 
_recalcLinearX2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2642 static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2643 {
2644     if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w;
2645 }
2646 
2647 
_recalcLinearY2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2648 static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2649 {
2650     if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h;
2651 }
2652 
2653 
_recalcInheritedLinearX1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2654 static void _recalcInheritedLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2655 {
2656     if (!linear->isX1Percentage) {
2657         if (userSpace) linear->x1 /= loader->svgParse->global.w;
2658         else linear->x1 *= loader->svgParse->global.w;
2659     }
2660 }
2661 
2662 
_recalcInheritedLinearX2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2663 static void _recalcInheritedLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2664 {
2665     if (!linear->isX2Percentage) {
2666         if (userSpace) linear->x2 /= loader->svgParse->global.w;
2667         else linear->x2 *= loader->svgParse->global.w;
2668     }
2669 }
2670 
2671 
_recalcInheritedLinearY1Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2672 static void _recalcInheritedLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2673 {
2674     if (!linear->isY1Percentage) {
2675         if (userSpace) linear->y1 /= loader->svgParse->global.h;
2676         else linear->y1 *= loader->svgParse->global.h;
2677     }
2678 }
2679 
2680 
_recalcInheritedLinearY2Attr(SvgLoaderData * loader,SvgLinearGradient * linear,bool userSpace)2681 static void _recalcInheritedLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2682 {
2683     if (!linear->isY2Percentage) {
2684         if (userSpace) linear->y2 /= loader->svgParse->global.h;
2685         else linear->y2 *= loader->svgParse->global.h;
2686     }
2687 }
2688 
2689 
_inheritLinearX1Attr(SvgStyleGradient * to,SvgStyleGradient * from)2690 static void _inheritLinearX1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
2691 {
2692     to->linear->x1 = from->linear->x1;
2693     to->linear->isX1Percentage = from->linear->isX1Percentage;
2694     to->flags = (to->flags | SvgGradientFlags::X1);
2695 }
2696 
2697 
_inheritLinearX2Attr(SvgStyleGradient * to,SvgStyleGradient * from)2698 static void _inheritLinearX2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
2699 {
2700     to->linear->x2 = from->linear->x2;
2701     to->linear->isX2Percentage = from->linear->isX2Percentage;
2702     to->flags = (to->flags | SvgGradientFlags::X2);
2703 }
2704 
2705 
_inheritLinearY1Attr(SvgStyleGradient * to,SvgStyleGradient * from)2706 static void _inheritLinearY1Attr(SvgStyleGradient* to, SvgStyleGradient* from)
2707 {
2708     to->linear->y1 = from->linear->y1;
2709     to->linear->isY1Percentage = from->linear->isY1Percentage;
2710     to->flags = (to->flags | SvgGradientFlags::Y1);
2711 }
2712 
2713 
_inheritLinearY2Attr(SvgStyleGradient * to,SvgStyleGradient * from)2714 static void _inheritLinearY2Attr(SvgStyleGradient* to, SvgStyleGradient* from)
2715 {
2716     to->linear->y2 = from->linear->y2;
2717     to->linear->isY2Percentage = from->linear->isY2Percentage;
2718     to->flags = (to->flags | SvgGradientFlags::Y2);
2719 }
2720 
2721 
2722 typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
2723 typedef void (*Linear_Inherit_Method)(SvgStyleGradient* to, SvgStyleGradient* from);
2724 typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
2725 
2726 
2727 #define LINEAR_DEF(Name, Name1, Flag)                                                    \
2728     {                                                                                    \
2729 #Name, sizeof(#Name), _handleLinear##Name1##Attr, _inheritLinear##Name1##Attr, _recalcLinear##Name1##Attr, _recalcInheritedLinear##Name1##Attr, Flag \
2730     }
2731 
2732 
2733 static constexpr struct
2734 {
2735     const char* tag;
2736     int sz;
2737     Linear_Method tagHandler;
2738     Linear_Inherit_Method tagInheritHandler;
2739     Linear_Method_Recalc tagRecalc;
2740     Linear_Method_Recalc tagInheritedRecalc;
2741     SvgGradientFlags flag;
2742 } linear_tags[] = {
2743     LINEAR_DEF(x1, X1, SvgGradientFlags::X1),
2744     LINEAR_DEF(y1, Y1, SvgGradientFlags::Y1),
2745     LINEAR_DEF(x2, X2, SvgGradientFlags::X2),
2746     LINEAR_DEF(y2, Y2, SvgGradientFlags::Y2)
2747 };
2748 
2749 
_attrParseLinearGradientNode(void * data,const char * key,const char * value)2750 static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
2751 {
2752     SvgLoaderData* loader = (SvgLoaderData*)data;
2753     SvgStyleGradient* grad = loader->svgParse->styleGrad;
2754     SvgLinearGradient* linear = grad->linear;
2755     int sz = strlen(key);
2756 
2757     for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2758         if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
2759             linear_tags[i].tagHandler(loader, linear, value);
2760             grad->flags = (grad->flags | linear_tags[i].flag);
2761             return true;
2762         }
2763     }
2764 
2765     if (!strcmp(key, "id")) {
2766         if (grad->id && value) free(grad->id);
2767         grad->id = _copyId(value);
2768     } else if (!strcmp(key, "spreadMethod")) {
2769         grad->spread = _parseSpreadValue(value);
2770         grad->flags = (grad->flags | SvgGradientFlags::SpreadMethod);
2771     } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
2772         if (grad->ref && value) free(grad->ref);
2773         grad->ref = _idFromHref(value);
2774     } else if (!strcmp(key, "gradientUnits")) {
2775         if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
2776         grad->flags = (grad->flags | SvgGradientFlags::GradientUnits);
2777     } else if (!strcmp(key, "gradientTransform")) {
2778         grad->transform = _parseTransformationMatrix(value);
2779     } else {
2780         return false;
2781     }
2782 
2783     return true;
2784 }
2785 
2786 
_createLinearGradient(SvgLoaderData * loader,const char * buf,unsigned bufLength)2787 static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2788 {
2789     auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
2790     loader->svgParse->styleGrad = grad;
2791 
2792     grad->flags = SvgGradientFlags::None;
2793     grad->type = SvgGradientType::Linear;
2794     grad->userSpace = false;
2795     grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
2796     if (!grad->linear) {
2797         grad->clear();
2798         free(grad);
2799         return nullptr;
2800     }
2801     /**
2802     * Default value of x2 is 100% - transformed to the global percentage
2803     */
2804     grad->linear->x2 = 1.0f;
2805     grad->linear->isX2Percentage = true;
2806 
2807     simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
2808 
2809     for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2810         linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
2811     }
2812 
2813     return loader->svgParse->styleGrad;
2814 }
2815 
2816 
2817 #define GRADIENT_DEF(Name, Name1)            \
2818     {                                        \
2819 #Name, sizeof(#Name), _create##Name1         \
2820     }
2821 
2822 
2823 /**
2824  * In the case when the gradients lengths are given as numbers (not percentages)
2825  * in the current user coordinate system, they are recalculated into percentages
2826  * related to the canvas width and height.
2827  */
2828 static constexpr struct
2829 {
2830     const char* tag;
2831     int sz;
2832     GradientFactoryMethod tagHandler;
2833 } gradientTags[] = {
2834     GRADIENT_DEF(linearGradient, LinearGradient),
2835     GRADIENT_DEF(radialGradient, RadialGradient)
2836 };
2837 
2838 
_findGradientFactory(const char * name)2839 static GradientFactoryMethod _findGradientFactory(const char* name)
2840 {
2841     int sz = strlen(name);
2842 
2843     for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
2844         if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
2845             return gradientTags[i].tagHandler;
2846         }
2847     }
2848     return nullptr;
2849 }
2850 
2851 
_cloneGradStops(Array<Fill::ColorStop> & dst,const Array<Fill::ColorStop> & src)2852 static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
2853 {
2854     for (uint32_t i = 0; i < src.count; ++i) {
2855         dst.push(src[i]);
2856     }
2857 }
2858 
2859 
_inheritGradient(SvgLoaderData * loader,SvgStyleGradient * to,SvgStyleGradient * from)2860 static void _inheritGradient(SvgLoaderData* loader, SvgStyleGradient* to, SvgStyleGradient* from)
2861 {
2862     if (!to || !from) return;
2863 
2864     if (!(to->flags & SvgGradientFlags::SpreadMethod) && (from->flags & SvgGradientFlags::SpreadMethod)) {
2865         to->spread = from->spread;
2866         to->flags = (to->flags | SvgGradientFlags::SpreadMethod);
2867     }
2868     bool gradUnitSet = (to->flags & SvgGradientFlags::GradientUnits);
2869     if (!(to->flags & SvgGradientFlags::GradientUnits) && (from->flags & SvgGradientFlags::GradientUnits)) {
2870         to->userSpace = from->userSpace;
2871         to->flags = (to->flags | SvgGradientFlags::GradientUnits);
2872     }
2873 
2874     if (!to->transform && from->transform) {
2875         to->transform = (Matrix*)malloc(sizeof(Matrix));
2876         if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
2877     }
2878 
2879     if (to->type == SvgGradientType::Linear) {
2880         for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2881             bool coordSet = to->flags & linear_tags[i].flag;
2882             if (!(to->flags & linear_tags[i].flag) && (from->flags & linear_tags[i].flag)) {
2883                 linear_tags[i].tagInheritHandler(to, from);
2884             }
2885 
2886             //GradUnits not set directly, coord set
2887             if (!gradUnitSet && coordSet) {
2888                 linear_tags[i].tagRecalc(loader, to->linear, to->userSpace);
2889             }
2890             //GradUnits set, coord not set directly
2891             if (to->userSpace == from->userSpace) continue;
2892             if (gradUnitSet && !coordSet) {
2893                 linear_tags[i].tagInheritedRecalc(loader, to->linear, to->userSpace);
2894             }
2895         }
2896     } else if (to->type == SvgGradientType::Radial) {
2897         for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
2898             bool coordSet = (to->flags & radialTags[i].flag);
2899             if (!(to->flags & radialTags[i].flag) && (from->flags & radialTags[i].flag)) {
2900                 radialTags[i].tagInheritHandler(to, from);
2901             }
2902 
2903             //GradUnits not set directly, coord set
2904             if (!gradUnitSet && coordSet) {
2905                 radialTags[i].tagRecalc(loader, to->radial, to->userSpace);
2906                 //If fx and fy are not set, set cx and cy.
2907                 if (!strcmp(radialTags[i].tag, "cx") && !(to->flags & SvgGradientFlags::Fx)) to->radial->fx = to->radial->cx;
2908                 if (!strcmp(radialTags[i].tag, "cy") && !(to->flags & SvgGradientFlags::Fy)) to->radial->fy = to->radial->cy;
2909             }
2910             //GradUnits set, coord not set directly
2911             if (to->userSpace == from->userSpace) continue;
2912             if (gradUnitSet && !coordSet) {
2913                 //If fx and fx are not set, do not call recalc.
2914                 if (!strcmp(radialTags[i].tag, "fx") && !(to->flags & SvgGradientFlags::Fx)) continue;
2915                 if (!strcmp(radialTags[i].tag, "fy") && !(to->flags & SvgGradientFlags::Fy)) continue;
2916                 radialTags[i].tagInheritedRecalc(loader, to->radial, to->userSpace);
2917             }
2918         }
2919     }
2920 
2921     if (to->stops.empty()) _cloneGradStops(to->stops, from->stops);
2922 }
2923 
2924 
_cloneGradient(SvgStyleGradient * from)2925 static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
2926 {
2927     if (!from) return nullptr;
2928 
2929     auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
2930     if (!grad) return nullptr;
2931 
2932     grad->type = from->type;
2933     grad->id = from->id ? _copyId(from->id) : nullptr;
2934     grad->ref = from->ref ? _copyId(from->ref) : nullptr;
2935     grad->spread = from->spread;
2936     grad->userSpace = from->userSpace;
2937     grad->flags = from->flags;
2938 
2939     if (from->transform) {
2940         grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
2941         if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
2942     }
2943 
2944     if (grad->type == SvgGradientType::Linear) {
2945         grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
2946         if (!grad->linear) goto error_grad_alloc;
2947         memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
2948     } else if (grad->type == SvgGradientType::Radial) {
2949         grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
2950         if (!grad->radial) goto error_grad_alloc;
2951         memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
2952     }
2953 
2954     _cloneGradStops(grad->stops, from->stops);
2955 
2956     return grad;
2957 
2958     error_grad_alloc:
2959     if (grad) {
2960         grad->clear();
2961         free(grad);
2962     }
2963     return nullptr;
2964 }
2965 
2966 
_styleInherit(SvgStyleProperty * child,const SvgStyleProperty * parent)2967 static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
2968 {
2969     if (parent == nullptr) return;
2970     //Inherit the property of parent if not present in child.
2971     if (!child->curColorSet) {
2972         child->color = parent->color;
2973         child->curColorSet = parent->curColorSet;
2974     }
2975     if (!(child->flags & SvgStyleFlags::PaintOrder)) {
2976         child->paintOrder = parent->paintOrder;
2977     }
2978     //Fill
2979     if (!(child->fill.flags & SvgFillFlags::Paint)) {
2980         child->fill.paint.color = parent->fill.paint.color;
2981         child->fill.paint.none = parent->fill.paint.none;
2982         child->fill.paint.curColor = parent->fill.paint.curColor;
2983         if (parent->fill.paint.url) {
2984             if (child->fill.paint.url) free(child->fill.paint.url);
2985             child->fill.paint.url = _copyId(parent->fill.paint.url);
2986         }
2987     }
2988     if (!(child->fill.flags & SvgFillFlags::Opacity)) {
2989         child->fill.opacity = parent->fill.opacity;
2990     }
2991     if (!(child->fill.flags & SvgFillFlags::FillRule)) {
2992         child->fill.fillRule = parent->fill.fillRule;
2993     }
2994     //Stroke
2995     if (!(child->stroke.flags & SvgStrokeFlags::Paint)) {
2996         child->stroke.paint.color = parent->stroke.paint.color;
2997         child->stroke.paint.none = parent->stroke.paint.none;
2998         child->stroke.paint.curColor = parent->stroke.paint.curColor;
2999         if (parent->stroke.paint.url) {
3000             if (child->stroke.paint.url) free(child->stroke.paint.url);
3001             child->stroke.paint.url = _copyId(parent->stroke.paint.url);
3002         }
3003     }
3004     if (!(child->stroke.flags & SvgStrokeFlags::Opacity)) {
3005         child->stroke.opacity = parent->stroke.opacity;
3006     }
3007     if (!(child->stroke.flags & SvgStrokeFlags::Width)) {
3008         child->stroke.width = parent->stroke.width;
3009     }
3010     if (!(child->stroke.flags & SvgStrokeFlags::Dash)) {
3011         if (parent->stroke.dash.array.count > 0) {
3012             child->stroke.dash.array.clear();
3013             child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
3014             for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
3015                 child->stroke.dash.array.push(parent->stroke.dash.array[i]);
3016             }
3017         }
3018     }
3019     if (!(child->stroke.flags & SvgStrokeFlags::DashOffset)) {
3020         child->stroke.dash.offset = parent->stroke.dash.offset;
3021     }
3022     if (!(child->stroke.flags & SvgStrokeFlags::Cap)) {
3023         child->stroke.cap = parent->stroke.cap;
3024     }
3025     if (!(child->stroke.flags & SvgStrokeFlags::Join)) {
3026         child->stroke.join = parent->stroke.join;
3027     }
3028     if (!(child->stroke.flags & SvgStrokeFlags::Miterlimit)) {
3029         child->stroke.miterlimit = parent->stroke.miterlimit;
3030     }
3031 }
3032 
3033 
_styleCopy(SvgStyleProperty * to,const SvgStyleProperty * from)3034 static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
3035 {
3036     if (from == nullptr) return;
3037     //Copy the properties of 'from' only if they were explicitly set (not the default ones).
3038     if (from->curColorSet) {
3039         to->color = from->color;
3040         to->curColorSet = true;
3041     }
3042     if (from->flags & SvgStyleFlags::Opacity) {
3043         to->opacity = from->opacity;
3044     }
3045     if (from->flags & SvgStyleFlags::PaintOrder) {
3046         to->paintOrder = from->paintOrder;
3047     }
3048     if (from->flags & SvgStyleFlags::Display) {
3049         to->display = from->display;
3050     }
3051     //Fill
3052     to->fill.flags = (to->fill.flags | from->fill.flags);
3053     if (from->fill.flags & SvgFillFlags::Paint) {
3054         to->fill.paint.color = from->fill.paint.color;
3055         to->fill.paint.none = from->fill.paint.none;
3056         to->fill.paint.curColor = from->fill.paint.curColor;
3057         if (from->fill.paint.url) {
3058             if (to->fill.paint.url) free(to->fill.paint.url);
3059             to->fill.paint.url = _copyId(from->fill.paint.url);
3060         }
3061     }
3062     if (from->fill.flags & SvgFillFlags::Opacity) {
3063         to->fill.opacity = from->fill.opacity;
3064     }
3065     if (from->fill.flags & SvgFillFlags::FillRule) {
3066         to->fill.fillRule = from->fill.fillRule;
3067     }
3068     //Stroke
3069     to->stroke.flags = (to->stroke.flags | from->stroke.flags);
3070     if (from->stroke.flags & SvgStrokeFlags::Paint) {
3071         to->stroke.paint.color = from->stroke.paint.color;
3072         to->stroke.paint.none = from->stroke.paint.none;
3073         to->stroke.paint.curColor = from->stroke.paint.curColor;
3074         if (from->stroke.paint.url) {
3075             if (to->stroke.paint.url) free(to->stroke.paint.url);
3076             to->stroke.paint.url = _copyId(from->stroke.paint.url);
3077         }
3078     }
3079     if (from->stroke.flags & SvgStrokeFlags::Opacity) {
3080         to->stroke.opacity = from->stroke.opacity;
3081     }
3082     if (from->stroke.flags & SvgStrokeFlags::Width) {
3083         to->stroke.width = from->stroke.width;
3084     }
3085     if (from->stroke.flags & SvgStrokeFlags::Dash) {
3086         if (from->stroke.dash.array.count > 0) {
3087             to->stroke.dash.array.clear();
3088             to->stroke.dash.array.reserve(from->stroke.dash.array.count);
3089             for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
3090                 to->stroke.dash.array.push(from->stroke.dash.array[i]);
3091             }
3092         }
3093     }
3094     if (from->stroke.flags & SvgStrokeFlags::DashOffset) {
3095         to->stroke.dash.offset = from->stroke.dash.offset;
3096     }
3097     if (from->stroke.flags & SvgStrokeFlags::Cap) {
3098         to->stroke.cap = from->stroke.cap;
3099     }
3100     if (from->stroke.flags & SvgStrokeFlags::Join) {
3101         to->stroke.join = from->stroke.join;
3102     }
3103     if (from->stroke.flags & SvgStrokeFlags::Miterlimit) {
3104         to->stroke.miterlimit = from->stroke.miterlimit;
3105     }
3106 }
3107 
3108 
_copyAttr(SvgNode * to,const SvgNode * from)3109 static void _copyAttr(SvgNode* to, const SvgNode* from)
3110 {
3111     //Copy matrix attribute
3112     if (from->transform) {
3113         to->transform = (Matrix*)malloc(sizeof(Matrix));
3114         if (to->transform) *to->transform = *from->transform;
3115     }
3116     //Copy style attribute
3117     _styleCopy(to->style, from->style);
3118     to->style->flags = (to->style->flags | from->style->flags);
3119     if (from->style->clipPath.url) {
3120         if (to->style->clipPath.url) free(to->style->clipPath.url);
3121         to->style->clipPath.url = strdup(from->style->clipPath.url);
3122     }
3123     if (from->style->mask.url) {
3124         if (to->style->mask.url) free(to->style->mask.url);
3125         to->style->mask.url = strdup(from->style->mask.url);
3126     }
3127 
3128     //Copy node attribute
3129     switch (from->type) {
3130         case SvgNodeType::Circle: {
3131             to->node.circle.cx = from->node.circle.cx;
3132             to->node.circle.cy = from->node.circle.cy;
3133             to->node.circle.r = from->node.circle.r;
3134             break;
3135         }
3136         case SvgNodeType::Ellipse: {
3137             to->node.ellipse.cx = from->node.ellipse.cx;
3138             to->node.ellipse.cy = from->node.ellipse.cy;
3139             to->node.ellipse.rx = from->node.ellipse.rx;
3140             to->node.ellipse.ry = from->node.ellipse.ry;
3141             break;
3142         }
3143         case SvgNodeType::Rect: {
3144             to->node.rect.x = from->node.rect.x;
3145             to->node.rect.y = from->node.rect.y;
3146             to->node.rect.w = from->node.rect.w;
3147             to->node.rect.h = from->node.rect.h;
3148             to->node.rect.rx = from->node.rect.rx;
3149             to->node.rect.ry = from->node.rect.ry;
3150             to->node.rect.hasRx = from->node.rect.hasRx;
3151             to->node.rect.hasRy = from->node.rect.hasRy;
3152             break;
3153         }
3154         case SvgNodeType::Line: {
3155             to->node.line.x1 = from->node.line.x1;
3156             to->node.line.y1 = from->node.line.y1;
3157             to->node.line.x2 = from->node.line.x2;
3158             to->node.line.y2 = from->node.line.y2;
3159             break;
3160         }
3161         case SvgNodeType::Path: {
3162             if (from->node.path.path) {
3163                 if (to->node.path.path) free(to->node.path.path);
3164                 to->node.path.path = strdup(from->node.path.path);
3165             }
3166             break;
3167         }
3168         case SvgNodeType::Polygon: {
3169             if ((to->node.polygon.pts.count = from->node.polygon.pts.count)) {
3170                 to->node.polygon.pts = from->node.polygon.pts;
3171             }
3172             break;
3173         }
3174         case SvgNodeType::Polyline: {
3175             if ((to->node.polyline.pts.count = from->node.polyline.pts.count)) {
3176                 to->node.polyline.pts = from->node.polyline.pts;
3177             }
3178             break;
3179         }
3180         case SvgNodeType::Image: {
3181             to->node.image.x = from->node.image.x;
3182             to->node.image.y = from->node.image.y;
3183             to->node.image.w = from->node.image.w;
3184             to->node.image.h = from->node.image.h;
3185             if (from->node.image.href) {
3186                 if (to->node.image.href) free(to->node.image.href);
3187                 to->node.image.href = strdup(from->node.image.href);
3188             }
3189             break;
3190         }
3191         case SvgNodeType::Use: {
3192             to->node.use.x = from->node.use.x;
3193             to->node.use.y = from->node.use.y;
3194             to->node.use.w = from->node.use.w;
3195             to->node.use.h = from->node.use.h;
3196             to->node.use.isWidthSet = from->node.use.isWidthSet;
3197             to->node.use.isHeightSet = from->node.use.isHeightSet;
3198             to->node.use.symbol = from->node.use.symbol;
3199             break;
3200         }
3201         case SvgNodeType::Text: {
3202             to->node.text.x = from->node.text.x;
3203             to->node.text.y = from->node.text.y;
3204             to->node.text.fontSize = from->node.text.fontSize;
3205             if (from->node.text.text) {
3206                 if (to->node.text.text) free(to->node.text.text);
3207                 to->node.text.text = strdup(from->node.text.text);
3208             }
3209             if (from->node.text.fontFamily) {
3210                 if (to->node.text.fontFamily) free(to->node.text.fontFamily);
3211                 to->node.text.fontFamily = strdup(from->node.text.fontFamily);
3212             }
3213             break;
3214         }
3215         default: {
3216             break;
3217         }
3218     }
3219 }
3220 
3221 
_cloneNode(SvgNode * from,SvgNode * parent,int depth)3222 static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
3223 {
3224     /* Exception handling: Prevent invalid SVG data input.
3225        The size is the arbitrary value, we need an experimental size. */
3226     if (depth == 8192) {
3227         TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
3228         return;
3229     }
3230 
3231     SvgNode* newNode;
3232     if (!from || !parent || from == parent) return;
3233 
3234     newNode = _createNode(parent, from->type);
3235     if (!newNode) return;
3236 
3237     _styleInherit(newNode->style, parent->style);
3238     _copyAttr(newNode, from);
3239 
3240     auto child = from->child.data;
3241     for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
3242         _cloneNode(*child, newNode, depth + 1);
3243     }
3244 }
3245 
3246 
_clonePostponedNodes(Array<SvgNodeIdPair> * cloneNodes,SvgNode * doc)3247 static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
3248 {
3249     for (uint32_t i = 0; i < cloneNodes->count; ++i) {
3250         auto nodeIdPair = (*cloneNodes)[i];
3251         auto defs = _getDefsNode(nodeIdPair.node);
3252         auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
3253         if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
3254         if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) {
3255             _cloneNode(nodeFrom, nodeIdPair.node, 0);
3256             if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
3257                 nodeIdPair.node->node.use.symbol = nodeFrom;
3258             }
3259         } else {
3260             TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair.id);
3261         }
3262         free(nodeIdPair.id);
3263     }
3264 }
3265 
3266 
_svgLoaderParserXmlClose(SvgLoaderData * loader,const char * content,unsigned int length)3267 static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content, unsigned int length)
3268 {
3269     const char* itr = nullptr;
3270     int sz = length;
3271     char tagName[20] = "";
3272 
3273     content = _skipSpace(content, nullptr);
3274     itr = content;
3275     while ((itr != nullptr) && *itr != '>') itr++;
3276 
3277     if (itr) {
3278         sz = itr - content;
3279         while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
3280         if ((unsigned int)sz >= sizeof(tagName)) sz = sizeof(tagName) - 1;
3281         strncpy(tagName, content, sz);
3282         tagName[sz] = '\0';
3283     }
3284     else return;
3285 
3286     for (unsigned int i = 0; i < sizeof(groupTags) / sizeof(groupTags[0]); i++) {
3287         if (!strncmp(tagName, groupTags[i].tag, sz)) {
3288             loader->stack.pop();
3289             break;
3290         }
3291     }
3292 
3293     for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
3294         if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
3295             loader->currentGraphicsNode = nullptr;
3296             if (!strncmp(tagName, "text", 4)) loader->openedTag = OpenedTagType::Other;
3297             loader->stack.pop();
3298             break;
3299         }
3300     }
3301 
3302     loader->level--;
3303 }
3304 
3305 
_svgLoaderParserXmlOpen(SvgLoaderData * loader,const char * content,unsigned int length,bool empty)3306 static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
3307 {
3308     const char* attrs = nullptr;
3309     int attrsLength = 0;
3310     int sz = length;
3311     char tagName[20] = "";
3312     FactoryMethod method;
3313     GradientFactoryMethod gradientMethod;
3314     SvgNode *node = nullptr, *parent = nullptr;
3315     loader->level++;
3316     attrs = simpleXmlFindAttributesTag(content, length);
3317 
3318     if (!attrs) {
3319         //Parse the empty tag
3320         attrs = content;
3321         while ((attrs != nullptr) && *attrs != '>') attrs++;
3322         if (empty) attrs--;
3323     }
3324 
3325     if (attrs) {
3326         //Find out the tag name starting from content till sz length
3327         sz = attrs - content;
3328         while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
3329         if ((unsigned)sz >= sizeof(tagName)) return;
3330         strncpy(tagName, content, sz);
3331         tagName[sz] = '\0';
3332         attrsLength = length - sz;
3333     }
3334 
3335     if ((method = _findGroupFactory(tagName))) {
3336         //Group
3337         if (empty) return;
3338         if (!loader->doc) {
3339             if (strcmp(tagName, "svg")) return; //Not a valid svg document
3340             node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
3341             loader->doc = node;
3342         } else {
3343             if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
3344             if (loader->stack.count > 0) parent = loader->stack.last();
3345             else parent = loader->doc;
3346             if (!strcmp(tagName, "style")) {
3347                 // TODO: For now only the first style node is saved. After the css id selector
3348                 // is introduced this if condition shouldn't be necessary any more
3349                 if (!loader->cssStyle) {
3350                     node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
3351                     loader->cssStyle = node;
3352                     loader->doc->node.doc.style = node;
3353                     loader->openedTag = OpenedTagType::Style;
3354                 }
3355             } else {
3356                 node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
3357             }
3358         }
3359 
3360         if (!node) return;
3361         if (node->type != SvgNodeType::Defs || !empty) {
3362             loader->stack.push(node);
3363         }
3364     } else if ((method = _findGraphicsFactory(tagName))) {
3365         if (loader->stack.count > 0) parent = loader->stack.last();
3366         else parent = loader->doc;
3367         node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
3368         if (node && !empty) {
3369             if (!strcmp(tagName, "text")) loader->openedTag = OpenedTagType::Text;
3370             auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
3371             loader->stack.push(defs);
3372             loader->currentGraphicsNode = node;
3373         }
3374     } else if ((gradientMethod = _findGradientFactory(tagName))) {
3375         SvgStyleGradient* gradient;
3376         gradient = gradientMethod(loader, attrs, attrsLength);
3377         //FIXME: The current parsing structure does not distinguish end tags.
3378         //       There is no way to know if the currently parsed gradient is in defs.
3379         //       If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
3380         //       But finally, the loader has a gradient style list regardless of defs.
3381         //       This is only to support this when multiple gradients are declared, even if no defs are declared.
3382         //       refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
3383         if (loader->def && loader->doc->node.doc.defs) {
3384             loader->def->node.defs.gradients.push(gradient);
3385         } else {
3386             loader->gradients.push(gradient);
3387         }
3388         loader->latestGradient = gradient;
3389     } else if (!strcmp(tagName, "stop")) {
3390         if (!loader->latestGradient) {
3391             TVGLOG("SVG", "Stop element is used outside of the Gradient element");
3392             return;
3393         }
3394         /* default value for opacity */
3395         loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
3396         loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
3397         simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
3398         loader->latestGradient->stops.push(loader->svgParse->gradStop);
3399     } else if (!isIgnoreUnsupportedLogElements(tagName)) {
3400         TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
3401     }
3402 }
3403 
3404 
_svgLoaderParserText(SvgLoaderData * loader,const char * content,unsigned int length)3405 static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length)
3406 {
3407     auto text = &loader->svgParse->node->node.text;
3408     text->text = strAppend(text->text, content, length);
3409 }
3410 
3411 
_svgLoaderParserXmlCssStyle(SvgLoaderData * loader,const char * content,unsigned int length)3412 static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
3413 {
3414     char* tag;
3415     char* name;
3416     const char* attrs = nullptr;
3417     unsigned int attrsLength = 0;
3418 
3419     FactoryMethod method;
3420     GradientFactoryMethod gradientMethod;
3421     SvgNode *node = nullptr;
3422 
3423     while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
3424         if ((method = _findGroupFactory(tag))) {
3425             if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
3426         } else if ((method = _findGraphicsFactory(tag))) {
3427             if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
3428         } else if ((gradientMethod = _findGradientFactory(tag))) {
3429             TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
3430         } else if (!strcmp(tag, "stop")) {
3431             TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
3432         } else if (!strcmp(tag, "all")) {
3433             if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
3434         } else if (!isIgnoreUnsupportedLogElements(tag)) {
3435             TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
3436         }
3437 
3438         length -= next - content;
3439         content = next;
3440 
3441         free(tag);
3442         free(name);
3443     }
3444     loader->openedTag = OpenedTagType::Other;
3445 }
3446 
3447 
_svgLoaderParser(void * data,SimpleXMLType type,const char * content,unsigned int length)3448 static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
3449 {
3450     SvgLoaderData* loader = (SvgLoaderData*)data;
3451 
3452     switch (type) {
3453         case SimpleXMLType::Open: {
3454             _svgLoaderParserXmlOpen(loader, content, length, false);
3455             break;
3456         }
3457         case SimpleXMLType::OpenEmpty: {
3458             _svgLoaderParserXmlOpen(loader, content, length, true);
3459             break;
3460         }
3461         case SimpleXMLType::Close: {
3462             _svgLoaderParserXmlClose(loader, content, length);
3463             break;
3464         }
3465         case SimpleXMLType::Data:
3466         case SimpleXMLType::CData: {
3467             if (loader->openedTag == OpenedTagType::Style) _svgLoaderParserXmlCssStyle(loader, content, length);
3468             else if (loader->openedTag == OpenedTagType::Text) _svgLoaderParserText(loader, content, length);
3469             break;
3470         }
3471         case SimpleXMLType::DoctypeChild: {
3472             break;
3473         }
3474         case SimpleXMLType::Ignored:
3475         case SimpleXMLType::Comment:
3476         case SimpleXMLType::Doctype: {
3477             break;
3478         }
3479         default: {
3480             break;
3481         }
3482     }
3483 
3484     return true;
3485 }
3486 
3487 
_inefficientNodeCheck(TVG_UNUSED SvgNode * node)3488 static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
3489 {
3490 #ifdef THORVG_LOG_ENABLED
3491     auto type = simpleXmlNodeTypeToString(node->type);
3492 
3493     if (!node->style->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
3494     if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
3495     if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
3496 
3497     switch (node->type) {
3498         case SvgNodeType::Path: {
3499             if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
3500             break;
3501         }
3502         case SvgNodeType::Ellipse: {
3503             if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
3504             break;
3505         }
3506         case SvgNodeType::Polygon:
3507         case SvgNodeType::Polyline: {
3508             if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
3509             break;
3510         }
3511         case SvgNodeType::Circle: {
3512             if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
3513             break;
3514         }
3515         case SvgNodeType::Rect: {
3516             if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
3517             break;
3518         }
3519         case SvgNodeType::Line: {
3520             if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
3521             break;
3522         }
3523         default: break;
3524     }
3525 #endif
3526 }
3527 
3528 
_updateStyle(SvgNode * node,SvgStyleProperty * parentStyle)3529 static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
3530 {
3531     _styleInherit(node->style, parentStyle);
3532     _inefficientNodeCheck(node);
3533 
3534     auto child = node->child.data;
3535     for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
3536         _updateStyle(*child, node->style);
3537     }
3538 }
3539 
3540 
_gradientDup(SvgLoaderData * loader,Array<SvgStyleGradient * > * gradients,const char * id)3541 static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
3542 {
3543     SvgStyleGradient* result = nullptr;
3544 
3545     auto gradList = gradients->data;
3546 
3547     for (uint32_t i = 0; i < gradients->count; ++i) {
3548         if ((*gradList)->id && !strcmp((*gradList)->id, id)) {
3549             result = _cloneGradient(*gradList);
3550             break;
3551         }
3552         ++gradList;
3553     }
3554 
3555     if (result && result->ref) {
3556         gradList = gradients->data;
3557         for (uint32_t i = 0; i < gradients->count; ++i) {
3558             if ((*gradList)->id && !strcmp((*gradList)->id, result->ref)) {
3559                 _inheritGradient(loader, result, *gradList);
3560                 break;
3561             }
3562             ++gradList;
3563         }
3564     }
3565 
3566     return result;
3567 }
3568 
3569 
_updateGradient(SvgLoaderData * loader,SvgNode * node,Array<SvgStyleGradient * > * gradients)3570 static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
3571 {
3572     if (node->child.count > 0) {
3573         auto child = node->child.data;
3574         for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
3575             _updateGradient(loader, *child, gradients);
3576         }
3577     } else {
3578         if (node->style->fill.paint.url) {
3579             auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
3580             if (newGrad) {
3581                 if (node->style->fill.paint.gradient) {
3582                     node->style->fill.paint.gradient->clear();
3583                     free(node->style->fill.paint.gradient);
3584                 }
3585                 node->style->fill.paint.gradient = newGrad;
3586             }
3587         }
3588         if (node->style->stroke.paint.url) {
3589             auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
3590             if (newGrad) {
3591                 if (node->style->stroke.paint.gradient) {
3592                     node->style->stroke.paint.gradient->clear();
3593                     free(node->style->stroke.paint.gradient);
3594                 }
3595                 node->style->stroke.paint.gradient = newGrad;
3596             }
3597         }
3598     }
3599 }
3600 
3601 
_updateComposite(SvgNode * node,SvgNode * root)3602 static void _updateComposite(SvgNode* node, SvgNode* root)
3603 {
3604     if (node->style->clipPath.url && !node->style->clipPath.node) {
3605         SvgNode* findResult = _findNodeById(root, node->style->clipPath.url);
3606         if (findResult) node->style->clipPath.node = findResult;
3607     }
3608     if (node->style->mask.url && !node->style->mask.node) {
3609         SvgNode* findResult = _findNodeById(root, node->style->mask.url);
3610         if (findResult) node->style->mask.node = findResult;
3611     }
3612     if (node->child.count > 0) {
3613         auto child = node->child.data;
3614         for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
3615             _updateComposite(*child, root);
3616         }
3617     }
3618 }
3619 
3620 
_freeNodeStyle(SvgStyleProperty * style)3621 static void _freeNodeStyle(SvgStyleProperty* style)
3622 {
3623     if (!style) return;
3624 
3625     //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
3626     free(style->clipPath.url);
3627     free(style->mask.url);
3628     free(style->cssClass);
3629 
3630     if (style->fill.paint.gradient) {
3631         style->fill.paint.gradient->clear();
3632         free(style->fill.paint.gradient);
3633     }
3634     if (style->stroke.paint.gradient) {
3635         style->stroke.paint.gradient->clear();
3636         free(style->stroke.paint.gradient);
3637     }
3638     free(style->fill.paint.url);
3639     free(style->stroke.paint.url);
3640     style->stroke.dash.array.reset();
3641     free(style);
3642 }
3643 
3644 
_freeNode(SvgNode * node)3645 static void _freeNode(SvgNode* node)
3646 {
3647     if (!node) return;
3648 
3649     auto child = node->child.data;
3650     for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
3651         _freeNode(*child);
3652     }
3653     node->child.reset();
3654 
3655     free(node->id);
3656     free(node->transform);
3657     _freeNodeStyle(node->style);
3658     switch (node->type) {
3659          case SvgNodeType::Path: {
3660              free(node->node.path.path);
3661              break;
3662          }
3663          case SvgNodeType::Polygon: {
3664              free(node->node.polygon.pts.data);
3665              break;
3666          }
3667          case SvgNodeType::Polyline: {
3668              free(node->node.polyline.pts.data);
3669              break;
3670          }
3671          case SvgNodeType::Doc: {
3672              _freeNode(node->node.doc.defs);
3673              _freeNode(node->node.doc.style);
3674              break;
3675          }
3676          case SvgNodeType::Defs: {
3677              auto gradients = node->node.defs.gradients.data;
3678              for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
3679                  (*gradients)->clear();
3680                  free(*gradients);
3681                  ++gradients;
3682              }
3683              node->node.defs.gradients.reset();
3684              break;
3685          }
3686          case SvgNodeType::Image: {
3687              free(node->node.image.href);
3688              break;
3689          }
3690          case SvgNodeType::Text: {
3691              free(node->node.text.text);
3692              free(node->node.text.fontFamily);
3693              break;
3694          }
3695          default: {
3696              break;
3697          }
3698     }
3699     free(node);
3700 }
3701 
3702 
_svgLoaderParserForValidCheckXmlOpen(SvgLoaderData * loader,const char * content,unsigned int length)3703 static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
3704 {
3705     const char* attrs = nullptr;
3706     int sz = length;
3707     char tagName[20] = "";
3708     FactoryMethod method;
3709     SvgNode *node = nullptr;
3710     int attrsLength = 0;
3711     loader->level++;
3712     attrs = simpleXmlFindAttributesTag(content, length);
3713 
3714     if (!attrs) {
3715         //Parse the empty tag
3716         attrs = content;
3717         while ((attrs != nullptr) && *attrs != '>') attrs++;
3718     }
3719 
3720     if (attrs) {
3721         sz = attrs - content;
3722         while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
3723         if ((unsigned)sz >= sizeof(tagName)) return false;
3724         strncpy(tagName, content, sz);
3725         tagName[sz] = '\0';
3726         attrsLength = length - sz;
3727     }
3728 
3729     if ((method = _findGroupFactory(tagName))) {
3730         if (!loader->doc) {
3731             if (strcmp(tagName, "svg")) return true; //Not a valid svg document
3732             node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
3733             loader->doc = node;
3734             loader->stack.push(node);
3735             return false;
3736         }
3737     }
3738     return true;
3739 }
3740 
3741 
_svgLoaderParserForValidCheck(void * data,SimpleXMLType type,const char * content,unsigned int length)3742 static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
3743 {
3744     SvgLoaderData* loader = (SvgLoaderData*)data;
3745     bool res = true;;
3746 
3747     switch (type) {
3748         case SimpleXMLType::Open:
3749         case SimpleXMLType::OpenEmpty: {
3750             //If 'res' is false, it means <svg> tag is found.
3751             res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
3752             break;
3753         }
3754         default: {
3755             break;
3756         }
3757     }
3758 
3759     return res;
3760 }
3761 
3762 
clear(bool all)3763 void SvgLoader::clear(bool all)
3764 {
3765     //flush out the intermediate data
3766     free(loaderData.svgParse);
3767     loaderData.svgParse = nullptr;
3768 
3769     for (auto gradient = loaderData.gradients.begin(); gradient < loaderData.gradients.end(); ++gradient) {
3770         (*gradient)->clear();
3771         free(*gradient);
3772     }
3773     loaderData.gradients.reset();
3774 
3775     _freeNode(loaderData.doc);
3776     loaderData.doc = nullptr;
3777     loaderData.stack.reset();
3778 
3779     if (!all) return;
3780 
3781     for (auto p = loaderData.images.begin(); p < loaderData.images.end(); ++p) {
3782         free(*p);
3783     }
3784     loaderData.images.reset();
3785 
3786     if (copy) free((char*)content);
3787 
3788     delete(root);
3789     root = nullptr;
3790 
3791     size = 0;
3792     content = nullptr;
3793     copy = false;
3794 }
3795 
3796 
3797 /************************************************************************/
3798 /* External Class Implementation                                        */
3799 /************************************************************************/
3800 
SvgLoader()3801 SvgLoader::SvgLoader() : ImageLoader(FileType::Svg)
3802 {
3803 }
3804 
3805 
~SvgLoader()3806 SvgLoader::~SvgLoader()
3807 {
3808     this->done();
3809     clear();
3810 }
3811 
3812 
run(unsigned tid)3813 void SvgLoader::run(unsigned tid)
3814 {
3815     //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering
3816     if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLOAT_EPSILON || fabsf(vh) <= FLOAT_EPSILON)) {
3817         TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
3818         root = Scene::gen().release();
3819         return;
3820     }
3821 
3822     if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
3823 
3824     if (loaderData.doc) {
3825         auto defs = loaderData.doc->node.doc.defs;
3826 
3827         if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
3828         if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle);
3829 
3830         if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
3831 
3832         _updateComposite(loaderData.doc, loaderData.doc);
3833         if (defs) _updateComposite(loaderData.doc, defs);
3834 
3835         _updateStyle(loaderData.doc, nullptr);
3836         if (defs) _updateStyle(defs, nullptr);
3837 
3838         if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
3839         if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
3840     }
3841     root = svgSceneBuild(loaderData, {vx, vy, vw, vh}, w, h, align, meetOrSlice, svgPath, viewFlag);
3842 
3843     //In case no viewbox and width/height data is provided the completion of loading
3844     //has to be forced, in order to establish this data based on the whole picture.
3845     if (!(viewFlag & SvgViewFlag::Viewbox)) {
3846         //Override viewbox & size again after svg loading.
3847         vx = loaderData.doc->node.doc.vx;
3848         vy = loaderData.doc->node.doc.vy;
3849         vw = loaderData.doc->node.doc.vw;
3850         vh = loaderData.doc->node.doc.vh;
3851         w = loaderData.doc->node.doc.w;
3852         h = loaderData.doc->node.doc.h;
3853     }
3854 
3855     clear(false);
3856 }
3857 
3858 
header()3859 bool SvgLoader::header()
3860 {
3861     //For valid check, only <svg> tag is parsed first.
3862     //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
3863     //After that, the remaining content data is parsed in order with async.
3864     loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
3865     if (!loaderData.svgParse) return false;
3866 
3867     loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
3868     viewFlag = SvgViewFlag::None;
3869 
3870     simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
3871 
3872     if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
3873         viewFlag = loaderData.doc->node.doc.viewFlag;
3874         align = loaderData.doc->node.doc.align;
3875         meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
3876 
3877         if (viewFlag & SvgViewFlag::Viewbox) {
3878             vx = loaderData.doc->node.doc.vx;
3879             vy = loaderData.doc->node.doc.vy;
3880             vw = loaderData.doc->node.doc.vw;
3881             vh = loaderData.doc->node.doc.vh;
3882 
3883             if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
3884             else {
3885                 w = loaderData.doc->node.doc.vw;
3886                 if (viewFlag & SvgViewFlag::WidthInPercent) {
3887                     w *= loaderData.doc->node.doc.w;
3888                     viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
3889                 }
3890                 viewFlag = (viewFlag | SvgViewFlag::Width);
3891             }
3892             if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
3893             else {
3894                 h = loaderData.doc->node.doc.vh;
3895                 if (viewFlag & SvgViewFlag::HeightInPercent) {
3896                     h *= loaderData.doc->node.doc.h;
3897                     viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
3898                 }
3899                 viewFlag = (viewFlag | SvgViewFlag::Height);
3900             }
3901         //In case no viewbox and width/height data is provided the completion of loading
3902         //has to be forced, in order to establish this data based on the whole picture.
3903         } else {
3904             //Before loading, set default viewbox & size if they are empty
3905             vx = vy = 0.0f;
3906             if (viewFlag & SvgViewFlag::Width) {
3907                 vw = w = loaderData.doc->node.doc.w;
3908             } else {
3909                 vw = 1.0f;
3910                 if (viewFlag & SvgViewFlag::WidthInPercent) {
3911                     w = loaderData.doc->node.doc.w;
3912                 } else w = 1.0f;
3913             }
3914 
3915             if (viewFlag & SvgViewFlag::Height) {
3916                 vh = h = loaderData.doc->node.doc.h;
3917             } else {
3918                 vh = 1.0f;
3919                 if (viewFlag & SvgViewFlag::HeightInPercent) {
3920                     h = loaderData.doc->node.doc.h;
3921                 } else h = 1.0f;
3922             }
3923 
3924             run(0);
3925         }
3926 
3927         return true;
3928     }
3929 
3930     TVGLOG("SVG", "No SVG File. There is no <svg/>");
3931     return false;
3932 }
3933 
3934 
open(const char * data,uint32_t size,bool copy)3935 bool SvgLoader::open(const char* data, uint32_t size, bool copy)
3936 {
3937     clear();
3938 
3939     if (copy) {
3940         content = (char*)malloc(size + 1);
3941         if (!content) return false;
3942         memcpy((char*)content, data, size);
3943         content[size] = '\0';
3944     } else content = (char*)data;
3945 
3946     this->size = size;
3947     this->copy = copy;
3948 
3949     return header();
3950 }
3951 
3952 
open(const string & path)3953 bool SvgLoader::open(const string& path)
3954 {
3955     clear();
3956 
3957     ifstream f;
3958     f.open(path);
3959 
3960     if (!f.is_open()) return false;
3961 
3962     svgPath = path;
3963     getline(f, filePath, '\0');
3964     f.close();
3965 
3966     if (filePath.empty()) return false;
3967 
3968     content = (char*)filePath.c_str();
3969     size = filePath.size();
3970 
3971     return header();
3972 }
3973 
3974 
resize(Paint * paint,float w,float h)3975 bool SvgLoader::resize(Paint* paint, float w, float h)
3976 {
3977     if (!paint) return false;
3978 
3979     auto sx = w / this->w;
3980     auto sy = h / this->h;
3981     Matrix m = {sx, 0, 0, 0, sy, 0, 0, 0, 1};
3982     paint->transform(m);
3983 
3984     return true;
3985 }
3986 
3987 
read()3988 bool SvgLoader::read()
3989 {
3990     if (!content || size == 0) return false;
3991 
3992     //the loading has been already completed in header()
3993     if (root || !LoadModule::read()) return true;
3994 
3995     TaskScheduler::request(this);
3996 
3997     return true;
3998 }
3999 
4000 
close()4001 bool SvgLoader::close()
4002 {
4003     if (!LoadModule::close()) return false;
4004     this->done();
4005     clear();
4006     return true;
4007 }
4008 
4009 
paint()4010 Paint* SvgLoader::paint()
4011 {
4012     this->done();
4013     auto ret = root;
4014     root = nullptr;
4015     return ret;
4016 }
4017 
4018 #endif /* LV_USE_THORVG_INTERNAL */
4019 
4020