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