Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsvghandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qplatformdefs.h"
5
6#include "qsvghandler_p.h"
7
9#include "qsvgstructure_p.h"
10#include "qsvggraphics_p.h"
11#include "qsvgnode_p.h"
12#include "qsvgfont_p.h"
13
14#include "qpen.h"
15#include "qpainterpath.h"
16#include "qbrush.h"
17#include "qcolor.h"
18#include "qtextformat.h"
19#include "qlist.h"
20#include "qfileinfo.h"
21#include "qfile.h"
22#include "qdir.h"
23#include "qdebug.h"
24#include "qmath.h"
25#include "qnumeric.h"
26#include <qregularexpression.h>
27#include "qtransform.h"
28#include "qvarlengtharray.h"
29#include "private/qmath_p.h"
30
31#include "float.h"
32#include <cmath>
33
35
36Q_LOGGING_CATEGORY(lcSvgHandler, "qt.svg")
37
38static const char *qt_inherit_text = "inherit";
39#define QT_INHERIT QLatin1String(qt_inherit_text)
40
41static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
42{
44 if (r) {
45 if (const QFile *file = qobject_cast<const QFile *>(r->device()))
47 else
48 result.append(QByteArrayLiteral("<input>"));
49 result.append(':');
50 result.append(QByteArray::number(r->lineNumber()));
51 if (const qint64 column = r->columnNumber()) {
52 result.append(':');
54 }
55 result.append(QByteArrayLiteral(": "));
56 }
57 result.append(msg);
58 return result;
59}
60
61static inline QByteArray msgProblemParsing(const QString &localName, const QXmlStreamReader *r)
62{
63 return prefixMessage(QByteArrayLiteral("Problem parsing ") + localName.toLocal8Bit(), r);
64}
65
66static inline QByteArray msgCouldNotResolveProperty(const QString &id, const QXmlStreamReader *r)
67{
68 return prefixMessage(QByteArrayLiteral("Could not resolve property: ") + id.toLocal8Bit(), r);
69}
70
71// ======== duplicated from qcolor_p
72
73static inline int qsvg_h2i(char hex, bool *ok = nullptr)
74{
75 if (hex >= '0' && hex <= '9')
76 return hex - '0';
77 if (hex >= 'a' && hex <= 'f')
78 return hex - 'a' + 10;
79 if (hex >= 'A' && hex <= 'F')
80 return hex - 'A' + 10;
81 if (ok)
82 *ok = false;
83 return -1;
84}
85
86static inline int qsvg_hex2int(const char *s, bool *ok = nullptr)
87{
88 return (qsvg_h2i(s[0], ok) * 16) | qsvg_h2i(s[1], ok);
89}
90
91static inline int qsvg_hex2int(char s, bool *ok = nullptr)
92{
93 int h = qsvg_h2i(s, ok);
94 return (h * 16) | h;
95}
96
97bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
98{
99 if(name[0] != '#')
100 return false;
101 name++;
102 const size_t len = qstrlen(name);
103 int r, g, b;
104 bool ok = true;
105 if (len == 12) {
106 r = qsvg_hex2int(name, &ok);
107 g = qsvg_hex2int(name + 4, &ok);
108 b = qsvg_hex2int(name + 8, &ok);
109 } else if (len == 9) {
110 r = qsvg_hex2int(name, &ok);
111 g = qsvg_hex2int(name + 3, &ok);
112 b = qsvg_hex2int(name + 6, &ok);
113 } else if (len == 6) {
114 r = qsvg_hex2int(name, &ok);
115 g = qsvg_hex2int(name + 2, &ok);
116 b = qsvg_hex2int(name + 4, &ok);
117 } else if (len == 3) {
118 r = qsvg_hex2int(name[0], &ok);
119 g = qsvg_hex2int(name[1], &ok);
120 b = qsvg_hex2int(name[2], &ok);
121 } else {
122 r = g = b = -1;
123 }
124 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
125 *rgb = 0;
126 return false;
127 }
128 *rgb = qRgb(r, g ,b);
129 return true;
130}
131
133{
134 if (len > 13)
135 return false;
136 char tmp[16];
137 for(int i = 0; i < len; ++i)
138 tmp[i] = str[i].toLatin1();
139 tmp[len] = 0;
140 return qsvg_get_hex_rgb(tmp, rgb);
141}
142
143// ======== end of qcolor_p duplicate
144
146
147static inline QString someId(const QXmlStreamAttributes &attributes)
148{
149 QString id = attributes.value(QLatin1String("id")).toString();
150 if (id.isEmpty())
151 id = attributes.value(QLatin1String("xml:id")).toString();
152 return id;
153}
154
156{
157 QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler);
158
160
190
191#ifndef QT_NO_CSSPARSER
193#endif
194};
195
197{
198 for (int i = 0; i < xmlAttributes.size(); ++i) {
199 const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
200 QStringView name = attribute.qualifiedName();
201 if (name.isEmpty())
202 continue;
203 QStringView value = attribute.value();
204
205 switch (name.at(0).unicode()) {
206
207 case 'c':
208 if (name == QLatin1String("color"))
209 color = value;
210 else if (name == QLatin1String("color-opacity"))
212 else if (name == QLatin1String("comp-op"))
213 compOp = value;
214 break;
215
216 case 'd':
217 if (name == QLatin1String("display"))
218 display = value;
219 break;
220
221 case 'f':
222 if (name == QLatin1String("fill"))
223 fill = value;
224 else if (name == QLatin1String("fill-rule"))
225 fillRule = value;
226 else if (name == QLatin1String("fill-opacity"))
228 else if (name == QLatin1String("font-family"))
230 else if (name == QLatin1String("font-size"))
231 fontSize = value;
232 else if (name == QLatin1String("font-style"))
234 else if (name == QLatin1String("font-weight"))
236 else if (name == QLatin1String("font-variant"))
238 break;
239
240 case 'i':
241 if (name == QLatin1String("id"))
242 id = value.toString();
243 else if (name == QLatin1String("image-rendering"))
245 break;
246
247 case 'o':
248 if (name == QLatin1String("opacity"))
249 opacity = value;
250 if (name == QLatin1String("offset"))
251 offset = value;
252 break;
253
254 case 's':
255 if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
256 QStringView strokeRef = name.mid(6, name.size() - 6);
257 if (strokeRef.isEmpty())
258 stroke = value;
259 else if (strokeRef == QLatin1String("-dasharray"))
261 else if (strokeRef == QLatin1String("-dashoffset"))
263 else if (strokeRef == QLatin1String("-linecap"))
265 else if (strokeRef == QLatin1String("-linejoin"))
267 else if (strokeRef == QLatin1String("-miterlimit"))
269 else if (strokeRef == QLatin1String("-opacity"))
271 else if (strokeRef == QLatin1String("-width"))
273 } else if (name == QLatin1String("stop-color"))
275 else if (name == QLatin1String("stop-opacity"))
277 break;
278
279 case 't':
280 if (name == QLatin1String("text-anchor"))
282 else if (name == QLatin1String("transform"))
284 break;
285
286 case 'v':
287 if (name == QLatin1String("vector-effect"))
289 else if (name == QLatin1String("visibility"))
291 break;
292
293 case 'x':
294 if (name == QLatin1String("xml:id") && id.isEmpty())
295 id = value.toString();
296 break;
297
298 default:
299 break;
300 }
301 }
302
303 // If a style attribute is present, let its attribute settings override the plain attribute
304 // values. The spec seems to indicate that, and it is common behavior in svg renderers.
305#ifndef QT_NO_CSSPARSER
306 QStringView style = xmlAttributes.value(QLatin1String("style"));
307 if (!style.isEmpty()) {
308 handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
309 for (int j = 0; j < m_cssAttributes.size(); ++j) {
313 if (name.isEmpty())
314 continue;
315
316 switch (name.at(0).unicode()) {
317
318 case 'c':
319 if (name == QLatin1String("color"))
320 color = value;
321 else if (name == QLatin1String("color-opacity"))
323 else if (name == QLatin1String("comp-op"))
324 compOp = value;
325 break;
326
327 case 'd':
328 if (name == QLatin1String("display"))
329 display = value;
330 break;
331
332 case 'f':
333 if (name == QLatin1String("fill"))
334 fill = value;
335 else if (name == QLatin1String("fill-rule"))
336 fillRule = value;
337 else if (name == QLatin1String("fill-opacity"))
339 else if (name == QLatin1String("font-family"))
341 else if (name == QLatin1String("font-size"))
342 fontSize = value;
343 else if (name == QLatin1String("font-style"))
345 else if (name == QLatin1String("font-weight"))
347 else if (name == QLatin1String("font-variant"))
349 break;
350 case 'i':
351 if (name == QLatin1String("image-rendering"))
353 break;
354
355 case 'o':
356 if (name == QLatin1String("opacity"))
357 opacity = value;
358 else if (name == QLatin1String("offset"))
359 offset = value;
360 break;
361
362 case 's':
363 if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
364 QStringView strokeRef = name.mid(6, name.size() - 6);
365 if (strokeRef.isEmpty())
366 stroke = value;
367 else if (strokeRef == QLatin1String("-dasharray"))
369 else if (strokeRef == QLatin1String("-dashoffset"))
371 else if (strokeRef == QLatin1String("-linecap"))
373 else if (strokeRef == QLatin1String("-linejoin"))
375 else if (strokeRef == QLatin1String("-miterlimit"))
377 else if (strokeRef == QLatin1String("-opacity"))
379 else if (strokeRef == QLatin1String("-width"))
381 } else if (name == QLatin1String("stop-color"))
383 else if (name == QLatin1String("stop-opacity"))
385 break;
386
387 case 't':
388 if (name == QLatin1String("text-anchor"))
390 else if (name == QLatin1String("transform"))
392 break;
393
394 case 'v':
395 if (name == QLatin1String("vector-effect"))
397 else if (name == QLatin1String("visibility"))
399 break;
400
401 default:
402 break;
403 }
404 }
405 }
406#else
407 Q_UNUSED(handler);
408#endif // QT_NO_CSSPARSER
409}
410
411#ifndef QT_NO_CSSPARSER
412
413static const char * QSvgStyleSelector_nodeString[] = {
414 "svg",
415 "g",
416 "defs",
417 "switch",
418 "animation",
419 "arc",
420 "circle",
421 "ellipse",
422 "image",
423 "line",
424 "path",
425 "polygon",
426 "polyline",
427 "rect",
428 "text",
429 "textarea",
430 "tspan",
431 "use",
432 "video"
433};
434
436{
437public:
439 {
441 }
443 {
444 }
445
446 inline QString nodeToName(QSvgNode *node) const
447 {
449 }
450
451 inline QSvgNode *svgNode(NodePtr node) const
452 {
453 return (QSvgNode*)node.ptr;
454 }
456 {
457 if (n &&
458 (n->type() == QSvgNode::DOC ||
459 n->type() == QSvgNode::G ||
460 n->type() == QSvgNode::DEFS ||
461 n->type() == QSvgNode::SWITCH)) {
462 return (QSvgStructureNode*)n;
463 }
464 return 0;
465 }
466
468 {
469 QSvgNode *n = svgNode(node);
471 return st;
472 }
473
474 bool nodeNameEquals(NodePtr node, const QString& nodeName) const override
475 {
476 QSvgNode *n = svgNode(node);
477 if (!n)
478 return false;
480 return QString::compare(name, nodeName, Qt::CaseInsensitive) == 0;
481 }
482 QString attributeValue(NodePtr node, const QCss::AttributeSelector &asel) const override
483 {
484 const QString &name = asel.name;
485 QSvgNode *n = svgNode(node);
486 if ((!n->nodeId().isEmpty() && (name == QLatin1String("id") ||
487 name == QLatin1String("xml:id"))))
488 return n->nodeId();
489 if (!n->xmlClass().isEmpty() && name == QLatin1String("class"))
490 return n->xmlClass();
491 return QString();
492 }
493 bool hasAttributes(NodePtr node) const override
494 {
495 QSvgNode *n = svgNode(node);
496 return (n &&
497 (!n->nodeId().isEmpty() || !n->xmlClass().isEmpty()));
498 }
499
500 QStringList nodeIds(NodePtr node) const override
501 {
502 QSvgNode *n = svgNode(node);
503 QString nid;
504 if (n)
505 nid = n->nodeId();
506 QStringList lst; lst.append(nid);
507 return lst;
508 }
509
510 QStringList nodeNames(NodePtr node) const override
511 {
512 QSvgNode *n = svgNode(node);
513 if (n)
514 return QStringList(nodeToName(n));
515 return QStringList();
516 }
517
518 bool isNullNode(NodePtr node) const override
519 {
520 return !node.ptr;
521 }
522
523 NodePtr parentNode(NodePtr node) const override
524 {
525 QSvgNode *n = svgNode(node);
526 NodePtr newNode;
527 newNode.ptr = 0;
528 newNode.id = 0;
529 if (n) {
530 QSvgNode *svgParent = n->parent();
531 if (svgParent) {
532 newNode.ptr = svgParent;
533 }
534 }
535 return newNode;
536 }
538 {
539 NodePtr newNode;
540 newNode.ptr = 0;
541 newNode.id = 0;
542
543 QSvgNode *n = svgNode(node);
544 if (!n)
545 return newNode;
546 QSvgStructureNode *svgParent = nodeToStructure(n->parent());
547
548 if (svgParent) {
549 newNode.ptr = svgParent->previousSiblingNode(n);
550 }
551 return newNode;
552 }
553 NodePtr duplicateNode(NodePtr node) const override
554 {
555 NodePtr n;
556 n.ptr = node.ptr;
557 n.id = node.id;
558 return n;
559 }
560 void freeNode(NodePtr node) const override
561 {
562 Q_UNUSED(node);
563 }
564};
565
566#endif // QT_NO_CSSPARSER
567
568// '0' is 0x30 and '9' is 0x39
569static inline bool isDigit(ushort ch)
570{
571 static quint16 magic = 0x3ff;
572 return ((ch >> 4) == 3) && (magic >> (ch & 15));
573}
574
575static qreal toDouble(const QChar *&str)
576{
577 const int maxLen = 255;//technically doubles can go til 308+ but whatever
578 char temp[maxLen+1];
579 int pos = 0;
580
581 if (*str == QLatin1Char('-')) {
582 temp[pos++] = '-';
583 ++str;
584 } else if (*str == QLatin1Char('+')) {
585 ++str;
586 }
587 while (isDigit(str->unicode()) && pos < maxLen) {
588 temp[pos++] = str->toLatin1();
589 ++str;
590 }
591 if (*str == QLatin1Char('.') && pos < maxLen) {
592 temp[pos++] = '.';
593 ++str;
594 }
595 while (isDigit(str->unicode()) && pos < maxLen) {
596 temp[pos++] = str->toLatin1();
597 ++str;
598 }
599 bool exponent = false;
600 if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
601 exponent = true;
602 temp[pos++] = 'e';
603 ++str;
604 if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
605 temp[pos++] = str->toLatin1();
606 ++str;
607 }
608 while (isDigit(str->unicode()) && pos < maxLen) {
609 temp[pos++] = str->toLatin1();
610 ++str;
611 }
612 }
613
614 temp[pos] = '\0';
615
616 qreal val;
617 if (!exponent && pos < 10) {
618 int ival = 0;
619 const char *t = temp;
620 bool neg = false;
621 if(*t == '-') {
622 neg = true;
623 ++t;
624 }
625 while(*t && *t != '.') {
626 ival *= 10;
627 ival += (*t) - '0';
628 ++t;
629 }
630 if(*t == '.') {
631 ++t;
632 int div = 1;
633 while(*t) {
634 ival *= 10;
635 ival += (*t) - '0';
636 div *= 10;
637 ++t;
638 }
639 val = ((qreal)ival)/((qreal)div);
640 } else {
641 val = ival;
642 }
643 if (neg)
644 val = -val;
645 } else {
647 // Do not tolerate values too wild to be represented normally by floats
648 if (qFpClassify(float(val)) != FP_NORMAL)
649 val = 0;
650 }
651 return val;
652
653}
654
655static qreal toDouble(QStringView str, bool *ok = NULL)
656{
657 const QChar *c = str.constData();
658 qreal res = (c == nullptr ? qreal{} : toDouble(c));
659 if (ok)
660 *ok = (c == (str.constData() + str.size()));
661 return res;
662}
663
665{
667 if (!str)
668 return points;
669 points.reserve(32);
670
671 while (str->isSpace())
672 ++str;
673 while (isDigit(str->unicode()) ||
674 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
675 *str == QLatin1Char('.')) {
676
678
679 while (str->isSpace())
680 ++str;
681 if (*str == QLatin1Char(','))
682 ++str;
683
684 //eat the rest of space
685 while (str->isSpace())
686 ++str;
687 }
688
689 return points;
690}
691
693 const char *pattern = nullptr)
694{
695 const size_t patternLen = qstrlen(pattern);
696 while (str->isSpace())
697 ++str;
698 while (isDigit(str->unicode()) ||
699 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
700 *str == QLatin1Char('.')) {
701
702 if (patternLen && pattern[points.size() % patternLen] == 'f') {
703 // flag expected, may only be 0 or 1
704 if (*str != QLatin1Char('0') && *str != QLatin1Char('1'))
705 return;
706 points.append(*str == QLatin1Char('0') ? 0.0 : 1.0);
707 ++str;
708 } else {
710 }
711
712 while (str->isSpace())
713 ++str;
714 if (*str == QLatin1Char(','))
715 ++str;
716
717 //eat the rest of space
718 while (str->isSpace())
719 ++str;
720 }
721}
722
724{
726 if (!str)
727 return points;
728
729 while (str->isSpace())
730 ++str;
731 while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
732 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
733 *str == QLatin1Char('.')) {
734
736
737 while (str->isSpace())
738 ++str;
739 if (*str == QLatin1Char('%'))
740 ++str;
741 while (str->isSpace())
742 ++str;
743 if (*str == QLatin1Char(','))
744 ++str;
745
746 //eat the rest of space
747 while (str->isSpace())
748 ++str;
749 }
750
751 return points;
752}
753
755{
756 // The form is url(<IRI>), where IRI can be
757 // just an ID on #<id> form.
758 QString::const_iterator itr = url.constBegin();
759 QString::const_iterator end = url.constEnd();
760 QString id;
761 while (itr != end && (*itr).isSpace())
762 ++itr;
763 if (itr != end && (*itr) == QLatin1Char('('))
764 ++itr;
765 else
766 return QString();
767 while (itr != end && (*itr).isSpace())
768 ++itr;
769 if (itr != end && (*itr) == QLatin1Char('#')) {
770 id += *itr;
771 ++itr;
772 } else {
773 return QString();
774 }
775 while (itr != end && (*itr) != QLatin1Char(')')) {
776 id += *itr;
777 ++itr;
778 }
779 if (itr == end || (*itr) != QLatin1Char(')'))
780 return QString();
781 return id;
782}
783
788static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
789{
790 QStringView colorStrTr = colorStr.trimmed();
791 if (colorStrTr.isEmpty())
792 return false;
793
794 switch(colorStrTr.at(0).unicode()) {
795
796 case '#':
797 {
798 // #rrggbb is very very common, so let's tackle it here
799 // rather than falling back to QColor
800 QRgb rgb;
801 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
802 if (ok)
803 color.setRgb(rgb);
804 return ok;
805 }
806 break;
807
808 case 'r':
809 {
810 // starts with "rgb(", ends with ")" and consists of at least 7 characters "rgb(,,)"
811 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(')')
812 && colorStrTr.mid(0, 4) == QLatin1String("rgb(")) {
813 const QChar *s = colorStrTr.constData() + 4;
815 //1 means that it failed after reaching non-parsable
816 //character which is going to be "%"
817 if (compo.size() == 1) {
818 s = colorStrTr.constData() + 4;
819 compo = parsePercentageList(s);
820 for (int i = 0; i < compo.size(); ++i)
821 compo[i] *= (qreal)2.55;
822 }
823
824 if (compo.size() == 3) {
825 color = QColor(int(compo[0]),
826 int(compo[1]),
827 int(compo[2]));
828 return true;
829 }
830 return false;
831 }
832 }
833 break;
834
835 case 'c':
836 if (colorStrTr == QLatin1String("currentColor")) {
837 color = handler->currentColor();
838 return true;
839 }
840 break;
841 case 'i':
842 if (colorStrTr == QT_INHERIT)
843 return false;
844 break;
845 default:
846 break;
847 }
848
849 color = QColor::fromString(colorStrTr.toString());
850 return color.isValid();
851}
852
853static bool constructColor(QStringView colorStr, QStringView opacity,
854 QColor &color, QSvgHandler *handler)
855{
856 if (!resolveColor(colorStr, color, handler))
857 return false;
858 if (!opacity.isEmpty()) {
859 bool ok = true;
860 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));
861 if (!ok)
862 op = 1.0;
863 color.setAlphaF(op);
864 }
865 return true;
866}
867
869 QSvgHandler *handler, bool *ok = NULL)
870{
871 QStringView numStr = str.trimmed();
872
873 if (numStr.endsWith(QLatin1Char('%'))) {
874 numStr.chop(1);
876 } else if (numStr.endsWith(QLatin1String("px"))) {
877 numStr.chop(2);
879 } else if (numStr.endsWith(QLatin1String("pc"))) {
880 numStr.chop(2);
882 } else if (numStr.endsWith(QLatin1String("pt"))) {
883 numStr.chop(2);
885 } else if (numStr.endsWith(QLatin1String("mm"))) {
886 numStr.chop(2);
888 } else if (numStr.endsWith(QLatin1String("cm"))) {
889 numStr.chop(2);
891 } else if (numStr.endsWith(QLatin1String("in"))) {
892 numStr.chop(2);
894 } else {
895 type = handler->defaultCoordinateSystem();
896 //type = QSvgHandler::LT_OTHER;
897 }
898 qreal len = toDouble(numStr, ok);
899 //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
900 return len;
901}
902
903static inline qreal convertToNumber(QStringView str, QSvgHandler *handler, bool *ok = NULL)
904{
906 qreal num = parseLength(str.toString(), type, handler, ok);
908 num = num/100.0;
909 }
910 return num;
911}
912
913static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes)
914{
915 QStringView uncStr = attributes.value(QLatin1String("unicode"));
916 QStringView havStr = attributes.value(QLatin1String("horiz-adv-x"));
917 QStringView pathStr = attributes.value(QLatin1String("d"));
918
919 QChar unicode = (uncStr.isEmpty()) ? u'\0' : uncStr.at(0);
920 qreal havx = (havStr.isEmpty()) ? -1 : toDouble(havStr);
922 path.setFillRule(Qt::WindingFill);
923 parsePathDataFast(pathStr, path);
924
925 font->addGlyph(unicode, path, havx);
926
927 return true;
928}
929
930// this should really be called convertToDefaultCoordinateSystem
931// and convert when type != QSvgHandler::defaultCoordinateSystem
933{
934
935 switch (type) {
937 break;
939 break;
941 break;
943 return len * 1.25;
944 break;
946 return len * 3.543307;
947 break;
949 return len * 35.43307;
950 break;
952 return len * 90;
953 break;
955 break;
956 default:
957 break;
958 }
959 return len;
960}
961
962static void parseColor(QSvgNode *,
963 const QSvgAttributes &attributes,
964 QSvgHandler *handler)
965{
967 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
968 handler->popColor();
969 handler->pushColor(color);
970 }
971}
972
974{
975 return node ? node->styleProperty(idFromUrl(url)) : 0;
976}
977
978static void parseBrush(QSvgNode *node,
979 const QSvgAttributes &attributes,
980 QSvgHandler *handler)
981{
982 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
983 QSvgFillStyle *prop = new QSvgFillStyle;
984
985 //fill-rule attribute handling
986 if (!attributes.fillRule.isEmpty() && attributes.fillRule != QT_INHERIT) {
987 if (attributes.fillRule == QLatin1String("evenodd"))
989 else if (attributes.fillRule == QLatin1String("nonzero"))
991 }
992
993 //fill-opacity attribute handling
994 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != QT_INHERIT) {
995 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.fillOpacity))));
996 }
997
998 //fill attribute handling
999 if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
1000 if (attributes.fill.size() > 3 && attributes.fill.mid(0, 3) == QLatin1String("url")) {
1001 QString value = attributes.fill.mid(3, attributes.fill.size() - 3).toString();
1002 QSvgStyleProperty *style = styleFromUrl(node, value);
1003 if (style) {
1005 prop->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
1006 } else {
1007 QString id = idFromUrl(value);
1008 prop->setGradientId(id);
1009 prop->setGradientResolved(false);
1010 }
1011 } else if (attributes.fill != QLatin1String("none")) {
1012 QColor color;
1013 if (resolveColor(attributes.fill, color, handler))
1014 prop->setBrush(QBrush(color));
1015 } else {
1016 prop->setBrush(QBrush(Qt::NoBrush));
1017 }
1018 }
1019 node->appendStyleProperty(prop, attributes.id);
1020 }
1021}
1022
1023
1024
1026{
1027 if (value.isEmpty())
1028 return QTransform();
1029
1031 const QChar *str = value.constData();
1032 const QChar *end = str + value.size();
1033
1034 while (str < end) {
1035 if (str->isSpace() || *str == QLatin1Char(',')) {
1036 ++str;
1037 continue;
1038 }
1039 enum State {
1040 Matrix,
1041 Translate,
1042 Rotate,
1043 Scale,
1044 SkewX,
1045 SkewY
1046 };
1047 State state = Matrix;
1048 if (*str == QLatin1Char('m')) { //matrix
1049 const char *ident = "atrix";
1050 for (int i = 0; i < 5; ++i)
1051 if (*(++str) != QLatin1Char(ident[i]))
1052 goto error;
1053 ++str;
1054 state = Matrix;
1055 } else if (*str == QLatin1Char('t')) { //translate
1056 const char *ident = "ranslate";
1057 for (int i = 0; i < 8; ++i)
1058 if (*(++str) != QLatin1Char(ident[i]))
1059 goto error;
1060 ++str;
1061 state = Translate;
1062 } else if (*str == QLatin1Char('r')) { //rotate
1063 const char *ident = "otate";
1064 for (int i = 0; i < 5; ++i)
1065 if (*(++str) != QLatin1Char(ident[i]))
1066 goto error;
1067 ++str;
1068 state = Rotate;
1069 } else if (*str == QLatin1Char('s')) { //scale, skewX, skewY
1070 ++str;
1071 if (*str == QLatin1Char('c')) {
1072 const char *ident = "ale";
1073 for (int i = 0; i < 3; ++i)
1074 if (*(++str) != QLatin1Char(ident[i]))
1075 goto error;
1076 ++str;
1077 state = Scale;
1078 } else if (*str == QLatin1Char('k')) {
1079 if (*(++str) != QLatin1Char('e'))
1080 goto error;
1081 if (*(++str) != QLatin1Char('w'))
1082 goto error;
1083 ++str;
1084 if (*str == QLatin1Char('X'))
1085 state = SkewX;
1086 else if (*str == QLatin1Char('Y'))
1087 state = SkewY;
1088 else
1089 goto error;
1090 ++str;
1091 } else {
1092 goto error;
1093 }
1094 } else {
1095 goto error;
1096 }
1097
1098
1099 while (str < end && str->isSpace())
1100 ++str;
1101 if (*str != QLatin1Char('('))
1102 goto error;
1103 ++str;
1106 if (*str != QLatin1Char(')'))
1107 goto error;
1108 ++str;
1109
1110 if(state == Matrix) {
1111 if(points.size() != 6)
1112 goto error;
1113 matrix = QTransform(points[0], points[1],
1114 points[2], points[3],
1115 points[4], points[5]) * matrix;
1116 } else if (state == Translate) {
1117 if (points.size() == 1)
1118 matrix.translate(points[0], 0);
1119 else if (points.size() == 2)
1120 matrix.translate(points[0], points[1]);
1121 else
1122 goto error;
1123 } else if (state == Rotate) {
1124 if(points.size() == 1) {
1125 matrix.rotate(points[0]);
1126 } else if (points.size() == 3) {
1127 matrix.translate(points[1], points[2]);
1128 matrix.rotate(points[0]);
1129 matrix.translate(-points[1], -points[2]);
1130 } else {
1131 goto error;
1132 }
1133 } else if (state == Scale) {
1134 if (points.size() < 1 || points.size() > 2)
1135 goto error;
1136 qreal sx = points[0];
1137 qreal sy = sx;
1138 if(points.size() == 2)
1139 sy = points[1];
1140 matrix.scale(sx, sy);
1141 } else if (state == SkewX) {
1142 if (points.size() != 1)
1143 goto error;
1144 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
1145 } else if (state == SkewY) {
1146 if (points.size() != 1)
1147 goto error;
1148 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
1149 }
1150 }
1151 error:
1152 return matrix;
1153}
1154
1155static void parsePen(QSvgNode *node,
1156 const QSvgAttributes &attributes,
1157 QSvgHandler *handler)
1158{
1159 //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
1160
1161 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
1162 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
1163 || !attributes.vectorEffect.isEmpty()) {
1164
1165 QSvgStrokeStyle *prop = new QSvgStrokeStyle;
1166
1167 //stroke attribute handling
1168 if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
1169 if (attributes.stroke.size() > 3 && attributes.stroke.mid(0, 3) == QLatin1String("url")) {
1170 QString value = attributes.stroke.mid(3, attributes.stroke.size() - 3).toString();
1171 QSvgStyleProperty *style = styleFromUrl(node, value);
1172 if (style) {
1174 prop->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
1175 } else {
1176 QString id = idFromUrl(value);
1177 prop->setGradientId(id);
1178 prop->setGradientResolved(false);
1179 }
1180 } else if (attributes.stroke != QLatin1String("none")) {
1181 QColor color;
1182 if (resolveColor(attributes.stroke, color, handler))
1183 prop->setStroke(QBrush(color));
1184 } else {
1186 }
1187 }
1188
1189 //stroke-width handling
1190 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != QT_INHERIT) {
1192 prop->setWidth(parseLength(attributes.strokeWidth, lt, handler));
1193 }
1194
1195 //stroke-dasharray
1196 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != QT_INHERIT) {
1197 if (attributes.strokeDashArray == QLatin1String("none")) {
1198 prop->setDashArrayNone();
1199 } else {
1201 const QChar *s = dashArray.constData();
1203 // if the dash count is odd the dashes should be duplicated
1204 if ((dashes.size() & 1) != 0)
1205 dashes << QList<qreal>(dashes);
1206 prop->setDashArray(dashes);
1207 }
1208 }
1209
1210 //stroke-linejoin attribute handling
1211 if (!attributes.strokeLineJoin.isEmpty()) {
1212 if (attributes.strokeLineJoin == QLatin1String("miter"))
1214 else if (attributes.strokeLineJoin == QLatin1String("round"))
1216 else if (attributes.strokeLineJoin == QLatin1String("bevel"))
1218 }
1219
1220 //stroke-linecap attribute handling
1221 if (!attributes.strokeLineCap.isEmpty()) {
1222 if (attributes.strokeLineCap == QLatin1String("butt"))
1223 prop->setLineCap(Qt::FlatCap);
1224 else if (attributes.strokeLineCap == QLatin1String("round"))
1225 prop->setLineCap(Qt::RoundCap);
1226 else if (attributes.strokeLineCap == QLatin1String("square"))
1228 }
1229
1230 //stroke-dashoffset attribute handling
1231 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != QT_INHERIT)
1232 prop->setDashOffset(toDouble(attributes.strokeDashOffset));
1233
1234 //vector-effect attribute handling
1235 if (!attributes.vectorEffect.isEmpty()) {
1236 if (attributes.vectorEffect == QLatin1String("non-scaling-stroke"))
1237 prop->setVectorEffect(true);
1238 else if (attributes.vectorEffect == QLatin1String("none"))
1239 prop->setVectorEffect(false);
1240 }
1241
1242 //stroke-miterlimit
1243 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != QT_INHERIT)
1244 prop->setMiterLimit(toDouble(attributes.strokeMiterLimit));
1245
1246 //stroke-opacity atttribute handling
1247 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != QT_INHERIT)
1248 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.strokeOpacity))));
1249
1250 node->appendStyleProperty(prop, attributes.id);
1251 }
1252}
1253
1256
1257static const qreal sizeTable[] =
1258{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
1259
1261
1263{
1264 switch (spec.at(0).unicode()) {
1265 case 'x':
1266 if (spec == QLatin1String("xx-small"))
1267 return XXSmall;
1268 if (spec == QLatin1String("x-small"))
1269 return XSmall;
1270 if (spec == QLatin1String("x-large"))
1271 return XLarge;
1272 if (spec == QLatin1String("xx-large"))
1273 return XXLarge;
1274 break;
1275 case 's':
1276 if (spec == QLatin1String("small"))
1277 return Small;
1278 break;
1279 case 'm':
1280 if (spec == QLatin1String("medium"))
1281 return Medium;
1282 break;
1283 case 'l':
1284 if (spec == QLatin1String("large"))
1285 return Large;
1286 break;
1287 case 'n':
1288 if (spec == QLatin1String("none"))
1289 return FontSizeNone;
1290 break;
1291 default:
1292 break;
1293 }
1294 return FontSizeValue;
1295}
1296
1297static void parseFont(QSvgNode *node,
1298 const QSvgAttributes &attributes,
1299 QSvgHandler *handler)
1300{
1301 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
1302 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
1303 return;
1304
1305 QSvgTinyDocument *doc = node->document();
1306 QSvgFontStyle *fontStyle = nullptr;
1307 if (!attributes.fontFamily.isEmpty()) {
1308 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
1309 if (svgFont)
1310 fontStyle = new QSvgFontStyle(svgFont, doc);
1311 }
1312 if (!fontStyle)
1314 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT) {
1315 QString family = attributes.fontFamily.toString().trimmed();
1316 if (family.at(0) == QLatin1Char('\'') || family.at(0) == QLatin1Char('\"'))
1317 family = family.mid(1, family.size() - 2);
1318 fontStyle->setFamily(family);
1319 }
1320
1321 if (!attributes.fontSize.isEmpty() && attributes.fontSize != QT_INHERIT) {
1322 // TODO: Support relative sizes 'larger' and 'smaller'.
1323 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
1324 switch (spec) {
1325 case FontSizeNone:
1326 break;
1327 case FontSizeValue: {
1329 qreal fs = parseLength(attributes.fontSize, type, handler);
1330 fs = convertToPixels(fs, true, type);
1331 fontStyle->setSize(qMin(fs, qreal(0xffff)));
1332 }
1333 break;
1334 default:
1335 fontStyle->setSize(sizeTable[spec]);
1336 break;
1337 }
1338 }
1339
1340 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
1341 if (attributes.fontStyle == QLatin1String("normal")) {
1342 fontStyle->setStyle(QFont::StyleNormal);
1343 } else if (attributes.fontStyle == QLatin1String("italic")) {
1344 fontStyle->setStyle(QFont::StyleItalic);
1345 } else if (attributes.fontStyle == QLatin1String("oblique")) {
1346 fontStyle->setStyle(QFont::StyleOblique);
1347 }
1348 }
1349
1350 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
1351 bool ok = false;
1352 const int weightNum = attributes.fontWeight.toInt(&ok);
1353 if (ok) {
1354 fontStyle->setWeight(weightNum);
1355 } else {
1356 if (attributes.fontWeight == QLatin1String("normal")) {
1357 fontStyle->setWeight(QFont::Normal);
1358 } else if (attributes.fontWeight == QLatin1String("bold")) {
1359 fontStyle->setWeight(QFont::Bold);
1360 } else if (attributes.fontWeight == QLatin1String("bolder")) {
1361 fontStyle->setWeight(QSvgFontStyle::BOLDER);
1362 } else if (attributes.fontWeight == QLatin1String("lighter")) {
1364 }
1365 }
1366 }
1367
1368 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
1369 if (attributes.fontVariant == QLatin1String("normal"))
1370 fontStyle->setVariant(QFont::MixedCase);
1371 else if (attributes.fontVariant == QLatin1String("small-caps"))
1372 fontStyle->setVariant(QFont::SmallCaps);
1373 }
1374
1375 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
1376 if (attributes.textAnchor == QLatin1String("start"))
1377 fontStyle->setTextAnchor(Qt::AlignLeft);
1378 if (attributes.textAnchor == QLatin1String("middle"))
1379 fontStyle->setTextAnchor(Qt::AlignHCenter);
1380 else if (attributes.textAnchor == QLatin1String("end"))
1381 fontStyle->setTextAnchor(Qt::AlignRight);
1382 }
1383
1384 node->appendStyleProperty(fontStyle, attributes.id);
1385}
1386
1387static void parseTransform(QSvgNode *node,
1388 const QSvgAttributes &attributes,
1389 QSvgHandler *)
1390{
1391 if (attributes.transform.isEmpty())
1392 return;
1394
1395 if (!matrix.isIdentity()) {
1397 }
1398
1399}
1400
1401static void parseVisibility(QSvgNode *node,
1402 const QSvgAttributes &attributes,
1403 QSvgHandler *)
1404{
1405 QSvgNode *parent = node->parent();
1406
1407 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
1408 node->setVisible(parent->isVisible());
1409 else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
1410 node->setVisible(false);
1411 } else
1412 node->setVisible(true);
1413}
1414
1416 qreal xc, qreal yc,
1417 qreal th0, qreal th1,
1418 qreal rx, qreal ry, qreal xAxisRotation)
1419{
1420 qreal sinTh, cosTh;
1421 qreal a00, a01, a10, a11;
1422 qreal x1, y1, x2, y2, x3, y3;
1423 qreal t;
1424 qreal thHalf;
1425
1426 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
1427 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
1428
1429 a00 = cosTh * rx;
1430 a01 = -sinTh * ry;
1431 a10 = sinTh * rx;
1432 a11 = cosTh * ry;
1433
1434 thHalf = 0.5 * (th1 - th0);
1435 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
1436 x1 = xc + qCos(th0) - t * qSin(th0);
1437 y1 = yc + qSin(th0) + t * qCos(th0);
1438 x3 = xc + qCos(th1);
1439 y3 = yc + qSin(th1);
1440 x2 = x3 + t * qSin(th1);
1441 y2 = y3 - t * qCos(th1);
1442
1443 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
1444 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
1445 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
1446}
1447
1448// the arc handling code underneath is from XSVG (BSD license)
1449/*
1450 * Copyright 2002 USC/Information Sciences Institute
1451 *
1452 * Permission to use, copy, modify, distribute, and sell this software
1453 * and its documentation for any purpose is hereby granted without
1454 * fee, provided that the above copyright notice appear in all copies
1455 * and that both that copyright notice and this permission notice
1456 * appear in supporting documentation, and that the name of
1457 * Information Sciences Institute not be used in advertising or
1458 * publicity pertaining to distribution of the software without
1459 * specific, written prior permission. Information Sciences Institute
1460 * makes no representations about the suitability of this software for
1461 * any purpose. It is provided "as is" without express or implied
1462 * warranty.
1463 *
1464 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
1465 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
1466 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
1467 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1468 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
1469 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1470 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1471 * PERFORMANCE OF THIS SOFTWARE.
1472 *
1473 */
1475 qreal rx,
1476 qreal ry,
1477 qreal x_axis_rotation,
1478 int large_arc_flag,
1479 int sweep_flag,
1480 qreal x,
1481 qreal y,
1482 qreal curx, qreal cury)
1483{
1484 const qreal Pr1 = rx * rx;
1485 const qreal Pr2 = ry * ry;
1486
1487 if (!Pr1 || !Pr2)
1488 return;
1489
1490 qreal sin_th, cos_th;
1491 qreal a00, a01, a10, a11;
1492 qreal x0, y0, x1, y1, xc, yc;
1493 qreal d, sfactor, sfactor_sq;
1494 qreal th0, th1, th_arc;
1495 int i, n_segs;
1496 qreal dx, dy, dx1, dy1, Px, Py, check;
1497
1498 rx = qAbs(rx);
1499 ry = qAbs(ry);
1500
1501 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
1502 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
1503
1504 dx = (curx - x) / 2.0;
1505 dy = (cury - y) / 2.0;
1506 dx1 = cos_th * dx + sin_th * dy;
1507 dy1 = -sin_th * dx + cos_th * dy;
1508 Px = dx1 * dx1;
1509 Py = dy1 * dy1;
1510 /* Spec : check if radii are large enough */
1511 check = Px / Pr1 + Py / Pr2;
1512 if (check > 1) {
1513 rx = rx * qSqrt(check);
1514 ry = ry * qSqrt(check);
1515 }
1516
1517 a00 = cos_th / rx;
1518 a01 = sin_th / rx;
1519 a10 = -sin_th / ry;
1520 a11 = cos_th / ry;
1521 x0 = a00 * curx + a01 * cury;
1522 y0 = a10 * curx + a11 * cury;
1523 x1 = a00 * x + a01 * y;
1524 y1 = a10 * x + a11 * y;
1525 /* (x0, y0) is current point in transformed coordinate space.
1526 (x1, y1) is new point in transformed coordinate space.
1527
1528 The arc fits a unit-radius circle in this space.
1529 */
1530 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
1531 if (!d)
1532 return;
1533 sfactor_sq = 1.0 / d - 0.25;
1534 if (sfactor_sq < 0) sfactor_sq = 0;
1535 sfactor = qSqrt(sfactor_sq);
1536 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
1537 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
1538 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
1539 /* (xc, yc) is center of the circle. */
1540
1541 th0 = qAtan2(y0 - yc, x0 - xc);
1542 th1 = qAtan2(y1 - yc, x1 - xc);
1543
1544 th_arc = th1 - th0;
1545 if (th_arc < 0 && sweep_flag)
1546 th_arc += 2 * Q_PI;
1547 else if (th_arc > 0 && !sweep_flag)
1548 th_arc -= 2 * Q_PI;
1549
1550 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
1551
1552 for (i = 0; i < n_segs; i++) {
1553 pathArcSegment(path, xc, yc,
1554 th0 + i * th_arc / n_segs,
1555 th0 + (i + 1) * th_arc / n_segs,
1556 rx, ry, x_axis_rotation);
1557 }
1558}
1559
1561{
1562 const int maxElementCount = 0x7fff; // Assume file corruption if more path elements than this
1563 qreal x0 = 0, y0 = 0; // starting point
1564 qreal x = 0, y = 0; // current point
1565 char lastMode = 0;
1566 QPointF ctrlPt;
1567 const QChar *str = dataStr.constData();
1568 const QChar *end = str + dataStr.size();
1569
1570 bool ok = true;
1571 while (ok && str != end) {
1572 while (str->isSpace() && (str + 1) != end)
1573 ++str;
1574 QChar pathElem = *str;
1575 ++str;
1576 QChar endc = *end;
1577 *const_cast<QChar *>(end) = u'\0'; // parseNumbersArray requires 0-termination that QStringView cannot guarantee
1578 const char *pattern = nullptr;
1579 if (pathElem == QLatin1Char('a') || pathElem == QLatin1Char('A'))
1580 pattern = "rrrffrr";
1583 *const_cast<QChar *>(end) = endc;
1584 if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
1585 arg.append(0);//dummy
1586 const qreal *num = arg.constData();
1587 int count = arg.size();
1588 while (ok && count > 0) {
1589 qreal offsetX = x; // correction offsets
1590 qreal offsetY = y; // for relative commands
1591 switch (pathElem.unicode()) {
1592 case 'm': {
1593 if (count < 2) {
1594 ok = false;
1595 break;
1596 }
1597 x = x0 = num[0] + offsetX;
1598 y = y0 = num[1] + offsetY;
1599 num += 2;
1600 count -= 2;
1601 path.moveTo(x0, y0);
1602
1603 // As per 1.2 spec 8.3.2 The "moveto" commands
1604 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1605 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1606 pathElem = QLatin1Char('l');
1607 }
1608 break;
1609 case 'M': {
1610 if (count < 2) {
1611 ok = false;
1612 break;
1613 }
1614 x = x0 = num[0];
1615 y = y0 = num[1];
1616 num += 2;
1617 count -= 2;
1618 path.moveTo(x0, y0);
1619
1620 // As per 1.2 spec 8.3.2 The "moveto" commands
1621 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1622 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1623 pathElem = QLatin1Char('L');
1624 }
1625 break;
1626 case 'z':
1627 case 'Z': {
1628 x = x0;
1629 y = y0;
1630 count--; // skip dummy
1631 num++;
1632 path.closeSubpath();
1633 }
1634 break;
1635 case 'l': {
1636 if (count < 2) {
1637 ok = false;
1638 break;
1639 }
1640 x = num[0] + offsetX;
1641 y = num[1] + offsetY;
1642 num += 2;
1643 count -= 2;
1644 path.lineTo(x, y);
1645
1646 }
1647 break;
1648 case 'L': {
1649 if (count < 2) {
1650 ok = false;
1651 break;
1652 }
1653 x = num[0];
1654 y = num[1];
1655 num += 2;
1656 count -= 2;
1657 path.lineTo(x, y);
1658 }
1659 break;
1660 case 'h': {
1661 x = num[0] + offsetX;
1662 num++;
1663 count--;
1664 path.lineTo(x, y);
1665 }
1666 break;
1667 case 'H': {
1668 x = num[0];
1669 num++;
1670 count--;
1671 path.lineTo(x, y);
1672 }
1673 break;
1674 case 'v': {
1675 y = num[0] + offsetY;
1676 num++;
1677 count--;
1678 path.lineTo(x, y);
1679 }
1680 break;
1681 case 'V': {
1682 y = num[0];
1683 num++;
1684 count--;
1685 path.lineTo(x, y);
1686 }
1687 break;
1688 case 'c': {
1689 if (count < 6) {
1690 ok = false;
1691 break;
1692 }
1693 QPointF c1(num[0] + offsetX, num[1] + offsetY);
1694 QPointF c2(num[2] + offsetX, num[3] + offsetY);
1695 QPointF e(num[4] + offsetX, num[5] + offsetY);
1696 num += 6;
1697 count -= 6;
1698 path.cubicTo(c1, c2, e);
1699 ctrlPt = c2;
1700 x = e.x();
1701 y = e.y();
1702 break;
1703 }
1704 case 'C': {
1705 if (count < 6) {
1706 ok = false;
1707 break;
1708 }
1709 QPointF c1(num[0], num[1]);
1710 QPointF c2(num[2], num[3]);
1711 QPointF e(num[4], num[5]);
1712 num += 6;
1713 count -= 6;
1714 path.cubicTo(c1, c2, e);
1715 ctrlPt = c2;
1716 x = e.x();
1717 y = e.y();
1718 break;
1719 }
1720 case 's': {
1721 if (count < 4) {
1722 ok = false;
1723 break;
1724 }
1725 QPointF c1;
1726 if (lastMode == 'c' || lastMode == 'C' ||
1727 lastMode == 's' || lastMode == 'S')
1728 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1729 else
1730 c1 = QPointF(x, y);
1731 QPointF c2(num[0] + offsetX, num[1] + offsetY);
1732 QPointF e(num[2] + offsetX, num[3] + offsetY);
1733 num += 4;
1734 count -= 4;
1735 path.cubicTo(c1, c2, e);
1736 ctrlPt = c2;
1737 x = e.x();
1738 y = e.y();
1739 break;
1740 }
1741 case 'S': {
1742 if (count < 4) {
1743 ok = false;
1744 break;
1745 }
1746 QPointF c1;
1747 if (lastMode == 'c' || lastMode == 'C' ||
1748 lastMode == 's' || lastMode == 'S')
1749 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1750 else
1751 c1 = QPointF(x, y);
1752 QPointF c2(num[0], num[1]);
1753 QPointF e(num[2], num[3]);
1754 num += 4;
1755 count -= 4;
1756 path.cubicTo(c1, c2, e);
1757 ctrlPt = c2;
1758 x = e.x();
1759 y = e.y();
1760 break;
1761 }
1762 case 'q': {
1763 if (count < 4) {
1764 ok = false;
1765 break;
1766 }
1767 QPointF c(num[0] + offsetX, num[1] + offsetY);
1768 QPointF e(num[2] + offsetX, num[3] + offsetY);
1769 num += 4;
1770 count -= 4;
1771 path.quadTo(c, e);
1772 ctrlPt = c;
1773 x = e.x();
1774 y = e.y();
1775 break;
1776 }
1777 case 'Q': {
1778 if (count < 4) {
1779 ok = false;
1780 break;
1781 }
1782 QPointF c(num[0], num[1]);
1783 QPointF e(num[2], num[3]);
1784 num += 4;
1785 count -= 4;
1786 path.quadTo(c, e);
1787 ctrlPt = c;
1788 x = e.x();
1789 y = e.y();
1790 break;
1791 }
1792 case 't': {
1793 if (count < 2) {
1794 ok = false;
1795 break;
1796 }
1797 QPointF e(num[0] + offsetX, num[1] + offsetY);
1798 num += 2;
1799 count -= 2;
1800 QPointF c;
1801 if (lastMode == 'q' || lastMode == 'Q' ||
1802 lastMode == 't' || lastMode == 'T')
1803 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1804 else
1805 c = QPointF(x, y);
1806 path.quadTo(c, e);
1807 ctrlPt = c;
1808 x = e.x();
1809 y = e.y();
1810 break;
1811 }
1812 case 'T': {
1813 if (count < 2) {
1814 ok = false;
1815 break;
1816 }
1817 QPointF e(num[0], num[1]);
1818 num += 2;
1819 count -= 2;
1820 QPointF c;
1821 if (lastMode == 'q' || lastMode == 'Q' ||
1822 lastMode == 't' || lastMode == 'T')
1823 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1824 else
1825 c = QPointF(x, y);
1826 path.quadTo(c, e);
1827 ctrlPt = c;
1828 x = e.x();
1829 y = e.y();
1830 break;
1831 }
1832 case 'a': {
1833 if (count < 7) {
1834 ok = false;
1835 break;
1836 }
1837 qreal rx = (*num++);
1838 qreal ry = (*num++);
1839 qreal xAxisRotation = (*num++);
1840 qreal largeArcFlag = (*num++);
1841 qreal sweepFlag = (*num++);
1842 qreal ex = (*num++) + offsetX;
1843 qreal ey = (*num++) + offsetY;
1844 count -= 7;
1845 qreal curx = x;
1846 qreal cury = y;
1847 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1848 int(sweepFlag), ex, ey, curx, cury);
1849
1850 x = ex;
1851 y = ey;
1852 }
1853 break;
1854 case 'A': {
1855 if (count < 7) {
1856 ok = false;
1857 break;
1858 }
1859 qreal rx = (*num++);
1860 qreal ry = (*num++);
1861 qreal xAxisRotation = (*num++);
1862 qreal largeArcFlag = (*num++);
1863 qreal sweepFlag = (*num++);
1864 qreal ex = (*num++);
1865 qreal ey = (*num++);
1866 count -= 7;
1867 qreal curx = x;
1868 qreal cury = y;
1869 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1870 int(sweepFlag), ex, ey, curx, cury);
1871
1872 x = ex;
1873 y = ey;
1874 }
1875 break;
1876 default:
1877 ok = false;
1878 break;
1879 }
1880 lastMode = pathElem.toLatin1();
1881 if (path.elementCount() > maxElementCount)
1882 ok = false;
1883 }
1884 }
1885 return ok;
1886}
1887
1888static bool parseStyle(QSvgNode *node,
1889 const QXmlStreamAttributes &attributes,
1890 QSvgHandler *);
1891
1892static bool parseStyle(QSvgNode *node,
1893 const QSvgAttributes &attributes,
1894 QSvgHandler *);
1895
1896#ifndef QT_NO_CSSPARSER
1897
1898static void parseCSStoXMLAttrs(const QList<QCss::Declaration> &declarations,
1899 QXmlStreamAttributes &attributes)
1900{
1901 for (int i = 0; i < declarations.size(); ++i) {
1902 const QCss::Declaration &decl = declarations.at(i);
1903 if (decl.d->property.isEmpty())
1904 continue;
1905 QCss::Value val = decl.d->values.first();
1906 QString valueStr;
1907 const int valCount = decl.d->values.size();
1908 if (valCount != 1) {
1909 for (int i = 0; i < valCount; ++i) {
1910 valueStr += decl.d->values[i].toString();
1911 if (i + 1 < valCount)
1912 valueStr += QLatin1Char(',');
1913 }
1914 } else {
1915 valueStr = val.toString();
1916 }
1917 if (val.type == QCss::Value::Uri) {
1918 valueStr.prepend(QLatin1String("url("));
1919 valueStr.append(QLatin1Char(')'));
1920 } else if (val.type == QCss::Value::Function) {
1921 QStringList lst = val.variant.toStringList();
1922 valueStr.append(lst.at(0));
1923 valueStr.append(QLatin1Char('('));
1924 for (int i = 1; i < lst.size(); ++i) {
1925 valueStr.append(lst.at(i));
1926 if ((i +1) < lst.size())
1927 valueStr.append(QLatin1Char(','));
1928 }
1929 valueStr.append(QLatin1Char(')'));
1930 } else if (val.type == QCss::Value::KnownIdentifier) {
1931 switch (val.variant.toInt()) {
1932 case QCss::Value_None:
1933 valueStr = QLatin1String("none");
1934 break;
1935 default:
1936 break;
1937 }
1938 }
1939
1940 attributes.append(QString(), decl.d->property, valueStr);
1941 }
1942}
1943
1945{
1946 // preprocess (for unicode escapes), tokenize and remove comments
1947 m_cssParser.init(css);
1948 QString key;
1949
1950 attributes->reserve(10);
1951
1952 while (m_cssParser.hasNext()) {
1953 m_cssParser.skipSpace();
1954
1955 if (!m_cssParser.hasNext())
1956 break;
1957 m_cssParser.next();
1958
1959 QString name;
1960 if (m_cssParser.hasEscapeSequences) {
1961 key = m_cssParser.lexem();
1962 name = key;
1963 } else {
1964 const QCss::Symbol &sym = m_cssParser.symbol();
1965 name = sym.text.mid(sym.start, sym.len);
1966 }
1967
1968 m_cssParser.skipSpace();
1969 if (!m_cssParser.test(QCss::COLON))
1970 break;
1971
1972 m_cssParser.skipSpace();
1973 if (!m_cssParser.hasNext())
1974 break;
1975
1977 attribute.name = name;
1978
1979 const int firstSymbol = m_cssParser.index;
1980 int symbolCount = 0;
1981 do {
1982 m_cssParser.next();
1983 ++symbolCount;
1984 } while (m_cssParser.hasNext() && !m_cssParser.test(QCss::SEMICOLON));
1985
1986 bool canExtractValueByRef = !m_cssParser.hasEscapeSequences;
1987 if (canExtractValueByRef) {
1988 int len = m_cssParser.symbols.at(firstSymbol).len;
1989 for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
1990 len += m_cssParser.symbols.at(i).len;
1991
1992 if (m_cssParser.symbols.at(i - 1).start + m_cssParser.symbols.at(i - 1).len
1993 != m_cssParser.symbols.at(i).start) {
1994 canExtractValueByRef = false;
1995 break;
1996 }
1997 }
1998 if (canExtractValueByRef) {
1999 const QCss::Symbol &sym = m_cssParser.symbols.at(firstSymbol);
2000 attribute.value = sym.text.mid(sym.start, len);
2001 }
2002 }
2003 if (!canExtractValueByRef) {
2004 QString value;
2005 for (int i = firstSymbol; i < m_cssParser.index - 1; ++i)
2006 value += m_cssParser.symbols.at(i).lexem();
2007 attribute.value = value;
2008 }
2009
2010 attributes->append(attribute);
2011
2012 m_cssParser.skipSpace();
2013 }
2014}
2015
2016static void cssStyleLookup(QSvgNode *node,
2017 QSvgHandler *handler,
2019{
2021 cssNode.ptr = node;
2022 QList<QCss::Declaration> decls = selector->declarationsForNode(cssNode);
2023
2024 QXmlStreamAttributes attributes;
2025 parseCSStoXMLAttrs(decls, attributes);
2026 parseStyle(node, attributes, handler);
2027}
2028
2029#endif // QT_NO_CSSPARSER
2030
2032{
2034 return lst;
2035}
2036
2037static bool parseCoreNode(QSvgNode *node,
2038 const QXmlStreamAttributes &attributes)
2039{
2040 QStringList features;
2041 QStringList extensions;
2042 QStringList languages;
2044 QStringList fonts;
2045 QString xmlClassStr;
2046
2047 for (int i = 0; i < attributes.size(); ++i) {
2048 const QXmlStreamAttribute &attribute = attributes.at(i);
2049 QStringView name = attribute.qualifiedName();
2050 if (name.isEmpty())
2051 continue;
2052 QStringView value = attribute.value();
2053 switch (name.at(0).unicode()) {
2054 case 'c':
2055 if (name == QLatin1String("class"))
2056 xmlClassStr = value.toString();
2057 break;
2058 case 'r':
2059 if (name == QLatin1String("requiredFeatures"))
2060 features = stringToList(value.toString());
2061 else if (name == QLatin1String("requiredExtensions"))
2062 extensions = stringToList(value.toString());
2063 else if (name == QLatin1String("requiredFormats"))
2064 formats = stringToList(value.toString());
2065 else if (name == QLatin1String("requiredFonts"))
2066 fonts = stringToList(value.toString());
2067 break;
2068 case 's':
2069 if (name == QLatin1String("systemLanguage"))
2070 languages = stringToList(value.toString());
2071 break;
2072 default:
2073 break;
2074 }
2075 }
2076
2077 node->setRequiredFeatures(features);
2078 node->setRequiredExtensions(extensions);
2079 node->setRequiredLanguages(languages);
2081 node->setRequiredFonts(fonts);
2082 node->setNodeId(someId(attributes));
2083 node->setXmlClass(xmlClassStr);
2084
2085 return true;
2086}
2087
2088static void parseOpacity(QSvgNode *node,
2089 const QSvgAttributes &attributes,
2090 QSvgHandler *)
2091{
2092 if (attributes.opacity.isEmpty())
2093 return;
2094
2095 const QStringView value = attributes.opacity.trimmed();
2096
2097 bool ok = false;
2098 qreal op = value.toDouble(&ok);
2099
2100 if (ok) {
2101 QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
2102 node->appendStyleProperty(opacity, attributes.id);
2103 }
2104}
2105
2107{
2108#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
2109 if (op == QLatin1String("clear")) {
2111 } else if (op == QLatin1String("src")) {
2113 } else if (op == QLatin1String("dst")) {
2115 } else if (op == QLatin1String("src-over")) {
2117 } else if (op == QLatin1String("dst-over")) {
2119 } else if (op == QLatin1String("src-in")) {
2121 } else if (op == QLatin1String("dst-in")) {
2123 } else if (op == QLatin1String("src-out")) {
2125 } else if (op == QLatin1String("dst-out")) {
2127 } else if (op == QLatin1String("src-atop")) {
2129 } else if (op == QLatin1String("dst-atop")) {
2131 } else if (op == QLatin1String("xor")) {
2133 } else if (op == QLatin1String("plus")) {
2135 } else if (op == QLatin1String("multiply")) {
2137 } else if (op == QLatin1String("screen")) {
2139 } else if (op == QLatin1String("overlay")) {
2141 } else if (op == QLatin1String("darken")) {
2143 } else if (op == QLatin1String("lighten")) {
2145 } else if (op == QLatin1String("color-dodge")) {
2147 } else if (op == QLatin1String("color-burn")) {
2149 } else if (op == QLatin1String("hard-light")) {
2151 } else if (op == QLatin1String("soft-light")) {
2153 } else if (op == QLatin1String("difference")) {
2155 } else if (op == QLatin1String("exclusion")) {
2157 } else {
2158 NOOP;
2159 }
2160
2162}
2163
2164static void parseCompOp(QSvgNode *node,
2165 const QSvgAttributes &attributes,
2166 QSvgHandler *)
2167{
2168 if (attributes.compOp.isEmpty())
2169 return;
2170 QString value = attributes.compOp.toString().trimmed();
2171
2172 if (!value.isEmpty()) {
2174 node->appendStyleProperty(compop, attributes.id);
2175 }
2176}
2177
2179{
2180 if (str == QLatin1String("inline")) {
2181 return QSvgNode::InlineMode;
2182 } else if (str == QLatin1String("block")) {
2183 return QSvgNode::BlockMode;
2184 } else if (str == QLatin1String("list-item")) {
2186 } else if (str == QLatin1String("run-in")) {
2187 return QSvgNode::RunInMode;
2188 } else if (str == QLatin1String("compact")) {
2189 return QSvgNode::CompactMode;
2190 } else if (str == QLatin1String("marker")) {
2191 return QSvgNode::MarkerMode;
2192 } else if (str == QLatin1String("table")) {
2193 return QSvgNode::TableMode;
2194 } else if (str == QLatin1String("inline-table")) {
2196 } else if (str == QLatin1String("table-row-group")) {
2198 } else if (str == QLatin1String("table-header-group")) {
2200 } else if (str == QLatin1String("table-footer-group")) {
2202 } else if (str == QLatin1String("table-row")) {
2204 } else if (str == QLatin1String("table-column-group")) {
2206 } else if (str == QLatin1String("table-column")) {
2208 } else if (str == QLatin1String("table-cell")) {
2210 } else if (str == QLatin1String("table-caption")) {
2212 } else if (str == QLatin1String("none")) {
2213 return QSvgNode::NoneMode;
2214 } else if (str == QT_INHERIT) {
2215 return QSvgNode::InheritMode;
2216 }
2217 return QSvgNode::BlockMode;
2218}
2219
2220static void parseOthers(QSvgNode *node,
2221 const QSvgAttributes &attributes,
2222 QSvgHandler *)
2223{
2224 if (attributes.display.isEmpty())
2225 return;
2226 QString displayStr = attributes.display.toString().trimmed();
2227
2228 if (!displayStr.isEmpty()) {
2229 node->setDisplayMode(displayStringToEnum(displayStr));
2230 }
2231}
2232
2234 const QSvgAttributes &attributes,
2235 QSvgHandler *)
2236{
2237 if (attributes.imageRendering.isEmpty())
2238 return;
2239
2240 QString ir = attributes.imageRendering.toString().trimmed();
2242 if (ir == QLatin1String("auto"))
2243 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
2244 else if (ir == QLatin1String("optimizeSpeed"))
2246 else if (ir == QLatin1String("optimizeQuality"))
2248 node->appendStyleProperty(p, attributes.id);
2249}
2250
2251
2252static bool parseStyle(QSvgNode *node,
2253 const QSvgAttributes &attributes,
2254 QSvgHandler *handler)
2255{
2256 parseColor(node, attributes, handler);
2257 parseBrush(node, attributes, handler);
2258 parsePen(node, attributes, handler);
2259 parseFont(node, attributes, handler);
2260 parseTransform(node, attributes, handler);
2261 parseVisibility(node, attributes, handler);
2262 parseOpacity(node, attributes, handler);
2263 parseCompOp(node, attributes, handler);
2264 parseRenderingHints(node, attributes, handler);
2265 parseOthers(node, attributes, handler);
2266
2267#if 0
2268 value = attributes.value("audio-level");
2269
2270 value = attributes.value("color-rendering");
2271
2272 value = attributes.value("display-align");
2273
2274 value = attributes.value("image-rendering");
2275
2276 value = attributes.value("line-increment");
2277
2278 value = attributes.value("pointer-events");
2279
2280 value = attributes.value("shape-rendering");
2281
2282 value = attributes.value("solid-color");
2283
2284 value = attributes.value("solid-opacity");
2285
2286 value = attributes.value("text-rendering");
2287
2288 value = attributes.value("vector-effect");
2289
2290 value = attributes.value("viewport-fill");
2291
2292 value = attributes.value("viewport-fill-opacity");
2293#endif
2294 return true;
2295}
2296
2297static bool parseStyle(QSvgNode *node,
2299 QSvgHandler *handler)
2300{
2301 return parseStyle(node, QSvgAttributes(attrs, handler), handler);
2302}
2303
2305 const QXmlStreamAttributes &attributes,
2306 QSvgHandler *)
2307{
2308 Q_UNUSED(parent); Q_UNUSED(attributes);
2309 return true;
2310}
2311
2313 const QXmlStreamAttributes &attributes,
2314 QSvgHandler *)
2315{
2316 Q_UNUSED(parent); Q_UNUSED(attributes);
2317 return true;
2318}
2319
2321{
2322 int res = 0;
2323 int ms = 1000;
2324 str = str.trimmed();
2325 if (str.endsWith(QLatin1String("ms"))) {
2326 str.chop(2);
2327 ms = 1;
2328 } else if (str.endsWith(QLatin1String("s"))) {
2329 str.chop(1);
2330 }
2331 double val = ms * toDouble(str, ok);
2332 if (ok) {
2333 if (val > std::numeric_limits<int>::min() && val < std::numeric_limits<int>::max())
2334 res = static_cast<int>(val);
2335 else
2336 *ok = false;
2337 }
2338 return res;
2339}
2340
2342 const QXmlStreamAttributes &attributes,
2343 QSvgHandler *handler)
2344{
2345 QStringView fromStr = attributes.value(QLatin1String("from"));
2346 QStringView toStr = attributes.value(QLatin1String("to"));
2347 QString valuesStr = attributes.value(QLatin1String("values")).toString();
2348 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2349 QString durStr = attributes.value(QLatin1String("dur")).toString();
2350 QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
2351 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2352 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2353
2355 if (valuesStr.isEmpty()) {
2356 QColor startColor, endColor;
2357 resolveColor(fromStr, startColor, handler);
2358 resolveColor(toStr, endColor, handler);
2359 colors.reserve(2);
2360 colors.append(startColor);
2361 colors.append(endColor);
2362 } else {
2363 QStringList str = valuesStr.split(QLatin1Char(';'));
2364 colors.reserve(str.size());
2365 QStringList::const_iterator itr;
2366 for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
2367 QColor color;
2368 resolveColor(*itr, color, handler);
2369 colors.append(color);
2370 }
2371 }
2372
2373 bool ok = true;
2374 int begin = parseClockValue(beginStr, &ok);
2375 if (!ok)
2376 return false;
2377 int end = begin + parseClockValue(durStr, &ok);
2378 if (!ok || end <= begin)
2379 return false;
2380
2381 QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
2382 anim->setArgs((targetStr == QLatin1String("fill")), colors);
2383 anim->setFreeze(fillStr == QLatin1String("freeze"));
2384 anim->setRepeatCount(
2385 (repeatStr == QLatin1String("indefinite")) ? -1 :
2386 (repeatStr == QLatin1String("")) ? 1 : toDouble(repeatStr));
2387
2388 parent->appendStyleProperty(anim, someId(attributes));
2389 parent->document()->setAnimated(true);
2390 handler->setAnimPeriod(begin, end);
2391 return true;
2392}
2393
2395 const QXmlStreamAttributes &attributes,
2396 QSvgHandler *)
2397{
2398 Q_UNUSED(parent); Q_UNUSED(attributes);
2399 return true;
2400}
2401
2403{
2405 values << list;
2406 for (int i = 3 - list.size(); i > 0; --i)
2407 values.append(0.0);
2408}
2409
2411 const QXmlStreamAttributes &attributes,
2412 QSvgHandler *handler)
2413{
2414 QString typeStr = attributes.value(QLatin1String("type")).toString();
2415 QString values = attributes.value(QLatin1String("values")).toString();
2416 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2417 QString durStr = attributes.value(QLatin1String("dur")).toString();
2418 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2419 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2420 QString fromStr = attributes.value(QLatin1String("from")).toString();
2421 QString toStr = attributes.value(QLatin1String("to")).toString();
2422 QString byStr = attributes.value(QLatin1String("by")).toString();
2423 QString addtv = attributes.value(QLatin1String("additive")).toString();
2424
2426 if (addtv == QLatin1String("sum"))
2427 additive = QSvgAnimateTransform::Sum;
2428
2429 QList<qreal> vals;
2430 if (values.isEmpty()) {
2431 const QChar *s;
2432 if (fromStr.isEmpty()) {
2433 if (!byStr.isEmpty()) {
2434 // By-animation.
2435 additive = QSvgAnimateTransform::Sum;
2436 vals.append(0.0);
2437 vals.append(0.0);
2438 vals.append(0.0);
2439 parseNumberTriplet(vals, s = byStr.constData());
2440 } else {
2441 // To-animation not defined.
2442 return false;
2443 }
2444 } else {
2445 if (!toStr.isEmpty()) {
2446 // From-to-animation.
2447 parseNumberTriplet(vals, s = fromStr.constData());
2448 parseNumberTriplet(vals, s = toStr.constData());
2449 } else if (!byStr.isEmpty()) {
2450 // From-by-animation.
2451 parseNumberTriplet(vals, s = fromStr.constData());
2452 parseNumberTriplet(vals, s = byStr.constData());
2453 for (int i = vals.size() - 3; i < vals.size(); ++i)
2454 vals[i] += vals[i - 3];
2455 } else {
2456 return false;
2457 }
2458 }
2459 } else {
2460 const QChar *s = values.constData();
2461 while (s && *s != QLatin1Char(0)) {
2462 parseNumberTriplet(vals, s);
2463 if (*s == QLatin1Char(0))
2464 break;
2465 ++s;
2466 }
2467 }
2468 if (vals.size() % 3 != 0)
2469 return false;
2470
2471 bool ok = true;
2472 int begin = parseClockValue(beginStr, &ok);
2473 if (!ok)
2474 return false;
2475 int end = begin + parseClockValue(durStr, &ok);
2476 if (!ok || end <= begin)
2477 return false;
2478
2480 if (typeStr == QLatin1String("translate")) {
2482 } else if (typeStr == QLatin1String("scale")) {
2484 } else if (typeStr == QLatin1String("rotate")) {
2486 } else if (typeStr == QLatin1String("skewX")) {
2488 } else if (typeStr == QLatin1String("skewY")) {
2490 } else {
2491 return false;
2492 }
2493
2495 anim->setArgs(type, additive, vals);
2496 anim->setFreeze(fillStr == QLatin1String("freeze"));
2497 anim->setRepeatCount(
2498 (repeatStr == QLatin1String("indefinite"))? -1 :
2499 (repeatStr == QLatin1String(""))? 1 : toDouble(repeatStr));
2500
2501 parent->appendStyleProperty(anim, someId(attributes));
2502 parent->document()->setAnimated(true);
2503 handler->setAnimPeriod(begin, end);
2504 return true;
2505}
2506
2508 const QXmlStreamAttributes &attributes,
2509 QSvgHandler *)
2510{
2511 Q_UNUSED(parent); Q_UNUSED(attributes);
2512 return 0;
2513}
2514
2516 const QXmlStreamAttributes &attributes,
2517 QSvgHandler *)
2518{
2519 Q_UNUSED(parent); Q_UNUSED(attributes);
2520 return true;
2521}
2522
2524 const QXmlStreamAttributes &attributes,
2525 QSvgHandler *)
2526{
2527 const QStringView cx = attributes.value(QLatin1String("cx"));
2528 const QStringView cy = attributes.value(QLatin1String("cy"));
2529 const QStringView r = attributes.value(QLatin1String("r"));
2530 qreal ncx = toDouble(cx);
2531 qreal ncy = toDouble(cy);
2532 qreal nr = toDouble(r);
2533 if (nr < 0.0)
2534 return nullptr;
2535
2536 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
2537 QSvgNode *circle = new QSvgCircle(parent, rect);
2538 return circle;
2539}
2540
2542 const QXmlStreamAttributes &attributes,
2543 QSvgHandler *)
2544{
2545 Q_UNUSED(attributes);
2546 QSvgDefs *defs = new QSvgDefs(parent);
2547 return defs;
2548}
2549
2551 const QXmlStreamAttributes &attributes,
2552 QSvgHandler *)
2553{
2554 Q_UNUSED(parent); Q_UNUSED(attributes);
2555 return true;
2556}
2557
2559 const QXmlStreamAttributes &attributes,
2560 QSvgHandler *)
2561{
2562 Q_UNUSED(parent); Q_UNUSED(attributes);
2563 return true;
2564}
2565
2567 const QXmlStreamAttributes &attributes,
2568 QSvgHandler *)
2569{
2570 const QStringView cx = attributes.value(QLatin1String("cx"));
2571 const QStringView cy = attributes.value(QLatin1String("cy"));
2572 const QStringView rx = attributes.value(QLatin1String("rx"));
2573 const QStringView ry = attributes.value(QLatin1String("ry"));
2574 qreal ncx = toDouble(cx);
2575 qreal ncy = toDouble(cy);
2576 qreal nrx = toDouble(rx);
2577 qreal nry = toDouble(ry);
2578
2579 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
2581 return ellipse;
2582}
2583
2585 const QXmlStreamAttributes &attributes,
2586 QSvgHandler *)
2587{
2588 const QStringView hax = attributes.value(QLatin1String("horiz-adv-x"));
2589 QString myId = someId(attributes);
2590
2591 qreal horizAdvX = toDouble(hax);
2592
2593 while (parent && parent->type() != QSvgNode::DOC) {
2594 parent = parent->parent();
2595 }
2596
2597 if (parent && !myId.isEmpty()) {
2598 QSvgTinyDocument *doc = static_cast<QSvgTinyDocument*>(parent);
2599 QSvgFont *font = doc->svgFont(myId);
2600 if (!font) {
2601 font = new QSvgFont(horizAdvX);
2602 font->setFamilyName(myId);
2603 doc->addSvgFont(font);
2604 }
2605 return new QSvgFontStyle(font, doc);
2606 }
2607 return nullptr;
2608}
2609
2611 const QXmlStreamAttributes &attributes,
2612 QSvgHandler *)
2613{
2614 if (parent->type() != QSvgStyleProperty::FONT) {
2615 return false;
2616 }
2617
2618 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2619 QSvgFont *font = style->svgFont();
2620 QString name = attributes.value(QLatin1String("font-family")).toString();
2621 const QStringView unitsPerEmStr = attributes.value(QLatin1String("units-per-em"));
2622
2623 qreal unitsPerEm = toDouble(unitsPerEmStr);
2624 if (!unitsPerEm)
2625 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
2626
2627 if (!name.isEmpty())
2628 font->setFamilyName(name);
2629 font->setUnitsPerEm(unitsPerEm);
2630
2631 if (!font->familyName().isEmpty())
2632 if (!style->doc()->svgFont(font->familyName()))
2633 style->doc()->addSvgFont(font);
2634
2635 return true;
2636}
2637
2639 const QXmlStreamAttributes &attributes,
2640 QSvgHandler *)
2641{
2642 if (parent->type() != QSvgStyleProperty::FONT) {
2643 return false;
2644 }
2645
2646 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2647 QSvgFont *font = style->svgFont();
2648 QString name = attributes.value(QLatin1String("name")).toString();
2649
2650 if (!name.isEmpty())
2651 font->setFamilyName(name);
2652
2653 if (!font->familyName().isEmpty())
2654 if (!style->doc()->svgFont(font->familyName()))
2655 style->doc()->addSvgFont(font);
2656
2657 return true;
2658}
2659
2661 const QXmlStreamAttributes &attributes,
2662 QSvgHandler *)
2663{
2664 Q_UNUSED(parent); Q_UNUSED(attributes);
2665 return true;
2666}
2667
2669 const QXmlStreamAttributes &attributes,
2670 QSvgHandler *)
2671{
2672 Q_UNUSED(parent); Q_UNUSED(attributes);
2673 return true;
2674}
2675
2677 const QXmlStreamAttributes &attributes,
2678 QSvgHandler *)
2679{
2680 Q_UNUSED(parent); Q_UNUSED(attributes);
2681 return true;
2682}
2683
2685 const QXmlStreamAttributes &attributes,
2686 QSvgHandler *)
2687{
2688 Q_UNUSED(attributes);
2689 QSvgG *node = new QSvgG(parent);
2690 return node;
2691}
2692
2694 const QXmlStreamAttributes &attributes,
2695 QSvgHandler *)
2696{
2697 if (parent->type() != QSvgStyleProperty::FONT) {
2698 return false;
2699 }
2700
2701 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2702 QSvgFont *font = style->svgFont();
2703 createSvgGlyph(font, attributes);
2704 return true;
2705}
2706
2708 const QXmlStreamAttributes &attributes,
2709 QSvgHandler *)
2710{
2711 Q_UNUSED(parent); Q_UNUSED(attributes);
2712 return true;
2713}
2714
2716 const QXmlStreamAttributes &attributes,
2717 QSvgHandler *)
2718{
2719 Q_UNUSED(parent); Q_UNUSED(attributes);
2720 return true;
2721}
2722
2724 const QXmlStreamAttributes &attributes,
2725 QSvgHandler *handler)
2726{
2727 const QStringView x = attributes.value(QLatin1String("x"));
2728 const QStringView y = attributes.value(QLatin1String("y"));
2729 const QStringView width = attributes.value(QLatin1String("width"));
2730 const QStringView height = attributes.value(QLatin1String("height"));
2731 QString filename = attributes.value(QLatin1String("xlink:href")).toString();
2732 qreal nx = toDouble(x);
2733 qreal ny = toDouble(y);
2735 qreal nwidth = parseLength(width.toString(), type, handler);
2736 nwidth = convertToPixels(nwidth, true, type);
2737
2738 qreal nheight = parseLength(height.toString(), type, handler);
2739 nheight = convertToPixels(nheight, false, type);
2740
2741 filename = filename.trimmed();
2742 if (filename.isEmpty()) {
2743 qCWarning(lcSvgHandler) << "QSvgHandler: Image filename is empty";
2744 return 0;
2745 }
2746 if (nwidth <= 0 || nheight <= 0) {
2747 qCWarning(lcSvgHandler) << "QSvgHandler: Width or height for" << filename << "image was not greater than 0";
2748 return 0;
2749 }
2750
2751 QImage image;
2752 if (filename.startsWith(QLatin1String("data"))) {
2753 int idx = filename.lastIndexOf(QLatin1String("base64,"));
2754 if (idx != -1) {
2755 idx += 7;
2756 const QString dataStr = filename.mid(idx);
2759 } else {
2760 qCDebug(lcSvgHandler) << "QSvgHandler::createImageNode: Unrecognized inline image format!";
2761 }
2762 } else {
2763 const auto *file = qobject_cast<QFile *>(handler->device());
2764 if (file) {
2765 QUrl url(filename);
2766 if (url.isRelative()) {
2768 filename = info.absoluteDir().absoluteFilePath(filename);
2769 }
2770 }
2771 image = QImage(filename);
2772 }
2773
2774 if (image.isNull()) {
2775 qCWarning(lcSvgHandler) << "Could not create image from" << filename;
2776 return 0;
2777 }
2778
2779 if (image.format() == QImage::Format_ARGB32)
2781
2783 image,
2784 QRectF(nx,
2785 ny,
2786 nwidth,
2787 nheight));
2788 return img;
2789}
2790
2792 const QXmlStreamAttributes &attributes,
2793 QSvgHandler *)
2794{
2795 const QStringView x1 = attributes.value(QLatin1String("x1"));
2796 const QStringView y1 = attributes.value(QLatin1String("y1"));
2797 const QStringView x2 = attributes.value(QLatin1String("x2"));
2798 const QStringView y2 = attributes.value(QLatin1String("y2"));
2799 qreal nx1 = toDouble(x1);
2800 qreal ny1 = toDouble(y1);
2801 qreal nx2 = toDouble(x2);
2802 qreal ny2 = toDouble(y2);
2803
2804 QLineF lineBounds(nx1, ny1, nx2, ny2);
2805 QSvgNode *line = new QSvgLine(parent, lineBounds);
2806 return line;
2807}
2808
2809
2810static void parseBaseGradient(QSvgNode *node,
2811 const QXmlStreamAttributes &attributes,
2812 QSvgGradientStyle *gradProp,
2813 QSvgHandler *handler)
2814{
2815 QString link = attributes.value(QLatin1String("xlink:href")).toString();
2816 QStringView trans = attributes.value(QLatin1String("gradientTransform"));
2817 QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
2818 QString units = attributes.value(QLatin1String("gradientUnits")).toString();
2819 QStringView colorStr = attributes.value(QLatin1String("color"));
2820 QStringView colorOpacityStr = attributes.value(QLatin1String("color-opacity"));
2821
2822 QColor color;
2823 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2824 handler->popColor();
2825 handler->pushColor(color);
2826 }
2827
2829 QGradient *grad = gradProp->qgradient();
2830 if (node && !link.isEmpty()) {
2831 QSvgStyleProperty *prop = node->styleProperty(link);
2832 //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
2833 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
2834 QSvgGradientStyle *inherited =
2835 static_cast<QSvgGradientStyle*>(prop);
2836 if (!inherited->stopLink().isEmpty()) {
2837 gradProp->setStopLink(inherited->stopLink(), handler->document());
2838 } else {
2839 grad->setStops(inherited->qgradient()->stops());
2840 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2841 }
2842
2843 matrix = inherited->qtransform();
2844 } else {
2845 gradProp->setStopLink(link, handler->document());
2846 }
2847 }
2848
2849 if (!trans.isEmpty()) {
2851 gradProp->setTransform(matrix);
2852 } else if (!matrix.isIdentity()) {
2853 gradProp->setTransform(matrix);
2854 }
2855
2856 if (!spread.isEmpty()) {
2857 if (spread == QLatin1String("pad")) {
2859 } else if (spread == QLatin1String("reflect")) {
2861 } else if (spread == QLatin1String("repeat")) {
2863 }
2864 }
2865
2866 if (units.isEmpty() || units == QLatin1String("objectBoundingBox")) {
2868 }
2869}
2870
2872 const QXmlStreamAttributes &attributes,
2873 QSvgHandler *handler)
2874{
2875 const QStringView x1 = attributes.value(QLatin1String("x1"));
2876 const QStringView y1 = attributes.value(QLatin1String("y1"));
2877 const QStringView x2 = attributes.value(QLatin1String("x2"));
2878 const QStringView y2 = attributes.value(QLatin1String("y2"));
2879
2880 qreal nx1 = 0.0;
2881 qreal ny1 = 0.0;
2882 qreal nx2 = 1.0;
2883 qreal ny2 = 0.0;
2884
2885 if (!x1.isEmpty())
2886 nx1 = convertToNumber(x1, handler);
2887 if (!y1.isEmpty())
2888 ny1 = convertToNumber(y1, handler);
2889 if (!x2.isEmpty())
2890 nx2 = convertToNumber(x2, handler);
2891 if (!y2.isEmpty())
2892 ny2 = convertToNumber(y2, handler);
2893
2894 QSvgNode *itr = node;
2895 while (itr && itr->type() != QSvgNode::DOC) {
2896 itr = itr->parent();
2897 }
2898
2899 QLinearGradient *grad = new QLinearGradient(nx1, ny1, nx2, ny2);
2901 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
2902 parseBaseGradient(node, attributes, prop, handler);
2903
2904 return prop;
2905}
2906
2908 const QXmlStreamAttributes &attributes,
2909 QSvgHandler *)
2910{
2911 Q_UNUSED(parent); Q_UNUSED(attributes);
2912 return true;
2913}
2914
2916 const QXmlStreamAttributes &attributes,
2917 QSvgHandler *)
2918{
2919 if (parent->type() != QSvgStyleProperty::FONT) {
2920 return false;
2921 }
2922
2923 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2924 QSvgFont *font = style->svgFont();
2925 createSvgGlyph(font, attributes);
2926 return true;
2927}
2928
2930 const QXmlStreamAttributes &attributes,
2931 QSvgHandler *)
2932{
2933 Q_UNUSED(parent); Q_UNUSED(attributes);
2934 return true;
2935}
2936
2938 const QXmlStreamAttributes &attributes,
2939 QSvgHandler *)
2940{
2941 QStringView data = attributes.value(QLatin1String("d"));
2942
2943 QPainterPath qpath;
2945 if (!parsePathDataFast(data, qpath))
2946 qCWarning(lcSvgHandler, "Invalid path data; path truncated.");
2947
2948 QSvgNode *path = new QSvgPath(parent, qpath);
2949 return path;
2950}
2951
2953 const QXmlStreamAttributes &attributes,
2954 QSvgHandler *)
2955{
2956 QString pointsStr = attributes.value(QLatin1String("points")).toString();
2957
2958 //same QPolygon parsing is in createPolylineNode
2959 const QChar *s = pointsStr.constData();
2961 QPolygonF poly(points.size()/2);
2962 for (int i = 0; i < poly.size(); ++i)
2963 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2964 QSvgNode *polygon = new QSvgPolygon(parent, poly);
2965 return polygon;
2966}
2967
2969 const QXmlStreamAttributes &attributes,
2970 QSvgHandler *)
2971{
2972 QString pointsStr = attributes.value(QLatin1String("points")).toString();
2973
2974 //same QPolygon parsing is in createPolygonNode
2975 const QChar *s = pointsStr.constData();
2977 QPolygonF poly(points.size()/2);
2978 for (int i = 0; i < poly.size(); ++i)
2979 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2980
2981 QSvgNode *line = new QSvgPolyline(parent, poly);
2982 return line;
2983}
2984
2986 const QXmlStreamAttributes &attributes,
2987 QSvgHandler *)
2988{
2989 Q_UNUSED(parent); Q_UNUSED(attributes);
2990 return true;
2991}
2992
2994 const QXmlStreamAttributes &attributes,
2995 QSvgHandler *handler)
2996{
2997 const QStringView cx = attributes.value(QLatin1String("cx"));
2998 const QStringView cy = attributes.value(QLatin1String("cy"));
2999 const QStringView r = attributes.value(QLatin1String("r"));
3000 const QStringView fx = attributes.value(QLatin1String("fx"));
3001 const QStringView fy = attributes.value(QLatin1String("fy"));
3002
3003 qreal ncx = 0.5;
3004 qreal ncy = 0.5;
3005 if (!cx.isEmpty())
3006 ncx = toDouble(cx);
3007 if (!cy.isEmpty())
3008 ncy = toDouble(cy);
3009
3010 qreal nr = 0.0;
3011 if (!r.isEmpty())
3012 nr = toDouble(r);
3013 if (nr <= 0.0)
3014 return nullptr;
3015
3016 qreal nfx = ncx;
3017 if (!fx.isEmpty())
3018 nfx = toDouble(fx);
3019 qreal nfy = ncy;
3020 if (!fy.isEmpty())
3021 nfy = toDouble(fy);
3022
3023 QRadialGradient *grad = new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
3025
3026 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
3027 parseBaseGradient(node, attributes, prop, handler);
3028
3029 return prop;
3030}
3031
3033 const QXmlStreamAttributes &attributes,
3034 QSvgHandler *handler)
3035{
3036 const QStringView x = attributes.value(QLatin1String("x"));
3037 const QStringView y = attributes.value(QLatin1String("y"));
3038 const QStringView width = attributes.value(QLatin1String("width"));
3039 const QStringView height = attributes.value(QLatin1String("height"));
3040 const QStringView rx = attributes.value(QLatin1String("rx"));
3041 const QStringView ry = attributes.value(QLatin1String("ry"));
3042
3043 bool ok = true;
3045 qreal nwidth = parseLength(width.toString(), type, handler, &ok);
3046 if (!ok)
3047 return nullptr;
3048 nwidth = convertToPixels(nwidth, true, type);
3049 qreal nheight = parseLength(height.toString(), type, handler, &ok);
3050 if (!ok)
3051 return nullptr;
3052 nheight = convertToPixels(nheight, true, type);
3053 qreal nrx = toDouble(rx);
3054 qreal nry = toDouble(ry);
3055
3056 QRectF bounds(toDouble(x), toDouble(y), nwidth, nheight);
3057 if (bounds.isEmpty())
3058 return nullptr;
3059
3060 if (!rx.isEmpty() && ry.isEmpty())
3061 nry = nrx;
3062 else if (!ry.isEmpty() && rx.isEmpty())
3063 nrx = nry;
3064
3065 //9.2 The 'rect' element clearly specifies it
3066 // but the case might in fact be handled because
3067 // we draw rounded rectangles differently
3068 if (nrx > bounds.width()/2)
3069 nrx = bounds.width()/2;
3070 if (nry > bounds.height()/2)
3071 nry = bounds.height()/2;
3072
3073 //we draw rounded rect from 0...99
3074 //svg from 0...bounds.width()/2 so we're adjusting the
3075 //coordinates
3076 nrx *= (100/(bounds.width()/2));
3077 nry *= (100/(bounds.height()/2));
3078
3079 QSvgNode *rect = new QSvgRect(parent, bounds,
3080 int(nrx),
3081 int(nry));
3082 return rect;
3083}
3084
3086 const QXmlStreamAttributes &attributes,
3087 QSvgHandler *)
3088{
3089 Q_UNUSED(parent); Q_UNUSED(attributes);
3090 return true;
3091}
3092
3094 const QXmlStreamAttributes &attributes,
3095 QSvgHandler *)
3096{
3097 Q_UNUSED(parent); Q_UNUSED(attributes);
3098 return true;
3099}
3100
3102 const QXmlStreamAttributes &attributes,
3103 QSvgHandler *handler)
3104{
3105 Q_UNUSED(parent); Q_UNUSED(attributes);
3106 QStringView solidColorStr = attributes.value(QLatin1String("solid-color"));
3107 QStringView solidOpacityStr = attributes.value(QLatin1String("solid-opacity"));
3108
3109 if (solidOpacityStr.isEmpty())
3110 solidOpacityStr = attributes.value(QLatin1String("opacity"));
3111
3112 QColor color;
3113 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3114 return 0;
3116 return style;
3117}
3118
3120 const QXmlStreamAttributes &attributes,
3121 QSvgHandler *handler)
3122{
3123 if (parent->type() != QSvgStyleProperty::GRADIENT)
3124 return false;
3125 QString nodeIdStr = someId(attributes);
3126 QString xmlClassStr = attributes.value(QLatin1String("class")).toString();
3127
3128 //### nasty hack because stop gradients are not in the rendering tree
3129 // we force a dummy node with the same id and class into a rendering
3130 // tree to figure out whether the selector has a style for it
3131 // QSvgStyleSelector should be coded in a way that could avoid it
3132 QSvgAnimation anim;
3133 anim.setNodeId(nodeIdStr);
3134 anim.setXmlClass(xmlClassStr);
3135
3136 QXmlStreamAttributes xmlAttr = attributes;
3137
3138#ifndef QT_NO_CSSPARSER
3140 cssNode.ptr = &anim;
3141 QList<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
3142
3143 for (int i = 0; i < decls.size(); ++i) {
3144 const QCss::Declaration &decl = decls.at(i);
3145
3146 if (decl.d->property.isEmpty())
3147 continue;
3148 if (decl.d->values.size() != 1)
3149 continue;
3150 QCss::Value val = decl.d->values.first();
3151 QString valueStr = val.toString();
3152 if (val.type == QCss::Value::Uri) {
3153 valueStr.prepend(QLatin1String("url("));
3154 valueStr.append(QLatin1Char(')'));
3155 }
3156 xmlAttr.append(QString(), decl.d->property, valueStr);
3157 }
3158
3159#endif
3160
3161 QSvgAttributes attrs(xmlAttr, handler);
3162
3163 QSvgGradientStyle *style =
3164 static_cast<QSvgGradientStyle*>(parent);
3165 QStringView colorStr = attrs.stopColor;
3166 QColor color;
3167
3168 bool ok = true;
3169 qreal offset = convertToNumber(attrs.offset, handler, &ok);
3170 if (!ok)
3171 offset = 0.0;
3172 QString black = QString::fromLatin1("#000000");
3173 if (colorStr.isEmpty()) {
3174 colorStr = black;
3175 }
3176
3177 constructColor(colorStr, attrs.stopOpacity, color, handler);
3178
3179 QGradient *grad = style->qgradient();
3180
3181 offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
3182 QGradientStops stops;
3183 if (style->gradientStopsSet()) {
3184 stops = grad->stops();
3185 // If the stop offset equals the one previously added, add an epsilon to make it greater.
3186 if (offset <= stops.back().first)
3187 offset = stops.back().first + FLT_EPSILON;
3188 }
3189
3190 // If offset is greater than one, it must be clamped to one.
3191 if (offset > 1.0) {
3192 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3193 stops.back().first = 1.0 - FLT_EPSILON;
3194 grad->setStops(stops);
3195 }
3196 offset = 1.0;
3197 }
3198
3199 grad->setColorAt(offset, color);
3200 style->setGradientStopsSet(true);
3201 return true;
3202}
3203
3205 const QXmlStreamAttributes &attributes,
3206 QSvgHandler *handler)
3207{
3209#ifdef QT_NO_CSSPARSER
3210 Q_UNUSED(attributes);
3211 Q_UNUSED(handler);
3212#else
3213 const QStringView type = attributes.value(QLatin1String("type"));
3214 if (type.compare(QLatin1String("text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3215 handler->setInStyle(true);
3216#endif
3217
3218 return true;
3219}
3220
3222 const QXmlStreamAttributes &attributes,
3223 QSvgHandler *handler)
3224{
3225 Q_UNUSED(parent); Q_UNUSED(attributes);
3226
3227 QSvgTinyDocument *node = new QSvgTinyDocument();
3228 const QStringView widthStr = attributes.value(QLatin1String("width"));
3229 const QStringView heightStr = attributes.value(QLatin1String("height"));
3230 QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
3231
3232 QSvgHandler::LengthType type = QSvgHandler::LT_PX; // FIXME: is the default correct?
3233 qreal width = 0;
3234 if (!widthStr.isEmpty()) {
3235 width = parseLength(widthStr.toString(), type, handler);
3236 if (type != QSvgHandler::LT_PT)
3237 width = convertToPixels(width, true, type);
3239 }
3240 qreal height = 0;
3241 if (!heightStr.isEmpty()) {
3242 height = parseLength(heightStr.toString(), type, handler);
3243 if (type != QSvgHandler::LT_PT)
3244 height = convertToPixels(height, false, type);
3246 }
3247
3248 QStringList viewBoxValues;
3249 if (!viewBoxStr.isEmpty()) {
3250 viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
3251 viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
3252 viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
3253 viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
3254 viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts);
3255 }
3256 if (viewBoxValues.size() == 4) {
3257 QString xStr = viewBoxValues.at(0).trimmed();
3258 QString yStr = viewBoxValues.at(1).trimmed();
3259 QString widthStr = viewBoxValues.at(2).trimmed();
3260 QString heightStr = viewBoxValues.at(3).trimmed();
3261
3263 qreal x = parseLength(xStr, lt, handler);
3264 qreal y = parseLength(yStr, lt, handler);
3265 qreal w = parseLength(widthStr, lt, handler);
3266 qreal h = parseLength(heightStr, lt, handler);
3267
3268 node->setViewBox(QRectF(x, y, w, h));
3269
3270 } else if (width && height) {
3271 if (type == QSvgHandler::LT_PT) {
3272 width = convertToPixels(width, false, type);
3273 height = convertToPixels(height, false, type);
3274 }
3275 node->setViewBox(QRectF(0, 0, width, height));
3276 }
3278
3279 return node;
3280}
3281
3283 const QXmlStreamAttributes &attributes,
3284 QSvgHandler *)
3285{
3286 Q_UNUSED(attributes);
3287 QSvgSwitch *node = new QSvgSwitch(parent);
3288 return node;
3289}
3290
3292 const QXmlStreamAttributes &,
3293 QSvgHandler *)
3294{
3295 if (parent->type() != QSvgNode::TEXTAREA)
3296 return false;
3297 static_cast<QSvgText*>(parent)->addLineBreak();
3298 return true;
3299}
3300
3302 const QXmlStreamAttributes &attributes,
3303 QSvgHandler *handler)
3304{
3305 const QStringView x = attributes.value(QLatin1String("x"));
3306 const QStringView y = attributes.value(QLatin1String("y"));
3307 //### editable and rotate not handled
3309 qreal nx = parseLength(x.toString(), type, handler);
3310 nx = convertToPixels(nx, true, type);
3311 qreal ny = parseLength(y.toString(), type, handler);
3312 ny = convertToPixels(ny, true, type);
3313
3315 return text;
3316}
3317
3319 const QXmlStreamAttributes &attributes,
3320 QSvgHandler *handler)
3321{
3322 QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3323 if (node) {
3325 qreal width = parseLength(attributes.value(QLatin1String("width")), type, handler);
3326 qreal height = parseLength(attributes.value(QLatin1String("height")), type, handler);
3327 node->setTextArea(QSizeF(width, height));
3328 }
3329 return node;
3330}
3331
3333 const QXmlStreamAttributes &,
3334 QSvgHandler *)
3335{
3336 return new QSvgTspan(parent);
3337}
3338
3340 const QXmlStreamAttributes &attributes,
3341 QSvgHandler *)
3342{
3343 Q_UNUSED(parent); Q_UNUSED(attributes);
3344 return true;
3345}
3346
3348 const QXmlStreamAttributes &attributes,
3349 QSvgHandler *handler)
3350{
3351 QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
3352 const QStringView xStr = attributes.value(QLatin1String("x"));
3353 const QStringView yStr = attributes.value(QLatin1String("y"));
3354 QSvgStructureNode *group = nullptr;
3355
3356 if (linkId.isEmpty())
3357 linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
3358 switch (parent->type()) {
3359 case QSvgNode::DOC:
3360 case QSvgNode::DEFS:
3361 case QSvgNode::G:
3362 case QSvgNode::SWITCH:
3363 group = static_cast<QSvgStructureNode*>(parent);
3364 break;
3365 default:
3366 break;
3367 }
3368
3369 if (group) {
3370 QPointF pt;
3371 if (!xStr.isNull() || !yStr.isNull()) {
3373 qreal nx = parseLength(xStr.toString(), type, handler);
3374 nx = convertToPixels(nx, true, type);
3375
3376 qreal ny = parseLength(yStr.toString(), type, handler);
3377 ny = convertToPixels(ny, true, type);
3378 pt = QPointF(nx, ny);
3379 }
3380
3381 QSvgNode *link = group->scopeNode(linkId);
3382 if (link) {
3383 if (parent->isDescendantOf(link))
3384 qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(linkId));
3385
3386 return new QSvgUse(pt, parent, link);
3387 }
3388
3389 //delay link resolving, link might have not been created yet
3390 return new QSvgUse(pt, parent, linkId);
3391 }
3392
3393 qCWarning(lcSvgHandler, "<use> element %s in wrong context!", qPrintable(linkId));
3394 return 0;
3395}
3396
3398 const QXmlStreamAttributes &attributes,
3399 QSvgHandler *)
3400{
3401 Q_UNUSED(parent); Q_UNUSED(attributes);
3402 return 0;
3403}
3404
3405typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
3406
3408{
3409 if (name.isEmpty())
3410 return 0;
3411
3412 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
3413 switch (name.at(0).unicode()) {
3414 case 'd':
3415 if (ref == QLatin1String("efs")) return createDefsNode;
3416 break;
3417 case 'g':
3418 if (ref.isEmpty()) return createGNode;
3419 break;
3420 case 's':
3421 if (ref == QLatin1String("vg")) return createSvgNode;
3422 if (ref == QLatin1String("witch")) return createSwitchNode;
3423 break;
3424 default:
3425 break;
3426 }
3427 return 0;
3428}
3429
3431{
3432 if (name.isEmpty())
3433 return 0;
3434
3435 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
3436 switch (name.at(0).unicode()) {
3437 case 'a':
3438 if (ref == QLatin1String("nimation")) return createAnimationNode;
3439 break;
3440 case 'c':
3441 if (ref == QLatin1String("ircle")) return createCircleNode;
3442 break;
3443 case 'e':
3444 if (ref == QLatin1String("llipse")) return createEllipseNode;
3445 break;
3446 case 'i':
3447 if (ref == QLatin1String("mage")) return createImageNode;
3448 break;
3449 case 'l':
3450 if (ref == QLatin1String("ine")) return createLineNode;
3451 break;
3452 case 'p':
3453 if (ref == QLatin1String("ath")) return createPathNode;
3454 if (ref == QLatin1String("olygon")) return createPolygonNode;
3455 if (ref == QLatin1String("olyline")) return createPolylineNode;
3456 break;
3457 case 'r':
3458 if (ref == QLatin1String("ect")) return createRectNode;
3459 break;
3460 case 't':
3461 if (ref == QLatin1String("ext")) return createTextNode;
3462 if (ref == QLatin1String("extArea")) return createTextAreaNode;
3463 if (ref == QLatin1String("span")) return createTspanNode;
3464 break;
3465 case 'u':
3466 if (ref == QLatin1String("se")) return createUseNode;
3467 break;
3468 case 'v':
3469 if (ref == QLatin1String("ideo")) return createVideoNode;
3470 break;
3471 default:
3472 break;
3473 }
3474 return 0;
3475}
3476
3478
3480{
3481 if (name.isEmpty())
3482 return 0;
3483
3484 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
3485 switch (name.at(0).unicode()) {
3486 case 'a':
3487 if (ref.isEmpty()) return parseAnchorNode;
3488 if (ref == QLatin1String("nimate")) return parseAnimateNode;
3489 if (ref == QLatin1String("nimateColor")) return parseAnimateColorNode;
3490 if (ref == QLatin1String("nimateMotion")) return parseAimateMotionNode;
3491 if (ref == QLatin1String("nimateTransform")) return parseAnimateTransformNode;
3492 if (ref == QLatin1String("udio")) return parseAudioNode;
3493 break;
3494 case 'd':
3495 if (ref == QLatin1String("esc")) return parseDescNode;
3496 if (ref == QLatin1String("iscard")) return parseDiscardNode;
3497 break;
3498 case 'f':
3499 if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
3500 break;
3501 case 'h':
3502 if (ref == QLatin1String("andler")) return parseHandlerNode;
3503 if (ref == QLatin1String("kern")) return parseHkernNode;
3504 break;
3505 case 'm':
3506 if (ref == QLatin1String("etadata")) return parseMetadataNode;
3507 if (ref == QLatin1String("path")) return parseMpathNode;
3508 break;
3509 case 'p':
3510 if (ref == QLatin1String("refetch")) return parsePrefetchNode;
3511 break;
3512 case 's':
3513 if (ref == QLatin1String("cript")) return parseScriptNode;
3514 if (ref == QLatin1String("et")) return parseSetNode;
3515 if (ref == QLatin1String("tyle")) return parseStyleNode;
3516 break;
3517 case 't':
3518 if (ref == QLatin1String("break")) return parseTbreakNode;
3519 if (ref == QLatin1String("itle")) return parseTitleNode;
3520 break;
3521 default:
3522 break;
3523 }
3524 return 0;
3525}
3526
3527typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
3528 const QXmlStreamAttributes &,
3529 QSvgHandler *);
3530
3532{
3533 if (name.isEmpty())
3534 return 0;
3535
3536 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
3537 switch (name.at(0).unicode()) {
3538 case 'f':
3539 if (ref == QLatin1String("ont")) return createFontNode;
3540 break;
3541 case 'l':
3542 if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
3543 break;
3544 case 'r':
3545 if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
3546 break;
3547 case 's':
3548 if (ref == QLatin1String("olidColor")) return createSolidColorNode;
3549 break;
3550 default:
3551 break;
3552 }
3553 return 0;
3554}
3555
3557 const QXmlStreamAttributes &,
3558 QSvgHandler *);
3559
3561{
3562 if (name.isEmpty())
3563 return 0;
3564
3565 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
3566 switch (name.at(0).unicode()) {
3567 case 'f':
3568 if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
3569 if (ref == QLatin1String("ont-face-name")) return parseFontFaceNameNode;
3570 if (ref == QLatin1String("ont-face-src")) return parseFontFaceSrcNode;
3571 if (ref == QLatin1String("ont-face-uri")) return parseFontFaceUriNode;
3572 break;
3573 case 'g':
3574 if (ref == QLatin1String("lyph")) return parseGlyphNode;
3575 break;
3576 case 'm':
3577 if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
3578 break;
3579 case 's':
3580 if (ref == QLatin1String("top")) return parseStopNode;
3581 break;
3582 default:
3583 break;
3584 }
3585 return 0;
3586}
3587
3589 , m_ownsReader(true)
3590{
3591 init();
3592}
3593
3594QSvgHandler::QSvgHandler(const QByteArray &data) : xml(new QXmlStreamReader(data))
3595 , m_ownsReader(true)
3596{
3597 init();
3598}
3599
3600QSvgHandler::QSvgHandler(QXmlStreamReader *const reader) : xml(reader)
3601 , m_ownsReader(false)
3602{
3603 init();
3604}
3605
3606void QSvgHandler::init()
3607{
3608 m_doc = 0;
3609 m_style = 0;
3610 m_animEnd = 0;
3611 m_defaultCoords = LT_PX;
3613 m_defaultPen.setMiterLimit(4);
3614 parse();
3615}
3616
3617// Having too many unfinished elements will cause a stack overflow
3618// in the dtor of QSvgTinyDocument, see oss-fuzz issue 24000.
3619static const int unfinishedElementsLimit = 2048;
3620
3621void QSvgHandler::parse()
3622{
3623 xml->setNamespaceProcessing(false);
3624#ifndef QT_NO_CSSPARSER
3625 m_selector = new QSvgStyleSelector;
3626 m_inStyle = false;
3627#endif
3628 bool done = false;
3629 int remainingUnfinishedElements = unfinishedElementsLimit;
3630 while (!xml->atEnd() && !done) {
3631 switch (xml->readNext()) {
3632 case QXmlStreamReader::StartElement:
3633 // he we could/should verify the namespaces, and simply
3634 // call m_skipNodes(Unknown) if we don't know the
3635 // namespace. We do support http://www.w3.org/2000/svg
3636 // but also http://www.w3.org/2000/svg-20000303-stylable
3637 // And if the document uses an external dtd, the reported
3638 // namespaceUri is empty. The only possible strategy at
3639 // this point is to do what everyone else seems to do and
3640 // ignore the reported namespaceUri completely.
3641 if (remainingUnfinishedElements
3642 && startElement(xml->name().toString(), xml->attributes())) {
3643 --remainingUnfinishedElements;
3644 } else {
3645 delete m_doc;
3646 m_doc = nullptr;
3647 return;
3648 }
3649 break;
3650 case QXmlStreamReader::EndElement:
3651 endElement(xml->name());
3652 ++remainingUnfinishedElements;
3653 done = (xml->name() == QLatin1String("svg"));
3654 break;
3655 case QXmlStreamReader::Characters:
3656 characters(xml->text());
3657 break;
3658 case QXmlStreamReader::ProcessingInstruction:
3659 processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
3660 break;
3661 default:
3662 break;
3663 }
3664 }
3665 resolveGradients(m_doc);
3666 resolveNodes();
3667}
3668
3670 const QXmlStreamAttributes &attributes)
3671{
3672 QSvgNode *node = nullptr;
3673
3674 pushColorCopy();
3675
3676 /* The xml:space attribute may appear on any element. We do
3677 * a lookup by the qualified name here, but this is namespace aware, since
3678 * the XML namespace can only be bound to prefix "xml." */
3679 const QStringView xmlSpace(attributes.value(QLatin1String("xml:space")));
3680 if (xmlSpace.isNull()) {
3681 // This element has no xml:space attribute.
3682 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3683 } else if (xmlSpace == QLatin1String("preserve")) {
3684 m_whitespaceMode.push(QSvgText::Preserve);
3685 } else if (xmlSpace == QLatin1String("default")) {
3686 m_whitespaceMode.push(QSvgText::Default);
3687 } else {
3688 const QByteArray msg = '"' + xmlSpace.toString().toLocal8Bit()
3689 + "\" is an invalid value for attribute xml:space. "
3690 "Valid values are \"preserve\" and \"default\".";
3691 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3692 m_whitespaceMode.push(QSvgText::Default);
3693 }
3694
3695 if (!m_doc && localName != QLatin1String("svg"))
3696 return false;
3697
3698 if (FactoryMethod method = findGroupFactory(localName)) {
3699 //group
3700 node = method(m_doc ? m_nodes.top() : 0, attributes, this);
3701 Q_ASSERT(node);
3702 if (!m_doc) {
3703 Q_ASSERT(node->type() == QSvgNode::DOC);
3704 m_doc = static_cast<QSvgTinyDocument*>(node);
3705 } else {
3706 switch (m_nodes.top()->type()) {
3707 case QSvgNode::DOC:
3708 case QSvgNode::G:
3709 case QSvgNode::DEFS:
3710 case QSvgNode::SWITCH:
3711 {
3713 static_cast<QSvgStructureNode*>(m_nodes.top());
3714 group->addChild(node, someId(attributes));
3715 }
3716 break;
3717 default:
3718 const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect.");
3719 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3720 delete node;
3721 node = 0;
3722 break;
3723 }
3724 }
3725 if (node) {
3726 parseCoreNode(node, attributes);
3727#ifndef QT_NO_CSSPARSER
3728 cssStyleLookup(node, this, m_selector);
3729#endif
3730 parseStyle(node, attributes, this);
3731 }
3732 } else if (FactoryMethod method = findGraphicsFactory(localName)) {
3733 //rendering element
3734 Q_ASSERT(!m_nodes.isEmpty());
3735 node = method(m_nodes.top(), attributes, this);
3736 if (node) {
3737 switch (m_nodes.top()->type()) {
3738 case QSvgNode::DOC:
3739 case QSvgNode::G:
3740 case QSvgNode::DEFS:
3741 case QSvgNode::SWITCH:
3742 {
3743 if (node->type() == QSvgNode::TSPAN) {
3744 const QByteArray msg = QByteArrayLiteral("\'tspan\' element in wrong context.");
3745 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3746 delete node;
3747 node = 0;
3748 break;
3749 }
3751 static_cast<QSvgStructureNode*>(m_nodes.top());
3752 group->addChild(node, someId(attributes));
3753 }
3754 break;
3755 case QSvgNode::TEXT:
3756 case QSvgNode::TEXTAREA:
3757 if (node->type() == QSvgNode::TSPAN) {
3758 static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
3759 } else {
3760 const QByteArray msg = QByteArrayLiteral("\'text\' or \'textArea\' element contains invalid element type.");
3761 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3762 delete node;
3763 node = 0;
3764 }
3765 break;
3766 default:
3767 const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect.");
3768 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3769 delete node;
3770 node = 0;
3771 break;
3772 }
3773
3774 if (node) {
3775 parseCoreNode(node, attributes);
3776#ifndef QT_NO_CSSPARSER
3777 cssStyleLookup(node, this, m_selector);
3778#endif
3779 parseStyle(node, attributes, this);
3780 if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) {
3781 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3782 } else if (node->type() == QSvgNode::TSPAN) {
3783 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3784 } else if (node->type() == QSvgNode::USE) {
3785 auto useNode = static_cast<QSvgUse *>(node);
3786 if (!useNode->isResolved())
3787 m_toBeResolved.append(useNode);
3788 }
3789 }
3790 }
3791 } else if (ParseMethod method = findUtilFactory(localName)) {
3792 Q_ASSERT(!m_nodes.isEmpty());
3793 if (!method(m_nodes.top(), attributes, this))
3794 qCWarning(lcSvgHandler, "%s", msgProblemParsing(localName, xml).constData());
3795 } else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3796 QSvgStyleProperty *prop = method(m_nodes.top(), attributes, this);
3797 if (prop) {
3798 m_style = prop;
3799 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3800 } else {
3801 const QByteArray msg = QByteArrayLiteral("Could not parse node: ") + localName.toLocal8Bit();
3802 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
3803 }
3804 } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3805 if (m_style) {
3806 if (!method(m_style, attributes, this))
3807 qCWarning(lcSvgHandler, "%s", msgProblemParsing(localName, xml).constData());
3808 }
3809 } else {
3810 //qCWarning(lcSvgHandler) <<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
3811 m_skipNodes.push(Unknown);
3812 return true;
3813 }
3814
3815 if (node) {
3816 m_nodes.push(node);
3817 m_skipNodes.push(Graphics);
3818 } else {
3819 //qDebug()<<"Skipping "<<localName;
3820 m_skipNodes.push(Style);
3821 }
3822 return true;
3823}
3824
3826{
3827 CurrentNode node = m_skipNodes.top();
3828 m_skipNodes.pop();
3829 m_whitespaceMode.pop();
3830
3831 popColor();
3832
3833 if (node == Unknown) {
3834 return true;
3835 }
3836
3837#ifdef QT_NO_CSSPARSER
3838 Q_UNUSED(localName);
3839#else
3840 if (m_inStyle && localName == QLatin1String("style"))
3841 m_inStyle = false;
3842#endif
3843
3844 if (node == Graphics)
3845 m_nodes.pop();
3846 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3847 m_style = 0;
3848
3849 return true;
3850}
3851
3852void QSvgHandler::resolveGradients(QSvgNode *node, int nestedDepth)
3853{
3854 if (!node || (node->type() != QSvgNode::DOC && node->type() != QSvgNode::G
3855 && node->type() != QSvgNode::DEFS && node->type() != QSvgNode::SWITCH)) {
3856 return;
3857 }
3858
3859 QSvgStructureNode *structureNode = static_cast<QSvgStructureNode *>(node);
3860
3861 const QList<QSvgNode *> ren = structureNode->renderers();
3862 for (auto it = ren.begin(); it != ren.end(); ++it) {
3863 QSvgFillStyle *fill = static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
3864 if (fill && !fill->isGradientResolved()) {
3865 QString id = fill->gradientId();
3866 QSvgFillStyleProperty *style = structureNode->styleProperty(id);
3867 if (style) {
3868 fill->setFillStyle(style);
3869 } else {
3870 qCWarning(lcSvgHandler, "%s", msgCouldNotResolveProperty(id, xml).constData());
3871 fill->setBrush(Qt::NoBrush);
3872 }
3873 }
3874
3875 QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
3876 if (stroke && !stroke->isGradientResolved()) {
3877 QString id = stroke->gradientId();
3878 QSvgFillStyleProperty *style = structureNode->styleProperty(id);
3879 if (style) {
3880 stroke->setStyle(style);
3881 } else {
3882 qCWarning(lcSvgHandler, "%s", msgCouldNotResolveProperty(id, xml).constData());
3883 stroke->setStroke(Qt::NoBrush);
3884 }
3885 }
3886
3887 if (nestedDepth < 2048)
3888 resolveGradients(*it, nestedDepth + 1);
3889 }
3890}
3891
3892void QSvgHandler::resolveNodes()
3893{
3894 for (QSvgUse *useNode : std::as_const(m_toBeResolved)) {
3895 const auto parent = useNode->parent();
3896 if (!parent)
3897 continue;
3898
3899 QSvgNode::Type t = parent->type();
3900 if (t != QSvgNode::DOC && t != QSvgNode::DEFS && t != QSvgNode::G && t != QSvgNode::SWITCH)
3901 continue;
3902
3904 QSvgNode *link = group->scopeNode(useNode->linkId());
3905 if (!link) {
3906 qCWarning(lcSvgHandler, "link #%s is undefined!", qPrintable(useNode->linkId()));
3907 continue;
3908 }
3909
3910 if (useNode->parent()->isDescendantOf(link))
3911 qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(useNode->linkId()));
3912
3913 useNode->setLink(link);
3914 }
3915 m_toBeResolved.clear();
3916}
3917
3919{
3920#ifndef QT_NO_CSSPARSER
3921 if (m_inStyle) {
3922 QString css = str.toString();
3923 QCss::StyleSheet sheet;
3924 QCss::Parser(css).parse(&sheet);
3925 m_selector->styleSheets.append(sheet);
3926 return true;
3927 }
3928#endif
3929 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
3930 return true;
3931
3932 if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
3933 static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
3934 } else if (m_nodes.top()->type() == QSvgNode::TSPAN) {
3935 static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
3936 }
3937
3938 return true;
3939}
3940
3942{
3943 return xml->device();
3944}
3945
3947{
3948 return m_doc;
3949}
3950
3952{
3953 return m_defaultCoords;
3954}
3955
3957{
3958 m_defaultCoords = type;
3959}
3960
3962{
3963 m_colorStack.push(color);
3964 m_colorTagCount.push(1);
3965}
3966
3968{
3969 if (m_colorTagCount.size())
3970 ++m_colorTagCount.top();
3971 else
3973}
3974
3976{
3977 if (m_colorTagCount.size()) {
3978 if (!--m_colorTagCount.top()) {
3979 m_colorStack.pop();
3980 m_colorTagCount.pop();
3981 }
3982 }
3983}
3984
3986{
3987 if (!m_colorStack.isEmpty())
3988 return m_colorStack.top();
3989 else
3990 return QColor(0, 0, 0);
3991}
3992
3993#ifndef QT_NO_CSSPARSER
3994
3996{
3997 m_inStyle = b;
3998}
3999
4001{
4002 return m_inStyle;
4003}
4004
4006{
4007 return m_selector;
4008}
4009
4010#endif // QT_NO_CSSPARSER
4011
4013{
4014#ifdef QT_NO_CSSPARSER
4016 Q_UNUSED(data);
4017#else
4018 if (target == QLatin1String("xml-stylesheet")) {
4019 QRegularExpression rx(QLatin1String("type=\\\"(.+)\\\""),
4022 bool isCss = false;
4023 while (iter.hasNext()) {
4025 QString type = match.captured(1);
4026 if (type.toLower() == QLatin1String("text/css")) {
4027 isCss = true;
4028 }
4029 }
4030
4031 if (isCss) {
4032 QRegularExpression rx(QLatin1String("href=\\\"(.+)\\\""),
4035 QString addr = match.captured(1);
4036 QFileInfo fi(addr);
4037 //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
4038 if (fi.exists()) {
4041 return true;
4042 }
4043 QByteArray cssData = file.readAll();
4044 QString css = QString::fromUtf8(cssData);
4045
4046 QCss::StyleSheet sheet;
4047 QCss::Parser(css).parse(&sheet);
4048 m_selector->styleSheets.append(sheet);
4049 }
4050
4051 }
4052 }
4053#endif
4054
4055 return true;
4056}
4057
4059{
4060 Q_UNUSED(start);
4061 m_animEnd = qMax(end, m_animEnd);
4062}
4063
4065{
4066 return m_animEnd;
4067}
4068
4070{
4071#ifndef QT_NO_CSSPARSER
4072 delete m_selector;
4073 m_selector = 0;
4074#endif
4075
4076 if(m_ownsReader)
4077 delete xml;
4078}
4079
IOBluetoothDevice * device
\inmodule QtGui
Definition qbrush.h:30
\inmodule QtCore
Definition qbytearray.h:57
double toDouble(bool *ok=nullptr) const
Returns the byte array converted to a double value.
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:394
\inmodule QtCore
Definition qchar.h:48
constexpr char toLatin1() const noexcept
Returns the Latin-1 character equivalent to the QChar, or 0.
Definition qchar.h:457
constexpr char16_t unicode() const noexcept
Returns the numeric Unicode value of the QChar.
Definition qchar.h:458
constexpr bool isSpace() const noexcept
Returns true if the character is a separator character (Separator_* categories or certain code points...
Definition qchar.h:466
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromString(QAnyStringView name) noexcept
Definition qcolor.cpp:980
bool hasNext() const
bool test(TokenType t)
void init(const QString &css, bool file=false)
bool parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity=Qt::CaseSensitive)
const Symbol & symbol() const
QString lexem() const
TokenType next()
bool hasEscapeSequences
QList< Symbol > symbols
QList< StyleSheet > styleSheets
Qt::CaseSensitivity nameCaseSensitivity
QList< Declaration > declarationsForNode(NodePtr node, const char *extraPseudo=nullptr)
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
QString absoluteFilePath(const QString &fileName) const
Returns the absolute path name of a file in the directory.
Definition qdir.cpp:809
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString absoluteFilePath() const
Returns an absolute path including the file name.
QDir absoluteDir() const
Returns the file's absolute path as a QDir object.
bool exists() const
Returns true if the file exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
QString fileName() const override
Returns the name set by setFileName() or to the QFile constructors.
Definition qfile.cpp:277
@ MixedCase
Definition qfont.h:95
@ SmallCaps
Definition qfont.h:98
@ Bold
Definition qfont.h:67
@ Normal
Definition qfont.h:64
@ StyleItalic
Definition qfont.h:75
@ StyleNormal
Definition qfont.h:74
@ StyleOblique
Definition qfont.h:76
\inmodule QtGui
Definition qbrush.h:135
void setSpread(Spread spread)
Specifies the spread method that should be used for this gradient.
Definition qbrush.h:390
void setCoordinateMode(CoordinateMode mode)
Definition qbrush.cpp:1683
@ ComponentInterpolation
Definition qbrush.h:163
void setStops(const QGradientStops &stops)
Replaces the current set of stop points with the given stopPoints.
Definition qbrush.cpp:1608
@ ObjectMode
Definition qbrush.h:157
void setColorAt(qreal pos, const QColor &color)
Creates a stop point at the given position with the given color.
Definition qbrush.cpp:1563
@ ReflectSpread
Definition qbrush.h:148
@ RepeatSpread
Definition qbrush.h:149
@ PadSpread
Definition qbrush.h:147
void setInterpolationMode(InterpolationMode mode)
Definition qbrush.cpp:1718
QGradientStops stops() const
Returns the stop points for this gradient.
Definition qbrush.cpp:1631
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtGui
Definition qimage.h:37
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
@ Format_ARGB32
Definition qimage.h:47
static QImage fromData(QByteArrayView data, const char *format=nullptr)
Definition qimage.cpp:3823
\inmodule QtCore
Definition qline.h:182
\inmodule QtGui
Definition qbrush.h:394
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
reference back()
Definition qlist.h:686
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator begin()
Definition qlist.h:608
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
\inmodule QtGui
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
CompositionMode
Defines the modes supported for digital image compositing.
Definition qpainter.h:97
@ CompositionMode_Xor
Definition qpainter.h:109
@ CompositionMode_Destination
Definition qpainter.h:102
@ CompositionMode_Lighten
Definition qpainter.h:117
@ CompositionMode_ColorDodge
Definition qpainter.h:118
@ CompositionMode_DestinationAtop
Definition qpainter.h:108
@ CompositionMode_SourceOver
Definition qpainter.h:98
@ CompositionMode_DestinationOut
Definition qpainter.h:106
@ CompositionMode_Clear
Definition qpainter.h:100
@ CompositionMode_SourceAtop
Definition qpainter.h:107
@ CompositionMode_Plus
Definition qpainter.h:112
@ CompositionMode_Overlay
Definition qpainter.h:115
@ CompositionMode_Multiply
Definition qpainter.h:113
@ CompositionMode_Darken
Definition qpainter.h:116
@ CompositionMode_DestinationOver
Definition qpainter.h:99
@ CompositionMode_HardLight
Definition qpainter.h:120
@ CompositionMode_Exclusion
Definition qpainter.h:123
@ CompositionMode_Source
Definition qpainter.h:101
@ CompositionMode_ColorBurn
Definition qpainter.h:119
@ CompositionMode_Difference
Definition qpainter.h:122
@ CompositionMode_SoftLight
Definition qpainter.h:121
@ CompositionMode_DestinationIn
Definition qpainter.h:104
@ CompositionMode_Screen
Definition qpainter.h:114
@ CompositionMode_SourceOut
Definition qpainter.h:105
@ CompositionMode_SourceIn
Definition qpainter.h:103
\inmodule QtGui
Definition qpen.h:25
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:570
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
The QPolygonF class provides a list of points using floating point precision.
Definition qpolygon.h:96
\inmodule QtGui
Definition qbrush.h:412
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:647
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore
Definition qsize.h:207
T & top()
Returns a reference to the stack's top item.
Definition qstack.h:19
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
constexpr void chop(qsizetype n) noexcept
Truncates this string view by length characters.
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
bool endsWith(QStringView s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1014
int toInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an int using base base, which is 10 by default and must be betwe...
Definition qstring.h:1025
constexpr bool isNull() const noexcept
Returns whether this string view is null - that is, whether {data() == nullptr}.
constexpr QChar at(qsizetype n) const noexcept
Returns the character at position n in this string view.
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
QStringView trimmed() const noexcept
Strips leading and trailing whitespace and returns the result.
const_pointer constData() const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
const_iterator constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing just after the last character in...
Definition qstring.h:1211
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
QByteArray toLocal8Bit() const &
Definition qstring.h:567
QString & append(QChar c)
Definition qstring.cpp:3227
QString trimmed() const &
Definition qstring.h:380
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
QString & prepend(QChar c)
Definition qstring.h:411
const_iterator constBegin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in the st...
Definition qstring.h:1203
const QChar * unicode() const
Returns a Unicode representation of the string.
Definition qstring.h:1085
void setRepeatCount(qreal repeatCount)
void setFreeze(bool freeze)
void setArgs(bool fill, const QList< QColor > &colors)
void setFreeze(bool freeze)
void setArgs(TransformType type, Additive additive, const QList< qreal > &args)
void setRepeatCount(qreal repeatCount)
void setFillRule(Qt::FillRule f)
void setFillOpacity(qreal opacity)
void setGradientId(const QString &Id)
void setBrush(QBrush brush)
void setGradientResolved(bool resolved)
void setFillStyle(QSvgFillStyleProperty *style)
QSvgTinyDocument * doc() const
QSvgFont * svgFont() const
static const int BOLDER
static const int LIGHTER
static constexpr qreal DEFAULT_UNITS_PER_EM
Definition qsvgfont_p.h:41
bool gradientStopsSet() const
void setTransform(const QTransform &transform)
void setStopLink(const QString &link, QSvgTinyDocument *doc)
QString stopLink() const
QGradient * qgradient() const
QTransform qtransform() const
void setGradientStopsSet(bool set)
QIODevice * device() const
void pushColorCopy()
bool inStyle() const
QSvgTinyDocument * document() const
int animationDuration() const
void parseCSStoXMLAttrs(const QString &css, QList< QSvgCssAttribute > *attributes)
void setAnimPeriod(int start, int end)
QColor currentColor() const
QSvgHandler(QIODevice *device)
LengthType defaultCoordinateSystem() const
bool processingInstruction(const QString &target, const QString &data)
void setDefaultCoordinateSystem(LengthType type)
QSvgStyleSelector * selector() const
bool endElement(QStringView localName)
void setInStyle(bool b)
bool startElement(const QString &localName, const QXmlStreamAttributes &attributes)
bool characters(QStringView str)
void pushColor(const QColor &color)
@ TableCaptionMode
Definition qsvgnode_p.h:70
@ TableColumnMode
Definition qsvgnode_p.h:68
@ TableRowMode
Definition qsvgnode_p.h:66
@ TableColumnGroupMode
Definition qsvgnode_p.h:67
@ TableCellMode
Definition qsvgnode_p.h:69
@ CompactMode
Definition qsvgnode_p.h:59
@ TableRowGroupMode
Definition qsvgnode_p.h:63
@ TableHeaderGroupMode
Definition qsvgnode_p.h:64
@ InlineTableMode
Definition qsvgnode_p.h:62
@ TableFooterGroupMode
Definition qsvgnode_p.h:65
@ InheritMode
Definition qsvgnode_p.h:72
@ ListItemMode
Definition qsvgnode_p.h:57
void setRequiredFeatures(const QStringList &lst)
Definition qsvgnode.cpp:231
void setXmlClass(const QString &str)
Definition qsvgnode.cpp:306
QSvgStyleProperty * styleProperty(QSvgStyleProperty::Type type) const
Definition qsvgnode.cpp:109
void setRequiredExtensions(const QStringList &lst)
Definition qsvgnode.cpp:241
void setRequiredFonts(const QStringList &lst)
Definition qsvgnode.cpp:271
void setNodeId(const QString &i)
Definition qsvgnode.cpp:301
QSvgNode * parent() const
Definition qsvgnode_p.h:148
void appendStyleProperty(QSvgStyleProperty *prop, const QString &id)
Definition qsvgnode.cpp:45
void setDisplayMode(DisplayMode display)
Definition qsvgnode.cpp:311
void setRequiredLanguages(const QStringList &lst)
Definition qsvgnode.cpp:251
QSvgTinyDocument * document() const
Definition qsvgnode.cpp:219
virtual Type type() const =0
void setVisible(bool visible)
Definition qsvgnode.cpp:281
void setRequiredFormats(const QStringList &lst)
Definition qsvgnode.cpp:261
bool isGradientResolved() const
void setDashArray(const QList< qreal > &dashes)
void setMiterLimit(qreal limit)
void setDashArrayNone()
void setStyle(QSvgFillStyleProperty *style)
void setGradientId(const QString &Id)
QString gradientId() const
void setStroke(QBrush brush)
void setWidth(qreal width)
void setGradientResolved(bool resolved)
void setDashOffset(qreal offset)
void setLineCap(Qt::PenCapStyle cap)
void setVectorEffect(bool nonScalingStroke)
void setOpacity(qreal opacity)
void setLineJoin(Qt::PenJoinStyle join)
QSvgNode * previousSiblingNode(QSvgNode *n) const
void addChild(QSvgNode *child, const QString &id)
QList< QSvgNode * > renderers() const
virtual Type type() const =0
QStringList nodeNames(NodePtr node) const override
QSvgStructureNode * svgStructure(NodePtr node) const
bool hasAttributes(NodePtr node) const override
QString attributeValue(NodePtr node, const QCss::AttributeSelector &asel) const override
bool isNullNode(NodePtr node) const override
QSvgStructureNode * nodeToStructure(QSvgNode *n) const
void freeNode(NodePtr node) const override
QString nodeToName(QSvgNode *node) const
QSvgNode * svgNode(NodePtr node) const
QStringList nodeIds(NodePtr node) const override
virtual ~QSvgStyleSelector()
NodePtr duplicateNode(NodePtr node) const override
NodePtr previousSiblingNode(NodePtr node) const override
bool nodeNameEquals(NodePtr node, const QString &nodeName) const override
NodePtr parentNode(NodePtr node) const override
void setTextArea(const QSizeF &size)
void setViewBox(const QRectF &rect)
void setWidth(int len, bool percent)
QSvgFont * svgFont(const QString &family) const
void addSvgFont(QSvgFont *)
void setHeight(int len, bool percent)
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
\inmodule QtCore
Definition qurl.h:94
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2797
Q_CORE_EXPORT void append(const QString &namespaceUri, const QString &name, const QString &value)
Q_CORE_EXPORT QStringView value(QAnyStringView namespaceUri, QAnyStringView name) const noexcept
QString str
[2]
QString text
double e
QSet< QString >::iterator it
rect
[4]
else opt state
[0]
Token ident
Definition keywords.cpp:448
struct wl_display * display
Definition linuxdmabuf.h:41
EGLint EGLint * formats
@ SEMICOLON
@ Value_None
Combined button and popup list for selecting options.
@ AlignRight
Definition qnamespace.h:145
@ AlignHCenter
Definition qnamespace.h:147
@ AlignLeft
Definition qnamespace.h:143
@ black
Definition qnamespace.h:29
@ SolidLine
@ SvgMiterJoin
@ BevelJoin
@ RoundJoin
@ CaseInsensitive
@ NoBrush
@ WindingFill
@ OddEvenFill
@ SkipEmptyParts
Definition qnamespace.h:127
@ SquareCap
@ RoundCap
@ FlatCap
Definition image.cpp:4
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:105
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
size_t qstrlen(const char *str)
#define rgb(r, g, b)
Definition qcolor.cpp:124
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError * error
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char * method
static struct AttrInfo attrs[]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT EGLint attribute
int qFpClassify(qfloat16 f) noexcept
Definition qfloat16.h:240
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:243
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
auto qAtan2(T1 y, T2 x)
Definition qmath.h:90
auto qCos(T v)
Definition qmath.h:60
int qCeil(T v)
Definition qmath.h:36
auto qSin(T v)
Definition qmath.h:54
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
auto qTan(T v)
Definition qmath.h:66
static QT_BEGIN_NAMESPACE const qreal Q_PI
Definition qmath_p.h:24
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLint GLsizei width
GLenum type
GLboolean GLuint group
GLuint GLfloat x0
GLenum GLsizeiptr fontSize
GLenum target
GLsizei const GLfloat * dashArray
GLenum const void GLbitfield fontStyle
GLfloat units
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLint ref
GLuint name
GLfloat n
GLuint GLfloat GLfloat y0
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLenum GLenum GLsizei void GLsizei void * column
GLuint GLenum GLenum transform
GLfixed GLfixed GLint GLint GLfixed points
GLbyte nx
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLfixed GLfixed GLfixed y2
GLint void * img
Definition qopenglext.h:233
GLfixed ny
GLint * exponent
GLenum GLsizei len
GLenum const void * addr
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLuint num
GLubyte * pattern
static QT_BEGIN_NAMESPACE const QRgb colors[][14]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr QRgb qRgb(int r, int g, int b)
Definition qrgb.h:30
static QLatin1StringView typeStr(QShaderDescription::VariableType t)
SSL_CTX int(*) void arg)
#define qPrintable(string)
Definition qstring.h:1391
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
static FactoryMethod findGraphicsFactory(const QString &name)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * styleFromUrl(QSvgNode *node, const QString &url)
static qreal convertToPixels(qreal len, bool, QSvgHandler::LengthType type)
static QSvgNode * createGNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseStyleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createImageNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createPolygonNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static const int unfinishedElementsLimit
static bool isDigit(ushort ch)
static void parseCSStoXMLAttrs(const QList< QCss::Declaration > &declarations, QXmlStreamAttributes &attributes)
static QSvgNode::DisplayMode displayStringToEnum(const QString &str)
static bool parseMpathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
static QString idFromUrl(const QString &url)
static bool parseTitleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QPainter::CompositionMode svgToQtCompositionMode(const QString &op)
static QSvgNode * createDefsNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgStyleProperty * createFontNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int parseClockValue(QStringView str, bool *ok)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static ParseMethod findUtilFactory(const QString &name)
bool(* StyleParseMethod)(QSvgStyleProperty *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createAnimationNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
FontSizeSpec
@ XXLarge
@ Large
@ FontSizeValue
@ FontSizeNone
@ XLarge
@ XSmall
@ Medium
@ XXSmall
@ Small
static void parseTransform(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static qreal parseLength(QStringView str, QSvgHandler::LengthType &type, QSvgHandler *handler, bool *ok=NULL)
static void parseColor(QSvgNode *, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createCircleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QByteArray msgCouldNotResolveProperty(const QString &id, const QXmlStreamReader *r)
static QSvgStyleProperty * createLinearGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createLineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static FontSizeSpec fontSizeSpec(QStringView spec)
static bool parseAnimateColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgStyleProperty * createSolidColorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
static QT_BEGIN_NAMESPACE const char * qt_inherit_text
QSvgNode *(* FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
static void parseBaseGradient(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgGradientStyle *gradProp, QSvgHandler *handler)
static bool parseAudioNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
#define QT_INHERIT
static QList< qreal > parsePercentageList(const QChar *&str)
static bool parseMetadataNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnimateTransformNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QTransform parseTransformationMatrix(QStringView value)
static QSvgNode * createSwitchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseDiscardNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< qreal > parseNumbersList(const QChar *&str)
static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes)
static bool parseCoreNode(QSvgNode *node, const QXmlStreamAttributes &attributes)
static bool constructColor(QStringView colorStr, QStringView opacity, QColor &color, QSvgHandler *handler)
static const char * QSvgStyleSelector_nodeString[]
static bool parseStyle(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseHkernNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parsePen(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createSvgNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation, int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx, qreal cury)
static FactoryMethod findGroupFactory(const QString &name)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static bool parseDescNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
QSvgStyleProperty *(* StyleFactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
#define NOOP
static QSvgNode * createPolylineNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static qreal toDouble(const QChar *&str)
static bool parseMissingGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parsePrefetchNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_hex2int(const char *s, bool *ok=nullptr)
static bool parseGlyphNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static int qsvg_h2i(char hex, bool *ok=nullptr)
static QSvgNode * createVideoNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseCompOp(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parsePathDataFast(QStringView data, QPainterPath &path)
static bool parseFontFaceSrcNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnchorNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseFontFaceUriNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QStringList stringToList(const QString &str)
static bool parseStopNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseBrush(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static void parseNumbersArray(const QChar *&str, QVarLengthArray< qreal, 8 > &points, const char *pattern=nullptr)
static const qreal sizeTable[]
static void pathArcSegment(QPainterPath &path, qreal xc, qreal yc, qreal th0, qreal th1, qreal rx, qreal ry, qreal xAxisRotation)
static void cssStyleLookup(QSvgNode *node, QSvgHandler *handler, QSvgStyleSelector *selector)
static bool parseAimateMotionNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static qreal convertToNumber(QStringView str, QSvgHandler *handler, bool *ok=NULL)
static QSvgNode * createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QByteArray msgProblemParsing(const QString &localName, const QXmlStreamReader *r)
static QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, const QChar *&s)
static QSvgNode * createEllipseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseAnimateNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QSvgNode * createTextAreaNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgStyleProperty * createRadialGradientNode(QSvgNode *node, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QString someId(const QXmlStreamAttributes &attributes)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
static const uchar magic[MagicLength]
unsigned short quint16
Definition qtypes.h:43
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned short ushort
Definition qtypes.h:28
double qreal
Definition qtypes.h:92
QList< int > list
[14]
QList< QChar > characters
QFile file
[0]
QFileInfo info(fileName)
[8]
QFileInfo fi("c:/temp/foo")
[newstuff]
QFileSelector selector
[1]
QUrl url("example.com")
[constructor-url-reference]
MyCustomStruct c2
ba fill(true)
p ry()++
p rx()++
QXmlStreamReader xml
[0]
QGraphicsEllipseItem * ellipse
scene addText("Hello, world!")
char * toString(const MyType &t)
[31]
QGraphicsSvgItem * black
QExplicitlySharedDataPointer< DeclarationData > d
Q_GUI_EXPORT QString lexem() const
\inmodule QtCore \reentrant
Definition qchar.h:17
QStringView fontVariant
QStringView strokeDashOffset
QStringView stroke
QStringView opacity
QStringView fontFamily
QStringView strokeDashArray
QStringView strokeOpacity
QStringView stopColor
QStringView fillOpacity
QStringView strokeLineJoin
QList< QSvgCssAttribute > m_cssAttributes
QStringView color
QStringView fontSize
QStringView visibility
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView transform
QStringView fontWeight
QStringView fillRule
QStringView fill
QStringView vectorEffect
QStringView strokeLineCap
QStringView fontStyle
QStringView display
QStringView compOp
QStringView strokeMiterLimit
QStringView colorOpacity
QStringView textAnchor
QStringView offset
QStringView strokeWidth
QStringView stopOpacity
QStringView imageRendering
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent