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

Last change on this file since 440 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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