1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtSvg module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qsvghandler_p.h"
46 #include "qsvgtinydocument_p.h"
47 #include "qsvgstructure_p.h"
48 #include "qsvggraphics_p.h"
49 #include "qsvgnode_p.h"
50 #include "qsvgfont_p.h"
52 #include "qapplication.h"
55 #include "qpainterpath.h"
58 #include "qtextformat.h"
60 #include "qfileinfo.h"
65 #include "private/qmath_p.h"
71 double qstrtod(const char *s00
, char const **se
, bool *ok
);
73 static bool parsePathDataFast(const QStringRef
&data
, QPainterPath
&path
);
77 QSvgAttributes(const QXmlStreamAttributes
&xmlAttributes
, QSvgHandler
*handler
);
79 QStringRef
value(const QLatin1String
&name
) const;
80 QStringRef
value(const QString
&namespaceUri
, const QLatin1String
&name
) const;
82 QXmlStreamAttributes m_xmlAttributes
;
83 QVector
<QSvgCssAttribute
> m_cssAttributes
;
86 QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes
&xmlAttributes
, QSvgHandler
*handler
)
87 : m_xmlAttributes(xmlAttributes
)
89 QStringRef style
= xmlAttributes
.value(QLatin1String("style"));
91 handler
->parseCSStoXMLAttrs(style
.toString(), &m_cssAttributes
);
94 QStringRef
QSvgAttributes::value(const QLatin1String
&name
) const
96 QStringRef v
= m_xmlAttributes
.value(name
);
98 for (int i
= 0; i
< m_cssAttributes
.count(); ++i
) {
99 if (m_cssAttributes
.at(i
).name
== name
) {
100 v
= m_cssAttributes
.at(i
).value
;
108 QStringRef
QSvgAttributes::value(const QString
&namespaceUri
, const QLatin1String
&name
) const
110 QStringRef v
= m_xmlAttributes
.value(namespaceUri
, name
);
112 for (int i
= 0; i
< m_cssAttributes
.count(); ++i
) {
113 if (m_cssAttributes
.at(i
).name
== name
) {
114 v
= m_cssAttributes
.at(i
).value
;
122 static inline QString
someId(const QXmlStreamAttributes
&attributes
)
124 QString id
= attributes
.value(QLatin1String("id")).toString();
126 id
= attributes
.value(QLatin1String("xml:id")).toString();
129 static inline QString
someId(const QSvgAttributes
&attributes
)
130 { return someId(attributes
.m_xmlAttributes
); }
134 static const char * QSvgStyleSelector_nodeString
[] = {
155 class QSvgStyleSelector
: public QCss::StyleSelector
160 nameCaseSensitivity
= Qt::CaseInsensitive
;
162 virtual ~QSvgStyleSelector()
166 inline QString
nodeToName(QSvgNode
*node
) const
168 return QLatin1String(QSvgStyleSelector_nodeString
[node
->type()]);
171 inline QSvgNode
*svgNode(NodePtr node
) const
173 return (QSvgNode
*)node
.ptr
;
175 inline QSvgStructureNode
*nodeToStructure(QSvgNode
*n
) const
178 (n
->type() == QSvgNode::DOC
||
179 n
->type() == QSvgNode::G
||
180 n
->type() == QSvgNode::DEFS
||
181 n
->type() == QSvgNode::SWITCH
)) {
182 return (QSvgStructureNode
*)n
;
187 inline QSvgStructureNode
*svgStructure(NodePtr node
) const
189 QSvgNode
*n
= svgNode(node
);
190 QSvgStructureNode
*st
= nodeToStructure(n
);
194 virtual bool nodeNameEquals(NodePtr node
, const QString
& nodeName
) const
196 QSvgNode
*n
= svgNode(node
);
199 QString name
= nodeToName(n
);
200 return QString::compare(name
, nodeName
, Qt::CaseInsensitive
) == 0;
202 virtual QString
attribute(NodePtr node
, const QString
&name
) const
204 QSvgNode
*n
= svgNode(node
);
205 if ((!n
->nodeId().isEmpty() && (name
== QLatin1String("id") ||
206 name
== QLatin1String("xml:id"))))
208 if (!n
->xmlClass().isEmpty() && name
== QLatin1String("class"))
209 return n
->xmlClass();
212 virtual bool hasAttributes(NodePtr node
) const
214 QSvgNode
*n
= svgNode(node
);
216 (!n
->nodeId().isEmpty() || !n
->xmlClass().isEmpty()));
219 virtual QStringList
nodeIds(NodePtr node
) const
221 QSvgNode
*n
= svgNode(node
);
225 QStringList lst
; lst
.append(nid
);
229 virtual QStringList
nodeNames(NodePtr node
) const
231 QSvgNode
*n
= svgNode(node
);
233 return QStringList(nodeToName(n
));
234 return QStringList();
237 virtual bool isNullNode(NodePtr node
) const
242 virtual NodePtr
parentNode(NodePtr node
) const
244 QSvgNode
*n
= svgNode(node
);
249 QSvgNode
*svgParent
= n
->parent();
251 newNode
.ptr
= svgParent
;
256 virtual NodePtr
previousSiblingNode(NodePtr node
) const
262 QSvgNode
*n
= svgNode(node
);
265 QSvgStructureNode
*svgParent
= nodeToStructure(n
->parent());
268 newNode
.ptr
= svgParent
->previousSiblingNode(n
);
272 virtual NodePtr
duplicateNode(NodePtr node
) const
279 virtual void freeNode(NodePtr node
) const
285 static qreal
toDouble(const QChar
*&str
)
287 const int maxLen
= 255;//technically doubles can go til 308+ but whatever
291 if (*str
== QLatin1Char('-')) {
294 } else if (*str
== QLatin1Char('+')) {
297 while (*str
>= QLatin1Char('0') && *str
<= QLatin1Char('9') && pos
< maxLen
) {
298 temp
[pos
++] = str
->toLatin1();
301 if (*str
== QLatin1Char('.') && pos
< maxLen
) {
305 while (*str
>= QLatin1Char('0') && *str
<= QLatin1Char('9') && pos
< maxLen
) {
306 temp
[pos
++] = str
->toLatin1();
309 bool exponent
= false;
310 if (*str
== QLatin1Char('e') && pos
< maxLen
) {
314 if ((*str
== QLatin1Char('-') || *str
== QLatin1Char('+')) && pos
< maxLen
) {
315 temp
[pos
++] = str
->toLatin1();
318 while (*str
>= QLatin1Char('0') && *str
<= QLatin1Char('9') && pos
< maxLen
) {
319 temp
[pos
++] = str
->toLatin1();
326 if (!exponent
&& pos
< 10) {
328 const char *t
= temp
;
334 while(*t
&& *t
!= '.') {
348 val
= ((qreal
)ival
)/((qreal
)div
);
356 if(sizeof(qreal
) == sizeof(float))
357 val
= strtof(temp
, 0);
362 val
= qstrtod(temp
, 0, &ok
);
368 static qreal
toDouble(const QString
&str
)
370 const QChar
*c
= str
.constData();
374 static qreal
toDouble(const QStringRef
&str
)
376 const QChar
*c
= str
.constData();
380 static QVector
<qreal
> parseNumbersList(const QChar
*&str
)
382 QVector
<qreal
> points
;
387 while (*str
== QLatin1Char(' '))
389 while ((*str
>= QLatin1Char('0') && *str
<= QLatin1Char('9')) ||
390 *str
== QLatin1Char('-') || *str
== QLatin1Char('+') ||
391 *str
== QLatin1Char('.')) {
393 points
.append(toDouble(str
));
395 while (*str
== QLatin1Char(' '))
397 if (*str
== QLatin1Char(','))
400 //eat the rest of space
401 while (*str
== QLatin1Char(' '))
408 static QVector
<qreal
> parsePercentageList(const QChar
*&str
)
410 QVector
<qreal
> points
;
414 while (str
->isSpace())
416 while ((*str
>= QLatin1Char('0') && *str
<= QLatin1Char('9')) ||
417 *str
== QLatin1Char('-') || *str
== QLatin1Char('+') ||
418 *str
== QLatin1Char('.')) {
420 points
.append(toDouble(str
));
422 while (*str
== QLatin1Char(' '))
424 if (*str
== QLatin1Char('%'))
426 while (*str
== QLatin1Char(' '))
428 if (*str
== QLatin1Char(','))
431 //eat the rest of space
432 while (*str
== QLatin1Char(' '))
439 static QString
idFromUrl(const QString
&url
)
441 QString::const_iterator itr
= url
.constBegin();
442 while ((*itr
).isSpace())
444 if ((*itr
) == QLatin1Char('('))
446 while ((*itr
).isSpace())
448 if ((*itr
) == QLatin1Char('#'))
451 while ((*itr
) != QLatin1Char(')')) {
459 * returns true when successfuly set the color. false signifies
460 * that the color should be inherited
462 static bool resolveColor(const QString
&colorStr
, QColor
&color
, QSvgHandler
*handler
)
464 QString colorStrTr
= colorStr
.trimmed();
465 if (colorStr
.startsWith(QLatin1String("rgb("))) {
466 const QChar
*s
= colorStr
.constData() + 4;
467 QVector
<qreal
> compo
= parseNumbersList(s
);
468 //1 means that it failed after reaching non-parsable
469 //character which is going to be "%"
470 if (compo
.size() == 1) {
471 const QChar
*s
= colorStr
.constData() + 4;
472 compo
= parsePercentageList(s
);
473 compo
[0] *= (qreal
)2.55;
474 compo
[1] *= (qreal
)2.55;
475 compo
[2] *= (qreal
)2.55;
478 color
= QColor(int(compo
[0]),
482 } else if (colorStr
== QLatin1String("inherited") ||
483 colorStr
== QLatin1String("inherit")) {
485 } else if (colorStr
== QLatin1String("currentColor")) {
486 color
= handler
->currentColor();
490 color
= QColor(colorStrTr
);
491 return color
.isValid();
494 static bool constructColor(const QString
&colorStr
, const QString
&opacity
,
495 QColor
&color
, QSvgHandler
*handler
)
497 if (!resolveColor(colorStr
, color
, handler
))
499 if (!opacity
.isEmpty()) {
500 qreal op
= toDouble(opacity
);
503 color
.setAlpha(int(op
));
508 static qreal
parseLength(const QString
&str
, QSvgHandler::LengthType
&type
,
509 QSvgHandler
*handler
)
511 QString numStr
= str
.trimmed();
513 if (numStr
.endsWith(QLatin1Char('%'))) {
515 type
= QSvgHandler::LT_PERCENT
;
516 } else if (numStr
.endsWith(QLatin1String("px"))) {
518 type
= QSvgHandler::LT_PX
;
519 } else if (numStr
.endsWith(QLatin1String("pc"))) {
521 type
= QSvgHandler::LT_PC
;
522 } else if (numStr
.endsWith(QLatin1String("pt"))) {
524 type
= QSvgHandler::LT_PT
;
525 } else if (numStr
.endsWith(QLatin1String("mm"))) {
527 type
= QSvgHandler::LT_MM
;
528 } else if (numStr
.endsWith(QLatin1String("cm"))) {
530 type
= QSvgHandler::LT_CM
;
531 } else if (numStr
.endsWith(QLatin1String("in"))) {
533 type
= QSvgHandler::LT_IN
;
535 type
= handler
->defaultCoordinateSystem();
536 //type = QSvgHandler::LT_OTHER;
538 qreal len
= toDouble(numStr
);
539 //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
543 static inline qreal
convertToNumber(const QString
&str
, QSvgHandler
*handler
)
545 QSvgHandler::LengthType type
;
546 qreal num
= parseLength(str
, type
, handler
);
547 if (type
== QSvgHandler::LT_PERCENT
) {
553 static bool createSvgGlyph(QSvgFont
*font
, const QXmlStreamAttributes
&attributes
)
555 QStringRef uncStr
= attributes
.value(QLatin1String("unicode"));
556 QStringRef havStr
= attributes
.value(QLatin1String("horiz-adv-x"));
557 QStringRef pathStr
= attributes
.value(QLatin1String("d"));
559 QChar unicode
= (uncStr
.isEmpty()) ? 0 : uncStr
.at(0);
560 qreal havx
= (havStr
.isEmpty()) ? -1 : toDouble(havStr
);
562 path
.setFillRule(Qt::WindingFill
);
563 parsePathDataFast(pathStr
, path
);
565 font
->addGlyph(unicode
, path
, havx
);
570 // this should really be called convertToDefaultCoordinateSystem
571 // and convert when type != QSvgHandler::defaultCoordinateSystem
572 static qreal
convertToPixels(qreal len
, bool , QSvgHandler::LengthType type
)
576 case QSvgHandler::LT_PERCENT
:
578 case QSvgHandler::LT_PX
:
580 case QSvgHandler::LT_PC
:
582 case QSvgHandler::LT_PT
:
585 case QSvgHandler::LT_MM
:
586 return len
* 3.543307;
588 case QSvgHandler::LT_CM
:
589 return len
* 35.43307;
591 case QSvgHandler::LT_IN
:
594 case QSvgHandler::LT_OTHER
:
602 static void parseColor(QSvgNode
*,
603 const QSvgAttributes
&attributes
,
604 QSvgHandler
*handler
)
606 QString colorStr
= attributes
.value(QLatin1String("color")).toString();
607 QString opacity
= attributes
.value(QLatin1String("color-opacity")).toString();
609 if (constructColor(colorStr
, opacity
, color
, handler
)) {
610 handler
->pushColor(color
);
614 static QSvgStyleProperty
*styleFromUrl(QSvgNode
*node
, const QString
&url
)
616 while (node
&& (node
->type() != QSvgNode::DOC
&&
617 node
->type() != QSvgNode::G
&&
618 node
->type() != QSvgNode::DEFS
&&
619 node
->type() != QSvgNode::SWITCH
)) {
620 node
= node
->parent();
624 return static_cast<QSvgStructureNode
*>(node
)->scopeStyle(idFromUrl(url
));
627 static void parseBrush(QSvgNode
*node
,
628 const QSvgAttributes
&attributes
,
629 QSvgHandler
*handler
)
631 QString value
= attributes
.value(QLatin1String("fill")).toString();
632 QString fillRule
= attributes
.value(QLatin1String("fill-rule")).toString();
633 QString opacity
= attributes
.value(QLatin1String("fill-opacity")).toString();
634 QString myId
= someId(attributes
);
636 value
= value
.trimmed();
637 fillRule
= fillRule
.trimmed();
638 if (!value
.isEmpty() || !fillRule
.isEmpty()) {
639 Qt::FillRule f
= Qt::WindingFill
;
640 if (fillRule
== QLatin1String("evenodd"))
642 if (value
.startsWith(QLatin1String("url"))) {
643 value
= value
.remove(0, 3);
644 QSvgStyleProperty
*style
= styleFromUrl(node
, value
);
646 QSvgFillStyle
*prop
= new QSvgFillStyle(style
);
647 if (!opacity
.isEmpty())
648 prop
->setFillOpacity(toDouble(opacity
));
649 node
->appendStyleProperty(prop
, myId
);
651 qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value
)));
653 } else if (value
!= QLatin1String("none")) {
655 if (constructColor(value
, opacity
, color
, handler
)) {
656 QSvgFillStyle
*prop
= new QSvgFillStyle(QBrush(color
));
657 if (!fillRule
.isEmpty())
658 prop
->setFillRule(f
);
659 node
->appendStyleProperty(prop
, myId
);
662 QSvgFillStyle
*prop
= new QSvgFillStyle(QBrush(Qt::NoBrush
));
663 if (!fillRule
.isEmpty())
664 prop
->setFillRule(f
);
665 node
->appendStyleProperty(prop
, myId
);
670 static void parseQPen(QPen
&pen
, QSvgNode
*node
,
671 const QSvgAttributes
&attributes
,
672 QSvgHandler
*handler
)
674 QString value
= attributes
.value(QLatin1String("stroke")).toString();
675 QString dashArray
= attributes
.value(QLatin1String("stroke-dasharray")).toString();
676 QString dashOffset
= attributes
.value(QLatin1String("stroke-dashoffset")).toString();
677 QString linecap
= attributes
.value(QLatin1String("stroke-linecap")).toString();
678 QString linejoin
= attributes
.value(QLatin1String("stroke-linejoin")).toString();
679 QString miterlimit
= attributes
.value(QLatin1String("stroke-miterlimit")).toString();
680 QString opacity
= attributes
.value(QLatin1String("stroke-opacity")).toString();
681 QString width
= attributes
.value(QLatin1String("stroke-width")).toString();
682 QString myId
= someId(attributes
);
684 if (!value
.isEmpty() || !width
.isEmpty()) {
685 if (value
!= QLatin1String("none")) {
686 if (!value
.isEmpty()) {
687 if (node
&& value
.startsWith(QLatin1String("url"))) {
688 value
= value
.remove(0, 3);
689 QSvgStyleProperty
*style
= styleFromUrl(node
, value
);
691 if (style
->type() == QSvgStyleProperty::GRADIENT
) {
692 QBrush
b(*((QSvgGradientStyle
*)style
)->qgradient());
694 } else if (style
->type() == QSvgStyleProperty::SOLID_COLOR
) {
696 ((QSvgSolidColorStyle
*)style
)->qcolor());
699 qWarning()<<"QSvgHandler::parsePen could not resolve property" << idFromUrl(value
);
703 if (constructColor(value
, opacity
, color
, handler
))
706 //since we could inherit stroke="none"
707 //we need to reset the style of our stroke to something
708 pen
.setStyle(Qt::SolidLine
);
710 if (!width
.isEmpty()) {
711 QSvgHandler::LengthType lt
;
712 qreal widthF
= parseLength(width
, lt
, handler
);
715 pen
.setStyle(Qt::NoPen
);
718 pen
.setWidthF(widthF
);
720 qreal penw
= pen
.widthF();
722 if (!linejoin
.isEmpty()) {
723 if (linejoin
== QLatin1String("miter"))
724 pen
.setJoinStyle(Qt::SvgMiterJoin
);
725 else if (linejoin
== QLatin1String("round"))
726 pen
.setJoinStyle(Qt::RoundJoin
);
727 else if (linejoin
== QLatin1String("bevel"))
728 pen
.setJoinStyle(Qt::BevelJoin
);
730 if (!miterlimit
.isEmpty())
731 pen
.setMiterLimit(toDouble(miterlimit
));
733 if (!linecap
.isEmpty()) {
734 if (linecap
== QLatin1String("butt"))
735 pen
.setCapStyle(Qt::FlatCap
);
736 else if (linecap
== QLatin1String("round"))
737 pen
.setCapStyle(Qt::RoundCap
);
738 else if (linecap
== QLatin1String("square"))
739 pen
.setCapStyle(Qt::SquareCap
);
742 if (!dashArray
.isEmpty()) {
743 const QChar
*s
= dashArray
.constData();
744 QVector
<qreal
> dashes
= parseNumbersList(s
);
745 qreal
*d
= dashes
.data();
747 for (int i
= 0; i
< dashes
.size(); ++i
) {
751 pen
.setDashPattern(dashes
);
753 if (!dashOffset
.isEmpty()) {
754 pen
.setDashOffset(toDouble(dashOffset
));
758 pen
.setStyle(Qt::NoPen
);
763 static QMatrix
parseTransformationMatrix(const QString
&value
)
766 const QChar
*str
= value
.constData();
768 while (*str
!= QLatin1Char(0)) {
769 if (str
->isSpace() || *str
== QLatin1Char(',')) {
781 State state
= Matrix
;
782 if (*str
== QLatin1Char('m')) { //matrix
783 const char *ident
= "atrix";
784 for (int i
= 0; i
< 5; ++i
)
785 if (*(++str
) != QLatin1Char(ident
[i
]))
789 } else if (*str
== QLatin1Char('t')) { //translate
790 const char *ident
= "ranslate";
791 for (int i
= 0; i
< 8; ++i
)
792 if (*(++str
) != QLatin1Char(ident
[i
]))
796 } else if (*str
== QLatin1Char('r')) { //rotate
797 const char *ident
= "otate";
798 for (int i
= 0; i
< 5; ++i
)
799 if (*(++str
) != QLatin1Char(ident
[i
]))
803 } else if (*str
== QLatin1Char('s')) { //scale, skewX, skewY
805 if (*str
== QLatin1Char('c')) {
806 const char *ident
= "ale";
807 for (int i
= 0; i
< 3; ++i
)
808 if (*(++str
) != QLatin1Char(ident
[i
]))
812 } else if (*str
== QLatin1Char('k')) {
813 if (*(++str
) != QLatin1Char('e'))
815 if (*(++str
) != QLatin1Char('w'))
818 if (*str
== QLatin1Char('X'))
820 else if (*str
== QLatin1Char('Y'))
833 while (str
->isSpace())
835 if (*str
!= QLatin1Char('('))
838 QVector
<qreal
> points
= parseNumbersList(str
);
839 if (*str
!= QLatin1Char(')'))
843 if(state
== Matrix
) {
844 if(points
.count() != 6)
846 matrix
= matrix
* QMatrix(points
[0], points
[1],
847 points
[2], points
[3],
848 points
[4], points
[5]);
849 } else if (state
== Translate
) {
850 if (points
.count() == 1)
851 matrix
.translate(points
[0], 0);
852 else if (points
.count() == 2)
853 matrix
.translate(points
[0], points
[1]);
856 } else if (state
== Rotate
) {
857 if(points
.count() == 1) {
858 matrix
.rotate(points
[0]);
859 } else if (points
.count() == 3) {
860 matrix
.translate(points
[1], points
[2]);
861 matrix
.rotate(points
[0]);
862 matrix
.translate(-points
[1], -points
[2]);
866 } else if (state
== Scale
) {
867 if (points
.count() < 1 || points
.count() > 2)
869 qreal sx
= points
[0];
871 if(points
.count() == 2)
873 matrix
.scale(sx
, sy
);
874 } else if (state
== SkewX
) {
875 if (points
.count() != 1)
877 const qreal deg2rad
= qreal(0.017453292519943295769);
878 matrix
.shear(tan(points
[0]*deg2rad
), 0);
879 } else if (state
== SkewY
) {
880 if (points
.count() != 1)
882 const qreal deg2rad
= qreal(0.017453292519943295769);
883 matrix
.shear(0, tan(points
[0]*deg2rad
));
890 static void parsePen(QSvgNode
*node
,
891 const QSvgAttributes
&attributes
,
892 QSvgHandler
*handler
)
894 QString value
= attributes
.value(QLatin1String("stroke")).toString();
895 QString dashArray
= attributes
.value(QLatin1String("stroke-dasharray")).toString();
896 QString dashOffset
= attributes
.value(QLatin1String("stroke-dashoffset")).toString();
897 QString linecap
= attributes
.value(QLatin1String("stroke-linecap")).toString();
898 QString linejoin
= attributes
.value(QLatin1String("stroke-linejoin")).toString();
899 QString miterlimit
= attributes
.value(QLatin1String("stroke-miterlimit")).toString();
900 QString opacity
= attributes
.value(QLatin1String("stroke-opacity")).toString();
901 QString width
= attributes
.value(QLatin1String("stroke-width")).toString();
902 QString myId
= someId(attributes
);
904 //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
906 if (!value
.isEmpty() || !width
.isEmpty() || !linecap
.isEmpty() || !linejoin
.isEmpty()) {
907 if (value
!= QLatin1String("none")) {
908 QSvgStrokeStyle
*inherited
=
909 static_cast<QSvgStrokeStyle
*>(node
->styleProperty(
910 QSvgStyleProperty::STROKE
));
912 inherited
= static_cast<QSvgStrokeStyle
*>(node
->parent()->styleProperty(
913 QSvgStyleProperty::STROKE
));
914 QPen
pen(handler
->defaultPen());
916 pen
= inherited
->qpen();
918 if (!value
.isEmpty()) {
919 if (value
.startsWith(QLatin1String("url"))) {
920 value
= value
.remove(0, 3);
921 QSvgStyleProperty
*style
= styleFromUrl(node
, value
);
923 if (style
->type() == QSvgStyleProperty::GRADIENT
) {
924 QBrush
b(*((QSvgGradientStyle
*)style
)->qgradient());
926 } else if (style
->type() == QSvgStyleProperty::SOLID_COLOR
) {
928 ((QSvgSolidColorStyle
*)style
)->qcolor());
931 qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value
);
935 if (constructColor(value
, opacity
, color
, handler
))
938 //since we could inherit stroke="none"
939 //we need to reset the style of our stroke to something
940 pen
.setStyle(Qt::SolidLine
);
942 if (!width
.isEmpty()) {
943 QSvgHandler::LengthType lt
;
944 qreal widthF
= parseLength(width
, lt
, handler
);
947 pen
.setStyle(Qt::NoPen
);
950 pen
.setWidthF(widthF
);
953 if (!linejoin
.isEmpty()) {
954 if (linejoin
== QLatin1String("miter"))
955 pen
.setJoinStyle(Qt::SvgMiterJoin
);
956 else if (linejoin
== QLatin1String("round"))
957 pen
.setJoinStyle(Qt::RoundJoin
);
958 else if (linejoin
== QLatin1String("bevel"))
959 pen
.setJoinStyle(Qt::BevelJoin
);
962 if (!linecap
.isEmpty()) {
963 if (linecap
== QLatin1String("butt"))
964 pen
.setCapStyle(Qt::FlatCap
);
965 else if (linecap
== QLatin1String("round"))
966 pen
.setCapStyle(Qt::RoundCap
);
967 else if (linecap
== QLatin1String("square"))
968 pen
.setCapStyle(Qt::SquareCap
);
971 qreal penw
= pen
.widthF();
972 if (!dashArray
.isEmpty()) {
973 const QChar
*s
= dashArray
.constData();
974 QVector
<qreal
> dashes
= parseNumbersList(s
);
975 qreal
*d
= dashes
.data();
977 for (int i
= 0; i
< dashes
.size(); ++i
) {
982 // if the dash count is odd the dashes should be duplicated
983 if (dashes
.size() % 2 != 0)
984 dashes
<< QVector
<qreal
>(dashes
);
986 pen
.setDashPattern(dashes
);
988 if (!dashOffset
.isEmpty()) {
989 qreal doffset
= toDouble(dashOffset
);
992 pen
.setDashOffset(doffset
);
994 if (!miterlimit
.isEmpty())
995 pen
.setMiterLimit(toDouble(miterlimit
));
997 node
->appendStyleProperty(new QSvgStrokeStyle(pen
), myId
);
999 QPen
pen(handler
->defaultPen());
1000 pen
.setStyle(Qt::NoPen
);
1001 node
->appendStyleProperty(new QSvgStrokeStyle(pen
), myId
);
1007 static bool parseQBrush(const QSvgAttributes
&attributes
, QSvgNode
*node
,
1008 QBrush
&brush
, QSvgHandler
*handler
)
1010 QString value
= attributes
.value(QLatin1String("fill")).toString();
1011 QString opacity
= attributes
.value(QLatin1String("fill-opacity")).toString();
1014 if (!value
.isEmpty() || !opacity
.isEmpty()) {
1015 if (value
.startsWith(QLatin1String("url"))) {
1016 value
= value
.remove(0, 3);
1017 QSvgStyleProperty
*style
= styleFromUrl(node
, value
);
1019 switch (style
->type()) {
1020 case QSvgStyleProperty::FILL
:
1022 brush
= static_cast<QSvgFillStyle
*>(style
)->qbrush();
1025 case QSvgStyleProperty::SOLID_COLOR
:
1027 brush
= QBrush(static_cast<QSvgSolidColorStyle
*>(style
)->qcolor());
1030 case QSvgStyleProperty::GRADIENT
:
1032 brush
= QBrush(*static_cast<QSvgGradientStyle
*>(style
)->qgradient());
1036 qWarning("Cannot use property \"%s\" as brush.", qPrintable(idFromUrl(value
)));
1039 qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value
)));
1041 } else if (value
!= QLatin1String("none")) {
1042 if (constructColor(value
, opacity
, color
, handler
)) {
1043 brush
.setStyle(Qt::SolidPattern
);
1044 brush
.setColor(color
);
1047 brush
= QBrush(Qt::NoBrush
);
1054 static bool parseQFont(const QSvgAttributes
&attributes
,
1055 QFont
&font
, qreal
&fontSize
, QSvgHandler
*handler
)
1057 QString family
= attributes
.value(QLatin1String("font-family")).toString();
1058 QString size
= attributes
.value(QLatin1String("font-size")).toString();
1059 QString style
= attributes
.value(QLatin1String("font-style")).toString();
1060 QString weight
= attributes
.value(QLatin1String("font-weight")).toString();
1062 if (!family
.isEmpty() || !size
.isEmpty() ||
1063 !style
.isEmpty() || !weight
.isEmpty()) {
1065 if (!family
.isEmpty()) {
1066 font
.setFamily(family
.trimmed());
1068 if (!size
.isEmpty()) {
1069 QSvgHandler::LengthType dummy
; // should always be pixel size
1070 fontSize
= parseLength(size
, dummy
, handler
);
1073 font
.setPixelSize(qMax(1, int(fontSize
)));
1075 if (!style
.isEmpty()) {
1076 if (style
== QLatin1String("normal")) {
1077 font
.setStyle(QFont::StyleNormal
);
1078 } else if (style
== QLatin1String("italic")) {
1079 font
.setStyle(QFont::StyleItalic
);
1080 } else if (style
== QLatin1String("oblique")) {
1081 font
.setStyle(QFont::StyleOblique
);
1082 } else if (style
== QLatin1String("inherit")) {
1086 if (!weight
.isEmpty()) {
1088 int weightNum
= weight
.toInt(&ok
);
1090 switch (weightNum
) {
1093 font
.setWeight(QFont::Light
);
1097 font
.setWeight(QFont::Normal
);
1101 font
.setWeight(QFont::DemiBold
);
1105 font
.setWeight(QFont::Bold
);
1108 font
.setWeight(QFont::Black
);
1114 if (weight
== QLatin1String("normal")) {
1115 font
.setWeight(QFont::Normal
);
1116 } else if (weight
== QLatin1String("bold")) {
1117 font
.setWeight(QFont::Bold
);
1118 } else if (weight
== QLatin1String("bolder")) {
1119 font
.setWeight(QFont::DemiBold
);
1120 } else if (weight
== QLatin1String("lighter")) {
1121 font
.setWeight(QFont::Light
);
1125 // QFontInfo fi(font);
1126 // font.setPointSize(fi.pointSize());
1133 static void parseFont(QSvgNode
*node
,
1134 const QSvgAttributes
&attributes
,
1135 QSvgHandler
*handler
)
1138 font
.setPixelSize(12);
1139 qreal fontSize
= font
.pixelSize();
1141 QSvgFontStyle
*inherited
=
1142 static_cast<QSvgFontStyle
*>(node
->styleProperty(
1143 QSvgStyleProperty::FONT
));
1146 static_cast<QSvgFontStyle
*>(node
->parent()->styleProperty(
1147 QSvgStyleProperty::FONT
));
1149 font
= inherited
->qfont();
1150 fontSize
= inherited
->pointSize();
1152 if (parseQFont(attributes
, font
, fontSize
, handler
)) {
1153 QString myId
= someId(attributes
);
1154 QString anchor
= attributes
.value(QLatin1String("text-anchor")).toString();
1155 QSvgTinyDocument
*doc
= node
->document();
1156 QSvgFontStyle
*fontStyle
= 0;
1157 QString family
= (font
.family().isEmpty())?myId
:font
.family();
1158 if (!family
.isEmpty()) {
1159 QSvgFont
*svgFont
= doc
->svgFont(family
);
1161 fontStyle
= new QSvgFontStyle(svgFont
, doc
);
1162 fontStyle
->setPointSize(fontSize
);
1166 fontStyle
= new QSvgFontStyle(font
, node
->document());
1167 fontStyle
->setPointSize(fontSize
);
1169 if (!anchor
.isEmpty())
1170 fontStyle
->setTextAnchor(anchor
);
1172 node
->appendStyleProperty(fontStyle
, myId
);
1176 static void parseTransform(QSvgNode
*node
,
1177 const QSvgAttributes
&attributes
,
1180 QString value
= attributes
.value(QLatin1String("transform")).toString();
1181 QString myId
= someId(attributes
);
1182 value
= value
.trimmed();
1183 if (value
.isEmpty())
1185 QMatrix matrix
= parseTransformationMatrix(value
);
1187 if (!matrix
.isIdentity()) {
1188 node
->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix
)), myId
);
1193 static void parseVisibility(QSvgNode
*node
,
1194 const QSvgAttributes
&attributes
,
1197 QString value
= attributes
.value(QLatin1String("visibility")).toString();
1198 QSvgNode
*parent
= node
->parent();
1200 if (parent
&& (value
.isEmpty() || value
== QLatin1String("inherit")))
1201 node
->setVisible(parent
->isVisible());
1202 else if (value
== QLatin1String("hidden") || value
== QLatin1String("collapse")) {
1203 node
->setVisible(false);
1205 node
->setVisible(true);
1208 static void pathArcSegment(QPainterPath
&path
,
1210 qreal th0
, qreal th1
,
1211 qreal rx
, qreal ry
, qreal xAxisRotation
)
1214 qreal a00
, a01
, a10
, a11
;
1215 qreal x1
, y1
, x2
, y2
, x3
, y3
;
1219 sinTh
= qSin(xAxisRotation
* (Q_PI
/ 180.0));
1220 cosTh
= qCos(xAxisRotation
* (Q_PI
/ 180.0));
1227 thHalf
= 0.5 * (th1
- th0
);
1228 t
= (8.0 / 3.0) * qSin(thHalf
* 0.5) * qSin(thHalf
* 0.5) / qSin(thHalf
);
1229 x1
= xc
+ qCos(th0
) - t
* qSin(th0
);
1230 y1
= yc
+ qSin(th0
) + t
* qCos(th0
);
1231 x3
= xc
+ qCos(th1
);
1232 y3
= yc
+ qSin(th1
);
1233 x2
= x3
+ t
* qSin(th1
);
1234 y2
= y3
- t
* qCos(th1
);
1236 path
.cubicTo(a00
* x1
+ a01
* y1
, a10
* x1
+ a11
* y1
,
1237 a00
* x2
+ a01
* y2
, a10
* x2
+ a11
* y2
,
1238 a00
* x3
+ a01
* y3
, a10
* x3
+ a11
* y3
);
1241 // the arc handling code underneath is from XSVG (BSD license)
1243 * Copyright 2002 USC/Information Sciences Institute
1245 * Permission to use, copy, modify, distribute, and sell this software
1246 * and its documentation for any purpose is hereby granted without
1247 * fee, provided that the above copyright notice appear in all copies
1248 * and that both that copyright notice and this permission notice
1249 * appear in supporting documentation, and that the name of
1250 * Information Sciences Institute not be used in advertising or
1251 * publicity pertaining to distribution of the software without
1252 * specific, written prior permission. Information Sciences Institute
1253 * makes no representations about the suitability of this software for
1254 * any purpose. It is provided "as is" without express or implied
1257 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
1258 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
1259 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
1260 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1261 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
1262 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1263 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1264 * PERFORMANCE OF THIS SOFTWARE.
1267 static void pathArc(QPainterPath
&path
,
1270 qreal x_axis_rotation
,
1275 qreal curx
, qreal cury
)
1277 qreal sin_th
, cos_th
;
1278 qreal a00
, a01
, a10
, a11
;
1279 qreal x0
, y0
, x1
, y1
, xc
, yc
;
1280 qreal d
, sfactor
, sfactor_sq
;
1281 qreal th0
, th1
, th_arc
;
1283 qreal dx
, dy
, dx1
, dy1
, Pr1
, Pr2
, Px
, Py
, check
;
1288 sin_th
= qSin(x_axis_rotation
* (Q_PI
/ 180.0));
1289 cos_th
= qCos(x_axis_rotation
* (Q_PI
/ 180.0));
1291 dx
= (curx
- x
) / 2.0;
1292 dy
= (cury
- y
) / 2.0;
1293 dx1
= cos_th
* dx
+ sin_th
* dy
;
1294 dy1
= -sin_th
* dx
+ cos_th
* dy
;
1299 /* Spec : check if radii are large enough */
1300 check
= Px
/ Pr1
+ Py
/ Pr2
;
1302 rx
= rx
* qSqrt(check
);
1303 ry
= ry
* qSqrt(check
);
1310 x0
= a00
* curx
+ a01
* cury
;
1311 y0
= a10
* curx
+ a11
* cury
;
1312 x1
= a00
* x
+ a01
* y
;
1313 y1
= a10
* x
+ a11
* y
;
1314 /* (x0, y0) is current point in transformed coordinate space.
1315 (x1, y1) is new point in transformed coordinate space.
1317 The arc fits a unit-radius circle in this space.
1319 d
= (x1
- x0
) * (x1
- x0
) + (y1
- y0
) * (y1
- y0
);
1320 sfactor_sq
= 1.0 / d
- 0.25;
1321 if (sfactor_sq
< 0) sfactor_sq
= 0;
1322 sfactor
= qSqrt(sfactor_sq
);
1323 if (sweep_flag
== large_arc_flag
) sfactor
= -sfactor
;
1324 xc
= 0.5 * (x0
+ x1
) - sfactor
* (y1
- y0
);
1325 yc
= 0.5 * (y0
+ y1
) + sfactor
* (x1
- x0
);
1326 /* (xc, yc) is center of the circle. */
1328 th0
= atan2(y0
- yc
, x0
- xc
);
1329 th1
= atan2(y1
- yc
, x1
- xc
);
1332 if (th_arc
< 0 && sweep_flag
)
1334 else if (th_arc
> 0 && !sweep_flag
)
1337 n_segs
= qCeil(qAbs(th_arc
/ (Q_PI
* 0.5 + 0.001)));
1339 for (i
= 0; i
< n_segs
; i
++) {
1340 pathArcSegment(path
, xc
, yc
,
1341 th0
+ i
* th_arc
/ n_segs
,
1342 th0
+ (i
+ 1) * th_arc
/ n_segs
,
1343 rx
, ry
, x_axis_rotation
);
1347 static bool parsePathDataFast(const QStringRef
&dataStr
, QPainterPath
&path
)
1349 qreal x0
= 0, y0
= 0; // starting point
1350 qreal x
= 0, y
= 0; // current point
1353 const QChar
*str
= dataStr
.constData();
1354 const QChar
*end
= str
+ dataStr
.size();
1356 while (str
!= end
) {
1357 while (*str
== QLatin1Char(' '))
1359 QChar pathElem
= *str
;
1362 *const_cast<QChar
*>(end
) = 0; // parseNumbersList requires 0-termination that QStringRef cannot guarantee
1363 QVector
<qreal
> arg
= parseNumbersList(str
);
1364 *const_cast<QChar
*>(end
) = endc
;
1365 if (pathElem
== QLatin1Char('z') || pathElem
== QLatin1Char('Z'))
1366 arg
.append(0);//dummy
1367 while (!arg
.isEmpty()) {
1368 qreal offsetX
= x
; // correction offsets
1369 qreal offsetY
= y
; // for relative commands
1370 switch (pathElem
.unicode()) {
1372 if (arg
.count() < 2) {
1376 x
= x0
= arg
[0] + offsetX
;
1377 y
= y0
= arg
[1] + offsetY
;
1378 path
.moveTo(x0
, y0
);
1379 arg
.pop_front(); arg
.pop_front();
1383 if (arg
.count() < 2) {
1390 path
.moveTo(x0
, y0
);
1391 arg
.pop_front(); arg
.pop_front();
1398 path
.closeSubpath();
1399 arg
.pop_front();//pop dummy
1403 if (arg
.count() < 2) {
1407 x
= arg
.front() + offsetX
;
1409 y
= arg
.front() + offsetY
;
1416 if (arg
.count() < 2) {
1420 x
= arg
.front(); arg
.pop_front();
1421 y
= arg
.front(); arg
.pop_front();
1426 x
= arg
.front() + offsetX
; arg
.pop_front();
1437 y
= arg
[0] + offsetY
;
1449 if (arg
.count() < 6) {
1454 QPointF
c1(arg
[0]+offsetX
, arg
[1]+offsetY
);
1455 QPointF
c2(arg
[2]+offsetX
, arg
[3]+offsetY
);
1456 QPointF
e(arg
[4]+offsetX
, arg
[5]+offsetY
);
1457 path
.cubicTo(c1
, c2
, e
);
1461 arg
.pop_front(); arg
.pop_front();
1462 arg
.pop_front(); arg
.pop_front();
1463 arg
.pop_front(); arg
.pop_front();
1467 if (arg
.count() < 6) {
1472 QPointF
c1(arg
[0], arg
[1]);
1473 QPointF
c2(arg
[2], arg
[3]);
1474 QPointF
e(arg
[4], arg
[5]);
1475 path
.cubicTo(c1
, c2
, e
);
1479 arg
.pop_front(); arg
.pop_front();
1480 arg
.pop_front(); arg
.pop_front();
1481 arg
.pop_front(); arg
.pop_front();
1485 if (arg
.count() < 4) {
1491 if (lastMode
== 'c' || lastMode
== 'C' ||
1492 lastMode
== 's' || lastMode
== 'S')
1493 c1
= QPointF(2*x
-ctrlPt
.x(), 2*y
-ctrlPt
.y());
1496 QPointF
c2(arg
[0]+offsetX
, arg
[1]+offsetY
);
1497 QPointF
e(arg
[2]+offsetX
, arg
[3]+offsetY
);
1498 path
.cubicTo(c1
, c2
, e
);
1502 arg
.pop_front(); arg
.pop_front();
1503 arg
.pop_front(); arg
.pop_front();
1507 if (arg
.count() < 4) {
1513 if (lastMode
== 'c' || lastMode
== 'C' ||
1514 lastMode
== 's' || lastMode
== 'S')
1515 c1
= QPointF(2*x
-ctrlPt
.x(), 2*y
-ctrlPt
.y());
1518 QPointF
c2(arg
[0], arg
[1]);
1519 QPointF
e(arg
[2], arg
[3]);
1520 path
.cubicTo(c1
, c2
, e
);
1524 arg
.pop_front(); arg
.pop_front();
1525 arg
.pop_front(); arg
.pop_front();
1529 if (arg
.count() < 4) {
1534 QPointF
c(arg
[0]+offsetX
, arg
[1]+offsetY
);
1535 QPointF
e(arg
[2]+offsetX
, arg
[3]+offsetY
);
1540 arg
.pop_front(); arg
.pop_front();
1541 arg
.pop_front(); arg
.pop_front();
1545 if (arg
.count() < 4) {
1550 QPointF
c(arg
[0], arg
[1]);
1551 QPointF
e(arg
[2], arg
[3]);
1556 arg
.pop_front(); arg
.pop_front();
1557 arg
.pop_front(); arg
.pop_front();
1561 if (arg
.count() < 2) {
1566 QPointF
e(arg
[0]+offsetX
, arg
[1]+offsetY
);
1568 if (lastMode
== 'q' || lastMode
== 'Q' ||
1569 lastMode
== 't' || lastMode
== 'T')
1570 c
= QPointF(2*x
-ctrlPt
.x(), 2*y
-ctrlPt
.y());
1577 arg
.pop_front(); arg
.pop_front();
1581 if (arg
.count() < 2) {
1586 QPointF
e(arg
[0], arg
[1]);
1588 if (lastMode
== 'q' || lastMode
== 'Q' ||
1589 lastMode
== 't' || lastMode
== 'T')
1590 c
= QPointF(2*x
-ctrlPt
.x(), 2*y
-ctrlPt
.y());
1597 arg
.pop_front(); arg
.pop_front();
1601 if (arg
.count() < 7) {
1608 qreal xAxisRotation
= arg
[2];
1609 qreal largeArcFlag
= arg
[3];
1610 qreal sweepFlag
= arg
[4];
1611 qreal ex
= arg
[5] + offsetX
;
1612 qreal ey
= arg
[6] + offsetY
;
1615 pathArc(path
, rx
, ry
, xAxisRotation
, int(largeArcFlag
),
1616 int(sweepFlag
), ex
, ey
, curx
, cury
);
1621 arg
.pop_front(); arg
.pop_front();
1622 arg
.pop_front(); arg
.pop_front();
1623 arg
.pop_front(); arg
.pop_front();
1628 if (arg
.count() < 7) {
1635 qreal xAxisRotation
= arg
[2];
1636 qreal largeArcFlag
= arg
[3];
1637 qreal sweepFlag
= arg
[4];
1642 pathArc(path
, rx
, ry
, xAxisRotation
, int(largeArcFlag
),
1643 int(sweepFlag
), ex
, ey
, curx
, cury
);
1646 arg
.pop_front(); arg
.pop_front();
1647 arg
.pop_front(); arg
.pop_front();
1648 arg
.pop_front(); arg
.pop_front();
1655 lastMode
= pathElem
.toLatin1();
1661 static bool parseStyle(QSvgNode
*node
,
1662 const QXmlStreamAttributes
&attributes
,
1665 static bool parseStyle(QSvgNode
*node
,
1666 const QSvgAttributes
&attributes
,
1669 static void parseCSStoXMLAttrs(const QVector
<QCss::Declaration
> &declarations
,
1670 QXmlStreamAttributes
&attributes
)
1672 for (int i
= 0; i
< declarations
.count(); ++i
) {
1673 const QCss::Declaration
&decl
= declarations
.at(i
);
1674 if (decl
.d
->property
.isEmpty())
1676 QCss::Value val
= decl
.d
->values
.first();
1678 if (decl
.d
->values
.count() != 1) {
1679 for (int i
=0; i
<decl
.d
->values
.count(); ++i
) {
1680 const QString
&value
= decl
.d
->values
[i
].toString();
1681 if (value
.isEmpty())
1682 valueStr
+= QLatin1Char(',');
1687 valueStr
= val
.toString();
1689 if (val
.type
== QCss::Value::Uri
) {
1690 valueStr
.prepend(QLatin1String("url("));
1691 valueStr
.append(QLatin1Char(')'));
1692 } else if (val
.type
== QCss::Value::Function
) {
1693 QStringList lst
= val
.variant
.toStringList();
1694 valueStr
.append(lst
.at(0));
1695 valueStr
.append(QLatin1Char('('));
1696 for (int i
= 1; i
< lst
.count(); ++i
) {
1697 valueStr
.append(lst
.at(i
));
1698 if ((i
+1) < lst
.count())
1699 valueStr
.append(QLatin1Char(','));
1701 valueStr
.append(QLatin1Char(')'));
1702 } else if (val
.type
== QCss::Value::KnownIdentifier
) {
1703 switch (val
.variant
.toInt()) {
1704 case QCss::Value_None
:
1705 valueStr
= QLatin1String("none");
1712 attributes
.append(QString(), decl
.d
->property
, valueStr
);
1716 void QSvgHandler::parseCSStoXMLAttrs(QString css
, QVector
<QSvgCssAttribute
> *attributes
)
1718 // preprocess (for unicode escapes), tokenize and remove comments
1719 m_cssParser
.init(css
);
1722 attributes
->reserve(10);
1724 while (m_cssParser
.hasNext()) {
1725 m_cssParser
.skipSpace();
1727 if (!m_cssParser
.hasNext())
1732 if (m_cssParser
.hasEscapeSequences
) {
1733 key
= m_cssParser
.lexem();
1734 name
= QStringRef(&key
, 0, key
.length());
1736 const QCss::Symbol
&sym
= m_cssParser
.symbol();
1737 name
= QStringRef(&sym
.text
, sym
.start
, sym
.len
);
1740 m_cssParser
.skipSpace();
1741 if (!m_cssParser
.test(QCss::COLON
))
1744 m_cssParser
.skipSpace();
1745 if (!m_cssParser
.hasNext())
1748 QSvgCssAttribute attribute
;
1749 attribute
.name
= QXmlStreamStringRef(name
);
1751 const int firstSymbol
= m_cssParser
.index
;
1752 int symbolCount
= 0;
1756 } while (m_cssParser
.hasNext() && !m_cssParser
.test(QCss::SEMICOLON
));
1758 bool canExtractValueByRef
= !m_cssParser
.hasEscapeSequences
;
1759 if (canExtractValueByRef
) {
1760 int len
= m_cssParser
.symbols
.at(firstSymbol
).len
;
1761 for (int i
= firstSymbol
+ 1; i
< firstSymbol
+ symbolCount
; ++i
) {
1762 len
+= m_cssParser
.symbols
.at(i
).len
;
1764 if (m_cssParser
.symbols
.at(i
- 1).start
+ m_cssParser
.symbols
.at(i
- 1).len
1765 != m_cssParser
.symbols
.at(i
).start
) {
1766 canExtractValueByRef
= false;
1770 if (canExtractValueByRef
) {
1771 const QCss::Symbol
&sym
= m_cssParser
.symbols
.at(firstSymbol
);
1772 attribute
.value
= QXmlStreamStringRef(QStringRef(&sym
.text
, sym
.start
, len
));
1775 if (!canExtractValueByRef
) {
1777 for (int i
= firstSymbol
; i
< m_cssParser
.index
- 1; ++i
)
1778 value
+= m_cssParser
.symbols
.at(i
).lexem();
1779 attribute
.value
= QXmlStreamStringRef(QStringRef(&value
, 0, value
.length()));
1782 attributes
->append(attribute
);
1784 m_cssParser
.skipSpace();
1788 static void cssStyleLookup(QSvgNode
*node
,
1789 QSvgHandler
*handler
,
1790 QSvgStyleSelector
*selector
)
1792 QCss::StyleSelector::NodePtr cssNode
;
1794 QVector
<QCss::Declaration
> decls
= selector
->declarationsForNode(cssNode
);
1796 QXmlStreamAttributes attributes
;
1797 parseCSStoXMLAttrs(decls
, attributes
);
1798 parseStyle(node
, attributes
, handler
);
1801 static bool parseDefaultTextStyle(QSvgNode
*node
,
1802 const QXmlStreamAttributes
&attributes
,
1804 QSvgHandler
*handler
)
1806 Q_ASSERT(node
->type() == QSvgText::TEXT
|| node
->type() == QSvgNode::TEXTAREA
);
1807 QSvgText
*textNode
= static_cast<QSvgText
*>(node
);
1809 QSvgAttributes
attrs(attributes
, handler
);
1811 QString fontFamily
= attrs
.value(QString(), QLatin1String("font-family")).toString();
1813 QString anchor
= attrs
.value(QString(), QLatin1String("text-anchor")).toString();
1815 QSvgFontStyle
*fontStyle
= static_cast<QSvgFontStyle
*>(
1816 node
->styleProperty(QSvgStyleProperty::FONT
));
1818 QSvgTinyDocument
*doc
= fontStyle
->doc();
1819 if (doc
&& fontStyle
->svgFont()) {
1820 cssStyleLookup(node
, handler
, handler
->selector());
1821 parseStyle(node
, attrs
, handler
);
1824 } else if (!fontFamily
.isEmpty()) {
1825 QSvgTinyDocument
*doc
= node
->document();
1826 QSvgFont
*svgFont
= doc
->svgFont(fontFamily
);
1828 cssStyleLookup(node
, handler
, handler
->selector());
1829 parseStyle(node
, attrs
, handler
);
1834 QTextCharFormat format
;
1835 QBrush
brush(QColor(0, 0, 0));
1837 font
.setPixelSize(12);
1838 qreal fontSize
= font
.pixelSize();
1841 font
= textNode
->topFormat().font();
1842 fontSize
= font
.pixelSize() / textNode
->scale();
1843 brush
= textNode
->topFormat().foreground();
1845 QSvgFontStyle
*fontStyle
= static_cast<QSvgFontStyle
*>(
1846 node
->styleProperty(QSvgStyleProperty::FONT
));
1848 fontStyle
= static_cast<QSvgFontStyle
*>(
1849 node
->parent()->styleProperty(QSvgStyleProperty::FONT
));
1851 font
= fontStyle
->qfont();
1852 fontSize
= fontStyle
->pointSize();
1853 if (anchor
.isEmpty())
1854 anchor
= fontStyle
->textAnchor();
1857 Qt::Alignment align
= Qt::AlignLeft
;
1858 if (anchor
== QLatin1String("middle"))
1859 align
= Qt::AlignHCenter
;
1860 else if (anchor
== QLatin1String("end"))
1861 align
= Qt::AlignRight
;
1862 textNode
->setTextAlignment(align
);
1864 QSvgFillStyle
*fillStyle
= static_cast<QSvgFillStyle
*>(
1865 node
->styleProperty(QSvgStyleProperty::FILL
));
1867 brush
= fillStyle
->qbrush();
1870 if (parseQFont(attrs
, font
, fontSize
, handler
) || initial
) {
1872 textNode
->setScale(100 / fontSize
);
1873 font
.setPixelSize(qMax(1, int(fontSize
* textNode
->scale())));
1874 format
.setFont(font
);
1877 if (parseQBrush(attrs
, node
, brush
, handler
) || initial
) {
1878 if (brush
.style() != Qt::NoBrush
|| initial
)
1879 format
.setForeground(brush
);
1882 QPen
pen(Qt::NoPen
);
1883 // QSvgStrokeStyle *inherited =
1884 // static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty(
1885 // QSvgStyleProperty::STROKE));
1887 // pen = inherited->qpen();
1888 parseQPen(pen
, node
, attrs
, handler
);
1889 if (pen
.style() != Qt::NoPen
) {
1890 format
.setTextOutline(pen
);
1893 parseTransform(node
, attrs
, handler
);
1895 textNode
->insertFormat(format
);
1900 static inline QStringList
stringToList(const QString
&str
)
1902 QStringList lst
= str
.split(QLatin1Char(','), QString::SkipEmptyParts
);
1906 static bool parseCoreNode(QSvgNode
*node
,
1907 const QXmlStreamAttributes
&attributes
)
1909 QString featuresStr
= attributes
.value(QLatin1String("requiredFeatures")).toString();
1910 QString extensionsStr
= attributes
.value(QLatin1String("requiredExtensions")).toString();
1911 QString languagesStr
= attributes
.value(QLatin1String("systemLanguage")).toString();
1912 QString formatsStr
= attributes
.value(QLatin1String("requiredFormats")).toString();
1913 QString fontsStr
= attributes
.value(QLatin1String("requiredFonts")).toString();
1914 QString nodeIdStr
= someId(attributes
);
1915 QString xmlClassStr
= attributes
.value(QLatin1String("class")).toString();
1918 QStringList features
= stringToList(featuresStr
);
1919 QStringList extensions
= stringToList(extensionsStr
);
1920 QStringList languages
= stringToList(languagesStr
);
1921 QStringList formats
= stringToList(formatsStr
);
1922 QStringList fonts
= stringToList(fontsStr
);
1924 node
->setRequiredFeatures(features
);
1925 node
->setRequiredExtensions(extensions
);
1926 node
->setRequiredLanguages(languages
);
1927 node
->setRequiredFormats(formats
);
1928 node
->setRequiredFonts(fonts
);
1929 node
->setNodeId(nodeIdStr
);
1930 node
->setXmlClass(xmlClassStr
);
1935 static void parseOpacity(QSvgNode
*node
,
1936 const QSvgAttributes
&attributes
,
1939 QString value
= attributes
.value(QLatin1String("opacity")).toString();
1940 value
= value
.trimmed();
1943 qreal op
= value
.toDouble(&ok
);
1946 QSvgOpacityStyle
*opacity
= new QSvgOpacityStyle(op
);
1947 node
->appendStyleProperty(opacity
, someId(attributes
));
1951 static QPainter::CompositionMode
svgToQtCompositionMode(const QString
&op
)
1953 #define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
1954 if (op
== QLatin1String("clear")) {
1955 return QPainter::CompositionMode_Clear
;
1956 } else if (op
== QLatin1String("src")) {
1957 return QPainter::CompositionMode_Source
;
1958 } else if (op
== QLatin1String("dst")) {
1959 return QPainter::CompositionMode_Destination
;
1960 } else if (op
== QLatin1String("src-over")) {
1961 return QPainter::CompositionMode_SourceOver
;
1962 } else if (op
== QLatin1String("dst-over")) {
1963 return QPainter::CompositionMode_DestinationOver
;
1964 } else if (op
== QLatin1String("src-in")) {
1965 return QPainter::CompositionMode_SourceIn
;
1966 } else if (op
== QLatin1String("dst-in")) {
1967 return QPainter::CompositionMode_DestinationIn
;
1968 } else if (op
== QLatin1String("src-out")) {
1969 return QPainter::CompositionMode_SourceOut
;
1970 } else if (op
== QLatin1String("dst-out")) {
1971 return QPainter::CompositionMode_DestinationOut
;
1972 } else if (op
== QLatin1String("src-atop")) {
1973 return QPainter::CompositionMode_SourceAtop
;
1974 } else if (op
== QLatin1String("dst-atop")) {
1975 return QPainter::CompositionMode_DestinationAtop
;
1976 } else if (op
== QLatin1String("xor")) {
1977 return QPainter::CompositionMode_Xor
;
1978 } else if (op
== QLatin1String("plus")) {
1979 return QPainter::CompositionMode_Plus
;
1980 } else if (op
== QLatin1String("multiply")) {
1981 return QPainter::CompositionMode_Multiply
;
1982 } else if (op
== QLatin1String("screen")) {
1983 return QPainter::CompositionMode_Screen
;
1984 } else if (op
== QLatin1String("overlay")) {
1985 return QPainter::CompositionMode_Overlay
;
1986 } else if (op
== QLatin1String("darken")) {
1987 return QPainter::CompositionMode_Darken
;
1988 } else if (op
== QLatin1String("lighten")) {
1989 return QPainter::CompositionMode_Lighten
;
1990 } else if (op
== QLatin1String("color-dodge")) {
1991 return QPainter::CompositionMode_ColorDodge
;
1992 } else if (op
== QLatin1String("color-burn")) {
1993 return QPainter::CompositionMode_ColorBurn
;
1994 } else if (op
== QLatin1String("hard-light")) {
1995 return QPainter::CompositionMode_HardLight
;
1996 } else if (op
== QLatin1String("soft-light")) {
1997 return QPainter::CompositionMode_SoftLight
;
1998 } else if (op
== QLatin1String("difference")) {
1999 return QPainter::CompositionMode_Difference
;
2000 } else if (op
== QLatin1String("exclusion")) {
2001 return QPainter::CompositionMode_Exclusion
;
2006 return QPainter::CompositionMode_SourceOver
;
2009 static void parseCompOp(QSvgNode
*node
,
2010 const QSvgAttributes
&attributes
,
2013 QString value
= attributes
.value(QLatin1String("comp-op")).toString();
2014 value
= value
.trimmed();
2016 if (!value
.isEmpty()) {
2017 QSvgCompOpStyle
*compop
= new QSvgCompOpStyle(svgToQtCompositionMode(value
));
2018 node
->appendStyleProperty(compop
, someId(attributes
));
2022 static inline QSvgNode::DisplayMode
displayStringToEnum(const QString
&str
)
2024 if (str
== QLatin1String("inline")) {
2025 return QSvgNode::InlineMode
;
2026 } else if (str
== QLatin1String("block")) {
2027 return QSvgNode::BlockMode
;
2028 } else if (str
== QLatin1String("list-item")) {
2029 return QSvgNode::ListItemMode
;
2030 } else if (str
== QLatin1String("run-in")) {
2031 return QSvgNode::RunInMode
;
2032 } else if (str
== QLatin1String("compact")) {
2033 return QSvgNode::CompactMode
;
2034 } else if (str
== QLatin1String("marker")) {
2035 return QSvgNode::MarkerMode
;
2036 } else if (str
== QLatin1String("table")) {
2037 return QSvgNode::TableMode
;
2038 } else if (str
== QLatin1String("inline-table")) {
2039 return QSvgNode::InlineTableMode
;
2040 } else if (str
== QLatin1String("table-row")) {
2041 return QSvgNode::TableRowGroupMode
;
2042 } else if (str
== QLatin1String("table-header-group")) {
2043 return QSvgNode::TableHeaderGroupMode
;
2044 } else if (str
== QLatin1String("table-footer-group")) {
2045 return QSvgNode::TableFooterGroupMode
;
2046 } else if (str
== QLatin1String("table-row")) {
2047 return QSvgNode::TableRowMode
;
2048 } else if (str
== QLatin1String("table-column-group")) {
2049 return QSvgNode::TableColumnGroupMode
;
2050 } else if (str
== QLatin1String("table-column")) {
2051 return QSvgNode::TableColumnMode
;
2052 } else if (str
== QLatin1String("table-cell")) {
2053 return QSvgNode::TableCellMode
;
2054 } else if (str
== QLatin1String("table-caption")) {
2055 return QSvgNode::TableCaptionMode
;
2056 } else if (str
== QLatin1String("none")) {
2057 return QSvgNode::NoneMode
;
2058 } else if (str
== QLatin1String("inherit")) {
2059 return QSvgNode::InheritMode
;
2061 return QSvgNode::BlockMode
;
2064 static void parseOthers(QSvgNode
*node
,
2065 const QSvgAttributes
&attributes
,
2068 QString displayStr
= attributes
.value(QLatin1String("display")).toString();
2069 displayStr
= displayStr
.trimmed();
2071 if (!displayStr
.isEmpty()) {
2072 node
->setDisplayMode(displayStringToEnum(displayStr
));
2076 static bool parseStyle(QSvgNode
*node
,
2077 const QSvgAttributes
&attributes
,
2078 QSvgHandler
*handler
)
2080 parseColor(node
, attributes
, handler
);
2081 parseBrush(node
, attributes
, handler
);
2082 parsePen(node
, attributes
, handler
);
2083 parseFont(node
, attributes
, handler
);
2084 parseTransform(node
, attributes
, handler
);
2085 parseVisibility(node
, attributes
, handler
);
2086 parseOpacity(node
, attributes
, handler
);
2087 parseCompOp(node
, attributes
, handler
);
2088 parseOthers(node
, attributes
, handler
);
2090 value
= attributes
.value("audio-level");
2092 value
= attributes
.value("color-rendering");
2094 value
= attributes
.value("display-align");
2096 value
= attributes
.value("image-rendering");
2098 value
= attributes
.value("line-increment");
2100 value
= attributes
.value("pointer-events");
2102 value
= attributes
.value("shape-rendering");
2104 value
= attributes
.value("solid-color");
2106 value
= attributes
.value("solid-opacity");
2108 value
= attributes
.value("text-rendering");
2110 value
= attributes
.value("vector-effect");
2112 value
= attributes
.value("viewport-fill");
2114 value
= attributes
.value("viewport-fill-opacity");
2119 static bool parseStyle(QSvgNode
*node
,
2120 const QXmlStreamAttributes
&attrs
,
2121 QSvgHandler
*handler
)
2123 return parseStyle(node
, QSvgAttributes(attrs
, handler
), handler
);
2126 static bool parseAnchorNode(QSvgNode
*parent
,
2127 const QXmlStreamAttributes
&attributes
,
2130 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2134 static bool parseAnimateNode(QSvgNode
*parent
,
2135 const QXmlStreamAttributes
&attributes
,
2138 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2142 static bool parseAnimateColorNode(QSvgNode
*parent
,
2143 const QXmlStreamAttributes
&attributes
,
2144 QSvgHandler
*handler
)
2146 QString typeStr
= attributes
.value(QLatin1String("type")).toString();
2147 QString fromStr
= attributes
.value(QLatin1String("from")).toString();
2148 QString toStr
= attributes
.value(QLatin1String("to")).toString();
2149 QString valuesStr
= attributes
.value(QLatin1String("values")).toString();
2150 QString beginStr
= attributes
.value(QLatin1String("begin")).toString();
2151 QString durStr
= attributes
.value(QLatin1String("dur")).toString();
2152 QString targetStr
= attributes
.value(QLatin1String("attributeName")).toString();
2153 QString repeatStr
= attributes
.value(QLatin1String("repeatCount")).toString();
2154 QString fillStr
= attributes
.value(QLatin1String("fill")).toString();
2156 QList
<QColor
> colors
;
2157 if (valuesStr
.isEmpty()) {
2158 QColor startColor
, endColor
;
2159 constructColor(fromStr
, QString(), startColor
, handler
);
2160 constructColor(toStr
, QString(), endColor
, handler
);
2161 colors
.append(startColor
);
2162 colors
.append(endColor
);
2164 QStringList str
= valuesStr
.split(QLatin1Char(';'));
2165 QStringList::const_iterator itr
;
2166 for (itr
= str
.constBegin(); itr
!= str
.constEnd(); ++itr
) {
2168 constructColor(*itr
, QString(), color
, handler
);
2169 colors
.append(color
);
2174 beginStr
= beginStr
.trimmed();
2175 if (beginStr
.endsWith(QLatin1String("ms"))) {
2178 } else if (beginStr
.endsWith(QLatin1String("s"))) {
2181 durStr
= durStr
.trimmed();
2182 if (durStr
.endsWith(QLatin1String("ms"))) {
2185 } else if (durStr
.endsWith(QLatin1String("s"))) {
2188 int begin
= static_cast<int>(toDouble(beginStr
) * ms
);
2189 int end
= static_cast<int>((toDouble(durStr
) + begin
) * ms
);
2191 QSvgAnimateColor
*anim
= new QSvgAnimateColor(begin
, end
, 0);
2192 anim
->setArgs((targetStr
== QLatin1String("fill")), colors
);
2193 anim
->setFreeze(fillStr
== QLatin1String("freeze"));
2194 anim
->setRepeatCount(
2195 (repeatStr
== QLatin1String("indefinite")) ? -1 :
2196 (repeatStr
== QLatin1String("")) ? 1 : toDouble(repeatStr
));
2198 parent
->appendStyleProperty(anim
, someId(attributes
));
2199 parent
->document()->setAnimated(true);
2200 handler
->setAnimPeriod(begin
, end
);
2204 static bool parseAimateMotionNode(QSvgNode
*parent
,
2205 const QXmlStreamAttributes
&attributes
,
2208 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2212 static bool parseAnimateTransformNode(QSvgNode
*parent
,
2213 const QXmlStreamAttributes
&attributes
,
2214 QSvgHandler
*handler
)
2216 QString typeStr
= attributes
.value(QLatin1String("type")).toString();
2217 QString values
= attributes
.value(QLatin1String("values")).toString();
2218 QString beginStr
= attributes
.value(QLatin1String("begin")).toString();
2219 QString durStr
= attributes
.value(QLatin1String("dur")).toString();
2220 QString targetStr
= attributes
.value(QLatin1String("attributeName")).toString();
2221 QString repeatStr
= attributes
.value(QLatin1String("repeatCount")).toString();
2222 QString fillStr
= attributes
.value(QLatin1String("fill")).toString();
2223 QString fromStr
= attributes
.value(QLatin1String("from")).toString();
2224 QString toStr
= attributes
.value(QLatin1String("to")).toString();
2226 QVector
<qreal
> vals
;
2227 if (values
.isEmpty()) {
2228 const QChar
*s
= fromStr
.constData();
2229 QVector
<qreal
> lst
= parseNumbersList(s
);
2230 while (lst
.count() < 3)
2234 s
= toStr
.constData();
2235 lst
= parseNumbersList(s
);
2236 while (lst
.count() < 3)
2240 const QChar
*s
= values
.constData();
2241 while (s
&& *s
!= QLatin1Char(0)) {
2242 QVector
<qreal
> tmpVals
= parseNumbersList(s
);
2243 while (tmpVals
.count() < 3)
2244 tmpVals
.append(0.0);
2247 if (*s
== QLatin1Char(0))
2254 beginStr
= beginStr
.trimmed();
2255 if (beginStr
.endsWith(QLatin1String("ms"))) {
2258 } else if (beginStr
.endsWith(QLatin1String("s"))) {
2261 int begin
= static_cast<int>(toDouble(beginStr
) * ms
);
2262 durStr
= durStr
.trimmed();
2263 if (durStr
.endsWith(QLatin1String("ms"))) {
2266 } else if (durStr
.endsWith(QLatin1String("s"))) {
2270 int end
= static_cast<int>(toDouble(durStr
)*ms
) + begin
;
2272 QSvgAnimateTransform::TransformType type
= QSvgAnimateTransform::Empty
;
2273 if (typeStr
== QLatin1String("translate")) {
2274 type
= QSvgAnimateTransform::Translate
;
2275 } else if (typeStr
== QLatin1String("scale")) {
2276 type
= QSvgAnimateTransform::Scale
;
2277 } else if (typeStr
== QLatin1String("rotate")) {
2278 type
= QSvgAnimateTransform::Rotate
;
2279 } else if (typeStr
== QLatin1String("skewX")) {
2280 type
= QSvgAnimateTransform::SkewX
;
2281 } else if (typeStr
== QLatin1String("skewY")) {
2282 type
= QSvgAnimateTransform::SkewY
;
2287 QSvgAnimateTransform
*anim
= new QSvgAnimateTransform(begin
, end
, 0);
2288 anim
->setArgs(type
, vals
);
2289 anim
->setFreeze(fillStr
== QLatin1String("freeze"));
2290 anim
->setRepeatCount(
2291 (repeatStr
== QLatin1String("indefinite"))? -1 :
2292 (repeatStr
== QLatin1String(""))? 1 : toDouble(repeatStr
));
2294 parent
->appendStyleProperty(anim
, someId(attributes
));
2295 parent
->document()->setAnimated(true);
2296 handler
->setAnimPeriod(begin
, end
);
2300 static QSvgNode
* createAnimationNode(QSvgNode
*parent
,
2301 const QXmlStreamAttributes
&attributes
,
2304 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2308 static bool parseAudioNode(QSvgNode
*parent
,
2309 const QXmlStreamAttributes
&attributes
,
2312 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2316 static QSvgNode
*createCircleNode(QSvgNode
*parent
,
2317 const QXmlStreamAttributes
&attributes
,
2320 QString cx
= attributes
.value(QLatin1String("cx")).toString();
2321 QString cy
= attributes
.value(QLatin1String("cy")).toString();
2322 QString r
= attributes
.value(QLatin1String("r")).toString();
2323 qreal ncx
= toDouble(cx
);
2324 qreal ncy
= toDouble(cy
);
2325 qreal nr
= toDouble(r
);
2327 QRectF
rect(ncx
-nr
, ncy
-nr
, nr
*2, nr
*2);
2328 QSvgNode
*circle
= new QSvgCircle(parent
, rect
);
2332 static QSvgNode
*createDefsNode(QSvgNode
*parent
,
2333 const QXmlStreamAttributes
&attributes
,
2336 Q_UNUSED(attributes
);
2337 QSvgDefs
*defs
= new QSvgDefs(parent
);
2341 static bool parseDescNode(QSvgNode
*parent
,
2342 const QXmlStreamAttributes
&attributes
,
2345 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2349 static bool parseDiscardNode(QSvgNode
*parent
,
2350 const QXmlStreamAttributes
&attributes
,
2353 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2357 static QSvgNode
*createEllipseNode(QSvgNode
*parent
,
2358 const QXmlStreamAttributes
&attributes
,
2361 QString cx
= attributes
.value(QLatin1String("cx")).toString();
2362 QString cy
= attributes
.value(QLatin1String("cy")).toString();
2363 QString rx
= attributes
.value(QLatin1String("rx")).toString();
2364 QString ry
= attributes
.value(QLatin1String("ry")).toString();
2365 qreal ncx
= toDouble(cx
);
2366 qreal ncy
= toDouble(cy
);
2367 qreal nrx
= toDouble(rx
);
2368 qreal nry
= toDouble(ry
);
2370 QRectF
rect(ncx
-nrx
, ncy
-nry
, nrx
*2, nry
*2);
2371 QSvgNode
*ellipse
= new QSvgEllipse(parent
, rect
);
2375 static QSvgStyleProperty
*createFontNode(QSvgNode
*parent
,
2376 const QXmlStreamAttributes
&attributes
,
2379 QString hax
= attributes
.value(QLatin1String("horiz-adv-x")).toString();
2380 QString myId
= someId(attributes
);
2382 qreal horizAdvX
= toDouble(hax
);
2384 while (parent
&& parent
->type() != QSvgNode::DOC
) {
2385 parent
= parent
->parent();
2389 QSvgTinyDocument
*doc
= static_cast<QSvgTinyDocument
*>(parent
);
2390 QSvgFont
*font
= new QSvgFont(horizAdvX
);
2391 font
->setFamilyName(myId
);
2392 if (!font
->familyName().isEmpty()) {
2393 if (!doc
->svgFont(font
->familyName()))
2394 doc
->addSvgFont(font
);
2396 return new QSvgFontStyle(font
, doc
);
2401 static bool parseFontFaceNode(QSvgStyleProperty
*parent
,
2402 const QXmlStreamAttributes
&attributes
,
2405 if (parent
->type() != QSvgStyleProperty::FONT
) {
2409 QSvgFontStyle
*style
= static_cast<QSvgFontStyle
*>(parent
);
2410 QSvgFont
*font
= style
->svgFont();
2411 QString name
= attributes
.value(QLatin1String("font-family")).toString();
2412 QString unitsPerEmStr
= attributes
.value(QLatin1String("units-per-em")).toString();
2414 qreal unitsPerEm
= toDouble(unitsPerEmStr
);
2418 if (!name
.isEmpty())
2419 font
->setFamilyName(name
);
2420 font
->setUnitsPerEm(unitsPerEm
);
2422 if (!font
->familyName().isEmpty())
2423 if (!style
->doc()->svgFont(font
->familyName()))
2424 style
->doc()->addSvgFont(font
);
2429 static bool parseFontFaceNameNode(QSvgStyleProperty
*parent
,
2430 const QXmlStreamAttributes
&attributes
,
2433 if (parent
->type() != QSvgStyleProperty::FONT
) {
2437 QSvgFontStyle
*style
= static_cast<QSvgFontStyle
*>(parent
);
2438 QSvgFont
*font
= style
->svgFont();
2439 QString name
= attributes
.value(QLatin1String("name")).toString();
2441 if (!name
.isEmpty())
2442 font
->setFamilyName(name
);
2444 if (!font
->familyName().isEmpty())
2445 if (!style
->doc()->svgFont(font
->familyName()))
2446 style
->doc()->addSvgFont(font
);
2451 static bool parseFontFaceSrcNode(QSvgStyleProperty
*parent
,
2452 const QXmlStreamAttributes
&attributes
,
2455 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2459 static bool parseFontFaceUriNode(QSvgStyleProperty
*parent
,
2460 const QXmlStreamAttributes
&attributes
,
2463 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2467 static bool parseForeignObjectNode(QSvgNode
*parent
,
2468 const QXmlStreamAttributes
&attributes
,
2471 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2475 static QSvgNode
*createGNode(QSvgNode
*parent
,
2476 const QXmlStreamAttributes
&attributes
,
2479 Q_UNUSED(attributes
);
2480 QSvgG
*node
= new QSvgG(parent
);
2484 static bool parseGlyphNode(QSvgStyleProperty
*parent
,
2485 const QXmlStreamAttributes
&attributes
,
2488 if (parent
->type() != QSvgStyleProperty::FONT
) {
2492 QSvgFontStyle
*style
= static_cast<QSvgFontStyle
*>(parent
);
2493 QSvgFont
*font
= style
->svgFont();
2494 createSvgGlyph(font
, attributes
);
2498 static bool parseHandlerNode(QSvgNode
*parent
,
2499 const QXmlStreamAttributes
&attributes
,
2502 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2506 static bool parseHkernNode(QSvgNode
*parent
,
2507 const QXmlStreamAttributes
&attributes
,
2510 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2514 static QSvgNode
*createImageNode(QSvgNode
*parent
,
2515 const QXmlStreamAttributes
&attributes
,
2516 QSvgHandler
*handler
)
2518 QString x
= attributes
.value(QLatin1String("x")).toString();
2519 QString y
= attributes
.value(QLatin1String("y")).toString();
2520 QString width
= attributes
.value(QLatin1String("width")).toString();
2521 QString height
= attributes
.value(QLatin1String("height")).toString();
2522 QString filename
= attributes
.value(QLatin1String("xlink:href")).toString();
2523 qreal nx
= toDouble(x
);
2524 qreal ny
= toDouble(y
);
2525 QSvgHandler::LengthType type
;
2526 qreal nwidth
= parseLength(width
, type
, handler
);
2527 nwidth
= convertToPixels(nwidth
, true, type
);
2529 qreal nheight
= parseLength(height
, type
, handler
);
2530 nheight
= convertToPixels(nheight
, false, type
);
2533 filename
= filename
.trimmed();
2535 if (filename
.startsWith(QLatin1String("data"))) {
2536 int idx
= filename
.lastIndexOf(QLatin1String("base64,"));
2539 QString dataStr
= filename
.mid(idx
);
2540 QByteArray data
= QByteArray::fromBase64(dataStr
.toAscii());
2541 image
= QImage::fromData(data
);
2543 qDebug()<<"QSvgHandler::createImageNode: Unrecognized inline image format!";
2546 image
= QImage(filename
);
2548 if (image
.isNull()) {
2549 qDebug()<<"couldn't create image from "<<filename
;
2553 QSvgNode
*img
= new QSvgImage(parent
,
2562 static QSvgNode
*createLineNode(QSvgNode
*parent
,
2563 const QXmlStreamAttributes
&attributes
,
2566 QString x1
= attributes
.value(QLatin1String("x1")).toString();
2567 QString y1
= attributes
.value(QLatin1String("y1")).toString();
2568 QString x2
= attributes
.value(QLatin1String("x2")).toString();
2569 QString y2
= attributes
.value(QLatin1String("y2")).toString();
2570 qreal nx1
= toDouble(x1
);
2571 qreal ny1
= toDouble(y1
);
2572 qreal nx2
= toDouble(x2
);
2573 qreal ny2
= toDouble(y2
);
2575 QLineF
lineBounds(nx1
, ny1
, nx2
, ny2
);
2576 QSvgNode
*line
= new QSvgLine(parent
, lineBounds
);
2581 static void parseBaseGradient(QSvgNode
*node
,
2582 const QXmlStreamAttributes
&attributes
,
2583 QSvgGradientStyle
*gradProp
,
2584 QSvgHandler
*handler
)
2586 QString link
= attributes
.value(QLatin1String("xlink:href")).toString();
2587 QString trans
= attributes
.value(QLatin1String("gradientTransform")).toString();
2588 QString spread
= attributes
.value(QLatin1String("spreadMethod")).toString();
2589 QString units
= attributes
.value(QLatin1String("gradientUnits")).toString();
2592 QGradient
*grad
= gradProp
->qgradient();
2593 if (!link
.isEmpty()) {
2594 QSvgStyleProperty
*prop
= node
->styleProperty(link
);
2595 //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
2596 if (prop
&& prop
->type() == QSvgStyleProperty::GRADIENT
) {
2597 QSvgGradientStyle
*inherited
=
2598 static_cast<QSvgGradientStyle
*>(prop
);
2599 if (!inherited
->stopLink().isEmpty()) {
2600 gradProp
->setStopLink(inherited
->stopLink(), handler
->document());
2602 grad
->setStops(inherited
->qgradient()->stops());
2603 gradProp
->setGradientStopsSet(inherited
->gradientStopsSet());
2606 matrix
= inherited
->qmatrix();
2608 gradProp
->setStopLink(link
, handler
->document());
2612 if (!trans
.isEmpty()) {
2613 matrix
= parseTransformationMatrix(trans
);
2614 gradProp
->setMatrix(matrix
);
2615 } else if (!matrix
.isIdentity()) {
2616 gradProp
->setMatrix(matrix
);
2619 if (!spread
.isEmpty()) {
2620 if (spread
== QLatin1String("pad")) {
2621 grad
->setSpread(QGradient::PadSpread
);
2622 } else if (spread
== QLatin1String("reflect")) {
2623 grad
->setSpread(QGradient::ReflectSpread
);
2624 } else if (spread
== QLatin1String("repeat")) {
2625 grad
->setSpread(QGradient::RepeatSpread
);
2629 if (units
.isEmpty() || units
== QLatin1String("objectBoundingBox")) {
2630 grad
->setCoordinateMode(QGradient::ObjectBoundingMode
);
2634 static QSvgStyleProperty
*createLinearGradientNode(QSvgNode
*node
,
2635 const QXmlStreamAttributes
&attributes
,
2636 QSvgHandler
*handler
)
2638 QString x1
= attributes
.value(QLatin1String("x1")).toString();
2639 QString y1
= attributes
.value(QLatin1String("y1")).toString();
2640 QString x2
= attributes
.value(QLatin1String("x2")).toString();
2641 QString y2
= attributes
.value(QLatin1String("y2")).toString();
2649 nx1
= convertToNumber(x1
, handler
);
2651 ny1
= convertToNumber(y1
, handler
);
2653 nx2
= convertToNumber(x2
, handler
);
2655 ny2
= convertToNumber(y2
, handler
);
2657 QSvgNode
*itr
= node
;
2658 while (itr
&& itr
->type() != QSvgNode::DOC
) {
2659 itr
= itr
->parent();
2662 QLinearGradient
*grad
= new QLinearGradient(nx1
, ny1
, nx2
, ny2
);
2663 grad
->setInterpolationMode(QGradient::ComponentInterpolation
);
2664 QSvgGradientStyle
*prop
= new QSvgGradientStyle(grad
);
2665 parseBaseGradient(node
, attributes
, prop
, handler
);
2670 static bool parseMetadataNode(QSvgNode
*parent
,
2671 const QXmlStreamAttributes
&attributes
,
2674 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2678 static bool parseMissingGlyphNode(QSvgStyleProperty
*parent
,
2679 const QXmlStreamAttributes
&attributes
,
2682 if (parent
->type() != QSvgStyleProperty::FONT
) {
2686 QSvgFontStyle
*style
= static_cast<QSvgFontStyle
*>(parent
);
2687 QSvgFont
*font
= style
->svgFont();
2688 createSvgGlyph(font
, attributes
);
2692 static bool parseMpathNode(QSvgNode
*parent
,
2693 const QXmlStreamAttributes
&attributes
,
2696 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2700 static QSvgNode
*createPathNode(QSvgNode
*parent
,
2701 const QXmlStreamAttributes
&attributes
,
2704 QStringRef data
= attributes
.value(QLatin1String("d"));
2707 qpath
.setFillRule(Qt::WindingFill
);
2708 //XXX do error handling
2709 parsePathDataFast(data
, qpath
);
2711 QSvgNode
*path
= new QSvgPath(parent
, qpath
);
2715 static QSvgNode
*createPolygonNode(QSvgNode
*parent
,
2716 const QXmlStreamAttributes
&attributes
,
2719 QString pointsStr
= attributes
.value(QLatin1String("points")).toString();
2721 //same QPolygon parsing is in createPolylineNode
2722 const QChar
*s
= pointsStr
.constData();
2723 QVector
<qreal
> points
= parseNumbersList(s
);
2724 QPolygonF
poly(points
.count()/2);
2726 QVector
<qreal
>::const_iterator itr
= points
.constBegin();
2727 while (itr
!= points
.constEnd()) {
2728 qreal one
= *itr
; ++itr
;
2729 qreal two
= *itr
; ++itr
;
2730 poly
[i
] = QPointF(one
, two
);
2733 QSvgNode
*polygon
= new QSvgPolygon(parent
, poly
);
2737 static QSvgNode
*createPolylineNode(QSvgNode
*parent
,
2738 const QXmlStreamAttributes
&attributes
,
2741 QString pointsStr
= attributes
.value(QLatin1String("points")).toString();
2743 //same QPolygon parsing is in createPolygonNode
2744 const QChar
*s
= pointsStr
.constData();
2745 QVector
<qreal
> points
= parseNumbersList(s
);
2746 QPolygonF
poly(points
.count()/2);
2748 QVector
<qreal
>::const_iterator itr
= points
.constBegin();
2749 while (itr
!= points
.constEnd()) {
2750 qreal one
= *itr
; ++itr
;
2751 qreal two
= *itr
; ++itr
;
2752 poly
[i
] = QPointF(one
, two
);
2756 QSvgNode
*line
= new QSvgPolyline(parent
, poly
);
2760 static bool parsePrefetchNode(QSvgNode
*parent
,
2761 const QXmlStreamAttributes
&attributes
,
2764 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2768 static QSvgStyleProperty
*createRadialGradientNode(QSvgNode
*node
,
2769 const QXmlStreamAttributes
&attributes
,
2770 QSvgHandler
*handler
)
2772 QString cx
= attributes
.value(QLatin1String("cx")).toString();
2773 QString cy
= attributes
.value(QLatin1String("cy")).toString();
2774 QString r
= attributes
.value(QLatin1String("r")).toString();
2775 QString fx
= attributes
.value(QLatin1String("fx")).toString();
2776 QString fy
= attributes
.value(QLatin1String("fy")).toString();
2795 QRadialGradient
*grad
= new QRadialGradient(ncx
, ncy
, nr
, nfx
, nfy
);
2796 grad
->setInterpolationMode(QGradient::ComponentInterpolation
);
2798 QSvgGradientStyle
*prop
= new QSvgGradientStyle(grad
);
2799 parseBaseGradient(node
, attributes
, prop
, handler
);
2804 static QSvgNode
*createRectNode(QSvgNode
*parent
,
2805 const QXmlStreamAttributes
&attributes
,
2806 QSvgHandler
*handler
)
2808 QString x
= attributes
.value(QLatin1String("x")).toString();
2809 QString y
= attributes
.value(QLatin1String("y")).toString();
2810 QString width
= attributes
.value(QLatin1String("width")).toString();
2811 QString height
= attributes
.value(QLatin1String("height")).toString();
2812 QString rx
= attributes
.value(QLatin1String("rx")).toString();
2813 QString ry
= attributes
.value(QLatin1String("ry")).toString();
2815 QSvgHandler::LengthType type
;
2816 qreal nwidth
= parseLength(width
, type
, handler
);
2817 nwidth
= convertToPixels(nwidth
, true, type
);
2819 qreal nheight
= parseLength(height
, type
, handler
);
2820 nheight
= convertToPixels(nheight
, true, type
);
2821 qreal nrx
= toDouble(rx
);
2822 qreal nry
= toDouble(ry
);
2824 QRectF
bounds(toDouble(x
), toDouble(y
),
2827 //9.2 The 'rect' element clearly specifies it
2828 // but the case might in fact be handled because
2829 // we draw rounded rectangles differently
2830 if (nrx
> bounds
.width()/2)
2831 nrx
= bounds
.width()/2;
2832 if (nry
> bounds
.height()/2)
2833 nry
= bounds
.height()/2;
2837 else if (nry
&& !nrx
)
2840 //we draw rounded rect from 0...99
2841 //svg from 0...bounds.width()/2 so we're adjusting the
2843 nrx
*= (100/(bounds
.width()/2));
2844 nry
*= (100/(bounds
.height()/2));
2846 QSvgNode
*rect
= new QSvgRect(parent
, bounds
,
2852 static bool parseScriptNode(QSvgNode
*parent
,
2853 const QXmlStreamAttributes
&attributes
,
2856 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2860 static bool parseSetNode(QSvgNode
*parent
,
2861 const QXmlStreamAttributes
&attributes
,
2864 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2868 static QSvgStyleProperty
*createSolidColorNode(QSvgNode
*parent
,
2869 const QXmlStreamAttributes
&attributes
,
2870 QSvgHandler
*handler
)
2872 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2873 QString solidColorStr
= attributes
.value(QLatin1String("solid-color")).toString();
2874 QString solidOpacityStr
= attributes
.value(QLatin1String("solid-opacity")).toString();
2876 if (solidOpacityStr
.isEmpty())
2877 solidOpacityStr
= attributes
.value(QLatin1String("opacity")).toString();
2880 if (!constructColor(solidColorStr
, solidOpacityStr
, color
, handler
))
2882 QSvgSolidColorStyle
*style
= new QSvgSolidColorStyle(color
);
2886 static bool parseStopNode(QSvgStyleProperty
*parent
,
2887 const QXmlStreamAttributes
&attributes
,
2888 QSvgHandler
*handler
)
2890 if (parent
->type() != QSvgStyleProperty::GRADIENT
)
2892 QString nodeIdStr
= someId(attributes
);
2893 QString xmlClassStr
= attributes
.value(QLatin1String("class")).toString();
2895 //### nasty hack because stop gradients are not in the rendering tree
2896 // we force a dummy node with the same id and class into a rendering
2897 // tree to figure out whether the selector has a style for it
2898 // QSvgStyleSelector should be coded in a way that could avoid it
2900 anim
.setNodeId(nodeIdStr
);
2901 anim
.setXmlClass(xmlClassStr
);
2903 QCss::StyleSelector::NodePtr cssNode
;
2904 cssNode
.ptr
= &anim
;
2905 QVector
<QCss::Declaration
> decls
= handler
->selector()->declarationsForNode(cssNode
);
2907 QSvgAttributes
attrs(attributes
, handler
);
2909 for (int i
= 0; i
< decls
.count(); ++i
) {
2910 const QCss::Declaration
&decl
= decls
.at(i
);
2912 if (decl
.d
->property
.isEmpty())
2914 if (decl
.d
->values
.count() != 1)
2916 QCss::Value val
= decl
.d
->values
.first();
2917 QString valueStr
= val
.toString();
2918 if (val
.type
== QCss::Value::Uri
) {
2919 valueStr
.prepend(QLatin1String("url("));
2920 valueStr
.append(QLatin1Char(')'));
2922 attrs
.m_xmlAttributes
.append(QString(), decl
.d
->property
, valueStr
);
2925 QSvgGradientStyle
*style
=
2926 static_cast<QSvgGradientStyle
*>(parent
);
2927 QString offsetStr
= attrs
.value(QString(), QLatin1String("offset")).toString();
2928 QString colorStr
= attrs
.value(QString(), QLatin1String("stop-color")).toString();
2929 QString opacityStr
= attrs
.value(QString(), QLatin1String("stop-opacity")).toString();
2931 qreal offset
= convertToNumber(offsetStr
, handler
);
2932 if (colorStr
.isEmpty()) {
2933 colorStr
= QLatin1String("#000000");
2936 bool colorOK
= constructColor(colorStr
, opacityStr
, color
, handler
);
2938 QGradient
*grad
= style
->qgradient();
2940 offset
= qMin(qreal(1), qMax(qreal(0), offset
)); // Clamp to range [0, 1]
2941 QGradientStops stops
;
2942 if (style
->gradientStopsSet()) {
2943 stops
= grad
->stops();
2944 // If the stop offset equals the one previously added, add an epsilon to make it greater.
2945 if (offset
<= stops
.back().first
)
2946 offset
= stops
.back().first
+ FLT_EPSILON
;
2949 // If offset is greater than one, it must be clamped to one.
2951 if ((stops
.size() == 1) || (stops
.at(stops
.size() - 2).first
< 1.0 - FLT_EPSILON
)) {
2952 stops
.back().first
= 1.0 - FLT_EPSILON
;
2953 grad
->setStops(stops
);
2958 grad
->setColorAt(offset
, color
);
2959 style
->setGradientStopsSet(true);
2961 style
->addResolve(offset
);
2965 static bool parseStyleNode(QSvgNode
*parent
,
2966 const QXmlStreamAttributes
&attributes
,
2967 QSvgHandler
*handler
)
2970 QString type
= attributes
.value(QLatin1String("type")).toString();
2971 type
= type
.toLower();
2973 if (type
== QLatin1String("text/css")) {
2974 handler
->setInStyle(true);
2980 static QSvgNode
*createSvgNode(QSvgNode
*parent
,
2981 const QXmlStreamAttributes
&attributes
,
2982 QSvgHandler
*handler
)
2984 Q_UNUSED(parent
); Q_UNUSED(attributes
);
2986 QString baseProfile
= attributes
.value(QLatin1String("baseProfile")).toString();
2988 if (baseProfile
.isEmpty() && baseProfile
!= QLatin1String("tiny")) {
2989 qWarning("Profile is %s while we only support tiny!",
2990 qPrintable(baseProfile
));
2994 QSvgTinyDocument
*node
= new QSvgTinyDocument();
2995 QString widthStr
= attributes
.value(QLatin1String("width")).toString();
2996 QString heightStr
= attributes
.value(QLatin1String("height")).toString();
2997 QString viewBoxStr
= attributes
.value(QLatin1String("viewBox")).toString();
2999 QSvgHandler::LengthType type
= QSvgHandler::LT_PX
; // FIXME: is the default correct?
3001 if (!widthStr
.isEmpty()) {
3002 width
= parseLength(widthStr
, type
, handler
);
3003 if (type
!= QSvgHandler::LT_PT
)
3004 width
= convertToPixels(width
, true, type
);
3005 node
->setWidth(int(width
), type
== QSvgHandler::LT_PERCENT
);
3008 if (!heightStr
.isEmpty()) {
3009 height
= parseLength(heightStr
, type
, handler
);
3010 if (type
!= QSvgHandler::LT_PT
)
3011 height
= convertToPixels(height
, false, type
);
3012 node
->setHeight(int(height
), type
== QSvgHandler::LT_PERCENT
);
3015 QStringList viewBoxValues
;
3016 if (!viewBoxStr
.isEmpty()) {
3017 viewBoxStr
= viewBoxStr
.replace(QLatin1Char(' '), QLatin1Char(','));
3018 viewBoxValues
= viewBoxStr
.split(QLatin1Char(','), QString::SkipEmptyParts
);
3020 if (viewBoxValues
.count() == 4) {
3021 QString xStr
= viewBoxValues
.at(0).trimmed();
3022 QString yStr
= viewBoxValues
.at(1).trimmed();
3023 QString widthStr
= viewBoxValues
.at(2).trimmed();
3024 QString heightStr
= viewBoxValues
.at(3).trimmed();
3026 QSvgHandler::LengthType lt
;
3027 qreal x
= parseLength(xStr
, lt
, handler
);
3028 qreal y
= parseLength(yStr
, lt
, handler
);
3029 qreal w
= parseLength(widthStr
, lt
, handler
);
3030 qreal h
= parseLength(heightStr
, lt
, handler
);
3032 node
->setViewBox(QRectF(x
, y
, w
, h
));
3033 } else if (width
&& height
){
3034 if (type
== QSvgHandler::LT_PT
) {
3035 width
= convertToPixels(width
, false, type
);
3036 height
= convertToPixels(height
, false, type
);
3039 node
->setViewBox(QRectF(0, 0, width
, height
));
3042 handler
->setDefaultCoordinateSystem(QSvgHandler::LT_PX
);
3047 static QSvgNode
*createSwitchNode(QSvgNode
*parent
,
3048 const QXmlStreamAttributes
&attributes
,
3051 Q_UNUSED(attributes
);
3052 QSvgSwitch
*node
= new QSvgSwitch(parent
);
3056 static bool parseTbreakNode(QSvgNode
*parent
,
3057 const QXmlStreamAttributes
&,
3060 if (parent
->type() != QSvgNode::TEXTAREA
)
3062 static_cast<QSvgText
*>(parent
)->insertLineBreak();
3066 static QSvgNode
*createTextNode(QSvgNode
*parent
,
3067 const QXmlStreamAttributes
&attributes
,
3068 QSvgHandler
*handler
)
3070 QString x
= attributes
.value(QLatin1String("x")).toString();
3071 QString y
= attributes
.value(QLatin1String("y")).toString();
3072 //### editable and rotate not handled
3073 QSvgHandler::LengthType type
;
3074 qreal nx
= parseLength(x
, type
, handler
);
3075 qreal ny
= parseLength(y
, type
, handler
);
3077 //### not to pixels but to the default coordinate system
3078 // and text should be already in the correct coordinate
3080 //nx = convertToPixels(nx, true, type);
3081 //ny = convertToPixels(ny, true, type);
3083 QSvgNode
*text
= new QSvgText(parent
, QPointF(nx
, ny
));
3087 static QSvgNode
*createTextAreaNode(QSvgNode
*parent
,
3088 const QXmlStreamAttributes
&attributes
,
3089 QSvgHandler
*handler
)
3091 QSvgText
*node
= static_cast<QSvgText
*>(createTextNode(parent
, attributes
, handler
));
3093 QSvgHandler::LengthType type
;
3094 qreal width
= parseLength(attributes
.value(QLatin1String("width")).toString(), type
, handler
);
3095 qreal height
= parseLength(attributes
.value(QLatin1String("height")).toString(), type
, handler
);
3096 node
->setTextArea(QSizeF(width
, height
));
3101 static bool parseTitleNode(QSvgNode
*parent
,
3102 const QXmlStreamAttributes
&attributes
,
3105 Q_UNUSED(parent
); Q_UNUSED(attributes
);
3109 static bool parseTspanNode(QSvgNode
*parent
,
3110 const QXmlStreamAttributes
&attributes
,
3111 QSvgHandler
*handler
)
3114 cssStyleLookup(parent
, handler
, handler
->selector());
3115 return parseDefaultTextStyle(parent
, attributes
, false, handler
);
3118 static QSvgNode
*createUseNode(QSvgNode
*parent
,
3119 const QXmlStreamAttributes
&attributes
,
3120 QSvgHandler
*handler
)
3122 QString linkId
= attributes
.value(QLatin1String("xlink:href")).toString().remove(0, 1);
3123 QString xStr
= attributes
.value(QLatin1String("x")).toString();
3124 QString yStr
= attributes
.value(QLatin1String("y")).toString();
3125 QSvgStructureNode
*group
= 0;
3127 if (linkId
.isEmpty())
3128 linkId
= attributes
.value(QLatin1String("href")).toString().remove(0, 1);
3129 switch (parent
->type()) {
3131 case QSvgNode::DEFS
:
3133 case QSvgNode::SWITCH
:
3134 group
= static_cast<QSvgStructureNode
*>(parent
);
3141 QSvgNode
*link
= group
->scopeNode(linkId
);
3144 if (!xStr
.isNull() || !yStr
.isNull()) {
3145 QSvgHandler::LengthType type
;
3146 qreal nx
= parseLength(xStr
, type
, handler
);
3147 nx
= convertToPixels(nx
, true, type
);
3149 qreal ny
= parseLength(yStr
, type
, handler
);
3150 ny
= convertToPixels(ny
, true, type
);
3151 pt
= QPointF(nx
, ny
);
3154 //delay link resolving till the first draw call on
3155 //use nodes, link 2might have not been created yet
3156 QSvgUse
*node
= new QSvgUse(pt
, parent
, link
);
3161 qWarning("link %s hasn't been detected!", qPrintable(linkId
));
3165 static QSvgNode
*createVideoNode(QSvgNode
*parent
,
3166 const QXmlStreamAttributes
&attributes
,
3169 Q_UNUSED(parent
); Q_UNUSED(attributes
);
3173 typedef QSvgNode
*(*FactoryMethod
)(QSvgNode
*, const QXmlStreamAttributes
&, QSvgHandler
*);
3175 static FactoryMethod
findGroupFactory(const QString
&name
)
3180 QStringRef
ref(&name
, 1, name
.length() - 1);
3181 switch (name
.at(0).unicode()) {
3183 if (ref
== QLatin1String("efs")) return createDefsNode
;
3186 if (ref
.isEmpty()) return createGNode
;
3189 if (ref
== QLatin1String("vg")) return createSvgNode
;
3190 if (ref
== QLatin1String("witch")) return createSwitchNode
;
3198 static FactoryMethod
findGraphicsFactory(const QString
&name
)
3203 QStringRef
ref(&name
, 1, name
.length() - 1);
3204 switch (name
.at(0).unicode()) {
3206 if (ref
== QLatin1String("nimation")) return createAnimationNode
;
3209 if (ref
== QLatin1String("ircle")) return createCircleNode
;
3212 if (ref
== QLatin1String("llipse")) return createEllipseNode
;
3215 if (ref
== QLatin1String("mage")) return createImageNode
;
3218 if (ref
== QLatin1String("ine")) return createLineNode
;
3221 if (ref
== QLatin1String("ath")) return createPathNode
;
3222 if (ref
== QLatin1String("olygon")) return createPolygonNode
;
3223 if (ref
== QLatin1String("olyline")) return createPolylineNode
;
3226 if (ref
== QLatin1String("ect")) return createRectNode
;
3229 if (ref
== QLatin1String("ext")) return createTextNode
;
3230 if (ref
== QLatin1String("extArea")) return createTextAreaNode
;
3233 if (ref
== QLatin1String("se")) return createUseNode
;
3236 if (ref
== QLatin1String("ideo")) return createVideoNode
;
3244 typedef bool (*ParseMethod
)(QSvgNode
*, const QXmlStreamAttributes
&, QSvgHandler
*);
3246 static ParseMethod
findUtilFactory(const QString
&name
)
3251 QStringRef
ref(&name
, 1, name
.length() - 1);
3252 switch (name
.at(0).unicode()) {
3254 if (ref
.isEmpty()) return parseAnchorNode
;
3255 if (ref
== QLatin1String("nimate")) return parseAnimateNode
;
3256 if (ref
== QLatin1String("nimateColor")) return parseAnimateColorNode
;
3257 if (ref
== QLatin1String("nimateMotion")) return parseAimateMotionNode
;
3258 if (ref
== QLatin1String("nimateTransform")) return parseAnimateTransformNode
;
3259 if (ref
== QLatin1String("udio")) return parseAudioNode
;
3262 if (ref
== QLatin1String("esc")) return parseDescNode
;
3263 if (ref
== QLatin1String("iscard")) return parseDiscardNode
;
3266 if (ref
== QLatin1String("oreignObject")) return parseForeignObjectNode
;
3269 if (ref
== QLatin1String("andler")) return parseHandlerNode
;
3270 if (ref
== QLatin1String("kern")) return parseHkernNode
;
3273 if (ref
== QLatin1String("etadata")) return parseMetadataNode
;
3274 if (ref
== QLatin1String("path")) return parseMpathNode
;
3277 if (ref
== QLatin1String("refetch")) return parsePrefetchNode
;
3280 if (ref
== QLatin1String("cript")) return parseScriptNode
;
3281 if (ref
== QLatin1String("et")) return parseSetNode
;
3282 if (ref
== QLatin1String("tyle")) return parseStyleNode
;
3285 if (ref
== QLatin1String("break")) return parseTbreakNode
;
3286 if (ref
== QLatin1String("itle")) return parseTitleNode
;
3287 if (ref
== QLatin1String("span")) return parseTspanNode
;
3295 typedef QSvgStyleProperty
*(*StyleFactoryMethod
)(QSvgNode
*,
3296 const QXmlStreamAttributes
&,
3299 static StyleFactoryMethod
findStyleFactoryMethod(const QString
&name
)
3304 QStringRef
ref(&name
, 1, name
.length() - 1);
3305 switch (name
.at(0).unicode()) {
3307 if (ref
== QLatin1String("ont")) return createFontNode
;
3310 if (ref
== QLatin1String("inearGradient")) return createLinearGradientNode
;
3313 if (ref
== QLatin1String("adialGradient")) return createRadialGradientNode
;
3316 if (ref
== QLatin1String("olidColor")) return createSolidColorNode
;
3324 typedef bool (*StyleParseMethod
)(QSvgStyleProperty
*,
3325 const QXmlStreamAttributes
&,
3328 static StyleParseMethod
findStyleUtilFactoryMethod(const QString
&name
)
3333 QStringRef
ref(&name
, 1, name
.length() - 1);
3334 switch (name
.at(0).unicode()) {
3336 if (ref
== QLatin1String("ont-face")) return parseFontFaceNode
;
3337 if (ref
== QLatin1String("ont-face-name")) return parseFontFaceNameNode
;
3338 if (ref
== QLatin1String("ont-face-src")) return parseFontFaceSrcNode
;
3339 if (ref
== QLatin1String("ont-face-uri")) return parseFontFaceUriNode
;
3342 if (ref
== QLatin1String("lyph")) return parseGlyphNode
;
3345 if (ref
== QLatin1String("issing-glyph")) return parseMissingGlyphNode
;
3348 if (ref
== QLatin1String("top")) return parseStopNode
;
3356 QSvgHandler::QSvgHandler(QIODevice
*device
) : xml(new QXmlStreamReader(device
))
3357 , m_ownsReader(true)
3362 QSvgHandler::QSvgHandler(const QByteArray
&data
) : xml(new QXmlStreamReader(data
))
3363 , m_ownsReader(true)
3368 QSvgHandler::QSvgHandler(QXmlStreamReader
*const reader
) : xml(reader
)
3369 , m_ownsReader(false)
3374 void QSvgHandler::init()
3379 m_defaultCoords
= LT_PX
;
3380 m_defaultPen
= QPen(Qt::black
, 1, Qt::NoPen
, Qt::FlatCap
, Qt::SvgMiterJoin
);
3381 m_defaultPen
.setMiterLimit(4);
3385 void QSvgHandler::parse()
3387 xml
->setNamespaceProcessing(false);
3388 m_selector
= new QSvgStyleSelector
;
3391 while (!xml
->atEnd() && !done
) {
3392 switch (xml
->readNext()) {
3393 case QXmlStreamReader::StartElement
:
3394 // he we could/should verify the namespaces, and simply
3395 // call m_skipNodes(Unknown) if we don't know the
3396 // namespace. We do support http://www.w3.org/2000/svg
3397 // but also http://www.w3.org/2000/svg-20000303-stylable
3398 // And if the document uses an external dtd, the reported
3399 // namespaceUri is empty. The only possible strategy at
3400 // this point is to do what everyone else seems to do and
3401 // ignore the reported namespaceUri completely.
3402 startElement(xml
->name().toString(), xml
->attributes());
3404 case QXmlStreamReader::EndElement
:
3405 endElement(xml
->name());
3406 // if we are using somebody else's qxmlstreamreader
3407 // we should not read until the end of the stream
3408 done
= !m_ownsReader
&& (xml
->name() == QLatin1String("svg"));
3410 case QXmlStreamReader::Characters
:
3411 characters(xml
->text());
3413 case QXmlStreamReader::ProcessingInstruction
:
3414 processingInstruction(xml
->processingInstructionTarget().toString(), xml
->processingInstructionData().toString());
3422 bool QSvgHandler::startElement(const QString
&localName
,
3423 const QXmlStreamAttributes
&attributes
)
3427 if (m_colorTagCount
.count()) {
3428 int top
= m_colorTagCount
.pop();
3430 m_colorTagCount
.push(top
);
3433 /* The xml:space attribute may appear on any element. We do
3434 * a lookup by the qualified name here, but this is namespace aware, since
3435 * the XML namespace can only be bound to prefix "xml." */
3436 const QStringRef
xmlSpace(attributes
.value(QLatin1String("xml:space")));
3437 if(xmlSpace
.isNull())
3439 // This element has no xml:space attribute.
3440 m_whitespaceMode
.push(QSvgText::Default
);
3442 else if(xmlSpace
== QLatin1String("preserve"))
3443 m_whitespaceMode
.push(QSvgText::Preserve
);
3444 else if(xmlSpace
== QLatin1String("default"))
3445 m_whitespaceMode
.push(QSvgText::Default
);
3448 qWarning() << QString::fromLatin1("\"%1\" is an invalid value for attribute xml:space. "
3449 "Valid values are \"preserve\" and \"default\".").arg(xmlSpace
.toString());
3451 m_whitespaceMode
.push(QSvgText::Default
);
3454 if (FactoryMethod method
= findGroupFactory(localName
)) {
3456 node
= method(m_doc
? m_nodes
.top() : 0, attributes
, this);
3459 Q_ASSERT(node
->type() == QSvgNode::DOC
);
3460 m_doc
= static_cast<QSvgTinyDocument
*>(node
);
3462 switch (m_nodes
.top()->type()) {
3465 case QSvgNode::DEFS
:
3466 case QSvgNode::SWITCH
:
3468 QSvgStructureNode
*group
=
3469 static_cast<QSvgStructureNode
*>(m_nodes
.top());
3470 group
->addChild(node
, someId(attributes
));
3477 parseCoreNode(node
, attributes
);
3478 cssStyleLookup(node
, this, m_selector
);
3479 parseStyle(node
, attributes
, this);
3480 } else if (FactoryMethod method
= findGraphicsFactory(localName
)) {
3482 Q_ASSERT(!m_nodes
.isEmpty());
3483 node
= method(m_nodes
.top(), attributes
, this);
3485 switch (m_nodes
.top()->type()) {
3488 case QSvgNode::DEFS
:
3489 case QSvgNode::SWITCH
:
3491 QSvgStructureNode
*group
=
3492 static_cast<QSvgStructureNode
*>(m_nodes
.top());
3493 group
->addChild(node
, someId(attributes
));
3497 Q_ASSERT(!"not a grouping element is the parent");
3500 parseCoreNode(node
, attributes
);
3501 cssStyleLookup(node
, this, m_selector
);
3502 if (node
->type() != QSvgNode::TEXT
&& node
->type() != QSvgNode::TEXTAREA
)
3503 parseStyle(node
, attributes
, this);
3505 parseDefaultTextStyle(node
, attributes
, true, this);
3507 } else if (ParseMethod method
= findUtilFactory(localName
)) {
3508 Q_ASSERT(!m_nodes
.isEmpty());
3509 if (!method(m_nodes
.top(), attributes
, this)) {
3510 qWarning("Problem parsing %s", qPrintable(localName
));
3512 } else if (StyleFactoryMethod method
= findStyleFactoryMethod(localName
)) {
3513 QSvgStyleProperty
*prop
= method(m_nodes
.top(), attributes
, this);
3516 m_nodes
.top()->appendStyleProperty(prop
, someId(attributes
), true);
3518 qWarning("Couldn't parse node: %s", qPrintable(localName
));
3520 } else if (StyleParseMethod method
= findStyleUtilFactoryMethod(localName
)) {
3522 if (!method(m_style
, attributes
, this)) {
3523 qWarning("Problem parsing %s", qPrintable(localName
));
3527 //qWarning()<<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
3528 m_skipNodes
.push(Unknown
);
3534 m_skipNodes
.push(Graphics
);
3536 //qDebug()<<"Skipping "<<localName;
3537 m_skipNodes
.push(Style
);
3542 bool QSvgHandler::endElement(const QStringRef
&localName
)
3544 CurrentNode node
= m_skipNodes
.top();
3546 m_whitespaceMode
.pop();
3548 if (m_colorTagCount
.count()) {
3549 int top
= m_colorTagCount
.pop();
3554 m_colorTagCount
.push(top
);
3558 if (node
== Unknown
) {
3562 if (m_inStyle
&& localName
== QLatin1String("style")) {
3564 } else if (m_nodes
.top()->type() == QSvgNode::TEXT
|| m_nodes
.top()->type() == QSvgNode::TEXTAREA
) {
3565 QSvgText
*node
= static_cast<QSvgText
*>(m_nodes
.top());
3566 if (localName
== QLatin1String("tspan"))
3570 if (node
== Graphics
)
3572 else if (m_style
&& !m_skipNodes
.isEmpty() && m_skipNodes
.top() != Style
)
3578 bool QSvgHandler::characters(const QStringRef
&str
)
3581 QString css
= str
.toString();
3582 QCss::StyleSheet sheet
;
3583 QCss::Parser(css
).parse(&sheet
);
3584 m_selector
->styleSheets
.append(sheet
);
3586 } else if (m_skipNodes
.isEmpty() || m_skipNodes
.top() == Unknown
)
3589 if (m_nodes
.top()->type() == QSvgNode::TEXT
|| m_nodes
.top()->type() == QSvgNode::TEXTAREA
) {
3590 QSvgText
*node
= static_cast<QSvgText
*>(m_nodes
.top());
3591 node
->insertText(str
.toString(), m_whitespaceMode
.top());
3597 QSvgTinyDocument
* QSvgHandler::document() const
3602 QSvgHandler::LengthType
QSvgHandler::defaultCoordinateSystem() const
3604 return m_defaultCoords
;
3607 void QSvgHandler::setDefaultCoordinateSystem(LengthType type
)
3609 m_defaultCoords
= type
;
3612 void QSvgHandler::pushColor(const QColor
&color
)
3614 m_colorStack
.push(color
);
3615 m_colorTagCount
.push(1);
3618 QColor
QSvgHandler::currentColor() const
3620 if (!m_colorStack
.isEmpty())
3621 return m_colorStack
.top();
3623 return QColor(0, 0, 0);
3626 void QSvgHandler::setInStyle(bool b
)
3631 bool QSvgHandler::inStyle() const
3636 QSvgStyleSelector
* QSvgHandler::selector() const
3641 bool QSvgHandler::processingInstruction(const QString
&target
, const QString
&data
)
3643 if (target
== QLatin1String("xml-stylesheet")) {
3644 QRegExp
rx(QLatin1String("type=\\\"(.+)\\\""));
3645 rx
.setMinimal(true);
3648 while ((pos
= rx
.indexIn(data
, pos
)) != -1) {
3649 QString type
= rx
.cap(1);
3650 if (type
.toLower() == QLatin1String("text/css")) {
3653 pos
+= rx
.matchedLength();
3657 QRegExp
rx(QLatin1String("href=\\\"(.+)\\\""));
3658 rx
.setMinimal(true);
3660 pos
= rx
.indexIn(data
, pos
);
3661 QString addr
= rx
.cap(1);
3663 //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
3665 QFile
file(fi
.absoluteFilePath());
3666 if (!file
.open(QIODevice::ReadOnly
| QIODevice::Text
)) {
3669 QByteArray cssData
= file
.readAll();
3670 QString css
= QString::fromUtf8(cssData
);
3672 QCss::StyleSheet sheet
;
3673 QCss::Parser(css
).parse(&sheet
);
3674 m_selector
->styleSheets
.append(sheet
);
3683 void QSvgHandler::setAnimPeriod(int start
, int end
)
3686 m_animEnd
= qMax(end
, m_animEnd
);
3689 int QSvgHandler::animationDuration() const
3694 QSvgHandler::~QSvgHandler()