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
24 * Copyright notice for the EFL:
26 * Copyright (C) EFL developers (see AUTHORS)
28 * All rights reserved.
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
39 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
40 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
42 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
43 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
44 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
45 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
46 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
47 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
48 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 #define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
57 #include "tvgLoader.h"
58 #include "tvgXmlParser.h"
59 #include "tvgSvgLoader.h"
60 #include "tvgSvgSceneBuilder.h"
61 #include "tvgSvgUtil.h"
63 /************************************************************************/
64 /* Internal Class Implementation */
65 /************************************************************************/
67 typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
68 typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
71 static char* _skipSpace(const char* str, const char* end)
73 while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
80 static string* _copyId(const char* str)
82 if (!str) return nullptr;
84 return new string(str);
88 static const char* _skipComma(const char* content)
90 content = _skipSpace(content, nullptr);
91 if (*content == ',') return content + 1;
96 static bool _parseNumber(const char** content, float* number)
100 *number = svgUtilStrtof(*content, &end);
101 //If the start of string is not number
102 if ((*content) == end) return false;
104 *content = _skipComma(end);
109 * According to https://www.w3.org/TR/SVG/coords.html#Units
112 * Since this documentation is not obvious, more clean recalculation with dpi
113 * is required, but for now default w3 constants would be used
115 static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
117 float parsedValue = svgUtilStrtof(str, nullptr);
119 if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
120 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
121 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
122 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
123 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
124 else if (strstr(str, "%")) {
125 if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
126 else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
127 else //if other then it's radius
129 float max = (float)svgParse->global.w;
130 if (max < svgParse->global.h)
131 max = (float)svgParse->global.h;
132 parsedValue = (parsedValue / 100.0) * max;
136 //TODO: Implement 'em', 'ex' attributes
142 static float _gradientToFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
146 float parsedValue = svgUtilStrtof(str, &end);
150 * That is according to Units in here
152 * https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
154 if (type == SvgParserLengthType::Vertical) max = (float)svgParse->global.h;
155 else if (type == SvgParserLengthType::Horizontal) max = (float)svgParse->global.w;
156 else if (type == SvgParserLengthType::Other) max = sqrtf(pow(svgParse->global.h, 2) + pow(svgParse->global.w, 2)) / sqrtf(2.0);
158 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
159 else if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
160 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
161 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
162 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
163 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
164 //TODO: Implement 'em', 'ex' attributes
166 //Transform into global percentage
167 parsedValue = parsedValue / max;
173 static float _toOffset(const char* str)
176 auto strEnd = str + strlen(str);
178 float parsedValue = svgUtilStrtof(str, &end);
180 end = _skipSpace(end, nullptr);
181 auto ptr = strstr(str, "%");
184 parsedValue = parsedValue / 100.0;
185 if (end != ptr || (end + 1) != strEnd) return 0;
186 } else if (end != strEnd) return 0;
192 static int _toOpacity(const char* str)
195 float opacity = svgUtilStrtof(str, &end);
198 if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
199 else if (*end == '\0') return lrint(opacity * 255);
205 #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
206 static Type _to##Name1(const char* str) \
210 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
211 if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
217 /* parse the line cap used during stroking a path.
218 * Value: butt | round | square | inherit
220 * https://www.w3.org/TR/SVG/painting.html
222 static constexpr struct
227 { StrokeCap::Butt, "butt" },
228 { StrokeCap::Round, "round" },
229 { StrokeCap::Square, "square" }
233 _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
236 /* parse the line join used during stroking a path.
237 * Value: miter | round | bevel | inherit
239 * https://www.w3.org/TR/SVG/painting.html
241 static constexpr struct
246 { StrokeJoin::Miter, "miter" },
247 { StrokeJoin::Round, "round" },
248 { StrokeJoin::Bevel, "bevel" }
252 _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
255 /* parse the fill rule used during filling a path.
256 * Value: nonzero | evenodd | inherit
258 * https://www.w3.org/TR/SVG/painting.html
260 static constexpr struct
265 { FillRule::EvenOdd, "evenodd" }
269 _PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding)
272 /* parse the dash pattern used during stroking a path.
273 * Value: none | <dasharray> | inherit
275 * https://www.w3.org/TR/SVG/painting.html
278 _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash)
280 if (!strncmp(str, "none", 4)) return;
285 str = _skipComma(str);
286 float parsedValue = svgUtilStrtof(str, &end);
287 if (str == end) break;
288 if (parsedValue <= 0.0f) break;
291 //Refers to the diagonal length of the viewport.
292 //https://www.w3.org/TR/SVG2/coords.html#Units
293 parsedValue = (sqrtf(pow(loader->svgParse->global.w, 2) + pow(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
295 (*dash).array.push(parsedValue);
298 //If dash array size is 1, it means that dash and gap size are the same.
299 if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
302 static string* _idFromUrl(const char* url)
304 url = _skipSpace(url, nullptr);
307 url = _skipSpace(url, nullptr);
310 if ((*url) == '\'') ++url;
311 if ((*url) == '#') ++url;
314 while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
316 return new string(url, i);
320 static unsigned char _parserColor(const char* value, char** end)
324 r = svgUtilStrtof(value, end);
325 *end = _skipSpace(*end, nullptr);
326 if (**end == '%') r = 255 * r / 100;
327 *end = _skipSpace(*end, nullptr);
329 if (r < 0 || r > 255) {
338 static constexpr struct
343 { "aliceblue", 0xfff0f8ff },
344 { "antiquewhite", 0xfffaebd7 },
345 { "aqua", 0xff00ffff },
346 { "aquamarine", 0xff7fffd4 },
347 { "azure", 0xfff0ffff },
348 { "beige", 0xfff5f5dc },
349 { "bisque", 0xffffe4c4 },
350 { "black", 0xff000000 },
351 { "blanchedalmond", 0xffffebcd },
352 { "blue", 0xff0000ff },
353 { "blueviolet", 0xff8a2be2 },
354 { "brown", 0xffa52a2a },
355 { "burlywood", 0xffdeb887 },
356 { "cadetblue", 0xff5f9ea0 },
357 { "chartreuse", 0xff7fff00 },
358 { "chocolate", 0xffd2691e },
359 { "coral", 0xffff7f50 },
360 { "cornflowerblue", 0xff6495ed },
361 { "cornsilk", 0xfffff8dc },
362 { "crimson", 0xffdc143c },
363 { "cyan", 0xff00ffff },
364 { "darkblue", 0xff00008b },
365 { "darkcyan", 0xff008b8b },
366 { "darkgoldenrod", 0xffb8860b },
367 { "darkgray", 0xffa9a9a9 },
368 { "darkgrey", 0xffa9a9a9 },
369 { "darkgreen", 0xff006400 },
370 { "darkkhaki", 0xffbdb76b },
371 { "darkmagenta", 0xff8b008b },
372 { "darkolivegreen", 0xff556b2f },
373 { "darkorange", 0xffff8c00 },
374 { "darkorchid", 0xff9932cc },
375 { "darkred", 0xff8b0000 },
376 { "darksalmon", 0xffe9967a },
377 { "darkseagreen", 0xff8fbc8f },
378 { "darkslateblue", 0xff483d8b },
379 { "darkslategray", 0xff2f4f4f },
380 { "darkslategrey", 0xff2f4f4f },
381 { "darkturquoise", 0xff00ced1 },
382 { "darkviolet", 0xff9400d3 },
383 { "deeppink", 0xffff1493 },
384 { "deepskyblue", 0xff00bfff },
385 { "dimgray", 0xff696969 },
386 { "dimgrey", 0xff696969 },
387 { "dodgerblue", 0xff1e90ff },
388 { "firebrick", 0xffb22222 },
389 { "floralwhite", 0xfffffaf0 },
390 { "forestgreen", 0xff228b22 },
391 { "fuchsia", 0xffff00ff },
392 { "gainsboro", 0xffdcdcdc },
393 { "ghostwhite", 0xfff8f8ff },
394 { "gold", 0xffffd700 },
395 { "goldenrod", 0xffdaa520 },
396 { "gray", 0xff808080 },
397 { "grey", 0xff808080 },
398 { "green", 0xff008000 },
399 { "greenyellow", 0xffadff2f },
400 { "honeydew", 0xfff0fff0 },
401 { "hotpink", 0xffff69b4 },
402 { "indianred", 0xffcd5c5c },
403 { "indigo", 0xff4b0082 },
404 { "ivory", 0xfffffff0 },
405 { "khaki", 0xfff0e68c },
406 { "lavender", 0xffe6e6fa },
407 { "lavenderblush", 0xfffff0f5 },
408 { "lawngreen", 0xff7cfc00 },
409 { "lemonchiffon", 0xfffffacd },
410 { "lightblue", 0xffadd8e6 },
411 { "lightcoral", 0xfff08080 },
412 { "lightcyan", 0xffe0ffff },
413 { "lightgoldenrodyellow", 0xfffafad2 },
414 { "lightgray", 0xffd3d3d3 },
415 { "lightgrey", 0xffd3d3d3 },
416 { "lightgreen", 0xff90ee90 },
417 { "lightpink", 0xffffb6c1 },
418 { "lightsalmon", 0xffffa07a },
419 { "lightseagreen", 0xff20b2aa },
420 { "lightskyblue", 0xff87cefa },
421 { "lightslategray", 0xff778899 },
422 { "lightslategrey", 0xff778899 },
423 { "lightsteelblue", 0xffb0c4de },
424 { "lightyellow", 0xffffffe0 },
425 { "lime", 0xff00ff00 },
426 { "limegreen", 0xff32cd32 },
427 { "linen", 0xfffaf0e6 },
428 { "magenta", 0xffff00ff },
429 { "maroon", 0xff800000 },
430 { "mediumaquamarine", 0xff66cdaa },
431 { "mediumblue", 0xff0000cd },
432 { "mediumorchid", 0xffba55d3 },
433 { "mediumpurple", 0xff9370d8 },
434 { "mediumseagreen", 0xff3cb371 },
435 { "mediumslateblue", 0xff7b68ee },
436 { "mediumspringgreen", 0xff00fa9a },
437 { "mediumturquoise", 0xff48d1cc },
438 { "mediumvioletred", 0xffc71585 },
439 { "midnightblue", 0xff191970 },
440 { "mintcream", 0xfff5fffa },
441 { "mistyrose", 0xffffe4e1 },
442 { "moccasin", 0xffffe4b5 },
443 { "navajowhite", 0xffffdead },
444 { "navy", 0xff000080 },
445 { "oldlace", 0xfffdf5e6 },
446 { "olive", 0xff808000 },
447 { "olivedrab", 0xff6b8e23 },
448 { "orange", 0xffffa500 },
449 { "orangered", 0xffff4500 },
450 { "orchid", 0xffda70d6 },
451 { "palegoldenrod", 0xffeee8aa },
452 { "palegreen", 0xff98fb98 },
453 { "paleturquoise", 0xffafeeee },
454 { "palevioletred", 0xffd87093 },
455 { "papayawhip", 0xffffefd5 },
456 { "peachpuff", 0xffffdab9 },
457 { "peru", 0xffcd853f },
458 { "pink", 0xffffc0cb },
459 { "plum", 0xffdda0dd },
460 { "powderblue", 0xffb0e0e6 },
461 { "purple", 0xff800080 },
462 { "red", 0xffff0000 },
463 { "rosybrown", 0xffbc8f8f },
464 { "royalblue", 0xff4169e1 },
465 { "saddlebrown", 0xff8b4513 },
466 { "salmon", 0xfffa8072 },
467 { "sandybrown", 0xfff4a460 },
468 { "seagreen", 0xff2e8b57 },
469 { "seashell", 0xfffff5ee },
470 { "sienna", 0xffa0522d },
471 { "silver", 0xffc0c0c0 },
472 { "skyblue", 0xff87ceeb },
473 { "slateblue", 0xff6a5acd },
474 { "slategray", 0xff708090 },
475 { "slategrey", 0xff708090 },
476 { "snow", 0xfffffafa },
477 { "springgreen", 0xff00ff7f },
478 { "steelblue", 0xff4682b4 },
479 { "tan", 0xffd2b48c },
480 { "teal", 0xff008080 },
481 { "thistle", 0xffd8bfd8 },
482 { "tomato", 0xffff6347 },
483 { "turquoise", 0xff40e0d0 },
484 { "violet", 0xffee82ee },
485 { "wheat", 0xfff5deb3 },
486 { "white", 0xffffffff },
487 { "whitesmoke", 0xfff5f5f5 },
488 { "yellow", 0xffffff00 },
489 { "yellowgreen", 0xff9acd32 }
493 static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, string** ref)
495 unsigned int len = strlen(str);
496 char *red, *green, *blue;
497 unsigned char tr, tg, tb;
499 if (len == 4 && str[0] == '#') {
500 //Case for "#456" should be interprete as "#445566"
501 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
502 char tmp[3] = { '\0', '\0', '\0' };
505 *r = strtol(tmp, nullptr, 16);
508 *g = strtol(tmp, nullptr, 16);
511 *b = strtol(tmp, nullptr, 16);
513 } else if (len == 7 && str[0] == '#') {
514 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
515 char tmp[3] = { '\0', '\0', '\0' };
518 *r = strtol(tmp, nullptr, 16);
521 *g = strtol(tmp, nullptr, 16);
524 *b = strtol(tmp, nullptr, 16);
526 } 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] == ')') {
527 tr = _parserColor(str + 4, &red);
528 if (red && *red == ',') {
529 tg = _parserColor(red + 1, &green);
530 if (green && *green == ',') {
531 tb = _parserColor(green + 1, &blue);
532 if (blue && blue[0] == ')' && blue[1] == '\0') {
539 } else if (len >= 3 && !strncmp(str, "url", 3)) {
540 *ref = _idFromUrl((const char*)(str + 3));
543 for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
544 if (!strcasecmp(colors[i].name, str)) {
545 *r = (((uint8_t*)(&(colors[i].value)))[2]);
546 *g = (((uint8_t*)(&(colors[i].value)))[1]);
547 *b = (((uint8_t*)(&(colors[i].value)))[0]);
555 static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
560 str = _skipSpace(str, nullptr);
561 while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
562 points[count++] = svgUtilStrtof(str, &end);
564 str = _skipSpace(str, nullptr);
565 if (*str == ',') ++str;
566 //Eat the rest of space
567 str = _skipSpace(str, nullptr);
574 enum class MatrixState {
585 #define MATRIX_DEF(Name, Value) \
587 #Name, sizeof(#Name), Value \
591 static constexpr struct
597 MATRIX_DEF(matrix, MatrixState::Matrix),
598 MATRIX_DEF(translate, MatrixState::Translate),
599 MATRIX_DEF(rotate, MatrixState::Rotate),
600 MATRIX_DEF(scale, MatrixState::Scale),
601 MATRIX_DEF(skewX, MatrixState::SkewX),
602 MATRIX_DEF(skewY, MatrixState::SkewY)
606 static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
608 auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
609 auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
610 auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
612 auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
613 auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
614 auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
616 auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
617 auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
618 auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
632 /* parse transform attribute
633 * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
635 static Matrix* _parseTransformationMatrix(const char* value)
637 const int POINT_CNT = 8;
639 auto matrix = (Matrix*)malloc(sizeof(Matrix));
640 if (!matrix) return nullptr;
641 *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
643 float points[POINT_CNT];
645 char* str = (char*)value;
646 char* end = str + strlen(str);
649 auto state = MatrixState::Unknown;
651 if (isspace(*str) || (*str == ',')) {
655 for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
656 if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
657 state = matrixTags[i].state;
658 str += (matrixTags[i].sz - 1);
662 if (state == MatrixState::Unknown) goto error;
664 str = _skipSpace(str, end);
665 if (*str != '(') goto error;
667 str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
668 if (*str != ')') goto error;
671 if (state == MatrixState::Matrix) {
672 if (ptCount != 6) goto error;
673 Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
674 _matrixCompose(matrix, &tmp, matrix);
675 } else if (state == MatrixState::Translate) {
677 Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
678 _matrixCompose(matrix, &tmp, matrix);
679 } else if (ptCount == 2) {
680 Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
681 _matrixCompose(matrix, &tmp, matrix);
683 } else if (state == MatrixState::Rotate) {
684 //Transform to signed.
685 points[0] = fmod(points[0], 360);
686 if (points[0] < 0) points[0] += 360;
687 auto c = cosf(points[0] * (M_PI / 180.0));
688 auto s = sinf(points[0] * (M_PI / 180.0));
690 Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
691 _matrixCompose(matrix, &tmp, matrix);
692 } else if (ptCount == 3) {
693 Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
694 _matrixCompose(matrix, &tmp, matrix);
695 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
696 _matrixCompose(matrix, &tmp, matrix);
697 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
698 _matrixCompose(matrix, &tmp, matrix);
702 } else if (state == MatrixState::Scale) {
703 if (ptCount < 1 || ptCount > 2) goto error;
706 if (ptCount == 2) sy = points[1];
707 Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
708 _matrixCompose(matrix, &tmp, matrix);
713 if (matrix) free(matrix);
718 #define LENGTH_DEF(Name, Value) \
720 #Name, sizeof(#Name), Value \
726 static constexpr struct
732 LENGTH_DEF(%, SvgLengthType::Percent),
733 LENGTH_DEF(px, SvgLengthType::Px),
734 LENGTH_DEF(pc, SvgLengthType::Pc),
735 LENGTH_DEF(pt, SvgLengthType::Pt),
736 LENGTH_DEF(mm, SvgLengthType::Mm),
737 LENGTH_DEF(cm, SvgLengthType::Cm),
738 LENGTH_DEF(in, SvgLengthType::In)
741 static float _parseLength(const char* str, SvgLengthType* type)
744 int sz = strlen(str);
746 *type = SvgLengthType::Px;
747 for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
748 if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
750 value = svgUtilStrtof(str, nullptr);
755 static bool _parseStyleAttr(void* data, const char* key, const char* value);
756 static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
759 static bool _attrParseSvgNode(void* data, const char* key, const char* value)
761 SvgLoaderData* loader = (SvgLoaderData*)data;
762 SvgNode* node = loader->svgParse->node;
763 SvgDocNode* doc = &(node->node.doc);
765 if (!strcmp(key, "width")) {
766 doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
767 } else if (!strcmp(key, "height")) {
768 doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
769 } else if (!strcmp(key, "viewBox")) {
770 if (_parseNumber(&value, &doc->vx)) {
771 if (_parseNumber(&value, &doc->vy)) {
772 if (_parseNumber(&value, &doc->vw)) {
773 _parseNumber(&value, &doc->vh);
774 loader->svgParse->global.h = (uint32_t)doc->vh;
776 loader->svgParse->global.w = (uint32_t)doc->vw;
778 loader->svgParse->global.y = (int)doc->vy;
780 loader->svgParse->global.x = (int)doc->vx;
781 } else if (!strcmp(key, "preserveAspectRatio")) {
782 if (!strcmp(value, "none")) doc->preserveAspect = false;
783 } else if (!strcmp(key, "style")) {
784 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
786 #ifdef THORVG_LOG_ENABLED
787 else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON ) {
788 TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
792 return _parseStyleAttr(loader, key, value, false);
798 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
799 static void _handlePaintAttr(SvgPaint* paint, const char* value)
801 if (!strcmp(value, "none")) {
807 if (!strcmp(value, "currentColor")) {
808 paint->curColor = true;
811 _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
815 static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
817 SvgStyleProperty* style = node->style;
818 style->curColorSet = true;
819 _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
823 static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
825 SvgStyleProperty* style = node->style;
826 style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
827 _handlePaintAttr(&style->fill.paint, value);
831 static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
833 SvgStyleProperty* style = node->style;
834 style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
835 _handlePaintAttr(&style->stroke.paint, value);
839 static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
841 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
842 node->style->stroke.opacity = _toOpacity(value);
845 static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
847 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
848 _parseDashArray(loader, value, &node->style->stroke.dash);
851 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
853 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
854 node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
858 static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
860 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
861 node->style->stroke.cap = _toLineCap(value);
865 static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
867 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
868 node->style->stroke.join = _toLineJoin(value);
872 static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
874 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
875 node->style->fill.fillRule = _toFillRule(value);
879 static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
881 node->style->opacity = _toOpacity(value);
885 static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
887 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
888 node->style->fill.opacity = _toOpacity(value);
892 static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
894 node->transform = _parseTransformationMatrix(value);
898 static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
900 SvgStyleProperty* style = node->style;
901 int len = strlen(value);
902 if (len >= 3 && !strncmp(value, "url", 3)) {
903 if (style->clipPath.url) delete(style->clipPath.url);
904 style->clipPath.url = _idFromUrl((const char*)(value + 3));
909 static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
911 SvgStyleProperty* style = node->style;
912 int len = strlen(value);
913 if (len >= 3 && !strncmp(value, "url", 3)) {
914 if (style->mask.url) delete(style->mask.url);
915 style->mask.url = _idFromUrl((const char*)(value + 3));
920 static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
922 //TODO : The display attribute can have various values as well as "none".
923 // The default is "inline" which means visible and "none" means invisible.
924 // Depending on the type of node, additional functionality may be required.
925 // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
926 if (!strcmp(value, "none")) node->display = false;
927 else node->display = true;
931 typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
933 #define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
936 static constexpr struct
940 styleMethod tagHandler;
943 STYLE_DEF(color, Color, SvgStyleFlags::Color),
944 STYLE_DEF(fill, Fill, SvgStyleFlags::Fill),
945 STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule),
946 STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity),
947 STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity),
948 STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
949 STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
950 STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
951 STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
952 STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
953 STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
954 STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
955 STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
956 STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
957 STYLE_DEF(display, Display, SvgStyleFlags::Display)
961 static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style)
963 SvgLoaderData* loader = (SvgLoaderData*)data;
964 SvgNode* node = loader->svgParse->node;
966 if (!key || !value) return false;
968 //Trim the white space
969 key = _skipSpace(key, nullptr);
970 value = _skipSpace(value, nullptr);
973 for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
974 if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
976 styleTags[i].tagHandler(loader, node, value);
977 node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
978 } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
979 styleTags[i].tagHandler(loader, node, value);
989 static bool _parseStyleAttr(void* data, const char* key, const char* value)
991 return _parseStyleAttr(data, key, value, true);
996 * https://www.w3.org/TR/SVG/struct.html#Groups
998 static bool _attrParseGNode(void* data, const char* key, const char* value)
1000 SvgLoaderData* loader = (SvgLoaderData*)data;
1001 SvgNode* node = loader->svgParse->node;
1003 if (!strcmp(key, "style")) {
1004 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1005 } else if (!strcmp(key, "transform")) {
1006 node->transform = _parseTransformationMatrix(value);
1007 } else if (!strcmp(key, "id")) {
1008 if (node->id && value) delete node->id;
1009 node->id = _copyId(value);
1010 } else if (!strcmp(key, "clip-path")) {
1011 _handleClipPathAttr(loader, node, value);
1012 } else if (!strcmp(key, "mask")) {
1013 _handleMaskAttr(loader, node, value);
1015 return _parseStyleAttr(loader, key, value, false);
1021 /* parse clipPath node
1022 * https://www.w3.org/TR/SVG/struct.html#Groups
1024 static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
1026 SvgLoaderData* loader = (SvgLoaderData*)data;
1027 SvgNode* node = loader->svgParse->node;
1029 if (!strcmp(key, "style")) {
1030 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1031 } else if (!strcmp(key, "transform")) {
1032 node->transform = _parseTransformationMatrix(value);
1033 } else if (!strcmp(key, "id")) {
1034 if (node->id && value) delete node->id;
1035 node->id = _copyId(value);
1037 return _parseStyleAttr(loader, key, value, false);
1043 static bool _attrParseMaskNode(void* data, const char* key, const char* value)
1045 SvgLoaderData* loader = (SvgLoaderData*)data;
1046 SvgNode* node = loader->svgParse->node;
1048 if (!strcmp(key, "style")) {
1049 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1050 } else if (!strcmp(key, "transform")) {
1051 node->transform = _parseTransformationMatrix(value);
1052 } else if (!strcmp(key, "id")) {
1053 if (node->id && value) delete node->id;
1054 node->id = _copyId(value);
1056 return _parseStyleAttr(loader, key, value, false);
1062 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
1064 SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
1066 if (!node) return nullptr;
1068 //Default fill property
1069 node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
1076 //Update the default value of stroke and fill
1077 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
1078 node->style->fill.paint.none = false;
1079 //Default fill opacity is 1
1080 node->style->fill.opacity = 255;
1081 node->style->opacity = 255;
1082 //Default current color is not set
1083 node->style->fill.paint.curColor = false;
1084 node->style->curColorSet = false;
1085 //Default fill rule is nonzero
1086 node->style->fill.fillRule = FillRule::Winding;
1088 //Default stroke is none
1089 node->style->stroke.paint.none = true;
1090 //Default stroke opacity is 1
1091 node->style->stroke.opacity = 255;
1092 //Default stroke current color is not set
1093 node->style->stroke.paint.curColor = false;
1094 //Default stroke width is 1
1095 node->style->stroke.width = 1;
1096 //Default line cap is butt
1097 node->style->stroke.cap = StrokeCap::Butt;
1098 //Default line join is miter
1099 node->style->stroke.join = StrokeJoin::Miter;
1100 node->style->stroke.scale = 1.0;
1102 //Default display is true("inline").
1103 node->display = true;
1105 node->parent = parent;
1108 if (parent) parent->child.push(node);
1113 static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength)
1115 if (loader->def && loader->doc->node.doc.defs) return loader->def;
1116 SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
1119 loader->doc->node.doc.defs = node;
1124 static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1126 loader->svgParse->node = _createNode(parent, SvgNodeType::G);
1127 if (!loader->svgParse->node) return nullptr;
1129 simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
1130 return loader->svgParse->node;
1134 static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1136 loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
1137 if (!loader->svgParse->node) return nullptr;
1138 SvgDocNode* doc = &(loader->svgParse->node->node.doc);
1140 loader->svgParse->global.w = 0;
1141 loader->svgParse->global.h = 0;
1143 doc->preserveAspect = true;
1144 simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
1146 if (loader->svgParse->global.w == 0) {
1147 if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
1148 else loader->svgParse->global.w = (uint32_t)doc->w;
1150 if (loader->svgParse->global.h == 0) {
1151 if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
1152 else loader->svgParse->global.h = (uint32_t)doc->h;
1155 return loader->svgParse->node;
1159 static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength)
1161 loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
1162 if (!loader->svgParse->node) return nullptr;
1164 simpleXmlParseAttributes(buf, bufLength, _attrParseMaskNode, loader);
1166 return loader->svgParse->node;
1170 static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1172 loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
1174 if (!loader->svgParse->node) return nullptr;
1176 loader->svgParse->node->display = false;
1178 simpleXmlParseAttributes(buf, bufLength, _attrParseClipPathNode, loader);
1180 return loader->svgParse->node;
1183 static bool _attrParsePathNode(void* data, const char* key, const char* value)
1185 SvgLoaderData* loader = (SvgLoaderData*)data;
1186 SvgNode* node = loader->svgParse->node;
1187 SvgPathNode* path = &(node->node.path);
1189 if (!strcmp(key, "d")) {
1190 //Temporary: need to copy
1191 path->path = _copyId(value);
1192 } else if (!strcmp(key, "style")) {
1193 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1194 } else if (!strcmp(key, "clip-path")) {
1195 _handleClipPathAttr(loader, node, value);
1196 } else if (!strcmp(key, "mask")) {
1197 _handleMaskAttr(loader, node, value);
1198 } else if (!strcmp(key, "id")) {
1199 if (node->id && value) delete node->id;
1200 node->id = _copyId(value);
1202 return _parseStyleAttr(loader, key, value, false);
1208 static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1210 loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
1212 if (!loader->svgParse->node) return nullptr;
1214 simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
1216 return loader->svgParse->node;
1220 static constexpr struct
1223 SvgParserLengthType type;
1227 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
1228 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
1229 {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
1233 /* parse the attributes for a circle element.
1234 * https://www.w3.org/TR/SVG/shapes.html#CircleElement
1236 static bool _attrParseCircleNode(void* data, const char* key, const char* value)
1238 SvgLoaderData* loader = (SvgLoaderData*)data;
1239 SvgNode* node = loader->svgParse->node;
1240 SvgCircleNode* circle = &(node->node.circle);
1241 unsigned char* array;
1242 int sz = strlen(key);
1244 array = (unsigned char*)circle;
1245 for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
1246 if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
1247 *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
1252 if (!strcmp(key, "style")) {
1253 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1254 } else if (!strcmp(key, "clip-path")) {
1255 _handleClipPathAttr(loader, node, value);
1256 } else if (!strcmp(key, "mask")) {
1257 _handleMaskAttr(loader, node, value);
1258 } else if (!strcmp(key, "id")) {
1259 if (node->id && value) delete node->id;
1260 node->id = _copyId(value);
1262 return _parseStyleAttr(loader, key, value, false);
1268 static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1270 loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
1272 if (!loader->svgParse->node) return nullptr;
1274 simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
1275 return loader->svgParse->node;
1279 static constexpr struct
1282 SvgParserLengthType type;
1286 {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
1287 {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
1288 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
1289 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
1293 /* parse the attributes for an ellipse element.
1294 * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
1296 static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
1298 SvgLoaderData* loader = (SvgLoaderData*)data;
1299 SvgNode* node = loader->svgParse->node;
1300 SvgEllipseNode* ellipse = &(node->node.ellipse);
1301 unsigned char* array;
1302 int sz = strlen(key);
1304 array = (unsigned char*)ellipse;
1305 for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
1306 if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
1307 *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
1312 if (!strcmp(key, "id")) {
1313 if (node->id && value) delete node->id;
1314 node->id = _copyId(value);
1315 } else if (!strcmp(key, "style")) {
1316 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1317 } else if (!strcmp(key, "clip-path")) {
1318 _handleClipPathAttr(loader, node, value);
1319 } else if (!strcmp(key, "mask")) {
1320 _handleMaskAttr(loader, node, value);
1322 return _parseStyleAttr(loader, key, value, false);
1328 static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1330 loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
1332 if (!loader->svgParse->node) return nullptr;
1334 simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
1335 return loader->svgParse->node;
1339 static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
1345 float *pointArray = nullptr, *tmpArray;
1347 while (_parseNumber(&str, &num)) {
1348 tmp[tmpCount++] = num;
1349 if (tmpCount == 50) {
1350 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1351 if (!tmpArray) goto error_alloc;
1352 pointArray = tmpArray;
1353 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1360 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1361 if (!tmpArray) goto error_alloc;
1362 pointArray = tmpArray;
1363 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1367 *points = pointArray;
1375 /* parse the attributes for a polygon element.
1376 * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
1378 static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
1380 SvgLoaderData* loader = (SvgLoaderData*)data;
1381 SvgNode* node = loader->svgParse->node;
1382 SvgPolygonNode* polygon = nullptr;
1384 if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
1385 else polygon = &(node->node.polyline);
1387 if (!strcmp(key, "points")) {
1388 return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
1389 } else if (!strcmp(key, "style")) {
1390 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1391 } else if (!strcmp(key, "clip-path")) {
1392 _handleClipPathAttr(loader, node, value);
1393 } else if (!strcmp(key, "mask")) {
1394 _handleMaskAttr(loader, node, value);
1395 } else if (!strcmp(key, "id")) {
1396 if (node->id && value) delete node->id;
1397 node->id = _copyId(value);
1399 return _parseStyleAttr(loader, key, value, false);
1405 static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1407 loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
1409 if (!loader->svgParse->node) return nullptr;
1411 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1412 return loader->svgParse->node;
1416 static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1418 loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
1420 if (!loader->svgParse->node) return nullptr;
1422 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1423 return loader->svgParse->node;
1426 static constexpr struct
1429 SvgParserLengthType type;
1433 {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1434 {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1435 {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1436 {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1437 {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
1438 {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
1442 /* parse the attributes for a rect element.
1443 * https://www.w3.org/TR/SVG/shapes.html#RectElement
1445 static bool _attrParseRectNode(void* data, const char* key, const char* value)
1447 SvgLoaderData* loader = (SvgLoaderData*)data;
1448 SvgNode* node = loader->svgParse->node;
1449 SvgRectNode* rect = &(node->node.rect);
1450 unsigned char* array;
1452 int sz = strlen(key);
1454 array = (unsigned char*)rect;
1455 for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
1456 if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
1457 *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
1459 //Case if only rx or ry is declared
1460 if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
1461 if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
1463 if ((rect->rx > FLT_EPSILON) && (rect->ry <= FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
1464 if ((rect->ry > FLT_EPSILON) && (rect->rx <= FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
1469 if (!strcmp(key, "id")) {
1470 if (node->id && value) delete node->id;
1471 node->id = _copyId(value);
1472 } else if (!strcmp(key, "style")) {
1473 ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1474 } else if (!strcmp(key, "clip-path")) {
1475 _handleClipPathAttr(loader, node, value);
1476 } else if (!strcmp(key, "mask")) {
1477 _handleMaskAttr(loader, node, value);
1479 ret = _parseStyleAttr(loader, key, value, false);
1486 static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1488 loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
1490 if (!loader->svgParse->node) return nullptr;
1492 loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
1494 simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
1495 return loader->svgParse->node;
1499 static constexpr struct
1502 SvgParserLengthType type;
1506 {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
1507 {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
1508 {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
1509 {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
1513 /* parse the attributes for a line element.
1514 * https://www.w3.org/TR/SVG/shapes.html#LineElement
1516 static bool _attrParseLineNode(void* data, const char* key, const char* value)
1518 SvgLoaderData* loader = (SvgLoaderData*)data;
1519 SvgNode* node = loader->svgParse->node;
1520 SvgLineNode* line = &(node->node.line);
1521 unsigned char* array;
1522 int sz = strlen(key);
1524 array = (unsigned char*)line;
1525 for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
1526 if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
1527 *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
1532 if (!strcmp(key, "id")) {
1533 if (node->id && value) delete node->id;
1534 node->id = _copyId(value);
1535 } else if (!strcmp(key, "style")) {
1536 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1537 } else if (!strcmp(key, "clip-path")) {
1538 _handleClipPathAttr(loader, node, value);
1539 } else if (!strcmp(key, "mask")) {
1540 _handleMaskAttr(loader, node, value);
1542 return _parseStyleAttr(loader, key, value, false);
1548 static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1550 loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
1552 if (!loader->svgParse->node) return nullptr;
1554 simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
1555 return loader->svgParse->node;
1559 static string* _idFromHref(const char* href)
1561 href = _skipSpace(href, nullptr);
1562 if ((*href) == '#') href++;
1563 return new string(href);
1567 static constexpr struct
1570 SvgParserLengthType type;
1574 {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
1575 {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
1576 {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
1577 {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)},
1581 /* parse the attributes for a image element.
1582 * https://www.w3.org/TR/SVG/embedded.html#ImageElement
1584 static bool _attrParseImageNode(void* data, const char* key, const char* value)
1586 SvgLoaderData* loader = (SvgLoaderData*)data;
1587 SvgNode* node = loader->svgParse->node;
1588 SvgImageNode* image = &(node->node.image);
1589 unsigned char* array;
1590 int sz = strlen(key);
1592 array = (unsigned char*)image;
1593 for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) {
1594 if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) {
1595 *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type);
1600 if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
1601 image->href = _idFromHref(value);
1602 } else if (!strcmp(key, "id")) {
1603 if (node->id && value) delete node->id;
1604 node->id = _copyId(value);
1605 } else if (!strcmp(key, "style")) {
1606 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1607 } else if (!strcmp(key, "clip-path")) {
1608 _handleClipPathAttr(loader, node, value);
1609 } else if (!strcmp(key, "mask")) {
1610 _handleMaskAttr(loader, node, value);
1612 return _parseStyleAttr(loader, key, value);
1618 static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1620 loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
1622 if (!loader->svgParse->node) return nullptr;
1624 simpleXmlParseAttributes(buf, bufLength, _attrParseImageNode, loader);
1625 return loader->svgParse->node;
1629 static SvgNode* _getDefsNode(SvgNode* node)
1631 if (!node) return nullptr;
1633 while (node->parent != nullptr) {
1634 node = node->parent;
1637 if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
1643 static SvgNode* _findChildById(const SvgNode* node, const char* id)
1645 if (!node) return nullptr;
1647 auto child = node->child.data;
1648 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1649 if (((*child)->id != nullptr) && !strcmp((*child)->id->c_str(), id)) return (*child);
1654 static SvgNode* _findNodeById(SvgNode *node, string* id)
1656 SvgNode* result = nullptr;
1657 if (node->id && !node->id->compare(*id)) return node;
1659 if (node->child.count > 0) {
1660 auto child = node->child.data;
1661 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
1662 result = _findNodeById(*child, id);
1669 static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
1671 for (uint32_t i = 0; i < src.count; ++i) {
1672 dst.push(src.data[i]);
1677 static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
1679 if (!from) return nullptr;
1681 auto grad = new SvgStyleGradient;
1682 if (!grad) return nullptr;
1684 grad->type = from->type;
1685 grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
1686 grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
1687 grad->spread = from->spread;
1688 grad->userSpace = from->userSpace;
1690 if (from->transform) {
1691 grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
1692 if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
1695 if (grad->type == SvgGradientType::Linear) {
1696 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
1697 if (!grad->linear) goto error_grad_alloc;
1698 memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
1699 } else if (grad->type == SvgGradientType::Radial) {
1700 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1701 if (!grad->radial) goto error_grad_alloc;
1702 memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
1705 _cloneGradStops(grad->stops, from->stops);
1710 if (grad) delete(grad);
1715 static void _copyAttr(SvgNode* to, const SvgNode* from)
1717 //Copy matrix attribute
1718 if (from->transform) {
1719 to->transform = (Matrix*)malloc(sizeof(Matrix));
1720 if (to->transform) *to->transform = *from->transform;
1722 //Copy style attribute
1723 *to->style = *from->style;
1724 if (from->style->fill.paint.url) to->style->fill.paint.url = new string(from->style->fill.paint.url->c_str());
1725 if (from->style->stroke.paint.url) to->style->stroke.paint.url = new string(from->style->stroke.paint.url->c_str());
1726 if (from->style->clipPath.url) to->style->clipPath.url = new string(from->style->clipPath.url->c_str());
1727 if (from->style->mask.url) to->style->mask.url = new string(from->style->mask.url->c_str());
1729 //Copy node attribute
1730 switch (from->type) {
1731 case SvgNodeType::Circle: {
1732 to->node.circle.cx = from->node.circle.cx;
1733 to->node.circle.cy = from->node.circle.cy;
1734 to->node.circle.r = from->node.circle.r;
1737 case SvgNodeType::Ellipse: {
1738 to->node.ellipse.cx = from->node.ellipse.cx;
1739 to->node.ellipse.cy = from->node.ellipse.cy;
1740 to->node.ellipse.rx = from->node.ellipse.rx;
1741 to->node.ellipse.ry = from->node.ellipse.ry;
1744 case SvgNodeType::Rect: {
1745 to->node.rect.x = from->node.rect.x;
1746 to->node.rect.y = from->node.rect.y;
1747 to->node.rect.w = from->node.rect.w;
1748 to->node.rect.h = from->node.rect.h;
1749 to->node.rect.rx = from->node.rect.rx;
1750 to->node.rect.ry = from->node.rect.ry;
1751 to->node.rect.hasRx = from->node.rect.hasRx;
1752 to->node.rect.hasRy = from->node.rect.hasRy;
1755 case SvgNodeType::Line: {
1756 to->node.line.x1 = from->node.line.x1;
1757 to->node.line.y1 = from->node.line.y1;
1758 to->node.line.x2 = from->node.line.x2;
1759 to->node.line.y2 = from->node.line.y2;
1762 case SvgNodeType::Path: {
1763 if (from->node.path.path) to->node.path.path = new string(from->node.path.path->c_str());
1766 case SvgNodeType::Polygon: {
1767 to->node.polygon.pointsCount = from->node.polygon.pointsCount;
1768 to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
1769 memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
1772 case SvgNodeType::Polyline: {
1773 to->node.polyline.pointsCount = from->node.polyline.pointsCount;
1774 to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
1775 memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
1778 case SvgNodeType::Image: {
1779 to->node.image.x = from->node.image.x;
1780 to->node.image.y = from->node.image.y;
1781 to->node.image.w = from->node.image.w;
1782 to->node.image.h = from->node.image.h;
1783 if (from->node.image.href) to->node.image.href = new string(from->node.image.href->c_str());
1793 static void _cloneNode(SvgNode* from, SvgNode* parent)
1796 if (!from || !parent) return;
1798 newNode = _createNode(parent, from->type);
1800 if (!newNode) return;
1802 _copyAttr(newNode, from);
1804 auto child = from->child.data;
1805 for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
1806 _cloneNode(*child, newNode);
1811 static void _postponeCloneNode(SvgLoaderData* loader, SvgNode *node, string* id) {
1812 SvgNodeIdPair nodeIdPair;
1813 nodeIdPair.node = node;
1815 loader->cloneNodes.push(nodeIdPair);
1819 static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes) {
1820 for (uint32_t i = 0; i < cloneNodes->count; ++i) {
1821 SvgNodeIdPair nodeIdPair = cloneNodes->data[i];
1822 SvgNode *defs = _getDefsNode(nodeIdPair.node);
1823 SvgNode *nodeFrom = _findChildById(defs, nodeIdPair.id->c_str());
1824 _cloneNode(nodeFrom, nodeIdPair.node);
1825 delete nodeIdPair.id;
1830 static bool _attrParseUseNode(void* data, const char* key, const char* value)
1832 SvgLoaderData* loader = (SvgLoaderData*)data;
1833 SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
1836 if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
1837 id = _idFromHref(value);
1838 defs = _getDefsNode(node);
1839 nodeFrom = _findChildById(defs, id->c_str());
1841 _cloneNode(nodeFrom, node);
1844 //some svg export software include <defs> element at the end of the file
1845 //if so the 'from' element won't be found now and we have to repeat finding
1846 //after the whole file is parsed
1847 _postponeCloneNode(loader, node, id);
1849 } else if (!strcmp(key, "clip-path")) {
1850 _handleClipPathAttr(loader, node, value);
1851 } else if (!strcmp(key, "mask")) {
1852 _handleMaskAttr(loader, node, value);
1854 return _attrParseGNode(data, key, value);
1860 static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1862 loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
1864 if (!loader->svgParse->node) return nullptr;
1866 simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
1867 return loader->svgParse->node;
1870 //TODO: Implement 'text' primitive
1871 static constexpr struct
1875 FactoryMethod tagHandler;
1876 } graphicsTags[] = {
1877 {"use", sizeof("use"), _createUseNode},
1878 {"circle", sizeof("circle"), _createCircleNode},
1879 {"ellipse", sizeof("ellipse"), _createEllipseNode},
1880 {"path", sizeof("path"), _createPathNode},
1881 {"polygon", sizeof("polygon"), _createPolygonNode},
1882 {"rect", sizeof("rect"), _createRectNode},
1883 {"polyline", sizeof("polyline"), _createPolylineNode},
1884 {"line", sizeof("line"), _createLineNode},
1885 {"image", sizeof("image"), _createImageNode}
1889 static constexpr struct
1893 FactoryMethod tagHandler;
1895 {"defs", sizeof("defs"), _createDefsNode},
1896 {"g", sizeof("g"), _createGNode},
1897 {"svg", sizeof("svg"), _createSvgNode},
1898 {"mask", sizeof("mask"), _createMaskNode},
1899 {"clipPath", sizeof("clipPath"), _createClipPathNode}
1903 #define FIND_FACTORY(Short_Name, Tags_Array) \
1904 static FactoryMethod \
1905 _find##Short_Name##Factory(const char* name) \
1908 int sz = strlen(name); \
1910 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
1911 if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
1912 return Tags_Array[i].tagHandler; \
1918 FIND_FACTORY(Group, groupTags)
1919 FIND_FACTORY(Graphics, graphicsTags)
1922 FillSpread _parseSpreadValue(const char* value)
1924 auto spread = FillSpread::Pad;
1926 if (!strcmp(value, "reflect")) {
1927 spread = FillSpread::Reflect;
1928 } else if (!strcmp(value, "repeat")) {
1929 spread = FillSpread::Repeat;
1936 static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1938 radial->cx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1939 if (!loader->svgParse->gradient.parsedFx) radial->fx = radial->cx;
1943 static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1945 radial->cy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1946 if (!loader->svgParse->gradient.parsedFy) radial->fy = radial->cy;
1950 static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1952 radial->fx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1953 loader->svgParse->gradient.parsedFx = true;
1957 static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1959 radial->fy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1960 loader->svgParse->gradient.parsedFy = true;
1964 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1966 radial->r = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Other);
1970 static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1972 if (!userSpace) radial->cx = radial->cx * loader->svgParse->global.w;
1976 static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1978 if (!userSpace) radial->cy = radial->cy * loader->svgParse->global.h;
1982 static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1984 if (!userSpace) radial->fx = radial->fx * loader->svgParse->global.w;
1988 static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1990 if (!userSpace) radial->fy = radial->fy * loader->svgParse->global.h;
1994 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1996 if (!userSpace) radial->r = radial->r * (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0));
2000 typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
2001 typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
2004 #define RADIAL_DEF(Name, Name1) \
2006 #Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
2010 static constexpr struct
2014 radialMethod tagHandler;
2015 radialMethodRecalc tagRecalc;
2025 static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
2027 SvgLoaderData* loader = (SvgLoaderData*)data;
2028 SvgStyleGradient* grad = loader->svgParse->styleGrad;
2029 SvgRadialGradient* radial = grad->radial;
2030 int sz = strlen(key);
2032 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
2033 if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
2034 radialTags[i].tagHandler(loader, radial, value);
2039 if (!strcmp(key, "id")) {
2040 grad->id = _copyId(value);
2041 } else if (!strcmp(key, "spreadMethod")) {
2042 grad->spread = _parseSpreadValue(value);
2043 } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
2044 grad->ref = _idFromHref(value);
2045 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
2046 grad->userSpace = true;
2047 } else if (!strcmp(key, "gradientTransform")) {
2048 grad->transform = _parseTransformationMatrix(value);
2057 static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2059 auto grad = new SvgStyleGradient;
2060 if (!grad) return nullptr;
2062 loader->svgParse->styleGrad = grad;
2064 grad->type = SvgGradientType::Radial;
2065 grad->userSpace = false;
2066 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
2067 if (!grad->radial) {
2072 * Default values of gradient transformed into global percentage
2074 grad->radial->cx = 0.5f / loader->svgParse->global.w;
2075 grad->radial->cy = 0.5f / loader->svgParse->global.h;
2076 grad->radial->fx = 0.5f / loader->svgParse->global.w;
2077 grad->radial->fy = 0.5f / loader->svgParse->global.h;
2078 grad->radial->r = 0.5f / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0f));
2080 loader->svgParse->gradient.parsedFx = false;
2081 loader->svgParse->gradient.parsedFy = false;
2082 simpleXmlParseAttributes(buf, bufLength,
2083 _attrParseRadialGradientNode, loader);
2085 for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
2086 radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
2089 return loader->svgParse->styleGrad;
2093 static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
2095 SvgLoaderData* loader = (SvgLoaderData*)data;
2096 auto stop = &loader->svgParse->gradStop;
2098 if (!strcmp(key, "stop-opacity")) {
2099 stop->a = _toOpacity(value);
2100 loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
2101 } else if (!strcmp(key, "stop-color")) {
2102 _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
2103 loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
2112 static bool _attrParseStops(void* data, const char* key, const char* value)
2114 SvgLoaderData* loader = (SvgLoaderData*)data;
2115 auto stop = &loader->svgParse->gradStop;
2117 if (!strcmp(key, "offset")) {
2118 stop->offset = _toOffset(value);
2119 } else if (!strcmp(key, "stop-opacity")) {
2120 if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
2121 stop->a = _toOpacity(value);
2123 } else if (!strcmp(key, "stop-color")) {
2124 if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
2125 _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
2127 } else if (!strcmp(key, "style")) {
2128 simpleXmlParseW3CAttribute(value, _attrParseStopsStyle, data);
2137 static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2139 linear->x1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
2143 static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2145 linear->y1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
2149 static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2151 linear->x2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
2155 static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
2157 linear->y2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
2161 static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2163 if (!userSpace) linear->x1 = linear->x1 * loader->svgParse->global.w;
2167 static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2169 if (!userSpace) linear->y1 = linear->y1 * loader->svgParse->global.h;
2173 static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2175 if (!userSpace) linear->x2 = linear->x2 * loader->svgParse->global.w;
2179 static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
2181 if (!userSpace) linear->y2 = linear->y2 * loader->svgParse->global.h;
2185 typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
2186 typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
2189 #define LINEAR_DEF(Name, Name1) \
2191 #Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
2195 static constexpr struct
2199 Linear_Method tagHandler;
2200 Linear_Method_Recalc tagRecalc;
2209 static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
2211 SvgLoaderData* loader = (SvgLoaderData*)data;
2212 SvgStyleGradient* grad = loader->svgParse->styleGrad;
2213 SvgLinearGradient* linear = grad->linear;
2214 int sz = strlen(key);
2216 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2217 if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
2218 linear_tags[i].tagHandler(loader, linear, value);
2223 if (!strcmp(key, "id")) {
2224 grad->id = _copyId(value);
2225 } else if (!strcmp(key, "spreadMethod")) {
2226 grad->spread = _parseSpreadValue(value);
2227 } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
2228 grad->ref = _idFromHref(value);
2229 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
2230 grad->userSpace = true;
2231 } else if (!strcmp(key, "gradientTransform")) {
2232 grad->transform = _parseTransformationMatrix(value);
2241 static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
2243 auto grad = new SvgStyleGradient;
2244 if (!grad) return nullptr;
2246 loader->svgParse->styleGrad = grad;
2248 grad->type = SvgGradientType::Linear;
2249 grad->userSpace = false;
2250 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
2251 if (!grad->linear) {
2256 * Default value of x2 is 100% - transformed to the global percentage
2258 grad->linear->x2 = 1.0f / loader->svgParse->global.w;
2259 simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
2261 for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
2262 linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
2265 return loader->svgParse->styleGrad;
2269 #define GRADIENT_DEF(Name, Name1) \
2271 #Name, sizeof(#Name), _create##Name1 \
2276 * For all Gradients lengths would be calculated into percentages related to
2277 * canvas width and height.
2279 * if user then recalculate actual pixels into percentages
2281 static constexpr struct
2285 GradientFactoryMethod tagHandler;
2286 } gradientTags[] = {
2287 GRADIENT_DEF(linearGradient, LinearGradient),
2288 GRADIENT_DEF(radialGradient, RadialGradient)
2292 static GradientFactoryMethod _findGradientFactory(const char* name)
2294 int sz = strlen(name);
2296 for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
2297 if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
2298 return gradientTags[i].tagHandler;
2305 static constexpr struct
2311 {"svg", sizeof("svg")},
2312 {"defs", sizeof("defs")},
2313 {"mask", sizeof("mask")},
2314 {"clipPath", sizeof("clipPath")}
2318 static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content)
2320 content = _skipSpace(content, nullptr);
2322 for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
2323 if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
2324 loader->stack.pop();
2333 static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
2335 const char* attrs = nullptr;
2336 int attrsLength = 0;
2338 char tagName[20] = "";
2339 FactoryMethod method;
2340 GradientFactoryMethod gradientMethod;
2341 SvgNode *node = nullptr, *parent = nullptr;
2343 attrs = simpleXmlFindAttributesTag(content, length);
2346 //Parse the empty tag
2348 while ((attrs != nullptr) && *attrs != '>') attrs++;
2352 //Find out the tag name starting from content till sz length
2353 sz = attrs - content;
2354 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2355 if ((unsigned)sz >= sizeof(tagName)) return;
2356 strncpy(tagName, content, sz);
2358 attrsLength = length - sz;
2361 if ((method = _findGroupFactory(tagName))) {
2364 if (strcmp(tagName, "svg")) return; //Not a valid svg document
2365 node = method(loader, nullptr, attrs, attrsLength);
2368 if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
2369 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2370 else parent = loader->doc;
2371 node = method(loader, parent, attrs, attrsLength);
2375 if (node->type != SvgNodeType::Defs || !empty) {
2376 loader->stack.push(node);
2378 } else if ((method = _findGraphicsFactory(tagName))) {
2379 if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
2380 else parent = loader->doc;
2381 node = method(loader, parent, attrs, attrsLength);
2382 } else if ((gradientMethod = _findGradientFactory(tagName))) {
2383 SvgStyleGradient* gradient;
2384 gradient = gradientMethod(loader, attrs, attrsLength);
2385 //FIXME: The current parsing structure does not distinguish end tags.
2386 // There is no way to know if the currently parsed gradient is in defs.
2387 // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
2388 // But finally, the loader has a gradient style list regardless of defs.
2389 // This is only to support this when multiple gradients are declared, even if no defs are declared.
2390 // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
2391 if (loader->def && loader->doc->node.doc.defs) {
2392 loader->def->node.defs.gradients.push(gradient);
2394 loader->gradients.push(gradient);
2396 loader->latestGradient = gradient;
2397 } else if (!strcmp(tagName, "stop")) {
2398 if (!loader->latestGradient) {
2399 TVGLOG("SVG", "Stop element is used outside of the Gradient element");
2402 /* default value for opacity */
2403 loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
2404 simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
2405 loader->latestGradient->stops.push(loader->svgParse->gradStop);
2406 } else if (!isIgnoreUnsupportedLogElements(tagName)) {
2407 TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
2412 static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
2414 SvgLoaderData* loader = (SvgLoaderData*)data;
2417 case SimpleXMLType::Open: {
2418 _svgLoaderParserXmlOpen(loader, content, length, false);
2421 case SimpleXMLType::OpenEmpty: {
2422 _svgLoaderParserXmlOpen(loader, content, length, true);
2425 case SimpleXMLType::Close: {
2426 _svgLoaderParerXmlClose(loader, content);
2429 case SimpleXMLType::Data:
2430 case SimpleXMLType::CData:
2431 case SimpleXMLType::DoctypeChild: {
2434 case SimpleXMLType::Ignored:
2435 case SimpleXMLType::Comment:
2436 case SimpleXMLType::Doctype: {
2448 static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
2450 if (parent == nullptr) return;
2451 //Inherit the property of parent if not present in child.
2453 if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
2454 child->fill.paint.color = parent->fill.paint.color;
2455 child->fill.paint.none = parent->fill.paint.none;
2456 child->fill.paint.curColor = parent->fill.paint.curColor;
2457 if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url->c_str());
2458 } else if (child->fill.paint.curColor && !child->curColorSet) {
2459 child->color = parent->color;
2461 if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
2462 child->fill.opacity = parent->fill.opacity;
2464 if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
2465 child->fill.fillRule = parent->fill.fillRule;
2468 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
2469 child->stroke.paint.color = parent->stroke.paint.color;
2470 child->stroke.paint.none = parent->stroke.paint.none;
2471 child->stroke.paint.curColor = parent->stroke.paint.curColor;
2472 child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url->c_str()) : nullptr;
2473 } else if (child->stroke.paint.curColor && !child->curColorSet) {
2474 child->color = parent->color;
2476 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
2477 child->stroke.opacity = parent->stroke.opacity;
2479 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
2480 child->stroke.width = parent->stroke.width;
2482 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
2483 if (parent->stroke.dash.array.count > 0) {
2484 child->stroke.dash.array.clear();
2485 child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
2486 for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
2487 child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
2491 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
2492 child->stroke.cap = parent->stroke.cap;
2494 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
2495 child->stroke.join = parent->stroke.join;
2500 static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node){
2501 #ifdef THORVG_LOG_ENABLED
2502 auto type = simpleXmlNodeTypeToString(node->type);
2504 if (!node->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
2505 if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
2506 if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
2508 switch (node->type) {
2509 case SvgNodeType::Path: {
2510 if (!node->node.path.path || node->node.path.path->empty()) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
2513 case SvgNodeType::Ellipse: {
2514 if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
2517 case SvgNodeType::Polygon:
2518 case SvgNodeType::Polyline: {
2519 if (node->node.polygon.pointsCount < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
2522 case SvgNodeType::Circle: {
2523 if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
2526 case SvgNodeType::Rect: {
2527 if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
2530 case SvgNodeType::Line: {
2531 if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
2540 static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
2542 _styleInherit(node->style, parentStyle);
2543 _inefficientNodeCheck(node);
2545 auto child = node->child.data;
2546 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2547 _updateStyle(*child, node->style);
2552 static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const string* id)
2554 SvgStyleGradient* result = nullptr;
2556 auto gradList = gradients->data;
2558 for (uint32_t i = 0; i < gradients->count; ++i) {
2559 if (!((*gradList)->id->compare(*id))) {
2560 result = _cloneGradient(*gradList);
2566 if (result && result->ref) {
2567 gradList = gradients->data;
2568 for (uint32_t i = 0; i < gradients->count; ++i) {
2569 if (!((*gradList)->id->compare(*result->ref))) {
2570 if (result->stops.count == 0) {
2571 _cloneGradStops(result->stops, (*gradList)->stops);
2573 //TODO: Properly inherit other property
2584 static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
2586 if (node->child.count > 0) {
2587 auto child = node->child.data;
2588 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2589 _updateGradient(*child, gradients);
2592 if (node->style->fill.paint.url) {
2593 if (node->style->fill.paint.gradient) delete(node->style->fill.paint.gradient);
2594 node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
2596 if (node->style->stroke.paint.url) {
2597 if (node->style->stroke.paint.gradient) delete(node->style->stroke.paint.gradient);
2598 node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
2604 static void _updateComposite(SvgNode* node, SvgNode* root)
2606 if (node->style->clipPath.url && !node->style->clipPath.node) {
2607 SvgNode *findResult = _findNodeById(root, node->style->clipPath.url);
2608 if (findResult) node->style->clipPath.node = findResult;
2610 if (node->style->mask.url && !node->style->mask.node) {
2611 SvgNode *findResult = _findNodeById(root, node->style->mask.url);
2612 if (findResult) node->style->mask.node = findResult;
2614 if (node->child.count > 0) {
2615 auto child = node->child.data;
2616 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2617 _updateComposite(*child, root);
2623 static void _freeNodeStyle(SvgStyleProperty* style)
2627 //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
2628 delete(style->clipPath.url);
2629 delete(style->mask.url);
2631 if (style->fill.paint.gradient) delete(style->fill.paint.gradient);
2632 if (style->stroke.paint.gradient) delete(style->stroke.paint.gradient);
2633 delete(style->fill.paint.url);
2634 delete(style->stroke.paint.url);
2635 style->stroke.dash.array.reset();
2640 static void _freeNode(SvgNode* node)
2644 auto child = node->child.data;
2645 for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
2648 node->child.reset();
2651 free(node->transform);
2652 _freeNodeStyle(node->style);
2653 switch (node->type) {
2654 case SvgNodeType::Path: {
2655 delete node->node.path.path;
2658 case SvgNodeType::Polygon: {
2659 free(node->node.polygon.points);
2662 case SvgNodeType::Polyline: {
2663 free(node->node.polyline.points);
2666 case SvgNodeType::Doc: {
2667 _freeNode(node->node.doc.defs);
2670 case SvgNodeType::Defs: {
2671 auto gradients = node->node.defs.gradients.data;
2672 for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
2676 node->node.defs.gradients.reset();
2679 case SvgNodeType::Image: {
2680 delete node->node.image.href;
2691 static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
2693 const char* attrs = nullptr;
2695 char tagName[20] = "";
2696 FactoryMethod method;
2697 SvgNode *node = nullptr;
2698 int attrsLength = 0;
2700 attrs = simpleXmlFindAttributesTag(content, length);
2703 //Parse the empty tag
2705 while ((attrs != nullptr) && *attrs != '>') attrs++;
2709 sz = attrs - content;
2710 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
2711 if ((unsigned)sz >= sizeof(tagName)) return false;
2712 strncpy(tagName, content, sz);
2714 attrsLength = length - sz;
2717 if ((method = _findGroupFactory(tagName))) {
2719 if (strcmp(tagName, "svg")) return true; //Not a valid svg document
2720 node = method(loader, nullptr, attrs, attrsLength);
2722 loader->stack.push(node);
2730 static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
2732 SvgLoaderData* loader = (SvgLoaderData*)data;
2736 case SimpleXMLType::Open:
2737 case SimpleXMLType::OpenEmpty: {
2738 //If 'res' is false, it means <svg> tag is found.
2739 res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
2751 void SvgLoader::clear()
2753 if (copy) free((char*)content);
2760 /************************************************************************/
2761 /* External Class Implementation */
2762 /************************************************************************/
2764 SvgLoader::SvgLoader()
2769 SvgLoader::~SvgLoader()
2775 void SvgLoader::run(unsigned tid)
2777 if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
2779 if (loaderData.doc) {
2780 _updateStyle(loaderData.doc, nullptr);
2781 auto defs = loaderData.doc->node.doc.defs;
2782 if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
2784 if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
2786 _updateComposite(loaderData.doc, loaderData.doc);
2787 if (defs) _updateComposite(loaderData.doc, defs);
2789 if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes);
2791 root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
2795 bool SvgLoader::header()
2797 //For valid check, only <svg> tag is parsed first.
2798 //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
2799 //After that, the remaining content data is parsed in order with async.
2800 loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
2801 if (!loaderData.svgParse) return false;
2803 loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
2805 simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
2807 if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
2808 //Return the brief resource info such as viewbox:
2809 vx = loaderData.doc->node.doc.vx;
2810 vy = loaderData.doc->node.doc.vy;
2811 w = vw = loaderData.doc->node.doc.vw;
2812 h = vh = loaderData.doc->node.doc.vh;
2815 if (loaderData.doc->node.doc.w > 0) {
2816 w = loaderData.doc->node.doc.w;
2817 if (vw < FLT_EPSILON) vw = w;
2819 if (loaderData.doc->node.doc.h > 0) {
2820 h = loaderData.doc->node.doc.h;
2821 if (vh < FLT_EPSILON) vh = h;
2824 preserveAspect = loaderData.doc->node.doc.preserveAspect;
2826 TVGLOG("SVG", "No SVG File. There is no <svg/>");
2834 bool SvgLoader::open(const char* data, uint32_t size, bool copy)
2839 content = (char*)malloc(size);
2840 if (!content) return false;
2841 memcpy((char*)content, data, size);
2842 } else content = data;
2851 bool SvgLoader::open(const string& path)
2858 if (!f.is_open()) return false;
2861 getline(f, filePath, '\0');
2864 if (filePath.empty()) return false;
2866 content = filePath.c_str();
2867 size = filePath.size();
2873 bool SvgLoader::resize(Paint* paint, float w, float h)
2875 if (!paint) return false;
2877 auto sx = w / this->w;
2878 auto sy = h / this->h;
2880 if (preserveAspect) {
2882 auto scale = sx < sy ? sx : sy;
2883 paint->scale(scale);
2887 auto tw = this->w * scale;
2888 auto th = this->h * scale;
2889 if (tw > th) ty -= (h - th) * 0.5f;
2890 else tx -= (w - tw) * 0.5f;
2891 paint->translate(-tx, -ty);
2896 auto tw = this->w * sx;
2897 auto th = this->h * sy;
2898 if (tw > th) ty -= (h - th) * 0.5f;
2899 else tx -= (w - tw) * 0.5f;
2901 Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1};
2902 paint->transform(m);
2908 bool SvgLoader::read()
2910 if (!content || size == 0) return false;
2912 TaskScheduler::request(this);
2918 bool SvgLoader::close()
2922 if (loaderData.svgParse) {
2923 free(loaderData.svgParse);
2924 loaderData.svgParse = nullptr;
2926 auto gradients = loaderData.gradients.data;
2927 for (size_t i = 0; i < loaderData.gradients.count; ++i) {
2931 loaderData.gradients.reset();
2933 _freeNode(loaderData.doc);
2934 loaderData.doc = nullptr;
2935 loaderData.stack.reset();
2943 unique_ptr<Paint> SvgLoader::paint()
2946 if (root) return move(root);
2947 else return nullptr;