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