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);
39 static char* _skipSpace(const char* str, const char* end)
41 while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
48 static string* _copyId(const char* str)
50 if (!str) return nullptr;
52 return new string(str);
56 static const char* _skipComma(const char* content)
58 content = _skipSpace(content, nullptr);
59 if (*content == ',') return content + 1;
64 static bool _parseNumber(const char** content, float* number)
68 *number = svgUtilStrtof(*content, &end);
69 //If the start of string is not number
70 if ((*content) == end) return false;
72 *content = _skipComma(end);
77 * According to https://www.w3.org/TR/SVG/coords.html#Units
80 * Since this documentation is not obvious, more clean recalculation with dpi
81 * is required, but for now default w3 constants would be used
83 static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
85 float parsedValue = svgUtilStrtof(str, nullptr);
87 if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
88 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
89 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
90 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
91 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
92 else if (strstr(str, "%")) {
93 if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
94 else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
95 else //if other then it's radius
97 float max = svgParse->global.w;
98 if (max < svgParse->global.h)
99 max = svgParse->global.h;
100 parsedValue = (parsedValue / 100.0) * max;
104 //TODO: Implement 'em', 'ex' attributes
110 static float _gradientToFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
114 float parsedValue = svgUtilStrtof(str, &end);
118 * That is according to Units in here
120 * https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
122 if (type == SvgParserLengthType::Vertical) max = svgParse->global.h;
123 else if (type == SvgParserLengthType::Horizontal) max = svgParse->global.w;
124 else if (type == SvgParserLengthType::Other) max = sqrt(pow(svgParse->global.h, 2) + pow(svgParse->global.w, 2)) / sqrt(2.0);
126 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
127 else if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
128 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
129 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
130 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
131 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
132 //TODO: Implement 'em', 'ex' attributes
134 //Transform into global percentage
135 parsedValue = parsedValue / max;
141 static float _toOffset(const char* str)
145 float parsedValue = svgUtilStrtof(str, &end);
147 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
153 static int _toOpacity(const char* str)
157 float opacity = svgUtilStrtof(str, &end);
159 if (end && (*end == '\0')) a = lrint(opacity * 255);
164 #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
165 static Type _to##Name1(const char* str) \
169 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
170 if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
176 /* parse the line cap used during stroking a path.
177 * Value: butt | round | square | inherit
179 * https://www.w3.org/TR/SVG/painting.html
181 static constexpr struct
186 { StrokeCap::Butt, "butt" },
187 { StrokeCap::Round, "round" },
188 { StrokeCap::Square, "square" }
192 _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
195 /* parse the line join used during stroking a path.
196 * Value: miter | round | bevel | inherit
198 * https://www.w3.org/TR/SVG/painting.html
200 static constexpr struct
205 { StrokeJoin::Miter, "miter" },
206 { StrokeJoin::Round, "round" },
207 { StrokeJoin::Bevel, "bevel" }
211 _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
214 /* parse the fill rule used during filling a path.
215 * Value: nonzero | evenodd | inherit
217 * https://www.w3.org/TR/SVG/painting.html
219 static constexpr struct
221 SvgFillRule fillRule;
224 { SvgFillRule::OddEven, "evenodd" }
228 _PARSE_TAG(SvgFillRule, fillRule, FillRule, fillRuleTags, SvgFillRule::Winding)
231 /* parse the dash pattern used during stroking a path.
232 * Value: none | <dasharray> | inherit
234 * https://www.w3.org/TR/SVG/painting.html
237 _parseDashArray(const char *str, SvgDash* dash)
242 // skip white space, comma
243 str = _skipComma(str);
244 (*dash).array.push(svgUtilStrtof(str, &end));
245 str = _skipComma(end);
247 //If dash array size is 1, it means that dash and gap size are the same.
248 if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
251 static string* _idFromUrl(const char* url)
256 url = _skipSpace(url, nullptr);
259 url = _skipSpace(url, nullptr);
262 if ((*url) == '#') ++url;
264 while ((*url) != ')') {
270 return new string(tmp);
274 static unsigned char _parserColor(const char* value, char** end)
278 r = svgUtilStrtof(value + 4, end);
279 *end = _skipSpace(*end, nullptr);
280 if (**end == '%') r = 255 * r / 100;
281 *end = _skipSpace(*end, nullptr);
283 if (r < 0 || r > 255) {
292 static constexpr struct
297 { "aliceblue", 0xfff0f8ff },
298 { "antiquewhite", 0xfffaebd7 },
299 { "aqua", 0xff00ffff },
300 { "aquamarine", 0xff7fffd4 },
301 { "azure", 0xfff0ffff },
302 { "beige", 0xfff5f5dc },
303 { "bisque", 0xffffe4c4 },
304 { "black", 0xff000000 },
305 { "blanchedalmond", 0xffffebcd },
306 { "blue", 0xff0000ff },
307 { "blueviolet", 0xff8a2be2 },
308 { "brown", 0xffa52a2a },
309 { "burlywood", 0xffdeb887 },
310 { "cadetblue", 0xff5f9ea0 },
311 { "chartreuse", 0xff7fff00 },
312 { "chocolate", 0xffd2691e },
313 { "coral", 0xffff7f50 },
314 { "cornflowerblue", 0xff6495ed },
315 { "cornsilk", 0xfffff8dc },
316 { "crimson", 0xffdc143c },
317 { "cyan", 0xff00ffff },
318 { "darkblue", 0xff00008b },
319 { "darkcyan", 0xff008b8b },
320 { "darkgoldenrod", 0xffb8860b },
321 { "darkgray", 0xffa9a9a9 },
322 { "darkgrey", 0xffa9a9a9 },
323 { "darkgreen", 0xff006400 },
324 { "darkkhaki", 0xffbdb76b },
325 { "darkmagenta", 0xff8b008b },
326 { "darkolivegreen", 0xff556b2f },
327 { "darkorange", 0xffff8c00 },
328 { "darkorchid", 0xff9932cc },
329 { "darkred", 0xff8b0000 },
330 { "darksalmon", 0xffe9967a },
331 { "darkseagreen", 0xff8fbc8f },
332 { "darkslateblue", 0xff483d8b },
333 { "darkslategray", 0xff2f4f4f },
334 { "darkslategrey", 0xff2f4f4f },
335 { "darkturquoise", 0xff00ced1 },
336 { "darkviolet", 0xff9400d3 },
337 { "deeppink", 0xffff1493 },
338 { "deepskyblue", 0xff00bfff },
339 { "dimgray", 0xff696969 },
340 { "dimgrey", 0xff696969 },
341 { "dodgerblue", 0xff1e90ff },
342 { "firebrick", 0xffb22222 },
343 { "floralwhite", 0xfffffaf0 },
344 { "forestgreen", 0xff228b22 },
345 { "fuchsia", 0xffff00ff },
346 { "gainsboro", 0xffdcdcdc },
347 { "ghostwhite", 0xfff8f8ff },
348 { "gold", 0xffffd700 },
349 { "goldenrod", 0xffdaa520 },
350 { "gray", 0xff808080 },
351 { "grey", 0xff808080 },
352 { "green", 0xff008000 },
353 { "greenyellow", 0xffadff2f },
354 { "honeydew", 0xfff0fff0 },
355 { "hotpink", 0xffff69b4 },
356 { "indianred", 0xffcd5c5c },
357 { "indigo", 0xff4b0082 },
358 { "ivory", 0xfffffff0 },
359 { "khaki", 0xfff0e68c },
360 { "lavender", 0xffe6e6fa },
361 { "lavenderblush", 0xfffff0f5 },
362 { "lawngreen", 0xff7cfc00 },
363 { "lemonchiffon", 0xfffffacd },
364 { "lightblue", 0xffadd8e6 },
365 { "lightcoral", 0xfff08080 },
366 { "lightcyan", 0xffe0ffff },
367 { "lightgoldenrodyellow", 0xfffafad2 },
368 { "lightgray", 0xffd3d3d3 },
369 { "lightgrey", 0xffd3d3d3 },
370 { "lightgreen", 0xff90ee90 },
371 { "lightpink", 0xffffb6c1 },
372 { "lightsalmon", 0xffffa07a },
373 { "lightseagreen", 0xff20b2aa },
374 { "lightskyblue", 0xff87cefa },
375 { "lightslategray", 0xff778899 },
376 { "lightslategrey", 0xff778899 },
377 { "lightsteelblue", 0xffb0c4de },
378 { "lightyellow", 0xffffffe0 },
379 { "lime", 0xff00ff00 },
380 { "limegreen", 0xff32cd32 },
381 { "linen", 0xfffaf0e6 },
382 { "magenta", 0xffff00ff },
383 { "maroon", 0xff800000 },
384 { "mediumaquamarine", 0xff66cdaa },
385 { "mediumblue", 0xff0000cd },
386 { "mediumorchid", 0xffba55d3 },
387 { "mediumpurple", 0xff9370d8 },
388 { "mediumseagreen", 0xff3cb371 },
389 { "mediumslateblue", 0xff7b68ee },
390 { "mediumspringgreen", 0xff00fa9a },
391 { "mediumturquoise", 0xff48d1cc },
392 { "mediumvioletred", 0xffc71585 },
393 { "midnightblue", 0xff191970 },
394 { "mintcream", 0xfff5fffa },
395 { "mistyrose", 0xffffe4e1 },
396 { "moccasin", 0xffffe4b5 },
397 { "navajowhite", 0xffffdead },
398 { "navy", 0xff000080 },
399 { "oldlace", 0xfffdf5e6 },
400 { "olive", 0xff808000 },
401 { "olivedrab", 0xff6b8e23 },
402 { "orange", 0xffffa500 },
403 { "orangered", 0xffff4500 },
404 { "orchid", 0xffda70d6 },
405 { "palegoldenrod", 0xffeee8aa },
406 { "palegreen", 0xff98fb98 },
407 { "paleturquoise", 0xffafeeee },
408 { "palevioletred", 0xffd87093 },
409 { "papayawhip", 0xffffefd5 },
410 { "peachpuff", 0xffffdab9 },
411 { "peru", 0xffcd853f },
412 { "pink", 0xffffc0cb },
413 { "plum", 0xffdda0dd },
414 { "powderblue", 0xffb0e0e6 },
415 { "purple", 0xff800080 },
416 { "red", 0xffff0000 },
417 { "rosybrown", 0xffbc8f8f },
418 { "royalblue", 0xff4169e1 },
419 { "saddlebrown", 0xff8b4513 },
420 { "salmon", 0xfffa8072 },
421 { "sandybrown", 0xfff4a460 },
422 { "seagreen", 0xff2e8b57 },
423 { "seashell", 0xfffff5ee },
424 { "sienna", 0xffa0522d },
425 { "silver", 0xffc0c0c0 },
426 { "skyblue", 0xff87ceeb },
427 { "slateblue", 0xff6a5acd },
428 { "slategray", 0xff708090 },
429 { "slategrey", 0xff708090 },
430 { "snow", 0xfffffafa },
431 { "springgreen", 0xff00ff7f },
432 { "steelblue", 0xff4682b4 },
433 { "tan", 0xffd2b48c },
434 { "teal", 0xff008080 },
435 { "thistle", 0xffd8bfd8 },
436 { "tomato", 0xffff6347 },
437 { "turquoise", 0xff40e0d0 },
438 { "violet", 0xffee82ee },
439 { "wheat", 0xfff5deb3 },
440 { "white", 0xffffffff },
441 { "whitesmoke", 0xfff5f5f5 },
442 { "yellow", 0xffffff00 },
443 { "yellowgreen", 0xff9acd32 }
447 static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, string** ref)
449 unsigned int len = strlen(str);
450 char *red, *green, *blue;
451 unsigned char tr, tg, tb;
453 if (len == 4 && str[0] == '#') {
454 //Case for "#456" should be interprete as "#445566"
455 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
456 char tmp[3] = { '\0', '\0', '\0' };
459 *r = strtol(tmp, nullptr, 16);
462 *g = strtol(tmp, nullptr, 16);
465 *b = strtol(tmp, nullptr, 16);
467 } else if (len == 7 && str[0] == '#') {
468 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
469 char tmp[3] = { '\0', '\0', '\0' };
472 *r = strtol(tmp, nullptr, 16);
475 *g = strtol(tmp, nullptr, 16);
478 *b = strtol(tmp, nullptr, 16);
480 } 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] == ')') {
481 tr = _parserColor(str + 4, &red);
482 if (red && *red == ',') {
483 tg = _parserColor(red + 1, &green);
484 if (green && *green == ',') {
485 tb = _parserColor(green + 1, &blue);
486 if (blue && blue[0] == ')' && blue[1] == '\0') {
493 } else if (len >= 3 && !strncmp(str, "url", 3)) {
494 *ref = _idFromUrl((const char*)(str + 3));
497 for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
498 if (!strcasecmp(colors[i].name, str)) {
499 *r = (((uint8_t*)(&(colors[i].value)))[2]);
500 *g = (((uint8_t*)(&(colors[i].value)))[1]);
501 *b = (((uint8_t*)(&(colors[i].value)))[0]);
508 static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
513 str = _skipSpace(str, nullptr);
514 while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
515 points[count++] = svgUtilStrtof(str, &end);
517 str = _skipSpace(str, nullptr);
518 if (*str == ',') ++str;
519 //Eat the rest of space
520 str = _skipSpace(str, nullptr);
527 enum class MatrixState {
538 #define MATRIX_DEF(Name, Value) \
540 #Name, sizeof(#Name), Value \
544 static constexpr struct
550 MATRIX_DEF(matrix, MatrixState::Matrix),
551 MATRIX_DEF(translate, MatrixState::Translate),
552 MATRIX_DEF(rotate, MatrixState::Rotate),
553 MATRIX_DEF(scale, MatrixState::Scale),
554 MATRIX_DEF(skewX, MatrixState::SkewX),
555 MATRIX_DEF(skewY, MatrixState::SkewY)
559 static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
561 auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
562 auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
563 auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
565 auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
566 auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
567 auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
569 auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
570 auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
571 auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
585 /* parse transform attribute
586 * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
588 static Matrix* _parseTransformationMatrix(const char* value)
590 const int POINT_CNT = 8;
592 auto matrix = (Matrix*)malloc(sizeof(Matrix));
593 if (!matrix) return nullptr;
594 *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
596 float points[POINT_CNT];
598 char* str = (char*)value;
599 char* end = str + strlen(str);
602 auto state = MatrixState::Unknown;
604 if (isspace(*str) || (*str == ',')) {
608 for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
609 if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
610 state = matrixTags[i].state;
611 str += (matrixTags[i].sz - 1);
615 if (state == MatrixState::Unknown) goto error;
617 str = _skipSpace(str, end);
618 if (*str != '(') goto error;
620 str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
621 if (*str != ')') goto error;
624 if (state == MatrixState::Matrix) {
625 if (ptCount != 6) goto error;
626 Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
627 _matrixCompose(matrix, &tmp, matrix);
628 } else if (state == MatrixState::Translate) {
630 Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
631 _matrixCompose(matrix, &tmp, matrix);
632 } else if (ptCount == 2) {
633 Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
634 _matrixCompose(matrix, &tmp, matrix);
636 } else if (state == MatrixState::Rotate) {
637 //Transform to signed.
638 points[0] = fmod(points[0], 360);
639 if (points[0] < 0) points[0] += 360;
640 auto c = cosf(points[0] * (M_PI / 180.0));
641 auto s = sinf(points[0] * (M_PI / 180.0));
643 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
644 _matrixCompose(matrix, &tmp, matrix);
645 } else if (ptCount == 3) {
646 Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
647 _matrixCompose(matrix, &tmp, matrix);
648 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
649 _matrixCompose(matrix, &tmp, matrix);
650 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
651 _matrixCompose(matrix, &tmp, matrix);
655 } else if (state == MatrixState::Scale) {
656 if (ptCount < 1 || ptCount > 2) goto error;
659 if (ptCount == 2) sy = points[1];
660 Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
661 _matrixCompose(matrix, &tmp, matrix);
666 if (matrix) free(matrix);
671 #define LENGTH_DEF(Name, Value) \
673 #Name, sizeof(#Name), Value \
677 static constexpr struct
683 LENGTH_DEF(%, SvgLengthType::Percent),
684 LENGTH_DEF(px, SvgLengthType::Px),
685 LENGTH_DEF(pc, SvgLengthType::Pc),
686 LENGTH_DEF(pt, SvgLengthType::Pt),
687 LENGTH_DEF(mm, SvgLengthType::Mm),
688 LENGTH_DEF(cm, SvgLengthType::Cm),
689 LENGTH_DEF(in, SvgLengthType::In)
693 static float _parseLength(const char* str, SvgLengthType* type)
696 int sz = strlen(str);
698 *type = SvgLengthType::Px;
699 for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
700 if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
702 value = svgUtilStrtof(str, nullptr);
707 static bool _parseStyleAttr(void* data, const char* key, const char* value);
710 static bool _attrParseSvgNode(void* data, const char* key, const char* value)
712 SvgLoaderData* loader = (SvgLoaderData*)data;
713 SvgNode* node = loader->svgParse->node;
714 SvgDocNode* doc = &(node->node.doc);
717 //TODO: handle length unit.
718 if (!strcmp(key, "width")) {
719 doc->w = _parseLength(value, &type);
720 } else if (!strcmp(key, "height")) {
721 doc->h = _parseLength(value, &type);
722 } else if (!strcmp(key, "viewBox")) {
723 if (_parseNumber(&value, &doc->vx)) {
724 if (_parseNumber(&value, &doc->vy)) {
725 if (_parseNumber(&value, &doc->vw)) {
726 _parseNumber(&value, &doc->vh);
727 loader->svgParse->global.h = doc->vh;
729 loader->svgParse->global.w = doc->vw;
731 loader->svgParse->global.y = doc->vy;
733 loader->svgParse->global.x = doc->vx;
734 } else if (!strcmp(key, "preserveAspectRatio")) {
735 if (!strcmp(value, "none")) doc->preserveAspect = false;
736 } else if (!strcmp(key, "style")) {
737 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
739 #ifdef THORVG_LOG_ENABLED
740 else if (!strcmp(key, "x") || !strcmp(key, "y")) {
741 if (0.0f == _parseLength(value, &type)) printf("SVG: Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]\n", key, value);
745 return _parseStyleAttr(loader, key, value);
751 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
752 static void _handlePaintAttr(SvgPaint* paint, const char* value)
754 if (!strcmp(value, "none")) {
760 if (!strcmp(value, "currentColor")) {
761 paint->curColor = true;
764 _toColor(value, &paint->r, &paint->g, &paint->b, &paint->url);
768 static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
770 SvgStyleProperty* style = node->style;
771 _toColor(value, &style->r, &style->g, &style->b, nullptr);
775 static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
777 SvgStyleProperty* style = node->style;
778 style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
779 _handlePaintAttr(&style->fill.paint, value);
783 static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
785 SvgStyleProperty* style = node->style;
786 style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
787 _handlePaintAttr(&style->stroke.paint, value);
791 static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
793 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
794 node->style->stroke.opacity = _toOpacity(value);
797 static void _handleStrokeDashArrayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
799 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
800 _parseDashArray(value, &node->style->stroke.dash);
803 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
805 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
806 node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
810 static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
812 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
813 node->style->stroke.cap = _toLineCap(value);
817 static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
819 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
820 node->style->stroke.join = _toLineJoin(value);
824 static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
826 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
827 node->style->fill.fillRule = _toFillRule(value);
831 static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
833 node->style->opacity = _toOpacity(value);
837 static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
839 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
840 node->style->fill.opacity = _toOpacity(value);
844 static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
846 node->transform = _parseTransformationMatrix(value);
850 static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
852 SvgStyleProperty* style = node->style;
853 #ifdef THORVG_LOG_ENABLED
854 if (style->comp.method != CompositeMethod::None) printf("SVG: Multiple Composition Tried!\n");
856 style->comp.method = CompositeMethod::ClipPath;
857 int len = strlen(value);
858 if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
862 static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
864 SvgStyleProperty* style = node->style;
865 #ifdef THORVG_LOG_ENABLED
866 if (style->comp.method != CompositeMethod::None) printf("SVG: Multiple Composition Tried!\n");
868 style->comp.method = CompositeMethod::AlphaMask;
869 int len = strlen(value);
870 if (len >= 3 && !strncmp(value, "url", 3)) style->comp.url = _idFromUrl((const char*)(value + 3));
874 static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
876 //TODO : The display attribute can have various values as well as "none".
877 // The default is "inline" which means visible and "none" means invisible.
878 // Depending on the type of node, additional functionality may be required.
879 // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
880 if (!strcmp(value, "none")) node->display = false;
881 else node->display = true;
885 typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
887 #define STYLE_DEF(Name, Name1) { #Name, sizeof(#Name), _handle##Name1##Attr }
889 static constexpr struct
893 styleMethod tagHandler;
895 STYLE_DEF(color, Color),
896 STYLE_DEF(fill, Fill),
897 STYLE_DEF(fill-rule, FillRule),
898 STYLE_DEF(fill-opacity, FillOpacity),
899 STYLE_DEF(opacity, Opacity),
900 STYLE_DEF(stroke, Stroke),
901 STYLE_DEF(stroke-width, StrokeWidth),
902 STYLE_DEF(stroke-linejoin, StrokeLineJoin),
903 STYLE_DEF(stroke-linecap, StrokeLineCap),
904 STYLE_DEF(stroke-opacity, StrokeOpacity),
905 STYLE_DEF(stroke-dasharray, StrokeDashArray),
906 STYLE_DEF(transform, Transform),
907 STYLE_DEF(clip-path, ClipPath),
908 STYLE_DEF(mask, Mask),
909 STYLE_DEF(display, Display)
913 static bool _parseStyleAttr(void* data, const char* key, const char* value)
915 SvgLoaderData* loader = (SvgLoaderData*)data;
916 SvgNode* node = loader->svgParse->node;
918 if (!key || !value) return false;
920 //Trim the white space
921 key = _skipSpace(key, nullptr);
923 value = _skipSpace(value, nullptr);
926 for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
927 if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
928 styleTags[i].tagHandler(loader, node, value);
937 * https://www.w3.org/TR/SVG/struct.html#Groups
939 static bool _attrParseGNode(void* data, const char* key, const char* value)
941 SvgLoaderData* loader = (SvgLoaderData*)data;
942 SvgNode* node = loader->svgParse->node;
944 if (!strcmp(key, "style")) {
945 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
946 } else if (!strcmp(key, "transform")) {
947 node->transform = _parseTransformationMatrix(value);
948 } else if (!strcmp(key, "id")) {
949 node->id = _copyId(value);
950 } else if (!strcmp(key, "clip-path")) {
951 _handleClipPathAttr(loader, node, value);
952 } else if (!strcmp(key, "mask")) {
953 _handleMaskAttr(loader, node, value);
955 return _parseStyleAttr(loader, key, value);
961 /* parse clipPath node
962 * https://www.w3.org/TR/SVG/struct.html#Groups
964 static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
966 SvgLoaderData* loader = (SvgLoaderData*)data;
967 SvgNode* node = loader->svgParse->node;
969 if (!strcmp(key, "style")) {
970 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
971 } else if (!strcmp(key, "transform")) {
972 node->transform = _parseTransformationMatrix(value);
973 } else if (!strcmp(key, "id")) {
974 node->id = _copyId(value);
976 return _parseStyleAttr(loader, key, value);
982 static bool _attrParseMaskNode(void* data, const char* key, const char* value)
984 SvgLoaderData* loader = (SvgLoaderData*)data;
985 SvgNode* node = loader->svgParse->node;
987 if (!strcmp(key, "style")) {
988 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
989 } else if (!strcmp(key, "transform")) {
990 node->transform = _parseTransformationMatrix(value);
991 } else if (!strcmp(key, "id")) {
992 node->id = _copyId(value);
994 return _parseStyleAttr(loader, key, value);
1000 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
1002 SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
1004 if (!node) return nullptr;
1006 //Default fill property
1007 node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
1014 //Update the default value of stroke and fill
1015 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
1016 node->style->fill.paint.none = false;
1017 //Default fill opacity is 1
1018 node->style->fill.opacity = 255;
1019 node->style->opacity = 255;
1021 //Default fill rule is nonzero
1022 node->style->fill.fillRule = SvgFillRule::Winding;
1024 //Default stroke is none
1025 node->style->stroke.paint.none = true;
1026 //Default stroke opacity is 1
1027 node->style->stroke.opacity = 255;
1028 //Default stroke width is 1
1029 node->style->stroke.width = 1;
1030 //Default line cap is butt
1031 node->style->stroke.cap = StrokeCap::Butt;
1032 //Default line join is miter
1033 node->style->stroke.join = StrokeJoin::Miter;
1034 node->style->stroke.scale = 1.0;
1036 //Default display is true("inline").
1037 node->display = true;
1039 node->parent = parent;
1042 if (parent) parent->child.push(node);
1047 static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength)
1049 SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
1050 if (!node) return nullptr;
1051 simpleXmlParseAttributes(buf, bufLength, nullptr, node);
1056 static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1058 loader->svgParse->node = _createNode(parent, SvgNodeType::G);
1059 if (!loader->svgParse->node) return nullptr;
1061 simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
1062 return loader->svgParse->node;
1066 static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1068 loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
1069 if (!loader->svgParse->node) return nullptr;
1070 SvgDocNode* doc = &(loader->svgParse->node->node.doc);
1072 doc->preserveAspect = true;
1073 simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
1075 return loader->svgParse->node;
1079 static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength)
1081 loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
1082 if (!loader->svgParse->node) return nullptr;
1084 simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader);
1086 return loader->svgParse->node;
1090 static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1092 loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
1094 if (!loader->svgParse->node) return nullptr;
1096 loader->svgParse->node->display = false;
1098 simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
1100 return loader->svgParse->node;
1103 static bool _attrParsePathNode(void* data, const char* key, const char* value)
1105 SvgLoaderData* loader = (SvgLoaderData*)data;
1106 SvgNode* node = loader->svgParse->node;
1107 SvgPathNode* path = &(node->node.path);
1109 if (!strcmp(key, "d")) {
1110 //Temporary: need to copy
1111 path->path = _copyId(value);
1112 } else if (!strcmp(key, "style")) {
1113 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1114 } else if (!strcmp(key, "clip-path")) {
1115 _handleClipPathAttr(loader, node, value);
1116 } else if (!strcmp(key, "mask")) {
1117 _handleMaskAttr(loader, node, value);
1118 } else if (!strcmp(key, "id")) {
1119 node->id = _copyId(value);
1121 return _parseStyleAttr(loader, key, value);
1127 static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1129 loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
1131 if (!loader->svgParse->node) return nullptr;
1133 simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
1135 return loader->svgParse->node;
1139 static constexpr struct
1142 SvgParserLengthType type;
1146 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
1147 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
1148 {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
1152 /* parse the attributes for a circle element.
1153 * https://www.w3.org/TR/SVG/shapes.html#CircleElement
1155 static bool _attrParseCircleNode(void* data, const char* key, const char* value)
1157 SvgLoaderData* loader = (SvgLoaderData*)data;
1158 SvgNode* node = loader->svgParse->node;
1159 SvgCircleNode* circle = &(node->node.circle);
1160 unsigned char* array;
1161 int sz = strlen(key);
1163 array = (unsigned char*)circle;
1164 for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
1165 if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
1166 *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
1171 if (!strcmp(key, "style")) {
1172 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1173 } else if (!strcmp(key, "clip-path")) {
1174 _handleClipPathAttr(loader, node, value);
1175 } else if (!strcmp(key, "mask")) {
1176 _handleMaskAttr(loader, node, value);
1177 } else if (!strcmp(key, "id")) {
1178 node->id = _copyId(value);
1180 return _parseStyleAttr(loader, key, value);
1186 static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1188 loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
1190 if (!loader->svgParse->node) return nullptr;
1192 simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
1193 return loader->svgParse->node;
1197 static constexpr struct
1200 SvgParserLengthType type;
1204 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
1205 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
1206 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
1207 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
1211 /* parse the attributes for an ellipse element.
1212 * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
1214 static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
1216 SvgLoaderData* loader = (SvgLoaderData*)data;
1217 SvgNode* node = loader->svgParse->node;
1218 SvgEllipseNode* ellipse = &(node->node.ellipse);
1219 unsigned char* array;
1220 int sz = strlen(key);
1222 array = (unsigned char*)ellipse;
1223 for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
1224 if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
1225 *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
1230 if (!strcmp(key, "id")) {
1231 node->id = _copyId(value);
1232 } else if (!strcmp(key, "style")) {
1233 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1234 } else if (!strcmp(key, "clip-path")) {
1235 _handleClipPathAttr(loader, node, value);
1236 } else if (!strcmp(key, "mask")) {
1237 _handleMaskAttr(loader, node, value);
1239 return _parseStyleAttr(loader, key, value);
1245 static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1247 loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
1249 if (!loader->svgParse->node) return nullptr;
1251 simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
1252 return loader->svgParse->node;
1256 static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
1262 float *pointArray = nullptr, *tmpArray;
1264 while (_parseNumber(&str, &num)) {
1265 tmp[tmpCount++] = num;
1266 if (tmpCount == 50) {
1267 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1268 if (!tmpArray) goto error_alloc;
1269 pointArray = tmpArray;
1270 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1277 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1278 if (!tmpArray) goto error_alloc;
1279 pointArray = tmpArray;
1280 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1284 *points = pointArray;
1288 //LOG: allocation for point array failed. out of memory
1293 /* parse the attributes for a polygon element.
1294 * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
1296 static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
1298 SvgLoaderData* loader = (SvgLoaderData*)data;
1299 SvgNode* node = loader->svgParse->node;
1300 SvgPolygonNode* polygon = nullptr;
1302 if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
1303 else polygon = &(node->node.polyline);
1305 if (!strcmp(key, "points")) {
1306 return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
1307 } else if (!strcmp(key, "style")) {
1308 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1309 } else if (!strcmp(key, "clip-path")) {
1310 _handleClipPathAttr(loader, node, value);
1311 } else if (!strcmp(key, "mask")) {
1312 _handleMaskAttr(loader, node, value);
1313 } else if (!strcmp(key, "id")) {
1314 node->id = _copyId(value);
1316 return _parseStyleAttr(loader, key, value);
1322 static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1324 loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
1326 if (!loader->svgParse->node) return nullptr;
1328 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1329 return loader->svgParse->node;
1333 static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1335 loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
1337 if (!loader->svgParse->node) return nullptr;
1339 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1340 return loader->svgParse->node;
1343 static constexpr struct
1346 SvgParserLengthType type;
1350 {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1351 {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1352 {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1353 {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1354 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
1355 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
1359 /* parse the attributes for a rect element.
1360 * https://www.w3.org/TR/SVG/shapes.html#RectElement
1362 static bool _attrParseRectNode(void* data, const char* key, const char* value)
1364 SvgLoaderData* loader = (SvgLoaderData*)data;
1365 SvgNode* node = loader->svgParse->node;
1366 SvgRectNode* rect = &(node->node.rect);
1367 unsigned char* array;
1369 int sz = strlen(key);
1371 array = (unsigned char*)rect;
1372 for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
1373 if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
1374 *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
1376 //Case if only rx or ry is declared
1377 if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
1378 if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
1380 if ((rect->rx > FLT_EPSILON) && (rect->ry <= FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
1381 if ((rect->ry > FLT_EPSILON) && (rect->rx <= FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
1386 if (!strcmp(key, "id")) {
1387 node->id = _copyId(value);
1388 } else if (!strcmp(key, "style")) {
1389 ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1390 } else if (!strcmp(key, "clip-path")) {
1391 _handleClipPathAttr(loader, node, value);
1392 } else if (!strcmp(key, "mask")) {
1393 _handleMaskAttr(loader, node, value);
1395 ret = _parseStyleAttr(loader, key, value);
1402 static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1404 loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
1406 if (!loader->svgParse->node) return nullptr;
1408 loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
1410 simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
1411 return loader->svgParse->node;
1415 static constexpr struct
1418 SvgParserLengthType type;
1422 {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
1423 {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
1424 {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
1425 {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
1429 /* parse the attributes for a rect element.
1430 * https://www.w3.org/TR/SVG/shapes.html#LineElement
1432 static bool _attrParseLineNode(void* data, const char* key, const char* value)
1434 SvgLoaderData* loader = (SvgLoaderData*)data;
1435 SvgNode* node = loader->svgParse->node;
1436 SvgLineNode* line = &(node->node.line);
1437 unsigned char* array;
1438 int sz = strlen(key);
1440 array = (unsigned char*)line;
1441 for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
1442 if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
1443 *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
1448 if (!strcmp(key, "id")) {
1449 node->id = _copyId(value);
1450 } else if (!strcmp(key, "style")) {
1451 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1452 } else if (!strcmp(key, "clip-path")) {
1453 _handleClipPathAttr(loader, node, value);
1454 } else if (!strcmp(key, "mask")) {
1455 _handleMaskAttr(loader, node, value);
1457 return _parseStyleAttr(loader, key, value);
1463 static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1465 loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
1467 if (!loader->svgParse->node) return nullptr;
1469 simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
1470 return loader->svgParse->node;
1474 static string* _idFromHref(const char* href)
1476 href = _skipSpace(href, nullptr);
1477 if ((*href) == '#') href++;
1478 return new string(href);
1482 static SvgNode* _getDefsNode(SvgNode* node)
1484 if (!node) return nullptr;
1486 while (node->parent != nullptr) {
1487 node = node->parent;
1490 if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
1496 static SvgNode* _findChildById(const SvgNode* node, const char* id)
1498 if (!node) return nullptr;
1500 auto child = node->child.data;
1501 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1502 if (((*child)->id != nullptr) && !strcmp((*child)->id->c_str(), id)) return (*child);
1507 static SvgNode* _findNodeById(SvgNode *node, string* id)
1509 SvgNode* result = nullptr;
1510 if (node->id && !node->id->compare(*id)) return node;
1512 if (node->child.count > 0) {
1513 auto child = node->child.data;
1514 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1515 result = _findNodeById(*child, id);
1522 static void _cloneGradStops(Array<Fill::ColorStop*>* dst, const Array<Fill::ColorStop*>* src)
1524 for (uint32_t i = 0; i < src->count; ++i) {
1525 auto stop = static_cast<Fill::ColorStop *>(malloc(sizeof(Fill::ColorStop)));
1526 *stop = *src->data[i];
1532 static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
1534 SvgStyleGradient* grad;
1536 if (!from) return nullptr;
1538 grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1539 if (!grad) return nullptr;
1540 grad->type = from->type;
1541 grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
1542 grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
1543 grad->spread = from->spread;
1544 grad->usePercentage = from->usePercentage;
1545 grad->userSpace = from->userSpace;
1546 if (from->transform) {
1547 grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
1548 if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
1550 if (grad->type == SvgGradientType::Linear) {
1551 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
1552 if (!grad->linear) goto error_grad_alloc;
1553 memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
1554 } else if (grad->type == SvgGradientType::Radial) {
1555 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1556 if (!grad->radial) goto error_grad_alloc;
1557 memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
1560 _cloneGradStops(&grad->stops, &from->stops);
1563 //LOG: allocation failed. out of memory
1564 if (grad->transform) free(grad->transform);
1565 if (grad->ref) delete grad->ref;
1566 if (grad->id) delete grad->id;
1567 if (grad) free(grad);
1572 static void _copyAttr(SvgNode* to, const SvgNode* from)
1574 //Copy matrix attribute
1575 if (from->transform) {
1576 to->transform = (Matrix*)calloc(1, sizeof(Matrix));
1577 if (to->transform) memcpy(to->transform, from->transform, sizeof(Matrix));
1579 //Copy style attribute;
1580 memcpy(to->style, from->style, sizeof(SvgStyleProperty));
1582 //Copy node attribute
1583 switch (from->type) {
1584 case SvgNodeType::Circle: {
1585 to->node.circle.cx = from->node.circle.cx;
1586 to->node.circle.cy = from->node.circle.cy;
1587 to->node.circle.r = from->node.circle.r;
1590 case SvgNodeType::Ellipse: {
1591 to->node.ellipse.cx = from->node.ellipse.cx;
1592 to->node.ellipse.cy = from->node.ellipse.cy;
1593 to->node.ellipse.rx = from->node.ellipse.rx;
1594 to->node.ellipse.ry = from->node.ellipse.ry;
1597 case SvgNodeType::Rect: {
1598 to->node.rect.x = from->node.rect.x;
1599 to->node.rect.y = from->node.rect.y;
1600 to->node.rect.w = from->node.rect.w;
1601 to->node.rect.h = from->node.rect.h;
1602 to->node.rect.rx = from->node.rect.rx;
1603 to->node.rect.ry = from->node.rect.ry;
1604 to->node.rect.hasRx = from->node.rect.hasRx;
1605 to->node.rect.hasRy = from->node.rect.hasRy;
1608 case SvgNodeType::Line: {
1609 to->node.line.x1 = from->node.line.x1;
1610 to->node.line.y1 = from->node.line.y1;
1611 to->node.line.x2 = from->node.line.x2;
1612 to->node.line.y2 = from->node.line.y2;
1615 case SvgNodeType::Path: {
1616 to->node.path.path = new string(from->node.path.path->c_str());
1619 case SvgNodeType::Polygon: {
1620 to->node.polygon.pointsCount = from->node.polygon.pointsCount;
1621 to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
1622 memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
1625 case SvgNodeType::Polyline: {
1626 to->node.polyline.pointsCount = from->node.polyline.pointsCount;
1627 to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
1628 memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
1638 static void _cloneNode(SvgNode* from, SvgNode* parent)
1641 if (!from || !parent) return;
1643 newNode = _createNode(parent, from->type);
1645 if (!newNode) return;
1647 _copyAttr(newNode, from);
1649 auto child = from->child.data;
1650 for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
1651 _cloneNode(*child, newNode);
1656 static bool _attrParseUseNode(void* data, const char* key, const char* value)
1658 SvgLoaderData* loader = (SvgLoaderData*)data;
1659 SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
1662 if (!strcmp(key, "xlink:href")) {
1663 id = _idFromHref(value);
1664 defs = _getDefsNode(node);
1665 nodeFrom = _findChildById(defs, id->c_str());
1666 _cloneNode(nodeFrom, node);
1668 } else if (!strcmp(key, "clip-path")) {
1669 _handleClipPathAttr(loader, node, value);
1670 } else if (!strcmp(key, "mask")) {
1671 _handleMaskAttr(loader, node, value);
1673 return _attrParseGNode(data, key, value);
1679 static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1681 loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
1683 if (!loader->svgParse->node) return nullptr;
1685 simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
1686 return loader->svgParse->node;
1689 //TODO: Implement 'text' primitive
1690 static constexpr struct
1694 FactoryMethod tagHandler;
1695 } graphicsTags[] = {
1696 {"use", sizeof("use"), _createUseNode},
1697 {"circle", sizeof("circle"), _createCircleNode},
1698 {"ellipse", sizeof("ellipse"), _createEllipseNode},
1699 {"path", sizeof("path"), _createPathNode},
1700 {"polygon", sizeof("polygon"), _createPolygonNode},
1701 {"rect", sizeof("rect"), _createRectNode},
1702 {"polyline", sizeof("polyline"), _createPolylineNode},
1703 {"line", sizeof("line"), _createLineNode}
1707 static constexpr struct
1711 FactoryMethod tagHandler;
1713 {"defs", sizeof("defs"), _createDefsNode},
1714 {"g", sizeof("g"), _createGNode},
1715 {"svg", sizeof("svg"), _createSvgNode},
1716 {"mask", sizeof("mask"), _createMaskNode},
1717 {"clipPath", sizeof("clipPath"), _createClipPathNode}
1721 #define FIND_FACTORY(Short_Name, Tags_Array) \
1722 static FactoryMethod \
1723 _find##Short_Name##Factory(const char* name) \
1726 int sz = strlen(name); \
1728 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
1729 if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
1730 return Tags_Array[i].tagHandler; \
1736 FIND_FACTORY(Group, groupTags)
1737 FIND_FACTORY(Graphics, graphicsTags)
1740 FillSpread _parseSpreadValue(const char* value)
1742 auto spread = FillSpread::Pad;
1744 if (!strcmp(value, "reflect")) {
1745 spread = FillSpread::Reflect;
1746 } else if (!strcmp(value, "repeat")) {
1747 spread = FillSpread::Repeat;
1754 static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1756 radial->cx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1757 if (!loader->svgParse->gradient.parsedFx) radial->fx = radial->cx;
1761 static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1763 radial->cy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1764 if (!loader->svgParse->gradient.parsedFy) radial->fy = radial->cy;
1768 static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1770 radial->fx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1771 loader->svgParse->gradient.parsedFx = true;
1775 static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1777 radial->fy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1778 loader->svgParse->gradient.parsedFy = true;
1782 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1784 radial->r = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Other);
1788 static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1790 if (!userSpace) radial->cx = radial->cx * loader->svgParse->global.w;
1794 static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1796 if (!userSpace) radial->cy = radial->cy * loader->svgParse->global.h;
1800 static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1802 if (!userSpace) radial->fx = radial->fx * loader->svgParse->global.w;
1806 static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1808 if (!userSpace) radial->fy = radial->fy * loader->svgParse->global.h;
1812 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1814 if (!userSpace) radial->r = radial->r * (sqrt(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrt(2.0));
1818 typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
1819 typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
1822 #define RADIAL_DEF(Name, Name1) \
1824 #Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
1828 static constexpr struct
1832 radialMethod tagHandler;
1833 radialMethodRecalc tagRecalc;
1843 static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
1845 SvgLoaderData* loader = (SvgLoaderData*)data;
1846 SvgStyleGradient* grad = loader->svgParse->styleGrad;
1847 SvgRadialGradient* radial = grad->radial;
1848 int sz = strlen(key);
1850 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1851 if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
1852 radialTags[i].tagHandler(loader, radial, value);
1857 if (!strcmp(key, "id")) {
1858 grad->id = _copyId(value);
1859 } else if (!strcmp(key, "spreadMethod")) {
1860 grad->spread = _parseSpreadValue(value);
1861 } else if (!strcmp(key, "xlink:href")) {
1862 grad->ref = _idFromHref(value);
1863 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
1864 grad->userSpace = true;
1873 static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
1875 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1876 if (!grad) return nullptr;
1877 loader->svgParse->styleGrad = grad;
1879 grad->type = SvgGradientType::Radial;
1880 grad->userSpace = false;
1881 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1882 if (!grad->radial) {
1887 * Default values of gradient
1889 grad->radial->cx = 0.5;
1890 grad->radial->cy = 0.5;
1891 grad->radial->fx = 0.5;
1892 grad->radial->fy = 0.5;
1893 grad->radial->r = 0.5;
1895 loader->svgParse->gradient.parsedFx = false;
1896 loader->svgParse->gradient.parsedFy = false;
1897 simpleXmlParseAttributes(buf, bufLength,
1898 _attrParseRadialGradientNode, loader);
1900 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1901 radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
1904 grad->usePercentage = true;
1906 return loader->svgParse->styleGrad;
1910 static bool _attrParseStops(void* data, const char* key, const char* value)
1912 SvgLoaderData* loader = (SvgLoaderData*)data;
1913 auto stop = loader->svgParse->gradStop;
1915 if (!strcmp(key, "offset")) {
1916 stop->offset = _toOffset(value);
1917 } else if (!strcmp(key, "stop-opacity")) {
1918 stop->a = _toOpacity(value);
1919 } else if (!strcmp(key, "stop-color")) {
1920 _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
1921 } else if (!strcmp(key, "style")) {
1922 simpleXmlParseW3CAttribute(value, _attrParseStops, data);
1931 static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1933 linear->x1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1937 static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1939 linear->y1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1943 static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1945 linear->x2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1949 static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1951 linear->y2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1955 static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1957 if (!userSpace) linear->x1 = linear->x1 * loader->svgParse->global.w;
1961 static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1963 if (!userSpace) linear->y1 = linear->y1 * loader->svgParse->global.h;
1967 static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1969 if (!userSpace) linear->x2 = linear->x2 * loader->svgParse->global.w;
1973 static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1975 if (!userSpace) linear->y2 = linear->y2 * loader->svgParse->global.h;
1979 typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
1980 typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
1983 #define LINEAR_DEF(Name, Name1) \
1985 #Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
1989 static constexpr struct
1993 Linear_Method tagHandler;
1994 Linear_Method_Recalc tagRecalc;
2003 static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
2005 SvgLoaderData* loader = (SvgLoaderData*)data;
2006 SvgStyleGradient* grad = loader->svgParse->styleGrad;
2007 SvgLinearGradient* linear = grad->linear;
2008 int sz = strlen(key);
2010 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2011 if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
2012 linear_tags[i].tagHandler(loader, linear, value);
2017 if (!strcmp(key, "id")) {
2018 grad->id = _copyId(value);
2019 } else if (!strcmp(key, "spreadMethod")) {
2020 grad->spread = _parseSpreadValue(value);
2021 } else if (!strcmp(key, "xlink:href")) {
2022 grad->ref = _idFromHref(value);
2023 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
2024 grad->userSpace = true;
2025 } else if (!strcmp(key, "gradientTransform")) {
2026 grad->transform = _parseTransformationMatrix(value);
2035 static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2037 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
2038 if (!grad) return nullptr;
2039 loader->svgParse->styleGrad = grad;
2041 grad->type = SvgGradientType::Linear;
2042 grad->userSpace = false;
2043 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
2044 if (!grad->linear) {
2049 * Default value of x2 is 100%
2051 grad->linear->x2 = 1;
2052 simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
2054 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2055 linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
2058 grad->usePercentage = true;
2060 return loader->svgParse->styleGrad;
2064 #define GRADIENT_DEF(Name, Name1) \
2066 #Name, sizeof(#Name), _create##Name1 \
2071 * For all Gradients lengths would be calculated into percentages related to
2072 * canvas width and height.
2074 * if user then recalculate actual pixels into percentages
2076 static constexpr struct
2080 GradientFactoryMethod tagHandler;
2081 } gradientTags[] = {
2082 GRADIENT_DEF(linearGradient, LinearGradient),
2083 GRADIENT_DEF(radialGradient, RadialGradient)
2087 static GradientFactoryMethod _findGradientFactory(const char* name)
2089 int sz = strlen(name);
2091 for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
2092 if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
2093 return gradientTags[i].tagHandler;
2100 static constexpr struct
2106 {"svg", sizeof("svg")},
2107 {"defs", sizeof("defs")},
2108 {"mask", sizeof("mask")},
2109 {"clipPath", sizeof("clipPath")}
2113 static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content)
2115 content = _skipSpace(content, nullptr);
2117 for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
2118 if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
2119 loader->stack.pop();
2128 static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
2130 const char* attrs = nullptr;
2131 int attrsLength = 0;
2133 char tagName[20] = "";
2134 FactoryMethod method;
2135 GradientFactoryMethod gradientMethod;
2136 SvgNode *node = nullptr, *parent = nullptr;
2138 attrs = simpleXmlFindAttributesTag(content, length);
2141 //Parse the empty tag
2143 while ((attrs != nullptr) && *attrs != '>') attrs++;
2147 //Find out the tag name starting from content till sz length
2148 sz = attrs - content;
2149 attrsLength = length - sz;
2150 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2151 if ((unsigned)sz >= sizeof(tagName)) return;
2152 strncpy(tagName, content, sz);
2156 if ((method = _findGroupFactory(tagName))) {
2159 if (strcmp(tagName, "svg")) return; //Not a valid svg document
2160 node = method(loader, nullptr, attrs, attrsLength);
2163 if (!strcmp(tagName, "svg")) return; //Already loadded <svg>(SvgNodeType::Doc) tag
2164 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2165 else parent = loader->doc;
2166 node = method(loader, parent, attrs, attrsLength);
2169 if (node->type == SvgNodeType::Defs) {
2170 loader->doc->node.doc.defs = node;
2172 if (!empty) loader->stack.push(node);
2174 loader->stack.push(node);
2176 } else if ((method = _findGraphicsFactory(tagName))) {
2177 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2178 else parent = loader->doc;
2179 node = method(loader, parent, attrs, attrsLength);
2180 } else if ((gradientMethod = _findGradientFactory(tagName))) {
2181 SvgStyleGradient* gradient;
2182 gradient = gradientMethod(loader, attrs, attrsLength);
2183 //FIXME: The current parsing structure does not distinguish end tags.
2184 // There is no way to know if the currently parsed gradient is in defs.
2185 // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
2186 // But finally, the loader has a gradient style list regardless of defs.
2187 // This is only to support this when multiple gradients are declared, even if no defs are declared.
2188 // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
2189 if (loader->def && loader->doc->node.doc.defs) {
2190 loader->def->node.defs.gradients.push(gradient);
2192 loader->gradients.push(gradient);
2194 loader->latestGradient = gradient;
2195 } else if (!strcmp(tagName, "stop")) {
2196 auto stop = static_cast<Fill::ColorStop*>(calloc(1, sizeof(Fill::ColorStop)));
2198 loader->svgParse->gradStop = stop;
2199 /* default value for opacity */
2201 simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
2202 if (loader->latestGradient) {
2203 loader->latestGradient->stops.push(stop);
2206 #ifdef THORVG_LOG_ENABLED
2208 if (!isIgnoreUnsupportedLogElements(tagName)) printf("SVG: Unsupported elements used [Elements: %s]\n", tagName);
2214 static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
2216 SvgLoaderData* loader = (SvgLoaderData*)data;
2219 case SimpleXMLType::Open: {
2220 _svgLoaderParserXmlOpen(loader, content, length, false);
2223 case SimpleXMLType::OpenEmpty: {
2224 _svgLoaderParserXmlOpen(loader, content, length, true);
2227 case SimpleXMLType::Close: {
2228 _svgLoaderParerXmlClose(loader, content);
2231 case SimpleXMLType::Data:
2232 case SimpleXMLType::CData:
2233 case SimpleXMLType::DoctypeChild: {
2236 case SimpleXMLType::Ignored:
2237 case SimpleXMLType::Comment:
2238 case SimpleXMLType::Doctype: {
2250 static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
2252 if (parent == nullptr) return;
2253 //Inherit the property of parent if not present in child.
2255 if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
2256 child->fill.paint.r = parent->fill.paint.r;
2257 child->fill.paint.g = parent->fill.paint.g;
2258 child->fill.paint.b = parent->fill.paint.b;
2259 child->fill.paint.none = parent->fill.paint.none;
2260 child->fill.paint.curColor = parent->fill.paint.curColor;
2261 if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url->c_str());
2263 if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
2264 child->fill.opacity = parent->fill.opacity;
2266 if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
2267 child->fill.fillRule = parent->fill.fillRule;
2270 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
2271 child->stroke.paint.r = parent->stroke.paint.r;
2272 child->stroke.paint.g = parent->stroke.paint.g;
2273 child->stroke.paint.b = parent->stroke.paint.b;
2274 child->stroke.paint.none = parent->stroke.paint.none;
2275 child->stroke.paint.curColor = parent->stroke.paint.curColor;
2276 child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url->c_str()) : nullptr;
2278 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
2279 child->stroke.opacity = parent->stroke.opacity;
2281 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
2282 child->stroke.width = parent->stroke.width;
2284 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
2285 if (parent->stroke.dash.array.count > 0) {
2286 child->stroke.dash.array.clear();
2287 child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
2288 for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
2289 child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
2293 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
2294 child->stroke.cap = parent->stroke.cap;
2296 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
2297 child->stroke.join = parent->stroke.join;
2302 #ifdef THORVG_LOG_ENABLED
2303 static void _inefficientNodeCheck(SvgNode* node){
2304 if (!node->display && node->type != SvgNodeType::ClipPath) printf("SVG: Inefficient elements used [Display is none][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2305 if (node->style->opacity == 0) printf("SVG: Inefficient elements used [Opacity is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2306 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());
2308 switch (node->type) {
2309 case SvgNodeType::Path: {
2310 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());
2313 case SvgNodeType::Ellipse: {
2314 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());
2317 case SvgNodeType::Polygon:
2318 case SvgNodeType::Polyline: {
2319 if (node->node.polygon.pointsCount < 2) printf("SVG: Inefficient elements used [Invalid Polygon][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2322 case SvgNodeType::Circle: {
2323 if (node->node.circle.r == 0) printf("SVG: Inefficient elements used [Size is zero][Node Type : %s]\n", simpleXmlNodeTypeToString(node->type).c_str());
2326 case SvgNodeType::Rect: {
2327 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());
2330 case SvgNodeType::Line: {
2331 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());
2339 static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
2341 _styleInherit(node->style, parentStyle);
2342 #ifdef THORVG_LOG_ENABLED
2343 _inefficientNodeCheck(node);
2346 auto child = node->child.data;
2347 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2348 _updateStyle(*child, node->style);
2353 static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const string* id)
2355 SvgStyleGradient* result = nullptr;
2357 auto gradList = gradients->data;
2359 for (uint32_t i = 0; i < gradients->count; ++i) {
2360 if (!((*gradList)->id->compare(*id))) {
2361 result = _cloneGradient(*gradList);
2367 if (result && result->ref) {
2368 gradList = gradients->data;
2369 for (uint32_t i = 0; i < gradients->count; ++i) {
2370 if (!((*gradList)->id->compare(*result->ref))) {
2371 if (result->stops.count == 0) {
2372 _cloneGradStops(&result->stops, &(*gradList)->stops);
2374 //TODO: Properly inherit other property
2385 static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradidents)
2387 if (node->child.count > 0) {
2388 auto child = node->child.data;
2389 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2390 _updateGradient(*child, gradidents);
2393 if (node->style->fill.paint.url) {
2394 node->style->fill.paint.gradient = _gradientDup(gradidents, node->style->fill.paint.url);
2395 } else if (node->style->stroke.paint.url) {
2396 //node->style->stroke.paint.gradient = _gradientDup(gradList, node->style->stroke.paint.url);
2401 static void _updateComposite(SvgNode* node, SvgNode* root)
2403 if (node->style->comp.url && !node->style->comp.node) {
2404 SvgNode *findResult = _findNodeById(root, node->style->comp.url);
2405 if (findResult) node->style->comp.node = findResult;
2407 if (node->child.count > 0) {
2408 auto child = node->child.data;
2409 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2410 _updateComposite(*child, root);
2415 static void _freeGradientStyle(SvgStyleGradient* grad)
2423 if (grad->transform) free(grad->transform);
2425 for (uint32_t i = 0; i < grad->stops.count; ++i) {
2426 auto colorStop = grad->stops.data[i];
2429 grad->stops.reset();
2433 static void _freeNodeStyle(SvgStyleProperty* style)
2437 //style->comp.node has only the addresses of node. Therefore, style->comp.node is released from _freeNode.
2438 delete style->comp.url;
2440 _freeGradientStyle(style->fill.paint.gradient);
2441 delete style->fill.paint.url;
2442 _freeGradientStyle(style->stroke.paint.gradient);
2443 if (style->stroke.dash.array.count > 0) style->stroke.dash.array.reset();
2444 delete style->stroke.paint.url;
2448 static void _freeNode(SvgNode* node)
2452 auto child = node->child.data;
2453 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2456 node->child.reset();
2459 free(node->transform);
2460 _freeNodeStyle(node->style);
2461 switch (node->type) {
2462 case SvgNodeType::Path: {
2463 delete node->node.path.path;
2466 case SvgNodeType::Polygon: {
2467 free(node->node.polygon.points);
2470 case SvgNodeType::Polyline: {
2471 free(node->node.polyline.points);
2474 case SvgNodeType::Doc: {
2475 _freeNode(node->node.doc.defs);
2478 case SvgNodeType::Defs: {
2479 auto gradients = node->node.defs.gradients.data;
2480 for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
2481 _freeGradientStyle(*gradients);
2484 node->node.defs.gradients.reset();
2495 static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
2497 const char* attrs = nullptr;
2499 char tagName[20] = "";
2500 FactoryMethod method;
2501 SvgNode *node = nullptr;
2502 int attrsLength = 0;
2504 attrs = simpleXmlFindAttributesTag(content, length);
2507 //Parse the empty tag
2509 while ((attrs != nullptr) && *attrs != '>') attrs++;
2513 sz = attrs - content;
2514 attrsLength = length - sz;
2515 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2516 if ((unsigned)sz >= sizeof(tagName)) return false;
2517 strncpy(tagName, content, sz);
2521 if ((method = _findGroupFactory(tagName))) {
2523 if (strcmp(tagName, "svg")) return true; //Not a valid svg document
2524 node = method(loader, nullptr, attrs, attrsLength);
2526 loader->stack.push(node);
2534 static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
2536 SvgLoaderData* loader = (SvgLoaderData*)data;
2540 case SimpleXMLType::Open:
2541 case SimpleXMLType::OpenEmpty: {
2542 //If 'res' is false, it means <svg> tag is found.
2543 res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
2555 /************************************************************************/
2556 /* External Class Implementation */
2557 /************************************************************************/
2559 SvgLoader::SvgLoader()
2564 SvgLoader::~SvgLoader()
2570 void SvgLoader::run(unsigned tid)
2572 if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
2574 if (loaderData.doc) {
2575 _updateStyle(loaderData.doc, nullptr);
2576 auto defs = loaderData.doc->node.doc.defs;
2577 if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
2579 if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
2581 _updateComposite(loaderData.doc, loaderData.doc);
2582 if (defs) _updateComposite(loaderData.doc, defs);
2584 root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh);
2588 bool SvgLoader::header()
2590 //For valid check, only <svg> tag is parsed first.
2591 //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
2592 //After that, the remaining content data is parsed in order with async.
2593 loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
2594 if (!loaderData.svgParse) return false;
2596 simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
2598 if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
2599 //Return the brief resource info such as viewbox:
2600 vx = loaderData.doc->node.doc.vx;
2601 vy = loaderData.doc->node.doc.vy;
2602 w = vw = loaderData.doc->node.doc.vw;
2603 h = vh = loaderData.doc->node.doc.vh;
2606 if (loaderData.doc->node.doc.w > 0) {
2607 w = loaderData.doc->node.doc.w;
2608 if (vw < FLT_EPSILON) vw = w;
2610 if (loaderData.doc->node.doc.h > 0) {
2611 h = loaderData.doc->node.doc.h;
2612 if (vh < FLT_EPSILON) vh = h;
2615 preserveAspect = loaderData.doc->node.doc.preserveAspect;
2617 //LOG: No SVG File. There is no <svg/>
2625 bool SvgLoader::open(const char* data, uint32_t size)
2627 this->content = data;
2634 bool SvgLoader::open(const string& path)
2641 //LOG: Failed to open file
2644 getline(f, filePath, '\0');
2647 if (filePath.empty()) return false;
2649 this->content = filePath.c_str();
2650 this->size = filePath.size();
2657 bool SvgLoader::read()
2659 if (!content || size == 0) return false;
2661 TaskScheduler::request(this);
2667 bool SvgLoader::close()
2671 if (loaderData.svgParse) {
2672 free(loaderData.svgParse);
2673 loaderData.svgParse = nullptr;
2675 auto gradients = loaderData.gradients.data;
2676 for (size_t i = 0; i < loaderData.gradients.count; ++i) {
2677 _freeGradientStyle(*gradients);
2680 loaderData.gradients.reset();
2682 _freeNode(loaderData.doc);
2683 loaderData.doc = nullptr;
2684 loaderData.stack.reset();
2690 unique_ptr<Scene> SvgLoader::scene()
2693 if (root) return move(root);
2694 else return nullptr;