2 * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
25 #include "tvgLoaderMgr.h"
26 #include "tvgXmlParser.h"
27 #include "tvgSvgLoader.h"
28 #include "tvgSvgSceneBuilder.h"
29 #include "tvgSvgUtil.h"
31 /************************************************************************/
32 /* Internal Class Implementation */
33 /************************************************************************/
35 typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
36 typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
41 static char* _skipSpace(const char* str, const char* end)
43 while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
50 static string* _copyId(const char* str)
52 if (!str) return nullptr;
54 return new string(str);
58 static const char* _skipComma(const char* content)
60 content = _skipSpace(content, nullptr);
61 if (*content == ',') return content + 1;
66 static bool _parseNumber(const char** content, float* number)
70 *number = svgUtilStrtof(*content, &end);
71 //If the start of string is not number
72 if ((*content) == end) return false;
74 *content = _skipComma(end);
79 * According to https://www.w3.org/TR/SVG/coords.html#Units
82 * Since this documentation is not obvious, more clean recalculation with dpi
83 * is required, but for now default w3 constants would be used
85 static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
87 float parsedValue = svgUtilStrtof(str, nullptr);
89 if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
90 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
91 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
92 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
93 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
94 else if (strstr(str, "%")) {
95 if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
96 else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
97 else //if other then it's radius
99 float max = svgParse->global.w;
100 if (max < svgParse->global.h)
101 max = svgParse->global.h;
102 parsedValue = (parsedValue / 100.0) * max;
106 //TODO: Implement 'em', 'ex' attributes
112 static float _gradientToFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
116 float parsedValue = svgUtilStrtof(str, &end);
120 * That is according to Units in here
122 * https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
124 if (type == SvgParserLengthType::Vertical) max = svgParse->global.h;
125 else if (type == SvgParserLengthType::Horizontal) max = svgParse->global.w;
126 else if (type == SvgParserLengthType::Other) max = sqrt(pow(svgParse->global.h, 2) + pow(svgParse->global.w, 2)) / sqrt(2.0);
128 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
129 else if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
130 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
131 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
132 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
133 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
134 //TODO: Implement 'em', 'ex' attributes
136 //Transform into global percentage
137 parsedValue = parsedValue / max;
143 static float _toOffset(const char* str)
147 float parsedValue = svgUtilStrtof(str, &end);
149 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
155 static int _toOpacity(const char* str)
159 float opacity = svgUtilStrtof(str, &end);
161 if (end && (*end == '\0')) a = lrint(opacity * 255);
166 #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
167 static Type _to##Name1(const char* str) \
171 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
172 if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
178 /* parse the line cap used during stroking a path.
179 * Value: butt | round | square | inherit
181 * https://www.w3.org/TR/SVG/painting.html
183 static constexpr struct
188 { StrokeCap::Butt, "butt" },
189 { StrokeCap::Round, "round" },
190 { StrokeCap::Square, "square" }
194 _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
197 /* parse the line join used during stroking a path.
198 * Value: miter | round | bevel | inherit
200 * https://www.w3.org/TR/SVG/painting.html
202 static constexpr struct
207 { StrokeJoin::Miter, "miter" },
208 { StrokeJoin::Round, "round" },
209 { StrokeJoin::Bevel, "bevel" }
213 _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
216 /* parse the fill rule used during filling a path.
217 * Value: nonzero | evenodd | inherit
219 * https://www.w3.org/TR/SVG/painting.html
221 static constexpr struct
223 SvgFillRule fillRule;
226 { SvgFillRule::OddEven, "evenodd" }
230 _PARSE_TAG(SvgFillRule, fillRule, FillRule, fillRuleTags, SvgFillRule::Winding)
233 /* parse the dash pattern used during stroking a path.
234 * Value: none | <dasharray> | inherit
236 * https://www.w3.org/TR/SVG/painting.html
239 _parseDashArray(const char *str, SvgDash* dash)
244 // skip white space, comma
245 str = _skipComma(str);
246 (*dash).array.push(svgUtilStrtof(str, &end));
247 str = _skipComma(end);
249 //If dash array size is 1, it means that dash and gap size are the same.
250 if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
253 static string* _idFromUrl(const char* url)
258 url = _skipSpace(url, nullptr);
261 url = _skipSpace(url, nullptr);
264 if ((*url) == '#') ++url;
266 while ((*url) != ')') {
272 return new string(tmp);
276 static unsigned char _parserColor(const char* value, char** end)
280 r = svgUtilStrtof(value + 4, end);
281 *end = _skipSpace(*end, nullptr);
282 if (**end == '%') r = 255 * r / 100;
283 *end = _skipSpace(*end, nullptr);
285 if (r < 0 || r > 255) {
294 static constexpr struct
299 { "aliceblue", 0xfff0f8ff },
300 { "antiquewhite", 0xfffaebd7 },
301 { "aqua", 0xff00ffff },
302 { "aquamarine", 0xff7fffd4 },
303 { "azure", 0xfff0ffff },
304 { "beige", 0xfff5f5dc },
305 { "bisque", 0xffffe4c4 },
306 { "black", 0xff000000 },
307 { "blanchedalmond", 0xffffebcd },
308 { "blue", 0xff0000ff },
309 { "blueviolet", 0xff8a2be2 },
310 { "brown", 0xffa52a2a },
311 { "burlywood", 0xffdeb887 },
312 { "cadetblue", 0xff5f9ea0 },
313 { "chartreuse", 0xff7fff00 },
314 { "chocolate", 0xffd2691e },
315 { "coral", 0xffff7f50 },
316 { "cornflowerblue", 0xff6495ed },
317 { "cornsilk", 0xfffff8dc },
318 { "crimson", 0xffdc143c },
319 { "cyan", 0xff00ffff },
320 { "darkblue", 0xff00008b },
321 { "darkcyan", 0xff008b8b },
322 { "darkgoldenrod", 0xffb8860b },
323 { "darkgray", 0xffa9a9a9 },
324 { "darkgrey", 0xffa9a9a9 },
325 { "darkgreen", 0xff006400 },
326 { "darkkhaki", 0xffbdb76b },
327 { "darkmagenta", 0xff8b008b },
328 { "darkolivegreen", 0xff556b2f },
329 { "darkorange", 0xffff8c00 },
330 { "darkorchid", 0xff9932cc },
331 { "darkred", 0xff8b0000 },
332 { "darksalmon", 0xffe9967a },
333 { "darkseagreen", 0xff8fbc8f },
334 { "darkslateblue", 0xff483d8b },
335 { "darkslategray", 0xff2f4f4f },
336 { "darkslategrey", 0xff2f4f4f },
337 { "darkturquoise", 0xff00ced1 },
338 { "darkviolet", 0xff9400d3 },
339 { "deeppink", 0xffff1493 },
340 { "deepskyblue", 0xff00bfff },
341 { "dimgray", 0xff696969 },
342 { "dimgrey", 0xff696969 },
343 { "dodgerblue", 0xff1e90ff },
344 { "firebrick", 0xffb22222 },
345 { "floralwhite", 0xfffffaf0 },
346 { "forestgreen", 0xff228b22 },
347 { "fuchsia", 0xffff00ff },
348 { "gainsboro", 0xffdcdcdc },
349 { "ghostwhite", 0xfff8f8ff },
350 { "gold", 0xffffd700 },
351 { "goldenrod", 0xffdaa520 },
352 { "gray", 0xff808080 },
353 { "grey", 0xff808080 },
354 { "green", 0xff008000 },
355 { "greenyellow", 0xffadff2f },
356 { "honeydew", 0xfff0fff0 },
357 { "hotpink", 0xffff69b4 },
358 { "indianred", 0xffcd5c5c },
359 { "indigo", 0xff4b0082 },
360 { "ivory", 0xfffffff0 },
361 { "khaki", 0xfff0e68c },
362 { "lavender", 0xffe6e6fa },
363 { "lavenderblush", 0xfffff0f5 },
364 { "lawngreen", 0xff7cfc00 },
365 { "lemonchiffon", 0xfffffacd },
366 { "lightblue", 0xffadd8e6 },
367 { "lightcoral", 0xfff08080 },
368 { "lightcyan", 0xffe0ffff },
369 { "lightgoldenrodyellow", 0xfffafad2 },
370 { "lightgray", 0xffd3d3d3 },
371 { "lightgrey", 0xffd3d3d3 },
372 { "lightgreen", 0xff90ee90 },
373 { "lightpink", 0xffffb6c1 },
374 { "lightsalmon", 0xffffa07a },
375 { "lightseagreen", 0xff20b2aa },
376 { "lightskyblue", 0xff87cefa },
377 { "lightslategray", 0xff778899 },
378 { "lightslategrey", 0xff778899 },
379 { "lightsteelblue", 0xffb0c4de },
380 { "lightyellow", 0xffffffe0 },
381 { "lime", 0xff00ff00 },
382 { "limegreen", 0xff32cd32 },
383 { "linen", 0xfffaf0e6 },
384 { "magenta", 0xffff00ff },
385 { "maroon", 0xff800000 },
386 { "mediumaquamarine", 0xff66cdaa },
387 { "mediumblue", 0xff0000cd },
388 { "mediumorchid", 0xffba55d3 },
389 { "mediumpurple", 0xff9370d8 },
390 { "mediumseagreen", 0xff3cb371 },
391 { "mediumslateblue", 0xff7b68ee },
392 { "mediumspringgreen", 0xff00fa9a },
393 { "mediumturquoise", 0xff48d1cc },
394 { "mediumvioletred", 0xffc71585 },
395 { "midnightblue", 0xff191970 },
396 { "mintcream", 0xfff5fffa },
397 { "mistyrose", 0xffffe4e1 },
398 { "moccasin", 0xffffe4b5 },
399 { "navajowhite", 0xffffdead },
400 { "navy", 0xff000080 },
401 { "oldlace", 0xfffdf5e6 },
402 { "olive", 0xff808000 },
403 { "olivedrab", 0xff6b8e23 },
404 { "orange", 0xffffa500 },
405 { "orangered", 0xffff4500 },
406 { "orchid", 0xffda70d6 },
407 { "palegoldenrod", 0xffeee8aa },
408 { "palegreen", 0xff98fb98 },
409 { "paleturquoise", 0xffafeeee },
410 { "palevioletred", 0xffd87093 },
411 { "papayawhip", 0xffffefd5 },
412 { "peachpuff", 0xffffdab9 },
413 { "peru", 0xffcd853f },
414 { "pink", 0xffffc0cb },
415 { "plum", 0xffdda0dd },
416 { "powderblue", 0xffb0e0e6 },
417 { "purple", 0xff800080 },
418 { "red", 0xffff0000 },
419 { "rosybrown", 0xffbc8f8f },
420 { "royalblue", 0xff4169e1 },
421 { "saddlebrown", 0xff8b4513 },
422 { "salmon", 0xfffa8072 },
423 { "sandybrown", 0xfff4a460 },
424 { "seagreen", 0xff2e8b57 },
425 { "seashell", 0xfffff5ee },
426 { "sienna", 0xffa0522d },
427 { "silver", 0xffc0c0c0 },
428 { "skyblue", 0xff87ceeb },
429 { "slateblue", 0xff6a5acd },
430 { "slategray", 0xff708090 },
431 { "slategrey", 0xff708090 },
432 { "snow", 0xfffffafa },
433 { "springgreen", 0xff00ff7f },
434 { "steelblue", 0xff4682b4 },
435 { "tan", 0xffd2b48c },
436 { "teal", 0xff008080 },
437 { "thistle", 0xffd8bfd8 },
438 { "tomato", 0xffff6347 },
439 { "turquoise", 0xff40e0d0 },
440 { "violet", 0xffee82ee },
441 { "wheat", 0xfff5deb3 },
442 { "white", 0xffffffff },
443 { "whitesmoke", 0xfff5f5f5 },
444 { "yellow", 0xffffff00 },
445 { "yellowgreen", 0xff9acd32 }
449 static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, string** ref)
451 unsigned int len = strlen(str);
452 char *red, *green, *blue;
453 unsigned char tr, tg, tb;
455 if (len == 4 && str[0] == '#') {
456 //Case for "#456" should be interprete as "#445566"
457 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
458 char tmp[3] = { '\0', '\0', '\0' };
461 *r = strtol(tmp, nullptr, 16);
464 *g = strtol(tmp, nullptr, 16);
467 *b = strtol(tmp, nullptr, 16);
469 } else if (len == 7 && str[0] == '#') {
470 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
471 char tmp[3] = { '\0', '\0', '\0' };
474 *r = strtol(tmp, nullptr, 16);
477 *g = strtol(tmp, nullptr, 16);
480 *b = strtol(tmp, nullptr, 16);
482 } 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] == ')') {
483 tr = _parserColor(str + 4, &red);
484 if (red && *red == ',') {
485 tg = _parserColor(red + 1, &green);
486 if (green && *green == ',') {
487 tb = _parserColor(green + 1, &blue);
488 if (blue && blue[0] == ')' && blue[1] == '\0') {
495 } else if (len >= 3 && !strncmp(str, "url", 3)) {
496 *ref = _idFromUrl((const char*)(str + 3));
499 for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
500 if (!strcasecmp(colors[i].name, str)) {
501 *r = (((uint8_t*)(&(colors[i].value)))[2]);
502 *g = (((uint8_t*)(&(colors[i].value)))[1]);
503 *b = (((uint8_t*)(&(colors[i].value)))[0]);
510 static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
515 str = _skipSpace(str, nullptr);
516 while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
517 points[count++] = svgUtilStrtof(str, &end);
519 str = _skipSpace(str, nullptr);
520 if (*str == ',') ++str;
521 //Eat the rest of space
522 str = _skipSpace(str, nullptr);
529 enum class MatrixState {
540 #define MATRIX_DEF(Name, Value) \
542 #Name, sizeof(#Name), Value \
546 static constexpr struct
552 MATRIX_DEF(matrix, MatrixState::Matrix),
553 MATRIX_DEF(translate, MatrixState::Translate),
554 MATRIX_DEF(rotate, MatrixState::Rotate),
555 MATRIX_DEF(scale, MatrixState::Scale),
556 MATRIX_DEF(skewX, MatrixState::SkewX),
557 MATRIX_DEF(skewY, MatrixState::SkewY)
561 static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
563 auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
564 auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
565 auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
567 auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
568 auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
569 auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
571 auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
572 auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
573 auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
587 /* parse transform attribute
588 * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
590 static Matrix* _parseTransformationMatrix(const char* value)
592 const int POINT_CNT = 8;
594 auto matrix = (Matrix*)malloc(sizeof(Matrix));
595 if (!matrix) return nullptr;
596 *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
598 float points[POINT_CNT];
600 char* str = (char*)value;
601 char* end = str + strlen(str);
604 auto state = MatrixState::Unknown;
606 if (isspace(*str) || (*str == ',')) {
610 for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
611 if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
612 state = matrixTags[i].state;
613 str += (matrixTags[i].sz - 1);
617 if (state == MatrixState::Unknown) goto error;
619 str = _skipSpace(str, end);
620 if (*str != '(') goto error;
622 str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
623 if (*str != ')') goto error;
626 if (state == MatrixState::Matrix) {
627 if (ptCount != 6) goto error;
628 Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
629 _matrixCompose(matrix, &tmp, matrix);
630 } else if (state == MatrixState::Translate) {
632 Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
633 _matrixCompose(matrix, &tmp, matrix);
634 } else if (ptCount == 2) {
635 Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
636 _matrixCompose(matrix, &tmp, matrix);
638 } else if (state == MatrixState::Rotate) {
639 //Transform to signed.
640 points[0] = fmod(points[0], 360);
641 if (points[0] < 0) points[0] += 360;
642 auto c = cosf(points[0] * (M_PI / 180.0));
643 auto s = sinf(points[0] * (M_PI / 180.0));
645 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
646 _matrixCompose(matrix, &tmp, matrix);
647 } else if (ptCount == 3) {
648 Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
649 _matrixCompose(matrix, &tmp, matrix);
650 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
651 _matrixCompose(matrix, &tmp, matrix);
652 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
653 _matrixCompose(matrix, &tmp, matrix);
657 } else if (state == MatrixState::Scale) {
658 if (ptCount < 1 || ptCount > 2) goto error;
661 if (ptCount == 2) sy = points[1];
662 Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
663 _matrixCompose(matrix, &tmp, matrix);
668 if (matrix) free(matrix);
673 #define LENGTH_DEF(Name, Value) \
675 #Name, sizeof(#Name), Value \
679 static constexpr struct
685 LENGTH_DEF(%, SvgLengthType::Percent),
686 LENGTH_DEF(px, SvgLengthType::Px),
687 LENGTH_DEF(pc, SvgLengthType::Pc),
688 LENGTH_DEF(pt, SvgLengthType::Pt),
689 LENGTH_DEF(mm, SvgLengthType::Mm),
690 LENGTH_DEF(cm, SvgLengthType::Cm),
691 LENGTH_DEF(in, SvgLengthType::In)
695 static float _parseLength(const char* str, SvgLengthType* type)
698 int sz = strlen(str);
700 *type = SvgLengthType::Px;
701 for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
702 if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
704 value = svgUtilStrtof(str, nullptr);
709 static bool _parseStyleAttr(void* data, const char* key, const char* value);
712 static bool _attrParseSvgNode(void* data, const char* key, const char* value)
714 SvgLoaderData* loader = (SvgLoaderData*)data;
715 SvgNode* node = loader->svgParse->node;
716 SvgDocNode* doc = &(node->node.doc);
719 //TODO: handle length unit.
720 if (!strcmp(key, "width")) {
721 doc->w = _parseLength(value, &type);
722 } else if (!strcmp(key, "height")) {
723 doc->h = _parseLength(value, &type);
724 } else if (!strcmp(key, "viewBox")) {
725 if (_parseNumber(&value, &doc->vx)) {
726 if (_parseNumber(&value, &doc->vy)) {
727 if (_parseNumber(&value, &doc->vw)) {
728 _parseNumber(&value, &doc->vh);
729 loader->svgParse->global.h = doc->vh;
731 loader->svgParse->global.w = doc->vw;
733 loader->svgParse->global.y = doc->vy;
735 loader->svgParse->global.x = doc->vx;
736 } else if (!strcmp(key, "preserveAspectRatio")) {
737 if (!strcmp(value, "none")) doc->preserveAspect = false;
738 } else if (!strcmp(key, "style")) {
739 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
741 #ifdef THORVG_LOG_ENABLED
742 else if (!strcmp(key, "xmlns") || !strcmp(key, "xmlns:xlink") || !strcmp(key, "xmlns:svg")) {
747 return _parseStyleAttr(loader, key, value);
753 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
754 static void _handlePaintAttr(SvgPaint* paint, const char* value)
756 if (!strcmp(value, "none")) {
762 if (!strcmp(value, "currentColor")) {
763 paint->curColor = true;
766 _toColor(value, &paint->r, &paint->g, &paint->b, &paint->url);
770 static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
772 SvgStyleProperty* style = node->style;
773 _toColor(value, &style->r, &style->g, &style->b, nullptr);
777 static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
779 SvgStyleProperty* style = node->style;
780 style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
781 _handlePaintAttr(&style->fill.paint, value);
785 static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
787 SvgStyleProperty* style = node->style;
788 style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
789 _handlePaintAttr(&style->stroke.paint, value);
793 static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
795 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
796 node->style->stroke.opacity = _toOpacity(value);
799 static void _handleStrokeDashArrayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
801 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
802 _parseDashArray(value, &node->style->stroke.dash);
805 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
807 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
808 node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
812 static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
814 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
815 node->style->stroke.cap = _toLineCap(value);
819 static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
821 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
822 node->style->stroke.join = _toLineJoin(value);
826 static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
828 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
829 node->style->fill.fillRule = _toFillRule(value);
833 static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
835 node->style->opacity = _toOpacity(value);
839 static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
841 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
842 node->style->fill.opacity = _toOpacity(value);
846 static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
848 node->transform = _parseTransformationMatrix(value);
852 static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
854 SvgStyleProperty* style = node->style;
855 #ifdef THORVG_LOG_ENABLED
856 if (style->comp.method != CompositeMethod::None) printf("SVG: Multiple Composition Tried!\n");
858 style->comp.method = CompositeMethod::ClipPath;
859 int len = strlen(value);
860 if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
864 static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
866 SvgStyleProperty* style = node->style;
867 #ifdef THORVG_LOG_ENABLED
868 if (style->comp.method != CompositeMethod::None) printf("SVG: Multiple Composition Tried!\n");
870 style->comp.method = CompositeMethod::AlphaMask;
871 int len = strlen(value);
872 if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
876 static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
878 //TODO : The display attribute can have various values as well as "none".
879 // The default is "inline" which means visible and "none" means invisible.
880 // Depending on the type of node, additional functionality may be required.
881 // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
882 if (!strcmp(value, "none")) node->display = false;
883 else node->display = true;
887 typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
889 #define STYLE_DEF(Name, Name1) { #Name, sizeof(#Name), _handle##Name1##Attr }
891 static constexpr struct
895 styleMethod tagHandler;
897 STYLE_DEF(color, Color),
898 STYLE_DEF(fill, Fill),
899 STYLE_DEF(fill-rule, FillRule),
900 STYLE_DEF(fill-opacity, FillOpacity),
901 STYLE_DEF(opacity, Opacity),
902 STYLE_DEF(stroke, Stroke),
903 STYLE_DEF(stroke-width, StrokeWidth),
904 STYLE_DEF(stroke-linejoin, StrokeLineJoin),
905 STYLE_DEF(stroke-linecap, StrokeLineCap),
906 STYLE_DEF(stroke-opacity, StrokeOpacity),
907 STYLE_DEF(stroke-dasharray, StrokeDashArray),
908 STYLE_DEF(transform, Transform),
909 STYLE_DEF(clip-path, ClipPath),
910 STYLE_DEF(mask, Mask),
911 STYLE_DEF(display, Display)
915 static bool _parseStyleAttr(void* data, const char* key, const char* value)
917 SvgLoaderData* loader = (SvgLoaderData*)data;
918 SvgNode* node = loader->svgParse->node;
920 if (!key || !value) return false;
922 //Trim the white space
923 key = _skipSpace(key, nullptr);
925 value = _skipSpace(value, nullptr);
928 for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
929 if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
930 styleTags[i].tagHandler(loader, node, value);
939 * https://www.w3.org/TR/SVG/struct.html#Groups
941 static bool _attrParseGNode(void* data, const char* key, const char* value)
943 SvgLoaderData* loader = (SvgLoaderData*)data;
944 SvgNode* node = loader->svgParse->node;
946 if (!strcmp(key, "style")) {
947 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
948 } else if (!strcmp(key, "transform")) {
949 node->transform = _parseTransformationMatrix(value);
950 } else if (!strcmp(key, "id")) {
951 node->id = _copyId(value);
952 } else if (!strcmp(key, "clip-path")) {
953 _handleClipPathAttr(loader, node, value);
954 } else if (!strcmp(key, "mask")) {
955 _handleMaskAttr(loader, node, value);
957 return _parseStyleAttr(loader, key, value);
963 /* parse clipPath node
964 * https://www.w3.org/TR/SVG/struct.html#Groups
966 static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
968 SvgLoaderData* loader = (SvgLoaderData*)data;
969 SvgNode* node = loader->svgParse->node;
971 if (!strcmp(key, "style")) {
972 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
973 } else if (!strcmp(key, "transform")) {
974 node->transform = _parseTransformationMatrix(value);
975 } else if (!strcmp(key, "id")) {
976 node->id = _copyId(value);
978 return _parseStyleAttr(loader, key, value);
984 static bool _attrParseMaskNode(void* data, const char* key, const char* value)
986 SvgLoaderData* loader = (SvgLoaderData*)data;
987 SvgNode* node = loader->svgParse->node;
989 if (!strcmp(key, "style")) {
990 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
991 } else if (!strcmp(key, "transform")) {
992 node->transform = _parseTransformationMatrix(value);
993 } else if (!strcmp(key, "id")) {
994 node->id = _copyId(value);
996 return _parseStyleAttr(loader, key, value);
1002 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
1004 SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
1006 if (!node) return nullptr;
1008 //Default fill property
1009 node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
1016 //Update the default value of stroke and fill
1017 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
1018 node->style->fill.paint.none = false;
1019 //Default fill opacity is 1
1020 node->style->fill.opacity = 255;
1021 node->style->opacity = 255;
1023 //Default fill rule is nonzero
1024 node->style->fill.fillRule = SvgFillRule::Winding;
1026 //Default stroke is none
1027 node->style->stroke.paint.none = true;
1028 //Default stroke opacity is 1
1029 node->style->stroke.opacity = 255;
1030 //Default stroke width is 1
1031 node->style->stroke.width = 1;
1032 //Default line cap is butt
1033 node->style->stroke.cap = StrokeCap::Butt;
1034 //Default line join is miter
1035 node->style->stroke.join = StrokeJoin::Miter;
1036 node->style->stroke.scale = 1.0;
1038 //Default display is true("inline").
1039 node->display = true;
1041 node->parent = parent;
1044 if (parent) parent->child.push(node);
1049 static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength)
1051 SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
1052 if (!node) return nullptr;
1053 simpleXmlParseAttributes(buf, bufLength, nullptr, node);
1058 static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1060 loader->svgParse->node = _createNode(parent, SvgNodeType::G);
1061 if (!loader->svgParse->node) return nullptr;
1063 simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
1064 return loader->svgParse->node;
1068 static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1070 loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
1071 if (!loader->svgParse->node) return nullptr;
1072 SvgDocNode* doc = &(loader->svgParse->node->node.doc);
1074 doc->preserveAspect = true;
1075 simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
1077 return loader->svgParse->node;
1081 static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength)
1083 loader->svgParse->node = _createNode(parent, SvgNodeType::Unknown);
1084 if (!loader->svgParse->node) return nullptr;
1086 simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader);
1088 return loader->svgParse->node;
1092 static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1094 loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
1096 if (!loader->svgParse->node) return nullptr;
1098 loader->svgParse->node->display = false;
1100 simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
1102 return loader->svgParse->node;
1105 static bool _attrParsePathNode(void* data, const char* key, const char* value)
1107 SvgLoaderData* loader = (SvgLoaderData*)data;
1108 SvgNode* node = loader->svgParse->node;
1109 SvgPathNode* path = &(node->node.path);
1111 if (!strcmp(key, "d")) {
1112 //Temporary: need to copy
1113 path->path = _copyId(value);
1114 } else if (!strcmp(key, "style")) {
1115 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1116 } else if (!strcmp(key, "clip-path")) {
1117 _handleClipPathAttr(loader, node, value);
1118 } else if (!strcmp(key, "mask")) {
1119 _handleMaskAttr(loader, node, value);
1120 } else if (!strcmp(key, "id")) {
1121 node->id = _copyId(value);
1123 return _parseStyleAttr(loader, key, value);
1129 static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1131 loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
1133 if (!loader->svgParse->node) return nullptr;
1135 simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
1137 return loader->svgParse->node;
1141 static constexpr struct
1144 SvgParserLengthType type;
1148 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
1149 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
1150 {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
1154 /* parse the attributes for a circle element.
1155 * https://www.w3.org/TR/SVG/shapes.html#CircleElement
1157 static bool _attrParseCircleNode(void* data, const char* key, const char* value)
1159 SvgLoaderData* loader = (SvgLoaderData*)data;
1160 SvgNode* node = loader->svgParse->node;
1161 SvgCircleNode* circle = &(node->node.circle);
1162 unsigned char* array;
1163 int sz = strlen(key);
1165 array = (unsigned char*)circle;
1166 for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
1167 if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
1168 *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
1173 if (!strcmp(key, "style")) {
1174 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1175 } else if (!strcmp(key, "clip-path")) {
1176 _handleClipPathAttr(loader, node, value);
1177 } else if (!strcmp(key, "mask")) {
1178 _handleMaskAttr(loader, node, value);
1179 } else if (!strcmp(key, "id")) {
1180 node->id = _copyId(value);
1182 return _parseStyleAttr(loader, key, value);
1188 static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1190 loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
1192 if (!loader->svgParse->node) return nullptr;
1194 simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
1195 return loader->svgParse->node;
1199 static constexpr struct
1202 SvgParserLengthType type;
1206 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
1207 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
1208 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
1209 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
1213 /* parse the attributes for an ellipse element.
1214 * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
1216 static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
1218 SvgLoaderData* loader = (SvgLoaderData*)data;
1219 SvgNode* node = loader->svgParse->node;
1220 SvgEllipseNode* ellipse = &(node->node.ellipse);
1221 unsigned char* array;
1222 int sz = strlen(key);
1224 array = (unsigned char*)ellipse;
1225 for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
1226 if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
1227 *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
1232 if (!strcmp(key, "id")) {
1233 node->id = _copyId(value);
1234 } else if (!strcmp(key, "style")) {
1235 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1236 } else if (!strcmp(key, "clip-path")) {
1237 _handleClipPathAttr(loader, node, value);
1238 } else if (!strcmp(key, "mask")) {
1239 _handleMaskAttr(loader, node, value);
1241 return _parseStyleAttr(loader, key, value);
1247 static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1249 loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
1251 if (!loader->svgParse->node) return nullptr;
1253 simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
1254 return loader->svgParse->node;
1258 static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
1264 float *pointArray = nullptr, *tmpArray;
1266 while (_parseNumber(&str, &num)) {
1267 tmp[tmpCount++] = num;
1268 if (tmpCount == 50) {
1269 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1270 if (!tmpArray) goto error_alloc;
1271 pointArray = tmpArray;
1272 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1279 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1280 if (!tmpArray) goto error_alloc;
1281 pointArray = tmpArray;
1282 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1286 *points = pointArray;
1290 //LOG: allocation for point array failed. out of memory
1295 /* parse the attributes for a polygon element.
1296 * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
1298 static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
1300 SvgLoaderData* loader = (SvgLoaderData*)data;
1301 SvgNode* node = loader->svgParse->node;
1302 SvgPolygonNode* polygon = nullptr;
1304 if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
1305 else polygon = &(node->node.polyline);
1307 if (!strcmp(key, "points")) {
1308 return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
1309 } else if (!strcmp(key, "style")) {
1310 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1311 } else if (!strcmp(key, "clip-path")) {
1312 _handleClipPathAttr(loader, node, value);
1313 } else if (!strcmp(key, "mask")) {
1314 _handleMaskAttr(loader, node, value);
1315 } else if (!strcmp(key, "id")) {
1316 node->id = _copyId(value);
1318 return _parseStyleAttr(loader, key, value);
1324 static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1326 loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
1328 if (!loader->svgParse->node) return nullptr;
1330 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1331 return loader->svgParse->node;
1335 static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1337 loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
1339 if (!loader->svgParse->node) return nullptr;
1341 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1342 return loader->svgParse->node;
1345 static constexpr struct
1348 SvgParserLengthType type;
1352 {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1353 {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1354 {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1355 {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1356 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
1357 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
1361 /* parse the attributes for a rect element.
1362 * https://www.w3.org/TR/SVG/shapes.html#RectElement
1364 static bool _attrParseRectNode(void* data, const char* key, const char* value)
1366 SvgLoaderData* loader = (SvgLoaderData*)data;
1367 SvgNode* node = loader->svgParse->node;
1368 SvgRectNode* rect = &(node->node.rect);
1369 unsigned char* array;
1371 int sz = strlen(key);
1373 array = (unsigned char*)rect;
1374 for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
1375 if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
1376 *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
1378 //Case if only rx or ry is declared
1379 if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
1380 if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
1382 if ((rect->rx > FLT_EPSILON) && (rect->ry <= FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
1383 if ((rect->ry > FLT_EPSILON) && (rect->rx <= FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
1388 if (!strcmp(key, "id")) {
1389 node->id = _copyId(value);
1390 } else if (!strcmp(key, "style")) {
1391 ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1392 } else if (!strcmp(key, "clip-path")) {
1393 _handleClipPathAttr(loader, node, value);
1394 } else if (!strcmp(key, "mask")) {
1395 _handleMaskAttr(loader, node, value);
1397 ret = _parseStyleAttr(loader, key, value);
1404 static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1406 loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
1408 if (!loader->svgParse->node) return nullptr;
1410 loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
1412 simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
1413 return loader->svgParse->node;
1417 static constexpr struct
1420 SvgParserLengthType type;
1424 {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
1425 {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
1426 {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
1427 {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
1431 /* parse the attributes for a rect element.
1432 * https://www.w3.org/TR/SVG/shapes.html#LineElement
1434 static bool _attrParseLineNode(void* data, const char* key, const char* value)
1436 SvgLoaderData* loader = (SvgLoaderData*)data;
1437 SvgNode* node = loader->svgParse->node;
1438 SvgLineNode* line = &(node->node.line);
1439 unsigned char* array;
1440 int sz = strlen(key);
1442 array = (unsigned char*)line;
1443 for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
1444 if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
1445 *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
1450 if (!strcmp(key, "id")) {
1451 node->id = _copyId(value);
1452 } else if (!strcmp(key, "style")) {
1453 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1454 } else if (!strcmp(key, "clip-path")) {
1455 _handleClipPathAttr(loader, node, value);
1456 } else if (!strcmp(key, "mask")) {
1457 _handleMaskAttr(loader, node, value);
1459 return _parseStyleAttr(loader, key, value);
1465 static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1467 loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
1469 if (!loader->svgParse->node) return nullptr;
1471 simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
1472 return loader->svgParse->node;
1476 static string* _idFromHref(const char* href)
1478 href = _skipSpace(href, nullptr);
1479 if ((*href) == '#') href++;
1480 return new string(href);
1484 static SvgNode* _getDefsNode(SvgNode* node)
1486 if (!node) return nullptr;
1488 while (node->parent != nullptr) {
1489 node = node->parent;
1492 if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
1498 static SvgNode* _findChildById(const SvgNode* node, const char* id)
1500 if (!node) return nullptr;
1502 auto child = node->child.data;
1503 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1504 if (((*child)->id != nullptr) && !strcmp((*child)->id->c_str(), id)) return (*child);
1509 static SvgNode* _findNodeById(SvgNode *node, string* id)
1511 SvgNode* result = nullptr;
1512 if (node->id && !node->id->compare(*id)) return node;
1514 if (node->child.count > 0) {
1515 auto child = node->child.data;
1516 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1517 result = _findNodeById(*child, id);
1524 static void _cloneGradStops(Array<Fill::ColorStop*>* dst, const Array<Fill::ColorStop*>* src)
1526 for (uint32_t i = 0; i < src->count; ++i) {
1527 auto stop = static_cast<Fill::ColorStop *>(malloc(sizeof(Fill::ColorStop)));
1528 *stop = *src->data[i];
1534 static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
1536 SvgStyleGradient* grad;
1538 if (!from) return nullptr;
1540 grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1541 if (!grad) return nullptr;
1542 grad->type = from->type;
1543 grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
1544 grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
1545 grad->spread = from->spread;
1546 grad->usePercentage = from->usePercentage;
1547 grad->userSpace = from->userSpace;
1548 if (from->transform) {
1549 grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
1550 if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
1552 if (grad->type == SvgGradientType::Linear) {
1553 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
1554 if (!grad->linear) goto error_grad_alloc;
1555 memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
1556 } else if (grad->type == SvgGradientType::Radial) {
1557 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1558 if (!grad->radial) goto error_grad_alloc;
1559 memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
1562 _cloneGradStops(&grad->stops, &from->stops);
1565 //LOG: allocation failed. out of memory
1566 if (grad->transform) free(grad->transform);
1567 if (grad->ref) delete grad->ref;
1568 if (grad->id) delete grad->id;
1569 if (grad) free(grad);
1574 static void _copyAttr(SvgNode* to, const SvgNode* from)
1576 //Copy matrix attribute
1577 if (from->transform) {
1578 to->transform = (Matrix*)calloc(1, sizeof(Matrix));
1579 if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
1581 //Copy style attribute;
1582 memcpy(to->style, from->style, sizeof(SvgStyleProperty));
1584 //Copy node attribute
1585 switch (from->type) {
1586 case SvgNodeType::Circle: {
1587 to->node.circle.cx = from->node.circle.cx;
1588 to->node.circle.cy = from->node.circle.cy;
1589 to->node.circle.r = from->node.circle.r;
1592 case SvgNodeType::Ellipse: {
1593 to->node.ellipse.cx = from->node.ellipse.cx;
1594 to->node.ellipse.cy = from->node.ellipse.cy;
1595 to->node.ellipse.rx = from->node.ellipse.rx;
1596 to->node.ellipse.ry = from->node.ellipse.ry;
1599 case SvgNodeType::Rect: {
1600 to->node.rect.x = from->node.rect.x;
1601 to->node.rect.y = from->node.rect.y;
1602 to->node.rect.w = from->node.rect.w;
1603 to->node.rect.h = from->node.rect.h;
1604 to->node.rect.rx = from->node.rect.rx;
1605 to->node.rect.ry = from->node.rect.ry;
1606 to->node.rect.hasRx = from->node.rect.hasRx;
1607 to->node.rect.hasRy = from->node.rect.hasRy;
1610 case SvgNodeType::Line: {
1611 to->node.line.x1 = from->node.line.x1;
1612 to->node.line.y1 = from->node.line.y1;
1613 to->node.line.x2 = from->node.line.x2;
1614 to->node.line.y2 = from->node.line.y2;
1617 case SvgNodeType::Path: {
1618 to->node.path.path = new string(from->node.path.path->c_str());
1621 case SvgNodeType::Polygon: {
1622 to->node.polygon.pointsCount = from->node.polygon.pointsCount;
1623 to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
1624 memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
1627 case SvgNodeType::Polyline: {
1628 to->node.polyline.pointsCount = from->node.polyline.pointsCount;
1629 to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
1630 memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
1640 static void _cloneNode(SvgNode* from, SvgNode* parent)
1643 if (!from || !parent) return;
1645 newNode = _createNode(parent, from->type);
1647 if (!newNode) return;
1649 _copyAttr(newNode, from);
1651 auto child = from->child.data;
1652 for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
1653 _cloneNode(*child, newNode);
1658 static bool _attrParseUseNode(void* data, const char* key, const char* value)
1660 SvgLoaderData* loader = (SvgLoaderData*)data;
1661 SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
1664 if (!strcmp(key, "xlink:href")) {
1665 id = _idFromHref(value);
1666 defs = _getDefsNode(node);
1667 nodeFrom = _findChildById(defs, id->c_str());
1668 _cloneNode(nodeFrom, node);
1670 } else if (!strcmp(key, "clip-path")) {
1671 _handleClipPathAttr(loader, node, value);
1672 } else if (!strcmp(key, "mask")) {
1673 _handleMaskAttr(loader, node, value);
1675 return _attrParseGNode(data, key, value);
1681 static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1683 loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
1685 if (!loader->svgParse->node) return nullptr;
1687 simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
1688 return loader->svgParse->node;
1691 //TODO: Implement 'text' primitive
1692 static constexpr struct
1696 FactoryMethod tagHandler;
1697 } graphicsTags[] = {
1698 {"use", sizeof("use"), _createUseNode},
1699 {"circle", sizeof("circle"), _createCircleNode},
1700 {"ellipse", sizeof("ellipse"), _createEllipseNode},
1701 {"path", sizeof("path"), _createPathNode},
1702 {"polygon", sizeof("polygon"), _createPolygonNode},
1703 {"rect", sizeof("rect"), _createRectNode},
1704 {"polyline", sizeof("polyline"), _createPolylineNode},
1705 {"line", sizeof("line"), _createLineNode}
1709 static constexpr struct
1713 FactoryMethod tagHandler;
1715 {"defs", sizeof("defs"), _createDefsNode},
1716 {"g", sizeof("g"), _createGNode},
1717 {"svg", sizeof("svg"), _createSvgNode},
1718 {"mask", sizeof("mask"), _createMaskNode},
1719 {"clipPath", sizeof("clipPath"), _createClipPathNode}
1723 #define FIND_FACTORY(Short_Name, Tags_Array) \
1724 static FactoryMethod \
1725 _find##Short_Name##Factory(const char* name) \
1728 int sz = strlen(name); \
1730 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
1731 if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
1732 return Tags_Array[i].tagHandler; \
1738 FIND_FACTORY(Group, groupTags)
1739 FIND_FACTORY(Graphics, graphicsTags)
1742 FillSpread _parseSpreadValue(const char* value)
1744 auto spread = FillSpread::Pad;
1746 if (!strcmp(value, "reflect")) {
1747 spread = FillSpread::Reflect;
1748 } else if (!strcmp(value, "repeat")) {
1749 spread = FillSpread::Repeat;
1756 static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1758 radial->cx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1759 if (!loader->svgParse->gradient.parsedFx) radial->fx = radial->cx;
1763 static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1765 radial->cy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1766 if (!loader->svgParse->gradient.parsedFy) radial->fy = radial->cy;
1770 static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1772 radial->fx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1773 loader->svgParse->gradient.parsedFx = true;
1777 static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1779 radial->fy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1780 loader->svgParse->gradient.parsedFy = true;
1784 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1786 radial->r = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Other);
1790 static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1792 if (!userSpace) radial->cx = radial->cx * loader->svgParse->global.w;
1796 static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1798 if (!userSpace) radial->cy = radial->cy * loader->svgParse->global.h;
1802 static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1804 if (!userSpace) radial->fx = radial->fx * loader->svgParse->global.w;
1808 static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1810 if (!userSpace) radial->fy = radial->fy * loader->svgParse->global.h;
1814 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1816 if (!userSpace) radial->r = radial->r * (sqrt(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrt(2.0));
1820 typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
1821 typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
1824 #define RADIAL_DEF(Name, Name1) \
1826 #Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
1830 static constexpr struct
1834 radialMethod tagHandler;
1835 radialMethodRecalc tagRecalc;
1845 static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
1847 SvgLoaderData* loader = (SvgLoaderData*)data;
1848 SvgStyleGradient* grad = loader->svgParse->styleGrad;
1849 SvgRadialGradient* radial = grad->radial;
1850 int sz = strlen(key);
1852 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1853 if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
1854 radialTags[i].tagHandler(loader, radial, value);
1859 if (!strcmp(key, "id")) {
1860 grad->id = _copyId(value);
1861 } else if (!strcmp(key, "spreadMethod")) {
1862 grad->spread = _parseSpreadValue(value);
1863 } else if (!strcmp(key, "xlink:href")) {
1864 grad->ref = _idFromHref(value);
1865 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
1866 grad->userSpace = true;
1875 static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
1877 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1878 if (!grad) return nullptr;
1879 loader->svgParse->styleGrad = grad;
1881 grad->type = SvgGradientType::Radial;
1882 grad->userSpace = false;
1883 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1884 if (!grad->radial) {
1889 * Default values of gradient
1891 grad->radial->cx = 0.5;
1892 grad->radial->cy = 0.5;
1893 grad->radial->fx = 0.5;
1894 grad->radial->fy = 0.5;
1895 grad->radial->r = 0.5;
1897 loader->svgParse->gradient.parsedFx = false;
1898 loader->svgParse->gradient.parsedFy = false;
1899 simpleXmlParseAttributes(buf, bufLength,
1900 _attrParseRadialGradientNode, loader);
1902 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1903 radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
1906 grad->usePercentage = true;
1908 return loader->svgParse->styleGrad;
1912 static bool _attrParseStops(void* data, const char* key, const char* value)
1914 SvgLoaderData* loader = (SvgLoaderData*)data;
1915 auto stop = loader->svgParse->gradStop;
1917 if (!strcmp(key, "offset")) {
1918 stop->offset = _toOffset(value);
1919 } else if (!strcmp(key, "stop-opacity")) {
1920 stop->a = _toOpacity(value);
1921 } else if (!strcmp(key, "stop-color")) {
1922 _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
1923 } else if (!strcmp(key, "style")) {
1924 simpleXmlParseW3CAttribute(value, _attrParseStops, data);
1933 static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1935 linear->x1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1939 static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1941 linear->y1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1945 static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1947 linear->x2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1951 static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1953 linear->y2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1957 static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1959 if (!userSpace) linear->x1 = linear->x1 * loader->svgParse->global.w;
1963 static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1965 if (!userSpace) linear->y1 = linear->y1 * loader->svgParse->global.h;
1969 static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1971 if (!userSpace) linear->x2 = linear->x2 * loader->svgParse->global.w;
1975 static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1977 if (!userSpace) linear->y2 = linear->y2 * loader->svgParse->global.h;
1981 typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
1982 typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
1985 #define LINEAR_DEF(Name, Name1) \
1987 #Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
1991 static constexpr struct
1995 Linear_Method tagHandler;
1996 Linear_Method_Recalc tagRecalc;
2005 static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
2007 SvgLoaderData* loader = (SvgLoaderData*)data;
2008 SvgStyleGradient* grad = loader->svgParse->styleGrad;
2009 SvgLinearGradient* linear = grad->linear;
2010 int sz = strlen(key);
2012 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2013 if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
2014 linear_tags[i].tagHandler(loader, linear, value);
2019 if (!strcmp(key, "id")) {
2020 grad->id = _copyId(value);
2021 } else if (!strcmp(key, "spreadMethod")) {
2022 grad->spread = _parseSpreadValue(value);
2023 } else if (!strcmp(key, "xlink:href")) {
2024 grad->ref = _idFromHref(value);
2025 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
2026 grad->userSpace = true;
2027 } else if (!strcmp(key, "gradientTransform")) {
2028 grad->transform = _parseTransformationMatrix(value);
2037 static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2039 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
2040 if (!grad) return nullptr;
2041 loader->svgParse->styleGrad = grad;
2043 grad->type = SvgGradientType::Linear;
2044 grad->userSpace = false;
2045 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
2046 if (!grad->linear) {
2051 * Default value of x2 is 100%
2053 grad->linear->x2 = 1;
2054 simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
2056 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2057 linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
2060 grad->usePercentage = true;
2062 return loader->svgParse->styleGrad;
2066 #define GRADIENT_DEF(Name, Name1) \
2068 #Name, sizeof(#Name), _create##Name1 \
2073 * For all Gradients lengths would be calculated into percentages related to
2074 * canvas width and height.
2076 * if user then recalculate actual pixels into percentages
2078 static constexpr struct
2082 GradientFactoryMethod tagHandler;
2083 } gradientTags[] = {
2084 GRADIENT_DEF(linearGradient, LinearGradient),
2085 GRADIENT_DEF(radialGradient, RadialGradient)
2089 static GradientFactoryMethod _findGradientFactory(const char* name)
2091 int sz = strlen(name);
2093 for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
2094 if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
2095 return gradientTags[i].tagHandler;
2102 static constexpr struct
2108 {"svg", sizeof("svg")},
2109 {"defs", sizeof("defs")},
2110 {"mask", sizeof("mask")},
2111 {"clipPath", sizeof("clipPath")}
2115 static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content)
2117 content = _skipSpace(content, nullptr);
2119 for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
2120 if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
2121 loader->stack.pop();
2130 static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
2132 const char* attrs = nullptr;
2133 int attrsLength = 0;
2135 char tagName[20] = "";
2136 FactoryMethod method;
2137 GradientFactoryMethod gradientMethod;
2138 SvgNode *node = nullptr, *parent = nullptr;
2140 attrs = simpleXmlFindAttributesTag(content, length);
2143 //Parse the empty tag
2145 while ((attrs != nullptr) && *attrs != '>') attrs++;
2149 //Find out the tag name starting from content till sz length
2150 sz = attrs - content;
2151 attrsLength = length - sz;
2152 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2153 if ((unsigned)sz >= sizeof(tagName)) return;
2154 strncpy(tagName, content, sz);
2158 if ((method = _findGroupFactory(tagName))) {
2161 if (strcmp(tagName, "svg")) return; //Not a valid svg document
2162 node = method(loader, nullptr, attrs, attrsLength);
2165 if (!strcmp(tagName, "svg")) return; //Already loadded <svg>(SvgNodeType::Doc) tag
2166 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2167 else parent = loader->doc;
2168 node = method(loader, parent, attrs, attrsLength);
2171 if (node->type == SvgNodeType::Defs) {
2172 loader->doc->node.doc.defs = node;
2174 if (!empty) loader->stack.push(node);
2176 loader->stack.push(node);
2178 } else if ((method = _findGraphicsFactory(tagName))) {
2179 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2180 else parent = loader->doc;
2181 node = method(loader, parent, attrs, attrsLength);
2182 } else if ((gradientMethod = _findGradientFactory(tagName))) {
2183 SvgStyleGradient* gradient;
2184 gradient = gradientMethod(loader, attrs, attrsLength);
2185 //FIXME: The current parsing structure does not distinguish end tags.
2186 // There is no way to know if the currently parsed gradient is in defs.
2187 // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
2188 // But finally, the loader has a gradient style list regardless of defs.
2189 // This is only to support this when multiple gradients are declared, even if no defs are declared.
2190 // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
2191 if (loader->def && loader->doc->node.doc.defs) {
2192 loader->def->node.defs.gradients.push(gradient);
2194 loader->gradients.push(gradient);
2196 loader->latestGradient = gradient;
2197 } else if (!strcmp(tagName, "stop")) {
2198 auto stop = static_cast<Fill::ColorStop*>(calloc(1, sizeof(Fill::ColorStop)));
2200 loader->svgParse->gradStop = stop;
2201 /* default value for opacity */
2203 simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
2204 if (loader->latestGradient) {
2205 loader->latestGradient->stops.push(stop);
2208 #ifdef THORVG_LOG_ENABLED
2210 printf("SVG: Unsupported elements used [Elements: %s]\n", tagName);
2216 static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
2218 SvgLoaderData* loader = (SvgLoaderData*)data;
2221 case SimpleXMLType::Open: {
2222 _svgLoaderParserXmlOpen(loader, content, length, false);
2225 case SimpleXMLType::OpenEmpty: {
2226 _svgLoaderParserXmlOpen(loader, content, length, true);
2229 case SimpleXMLType::Close: {
2230 _svgLoaderParerXmlClose(loader, content);
2233 case SimpleXMLType::Data:
2234 case SimpleXMLType::CData:
2235 case SimpleXMLType::DoctypeChild: {
2238 case SimpleXMLType::Ignored:
2239 case SimpleXMLType::Comment:
2240 case SimpleXMLType::Doctype: {
2252 static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
2254 if (parent == nullptr) return;
2255 //Inherit the property of parent if not present in child.
2257 if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
2258 child->fill.paint.r = parent->fill.paint.r;
2259 child->fill.paint.g = parent->fill.paint.g;
2260 child->fill.paint.b = parent->fill.paint.b;
2261 child->fill.paint.none = parent->fill.paint.none;
2262 child->fill.paint.curColor = parent->fill.paint.curColor;
2263 if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url->c_str());
2265 if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
2266 child->fill.opacity = parent->fill.opacity;
2268 if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
2269 child->fill.fillRule = parent->fill.fillRule;
2272 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
2273 child->stroke.paint.r = parent->stroke.paint.r;
2274 child->stroke.paint.g = parent->stroke.paint.g;
2275 child->stroke.paint.b = parent->stroke.paint.b;
2276 child->stroke.paint.none = parent->stroke.paint.none;
2277 child->stroke.paint.curColor = parent->stroke.paint.curColor;
2278 child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url->c_str()) : nullptr;
2280 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
2281 child->stroke.opacity = parent->stroke.opacity;
2283 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
2284 child->stroke.width = parent->stroke.width;
2286 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
2287 if (parent->stroke.dash.array.count > 0) {
2288 child->stroke.dash.array.clear();
2289 child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
2290 for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
2291 child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
2295 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
2296 child->stroke.cap = parent->stroke.cap;
2298 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
2299 child->stroke.join = parent->stroke.join;
2304 #ifdef THORVG_LOG_ENABLED
2305 static void _inefficientNodeCheck(SvgNode* node){
2306 if (!node->display && node->type != SvgNodeType::ClipPath) printf("SVG: Inefficient elements used [Display is none][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2307 if (node->style->opacity == 0) printf("SVG: Inefficient elements used [Opacity is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2308 if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) printf("SVG: Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2310 switch (node->type) {
2311 case SvgNodeType::Path: {
2312 if (!node->node.path.path || node->node.path.path->empty()) printf("SVG: Inefficient elements used [Empty path][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2315 case SvgNodeType::Ellipse: {
2316 if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) printf("SVG: Inefficient elements used [Size is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2319 case SvgNodeType::Polygon:
2320 case SvgNodeType::Polyline: {
2321 if (node->node.polygon.pointsCount < 2) printf("SVG: Inefficient elements used [Invalid Polygon][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2324 case SvgNodeType::Circle: {
2325 if (node->node.circle.r == 0) printf("SVG: Inefficient elements used [Size is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2328 case SvgNodeType::Rect: {
2329 if (node->node.rect.w == 0 && node->node.rect.h) printf("SVG: Inefficient elements used [Size is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2332 case SvgNodeType::Line: {
2333 if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) printf("SVG: Inefficient elements used [Size is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2341 static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
2343 _styleInherit(node->style, parentStyle);
2344 #ifdef THORVG_LOG_ENABLED
2345 _inefficientNodeCheck(node);
2348 auto child = node->child.data;
2349 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2350 _updateStyle(*child, node->style);
2355 static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const string* id)
2357 SvgStyleGradient* result = nullptr;
2359 auto gradList = gradients->data;
2361 for (uint32_t i = 0; i < gradients->count; ++i) {
2362 if (!((*gradList)->id->compare(*id))) {
2363 result = _cloneGradient(*gradList);
2369 if (result && result->ref) {
2370 gradList = gradients->data;
2371 for (uint32_t i = 0; i < gradients->count; ++i) {
2372 if (!((*gradList)->id->compare(*result->ref))) {
2373 if (result->stops.count == 0) {
2374 _cloneGradStops(&result->stops, &(*gradList)->stops);
2376 //TODO: Properly inherit other property
2387 static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradidents)
2389 if (node->child.count > 0) {
2390 auto child = node->child.data;
2391 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2392 _updateGradient(*child, gradidents);
2395 if (node->style->fill.paint.url) {
2396 node->style->fill.paint.gradient = _gradientDup(gradidents, node->style->fill.paint.url);
2397 } else if (node->style->stroke.paint.url) {
2398 //node->style->stroke.paint.gradient = _gradientDup(gradList, node->style->stroke.paint.url);
2403 static void _updateComposite(SvgNode* node, SvgNode* root)
2405 if (node->style->comp.url && !node->style->comp.node) {
2406 SvgNode *findResult = _findNodeById(root, node->style->comp.url);
2407 if (findResult) node->style->comp.node = findResult;
2409 if (node->child.count > 0) {
2410 auto child = node->child.data;
2411 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2412 _updateComposite(*child, root);
2417 static void _freeGradientStyle(SvgStyleGradient* grad)
2425 if (grad->transform) free(grad->transform);
2427 for (uint32_t i = 0; i < grad->stops.count; ++i) {
2428 auto colorStop = grad->stops.data[i];
2431 grad->stops.reset();
2435 static void _freeNodeStyle(SvgStyleProperty* style)
2439 //style->comp.node has only the addresses of node. Therefore, style->comp.node is released from _freeNode.
2440 delete style->comp.url;
2442 _freeGradientStyle(style->fill.paint.gradient);
2443 delete style->fill.paint.url;
2444 _freeGradientStyle(style->stroke.paint.gradient);
2445 if (style->stroke.dash.array.count > 0) style->stroke.dash.array.reset();
2446 delete style->stroke.paint.url;
2450 static void _freeNode(SvgNode* node)
2454 auto child = node->child.data;
2455 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2458 node->child.reset();
2461 free(node->transform);
2462 _freeNodeStyle(node->style);
2463 switch (node->type) {
2464 case SvgNodeType::Path: {
2465 delete node->node.path.path;
2468 case SvgNodeType::Polygon: {
2469 free(node->node.polygon.points);
2472 case SvgNodeType::Polyline: {
2473 free(node->node.polyline.points);
2476 case SvgNodeType::Doc: {
2477 _freeNode(node->node.doc.defs);
2480 case SvgNodeType::Defs: {
2481 auto gradients = node->node.defs.gradients.data;
2482 for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
2483 _freeGradientStyle(*gradients);
2486 node->node.defs.gradients.reset();
2497 static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
2499 const char* attrs = nullptr;
2501 char tagName[20] = "";
2502 FactoryMethod method;
2503 SvgNode *node = nullptr;
2504 int attrsLength = 0;
2506 attrs = simpleXmlFindAttributesTag(content, length);
2509 //Parse the empty tag
2511 while ((attrs != nullptr) && *attrs != '>') attrs++;
2515 sz = attrs - content;
2516 attrsLength = length - sz;
2517 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2518 if ((unsigned)sz >= sizeof(tagName)) return false;
2519 strncpy(tagName, content, sz);
2523 if ((method = _findGroupFactory(tagName))) {
2525 if (strcmp(tagName, "svg")) return true; //Not a valid svg document
2526 node = method(loader, nullptr, attrs, attrsLength);
2528 loader->stack.push(node);
2536 static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
2538 SvgLoaderData* loader = (SvgLoaderData*)data;
2542 case SimpleXMLType::Open:
2543 case SimpleXMLType::OpenEmpty: {
2544 //If 'res' is false, it means <svg> tag is found.
2545 res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
2557 /************************************************************************/
2558 /* External Class Implementation */
2559 /************************************************************************/
2561 SvgLoader::SvgLoader()
2566 SvgLoader::~SvgLoader()
2572 void SvgLoader::run(unsigned tid)
2574 if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
2576 if (loaderData.doc) {
2577 _updateStyle(loaderData.doc, nullptr);
2578 auto defs = loaderData.doc->node.doc.defs;
2579 if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
2581 if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
2583 _updateComposite(loaderData.doc, loaderData.doc);
2584 if (defs) _updateComposite(loaderData.doc, defs);
2586 root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh);
2590 bool SvgLoader::header()
2592 //For valid check, only <svg> tag is parsed first.
2593 //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
2594 //After that, the remaining content data is parsed in order with async.
2595 loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
2596 if (!loaderData.svgParse) return false;
2598 simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
2600 if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
2601 //Return the brief resource info such as viewbox:
2602 vx = loaderData.doc->node.doc.vx;
2603 vy = loaderData.doc->node.doc.vy;
2604 w = vw = loaderData.doc->node.doc.vw;
2605 h = vh = loaderData.doc->node.doc.vh;
2608 if (loaderData.doc->node.doc.w > 0) {
2609 w = loaderData.doc->node.doc.w;
2610 if (vw < FLT_EPSILON) vw = w;
2612 if (loaderData.doc->node.doc.h > 0) {
2613 h = loaderData.doc->node.doc.h;
2614 if (vh < FLT_EPSILON) vh = h;
2617 preserveAspect = loaderData.doc->node.doc.preserveAspect;
2619 //LOG: No SVG File. There is no <svg/>
2627 bool SvgLoader::open(const char* data, uint32_t size)
2629 this->content = data;
2636 bool SvgLoader::open(const string& path)
2643 //LOG: Failed to open file
2646 getline(f, filePath, '\0');
2649 if (filePath.empty()) return false;
2651 this->content = filePath.c_str();
2652 this->size = filePath.size();
2659 bool SvgLoader::read()
2661 if (!content || size == 0) return false;
2663 TaskScheduler::request(this);
2669 bool SvgLoader::close()
2673 if (loaderData.svgParse) {
2674 free(loaderData.svgParse);
2675 loaderData.svgParse = nullptr;
2677 auto gradients = loaderData.gradients.data;
2678 for (size_t i = 0; i < loaderData.gradients.count; ++i) {
2679 _freeGradientStyle(*gradients);
2682 loaderData.gradients.reset();
2684 _freeNode(loaderData.doc);
2685 loaderData.doc = nullptr;
2686 loaderData.stack.reset();
2692 unique_ptr<Scene> SvgLoader::scene()
2695 if (root) return move(root);
2696 else return nullptr;