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