Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 "qsvgfilter_p.h"
12#include "qsvgnode_p.h"
13#include "qsvgfont_p.h"
14
15#include "qpen.h"
16#include "qpainterpath.h"
17#include "qbrush.h"
18#include "qcolor.h"
19#include "qtextformat.h"
20#include "qlist.h"
21#include "qfileinfo.h"
22#include "qfile.h"
23#include "qdir.h"
24#include "qdebug.h"
25#include "qmath.h"
26#include "qnumeric.h"
27#include <qregularexpression.h>
28#include "qtransform.h"
29#include "qvarlengtharray.h"
30#include "private/qmath_p.h"
31#include "qimagereader.h"
32
33#include "float.h"
34#include <cmath>
35
37
38Q_LOGGING_CATEGORY(lcSvgHandler, "qt.svg")
39
40static const char *qt_inherit_text = "inherit";
41#define QT_INHERIT QLatin1String(qt_inherit_text)
42
43static QByteArray prefixMessage(const QByteArray &msg, const QXmlStreamReader *r)
44{
46 if (r) {
47 if (const QFile *file = qobject_cast<const QFile *>(r->device()))
49 else
51 result.append(':');
52 result.append(QByteArray::number(r->lineNumber()));
53 if (const qint64 column = r->columnNumber()) {
54 result.append(':');
56 }
58 }
59 result.append(msg);
60 return result;
61}
62
63static inline QByteArray msgProblemParsing(const QString &localName, const QXmlStreamReader *r)
64{
65 return prefixMessage(QByteArrayLiteral("Problem parsing ") + localName.toLocal8Bit(), r);
66}
67
68static inline QByteArray msgCouldNotResolveProperty(const QString &id, const QXmlStreamReader *r)
69{
70 return prefixMessage(QByteArrayLiteral("Could not resolve property: ") + id.toLocal8Bit(), r);
71}
72
73// ======== duplicated from qcolor_p
74
75static inline int qsvg_h2i(char hex, bool *ok = nullptr)
76{
77 if (hex >= '0' && hex <= '9')
78 return hex - '0';
79 if (hex >= 'a' && hex <= 'f')
80 return hex - 'a' + 10;
81 if (hex >= 'A' && hex <= 'F')
82 return hex - 'A' + 10;
83 if (ok)
84 *ok = false;
85 return -1;
86}
87
88static inline int qsvg_hex2int(const char *s, bool *ok = nullptr)
89{
90 return (qsvg_h2i(s[0], ok) * 16) | qsvg_h2i(s[1], ok);
91}
92
93static inline int qsvg_hex2int(char s, bool *ok = nullptr)
94{
95 int h = qsvg_h2i(s, ok);
96 return (h * 16) | h;
97}
98
99bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
100{
101 if(name[0] != '#')
102 return false;
103 name++;
104 const size_t len = qstrlen(name);
105 int r, g, b;
106 bool ok = true;
107 if (len == 12) {
108 r = qsvg_hex2int(name, &ok);
109 g = qsvg_hex2int(name + 4, &ok);
110 b = qsvg_hex2int(name + 8, &ok);
111 } else if (len == 9) {
112 r = qsvg_hex2int(name, &ok);
113 g = qsvg_hex2int(name + 3, &ok);
114 b = qsvg_hex2int(name + 6, &ok);
115 } else if (len == 6) {
116 r = qsvg_hex2int(name, &ok);
117 g = qsvg_hex2int(name + 2, &ok);
118 b = qsvg_hex2int(name + 4, &ok);
119 } else if (len == 3) {
120 r = qsvg_hex2int(name[0], &ok);
121 g = qsvg_hex2int(name[1], &ok);
122 b = qsvg_hex2int(name[2], &ok);
123 } else {
124 r = g = b = -1;
125 }
126 if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255 || !ok) {
127 *rgb = 0;
128 return false;
129 }
130 *rgb = qRgb(r, g ,b);
131 return true;
132}
133
135{
136 if (len > 13)
137 return false;
138 char tmp[16];
139 for(int i = 0; i < len; ++i)
140 tmp[i] = str[i].toLatin1();
141 tmp[len] = 0;
142 return qsvg_get_hex_rgb(tmp, rgb);
143}
144
145// ======== end of qcolor_p duplicate
146
147static bool parsePathDataFast(QStringView data, QPainterPath &path, bool limitLength = true);
148
149static inline QString someId(const QXmlStreamAttributes &attributes)
150{
151 QString id = attributes.value(QLatin1String("id")).toString();
152 if (id.isEmpty())
153 id = attributes.value(QLatin1String("xml:id")).toString();
154 return id;
155}
156
203
204QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
205{
206 for (int i = 0; i < xmlAttributes.size(); ++i) {
207 const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
208 QStringView name = attribute.qualifiedName();
209 if (name.isEmpty())
210 continue;
211 QStringView value = attribute.value();
212
213 switch (name.at(0).unicode()) {
214
215 case 'c':
216 if (name == QLatin1String("color"))
217 color = value;
218 else if (name == QLatin1String("color-opacity"))
220 else if (name == QLatin1String("comp-op"))
221 compOp = value;
222 break;
223
224 case 'd':
225 if (name == QLatin1String("display"))
226 display = value;
227 break;
228
229 case 'f':
230 if (name == QLatin1String("fill"))
231 fill = value;
232 else if (name == QLatin1String("fill-rule"))
233 fillRule = value;
234 else if (name == QLatin1String("fill-opacity"))
236 else if (name == QLatin1String("font-family"))
238 else if (name == QLatin1String("font-size"))
239 fontSize = value;
240 else if (name == QLatin1String("font-style"))
242 else if (name == QLatin1String("font-weight"))
244 else if (name == QLatin1String("font-variant"))
246 else if (name == QLatin1String("filter") &&
247 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
248 filter = value;
249 break;
250
251 case 'i':
252 if (name == QLatin1String("id"))
253 id = value.toString();
254 else if (name == QLatin1String("image-rendering"))
256 break;
257
258 case 'm':
259 if (name == QLatin1String("mask") &&
260 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
261 mask = value;
262 if (name == QLatin1String("marker-start") &&
263 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
265 if (name == QLatin1String("marker-mid") &&
266 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
268 if (name == QLatin1String("marker-end") &&
269 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
271 break;
272
273 case 'o':
274 if (name == QLatin1String("opacity"))
275 opacity = value;
276 if (name == QLatin1String("offset"))
277 offset = value;
278 break;
279
280 case 's':
281 if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
282 QStringView strokeRef = name.mid(6, name.size() - 6);
283 if (strokeRef.isEmpty())
284 stroke = value;
285 else if (strokeRef == QLatin1String("-dasharray"))
287 else if (strokeRef == QLatin1String("-dashoffset"))
289 else if (strokeRef == QLatin1String("-linecap"))
291 else if (strokeRef == QLatin1String("-linejoin"))
293 else if (strokeRef == QLatin1String("-miterlimit"))
295 else if (strokeRef == QLatin1String("-opacity"))
297 else if (strokeRef == QLatin1String("-width"))
299 } else if (name == QLatin1String("stop-color"))
301 else if (name == QLatin1String("stop-opacity"))
303 break;
304
305 case 't':
306 if (name == QLatin1String("text-anchor"))
308 else if (name == QLatin1String("transform"))
310 break;
311
312 case 'v':
313 if (name == QLatin1String("vector-effect"))
315 else if (name == QLatin1String("visibility"))
317 break;
318
319 case 'x':
320 if (name == QLatin1String("xml:id") && id.isEmpty())
321 id = value.toString();
322 break;
323
324 default:
325 break;
326 }
327 }
328
329 // If a style attribute is present, let its attribute settings override the plain attribute
330 // values. The spec seems to indicate that, and it is common behavior in svg renderers.
331#ifndef QT_NO_CSSPARSER
332 QStringView style = xmlAttributes.value(QLatin1String("style"));
333 if (!style.isEmpty()) {
334 handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
335 for (int j = 0; j < m_cssAttributes.size(); ++j) {
339 if (name.isEmpty())
340 continue;
341
342 switch (name.at(0).unicode()) {
343
344 case 'c':
345 if (name == QLatin1String("color"))
346 color = value;
347 else if (name == QLatin1String("color-opacity"))
349 else if (name == QLatin1String("comp-op"))
350 compOp = value;
351 break;
352
353 case 'd':
354 if (name == QLatin1String("display"))
355 display = value;
356 break;
357
358 case 'f':
359 if (name == QLatin1String("fill"))
360 fill = value;
361 else if (name == QLatin1String("fill-rule"))
362 fillRule = value;
363 else if (name == QLatin1String("fill-opacity"))
365 else if (name == QLatin1String("font-family"))
367 else if (name == QLatin1String("font-size"))
368 fontSize = value;
369 else if (name == QLatin1String("font-style"))
371 else if (name == QLatin1String("font-weight"))
373 else if (name == QLatin1String("font-variant"))
375 else if (name == QLatin1String("filter") &&
376 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
377 filter = value;
378 break;
379
380 case 'i':
381 if (name == QLatin1String("image-rendering"))
383 break;
384
385 case 'm':
386 if (name == QLatin1String("mask") &&
387 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
388 mask = value;
389 if (name == QLatin1String("marker-start") &&
390 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
392 if (name == QLatin1String("marker-mid") &&
393 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
395 if (name == QLatin1String("marker-end") &&
396 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
398 break;
399
400 case 'o':
401 if (name == QLatin1String("opacity"))
402 opacity = value;
403 else if (name == QLatin1String("offset"))
404 offset = value;
405 break;
406
407 case 's':
408 if (name.size() > 5 && name.mid(1, 5) == QLatin1String("troke")) {
409 QStringView strokeRef = name.mid(6, name.size() - 6);
410 if (strokeRef.isEmpty())
411 stroke = value;
412 else if (strokeRef == QLatin1String("-dasharray"))
414 else if (strokeRef == QLatin1String("-dashoffset"))
416 else if (strokeRef == QLatin1String("-linecap"))
418 else if (strokeRef == QLatin1String("-linejoin"))
420 else if (strokeRef == QLatin1String("-miterlimit"))
422 else if (strokeRef == QLatin1String("-opacity"))
424 else if (strokeRef == QLatin1String("-width"))
426 } else if (name == QLatin1String("stop-color"))
428 else if (name == QLatin1String("stop-opacity"))
430 break;
431
432 case 't':
433 if (name == QLatin1String("text-anchor"))
435 else if (name == QLatin1String("transform"))
437 break;
438
439 case 'v':
440 if (name == QLatin1String("vector-effect"))
442 else if (name == QLatin1String("visibility"))
444 break;
445
446 default:
447 break;
448 }
449 }
450 }
451#else
452 Q_UNUSED(handler);
453#endif // QT_NO_CSSPARSER
454}
455
456#ifndef QT_NO_CSSPARSER
457
459{
460public:
466 {
467 }
468
469 inline QString nodeToName(QSvgNode *node) const
470 {
471 return node->typeName();
472 }
473
474 inline QSvgNode *svgNode(NodePtr node) const
475 {
476 return (QSvgNode*)node.ptr;
477 }
479 {
480 if (n &&
481 (n->type() == QSvgNode::Doc ||
482 n->type() == QSvgNode::Group ||
483 n->type() == QSvgNode::Defs ||
484 n->type() == QSvgNode::Switch)) {
485 return (QSvgStructureNode*)n;
486 }
487 return 0;
488 }
489
491 {
492 QSvgNode *n = svgNode(node);
494 return st;
495 }
496
497 bool nodeNameEquals(NodePtr node, const QString& nodeName) const override
498 {
499 QSvgNode *n = svgNode(node);
500 if (!n)
501 return false;
503 return QString::compare(name, nodeName, Qt::CaseInsensitive) == 0;
504 }
505 QString attributeValue(NodePtr node, const QCss::AttributeSelector &asel) const override
506 {
507 const QString &name = asel.name;
508 QSvgNode *n = svgNode(node);
509 if ((!n->nodeId().isEmpty() && (name == QLatin1String("id") ||
510 name == QLatin1String("xml:id"))))
511 return n->nodeId();
512 if (!n->xmlClass().isEmpty() && name == QLatin1String("class"))
513 return n->xmlClass();
514 return QString();
515 }
516 bool hasAttributes(NodePtr node) const override
517 {
518 QSvgNode *n = svgNode(node);
519 return (n &&
520 (!n->nodeId().isEmpty() || !n->xmlClass().isEmpty()));
521 }
522
523 QStringList nodeIds(NodePtr node) const override
524 {
525 QSvgNode *n = svgNode(node);
526 QString nid;
527 if (n)
528 nid = n->nodeId();
529 QStringList lst; lst.append(nid);
530 return lst;
531 }
532
533 QStringList nodeNames(NodePtr node) const override
534 {
535 QSvgNode *n = svgNode(node);
536 if (n)
537 return QStringList(nodeToName(n));
538 return QStringList();
539 }
540
541 bool isNullNode(NodePtr node) const override
542 {
543 return !node.ptr;
544 }
545
546 NodePtr parentNode(NodePtr node) const override
547 {
548 QSvgNode *n = svgNode(node);
549 NodePtr newNode;
550 newNode.ptr = 0;
551 newNode.id = 0;
552 if (n) {
553 QSvgNode *svgParent = n->parent();
554 if (svgParent) {
555 newNode.ptr = svgParent;
556 }
557 }
558 return newNode;
559 }
561 {
562 NodePtr newNode;
563 newNode.ptr = 0;
564 newNode.id = 0;
565
566 QSvgNode *n = svgNode(node);
567 if (!n)
568 return newNode;
569 QSvgStructureNode *svgParent = nodeToStructure(n->parent());
570
571 if (svgParent) {
572 newNode.ptr = svgParent->previousSiblingNode(n);
573 }
574 return newNode;
575 }
576 NodePtr duplicateNode(NodePtr node) const override
577 {
578 NodePtr n;
579 n.ptr = node.ptr;
580 n.id = node.id;
581 return n;
582 }
583 void freeNode(NodePtr node) const override
584 {
585 Q_UNUSED(node);
586 }
587};
588
589#endif // QT_NO_CSSPARSER
590
591// '0' is 0x30 and '9' is 0x39
592static inline bool isDigit(ushort ch)
593{
594 static quint16 magic = 0x3ff;
595 return ((ch >> 4) == 3) && (magic >> (ch & 15));
596}
597
598static qreal toDouble(const QChar *&str)
599{
600 const int maxLen = 255;//technically doubles can go til 308+ but whatever
601 char temp[maxLen+1];
602 int pos = 0;
603
604 if (*str == QLatin1Char('-')) {
605 temp[pos++] = '-';
606 ++str;
607 } else if (*str == QLatin1Char('+')) {
608 ++str;
609 }
610 while (isDigit(str->unicode()) && pos < maxLen) {
611 temp[pos++] = str->toLatin1();
612 ++str;
613 }
614 if (*str == QLatin1Char('.') && pos < maxLen) {
615 temp[pos++] = '.';
616 ++str;
617 }
618 while (isDigit(str->unicode()) && pos < maxLen) {
619 temp[pos++] = str->toLatin1();
620 ++str;
621 }
622 bool exponent = false;
623 if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
624 exponent = true;
625 temp[pos++] = 'e';
626 ++str;
627 if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
628 temp[pos++] = str->toLatin1();
629 ++str;
630 }
631 while (isDigit(str->unicode()) && pos < maxLen) {
632 temp[pos++] = str->toLatin1();
633 ++str;
634 }
635 }
636
637 temp[pos] = '\0';
638
639 qreal val;
640 if (!exponent && pos < 10) {
641 int ival = 0;
642 const char *t = temp;
643 bool neg = false;
644 if(*t == '-') {
645 neg = true;
646 ++t;
647 }
648 while(*t && *t != '.') {
649 ival *= 10;
650 ival += (*t) - '0';
651 ++t;
652 }
653 if(*t == '.') {
654 ++t;
655 int div = 1;
656 while(*t) {
657 ival *= 10;
658 ival += (*t) - '0';
659 div *= 10;
660 ++t;
661 }
662 val = ((qreal)ival)/((qreal)div);
663 } else {
664 val = ival;
665 }
666 if (neg)
667 val = -val;
668 } else {
669 val = QByteArray::fromRawData(temp, pos).toDouble();
670 // Do not tolerate values too wild to be represented normally by floats
671 if (qFpClassify(float(val)) != FP_NORMAL)
672 val = 0;
673 }
674 return val;
675
676}
677
678static qreal toDouble(QStringView str, bool *ok = NULL)
679{
680 const QChar *c = str.constData();
681 qreal res = (c == nullptr ? qreal{} : toDouble(c));
682 if (ok)
683 *ok = (c == (str.constData() + str.size()));
684 return res;
685}
686
687static QList<qreal> parseNumbersList(const QChar *&str)
688{
689 QList<qreal> points;
690 if (!str)
691 return points;
692 points.reserve(32);
693
694 while (str->isSpace())
695 ++str;
696 while (isDigit(str->unicode()) ||
697 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
698 *str == QLatin1Char('.')) {
699
701
702 while (str->isSpace())
703 ++str;
704 if (*str == QLatin1Char(','))
705 ++str;
706
707 //eat the rest of space
708 while (str->isSpace())
709 ++str;
710 }
711
712 return points;
713}
714
715static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points,
716 const char *pattern = nullptr)
717{
718 const size_t patternLen = qstrlen(pattern);
719 while (str->isSpace())
720 ++str;
721 while (isDigit(str->unicode()) ||
722 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
723 *str == QLatin1Char('.')) {
724
725 if (patternLen && pattern[points.size() % patternLen] == 'f') {
726 // flag expected, may only be 0 or 1
727 if (*str != QLatin1Char('0') && *str != QLatin1Char('1'))
728 return;
729 points.append(*str == QLatin1Char('0') ? 0.0 : 1.0);
730 ++str;
731 } else {
733 }
734
735 while (str->isSpace())
736 ++str;
737 if (*str == QLatin1Char(','))
738 ++str;
739
740 //eat the rest of space
741 while (str->isSpace())
742 ++str;
743 }
744}
745
746static QList<qreal> parsePercentageList(const QChar *&str)
747{
748 QList<qreal> points;
749 if (!str)
750 return points;
751
752 while (str->isSpace())
753 ++str;
754 while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
755 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
756 *str == QLatin1Char('.')) {
757
759
760 while (str->isSpace())
761 ++str;
762 if (*str == QLatin1Char('%'))
763 ++str;
764 while (str->isSpace())
765 ++str;
766 if (*str == QLatin1Char(','))
767 ++str;
768
769 //eat the rest of space
770 while (str->isSpace())
771 ++str;
772 }
773
774 return points;
775}
776
778{
779 // The form is url(<IRI>), where IRI can be
780 // just an ID on #<id> form.
781 QString::const_iterator itr = url.constBegin();
782 QString::const_iterator end = url.constEnd();
783 QString id;
784 while (itr != end && (*itr).isSpace())
785 ++itr;
786 if (itr != end && (*itr) == QLatin1Char('('))
787 ++itr;
788 else
789 return QString();
790 while (itr != end && (*itr).isSpace())
791 ++itr;
792 if (itr != end && (*itr) == QLatin1Char('#')) {
793 id += *itr;
794 ++itr;
795 } else {
796 return QString();
797 }
798 while (itr != end && (*itr) != QLatin1Char(')')) {
799 id += *itr;
800 ++itr;
801 }
802 if (itr == end || (*itr) != QLatin1Char(')'))
803 return QString();
804 return id;
805}
806
811static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
812{
813 QStringView colorStrTr = colorStr.trimmed();
814 if (colorStrTr.isEmpty())
815 return false;
816
817 switch(colorStrTr.at(0).unicode()) {
818
819 case '#':
820 {
821 // #rrggbb is very very common, so let's tackle it here
822 // rather than falling back to QColor
823 QRgb rgb;
824 bool ok = qsvg_get_hex_rgb(colorStrTr.constData(), colorStrTr.size(), &rgb);
825 if (ok)
826 color.setRgb(rgb);
827 return ok;
828 }
829 break;
830
831 case 'r':
832 {
833 // starts with "rgb(", ends with ")" and consists of at least 7 characters "rgb(,,)"
834 if (colorStrTr.size() >= 7 && colorStrTr.at(colorStrTr.size() - 1) == QLatin1Char(')')
835 && colorStrTr.mid(0, 4) == QLatin1String("rgb(")) {
836 const QChar *s = colorStrTr.constData() + 4;
837 QList<qreal> compo = parseNumbersList(s);
838 //1 means that it failed after reaching non-parsable
839 //character which is going to be "%"
840 if (compo.size() == 1) {
841 s = colorStrTr.constData() + 4;
842 compo = parsePercentageList(s);
843 for (int i = 0; i < compo.size(); ++i)
844 compo[i] *= (qreal)2.55;
845 }
846
847 if (compo.size() == 3) {
848 color = QColor(int(compo[0]),
849 int(compo[1]),
850 int(compo[2]));
851 return true;
852 }
853 return false;
854 }
855 }
856 break;
857
858 case 'c':
859 if (colorStrTr == QLatin1String("currentColor")) {
860 color = handler->currentColor();
861 return true;
862 }
863 break;
864 case 'i':
865 if (colorStrTr == QT_INHERIT)
866 return false;
867 break;
868 default:
869 break;
870 }
871
872 color = QColor::fromString(colorStrTr.toString());
873 return color.isValid();
874}
875
876static bool constructColor(QStringView colorStr, QStringView opacity,
877 QColor &color, QSvgHandler *handler)
878{
879 if (!resolveColor(colorStr, color, handler))
880 return false;
881 if (!opacity.isEmpty()) {
882 bool ok = true;
883 qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));
884 if (!ok)
885 op = 1.0;
886 color.setAlphaF(op);
887 }
888 return true;
889}
890
892 QSvgHandler *handler, bool *ok = NULL)
893{
894 QStringView numStr = str.trimmed();
895
896 if (numStr.isEmpty()) {
897 if (ok)
898 *ok = false;
900 return false;
901 }
902 if (numStr.endsWith(QLatin1Char('%'))) {
903 numStr.chop(1);
905 } else if (numStr.endsWith(QLatin1String("px"))) {
906 numStr.chop(2);
908 } else if (numStr.endsWith(QLatin1String("pc"))) {
909 numStr.chop(2);
911 } else if (numStr.endsWith(QLatin1String("pt"))) {
912 numStr.chop(2);
914 } else if (numStr.endsWith(QLatin1String("mm"))) {
915 numStr.chop(2);
917 } else if (numStr.endsWith(QLatin1String("cm"))) {
918 numStr.chop(2);
920 } else if (numStr.endsWith(QLatin1String("in"))) {
921 numStr.chop(2);
923 } else {
924 *type = handler->defaultCoordinateSystem();
925 //type = QSvgHandler::LT_OTHER;
926 }
927 qreal len = toDouble(numStr, ok);
928 //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
929 return len;
930}
931
932static inline qreal convertToNumber(QStringView str, QSvgHandler *handler, bool *ok = NULL)
933{
935 qreal num = parseLength(str.toString(), &type, handler, ok);
937 num = num/100.0;
938 }
939 return num;
940}
941
942static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes)
943{
944 QStringView uncStr = attributes.value(QLatin1String("unicode"));
945 QStringView havStr = attributes.value(QLatin1String("horiz-adv-x"));
946 QStringView pathStr = attributes.value(QLatin1String("d"));
947
948 QChar unicode = (uncStr.isEmpty()) ? u'\0' : uncStr.at(0);
949 qreal havx = (havStr.isEmpty()) ? -1 : toDouble(havStr);
951 path.setFillRule(Qt::WindingFill);
952 parsePathDataFast(pathStr, path);
953
954 font->addGlyph(unicode, path, havx);
955
956 return true;
957}
958
959// this should really be called convertToDefaultCoordinateSystem
960// and convert when type != QSvgHandler::defaultCoordinateSystem
962{
963
964 switch (type) {
966 break;
968 break;
970 break;
972 return len * 1.25;
973 break;
975 return len * 3.543307;
976 break;
978 return len * 35.43307;
979 break;
981 return len * 90;
982 break;
984 break;
985 default:
986 break;
987 }
988 return len;
989}
990
991static void parseColor(QSvgNode *,
992 const QSvgAttributes &attributes,
993 QSvgHandler *handler)
994{
996 if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
997 handler->popColor();
998 handler->pushColor(color);
999 }
1000}
1001
1003{
1004 return node ? node->styleProperty(idFromUrl(url)) : 0;
1005}
1006
1007static void parseBrush(QSvgNode *node,
1008 const QSvgAttributes &attributes,
1009 QSvgHandler *handler)
1010{
1011 if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
1012 QSvgFillStyle *prop = new QSvgFillStyle;
1013
1014 //fill-rule attribute handling
1015 if (!attributes.fillRule.isEmpty() && attributes.fillRule != QT_INHERIT) {
1016 if (attributes.fillRule == QLatin1String("evenodd"))
1018 else if (attributes.fillRule == QLatin1String("nonzero"))
1020 }
1021
1022 //fill-opacity attribute handling
1023 if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != QT_INHERIT) {
1024 prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.fillOpacity))));
1025 }
1026
1027 //fill attribute handling
1028 if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
1029 if (attributes.fill.size() > 3 && attributes.fill.mid(0, 3) == QLatin1String("url")) {
1030 QString value = attributes.fill.mid(3, attributes.fill.size() - 3).toString();
1031 QSvgStyleProperty *style = styleFromUrl(node, value);
1032 if (style) {
1034 || style->type() == QSvgStyleProperty::PATTERN)
1035 prop->setFillStyle(reinterpret_cast<QSvgPaintStyleProperty *>(style));
1036 } else {
1037 QString id = idFromUrl(value);
1038 prop->setPaintStyleId(id);
1039 prop->setPaintStyleResolved(false);
1040 }
1041 } else if (attributes.fill != QLatin1String("none")) {
1042 QColor color;
1043 if (resolveColor(attributes.fill, color, handler))
1044 prop->setBrush(QBrush(color));
1045 } else {
1046 prop->setBrush(QBrush(Qt::NoBrush));
1047 }
1048 }
1049 node->appendStyleProperty(prop, attributes.id);
1050 }
1051}
1052
1053
1054
1056{
1057 if (value.isEmpty())
1058 return QTransform();
1059
1061 const QChar *str = value.constData();
1062 const QChar *end = str + value.size();
1063
1064 while (str < end) {
1065 if (str->isSpace() || *str == QLatin1Char(',')) {
1066 ++str;
1067 continue;
1068 }
1069 enum State {
1070 Matrix,
1071 Translate,
1072 Rotate,
1073 Scale,
1074 SkewX,
1075 SkewY
1076 };
1077 State state = Matrix;
1078 if (*str == QLatin1Char('m')) { //matrix
1079 const char *ident = "atrix";
1080 for (int i = 0; i < 5; ++i)
1081 if (*(++str) != QLatin1Char(ident[i]))
1082 goto error;
1083 ++str;
1084 state = Matrix;
1085 } else if (*str == QLatin1Char('t')) { //translate
1086 const char *ident = "ranslate";
1087 for (int i = 0; i < 8; ++i)
1088 if (*(++str) != QLatin1Char(ident[i]))
1089 goto error;
1090 ++str;
1091 state = Translate;
1092 } else if (*str == QLatin1Char('r')) { //rotate
1093 const char *ident = "otate";
1094 for (int i = 0; i < 5; ++i)
1095 if (*(++str) != QLatin1Char(ident[i]))
1096 goto error;
1097 ++str;
1098 state = Rotate;
1099 } else if (*str == QLatin1Char('s')) { //scale, skewX, skewY
1100 ++str;
1101 if (*str == QLatin1Char('c')) {
1102 const char *ident = "ale";
1103 for (int i = 0; i < 3; ++i)
1104 if (*(++str) != QLatin1Char(ident[i]))
1105 goto error;
1106 ++str;
1107 state = Scale;
1108 } else if (*str == QLatin1Char('k')) {
1109 if (*(++str) != QLatin1Char('e'))
1110 goto error;
1111 if (*(++str) != QLatin1Char('w'))
1112 goto error;
1113 ++str;
1114 if (*str == QLatin1Char('X'))
1115 state = SkewX;
1116 else if (*str == QLatin1Char('Y'))
1117 state = SkewY;
1118 else
1119 goto error;
1120 ++str;
1121 } else {
1122 goto error;
1123 }
1124 } else {
1125 goto error;
1126 }
1127
1128
1129 while (str < end && str->isSpace())
1130 ++str;
1131 if (*str != QLatin1Char('('))
1132 goto error;
1133 ++str;
1134 QVarLengthArray<qreal, 8> points;
1136 if (*str != QLatin1Char(')'))
1137 goto error;
1138 ++str;
1139
1140 if(state == Matrix) {
1141 if(points.size() != 6)
1142 goto error;
1143 matrix = QTransform(points[0], points[1],
1144 points[2], points[3],
1145 points[4], points[5]) * matrix;
1146 } else if (state == Translate) {
1147 if (points.size() == 1)
1148 matrix.translate(points[0], 0);
1149 else if (points.size() == 2)
1150 matrix.translate(points[0], points[1]);
1151 else
1152 goto error;
1153 } else if (state == Rotate) {
1154 if(points.size() == 1) {
1155 matrix.rotate(points[0]);
1156 } else if (points.size() == 3) {
1157 matrix.translate(points[1], points[2]);
1158 matrix.rotate(points[0]);
1159 matrix.translate(-points[1], -points[2]);
1160 } else {
1161 goto error;
1162 }
1163 } else if (state == Scale) {
1164 if (points.size() < 1 || points.size() > 2)
1165 goto error;
1166 qreal sx = points[0];
1167 qreal sy = sx;
1168 if(points.size() == 2)
1169 sy = points[1];
1170 matrix.scale(sx, sy);
1171 } else if (state == SkewX) {
1172 if (points.size() != 1)
1173 goto error;
1174 matrix.shear(qTan(qDegreesToRadians(points[0])), 0);
1175 } else if (state == SkewY) {
1176 if (points.size() != 1)
1177 goto error;
1178 matrix.shear(0, qTan(qDegreesToRadians(points[0])));
1179 }
1180 }
1181 error:
1182 return matrix;
1183}
1184
1185static void parsePen(QSvgNode *node,
1186 const QSvgAttributes &attributes,
1187 QSvgHandler *handler)
1188{
1189 //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
1190
1191 if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
1192 || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
1193 || !attributes.vectorEffect.isEmpty()) {
1194
1195 QSvgStrokeStyle *prop = new QSvgStrokeStyle;
1196
1197 //stroke attribute handling
1198 if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
1199 if (attributes.stroke.size() > 3 && attributes.stroke.mid(0, 3) == QLatin1String("url")) {
1200 QString value = attributes.stroke.mid(3, attributes.stroke.size() - 3).toString();
1201 QSvgStyleProperty *style = styleFromUrl(node, value);
1202 if (style) {
1204 || style->type() == QSvgStyleProperty::PATTERN)
1205 prop->setStyle(reinterpret_cast<QSvgPaintStyleProperty *>(style));
1206 } else {
1207 QString id = idFromUrl(value);
1208 prop->setPaintStyleId(id);
1209 prop->setPaintStyleResolved(false);
1210 }
1211 } else if (attributes.stroke != QLatin1String("none")) {
1212 QColor color;
1213 if (resolveColor(attributes.stroke, color, handler))
1214 prop->setStroke(QBrush(color));
1215 } else {
1217 }
1218 }
1219
1220 //stroke-width handling
1221 if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != QT_INHERIT) {
1223 prop->setWidth(parseLength(attributes.strokeWidth, &lt, handler));
1224 }
1225
1226 //stroke-dasharray
1227 if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != QT_INHERIT) {
1228 if (attributes.strokeDashArray == QLatin1String("none")) {
1229 prop->setDashArrayNone();
1230 } else {
1232 const QChar *s = dashArray.constData();
1233 QList<qreal> dashes = parseNumbersList(s);
1234 bool allZeroes = true;
1235 for (qreal dash : dashes) {
1236 if (dash != 0.0) {
1237 allZeroes = false;
1238 break;
1239 }
1240 }
1241
1242 // if the stroke dash array contains only zeros,
1243 // force drawing of solid line.
1244 if (allZeroes == false) {
1245 // if the dash count is odd the dashes should be duplicated
1246 if ((dashes.size() & 1) != 0)
1247 dashes << QList<qreal>(dashes);
1248 prop->setDashArray(dashes);
1249 } else {
1250 prop->setDashArrayNone();
1251 }
1252 }
1253 }
1254
1255 //stroke-linejoin attribute handling
1256 if (!attributes.strokeLineJoin.isEmpty()) {
1257 if (attributes.strokeLineJoin == QLatin1String("miter"))
1259 else if (attributes.strokeLineJoin == QLatin1String("round"))
1261 else if (attributes.strokeLineJoin == QLatin1String("bevel"))
1263 }
1264
1265 //stroke-linecap attribute handling
1266 if (!attributes.strokeLineCap.isEmpty()) {
1267 if (attributes.strokeLineCap == QLatin1String("butt"))
1268 prop->setLineCap(Qt::FlatCap);
1269 else if (attributes.strokeLineCap == QLatin1String("round"))
1270 prop->setLineCap(Qt::RoundCap);
1271 else if (attributes.strokeLineCap == QLatin1String("square"))
1273 }
1274
1275 //stroke-dashoffset attribute handling
1276 if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != QT_INHERIT)
1277 prop->setDashOffset(toDouble(attributes.strokeDashOffset));
1278
1279 //vector-effect attribute handling
1280 if (!attributes.vectorEffect.isEmpty()) {
1281 if (attributes.vectorEffect == QLatin1String("non-scaling-stroke"))
1282 prop->setVectorEffect(true);
1283 else if (attributes.vectorEffect == QLatin1String("none"))
1284 prop->setVectorEffect(false);
1285 }
1286
1287 //stroke-miterlimit
1288 if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != QT_INHERIT)
1289 prop->setMiterLimit(toDouble(attributes.strokeMiterLimit));
1290
1291 //stroke-opacity atttribute handling
1292 if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != QT_INHERIT)
1293 prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.strokeOpacity))));
1294
1295 node->appendStyleProperty(prop, attributes.id);
1296 }
1297}
1298
1301
1302static const qreal sizeTable[] =
1303{ qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
1304
1306
1308{
1309 switch (spec.at(0).unicode()) {
1310 case 'x':
1311 if (spec == QLatin1String("xx-small"))
1312 return XXSmall;
1313 if (spec == QLatin1String("x-small"))
1314 return XSmall;
1315 if (spec == QLatin1String("x-large"))
1316 return XLarge;
1317 if (spec == QLatin1String("xx-large"))
1318 return XXLarge;
1319 break;
1320 case 's':
1321 if (spec == QLatin1String("small"))
1322 return Small;
1323 break;
1324 case 'm':
1325 if (spec == QLatin1String("medium"))
1326 return Medium;
1327 break;
1328 case 'l':
1329 if (spec == QLatin1String("large"))
1330 return Large;
1331 break;
1332 case 'n':
1333 if (spec == QLatin1String("none"))
1334 return FontSizeNone;
1335 break;
1336 default:
1337 break;
1338 }
1339 return FontSizeValue;
1340}
1341
1342static void parseFont(QSvgNode *node,
1343 const QSvgAttributes &attributes,
1344 QSvgHandler *handler)
1345{
1346 if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
1347 attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
1348 return;
1349
1350 QSvgFontStyle *fontStyle = nullptr;
1351 if (!attributes.fontFamily.isEmpty()) {
1352 QSvgTinyDocument *doc = node->document();
1353 if (doc) {
1354 QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
1355 if (svgFont)
1356 fontStyle = new QSvgFontStyle(svgFont, doc);
1357 }
1358 }
1359 if (!fontStyle)
1361 if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT) {
1362 QString family = attributes.fontFamily.toString().trimmed();
1363 if (family.at(0) == QLatin1Char('\'') || family.at(0) == QLatin1Char('\"'))
1364 family = family.mid(1, family.size() - 2);
1365 fontStyle->setFamily(family);
1366 }
1367
1368 if (!attributes.fontSize.isEmpty() && attributes.fontSize != QT_INHERIT) {
1369 // TODO: Support relative sizes 'larger' and 'smaller'.
1370 const FontSizeSpec spec = fontSizeSpec(attributes.fontSize);
1371 switch (spec) {
1372 case FontSizeNone:
1373 break;
1374 case FontSizeValue: {
1376 qreal fs = parseLength(attributes.fontSize, &type, handler);
1377 fs = convertToPixels(fs, true, type);
1378 fontStyle->setSize(qMin(fs, qreal(0xffff)));
1379 }
1380 break;
1381 default:
1382 fontStyle->setSize(sizeTable[spec]);
1383 break;
1384 }
1385 }
1386
1387 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
1388 if (attributes.fontStyle == QLatin1String("normal")) {
1389 fontStyle->setStyle(QFont::StyleNormal);
1390 } else if (attributes.fontStyle == QLatin1String("italic")) {
1391 fontStyle->setStyle(QFont::StyleItalic);
1392 } else if (attributes.fontStyle == QLatin1String("oblique")) {
1393 fontStyle->setStyle(QFont::StyleOblique);
1394 }
1395 }
1396
1397 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
1398 bool ok = false;
1399 const int weightNum = attributes.fontWeight.toInt(&ok);
1400 if (ok) {
1401 fontStyle->setWeight(weightNum);
1402 } else {
1403 if (attributes.fontWeight == QLatin1String("normal")) {
1404 fontStyle->setWeight(QFont::Normal);
1405 } else if (attributes.fontWeight == QLatin1String("bold")) {
1406 fontStyle->setWeight(QFont::Bold);
1407 } else if (attributes.fontWeight == QLatin1String("bolder")) {
1408 fontStyle->setWeight(QSvgFontStyle::BOLDER);
1409 } else if (attributes.fontWeight == QLatin1String("lighter")) {
1411 }
1412 }
1413 }
1414
1415 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
1416 if (attributes.fontVariant == QLatin1String("normal"))
1417 fontStyle->setVariant(QFont::MixedCase);
1418 else if (attributes.fontVariant == QLatin1String("small-caps"))
1419 fontStyle->setVariant(QFont::SmallCaps);
1420 }
1421
1422 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
1423 if (attributes.textAnchor == QLatin1String("start"))
1424 fontStyle->setTextAnchor(Qt::AlignLeft);
1425 if (attributes.textAnchor == QLatin1String("middle"))
1426 fontStyle->setTextAnchor(Qt::AlignHCenter);
1427 else if (attributes.textAnchor == QLatin1String("end"))
1428 fontStyle->setTextAnchor(Qt::AlignRight);
1429 }
1430
1431 node->appendStyleProperty(fontStyle, attributes.id);
1432}
1433
1434static void parseTransform(QSvgNode *node,
1435 const QSvgAttributes &attributes,
1436 QSvgHandler *)
1437{
1438 if (attributes.transform.isEmpty())
1439 return;
1441
1442 if (!matrix.isIdentity()) {
1444 }
1445
1446}
1447
1448static void parseVisibility(QSvgNode *node,
1449 const QSvgAttributes &attributes,
1450 QSvgHandler *)
1451{
1452 QSvgNode *parent = node->parent();
1453
1454 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
1455 node->setVisible(parent->isVisible());
1456 else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
1457 node->setVisible(false);
1458 } else
1459 node->setVisible(true);
1460}
1461
1463 qreal xc, qreal yc,
1464 qreal th0, qreal th1,
1465 qreal rx, qreal ry, qreal xAxisRotation)
1466{
1467 qreal sinTh, cosTh;
1468 qreal a00, a01, a10, a11;
1469 qreal x1, y1, x2, y2, x3, y3;
1470 qreal t;
1471 qreal thHalf;
1472
1473 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
1474 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
1475
1476 a00 = cosTh * rx;
1477 a01 = -sinTh * ry;
1478 a10 = sinTh * rx;
1479 a11 = cosTh * ry;
1480
1481 thHalf = 0.5 * (th1 - th0);
1482 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
1483 x1 = xc + qCos(th0) - t * qSin(th0);
1484 y1 = yc + qSin(th0) + t * qCos(th0);
1485 x3 = xc + qCos(th1);
1486 y3 = yc + qSin(th1);
1487 x2 = x3 + t * qSin(th1);
1488 y2 = y3 - t * qCos(th1);
1489
1490 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
1491 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
1492 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
1493}
1494
1495// the arc handling code underneath is from XSVG (BSD license)
1496/*
1497 * Copyright 2002 USC/Information Sciences Institute
1498 *
1499 * Permission to use, copy, modify, distribute, and sell this software
1500 * and its documentation for any purpose is hereby granted without
1501 * fee, provided that the above copyright notice appear in all copies
1502 * and that both that copyright notice and this permission notice
1503 * appear in supporting documentation, and that the name of
1504 * Information Sciences Institute not be used in advertising or
1505 * publicity pertaining to distribution of the software without
1506 * specific, written prior permission. Information Sciences Institute
1507 * makes no representations about the suitability of this software for
1508 * any purpose. It is provided "as is" without express or implied
1509 * warranty.
1510 *
1511 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
1512 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
1513 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
1514 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1515 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
1516 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1517 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1518 * PERFORMANCE OF THIS SOFTWARE.
1519 *
1520 */
1522 qreal rx,
1523 qreal ry,
1524 qreal x_axis_rotation,
1525 int large_arc_flag,
1526 int sweep_flag,
1527 qreal x,
1528 qreal y,
1529 qreal curx, qreal cury)
1530{
1531 const qreal Pr1 = rx * rx;
1532 const qreal Pr2 = ry * ry;
1533
1534 if (!Pr1 || !Pr2)
1535 return;
1536
1537 qreal sin_th, cos_th;
1538 qreal a00, a01, a10, a11;
1539 qreal x0, y0, x1, y1, xc, yc;
1540 qreal d, sfactor, sfactor_sq;
1541 qreal th0, th1, th_arc;
1542 int i, n_segs;
1543 qreal dx, dy, dx1, dy1, Px, Py, check;
1544
1545 rx = qAbs(rx);
1546 ry = qAbs(ry);
1547
1548 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
1549 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
1550
1551 dx = (curx - x) / 2.0;
1552 dy = (cury - y) / 2.0;
1553 dx1 = cos_th * dx + sin_th * dy;
1554 dy1 = -sin_th * dx + cos_th * dy;
1555 Px = dx1 * dx1;
1556 Py = dy1 * dy1;
1557 /* Spec : check if radii are large enough */
1558 check = Px / Pr1 + Py / Pr2;
1559 if (check > 1) {
1560 rx = rx * qSqrt(check);
1561 ry = ry * qSqrt(check);
1562 }
1563
1564 a00 = cos_th / rx;
1565 a01 = sin_th / rx;
1566 a10 = -sin_th / ry;
1567 a11 = cos_th / ry;
1568 x0 = a00 * curx + a01 * cury;
1569 y0 = a10 * curx + a11 * cury;
1570 x1 = a00 * x + a01 * y;
1571 y1 = a10 * x + a11 * y;
1572 /* (x0, y0) is current point in transformed coordinate space.
1573 (x1, y1) is new point in transformed coordinate space.
1574
1575 The arc fits a unit-radius circle in this space.
1576 */
1577 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
1578 if (!d)
1579 return;
1580 sfactor_sq = 1.0 / d - 0.25;
1581 if (sfactor_sq < 0) sfactor_sq = 0;
1582 sfactor = qSqrt(sfactor_sq);
1583 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
1584 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
1585 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
1586 /* (xc, yc) is center of the circle. */
1587
1588 th0 = qAtan2(y0 - yc, x0 - xc);
1589 th1 = qAtan2(y1 - yc, x1 - xc);
1590
1591 th_arc = th1 - th0;
1592 if (th_arc < 0 && sweep_flag)
1593 th_arc += 2 * Q_PI;
1594 else if (th_arc > 0 && !sweep_flag)
1595 th_arc -= 2 * Q_PI;
1596
1597 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
1598
1599 for (i = 0; i < n_segs; i++) {
1600 pathArcSegment(path, xc, yc,
1601 th0 + i * th_arc / n_segs,
1602 th0 + (i + 1) * th_arc / n_segs,
1603 rx, ry, x_axis_rotation);
1604 }
1605}
1606
1607static bool parsePathDataFast(QStringView dataStr, QPainterPath &path, bool limitLength)
1608{
1609 const int maxElementCount = 0x7fff; // Assume file corruption if more path elements than this
1610 qreal x0 = 0, y0 = 0; // starting point
1611 qreal x = 0, y = 0; // current point
1612 char lastMode = 0;
1613 QPointF ctrlPt;
1614 const QChar *str = dataStr.constData();
1615 const QChar *end = str + dataStr.size();
1616
1617 bool ok = true;
1618 while (ok && str != end) {
1619 while (str->isSpace() && (str + 1) != end)
1620 ++str;
1621 QChar pathElem = *str;
1622 ++str;
1623 QChar endc = *end;
1624 *const_cast<QChar *>(end) = u'\0'; // parseNumbersArray requires 0-termination that QStringView cannot guarantee
1625 const char *pattern = nullptr;
1626 if (pathElem == QLatin1Char('a') || pathElem == QLatin1Char('A'))
1627 pattern = "rrrffrr";
1628 QVarLengthArray<qreal, 8> arg;
1630 *const_cast<QChar *>(end) = endc;
1631 if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
1632 arg.append(0);//dummy
1633 const qreal *num = arg.constData();
1634 int count = arg.size();
1635 while (ok && count > 0) {
1636 qreal offsetX = x; // correction offsets
1637 qreal offsetY = y; // for relative commands
1638 switch (pathElem.unicode()) {
1639 case 'm': {
1640 if (count < 2) {
1641 ok = false;
1642 break;
1643 }
1644 x = x0 = num[0] + offsetX;
1645 y = y0 = num[1] + offsetY;
1646 num += 2;
1647 count -= 2;
1648 path.moveTo(x0, y0);
1649
1650 // As per 1.2 spec 8.3.2 The "moveto" commands
1651 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1652 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1653 pathElem = QLatin1Char('l');
1654 }
1655 break;
1656 case 'M': {
1657 if (count < 2) {
1658 ok = false;
1659 break;
1660 }
1661 x = x0 = num[0];
1662 y = y0 = num[1];
1663 num += 2;
1664 count -= 2;
1665 path.moveTo(x0, y0);
1666
1667 // As per 1.2 spec 8.3.2 The "moveto" commands
1668 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1669 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1670 pathElem = QLatin1Char('L');
1671 }
1672 break;
1673 case 'z':
1674 case 'Z': {
1675 x = x0;
1676 y = y0;
1677 count--; // skip dummy
1678 num++;
1679 path.closeSubpath();
1680 }
1681 break;
1682 case 'l': {
1683 if (count < 2) {
1684 ok = false;
1685 break;
1686 }
1687 x = num[0] + offsetX;
1688 y = num[1] + offsetY;
1689 num += 2;
1690 count -= 2;
1691 path.lineTo(x, y);
1692
1693 }
1694 break;
1695 case 'L': {
1696 if (count < 2) {
1697 ok = false;
1698 break;
1699 }
1700 x = num[0];
1701 y = num[1];
1702 num += 2;
1703 count -= 2;
1704 path.lineTo(x, y);
1705 }
1706 break;
1707 case 'h': {
1708 x = num[0] + offsetX;
1709 num++;
1710 count--;
1711 path.lineTo(x, y);
1712 }
1713 break;
1714 case 'H': {
1715 x = num[0];
1716 num++;
1717 count--;
1718 path.lineTo(x, y);
1719 }
1720 break;
1721 case 'v': {
1722 y = num[0] + offsetY;
1723 num++;
1724 count--;
1725 path.lineTo(x, y);
1726 }
1727 break;
1728 case 'V': {
1729 y = num[0];
1730 num++;
1731 count--;
1732 path.lineTo(x, y);
1733 }
1734 break;
1735 case 'c': {
1736 if (count < 6) {
1737 ok = false;
1738 break;
1739 }
1740 QPointF c1(num[0] + offsetX, num[1] + offsetY);
1741 QPointF c2(num[2] + offsetX, num[3] + offsetY);
1742 QPointF e(num[4] + offsetX, num[5] + offsetY);
1743 num += 6;
1744 count -= 6;
1745 path.cubicTo(c1, c2, e);
1746 ctrlPt = c2;
1747 x = e.x();
1748 y = e.y();
1749 break;
1750 }
1751 case 'C': {
1752 if (count < 6) {
1753 ok = false;
1754 break;
1755 }
1756 QPointF c1(num[0], num[1]);
1757 QPointF c2(num[2], num[3]);
1758 QPointF e(num[4], num[5]);
1759 num += 6;
1760 count -= 6;
1761 path.cubicTo(c1, c2, e);
1762 ctrlPt = c2;
1763 x = e.x();
1764 y = e.y();
1765 break;
1766 }
1767 case 's': {
1768 if (count < 4) {
1769 ok = false;
1770 break;
1771 }
1772 QPointF c1;
1773 if (lastMode == 'c' || lastMode == 'C' ||
1774 lastMode == 's' || lastMode == 'S')
1775 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1776 else
1777 c1 = QPointF(x, y);
1778 QPointF c2(num[0] + offsetX, num[1] + offsetY);
1779 QPointF e(num[2] + offsetX, num[3] + offsetY);
1780 num += 4;
1781 count -= 4;
1782 path.cubicTo(c1, c2, e);
1783 ctrlPt = c2;
1784 x = e.x();
1785 y = e.y();
1786 break;
1787 }
1788 case 'S': {
1789 if (count < 4) {
1790 ok = false;
1791 break;
1792 }
1793 QPointF c1;
1794 if (lastMode == 'c' || lastMode == 'C' ||
1795 lastMode == 's' || lastMode == 'S')
1796 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1797 else
1798 c1 = QPointF(x, y);
1799 QPointF c2(num[0], num[1]);
1800 QPointF e(num[2], num[3]);
1801 num += 4;
1802 count -= 4;
1803 path.cubicTo(c1, c2, e);
1804 ctrlPt = c2;
1805 x = e.x();
1806 y = e.y();
1807 break;
1808 }
1809 case 'q': {
1810 if (count < 4) {
1811 ok = false;
1812 break;
1813 }
1814 QPointF c(num[0] + offsetX, num[1] + offsetY);
1815 QPointF e(num[2] + offsetX, num[3] + offsetY);
1816 num += 4;
1817 count -= 4;
1818 path.quadTo(c, e);
1819 ctrlPt = c;
1820 x = e.x();
1821 y = e.y();
1822 break;
1823 }
1824 case 'Q': {
1825 if (count < 4) {
1826 ok = false;
1827 break;
1828 }
1829 QPointF c(num[0], num[1]);
1830 QPointF e(num[2], num[3]);
1831 num += 4;
1832 count -= 4;
1833 path.quadTo(c, e);
1834 ctrlPt = c;
1835 x = e.x();
1836 y = e.y();
1837 break;
1838 }
1839 case 't': {
1840 if (count < 2) {
1841 ok = false;
1842 break;
1843 }
1844 QPointF e(num[0] + offsetX, num[1] + offsetY);
1845 num += 2;
1846 count -= 2;
1847 QPointF c;
1848 if (lastMode == 'q' || lastMode == 'Q' ||
1849 lastMode == 't' || lastMode == 'T')
1850 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1851 else
1852 c = QPointF(x, y);
1853 path.quadTo(c, e);
1854 ctrlPt = c;
1855 x = e.x();
1856 y = e.y();
1857 break;
1858 }
1859 case 'T': {
1860 if (count < 2) {
1861 ok = false;
1862 break;
1863 }
1864 QPointF e(num[0], num[1]);
1865 num += 2;
1866 count -= 2;
1867 QPointF c;
1868 if (lastMode == 'q' || lastMode == 'Q' ||
1869 lastMode == 't' || lastMode == 'T')
1870 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1871 else
1872 c = QPointF(x, y);
1873 path.quadTo(c, e);
1874 ctrlPt = c;
1875 x = e.x();
1876 y = e.y();
1877 break;
1878 }
1879 case 'a': {
1880 if (count < 7) {
1881 ok = false;
1882 break;
1883 }
1884 qreal rx = (*num++);
1885 qreal ry = (*num++);
1886 qreal xAxisRotation = (*num++);
1887 qreal largeArcFlag = (*num++);
1888 qreal sweepFlag = (*num++);
1889 qreal ex = (*num++) + offsetX;
1890 qreal ey = (*num++) + offsetY;
1891 count -= 7;
1892 qreal curx = x;
1893 qreal cury = y;
1894 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1895 int(sweepFlag), ex, ey, curx, cury);
1896
1897 x = ex;
1898 y = ey;
1899 }
1900 break;
1901 case 'A': {
1902 if (count < 7) {
1903 ok = false;
1904 break;
1905 }
1906 qreal rx = (*num++);
1907 qreal ry = (*num++);
1908 qreal xAxisRotation = (*num++);
1909 qreal largeArcFlag = (*num++);
1910 qreal sweepFlag = (*num++);
1911 qreal ex = (*num++);
1912 qreal ey = (*num++);
1913 count -= 7;
1914 qreal curx = x;
1915 qreal cury = y;
1916 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1917 int(sweepFlag), ex, ey, curx, cury);
1918
1919 x = ex;
1920 y = ey;
1921 }
1922 break;
1923 default:
1924 ok = false;
1925 break;
1926 }
1927 lastMode = pathElem.toLatin1();
1928 if (limitLength && path.elementCount() > maxElementCount)
1929 ok = false;
1930 }
1931 }
1932 return ok;
1933}
1934
1935static bool parseStyle(QSvgNode *node,
1936 const QXmlStreamAttributes &attributes,
1937 QSvgHandler *);
1938
1939static bool parseStyle(QSvgNode *node,
1940 const QSvgAttributes &attributes,
1941 QSvgHandler *);
1942
1943#ifndef QT_NO_CSSPARSER
1944
1945static void parseCSStoXMLAttrs(const QList<QCss::Declaration> &declarations,
1946 QXmlStreamAttributes &attributes)
1947{
1948 for (int i = 0; i < declarations.size(); ++i) {
1949 const QCss::Declaration &decl = declarations.at(i);
1950 if (decl.d->property.isEmpty())
1951 continue;
1952 QCss::Value val = decl.d->values.first();
1953 QString valueStr;
1954 const int valCount = decl.d->values.size();
1955 if (valCount != 1) {
1956 for (int i = 0; i < valCount; ++i) {
1957 valueStr += decl.d->values[i].toString();
1958 if (i + 1 < valCount)
1959 valueStr += QLatin1Char(',');
1960 }
1961 } else {
1962 valueStr = val.toString();
1963 }
1964 if (val.type == QCss::Value::Uri) {
1965 valueStr.prepend(QLatin1String("url("));
1966 valueStr.append(QLatin1Char(')'));
1967 } else if (val.type == QCss::Value::Function) {
1968 QStringList lst = val.variant.toStringList();
1969 valueStr.append(lst.at(0));
1970 valueStr.append(QLatin1Char('('));
1971 for (int i = 1; i < lst.size(); ++i) {
1972 valueStr.append(lst.at(i));
1973 if ((i +1) < lst.size())
1974 valueStr.append(QLatin1Char(','));
1975 }
1976 valueStr.append(QLatin1Char(')'));
1977 } else if (val.type == QCss::Value::KnownIdentifier) {
1978 switch (val.variant.toInt()) {
1979 case QCss::Value_None:
1980 valueStr = QLatin1String("none");
1981 break;
1982 default:
1983 break;
1984 }
1985 }
1986
1987 attributes.append(QString(), decl.d->property, valueStr);
1988 }
1989}
1990
1991void QSvgHandler::parseCSStoXMLAttrs(const QString &css, QList<QSvgCssAttribute> *attributes)
1992{
1993 // preprocess (for unicode escapes), tokenize and remove comments
1994 m_cssParser.init(css);
1995 QString key;
1996
1997 attributes->reserve(10);
1998
1999 while (m_cssParser.hasNext()) {
2000 m_cssParser.skipSpace();
2001
2002 if (!m_cssParser.hasNext())
2003 break;
2004 m_cssParser.next();
2005
2006 QString name;
2007 if (m_cssParser.hasEscapeSequences) {
2008 key = m_cssParser.lexem();
2009 name = key;
2010 } else {
2011 const QCss::Symbol &sym = m_cssParser.symbol();
2012 name = sym.text.mid(sym.start, sym.len);
2013 }
2014
2015 m_cssParser.skipSpace();
2016 if (!m_cssParser.test(QCss::COLON))
2017 break;
2018
2019 m_cssParser.skipSpace();
2020 if (!m_cssParser.hasNext())
2021 break;
2022
2024 attribute.name = name;
2025
2026 const int firstSymbol = m_cssParser.index;
2027 int symbolCount = 0;
2028 do {
2029 m_cssParser.next();
2030 ++symbolCount;
2031 } while (m_cssParser.hasNext() && !m_cssParser.test(QCss::SEMICOLON));
2032
2033 bool canExtractValueByRef = !m_cssParser.hasEscapeSequences;
2034 if (canExtractValueByRef) {
2035 int len = m_cssParser.symbols.at(firstSymbol).len;
2036 for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
2037 len += m_cssParser.symbols.at(i).len;
2038
2039 if (m_cssParser.symbols.at(i - 1).start + m_cssParser.symbols.at(i - 1).len
2040 != m_cssParser.symbols.at(i).start) {
2041 canExtractValueByRef = false;
2042 break;
2043 }
2044 }
2045 if (canExtractValueByRef) {
2046 const QCss::Symbol &sym = m_cssParser.symbols.at(firstSymbol);
2047 attribute.value = sym.text.mid(sym.start, len);
2048 }
2049 }
2050 if (!canExtractValueByRef) {
2051 QString value;
2052 for (int i = firstSymbol; i < m_cssParser.index - 1; ++i)
2053 value += m_cssParser.symbols.at(i).lexem();
2054 attribute.value = value;
2055 }
2056
2057 attributes->append(attribute);
2058
2059 m_cssParser.skipSpace();
2060 }
2061}
2062
2063static void cssStyleLookup(QSvgNode *node,
2064 QSvgHandler *handler,
2066 QXmlStreamAttributes &attributes)
2067{
2069 cssNode.ptr = node;
2070 QList<QCss::Declaration> decls = selector->declarationsForNode(cssNode);
2071
2072 parseCSStoXMLAttrs(decls, attributes);
2073 parseStyle(node, attributes, handler);
2074}
2075
2076static void cssStyleLookup(QSvgNode *node,
2077 QSvgHandler *handler,
2079{
2080 QXmlStreamAttributes attributes;
2081 cssStyleLookup(node, handler, selector, attributes);
2082}
2083
2084#endif // QT_NO_CSSPARSER
2085
2086QtSvg::Options QSvgHandler::options() const
2087{
2088 return m_options;
2089}
2090
2092{
2093 static const bool envAssumeTrusted = qEnvironmentVariableIsSet("QT_SVG_ASSUME_TRUSTED_SOURCE");
2094 return envAssumeTrusted;
2095}
2096
2098{
2100 return lst;
2101}
2102
2103static bool parseCoreNode(QSvgNode *node,
2104 const QXmlStreamAttributes &attributes)
2105{
2106 QStringList features;
2107 QStringList extensions;
2108 QStringList languages;
2110 QStringList fonts;
2111 QString xmlClassStr;
2112
2113 for (int i = 0; i < attributes.size(); ++i) {
2114 const QXmlStreamAttribute &attribute = attributes.at(i);
2115 QStringView name = attribute.qualifiedName();
2116 if (name.isEmpty())
2117 continue;
2118 QStringView value = attribute.value();
2119 switch (name.at(0).unicode()) {
2120 case 'c':
2121 if (name == QLatin1String("class"))
2122 xmlClassStr = value.toString();
2123 break;
2124 case 'r':
2125 if (name == QLatin1String("requiredFeatures"))
2126 features = stringToList(value.toString());
2127 else if (name == QLatin1String("requiredExtensions"))
2128 extensions = stringToList(value.toString());
2129 else if (name == QLatin1String("requiredFormats"))
2130 formats = stringToList(value.toString());
2131 else if (name == QLatin1String("requiredFonts"))
2132 fonts = stringToList(value.toString());
2133 break;
2134 case 's':
2135 if (name == QLatin1String("systemLanguage"))
2136 languages = stringToList(value.toString());
2137 break;
2138 default:
2139 break;
2140 }
2141 }
2142
2143 node->setRequiredFeatures(features);
2144 node->setRequiredExtensions(extensions);
2145 node->setRequiredLanguages(languages);
2147 node->setRequiredFonts(fonts);
2148 node->setNodeId(someId(attributes));
2149 node->setXmlClass(xmlClassStr);
2150
2151 return true;
2152}
2153
2154static void parseOpacity(QSvgNode *node,
2155 const QSvgAttributes &attributes,
2156 QSvgHandler *)
2157{
2158 if (attributes.opacity.isEmpty())
2159 return;
2160
2161 const QStringView value = attributes.opacity.trimmed();
2162
2163 bool ok = false;
2164 qreal op = value.toDouble(&ok);
2165
2166 if (ok) {
2167 QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
2168 node->appendStyleProperty(opacity, attributes.id);
2169 }
2170}
2171
2173{
2174#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
2175 if (op == QLatin1String("clear")) {
2177 } else if (op == QLatin1String("src")) {
2179 } else if (op == QLatin1String("dst")) {
2181 } else if (op == QLatin1String("src-over")) {
2183 } else if (op == QLatin1String("dst-over")) {
2185 } else if (op == QLatin1String("src-in")) {
2187 } else if (op == QLatin1String("dst-in")) {
2189 } else if (op == QLatin1String("src-out")) {
2191 } else if (op == QLatin1String("dst-out")) {
2193 } else if (op == QLatin1String("src-atop")) {
2195 } else if (op == QLatin1String("dst-atop")) {
2197 } else if (op == QLatin1String("xor")) {
2199 } else if (op == QLatin1String("plus")) {
2201 } else if (op == QLatin1String("multiply")) {
2203 } else if (op == QLatin1String("screen")) {
2205 } else if (op == QLatin1String("overlay")) {
2207 } else if (op == QLatin1String("darken")) {
2209 } else if (op == QLatin1String("lighten")) {
2211 } else if (op == QLatin1String("color-dodge")) {
2213 } else if (op == QLatin1String("color-burn")) {
2215 } else if (op == QLatin1String("hard-light")) {
2217 } else if (op == QLatin1String("soft-light")) {
2219 } else if (op == QLatin1String("difference")) {
2221 } else if (op == QLatin1String("exclusion")) {
2223 } else {
2224 NOOP;
2225 }
2226
2228}
2229
2230static void parseCompOp(QSvgNode *node,
2231 const QSvgAttributes &attributes,
2232 QSvgHandler *)
2233{
2234 if (attributes.compOp.isEmpty())
2235 return;
2236 QString value = attributes.compOp.toString().trimmed();
2237
2238 if (!value.isEmpty()) {
2240 node->appendStyleProperty(compop, attributes.id);
2241 }
2242}
2243
2245{
2246 if (str == QLatin1String("inline")) {
2247 return QSvgNode::InlineMode;
2248 } else if (str == QLatin1String("block")) {
2249 return QSvgNode::BlockMode;
2250 } else if (str == QLatin1String("list-item")) {
2252 } else if (str == QLatin1String("run-in")) {
2253 return QSvgNode::RunInMode;
2254 } else if (str == QLatin1String("compact")) {
2255 return QSvgNode::CompactMode;
2256 } else if (str == QLatin1String("marker")) {
2257 return QSvgNode::MarkerMode;
2258 } else if (str == QLatin1String("table")) {
2259 return QSvgNode::TableMode;
2260 } else if (str == QLatin1String("inline-table")) {
2262 } else if (str == QLatin1String("table-row-group")) {
2264 } else if (str == QLatin1String("table-header-group")) {
2266 } else if (str == QLatin1String("table-footer-group")) {
2268 } else if (str == QLatin1String("table-row")) {
2270 } else if (str == QLatin1String("table-column-group")) {
2272 } else if (str == QLatin1String("table-column")) {
2274 } else if (str == QLatin1String("table-cell")) {
2276 } else if (str == QLatin1String("table-caption")) {
2278 } else if (str == QLatin1String("none")) {
2279 return QSvgNode::NoneMode;
2280 } else if (str == QT_INHERIT) {
2281 return QSvgNode::InheritMode;
2282 }
2283 return QSvgNode::BlockMode;
2284}
2285
2286static void parseOthers(QSvgNode *node,
2287 const QSvgAttributes &attributes,
2288 QSvgHandler *)
2289{
2290 if (attributes.display.isEmpty())
2291 return;
2292 QString displayStr = attributes.display.toString().trimmed();
2293
2294 if (!displayStr.isEmpty()) {
2295 node->setDisplayMode(displayStringToEnum(displayStr));
2296 }
2297}
2298
2300 const QSvgAttributes &attributes,
2301 QSvgHandler *handler)
2302{
2303 if (handler->options().testFlag(QtSvg::Tiny12FeaturesOnly))
2304 return;
2305
2306 if (!attributes.mask.isEmpty()) {
2307 QString maskStr = attributes.mask.toString().trimmed();
2308 if (maskStr.size() > 3 && maskStr.mid(0, 3) == QLatin1String("url"))
2309 maskStr = maskStr.mid(3, maskStr.size() - 3);
2310 QString maskId = idFromUrl(maskStr);
2311 if (maskId.startsWith(QLatin1Char('#'))) //TODO: handle urls and ids in a single place
2312 maskId.remove(0, 1);
2313
2314 node->setMaskId(maskId);
2315 }
2316
2317 if (!attributes.markerStart.isEmpty() &&
2318 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly)) {
2319 QString markerStr = attributes.markerStart.toString().trimmed();
2320 if (markerStr.size() > 3 && markerStr.mid(0, 3) == QLatin1String("url"))
2321 markerStr = markerStr.mid(3, markerStr.size() - 3);
2322 QString markerId = idFromUrl(markerStr);
2323 if (markerId.startsWith(QLatin1Char('#'))) //TODO: handle urls and ids in a single place
2324 markerId.remove(0, 1);
2325 node->setMarkerStartId(markerId);
2326 }
2327 if (!attributes.markerMid.isEmpty() &&
2328 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly)) {
2329 QString markerStr = attributes.markerMid.toString().trimmed();
2330 if (markerStr.size() > 3 && markerStr.mid(0, 3) == QLatin1String("url"))
2331 markerStr = markerStr.mid(3, markerStr.size() - 3);
2332 QString markerId = idFromUrl(markerStr);
2333 if (markerId.startsWith(QLatin1Char('#'))) //TODO: handle urls and ids in a single place
2334 markerId.remove(0, 1);
2335 node->setMarkerMidId(markerId);
2336 }
2337 if (!attributes.markerEnd.isEmpty() &&
2338 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly)) {
2339 QString markerStr = attributes.markerEnd.toString().trimmed();
2340 if (markerStr.size() > 3 && markerStr.mid(0, 3) == QLatin1String("url"))
2341 markerStr = markerStr.mid(3, markerStr.size() - 3);
2342 QString markerId = idFromUrl(markerStr);
2343 if (markerId.startsWith(QLatin1Char('#'))) //TODO: handle urls and ids in a single place
2344 markerId.remove(0, 1);
2345 node->setMarkerEndId(markerId);
2346 }
2347
2348 if (!attributes.filter.isEmpty() &&
2349 !handler->options().testFlag(QtSvg::Tiny12FeaturesOnly)) {
2350 QString filterStr = attributes.filter.toString().trimmed();
2351
2352 if (filterStr.size() > 3 && filterStr.mid(0, 3) == QLatin1String("url"))
2353 filterStr = filterStr.mid(3, filterStr.size() - 3);
2354 QString filterId = idFromUrl(filterStr);
2355 if (filterId.startsWith(QLatin1Char('#'))) //TODO: handle urls and ids in a single place
2356 filterId.remove(0, 1);
2357 node->setFilterId(filterId);
2358 }
2359
2360}
2361
2363 const QSvgAttributes &attributes,
2364 QSvgHandler *)
2365{
2366 if (attributes.imageRendering.isEmpty())
2367 return;
2368
2369 QString ir = attributes.imageRendering.toString().trimmed();
2371 if (ir == QLatin1String("auto"))
2372 p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto);
2373 else if (ir == QLatin1String("optimizeSpeed"))
2375 else if (ir == QLatin1String("optimizeQuality"))
2377 node->appendStyleProperty(p, attributes.id);
2378}
2379
2380
2381static bool parseStyle(QSvgNode *node,
2382 const QSvgAttributes &attributes,
2383 QSvgHandler *handler)
2384{
2385 parseColor(node, attributes, handler);
2386 parseBrush(node, attributes, handler);
2387 parsePen(node, attributes, handler);
2388 parseFont(node, attributes, handler);
2389 parseTransform(node, attributes, handler);
2390 parseVisibility(node, attributes, handler);
2391 parseOpacity(node, attributes, handler);
2392 parseCompOp(node, attributes, handler);
2393 parseRenderingHints(node, attributes, handler);
2394 parseOthers(node, attributes, handler);
2395 parseExtendedAttributes(node, attributes, handler);
2396
2397#if 0
2398 value = attributes.value("audio-level");
2399
2400 value = attributes.value("color-rendering");
2401
2402 value = attributes.value("display-align");
2403
2404 value = attributes.value("image-rendering");
2405
2406 value = attributes.value("line-increment");
2407
2408 value = attributes.value("pointer-events");
2409
2410 value = attributes.value("shape-rendering");
2411
2412 value = attributes.value("solid-color");
2413
2414 value = attributes.value("solid-opacity");
2415
2416 value = attributes.value("text-rendering");
2417
2418 value = attributes.value("vector-effect");
2419
2420 value = attributes.value("viewport-fill");
2421
2422 value = attributes.value("viewport-fill-opacity");
2423#endif
2424 return true;
2425}
2426
2427static bool parseStyle(QSvgNode *node,
2428 const QXmlStreamAttributes &attrs,
2429 QSvgHandler *handler)
2430{
2431 return parseStyle(node, QSvgAttributes(attrs, handler), handler);
2432}
2433
2434static bool parseAnchorNode(QSvgNode *parent,
2435 const QXmlStreamAttributes &attributes,
2436 QSvgHandler *)
2437{
2438 Q_UNUSED(parent); Q_UNUSED(attributes);
2439 return true;
2440}
2441
2442static bool parseAnimateNode(QSvgNode *parent,
2443 const QXmlStreamAttributes &attributes,
2444 QSvgHandler *)
2445{
2446 Q_UNUSED(parent); Q_UNUSED(attributes);
2447 return true;
2448}
2449
2451{
2452 int res = 0;
2453 int ms = 1000;
2454 str = str.trimmed();
2455 if (str.endsWith(QLatin1String("ms"))) {
2456 str.chop(2);
2457 ms = 1;
2458 } else if (str.endsWith(QLatin1String("s"))) {
2459 str.chop(1);
2460 }
2461 double val = ms * toDouble(str, ok);
2462 if (ok) {
2463 if (val > std::numeric_limits<int>::min() && val < std::numeric_limits<int>::max())
2464 res = static_cast<int>(val);
2465 else
2466 *ok = false;
2467 }
2468 return res;
2469}
2470
2472 const QXmlStreamAttributes &attributes,
2473 QSvgHandler *handler)
2474{
2475 QStringView fromStr = attributes.value(QLatin1String("from"));
2476 QStringView toStr = attributes.value(QLatin1String("to"));
2477 QString valuesStr = attributes.value(QLatin1String("values")).toString();
2478 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2479 QString durStr = attributes.value(QLatin1String("dur")).toString();
2480 QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
2481 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2482 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2483
2484 QList<QColor> colors;
2485 if (valuesStr.isEmpty()) {
2486 QColor startColor, endColor;
2487 resolveColor(fromStr, startColor, handler);
2488 resolveColor(toStr, endColor, handler);
2489 colors.reserve(2);
2490 colors.append(startColor);
2491 colors.append(endColor);
2492 } else {
2493 QStringList str = valuesStr.split(QLatin1Char(';'));
2494 colors.reserve(str.size());
2495 QStringList::const_iterator itr;
2496 for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
2497 QColor color;
2498 resolveColor(*itr, color, handler);
2499 colors.append(color);
2500 }
2501 }
2502
2503 bool ok = true;
2504 int begin = parseClockValue(beginStr, &ok);
2505 if (!ok)
2506 return false;
2507 int end = begin + parseClockValue(durStr, &ok);
2508 if (!ok || end <= begin)
2509 return false;
2510
2511 QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
2512 anim->setArgs((targetStr == QLatin1String("fill")), colors);
2513 anim->setFreeze(fillStr == QLatin1String("freeze"));
2514 anim->setRepeatCount(
2515 (repeatStr == QLatin1String("indefinite")) ? -1 :
2516 (repeatStr == QLatin1String("")) ? 1 : toDouble(repeatStr));
2517
2518 parent->appendStyleProperty(anim, someId(attributes));
2519 parent->document()->setAnimated(true);
2520 handler->setAnimPeriod(begin, end);
2521 return true;
2522}
2523
2525 const QXmlStreamAttributes &attributes,
2526 QSvgHandler *)
2527{
2528 Q_UNUSED(parent); Q_UNUSED(attributes);
2529 return true;
2530}
2531
2532static void parseNumberTriplet(QList<qreal> &values, const QChar *&s)
2533{
2534 QList<qreal> list = parseNumbersList(s);
2535 values << list;
2536 for (int i = 3 - list.size(); i > 0; --i)
2537 values.append(0.0);
2538}
2539
2541 const QXmlStreamAttributes &attributes,
2542 QSvgHandler *handler)
2543{
2544 QString typeStr = attributes.value(QLatin1String("type")).toString();
2545 QString values = attributes.value(QLatin1String("values")).toString();
2546 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2547 QString durStr = attributes.value(QLatin1String("dur")).toString();
2548 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2549 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2550 QString fromStr = attributes.value(QLatin1String("from")).toString();
2551 QString toStr = attributes.value(QLatin1String("to")).toString();
2552 QString byStr = attributes.value(QLatin1String("by")).toString();
2553 QString addtv = attributes.value(QLatin1String("additive")).toString();
2554
2556 if (addtv == QLatin1String("sum"))
2557 additive = QSvgAnimateTransform::Sum;
2558
2559 QList<qreal> vals;
2560 if (values.isEmpty()) {
2561 const QChar *s;
2562 if (fromStr.isEmpty()) {
2563 if (!byStr.isEmpty()) {
2564 // By-animation.
2565 additive = QSvgAnimateTransform::Sum;
2566 vals.append(0.0);
2567 vals.append(0.0);
2568 vals.append(0.0);
2569 parseNumberTriplet(vals, s = byStr.constData());
2570 } else {
2571 // To-animation not defined.
2572 return false;
2573 }
2574 } else {
2575 if (!toStr.isEmpty()) {
2576 // From-to-animation.
2577 parseNumberTriplet(vals, s = fromStr.constData());
2578 parseNumberTriplet(vals, s = toStr.constData());
2579 } else if (!byStr.isEmpty()) {
2580 // From-by-animation.
2581 parseNumberTriplet(vals, s = fromStr.constData());
2582 parseNumberTriplet(vals, s = byStr.constData());
2583 for (int i = vals.size() - 3; i < vals.size(); ++i)
2584 vals[i] += vals[i - 3];
2585 } else {
2586 return false;
2587 }
2588 }
2589 } else {
2590 const QChar *s = values.constData();
2591 while (s && *s != QLatin1Char(0)) {
2592 parseNumberTriplet(vals, s);
2593 if (*s == QLatin1Char(0))
2594 break;
2595 ++s;
2596 }
2597 }
2598 if (vals.size() % 3 != 0)
2599 return false;
2600
2601 bool ok = true;
2602 int begin = parseClockValue(beginStr, &ok);
2603 if (!ok)
2604 return false;
2605 int end = begin + parseClockValue(durStr, &ok);
2606 if (!ok || end <= begin)
2607 return false;
2608
2610 if (typeStr == QLatin1String("translate")) {
2612 } else if (typeStr == QLatin1String("scale")) {
2614 } else if (typeStr == QLatin1String("rotate")) {
2616 } else if (typeStr == QLatin1String("skewX")) {
2618 } else if (typeStr == QLatin1String("skewY")) {
2620 } else {
2621 return false;
2622 }
2623
2625 anim->setArgs(type, additive, vals);
2626 anim->setFreeze(fillStr == QLatin1String("freeze"));
2627 anim->setRepeatCount(
2628 (repeatStr == QLatin1String("indefinite"))? -1 :
2629 (repeatStr == QLatin1String(""))? 1 : toDouble(repeatStr));
2630
2631 parent->appendStyleProperty(anim, someId(attributes));
2632 parent->document()->setAnimated(true);
2633 handler->setAnimPeriod(begin, end);
2634 return true;
2635}
2636
2638 const QXmlStreamAttributes &attributes,
2639 QSvgHandler *)
2640{
2641 Q_UNUSED(parent); Q_UNUSED(attributes);
2642 return 0;
2643}
2644
2645static bool parseAudioNode(QSvgNode *parent,
2646 const QXmlStreamAttributes &attributes,
2647 QSvgHandler *)
2648{
2649 Q_UNUSED(parent); Q_UNUSED(attributes);
2650 return true;
2651}
2652
2654 const QXmlStreamAttributes &attributes,
2655 QSvgHandler *)
2656{
2657 const QStringView cx = attributes.value(QLatin1String("cx"));
2658 const QStringView cy = attributes.value(QLatin1String("cy"));
2659 const QStringView r = attributes.value(QLatin1String("r"));
2660 qreal ncx = toDouble(cx);
2661 qreal ncy = toDouble(cy);
2662 qreal nr = toDouble(r);
2663 if (nr < 0.0)
2664 return nullptr;
2665
2666 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
2667 QSvgNode *circle = new QSvgCircle(parent, rect);
2668 return circle;
2669}
2670
2672 const QXmlStreamAttributes &attributes,
2673 QSvgHandler *)
2674{
2675 Q_UNUSED(attributes);
2676 QSvgDefs *defs = new QSvgDefs(parent);
2677 return defs;
2678}
2679
2680static bool parseDiscardNode(QSvgNode *parent,
2681 const QXmlStreamAttributes &attributes,
2682 QSvgHandler *)
2683{
2684 Q_UNUSED(parent); Q_UNUSED(attributes);
2685 return true;
2686}
2687
2689 const QXmlStreamAttributes &attributes,
2690 QSvgHandler *)
2691{
2692 const QStringView cx = attributes.value(QLatin1String("cx"));
2693 const QStringView cy = attributes.value(QLatin1String("cy"));
2694 const QStringView rx = attributes.value(QLatin1String("rx"));
2695 const QStringView ry = attributes.value(QLatin1String("ry"));
2696 qreal ncx = toDouble(cx);
2697 qreal ncy = toDouble(cy);
2698 qreal nrx = toDouble(rx);
2699 qreal nry = toDouble(ry);
2700
2701 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
2702 QSvgNode *ellipse = new QSvgEllipse(parent, rect);
2703 return ellipse;
2704}
2705
2707 const QXmlStreamAttributes &attributes,
2708 QSvgHandler *)
2709{
2710 const QStringView hax = attributes.value(QLatin1String("horiz-adv-x"));
2711 QString myId = someId(attributes);
2712
2713 qreal horizAdvX = toDouble(hax);
2714
2715 while (parent && parent->type() != QSvgNode::Doc) {
2716 parent = parent->parent();
2717 }
2718
2719 if (parent && !myId.isEmpty()) {
2720 QSvgTinyDocument *doc = static_cast<QSvgTinyDocument*>(parent);
2721 QSvgFont *font = doc->svgFont(myId);
2722 if (!font) {
2723 font = new QSvgFont(horizAdvX);
2724 font->setFamilyName(myId);
2725 doc->addSvgFont(font);
2726 }
2727 return new QSvgFontStyle(font, doc);
2728 }
2729 return nullptr;
2730}
2731
2733 const QXmlStreamAttributes &attributes,
2734 QSvgHandler *)
2735{
2736 if (parent->type() != QSvgStyleProperty::FONT) {
2737 return false;
2738 }
2739
2740 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2741 QSvgFont *font = style->svgFont();
2742 QString name = attributes.value(QLatin1String("font-family")).toString();
2743 const QStringView unitsPerEmStr = attributes.value(QLatin1String("units-per-em"));
2744
2745 qreal unitsPerEm = toDouble(unitsPerEmStr);
2746 if (!unitsPerEm)
2747 unitsPerEm = QSvgFont::DEFAULT_UNITS_PER_EM;
2748
2749 if (!name.isEmpty())
2750 font->setFamilyName(name);
2751 font->setUnitsPerEm(unitsPerEm);
2752
2753 if (!font->familyName().isEmpty())
2754 if (!style->doc()->svgFont(font->familyName()))
2755 style->doc()->addSvgFont(font);
2756
2757 return true;
2758}
2759
2761 const QXmlStreamAttributes &attributes,
2762 QSvgHandler *)
2763{
2764 if (parent->type() != QSvgStyleProperty::FONT) {
2765 return false;
2766 }
2767
2768 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2769 QSvgFont *font = style->svgFont();
2770 QString name = attributes.value(QLatin1String("name")).toString();
2771
2772 if (!name.isEmpty())
2773 font->setFamilyName(name);
2774
2775 if (!font->familyName().isEmpty())
2776 if (!style->doc()->svgFont(font->familyName()))
2777 style->doc()->addSvgFont(font);
2778
2779 return true;
2780}
2781
2783 const QXmlStreamAttributes &attributes,
2784 QSvgHandler *)
2785{
2786 Q_UNUSED(parent); Q_UNUSED(attributes);
2787 return true;
2788}
2789
2791 const QXmlStreamAttributes &attributes,
2792 QSvgHandler *)
2793{
2794 Q_UNUSED(parent); Q_UNUSED(attributes);
2795 return true;
2796}
2797
2799 const QXmlStreamAttributes &attributes,
2800 QSvgHandler *)
2801{
2802 Q_UNUSED(parent); Q_UNUSED(attributes);
2803 return true;
2804}
2805
2807 const QXmlStreamAttributes &attributes,
2808 QSvgHandler *)
2809{
2810 Q_UNUSED(attributes);
2811 QSvgG *node = new QSvgG(parent);
2812 return node;
2813}
2814
2816 const QXmlStreamAttributes &attributes,
2817 QSvgHandler *)
2818{
2819 if (parent->type() != QSvgStyleProperty::FONT) {
2820 return false;
2821 }
2822
2823 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2824 QSvgFont *font = style->svgFont();
2825 createSvgGlyph(font, attributes);
2826 return true;
2827}
2828
2829static bool parseHandlerNode(QSvgNode *parent,
2830 const QXmlStreamAttributes &attributes,
2831 QSvgHandler *)
2832{
2833 Q_UNUSED(parent); Q_UNUSED(attributes);
2834 return true;
2835}
2836
2837static bool parseHkernNode(QSvgNode *parent,
2838 const QXmlStreamAttributes &attributes,
2839 QSvgHandler *)
2840{
2841 Q_UNUSED(parent); Q_UNUSED(attributes);
2842 return true;
2843}
2844
2846 const QXmlStreamAttributes &attributes,
2847 QSvgHandler *handler)
2848{
2849 const QStringView x = attributes.value(QLatin1String("x"));
2850 const QStringView y = attributes.value(QLatin1String("y"));
2851 const QStringView width = attributes.value(QLatin1String("width"));
2852 const QStringView height = attributes.value(QLatin1String("height"));
2853 QString filename = attributes.value(QLatin1String("xlink:href")).toString();
2854 qreal nx = toDouble(x);
2855 qreal ny = toDouble(y);
2857 qreal nwidth = parseLength(width.toString(), &type, handler);
2858 nwidth = convertToPixels(nwidth, true, type);
2859
2860 qreal nheight = parseLength(height.toString(), &type, handler);
2861 nheight = convertToPixels(nheight, false, type);
2862
2863 filename = filename.trimmed();
2864 if (filename.isEmpty()) {
2865 qCWarning(lcSvgHandler) << "QSvgHandler: Image filename is empty";
2866 return 0;
2867 }
2868 if (nwidth <= 0 || nheight <= 0) {
2869 qCWarning(lcSvgHandler) << "QSvgHandler: Width or height for" << filename << "image was not greater than 0";
2870 return 0;
2871 }
2872
2873 QImage image;
2874 enum {
2875 NotLoaded,
2876 LoadedFromData,
2877 LoadedFromFile
2878 } filenameType = NotLoaded;
2879
2880 if (filename.startsWith(QLatin1String("data"))) {
2881 int idx = filename.lastIndexOf(QLatin1String("base64,"));
2882 if (idx != -1) {
2883 idx += 7;
2884 const QString dataStr = filename.mid(idx);
2885 QByteArray data = QByteArray::fromBase64(dataStr.toLatin1());
2887 filenameType = LoadedFromData;
2888 }
2889 }
2890
2891 if (image.isNull()) {
2892 const auto *file = qobject_cast<QFile *>(handler->device());
2893 if (file) {
2894 QUrl url(filename);
2895 if (url.isRelative()) {
2897 filename = info.absoluteDir().absoluteFilePath(filename);
2898 }
2899 }
2900
2901 if (handler->trustedSourceMode() || !QImageReader::imageFormat(filename).startsWith("svg")) {
2902 image = QImage(filename);
2903 filenameType = LoadedFromFile;
2904 }
2905 }
2906
2907 if (image.isNull()) {
2908 qCWarning(lcSvgHandler) << "Could not create image from" << filename;
2909 return 0;
2910 }
2911
2912 if (image.format() == QImage::Format_ARGB32)
2914
2915 QSvgNode *img = new QSvgImage(parent,
2916 image,
2917 filenameType == LoadedFromFile ? filename : QString{},
2918 QRectF(nx,
2919 ny,
2920 nwidth,
2921 nheight));
2922 return img;
2923}
2924
2926 const QXmlStreamAttributes &attributes,
2927 QSvgHandler *)
2928{
2929 const QStringView x1 = attributes.value(QLatin1String("x1"));
2930 const QStringView y1 = attributes.value(QLatin1String("y1"));
2931 const QStringView x2 = attributes.value(QLatin1String("x2"));
2932 const QStringView y2 = attributes.value(QLatin1String("y2"));
2933 qreal nx1 = toDouble(x1);
2934 qreal ny1 = toDouble(y1);
2935 qreal nx2 = toDouble(x2);
2936 qreal ny2 = toDouble(y2);
2937
2938 QLineF lineBounds(nx1, ny1, nx2, ny2);
2939 QSvgNode *line = new QSvgLine(parent, lineBounds);
2940 return line;
2941}
2942
2943
2944static void parseBaseGradient(QSvgNode *node,
2945 const QXmlStreamAttributes &attributes,
2946 QSvgGradientStyle *gradProp,
2947 QSvgHandler *handler)
2948{
2949 QString link = attributes.value(QLatin1String("xlink:href")).toString();
2950 QStringView trans = attributes.value(QLatin1String("gradientTransform"));
2951 QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
2952 QString units = attributes.value(QLatin1String("gradientUnits")).toString();
2953 QStringView colorStr = attributes.value(QLatin1String("color"));
2954 QStringView colorOpacityStr = attributes.value(QLatin1String("color-opacity"));
2955
2956 QColor color;
2957 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2958 handler->popColor();
2959 handler->pushColor(color);
2960 }
2961
2963 QGradient *grad = gradProp->qgradient();
2964 if (node && !link.isEmpty()) {
2965 QSvgStyleProperty *prop = node->styleProperty(link);
2966 //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
2967 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
2968 QSvgGradientStyle *inherited =
2969 static_cast<QSvgGradientStyle*>(prop);
2970 if (!inherited->stopLink().isEmpty()) {
2971 gradProp->setStopLink(inherited->stopLink(), handler->document());
2972 } else {
2973 grad->setStops(inherited->qgradient()->stops());
2974 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2975 }
2976
2977 matrix = inherited->qtransform();
2978 } else {
2979 gradProp->setStopLink(link, handler->document());
2980 }
2981 }
2982
2983 if (!trans.isEmpty()) {
2985 gradProp->setTransform(matrix);
2986 } else if (!matrix.isIdentity()) {
2987 gradProp->setTransform(matrix);
2988 }
2989
2990 if (!spread.isEmpty()) {
2991 if (spread == QLatin1String("pad")) {
2993 } else if (spread == QLatin1String("reflect")) {
2995 } else if (spread == QLatin1String("repeat")) {
2997 }
2998 }
2999
3000 if (units.isEmpty() || units == QLatin1String("objectBoundingBox")) {
3002 }
3003}
3004
3006 const QXmlStreamAttributes &attributes,
3007 QSvgHandler *handler)
3008{
3009 const QStringView x1 = attributes.value(QLatin1String("x1"));
3010 const QStringView y1 = attributes.value(QLatin1String("y1"));
3011 const QStringView x2 = attributes.value(QLatin1String("x2"));
3012 const QStringView y2 = attributes.value(QLatin1String("y2"));
3013
3014 qreal nx1 = 0.0;
3015 qreal ny1 = 0.0;
3016 qreal nx2 = 1.0;
3017 qreal ny2 = 0.0;
3018
3019 if (!x1.isEmpty())
3020 nx1 = convertToNumber(x1, handler);
3021 if (!y1.isEmpty())
3022 ny1 = convertToNumber(y1, handler);
3023 if (!x2.isEmpty())
3024 nx2 = convertToNumber(x2, handler);
3025 if (!y2.isEmpty())
3026 ny2 = convertToNumber(y2, handler);
3027
3028 QSvgNode *itr = node;
3029 while (itr && itr->type() != QSvgNode::Doc) {
3030 itr = itr->parent();
3031 }
3032
3033 QLinearGradient *grad = new QLinearGradient(nx1, ny1, nx2, ny2);
3035 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
3036 parseBaseGradient(node, attributes, prop, handler);
3037
3038 return prop;
3039}
3040
3041static bool parseMetadataNode(QSvgNode *parent,
3042 const QXmlStreamAttributes &attributes,
3043 QSvgHandler *)
3044{
3045 Q_UNUSED(parent); Q_UNUSED(attributes);
3046 return true;
3047}
3048
3050 const QXmlStreamAttributes &attributes,
3051 QSvgHandler *)
3052{
3053 if (parent->type() != QSvgStyleProperty::FONT) {
3054 return false;
3055 }
3056
3057 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
3058 QSvgFont *font = style->svgFont();
3059 createSvgGlyph(font, attributes);
3060 return true;
3061}
3062
3063static bool parseMpathNode(QSvgNode *parent,
3064 const QXmlStreamAttributes &attributes,
3065 QSvgHandler *)
3066{
3067 Q_UNUSED(parent); Q_UNUSED(attributes);
3068 return true;
3069}
3070
3071static bool parseMaskNode(QSvgNode *parent,
3072 const QXmlStreamAttributes &attributes,
3073 QSvgHandler *)
3074{
3075 Q_UNUSED(parent); Q_UNUSED(attributes);
3076 return true;
3077}
3078
3080 const QXmlStreamAttributes &,
3081 QSvgHandler *)
3082{
3083 return true;
3084}
3085
3087 const QXmlStreamAttributes &attributes,
3088 QSvgHandler *handler)
3089{
3090 const QStringView x = attributes.value(QLatin1String("x"));
3091 const QStringView y = attributes.value(QLatin1String("y"));
3092 const QStringView width = attributes.value(QLatin1String("width"));
3093 const QStringView height = attributes.value(QLatin1String("height"));
3094 const QStringView mU = attributes.value(QLatin1String("maskUnits"));
3095 const QStringView mCU = attributes.value(QLatin1String("maskContentUnits"));
3096
3097 QtSvg::UnitTypes nmU = mU.contains(QLatin1String("userSpaceOnUse")) ?
3099
3100 QtSvg::UnitTypes nmCU = mCU.contains(QLatin1String("objectBoundingBox")) ?
3102
3103 bool ok;
3105
3106 QtSvg::UnitTypes nmUx = nmU;
3107 QtSvg::UnitTypes nmUy = nmU;
3108 QtSvg::UnitTypes nmUw = nmU;
3109 QtSvg::UnitTypes nmUh = nmU;
3110 qreal nx = parseLength(x.toString(), &type, handler, &ok);
3111 nx = convertToPixels(nx, true, type);
3112 if (x.isEmpty() || !ok) {
3113 nx = -0.1;
3116 nx = nx / 100. * parent->document()->viewBox().width();
3117 } else if (type == QSvgHandler::LT_PERCENT) {
3118 nx = nx / 100.;
3119 }
3120
3121 qreal ny = parseLength(y.toString(), &type, handler, &ok);
3122 ny = convertToPixels(ny, true, type);
3123 if (y.isEmpty() || !ok) {
3124 ny = -0.1;
3127 ny = ny / 100. * parent->document()->viewBox().height();
3128 } else if (type == QSvgHandler::LT_PERCENT) {
3129 ny = ny / 100.;
3130 }
3131
3132 qreal nwidth = parseLength(width.toString(), &type, handler, &ok);
3133 nwidth = convertToPixels(nwidth, true, type);
3134 if (width.isEmpty() || !ok) {
3135 nwidth = 1.2;
3138 nwidth = nwidth / 100. * parent->document()->viewBox().width();
3139 } else if (type == QSvgHandler::LT_PERCENT) {
3140 nwidth = nwidth / 100.;
3141 }
3142
3143 qreal nheight = parseLength(height.toString(), &type, handler, &ok);
3144 nheight = convertToPixels(nheight, true, type);
3145 if (height.isEmpty() || !ok) {
3146 nheight = 1.2;
3149 nheight = nheight / 100. * parent->document()->viewBox().height();
3150 } else if (type == QSvgHandler::LT_PERCENT) {
3151 nheight = nheight / 100.;
3152 }
3153
3154 QRectF bounds(nx, ny, nwidth, nheight);
3155 if (bounds.isEmpty())
3156 return nullptr;
3157
3158 QSvgNode *mask = new QSvgMask(parent, QSvgRectF(bounds, nmUx, nmUy, nmUw, nmUh), nmCU);
3159
3160 return mask;
3161}
3162
3163static void parseFilterBounds(QSvgNode *, const QXmlStreamAttributes &attributes,
3164 QSvgHandler *handler, QSvgRectF *rect)
3165{
3166 const QStringView xStr = attributes.value(QLatin1String("x"));
3167 const QStringView yStr = attributes.value(QLatin1String("y"));
3168 const QStringView widthStr = attributes.value(QLatin1String("width"));
3169 const QStringView heightStr = attributes.value(QLatin1String("height"));
3170
3171 qreal x = 0;
3172 if (!xStr.isEmpty()) {
3174 x = parseLength(xStr.toString(), &type, handler);
3175 if (type != QSvgHandler::LT_PT)
3176 x = convertToPixels(x, true, type);
3178 x /= 100.;
3180 }
3181 rect->setX(x);
3182 } else {
3183 rect->setX(-0.1);
3185 }
3186 qreal y = 0;
3187 if (!yStr.isEmpty()) {
3189 y = parseLength(yStr.toString(), &type, handler);
3190 if (type != QSvgHandler::LT_PT)
3191 y = convertToPixels(y, false, type);
3193 y /= 100.;
3195 }
3196 rect->setY(y);
3197 } else {
3198 rect->setY(-0.1);
3200 }
3201 qreal width = 0;
3202 if (!widthStr.isEmpty()) {
3204 width = parseLength(widthStr.toString(), &type, handler);
3205 if (type != QSvgHandler::LT_PT)
3206 width = convertToPixels(width, true, type);
3208 width /= 100.;
3210 }
3211 rect->setWidth(width);
3212 } else {
3213 rect->setWidth(1.2);
3215 }
3216 qreal height = 0;
3217 if (!heightStr.isEmpty()) {
3219 height = parseLength(heightStr.toString(), &type, handler);
3220 if (type != QSvgHandler::LT_PT)
3221 height = convertToPixels(height, false, type);
3223 height /= 100.;
3225 }
3226 rect->setHeight(height);
3227 } else {
3228 rect->setHeight(1.2);
3230 }
3231}
3232
3234 const QXmlStreamAttributes &attributes,
3235 QSvgHandler *handler)
3236{
3237 QString fU = attributes.value(QLatin1String("filterUnits")).toString();
3238 QString pU = attributes.value(QLatin1String("primitiveUnits")).toString();
3239
3240 QtSvg::UnitTypes filterUnits = fU.contains(QLatin1String("userSpaceOnUse")) ?
3242
3243 QtSvg::UnitTypes primitiveUnits = pU.contains(QLatin1String("objectBoundingBox")) ?
3245
3247 parseFilterBounds(parent, attributes, handler, &rect);
3248
3249 QSvgNode *filter = new QSvgFilterContainer(parent, rect, filterUnits, primitiveUnits);
3250 return filter;
3251}
3252
3253static void parseFilterAttributes(QSvgNode *parent, const QXmlStreamAttributes &attributes,
3254 QSvgHandler *handler, QString *inString, QString *outString,
3255 QSvgRectF *rect)
3256{
3257 *inString = attributes.value(QLatin1String("in")).toString();
3258 *outString = attributes.value(QLatin1String("result")).toString();
3259
3260 parseFilterBounds(parent, attributes, handler, rect);
3261}
3262
3264 const QXmlStreamAttributes &attributes,
3265 QSvgHandler *handler)
3266{
3267 const QString typeString = attributes.value(QLatin1String("type")).toString();
3268 QString valuesString = attributes.value(QLatin1String("values")).toString();
3269
3270 QString inputString;
3271 QString outputString;
3273
3276 values.fill(0);
3277
3278 parseFilterAttributes(parent, attributes, handler,
3279 &inputString, &outputString, &rect);
3280
3281 if (typeString.startsWith(QLatin1String("saturate")))
3283 else if (typeString.startsWith(QLatin1String("hueRotate")))
3285 else if (typeString.startsWith(QLatin1String("luminanceToAlpha")))
3287 else
3289
3290 if (!valuesString.isEmpty()) {
3291 static QRegularExpression delimiterRE(QLatin1String("[,\\s]"));
3292 const QStringList valueStringList = valuesString.split(delimiterRE, Qt::SkipEmptyParts);
3293
3294 for (int i = 0, j = 0; i < qMin(20, valueStringList.size()); i++) {
3295 bool ok;
3296 qreal v = toDouble(valueStringList.at(i), &ok);
3297 if (ok) {
3298 values.data()[j] = v;
3299 j++;
3300 }
3301 }
3302 } else {
3303 values.setToIdentity();
3304 }
3305
3306 QSvgNode *filter = new QSvgFeColorMatrix(parent, inputString, outputString, rect,
3307 type, values);
3308 return filter;
3309}
3310
3312 const QXmlStreamAttributes &attributes,
3313 QSvgHandler *handler)
3314{
3315 const QString edgeModeString = attributes.value(QLatin1String("edgeMode")).toString();
3316 QString stdDeviationString = attributes.value(QLatin1String("stdDeviation")).toString();
3317
3318 QString inputString;
3319 QString outputString;
3321
3323
3324 parseFilterAttributes(parent, attributes, handler,
3325 &inputString, &outputString, &rect);
3326 qreal stdDeviationX = 0;
3327 qreal stdDeviationY = 0;
3328 if (stdDeviationString.contains(QStringLiteral(" "))){
3329 stdDeviationX = qMax(0., toDouble(stdDeviationString.split(QStringLiteral(" ")).first()));
3330 stdDeviationY = qMax(0., toDouble(stdDeviationString.split(QStringLiteral(" ")).last()));
3331 } else {
3332 stdDeviationY = stdDeviationX = qMax(0., toDouble(stdDeviationString));
3333 }
3334
3335 if (edgeModeString.startsWith(QLatin1String("wrap")))
3337 else if (edgeModeString.startsWith(QLatin1String("none")))
3339
3340 QSvgNode *filter = new QSvgFeGaussianBlur(parent, inputString, outputString, rect,
3341 stdDeviationX, stdDeviationY, edgemode);
3342 return filter;
3343}
3344
3346 const QXmlStreamAttributes &attributes,
3347 QSvgHandler *handler)
3348{
3349 QStringView dxString = attributes.value(QLatin1String("dx"));
3350 QStringView dyString = attributes.value(QLatin1String("dy"));
3351
3352 QString inputString;
3353 QString outputString;
3355
3356 parseFilterAttributes(parent, attributes, handler,
3357 &inputString, &outputString, &rect);
3358
3359 qreal dx = 0;
3360 if (!dxString.isEmpty()) {
3362 dx = parseLength(dxString.toString(), &type, handler);
3363 if (type != QSvgHandler::LT_PT)
3364 dx = convertToPixels(dx, true, type);
3365 }
3366
3367 qreal dy = 0;
3368 if (!dyString.isEmpty()) {
3370 dy = parseLength(dyString.toString(), &type, handler);
3371 if (type != QSvgHandler::LT_PT)
3372 dy = convertToPixels(dy, true, type);
3373 }
3374
3375 QSvgNode *filter = new QSvgFeOffset(parent, inputString, outputString, rect,
3376 dx, dy);
3377 return filter;
3378}
3379
3381 const QXmlStreamAttributes &attributes,
3382 QSvgHandler *handler)
3383{
3384 QString in2String = attributes.value(QLatin1String("in2")).toString();
3385 QString operatorString = attributes.value(QLatin1String("operator")).toString();
3386 QString k1String = attributes.value(QLatin1String("k1")).toString();
3387 QString k2String = attributes.value(QLatin1String("k2")).toString();
3388 QString k3String = attributes.value(QLatin1String("k3")).toString();
3389 QString k4String = attributes.value(QLatin1String("k4")).toString();
3390
3391 QString inputString;
3392 QString outputString;
3394
3395 parseFilterAttributes(parent, attributes, handler,
3396 &inputString, &outputString, &rect);
3397
3399 if (operatorString.startsWith(QStringLiteral("in")))
3401 else if (operatorString.startsWith(QStringLiteral("out")))
3403 else if (operatorString.startsWith(QStringLiteral("atop")))
3405 else if (operatorString.startsWith(QStringLiteral("xor")))
3407 else if (operatorString.startsWith(QStringLiteral("lighter")))
3409 else if (operatorString.startsWith(QStringLiteral("arithmetic")))
3411
3412 QVector4D k(0, 0, 0, 0);
3413
3415 bool ok;
3416 qreal v = toDouble(k1String, &ok);
3417 if (ok)
3418 k.setX(v);
3419 v = toDouble(k2String, &ok);
3420 if (ok)
3421 k.setY(v);
3422 v = toDouble(k3String, &ok);
3423 if (ok)
3424 k.setZ(v);
3425 v = toDouble(k4String, &ok);
3426 if (ok)
3427 k.setW(v);
3428 }
3429
3430 QSvgNode *filter = new QSvgFeComposite(parent, inputString, outputString, rect,
3431 in2String, op, k);
3432 return filter;
3433}
3434
3435
3437 const QXmlStreamAttributes &attributes,
3438 QSvgHandler *handler)
3439{
3440 QString inputString;
3441 QString outputString;
3443
3444 parseFilterAttributes(parent, attributes, handler,
3445 &inputString, &outputString, &rect);
3446
3447 QSvgNode *filter = new QSvgFeMerge(parent, inputString, outputString, rect);
3448 return filter;
3449}
3450
3452 const QXmlStreamAttributes &attributes,
3453 QSvgHandler *handler)
3454{
3455 QStringView colorStr = attributes.value(QLatin1String("flood-color"));
3456 const QStringView opacityStr = attributes.value(QLatin1String("flood-opacity"));
3457
3458 QColor color;
3459 if (!constructColor(colorStr, opacityStr, color, handler))
3461
3462 QString inputString;
3463 QString outputString;
3465
3466 parseFilterAttributes(parent, attributes, handler,
3467 &inputString, &outputString, &rect);
3468
3469 QSvgNode *filter = new QSvgFeFlood(parent, inputString, outputString, rect, color);
3470 return filter;
3471}
3472
3474 const QXmlStreamAttributes &attributes,
3475 QSvgHandler *handler)
3476{
3477 QString inputString;
3478 QString outputString;
3480
3481 parseFilterAttributes(parent, attributes, handler,
3482 &inputString, &outputString, &rect);
3483
3484 QSvgNode *filter = new QSvgFeMergeNode(parent, inputString, outputString, rect);
3485 return filter;
3486}
3487
3489 const QXmlStreamAttributes &attributes,
3490 QSvgHandler *handler)
3491{
3492 QString inputString;
3493 QString outputString;
3495
3496 parseFilterAttributes(parent, attributes, handler,
3497 &inputString, &outputString, &rect);
3498
3499 QSvgNode *filter = new QSvgFeUnsupported(parent, inputString, outputString, rect);
3500 return filter;
3501}
3502
3503static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler,
3504 QRectF *rect, QRectF *viewBox, QPointF *refPoint,
3505 QSvgSymbolLike::PreserveAspectRatios *aspect,
3506 QSvgSymbolLike::Overflow *overflow,
3507 bool marker = false)
3508{
3509 const QStringView xStr = attributes.value(QLatin1String("x"));
3510 const QStringView yStr = attributes.value(QLatin1String("y"));
3511 const QStringView refXStr = attributes.value(QLatin1String("refX"));
3512 const QStringView refYStr = attributes.value(QLatin1String("refY"));
3513 const QStringView widthStr = attributes.value(QLatin1String(marker ? "markerWidth":"width"));
3514 const QStringView heightStr = attributes.value(QLatin1String(marker ? "markerHeight":"height"));
3515 const QString pAspectRStr = attributes.value(QLatin1String("preserveAspectRatio")).toString();
3516 const QStringView overflowStr = attributes.value(QLatin1String("overflow"));
3517
3518 QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
3519
3520
3521 qreal x = 0;
3522 if (!xStr.isEmpty()) {
3524 x = parseLength(xStr.toString(), &type, handler);
3525 if (type != QSvgHandler::LT_PT)
3526 x = convertToPixels(x, true, type);
3527 }
3528 qreal y = 0;
3529 if (!yStr.isEmpty()) {
3531 y = parseLength(yStr.toString(), &type, handler);
3532 if (type != QSvgHandler::LT_PT)
3533 y = convertToPixels(y, false, type);
3534 }
3535 qreal width = 0;
3536 if (!widthStr.isEmpty()) {
3538 width = parseLength(widthStr.toString(), &type, handler);
3539 if (type != QSvgHandler::LT_PT)
3540 width = convertToPixels(width, true, type);
3541 }
3542 qreal height = 0;
3543 if (!heightStr.isEmpty()) {
3545 height = parseLength(heightStr.toString(), &type, handler);
3546 if (type != QSvgHandler::LT_PT)
3547 height = convertToPixels(height, false, type);
3548 }
3549
3550 *rect = QRectF(x, y, width, height);
3551
3552 x = 0;
3553 if (!refXStr.isEmpty()) {
3555 x = parseLength(refXStr.toString(), &type, handler);
3556 if (type != QSvgHandler::LT_PT)
3557 x = convertToPixels(x, true, type);
3558 }
3559 y = 0;
3560 if (!refYStr.isEmpty()) {
3562 y = parseLength(refYStr.toString(), &type, handler);
3563 if (type != QSvgHandler::LT_PT)
3564 y = convertToPixels(y, false, type);
3565 }
3566 *refPoint = QPointF(x,y);
3567
3568 QStringList viewBoxValues;
3569 if (!viewBoxStr.isEmpty()) {
3570 viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
3571 viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
3572 viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
3573 viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
3574 viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts);
3575 }
3576 if (viewBoxValues.size() == 4) {
3577 QString xStr = viewBoxValues.at(0).trimmed();
3578 QString yStr = viewBoxValues.at(1).trimmed();
3579 QString widthStr = viewBoxValues.at(2).trimmed();
3580 QString heightStr = viewBoxValues.at(3).trimmed();
3581
3583 qreal x = parseLength(xStr, &lt, handler);
3584 qreal y = parseLength(yStr, &lt, handler);
3585 qreal w = parseLength(widthStr, &lt, handler);
3586 qreal h = parseLength(heightStr, &lt, handler);
3587
3588 *viewBox = QRectF(x, y, w, h);
3589
3590 } else if (width > 0 && height > 0) {
3591 *viewBox = QRectF(0, 0, width, height);
3592 } else {
3593 *viewBox = handler->document()->viewBox();
3594 }
3595
3596 if (viewBox->isNull())
3597 return false;
3598
3599 QStringList pAspectRStrs = pAspectRStr.split(QLatin1String(" "));
3603
3604 for (auto &pAStr : std::as_const(pAspectRStrs)) {
3605 if (pAStr.startsWith(QLatin1String("none"))) {
3608 }else {
3609 if (pAStr.startsWith(QLatin1String("xMin")))
3611 else if (pAStr.startsWith(QLatin1String("xMax")))
3613 if (pAStr.endsWith(QLatin1String("YMin")))
3615 else if (pAStr.endsWith(QLatin1String("YMax")))
3617 }
3618
3619 if (pAStr.endsWith(QLatin1String("slice")))
3621 }
3622 *aspect = aspectX | aspectY | aspectMS;
3623
3624 // overflow is not limited to the symbol element but it is often found with the symbol element.
3625 // the symbol element makes little sense without the overflow attribute so it is added here.
3626 // if we decide to remove this from QSvgSymbol, the default value should be set to visible.
3627
3628 // The default value is visible but chrome uses default value hidden.
3630
3631 if (overflowStr.endsWith(QLatin1String("auto")))
3633 else if (overflowStr.endsWith(QLatin1String("visible")))
3635 else if (overflowStr.endsWith(QLatin1String("hidden")))
3637 else if (overflowStr.endsWith(QLatin1String("scroll")))
3639
3640 return true;
3641}
3642
3644 const QXmlStreamAttributes &attributes,
3645 QSvgHandler *handler)
3646{
3647 QRectF rect, viewBox;
3648 QPointF refP;
3649 QSvgSymbolLike::PreserveAspectRatios aspect;
3650 QSvgSymbolLike::Overflow overflow;
3651
3652 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow))
3653 return nullptr;
3654
3655 refP = QPointF(0, 0); //refX, refY is ignored in Symbol in Firefox and Chrome.
3656 QSvgNode *symbol = new QSvgSymbol(parent, rect, viewBox, refP, aspect, overflow);
3657 return symbol;
3658}
3659
3661 const QXmlStreamAttributes &attributes,
3662 QSvgHandler *handler)
3663{
3664 QRectF rect, viewBox;
3665 QPointF refP;
3666 QSvgSymbolLike::PreserveAspectRatios aspect;
3667 QSvgSymbolLike::Overflow overflow;
3668
3669 const QString orientStr = attributes.value(QLatin1String("orient")).toString();
3670 const QString markerUnitsStr = attributes.value(QLatin1String("markerUnits")).toString();
3671
3672 qreal orientationAngle = 0;
3673 QSvgMarker::Orientation orientation;
3674 if (orientStr.startsWith(QLatin1String("auto-start-reverse")))
3676 else if (orientStr.startsWith(QLatin1String("auto")))
3677 orientation = QSvgMarker::Orientation::Auto;
3678 else {
3679 orientation = QSvgMarker::Orientation::Value;
3680 bool ok;
3681 qreal a;
3682 if (orientStr.endsWith(QStringLiteral("turn")))
3683 a = 360. * toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
3684 else if (orientStr.endsWith(QStringLiteral("grad")))
3685 a = toDouble(orientStr.mid(0, orientStr.length()-4), &ok);
3686 else if (orientStr.endsWith(QStringLiteral("rad")))
3687 a = 180. / M_PI * toDouble(orientStr.mid(0, orientStr.length()-3), &ok);
3688 else
3689 a = toDouble(orientStr, &ok);
3690 if (ok)
3691 orientationAngle = a;
3692 }
3693
3695 if (markerUnitsStr.startsWith(QLatin1String("userSpaceOnUse")))
3697
3698 if (!parseSymbolLikeAttributes(attributes, handler, &rect, &viewBox, &refP, &aspect, &overflow, true))
3699 return nullptr;
3700
3701 QSvgNode *marker = new QSvgMarker(parent, rect, viewBox, refP, aspect, overflow,
3702 orientation, orientationAngle, markerUnits);
3703 return marker;
3704}
3705
3707 const QXmlStreamAttributes &attributes,
3708 QSvgHandler *handler)
3709{
3710 QStringView data = attributes.value(QLatin1String("d"));
3711
3712 QPainterPath qpath;
3714 if (!parsePathDataFast(data, qpath, !handler->trustedSourceMode()))
3715 qCWarning(lcSvgHandler, "Invalid path data; path truncated.");
3716
3717 QSvgNode *path = new QSvgPath(parent, qpath);
3718 return path;
3719}
3720
3722 const QXmlStreamAttributes &attributes,
3723 QSvgHandler *)
3724{
3725 QString pointsStr = attributes.value(QLatin1String("points")).toString();
3726
3727 //same QPolygon parsing is in createPolylineNode
3728 const QChar *s = pointsStr.constData();
3729 QList<qreal> points = parseNumbersList(s);
3730 QPolygonF poly(points.size()/2);
3731 for (int i = 0; i < poly.size(); ++i)
3732 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
3733 QSvgNode *polygon = new QSvgPolygon(parent, poly);
3734 return polygon;
3735}
3736
3738 const QXmlStreamAttributes &attributes,
3739 QSvgHandler *)
3740{
3741 QString pointsStr = attributes.value(QLatin1String("points")).toString();
3742
3743 //same QPolygon parsing is in createPolygonNode
3744 const QChar *s = pointsStr.constData();
3745 QList<qreal> points = parseNumbersList(s);
3746 QPolygonF poly(points.size()/2);
3747 for (int i = 0; i < poly.size(); ++i)
3748 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
3749
3750 QSvgNode *line = new QSvgPolyline(parent, poly);
3751 return line;
3752}
3753
3754static bool parsePrefetchNode(QSvgNode *parent,
3755 const QXmlStreamAttributes &attributes,
3756 QSvgHandler *)
3757{
3758 Q_UNUSED(parent); Q_UNUSED(attributes);
3759 return true;
3760}
3761
3763 const QXmlStreamAttributes &attributes,
3764 QSvgHandler *handler)
3765{
3766 const QStringView cx = attributes.value(QLatin1String("cx"));
3767 const QStringView cy = attributes.value(QLatin1String("cy"));
3768 const QStringView r = attributes.value(QLatin1String("r"));
3769 const QStringView fx = attributes.value(QLatin1String("fx"));
3770 const QStringView fy = attributes.value(QLatin1String("fy"));
3771
3772 qreal ncx = 0.5;
3773 qreal ncy = 0.5;
3774 if (!cx.isEmpty())
3775 ncx = toDouble(cx);
3776 if (!cy.isEmpty())
3777 ncy = toDouble(cy);
3778
3779 qreal nr = 0.5;
3780 if (!r.isEmpty())
3781 nr = toDouble(r);
3782 if (nr <= 0.0)
3783 return nullptr;
3784
3785 qreal nfx = ncx;
3786 if (!fx.isEmpty())
3787 nfx = toDouble(fx);
3788 qreal nfy = ncy;
3789 if (!fy.isEmpty())
3790 nfy = toDouble(fy);
3791
3792 QRadialGradient *grad = new QRadialGradient(ncx, ncy, nr, nfx, nfy, 0);
3794
3795 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
3796 parseBaseGradient(node, attributes, prop, handler);
3797
3798 return prop;
3799}
3800
3802 const QXmlStreamAttributes &attributes,
3803 QSvgHandler *handler)
3804{
3805 const QStringView x = attributes.value(QLatin1String("x"));
3806 const QStringView y = attributes.value(QLatin1String("y"));
3807 const QStringView width = attributes.value(QLatin1String("width"));
3808 const QStringView height = attributes.value(QLatin1String("height"));
3809 const QStringView rx = attributes.value(QLatin1String("rx"));
3810 const QStringView ry = attributes.value(QLatin1String("ry"));
3811
3812 bool ok = true;
3814 qreal nwidth = parseLength(width.toString(), &type, handler, &ok);
3815 if (!ok)
3816 return nullptr;
3817 nwidth = convertToPixels(nwidth, true, type);
3818 qreal nheight = parseLength(height.toString(), &type, handler, &ok);
3819 if (!ok)
3820 return nullptr;
3821 nheight = convertToPixels(nheight, true, type);
3822 qreal nrx = toDouble(rx);
3823 qreal nry = toDouble(ry);
3824
3825 QRectF bounds(toDouble(x), toDouble(y), nwidth, nheight);
3826 if (bounds.isEmpty())
3827 return nullptr;
3828
3829 if (!rx.isEmpty() && ry.isEmpty())
3830 nry = nrx;
3831 else if (!ry.isEmpty() && rx.isEmpty())
3832 nrx = nry;
3833
3834 //9.2 The 'rect' element clearly specifies it
3835 // but the case might in fact be handled because
3836 // we draw rounded rectangles differently
3837 if (nrx > bounds.width()/2)
3838 nrx = bounds.width()/2;
3839 if (nry > bounds.height()/2)
3840 nry = bounds.height()/2;
3841
3842 //we draw rounded rect from 0...99
3843 //svg from 0...bounds.width()/2 so we're adjusting the
3844 //coordinates
3845 nrx *= (100/(bounds.width()/2));
3846 nry *= (100/(bounds.height()/2));
3847
3848 QSvgNode *rect = new QSvgRect(parent, bounds, nrx, nry);
3849 return rect;
3850}
3851
3852static bool parseScriptNode(QSvgNode *parent,
3853 const QXmlStreamAttributes &attributes,
3854 QSvgHandler *)
3855{
3856 Q_UNUSED(parent); Q_UNUSED(attributes);
3857 return true;
3858}
3859
3860static bool parseSetNode(QSvgNode *parent,
3861 const QXmlStreamAttributes &attributes,
3862 QSvgHandler *)
3863{
3864 Q_UNUSED(parent); Q_UNUSED(attributes);
3865 return true;
3866}
3867
3869 const QXmlStreamAttributes &attributes,
3870 QSvgHandler *handler)
3871{
3872 Q_UNUSED(parent); Q_UNUSED(attributes);
3873 QStringView solidColorStr = attributes.value(QLatin1String("solid-color"));
3874 QStringView solidOpacityStr = attributes.value(QLatin1String("solid-opacity"));
3875
3876 if (solidOpacityStr.isEmpty())
3877 solidOpacityStr = attributes.value(QLatin1String("opacity"));
3878
3879 QColor color;
3880 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3881 return 0;
3883 return style;
3884}
3885
3887 const QXmlStreamAttributes &attributes,
3888 QSvgHandler *handler)
3889{
3890 if (parent->type() != QSvgStyleProperty::GRADIENT)
3891 return false;
3892 QString nodeIdStr = someId(attributes);
3893 QString xmlClassStr = attributes.value(QLatin1String("class")).toString();
3894
3895 //### nasty hack because stop gradients are not in the rendering tree
3896 // we force a dummy node with the same id and class into a rendering
3897 // tree to figure out whether the selector has a style for it
3898 // QSvgStyleSelector should be coded in a way that could avoid it
3899 QSvgAnimation anim;
3900 anim.setNodeId(nodeIdStr);
3901 anim.setXmlClass(xmlClassStr);
3902
3903 QXmlStreamAttributes xmlAttr = attributes;
3904
3905#ifndef QT_NO_CSSPARSER
3906 cssStyleLookup(&anim, handler, handler->selector(), xmlAttr);
3907#endif
3908 parseStyle(&anim, xmlAttr, handler);
3909
3910 QSvgAttributes attrs(xmlAttr, handler);
3911
3912 QSvgGradientStyle *style =
3913 static_cast<QSvgGradientStyle*>(parent);
3914 QStringView colorStr = attrs.stopColor;
3915 QColor color;
3916
3917 bool ok = true;
3918 qreal offset = convertToNumber(attrs.offset, handler, &ok);
3919 if (!ok)
3920 offset = 0.0;
3921 QString black = QString::fromLatin1("#000000");
3922 if (colorStr.isEmpty()) {
3923 colorStr = black;
3924 }
3925
3926 constructColor(colorStr, attrs.stopOpacity, color, handler);
3927
3928 QGradient *grad = style->qgradient();
3929
3930 offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
3931 QGradientStops stops;
3932 if (style->gradientStopsSet()) {
3933 stops = grad->stops();
3934 // If the stop offset equals the one previously added, add an epsilon to make it greater.
3935 if (offset <= stops.back().first)
3936 offset = stops.back().first + FLT_EPSILON;
3937 }
3938
3939 // If offset is greater than one, it must be clamped to one.
3940 if (offset > 1.0) {
3941 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3942 stops.back().first = 1.0 - FLT_EPSILON;
3943 grad->setStops(stops);
3944 }
3945 offset = 1.0;
3946 }
3947
3948 grad->setColorAt(offset, color);
3949 style->setGradientStopsSet(true);
3950 return true;
3951}
3952
3953static bool parseStyleNode(QSvgNode *parent,
3954 const QXmlStreamAttributes &attributes,
3955 QSvgHandler *handler)
3956{
3957 Q_UNUSED(parent);
3958#ifdef QT_NO_CSSPARSER
3959 Q_UNUSED(attributes);
3960 Q_UNUSED(handler);
3961#else
3962 const QStringView type = attributes.value(QLatin1String("type"));
3963 if (type.compare(QLatin1String("text/css"), Qt::CaseInsensitive) == 0 || type.isNull())
3964 handler->setInStyle(true);
3965#endif
3966
3967 return true;
3968}
3969
3971 const QXmlStreamAttributes &attributes,
3972 QSvgHandler *handler)
3973{
3974 Q_UNUSED(parent); Q_UNUSED(attributes);
3975
3976 QSvgTinyDocument *node = new QSvgTinyDocument(handler->options());
3977 const QStringView widthStr = attributes.value(QLatin1String("width"));
3978 const QStringView heightStr = attributes.value(QLatin1String("height"));
3979 QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
3980
3981 QSvgHandler::LengthType type = QSvgHandler::LT_PX; // FIXME: is the default correct?
3982 qreal width = 0;
3983 if (!widthStr.isEmpty()) {
3984 width = parseLength(widthStr.toString(), &type, handler);
3985 if (type != QSvgHandler::LT_PT)
3986 width = convertToPixels(width, true, type);
3988 }
3989 qreal height = 0;
3990 if (!heightStr.isEmpty()) {
3991 height = parseLength(heightStr.toString(), &type, handler);
3992 if (type != QSvgHandler::LT_PT)
3993 height = convertToPixels(height, false, type);
3995 }
3996
3997 QStringList viewBoxValues;
3998 if (!viewBoxStr.isEmpty()) {
3999 viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
4000 viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
4001 viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
4002 viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
4003 viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts);
4004 }
4005 if (viewBoxValues.size() == 4) {
4006 QString xStr = viewBoxValues.at(0).trimmed();
4007 QString yStr = viewBoxValues.at(1).trimmed();
4008 QString widthStr = viewBoxValues.at(2).trimmed();
4009 QString heightStr = viewBoxValues.at(3).trimmed();
4010
4012 qreal x = parseLength(xStr, &lt, handler);
4013 qreal y = parseLength(yStr, &lt, handler);
4014 qreal w = parseLength(widthStr, &lt, handler);
4015 qreal h = parseLength(heightStr, &lt, handler);
4016
4017 node->setViewBox(QRectF(x, y, w, h));
4018
4019 } else if (width && height) {
4020 if (type == QSvgHandler::LT_PT) {
4021 width = convertToPixels(width, false, type);
4022 height = convertToPixels(height, false, type);
4023 }
4024 node->setViewBox(QRectF(0, 0, width, height));
4025 }
4027
4028 return node;
4029}
4030
4032 const QXmlStreamAttributes &attributes,
4033 QSvgHandler *)
4034{
4035 Q_UNUSED(attributes);
4036 QSvgSwitch *node = new QSvgSwitch(parent);
4037 return node;
4038}
4039
4041 const QXmlStreamAttributes &attributes,
4042 QSvgHandler *handler)
4043{
4044 const QStringView x = attributes.value(QLatin1String("x"));
4045 const QStringView y = attributes.value(QLatin1String("y"));
4046 const QStringView width = attributes.value(QLatin1String("width"));
4047 const QStringView height = attributes.value(QLatin1String("height"));
4048 const QStringView patternUnits = attributes.value(QLatin1String("patternUnits"));
4049 const QStringView patternContentUnits = attributes.value(QLatin1String("patternContentUnits"));
4050 const QStringView patternTransform = attributes.value(QLatin1String("patternTransform"));
4051
4052 QtSvg::UnitTypes nPatternUnits = patternUnits.contains(QLatin1String("userSpaceOnUse")) ?
4054
4055 QtSvg::UnitTypes nPatternContentUnits = patternContentUnits.contains(QLatin1String("objectBoundingBox")) ?
4057
4058 QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
4059
4060 bool ok = false;
4062
4063 qreal nx = parseLength(x.toString(), &type, handler, &ok);
4064 nx = convertToPixels(nx, true, type);
4065 if (!ok)
4066 nx = 0.0;
4067 else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
4068 nx = (nx / 100.) * handler->document()->viewBox().width();
4069 else if (type == QSvgHandler::LT_PERCENT)
4070 nx = nx / 100.;
4071
4072 qreal ny = parseLength(y.toString(), &type, handler, &ok);
4073 ny = convertToPixels(ny, true, type);
4074 if (!ok)
4075 ny = 0.0;
4076 else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
4077 ny = (ny / 100.) * handler->document()->viewBox().height();
4078 else if (type == QSvgHandler::LT_PERCENT)
4079 ny = ny / 100.;
4080
4081 qreal nwidth = parseLength(width.toString(), &type, handler, &ok);
4082 nwidth = convertToPixels(nwidth, true, type);
4083 if (!ok)
4084 nwidth = 0.0;
4085 else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
4086 nwidth = (nwidth / 100.) * handler->document()->viewBox().width();
4087 else if (type == QSvgHandler::LT_PERCENT)
4088 nwidth = nwidth / 100.;
4089
4090 qreal nheight = parseLength(height.toString(), &type, handler, &ok);
4091 nheight = convertToPixels(nheight, true, type);
4092 if (!ok)
4093 nheight = 0.0;
4094 else if (type == QSvgHandler::LT_PERCENT && nPatternUnits == QtSvg::UnitTypes::userSpaceOnUse)
4095 nheight = (nheight / 100.) * handler->document()->viewBox().height();
4096 else if (type == QSvgHandler::LT_PERCENT)
4097 nheight = nheight / 100.;
4098
4099
4100 QStringList viewBoxValues;
4101 QRectF viewBox;
4102 if (!viewBoxStr.isEmpty()) {
4103 viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
4104 viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
4105 viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
4106 viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
4107 viewBoxValues = viewBoxStr.split(QLatin1Char(','), Qt::SkipEmptyParts);
4108 }
4109 if (viewBoxValues.size() == 4) {
4110 QString xStr = viewBoxValues.at(0).trimmed();
4111 QString yStr = viewBoxValues.at(1).trimmed();
4112 QString widthStr = viewBoxValues.at(2).trimmed();
4113 QString heightStr = viewBoxValues.at(3).trimmed();
4114
4115 qreal x = convertToNumber(xStr, handler);
4116 qreal y = convertToNumber(yStr, handler);
4117 qreal w = convertToNumber(widthStr, handler);
4118 qreal h = convertToNumber(heightStr, handler);
4119
4120 if (w > 0 && h > 0)
4121 viewBox.setRect(x, y, w, h);
4122 }
4123
4125 if (!patternTransform.isEmpty())
4126 matrix = parseTransformationMatrix(patternTransform);
4127
4128 QRectF bounds(nx, ny, nwidth, nheight);
4129 if (bounds.isEmpty())
4130 return nullptr;
4131
4132 QSvgRectF patternRectF(bounds, nPatternUnits, nPatternUnits, nPatternUnits, nPatternUnits);
4133 QSvgPattern *node = new QSvgPattern(parent, patternRectF, viewBox, nPatternContentUnits, matrix);
4134
4135 // Create a style node for the Pattern.
4136 QSvgPatternStyle *prop = new QSvgPatternStyle(node);
4137 node->appendStyleProperty(prop, someId(attributes));
4138
4139 return node;
4140}
4141
4142static bool parseTbreakNode(QSvgNode *parent,
4143 const QXmlStreamAttributes &,
4144 QSvgHandler *)
4145{
4146 if (parent->type() != QSvgNode::Textarea)
4147 return false;
4148 static_cast<QSvgText*>(parent)->addLineBreak();
4149 return true;
4150}
4151
4153 const QXmlStreamAttributes &attributes,
4154 QSvgHandler *handler)
4155{
4156 const QStringView x = attributes.value(QLatin1String("x"));
4157 const QStringView y = attributes.value(QLatin1String("y"));
4158 //### editable and rotate not handled
4160 qreal nx = parseLength(x.toString(), &type, handler);
4161 nx = convertToPixels(nx, true, type);
4162 qreal ny = parseLength(y.toString(), &type, handler);
4163 ny = convertToPixels(ny, true, type);
4164
4165 QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
4166 return text;
4167}
4168
4170 const QXmlStreamAttributes &attributes,
4171 QSvgHandler *handler)
4172{
4173 QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
4174 if (node) {
4176 qreal width = parseLength(attributes.value(QLatin1String("width")), &type, handler);
4177 qreal height = parseLength(attributes.value(QLatin1String("height")), &type, handler);
4178 node->setTextArea(QSizeF(width, height));
4179 }
4180 return node;
4181}
4182
4184 const QXmlStreamAttributes &,
4185 QSvgHandler *)
4186{
4187 return new QSvgTspan(parent);
4188}
4189
4191 const QXmlStreamAttributes &attributes,
4192 QSvgHandler *handler)
4193{
4194 QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
4195 const QStringView xStr = attributes.value(QLatin1String("x"));
4196 const QStringView yStr = attributes.value(QLatin1String("y"));
4197 QSvgStructureNode *group = nullptr;
4198
4199 if (linkId.isEmpty())
4200 linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
4201 switch (parent->type()) {
4202 case QSvgNode::Doc:
4203 case QSvgNode::Defs:
4204 case QSvgNode::Group:
4205 case QSvgNode::Switch:
4206 case QSvgNode::Mask:
4207 group = static_cast<QSvgStructureNode*>(parent);
4208 break;
4209 default:
4210 break;
4211 }
4212
4213 if (group) {
4214 QPointF pt;
4215 if (!xStr.isNull() || !yStr.isNull()) {
4217 qreal nx = parseLength(xStr.toString(), &type, handler);
4218 nx = convertToPixels(nx, true, type);
4219
4220 qreal ny = parseLength(yStr.toString(), &type, handler);
4221 ny = convertToPixels(ny, true, type);
4222 pt = QPointF(nx, ny);
4223 }
4224
4225 QSvgNode *link = group->scopeNode(linkId);
4226 if (link) {
4227 if (parent->isDescendantOf(link))
4228 qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(linkId));
4229
4230 return new QSvgUse(pt, parent, link);
4231 }
4232
4233 //delay link resolving, link might have not been created yet
4234 return new QSvgUse(pt, parent, linkId);
4235 }
4236
4237 qCWarning(lcSvgHandler, "<use> element %s in wrong context!", qPrintable(linkId));
4238 return 0;
4239}
4240
4242 const QXmlStreamAttributes &attributes,
4243 QSvgHandler *)
4244{
4245 Q_UNUSED(parent); Q_UNUSED(attributes);
4246 return 0;
4247}
4248
4249typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
4250
4251static FactoryMethod findGroupFactory(const QString &name, QtSvg::Options options)
4252{
4253 if (name.isEmpty())
4254 return 0;
4255
4256 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
4257 switch (name.at(0).unicode()) {
4258 case 'd':
4259 if (ref == QLatin1String("efs")) return createDefsNode;
4260 break;
4261 case 'f':
4262 if (ref == QLatin1String("ilter") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return createFilterNode;
4263 break;
4264 case 'g':
4265 if (ref.isEmpty()) return createGNode;
4266 break;
4267 case 'm':
4268 if (ref == QLatin1String("ask") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return createMaskNode;
4269 if (ref == QLatin1String("arker") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return createMarkerNode;
4270 break;
4271 case 's':
4272 if (ref == QLatin1String("vg")) return createSvgNode;
4273 if (ref == QLatin1String("witch")) return createSwitchNode;
4274 if (ref == QLatin1String("ymbol") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return createSymbolNode;
4275 break;
4276 case 'p':
4277 if (ref == QLatin1String("attern") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return createPatternNode;
4278 break;
4279 default:
4280 break;
4281 }
4282 return 0;
4283}
4284
4285static FactoryMethod findGraphicsFactory(const QString &name, QtSvg::Options options)
4286{
4287 Q_UNUSED(options);
4288 if (name.isEmpty())
4289 return 0;
4290
4291 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
4292 switch (name.at(0).unicode()) {
4293 case 'a':
4294 if (ref == QLatin1String("nimation")) return createAnimationNode;
4295 break;
4296 case 'c':
4297 if (ref == QLatin1String("ircle")) return createCircleNode;
4298 break;
4299 case 'e':
4300 if (ref == QLatin1String("llipse")) return createEllipseNode;
4301 break;
4302 case 'i':
4303 if (ref == QLatin1String("mage")) return createImageNode;
4304 break;
4305 case 'l':
4306 if (ref == QLatin1String("ine")) return createLineNode;
4307 break;
4308 case 'p':
4309 if (ref == QLatin1String("ath")) return createPathNode;
4310 if (ref == QLatin1String("olygon")) return createPolygonNode;
4311 if (ref == QLatin1String("olyline")) return createPolylineNode;
4312 break;
4313 case 'r':
4314 if (ref == QLatin1String("ect")) return createRectNode;
4315 break;
4316 case 't':
4317 if (ref == QLatin1String("ext")) return createTextNode;
4318 if (ref == QLatin1String("extArea")) return createTextAreaNode;
4319 if (ref == QLatin1String("span")) return createTspanNode;
4320 break;
4321 case 'u':
4322 if (ref == QLatin1String("se")) return createUseNode;
4323 break;
4324 case 'v':
4325 if (ref == QLatin1String("ideo")) return createVideoNode;
4326 break;
4327 default:
4328 break;
4329 }
4330 return 0;
4331}
4332
4333static FactoryMethod findFilterFactory(const QString &name, QtSvg::Options options)
4334{
4335 if (options.testFlag(QtSvg::Tiny12FeaturesOnly))
4336 return 0;
4337
4338 if (name.isEmpty())
4339 return 0;
4340
4341 if (!name.startsWith(QLatin1String("fe")))
4342 return 0;
4343
4344 if (name == QLatin1String("feMerge")) return createFeMergeNode;
4345 if (name == QLatin1String("feColorMatrix")) return createFeColorMatrixNode;
4346 if (name == QLatin1String("feGaussianBlur")) return createFeGaussianBlurNode;
4347 if (name == QLatin1String("feOffset")) return createFeOffsetNode;
4348 if (name == QLatin1String("feMergeNode")) return createFeMergeNodeNode;
4349 if (name == QLatin1String("feComposite")) return createFeCompositeNode;
4350 if (name == QLatin1String("feFlood")) return createFeFloodNode;
4351
4352 static const QStringList unsupportedFilters = {
4353 QStringLiteral("feBlend"),
4354 QStringLiteral("feComponentTransfer"),
4355 QStringLiteral("feConvolveMatrix"),
4356 QStringLiteral("feDiffuseLighting"),
4357 QStringLiteral("feDisplacementMap"),
4358 QStringLiteral("feDropShadow"),
4359 QStringLiteral("feFuncA"),
4360 QStringLiteral("feFuncB"),
4361 QStringLiteral("feFuncG"),
4362 QStringLiteral("feFuncR"),
4363 QStringLiteral("feImage"),
4364 QStringLiteral("feMorphology"),
4365 QStringLiteral("feSpecularLighting"),
4366 QStringLiteral("feTile"),
4367 QStringLiteral("feTurbulence")
4368 };
4369
4370 if (unsupportedFilters.contains(name))
4372
4373 return 0;
4374}
4375
4376typedef bool (*ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
4377
4378static ParseMethod findUtilFactory(const QString &name, QtSvg::Options options)
4379{
4380 if (name.isEmpty())
4381 return 0;
4382
4383 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
4384 switch (name.at(0).unicode()) {
4385 case 'a':
4386 if (ref.isEmpty()) return parseAnchorNode;
4387 if (ref == QLatin1String("nimate")) return parseAnimateNode;
4388 if (ref == QLatin1String("nimateColor")) return parseAnimateColorNode;
4389 if (ref == QLatin1String("nimateMotion")) return parseAimateMotionNode;
4390 if (ref == QLatin1String("nimateTransform")) return parseAnimateTransformNode;
4391 if (ref == QLatin1String("udio")) return parseAudioNode;
4392 break;
4393 case 'd':
4394 if (ref == QLatin1String("iscard")) return parseDiscardNode;
4395 break;
4396 case 'f':
4397 if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
4398 break;
4399 case 'h':
4400 if (ref == QLatin1String("andler")) return parseHandlerNode;
4401 if (ref == QLatin1String("kern")) return parseHkernNode;
4402 break;
4403 case 'm':
4404 if (ref == QLatin1String("etadata")) return parseMetadataNode;
4405 if (ref == QLatin1String("path")) return parseMpathNode;
4406 if (ref == QLatin1String("ask") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return parseMaskNode;
4407 if (ref == QLatin1String("arker") && !options.testFlag(QtSvg::Tiny12FeaturesOnly)) return parseMarkerNode;
4408 break;
4409 case 'p':
4410 if (ref == QLatin1String("refetch")) return parsePrefetchNode;
4411 break;
4412 case 's':
4413 if (ref == QLatin1String("cript")) return parseScriptNode;
4414 if (ref == QLatin1String("et")) return parseSetNode;
4415 if (ref == QLatin1String("tyle")) return parseStyleNode;
4416 break;
4417 case 't':
4418 if (ref == QLatin1String("break")) return parseTbreakNode;
4419 break;
4420 default:
4421 break;
4422 }
4423 return 0;
4424}
4425
4426typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
4427 const QXmlStreamAttributes &,
4428 QSvgHandler *);
4429
4431{
4432 if (name.isEmpty())
4433 return 0;
4434
4435 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
4436 switch (name.at(0).unicode()) {
4437 case 'f':
4438 if (ref == QLatin1String("ont")) return createFontNode;
4439 break;
4440 case 'l':
4441 if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
4442 break;
4443 case 'r':
4444 if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
4445 break;
4446 case 's':
4447 if (ref == QLatin1String("olidColor")) return createSolidColorNode;
4448 break;
4449 default:
4450 break;
4451 }
4452 return 0;
4453}
4454
4456 const QXmlStreamAttributes &,
4457 QSvgHandler *);
4458
4460{
4461 if (name.isEmpty())
4462 return 0;
4463
4464 QStringView ref = QStringView{name}.mid(1, name.size() - 1);
4465 switch (name.at(0).unicode()) {
4466 case 'f':
4467 if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
4468 if (ref == QLatin1String("ont-face-name")) return parseFontFaceNameNode;
4469 if (ref == QLatin1String("ont-face-src")) return parseFontFaceSrcNode;
4470 if (ref == QLatin1String("ont-face-uri")) return parseFontFaceUriNode;
4471 break;
4472 case 'g':
4473 if (ref == QLatin1String("lyph")) return parseGlyphNode;
4474 break;
4475 case 'm':
4476 if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
4477 break;
4478 case 's':
4479 if (ref == QLatin1String("top")) return parseStopNode;
4480 break;
4481 default:
4482 break;
4483 }
4484 return 0;
4485}
4486
4488 : xml(new QXmlStreamReader(device))
4489 , m_ownsReader(true)
4490 , m_options(options)
4491{
4492 init();
4493}
4494
4495QSvgHandler::QSvgHandler(const QByteArray &data, QtSvg::Options options)
4496 : xml(new QXmlStreamReader(data))
4497 , m_ownsReader(true)
4498 , m_options(options)
4499{
4500 init();
4501}
4502
4503QSvgHandler::QSvgHandler(QXmlStreamReader *const reader, QtSvg::Options options)
4504 : xml(reader)
4505 , m_ownsReader(false)
4506 , m_options(options)
4507{
4508 init();
4509}
4510
4511void QSvgHandler::init()
4512{
4513 m_doc = 0;
4514 m_style = 0;
4515 m_animEnd = 0;
4516 m_defaultCoords = LT_PX;
4518 m_defaultPen.setMiterLimit(4);
4519 parse();
4520}
4521
4522static bool detectPatternCycles(const QSvgNode *node, QList<const QSvgNode *> active = {})
4523{
4524 QSvgFillStyle *fillStyle = static_cast<QSvgFillStyle*>
4526 if (fillStyle && fillStyle->style() && fillStyle->style()->type() == QSvgStyleProperty::PATTERN) {
4527 QSvgPatternStyle *patternStyle = static_cast<QSvgPatternStyle *>(fillStyle->style());
4528 if (active.contains(patternStyle->patternNode()))
4529 return true;
4530 }
4531
4532 QSvgStrokeStyle *strokeStyle = static_cast<QSvgStrokeStyle*>
4534 if (strokeStyle && strokeStyle->style() && strokeStyle->style()->type() == QSvgStyleProperty::PATTERN) {
4535 QSvgPatternStyle *patternStyle = static_cast<QSvgPatternStyle *>(strokeStyle->style());
4536 if (active.contains(patternStyle->patternNode()))
4537 return true;
4538 }
4539
4540 return false;
4541}
4542
4543static bool detectCycles(const QSvgNode *node, QList<const QSvgNode *> active = {})
4544{
4545 if (Q_UNLIKELY(!node))
4546 return false;
4547 switch (node->type()) {
4548 case QSvgNode::Doc:
4549 case QSvgNode::Group:
4550 case QSvgNode::Defs:
4551 case QSvgNode::Pattern:
4552 {
4553 if (node->type() == QSvgNode::Pattern)
4554 active.append(node);
4555
4556 auto *g = static_cast<const QSvgStructureNode*>(node);
4557 for (auto *r : g->renderers()) {
4558 if (detectCycles(r, active))
4559 return true;
4560 }
4561 }
4562 break;
4563 case QSvgNode::Use:
4564 {
4565 if (active.contains(node))
4566 return true;
4567
4568 auto *u = static_cast<const QSvgUse*>(node);
4569 auto *target = u->link();
4570 if (target) {
4571 active.append(u);
4572 if (detectCycles(target, active))
4573 return true;
4574 }
4575 }
4576 break;
4577 case QSvgNode::Rect:
4578 case QSvgNode::Ellipse:
4579 case QSvgNode::Circle:
4580 case QSvgNode::Line:
4581 case QSvgNode::Path:
4582 case QSvgNode::Polygon:
4583 case QSvgNode::Polyline:
4584 case QSvgNode::Tspan:
4585 if (detectPatternCycles(node, active))
4586 return true;
4587 break;
4588 default:
4589 break;
4590 }
4591 return false;
4592}
4593
4594// Having too many unfinished elements will cause a stack overflow
4595// in the dtor of QSvgTinyDocument, see oss-fuzz issue 24000.
4596static const int unfinishedElementsLimit = 2048;
4597
4598void QSvgHandler::parse()
4599{
4600 xml->setNamespaceProcessing(false);
4601#ifndef QT_NO_CSSPARSER
4602 m_selector = new QSvgStyleSelector;
4603 m_inStyle = false;
4604#endif
4605 bool done = false;
4606 int remainingUnfinishedElements = unfinishedElementsLimit;
4607 while (!xml->atEnd() && !done) {
4608 switch (xml->readNext()) {
4609 case QXmlStreamReader::StartElement:
4610 // he we could/should verify the namespaces, and simply
4611 // call m_skipNodes(Unknown) if we don't know the
4612 // namespace. We do support http://www.w3.org/2000/svg
4613 // but also http://www.w3.org/2000/svg-20000303-stylable
4614 // And if the document uses an external dtd, the reported
4615 // namespaceUri is empty. The only possible strategy at
4616 // this point is to do what everyone else seems to do and
4617 // ignore the reported namespaceUri completely.
4618 if (remainingUnfinishedElements
4619 && startElement(xml->name().toString(), xml->attributes())) {
4620 --remainingUnfinishedElements;
4621 } else {
4622 delete m_doc;
4623 m_doc = nullptr;
4624 return;
4625 }
4626 break;
4627 case QXmlStreamReader::EndElement:
4628 done = endElement(xml->name());
4629 ++remainingUnfinishedElements;
4630 break;
4631 case QXmlStreamReader::Characters:
4632 characters(xml->text());
4633 break;
4634 case QXmlStreamReader::ProcessingInstruction:
4635 processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
4636 break;
4637 default:
4638 break;
4639 }
4640 }
4641 resolvePaintServers(m_doc);
4642 resolveNodes();
4643 if (detectCycles(m_doc)) {
4644 qCWarning(lcSvgHandler, "Cycles detected in SVG, document discarded.");
4645 delete m_doc;
4646 m_doc = nullptr;
4647 }
4648}
4649
4651 const QXmlStreamAttributes &attributes)
4652{
4653 QSvgNode *node = nullptr;
4654
4655 pushColorCopy();
4656
4657 /* The xml:space attribute may appear on any element. We do
4658 * a lookup by the qualified name here, but this is namespace aware, since
4659 * the XML namespace can only be bound to prefix "xml." */
4660 const QStringView xmlSpace(attributes.value(QLatin1String("xml:space")));
4661 if (xmlSpace.isNull()) {
4662 // This element has no xml:space attribute.
4663 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
4664 } else if (xmlSpace == QLatin1String("preserve")) {
4665 m_whitespaceMode.push(QSvgText::Preserve);
4666 } else if (xmlSpace == QLatin1String("default")) {
4667 m_whitespaceMode.push(QSvgText::Default);
4668 } else {
4669 const QByteArray msg = '"' + xmlSpace.toString().toLocal8Bit()
4670 + "\" is an invalid value for attribute xml:space. "
4671 "Valid values are \"preserve\" and \"default\".";
4672 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4673 m_whitespaceMode.push(QSvgText::Default);
4674 }
4675
4676 if (!m_doc && localName != QLatin1String("svg"))
4677 return false;
4678
4679 if (m_doc && localName == QLatin1String("svg")) {
4680 m_skipNodes.push(Doc);
4681 qCWarning(lcSvgHandler) << "Skipping a nested svg element, because "
4682 "SVG Document must not contain nested svg elements in Svg Tiny 1.2";
4683 }
4684
4685 if (!m_skipNodes.isEmpty() && m_skipNodes.top() == Doc)
4686 return true;
4687
4688 if (FactoryMethod method = findGroupFactory(localName, options())) {
4689 //group
4690 node = method(m_doc ? m_nodes.top() : 0, attributes, this);
4691
4692 if (node) {
4693 if (!m_doc) {
4694 Q_ASSERT(node->type() == QSvgNode::Doc);
4695 m_doc = static_cast<QSvgTinyDocument*>(node);
4696 } else {
4697 switch (m_nodes.top()->type()) {
4698 case QSvgNode::Doc:
4699 case QSvgNode::Group:
4700 case QSvgNode::Defs:
4701 case QSvgNode::Switch:
4702 case QSvgNode::Mask:
4703 case QSvgNode::Symbol:
4704 case QSvgNode::Marker:
4705 case QSvgNode::Pattern:
4706 {
4708 static_cast<QSvgStructureNode*>(m_nodes.top());
4709 group->addChild(node, someId(attributes));
4710 }
4711 break;
4712 default:
4713 const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect.");
4714 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4715 delete node;
4716 node = 0;
4717 break;
4718 }
4719 }
4720
4721 if (node) {
4722 parseCoreNode(node, attributes);
4723#ifndef QT_NO_CSSPARSER
4724 cssStyleLookup(node, this, m_selector);
4725#endif
4726 parseStyle(node, attributes, this);
4727 if (node->type() == QSvgNode::Filter)
4728 m_toBeResolved.append(node);
4729 }
4730 }
4731 } else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
4732 //rendering element
4733 Q_ASSERT(!m_nodes.isEmpty());
4734 node = method(m_nodes.top(), attributes, this);
4735 if (node) {
4736 switch (m_nodes.top()->type()) {
4737 case QSvgNode::Doc:
4738 case QSvgNode::Group:
4739 case QSvgNode::Defs:
4740 case QSvgNode::Switch:
4741 case QSvgNode::Mask:
4742 case QSvgNode::Symbol:
4743 case QSvgNode::Marker:
4744 case QSvgNode::Pattern:
4745 {
4746 if (node->type() == QSvgNode::Tspan) {
4747 const QByteArray msg = QByteArrayLiteral("\'tspan\' element in wrong context.");
4748 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4749 delete node;
4750 node = 0;
4751 break;
4752 }
4754 static_cast<QSvgStructureNode*>(m_nodes.top());
4755 group->addChild(node, someId(attributes));
4756 }
4757 break;
4758 case QSvgNode::Text:
4759 case QSvgNode::Textarea:
4760 if (node->type() == QSvgNode::Tspan) {
4761 static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
4762 } else {
4763 const QByteArray msg = QByteArrayLiteral("\'text\' or \'textArea\' element contains invalid element type.");
4764 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4765 delete node;
4766 node = 0;
4767 }
4768 break;
4769 default:
4770 const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect.");
4771 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4772 delete node;
4773 node = 0;
4774 break;
4775 }
4776
4777 if (node) {
4778 parseCoreNode(node, attributes);
4779#ifndef QT_NO_CSSPARSER
4780 cssStyleLookup(node, this, m_selector);
4781#endif
4782 parseStyle(node, attributes, this);
4783 if (node->type() == QSvgNode::Text || node->type() == QSvgNode::Textarea) {
4784 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
4785 } else if (node->type() == QSvgNode::Tspan) {
4786 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
4787 } else if (node->type() == QSvgNode::Use) {
4788 auto useNode = static_cast<QSvgUse *>(node);
4789 if (!useNode->isResolved())
4790 m_toBeResolved.append(useNode);
4791 }
4792 }
4793 }
4794 } else if (FactoryMethod method = findFilterFactory(localName, options())) {
4795 //filter nodes to be aded to be filtercontainer
4796 Q_ASSERT(!m_nodes.isEmpty());
4797 node = method(m_nodes.top(), attributes, this);
4798 if (node) {
4799 if (m_nodes.top()->type() == QSvgNode::Filter ||
4800 (m_nodes.top()->type() == QSvgNode::FeMerge && node->type() == QSvgNode::FeMergenode)) {
4801 QSvgStructureNode *container =
4802 static_cast<QSvgStructureNode*>(m_nodes.top());
4803 container->addChild(node, someId(attributes));
4804 } else {
4805 const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect.");
4806 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4807 delete node;
4808 node = 0;
4809 }
4810 }
4811 } else if (ParseMethod method = findUtilFactory(localName, options())) {
4812 Q_ASSERT(!m_nodes.isEmpty());
4813 if (!method(m_nodes.top(), attributes, this))
4814 qCWarning(lcSvgHandler, "%s", msgProblemParsing(localName, xml).constData());
4815 } else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
4816 QSvgStyleProperty *prop = method(m_nodes.top(), attributes, this);
4817 if (prop) {
4818 m_style = prop;
4819 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
4820 } else {
4821 const QByteArray msg = QByteArrayLiteral("Could not parse node: ") + localName.toLocal8Bit();
4822 qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData());
4823 }
4824 } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
4825 if (m_style) {
4826 if (!method(m_style, attributes, this))
4827 qCWarning(lcSvgHandler, "%s", msgProblemParsing(localName, xml).constData());
4828 }
4829 } else {
4830 qCDebug(lcSvgHandler) << "Skipping unknown element" << localName;
4831 m_skipNodes.push(Unknown);
4832 return true;
4833 }
4834
4835 if (node) {
4836 m_nodes.push(node);
4837 m_skipNodes.push(Graphics);
4838 } else {
4839 //qDebug()<<"Skipping "<<localName;
4840 m_skipNodes.push(Style);
4841 }
4842 return true;
4843}
4844
4846{
4847 CurrentNode node = m_skipNodes.top();
4848
4849 if (node == Doc && localName != QLatin1String("svg"))
4850 return false;
4851
4852 m_skipNodes.pop();
4853 m_whitespaceMode.pop();
4854
4855 popColor();
4856
4857 if (node == Unknown)
4858 return false;
4859
4860#ifdef QT_NO_CSSPARSER
4861 Q_UNUSED(localName);
4862#else
4863 if (m_inStyle && localName == QLatin1String("style"))
4864 m_inStyle = false;
4865#endif
4866
4867 if (node == Graphics)
4868 m_nodes.pop();
4869 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
4870 m_style = 0;
4871
4872 return ((localName == QLatin1String("svg")) && (node != Doc));
4873}
4874
4875void QSvgHandler::resolvePaintServers(QSvgNode *node, int nestedDepth)
4876{
4877 if (!node || (node->type() != QSvgNode::Doc && node->type() != QSvgNode::Group
4878 && node->type() != QSvgNode::Defs && node->type() != QSvgNode::Switch)) {
4879 return;
4880 }
4881
4882 QSvgStructureNode *structureNode = static_cast<QSvgStructureNode *>(node);
4883
4884 const QList<QSvgNode *> ren = structureNode->renderers();
4885 for (auto it = ren.begin(); it != ren.end(); ++it) {
4886 QSvgFillStyle *fill = static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
4887 if (fill && !fill->isPaintStyleResolved()) {
4888 QString id = fill->paintStyleId();
4889 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
4890 if (style) {
4891 fill->setFillStyle(style);
4892 } else {
4893 qCWarning(lcSvgHandler, "%s", msgCouldNotResolveProperty(id, xml).constData());
4894 fill->setBrush(Qt::NoBrush);
4895 }
4896 }
4897
4898 QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
4899 if (stroke && !stroke->isPaintStyleResolved()) {
4900 QString id = stroke->paintStyleId();
4901 QSvgPaintStyleProperty *style = structureNode->styleProperty(id);
4902 if (style) {
4903 stroke->setStyle(style);
4904 } else {
4905 qCWarning(lcSvgHandler, "%s", msgCouldNotResolveProperty(id, xml).constData());
4906 stroke->setStroke(Qt::NoBrush);
4907 }
4908 }
4909
4910 if (nestedDepth < 2048)
4911 resolvePaintServers(*it, nestedDepth + 1);
4912 }
4913}
4914
4915void QSvgHandler::resolveNodes()
4916{
4917 for (QSvgNode *node : std::as_const(m_toBeResolved)) {
4918 if (node->type() == QSvgNode::Use) {
4919 QSvgUse *useNode = static_cast<QSvgUse *>(node);
4920 const auto parent = useNode->parent();
4921 if (!parent)
4922 continue;
4923
4924 QSvgNode::Type t = parent->type();
4926 continue;
4927
4928 QSvgStructureNode *group = static_cast<QSvgStructureNode *>(parent);
4929 QSvgNode *link = group->scopeNode(useNode->linkId());
4930 if (!link) {
4931 qCWarning(lcSvgHandler, "link #%s is undefined!", qPrintable(useNode->linkId()));
4932 continue;
4933 }
4934
4935 if (useNode->parent()->isDescendantOf(link))
4936 qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(useNode->linkId()));
4937
4938 useNode->setLink(link);
4939 } else if (node->type() == QSvgNode::Filter) {
4940 QSvgFilterContainer *filter = static_cast<QSvgFilterContainer *>(node);
4941 for (const QSvgNode *renderer : filter->renderers()) {
4943 if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
4944 filter->setSupported(false);
4945 break;
4946 }
4947 }
4948 }
4949 }
4950 m_toBeResolved.clear();
4951}
4952
4954{
4955#ifndef QT_NO_CSSPARSER
4956 if (m_inStyle) {
4957 QString css = str.toString();
4958 QCss::StyleSheet sheet;
4959 QCss::Parser(css).parse(&sheet);
4960 m_selector->styleSheets.append(sheet);
4961 return true;
4962 }
4963#endif
4964 if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
4965 return true;
4966
4967 if (m_nodes.top()->type() == QSvgNode::Text || m_nodes.top()->type() == QSvgNode::Textarea) {
4968 static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
4969 } else if (m_nodes.top()->type() == QSvgNode::Tspan) {
4970 static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
4971 }
4972
4973 return true;
4974}
4975
4977{
4978 return xml->device();
4979}
4980
4982{
4983 return m_doc;
4984}
4985
4987{
4988 return m_defaultCoords;
4989}
4990
4992{
4993 m_defaultCoords = type;
4994}
4995
4997{
4998 m_colorStack.push(color);
4999 m_colorTagCount.push(1);
5000}
5001
5003{
5004 if (m_colorTagCount.size())
5005 ++m_colorTagCount.top();
5006 else
5008}
5009
5011{
5012 if (m_colorTagCount.size()) {
5013 if (!--m_colorTagCount.top()) {
5014 m_colorStack.pop();
5015 m_colorTagCount.pop();
5016 }
5017 }
5018}
5019
5021{
5022 if (!m_colorStack.isEmpty())
5023 return m_colorStack.top();
5024 else
5025 return QColor(0, 0, 0);
5026}
5027
5028#ifndef QT_NO_CSSPARSER
5029
5031{
5032 m_inStyle = b;
5033}
5034
5036{
5037 return m_inStyle;
5038}
5039
5041{
5042 return m_selector;
5043}
5044
5045#endif // QT_NO_CSSPARSER
5046
5048{
5049#ifdef QT_NO_CSSPARSER
5051 Q_UNUSED(data);
5052#else
5053 if (target == QLatin1String("xml-stylesheet")) {
5054 QRegularExpression rx(QLatin1String("type=\\\"(.+)\\\""),
5057 bool isCss = false;
5058 while (iter.hasNext()) {
5060 QString type = match.captured(1);
5061 if (type.toLower() == QLatin1String("text/css")) {
5062 isCss = true;
5063 }
5064 }
5065
5066 if (isCss) {
5067 QRegularExpression rx(QLatin1String("href=\\\"(.+)\\\""),
5070 QString addr = match.captured(1);
5071 QFileInfo fi(addr);
5072 //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
5073 if (fi.exists()) {
5074 QFile file(fi.absoluteFilePath());
5076 return true;
5077 }
5078 QByteArray cssData = file.readAll();
5079 QString css = QString::fromUtf8(cssData);
5080
5081 QCss::StyleSheet sheet;
5082 QCss::Parser(css).parse(&sheet);
5083 m_selector->styleSheets.append(sheet);
5084 }
5085
5086 }
5087 }
5088#endif
5089
5090 return true;
5091}
5092
5094{
5095 Q_UNUSED(start);
5096 m_animEnd = qMax(end, m_animEnd);
5097}
5098
5100{
5101 return m_animEnd;
5102}
5103
5105{
5106#ifndef QT_NO_CSSPARSER
5107 delete m_selector;
5108 m_selector = 0;
5109#endif
5110
5111 if(m_ownsReader)
5112 delete xml;
5113}
5114
IOBluetoothDevice * device
\inmodule QtGui
Definition qbrush.h:30
\inmodule QtCore
Definition qbytearray.h:57
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.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
\inmodule QtCore
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
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
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:98
@ SmallCaps
Definition qfont.h:101
@ Bold
Definition qfont.h:70
@ Normal
Definition qfont.h:67
@ StyleItalic
Definition qfont.h:78
@ StyleNormal
Definition qfont.h:77
@ StyleOblique
Definition qfont.h:79
void fill(T value)
Fills all elements of this matrix with value.
\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.
QImage::Format imageFormat() const
\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:3841
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
\inmodule QtGui
Definition qbrush.h:394
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
reference back()
Definition qlist.h:689
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
\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:28
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:545
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
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:484
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:661
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:658
constexpr void setRect(qreal x, qreal y, qreal w, qreal h) noexcept
Sets the coordinates of the rectangle's top-left corner to (x, y), and its size to the given width an...
Definition qrect.h:781
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
\inmodule QtCore
Definition qsize.h:208
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:78
Q_CORE_EXPORT double toDouble(bool *ok=nullptr) const
Returns the string view converted to a double value.
Definition qstring.cpp:7909
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...
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1121
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:1132
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.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
const_iterator constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing just after the last character in...
Definition qstring.h:1363
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:5455
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
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:8218
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
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:5506
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6664
QByteArray toLocal8Bit() const &
Definition qstring.h:638
QString & append(QChar c)
Definition qstring.cpp:3252
QString trimmed() const &
Definition qstring.h:447
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:3466
QString & prepend(QChar c)
Definition qstring.h:478
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:1355
const QChar * unicode() const
Returns a Unicode representation of the string.
Definition qstring.h:1230
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)
static const QSvgFeFilterPrimitive * castToFilterPrimitive(const QSvgNode *node)
void setFillRule(Qt::FillRule f)
void setFillOpacity(qreal opacity)
void setBrush(QBrush brush)
void setFillStyle(QSvgPaintStyleProperty *style)
void setPaintStyleResolved(bool resolved)
void setPaintStyleId(const QString &Id)
QSvgPaintStyleProperty * style() const
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 setStopLink(const QString &link, QSvgTinyDocument *doc)
QGradient * qgradient() const
void setGradientStopsSet(bool set)
QtSvg::Options options() const
QIODevice * device() const
void pushColorCopy()
bool inStyle() const
QSvgTinyDocument * document() const
int animationDuration() const
QSvgHandler(QIODevice *device, QtSvg::Options options={})
void parseCSStoXMLAttrs(const QString &css, QList< QSvgCssAttribute > *attributes)
void setAnimPeriod(int start, int end)
QColor currentColor() const
LengthType defaultCoordinateSystem() const
bool processingInstruction(const QString &target, const QString &data)
bool trustedSourceMode() const
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)
void setMarkerMidId(const QString &str)
Definition qsvgnode.cpp:558
@ TableCaptionMode
Definition qsvgnode_p.h:83
@ TableColumnMode
Definition qsvgnode_p.h:81
@ TableRowMode
Definition qsvgnode_p.h:79
@ TableColumnGroupMode
Definition qsvgnode_p.h:80
@ TableCellMode
Definition qsvgnode_p.h:82
@ CompactMode
Definition qsvgnode_p.h:72
@ TableRowGroupMode
Definition qsvgnode_p.h:76
@ TableHeaderGroupMode
Definition qsvgnode_p.h:77
@ InlineTableMode
Definition qsvgnode_p.h:75
@ TableFooterGroupMode
Definition qsvgnode_p.h:78
@ InheritMode
Definition qsvgnode_p.h:85
@ ListItemMode
Definition qsvgnode_p.h:70
void setRequiredFeatures(const QStringList &lst)
Definition qsvgnode.cpp:422
void setMaskId(const QString &str)
Definition qsvgnode.cpp:507
void setXmlClass(const QString &str)
Definition qsvgnode.cpp:497
QString typeName() const
Definition qsvgnode.cpp:384
void setMarkerEndId(const QString &str)
Definition qsvgnode.cpp:575
bool isDescendantOf(const QSvgNode *parent) const
Definition qsvgnode.cpp:161
QSvgStyleProperty * styleProperty(QSvgStyleProperty::Type type) const
Definition qsvgnode.cpp:258
void setRequiredExtensions(const QStringList &lst)
Definition qsvgnode.cpp:432
void setMarkerStartId(const QString &str)
Definition qsvgnode.cpp:541
void setFilterId(const QString &str)
Definition qsvgnode.cpp:524
void setRequiredFonts(const QStringList &lst)
Definition qsvgnode.cpp:462
void setNodeId(const QString &i)
Definition qsvgnode.cpp:492
QSvgNode * parent() const
Definition qsvgnode_p.h:202
@ FeMergenode
Definition qsvgnode_p.h:59
@ FeUnsupported
Definition qsvgnode_p.h:65
void appendStyleProperty(QSvgStyleProperty *prop, const QString &id)
Definition qsvgnode.cpp:172
void setDisplayMode(DisplayMode display)
Definition qsvgnode.cpp:594
bool isVisible() const
Definition qsvgnode_p.h:207
void setRequiredLanguages(const QStringList &lst)
Definition qsvgnode.cpp:442
QSvgTinyDocument * document() const
Definition qsvgnode.cpp:372
virtual Type type() const =0
void setVisible(bool visible)
Definition qsvgnode.cpp:472
void setRequiredFormats(const QStringList &lst)
Definition qsvgnode.cpp:452
bool isPaintStyleResolved() const
QString paintStyleId() const
void setDashArray(const QList< qreal > &dashes)
void setMiterLimit(qreal limit)
void setDashArrayNone()
QSvgPaintStyleProperty * style() const
void setStroke(QBrush brush)
void setPaintStyleResolved(bool resolved)
void setWidth(qreal width)
void setPaintStyleId(const QString &Id)
void setDashOffset(qreal offset)
void setLineCap(Qt::PenCapStyle cap)
void setVectorEffect(bool nonScalingStroke)
void setOpacity(qreal opacity)
void setStyle(QSvgPaintStyleProperty *style)
void setLineJoin(Qt::PenJoinStyle join)
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
QRectF viewBox() const
void addSvgFont(QSvgFont *)
void setHeight(int len, bool percent)
QSvgNode * link() const
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
\inmodule QtCore
Definition qurl.h:94
bool isRelative() const
Returns true if the URL is relative; otherwise returns false.
Definition qurl.cpp:2800
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
constexpr void setZ(float z) noexcept
Sets the z coordinate of this point to the given finite z coordinate.
Definition qvectornd.h:885
constexpr void setY(float y) noexcept
Sets the y coordinate of this point to the given finite y coordinate.
Definition qvectornd.h:884
constexpr void setX(float x) noexcept
Sets the x coordinate of this point to the given finite x coordinate.
Definition qvectornd.h:883
constexpr void setW(float w) noexcept
Sets the w coordinate of this point to the given finite w coordinate.
Definition qvectornd.h:886
QString str
[2]
QString text
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.
@ Tiny12FeaturesOnly
Definition qtsvgglobal.h:17
@ AlignRight
Definition qnamespace.h:146
@ AlignHCenter
Definition qnamespace.h:148
@ AlignLeft
Definition qnamespace.h:144
@ black
Definition qnamespace.h:30
@ SolidLine
@ SvgMiterJoin
@ BevelJoin
@ RoundJoin
@ CaseInsensitive
@ NoBrush
@ WindingFill
@ OddEvenFill
@ SkipEmptyParts
Definition qnamespace.h:128
@ SquareCap
@ RoundCap
@ FlatCap
Definition image.cpp:4
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
size_t qstrlen(const char *str)
SharedPointerFileDialogOptions m_options
#define rgb(r, g, b)
Definition qcolor.cpp:124
#define Q_UNLIKELY(x)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
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:286
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#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
#define M_PI
Definition qmath.h:209
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
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
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 GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLuint color
[2]
GLenum type
GLboolean GLuint group
GLuint GLfloat x0
GLenum GLsizeiptr fontSize
GLenum target
GLsizei const GLfloat * dashArray
GLenum const void GLbitfield fontStyle
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLfloat units
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
const GLchar * marker
GLint ref
GLuint name
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
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
GLdouble s
[6]
Definition qopenglext.h:235
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 const void * addr
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
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:1531
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
bool(* ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static QSvgNode * createTspanNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static void parseVisibility(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool detectPatternCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
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 * createFeOffsetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeMergeNodeNode(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 QSvgNode * createFeGaussianBlurNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseOthers(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parsePathDataFast(QStringView data, QPainterPath &path, bool limitLength=true)
static QSvgNode * createMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static void parseRenderingHints(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static const int unfinishedElementsLimit
static void parseExtendedAttributes(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
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 QSvgNode * createPathNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 void cssStyleLookup(QSvgNode *node, QSvgHandler *handler, QSvgStyleSelector *selector, QXmlStreamAttributes &attributes)
static int parseClockValue(QStringView str, bool *ok)
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler, QRectF *rect, QRectF *viewBox, QPointF *refPoint, QSvgSymbolLike::PreserveAspectRatios *aspect, QSvgSymbolLike::Overflow *overflow, bool marker=false)
static bool parseFontFaceNameNode(QSvgStyleProperty *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
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 FactoryMethod findGraphicsFactory(const QString &name, QtSvg::Options options)
static QSvgNode * createFeColorMatrixNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 FactoryMethod findGroupFactory(const QString &name, QtSvg::Options options)
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 parseMarkerNode(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *)
static bool resolveColor(QStringView colorStr, QColor &color, QSvgHandler *handler)
#define QT_INHERIT
static qreal parseLength(QStringView str, QSvgHandler::LengthType *type, QSvgHandler *handler, bool *ok=NULL)
static QList< qreal > parsePercentageList(const QChar *&str)
static QSvgNode * createFeCompositeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 parseFilterAttributes(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler, QString *inString, QString *outString, QSvgRectF *rect)
static void parseOpacity(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *)
static bool parseScriptNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseFilterBounds(QSvgNode *, const QXmlStreamAttributes &attributes, QSvgHandler *handler, QSvgRectF *rect)
static QSvgNode * createPatternNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseHandlerNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static QList< qreal > parseNumbersList(const QChar *&str)
static FactoryMethod findFilterFactory(const QString &name, QtSvg::Options options)
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 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 QSvgNode * createFeUnsupportedNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static bool parseForeignObjectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static bool parseTbreakNode(QSvgNode *parent, const QXmlStreamAttributes &, QSvgHandler *)
static void parseFont(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createFeFloodNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createRectNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
static QSvgNode * createMarkerNode(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 QSvgNode * createFeMergeNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 parseMaskNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
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 bool detectCycles(const QSvgNode *node, QList< const QSvgNode * > active={})
static QStringList stringToList(const QString &str)
static QSvgNode * createSymbolNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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 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 bool parseSetNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *)
static void parseNumberTriplet(QList< qreal > &values, const QChar *&s)
static QSvgNode * createFilterNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler)
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)
static ParseMethod findUtilFactory(const QString &name, QtSvg::Options options)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#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:48
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
QList< int > list
[14]
QList< QChar > characters
QFile file
[0]
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!")
QHostInfo info
[0]
char * toString(const MyType &t)
[31]
QGraphicsSvgItem * black
QSvgRenderer * renderer
[0]
QExplicitlySharedDataPointer< DeclarationData > d
\inmodule QtCore \reentrant
Definition qchar.h:18
QStringView fontVariant
QStringView strokeDashOffset
QStringView stroke
QStringView opacity
QStringView fontFamily
QStringView strokeDashArray
QStringView mask
QStringView strokeOpacity
QStringView stopColor
QStringView fillOpacity
QStringView strokeLineJoin
QList< QSvgCssAttribute > m_cssAttributes
QStringView filter
QStringView color
QStringView fontSize
QStringView visibility
QStringView markerEnd
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
QStringView transform
QStringView fontWeight
QStringView fillRule
QStringView fill
QStringView vectorEffect
QStringView markerMid
QStringView strokeLineCap
QStringView fontStyle
QStringView display
QStringView compOp
QStringView markerStart
QStringView strokeMiterLimit
QStringView colorOpacity
QStringView textAnchor
QStringView offset
QStringView strokeWidth
QStringView stopOpacity
QStringView imageRendering
QByteArray lexem() const
Definition symbols.h:60
qsizetype len
Definition symbols.h:69