source: trunk/src/svg/qsvghandler.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 127.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtSvg module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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_DECL_IMPORT 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') && 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 QSvgHandler::LengthType dummy; // should always be pixel size
1286 fontStyle->setSize(parseLength(attributes.fontSize.toString(), dummy, handler));
1287 }
1288
1289 if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
1290 if (attributes.fontStyle == QLatin1String("normal")) {
1291 fontStyle->setStyle(QFont::StyleNormal);
1292 } else if (attributes.fontStyle == QLatin1String("italic")) {
1293 fontStyle->setStyle(QFont::StyleItalic);
1294 } else if (attributes.fontStyle == QLatin1String("oblique")) {
1295 fontStyle->setStyle(QFont::StyleOblique);
1296 }
1297 }
1298
1299 if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
1300 bool ok = false;
1301 int weightNum = attributes.fontWeight.toString().toInt(&ok);
1302 if (ok) {
1303 fontStyle->setWeight(weightNum);
1304 } else {
1305 if (attributes.fontWeight == QLatin1String("normal")) {
1306 fontStyle->setWeight(400);
1307 } else if (attributes.fontWeight == QLatin1String("bold")) {
1308 fontStyle->setWeight(700);
1309 } else if (attributes.fontWeight == QLatin1String("bolder")) {
1310 fontStyle->setWeight(QSvgFontStyle::BOLDER);
1311 } else if (attributes.fontWeight == QLatin1String("lighter")) {
1312 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
1313 }
1314 }
1315 }
1316
1317 if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
1318 if (attributes.fontVariant == QLatin1String("normal"))
1319 fontStyle->setVariant(QFont::MixedCase);
1320 else if (attributes.fontVariant == QLatin1String("small-caps"))
1321 fontStyle->setVariant(QFont::SmallCaps);
1322 }
1323
1324 if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
1325 if (attributes.textAnchor == QLatin1String("start"))
1326 fontStyle->setTextAnchor(Qt::AlignLeft);
1327 if (attributes.textAnchor == QLatin1String("middle"))
1328 fontStyle->setTextAnchor(Qt::AlignHCenter);
1329 else if (attributes.textAnchor == QLatin1String("end"))
1330 fontStyle->setTextAnchor(Qt::AlignRight);
1331 }
1332
1333 node->appendStyleProperty(fontStyle, attributes.id);
1334}
1335
1336static void parseTransform(QSvgNode *node,
1337 const QSvgAttributes &attributes,
1338 QSvgHandler *)
1339{
1340 if (attributes.transform.isEmpty())
1341 return;
1342 QMatrix matrix = parseTransformationMatrix(trimRef(attributes.transform));
1343
1344 if (!matrix.isIdentity()) {
1345 node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), attributes.id);
1346 }
1347
1348}
1349
1350static void parseVisibility(QSvgNode *node,
1351 const QSvgAttributes &attributes,
1352 QSvgHandler *)
1353{
1354 QSvgNode *parent = node->parent();
1355
1356 if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
1357 node->setVisible(parent->isVisible());
1358 else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
1359 node->setVisible(false);
1360 } else
1361 node->setVisible(true);
1362}
1363
1364static void pathArcSegment(QPainterPath &path,
1365 qreal xc, qreal yc,
1366 qreal th0, qreal th1,
1367 qreal rx, qreal ry, qreal xAxisRotation)
1368{
1369 qreal sinTh, cosTh;
1370 qreal a00, a01, a10, a11;
1371 qreal x1, y1, x2, y2, x3, y3;
1372 qreal t;
1373 qreal thHalf;
1374
1375 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
1376 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
1377
1378 a00 = cosTh * rx;
1379 a01 = -sinTh * ry;
1380 a10 = sinTh * rx;
1381 a11 = cosTh * ry;
1382
1383 thHalf = 0.5 * (th1 - th0);
1384 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
1385 x1 = xc + qCos(th0) - t * qSin(th0);
1386 y1 = yc + qSin(th0) + t * qCos(th0);
1387 x3 = xc + qCos(th1);
1388 y3 = yc + qSin(th1);
1389 x2 = x3 + t * qSin(th1);
1390 y2 = y3 - t * qCos(th1);
1391
1392 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
1393 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
1394 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
1395}
1396
1397// the arc handling code underneath is from XSVG (BSD license)
1398/*
1399 * Copyright 2002 USC/Information Sciences Institute
1400 *
1401 * Permission to use, copy, modify, distribute, and sell this software
1402 * and its documentation for any purpose is hereby granted without
1403 * fee, provided that the above copyright notice appear in all copies
1404 * and that both that copyright notice and this permission notice
1405 * appear in supporting documentation, and that the name of
1406 * Information Sciences Institute not be used in advertising or
1407 * publicity pertaining to distribution of the software without
1408 * specific, written prior permission. Information Sciences Institute
1409 * makes no representations about the suitability of this software for
1410 * any purpose. It is provided "as is" without express or implied
1411 * warranty.
1412 *
1413 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
1414 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
1415 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
1416 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
1417 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
1418 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1419 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
1420 * PERFORMANCE OF THIS SOFTWARE.
1421 *
1422 */
1423static void pathArc(QPainterPath &path,
1424 qreal rx,
1425 qreal ry,
1426 qreal x_axis_rotation,
1427 int large_arc_flag,
1428 int sweep_flag,
1429 qreal x,
1430 qreal y,
1431 qreal curx, qreal cury)
1432{
1433 qreal sin_th, cos_th;
1434 qreal a00, a01, a10, a11;
1435 qreal x0, y0, x1, y1, xc, yc;
1436 qreal d, sfactor, sfactor_sq;
1437 qreal th0, th1, th_arc;
1438 int i, n_segs;
1439 qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
1440
1441 rx = qAbs(rx);
1442 ry = qAbs(ry);
1443
1444 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
1445 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
1446
1447 dx = (curx - x) / 2.0;
1448 dy = (cury - y) / 2.0;
1449 dx1 = cos_th * dx + sin_th * dy;
1450 dy1 = -sin_th * dx + cos_th * dy;
1451 Pr1 = rx * rx;
1452 Pr2 = ry * ry;
1453 Px = dx1 * dx1;
1454 Py = dy1 * dy1;
1455 /* Spec : check if radii are large enough */
1456 check = Px / Pr1 + Py / Pr2;
1457 if (check > 1) {
1458 rx = rx * qSqrt(check);
1459 ry = ry * qSqrt(check);
1460 }
1461
1462 a00 = cos_th / rx;
1463 a01 = sin_th / rx;
1464 a10 = -sin_th / ry;
1465 a11 = cos_th / ry;
1466 x0 = a00 * curx + a01 * cury;
1467 y0 = a10 * curx + a11 * cury;
1468 x1 = a00 * x + a01 * y;
1469 y1 = a10 * x + a11 * y;
1470 /* (x0, y0) is current point in transformed coordinate space.
1471 (x1, y1) is new point in transformed coordinate space.
1472
1473 The arc fits a unit-radius circle in this space.
1474 */
1475 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
1476 sfactor_sq = 1.0 / d - 0.25;
1477 if (sfactor_sq < 0) sfactor_sq = 0;
1478 sfactor = qSqrt(sfactor_sq);
1479 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
1480 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
1481 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
1482 /* (xc, yc) is center of the circle. */
1483
1484 th0 = qAtan2(y0 - yc, x0 - xc);
1485 th1 = qAtan2(y1 - yc, x1 - xc);
1486
1487 th_arc = th1 - th0;
1488 if (th_arc < 0 && sweep_flag)
1489 th_arc += 2 * Q_PI;
1490 else if (th_arc > 0 && !sweep_flag)
1491 th_arc -= 2 * Q_PI;
1492
1493 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
1494
1495 for (i = 0; i < n_segs; i++) {
1496 pathArcSegment(path, xc, yc,
1497 th0 + i * th_arc / n_segs,
1498 th0 + (i + 1) * th_arc / n_segs,
1499 rx, ry, x_axis_rotation);
1500 }
1501}
1502
1503static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
1504{
1505 qreal x0 = 0, y0 = 0; // starting point
1506 qreal x = 0, y = 0; // current point
1507 char lastMode = 0;
1508 QPointF ctrlPt;
1509 const QChar *str = dataStr.constData();
1510 const QChar *end = str + dataStr.size();
1511
1512 while (str != end) {
1513 while (str->isSpace())
1514 ++str;
1515 QChar pathElem = *str;
1516 ++str;
1517 QChar endc = *end;
1518 *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
1519 QVarLengthArray<qreal, 8> arg;
1520 parseNumbersArray(str, arg);
1521 *const_cast<QChar *>(end) = endc;
1522 if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
1523 arg.append(0);//dummy
1524 const qreal *num = arg.constData();
1525 int count = arg.count();
1526 while (count > 0) {
1527 qreal offsetX = x; // correction offsets
1528 qreal offsetY = y; // for relative commands
1529 switch (pathElem.unicode()) {
1530 case 'm': {
1531 if (count < 2) {
1532 num++;
1533 count--;
1534 break;
1535 }
1536 x = x0 = num[0] + offsetX;
1537 y = y0 = num[1] + offsetY;
1538 num += 2;
1539 count -= 2;
1540 path.moveTo(x0, y0);
1541
1542 // As per 1.2 spec 8.3.2 The "moveto" commands
1543 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1544 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1545 pathElem = QLatin1Char('l');
1546 }
1547 break;
1548 case 'M': {
1549 if (count < 2) {
1550 num++;
1551 count--;
1552 break;
1553 }
1554 x = x0 = num[0];
1555 y = y0 = num[1];
1556 num += 2;
1557 count -= 2;
1558 path.moveTo(x0, y0);
1559
1560 // As per 1.2 spec 8.3.2 The "moveto" commands
1561 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
1562 // the subsequent pairs shall be treated as implicit 'lineto' commands.
1563 pathElem = QLatin1Char('L');
1564 }
1565 break;
1566 case 'z':
1567 case 'Z': {
1568 x = x0;
1569 y = y0;
1570 count--; // skip dummy
1571 num++;
1572 path.closeSubpath();
1573 }
1574 break;
1575 case 'l': {
1576 if (count < 2) {
1577 num++;
1578 count--;
1579 break;
1580 }
1581 x = num[0] + offsetX;
1582 y = num[1] + offsetY;
1583 num += 2;
1584 count -= 2;
1585 path.lineTo(x, y);
1586
1587 }
1588 break;
1589 case 'L': {
1590 if (count < 2) {
1591 num++;
1592 count--;
1593 break;
1594 }
1595 x = num[0];
1596 y = num[1];
1597 num += 2;
1598 count -= 2;
1599 path.lineTo(x, y);
1600 }
1601 break;
1602 case 'h': {
1603 x = num[0] + offsetX;
1604 num++;
1605 count--;
1606 path.lineTo(x, y);
1607 }
1608 break;
1609 case 'H': {
1610 x = num[0];
1611 num++;
1612 count--;
1613 path.lineTo(x, y);
1614 }
1615 break;
1616 case 'v': {
1617 y = num[0] + offsetY;
1618 num++;
1619 count--;
1620 path.lineTo(x, y);
1621 }
1622 break;
1623 case 'V': {
1624 y = num[0];
1625 num++;
1626 count--;
1627 path.lineTo(x, y);
1628 }
1629 break;
1630 case 'c': {
1631 if (count < 6) {
1632 num += count;
1633 count = 0;
1634 break;
1635 }
1636 QPointF c1(num[0] + offsetX, num[1] + offsetY);
1637 QPointF c2(num[2] + offsetX, num[3] + offsetY);
1638 QPointF e(num[4] + offsetX, num[5] + offsetY);
1639 num += 6;
1640 count -= 6;
1641 path.cubicTo(c1, c2, e);
1642 ctrlPt = c2;
1643 x = e.x();
1644 y = e.y();
1645 break;
1646 }
1647 case 'C': {
1648 if (count < 6) {
1649 num += count;
1650 count = 0;
1651 break;
1652 }
1653 QPointF c1(num[0], num[1]);
1654 QPointF c2(num[2], num[3]);
1655 QPointF e(num[4], num[5]);
1656 num += 6;
1657 count -= 6;
1658 path.cubicTo(c1, c2, e);
1659 ctrlPt = c2;
1660 x = e.x();
1661 y = e.y();
1662 break;
1663 }
1664 case 's': {
1665 if (count < 4) {
1666 num += count;
1667 count = 0;
1668 break;
1669 }
1670 QPointF c1;
1671 if (lastMode == 'c' || lastMode == 'C' ||
1672 lastMode == 's' || lastMode == 'S')
1673 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1674 else
1675 c1 = QPointF(x, y);
1676 QPointF c2(num[0] + offsetX, num[1] + offsetY);
1677 QPointF e(num[2] + offsetX, num[3] + offsetY);
1678 num += 4;
1679 count -= 4;
1680 path.cubicTo(c1, c2, e);
1681 ctrlPt = c2;
1682 x = e.x();
1683 y = e.y();
1684 break;
1685 }
1686 case 'S': {
1687 if (count < 4) {
1688 num += count;
1689 count = 0;
1690 break;
1691 }
1692 QPointF c1;
1693 if (lastMode == 'c' || lastMode == 'C' ||
1694 lastMode == 's' || lastMode == 'S')
1695 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1696 else
1697 c1 = QPointF(x, y);
1698 QPointF c2(num[0], num[1]);
1699 QPointF e(num[2], num[3]);
1700 num += 4;
1701 count -= 4;
1702 path.cubicTo(c1, c2, e);
1703 ctrlPt = c2;
1704 x = e.x();
1705 y = e.y();
1706 break;
1707 }
1708 case 'q': {
1709 if (count < 4) {
1710 num += count;
1711 count = 0;
1712 break;
1713 }
1714 QPointF c(num[0] + offsetX, num[1] + offsetY);
1715 QPointF e(num[2] + offsetX, num[3] + offsetY);
1716 num += 4;
1717 count -= 4;
1718 path.quadTo(c, e);
1719 ctrlPt = c;
1720 x = e.x();
1721 y = e.y();
1722 break;
1723 }
1724 case 'Q': {
1725 if (count < 4) {
1726 num += count;
1727 count = 0;
1728 break;
1729 }
1730 QPointF c(num[0], num[1]);
1731 QPointF e(num[2], num[3]);
1732 num += 4;
1733 count -= 4;
1734 path.quadTo(c, e);
1735 ctrlPt = c;
1736 x = e.x();
1737 y = e.y();
1738 break;
1739 }
1740 case 't': {
1741 if (count < 2) {
1742 num += count;
1743 count = 0;
1744 break;
1745 }
1746 QPointF e(num[0] + offsetX, num[1] + offsetY);
1747 num += 2;
1748 count -= 2;
1749 QPointF c;
1750 if (lastMode == 'q' || lastMode == 'Q' ||
1751 lastMode == 't' || lastMode == 'T')
1752 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1753 else
1754 c = QPointF(x, y);
1755 path.quadTo(c, e);
1756 ctrlPt = c;
1757 x = e.x();
1758 y = e.y();
1759 break;
1760 }
1761 case 'T': {
1762 if (count < 2) {
1763 num += count;
1764 count = 0;
1765 break;
1766 }
1767 QPointF e(num[0], num[1]);
1768 num += 2;
1769 count -= 2;
1770 QPointF c;
1771 if (lastMode == 'q' || lastMode == 'Q' ||
1772 lastMode == 't' || lastMode == 'T')
1773 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
1774 else
1775 c = QPointF(x, y);
1776 path.quadTo(c, e);
1777 ctrlPt = c;
1778 x = e.x();
1779 y = e.y();
1780 break;
1781 }
1782 case 'a': {
1783 if (count < 7) {
1784 num += count;
1785 count = 0;
1786 break;
1787 }
1788 qreal rx = (*num++);
1789 qreal ry = (*num++);
1790 qreal xAxisRotation = (*num++);
1791 qreal largeArcFlag = (*num++);
1792 qreal sweepFlag = (*num++);
1793 qreal ex = (*num++) + offsetX;
1794 qreal ey = (*num++) + offsetY;
1795 count -= 7;
1796 qreal curx = x;
1797 qreal cury = y;
1798 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1799 int(sweepFlag), ex, ey, curx, cury);
1800
1801 x = ex;
1802 y = ey;
1803 }
1804 break;
1805 case 'A': {
1806 if (count < 7) {
1807 num += count;
1808 count = 0;
1809 break;
1810 }
1811 qreal rx = (*num++);
1812 qreal ry = (*num++);
1813 qreal xAxisRotation = (*num++);
1814 qreal largeArcFlag = (*num++);
1815 qreal sweepFlag = (*num++);
1816 qreal ex = (*num++);
1817 qreal ey = (*num++);
1818 count -= 7;
1819 qreal curx = x;
1820 qreal cury = y;
1821 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
1822 int(sweepFlag), ex, ey, curx, cury);
1823
1824 x = ex;
1825 y = ey;
1826 }
1827 break;
1828 default:
1829 return false;
1830 }
1831 lastMode = pathElem.toLatin1();
1832 }
1833 }
1834 return true;
1835}
1836
1837static bool parseStyle(QSvgNode *node,
1838 const QXmlStreamAttributes &attributes,
1839 QSvgHandler *);
1840
1841static bool parseStyle(QSvgNode *node,
1842 const QSvgAttributes &attributes,
1843 QSvgHandler *);
1844
1845static void parseCSStoXMLAttrs(const QVector<QCss::Declaration> &declarations,
1846 QXmlStreamAttributes &attributes)
1847{
1848 for (int i = 0; i < declarations.count(); ++i) {
1849 const QCss::Declaration &decl = declarations.at(i);
1850 if (decl.d->property.isEmpty())
1851 continue;
1852 QCss::Value val = decl.d->values.first();
1853 QString valueStr;
1854 if (decl.d->values.count() != 1) {
1855 for (int i=0; i<decl.d->values.count(); ++i) {
1856 const QString &value = decl.d->values[i].toString();
1857 if (value.isEmpty())
1858 valueStr += QLatin1Char(',');
1859 else
1860 valueStr += value;
1861 }
1862 } else {
1863 valueStr = val.toString();
1864 }
1865 if (val.type == QCss::Value::Uri) {
1866 valueStr.prepend(QLatin1String("url("));
1867 valueStr.append(QLatin1Char(')'));
1868 } else if (val.type == QCss::Value::Function) {
1869 QStringList lst = val.variant.toStringList();
1870 valueStr.append(lst.at(0));
1871 valueStr.append(QLatin1Char('('));
1872 for (int i = 1; i < lst.count(); ++i) {
1873 valueStr.append(lst.at(i));
1874 if ((i +1) < lst.count())
1875 valueStr.append(QLatin1Char(','));
1876 }
1877 valueStr.append(QLatin1Char(')'));
1878 } else if (val.type == QCss::Value::KnownIdentifier) {
1879 switch (val.variant.toInt()) {
1880 case QCss::Value_None:
1881 valueStr = QLatin1String("none");
1882 break;
1883 default:
1884 break;
1885 }
1886 }
1887
1888 attributes.append(QString(), decl.d->property, valueStr);
1889 }
1890}
1891
1892void QSvgHandler::parseCSStoXMLAttrs(QString css, QVector<QSvgCssAttribute> *attributes)
1893{
1894 // preprocess (for unicode escapes), tokenize and remove comments
1895 m_cssParser.init(css);
1896 QString key;
1897
1898 attributes->reserve(10);
1899
1900 while (m_cssParser.hasNext()) {
1901 m_cssParser.skipSpace();
1902
1903 if (!m_cssParser.hasNext())
1904 break;
1905 m_cssParser.next();
1906
1907 QStringRef name;
1908 if (m_cssParser.hasEscapeSequences) {
1909 key = m_cssParser.lexem();
1910 name = QStringRef(&key, 0, key.length());
1911 } else {
1912 const QCss::Symbol &sym = m_cssParser.symbol();
1913 name = QStringRef(&sym.text, sym.start, sym.len);
1914 }
1915
1916 m_cssParser.skipSpace();
1917 if (!m_cssParser.test(QCss::COLON))
1918 break;
1919
1920 m_cssParser.skipSpace();
1921 if (!m_cssParser.hasNext())
1922 break;
1923
1924 QSvgCssAttribute attribute;
1925 attribute.name = QXmlStreamStringRef(name);
1926
1927 const int firstSymbol = m_cssParser.index;
1928 int symbolCount = 0;
1929 do {
1930 m_cssParser.next();
1931 ++symbolCount;
1932 } while (m_cssParser.hasNext() && !m_cssParser.test(QCss::SEMICOLON));
1933
1934 bool canExtractValueByRef = !m_cssParser.hasEscapeSequences;
1935 if (canExtractValueByRef) {
1936 int len = m_cssParser.symbols.at(firstSymbol).len;
1937 for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
1938 len += m_cssParser.symbols.at(i).len;
1939
1940 if (m_cssParser.symbols.at(i - 1).start + m_cssParser.symbols.at(i - 1).len
1941 != m_cssParser.symbols.at(i).start) {
1942 canExtractValueByRef = false;
1943 break;
1944 }
1945 }
1946 if (canExtractValueByRef) {
1947 const QCss::Symbol &sym = m_cssParser.symbols.at(firstSymbol);
1948 attribute.value = QXmlStreamStringRef(QStringRef(&sym.text, sym.start, len));
1949 }
1950 }
1951 if (!canExtractValueByRef) {
1952 QString value;
1953 for (int i = firstSymbol; i < m_cssParser.index - 1; ++i)
1954 value += m_cssParser.symbols.at(i).lexem();
1955 attribute.value = QXmlStreamStringRef(QStringRef(&value, 0, value.length()));
1956 }
1957
1958 attributes->append(attribute);
1959
1960 m_cssParser.skipSpace();
1961 }
1962}
1963
1964static void cssStyleLookup(QSvgNode *node,
1965 QSvgHandler *handler,
1966 QSvgStyleSelector *selector)
1967{
1968 QCss::StyleSelector::NodePtr cssNode;
1969 cssNode.ptr = node;
1970 QVector<QCss::Declaration> decls = selector->declarationsForNode(cssNode);
1971
1972 QXmlStreamAttributes attributes;
1973 parseCSStoXMLAttrs(decls, attributes);
1974 parseStyle(node, attributes, handler);
1975}
1976
1977static inline QStringList stringToList(const QString &str)
1978{
1979 QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts);
1980 return lst;
1981}
1982
1983static bool parseCoreNode(QSvgNode *node,
1984 const QXmlStreamAttributes &attributes)
1985{
1986 QStringList features;
1987 QStringList extensions;
1988 QStringList languages;
1989 QStringList formats;
1990 QStringList fonts;
1991 QString xmlClassStr;
1992
1993 for (int i = 0; i < attributes.count(); ++i) {
1994 const QXmlStreamAttribute &attribute = attributes.at(i);
1995 QStringRef name = attribute.qualifiedName();
1996 if (name.isEmpty())
1997 continue;
1998 QStringRef value = attribute.value();
1999 switch (name.at(0).unicode()) {
2000 case 'c':
2001 if (name == QLatin1String("class"))
2002 xmlClassStr = value.toString();
2003 break;
2004 case 'r':
2005 if (name == QLatin1String("requiredFeatures"))
2006 features = stringToList(value.toString());
2007 else if (name == QLatin1String("requiredExtensions"))
2008 extensions = stringToList(value.toString());
2009 else if (name == QLatin1String("requiredFormats"))
2010 formats = stringToList(value.toString());
2011 else if (name == QLatin1String("requiredFonts"))
2012 fonts = stringToList(value.toString());
2013 break;
2014 case 's':
2015 if (name == QLatin1String("systemLanguage"))
2016 languages = stringToList(value.toString());
2017 break;
2018 default:
2019 break;
2020 }
2021 }
2022
2023 node->setRequiredFeatures(features);
2024 node->setRequiredExtensions(extensions);
2025 node->setRequiredLanguages(languages);
2026 node->setRequiredFormats(formats);
2027 node->setRequiredFonts(fonts);
2028 node->setNodeId(someId(attributes));
2029 node->setXmlClass(xmlClassStr);
2030
2031 return true;
2032}
2033
2034static void parseOpacity(QSvgNode *node,
2035 const QSvgAttributes &attributes,
2036 QSvgHandler *)
2037{
2038 if (attributes.opacity.isEmpty())
2039 return;
2040
2041 const QString value = attributes.opacity.toString().trimmed();
2042
2043 bool ok = false;
2044 qreal op = value.toDouble(&ok);
2045
2046 if (ok) {
2047 QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
2048 node->appendStyleProperty(opacity, attributes.id);
2049 }
2050}
2051
2052static QPainter::CompositionMode svgToQtCompositionMode(const QString &op)
2053{
2054#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
2055 if (op == QLatin1String("clear")) {
2056 return QPainter::CompositionMode_Clear;
2057 } else if (op == QLatin1String("src")) {
2058 return QPainter::CompositionMode_Source;
2059 } else if (op == QLatin1String("dst")) {
2060 return QPainter::CompositionMode_Destination;
2061 } else if (op == QLatin1String("src-over")) {
2062 return QPainter::CompositionMode_SourceOver;
2063 } else if (op == QLatin1String("dst-over")) {
2064 return QPainter::CompositionMode_DestinationOver;
2065 } else if (op == QLatin1String("src-in")) {
2066 return QPainter::CompositionMode_SourceIn;
2067 } else if (op == QLatin1String("dst-in")) {
2068 return QPainter::CompositionMode_DestinationIn;
2069 } else if (op == QLatin1String("src-out")) {
2070 return QPainter::CompositionMode_SourceOut;
2071 } else if (op == QLatin1String("dst-out")) {
2072 return QPainter::CompositionMode_DestinationOut;
2073 } else if (op == QLatin1String("src-atop")) {
2074 return QPainter::CompositionMode_SourceAtop;
2075 } else if (op == QLatin1String("dst-atop")) {
2076 return QPainter::CompositionMode_DestinationAtop;
2077 } else if (op == QLatin1String("xor")) {
2078 return QPainter::CompositionMode_Xor;
2079 } else if (op == QLatin1String("plus")) {
2080 return QPainter::CompositionMode_Plus;
2081 } else if (op == QLatin1String("multiply")) {
2082 return QPainter::CompositionMode_Multiply;
2083 } else if (op == QLatin1String("screen")) {
2084 return QPainter::CompositionMode_Screen;
2085 } else if (op == QLatin1String("overlay")) {
2086 return QPainter::CompositionMode_Overlay;
2087 } else if (op == QLatin1String("darken")) {
2088 return QPainter::CompositionMode_Darken;
2089 } else if (op == QLatin1String("lighten")) {
2090 return QPainter::CompositionMode_Lighten;
2091 } else if (op == QLatin1String("color-dodge")) {
2092 return QPainter::CompositionMode_ColorDodge;
2093 } else if (op == QLatin1String("color-burn")) {
2094 return QPainter::CompositionMode_ColorBurn;
2095 } else if (op == QLatin1String("hard-light")) {
2096 return QPainter::CompositionMode_HardLight;
2097 } else if (op == QLatin1String("soft-light")) {
2098 return QPainter::CompositionMode_SoftLight;
2099 } else if (op == QLatin1String("difference")) {
2100 return QPainter::CompositionMode_Difference;
2101 } else if (op == QLatin1String("exclusion")) {
2102 return QPainter::CompositionMode_Exclusion;
2103 } else {
2104 NOOP;
2105 }
2106
2107 return QPainter::CompositionMode_SourceOver;
2108}
2109
2110static void parseCompOp(QSvgNode *node,
2111 const QSvgAttributes &attributes,
2112 QSvgHandler *)
2113{
2114 if (attributes.compOp.isEmpty())
2115 return;
2116 QString value = attributes.compOp.toString().trimmed();
2117
2118 if (!value.isEmpty()) {
2119 QSvgCompOpStyle *compop = new QSvgCompOpStyle(svgToQtCompositionMode(value));
2120 node->appendStyleProperty(compop, attributes.id);
2121 }
2122}
2123
2124static inline QSvgNode::DisplayMode displayStringToEnum(const QString &str)
2125{
2126 if (str == QLatin1String("inline")) {
2127 return QSvgNode::InlineMode;
2128 } else if (str == QLatin1String("block")) {
2129 return QSvgNode::BlockMode;
2130 } else if (str == QLatin1String("list-item")) {
2131 return QSvgNode::ListItemMode;
2132 } else if (str == QLatin1String("run-in")) {
2133 return QSvgNode::RunInMode;
2134 } else if (str == QLatin1String("compact")) {
2135 return QSvgNode::CompactMode;
2136 } else if (str == QLatin1String("marker")) {
2137 return QSvgNode::MarkerMode;
2138 } else if (str == QLatin1String("table")) {
2139 return QSvgNode::TableMode;
2140 } else if (str == QLatin1String("inline-table")) {
2141 return QSvgNode::InlineTableMode;
2142 } else if (str == QLatin1String("table-row")) {
2143 return QSvgNode::TableRowGroupMode;
2144 } else if (str == QLatin1String("table-header-group")) {
2145 return QSvgNode::TableHeaderGroupMode;
2146 } else if (str == QLatin1String("table-footer-group")) {
2147 return QSvgNode::TableFooterGroupMode;
2148 } else if (str == QLatin1String("table-row")) {
2149 return QSvgNode::TableRowMode;
2150 } else if (str == QLatin1String("table-column-group")) {
2151 return QSvgNode::TableColumnGroupMode;
2152 } else if (str == QLatin1String("table-column")) {
2153 return QSvgNode::TableColumnMode;
2154 } else if (str == QLatin1String("table-cell")) {
2155 return QSvgNode::TableCellMode;
2156 } else if (str == QLatin1String("table-caption")) {
2157 return QSvgNode::TableCaptionMode;
2158 } else if (str == QLatin1String("none")) {
2159 return QSvgNode::NoneMode;
2160 } else if (str == QT_INHERIT) {
2161 return QSvgNode::InheritMode;
2162 }
2163 return QSvgNode::BlockMode;
2164}
2165
2166static void parseOthers(QSvgNode *node,
2167 const QSvgAttributes &attributes,
2168 QSvgHandler *)
2169{
2170 if (attributes.display.isEmpty())
2171 return;
2172 QString displayStr = attributes.display.toString().trimmed();
2173
2174 if (!displayStr.isEmpty()) {
2175 node->setDisplayMode(displayStringToEnum(displayStr));
2176 }
2177}
2178
2179static bool parseStyle(QSvgNode *node,
2180 const QSvgAttributes &attributes,
2181 QSvgHandler *handler)
2182{
2183 parseColor(node, attributes, handler);
2184 parseBrush(node, attributes, handler);
2185 parsePen(node, attributes, handler);
2186 parseFont(node, attributes, handler);
2187 parseTransform(node, attributes, handler);
2188 parseVisibility(node, attributes, handler);
2189 parseOpacity(node, attributes, handler);
2190 parseCompOp(node, attributes, handler);
2191 parseOthers(node, attributes, handler);
2192#if 0
2193 value = attributes.value("audio-level");
2194
2195 value = attributes.value("color-rendering");
2196
2197 value = attributes.value("display-align");
2198
2199 value = attributes.value("image-rendering");
2200
2201 value = attributes.value("line-increment");
2202
2203 value = attributes.value("pointer-events");
2204
2205 value = attributes.value("shape-rendering");
2206
2207 value = attributes.value("solid-color");
2208
2209 value = attributes.value("solid-opacity");
2210
2211 value = attributes.value("text-rendering");
2212
2213 value = attributes.value("vector-effect");
2214
2215 value = attributes.value("viewport-fill");
2216
2217 value = attributes.value("viewport-fill-opacity");
2218#endif
2219 return true;
2220}
2221
2222static bool parseStyle(QSvgNode *node,
2223 const QXmlStreamAttributes &attrs,
2224 QSvgHandler *handler)
2225{
2226 return parseStyle(node, QSvgAttributes(attrs, handler), handler);
2227}
2228
2229static bool parseAnchorNode(QSvgNode *parent,
2230 const QXmlStreamAttributes &attributes,
2231 QSvgHandler *)
2232{
2233 Q_UNUSED(parent); Q_UNUSED(attributes);
2234 return true;
2235}
2236
2237static bool parseAnimateNode(QSvgNode *parent,
2238 const QXmlStreamAttributes &attributes,
2239 QSvgHandler *)
2240{
2241 Q_UNUSED(parent); Q_UNUSED(attributes);
2242 return true;
2243}
2244
2245static bool parseAnimateColorNode(QSvgNode *parent,
2246 const QXmlStreamAttributes &attributes,
2247 QSvgHandler *handler)
2248{
2249 QString typeStr = attributes.value(QLatin1String("type")).toString();
2250 QStringRef fromStr = attributes.value(QLatin1String("from"));
2251 QStringRef toStr = attributes.value(QLatin1String("to"));
2252 QString valuesStr = attributes.value(QLatin1String("values")).toString();
2253 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2254 QString durStr = attributes.value(QLatin1String("dur")).toString();
2255 QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
2256 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2257 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2258
2259 QList<QColor> colors;
2260 if (valuesStr.isEmpty()) {
2261 QColor startColor, endColor;
2262 resolveColor(fromStr, startColor, handler);
2263 resolveColor(toStr, endColor, handler);
2264 colors.append(startColor);
2265 colors.append(endColor);
2266 } else {
2267 QStringList str = valuesStr.split(QLatin1Char(';'));
2268 QStringList::const_iterator itr;
2269 for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
2270 QColor color;
2271 QString str = *itr;
2272 resolveColor(QStringRef(&str), color, handler);
2273 colors.append(color);
2274 }
2275 }
2276
2277 int ms = 1000;
2278 beginStr = beginStr.trimmed();
2279 if (beginStr.endsWith(QLatin1String("ms"))) {
2280 beginStr.chop(2);
2281 ms = 1;
2282 } else if (beginStr.endsWith(QLatin1String("s"))) {
2283 beginStr.chop(1);
2284 }
2285 durStr = durStr.trimmed();
2286 if (durStr.endsWith(QLatin1String("ms"))) {
2287 durStr.chop(2);
2288 ms = 1;
2289 } else if (durStr.endsWith(QLatin1String("s"))) {
2290 durStr.chop(1);
2291 }
2292 int begin = static_cast<int>(toDouble(beginStr) * ms);
2293 int end = static_cast<int>((toDouble(durStr) + begin) * ms);
2294
2295 QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
2296 anim->setArgs((targetStr == QLatin1String("fill")), colors);
2297 anim->setFreeze(fillStr == QLatin1String("freeze"));
2298 anim->setRepeatCount(
2299 (repeatStr == QLatin1String("indefinite")) ? -1 :
2300 (repeatStr == QLatin1String("")) ? 1 : toDouble(repeatStr));
2301
2302 parent->appendStyleProperty(anim, someId(attributes));
2303 parent->document()->setAnimated(true);
2304 handler->setAnimPeriod(begin, end);
2305 return true;
2306}
2307
2308static bool parseAimateMotionNode(QSvgNode *parent,
2309 const QXmlStreamAttributes &attributes,
2310 QSvgHandler *)
2311{
2312 Q_UNUSED(parent); Q_UNUSED(attributes);
2313 return true;
2314}
2315
2316static void parseNumberTriplet(QVector<qreal> &values, const QChar *&s)
2317{
2318 QVector<qreal> list = parseNumbersList(s);
2319 values << list;
2320 for (int i = 3 - list.size(); i > 0; --i)
2321 values.append(0.0);
2322}
2323
2324static bool parseAnimateTransformNode(QSvgNode *parent,
2325 const QXmlStreamAttributes &attributes,
2326 QSvgHandler *handler)
2327{
2328 QString typeStr = attributes.value(QLatin1String("type")).toString();
2329 QString values = attributes.value(QLatin1String("values")).toString();
2330 QString beginStr = attributes.value(QLatin1String("begin")).toString();
2331 QString durStr = attributes.value(QLatin1String("dur")).toString();
2332 QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
2333 QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
2334 QString fillStr = attributes.value(QLatin1String("fill")).toString();
2335 QString fromStr = attributes.value(QLatin1String("from")).toString();
2336 QString toStr = attributes.value(QLatin1String("to")).toString();
2337 QString byStr = attributes.value(QLatin1String("by")).toString();
2338 QString addtv = attributes.value(QLatin1String("additive")).toString();
2339
2340 QSvgAnimateTransform::Additive additive = QSvgAnimateTransform::Replace;
2341 if (addtv == QLatin1String("sum"))
2342 additive = QSvgAnimateTransform::Sum;
2343
2344 QVector<qreal> vals;
2345 if (values.isEmpty()) {
2346 const QChar *s;
2347 if (fromStr.isEmpty()) {
2348 if (!byStr.isEmpty()) {
2349 // By-animation.
2350 additive = QSvgAnimateTransform::Sum;
2351 vals.append(0.0);
2352 vals.append(0.0);
2353 vals.append(0.0);
2354 parseNumberTriplet(vals, s = byStr.constData());
2355 } else {
2356 // To-animation not defined.
2357 return false;
2358 }
2359 } else {
2360 if (!toStr.isEmpty()) {
2361 // From-to-animation.
2362 parseNumberTriplet(vals, s = fromStr.constData());
2363 parseNumberTriplet(vals, s = toStr.constData());
2364 } else if (!byStr.isEmpty()) {
2365 // From-by-animation.
2366 parseNumberTriplet(vals, s = fromStr.constData());
2367 parseNumberTriplet(vals, s = byStr.constData());
2368 for (int i = vals.size() - 3; i < vals.size(); ++i)
2369 vals[i] += vals[i - 3];
2370 } else {
2371 return false;
2372 }
2373 }
2374 } else {
2375 const QChar *s = values.constData();
2376 while (s && *s != QLatin1Char(0)) {
2377 parseNumberTriplet(vals, s);
2378 if (*s == QLatin1Char(0))
2379 break;
2380 ++s;
2381 }
2382 }
2383
2384 int ms = 1000;
2385 beginStr = beginStr.trimmed();
2386 if (beginStr.endsWith(QLatin1String("ms"))) {
2387 beginStr.chop(2);
2388 ms = 1;
2389 } else if (beginStr.endsWith(QLatin1String("s"))) {
2390 beginStr.chop(1);
2391 }
2392 int begin = static_cast<int>(toDouble(beginStr) * ms);
2393 durStr = durStr.trimmed();
2394 if (durStr.endsWith(QLatin1String("ms"))) {
2395 durStr.chop(2);
2396 ms = 1;
2397 } else if (durStr.endsWith(QLatin1String("s"))) {
2398 durStr.chop(1);
2399 ms = 1000;
2400 }
2401 int end = static_cast<int>(toDouble(durStr)*ms) + begin;
2402
2403 QSvgAnimateTransform::TransformType type = QSvgAnimateTransform::Empty;
2404 if (typeStr == QLatin1String("translate")) {
2405 type = QSvgAnimateTransform::Translate;
2406 } else if (typeStr == QLatin1String("scale")) {
2407 type = QSvgAnimateTransform::Scale;
2408 } else if (typeStr == QLatin1String("rotate")) {
2409 type = QSvgAnimateTransform::Rotate;
2410 } else if (typeStr == QLatin1String("skewX")) {
2411 type = QSvgAnimateTransform::SkewX;
2412 } else if (typeStr == QLatin1String("skewY")) {
2413 type = QSvgAnimateTransform::SkewY;
2414 } else {
2415 return false;
2416 }
2417
2418 QSvgAnimateTransform *anim = new QSvgAnimateTransform(begin, end, 0);
2419 anim->setArgs(type, additive, vals);
2420 anim->setFreeze(fillStr == QLatin1String("freeze"));
2421 anim->setRepeatCount(
2422 (repeatStr == QLatin1String("indefinite"))? -1 :
2423 (repeatStr == QLatin1String(""))? 1 : toDouble(repeatStr));
2424
2425 parent->appendStyleProperty(anim, someId(attributes));
2426 parent->document()->setAnimated(true);
2427 handler->setAnimPeriod(begin, end);
2428 return true;
2429}
2430
2431static QSvgNode * createAnimationNode(QSvgNode *parent,
2432 const QXmlStreamAttributes &attributes,
2433 QSvgHandler *)
2434{
2435 Q_UNUSED(parent); Q_UNUSED(attributes);
2436 return 0;
2437}
2438
2439static bool parseAudioNode(QSvgNode *parent,
2440 const QXmlStreamAttributes &attributes,
2441 QSvgHandler *)
2442{
2443 Q_UNUSED(parent); Q_UNUSED(attributes);
2444 return true;
2445}
2446
2447static QSvgNode *createCircleNode(QSvgNode *parent,
2448 const QXmlStreamAttributes &attributes,
2449 QSvgHandler *)
2450{
2451 QString cx = attributes.value(QLatin1String("cx")).toString();
2452 QString cy = attributes.value(QLatin1String("cy")).toString();
2453 QString r = attributes.value(QLatin1String("r")).toString();
2454 qreal ncx = toDouble(cx);
2455 qreal ncy = toDouble(cy);
2456 qreal nr = toDouble(r);
2457
2458 QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
2459 QSvgNode *circle = new QSvgCircle(parent, rect);
2460 return circle;
2461}
2462
2463static QSvgNode *createDefsNode(QSvgNode *parent,
2464 const QXmlStreamAttributes &attributes,
2465 QSvgHandler *)
2466{
2467 Q_UNUSED(attributes);
2468 QSvgDefs *defs = new QSvgDefs(parent);
2469 return defs;
2470}
2471
2472static bool parseDescNode(QSvgNode *parent,
2473 const QXmlStreamAttributes &attributes,
2474 QSvgHandler *)
2475{
2476 Q_UNUSED(parent); Q_UNUSED(attributes);
2477 return true;
2478}
2479
2480static bool parseDiscardNode(QSvgNode *parent,
2481 const QXmlStreamAttributes &attributes,
2482 QSvgHandler *)
2483{
2484 Q_UNUSED(parent); Q_UNUSED(attributes);
2485 return true;
2486}
2487
2488static QSvgNode *createEllipseNode(QSvgNode *parent,
2489 const QXmlStreamAttributes &attributes,
2490 QSvgHandler *)
2491{
2492 QString cx = attributes.value(QLatin1String("cx")).toString();
2493 QString cy = attributes.value(QLatin1String("cy")).toString();
2494 QString rx = attributes.value(QLatin1String("rx")).toString();
2495 QString ry = attributes.value(QLatin1String("ry")).toString();
2496 qreal ncx = toDouble(cx);
2497 qreal ncy = toDouble(cy);
2498 qreal nrx = toDouble(rx);
2499 qreal nry = toDouble(ry);
2500
2501 QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
2502 QSvgNode *ellipse = new QSvgEllipse(parent, rect);
2503 return ellipse;
2504}
2505
2506static QSvgStyleProperty *createFontNode(QSvgNode *parent,
2507 const QXmlStreamAttributes &attributes,
2508 QSvgHandler *)
2509{
2510 QString hax = attributes.value(QLatin1String("horiz-adv-x")).toString();
2511 QString myId = someId(attributes);
2512
2513 qreal horizAdvX = toDouble(hax);
2514
2515 while (parent && parent->type() != QSvgNode::DOC) {
2516 parent = parent->parent();
2517 }
2518
2519 if (parent) {
2520 QSvgTinyDocument *doc = static_cast<QSvgTinyDocument*>(parent);
2521 QSvgFont *font = new QSvgFont(horizAdvX);
2522 font->setFamilyName(myId);
2523 if (!font->familyName().isEmpty()) {
2524 if (!doc->svgFont(font->familyName()))
2525 doc->addSvgFont(font);
2526 }
2527 return new QSvgFontStyle(font, doc);
2528 }
2529 return 0;
2530}
2531
2532static bool parseFontFaceNode(QSvgStyleProperty *parent,
2533 const QXmlStreamAttributes &attributes,
2534 QSvgHandler *)
2535{
2536 if (parent->type() != QSvgStyleProperty::FONT) {
2537 return false;
2538 }
2539
2540 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2541 QSvgFont *font = style->svgFont();
2542 QString name = attributes.value(QLatin1String("font-family")).toString();
2543 QString unitsPerEmStr = attributes.value(QLatin1String("units-per-em")).toString();
2544
2545 qreal unitsPerEm = toDouble(unitsPerEmStr);
2546 if (!unitsPerEm)
2547 unitsPerEm = 1000;
2548
2549 if (!name.isEmpty())
2550 font->setFamilyName(name);
2551 font->setUnitsPerEm(unitsPerEm);
2552
2553 if (!font->familyName().isEmpty())
2554 if (!style->doc()->svgFont(font->familyName()))
2555 style->doc()->addSvgFont(font);
2556
2557 return true;
2558}
2559
2560static bool parseFontFaceNameNode(QSvgStyleProperty *parent,
2561 const QXmlStreamAttributes &attributes,
2562 QSvgHandler *)
2563{
2564 if (parent->type() != QSvgStyleProperty::FONT) {
2565 return false;
2566 }
2567
2568 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2569 QSvgFont *font = style->svgFont();
2570 QString name = attributes.value(QLatin1String("name")).toString();
2571
2572 if (!name.isEmpty())
2573 font->setFamilyName(name);
2574
2575 if (!font->familyName().isEmpty())
2576 if (!style->doc()->svgFont(font->familyName()))
2577 style->doc()->addSvgFont(font);
2578
2579 return true;
2580}
2581
2582static bool parseFontFaceSrcNode(QSvgStyleProperty *parent,
2583 const QXmlStreamAttributes &attributes,
2584 QSvgHandler *)
2585{
2586 Q_UNUSED(parent); Q_UNUSED(attributes);
2587 return true;
2588}
2589
2590static bool parseFontFaceUriNode(QSvgStyleProperty *parent,
2591 const QXmlStreamAttributes &attributes,
2592 QSvgHandler *)
2593{
2594 Q_UNUSED(parent); Q_UNUSED(attributes);
2595 return true;
2596}
2597
2598static bool parseForeignObjectNode(QSvgNode *parent,
2599 const QXmlStreamAttributes &attributes,
2600 QSvgHandler *)
2601{
2602 Q_UNUSED(parent); Q_UNUSED(attributes);
2603 return true;
2604}
2605
2606static QSvgNode *createGNode(QSvgNode *parent,
2607 const QXmlStreamAttributes &attributes,
2608 QSvgHandler *)
2609{
2610 Q_UNUSED(attributes);
2611 QSvgG *node = new QSvgG(parent);
2612 return node;
2613}
2614
2615static bool parseGlyphNode(QSvgStyleProperty *parent,
2616 const QXmlStreamAttributes &attributes,
2617 QSvgHandler *)
2618{
2619 if (parent->type() != QSvgStyleProperty::FONT) {
2620 return false;
2621 }
2622
2623 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2624 QSvgFont *font = style->svgFont();
2625 createSvgGlyph(font, attributes);
2626 return true;
2627}
2628
2629static bool parseHandlerNode(QSvgNode *parent,
2630 const QXmlStreamAttributes &attributes,
2631 QSvgHandler *)
2632{
2633 Q_UNUSED(parent); Q_UNUSED(attributes);
2634 return true;
2635}
2636
2637static bool parseHkernNode(QSvgNode *parent,
2638 const QXmlStreamAttributes &attributes,
2639 QSvgHandler *)
2640{
2641 Q_UNUSED(parent); Q_UNUSED(attributes);
2642 return true;
2643}
2644
2645static QSvgNode *createImageNode(QSvgNode *parent,
2646 const QXmlStreamAttributes &attributes,
2647 QSvgHandler *handler)
2648{
2649 QString x = attributes.value(QLatin1String("x")).toString();
2650 QString y = attributes.value(QLatin1String("y")).toString();
2651 QString width = attributes.value(QLatin1String("width")).toString();
2652 QString height = attributes.value(QLatin1String("height")).toString();
2653 QString filename = attributes.value(QLatin1String("xlink:href")).toString();
2654 qreal nx = toDouble(x);
2655 qreal ny = toDouble(y);
2656 QSvgHandler::LengthType type;
2657 qreal nwidth = parseLength(width, type, handler);
2658 nwidth = convertToPixels(nwidth, true, type);
2659
2660 qreal nheight = parseLength(height, type, handler);
2661 nheight = convertToPixels(nheight, false, type);
2662
2663
2664 filename = filename.trimmed();
2665 QImage image;
2666 if (filename.startsWith(QLatin1String("data"))) {
2667 int idx = filename.lastIndexOf(QLatin1String("base64,"));
2668 if (idx != -1) {
2669 idx += 7;
2670 QString dataStr = filename.mid(idx);
2671 QByteArray data = QByteArray::fromBase64(dataStr.toAscii());
2672 image = QImage::fromData(data);
2673 } else {
2674 qDebug()<<"QSvgHandler::createImageNode: Unrecognized inline image format!";
2675 }
2676 } else
2677 image = QImage(filename);
2678
2679 if (image.isNull()) {
2680 qDebug()<<"couldn't create image from "<<filename;
2681 return 0;
2682 }
2683
2684 if (image.format() == QImage::Format_ARGB32)
2685 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
2686
2687 QSvgNode *img = new QSvgImage(parent,
2688 image,
2689 QRect(int(nx),
2690 int(ny),
2691 int(nwidth),
2692 int(nheight)));
2693 return img;
2694}
2695
2696static QSvgNode *createLineNode(QSvgNode *parent,
2697 const QXmlStreamAttributes &attributes,
2698 QSvgHandler *)
2699{
2700 QString x1 = attributes.value(QLatin1String("x1")).toString();
2701 QString y1 = attributes.value(QLatin1String("y1")).toString();
2702 QString x2 = attributes.value(QLatin1String("x2")).toString();
2703 QString y2 = attributes.value(QLatin1String("y2")).toString();
2704 qreal nx1 = toDouble(x1);
2705 qreal ny1 = toDouble(y1);
2706 qreal nx2 = toDouble(x2);
2707 qreal ny2 = toDouble(y2);
2708
2709 QLineF lineBounds(nx1, ny1, nx2, ny2);
2710 QSvgNode *line = new QSvgLine(parent, lineBounds);
2711 return line;
2712}
2713
2714
2715static void parseBaseGradient(QSvgNode *node,
2716 const QXmlStreamAttributes &attributes,
2717 QSvgGradientStyle *gradProp,
2718 QSvgHandler *handler)
2719{
2720 QString link = attributes.value(QLatin1String("xlink:href")).toString();
2721 QStringRef trans = attributes.value(QLatin1String("gradientTransform"));
2722 QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
2723 QString units = attributes.value(QLatin1String("gradientUnits")).toString();
2724 QStringRef colorStr = attributes.value(QLatin1String("color"));
2725 QStringRef colorOpacityStr = attributes.value(QLatin1String("color-opacity"));
2726
2727 QColor color;
2728 if (constructColor(colorStr, colorOpacityStr, color, handler)) {
2729 handler->popColor();
2730 handler->pushColor(color);
2731 }
2732
2733 QMatrix matrix;
2734 QGradient *grad = gradProp->qgradient();
2735 if (!link.isEmpty()) {
2736 QSvgStyleProperty *prop = node->styleProperty(link);
2737 //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
2738 if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
2739 QSvgGradientStyle *inherited =
2740 static_cast<QSvgGradientStyle*>(prop);
2741 if (!inherited->stopLink().isEmpty()) {
2742 gradProp->setStopLink(inherited->stopLink(), handler->document());
2743 } else {
2744 grad->setStops(inherited->qgradient()->stops());
2745 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
2746 }
2747
2748 matrix = inherited->qmatrix();
2749 } else {
2750 gradProp->setStopLink(link, handler->document());
2751 }
2752 }
2753
2754 if (!trans.isEmpty()) {
2755 matrix = parseTransformationMatrix(trans);
2756 gradProp->setMatrix(matrix);
2757 } else if (!matrix.isIdentity()) {
2758 gradProp->setMatrix(matrix);
2759 }
2760
2761 if (!spread.isEmpty()) {
2762 if (spread == QLatin1String("pad")) {
2763 grad->setSpread(QGradient::PadSpread);
2764 } else if (spread == QLatin1String("reflect")) {
2765 grad->setSpread(QGradient::ReflectSpread);
2766 } else if (spread == QLatin1String("repeat")) {
2767 grad->setSpread(QGradient::RepeatSpread);
2768 }
2769 }
2770
2771 if (units.isEmpty() || units == QLatin1String("objectBoundingBox")) {
2772 grad->setCoordinateMode(QGradient::ObjectBoundingMode);
2773 }
2774}
2775
2776static QSvgStyleProperty *createLinearGradientNode(QSvgNode *node,
2777 const QXmlStreamAttributes &attributes,
2778 QSvgHandler *handler)
2779{
2780 QString x1 = attributes.value(QLatin1String("x1")).toString();
2781 QString y1 = attributes.value(QLatin1String("y1")).toString();
2782 QString x2 = attributes.value(QLatin1String("x2")).toString();
2783 QString y2 = attributes.value(QLatin1String("y2")).toString();
2784
2785 qreal nx1 = 0.0;
2786 qreal ny1 = 0.0;
2787 qreal nx2 = 1.0;
2788 qreal ny2 = 0.0;
2789
2790 if (!x1.isEmpty())
2791 nx1 = convertToNumber(x1, handler);
2792 if (!y1.isEmpty())
2793 ny1 = convertToNumber(y1, handler);
2794 if (!x2.isEmpty())
2795 nx2 = convertToNumber(x2, handler);
2796 if (!y2.isEmpty())
2797 ny2 = convertToNumber(y2, handler);
2798
2799 QSvgNode *itr = node;
2800 while (itr && itr->type() != QSvgNode::DOC) {
2801 itr = itr->parent();
2802 }
2803
2804 QLinearGradient *grad = new QLinearGradient(nx1, ny1, nx2, ny2);
2805 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2806 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
2807 parseBaseGradient(node, attributes, prop, handler);
2808
2809 return prop;
2810}
2811
2812static bool parseMetadataNode(QSvgNode *parent,
2813 const QXmlStreamAttributes &attributes,
2814 QSvgHandler *)
2815{
2816 Q_UNUSED(parent); Q_UNUSED(attributes);
2817 return true;
2818}
2819
2820static bool parseMissingGlyphNode(QSvgStyleProperty *parent,
2821 const QXmlStreamAttributes &attributes,
2822 QSvgHandler *)
2823{
2824 if (parent->type() != QSvgStyleProperty::FONT) {
2825 return false;
2826 }
2827
2828 QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
2829 QSvgFont *font = style->svgFont();
2830 createSvgGlyph(font, attributes);
2831 return true;
2832}
2833
2834static bool parseMpathNode(QSvgNode *parent,
2835 const QXmlStreamAttributes &attributes,
2836 QSvgHandler *)
2837{
2838 Q_UNUSED(parent); Q_UNUSED(attributes);
2839 return true;
2840}
2841
2842static QSvgNode *createPathNode(QSvgNode *parent,
2843 const QXmlStreamAttributes &attributes,
2844 QSvgHandler *)
2845{
2846 QStringRef data = attributes.value(QLatin1String("d"));
2847
2848 QPainterPath qpath;
2849 qpath.setFillRule(Qt::WindingFill);
2850 //XXX do error handling
2851 parsePathDataFast(data, qpath);
2852
2853 QSvgNode *path = new QSvgPath(parent, qpath);
2854 return path;
2855}
2856
2857static QSvgNode *createPolygonNode(QSvgNode *parent,
2858 const QXmlStreamAttributes &attributes,
2859 QSvgHandler *)
2860{
2861 QString pointsStr = attributes.value(QLatin1String("points")).toString();
2862
2863 //same QPolygon parsing is in createPolylineNode
2864 const QChar *s = pointsStr.constData();
2865 QVector<qreal> points = parseNumbersList(s);
2866 QPolygonF poly(points.count()/2);
2867 for (int i = 0; i < poly.size(); ++i)
2868 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2869 QSvgNode *polygon = new QSvgPolygon(parent, poly);
2870 return polygon;
2871}
2872
2873static QSvgNode *createPolylineNode(QSvgNode *parent,
2874 const QXmlStreamAttributes &attributes,
2875 QSvgHandler *)
2876{
2877 QString pointsStr = attributes.value(QLatin1String("points")).toString();
2878
2879 //same QPolygon parsing is in createPolygonNode
2880 const QChar *s = pointsStr.constData();
2881 QVector<qreal> points = parseNumbersList(s);
2882 QPolygonF poly(points.count()/2);
2883 for (int i = 0; i < poly.size(); ++i)
2884 poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
2885
2886 QSvgNode *line = new QSvgPolyline(parent, poly);
2887 return line;
2888}
2889
2890static bool parsePrefetchNode(QSvgNode *parent,
2891 const QXmlStreamAttributes &attributes,
2892 QSvgHandler *)
2893{
2894 Q_UNUSED(parent); Q_UNUSED(attributes);
2895 return true;
2896}
2897
2898static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node,
2899 const QXmlStreamAttributes &attributes,
2900 QSvgHandler *handler)
2901{
2902 QString cx = attributes.value(QLatin1String("cx")).toString();
2903 QString cy = attributes.value(QLatin1String("cy")).toString();
2904 QString r = attributes.value(QLatin1String("r")).toString();
2905 QString fx = attributes.value(QLatin1String("fx")).toString();
2906 QString fy = attributes.value(QLatin1String("fy")).toString();
2907
2908 qreal ncx = 0.5;
2909 qreal ncy = 0.5;
2910 qreal nr = 0.5;
2911 if (!cx.isEmpty())
2912 ncx = toDouble(cx);
2913 if (!cy.isEmpty())
2914 ncy = toDouble(cy);
2915 if (!r.isEmpty())
2916 nr = toDouble(r);
2917
2918 qreal nfx = ncx;
2919 if (!fx.isEmpty())
2920 nfx = toDouble(fx);
2921 qreal nfy = ncy;
2922 if (!fy.isEmpty())
2923 nfy = toDouble(fy);
2924
2925 QRadialGradient *grad = new QRadialGradient(ncx, ncy, nr, nfx, nfy);
2926 grad->setInterpolationMode(QGradient::ComponentInterpolation);
2927
2928 QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
2929 parseBaseGradient(node, attributes, prop, handler);
2930
2931 return prop;
2932}
2933
2934static QSvgNode *createRectNode(QSvgNode *parent,
2935 const QXmlStreamAttributes &attributes,
2936 QSvgHandler *handler)
2937{
2938 QString x = attributes.value(QLatin1String("x")).toString();
2939 QString y = attributes.value(QLatin1String("y")).toString();
2940 QString width = attributes.value(QLatin1String("width")).toString();
2941 QString height = attributes.value(QLatin1String("height")).toString();
2942 QString rx = attributes.value(QLatin1String("rx")).toString();
2943 QString ry = attributes.value(QLatin1String("ry")).toString();
2944
2945 QSvgHandler::LengthType type;
2946 qreal nwidth = parseLength(width, type, handler);
2947 nwidth = convertToPixels(nwidth, true, type);
2948
2949 qreal nheight = parseLength(height, type, handler);
2950 nheight = convertToPixels(nheight, true, type);
2951 qreal nrx = toDouble(rx);
2952 qreal nry = toDouble(ry);
2953
2954 QRectF bounds(toDouble(x), toDouble(y),
2955 nwidth, nheight);
2956
2957 //9.2 The 'rect' element clearly specifies it
2958 // but the case might in fact be handled because
2959 // we draw rounded rectangles differently
2960 if (nrx > bounds.width()/2)
2961 nrx = bounds.width()/2;
2962 if (nry > bounds.height()/2)
2963 nry = bounds.height()/2;
2964
2965 if (nrx && !nry)
2966 nry = nrx;
2967 else if (nry && !nrx)
2968 nrx = nry;
2969
2970 //we draw rounded rect from 0...99
2971 //svg from 0...bounds.width()/2 so we're adjusting the
2972 //coordinates
2973 nrx *= (100/(bounds.width()/2));
2974 nry *= (100/(bounds.height()/2));
2975
2976 QSvgNode *rect = new QSvgRect(parent, bounds,
2977 int(nrx),
2978 int(nry));
2979 return rect;
2980}
2981
2982static bool parseScriptNode(QSvgNode *parent,
2983 const QXmlStreamAttributes &attributes,
2984 QSvgHandler *)
2985{
2986 Q_UNUSED(parent); Q_UNUSED(attributes);
2987 return true;
2988}
2989
2990static bool parseSetNode(QSvgNode *parent,
2991 const QXmlStreamAttributes &attributes,
2992 QSvgHandler *)
2993{
2994 Q_UNUSED(parent); Q_UNUSED(attributes);
2995 return true;
2996}
2997
2998static QSvgStyleProperty *createSolidColorNode(QSvgNode *parent,
2999 const QXmlStreamAttributes &attributes,
3000 QSvgHandler *handler)
3001{
3002 Q_UNUSED(parent); Q_UNUSED(attributes);
3003 QStringRef solidColorStr = attributes.value(QLatin1String("solid-color"));
3004 QStringRef solidOpacityStr = attributes.value(QLatin1String("solid-opacity"));
3005
3006 if (solidOpacityStr.isEmpty())
3007 solidOpacityStr = attributes.value(QLatin1String("opacity"));
3008
3009 QColor color;
3010 if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
3011 return 0;
3012 QSvgSolidColorStyle *style = new QSvgSolidColorStyle(color);
3013 return style;
3014}
3015
3016static bool parseStopNode(QSvgStyleProperty *parent,
3017 const QXmlStreamAttributes &attributes,
3018 QSvgHandler *handler)
3019{
3020 if (parent->type() != QSvgStyleProperty::GRADIENT)
3021 return false;
3022 QString nodeIdStr = someId(attributes);
3023 QString xmlClassStr = attributes.value(QLatin1String("class")).toString();
3024
3025 //### nasty hack because stop gradients are not in the rendering tree
3026 // we force a dummy node with the same id and class into a rendering
3027 // tree to figure out whether the selector has a style for it
3028 // QSvgStyleSelector should be coded in a way that could avoid it
3029 QSvgAnimation anim;
3030 anim.setNodeId(nodeIdStr);
3031 anim.setXmlClass(xmlClassStr);
3032
3033 QCss::StyleSelector::NodePtr cssNode;
3034 cssNode.ptr = &anim;
3035 QVector<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
3036
3037 QXmlStreamAttributes xmlAttr = attributes;
3038 for (int i = 0; i < decls.count(); ++i) {
3039 const QCss::Declaration &decl = decls.at(i);
3040
3041 if (decl.d->property.isEmpty())
3042 continue;
3043 if (decl.d->values.count() != 1)
3044 continue;
3045 QCss::Value val = decl.d->values.first();
3046 QString valueStr = val.toString();
3047 if (val.type == QCss::Value::Uri) {
3048 valueStr.prepend(QLatin1String("url("));
3049 valueStr.append(QLatin1Char(')'));
3050 }
3051 xmlAttr.append(QString(), decl.d->property, valueStr);
3052 }
3053 QSvgAttributes attrs(xmlAttr, handler);
3054
3055 QSvgGradientStyle *style =
3056 static_cast<QSvgGradientStyle*>(parent);
3057 QString offsetStr = attrs.offset.toString();
3058 QStringRef colorStr = attrs.stopColor;
3059 QColor color;
3060
3061 bool ok = true;
3062 qreal offset = convertToNumber(offsetStr, handler, &ok);
3063 if (!ok)
3064 offset = 0.0;
3065 QString black = QString::fromLatin1("#000000");
3066 if (colorStr.isEmpty()) {
3067 colorStr = QStringRef(&black);
3068 }
3069
3070 constructColor(colorStr, attrs.stopOpacity, color, handler);
3071
3072 QGradient *grad = style->qgradient();
3073
3074 offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
3075 QGradientStops stops;
3076 if (style->gradientStopsSet()) {
3077 stops = grad->stops();
3078 // If the stop offset equals the one previously added, add an epsilon to make it greater.
3079 if (offset <= stops.back().first)
3080 offset = stops.back().first + FLT_EPSILON;
3081 }
3082
3083 // If offset is greater than one, it must be clamped to one.
3084 if (offset > 1.0) {
3085 if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
3086 stops.back().first = 1.0 - FLT_EPSILON;
3087 grad->setStops(stops);
3088 }
3089 offset = 1.0;
3090 }
3091
3092 grad->setColorAt(offset, color);
3093 style->setGradientStopsSet(true);
3094 return true;
3095}
3096
3097static bool parseStyleNode(QSvgNode *parent,
3098 const QXmlStreamAttributes &attributes,
3099 QSvgHandler *handler)
3100{
3101 Q_UNUSED(parent);
3102 QString type = attributes.value(QLatin1String("type")).toString();
3103 type = type.toLower();
3104
3105 if (type == QLatin1String("text/css")) {
3106 handler->setInStyle(true);
3107 }
3108
3109 return true;
3110}
3111
3112static QSvgNode *createSvgNode(QSvgNode *parent,
3113 const QXmlStreamAttributes &attributes,
3114 QSvgHandler *handler)
3115{
3116 Q_UNUSED(parent); Q_UNUSED(attributes);
3117
3118 QString baseProfile = attributes.value(QLatin1String("baseProfile")).toString();
3119#if 0
3120 if (baseProfile.isEmpty() && baseProfile != QLatin1String("tiny")) {
3121 qWarning("Profile is %s while we only support tiny!",
3122 qPrintable(baseProfile));
3123 }
3124#endif
3125
3126 QSvgTinyDocument *node = new QSvgTinyDocument();
3127 QString widthStr = attributes.value(QLatin1String("width")).toString();
3128 QString heightStr = attributes.value(QLatin1String("height")).toString();
3129 QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
3130
3131 QSvgHandler::LengthType type = QSvgHandler::LT_PX; // FIXME: is the default correct?
3132 qreal width = 0;
3133 if (!widthStr.isEmpty()) {
3134 width = parseLength(widthStr, type, handler);
3135 if (type != QSvgHandler::LT_PT)
3136 width = convertToPixels(width, true, type);
3137 node->setWidth(int(width), type == QSvgHandler::LT_PERCENT);
3138 }
3139 qreal height = 0;
3140 if (!heightStr.isEmpty()) {
3141 height = parseLength(heightStr, type, handler);
3142 if (type != QSvgHandler::LT_PT)
3143 height = convertToPixels(height, false, type);
3144 node->setHeight(int(height), type == QSvgHandler::LT_PERCENT);
3145 }
3146
3147 QStringList viewBoxValues;
3148 if (!viewBoxStr.isEmpty()) {
3149 viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
3150 viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
3151 viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
3152 viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
3153 viewBoxValues = viewBoxStr.split(QLatin1Char(','), QString::SkipEmptyParts);
3154 }
3155 if (viewBoxValues.count() == 4) {
3156 QString xStr = viewBoxValues.at(0).trimmed();
3157 QString yStr = viewBoxValues.at(1).trimmed();
3158 QString widthStr = viewBoxValues.at(2).trimmed();
3159 QString heightStr = viewBoxValues.at(3).trimmed();
3160
3161 QSvgHandler::LengthType lt;
3162 qreal x = parseLength(xStr, lt, handler);
3163 qreal y = parseLength(yStr, lt, handler);
3164 qreal w = parseLength(widthStr, lt, handler);
3165 qreal h = parseLength(heightStr, lt, handler);
3166
3167 node->setViewBox(QRectF(x, y, w, h));
3168
3169 } else if (width && height) {
3170 if (type == QSvgHandler::LT_PT) {
3171 width = convertToPixels(width, false, type);
3172 height = convertToPixels(height, false, type);
3173 }
3174 node->setViewBox(QRectF(0, 0, width, height));
3175 }
3176 handler->setDefaultCoordinateSystem(QSvgHandler::LT_PX);
3177
3178 return node;
3179}
3180
3181static QSvgNode *createSwitchNode(QSvgNode *parent,
3182 const QXmlStreamAttributes &attributes,
3183 QSvgHandler *)
3184{
3185 Q_UNUSED(attributes);
3186 QSvgSwitch *node = new QSvgSwitch(parent);
3187 return node;
3188}
3189
3190static bool parseTbreakNode(QSvgNode *parent,
3191 const QXmlStreamAttributes &,
3192 QSvgHandler *)
3193{
3194 if (parent->type() != QSvgNode::TEXTAREA)
3195 return false;
3196 static_cast<QSvgText*>(parent)->addLineBreak();
3197 return true;
3198}
3199
3200static QSvgNode *createTextNode(QSvgNode *parent,
3201 const QXmlStreamAttributes &attributes,
3202 QSvgHandler *handler)
3203{
3204 QString x = attributes.value(QLatin1String("x")).toString();
3205 QString y = attributes.value(QLatin1String("y")).toString();
3206 //### editable and rotate not handled
3207 QSvgHandler::LengthType type;
3208 qreal nx = parseLength(x, type, handler);
3209 qreal ny = parseLength(y, type, handler);
3210
3211 QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
3212 return text;
3213}
3214
3215static QSvgNode *createTextAreaNode(QSvgNode *parent,
3216 const QXmlStreamAttributes &attributes,
3217 QSvgHandler *handler)
3218{
3219 QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
3220 if (node) {
3221 QSvgHandler::LengthType type;
3222 qreal width = parseLength(attributes.value(QLatin1String("width")).toString(), type, handler);
3223 qreal height = parseLength(attributes.value(QLatin1String("height")).toString(), type, handler);
3224 node->setTextArea(QSizeF(width, height));
3225 }
3226 return node;
3227}
3228
3229static QSvgNode *createTspanNode(QSvgNode *parent,
3230 const QXmlStreamAttributes &,
3231 QSvgHandler *)
3232{
3233 return new QSvgTspan(parent);
3234}
3235
3236static bool parseTitleNode(QSvgNode *parent,
3237 const QXmlStreamAttributes &attributes,
3238 QSvgHandler *)
3239{
3240 Q_UNUSED(parent); Q_UNUSED(attributes);
3241 return true;
3242}
3243
3244static QSvgNode *createUseNode(QSvgNode *parent,
3245 const QXmlStreamAttributes &attributes,
3246 QSvgHandler *handler)
3247{
3248 QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
3249 QString xStr = attributes.value(QLatin1String("x")).toString();
3250 QString yStr = attributes.value(QLatin1String("y")).toString();
3251 QSvgStructureNode *group = 0;
3252
3253 if (linkId.isEmpty())
3254 linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
3255 switch (parent->type()) {
3256 case QSvgNode::DOC:
3257 case QSvgNode::DEFS:
3258 case QSvgNode::G:
3259 case QSvgNode::SWITCH:
3260 group = static_cast<QSvgStructureNode*>(parent);
3261 break;
3262 default:
3263 break;
3264 }
3265
3266 if (group) {
3267 QSvgNode *link = group->scopeNode(linkId);
3268 if (link) {
3269 QPointF pt;
3270 if (!xStr.isNull() || !yStr.isNull()) {
3271 QSvgHandler::LengthType type;
3272 qreal nx = parseLength(xStr, type, handler);
3273 nx = convertToPixels(nx, true, type);
3274
3275 qreal ny = parseLength(yStr, type, handler);
3276 ny = convertToPixels(ny, true, type);
3277 pt = QPointF(nx, ny);
3278 }
3279
3280 //delay link resolving till the first draw call on
3281 //use nodes, link 2might have not been created yet
3282 QSvgUse *node = new QSvgUse(pt, parent, link);
3283 return node;
3284 }
3285 }
3286
3287 qWarning("link %s hasn't been detected!", qPrintable(linkId));
3288 return 0;
3289}
3290
3291static QSvgNode *createVideoNode(QSvgNode *parent,
3292 const QXmlStreamAttributes &attributes,
3293 QSvgHandler *)
3294{
3295 Q_UNUSED(parent); Q_UNUSED(attributes);
3296 return 0;
3297}
3298
3299typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
3300
3301static FactoryMethod findGroupFactory(const QString &name)
3302{
3303 if (name.isEmpty())
3304 return 0;
3305
3306 QStringRef ref(&name, 1, name.length() - 1);
3307 switch (name.at(0).unicode()) {
3308 case 'd':
3309 if (ref == QLatin1String("efs")) return createDefsNode;
3310 break;
3311 case 'g':
3312 if (ref.isEmpty()) return createGNode;
3313 break;
3314 case 's':
3315 if (ref == QLatin1String("vg")) return createSvgNode;
3316 if (ref == QLatin1String("witch")) return createSwitchNode;
3317 break;
3318 default:
3319 break;
3320 }
3321 return 0;
3322}
3323
3324static FactoryMethod findGraphicsFactory(const QString &name)
3325{
3326 if (name.isEmpty())
3327 return 0;
3328
3329 QStringRef ref(&name, 1, name.length() - 1);
3330 switch (name.at(0).unicode()) {
3331 case 'a':
3332 if (ref == QLatin1String("nimation")) return createAnimationNode;
3333 break;
3334 case 'c':
3335 if (ref == QLatin1String("ircle")) return createCircleNode;
3336 break;
3337 case 'e':
3338 if (ref == QLatin1String("llipse")) return createEllipseNode;
3339 break;
3340 case 'i':
3341 if (ref == QLatin1String("mage")) return createImageNode;
3342 break;
3343 case 'l':
3344 if (ref == QLatin1String("ine")) return createLineNode;
3345 break;
3346 case 'p':
3347 if (ref == QLatin1String("ath")) return createPathNode;
3348 if (ref == QLatin1String("olygon")) return createPolygonNode;
3349 if (ref == QLatin1String("olyline")) return createPolylineNode;
3350 break;
3351 case 'r':
3352 if (ref == QLatin1String("ect")) return createRectNode;
3353 break;
3354 case 't':
3355 if (ref == QLatin1String("ext")) return createTextNode;
3356 if (ref == QLatin1String("extArea")) return createTextAreaNode;
3357 if (ref == QLatin1String("span")) return createTspanNode;
3358 break;
3359 case 'u':
3360 if (ref == QLatin1String("se")) return createUseNode;
3361 break;
3362 case 'v':
3363 if (ref == QLatin1String("ideo")) return createVideoNode;
3364 break;
3365 default:
3366 break;
3367 }
3368 return 0;
3369}
3370
3371typedef bool (*ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
3372
3373static ParseMethod findUtilFactory(const QString &name)
3374{
3375 if (name.isEmpty())
3376 return 0;
3377
3378 QStringRef ref(&name, 1, name.length() - 1);
3379 switch (name.at(0).unicode()) {
3380 case 'a':
3381 if (ref.isEmpty()) return parseAnchorNode;
3382 if (ref == QLatin1String("nimate")) return parseAnimateNode;
3383 if (ref == QLatin1String("nimateColor")) return parseAnimateColorNode;
3384 if (ref == QLatin1String("nimateMotion")) return parseAimateMotionNode;
3385 if (ref == QLatin1String("nimateTransform")) return parseAnimateTransformNode;
3386 if (ref == QLatin1String("udio")) return parseAudioNode;
3387 break;
3388 case 'd':
3389 if (ref == QLatin1String("esc")) return parseDescNode;
3390 if (ref == QLatin1String("iscard")) return parseDiscardNode;
3391 break;
3392 case 'f':
3393 if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
3394 break;
3395 case 'h':
3396 if (ref == QLatin1String("andler")) return parseHandlerNode;
3397 if (ref == QLatin1String("kern")) return parseHkernNode;
3398 break;
3399 case 'm':
3400 if (ref == QLatin1String("etadata")) return parseMetadataNode;
3401 if (ref == QLatin1String("path")) return parseMpathNode;
3402 break;
3403 case 'p':
3404 if (ref == QLatin1String("refetch")) return parsePrefetchNode;
3405 break;
3406 case 's':
3407 if (ref == QLatin1String("cript")) return parseScriptNode;
3408 if (ref == QLatin1String("et")) return parseSetNode;
3409 if (ref == QLatin1String("tyle")) return parseStyleNode;
3410 break;
3411 case 't':
3412 if (ref == QLatin1String("break")) return parseTbreakNode;
3413 if (ref == QLatin1String("itle")) return parseTitleNode;
3414 break;
3415 default:
3416 break;
3417 }
3418 return 0;
3419}
3420
3421typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
3422 const QXmlStreamAttributes &,
3423 QSvgHandler *);
3424
3425static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
3426{
3427 if (name.isEmpty())
3428 return 0;
3429
3430 QStringRef ref(&name, 1, name.length() - 1);
3431 switch (name.at(0).unicode()) {
3432 case 'f':
3433 if (ref == QLatin1String("ont")) return createFontNode;
3434 break;
3435 case 'l':
3436 if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
3437 break;
3438 case 'r':
3439 if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
3440 break;
3441 case 's':
3442 if (ref == QLatin1String("olidColor")) return createSolidColorNode;
3443 break;
3444 default:
3445 break;
3446 }
3447 return 0;
3448}
3449
3450typedef bool (*StyleParseMethod)(QSvgStyleProperty *,
3451 const QXmlStreamAttributes &,
3452 QSvgHandler *);
3453
3454static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
3455{
3456 if (name.isEmpty())
3457 return 0;
3458
3459 QStringRef ref(&name, 1, name.length() - 1);
3460 switch (name.at(0).unicode()) {
3461 case 'f':
3462 if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
3463 if (ref == QLatin1String("ont-face-name")) return parseFontFaceNameNode;
3464 if (ref == QLatin1String("ont-face-src")) return parseFontFaceSrcNode;
3465 if (ref == QLatin1String("ont-face-uri")) return parseFontFaceUriNode;
3466 break;
3467 case 'g':
3468 if (ref == QLatin1String("lyph")) return parseGlyphNode;
3469 break;
3470 case 'm':
3471 if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
3472 break;
3473 case 's':
3474 if (ref == QLatin1String("top")) return parseStopNode;
3475 break;
3476 default:
3477 break;
3478 }
3479 return 0;
3480}
3481
3482QSvgHandler::QSvgHandler(QIODevice *device) : xml(new QXmlStreamReader(device))
3483 , m_ownsReader(true)
3484{
3485 init();
3486}
3487
3488QSvgHandler::QSvgHandler(const QByteArray &data) : xml(new QXmlStreamReader(data))
3489 , m_ownsReader(true)
3490{
3491 init();
3492}
3493
3494QSvgHandler::QSvgHandler(QXmlStreamReader *const reader) : xml(reader)
3495 , m_ownsReader(false)
3496{
3497 init();
3498}
3499
3500void QSvgHandler::init()
3501{
3502 m_doc = 0;
3503 m_style = 0;
3504 m_animEnd = 0;
3505 m_defaultCoords = LT_PX;
3506 m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
3507 m_defaultPen.setMiterLimit(4);
3508 parse();
3509}
3510
3511void QSvgHandler::parse()
3512{
3513 xml->setNamespaceProcessing(false);
3514 m_selector = new QSvgStyleSelector;
3515 m_inStyle = false;
3516 bool done = false;
3517 while (!xml->atEnd() && !done) {
3518 switch (xml->readNext()) {
3519 case QXmlStreamReader::StartElement:
3520 // he we could/should verify the namespaces, and simply
3521 // call m_skipNodes(Unknown) if we don't know the
3522 // namespace. We do support http://www.w3.org/2000/svg
3523 // but also http://www.w3.org/2000/svg-20000303-stylable
3524 // And if the document uses an external dtd, the reported
3525 // namespaceUri is empty. The only possible strategy at
3526 // this point is to do what everyone else seems to do and
3527 // ignore the reported namespaceUri completely.
3528 startElement(xml->name().toString(), xml->attributes());
3529 break;
3530 case QXmlStreamReader::EndElement:
3531 endElement(xml->name());
3532 // if we are using somebody else's qxmlstreamreader
3533 // we should not read until the end of the stream
3534 done = !m_ownsReader && (xml->name() == QLatin1String("svg"));
3535 break;
3536 case QXmlStreamReader::Characters:
3537 characters(xml->text());
3538 break;
3539 case QXmlStreamReader::ProcessingInstruction:
3540 processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
3541 break;
3542 default:
3543 break;
3544 }
3545 }
3546 resolveGradients(m_doc);
3547}
3548
3549bool QSvgHandler::startElement(const QString &localName,
3550 const QXmlStreamAttributes &attributes)
3551{
3552 QSvgNode *node = 0;
3553
3554 pushColorCopy();
3555
3556 /* The xml:space attribute may appear on any element. We do
3557 * a lookup by the qualified name here, but this is namespace aware, since
3558 * the XML namespace can only be bound to prefix "xml." */
3559 const QStringRef xmlSpace(attributes.value(QLatin1String("xml:space")));
3560 if (xmlSpace.isNull()) {
3561 // This element has no xml:space attribute.
3562 m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
3563 } else if (xmlSpace == QLatin1String("preserve")) {
3564 m_whitespaceMode.push(QSvgText::Preserve);
3565 } else if (xmlSpace == QLatin1String("default")) {
3566 m_whitespaceMode.push(QSvgText::Default);
3567 } else {
3568 qWarning() << QString::fromLatin1("\"%1\" is an invalid value for attribute xml:space. "
3569 "Valid values are \"preserve\" and \"default\".").arg(xmlSpace.toString());
3570 m_whitespaceMode.push(QSvgText::Default);
3571 }
3572
3573 if (FactoryMethod method = findGroupFactory(localName)) {
3574 //group
3575 node = method(m_doc ? m_nodes.top() : 0, attributes, this);
3576 Q_ASSERT(node);
3577 if (!m_doc) {
3578 Q_ASSERT(node->type() == QSvgNode::DOC);
3579 m_doc = static_cast<QSvgTinyDocument*>(node);
3580 } else {
3581 switch (m_nodes.top()->type()) {
3582 case QSvgNode::DOC:
3583 case QSvgNode::G:
3584 case QSvgNode::DEFS:
3585 case QSvgNode::SWITCH:
3586 {
3587 QSvgStructureNode *group =
3588 static_cast<QSvgStructureNode*>(m_nodes.top());
3589 group->addChild(node, someId(attributes));
3590 }
3591 break;
3592 default:
3593 break;
3594 }
3595 }
3596 parseCoreNode(node, attributes);
3597 cssStyleLookup(node, this, m_selector);
3598 parseStyle(node, attributes, this);
3599 } else if (FactoryMethod method = findGraphicsFactory(localName)) {
3600 //rendering element
3601 Q_ASSERT(!m_nodes.isEmpty());
3602 node = method(m_nodes.top(), attributes, this);
3603 if (node) {
3604 switch (m_nodes.top()->type()) {
3605 case QSvgNode::DOC:
3606 case QSvgNode::G:
3607 case QSvgNode::DEFS:
3608 case QSvgNode::SWITCH:
3609 {
3610 QSvgStructureNode *group =
3611 static_cast<QSvgStructureNode*>(m_nodes.top());
3612 group->addChild(node, someId(attributes));
3613 }
3614 break;
3615 case QSvgNode::TEXT:
3616 case QSvgNode::TEXTAREA:
3617 if (node->type() == QSvgNode::TSPAN) {
3618 static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
3619 } else {
3620 qWarning("\'text\' or \'textArea\' element contains invalid element type.");
3621 delete node;
3622 node = 0;
3623 }
3624 break;
3625 default:
3626 qWarning("Could not add child element to parent element because the types are incorrect.");
3627 delete node;
3628 node = 0;
3629 break;
3630 }
3631
3632 if (node) {
3633 parseCoreNode(node, attributes);
3634 cssStyleLookup(node, this, m_selector);
3635 parseStyle(node, attributes, this);
3636 if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) {
3637 static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3638 } else if (node->type() == QSvgNode::TSPAN) {
3639 static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
3640 }
3641 }
3642 }
3643 } else if (ParseMethod method = findUtilFactory(localName)) {
3644 Q_ASSERT(!m_nodes.isEmpty());
3645 if (!method(m_nodes.top(), attributes, this)) {
3646 qWarning("Problem parsing %s", qPrintable(localName));
3647 }
3648 } else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
3649 QSvgStyleProperty *prop = method(m_nodes.top(), attributes, this);
3650 if (prop) {
3651 m_style = prop;
3652 m_nodes.top()->appendStyleProperty(prop, someId(attributes));
3653 } else {
3654 qWarning("Could not parse node: %s", qPrintable(localName));
3655 }
3656 } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
3657 if (m_style) {
3658 if (!method(m_style, attributes, this)) {
3659 qWarning("Problem parsing %s", qPrintable(localName));
3660 }
3661 }
3662 } else {
3663 //qWarning()<<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
3664 m_skipNodes.push(Unknown);
3665 return true;
3666 }
3667
3668 if (node) {
3669 m_nodes.push(node);
3670 m_skipNodes.push(Graphics);
3671 } else {
3672 //qDebug()<<"Skipping "<<localName;
3673 m_skipNodes.push(Style);
3674 }
3675 return true;
3676}
3677
3678bool QSvgHandler::endElement(const QStringRef &localName)
3679{
3680 CurrentNode node = m_skipNodes.top();
3681 m_skipNodes.pop();
3682 m_whitespaceMode.pop();
3683
3684 popColor();
3685
3686 if (node == Unknown) {
3687 return true;
3688 }
3689
3690 if (m_inStyle && localName == QLatin1String("style"))
3691 m_inStyle = false;
3692
3693 if (node == Graphics)
3694 m_nodes.pop();
3695 else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
3696 m_style = 0;
3697
3698 return true;
3699}
3700
3701void QSvgHandler::resolveGradients(QSvgNode *node)
3702{
3703 if (!node || (node->type() != QSvgNode::DOC && node->type() != QSvgNode::G
3704 && node->type() != QSvgNode::DEFS && node->type() != QSvgNode::SWITCH)) {
3705 return;
3706 }
3707 QSvgStructureNode *structureNode = static_cast<QSvgStructureNode *>(node);
3708
3709 QList<QSvgNode *> ren = structureNode->renderers();
3710 for (QList<QSvgNode *>::iterator it = ren.begin(); it != ren.end(); ++it) {
3711 QSvgFillStyle *fill = static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
3712 if (fill && !fill->isGradientResolved()) {
3713 QString id = fill->gradientId();
3714 QSvgFillStyleProperty *style = structureNode->styleProperty(id);
3715 if (style) {
3716 fill->setFillStyle(style);
3717 } else {
3718 qWarning("Could not resolve property : %s", qPrintable(id));
3719 fill->setBrush(Qt::NoBrush);
3720 }
3721 }
3722
3723 QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
3724 if (stroke && !stroke->isGradientResolved()) {
3725 QString id = stroke->gradientId();
3726 QSvgFillStyleProperty *style = structureNode->styleProperty(id);
3727 if (style) {
3728 stroke->setStyle(style);
3729 } else {
3730 qWarning("Could not resolve property : %s", qPrintable(id));
3731 stroke->setStroke(Qt::NoBrush);
3732 }
3733 }
3734
3735 resolveGradients(*it);
3736 }
3737}
3738
3739bool QSvgHandler::characters(const QStringRef &str)
3740{
3741 if (m_inStyle) {
3742 QString css = str.toString();
3743 QCss::StyleSheet sheet;
3744 QCss::Parser(css).parse(&sheet);
3745 m_selector->styleSheets.append(sheet);
3746 return true;
3747 } else if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown)
3748 return true;
3749
3750 if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
3751 static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
3752 } else if (m_nodes.top()->type() == QSvgNode::TSPAN) {
3753 static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
3754 }
3755
3756 return true;
3757}
3758
3759QSvgTinyDocument * QSvgHandler::document() const
3760{
3761 return m_doc;
3762}
3763
3764QSvgHandler::LengthType QSvgHandler::defaultCoordinateSystem() const
3765{
3766 return m_defaultCoords;
3767}
3768
3769void QSvgHandler::setDefaultCoordinateSystem(LengthType type)
3770{
3771 m_defaultCoords = type;
3772}
3773
3774void QSvgHandler::pushColor(const QColor &color)
3775{
3776 m_colorStack.push(color);
3777 m_colorTagCount.push(1);
3778}
3779
3780void QSvgHandler::pushColorCopy()
3781{
3782 if (m_colorTagCount.count())
3783 ++m_colorTagCount.top();
3784 else
3785 pushColor(Qt::black);
3786}
3787
3788void QSvgHandler::popColor()
3789{
3790 if (m_colorTagCount.count()) {
3791 if (!--m_colorTagCount.top()) {
3792 m_colorStack.pop();
3793 m_colorTagCount.pop();
3794 }
3795 }
3796}
3797
3798QColor QSvgHandler::currentColor() const
3799{
3800 if (!m_colorStack.isEmpty())
3801 return m_colorStack.top();
3802 else
3803 return QColor(0, 0, 0);
3804}
3805
3806void QSvgHandler::setInStyle(bool b)
3807{
3808 m_inStyle = b;
3809}
3810
3811bool QSvgHandler::inStyle() const
3812{
3813 return m_inStyle;
3814}
3815
3816QSvgStyleSelector * QSvgHandler::selector() const
3817{
3818 return m_selector;
3819}
3820
3821bool QSvgHandler::processingInstruction(const QString &target, const QString &data)
3822{
3823 if (target == QLatin1String("xml-stylesheet")) {
3824 QRegExp rx(QLatin1String("type=\\\"(.+)\\\""));
3825 rx.setMinimal(true);
3826 bool isCss = false;
3827 int pos = 0;
3828 while ((pos = rx.indexIn(data, pos)) != -1) {
3829 QString type = rx.cap(1);
3830 if (type.toLower() == QLatin1String("text/css")) {
3831 isCss = true;
3832 }
3833 pos += rx.matchedLength();
3834 }
3835
3836 if (isCss) {
3837 QRegExp rx(QLatin1String("href=\\\"(.+)\\\""));
3838 rx.setMinimal(true);
3839 pos = 0;
3840 pos = rx.indexIn(data, pos);
3841 QString addr = rx.cap(1);
3842 QFileInfo fi(addr);
3843 //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
3844 if (fi.exists()) {
3845 QFile file(fi.absoluteFilePath());
3846 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
3847 return true;
3848 }
3849 QByteArray cssData = file.readAll();
3850 QString css = QString::fromUtf8(cssData);
3851
3852 QCss::StyleSheet sheet;
3853 QCss::Parser(css).parse(&sheet);
3854 m_selector->styleSheets.append(sheet);
3855 }
3856
3857 }
3858 }
3859
3860 return true;
3861}
3862
3863void QSvgHandler::setAnimPeriod(int start, int end)
3864{
3865 Q_UNUSED(start);
3866 m_animEnd = qMax(end, m_animEnd);
3867}
3868
3869int QSvgHandler::animationDuration() const
3870{
3871 return m_animEnd;
3872}
3873
3874QSvgHandler::~QSvgHandler()
3875{
3876 delete m_selector;
3877 m_selector = 0;
3878
3879 if(m_ownsReader)
3880 delete xml;
3881}
3882
3883QT_END_NAMESPACE
3884
3885#endif // QT_NO_SVG
Note: See TracBrowser for help on using the repository browser.