2 * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #ifndef _TVG_SVG_LOADER_CPP_
18 #define _TVG_SVG_LOADER_CPP_
20 #include "tvgSvgLoader.h"
24 /************************************************************************/
25 /* Internal Class Implementation */
26 /************************************************************************/
28 typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
29 typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
32 static char* _skipSpace(const char* str, const char* end)
34 while (((end != nullptr && str < end) || (end == nullptr && *str != '\0')) && isspace(*str))
40 static string* _copyId(const char* str)
42 if (str == nullptr) return nullptr;
44 return new string(str);
48 static const char* _skipComma(const char* content)
50 content = _skipSpace(content, nullptr);
51 if (*content == ',') return content + 1;
56 static bool _parseNumber(const char** content, float* number)
60 *number = strtof(*content, &end);
61 //If the start of string is not number
62 if ((*content) == end) return false;
64 *content = _skipComma(end);
69 * According to https://www.w3.org/TR/SVG/coords.html#Units
72 * Since this documentation is not obvious, more clean recalculation with dpi
73 * is required, but for now default w3 constants would be used
75 static float _toFloat(SvgParser* svgParse, const char* str, SvgParserLengthType type)
77 float parsedValue = strtof(str, nullptr);
79 if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
80 else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
81 else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
82 else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
83 else if (strstr(str, "in")) parsedValue = parsedValue * 90;
84 else if (strstr(str, "%")) {
85 if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
86 else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
87 else //if other then it's radius
89 float max = svgParse->global.w;
90 if (max < svgParse->global.h)
91 max = svgParse->global.h;
92 parsedValue = (parsedValue / 100.0) * max;
96 //TODO: Implement 'em', 'ex' attributes
102 static float _gradientToFloat(SvgParser* svgParse, const char* str, SvgParserLengthType type)
106 float parsedValue = strtof(str, &end);
110 * That is according to Units in here
112 * https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
114 if (type == SvgParserLengthType::Vertical) max = svgParse->global.h;
115 else if (type == SvgParserLengthType::Horizontal) max = svgParse->global.w;
116 else if (type == SvgParserLengthType::Other) max = sqrt(pow(svgParse->global.h, 2) + pow(svgParse->global.w, 2)) / sqrt(2.0);
118 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
119 else 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 //TODO: Implement 'em', 'ex' attributes
126 //Transform into global percentage
127 parsedValue = parsedValue / max;
133 static float _toOffset(const char* str)
137 float parsedValue = strtof(str, &end);
139 if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
145 static int _toOpacity(const char* str)
149 float opacity = strtof(str, &end);
151 if (end && (*end == '\0')) a = lrint(opacity * 255);
156 #define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
157 static Type _to##Name1(const char* str) \
161 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
162 if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
168 /* parse the line cap used during stroking a path.
169 * Value: butt | round | square | inherit
171 * https://www.w3.org/TR/SVG/painting.html
173 static constexpr struct
178 { StrokeCap::Butt, "butt" },
179 { StrokeCap::Round, "round" },
180 { StrokeCap::Square, "square" }
184 _PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt);
187 /* parse the line join used during stroking a path.
188 * Value: miter | round | bevel | inherit
190 * https://www.w3.org/TR/SVG/painting.html
192 static constexpr struct
197 { StrokeJoin::Miter, "miter" },
198 { StrokeJoin::Round, "round" },
199 { StrokeJoin::Bevel, "bevel" }
203 _PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter);
206 /* parse the fill rule used during filling a path.
207 * Value: nonzero | evenodd | inherit
209 * https://www.w3.org/TR/SVG/painting.html
211 static constexpr struct
213 SvgFillRule fillRule;
216 { SvgFillRule::OddEven, "evenodd" }
220 _PARSE_TAG(SvgFillRule, fillRule, FillRule, fillRuleTags, SvgFillRule::Winding);
223 static string* _idFromUrl(const char* url)
228 url = _skipSpace(url, nullptr);
231 url = _skipSpace(url, nullptr);
234 if ((*url) == '#') ++url;
236 while ((*url) != ')') {
242 return new string(tmp);
246 static unsigned char _parserColor(const char* value, char** end)
250 r = strtof(value + 4, end);
251 *end = _skipSpace(*end, nullptr);
252 if (**end == '%') r = 255 * r / 100;
253 *end = _skipSpace(*end, nullptr);
255 if (r < 0 || r > 255) {
264 static constexpr struct
269 { "aliceblue", 0xfff0f8ff },
270 { "antiquewhite", 0xfffaebd7 },
271 { "aqua", 0xff00ffff },
272 { "aquamarine", 0xff7fffd4 },
273 { "azure", 0xfff0ffff },
274 { "beige", 0xfff5f5dc },
275 { "bisque", 0xffffe4c4 },
276 { "black", 0xff000000 },
277 { "blanchedalmond", 0xffffebcd },
278 { "blue", 0xff0000ff },
279 { "blueviolet", 0xff8a2be2 },
280 { "brown", 0xffa52a2a },
281 { "burlywood", 0xffdeb887 },
282 { "cadetblue", 0xff5f9ea0 },
283 { "chartreuse", 0xff7fff00 },
284 { "chocolate", 0xffd2691e },
285 { "coral", 0xffff7f50 },
286 { "cornflowerblue", 0xff6495ed },
287 { "cornsilk", 0xfffff8dc },
288 { "crimson", 0xffdc143c },
289 { "cyan", 0xff00ffff },
290 { "darkblue", 0xff00008b },
291 { "darkcyan", 0xff008b8b },
292 { "darkgoldenrod", 0xffb8860b },
293 { "darkgray", 0xffa9a9a9 },
294 { "darkgrey", 0xffa9a9a9 },
295 { "darkgreen", 0xff006400 },
296 { "darkkhaki", 0xffbdb76b },
297 { "darkmagenta", 0xff8b008b },
298 { "darkolivegreen", 0xff556b2f },
299 { "darkorange", 0xffff8c00 },
300 { "darkorchid", 0xff9932cc },
301 { "darkred", 0xff8b0000 },
302 { "darksalmon", 0xffe9967a },
303 { "darkseagreen", 0xff8fbc8f },
304 { "darkslateblue", 0xff483d8b },
305 { "darkslategray", 0xff2f4f4f },
306 { "darkslategrey", 0xff2f4f4f },
307 { "darkturquoise", 0xff00ced1 },
308 { "darkviolet", 0xff9400d3 },
309 { "deeppink", 0xffff1493 },
310 { "deepskyblue", 0xff00bfff },
311 { "dimgray", 0xff696969 },
312 { "dimgrey", 0xff696969 },
313 { "dodgerblue", 0xff1e90ff },
314 { "firebrick", 0xffb22222 },
315 { "floralwhite", 0xfffffaf0 },
316 { "forestgreen", 0xff228b22 },
317 { "fuchsia", 0xffff00ff },
318 { "gainsboro", 0xffdcdcdc },
319 { "ghostwhite", 0xfff8f8ff },
320 { "gold", 0xffffd700 },
321 { "goldenrod", 0xffdaa520 },
322 { "gray", 0xff808080 },
323 { "grey", 0xff808080 },
324 { "green", 0xff008000 },
325 { "greenyellow", 0xffadff2f },
326 { "honeydew", 0xfff0fff0 },
327 { "hotpink", 0xffff69b4 },
328 { "indianred", 0xffcd5c5c },
329 { "indigo", 0xff4b0082 },
330 { "ivory", 0xfffffff0 },
331 { "khaki", 0xfff0e68c },
332 { "lavender", 0xffe6e6fa },
333 { "lavenderblush", 0xfffff0f5 },
334 { "lawngreen", 0xff7cfc00 },
335 { "lemonchiffon", 0xfffffacd },
336 { "lightblue", 0xffadd8e6 },
337 { "lightcoral", 0xfff08080 },
338 { "lightcyan", 0xffe0ffff },
339 { "lightgoldenrodyellow", 0xfffafad2 },
340 { "lightgray", 0xffd3d3d3 },
341 { "lightgrey", 0xffd3d3d3 },
342 { "lightgreen", 0xff90ee90 },
343 { "lightpink", 0xffffb6c1 },
344 { "lightsalmon", 0xffffa07a },
345 { "lightseagreen", 0xff20b2aa },
346 { "lightskyblue", 0xff87cefa },
347 { "lightslategray", 0xff778899 },
348 { "lightslategrey", 0xff778899 },
349 { "lightsteelblue", 0xffb0c4de },
350 { "lightyellow", 0xffffffe0 },
351 { "lime", 0xff00ff00 },
352 { "limegreen", 0xff32cd32 },
353 { "linen", 0xfffaf0e6 },
354 { "magenta", 0xffff00ff },
355 { "maroon", 0xff800000 },
356 { "mediumaquamarine", 0xff66cdaa },
357 { "mediumblue", 0xff0000cd },
358 { "mediumorchid", 0xffba55d3 },
359 { "mediumpurple", 0xff9370d8 },
360 { "mediumseagreen", 0xff3cb371 },
361 { "mediumslateblue", 0xff7b68ee },
362 { "mediumspringgreen", 0xff00fa9a },
363 { "mediumturquoise", 0xff48d1cc },
364 { "mediumvioletred", 0xffc71585 },
365 { "midnightblue", 0xff191970 },
366 { "mintcream", 0xfff5fffa },
367 { "mistyrose", 0xffffe4e1 },
368 { "moccasin", 0xffffe4b5 },
369 { "navajowhite", 0xffffdead },
370 { "navy", 0xff000080 },
371 { "oldlace", 0xfffdf5e6 },
372 { "olive", 0xff808000 },
373 { "olivedrab", 0xff6b8e23 },
374 { "orange", 0xffffa500 },
375 { "orangered", 0xffff4500 },
376 { "orchid", 0xffda70d6 },
377 { "palegoldenrod", 0xffeee8aa },
378 { "palegreen", 0xff98fb98 },
379 { "paleturquoise", 0xffafeeee },
380 { "palevioletred", 0xffd87093 },
381 { "papayawhip", 0xffffefd5 },
382 { "peachpuff", 0xffffdab9 },
383 { "peru", 0xffcd853f },
384 { "pink", 0xffffc0cb },
385 { "plum", 0xffdda0dd },
386 { "powderblue", 0xffb0e0e6 },
387 { "purple", 0xff800080 },
388 { "red", 0xffff0000 },
389 { "rosybrown", 0xffbc8f8f },
390 { "royalblue", 0xff4169e1 },
391 { "saddlebrown", 0xff8b4513 },
392 { "salmon", 0xfffa8072 },
393 { "sandybrown", 0xfff4a460 },
394 { "seagreen", 0xff2e8b57 },
395 { "seashell", 0xfffff5ee },
396 { "sienna", 0xffa0522d },
397 { "silver", 0xffc0c0c0 },
398 { "skyblue", 0xff87ceeb },
399 { "slateblue", 0xff6a5acd },
400 { "slategray", 0xff708090 },
401 { "slategrey", 0xff708090 },
402 { "snow", 0xfffffafa },
403 { "springgreen", 0xff00ff7f },
404 { "steelblue", 0xff4682b4 },
405 { "tan", 0xffd2b48c },
406 { "teal", 0xff008080 },
407 { "thistle", 0xffd8bfd8 },
408 { "tomato", 0xffff6347 },
409 { "turquoise", 0xff40e0d0 },
410 { "violet", 0xffee82ee },
411 { "wheat", 0xfff5deb3 },
412 { "white", 0xffffffff },
413 { "whitesmoke", 0xfff5f5f5 },
414 { "yellow", 0xffffff00 },
415 { "yellowgreen", 0xff9acd32 }
419 static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, string** ref)
421 unsigned int i, len = strlen(str);
422 char *red, *green, *blue;
423 unsigned char tr, tg, tb;
425 if (len == 4 && str[0] == '#') {
426 //Case for "#456" should be interprete as "#445566"
427 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
428 char tmp[3] = { '\0', '\0', '\0' };
431 *r = strtol(tmp, nullptr, 16);
434 *g = strtol(tmp, nullptr, 16);
437 *b = strtol(tmp, nullptr, 16);
439 } else if (len == 7 && str[0] == '#') {
440 if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
441 char tmp[3] = { '\0', '\0', '\0' };
444 *r = strtol(tmp, nullptr, 16);
447 *g = strtol(tmp, nullptr, 16);
450 *b = strtol(tmp, nullptr, 16);
452 } 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] == ')') {
453 tr = _parserColor(str + 4, &red);
454 if (red && *red == ',') {
455 tg = _parserColor(red + 1, &green);
456 if (green && *green == ',') {
457 tb = _parserColor(green + 1, &blue);
458 if (blue && blue[0] == ')' && blue[1] == '\0') {
465 } else if (len >= 3 && !strncmp(str, "url", 3)) {
466 *ref = _idFromUrl((const char*)(str + 3));
469 for (i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
470 if (!strcasecmp(colors[i].name, str)) {
471 *r = (((uint8_t*)(&(colors[i].value)))[2]);
472 *g = (((uint8_t*)(&(colors[i].value)))[1]);
473 *b = (((uint8_t*)(&(colors[i].value)))[0]);
480 static char* _parseNumbersArray(char* str, float* points, int* ptCount)
485 str = _skipSpace(str, nullptr);
486 while (isdigit(*str) || *str == '-' || *str == '+' || *str == '.') {
487 points[count++] = strtof(str, &end);
489 str = _skipSpace(str, nullptr);
490 if (*str == ',') ++str;
491 //Eat the rest of space
492 str = _skipSpace(str, nullptr);
499 enum class MatrixState {
510 #define MATRIX_DEF(Name, Value) \
512 #Name, sizeof(#Name), Value \
516 static constexpr struct
522 MATRIX_DEF(matrix, MatrixState::Matrix),
523 MATRIX_DEF(translate, MatrixState::Translate),
524 MATRIX_DEF(rotate, MatrixState::Rotate),
525 MATRIX_DEF(scale, MatrixState::Scale),
526 MATRIX_DEF(skewX, MatrixState::SkewX),
527 MATRIX_DEF(skewY, MatrixState::SkewY)
531 static void _matrixCompose(const Matrix* m1,
535 float a11, a12, a13, a21, a22, a23, a31, a32, a33;
537 a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
538 a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
539 a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
541 a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
542 a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
543 a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
545 a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
546 a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
547 a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
561 /* parse transform attribute
562 * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
564 static Matrix* _parseTransformationMatrix(const char* value)
570 MatrixState state = MatrixState::Unknown;
571 Matrix* matrix = (Matrix*)calloc(1, sizeof(Matrix));
572 char* str = (char*)value;
573 char* end = str + strlen(str);
575 *matrix = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
577 if (isspace(*str) || (*str == ',')) {
581 for (i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
582 if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
583 state = matrixTags[i].state;
584 str += (matrixTags[i].sz - 1);
587 if (state == MatrixState::Unknown) goto error;
589 str = _skipSpace(str, end);
590 if (*str != '(') goto error;
592 str = _parseNumbersArray(str, points, &ptCount);
593 if (*str != ')') goto error;
596 if (state == MatrixState::Matrix) {
599 if (ptCount != 6) goto error;
601 tmp = { points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1 };
602 _matrixCompose(matrix, &tmp, matrix);
603 } else if (state == MatrixState::Translate) {
607 tmp = { 1, 0, points[0], 0, 1, 0, 0, 0, 1 };
608 _matrixCompose(matrix, &tmp, matrix);
609 } else if (ptCount == 2) {
610 tmp = { 1, 0, points[0], 0, 1, points[1], 0, 0, 1 };
611 _matrixCompose(matrix, &tmp, matrix);
613 } else if (state == MatrixState::Rotate) {
616 //Transform to signed.
617 points[0] = fmod(points[0], 360);
618 if (points[0] < 0) points[0] += 360;
620 c = cosf(points[0] * (M_PI / 180.0));
621 s = sinf(points[0] * (M_PI / 180.0));
623 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
624 _matrixCompose(matrix, &tmp, matrix);
625 } else if (ptCount == 3) {
626 tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
627 _matrixCompose(matrix, &tmp, matrix);
629 tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
630 _matrixCompose(matrix, &tmp, matrix);
632 tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
633 _matrixCompose(matrix, &tmp, matrix);
637 } else if (state == MatrixState::Scale) {
639 if (ptCount < 1 || ptCount > 2) goto error;
643 if (ptCount == 2) sy = points[1];
644 tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
645 _matrixCompose(matrix, &tmp, matrix);
653 #define LENGTH_DEF(Name, Value) \
655 #Name, sizeof(#Name), Value \
659 static constexpr struct
665 LENGTH_DEF(%, SvgLengthType::Percent),
666 LENGTH_DEF(px, SvgLengthType::Px),
667 LENGTH_DEF(pc, SvgLengthType::Pc),
668 LENGTH_DEF(pt, SvgLengthType::Pt),
669 LENGTH_DEF(mm, SvgLengthType::Mm),
670 LENGTH_DEF(cm, SvgLengthType::Cm),
671 LENGTH_DEF(in, SvgLengthType::In)
675 static float _parseLength(const char* str, SvgLengthType* type)
679 int sz = strlen(str);
681 *type = SvgLengthType::Px;
682 for (i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
683 if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
685 value = strtof(str, nullptr);
690 static bool _parseStyleAttr(void* data, const char* key, const char* value);
693 static bool _attrParseSvgNode(void* data, const char* key, const char* value)
695 SvgLoaderData* loader = (SvgLoaderData*)data;
696 SvgNode* node = loader->svgParse->node;
697 SvgDocNode* doc = &(node->node.doc);
700 //@TODO handle lenght unit.
701 if (!strcmp(key, "width")) {
702 doc->w = _parseLength(value, &type);
703 } else if (!strcmp(key, "height")) {
704 doc->h = _parseLength(value, &type);
705 } else if (!strcmp(key, "viewBox")) {
706 if (_parseNumber(&value, &doc->vx)) {
707 if (_parseNumber(&value, &doc->vy)) {
708 if (_parseNumber(&value, &doc->vw)) {
709 _parseNumber(&value, &doc->vh);
710 loader->svgParse->global.h = doc->vh;
712 loader->svgParse->global.w = doc->vw;
714 loader->svgParse->global.y = doc->vy;
716 loader->svgParse->global.x = doc->vx;
717 } else if (!strcmp(key, "preserveAspectRatio")) {
718 if (!strcmp(value, "none")) doc->preserveAspect = false;
719 } else if (!strcmp(key, "style")) {
720 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
722 return _parseStyleAttr(loader, key, value);
728 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
729 static void _handlePaintAttr(SvgLoaderData* loader, SvgPaint* paint, const char* value)
731 if (!strcmp(value, "none")) {
737 if (!strcmp(value, "currentColor")) {
738 paint->curColor = true;
741 _toColor(value, &paint->r, &paint->g, &paint->b, &paint->url);
745 static void _handleColorAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
747 SvgStyleProperty* style = node->style;
748 _toColor(value, &style->r, &style->g, &style->b, nullptr);
752 static void _handleFillAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
754 SvgStyleProperty* style = node->style;
755 style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
756 _handlePaintAttr(loader, &style->fill.paint, value);
760 static void _handleStrokeAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
762 SvgStyleProperty* style = node->style;
763 style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
764 _handlePaintAttr(loader, &style->stroke.paint, value);
768 static void _handleStrokeOpacityAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
770 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
771 node->style->stroke.opacity = _toOpacity(value);
775 static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
777 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
778 node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
782 static void _handleStrokeLineCapAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
784 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
785 node->style->stroke.cap = _toLineCap(value);
789 static void _handleStrokeLineJoinAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
791 node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
792 node->style->stroke.join = _toLineJoin(value);
796 static void _handleFillRuleAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
798 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
799 node->style->fill.fillRule = _toFillRule(value);
803 static void _handleOpacityAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
805 node->style->opacity = _toOpacity(value);
809 static void _handleFillOpacityAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
811 node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
812 node->style->fill.opacity = _toOpacity(value);
816 static void _handleTransformAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
818 node->transform = _parseTransformationMatrix(value);
822 static void _handleDisplayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
824 //TODO : The display attribute can have various values as well as "none".
825 // The default is "inline" which means visible and "none" means invisible.
826 // Depending on the type of node, additional functionality may be required.
827 // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
828 if (!strcmp(value, "none")) node->display = false;
829 else node->display = true;
833 typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
836 #define STYLE_DEF(Name, Name1) \
838 #Name, sizeof(#Name), _handle##Name1##Attr \
842 static constexpr struct
846 styleMethod tagHandler;
848 STYLE_DEF(color, Color),
849 STYLE_DEF(fill, Fill),
850 STYLE_DEF(fill-rule, FillRule),
851 STYLE_DEF(fill-opacity, FillOpacity),
852 STYLE_DEF(opacity, Opacity),
853 STYLE_DEF(stroke, Stroke),
854 STYLE_DEF(stroke-width, StrokeWidth),
855 STYLE_DEF(stroke-linejoin, StrokeLineJoin),
856 STYLE_DEF(stroke-linecap, StrokeLineCap),
857 STYLE_DEF(stroke-opacity, StrokeOpacity),
858 STYLE_DEF(transform, Transform),
859 STYLE_DEF(display, Display)
863 static bool _parseStyleAttr(void* data, const char* key, const char* value)
865 SvgLoaderData* loader = (SvgLoaderData*)data;
866 SvgNode* node = loader->svgParse->node;
869 if (!key || !value) return false;
871 //Trim the white space
872 key = _skipSpace(key, nullptr);
874 value = _skipSpace(value, nullptr);
877 for (i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
878 if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
879 styleTags[i].tagHandler(loader, node, value);
888 * https://www.w3.org/TR/SVG/struct.html#Groups
890 static bool _attrParseGNode(void* data, const char* key, const char* value)
892 SvgLoaderData* loader = (SvgLoaderData*)data;
893 SvgNode* node = loader->svgParse->node;
895 if (!strcmp(key, "style")) {
896 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
897 } else if (!strcmp(key, "transform")) {
898 node->transform = _parseTransformationMatrix(value);
899 } else if (!strcmp(key, "id")) {
900 node->id = _copyId(value);
902 return _parseStyleAttr(loader, key, value);
908 static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
910 SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
912 //Default fill property
913 node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
915 //Update the default value of stroke and fill
916 //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
917 node->style->fill.paint.none = false;
918 //Default fill opacity is 1
919 node->style->fill.opacity = 255;
920 node->style->opacity = 255;
922 //Default fill rule is nonzero
923 node->style->fill.fillRule = SvgFillRule::Winding;
925 //Default stroke is none
926 node->style->stroke.paint.none = true;
927 //Default stroke opacity is 1
928 node->style->stroke.opacity = 255;
929 //Default stroke width is 1
930 node->style->stroke.width = 1;
931 //Default line cap is butt
932 node->style->stroke.cap = StrokeCap::Butt;
933 //Default line join is miter
934 node->style->stroke.join = StrokeJoin::Miter;
935 node->style->stroke.scale = 1.0;
937 //Default display is true("inline").
938 node->display = true;
940 node->parent = parent;
943 if (parent) parent->child.push_back(node);
948 static SvgNode* _createDefsNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
950 SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
951 simpleXmlParseAttributes(buf, bufLength, nullptr, node);
956 static SvgNode* _createGNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
958 loader->svgParse->node = _createNode(parent, SvgNodeType::G);
960 simpleXmlParseAttributes(buf, bufLength, _attrParseGNode, loader);
961 return loader->svgParse->node;
965 static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
967 loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
968 SvgDocNode* doc = &(loader->svgParse->node->node.doc);
970 doc->preserveAspect = true;
971 simpleXmlParseAttributes(buf, bufLength, _attrParseSvgNode, loader);
973 return loader->svgParse->node;
977 static bool _attrParsePathNode(void* data, const char* key, const char* value)
979 SvgLoaderData* loader = (SvgLoaderData*)data;
980 SvgNode* node = loader->svgParse->node;
981 SvgPathNode* path = &(node->node.path);
983 if (!strcmp(key, "d")) {
984 //Temporary: need to copy
985 path->path = _copyId(value);
986 } else if (!strcmp(key, "style")) {
987 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
988 } else if (!strcmp(key, "id")) {
989 node->id = _copyId(value);
991 return _parseStyleAttr(loader, key, value);
997 static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
999 loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
1001 simpleXmlParseAttributes(buf, bufLength, _attrParsePathNode, loader);
1003 return loader->svgParse->node;
1007 #define CIRCLE_DEF(Name, Field, Type) \
1009 #Name, Type, sizeof(#Name), offsetof(SvgCircleNode, Field) \
1013 static constexpr struct
1016 SvgParserLengthType type;
1020 CIRCLE_DEF(cx, cx, SvgParserLengthType::Horizontal),
1021 CIRCLE_DEF(cy, cy, SvgParserLengthType::Vertical),
1022 CIRCLE_DEF(r, r, SvgParserLengthType::Other)
1026 /* parse the attributes for a circle element.
1027 * https://www.w3.org/TR/SVG/shapes.html#CircleElement
1029 static bool _attrParseCircleNode(void* data, const char* key, const char* value)
1031 SvgLoaderData* loader = (SvgLoaderData*)data;
1032 SvgNode* node = loader->svgParse->node;
1033 SvgCircleNode* circle = &(node->node.circle);
1035 unsigned char* array;
1036 int sz = strlen(key);
1038 array = (unsigned char*)circle;
1039 for (i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
1040 if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
1041 *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
1046 if (!strcmp(key, "style")) {
1047 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1048 } else if (!strcmp(key, "id")) {
1049 node->id = _copyId(value);
1051 return _parseStyleAttr(loader, key, value);
1057 static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1059 loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
1061 simpleXmlParseAttributes(buf, bufLength, _attrParseCircleNode, loader);
1062 return loader->svgParse->node;
1066 #define ELLIPSE_DEF(Name, Field, Type) \
1068 #Name, Type, sizeof(#Name), offsetof(SvgEllipseNode, Field) \
1072 static constexpr struct
1075 SvgParserLengthType type;
1079 ELLIPSE_DEF(cx, cx, SvgParserLengthType::Horizontal),
1080 ELLIPSE_DEF(cy, cy, SvgParserLengthType::Vertical),
1081 ELLIPSE_DEF(rx, rx, SvgParserLengthType::Horizontal),
1082 ELLIPSE_DEF(ry, ry, SvgParserLengthType::Vertical)
1086 /* parse the attributes for an ellipse element.
1087 * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
1089 static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
1091 SvgLoaderData* loader = (SvgLoaderData*)data;
1092 SvgNode* node = loader->svgParse->node;
1093 SvgEllipseNode* ellipse = &(node->node.ellipse);
1095 unsigned char* array;
1096 int sz = strlen(key);
1098 array = (unsigned char*)ellipse;
1099 for (i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
1100 if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
1101 *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
1106 if (!strcmp(key, "id")) {
1107 node->id = _copyId(value);
1108 } else if (!strcmp(key, "style")) {
1109 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1111 return _parseStyleAttr(loader, key, value);
1117 static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1119 loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
1121 simpleXmlParseAttributes(buf, bufLength, _attrParseEllipseNode, loader);
1122 return loader->svgParse->node;
1126 static void _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
1132 float *pointArray = nullptr, *tmpArray;
1134 while (_parseNumber(&str, &num)) {
1135 tmp[tmpCount++] = num;
1136 if (tmpCount == 50) {
1137 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1138 if (!tmpArray) goto error_alloc;
1139 pointArray = tmpArray;
1140 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1147 tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
1148 if (!tmpArray) goto error_alloc;
1149 pointArray = tmpArray;
1150 memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
1154 *points = pointArray;
1158 printf("ERR : allocation for point array failed. out of memory");
1163 /* parse the attributes for a polygon element.
1164 * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
1166 static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
1168 SvgLoaderData* loader = (SvgLoaderData*)data;
1169 SvgNode* node = loader->svgParse->node;
1170 SvgPolygonNode* polygon = nullptr;
1172 if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
1173 else polygon = &(node->node.polyline);
1175 if (!strcmp(key, "points")) {
1176 _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
1177 } else if (!strcmp(key, "style")) {
1178 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1179 } else if (!strcmp(key, "id")) {
1180 node->id = _copyId(value);
1182 return _parseStyleAttr(loader, key, value);
1188 static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1190 loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
1192 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1193 return loader->svgParse->node;
1197 static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1199 loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
1201 simpleXmlParseAttributes(buf, bufLength, _attrParsePolygonNode, loader);
1202 return loader->svgParse->node;
1206 #define RECT_DEF(Name, Field, Type) \
1208 #Name, Type, sizeof(#Name), offsetof(SvgRectNode, Field) \
1212 static constexpr struct
1215 SvgParserLengthType type;
1219 RECT_DEF(x, x, SvgParserLengthType::Horizontal),
1220 RECT_DEF(y, y, SvgParserLengthType::Vertical),
1221 RECT_DEF(width, w, SvgParserLengthType::Horizontal),
1222 RECT_DEF(height, h, SvgParserLengthType::Vertical),
1223 RECT_DEF(rx, rx, SvgParserLengthType::Horizontal),
1224 RECT_DEF(ry, ry, SvgParserLengthType::Vertical)
1228 /* parse the attributes for a rect element.
1229 * https://www.w3.org/TR/SVG/shapes.html#RectElement
1231 static bool _attrParseRectNode(void* data, const char* key, const char* value)
1233 SvgLoaderData* loader = (SvgLoaderData*)data;
1234 SvgNode* node = loader->svgParse->node;
1235 SvgRectNode* rect = &(node->node.rect);
1237 unsigned char* array;
1239 int sz = strlen(key);
1241 array = (unsigned char*)rect;
1242 for (i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
1243 if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
1244 *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
1250 if (!strcmp(key, "id")) {
1251 node->id = _copyId(value);
1252 } else if (!strcmp(key, "style")) {
1253 ret = simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1255 ret = _parseStyleAttr(loader, key, value);
1258 if (!(rect->rx - 0 <= FLT_EPSILON) && (rect->ry - 0 <= FLT_EPSILON)) rect->ry = rect->rx;
1259 if (!(rect->ry - 0 <= FLT_EPSILON) && (rect->rx - 0 <= FLT_EPSILON)) rect->rx = rect->ry;
1265 static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1267 loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
1269 simpleXmlParseAttributes(buf, bufLength, _attrParseRectNode, loader);
1270 return loader->svgParse->node;
1274 #define LINE_DEF(Name, Field, Type) \
1276 #Name, Type, sizeof(#Name), offsetof(SvgLineNode, Field) \
1280 static constexpr struct
1283 SvgParserLengthType type;
1287 LINE_DEF(x1, x1, SvgParserLengthType::Horizontal),
1288 LINE_DEF(y1, y1, SvgParserLengthType::Vertical),
1289 LINE_DEF(x2, x2, SvgParserLengthType::Horizontal),
1290 LINE_DEF(y2, y2, SvgParserLengthType::Vertical)
1294 /* parse the attributes for a rect element.
1295 * https://www.w3.org/TR/SVG/shapes.html#LineElement
1297 static bool _attrParseLineNode(void* data, const char* key, const char* value)
1299 SvgLoaderData* loader = (SvgLoaderData*)data;
1300 SvgNode* node = loader->svgParse->node;
1301 SvgLineNode* line = &(node->node.line);
1303 unsigned char* array;
1304 int sz = strlen(key);
1306 array = (unsigned char*)line;
1307 for (i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
1308 if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
1309 *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
1314 if (!strcmp(key, "id")) {
1315 node->id = _copyId(value);
1316 } else if (!strcmp(key, "style")) {
1317 return simpleXmlParseW3CAttribute(value, _parseStyleAttr, loader);
1319 return _parseStyleAttr(loader, key, value);
1325 static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1327 loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
1329 simpleXmlParseAttributes(buf, bufLength, _attrParseLineNode, loader);
1330 return loader->svgParse->node;
1334 static string* _idFromHref(const char* href)
1336 href = _skipSpace(href, nullptr);
1337 if ((*href) == '#') href++;
1338 return new string(href);
1342 static SvgNode* _getDefsNode(SvgNode* node)
1344 if (!node) return nullptr;
1346 while (node->parent != nullptr) {
1347 node = node->parent;
1350 if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
1356 static SvgNode* _findChildById(SvgNode* node, const char* id)
1359 if (!node) return nullptr;
1361 for (vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
1362 if (((*itrChild)->id != nullptr) && !strcmp((*itrChild)->id->c_str(), id)) return *itrChild;
1368 static void _cloneGradStops(vector<Fill::ColorStop*> *dst, vector<Fill::ColorStop*> src)
1370 for(vector<Fill::ColorStop*>::iterator itrStop = src.begin(); itrStop != src.end(); itrStop++) {
1371 Fill::ColorStop *stop = (Fill::ColorStop *)malloc(sizeof(Fill::ColorStop));
1372 stop->r = (*itrStop)->r;
1373 stop->g = (*itrStop)->g;
1374 stop->b = (*itrStop)->b;
1375 stop->a = (*itrStop)->a;
1376 stop->offset = (*itrStop)->offset;
1377 dst->push_back(stop);
1383 static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
1385 SvgStyleGradient* grad;
1387 if (!from) return nullptr;
1389 grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1390 grad->type = from->type;
1391 grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
1392 grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
1393 grad->spread = from->spread;
1394 grad->usePercentage = from->usePercentage;
1395 grad->userSpace = from->userSpace;
1396 if (from->transform) {
1397 grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
1398 memcpy(grad->transform, from->transform, sizeof(Matrix));
1400 if (grad->type == SvgGradientType::Linear) {
1401 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
1402 memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
1403 } else if (grad->type == SvgGradientType::Radial) {
1404 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1405 memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
1408 _cloneGradStops(&(grad->stops), from->stops);
1413 static void _copyAttr(SvgNode* to, SvgNode* from)
1415 //Copy matrix attribute
1416 if (from->transform) {
1417 to->transform = (Matrix*)calloc(1, sizeof(Matrix));
1418 memcpy(to->transform, from->transform, sizeof(Matrix));
1420 //Copy style attribute;
1421 memcpy(to->style, from->style, sizeof(SvgStyleProperty));
1423 //Copy node attribute
1424 switch (from->type) {
1425 case SvgNodeType::Circle: {
1426 to->node.circle.cx = from->node.circle.cx;
1427 to->node.circle.cy = from->node.circle.cy;
1428 to->node.circle.r = from->node.circle.r;
1431 case SvgNodeType::Ellipse: {
1432 to->node.ellipse.cx = from->node.ellipse.cx;
1433 to->node.ellipse.cy = from->node.ellipse.cy;
1434 to->node.ellipse.rx = from->node.ellipse.rx;
1435 to->node.ellipse.ry = from->node.ellipse.ry;
1438 case SvgNodeType::Rect: {
1439 to->node.rect.x = from->node.rect.x;
1440 to->node.rect.y = from->node.rect.y;
1441 to->node.rect.w = from->node.rect.w;
1442 to->node.rect.h = from->node.rect.h;
1443 to->node.rect.rx = from->node.rect.rx;
1444 to->node.rect.ry = from->node.rect.ry;
1447 case SvgNodeType::Line: {
1448 to->node.line.x1 = from->node.line.x1;
1449 to->node.line.y1 = from->node.line.y1;
1450 to->node.line.x2 = from->node.line.x2;
1451 to->node.line.y2 = from->node.line.y2;
1454 case SvgNodeType::Path: {
1455 to->node.path.path = new string(from->node.path.path->c_str());
1458 case SvgNodeType::Polygon: {
1459 to->node.polygon.pointsCount = from->node.polygon.pointsCount;
1460 to->node.polygon.points = (float*)calloc(to->node.polygon.pointsCount, sizeof(float));
1463 case SvgNodeType::Polyline: {
1464 to->node.polyline.pointsCount = from->node.polyline.pointsCount;
1465 to->node.polyline.points = (float*)calloc(to->node.polyline.pointsCount, sizeof(float));
1475 static void _cloneNode(SvgNode* from, SvgNode* parent)
1480 newNode = _createNode(parent, from->type);
1481 _copyAttr(newNode, from);
1483 for (vector<SvgNode*>::iterator itrChild = from->child.begin(); itrChild != from->child.end(); itrChild++) {
1484 _cloneNode(*itrChild, newNode);
1489 static bool _attrParseUseNode(void* data, const char* key, const char* value)
1491 SvgLoaderData* loader = (SvgLoaderData*)data;
1492 SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
1495 if (!strcmp(key, "xlink:href")) {
1496 id = _idFromHref(value);
1497 defs = _getDefsNode(node);
1498 nodeFrom = _findChildById(defs, id->c_str());
1499 _cloneNode(nodeFrom, node);
1502 _attrParseGNode(data, key, value);
1508 static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength)
1510 loader->svgParse->node = _createNode(parent, SvgNodeType::G);
1512 simpleXmlParseAttributes(buf, bufLength, _attrParseUseNode, loader);
1513 return loader->svgParse->node;
1517 #define TAG_DEF(Name, Name1) \
1519 #Name, sizeof(#Name), _create##Name1##Node \
1523 //TODO: Implement 'text' primitive
1524 static constexpr struct
1528 FactoryMethod tagHandler;
1529 } graphicsTags[] = {
1531 TAG_DEF(circle, Circle),
1532 TAG_DEF(ellipse, Ellipse),
1533 TAG_DEF(path, Path),
1534 TAG_DEF(polygon, Polygon),
1535 TAG_DEF(rect, Rect),
1536 TAG_DEF(polyline, Polyline),
1537 TAG_DEF(line, Line),
1541 static constexpr struct
1545 FactoryMethod tagHandler;
1547 TAG_DEF(defs, Defs),
1553 #define FIND_FACTORY(Short_Name, Tags_Array) \
1554 static FactoryMethod \
1555 _find##Short_Name##Factory(const char* name) \
1558 int sz = strlen(name); \
1560 for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
1561 if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
1562 return Tags_Array[i].tagHandler; \
1568 FIND_FACTORY(Group, groupTags);
1569 FIND_FACTORY(Graphics, graphicsTags);
1572 FillSpread _parseSpreadValue(const char* value)
1574 FillSpread spread = FillSpread::Pad;
1576 if (!strcmp(value, "reflect")) {
1577 spread = FillSpread::Reflect;
1578 } else if (!strcmp(value, "repeat")) {
1579 spread = FillSpread::Repeat;
1586 static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1588 radial->cx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1589 if (!loader->svgParse->gradient.parsedFx) radial->fx = radial->cx;
1593 static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1595 radial->cy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1596 if (!loader->svgParse->gradient.parsedFy) radial->fy = radial->cy;
1600 static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1602 radial->fx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1603 loader->svgParse->gradient.parsedFx = true;
1607 static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1609 radial->fy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1610 loader->svgParse->gradient.parsedFy = true;
1614 static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
1616 radial->r = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Other);
1620 static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1622 if (!userSpace) radial->cx = radial->cx * loader->svgParse->global.w;
1626 static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1628 if (!userSpace) radial->cy = radial->cy * loader->svgParse->global.h;
1632 static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1634 if (!userSpace) radial->fx = radial->fx * loader->svgParse->global.w;
1638 static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1640 if (!userSpace) radial->fy = radial->fy * loader->svgParse->global.h;
1644 static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
1646 if (!userSpace) radial->r = radial->r * (sqrt(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrt(2.0));
1650 typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
1651 typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
1654 #define RADIAL_DEF(Name, Name1) \
1656 #Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
1660 static constexpr struct
1664 radialMethod tagHandler;
1665 radialMethodRecalc tagRecalc;
1675 static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
1677 SvgLoaderData* loader = (SvgLoaderData*)data;
1678 SvgStyleGradient* grad = loader->svgParse->styleGrad;
1679 SvgRadialGradient* radial = grad->radial;
1681 int sz = strlen(key);
1683 for (i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1684 if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
1685 radialTags[i].tagHandler(loader, radial, value);
1690 if (!strcmp(key, "id")) {
1691 grad->id = _copyId(value);
1692 } else if (!strcmp(key, "spreadMethod")) {
1693 grad->spread = _parseSpreadValue(value);
1694 } else if (!strcmp(key, "xlink:href")) {
1695 grad->ref = _idFromHref(value);
1696 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
1697 grad->userSpace = true;
1704 static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
1707 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1708 loader->svgParse->styleGrad = grad;
1710 grad->type = SvgGradientType::Radial;
1711 grad->userSpace = false;
1712 grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
1714 * Default values of gradient
1716 grad->radial->cx = 0.5;
1717 grad->radial->cy = 0.5;
1718 grad->radial->fx = 0.5;
1719 grad->radial->fy = 0.5;
1720 grad->radial->r = 0.5;
1722 loader->svgParse->gradient.parsedFx = false;
1723 loader->svgParse->gradient.parsedFy = false;
1724 simpleXmlParseAttributes(buf, bufLength,
1725 _attrParseRadialGradientNode, loader);
1727 for (i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
1728 radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
1731 grad->usePercentage = true;
1733 return loader->svgParse->styleGrad;
1737 static bool _attrParseStops(void* data, const char* key, const char* value)
1739 SvgLoaderData* loader = (SvgLoaderData*)data;
1740 Fill::ColorStop* stop = loader->svgParse->gradStop;
1742 if (!strcmp(key, "offset")) {
1743 stop->offset = _toOffset(value);
1744 } else if (!strcmp(key, "stop-opacity")) {
1745 stop->a = _toOpacity(value);
1746 } else if (!strcmp(key, "stop-color")) {
1747 _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
1748 } else if (!strcmp(key, "style")) {
1749 simpleXmlParseW3CAttribute(value,
1750 _attrParseStops, data);
1757 static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1759 linear->x1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1763 static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1765 linear->y1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1769 static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1771 linear->x2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
1775 static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
1777 linear->y2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
1781 static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1783 if (!userSpace) linear->x1 = linear->x1 * loader->svgParse->global.w;
1787 static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1789 if (!userSpace) linear->y1 = linear->y1 * loader->svgParse->global.h;
1793 static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1795 if (!userSpace) linear->x2 = linear->x2 * loader->svgParse->global.w;
1799 static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
1801 if (!userSpace) linear->y2 = linear->y2 * loader->svgParse->global.h;
1805 typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
1806 typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
1809 #define LINEAR_DEF(Name, Name1) \
1811 #Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
1815 static constexpr struct
1819 Linear_Method tagHandler;
1820 Linear_Method_Recalc tagRecalc;
1829 static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
1831 SvgLoaderData* loader = (SvgLoaderData*)data;
1832 SvgStyleGradient* grad = loader->svgParse->styleGrad;
1833 SvgLinearGradient* linear = grad->linear;
1835 int sz = strlen(key);
1837 for (i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
1838 if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
1839 linear_tags[i].tagHandler(loader, linear, value);
1844 if (!strcmp(key, "id")) {
1845 grad->id = _copyId(value);
1846 } else if (!strcmp(key, "spreadMethod")) {
1847 grad->spread = _parseSpreadValue(value);
1848 } else if (!strcmp(key, "xlink:href")) {
1849 grad->ref = _idFromHref(value);
1850 } else if (!strcmp(key, "gradientUnits") && !strcmp(value, "userSpaceOnUse")) {
1851 grad->userSpace = true;
1852 } else if (!strcmp(key, "gradientTransform")) {
1853 grad->transform = _parseTransformationMatrix(value);
1860 static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
1862 SvgStyleGradient* grad = (SvgStyleGradient*)calloc(1, sizeof(SvgStyleGradient));
1863 loader->svgParse->styleGrad = grad;
1866 grad->type = SvgGradientType::Linear;
1867 grad->userSpace = false;
1868 grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
1870 * Default value of x2 is 100%
1872 grad->linear->x2 = 1;
1873 simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
1875 for (i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
1876 linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
1879 grad->usePercentage = true;
1881 return loader->svgParse->styleGrad;
1885 #define GRADIENT_DEF(Name, Name1) \
1887 #Name, sizeof(#Name), _create##Name1 \
1892 * For all Gradients lengths would be calculated into percentages related to
1893 * canvas width and height.
1895 * if user then recalculate actual pixels into percentages
1897 static constexpr struct
1901 GradientFactoryMethod tagHandler;
1902 } gradientTags[] = {
1903 GRADIENT_DEF(linearGradient, LinearGradient),
1904 GRADIENT_DEF(radialGradient, RadialGradient)
1908 static GradientFactoryMethod _findGradientFactory(const char* name)
1911 int sz = strlen(name);
1913 for (i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
1914 if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
1915 return gradientTags[i].tagHandler;
1922 #define POP_TAG(Tag) \
1924 #Tag, sizeof(#Tag) \
1928 static constexpr struct
1939 static void _svgLoaderParerXmlClose(SvgLoaderData* loader, const char* content, unsigned int length)
1943 content = _skipSpace(content, nullptr);
1945 for (i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
1946 if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
1947 loader->stack.pop_back();
1956 static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
1958 const char* attrs = nullptr;
1959 int attrsLength = 0;
1961 char tagName[20] = "";
1962 FactoryMethod method;
1963 GradientFactoryMethod gradientMethod;
1964 SvgNode *node = nullptr, *parent = nullptr;
1966 attrs = simpleXmlFindAttributesTag(content, length);
1969 //Parse the empty tag
1971 while ((attrs != nullptr) && *attrs != '>') attrs++;
1975 //Find out the tag name starting from content till sz length
1976 sz = attrs - content;
1977 attrsLength = length - sz;
1978 while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
1979 strncpy(tagName, content, sz);
1983 if ((method = _findGroupFactory(tagName))) {
1986 if (strcmp(tagName, "svg")) return; //Not a valid svg document
1987 node = method(loader, nullptr, attrs, attrsLength);
1990 if (loader->stack.size() > 0) parent = loader->stack.at(loader->stack.size() - 1);
1991 node = method(loader, parent, attrs, attrsLength);
1993 loader->stack.push_back(node);
1995 if (node->type == SvgNodeType::Defs) {
1996 loader->doc->node.doc.defs = node;
1999 } else if ((method = _findGraphicsFactory(tagName))) {
2000 parent = loader->stack.at(loader->stack.size() - 1);
2001 node = method(loader, parent, attrs, attrsLength);
2002 } else if ((gradientMethod = _findGradientFactory(tagName))) {
2003 SvgStyleGradient* gradient;
2004 gradient = gradientMethod(loader, attrs, attrsLength);
2005 //FIXME: The current parsing structure does not distinguish end tags.
2006 // There is no way to know if the currently parsed gradient is in defs.
2007 // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
2008 // But finally, the loader has a gradient style list regardless of defs.
2009 // This is only to support this when multiple gradients are declared, even if no defs are declared.
2010 // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
2011 if (loader->doc->node.doc.defs) {
2012 loader->def->node.defs.gradients.push_back(gradient);
2014 loader->gradients.push_back(gradient);
2016 loader->latestGradient = gradient;
2017 } else if (!strcmp(tagName, "stop")) {
2018 Fill::ColorStop* stop = (Fill::ColorStop*)calloc(1, sizeof(Fill::ColorStop));
2019 loader->svgParse->gradStop = stop;
2020 /* default value for opacity */
2022 simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
2023 if (loader->latestGradient) {
2024 loader->latestGradient->stops.push_back(stop);
2030 static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int offset, unsigned int length)
2032 SvgLoaderData* loader = (SvgLoaderData*)data;
2035 case SimpleXMLType::Open: {
2036 _svgLoaderParserXmlOpen(loader, content, length);
2039 case SimpleXMLType::OpenEmpty: {
2040 _svgLoaderParserXmlOpen(loader, content, length);
2043 case SimpleXMLType::Close: {
2044 _svgLoaderParerXmlClose(loader, content, length);
2047 case SimpleXMLType::Data:
2048 case SimpleXMLType::CData:
2049 case SimpleXMLType::DoctypeChild: {
2052 case SimpleXMLType::Ignored:
2053 case SimpleXMLType::Comment:
2054 case SimpleXMLType::Doctype: {
2066 static void _styleInherit(SvgStyleProperty* child, SvgStyleProperty* parent)
2068 if (parent == nullptr) return;
2069 //Inherit the property of parent if not present in child.
2071 if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
2072 child->fill.paint.r = parent->fill.paint.r;
2073 child->fill.paint.g = parent->fill.paint.g;
2074 child->fill.paint.b = parent->fill.paint.b;
2075 child->fill.paint.none = parent->fill.paint.none;
2076 child->fill.paint.curColor = parent->fill.paint.curColor;
2077 if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url->c_str());
2079 if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
2080 child->fill.opacity = parent->fill.opacity;
2082 if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
2083 child->fill.fillRule = parent->fill.fillRule;
2086 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
2087 child->stroke.paint.r = parent->stroke.paint.r;
2088 child->stroke.paint.g = parent->stroke.paint.g;
2089 child->stroke.paint.b = parent->stroke.paint.b;
2090 child->stroke.paint.none = parent->stroke.paint.none;
2091 child->stroke.paint.curColor = parent->stroke.paint.curColor;
2092 child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url->c_str()) : nullptr;
2094 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
2095 child->stroke.opacity = parent->stroke.opacity;
2097 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
2098 child->stroke.width = parent->stroke.width;
2100 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
2101 child->stroke.cap = parent->stroke.cap;
2103 if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
2104 child->stroke.join = parent->stroke.join;
2109 static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
2111 _styleInherit(node->style, parentStyle);
2113 for (vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
2114 _updateStyle(*itrChild, node->style);
2119 static SvgStyleGradient* _gradientDup(vector<SvgStyleGradient*> gradList, string* id)
2121 SvgStyleGradient* result = nullptr;
2123 for (vector<SvgStyleGradient*>::iterator itrGrad = gradList.begin(); itrGrad != gradList.end(); itrGrad++) {
2124 if (!((*itrGrad)->id->compare(*id))) {
2125 result = _cloneGradient(*itrGrad);
2130 if (result && result->ref) {
2131 for (vector<SvgStyleGradient*>::iterator itrGrad = gradList.begin(); itrGrad != gradList.end(); itrGrad++) {
2132 if (!((*itrGrad)->id->compare(*result->ref))) {
2133 if (!result->stops.empty()) {
2134 _cloneGradStops(&(result->stops), (*itrGrad)->stops);
2136 //TODO: Properly inherit other property
2146 static void _updateGradient(SvgNode* node, vector<SvgStyleGradient*> gradList)
2148 if (!node->child.empty()) {
2149 for (vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
2150 _updateGradient(*itrChild, gradList);
2153 if (node->style->fill.paint.url) {
2154 node->style->fill.paint.gradient = _gradientDup(gradList, node->style->fill.paint.url);
2155 } else if (node->style->stroke.paint.url) {
2156 //node->style->stroke.paint.gradient = _gradientDup(gradList, node->style->stroke.paint.url);
2161 static void _freeGradientStyle(SvgStyleGradient* grad)
2169 if (grad->transform) free(grad->transform);
2171 for(vector<Fill::ColorStop*>::iterator itrStop = grad->stops.begin(); itrStop != grad->stops.end(); itrStop++) {
2177 static void _freeNodeStyle(SvgStyleProperty* style)
2181 _freeGradientStyle(style->fill.paint.gradient);
2182 delete style->fill.paint.url;
2183 _freeGradientStyle(style->stroke.paint.gradient);
2184 delete style->stroke.paint.url;
2188 static void _freeSvgNode(SvgNode* node)
2192 for(vector<SvgNode*>::iterator itrChild = node->child.begin(); itrChild != node->child.end(); itrChild++) {
2193 _freeSvgNode(*itrChild);
2195 node->child.clear();
2198 free(node->transform);
2199 _freeNodeStyle(node->style);
2200 switch (node->type) {
2201 case SvgNodeType::Path: {
2202 delete node->node.path.path;
2205 case SvgNodeType::Polygon: {
2206 free(node->node.polygon.points);
2209 case SvgNodeType::Polyline: {
2210 free(node->node.polyline.points);
2213 case SvgNodeType::Doc: {
2214 _freeSvgNode(node->node.doc.defs);
2217 case SvgNodeType::Defs: {
2218 for(vector<SvgStyleGradient*>::iterator itrGrad = node->node.defs.gradients.begin(); itrGrad != node->node.defs.gradients.end(); itrGrad++) {
2219 _freeGradientStyle(*itrGrad);
2227 if (!node->child.empty()) node->child.clear();
2232 /************************************************************************/
2233 /* External Class Implementation */
2234 /************************************************************************/
2236 SvgLoader::SvgLoader()
2241 SvgLoader::~SvgLoader()
2247 bool SvgLoader::open(const char* path)
2254 cout << "ERROR: Failed to open file = " << path;
2257 getline(f, content, '\0');
2260 if (content.empty()) return false;
2263 //FIXME: Verify this resource is normal SVG, otherwise return false
2264 //Also, return the brief resource info such as viewbox:
2274 bool SvgLoader::read()
2276 if (content.empty()) return false;
2278 auto asyncTask = [](SvgLoader *loader) {
2280 loader->loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
2282 bool res = simpleXmlParse(loader->content.c_str(), loader->content.size(), true, _svgLoaderParser, &(loader->loaderData));
2284 if (!res) return unique_ptr<Scene>(nullptr);
2286 if (loader->loaderData.doc) {
2288 _updateStyle(loader->loaderData.doc, nullptr);
2289 defs = loader->loaderData.doc->node.doc.defs;
2290 if (defs) _updateGradient(loader->loaderData.doc, defs->node.defs.gradients);
2292 if (!loader->loaderData.gradients.empty()) {
2293 vector<SvgStyleGradient*> gradientList;
2294 for(vector<SvgStyleGradient*>::iterator itrGrad = loader->loaderData.gradients.begin(); itrGrad != loader->loaderData.gradients.end(); itrGrad++) {
2295 gradientList.push_back(*itrGrad);
2297 _updateGradient(loader->loaderData.doc, gradientList);
2298 gradientList.clear();
2302 return loader->builder.build(loader->loaderData.doc);
2305 rootProgress = async((launch::async | launch::deferred), asyncTask, this);
2311 bool SvgLoader::close()
2313 if (rootProgress.valid()) root = rootProgress.get();
2315 if (loaderData.svgParse) {
2316 free(loaderData.svgParse);
2317 loaderData.svgParse = nullptr;
2319 _freeSvgNode(loaderData.doc);
2320 loaderData.doc = nullptr;
2326 unique_ptr<Scene> SvgLoader::data()
2328 if (rootProgress.valid()) root = rootProgress.get();
2330 if (root) return move(root);
2331 else return unique_ptr<Scene>(nullptr);
2334 #endif //_TVG_SVG_LOADER_CPP_