Fixes a crash in QDoubleSpinBox
[qt-netbsd.git] / src / svg / qsvghandler.cpp
blob004dffeb492b57d3bd27941f08bcbcdc40f7169c
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtSvg module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial Usage
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.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "qsvghandler_p.h"
44 #ifndef QT_NO_SVG
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"
53 #include "qwidget.h"
54 #include "qpen.h"
55 #include "qpainterpath.h"
56 #include "qbrush.h"
57 #include "qcolor.h"
58 #include "qtextformat.h"
59 #include "qvector.h"
60 #include "qfileinfo.h"
61 #include "qfile.h"
62 #include "qdebug.h"
63 #include "qmath.h"
64 #include "qnumeric.h"
65 #include "private/qmath_p.h"
67 #include "float.h"
69 QT_BEGIN_NAMESPACE
71 double qstrtod(const char *s00, char const **se, bool *ok);
73 static bool parsePathDataFast(const QStringRef &data, QPainterPath &path);
75 struct QSvgAttributes
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"));
90 if (!style.isEmpty())
91 handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
94 QStringRef QSvgAttributes::value(const QLatin1String &name) const
96 QStringRef v = m_xmlAttributes.value(name);
97 if (v.isEmpty()) {
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;
101 break;
105 return v;
108 QStringRef QSvgAttributes::value(const QString &namespaceUri, const QLatin1String &name) const
110 QStringRef v = m_xmlAttributes.value(namespaceUri, name);
111 if (v.isEmpty()) {
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;
115 break;
119 return v;
122 static inline QString someId(const QXmlStreamAttributes &attributes)
124 QString id = attributes.value(QLatin1String("id")).toString();
125 if (id.isEmpty())
126 id = attributes.value(QLatin1String("xml:id")).toString();
127 return id;
129 static inline QString someId(const QSvgAttributes &attributes)
130 { return someId(attributes.m_xmlAttributes); }
134 static const char * QSvgStyleSelector_nodeString[] = {
135 "svg",
136 "g",
137 "defs",
138 "switch",
139 "animation",
140 "arc",
141 "circle",
142 "ellipse",
143 "image",
144 "line",
145 "path",
146 "polygon",
147 "polyline",
148 "rect",
149 "text",
150 "textarea",
151 "use",
152 "video"
155 class QSvgStyleSelector : public QCss::StyleSelector
157 public:
158 QSvgStyleSelector()
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
177 if (n &&
178 (n->type() == QSvgNode::DOC ||
179 n->type() == QSvgNode::G ||
180 n->type() == QSvgNode::DEFS ||
181 n->type() == QSvgNode::SWITCH)) {
182 return (QSvgStructureNode*)n;
184 return 0;
187 inline QSvgStructureNode *svgStructure(NodePtr node) const
189 QSvgNode *n = svgNode(node);
190 QSvgStructureNode *st = nodeToStructure(n);
191 return st;
194 virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const
196 QSvgNode *n = svgNode(node);
197 if (!n)
198 return false;
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"))))
207 return n->nodeId();
208 if (!n->xmlClass().isEmpty() && name == QLatin1String("class"))
209 return n->xmlClass();
210 return QString();
212 virtual bool hasAttributes(NodePtr node) const
214 QSvgNode *n = svgNode(node);
215 return (n &&
216 (!n->nodeId().isEmpty() || !n->xmlClass().isEmpty()));
219 virtual QStringList nodeIds(NodePtr node) const
221 QSvgNode *n = svgNode(node);
222 QString nid;
223 if (n)
224 nid = n->nodeId();
225 QStringList lst; lst.append(nid);
226 return lst;
229 virtual QStringList nodeNames(NodePtr node) const
231 QSvgNode *n = svgNode(node);
232 if (n)
233 return QStringList(nodeToName(n));
234 return QStringList();
237 virtual bool isNullNode(NodePtr node) const
239 return !node.ptr;
242 virtual NodePtr parentNode(NodePtr node) const
244 QSvgNode *n = svgNode(node);
245 NodePtr newNode;
246 newNode.ptr = 0;
247 newNode.id = 0;
248 if (n) {
249 QSvgNode *svgParent = n->parent();
250 if (svgParent) {
251 newNode.ptr = svgParent;
254 return newNode;
256 virtual NodePtr previousSiblingNode(NodePtr node) const
258 NodePtr newNode;
259 newNode.ptr = 0;
260 newNode.id = 0;
262 QSvgNode *n = svgNode(node);
263 if (!n)
264 return newNode;
265 QSvgStructureNode *svgParent = nodeToStructure(n->parent());
267 if (svgParent) {
268 newNode.ptr = svgParent->previousSiblingNode(n);
270 return newNode;
272 virtual NodePtr duplicateNode(NodePtr node) const
274 NodePtr n;
275 n.ptr = node.ptr;
276 n.id = node.id;
277 return n;
279 virtual void freeNode(NodePtr node) const
281 Q_UNUSED(node);
285 static qreal toDouble(const QChar *&str)
287 const int maxLen = 255;//technically doubles can go til 308+ but whatever
288 char temp[maxLen+1];
289 int pos = 0;
291 if (*str == QLatin1Char('-')) {
292 temp[pos++] = '-';
293 ++str;
294 } else if (*str == QLatin1Char('+')) {
295 ++str;
297 while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
298 temp[pos++] = str->toLatin1();
299 ++str;
301 if (*str == QLatin1Char('.') && pos < maxLen) {
302 temp[pos++] = '.';
303 ++str;
305 while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
306 temp[pos++] = str->toLatin1();
307 ++str;
309 bool exponent = false;
310 if (*str == QLatin1Char('e') && pos < maxLen) {
311 exponent = true;
312 temp[pos++] = 'e';
313 ++str;
314 if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
315 temp[pos++] = str->toLatin1();
316 ++str;
318 while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
319 temp[pos++] = str->toLatin1();
320 ++str;
323 temp[pos] = '\0';
325 qreal val;
326 if (!exponent && pos < 10) {
327 int ival = 0;
328 const char *t = temp;
329 bool neg = false;
330 if(*t == '-') {
331 neg = true;
332 ++t;
334 while(*t && *t != '.') {
335 ival *= 10;
336 ival += (*t) - '0';
337 ++t;
339 if(*t == '.') {
340 ++t;
341 int div = 1;
342 while(*t) {
343 ival *= 10;
344 ival += (*t) - '0';
345 div *= 10;
346 ++t;
348 val = ((qreal)ival)/((qreal)div);
349 } else {
350 val = ival;
352 if (neg)
353 val = -val;
354 } else {
355 #ifdef Q_WS_QWS
356 if(sizeof(qreal) == sizeof(float))
357 val = strtof(temp, 0);
358 else
359 #endif
361 bool ok = false;
362 val = qstrtod(temp, 0, &ok);
365 return val;
368 static qreal toDouble(const QString &str)
370 const QChar *c = str.constData();
371 return toDouble(c);
374 static qreal toDouble(const QStringRef &str)
376 const QChar *c = str.constData();
377 return toDouble(c);
380 static QVector<qreal> parseNumbersList(const QChar *&str)
382 QVector<qreal> points;
383 if (!str)
384 return points;
385 points.reserve(32);
387 while (*str == QLatin1Char(' '))
388 ++str;
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(' '))
396 ++str;
397 if (*str == QLatin1Char(','))
398 ++str;
400 //eat the rest of space
401 while (*str == QLatin1Char(' '))
402 ++str;
405 return points;
408 static QVector<qreal> parsePercentageList(const QChar *&str)
410 QVector<qreal> points;
411 if (!str)
412 return points;
414 while (str->isSpace())
415 ++str;
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(' '))
423 ++str;
424 if (*str == QLatin1Char('%'))
425 ++str;
426 while (*str == QLatin1Char(' '))
427 ++str;
428 if (*str == QLatin1Char(','))
429 ++str;
431 //eat the rest of space
432 while (*str == QLatin1Char(' '))
433 ++str;
436 return points;
439 static QString idFromUrl(const QString &url)
441 QString::const_iterator itr = url.constBegin();
442 while ((*itr).isSpace())
443 ++itr;
444 if ((*itr) == QLatin1Char('('))
445 ++itr;
446 while ((*itr).isSpace())
447 ++itr;
448 if ((*itr) == QLatin1Char('#'))
449 ++itr;
450 QString id;
451 while ((*itr) != QLatin1Char(')')) {
452 id += *itr;
453 ++itr;
455 return id;
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]),
479 int(compo[1]),
480 int(compo[2]));
481 return true;
482 } else if (colorStr == QLatin1String("inherited") ||
483 colorStr == QLatin1String("inherit")) {
484 return false;
485 } else if (colorStr == QLatin1String("currentColor")) {
486 color = handler->currentColor();
487 return true;
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))
498 return false;
499 if (!opacity.isEmpty()) {
500 qreal op = toDouble(opacity);
501 if (op <= 1)
502 op *= 255;
503 color.setAlpha(int(op));
505 return true;
508 static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
509 QSvgHandler *handler)
511 QString numStr = str.trimmed();
513 if (numStr.endsWith(QLatin1Char('%'))) {
514 numStr.chop(1);
515 type = QSvgHandler::LT_PERCENT;
516 } else if (numStr.endsWith(QLatin1String("px"))) {
517 numStr.chop(2);
518 type = QSvgHandler::LT_PX;
519 } else if (numStr.endsWith(QLatin1String("pc"))) {
520 numStr.chop(2);
521 type = QSvgHandler::LT_PC;
522 } else if (numStr.endsWith(QLatin1String("pt"))) {
523 numStr.chop(2);
524 type = QSvgHandler::LT_PT;
525 } else if (numStr.endsWith(QLatin1String("mm"))) {
526 numStr.chop(2);
527 type = QSvgHandler::LT_MM;
528 } else if (numStr.endsWith(QLatin1String("cm"))) {
529 numStr.chop(2);
530 type = QSvgHandler::LT_CM;
531 } else if (numStr.endsWith(QLatin1String("in"))) {
532 numStr.chop(2);
533 type = QSvgHandler::LT_IN;
534 } else {
535 type = handler->defaultCoordinateSystem();
536 //type = QSvgHandler::LT_OTHER;
538 qreal len = toDouble(numStr);
539 //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
540 return len;
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) {
548 num = num/100.0;
550 return num;
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);
561 QPainterPath path;
562 path.setFillRule(Qt::WindingFill);
563 parsePathDataFast(pathStr, path);
565 font->addGlyph(unicode, path, havx);
567 return true;
570 // this should really be called convertToDefaultCoordinateSystem
571 // and convert when type != QSvgHandler::defaultCoordinateSystem
572 static qreal convertToPixels(qreal len, bool , QSvgHandler::LengthType type)
575 switch (type) {
576 case QSvgHandler::LT_PERCENT:
577 break;
578 case QSvgHandler::LT_PX:
579 break;
580 case QSvgHandler::LT_PC:
581 break;
582 case QSvgHandler::LT_PT:
583 return len * 1.25;
584 break;
585 case QSvgHandler::LT_MM:
586 return len * 3.543307;
587 break;
588 case QSvgHandler::LT_CM:
589 return len * 35.43307;
590 break;
591 case QSvgHandler::LT_IN:
592 return len * 90;
593 break;
594 case QSvgHandler::LT_OTHER:
595 break;
596 default:
597 break;
599 return len;
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();
608 QColor color;
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();
622 if (!node)
623 return 0;
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"))
641 f = Qt::OddEvenFill;
642 if (value.startsWith(QLatin1String("url"))) {
643 value = value.remove(0, 3);
644 QSvgStyleProperty *style = styleFromUrl(node, value);
645 if (style) {
646 QSvgFillStyle *prop = new QSvgFillStyle(style);
647 if (!opacity.isEmpty())
648 prop->setFillOpacity(toDouble(opacity));
649 node->appendStyleProperty(prop, myId);
650 } else {
651 qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value)));
653 } else if (value != QLatin1String("none")) {
654 QColor color;
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);
661 } else {
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);
690 if (style) {
691 if (style->type() == QSvgStyleProperty::GRADIENT) {
692 QBrush b(*((QSvgGradientStyle*)style)->qgradient());
693 pen.setBrush(b);
694 } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
695 pen.setColor(
696 ((QSvgSolidColorStyle*)style)->qcolor());
698 } else {
699 qWarning()<<"QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
701 } else {
702 QColor color;
703 if (constructColor(value, opacity, color, handler))
704 pen.setColor(color);
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);
713 //### fixme
714 if (!widthF) {
715 pen.setStyle(Qt::NoPen);
716 return;
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();
746 if (penw != 0)
747 for (int i = 0; i < dashes.size(); ++i) {
748 *d /= penw;
749 ++d;
751 pen.setDashPattern(dashes);
753 if (!dashOffset.isEmpty()) {
754 pen.setDashOffset(toDouble(dashOffset));
757 } else {
758 pen.setStyle(Qt::NoPen);
763 static QMatrix parseTransformationMatrix(const QString &value)
765 QMatrix matrix;
766 const QChar *str = value.constData();
768 while (*str != QLatin1Char(0)) {
769 if (str->isSpace() || *str == QLatin1Char(',')) {
770 ++str;
771 continue;
773 enum State {
774 Matrix,
775 Translate,
776 Rotate,
777 Scale,
778 SkewX,
779 SkewY
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]))
786 goto error;
787 ++str;
788 state = Matrix;
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]))
793 goto error;
794 ++str;
795 state = Translate;
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]))
800 goto error;
801 ++str;
802 state = Rotate;
803 } else if (*str == QLatin1Char('s')) { //scale, skewX, skewY
804 ++str;
805 if (*str == QLatin1Char('c')) {
806 const char *ident = "ale";
807 for (int i = 0; i < 3; ++i)
808 if (*(++str) != QLatin1Char(ident[i]))
809 goto error;
810 ++str;
811 state = Scale;
812 } else if (*str == QLatin1Char('k')) {
813 if (*(++str) != QLatin1Char('e'))
814 goto error;
815 if (*(++str) != QLatin1Char('w'))
816 goto error;
817 ++str;
818 if (*str == QLatin1Char('X'))
819 state = SkewX;
820 else if (*str == QLatin1Char('Y'))
821 state = SkewY;
822 else
823 goto error;
824 ++str;
825 } else {
826 goto error;
828 } else {
829 goto error;
833 while (str->isSpace())
834 ++str;
835 if (*str != QLatin1Char('('))
836 goto error;
837 ++str;
838 QVector<qreal> points = parseNumbersList(str);
839 if (*str != QLatin1Char(')'))
840 goto error;
841 ++str;
843 if(state == Matrix) {
844 if(points.count() != 6)
845 goto error;
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]);
854 else
855 goto error;
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]);
863 } else {
864 goto error;
866 } else if (state == Scale) {
867 if (points.count() < 1 || points.count() > 2)
868 goto error;
869 qreal sx = points[0];
870 qreal sy = sx;
871 if(points.count() == 2)
872 sy = points[1];
873 matrix.scale(sx, sy);
874 } else if (state == SkewX) {
875 if (points.count() != 1)
876 goto error;
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)
881 goto error;
882 const qreal deg2rad = qreal(0.017453292519943295769);
883 matrix.shear(0, tan(points[0]*deg2rad));
886 error:
887 return matrix;
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));
911 if (!inherited)
912 inherited = static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty(
913 QSvgStyleProperty::STROKE));
914 QPen pen(handler->defaultPen());
915 if (inherited)
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);
922 if (style) {
923 if (style->type() == QSvgStyleProperty::GRADIENT) {
924 QBrush b(*((QSvgGradientStyle*)style)->qgradient());
925 pen.setBrush(b);
926 } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
927 pen.setColor(
928 ((QSvgSolidColorStyle*)style)->qcolor());
930 } else {
931 qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
933 } else {
934 QColor color;
935 if (constructColor(value, opacity, color, handler))
936 pen.setColor(color);
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);
945 //### fixme
946 if (!widthF) {
947 pen.setStyle(Qt::NoPen);
948 return;
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();
976 if(penw != 0)
977 for (int i = 0; i < dashes.size(); ++i) {
978 *d /= penw;
979 ++d;
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);
990 if (penw != 0)
991 doffset /= penw;
992 pen.setDashOffset(doffset);
994 if (!miterlimit.isEmpty())
995 pen.setMiterLimit(toDouble(miterlimit));
997 node->appendStyleProperty(new QSvgStrokeStyle(pen), myId);
998 } else {
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();
1013 QColor color;
1014 if (!value.isEmpty() || !opacity.isEmpty()) {
1015 if (value.startsWith(QLatin1String("url"))) {
1016 value = value.remove(0, 3);
1017 QSvgStyleProperty *style = styleFromUrl(node, value);
1018 if (style) {
1019 switch (style->type()) {
1020 case QSvgStyleProperty::FILL:
1022 brush = static_cast<QSvgFillStyle*>(style)->qbrush();
1023 break;
1025 case QSvgStyleProperty::SOLID_COLOR:
1027 brush = QBrush(static_cast<QSvgSolidColorStyle*>(style)->qcolor());
1028 break;
1030 case QSvgStyleProperty::GRADIENT:
1032 brush = QBrush(*static_cast<QSvgGradientStyle*>(style)->qgradient());
1033 break;
1035 default:
1036 qWarning("Cannot use property \"%s\" as brush.", qPrintable(idFromUrl(value)));
1038 } else {
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);
1046 } else {
1047 brush = QBrush(Qt::NoBrush);
1049 return true;
1051 return false;
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);
1071 if (fontSize <= 0)
1072 fontSize = 1;
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")) {
1083 //inherited already
1086 if (!weight.isEmpty()) {
1087 bool ok = false;
1088 int weightNum = weight.toInt(&ok);
1089 if (ok) {
1090 switch (weightNum) {
1091 case 100:
1092 case 200:
1093 font.setWeight(QFont::Light);
1094 break;
1095 case 300:
1096 case 400:
1097 font.setWeight(QFont::Normal);
1098 break;
1099 case 500:
1100 case 600:
1101 font.setWeight(QFont::DemiBold);
1102 break;
1103 case 700:
1104 case 800:
1105 font.setWeight(QFont::Bold);
1106 break;
1107 case 900:
1108 font.setWeight(QFont::Black);
1109 break;
1110 default:
1111 break;
1113 } else {
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());
1127 return true;
1130 return false;
1133 static void parseFont(QSvgNode *node,
1134 const QSvgAttributes &attributes,
1135 QSvgHandler *handler)
1137 QFont font;
1138 font.setPixelSize(12);
1139 qreal fontSize = font.pixelSize();
1141 QSvgFontStyle *inherited =
1142 static_cast<QSvgFontStyle*>(node->styleProperty(
1143 QSvgStyleProperty::FONT));
1144 if (!inherited)
1145 inherited =
1146 static_cast<QSvgFontStyle*>(node->parent()->styleProperty(
1147 QSvgStyleProperty::FONT));
1148 if (inherited) {
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);
1160 if (svgFont) {
1161 fontStyle = new QSvgFontStyle(svgFont, doc);
1162 fontStyle->setPointSize(fontSize);
1165 if (!fontStyle) {
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,
1178 QSvgHandler *)
1180 QString value = attributes.value(QLatin1String("transform")).toString();
1181 QString myId = someId(attributes);
1182 value = value.trimmed();
1183 if (value.isEmpty())
1184 return;
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,
1195 QSvgHandler *)
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);
1204 } else
1205 node->setVisible(true);
1208 static void pathArcSegment(QPainterPath &path,
1209 qreal xc, qreal yc,
1210 qreal th0, qreal th1,
1211 qreal rx, qreal ry, qreal xAxisRotation)
1213 qreal sinTh, cosTh;
1214 qreal a00, a01, a10, a11;
1215 qreal x1, y1, x2, y2, x3, y3;
1216 qreal t;
1217 qreal thHalf;
1219 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
1220 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
1222 a00 = cosTh * rx;
1223 a01 = -sinTh * ry;
1224 a10 = sinTh * rx;
1225 a11 = cosTh * ry;
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
1255 * warranty.
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,
1268 qreal rx,
1269 qreal ry,
1270 qreal x_axis_rotation,
1271 int large_arc_flag,
1272 int sweep_flag,
1273 qreal x,
1274 qreal y,
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;
1282 int i, n_segs;
1283 qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
1285 rx = qAbs(rx);
1286 ry = qAbs(ry);
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;
1295 Pr1 = rx * rx;
1296 Pr2 = ry * ry;
1297 Px = dx1 * dx1;
1298 Py = dy1 * dy1;
1299 /* Spec : check if radii are large enough */
1300 check = Px / Pr1 + Py / Pr2;
1301 if (check > 1) {
1302 rx = rx * qSqrt(check);
1303 ry = ry * qSqrt(check);
1306 a00 = cos_th / rx;
1307 a01 = sin_th / rx;
1308 a10 = -sin_th / ry;
1309 a11 = cos_th / ry;
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);
1331 th_arc = th1 - th0;
1332 if (th_arc < 0 && sweep_flag)
1333 th_arc += 2 * Q_PI;
1334 else if (th_arc > 0 && !sweep_flag)
1335 th_arc -= 2 * Q_PI;
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
1351 char lastMode = 0;
1352 QPointF ctrlPt;
1353 const QChar *str = dataStr.constData();
1354 const QChar *end = str + dataStr.size();
1356 while (str != end) {
1357 while (*str == QLatin1Char(' '))
1358 ++str;
1359 QChar pathElem = *str;
1360 ++str;
1361 QChar endc = *end;
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()) {
1371 case 'm': {
1372 if (arg.count() < 2) {
1373 arg.pop_front();
1374 break;
1376 x = x0 = arg[0] + offsetX;
1377 y = y0 = arg[1] + offsetY;
1378 path.moveTo(x0, y0);
1379 arg.pop_front(); arg.pop_front();
1381 break;
1382 case 'M': {
1383 if (arg.count() < 2) {
1384 arg.pop_front();
1385 break;
1387 x = x0 = arg[0];
1388 y = y0 = arg[1];
1390 path.moveTo(x0, y0);
1391 arg.pop_front(); arg.pop_front();
1393 break;
1394 case 'z':
1395 case 'Z': {
1396 x = x0;
1397 y = y0;
1398 path.closeSubpath();
1399 arg.pop_front();//pop dummy
1401 break;
1402 case 'l': {
1403 if (arg.count() < 2) {
1404 arg.pop_front();
1405 break;
1407 x = arg.front() + offsetX;
1408 arg.pop_front();
1409 y = arg.front() + offsetY;
1410 arg.pop_front();
1411 path.lineTo(x, y);
1414 break;
1415 case 'L': {
1416 if (arg.count() < 2) {
1417 arg.pop_front();
1418 break;
1420 x = arg.front(); arg.pop_front();
1421 y = arg.front(); arg.pop_front();
1422 path.lineTo(x, y);
1424 break;
1425 case 'h': {
1426 x = arg.front() + offsetX; arg.pop_front();
1427 path.lineTo(x, y);
1429 break;
1430 case 'H': {
1431 x = arg[0];
1432 path.lineTo(x, y);
1433 arg.pop_front();
1435 break;
1436 case 'v': {
1437 y = arg[0] + offsetY;
1438 path.lineTo(x, y);
1439 arg.pop_front();
1441 break;
1442 case 'V': {
1443 y = arg[0];
1444 path.lineTo(x, y);
1445 arg.pop_front();
1447 break;
1448 case 'c': {
1449 if (arg.count() < 6) {
1450 while (arg.count())
1451 arg.pop_front();
1452 break;
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);
1458 ctrlPt = c2;
1459 x = e.x();
1460 y = e.y();
1461 arg.pop_front(); arg.pop_front();
1462 arg.pop_front(); arg.pop_front();
1463 arg.pop_front(); arg.pop_front();
1464 break;
1466 case 'C': {
1467 if (arg.count() < 6) {
1468 while (arg.count())
1469 arg.pop_front();
1470 break;
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);
1476 ctrlPt = c2;
1477 x = e.x();
1478 y = e.y();
1479 arg.pop_front(); arg.pop_front();
1480 arg.pop_front(); arg.pop_front();
1481 arg.pop_front(); arg.pop_front();
1482 break;
1484 case 's': {
1485 if (arg.count() < 4) {
1486 while (arg.count())
1487 arg.pop_front();
1488 break;
1490 QPointF c1;
1491 if (lastMode == 'c' || lastMode == 'C' ||
1492 lastMode == 's' || lastMode == 'S')
1493 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1494 else
1495 c1 = QPointF(x, 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);
1499 ctrlPt = c2;
1500 x = e.x();
1501 y = e.y();
1502 arg.pop_front(); arg.pop_front();
1503 arg.pop_front(); arg.pop_front();
1504 break;
1506 case 'S': {
1507 if (arg.count() < 4) {
1508 while (arg.count())
1509 arg.pop_front();
1510 break;
1512 QPointF c1;
1513 if (lastMode == 'c' || lastMode == 'C' ||
1514 lastMode == 's' || lastMode == 'S')
1515 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1516 else
1517 c1 = QPointF(x, y);
1518 QPointF c2(arg[0], arg[1]);
1519 QPointF e(arg[2], arg[3]);
1520 path.cubicTo(c1, c2, e);
1521 ctrlPt = c2;
1522 x = e.x();
1523 y = e.y();
1524 arg.pop_front(); arg.pop_front();
1525 arg.pop_front(); arg.pop_front();
1526 break;
1528 case 'q': {
1529 if (arg.count() < 4) {
1530 while (arg.count())
1531 arg.pop_front();
1532 break;
1534 QPointF c(arg[0]+offsetX, arg[1]+offsetY);
1535 QPointF e(arg[2]+offsetX, arg[3]+offsetY);
1536 path.quadTo(c, e);
1537 ctrlPt = c;
1538 x = e.x();
1539 y = e.y();
1540 arg.pop_front(); arg.pop_front();
1541 arg.pop_front(); arg.pop_front();
1542 break;
1544 case 'Q': {
1545 if (arg.count() < 4) {
1546 while (arg.count())
1547 arg.pop_front();
1548 break;
1550 QPointF c(arg[0], arg[1]);
1551 QPointF e(arg[2], arg[3]);
1552 path.quadTo(c, e);
1553 ctrlPt = c;
1554 x = e.x();
1555 y = e.y();
1556 arg.pop_front(); arg.pop_front();
1557 arg.pop_front(); arg.pop_front();
1558 break;
1560 case 't': {
1561 if (arg.count() < 2) {
1562 while (arg.count())
1563 arg.pop_front();
1564 break;
1566 QPointF e(arg[0]+offsetX, arg[1]+offsetY);
1567 QPointF c;
1568 if (lastMode == 'q' || lastMode == 'Q' ||
1569 lastMode == 't' || lastMode == 'T')
1570 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1571 else
1572 c = QPointF(x, y);
1573 path.quadTo(c, e);
1574 ctrlPt = c;
1575 x = e.x();
1576 y = e.y();
1577 arg.pop_front(); arg.pop_front();
1578 break;
1580 case 'T': {
1581 if (arg.count() < 2) {
1582 while (arg.count())
1583 arg.pop_front();
1584 break;
1586 QPointF e(arg[0], arg[1]);
1587 QPointF c;
1588 if (lastMode == 'q' || lastMode == 'Q' ||
1589 lastMode == 't' || lastMode == 'T')
1590 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1591 else
1592 c = QPointF(x, y);
1593 path.quadTo(c, e);
1594 ctrlPt = c;
1595 x = e.x();
1596 y = e.y();
1597 arg.pop_front(); arg.pop_front();
1598 break;
1600 case 'a': {
1601 if (arg.count() < 7) {
1602 while (arg.count())
1603 arg.pop_front();
1604 break;
1606 qreal rx = arg[0];
1607 qreal ry = arg[1];
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;
1613 qreal curx = x;
1614 qreal cury = y;
1615 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1616 int(sweepFlag), ex, ey, curx, cury);
1618 x = ex;
1619 y = ey;
1621 arg.pop_front(); arg.pop_front();
1622 arg.pop_front(); arg.pop_front();
1623 arg.pop_front(); arg.pop_front();
1624 arg.pop_front();
1626 break;
1627 case 'A': {
1628 if (arg.count() < 7) {
1629 while (arg.count())
1630 arg.pop_front();
1631 break;
1633 qreal rx = arg[0];
1634 qreal ry = arg[1];
1635 qreal xAxisRotation = arg[2];
1636 qreal largeArcFlag = arg[3];
1637 qreal sweepFlag = arg[4];
1638 qreal ex = arg[5];
1639 qreal ey = arg[6];
1640 qreal curx = x;
1641 qreal cury = y;
1642 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1643 int(sweepFlag), ex, ey, curx, cury);
1644 x = ex;
1645 y = ey;
1646 arg.pop_front(); arg.pop_front();
1647 arg.pop_front(); arg.pop_front();
1648 arg.pop_front(); arg.pop_front();
1649 arg.pop_front();
1651 break;
1652 default:
1653 return false;
1655 lastMode = pathElem.toLatin1();
1658 return true;
1661 static bool parseStyle(QSvgNode *node,
1662 const QXmlStreamAttributes &attributes,
1663 QSvgHandler *);
1665 static bool parseStyle(QSvgNode *node,
1666 const QSvgAttributes &attributes,
1667 QSvgHandler *);
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())
1675 continue;
1676 QCss::Value val = decl.d->values.first();
1677 QString valueStr;
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(',');
1683 else
1684 valueStr += value;
1686 } else {
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");
1706 break;
1707 default:
1708 break;
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);
1720 QString key;
1722 attributes->reserve(10);
1724 while (m_cssParser.hasNext()) {
1725 m_cssParser.skipSpace();
1727 if (!m_cssParser.hasNext())
1728 break;
1729 m_cssParser.next();
1731 QStringRef name;
1732 if (m_cssParser.hasEscapeSequences) {
1733 key = m_cssParser.lexem();
1734 name = QStringRef(&key, 0, key.length());
1735 } else {
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))
1742 break;
1744 m_cssParser.skipSpace();
1745 if (!m_cssParser.hasNext())
1746 break;
1748 QSvgCssAttribute attribute;
1749 attribute.name = QXmlStreamStringRef(name);
1751 const int firstSymbol = m_cssParser.index;
1752 int symbolCount = 0;
1753 do {
1754 m_cssParser.next();
1755 ++symbolCount;
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;
1767 break;
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) {
1776 QString value;
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;
1793 cssNode.ptr = node;
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,
1803 bool initial,
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));
1817 if (fontStyle) {
1818 QSvgTinyDocument *doc = fontStyle->doc();
1819 if (doc && fontStyle->svgFont()) {
1820 cssStyleLookup(node, handler, handler->selector());
1821 parseStyle(node, attrs, handler);
1822 return true;
1824 } else if (!fontFamily.isEmpty()) {
1825 QSvgTinyDocument *doc = node->document();
1826 QSvgFont *svgFont = doc->svgFont(fontFamily);
1827 if (svgFont) {
1828 cssStyleLookup(node, handler, handler->selector());
1829 parseStyle(node, attrs, handler);
1830 return true;
1834 QTextCharFormat format;
1835 QBrush brush(QColor(0, 0, 0));
1836 QFont font;
1837 font.setPixelSize(12);
1838 qreal fontSize = font.pixelSize();
1840 if (!initial) {
1841 font = textNode->topFormat().font();
1842 fontSize = font.pixelSize() / textNode->scale();
1843 brush = textNode->topFormat().foreground();
1844 } else {
1845 QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
1846 node->styleProperty(QSvgStyleProperty::FONT));
1847 if (!fontStyle)
1848 fontStyle = static_cast<QSvgFontStyle*>(
1849 node->parent()->styleProperty(QSvgStyleProperty::FONT));
1850 if (fontStyle) {
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));
1866 if (fillStyle)
1867 brush = fillStyle->qbrush();
1870 if (parseQFont(attrs, font, fontSize, handler) || initial) {
1871 if (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));
1886 // if (inherited)
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);
1897 return true;
1900 static inline QStringList stringToList(const QString &str)
1902 QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts);
1903 return lst;
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);
1932 return true;
1935 static void parseOpacity(QSvgNode *node,
1936 const QSvgAttributes &attributes,
1937 QSvgHandler *)
1939 QString value = attributes.value(QLatin1String("opacity")).toString();
1940 value = value.trimmed();
1942 bool ok = false;
1943 qreal op = value.toDouble(&ok);
1945 if (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;
2002 } else {
2003 NOOP;
2006 return QPainter::CompositionMode_SourceOver;
2009 static void parseCompOp(QSvgNode *node,
2010 const QSvgAttributes &attributes,
2011 QSvgHandler *)
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,
2066 QSvgHandler *)
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);
2089 #if 0
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");
2115 #endif
2116 return true;
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,
2128 QSvgHandler *)
2130 Q_UNUSED(parent); Q_UNUSED(attributes);
2131 return true;
2134 static bool parseAnimateNode(QSvgNode *parent,
2135 const QXmlStreamAttributes &attributes,
2136 QSvgHandler *)
2138 Q_UNUSED(parent); Q_UNUSED(attributes);
2139 return true;
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);
2163 } else {
2164 QStringList str = valuesStr.split(QLatin1Char(';'));
2165 QStringList::const_iterator itr;
2166 for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
2167 QColor color;
2168 constructColor(*itr, QString(), color, handler);
2169 colors.append(color);
2173 int ms = 1000;
2174 beginStr = beginStr.trimmed();
2175 if (beginStr.endsWith(QLatin1String("ms"))) {
2176 beginStr.chop(2);
2177 ms = 1;
2178 } else if (beginStr.endsWith(QLatin1String("s"))) {
2179 beginStr.chop(1);
2181 durStr = durStr.trimmed();
2182 if (durStr.endsWith(QLatin1String("ms"))) {
2183 durStr.chop(2);
2184 ms = 1;
2185 } else if (durStr.endsWith(QLatin1String("s"))) {
2186 durStr.chop(1);
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);
2201 return true;
2204 static bool parseAimateMotionNode(QSvgNode *parent,
2205 const QXmlStreamAttributes &attributes,
2206 QSvgHandler *)
2208 Q_UNUSED(parent); Q_UNUSED(attributes);
2209 return true;
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)
2231 lst.append(0.0);
2232 vals << lst;
2234 s = toStr.constData();
2235 lst = parseNumbersList(s);
2236 while (lst.count() < 3)
2237 lst.append(0.0);
2238 vals << lst;
2239 } else {
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);
2246 vals << tmpVals;
2247 if (*s == QLatin1Char(0))
2248 break;
2249 ++s;
2253 int ms = 1000;
2254 beginStr = beginStr.trimmed();
2255 if (beginStr.endsWith(QLatin1String("ms"))) {
2256 beginStr.chop(2);
2257 ms = 1;
2258 } else if (beginStr.endsWith(QLatin1String("s"))) {
2259 beginStr.chop(1);
2261 int begin = static_cast<int>(toDouble(beginStr) * ms);
2262 durStr = durStr.trimmed();
2263 if (durStr.endsWith(QLatin1String("ms"))) {
2264 durStr.chop(2);
2265 ms = 1;
2266 } else if (durStr.endsWith(QLatin1String("s"))) {
2267 durStr.chop(1);
2268 ms = 1000;
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;
2283 } else {
2284 return false;
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);
2297 return true;
2300 static QSvgNode * createAnimationNode(QSvgNode *parent,
2301 const QXmlStreamAttributes &attributes,
2302 QSvgHandler *)
2304 Q_UNUSED(parent); Q_UNUSED(attributes);
2305 return 0;
2308 static bool parseAudioNode(QSvgNode *parent,
2309 const QXmlStreamAttributes &attributes,
2310 QSvgHandler *)
2312 Q_UNUSED(parent); Q_UNUSED(attributes);
2313 return true;
2316 static QSvgNode *createCircleNode(QSvgNode *parent,
2317 const QXmlStreamAttributes &attributes,
2318 QSvgHandler *)
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);
2329 return circle;
2332 static QSvgNode *createDefsNode(QSvgNode *parent,
2333 const QXmlStreamAttributes &attributes,
2334 QSvgHandler *)
2336 Q_UNUSED(attributes);
2337 QSvgDefs *defs = new QSvgDefs(parent);
2338 return defs;
2341 static bool parseDescNode(QSvgNode *parent,
2342 const QXmlStreamAttributes &attributes,
2343 QSvgHandler *)
2345 Q_UNUSED(parent); Q_UNUSED(attributes);
2346 return true;
2349 static bool parseDiscardNode(QSvgNode *parent,
2350 const QXmlStreamAttributes &attributes,
2351 QSvgHandler *)
2353 Q_UNUSED(parent); Q_UNUSED(attributes);
2354 return true;
2357 static QSvgNode *createEllipseNode(QSvgNode *parent,
2358 const QXmlStreamAttributes &attributes,
2359 QSvgHandler *)
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);
2372 return ellipse;
2375 static QSvgStyleProperty *createFontNode(QSvgNode *parent,
2376 const QXmlStreamAttributes &attributes,
2377 QSvgHandler *)
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();
2388 if (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);
2398 return 0;
2401 static bool parseFontFaceNode(QSvgStyleProperty *parent,
2402 const QXmlStreamAttributes &attributes,
2403 QSvgHandler *)
2405 if (parent->type() != QSvgStyleProperty::FONT) {
2406 return false;
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);
2415 if (!unitsPerEm)
2416 unitsPerEm = 1000;
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);
2426 return true;
2429 static bool parseFontFaceNameNode(QSvgStyleProperty *parent,
2430 const QXmlStreamAttributes &attributes,
2431 QSvgHandler *)
2433 if (parent->type() != QSvgStyleProperty::FONT) {
2434 return false;
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);
2448 return true;
2451 static bool parseFontFaceSrcNode(QSvgStyleProperty *parent,
2452 const QXmlStreamAttributes &attributes,
2453 QSvgHandler *)
2455 Q_UNUSED(parent); Q_UNUSED(attributes);
2456 return true;
2459 static bool parseFontFaceUriNode(QSvgStyleProperty *parent,
2460 const QXmlStreamAttributes &attributes,
2461 QSvgHandler *)
2463 Q_UNUSED(parent); Q_UNUSED(attributes);
2464 return true;
2467 static bool parseForeignObjectNode(QSvgNode *parent,
2468 const QXmlStreamAttributes &attributes,
2469 QSvgHandler *)
2471 Q_UNUSED(parent); Q_UNUSED(attributes);
2472 return true;
2475 static QSvgNode *createGNode(QSvgNode *parent,
2476 const QXmlStreamAttributes &attributes,
2477 QSvgHandler *)
2479 Q_UNUSED(attributes);
2480 QSvgG *node = new QSvgG(parent);
2481 return node;
2484 static bool parseGlyphNode(QSvgStyleProperty *parent,
2485 const QXmlStreamAttributes &attributes,
2486 QSvgHandler *)
2488 if (parent->type() != QSvgStyleProperty::FONT) {
2489 return false;
2492 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2493 QSvgFont *font = style->svgFont();
2494 createSvgGlyph(font, attributes);
2495 return true;
2498 static bool parseHandlerNode(QSvgNode *parent,
2499 const QXmlStreamAttributes &attributes,
2500 QSvgHandler *)
2502 Q_UNUSED(parent); Q_UNUSED(attributes);
2503 return true;
2506 static bool parseHkernNode(QSvgNode *parent,
2507 const QXmlStreamAttributes &attributes,
2508 QSvgHandler *)
2510 Q_UNUSED(parent); Q_UNUSED(attributes);
2511 return true;
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();
2534 QImage image;
2535 if (filename.startsWith(QLatin1String("data"))) {
2536 int idx = filename.lastIndexOf(QLatin1String("base64,"));
2537 if (idx != -1) {
2538 idx += 7;
2539 QString dataStr = filename.mid(idx);
2540 QByteArray data = QByteArray::fromBase64(dataStr.toAscii());
2541 image = QImage::fromData(data);
2542 } else {
2543 qDebug()<<"QSvgHandler::createImageNode: Unrecognized inline image format!";
2545 } else
2546 image = QImage(filename);
2548 if (image.isNull()) {
2549 qDebug()<<"couldn't create image from "<<filename;
2550 return 0;
2553 QSvgNode *img = new QSvgImage(parent,
2554 image,
2555 QRect(int(nx),
2556 int(ny),
2557 int(nwidth),
2558 int(nheight)));
2559 return img;
2562 static QSvgNode *createLineNode(QSvgNode *parent,
2563 const QXmlStreamAttributes &attributes,
2564 QSvgHandler *)
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);
2577 return line;
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();
2591 QMatrix matrix;
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());
2601 } else {
2602 grad->setStops(inherited->qgradient()->stops());
2603 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2606 matrix = inherited->qmatrix();
2607 } else {
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();
2643 qreal nx1 = 0.0;
2644 qreal ny1 = 0.0;
2645 qreal nx2 = 1.0;
2646 qreal ny2 = 0.0;
2648 if (!x1.isEmpty())
2649 nx1 = convertToNumber(x1, handler);
2650 if (!y1.isEmpty())
2651 ny1 = convertToNumber(y1, handler);
2652 if (!x2.isEmpty())
2653 nx2 = convertToNumber(x2, handler);
2654 if (!y2.isEmpty())
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);
2667 return prop;
2670 static bool parseMetadataNode(QSvgNode *parent,
2671 const QXmlStreamAttributes &attributes,
2672 QSvgHandler *)
2674 Q_UNUSED(parent); Q_UNUSED(attributes);
2675 return true;
2678 static bool parseMissingGlyphNode(QSvgStyleProperty *parent,
2679 const QXmlStreamAttributes &attributes,
2680 QSvgHandler *)
2682 if (parent->type() != QSvgStyleProperty::FONT) {
2683 return false;
2686 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2687 QSvgFont *font = style->svgFont();
2688 createSvgGlyph(font, attributes);
2689 return true;
2692 static bool parseMpathNode(QSvgNode *parent,
2693 const QXmlStreamAttributes &attributes,
2694 QSvgHandler *)
2696 Q_UNUSED(parent); Q_UNUSED(attributes);
2697 return true;
2700 static QSvgNode *createPathNode(QSvgNode *parent,
2701 const QXmlStreamAttributes &attributes,
2702 QSvgHandler *)
2704 QStringRef data = attributes.value(QLatin1String("d"));
2706 QPainterPath qpath;
2707 qpath.setFillRule(Qt::WindingFill);
2708 //XXX do error handling
2709 parsePathDataFast(data, qpath);
2711 QSvgNode *path = new QSvgPath(parent, qpath);
2712 return path;
2715 static QSvgNode *createPolygonNode(QSvgNode *parent,
2716 const QXmlStreamAttributes &attributes,
2717 QSvgHandler *)
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);
2725 int i = 0;
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);
2731 ++i;
2733 QSvgNode *polygon = new QSvgPolygon(parent, poly);
2734 return polygon;
2737 static QSvgNode *createPolylineNode(QSvgNode *parent,
2738 const QXmlStreamAttributes &attributes,
2739 QSvgHandler *)
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);
2747 int i = 0;
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);
2753 ++i;
2756 QSvgNode *line = new QSvgPolyline(parent, poly);
2757 return line;
2760 static bool parsePrefetchNode(QSvgNode *parent,
2761 const QXmlStreamAttributes &attributes,
2762 QSvgHandler *)
2764 Q_UNUSED(parent); Q_UNUSED(attributes);
2765 return true;
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();
2778 qreal ncx = 0.5;
2779 qreal ncy = 0.5;
2780 qreal nr = 0.5;
2781 if (!cx.isEmpty())
2782 ncx = toDouble(cx);
2783 if (!cy.isEmpty())
2784 ncy = toDouble(cy);
2785 if (!r.isEmpty())
2786 nr = toDouble(r);
2788 qreal nfx = ncx;
2789 if (!fx.isEmpty())
2790 nfx = toDouble(fx);
2791 qreal nfy = ncy;
2792 if (!fy.isEmpty())
2793 nfy = toDouble(fy);
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);
2801 return prop;
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),
2825 nwidth, nheight);
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;
2835 if (nrx && !nry)
2836 nry = nrx;
2837 else if (nry && !nrx)
2838 nrx = nry;
2840 //we draw rounded rect from 0...99
2841 //svg from 0...bounds.width()/2 so we're adjusting the
2842 //coordinates
2843 nrx *= (100/(bounds.width()/2));
2844 nry *= (100/(bounds.height()/2));
2846 QSvgNode *rect = new QSvgRect(parent, bounds,
2847 int(nrx),
2848 int(nry));
2849 return rect;
2852 static bool parseScriptNode(QSvgNode *parent,
2853 const QXmlStreamAttributes &attributes,
2854 QSvgHandler *)
2856 Q_UNUSED(parent); Q_UNUSED(attributes);
2857 return true;
2860 static bool parseSetNode(QSvgNode *parent,
2861 const QXmlStreamAttributes &attributes,
2862 QSvgHandler *)
2864 Q_UNUSED(parent); Q_UNUSED(attributes);
2865 return true;
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();
2879 QColor color;
2880 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
2881 return 0;
2882 QSvgSolidColorStyle *style = new QSvgSolidColorStyle(color);
2883 return style;
2886 static bool parseStopNode(QSvgStyleProperty *parent,
2887 const QXmlStreamAttributes &attributes,
2888 QSvgHandler *handler)
2890 if (parent->type() != QSvgStyleProperty::GRADIENT)
2891 return false;
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
2899 QSvgAnimation anim;
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())
2913 continue;
2914 if (decl.d->values.count() != 1)
2915 continue;
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();
2930 QColor color;
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.
2950 if (offset > 1.0) {
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);
2955 offset = 1.0;
2958 grad->setColorAt(offset, color);
2959 style->setGradientStopsSet(true);
2960 if (!colorOK)
2961 style->addResolve(offset);
2962 return true;
2965 static bool parseStyleNode(QSvgNode *parent,
2966 const QXmlStreamAttributes &attributes,
2967 QSvgHandler *handler)
2969 Q_UNUSED(parent);
2970 QString type = attributes.value(QLatin1String("type")).toString();
2971 type = type.toLower();
2973 if (type == QLatin1String("text/css")) {
2974 handler->setInStyle(true);
2977 return 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();
2987 #if 0
2988 if (baseProfile.isEmpty() && baseProfile != QLatin1String("tiny")) {
2989 qWarning("Profile is %s while we only support tiny!",
2990 qPrintable(baseProfile));
2992 #endif
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?
3000 qreal width = 0;
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);
3007 qreal height = 0;
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);
3044 return node;
3047 static QSvgNode *createSwitchNode(QSvgNode *parent,
3048 const QXmlStreamAttributes &attributes,
3049 QSvgHandler *)
3051 Q_UNUSED(attributes);
3052 QSvgSwitch *node = new QSvgSwitch(parent);
3053 return node;
3056 static bool parseTbreakNode(QSvgNode *parent,
3057 const QXmlStreamAttributes &,
3058 QSvgHandler *)
3060 if (parent->type() != QSvgNode::TEXTAREA)
3061 return false;
3062 static_cast<QSvgText*>(parent)->insertLineBreak();
3063 return true;
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
3079 // system here
3080 //nx = convertToPixels(nx, true, type);
3081 //ny = convertToPixels(ny, true, type);
3083 QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
3084 return text;
3087 static QSvgNode *createTextAreaNode(QSvgNode *parent,
3088 const QXmlStreamAttributes &attributes,
3089 QSvgHandler *handler)
3091 QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3092 if (node) {
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));
3098 return node;
3101 static bool parseTitleNode(QSvgNode *parent,
3102 const QXmlStreamAttributes &attributes,
3103 QSvgHandler *)
3105 Q_UNUSED(parent); Q_UNUSED(attributes);
3106 return true;
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()) {
3130 case QSvgNode::DOC:
3131 case QSvgNode::DEFS:
3132 case QSvgNode::G:
3133 case QSvgNode::SWITCH:
3134 group = static_cast<QSvgStructureNode*>(parent);
3135 break;
3136 default:
3137 break;
3140 if (group) {
3141 QSvgNode *link = group->scopeNode(linkId);
3142 if (link) {
3143 QPointF pt;
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);
3157 return node;
3161 qWarning("link %s hasn't been detected!", qPrintable(linkId));
3162 return 0;
3165 static QSvgNode *createVideoNode(QSvgNode *parent,
3166 const QXmlStreamAttributes &attributes,
3167 QSvgHandler *)
3169 Q_UNUSED(parent); Q_UNUSED(attributes);
3170 return 0;
3173 typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
3175 static FactoryMethod findGroupFactory(const QString &name)
3177 if (name.isEmpty())
3178 return 0;
3180 QStringRef ref(&name, 1, name.length() - 1);
3181 switch (name.at(0).unicode()) {
3182 case 'd':
3183 if (ref == QLatin1String("efs")) return createDefsNode;
3184 break;
3185 case 'g':
3186 if (ref.isEmpty()) return createGNode;
3187 break;
3188 case 's':
3189 if (ref == QLatin1String("vg")) return createSvgNode;
3190 if (ref == QLatin1String("witch")) return createSwitchNode;
3191 break;
3192 default:
3193 break;
3195 return 0;
3198 static FactoryMethod findGraphicsFactory(const QString &name)
3200 if (name.isEmpty())
3201 return 0;
3203 QStringRef ref(&name, 1, name.length() - 1);
3204 switch (name.at(0).unicode()) {
3205 case 'a':
3206 if (ref == QLatin1String("nimation")) return createAnimationNode;
3207 break;
3208 case 'c':
3209 if (ref == QLatin1String("ircle")) return createCircleNode;
3210 break;
3211 case 'e':
3212 if (ref == QLatin1String("llipse")) return createEllipseNode;
3213 break;
3214 case 'i':
3215 if (ref == QLatin1String("mage")) return createImageNode;
3216 break;
3217 case 'l':
3218 if (ref == QLatin1String("ine")) return createLineNode;
3219 break;
3220 case 'p':
3221 if (ref == QLatin1String("ath")) return createPathNode;
3222 if (ref == QLatin1String("olygon")) return createPolygonNode;
3223 if (ref == QLatin1String("olyline")) return createPolylineNode;
3224 break;
3225 case 'r':
3226 if (ref == QLatin1String("ect")) return createRectNode;
3227 break;
3228 case 't':
3229 if (ref == QLatin1String("ext")) return createTextNode;
3230 if (ref == QLatin1String("extArea")) return createTextAreaNode;
3231 break;
3232 case 'u':
3233 if (ref == QLatin1String("se")) return createUseNode;
3234 break;
3235 case 'v':
3236 if (ref == QLatin1String("ideo")) return createVideoNode;
3237 break;
3238 default:
3239 break;
3241 return 0;
3244 typedef bool (*ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
3246 static ParseMethod findUtilFactory(const QString &name)
3248 if (name.isEmpty())
3249 return 0;
3251 QStringRef ref(&name, 1, name.length() - 1);
3252 switch (name.at(0).unicode()) {
3253 case 'a':
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;
3260 break;
3261 case 'd':
3262 if (ref == QLatin1String("esc")) return parseDescNode;
3263 if (ref == QLatin1String("iscard")) return parseDiscardNode;
3264 break;
3265 case 'f':
3266 if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
3267 break;
3268 case 'h':
3269 if (ref == QLatin1String("andler")) return parseHandlerNode;
3270 if (ref == QLatin1String("kern")) return parseHkernNode;
3271 break;
3272 case 'm':
3273 if (ref == QLatin1String("etadata")) return parseMetadataNode;
3274 if (ref == QLatin1String("path")) return parseMpathNode;
3275 break;
3276 case 'p':
3277 if (ref == QLatin1String("refetch")) return parsePrefetchNode;
3278 break;
3279 case 's':
3280 if (ref == QLatin1String("cript")) return parseScriptNode;
3281 if (ref == QLatin1String("et")) return parseSetNode;
3282 if (ref == QLatin1String("tyle")) return parseStyleNode;
3283 break;
3284 case 't':
3285 if (ref == QLatin1String("break")) return parseTbreakNode;
3286 if (ref == QLatin1String("itle")) return parseTitleNode;
3287 if (ref == QLatin1String("span")) return parseTspanNode;
3288 break;
3289 default:
3290 break;
3292 return 0;
3295 typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
3296 const QXmlStreamAttributes &,
3297 QSvgHandler *);
3299 static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
3301 if (name.isEmpty())
3302 return 0;
3304 QStringRef ref(&name, 1, name.length() - 1);
3305 switch (name.at(0).unicode()) {
3306 case 'f':
3307 if (ref == QLatin1String("ont")) return createFontNode;
3308 break;
3309 case 'l':
3310 if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
3311 break;
3312 case 'r':
3313 if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
3314 break;
3315 case 's':
3316 if (ref == QLatin1String("olidColor")) return createSolidColorNode;
3317 break;
3318 default:
3319 break;
3321 return 0;
3324 typedef bool (*StyleParseMethod)(QSvgStyleProperty *,
3325 const QXmlStreamAttributes &,
3326 QSvgHandler *);
3328 static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
3330 if (name.isEmpty())
3331 return 0;
3333 QStringRef ref(&name, 1, name.length() - 1);
3334 switch (name.at(0).unicode()) {
3335 case 'f':
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;
3340 break;
3341 case 'g':
3342 if (ref == QLatin1String("lyph")) return parseGlyphNode;
3343 break;
3344 case 'm':
3345 if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
3346 break;
3347 case 's':
3348 if (ref == QLatin1String("top")) return parseStopNode;
3349 break;
3350 default:
3351 break;
3353 return 0;
3356 QSvgHandler::QSvgHandler(QIODevice *device) : xml(new QXmlStreamReader(device))
3357 , m_ownsReader(true)
3359 init();
3362 QSvgHandler::QSvgHandler(const QByteArray &data) : xml(new QXmlStreamReader(data))
3363 , m_ownsReader(true)
3365 init();
3368 QSvgHandler::QSvgHandler(QXmlStreamReader *const reader) : xml(reader)
3369 , m_ownsReader(false)
3371 init();
3374 void QSvgHandler::init()
3376 m_doc = 0;
3377 m_style = 0;
3378 m_animEnd = 0;
3379 m_defaultCoords = LT_PX;
3380 m_defaultPen = QPen(Qt::black, 1, Qt::NoPen, Qt::FlatCap, Qt::SvgMiterJoin);
3381 m_defaultPen.setMiterLimit(4);
3382 parse();
3385 void QSvgHandler::parse()
3387 xml->setNamespaceProcessing(false);
3388 m_selector = new QSvgStyleSelector;
3389 m_inStyle = false;
3390 bool done = false;
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());
3403 break;
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"));
3409 break;
3410 case QXmlStreamReader::Characters:
3411 characters(xml->text());
3412 break;
3413 case QXmlStreamReader::ProcessingInstruction:
3414 processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
3415 break;
3416 default:
3422 bool QSvgHandler::startElement(const QString &localName,
3423 const QXmlStreamAttributes &attributes)
3425 QSvgNode *node = 0;
3427 if (m_colorTagCount.count()) {
3428 int top = m_colorTagCount.pop();
3429 ++top;
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);
3446 else
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)) {
3455 //group
3456 node = method(m_doc ? m_nodes.top() : 0, attributes, this);
3457 Q_ASSERT(node);
3458 if (!m_doc) {
3459 Q_ASSERT(node->type() == QSvgNode::DOC);
3460 m_doc = static_cast<QSvgTinyDocument*>(node);
3461 } else {
3462 switch (m_nodes.top()->type()) {
3463 case QSvgNode::DOC:
3464 case QSvgNode::G:
3465 case QSvgNode::DEFS:
3466 case QSvgNode::SWITCH:
3468 QSvgStructureNode *group =
3469 static_cast<QSvgStructureNode*>(m_nodes.top());
3470 group->addChild(node, someId(attributes));
3472 break;
3473 default:
3474 break;
3477 parseCoreNode(node, attributes);
3478 cssStyleLookup(node, this, m_selector);
3479 parseStyle(node, attributes, this);
3480 } else if (FactoryMethod method = findGraphicsFactory(localName)) {
3481 //rendering element
3482 Q_ASSERT(!m_nodes.isEmpty());
3483 node = method(m_nodes.top(), attributes, this);
3484 if (node) {
3485 switch (m_nodes.top()->type()) {
3486 case QSvgNode::DOC:
3487 case QSvgNode::G:
3488 case QSvgNode::DEFS:
3489 case QSvgNode::SWITCH:
3491 QSvgStructureNode *group =
3492 static_cast<QSvgStructureNode*>(m_nodes.top());
3493 group->addChild(node, someId(attributes));
3495 break;
3496 default:
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);
3504 else
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);
3514 if (prop) {
3515 m_style = prop;
3516 m_nodes.top()->appendStyleProperty(prop, someId(attributes), true);
3517 } else {
3518 qWarning("Couldn't parse node: %s", qPrintable(localName));
3520 } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3521 if (m_style) {
3522 if (!method(m_style, attributes, this)) {
3523 qWarning("Problem parsing %s", qPrintable(localName));
3526 } else {
3527 //qWarning()<<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
3528 m_skipNodes.push(Unknown);
3529 return true;
3532 if (node) {
3533 m_nodes.push(node);
3534 m_skipNodes.push(Graphics);
3535 } else {
3536 //qDebug()<<"Skipping "<<localName;
3537 m_skipNodes.push(Style);
3539 return true;
3542 bool QSvgHandler::endElement(const QStringRef &localName)
3544 CurrentNode node = m_skipNodes.top();
3545 m_skipNodes.pop();
3546 m_whitespaceMode.pop();
3548 if (m_colorTagCount.count()) {
3549 int top = m_colorTagCount.pop();
3550 --top;
3551 if (!top) {
3552 m_colorStack.pop();
3553 } else {
3554 m_colorTagCount.push(top);
3558 if (node == Unknown) {
3559 return true;
3562 if (m_inStyle && localName == QLatin1String("style")) {
3563 m_inStyle = false;
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"))
3567 node->popFormat();
3570 if (node == Graphics)
3571 m_nodes.pop();
3572 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3573 m_style = 0;
3575 return true;
3578 bool QSvgHandler::characters(const QStringRef &str)
3580 if (m_inStyle) {
3581 QString css = str.toString();
3582 QCss::StyleSheet sheet;
3583 QCss::Parser(css).parse(&sheet);
3584 m_selector->styleSheets.append(sheet);
3585 return true;
3586 } else if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown)
3587 return true;
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());
3594 return true;
3597 QSvgTinyDocument * QSvgHandler::document() const
3599 return m_doc;
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();
3622 else
3623 return QColor(0, 0, 0);
3626 void QSvgHandler::setInStyle(bool b)
3628 m_inStyle = b;
3631 bool QSvgHandler::inStyle() const
3633 return m_inStyle;
3636 QSvgStyleSelector * QSvgHandler::selector() const
3638 return m_selector;
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);
3646 bool isCss = false;
3647 int pos = 0;
3648 while ((pos = rx.indexIn(data, pos)) != -1) {
3649 QString type = rx.cap(1);
3650 if (type.toLower() == QLatin1String("text/css")) {
3651 isCss = true;
3653 pos += rx.matchedLength();
3656 if (isCss) {
3657 QRegExp rx(QLatin1String("href=\\\"(.+)\\\""));
3658 rx.setMinimal(true);
3659 pos = 0;
3660 pos = rx.indexIn(data, pos);
3661 QString addr = rx.cap(1);
3662 QFileInfo fi(addr);
3663 //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
3664 if (fi.exists()) {
3665 QFile file(fi.absoluteFilePath());
3666 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
3667 return true;
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);
3680 return true;
3683 void QSvgHandler::setAnimPeriod(int start, int end)
3685 Q_UNUSED(start);
3686 m_animEnd = qMax(end, m_animEnd);
3689 int QSvgHandler::animationDuration() const
3691 return m_animEnd;
3694 QSvgHandler::~QSvgHandler()
3696 delete m_selector;
3697 m_selector = 0;
3699 if(m_ownsReader)
3700 delete xml;
3703 QT_END_NAMESPACE
3705 #endif // QT_NO_SVG