summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/iconengines/iconengines.pro3
-rw-r--r--src/plugins/iconengines/svgiconengine/main.cpp84
-rw-r--r--src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp357
-rw-r--r--src/plugins/iconengines/svgiconengine/qsvgiconengine.h84
-rw-r--r--src/plugins/iconengines/svgiconengine/svgiconengine.pro13
-rw-r--r--src/plugins/imageformats/imageformats.pro2
-rw-r--r--src/plugins/imageformats/svg/main.cpp94
-rw-r--r--src/plugins/imageformats/svg/qsvgiohandler.cpp267
-rw-r--r--src/plugins/imageformats/svg/qsvgiohandler.h77
-rw-r--r--src/plugins/imageformats/svg/svg.pro13
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/src.pro3
-rw-r--r--src/svg/qgraphicssvgitem.cpp391
-rw-r--r--src/svg/qgraphicssvgitem.h101
-rw-r--r--src/svg/qsvgfont.cpp142
-rw-r--r--src/svg/qsvgfont_p.h103
-rw-r--r--src/svg/qsvggenerator.cpp1072
-rw-r--r--src/svg/qsvggenerator.h112
-rw-r--r--src/svg/qsvggraphics.cpp615
-rw-r--r--src/svg/qsvggraphics_p.h262
-rw-r--r--src/svg/qsvghandler.cpp3923
-rw-r--r--src/svg/qsvghandler_p.h188
-rw-r--r--src/svg/qsvgnode.cpp345
-rw-r--r--src/svg/qsvgnode_p.h206
-rw-r--r--src/svg/qsvgrenderer.cpp501
-rw-r--r--src/svg/qsvgrenderer.h120
-rw-r--r--src/svg/qsvgstructure.cpp383
-rw-r--r--src/svg/qsvgstructure_p.h118
-rw-r--r--src/svg/qsvgstyle.cpp958
-rw-r--r--src/svg/qsvgstyle_p.h826
-rw-r--r--src/svg/qsvgtinydocument.cpp492
-rw-r--r--src/svg/qsvgtinydocument_p.h200
-rw-r--r--src/svg/qsvgwidget.cpp173
-rw-r--r--src/svg/qsvgwidget.h85
-rw-r--r--src/svg/svg.pro45
35 files changed, 12360 insertions, 0 deletions
diff --git a/src/plugins/iconengines/iconengines.pro b/src/plugins/iconengines/iconengines.pro
new file mode 100644
index 0000000..bef8995
--- /dev/null
+++ b/src/plugins/iconengines/iconengines.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+contains(QT_CONFIG, svg): SUBDIRS += svgiconengine
diff --git a/src/plugins/iconengines/svgiconengine/main.cpp b/src/plugins/iconengines/svgiconengine/main.cpp
new file mode 100644
index 0000000..f7ca0c2
--- /dev/null
+++ b/src/plugins/iconengines/svgiconengine/main.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qiconengineplugin.h>
+#include <qstringlist.h>
+
+#if !defined(QT_NO_IMAGEFORMATPLUGIN) && !defined(QT_NO_SVG)
+
+#include "qsvgiconengine.h"
+
+#include <qiodevice.h>
+#include <qbytearray.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSvgIconPlugin : public QIconEnginePluginV2
+{
+public:
+ QStringList keys() const;
+ QIconEngineV2 *create(const QString &filename = QString());
+};
+
+QStringList QSvgIconPlugin::keys() const
+{
+ QStringList keys(QLatin1String("svg"));
+#ifndef QT_NO_COMPRESS
+ keys << QLatin1String("svgz") << QLatin1String("svg.gz");
+#endif
+ return keys;
+}
+
+QIconEngineV2 *QSvgIconPlugin::create(const QString &file)
+{
+ QSvgIconEngine *engine = new QSvgIconEngine;
+ if (!file.isNull())
+ engine->addFile(file, QSize(), QIcon::Normal, QIcon::Off);
+ return engine;
+}
+
+Q_EXPORT_STATIC_PLUGIN(QSvgIconPlugin)
+Q_EXPORT_PLUGIN2(qsvgicon, QSvgIconPlugin)
+
+QT_END_NAMESPACE
+
+#endif // !QT_NO_IMAGEFORMATPLUGIN
diff --git a/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp b/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp
new file mode 100644
index 0000000..9d9abb6
--- /dev/null
+++ b/src/plugins/iconengines/svgiconengine/qsvgiconengine.cpp
@@ -0,0 +1,357 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qsvgiconengine.h"
+
+#ifndef QT_NO_SVGRENDERER
+
+#include "qpainter.h"
+#include "qpixmap.h"
+#include "qsvgrenderer.h"
+#include "qpixmapcache.h"
+#include "qstyle.h"
+#include "qapplication.h"
+#include "qstyleoption.h"
+#include "qfileinfo.h"
+#include <QAtomicInt>
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSvgIconEnginePrivate : public QSharedData
+{
+public:
+ QSvgIconEnginePrivate()
+ : svgBuffers(0), addedPixmaps(0)
+ { stepSerialNum(); }
+
+ ~QSvgIconEnginePrivate()
+ { delete addedPixmaps; delete svgBuffers; }
+
+ static int hashKey(QIcon::Mode mode, QIcon::State state)
+ { return (((mode)<<4)|state); }
+
+ QString pmcKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
+ { return QLatin1String("$qt_svgicon_")
+ + QString::number(serialNum, 16).append(QLatin1Char('_'))
+ + QString::number((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state, 16); }
+
+ void stepSerialNum()
+ { serialNum = lastSerialNum.fetchAndAddRelaxed(1); }
+
+ void loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state);
+
+ QHash<int, QString> svgFiles;
+ QHash<int, QByteArray> *svgBuffers;
+ QHash<int, QPixmap> *addedPixmaps;
+ int serialNum;
+ static QAtomicInt lastSerialNum;
+};
+
+QAtomicInt QSvgIconEnginePrivate::lastSerialNum;
+
+static inline int pmKey(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return ((((((size.width()<<11)|size.height())<<11)|mode)<<4)|state);
+}
+
+QSvgIconEngine::QSvgIconEngine()
+ : d(new QSvgIconEnginePrivate)
+{
+}
+
+QSvgIconEngine::QSvgIconEngine(const QSvgIconEngine &other)
+ : QIconEngineV2(other), d(new QSvgIconEnginePrivate)
+{
+ d->svgFiles = other.d->svgFiles;
+ if (other.d->svgBuffers)
+ d->svgBuffers = new QHash<int, QByteArray>(*other.d->svgBuffers);
+ if (other.d->addedPixmaps)
+ d->addedPixmaps = new QHash<int, QPixmap>(*other.d->addedPixmaps);
+}
+
+
+QSvgIconEngine::~QSvgIconEngine()
+{
+}
+
+
+QSize QSvgIconEngine::actualSize(const QSize &size, QIcon::Mode mode,
+ QIcon::State state)
+{
+ if (d->addedPixmaps) {
+ QPixmap pm = d->addedPixmaps->value(d->hashKey(mode, state));
+ if (!pm.isNull() && pm.size() == size)
+ return size;
+ }
+
+ QPixmap pm = pixmap(size, mode, state);
+ if (pm.isNull())
+ return QSize();
+ return pm.size();
+}
+
+void QSvgIconEnginePrivate::loadDataForModeAndState(QSvgRenderer *renderer, QIcon::Mode mode, QIcon::State state)
+{
+ QByteArray buf;
+ if (svgBuffers) {
+ buf = svgBuffers->value(hashKey(mode, state));
+ if (buf.isEmpty())
+ buf = svgBuffers->value(hashKey(QIcon::Normal, QIcon::Off));
+ }
+ if (!buf.isEmpty()) {
+#ifndef QT_NO_COMPRESS
+ buf = qUncompress(buf);
+#endif
+ renderer->load(buf);
+ } else {
+ QString svgFile = svgFiles.value(hashKey(mode, state));
+ if (svgFile.isEmpty())
+ svgFile = svgFiles.value(hashKey(QIcon::Normal, QIcon::Off));
+ if (!svgFile.isEmpty())
+ renderer->load(svgFile);
+ }
+}
+
+QPixmap QSvgIconEngine::pixmap(const QSize &size, QIcon::Mode mode,
+ QIcon::State state)
+{
+ QPixmap pm;
+
+ QString pmckey(d->pmcKey(size, mode, state));
+ if (QPixmapCache::find(pmckey, pm))
+ return pm;
+
+ if (d->addedPixmaps) {
+ pm = d->addedPixmaps->value(d->hashKey(mode, state));
+ if (!pm.isNull() && pm.size() == size)
+ return pm;
+ }
+
+ QSvgRenderer renderer;
+ d->loadDataForModeAndState(&renderer, mode, state);
+ if (!renderer.isValid())
+ return pm;
+
+ QSize actualSize = renderer.defaultSize();
+ if (!actualSize.isNull())
+ actualSize.scale(size, Qt::KeepAspectRatio);
+
+ QImage img(actualSize, QImage::Format_ARGB32_Premultiplied);
+ img.fill(0x00000000);
+ QPainter p(&img);
+ renderer.render(&p);
+ p.end();
+ pm = QPixmap::fromImage(img);
+ QStyleOption opt(0);
+ opt.palette = QApplication::palette();
+ QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
+ if (!generated.isNull())
+ pm = generated;
+
+ if (!pm.isNull())
+ QPixmapCache::insert(pmckey, pm);
+
+ return pm;
+}
+
+
+void QSvgIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode,
+ QIcon::State state)
+{
+ if (!d->addedPixmaps)
+ d->addedPixmaps = new QHash<int, QPixmap>;
+ d->stepSerialNum();
+ d->addedPixmaps->insert(d->hashKey(mode, state), pixmap);
+}
+
+
+void QSvgIconEngine::addFile(const QString &fileName, const QSize &,
+ QIcon::Mode mode, QIcon::State state)
+{
+ if (!fileName.isEmpty()) {
+ QString abs = fileName;
+ if (fileName.at(0) != QLatin1Char(':'))
+ abs = QFileInfo(fileName).absoluteFilePath();
+ if (abs.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)
+#ifndef QT_NO_COMPRESS
+ || abs.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
+ || abs.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive))
+#endif
+ {
+ QSvgRenderer renderer(abs);
+ if (renderer.isValid()) {
+ d->stepSerialNum();
+ d->svgFiles.insert(d->hashKey(mode, state), abs);
+ }
+ } else {
+ QPixmap pm(abs);
+ if (!pm.isNull())
+ addPixmap(pm, mode, state);
+ }
+ }
+}
+
+void QSvgIconEngine::paint(QPainter *painter, const QRect &rect,
+ QIcon::Mode mode, QIcon::State state)
+{
+ painter->drawPixmap(rect, pixmap(rect.size(), mode, state));
+}
+
+QString QSvgIconEngine::key() const
+{
+ return QLatin1String("svg");
+}
+
+QIconEngineV2 *QSvgIconEngine::clone() const
+{
+ return new QSvgIconEngine(*this);
+}
+
+
+bool QSvgIconEngine::read(QDataStream &in)
+{
+ d = new QSvgIconEnginePrivate;
+ d->svgBuffers = new QHash<int, QByteArray>;
+
+ if (in.version() >= QDataStream::Qt_4_4) {
+ int isCompressed;
+ QHash<int, QString> fileNames; // For memoryoptimization later
+ in >> fileNames >> isCompressed >> *d->svgBuffers;
+#ifndef QT_NO_COMPRESS
+ if (!isCompressed) {
+ foreach(int key, d->svgBuffers->keys())
+ d->svgBuffers->insert(key, qCompress(d->svgBuffers->value(key)));
+ }
+#else
+ if (isCompressed) {
+ qWarning("QSvgIconEngine: Can not decompress SVG data");
+ d->svgBuffers->clear();
+ }
+#endif
+ int hasAddedPixmaps;
+ in >> hasAddedPixmaps;
+ if (hasAddedPixmaps) {
+ d->addedPixmaps = new QHash<int, QPixmap>;
+ in >> *d->addedPixmaps;
+ }
+ }
+ else {
+ QPixmap pixmap;
+ QByteArray data;
+ uint mode;
+ uint state;
+ int num_entries;
+
+ in >> data;
+ if (!data.isEmpty()) {
+#ifndef QT_NO_COMPRESS
+ data = qUncompress(data);
+#endif
+ if (!data.isEmpty())
+ d->svgBuffers->insert(d->hashKey(QIcon::Normal, QIcon::Off), data);
+ }
+ in >> num_entries;
+ for (int i=0; i<num_entries; ++i) {
+ if (in.atEnd())
+ return false;
+ in >> pixmap;
+ in >> mode;
+ in >> state;
+ // The pm list written by 4.3 is buggy and/or useless, so ignore.
+ //addPixmap(pixmap, QIcon::Mode(mode), QIcon::State(state));
+ }
+ }
+
+ return true;
+}
+
+
+bool QSvgIconEngine::write(QDataStream &out) const
+{
+ if (out.version() >= QDataStream::Qt_4_4) {
+ int isCompressed = 0;
+#ifndef QT_NO_COMPRESS
+ isCompressed = 1;
+#endif
+ QHash<int, QByteArray> svgBuffers;
+ if (d->svgBuffers)
+ svgBuffers = *d->svgBuffers;
+ foreach(int key, d->svgFiles.keys()) {
+ QByteArray buf;
+ QFile f(d->svgFiles.value(key));
+ if (f.open(QIODevice::ReadOnly))
+ buf = f.readAll();
+#ifndef QT_NO_COMPRESS
+ buf = qCompress(buf);
+#endif
+ svgBuffers.insert(key, buf);
+ }
+ out << d->svgFiles << isCompressed << svgBuffers;
+ if (d->addedPixmaps)
+ out << (int)1 << *d->addedPixmaps;
+ else
+ out << (int)0;
+ }
+ else {
+ QByteArray buf;
+ if (d->svgBuffers)
+ buf = d->svgBuffers->value(d->hashKey(QIcon::Normal, QIcon::Off));
+ if (buf.isEmpty()) {
+ QString svgFile = d->svgFiles.value(d->hashKey(QIcon::Normal, QIcon::Off));
+ if (!svgFile.isEmpty()) {
+ QFile f(svgFile);
+ if (f.open(QIODevice::ReadOnly))
+ buf = f.readAll();
+ }
+ }
+#ifndef QT_NO_COMPRESS
+ buf = qCompress(buf);
+#endif
+ out << buf;
+ // 4.3 has buggy handling of added pixmaps, so don't write any
+ out << (int)0;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVGRENDERER
diff --git a/src/plugins/iconengines/svgiconengine/qsvgiconengine.h b/src/plugins/iconengines/svgiconengine/qsvgiconengine.h
new file mode 100644
index 0000000..245adf6
--- /dev/null
+++ b/src/plugins/iconengines/svgiconengine/qsvgiconengine.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGICONENGINE_H
+#define QSVGICONENGINE_H
+
+#include <QtGui/qiconengine.h>
+#include <QtCore/qshareddata.h>
+
+#ifndef QT_NO_SVG
+
+QT_BEGIN_NAMESPACE
+
+class QSvgIconEnginePrivate;
+
+class QSvgIconEngine : public QIconEngineV2
+{
+public:
+ QSvgIconEngine();
+ QSvgIconEngine(const QSvgIconEngine &other);
+ ~QSvgIconEngine();
+ void paint(QPainter *painter, const QRect &rect,
+ QIcon::Mode mode, QIcon::State state);
+ QSize actualSize(const QSize &size, QIcon::Mode mode,
+ QIcon::State state);
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode,
+ QIcon::State state);
+
+ void addPixmap(const QPixmap &pixmap, QIcon::Mode mode,
+ QIcon::State state);
+ void addFile(const QString &fileName, const QSize &size,
+ QIcon::Mode mode, QIcon::State state);
+
+ QString key() const;
+ QIconEngineV2 *clone() const;
+ bool read(QDataStream &in);
+ bool write(QDataStream &out) const;
+
+private:
+ QSharedDataPointer<QSvgIconEnginePrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif
diff --git a/src/plugins/iconengines/svgiconengine/svgiconengine.pro b/src/plugins/iconengines/svgiconengine/svgiconengine.pro
new file mode 100644
index 0000000..5c5a31e
--- /dev/null
+++ b/src/plugins/iconengines/svgiconengine/svgiconengine.pro
@@ -0,0 +1,13 @@
+TARGET = qsvgicon
+include(../../qpluginbase.pri)
+
+HEADERS += qsvgiconengine.h
+SOURCES += main.cpp \
+ qsvgiconengine.cpp
+QT += xml svg
+
+QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/iconengines
+target.path += $$[QT_INSTALL_PLUGINS]/iconengines
+INSTALLS += target
+
+symbian:TARGET.UID3=0x2001B2E3
diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro
new file mode 100644
index 0000000..edffb73
--- /dev/null
+++ b/src/plugins/imageformats/imageformats.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += svg
diff --git a/src/plugins/imageformats/svg/main.cpp b/src/plugins/imageformats/svg/main.cpp
new file mode 100644
index 0000000..e143d3e
--- /dev/null
+++ b/src/plugins/imageformats/svg/main.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qimageiohandler.h>
+#include <qstringlist.h>
+
+#if !defined(QT_NO_IMAGEFORMATPLUGIN) && !defined(QT_NO_SVGRENDERER)
+
+#include "qsvgiohandler.h"
+
+#include <qiodevice.h>
+#include <qbytearray.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSvgPlugin : public QImageIOPlugin
+{
+public:
+ QStringList keys() const;
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
+};
+
+QStringList QSvgPlugin::keys() const
+{
+ return QStringList() << QLatin1String("svg") << QLatin1String("svgz");
+}
+
+QImageIOPlugin::Capabilities QSvgPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+{
+ if (format == "svg" || format == "svgz")
+ return Capabilities(CanRead);
+ if (!format.isEmpty())
+ return 0;
+
+ Capabilities cap;
+ if (device->isReadable() && QSvgIOHandler::canRead(device))
+ cap |= CanRead;
+ return cap;
+}
+
+QImageIOHandler *QSvgPlugin::create(QIODevice *device, const QByteArray &format) const
+{
+ QSvgIOHandler *hand = new QSvgIOHandler();
+ hand->setDevice(device);
+ hand->setFormat(format);
+ return hand;
+}
+
+Q_EXPORT_STATIC_PLUGIN(QSvgPlugin)
+Q_EXPORT_PLUGIN2(qsvg, QSvgPlugin)
+
+QT_END_NAMESPACE
+
+#endif // !QT_NO_IMAGEFORMATPLUGIN
diff --git a/src/plugins/imageformats/svg/qsvgiohandler.cpp b/src/plugins/imageformats/svg/qsvgiohandler.cpp
new file mode 100644
index 0000000..3c8f2e9
--- /dev/null
+++ b/src/plugins/imageformats/svg/qsvgiohandler.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgiohandler.h"
+
+#ifndef QT_NO_SVGRENDERER
+
+#include "qsvgrenderer.h"
+#include "qimage.h"
+#include "qpixmap.h"
+#include "qpainter.h"
+#include "qvariant.h"
+#include "qbuffer.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSvgIOHandlerPrivate
+{
+public:
+ QSvgIOHandlerPrivate(QSvgIOHandler *qq)
+ : q(qq), loaded(false), readDone(false), backColor(Qt::transparent)
+ {}
+
+ bool load(QIODevice *device);
+
+ QSvgIOHandler *q;
+ QSvgRenderer r;
+ QXmlStreamReader xmlReader;
+ QSize defaultSize;
+ QRect clipRect;
+ QSize scaledSize;
+ QRect scaledClipRect;
+ bool loaded;
+ bool readDone;
+ QColor backColor;
+};
+
+
+bool QSvgIOHandlerPrivate::load(QIODevice *device)
+{
+ if (loaded)
+ return true;
+ if (q->format().isEmpty())
+ q->canRead();
+
+ // # The SVG renderer doesn't handle trailing, unrelated data, so we must
+ // assume that all available data in the device is to be read.
+ bool res = false;
+ QBuffer *buf = qobject_cast<QBuffer *>(device);
+ if (buf) {
+ const QByteArray &ba = buf->data();
+ res = r.load(QByteArray::fromRawData(ba.constData() + buf->pos(), ba.size() - buf->pos()));
+ buf->seek(ba.size());
+ } else if (q->format() == "svgz") {
+ res = r.load(device->readAll());
+ } else {
+ xmlReader.setDevice(device);
+ res = r.load(&xmlReader);
+ }
+
+ if (res) {
+ defaultSize = QSize(r.viewBox().width(), r.viewBox().height());
+ loaded = true;
+ }
+
+ return loaded;
+}
+
+
+QSvgIOHandler::QSvgIOHandler()
+ : d(new QSvgIOHandlerPrivate(this))
+{
+
+}
+
+
+QSvgIOHandler::~QSvgIOHandler()
+{
+ delete d;
+}
+
+
+bool QSvgIOHandler::canRead() const
+{
+ if (!device())
+ return false;
+ if (d->loaded && !d->readDone)
+ return true; // Will happen if we have been asked for the size
+
+ QByteArray buf = device()->peek(8);
+ if (buf.startsWith("\x1f\x8b")) {
+ setFormat("svgz");
+ return true;
+ } else if (buf.contains("<?xml") || buf.contains("<svg")) {
+ setFormat("svg");
+ return true;
+ }
+ return false;
+}
+
+
+QByteArray QSvgIOHandler::name() const
+{
+ return "svg";
+}
+
+
+bool QSvgIOHandler::read(QImage *image)
+{
+ if (!d->readDone && d->load(device())) {
+ bool xform = (d->clipRect.isValid() || d->scaledSize.isValid() || d->scaledClipRect.isValid());
+ QSize finalSize = d->defaultSize;
+ QRectF bounds;
+ if (xform && !d->defaultSize.isEmpty()) {
+ bounds = QRectF(QPointF(0,0), QSizeF(d->defaultSize));
+ QPoint tr1, tr2;
+ QSizeF sc(1, 1);
+ if (d->clipRect.isValid()) {
+ tr1 = -d->clipRect.topLeft();
+ finalSize = d->clipRect.size();
+ }
+ if (d->scaledSize.isValid()) {
+ sc = QSizeF(qreal(d->scaledSize.width()) / finalSize.width(),
+ qreal(d->scaledSize.height()) / finalSize.height());
+ finalSize = d->scaledSize;
+ }
+ if (d->scaledClipRect.isValid()) {
+ tr2 = -d->scaledClipRect.topLeft();
+ finalSize = d->scaledClipRect.size();
+ }
+ QTransform t;
+ t.translate(tr2.x(), tr2.y());
+ t.scale(sc.width(), sc.height());
+ t.translate(tr1.x(), tr1.y());
+ bounds = t.mapRect(bounds);
+ }
+ *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied);
+ if (!finalSize.isEmpty()) {
+ image->fill(d->backColor.rgba());
+ QPainter p(image);
+ d->r.render(&p, bounds);
+ p.end();
+ }
+ d->readDone = true;
+ return true;
+ }
+
+ return false;
+}
+
+
+QVariant QSvgIOHandler::option(ImageOption option) const
+{
+ switch(option) {
+ case ImageFormat:
+ return QImage::Format_ARGB32_Premultiplied;
+ break;
+ case Size:
+ d->load(device());
+ return d->defaultSize;
+ break;
+ case ClipRect:
+ return d->clipRect;
+ break;
+ case ScaledSize:
+ return d->scaledSize;
+ break;
+ case ScaledClipRect:
+ return d->scaledClipRect;
+ break;
+ case BackgroundColor:
+ return d->backColor;
+ break;
+ default:
+ break;
+ }
+ return QVariant();
+}
+
+
+void QSvgIOHandler::setOption(ImageOption option, const QVariant & value)
+{
+ switch(option) {
+ case ClipRect:
+ d->clipRect = value.toRect();
+ break;
+ case ScaledSize:
+ d->scaledSize = value.toSize();
+ break;
+ case ScaledClipRect:
+ d->scaledClipRect = value.toRect();
+ break;
+ case BackgroundColor:
+ d->backColor = value.value<QColor>();
+ break;
+ default:
+ break;
+ }
+}
+
+
+bool QSvgIOHandler::supportsOption(ImageOption option) const
+{
+ switch(option)
+ {
+ case ImageFormat:
+ case Size:
+ case ClipRect:
+ case ScaledSize:
+ case ScaledClipRect:
+ case BackgroundColor:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+
+bool QSvgIOHandler::canRead(QIODevice *device)
+{
+ QByteArray buf = device->peek(8);
+ return buf.startsWith("\x1f\x8b") || buf.contains("<?xml") || buf.contains("<svg");
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVGRENDERER
diff --git a/src/plugins/imageformats/svg/qsvgiohandler.h b/src/plugins/imageformats/svg/qsvgiohandler.h
new file mode 100644
index 0000000..7ef0009
--- /dev/null
+++ b/src/plugins/imageformats/svg/qsvgiohandler.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGIOHANDLER_H
+#define QSVGIOHANDLER_H
+
+#include <QtGui/qimageiohandler.h>
+
+#ifndef QT_NO_SVGRENDERER
+
+QT_BEGIN_NAMESPACE
+
+class QImage;
+class QByteArray;
+class QIODevice;
+class QVariant;
+class QSvgIOHandlerPrivate;
+
+class QSvgIOHandler : public QImageIOHandler
+{
+public:
+ QSvgIOHandler();
+ ~QSvgIOHandler();
+ virtual bool canRead() const;
+ virtual QByteArray name() const;
+ virtual bool read(QImage *image);
+ static bool canRead(QIODevice *device);
+ virtual QVariant option(ImageOption option) const;
+ virtual void setOption(ImageOption option, const QVariant & value);
+ virtual bool supportsOption(ImageOption option) const;
+
+private:
+ QSvgIOHandlerPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVGRENDERER
+#endif // QSVGIOHANDLER_H
diff --git a/src/plugins/imageformats/svg/svg.pro b/src/plugins/imageformats/svg/svg.pro
new file mode 100644
index 0000000..bcf4c21
--- /dev/null
+++ b/src/plugins/imageformats/svg/svg.pro
@@ -0,0 +1,13 @@
+TARGET = qsvg
+include(../../qpluginbase.pri)
+
+HEADERS += qsvgiohandler.h
+SOURCES += main.cpp \
+ qsvgiohandler.cpp
+QT += xml svg
+
+QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats
+target.path += $$[QT_INSTALL_PLUGINS]/imageformats
+INSTALLS += target
+
+symbian:TARGET.UID3=0x2001E618
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
new file mode 100644
index 0000000..1d714d4
--- /dev/null
+++ b/src/plugins/plugins.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += iconengines imageformats
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 0000000..903663f
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+CONFIG += ordered
+SUBDIRS += svg plugins
diff --git a/src/svg/qgraphicssvgitem.cpp b/src/svg/qgraphicssvgitem.cpp
new file mode 100644
index 0000000..9c3742e
--- /dev/null
+++ b/src/svg/qgraphicssvgitem.cpp
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qgraphicssvgitem.h"
+
+#ifndef QT_NO_GRAPHICSSVGITEM
+
+#include "qpainter.h"
+#include "qstyleoption.h"
+#include "qsvgrenderer.h"
+#include "qdebug.h"
+
+#include "private/qobject_p.h"
+#include "private/qgraphicsitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QGraphicsSvgItemPrivate : public QGraphicsItemPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QGraphicsSvgItem)
+
+ QGraphicsSvgItemPrivate()
+ : renderer(0), shared(false)
+ {
+ }
+
+ void init(QGraphicsItem *parent)
+ {
+ Q_Q(QGraphicsSvgItem);
+ q->setParentItem(parent);
+ renderer = new QSvgRenderer(q);
+ QObject::connect(renderer, SIGNAL(repaintNeeded()),
+ q, SLOT(_q_repaintItem()));
+ q->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+ q->setMaximumCacheSize(QSize(1024, 768));
+ }
+
+ void _q_repaintItem()
+ {
+ q_func()->update();
+ }
+
+ inline void updateDefaultSize()
+ {
+ QRectF bounds;
+ if (elemId.isEmpty()) {
+ bounds = QRectF(QPointF(0, 0), renderer->defaultSize());
+ } else {
+ bounds = renderer->boundsOnElement(elemId);
+ }
+ if (boundingRect.size() != bounds.size()) {
+ q_func()->prepareGeometryChange();
+ boundingRect.setSize(bounds.size());
+ }
+ }
+
+ QSvgRenderer *renderer;
+ QRectF boundingRect;
+ bool shared;
+ QString elemId;
+};
+
+/*!
+ \class QGraphicsSvgItem
+ \ingroup graphicsview-api
+ \brief The QGraphicsSvgItem class is a QGraphicsItem that can be used to render
+ the contents of SVG files.
+
+ \since 4.2
+
+ QGraphicsSvgItem provides a way of rendering SVG files onto QGraphicsView.
+ QGraphicsSvgItem can be created by passing the SVG file to be rendered to
+ its constructor or by explicit setting a shared QSvgRenderer on it.
+
+ Note that setting QSvgRenderer on a QGraphicsSvgItem doesn't make the item take
+ ownership of the renderer, therefore if using setSharedRenderer() method one has
+ to make sure that the lifetime of the QSvgRenderer object will be at least as long
+ as that of the QGraphicsSvgItem.
+
+ QGraphicsSvgItem provides a way of rendering only parts of the SVG files via
+ the setElementId. If setElementId() method is called, only the SVG element
+ (and its children) with the passed id will be renderer. This provides a convenient
+ way of selectively rendering large SVG files that contain a number of discrete
+ elements. For example the following code renders only jokers from a SVG file
+ containing a whole card deck:
+
+ \snippet doc/src/snippets/code/src_svg_qgraphicssvgitem.cpp 0
+
+ Size of the item can be set via the \l{QRectF::setSize()}
+ {setSize()} method of the \l{QGraphicsSvgItem::boundingRect()}
+ {bounding rectangle} or via direct manipulation of the items
+ transformation matrix.
+
+ By default the SVG rendering is cached using QGraphicsItem::DeviceCoordinateCache
+ mode to speedup the display of items. Caching can be disabled by passing
+ QGraphicsItem::NoCache to the QGraphicsItem::setCacheMode() method.
+
+ \sa QSvgWidget, {QtSvg Module}, QGraphicsItem, QGraphicsView
+*/
+
+/*!
+ Constructs a new SVG item with the given \a parent.
+*/
+QGraphicsSvgItem::QGraphicsSvgItem(QGraphicsItem *parent)
+ : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0, 0)
+{
+ Q_D(QGraphicsSvgItem);
+ d->init(parent);
+}
+
+/*!
+ Constructs a new item with the given \a parent and loads the contents of the
+ SVG file with the specified \a fileName.
+*/
+QGraphicsSvgItem::QGraphicsSvgItem(const QString &fileName, QGraphicsItem *parent)
+ : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0, 0)
+{
+ Q_D(QGraphicsSvgItem);
+ d->init(parent);
+ d->renderer->load(fileName);
+ d->updateDefaultSize();
+}
+
+/*!
+ Returns the currently use QSvgRenderer.
+*/
+QSvgRenderer *QGraphicsSvgItem::renderer() const
+{
+ return d_func()->renderer;
+}
+
+
+/*!
+ Returns the bounding rectangle of this item.
+*/
+QRectF QGraphicsSvgItem::boundingRect() const
+{
+ Q_D(const QGraphicsSvgItem);
+ return d->boundingRect;
+}
+
+/*!
+ \internal
+
+ Highlights \a item as selected.
+
+ NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in qgraphicsitem.cpp!
+*/
+static void qt_graphicsItem_highlightSelected(
+ QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option)
+{
+ const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1));
+ if (qFuzzyIsNull(qMax(murect.width(), murect.height())))
+ return;
+
+ const QRectF mbrect = painter->transform().mapRect(item->boundingRect());
+ if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0))
+ return;
+
+ qreal itemPenWidth;
+ switch (item->type()) {
+ case QGraphicsEllipseItem::Type:
+ itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPathItem::Type:
+ itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsPolygonItem::Type:
+ itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsRectItem::Type:
+ itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsSimpleTextItem::Type:
+ itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF();
+ break;
+ case QGraphicsLineItem::Type:
+ itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF();
+ break;
+ default:
+ itemPenWidth = 1.0;
+ }
+ const qreal pad = itemPenWidth / 2;
+
+ const qreal penWidth = 0; // cosmetic pen
+
+ const QColor fgcolor = option->palette.windowText().color();
+ const QColor bgcolor( // ensure good contrast against fgcolor
+ fgcolor.red() > 127 ? 0 : 255,
+ fgcolor.green() > 127 ? 0 : 255,
+ fgcolor.blue() > 127 ? 0 : 255);
+
+ painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+
+ painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
+ painter->setBrush(Qt::NoBrush);
+ painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad));
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
+{
+// Q_UNUSED(option);
+ Q_UNUSED(widget);
+
+ Q_D(QGraphicsSvgItem);
+ if (!d->renderer->isValid())
+ return;
+
+ if (d->elemId.isEmpty())
+ d->renderer->render(painter, d->boundingRect);
+ else
+ d->renderer->render(painter, d->elemId, d->boundingRect);
+
+ if (option->state & QStyle::State_Selected)
+ qt_graphicsItem_highlightSelected(this, painter, option);
+}
+
+/*!
+ \reimp
+*/
+int QGraphicsSvgItem::type() const
+{
+ return Type;
+}
+
+/*!
+ \property QGraphicsSvgItem::maximumCacheSize
+ \since 4.6
+
+ This property holds the maximum size of the device coordinate cache
+ for this item.
+ */
+
+/*!
+ Sets the maximum device coordinate cache size of the item to \a size.
+ If the item is cached using QGraphicsItem::DeviceCoordinateCache mode,
+ caching is bypassed if the extension of the item in device coordinates
+ is larger than \a size.
+
+ The cache corresponds to the QPixmap which is used to cache the
+ results of the rendering.
+ Use QPixmapCache::setCacheLimit() to set limitations on the whole cache
+ and use setMaximumCacheSize() when setting cache size for individual
+ items.
+
+ \sa QGraphicsItem::cacheMode()
+*/
+void QGraphicsSvgItem::setMaximumCacheSize(const QSize &size)
+{
+ QGraphicsItem::d_ptr->setExtra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize, size);
+ update();
+}
+
+/*!
+ Returns the current maximum size of the device coordinate cache for this item.
+ If the item is cached using QGraphicsItem::DeviceCoordinateCache mode,
+ caching is bypassed if the extension of the item in device coordinates
+ is larger than the maximum size.
+
+ The default maximum cache size is 1024x768.
+ QPixmapCache::cacheLimit() gives the
+ cumulative bounds of the whole cache, whereas maximumCacheSize() refers
+ to a maximum cache size for this particular item.
+
+ \sa QGraphicsItem::cacheMode()
+*/
+QSize QGraphicsSvgItem::maximumCacheSize() const
+{
+ return QGraphicsItem::d_ptr->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize();
+}
+
+/*!
+ \property QGraphicsSvgItem::elementId
+ \since 4.6
+
+ This property holds the element's XML ID.
+ */
+
+/*!
+ Sets the XML ID of the element to \a id.
+*/
+void QGraphicsSvgItem::setElementId(const QString &id)
+{
+ Q_D(QGraphicsSvgItem);
+ d->elemId = id;
+ d->updateDefaultSize();
+ update();
+}
+
+/*!
+ Returns the XML ID the element that is currently
+ being rendered. Returns an empty string if the whole
+ file is being rendered.
+*/
+QString QGraphicsSvgItem::elementId() const
+{
+ Q_D(const QGraphicsSvgItem);
+ return d->elemId;
+}
+
+/*!
+ Sets \a renderer to be a shared QSvgRenderer on the item. By
+ using this method one can share the same QSvgRenderer on a number
+ of items. This means that the SVG file will be parsed only once.
+ QSvgRenderer passed to this method has to exist for as long as
+ this item is used.
+*/
+void QGraphicsSvgItem::setSharedRenderer(QSvgRenderer *renderer)
+{
+ Q_D(QGraphicsSvgItem);
+ if (!d->shared)
+ delete d->renderer;
+
+ d->renderer = renderer;
+ d->shared = true;
+
+ d->updateDefaultSize();
+
+ update();
+}
+
+/*!
+ \obsolete
+
+ Use QGraphicsItem::setCacheMode() instead. Passing true to this function is equivalent
+ to QGraphicsItem::setCacheMode(QGraphicsItem::DeviceCoordinateCache).
+*/
+void QGraphicsSvgItem::setCachingEnabled(bool caching)
+{
+ setCacheMode(caching ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache);
+}
+
+/*!
+ \obsolete
+
+ Use QGraphicsItem::cacheMode() instead.
+*/
+bool QGraphicsSvgItem::isCachingEnabled() const
+{
+ return cacheMode() != QGraphicsItem::NoCache;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qgraphicssvgitem.cpp"
+
+#endif // QT_NO_GRAPHICSSVGITEM
diff --git a/src/svg/qgraphicssvgitem.h b/src/svg/qgraphicssvgitem.h
new file mode 100644
index 0000000..6577db4
--- /dev/null
+++ b/src/svg/qgraphicssvgitem.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QGRAPHICSSVGITEM_H
+#define QGRAPHICSSVGITEM_H
+
+#include <QtGui/qgraphicsitem.h>
+
+#ifndef QT_NO_GRAPHICSSVGITEM
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Svg)
+
+class QSvgRenderer;
+class QGraphicsSvgItemPrivate;
+
+class Q_SVG_EXPORT QGraphicsSvgItem : public QGraphicsObject
+{
+ Q_OBJECT
+ Q_INTERFACES(QGraphicsItem)
+ Q_PROPERTY(QString elementId READ elementId WRITE setElementId)
+ Q_PROPERTY(QSize maximumCacheSize READ maximumCacheSize WRITE setMaximumCacheSize)
+
+public:
+ QGraphicsSvgItem(QGraphicsItem *parentItem=0);
+ QGraphicsSvgItem(const QString &fileName, QGraphicsItem *parentItem=0);
+
+ void setSharedRenderer(QSvgRenderer *renderer);
+ QSvgRenderer *renderer() const;
+
+ void setElementId(const QString &id);
+ QString elementId() const;
+
+ void setCachingEnabled(bool);
+ bool isCachingEnabled() const;
+
+ void setMaximumCacheSize(const QSize &size);
+ QSize maximumCacheSize() const;
+
+ virtual QRectF boundingRect() const;
+
+ virtual void paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option,
+ QWidget *widget=0);
+
+ enum { Type = 13 };
+ virtual int type() const;
+
+private:
+ Q_DISABLE_COPY(QGraphicsSvgItem)
+ Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QGraphicsSvgItem)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_repaintItem())
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_GRAPHICSSVGITEM
+#endif // QGRAPHICSSVGITEM_H
diff --git a/src/svg/qsvgfont.cpp b/src/svg/qsvgfont.cpp
new file mode 100644
index 0000000..66950ff
--- /dev/null
+++ b/src/svg/qsvgfont.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgfont_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qpainter.h"
+#include "qpen.h"
+#include "qdebug.h"
+#include "qpicture.h"
+
+QT_BEGIN_NAMESPACE
+
+QSvgGlyph::QSvgGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX)
+ : m_unicode(unicode), m_path(path), m_horizAdvX(horizAdvX)
+{
+
+}
+
+
+QSvgFont::QSvgFont(qreal horizAdvX)
+ : m_horizAdvX(horizAdvX)
+{
+}
+
+
+QString QSvgFont::familyName() const
+{
+ return m_familyName;
+}
+
+
+void QSvgFont::addGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX )
+{
+ m_glyphs.insert(unicode, QSvgGlyph(unicode, path,
+ (horizAdvX==-1)?m_horizAdvX:horizAdvX));
+}
+
+
+void QSvgFont::draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const
+{
+ p->save();
+ p->translate(point);
+ p->scale(pixelSize / m_unitsPerEm, -pixelSize / m_unitsPerEm);
+
+ // Calculate the text width to be used for alignment
+ int textWidth = 0;
+ QString::const_iterator itr = str.constBegin();
+ for ( ; itr != str.constEnd(); ++itr) {
+ QChar unicode = *itr;
+ if (!m_glyphs.contains(*itr)) {
+ unicode = 0;
+ if (!m_glyphs.contains(unicode))
+ continue;
+ }
+ textWidth += static_cast<int>(m_glyphs[unicode].m_horizAdvX);
+ }
+
+ QPoint alignmentOffset(0, 0);
+ if (alignment == Qt::AlignHCenter) {
+ alignmentOffset.setX(-textWidth / 2);
+ } else if (alignment == Qt::AlignRight) {
+ alignmentOffset.setX(-textWidth);
+ }
+
+ p->translate(alignmentOffset);
+
+ // since in SVG the embedded font ain't really a path
+ // the outline has got to stay untransformed...
+ qreal penWidth = p->pen().widthF();
+ penWidth /= (pixelSize/m_unitsPerEm);
+ QPen pen = p->pen();
+ pen.setWidthF(penWidth);
+ p->setPen(pen);
+
+ itr = str.constBegin();
+ for ( ; itr != str.constEnd(); ++itr) {
+ QChar unicode = *itr;
+ if (!m_glyphs.contains(*itr)) {
+ unicode = 0;
+ if (!m_glyphs.contains(unicode))
+ continue;
+ }
+ p->drawPath(m_glyphs[unicode].m_path);
+ p->translate(m_glyphs[unicode].m_horizAdvX, 0);
+ }
+
+ p->restore();
+}
+
+void QSvgFont::setFamilyName(const QString &name)
+{
+ m_familyName = name;
+}
+
+void QSvgFont::setUnitsPerEm(qreal upem)
+{
+ m_unitsPerEm = upem;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvgfont_p.h b/src/svg/qsvgfont_p.h
new file mode 100644
index 0000000..df82ee5
--- /dev/null
+++ b/src/svg/qsvgfont_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGFONT_P_H
+#define QSVGFONT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qpainterpath.h"
+
+#ifndef QT_NO_SVG
+
+#include "qhash.h"
+#include "qstring.h"
+#include "qsvgstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSvgGlyph
+{
+public:
+ QSvgGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX);
+ QSvgGlyph() : m_unicode(0), m_horizAdvX(0) {}
+
+ QChar m_unicode;
+ QPainterPath m_path;
+ qreal m_horizAdvX;
+};
+
+
+class QSvgFont : public QSvgRefCounted
+{
+public:
+ QSvgFont(qreal horizAdvX);
+
+ void setFamilyName(const QString &name);
+ QString familyName() const;
+
+ void setUnitsPerEm(qreal upem);
+
+ void addGlyph(QChar unicode, const QPainterPath &path, qreal horizAdvX = -1);
+
+ void draw(QPainter *p, const QPointF &point, const QString &str, qreal pixelSize, Qt::Alignment alignment) const;
+public:
+ QString m_familyName;
+ qreal m_unitsPerEm;
+ qreal m_ascent;
+ qreal m_descent;
+ qreal m_horizAdvX;
+ QHash<QChar, QSvgGlyph> m_glyphs;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGFONT_P_H
diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp
new file mode 100644
index 0000000..d0d73ae
--- /dev/null
+++ b/src/svg/qsvggenerator.cpp
@@ -0,0 +1,1072 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvggenerator.h"
+
+#ifndef QT_NO_SVGGENERATOR
+
+#include "qpainterpath.h"
+
+#include "private/qpaintengine_p.h"
+#include "private/qtextengine_p.h"
+#include "private/qdrawhelper_p.h"
+
+#include "qfile.h"
+#include "qtextcodec.h"
+#include "qtextstream.h"
+#include "qbuffer.h"
+#include "qmath.h"
+
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+static void translate_color(const QColor &color, QString *color_string,
+ QString *opacity_string)
+{
+ Q_ASSERT(color_string);
+ Q_ASSERT(opacity_string);
+
+ *color_string =
+ QString::fromLatin1("#%1%2%3")
+ .arg(color.red(), 2, 16, QLatin1Char('0'))
+ .arg(color.green(), 2, 16, QLatin1Char('0'))
+ .arg(color.blue(), 2, 16, QLatin1Char('0'));
+ *opacity_string = QString::number(color.alphaF());
+}
+
+static void translate_dashPattern(QVector<qreal> pattern, const qreal& width, QString *pattern_string)
+{
+ Q_ASSERT(pattern_string);
+
+ // Note that SVG operates in absolute lengths, whereas Qt uses a length/width ratio.
+ foreach (qreal entry, pattern)
+ *pattern_string += QString::fromLatin1("%1,").arg(entry * width);
+
+ pattern_string->chop(1);
+}
+
+class QSvgPaintEnginePrivate : public QPaintEnginePrivate
+{
+public:
+ QSvgPaintEnginePrivate()
+ {
+ size = QSize();
+ viewBox = QRectF();
+ outputDevice = 0;
+ resolution = 72;
+
+ attributes.document_title = QLatin1String("Qt Svg Document");
+ attributes.document_description = QLatin1String("Generated with Qt");
+ attributes.font_family = QLatin1String("serif");
+ attributes.font_size = QLatin1String("10pt");
+ attributes.font_style = QLatin1String("normal");
+ attributes.font_weight = QLatin1String("normal");
+
+ afterFirstUpdate = false;
+ numGradients = 0;
+ }
+
+ QSize size;
+ QRectF viewBox;
+ QIODevice *outputDevice;
+ QTextStream *stream;
+ int resolution;
+
+ QString header;
+ QString defs;
+ QString body;
+ bool afterFirstUpdate;
+
+ QBrush brush;
+ QPen pen;
+ QMatrix matrix;
+ QFont font;
+
+ QString generateGradientName() {
+ ++numGradients;
+ currentGradientName = QString::fromLatin1("gradient%1").arg(numGradients);
+ return currentGradientName;
+ }
+
+ QString currentGradientName;
+ int numGradients;
+
+ struct _attributes {
+ QString document_title;
+ QString document_description;
+ QString font_weight;
+ QString font_size;
+ QString font_family;
+ QString font_style;
+ QString stroke, strokeOpacity;
+ QString dashPattern, dashOffset;
+ QString fill, fillOpacity;
+ } attributes;
+};
+
+static inline QPaintEngine::PaintEngineFeatures svgEngineFeatures()
+{
+ return QPaintEngine::PaintEngineFeatures(
+ QPaintEngine::AllFeatures
+ & ~QPaintEngine::PatternBrush
+ & ~QPaintEngine::PerspectiveTransform
+ & ~QPaintEngine::ConicalGradientFill
+ & ~QPaintEngine::PorterDuff);
+}
+
+class QSvgPaintEngine : public QPaintEngine
+{
+ Q_DECLARE_PRIVATE(QSvgPaintEngine)
+public:
+
+ QSvgPaintEngine()
+ : QPaintEngine(*new QSvgPaintEnginePrivate,
+ svgEngineFeatures())
+ {
+ }
+
+ bool begin(QPaintDevice *device);
+ bool end();
+
+ void updateState(const QPaintEngineState &state);
+ void popGroup();
+
+ void drawPath(const QPainterPath &path);
+ void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
+ void drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode);
+ void drawTextItem(const QPointF &pt, const QTextItem &item);
+ void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
+ Qt::ImageConversionFlag = Qt::AutoColor);
+
+ QPaintEngine::Type type() const { return QPaintEngine::SVG; }
+
+ QSize size() const { return d_func()->size; }
+ void setSize(const QSize &size) {
+ Q_ASSERT(!isActive());
+ d_func()->size = size;
+ }
+
+ QRectF viewBox() const { return d_func()->viewBox; }
+ void setViewBox(const QRectF &viewBox) {
+ Q_ASSERT(!isActive());
+ d_func()->viewBox = viewBox;
+ }
+
+ QString documentTitle() const { return d_func()->attributes.document_title; }
+ void setDocumentTitle(const QString &title) {
+ d_func()->attributes.document_title = title;
+ }
+
+ QString documentDescription() const { return d_func()->attributes.document_description; }
+ void setDocumentDescription(const QString &description) {
+ d_func()->attributes.document_description = description;
+ }
+
+ QIODevice *outputDevice() const { return d_func()->outputDevice; }
+ void setOutputDevice(QIODevice *device) {
+ Q_ASSERT(!isActive());
+ d_func()->outputDevice = device;
+ }
+
+ int resolution() { return d_func()->resolution; }
+ void setResolution(int resolution) {
+ Q_ASSERT(!isActive());
+ d_func()->resolution = resolution;
+ }
+ void saveLinearGradientBrush(const QGradient *g)
+ {
+ QTextStream str(&d_func()->defs, QIODevice::Append);
+ const QLinearGradient *grad = static_cast<const QLinearGradient*>(g);
+ str << QLatin1String("<linearGradient ");
+ saveGradientUnits(str, g);
+ if (grad) {
+ str << QLatin1String("x1=\"") <<grad->start().x()<< QLatin1String("\" ")
+ << QLatin1String("y1=\"") <<grad->start().y()<< QLatin1String("\" ")
+ << QLatin1String("x2=\"") <<grad->finalStop().x() << QLatin1String("\" ")
+ << QLatin1String("y2=\"") <<grad->finalStop().y() << QLatin1String("\" ");
+ }
+
+ str << QLatin1String("id=\"") << d_func()->generateGradientName() << QLatin1String("\">\n");
+ saveGradientStops(str, g);
+ str << QLatin1String("</linearGradient>") <<endl;
+ }
+ void saveRadialGradientBrush(const QGradient *g)
+ {
+ QTextStream str(&d_func()->defs, QIODevice::Append);
+ const QRadialGradient *grad = static_cast<const QRadialGradient*>(g);
+ str << QLatin1String("<radialGradient ");
+ saveGradientUnits(str, g);
+ if (grad) {
+ str << QLatin1String("cx=\"") <<grad->center().x()<< QLatin1String("\" ")
+ << QLatin1String("cy=\"") <<grad->center().y()<< QLatin1String("\" ")
+ << QLatin1String("r=\"") <<grad->radius() << QLatin1String("\" ")
+ << QLatin1String("fx=\"") <<grad->focalPoint().x() << QLatin1String("\" ")
+ << QLatin1String("fy=\"") <<grad->focalPoint().y() << QLatin1String("\" ");
+ }
+ str << QLatin1String("xml:id=\"") <<d_func()->generateGradientName()<< QLatin1String("\">\n");
+ saveGradientStops(str, g);
+ str << QLatin1String("</radialGradient>") << endl;
+ }
+ void saveConicalGradientBrush(const QGradient *)
+ {
+ qWarning("svg's don't support conical gradients!");
+ }
+
+ void saveGradientStops(QTextStream &str, const QGradient *g) {
+ QGradientStops stops = g->stops();
+
+ if (g->interpolationMode() == QGradient::ColorInterpolation) {
+ bool constantAlpha = true;
+ int alpha = stops.at(0).second.alpha();
+ for (int i = 1; i < stops.size(); ++i)
+ constantAlpha &= (stops.at(i).second.alpha() == alpha);
+
+ if (!constantAlpha) {
+ const qreal spacing = qreal(0.02);
+ QGradientStops newStops;
+ QRgb fromColor = PREMUL(stops.at(0).second.rgba());
+ QRgb toColor;
+ for (int i = 0; i + 1 < stops.size(); ++i) {
+ int parts = qCeil((stops.at(i + 1).first - stops.at(i).first) / spacing);
+ newStops.append(stops.at(i));
+ toColor = PREMUL(stops.at(i + 1).second.rgba());
+
+ if (parts > 1) {
+ qreal step = (stops.at(i + 1).first - stops.at(i).first) / parts;
+ for (int j = 1; j < parts; ++j) {
+ QRgb color = INV_PREMUL(INTERPOLATE_PIXEL_256(fromColor, 256 - 256 * j / parts, toColor, 256 * j / parts));
+ newStops.append(QGradientStop(stops.at(i).first + j * step, QColor::fromRgba(color)));
+ }
+ }
+ fromColor = toColor;
+ }
+ newStops.append(stops.back());
+ stops = newStops;
+ }
+ }
+
+ foreach(QGradientStop stop, stops) {
+ QString color =
+ QString::fromLatin1("#%1%2%3")
+ .arg(stop.second.red(), 2, 16, QLatin1Char('0'))
+ .arg(stop.second.green(), 2, 16, QLatin1Char('0'))
+ .arg(stop.second.blue(), 2, 16, QLatin1Char('0'));
+ str << QLatin1String(" <stop offset=\"")<< stop.first << QLatin1String("\" ")
+ << QLatin1String("stop-color=\"") << color << QLatin1String("\" ")
+ << QLatin1String("stop-opacity=\"") << stop.second.alphaF() <<QLatin1String("\" />\n");
+ }
+ }
+
+ void saveGradientUnits(QTextStream &str, const QGradient *gradient)
+ {
+ str << QLatin1String("gradientUnits=\"");
+ if (gradient && gradient->coordinateMode() == QGradient::ObjectBoundingMode)
+ str << QLatin1String("objectBoundingBox");
+ else
+ str << QLatin1String("userSpaceOnUse");
+ str << QLatin1String("\" ");
+ }
+
+ void generateQtDefaults()
+ {
+ *d_func()->stream << QLatin1String("fill=\"none\" ");
+ *d_func()->stream << QLatin1String("stroke=\"black\" ");
+ *d_func()->stream << QLatin1String("stroke-width=\"1\" ");
+ *d_func()->stream << QLatin1String("fill-rule=\"evenodd\" ");
+ *d_func()->stream << QLatin1String("stroke-linecap=\"square\" ");
+ *d_func()->stream << QLatin1String("stroke-linejoin=\"bevel\" ");
+ *d_func()->stream << QLatin1String(">\n");
+ }
+ inline QTextStream &stream()
+ {
+ return *d_func()->stream;
+ }
+
+
+ void qpenToSvg(const QPen &spen)
+ {
+ QString width;
+
+ d_func()->pen = spen;
+
+ switch (spen.style()) {
+ case Qt::NoPen:
+ stream() << QLatin1String("stroke=\"none\" ");
+
+ d_func()->attributes.stroke = QLatin1String("none");
+ d_func()->attributes.strokeOpacity = QString();
+ return;
+ break;
+ case Qt::SolidLine: {
+ QString color, colorOpacity;
+
+ translate_color(spen.color(), &color,
+ &colorOpacity);
+ d_func()->attributes.stroke = color;
+ d_func()->attributes.strokeOpacity = colorOpacity;
+
+ stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
+ stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
+ }
+ break;
+ case Qt::DashLine:
+ case Qt::DotLine:
+ case Qt::DashDotLine:
+ case Qt::DashDotDotLine:
+ case Qt::CustomDashLine: {
+ QString color, colorOpacity, dashPattern, dashOffset;
+
+ qreal penWidth = spen.width() == 0 ? qreal(1) : spen.widthF();
+
+ translate_color(spen.color(), &color, &colorOpacity);
+ translate_dashPattern(spen.dashPattern(), penWidth, &dashPattern);
+
+ // SVG uses absolute offset
+ dashOffset = QString::number(spen.dashOffset() * penWidth);
+
+ d_func()->attributes.stroke = color;
+ d_func()->attributes.strokeOpacity = colorOpacity;
+ d_func()->attributes.dashPattern = dashPattern;
+ d_func()->attributes.dashOffset = dashOffset;
+
+ stream() << QLatin1String("stroke=\"")<<color<< QLatin1String("\" ");
+ stream() << QLatin1String("stroke-opacity=\"")<<colorOpacity<< QLatin1String("\" ");
+ stream() << QLatin1String("stroke-dasharray=\"")<<dashPattern<< QLatin1String("\" ");
+ stream() << QLatin1String("stroke-dashoffset=\"")<<dashOffset<< QLatin1String("\" ");
+ break;
+ }
+ default:
+ qWarning("Unsupported pen style");
+ break;
+ }
+
+ if (spen.widthF() == 0)
+ stream() <<"stroke-width=\"1\" ";
+ else
+ stream() <<"stroke-width=\"" << spen.widthF() << "\" ";
+
+ switch (spen.capStyle()) {
+ case Qt::FlatCap:
+ stream() << "stroke-linecap=\"butt\" ";
+ break;
+ case Qt::SquareCap:
+ stream() << "stroke-linecap=\"square\" ";
+ break;
+ case Qt::RoundCap:
+ stream() << "stroke-linecap=\"round\" ";
+ break;
+ default:
+ qWarning("Unhandled cap style");
+ }
+ switch (spen.joinStyle()) {
+ case Qt::MiterJoin:
+ stream() << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ break;
+ case Qt::BevelJoin:
+ stream() << "stroke-linejoin=\"bevel\" ";
+ break;
+ case Qt::RoundJoin:
+ stream() << "stroke-linejoin=\"round\" ";
+ break;
+ case Qt::SvgMiterJoin:
+ stream() << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ break;
+ default:
+ qWarning("Unhandled join style");
+ }
+ }
+ void qbrushToSvg(const QBrush &sbrush)
+ {
+ d_func()->brush = sbrush;
+ switch (sbrush.style()) {
+ case Qt::SolidPattern: {
+ QString color, colorOpacity;
+ translate_color(sbrush.color(), &color, &colorOpacity);
+ stream() << "fill=\"" << color << "\" "
+ "fill-opacity=\""
+ << colorOpacity << "\" ";
+ d_func()->attributes.fill = color;
+ d_func()->attributes.fillOpacity = colorOpacity;
+ }
+ break;
+ case Qt::LinearGradientPattern:
+ saveLinearGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+ stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::RadialGradientPattern:
+ saveRadialGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+ stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::ConicalGradientPattern:
+ saveConicalGradientBrush(sbrush.gradient());
+ d_func()->attributes.fill = QString::fromLatin1("url(#%1)").arg(d_func()->currentGradientName);
+ d_func()->attributes.fillOpacity = QString();
+ stream() << QLatin1String("fill=\"url(#") << d_func()->currentGradientName << QLatin1String(")\" ");
+ break;
+ case Qt::NoBrush:
+ stream() << QLatin1String("fill=\"none\" ");
+ d_func()->attributes.fill = QLatin1String("none");
+ d_func()->attributes.fillOpacity = QString();
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+ void qfontToSvg(const QFont &sfont)
+ {
+ Q_D(QSvgPaintEngine);
+
+ d->font = sfont;
+
+ if (d->font.pixelSize() == -1)
+ d->attributes.font_size = QString::number(d->font.pointSizeF() * d->resolution / 72);
+ else
+ d->attributes.font_size = QString::number(d->font.pixelSize());
+
+ int svgWeight = d->font.weight();
+ switch (svgWeight) {
+ case QFont::Light:
+ svgWeight = 100;
+ break;
+ case QFont::Normal:
+ svgWeight = 400;
+ break;
+ case QFont::Bold:
+ svgWeight = 700;
+ break;
+ default:
+ svgWeight *= 10;
+ }
+
+ d->attributes.font_weight = QString::number(svgWeight);
+ d->attributes.font_family = d->font.family();
+ d->attributes.font_style = d->font.italic() ? QLatin1String("italic") : QLatin1String("normal");
+
+ *d->stream << "font-family=\"" << d->attributes.font_family << "\" "
+ "font-size=\"" << d->attributes.font_size << "\" "
+ "font-weight=\"" << d->attributes.font_weight << "\" "
+ "font-style=\"" << d->attributes.font_style << "\" "
+ << endl;
+ }
+};
+
+class QSvgGeneratorPrivate
+{
+public:
+ QSvgPaintEngine *engine;
+
+ uint owns_iodevice : 1;
+ QString fileName;
+};
+
+/*!
+ \class QSvgGenerator
+ \ingroup painting
+ \since 4.3
+ \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
+ \reentrant
+
+ This paint device represents a Scalable Vector Graphics (SVG) drawing. Like QPrinter, it is
+ designed as a write-only device that generates output in a specific format.
+
+ To write an SVG file, you first need to configure the output by setting the \l fileName
+ or \l outputDevice properties. It is usually necessary to specify the size of the drawing
+ by setting the \l size property, and in some cases where the drawing will be included in
+ another, the \l viewBox property also needs to be set.
+
+ \snippet examples/painting/svggenerator/window.cpp configure SVG generator
+
+ Other meta-data can be specified by setting the \a title, \a description and \a resolution
+ properties.
+
+ As with other QPaintDevice subclasses, a QPainter object is used to paint onto an instance
+ of this class:
+
+ \snippet examples/painting/svggenerator/window.cpp begin painting
+ \dots
+ \snippet examples/painting/svggenerator/window.cpp end painting
+
+ Painting is performed in the same way as for any other paint device. However,
+ it is necessary to use the QPainter::begin() and \l{QPainter::}{end()} to
+ explicitly begin and end painting on the device.
+
+ The \l{SVG Generator Example} shows how the same painting commands can be used
+ for painting a widget and writing an SVG file.
+
+ \sa QSvgRenderer, QSvgWidget, {About SVG}
+*/
+
+/*!
+ Constructs a new generator.
+*/
+QSvgGenerator::QSvgGenerator()
+ : d_ptr(new QSvgGeneratorPrivate)
+{
+ Q_D(QSvgGenerator);
+
+ d->engine = new QSvgPaintEngine;
+ d->owns_iodevice = false;
+}
+
+/*!
+ Destroys the generator.
+*/
+QSvgGenerator::~QSvgGenerator()
+{
+ Q_D(QSvgGenerator);
+ if (d->owns_iodevice)
+ delete d->engine->outputDevice();
+ delete d->engine;
+}
+
+/*!
+ \property QSvgGenerator::title
+ \brief the title of the generated SVG drawing
+ \since 4.5
+ \sa description
+*/
+QString QSvgGenerator::title() const
+{
+ Q_D(const QSvgGenerator);
+
+ return d->engine->documentTitle();
+}
+
+void QSvgGenerator::setTitle(const QString &title)
+{
+ Q_D(QSvgGenerator);
+
+ d->engine->setDocumentTitle(title);
+}
+
+/*!
+ \property QSvgGenerator::description
+ \brief the description of the generated SVG drawing
+ \since 4.5
+ \sa title
+*/
+QString QSvgGenerator::description() const
+{
+ Q_D(const QSvgGenerator);
+
+ return d->engine->documentDescription();
+}
+
+void QSvgGenerator::setDescription(const QString &description)
+{
+ Q_D(QSvgGenerator);
+
+ d->engine->setDocumentDescription(description);
+}
+
+/*!
+ \property QSvgGenerator::size
+ \brief the size of the generated SVG drawing
+ \since 4.5
+
+ By default this property is set to \c{QSize(-1, -1)}, which
+ indicates that the generator should not output the width and
+ height attributes of the \c<svg> element.
+
+ \note It is not possible to change this property while a
+ QPainter is active on the generator.
+
+ \sa viewBox, resolution
+*/
+QSize QSvgGenerator::size() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine->size();
+}
+
+void QSvgGenerator::setSize(const QSize &size)
+{
+ Q_D(QSvgGenerator);
+ if (d->engine->isActive()) {
+ qWarning("QSvgGenerator::setSize(), cannot set size while SVG is being generated");
+ return;
+ }
+ d->engine->setSize(size);
+}
+
+/*!
+ \property QSvgGenerator::viewBox
+ \brief the viewBox of the generated SVG drawing
+ \since 4.5
+
+ By default this property is set to \c{QRect(0, 0, -1, -1)}, which
+ indicates that the generator should not output the viewBox attribute
+ of the \c<svg> element.
+
+ \note It is not possible to change this property while a
+ QPainter is active on the generator.
+
+ \sa viewBox(), size, resolution
+*/
+QRectF QSvgGenerator::viewBoxF() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine->viewBox();
+}
+
+/*!
+ \since 4.5
+
+ Returns viewBoxF().toRect().
+
+ \sa viewBoxF()
+*/
+QRect QSvgGenerator::viewBox() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine->viewBox().toRect();
+}
+
+void QSvgGenerator::setViewBox(const QRectF &viewBox)
+{
+ Q_D(QSvgGenerator);
+ if (d->engine->isActive()) {
+ qWarning("QSvgGenerator::setViewBox(), cannot set viewBox while SVG is being generated");
+ return;
+ }
+ d->engine->setViewBox(viewBox);
+}
+
+void QSvgGenerator::setViewBox(const QRect &viewBox)
+{
+ setViewBox(QRectF(viewBox));
+}
+
+/*!
+ \property QSvgGenerator::fileName
+ \brief the target filename for the generated SVG drawing
+ \since 4.5
+
+ \sa outputDevice
+*/
+QString QSvgGenerator::fileName() const
+{
+ Q_D(const QSvgGenerator);
+ return d->fileName;
+}
+
+void QSvgGenerator::setFileName(const QString &fileName)
+{
+ Q_D(QSvgGenerator);
+ if (d->engine->isActive()) {
+ qWarning("QSvgGenerator::setFileName(), cannot set file name while SVG is being generated");
+ return;
+ }
+
+ if (d->owns_iodevice)
+ delete d->engine->outputDevice();
+
+ d->owns_iodevice = true;
+
+ d->fileName = fileName;
+ QFile *file = new QFile(fileName);
+ d->engine->setOutputDevice(file);
+}
+
+/*!
+ \property QSvgGenerator::outputDevice
+ \brief the output device for the generated SVG drawing
+ \since 4.5
+
+ If both output device and file name are specified, the output device
+ will have precedence.
+
+ \sa fileName
+*/
+QIODevice *QSvgGenerator::outputDevice() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine->outputDevice();
+}
+
+void QSvgGenerator::setOutputDevice(QIODevice *outputDevice)
+{
+ Q_D(QSvgGenerator);
+ if (d->engine->isActive()) {
+ qWarning("QSvgGenerator::setOutputDevice(), cannot set output device while SVG is being generated");
+ return;
+ }
+ d->owns_iodevice = false;
+ d->engine->setOutputDevice(outputDevice);
+ d->fileName = QString();
+}
+
+/*!
+ \property QSvgGenerator::resolution
+ \brief the resolution of the generated output
+ \since 4.5
+
+ The resolution is specified in dots per inch, and is used to
+ calculate the physical size of an SVG drawing.
+
+ \sa size, viewBox
+*/
+int QSvgGenerator::resolution() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine->resolution();
+}
+
+void QSvgGenerator::setResolution(int dpi)
+{
+ Q_D(QSvgGenerator);
+ d->engine->setResolution(dpi);
+}
+
+/*!
+ Returns the paint engine used to render graphics to be converted to SVG
+ format information.
+*/
+QPaintEngine *QSvgGenerator::paintEngine() const
+{
+ Q_D(const QSvgGenerator);
+ return d->engine;
+}
+
+/*!
+ \reimp
+*/
+int QSvgGenerator::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+ Q_D(const QSvgGenerator);
+ switch (metric) {
+ case QPaintDevice::PdmDepth:
+ return 32;
+ case QPaintDevice::PdmWidth:
+ return d->engine->size().width();
+ case QPaintDevice::PdmHeight:
+ return d->engine->size().height();
+ case QPaintDevice::PdmDpiX:
+ return d->engine->resolution();
+ case QPaintDevice::PdmDpiY:
+ return d->engine->resolution();
+ case QPaintDevice::PdmHeightMM:
+ return qRound(d->engine->size().height() * 25.4 / d->engine->resolution());
+ case QPaintDevice::PdmWidthMM:
+ return qRound(d->engine->size().width() * 25.4 / d->engine->resolution());
+ case QPaintDevice::PdmNumColors:
+ return 0xffffffff;
+ case QPaintDevice::PdmPhysicalDpiX:
+ return d->engine->resolution();
+ case QPaintDevice::PdmPhysicalDpiY:
+ return d->engine->resolution();
+ default:
+ qWarning("QSvgGenerator::metric(), unhandled metric %d\n", metric);
+ break;
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ * class QSvgPaintEngine
+ */
+
+bool QSvgPaintEngine::begin(QPaintDevice *)
+{
+ Q_D(QSvgPaintEngine);
+ if (!d->outputDevice) {
+ qWarning("QSvgPaintEngine::begin(), no output device");
+ return false;
+ }
+
+ if (!d->outputDevice->isOpen()) {
+ if (!d->outputDevice->open(QIODevice::WriteOnly | QIODevice::Text)) {
+ qWarning("QSvgPaintEngine::begin(), could not open output device: '%s'",
+ qPrintable(d->outputDevice->errorString()));
+ return false;
+ }
+ } else if (!d->outputDevice->isWritable()) {
+ qWarning("QSvgPaintEngine::begin(), could not write to read-only output device: '%s'",
+ qPrintable(d->outputDevice->errorString()));
+ return false;
+ }
+
+ d->stream = new QTextStream(&d->header);
+
+ // stream out the header...
+ *d->stream << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << endl << "<svg";
+
+ if (d->size.isValid()) {
+ qreal wmm = d->size.width() * 25.4 / d->resolution;
+ qreal hmm = d->size.height() * 25.4 / d->resolution;
+ *d->stream << " width=\"" << wmm << "mm\" height=\"" << hmm << "mm\"" << endl;
+ }
+
+ if (d->viewBox.isValid()) {
+ *d->stream << " viewBox=\"" << d->viewBox.left() << ' ' << d->viewBox.top();
+ *d->stream << ' ' << d->viewBox.width() << ' ' << d->viewBox.height() << '\"' << endl;
+ }
+
+ *d->stream << " xmlns=\"http://www.w3.org/2000/svg\""
+ " xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+ " version=\"1.2\" baseProfile=\"tiny\">" << endl;
+
+ if (!d->attributes.document_title.isEmpty()) {
+ *d->stream << "<title>" << d->attributes.document_title << "</title>" << endl;
+ }
+
+ if (!d->attributes.document_description.isEmpty()) {
+ *d->stream << "<desc>" << d->attributes.document_description << "</desc>" << endl;
+ }
+
+ d->stream->setString(&d->defs);
+ *d->stream << "<defs>\n";
+
+ d->stream->setString(&d->body);
+ // Start the initial graphics state...
+ *d->stream << "<g ";
+ generateQtDefaults();
+ *d->stream << endl;
+
+ return true;
+}
+
+bool QSvgPaintEngine::end()
+{
+ Q_D(QSvgPaintEngine);
+
+ d->stream->setString(&d->defs);
+ *d->stream << "</defs>\n";
+
+ d->stream->setDevice(d->outputDevice);
+#ifndef QT_NO_TEXTCODEC
+ d->stream->setCodec(QTextCodec::codecForName("UTF-8"));
+#endif
+
+ *d->stream << d->header;
+ *d->stream << d->defs;
+ *d->stream << d->body;
+ if (d->afterFirstUpdate)
+ *d->stream << "</g>" << endl; // close the updateState
+
+ *d->stream << "</g>" << endl // close the Qt defaults
+ << "</svg>" << endl;
+
+ delete d->stream;
+
+ return true;
+}
+
+void QSvgPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm,
+ const QRectF &sr)
+{
+ drawImage(r, pm.toImage(), sr);
+}
+
+void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
+ const QRectF &sr,
+ Qt::ImageConversionFlag flags)
+{
+ //Q_D(QSvgPaintEngine);
+
+ Q_UNUSED(sr);
+ Q_UNUSED(flags);
+ stream() << "<image ";
+ stream() << "x=\""<<r.x()<<"\" "
+ "y=\""<<r.y()<<"\" "
+ "width=\""<<r.width()<<"\" "
+ "height=\""<<r.height()<<"\" "
+ "preserveAspectRatio=\"none\" ";
+
+ QByteArray data;
+ QBuffer buffer(&data);
+ buffer.open(QBuffer::ReadWrite);
+ image.save(&buffer, "PNG");
+ buffer.close();
+ stream() << "xlink:href=\"data:image/png;base64,"
+ << data.toBase64()
+ <<"\" />\n";
+}
+
+void QSvgPaintEngine::updateState(const QPaintEngineState &state)
+{
+ Q_D(QSvgPaintEngine);
+ QPaintEngine::DirtyFlags flags = state.state();
+
+ // always stream full gstate, which is not required, but...
+ flags |= QPaintEngine::AllDirty;
+
+ // close old state and start a new one...
+ if (d->afterFirstUpdate)
+ *d->stream << "</g>\n\n";
+
+ *d->stream << "<g ";
+
+ if (flags & QPaintEngine::DirtyBrush) {
+ qbrushToSvg(state.brush());
+ }
+
+ if (flags & QPaintEngine::DirtyPen) {
+ qpenToSvg(state.pen());
+ }
+
+ if (flags & QPaintEngine::DirtyTransform) {
+ d->matrix = state.matrix();
+ *d->stream << "transform=\"matrix(" << d->matrix.m11() << ','
+ << d->matrix.m12() << ','
+ << d->matrix.m21() << ',' << d->matrix.m22() << ','
+ << d->matrix.dx() << ',' << d->matrix.dy()
+ << ")\""
+ << endl;
+ }
+
+ if (flags & QPaintEngine::DirtyFont) {
+ qfontToSvg(state.font());
+ }
+
+ if (flags & QPaintEngine::DirtyOpacity) {
+ if (!qFuzzyIsNull(state.opacity() - 1))
+ stream() << "opacity=\""<<state.opacity()<<"\" ";
+ }
+
+ *d->stream << '>' << endl;
+
+ d->afterFirstUpdate = true;
+}
+
+void QSvgPaintEngine::drawPath(const QPainterPath &p)
+{
+ Q_D(QSvgPaintEngine);
+
+ *d->stream << "<path vector-effect=\""
+ << (state->pen().isCosmetic() ? "non-scaling-stroke" : "none")
+ << "\" fill-rule=\""
+ << (p.fillRule() == Qt::OddEvenFill ? "evenodd" : "nonzero")
+ << "\" d=\"";
+
+ for (int i=0; i<p.elementCount(); ++i) {
+ const QPainterPath::Element &e = p.elementAt(i);
+ switch (e.type) {
+ case QPainterPath::MoveToElement:
+ *d->stream << 'M' << e.x << ',' << e.y;
+ break;
+ case QPainterPath::LineToElement:
+ *d->stream << 'L' << e.x << ',' << e.y;
+ break;
+ case QPainterPath::CurveToElement:
+ *d->stream << 'C' << e.x << ',' << e.y;
+ ++i;
+ while (i < p.elementCount()) {
+ const QPainterPath::Element &e = p.elementAt(i);
+ if (e.type != QPainterPath::CurveToDataElement) {
+ --i;
+ break;
+ } else
+ *d->stream << ' ';
+ *d->stream << e.x << ',' << e.y;
+ ++i;
+ }
+ break;
+ default:
+ break;
+ }
+ if (i != p.elementCount() - 1) {
+ *d->stream << ' ';
+ }
+ }
+
+ *d->stream << "\"/>" << endl;
+}
+
+void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
+ PolygonDrawMode mode)
+{
+ Q_ASSERT(pointCount >= 2);
+
+ //Q_D(QSvgPaintEngine);
+
+ QPainterPath path(points[0]);
+ for (int i=1; i<pointCount; ++i)
+ path.lineTo(points[i]);
+
+ if (mode == PolylineMode) {
+ stream() << "<polyline fill=\"none\" vector-effect=\""
+ << (state->pen().isCosmetic() ? "non-scaling-stroke" : "none")
+ << "\" points=\"";
+ for (int i = 0; i < pointCount; ++i) {
+ const QPointF &pt = points[i];
+ stream() << pt.x() << ',' << pt.y() << ' ';
+ }
+ stream() << "\" />" <<endl;
+ } else {
+ path.closeSubpath();
+ drawPath(path);
+ }
+}
+
+void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &textItem)
+{
+ Q_D(QSvgPaintEngine);
+ if (d->pen.style() == Qt::NoPen)
+ return;
+
+ const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
+ QString s = QString::fromRawData(ti.chars, ti.num_chars);
+
+ *d->stream << "<text "
+ "fill=\"" << d->attributes.stroke << "\" "
+ "fill-opacity=\"" << d->attributes.strokeOpacity << "\" "
+ "stroke=\"none\" "
+ "xml:space=\"preserve\" "
+ "x=\"" << pt.x() << "\" y=\"" << pt.y() << "\" ";
+ qfontToSvg(textItem.font());
+ *d->stream << " >"
+ << Qt::escape(s)
+ << "</text>"
+ << endl;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVGGENERATOR
diff --git a/src/svg/qsvggenerator.h b/src/svg/qsvggenerator.h
new file mode 100644
index 0000000..62fc6f8
--- /dev/null
+++ b/src/svg/qsvggenerator.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGGENERATOR_H
+#define QSVGGENERATOR_H
+
+#include <QtGui/qpaintdevice.h>
+
+#ifndef QT_NO_SVGGENERATOR
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qobjectdefs.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Svg)
+
+class QSvgGeneratorPrivate;
+
+class Q_SVG_EXPORT QSvgGenerator : public QPaintDevice
+{
+ Q_DECLARE_PRIVATE(QSvgGenerator)
+
+ Q_PROPERTY(QSize size READ size WRITE setSize)
+ Q_PROPERTY(QRectF viewBox READ viewBoxF WRITE setViewBox)
+ Q_PROPERTY(QString title READ title WRITE setTitle)
+ Q_PROPERTY(QString description READ description WRITE setDescription)
+ Q_PROPERTY(QString fileName READ fileName WRITE setFileName)
+ Q_PROPERTY(QIODevice* outputDevice READ outputDevice WRITE setOutputDevice)
+ Q_PROPERTY(int resolution READ resolution WRITE setResolution)
+public:
+ QSvgGenerator();
+ ~QSvgGenerator();
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ QString description() const;
+ void setDescription(const QString &description);
+
+ QSize size() const;
+ void setSize(const QSize &size);
+
+ QRect viewBox() const;
+ QRectF viewBoxF() const;
+ void setViewBox(const QRect &viewBox);
+ void setViewBox(const QRectF &viewBox);
+
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ QIODevice *outputDevice() const;
+ void setOutputDevice(QIODevice *outputDevice);
+
+ void setResolution(int dpi);
+ int resolution() const;
+protected:
+ QPaintEngine *paintEngine() const;
+ int metric(QPaintDevice::PaintDeviceMetric metric) const;
+
+private:
+ QScopedPointer<QSvgGeneratorPrivate> d_ptr;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SVGGENERATOR
+#endif // QSVGGENERATOR_H
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
new file mode 100644
index 0000000..0cfa2ed
--- /dev/null
+++ b/src/svg/qsvggraphics.cpp
@@ -0,0 +1,615 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvggraphics_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgfont_p.h"
+
+#include "qpainter.h"
+#include "qtextdocument.h"
+#include "qabstracttextdocumentlayout.h"
+#include "qtextcursor.h"
+#include "qdebug.h"
+
+#include <math.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QT_SVG_DRAW_SHAPE(command) \
+ qreal oldOpacity = p->opacity(); \
+ QBrush oldBrush = p->brush(); \
+ QPen oldPen = p->pen(); \
+ p->setPen(Qt::NoPen); \
+ p->setOpacity(oldOpacity * states.fillOpacity); \
+ command; \
+ p->setPen(oldPen); \
+ if (oldPen.widthF() != 0) { \
+ p->setOpacity(oldOpacity * states.strokeOpacity); \
+ p->setBrush(Qt::NoBrush); \
+ command; \
+ p->setBrush(oldBrush); \
+ } \
+ p->setOpacity(oldOpacity);
+
+
+void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
+{
+ qWarning("<animation> no implemented");
+}
+
+static inline QRectF boundsOnStroke(QPainter *p, const QPainterPath &path, qreal width)
+{
+ QPainterPathStroker stroker;
+ stroker.setWidth(width);
+ QPainterPath stroke = stroker.createStroke(path);
+ return p->transform().map(stroke).boundingRect();
+}
+
+QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
+ : QSvgNode(parent), m_bounds(rect)
+{
+}
+
+
+QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ QPainterPath path;
+ path.addEllipse(m_bounds);
+ qreal sw = strokeWidth(p);
+ return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect() : boundsOnStroke(p, path, sw);
+}
+
+void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ revertStyle(p, states);
+}
+
+QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
+ : QSvgNode(parent), m_path(path)
+{
+}
+
+void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPath(m_path);
+ p->setOpacity(oldOpacity);
+ }
+ revertStyle(p, states);
+}
+
+QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
+ const QRect &bounds)
+ : QSvgNode(parent), m_image(image),
+ m_bounds(bounds)
+{
+ if (m_bounds.width() == 0)
+ m_bounds.setWidth(m_image.width());
+ if (m_bounds.height() == 0)
+ m_bounds.setHeight(m_image.height());
+}
+
+void QSvgImage::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ p->drawImage(m_bounds, m_image);
+ revertStyle(p, states);
+}
+
+
+QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
+ : QSvgNode(parent), m_line(line)
+{
+}
+
+
+void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawLine(m_line);
+ p->setOpacity(oldOpacity);
+ }
+ revertStyle(p, states);
+}
+
+QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
+ : QSvgNode(parent), m_path(qpath)
+{
+}
+
+void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ m_path.setFillRule(states.fillRule);
+ QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+ revertStyle(p, states);
+}
+
+QRectF QSvgPath::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
+ : boundsOnStroke(p, m_path, sw);
+}
+
+QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
+ : QSvgNode(parent), m_poly(poly)
+{
+}
+
+QRectF QSvgPolygon::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ if (qFuzzyIsNull(sw)) {
+ return p->transform().map(m_poly).boundingRect();
+ } else {
+ QPainterPath path;
+ path.addPolygon(m_poly);
+ return boundsOnStroke(p, path, sw);
+ }
+}
+
+void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
+ revertStyle(p, states);
+}
+
+
+QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
+ : QSvgNode(parent), m_poly(poly)
+{
+
+}
+
+void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
+ if (p->brush().style() != Qt::NoBrush) {
+ QPen save = p->pen();
+ p->setPen(QPen(Qt::NoPen));
+ p->setOpacity(oldOpacity * states.fillOpacity);
+ p->drawPolygon(m_poly, states.fillRule);
+ p->setPen(save);
+ }
+ if (p->pen().widthF() != 0) {
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPolyline(m_poly);
+ }
+ p->setOpacity(oldOpacity);
+ revertStyle(p, states);
+}
+
+QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
+ : QSvgNode(node),
+ m_rect(rect), m_rx(rx), m_ry(ry)
+{
+}
+
+QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ if (qFuzzyIsNull(sw)) {
+ return p->transform().mapRect(m_rect);
+ } else {
+ QPainterPath path;
+ path.addRect(m_rect);
+ return boundsOnStroke(p, path, sw);
+ }
+}
+
+void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ if (m_rx || m_ry) {
+ QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
+ } else {
+ QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
+ }
+ revertStyle(p, states);
+}
+
+QSvgTspan * const QSvgText::LINEBREAK = 0;
+
+QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
+ : QSvgNode(parent)
+ , m_coord(coord)
+ , m_type(TEXT)
+ , m_size(0, 0)
+ , m_mode(Default)
+{
+}
+
+QSvgText::~QSvgText()
+{
+ for (int i = 0; i < m_tspans.size(); ++i) {
+ if (m_tspans[i] != LINEBREAK)
+ delete m_tspans[i];
+ }
+}
+
+void QSvgText::setTextArea(const QSizeF &size)
+{
+ m_size = size;
+ m_type = TEXTAREA;
+}
+
+//QRectF QSvgText::bounds(QPainter *p, QSvgExtraStates &) const {}
+
+void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.fillOpacity);
+
+ // Force the font to have a size of 100 pixels to avoid truncation problems
+ // when the font is very small.
+ qreal scale = 100.0 / p->font().pointSizeF();
+ Qt::Alignment alignment = states.textAnchor;
+
+ QTransform oldTransform = p->worldTransform();
+ p->scale(1 / scale, 1 / scale);
+
+ qreal y = 0;
+ bool initial = true;
+ qreal px = m_coord.x() * scale;
+ qreal py = m_coord.y() * scale;
+ QSizeF scaledSize = m_size * scale;
+
+ if (m_type == TEXTAREA) {
+ if (alignment == Qt::AlignHCenter)
+ px += scaledSize.width() / 2;
+ else if (alignment == Qt::AlignRight)
+ px += scaledSize.width();
+ }
+
+ QRectF bounds;
+ if (m_size.height() != 0)
+ bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
+
+ bool appendSpace = false;
+ QVector<QString> paragraphs;
+ QStack<QTextCharFormat> formats;
+ QVector<QList<QTextLayout::FormatRange> > formatRanges;
+ paragraphs.push_back(QString());
+ formatRanges.push_back(QList<QTextLayout::FormatRange>());
+
+ for (int i = 0; i < m_tspans.size(); ++i) {
+ if (m_tspans[i] == LINEBREAK) {
+ if (m_type == TEXTAREA) {
+ if (paragraphs.back().isEmpty()) {
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
+
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 1;
+ range.format.setFont(font);
+ formatRanges.back().append(range);
+
+ paragraphs.back().append(QLatin1Char(' '));;
+ }
+ appendSpace = false;
+ paragraphs.push_back(QString());
+ formatRanges.push_back(QList<QTextLayout::FormatRange>());
+ }
+ } else {
+ WhitespaceMode mode = m_tspans[i]->whitespaceMode();
+ m_tspans[i]->applyStyle(p, states);
+
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
+
+ QString newText(m_tspans[i]->text());
+ newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
+
+ bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
+ if (appendSpace || prependSpace)
+ paragraphs.back().append(QLatin1Char(' '));
+
+ bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
+
+ if (mode == Default) {
+ newText = newText.simplified();
+ if (newText.isEmpty())
+ appendSpaceNext = false;
+ }
+
+ QTextLayout::FormatRange range;
+ range.start = paragraphs.back().length();
+ range.length = newText.length();
+ range.format.setFont(font);
+ range.format.setTextOutline(p->pen());
+ range.format.setForeground(p->brush());
+
+ if (appendSpace) {
+ Q_ASSERT(!formatRanges.back().isEmpty());
+ ++formatRanges.back().back().length;
+ } else if (prependSpace) {
+ --range.start;
+ ++range.length;
+ }
+ formatRanges.back().append(range);
+
+ appendSpace = appendSpaceNext;
+ paragraphs.back() += newText;
+
+ m_tspans[i]->revertStyle(p, states);
+ }
+ }
+
+ if (states.svgFont) {
+ // SVG fonts not fully supported...
+ QString text = paragraphs.front();
+ for (int i = 1; i < paragraphs.size(); ++i) {
+ text.append(QLatin1Char('\n'));
+ text.append(paragraphs[i]);
+ }
+ states.svgFont->draw(p, m_coord * scale, text, p->font().pointSizeF() * scale, states.textAnchor);
+ } else {
+ for (int i = 0; i < paragraphs.size(); ++i) {
+ QTextLayout tl(paragraphs[i]);
+ QTextOption op = tl.textOption();
+ op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ tl.setTextOption(op);
+ tl.setAdditionalFormats(formatRanges[i]);
+ tl.beginLayout();
+
+ forever {
+ QTextLine line = tl.createLine();
+ if (!line.isValid())
+ break;
+ if (m_size.width() != 0)
+ line.setLineWidth(scaledSize.width());
+ }
+ tl.endLayout();
+
+ bool endOfBoundsReached = false;
+ for (int i = 0; i < tl.lineCount(); ++i) {
+ QTextLine line = tl.lineAt(i);
+
+ qreal x = 0;
+ if (alignment == Qt::AlignHCenter)
+ x -= 0.5 * line.naturalTextWidth();
+ else if (alignment == Qt::AlignRight)
+ x -= line.naturalTextWidth();
+
+ if (initial && m_type == TEXT)
+ y -= line.ascent();
+ initial = false;
+
+ line.setPosition(QPointF(x, y));
+
+ // Check if the current line fits into the bounding rectangle.
+ if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
+ || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
+ // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
+ // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
+ bounds.setHeight(y - 1);
+ endOfBoundsReached = true;
+ break;
+ }
+
+ y += 1.1 * line.height();
+ }
+ tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
+
+ if (endOfBoundsReached)
+ break;
+ }
+ }
+
+ p->setWorldTransform(oldTransform, false);
+ p->setOpacity(oldOpacity);
+ revertStyle(p, states);
+}
+
+void QSvgText::addText(const QString &text)
+{
+ m_tspans.append(new QSvgTspan(this, false));
+ m_tspans.back()->setWhitespaceMode(m_mode);
+ m_tspans.back()->addText(text);
+}
+
+QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
+ : QSvgNode(parent), m_link(node), m_start(start)
+{
+
+}
+
+void QSvgUse::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+
+ if (!m_start.isNull()) {
+ p->translate(m_start);
+ }
+ m_link->draw(p, states);
+ if (!m_start.isNull()) {
+ p->translate(-m_start);
+ }
+
+ revertStyle(p, states);
+}
+
+void QSvgVideo::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+
+ revertStyle(p, states);
+}
+
+QSvgNode::Type QSvgAnimation::type() const
+{
+ return ANIMATION;
+}
+
+QSvgNode::Type QSvgArc::type() const
+{
+ return ARC;
+}
+
+QSvgNode::Type QSvgCircle::type() const
+{
+ return CIRCLE;
+}
+
+QSvgNode::Type QSvgEllipse::type() const
+{
+ return ELLIPSE;
+}
+
+QSvgNode::Type QSvgImage::type() const
+{
+ return IMAGE;
+}
+
+QSvgNode::Type QSvgLine::type() const
+{
+ return LINE;
+}
+
+QSvgNode::Type QSvgPath::type() const
+{
+ return PATH;
+}
+
+QSvgNode::Type QSvgPolygon::type() const
+{
+ return POLYGON;
+}
+
+QSvgNode::Type QSvgPolyline::type() const
+{
+ return POLYLINE;
+}
+
+QSvgNode::Type QSvgRect::type() const
+{
+ return RECT;
+}
+
+QSvgNode::Type QSvgText::type() const
+{
+ return m_type;
+}
+
+QSvgNode::Type QSvgUse::type() const
+{
+ return USE;
+}
+
+QSvgNode::Type QSvgVideo::type() const
+{
+ return VIDEO;
+}
+
+QRectF QSvgUse::bounds(QPainter *p, QSvgExtraStates &states) const
+{
+ QRectF bounds;
+ if (m_link) {
+ p->translate(m_start);
+ bounds = m_link->transformedBounds(p, states);
+ p->translate(-m_start);
+ }
+ return bounds;
+}
+
+QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ if (qFuzzyIsNull(sw)) {
+ return p->transform().map(m_poly).boundingRect();
+ } else {
+ QPainterPath path;
+ path.addPolygon(m_poly);
+ return boundsOnStroke(p, path, sw);
+ }
+}
+
+QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect()
+ : boundsOnStroke(p, m_path, sw);
+}
+
+QRectF QSvgImage::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ return p->transform().mapRect(m_bounds);
+}
+
+QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const
+{
+ qreal sw = strokeWidth(p);
+ if (qFuzzyIsNull(sw)) {
+ QPointF p1 = p->transform().map(m_line.p1());
+ QPointF p2 = p->transform().map(m_line.p2());
+ qreal minX = qMin(p1.x(), p2.x());
+ qreal minY = qMin(p1.y(), p2.y());
+ qreal maxX = qMax(p1.x(), p2.x());
+ qreal maxY = qMax(p1.y(), p2.y());
+ return QRectF(minX, minY, maxX - minX, maxY - minY);
+ } else {
+ QPainterPath path;
+ path.moveTo(m_line.p1());
+ path.lineTo(m_line.p2());
+ return boundsOnStroke(p, path, sw);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h
new file mode 100644
index 0000000..58c4fdf
--- /dev/null
+++ b/src/svg/qsvggraphics_p.h
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGGRAPHICS_P_H
+#define QSVGGRAPHICS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsvgnode_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtGui/qpainterpath.h"
+#include "QtGui/qimage.h"
+#include "QtGui/qtextlayout.h"
+#include "QtGui/qtextoption.h"
+#include "QtCore/qstack.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTextCharFormat;
+
+class QSvgAnimation : public QSvgNode
+{
+public:
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+};
+
+class QSvgArc : public QSvgNode
+{
+public:
+ QSvgArc(QSvgNode *parent, const QPainterPath &path);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QPainterPath m_path;
+};
+
+class QSvgEllipse : public QSvgNode
+{
+public:
+ QSvgEllipse(QSvgNode *parent, const QRectF &rect);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QRectF m_bounds;
+};
+
+class QSvgCircle : public QSvgEllipse
+{
+public:
+ QSvgCircle(QSvgNode *parent, const QRectF &rect) : QSvgEllipse(parent, rect) { }
+ virtual Type type() const;
+};
+
+class QSvgImage : public QSvgNode
+{
+public:
+ QSvgImage(QSvgNode *parent, const QImage &image,
+ const QRect &bounds);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QImage m_image;
+ QRect m_bounds;
+};
+
+class QSvgLine : public QSvgNode
+{
+public:
+ QSvgLine(QSvgNode *parent, const QLineF &line);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QLineF m_line;
+};
+
+class QSvgPath : public QSvgNode
+{
+public:
+ QSvgPath(QSvgNode *parent, const QPainterPath &qpath);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+
+ QPainterPath *qpath() {
+ return &m_path;
+ }
+private:
+ QPainterPath m_path;
+};
+
+class QSvgPolygon : public QSvgNode
+{
+public:
+ QSvgPolygon(QSvgNode *parent, const QPolygonF &poly);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QPolygonF m_poly;
+};
+
+class QSvgPolyline : public QSvgNode
+{
+public:
+ QSvgPolyline(QSvgNode *parent, const QPolygonF &poly);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QPolygonF m_poly;
+};
+
+class QSvgRect : public QSvgNode
+{
+public:
+ QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0);
+ virtual Type type() const;
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ QRectF m_rect;
+ int m_rx, m_ry;
+};
+
+class QSvgTspan;
+
+class QSvgText : public QSvgNode
+{
+public:
+ enum WhitespaceMode
+ {
+ Default,
+ Preserve
+ };
+
+ QSvgText(QSvgNode *parent, const QPointF &coord);
+ ~QSvgText();
+ void setTextArea(const QSizeF &size);
+
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);}
+ void addText(const QString &text);
+ void addLineBreak() {m_tspans.append(LINEBREAK);}
+ void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;}
+
+ //virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+private:
+ static QSvgTspan * const LINEBREAK;
+
+ QPointF m_coord;
+
+ // 'm_tspans' is also used to store characters outside tspans and line breaks.
+ // If a 'm_tspan' item is null, it indicates a line break.
+ QVector<QSvgTspan *> m_tspans;
+
+ Type m_type;
+ QSizeF m_size;
+ WhitespaceMode m_mode;
+};
+
+class QSvgTspan : public QSvgNode
+{
+public:
+ // tspans are also used to store normal text, so the 'isProperTspan' is used to separate text from tspan.
+ QSvgTspan(QSvgNode *parent, bool isProperTspan = true)
+ : QSvgNode(parent), m_mode(QSvgText::Default), m_isTspan(isProperTspan)
+ {
+ }
+ ~QSvgTspan() { };
+ virtual Type type() const {return TSPAN;}
+ virtual void draw(QPainter *, QSvgExtraStates &) {Q_ASSERT(!"Tspans should be drawn through QSvgText::draw().");}
+ void addText(const QString &text) {m_text += text;}
+ const QString &text() const {return m_text;}
+ bool isTspan() const {return m_isTspan;}
+ void setWhitespaceMode(QSvgText::WhitespaceMode mode) {m_mode = mode;}
+ QSvgText::WhitespaceMode whitespaceMode() const {return m_mode;}
+private:
+ QString m_text;
+ QSvgText::WhitespaceMode m_mode;
+ bool m_isTspan;
+};
+
+class QSvgUse : public QSvgNode
+{
+public:
+ QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *link);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+
+private:
+ QSvgNode *m_link;
+ QPointF m_start;
+};
+
+class QSvgVideo : public QSvgNode
+{
+public:
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGGRAPHICS_P_H
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
new file mode 100644
index 0000000..3fbc08c
--- /dev/null
+++ b/src/svg/qsvghandler.cpp
@@ -0,0 +1,3923 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+
+#include "qsvghandler_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgtinydocument_p.h"
+#include "qsvgstructure_p.h"
+#include "qsvggraphics_p.h"
+#include "qsvgnode_p.h"
+#include "qsvgfont_p.h"
+
+#include "qapplication.h"
+#include "qwidget.h"
+#include "qpen.h"
+#include "qpainterpath.h"
+#include "qbrush.h"
+#include "qcolor.h"
+#include "qtextformat.h"
+#include "qvector.h"
+#include "qfileinfo.h"
+#include "qfile.h"
+#include "qdebug.h"
+#include "qmath.h"
+#include "qnumeric.h"
+#include "qvarlengtharray.h"
+#include "private/qmath_p.h"
+
+#include "float.h"
+
+QT_BEGIN_NAMESPACE
+
+static const char *qt_inherit_text = "inherit";
+#define QT_INHERIT QLatin1String(qt_inherit_text)
+
+Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
+
+// ======== duplicated from qcolor_p
+
+static inline int qsvg_h2i(char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return hex - '0';
+ if (hex >= 'a' && hex <= 'f')
+ return hex - 'a' + 10;
+ if (hex >= 'A' && hex <= 'F')
+ return hex - 'A' + 10;
+ return -1;
+}
+
+static inline int qsvg_hex2int(const char *s)
+{
+ return (qsvg_h2i(s[0]) << 4) | qsvg_h2i(s[1]);
+}
+
+static inline int qsvg_hex2int(char s)
+{
+ int h = qsvg_h2i(s);
+ return (h << 4) | h;
+}
+
+bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
+{
+ if(name[0] != '#')
+ return false;
+ name++;
+ int len = qstrlen(name);
+ int r, g, b;
+ if (len == 12) {
+ r = qsvg_hex2int(name);
+ g = qsvg_hex2int(name + 4);
+ b = qsvg_hex2int(name + 8);
+ } else if (len == 9) {
+ r = qsvg_hex2int(name);
+ g = qsvg_hex2int(name + 3);
+ b = qsvg_hex2int(name + 6);
+ } else if (len == 6) {
+ r = qsvg_hex2int(name);
+ g = qsvg_hex2int(name + 2);
+ b = qsvg_hex2int(name + 4);
+ } else if (len == 3) {
+ r = qsvg_hex2int(name[0]);
+ g = qsvg_hex2int(name[1]);
+ b = qsvg_hex2int(name[2]);
+ } else {
+ r = g = b = -1;
+ }
+ if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) {
+ *rgb = 0;
+ return false;
+ }
+ *rgb = qRgb(r, g ,b);
+ return true;
+}
+
+bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
+{
+ if (len > 13)
+ return false;
+ char tmp[16];
+ for(int i = 0; i < len; ++i)
+ tmp[i] = str[i].toLatin1();
+ tmp[len] = 0;
+ return qsvg_get_hex_rgb(tmp, rgb);
+}
+
+// ======== end of qcolor_p duplicate
+
+static bool parsePathDataFast(const QStringRef &data, QPainterPath &path);
+
+static inline QString someId(const QXmlStreamAttributes &attributes)
+{
+ QString id = attributes.value(QLatin1String("id")).toString();
+ if (id.isEmpty())
+ id = attributes.value(QLatin1String("xml:id")).toString();
+ return id;
+}
+
+struct QSvgAttributes
+{
+ QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler);
+
+ QString id;
+
+ QStringRef color;
+ QStringRef colorOpacity;
+ QStringRef fill;
+ QStringRef fillRule;
+ QStringRef fillOpacity;
+ QStringRef stroke;
+ QStringRef strokeDashArray;
+ QStringRef strokeDashOffset;
+ QStringRef strokeLineCap;
+ QStringRef strokeLineJoin;
+ QStringRef strokeMiterLimit;
+ QStringRef strokeOpacity;
+ QStringRef strokeWidth;
+ QStringRef vectorEffect;
+ QStringRef fontFamily;
+ QStringRef fontSize;
+ QStringRef fontStyle;
+ QStringRef fontWeight;
+ QStringRef fontVariant;
+ QStringRef textAnchor;
+ QStringRef transform;
+ QStringRef visibility;
+ QStringRef opacity;
+ QStringRef compOp;
+ QStringRef display;
+ QStringRef offset;
+ QStringRef stopColor;
+ QStringRef stopOpacity;
+
+ QVector<QSvgCssAttribute> m_cssAttributes;
+};
+
+QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
+{
+ QStringRef style = xmlAttributes.value(QLatin1String("style"));
+ if (!style.isEmpty()) {
+ handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
+ for (int j = 0; j < m_cssAttributes.count(); ++j) {
+ const QSvgCssAttribute &attribute = m_cssAttributes.at(j);
+ QStringRef name = attribute.name;
+ QStringRef value = attribute.value;
+ if (name.isEmpty())
+ continue;
+
+ switch (name.at(0).unicode()) {
+
+ case 'c':
+ if (name == QLatin1String("color"))
+ color = value;
+ else if (name == QLatin1String("color-opacity"))
+ colorOpacity = value;
+ else if (name == QLatin1String("comp-op"))
+ compOp = value;
+ break;
+
+ case 'd':
+ if (name == QLatin1String("display"))
+ display = value;
+ break;
+
+ case 'f':
+ if (name == QLatin1String("fill"))
+ fill = value;
+ else if (name == QLatin1String("fill-rule"))
+ fillRule = value;
+ else if (name == QLatin1String("fill-opacity"))
+ fillOpacity = value;
+ else if (name == QLatin1String("font-family"))
+ fontFamily = value;
+ else if (name == QLatin1String("font-size"))
+ fontSize = value;
+ else if (name == QLatin1String("font-style"))
+ fontStyle = value;
+ else if (name == QLatin1String("font-weight"))
+ fontWeight = value;
+ else if (name == QLatin1String("font-variant"))
+ fontVariant = value;
+ break;
+
+ case 'o':
+ if (name == QLatin1String("opacity"))
+ opacity = value;
+ else if (name == QLatin1String("offset"))
+ offset = value;
+ break;
+
+ case 's':
+ if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
+ QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
+ if (strokeRef.isEmpty())
+ stroke = value;
+ else if (strokeRef == QLatin1String("-dasharray"))
+ strokeDashArray = value;
+ else if (strokeRef == QLatin1String("-dashoffset"))
+ strokeDashOffset = value;
+ else if (strokeRef == QLatin1String("-linecap"))
+ strokeLineCap = value;
+ else if (strokeRef == QLatin1String("-linejoin"))
+ strokeLineJoin = value;
+ else if (strokeRef == QLatin1String("-miterlimit"))
+ strokeMiterLimit = value;
+ else if (strokeRef == QLatin1String("-opacity"))
+ strokeOpacity = value;
+ else if (strokeRef == QLatin1String("-width"))
+ strokeWidth = value;
+ }
+ else if (name == QLatin1String("stop-color"))
+ stopColor = value;
+ else if (name == QLatin1String("stop-opacity"))
+ stopOpacity = value;
+ break;
+
+ case 't':
+ if (name == QLatin1String("text-anchor"))
+ textAnchor = value;
+ else if (name == QLatin1String("transform"))
+ transform = value;
+ break;
+
+ case 'v':
+ if (name == QLatin1String("vector-effect"))
+ vectorEffect = value;
+ else if (name == QLatin1String("visibility"))
+ visibility = value;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < xmlAttributes.count(); ++i) {
+ const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
+ QStringRef name = attribute.qualifiedName();
+ if (name.isEmpty())
+ continue;
+ QStringRef value = attribute.value();
+
+ switch (name.at(0).unicode()) {
+
+ case 'c':
+ if (name == QLatin1String("color"))
+ color = value;
+ else if (name == QLatin1String("color-opacity"))
+ colorOpacity = value;
+ else if (name == QLatin1String("comp-op"))
+ compOp = value;
+ break;
+
+ case 'd':
+ if (name == QLatin1String("display"))
+ display = value;
+ break;
+
+ case 'f':
+ if (name == QLatin1String("fill"))
+ fill = value;
+ else if (name == QLatin1String("fill-rule"))
+ fillRule = value;
+ else if (name == QLatin1String("fill-opacity"))
+ fillOpacity = value;
+ else if (name == QLatin1String("font-family"))
+ fontFamily = value;
+ else if (name == QLatin1String("font-size"))
+ fontSize = value;
+ else if (name == QLatin1String("font-style"))
+ fontStyle = value;
+ else if (name == QLatin1String("font-weight"))
+ fontWeight = value;
+ else if (name == QLatin1String("font-variant"))
+ fontVariant = value;
+ break;
+
+ case 'i':
+ if (name == QLatin1String("id"))
+ id = value.toString();
+ break;
+
+ case 'o':
+ if (name == QLatin1String("opacity"))
+ opacity = value;
+ if (name == QLatin1String("offset"))
+ offset = value;
+ break;
+
+ case 's':
+ if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
+ QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
+ if (strokeRef.isEmpty())
+ stroke = value;
+ else if (strokeRef == QLatin1String("-dasharray"))
+ strokeDashArray = value;
+ else if (strokeRef == QLatin1String("-dashoffset"))
+ strokeDashOffset = value;
+ else if (strokeRef == QLatin1String("-linecap"))
+ strokeLineCap = value;
+ else if (strokeRef == QLatin1String("-linejoin"))
+ strokeLineJoin = value;
+ else if (strokeRef == QLatin1String("-miterlimit"))
+ strokeMiterLimit = value;
+ else if (strokeRef == QLatin1String("-opacity"))
+ strokeOpacity = value;
+ else if (strokeRef == QLatin1String("-width"))
+ strokeWidth = value;
+ }
+ else if (name == QLatin1String("stop-color"))
+ stopColor = value;
+ else if (name == QLatin1String("stop-opacity"))
+ stopOpacity = value;
+ break;
+
+ case 't':
+ if (name == QLatin1String("text-anchor"))
+ textAnchor = value;
+ else if (name == QLatin1String("transform"))
+ transform = value;
+ break;
+
+ case 'v':
+ if (name == QLatin1String("vector-effect"))
+ vectorEffect = value;
+ else if (name == QLatin1String("visibility"))
+ visibility = value;
+ break;
+
+ case 'x':
+ if (name == QLatin1String("xml:id") && id.isEmpty())
+ id = value.toString();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+}
+
+static const char * QSvgStyleSelector_nodeString[] = {
+ "svg",
+ "g",
+ "defs",
+ "switch",
+ "animation",
+ "arc",
+ "circle",
+ "ellipse",
+ "image",
+ "line",
+ "path",
+ "polygon",
+ "polyline",
+ "rect",
+ "text",
+ "textarea",
+ "use",
+ "video"
+};
+
+class QSvgStyleSelector : public QCss::StyleSelector
+{
+public:
+ QSvgStyleSelector()
+ {
+ nameCaseSensitivity = Qt::CaseInsensitive;
+ }
+ virtual ~QSvgStyleSelector()
+ {
+ }
+
+ inline QString nodeToName(QSvgNode *node) const
+ {
+ return QLatin1String(QSvgStyleSelector_nodeString[node->type()]);
+ }
+
+ inline QSvgNode *svgNode(NodePtr node) const
+ {
+ return (QSvgNode*)node.ptr;
+ }
+ inline QSvgStructureNode *nodeToStructure(QSvgNode *n) const
+ {
+ if (n &&
+ (n->type() == QSvgNode::DOC ||
+ n->type() == QSvgNode::G ||
+ n->type() == QSvgNode::DEFS ||
+ n->type() == QSvgNode::SWITCH)) {
+ return (QSvgStructureNode*)n;
+ }
+ return 0;
+ }
+
+ inline QSvgStructureNode *svgStructure(NodePtr node) const
+ {
+ QSvgNode *n = svgNode(node);
+ QSvgStructureNode *st = nodeToStructure(n);
+ return st;
+ }
+
+ virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const
+ {
+ QSvgNode *n = svgNode(node);
+ if (!n)
+ return false;
+ QString name = nodeToName(n);
+ return QString::compare(name, nodeName, Qt::CaseInsensitive) == 0;
+ }
+ virtual QString attribute(NodePtr node, const QString &name) const
+ {
+ QSvgNode *n = svgNode(node);
+ if ((!n->nodeId().isEmpty() && (name == QLatin1String("id") ||
+ name == QLatin1String("xml:id"))))
+ return n->nodeId();
+ if (!n->xmlClass().isEmpty() && name == QLatin1String("class"))
+ return n->xmlClass();
+ return QString();
+ }
+ virtual bool hasAttributes(NodePtr node) const
+ {
+ QSvgNode *n = svgNode(node);
+ return (n &&
+ (!n->nodeId().isEmpty() || !n->xmlClass().isEmpty()));
+ }
+
+ virtual QStringList nodeIds(NodePtr node) const
+ {
+ QSvgNode *n = svgNode(node);
+ QString nid;
+ if (n)
+ nid = n->nodeId();
+ QStringList lst; lst.append(nid);
+ return lst;
+ }
+
+ virtual QStringList nodeNames(NodePtr node) const
+ {
+ QSvgNode *n = svgNode(node);
+ if (n)
+ return QStringList(nodeToName(n));
+ return QStringList();
+ }
+
+ virtual bool isNullNode(NodePtr node) const
+ {
+ return !node.ptr;
+ }
+
+ virtual NodePtr parentNode(NodePtr node) const
+ {
+ QSvgNode *n = svgNode(node);
+ NodePtr newNode;
+ newNode.ptr = 0;
+ newNode.id = 0;
+ if (n) {
+ QSvgNode *svgParent = n->parent();
+ if (svgParent) {
+ newNode.ptr = svgParent;
+ }
+ }
+ return newNode;
+ }
+ virtual NodePtr previousSiblingNode(NodePtr node) const
+ {
+ NodePtr newNode;
+ newNode.ptr = 0;
+ newNode.id = 0;
+
+ QSvgNode *n = svgNode(node);
+ if (!n)
+ return newNode;
+ QSvgStructureNode *svgParent = nodeToStructure(n->parent());
+
+ if (svgParent) {
+ newNode.ptr = svgParent->previousSiblingNode(n);
+ }
+ return newNode;
+ }
+ virtual NodePtr duplicateNode(NodePtr node) const
+ {
+ NodePtr n;
+ n.ptr = node.ptr;
+ n.id = node.id;
+ return n;
+ }
+ virtual void freeNode(NodePtr node) const
+ {
+ Q_UNUSED(node);
+ }
+};
+
+// '0' is 0x30 and '9' is 0x39
+static inline bool isDigit(ushort ch)
+{
+ static quint16 magic = 0x3ff;
+ return ((ch >> 4) == 3) && (magic >> (ch & 15));
+}
+
+static qreal toDouble(const QChar *&str)
+{
+ const int maxLen = 255;//technically doubles can go til 308+ but whatever
+ char temp[maxLen+1];
+ int pos = 0;
+
+ if (*str == QLatin1Char('-')) {
+ temp[pos++] = '-';
+ ++str;
+ } else if (*str == QLatin1Char('+')) {
+ ++str;
+ }
+ while (isDigit(str->unicode()) && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ if (*str == QLatin1Char('.') && pos < maxLen) {
+ temp[pos++] = '.';
+ ++str;
+ }
+ while (isDigit(str->unicode()) && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ bool exponent = false;
+ if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
+ exponent = true;
+ temp[pos++] = 'e';
+ ++str;
+ if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ while (isDigit(str->unicode()) && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ }
+
+ temp[pos] = '\0';
+
+ qreal val;
+ if (!exponent && pos < 10) {
+ int ival = 0;
+ const char *t = temp;
+ bool neg = false;
+ if(*t == '-') {
+ neg = true;
+ ++t;
+ }
+ while(*t && *t != '.') {
+ ival *= 10;
+ ival += (*t) - '0';
+ ++t;
+ }
+ if(*t == '.') {
+ ++t;
+ int div = 1;
+ while(*t) {
+ ival *= 10;
+ ival += (*t) - '0';
+ div *= 10;
+ ++t;
+ }
+ val = ((qreal)ival)/((qreal)div);
+ } else {
+ val = ival;
+ }
+ if (neg)
+ val = -val;
+ } else {
+#if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
+ if(sizeof(qreal) == sizeof(float))
+ val = strtof(temp, 0);
+ else
+#endif
+ {
+ bool ok = false;
+ val = qstrtod(temp, 0, &ok);
+ }
+ }
+ return val;
+
+}
+static qreal toDouble(const QString &str, bool *ok = NULL)
+{
+ const QChar *c = str.constData();
+ qreal res = toDouble(c);
+ if (ok) {
+ *ok = ((*c) == QLatin1Char('\0'));
+ }
+ return res;
+}
+
+static qreal toDouble(const QStringRef &str, bool *ok = NULL)
+{
+ const QChar *c = str.constData();
+ qreal res = toDouble(c);
+ if (ok) {
+ *ok = (c == (str.constData() + str.length()));
+ }
+ return res;
+}
+
+static QVector<qreal> parseNumbersList(const QChar *&str)
+{
+ QVector<qreal> points;
+ if (!str)
+ return points;
+ points.reserve(32);
+
+ while (str->isSpace())
+ ++str;
+ while (isDigit(str->unicode()) ||
+ *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
+ *str == QLatin1Char('.')) {
+
+ points.append(toDouble(str));
+
+ while (str->isSpace())
+ ++str;
+ if (*str == QLatin1Char(','))
+ ++str;
+
+ //eat the rest of space
+ while (str->isSpace())
+ ++str;
+ }
+
+ return points;
+}
+
+static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
+{
+ while (str->isSpace())
+ ++str;
+ while (isDigit(str->unicode()) ||
+ *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
+ *str == QLatin1Char('.')) {
+
+ points.append(toDouble(str));
+
+ while (str->isSpace())
+ ++str;
+ if (*str == QLatin1Char(','))
+ ++str;
+
+ //eat the rest of space
+ while (str->isSpace())
+ ++str;
+ }
+}
+
+static QVector<qreal> parsePercentageList(const QChar *&str)
+{
+ QVector<qreal> points;
+ if (!str)
+ return points;
+
+ while (str->isSpace())
+ ++str;
+ while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
+ *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
+ *str == QLatin1Char('.')) {
+
+ points.append(toDouble(str));
+
+ while (str->isSpace())
+ ++str;
+ if (*str == QLatin1Char('%'))
+ ++str;
+ while (str->isSpace())
+ ++str;
+ if (*str == QLatin1Char(','))
+ ++str;
+
+ //eat the rest of space
+ while (str->isSpace())
+ ++str;
+ }
+
+ return points;
+}
+
+static QString idFromUrl(const QString &url)
+{
+ QString::const_iterator itr = url.constBegin();
+ while ((*itr).isSpace())
+ ++itr;
+ if ((*itr) == QLatin1Char('('))
+ ++itr;
+ while ((*itr).isSpace())
+ ++itr;
+ if ((*itr) == QLatin1Char('#'))
+ ++itr;
+ QString id;
+ while ((*itr) != QLatin1Char(')')) {
+ id += *itr;
+ ++itr;
+ }
+ return id;
+}
+
+static inline QStringRef trimRef(const QStringRef &str)
+{
+ if (str.isEmpty())
+ return QStringRef();
+ const QChar *s = str.string()->constData() + str.position();
+ int end = str.length() - 1;
+ if (!s[0].isSpace() && !s[end].isSpace())
+ return str;
+
+ int start = 0;
+ while (start<=end && s[start].isSpace()) // skip white space from start
+ start++;
+ if (start <= end) { // only white space
+ while (s[end].isSpace()) // skip white space from end
+ end--;
+ }
+ int l = end - start + 1;
+ if (l <= 0)
+ return QStringRef();
+ return QStringRef(str.string(), str.position() + start, l);
+}
+
+/**
+ * returns true when successfuly set the color. false signifies
+ * that the color should be inherited
+ */
+static bool resolveColor(const QStringRef &colorStr, QColor &color, QSvgHandler *handler)
+{
+ QStringRef colorStrTr = trimRef(colorStr);
+ if (colorStrTr.isEmpty())
+ return false;
+
+ switch(colorStrTr.at(0).unicode()) {
+
+ case '#':
+ {
+ // #rrggbb is very very common, so let's tackle it here
+ // rather than falling back to QColor
+ QRgb rgb;
+ bool ok = qsvg_get_hex_rgb(colorStrTr.unicode(), colorStrTr.length(), &rgb);
+ if (ok)
+ color.setRgb(rgb);
+ return ok;
+ }
+ break;
+
+ case 'r':
+ {
+ // starts with "rgb(", ends with ")" and consists of at least 7 characters "rgb(,,)"
+ if (colorStrTr.length() >= 7 && colorStrTr.at(colorStrTr.length() - 1) == QLatin1Char(')')
+ && QStringRef(colorStrTr.string(), colorStrTr.position(), 4) == QLatin1String("rgb(")) {
+ const QChar *s = colorStrTr.constData() + 4;
+ QVector<qreal> compo = parseNumbersList(s);
+ //1 means that it failed after reaching non-parsable
+ //character which is going to be "%"
+ if (compo.size() == 1) {
+ s = colorStrTr.constData() + 4;
+ compo = parsePercentageList(s);
+ for (int i = 0; i < compo.size(); ++i)
+ compo[i] *= (qreal)2.55;
+ }
+
+ if (compo.size() == 3) {
+ color = QColor(int(compo[0]),
+ int(compo[1]),
+ int(compo[2]));
+ return true;
+ }
+ return false;
+ }
+ }
+ break;
+
+ case 'c':
+ if (colorStrTr == QLatin1String("currentColor")) {
+ color = handler->currentColor();
+ return true;
+ }
+ break;
+ case 'i':
+ if (colorStrTr == QT_INHERIT)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ color = QColor(colorStrTr.toString());
+ return color.isValid();
+}
+
+static bool constructColor(const QStringRef &colorStr, const QStringRef &opacity,
+ QColor &color, QSvgHandler *handler)
+{
+ if (!resolveColor(colorStr, color, handler))
+ return false;
+ if (!opacity.isEmpty()) {
+ bool ok = true;
+ qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));
+ if (!ok)
+ op = 1.0;
+ color.setAlphaF(op);
+ }
+ return true;
+}
+
+static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
+ QSvgHandler *handler, bool *ok = NULL)
+{
+ QString numStr = str.trimmed();
+
+ if (numStr.endsWith(QLatin1Char('%'))) {
+ numStr.chop(1);
+ type = QSvgHandler::LT_PERCENT;
+ } else if (numStr.endsWith(QLatin1String("px"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_PX;
+ } else if (numStr.endsWith(QLatin1String("pc"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_PC;
+ } else if (numStr.endsWith(QLatin1String("pt"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_PT;
+ } else if (numStr.endsWith(QLatin1String("mm"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_MM;
+ } else if (numStr.endsWith(QLatin1String("cm"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_CM;
+ } else if (numStr.endsWith(QLatin1String("in"))) {
+ numStr.chop(2);
+ type = QSvgHandler::LT_IN;
+ } else {
+ type = handler->defaultCoordinateSystem();
+ //type = QSvgHandler::LT_OTHER;
+ }
+ qreal len = toDouble(numStr, ok);
+ //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
+ return len;
+}
+
+static inline qreal convertToNumber(const QString &str, QSvgHandler *handler, bool *ok = NULL)
+{
+ QSvgHandler::LengthType type;
+ qreal num = parseLength(str, type, handler, ok);
+ if (type == QSvgHandler::LT_PERCENT) {
+ num = num/100.0;
+ }
+ return num;
+}
+
+static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes)
+{
+ QStringRef uncStr = attributes.value(QLatin1String("unicode"));
+ QStringRef havStr = attributes.value(QLatin1String("horiz-adv-x"));
+ QStringRef pathStr = attributes.value(QLatin1String("d"));
+
+ QChar unicode = (uncStr.isEmpty()) ? 0 : uncStr.at(0);
+ qreal havx = (havStr.isEmpty()) ? -1 : toDouble(havStr);
+ QPainterPath path;
+ path.setFillRule(Qt::WindingFill);
+ parsePathDataFast(pathStr, path);
+
+ font->addGlyph(unicode, path, havx);
+
+ return true;
+}
+
+// this should really be called convertToDefaultCoordinateSystem
+// and convert when type != QSvgHandler::defaultCoordinateSystem
+static qreal convertToPixels(qreal len, bool , QSvgHandler::LengthType type)
+{
+
+ switch (type) {
+ case QSvgHandler::LT_PERCENT:
+ break;
+ case QSvgHandler::LT_PX:
+ break;
+ case QSvgHandler::LT_PC:
+ break;
+ case QSvgHandler::LT_PT:
+ return len * 1.25;
+ break;
+ case QSvgHandler::LT_MM:
+ return len * 3.543307;
+ break;
+ case QSvgHandler::LT_CM:
+ return len * 35.43307;
+ break;
+ case QSvgHandler::LT_IN:
+ return len * 90;
+ break;
+ case QSvgHandler::LT_OTHER:
+ break;
+ default:
+ break;
+ }
+ return len;
+}
+
+static void parseColor(QSvgNode *,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QColor color;
+ if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
+ handler->popColor();
+ handler->pushColor(color);
+ }
+}
+
+static QSvgStyleProperty *styleFromUrl(QSvgNode *node, const QString &url)
+{
+ return node ? node->styleProperty(idFromUrl(url)) : 0;
+}
+
+static void parseBrush(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
+ QSvgFillStyle *prop = new QSvgFillStyle;
+
+ //fill-rule attribute handling
+ if (!attributes.fillRule.isEmpty() && attributes.fillRule != QT_INHERIT) {
+ if (attributes.fillRule == QLatin1String("evenodd"))
+ prop->setFillRule(Qt::OddEvenFill);
+ else if (attributes.fillRule == QLatin1String("nonzero"))
+ prop->setFillRule(Qt::WindingFill);
+ }
+
+ //fill-opacity atttribute handling
+ if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != QT_INHERIT) {
+ prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.fillOpacity))));
+ }
+
+ //fill attribute handling
+ if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
+ if (attributes.fill.length() > 3 &&
+ QStringRef(attributes.fill.string(), attributes.fill.position(), 3) == QLatin1String("url")) {
+ QStringRef urlRef(attributes.fill.string(), attributes.fill.position() + 3, attributes.fill.length() - 3);
+ QString value = urlRef.toString();
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ QString id = idFromUrl(value);
+ prop->setGradientId(id);
+ prop->setGradientResolved(false);
+ }
+ } else if (attributes.fill != QLatin1String("none")) {
+ QColor color;
+ if (resolveColor(attributes.fill, color, handler))
+ prop->setBrush(QBrush(color));
+ } else {
+ prop->setBrush(QBrush(Qt::NoBrush));
+ }
+ }
+ node->appendStyleProperty(prop, attributes.id);
+ }
+}
+
+
+
+static QMatrix parseTransformationMatrix(const QStringRef &value)
+{
+ if (value.isEmpty())
+ return QMatrix();
+
+ QMatrix matrix;
+ const QChar *str = value.constData();
+ const QChar *end = str + value.length();
+
+ while (str < end) {
+ if (str->isSpace() || *str == QLatin1Char(',')) {
+ ++str;
+ continue;
+ }
+ enum State {
+ Matrix,
+ Translate,
+ Rotate,
+ Scale,
+ SkewX,
+ SkewY
+ };
+ State state = Matrix;
+ if (*str == QLatin1Char('m')) { //matrix
+ const char *ident = "atrix";
+ for (int i = 0; i < 5; ++i)
+ if (*(++str) != QLatin1Char(ident[i]))
+ goto error;
+ ++str;
+ state = Matrix;
+ } else if (*str == QLatin1Char('t')) { //translate
+ const char *ident = "ranslate";
+ for (int i = 0; i < 8; ++i)
+ if (*(++str) != QLatin1Char(ident[i]))
+ goto error;
+ ++str;
+ state = Translate;
+ } else if (*str == QLatin1Char('r')) { //rotate
+ const char *ident = "otate";
+ for (int i = 0; i < 5; ++i)
+ if (*(++str) != QLatin1Char(ident[i]))
+ goto error;
+ ++str;
+ state = Rotate;
+ } else if (*str == QLatin1Char('s')) { //scale, skewX, skewY
+ ++str;
+ if (*str == QLatin1Char('c')) {
+ const char *ident = "ale";
+ for (int i = 0; i < 3; ++i)
+ if (*(++str) != QLatin1Char(ident[i]))
+ goto error;
+ ++str;
+ state = Scale;
+ } else if (*str == QLatin1Char('k')) {
+ if (*(++str) != QLatin1Char('e'))
+ goto error;
+ if (*(++str) != QLatin1Char('w'))
+ goto error;
+ ++str;
+ if (*str == QLatin1Char('X'))
+ state = SkewX;
+ else if (*str == QLatin1Char('Y'))
+ state = SkewY;
+ else
+ goto error;
+ ++str;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
+
+
+ while (str < end && str->isSpace())
+ ++str;
+ if (*str != QLatin1Char('('))
+ goto error;
+ ++str;
+ QVarLengthArray<qreal, 8> points;
+ parseNumbersArray(str, points);
+ if (*str != QLatin1Char(')'))
+ goto error;
+ ++str;
+
+ if(state == Matrix) {
+ if(points.count() != 6)
+ goto error;
+ matrix = matrix * QMatrix(points[0], points[1],
+ points[2], points[3],
+ points[4], points[5]);
+ } else if (state == Translate) {
+ if (points.count() == 1)
+ matrix.translate(points[0], 0);
+ else if (points.count() == 2)
+ matrix.translate(points[0], points[1]);
+ else
+ goto error;
+ } else if (state == Rotate) {
+ if(points.count() == 1) {
+ matrix.rotate(points[0]);
+ } else if (points.count() == 3) {
+ matrix.translate(points[1], points[2]);
+ matrix.rotate(points[0]);
+ matrix.translate(-points[1], -points[2]);
+ } else {
+ goto error;
+ }
+ } else if (state == Scale) {
+ if (points.count() < 1 || points.count() > 2)
+ goto error;
+ qreal sx = points[0];
+ qreal sy = sx;
+ if(points.count() == 2)
+ sy = points[1];
+ matrix.scale(sx, sy);
+ } else if (state == SkewX) {
+ if (points.count() != 1)
+ goto error;
+ const qreal deg2rad = qreal(0.017453292519943295769);
+ matrix.shear(qTan(points[0]*deg2rad), 0);
+ } else if (state == SkewY) {
+ if (points.count() != 1)
+ goto error;
+ const qreal deg2rad = qreal(0.017453292519943295769);
+ matrix.shear(0, qTan(points[0]*deg2rad));
+ }
+ }
+ error:
+ return matrix;
+}
+
+static void parsePen(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
+
+ if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
+ || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
+ || !attributes.vectorEffect.isEmpty()) {
+
+ QSvgStrokeStyle *prop = new QSvgStrokeStyle;
+
+ //stroke attribute handling
+ if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
+ if (attributes.stroke.length() > 3 &&
+ QStringRef(attributes.stroke.string(), attributes.stroke.position(), 3) == QLatin1String("url")) {
+ QStringRef urlRef(attributes.stroke.string(), attributes.stroke.position() + 3, attributes.stroke.length() - 3);
+ QString value = urlRef.toString();
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ QString id = idFromUrl(value);
+ prop->setGradientId(id);
+ prop->setGradientResolved(false);
+ }
+ } else if (attributes.stroke != QLatin1String("none")) {
+ QColor color;
+ if (resolveColor(attributes.stroke, color, handler))
+ prop->setStroke(QBrush(color));
+ } else {
+ prop->setStroke(QBrush(Qt::NoBrush));
+ }
+ }
+
+ //stroke-width handling
+ if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != QT_INHERIT) {
+ QSvgHandler::LengthType lt;
+ prop->setWidth(parseLength(attributes.strokeWidth.toString(), lt, handler));
+ }
+
+ //stroke-dasharray
+ if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != QT_INHERIT) {
+ if (attributes.strokeDashArray == QLatin1String("none")) {
+ prop->setDashArrayNone();
+ } else {
+ QString dashArray = attributes.strokeDashArray.toString();
+ const QChar *s = dashArray.constData();
+ QVector<qreal> dashes = parseNumbersList(s);
+ // if the dash count is odd the dashes should be duplicated
+ if ((dashes.size() & 1) != 0)
+ dashes << QVector<qreal>(dashes);
+ prop->setDashArray(dashes);
+ }
+ }
+
+ //stroke-linejoin attribute handling
+ if (!attributes.strokeLineJoin.isEmpty()) {
+ if (attributes.strokeLineJoin == QLatin1String("miter"))
+ prop->setLineJoin(Qt::SvgMiterJoin);
+ else if (attributes.strokeLineJoin == QLatin1String("round"))
+ prop->setLineJoin(Qt::RoundJoin);
+ else if (attributes.strokeLineJoin == QLatin1String("bevel"))
+ prop->setLineJoin(Qt::BevelJoin);
+ }
+
+ //stroke-linecap attribute handling
+ if (!attributes.strokeLineCap.isEmpty()) {
+ if (attributes.strokeLineCap == QLatin1String("butt"))
+ prop->setLineCap(Qt::FlatCap);
+ else if (attributes.strokeLineCap == QLatin1String("round"))
+ prop->setLineCap(Qt::RoundCap);
+ else if (attributes.strokeLineCap == QLatin1String("square"))
+ prop->setLineCap(Qt::SquareCap);
+ }
+
+ //stroke-dashoffset attribute handling
+ if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != QT_INHERIT)
+ prop->setDashOffset(toDouble(attributes.strokeDashOffset));
+
+ //vector-effect attribute handling
+ if (!attributes.vectorEffect.isEmpty()) {
+ if (attributes.vectorEffect == QLatin1String("non-scaling-stroke"))
+ prop->setVectorEffect(true);
+ else if (attributes.vectorEffect == QLatin1String("none"))
+ prop->setVectorEffect(false);
+ }
+
+ //stroke-miterlimit
+ if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != QT_INHERIT)
+ prop->setMiterLimit(toDouble(attributes.strokeMiterLimit));
+
+ //stroke-opacity atttribute handling
+ if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != QT_INHERIT)
+ prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.strokeOpacity))));
+
+ node->appendStyleProperty(prop, attributes.id);
+ }
+}
+
+static void parseFont(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
+ attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
+ return;
+
+ QSvgTinyDocument *doc = node->document();
+ QSvgFontStyle *fontStyle = 0;
+ if (!attributes.fontFamily.isEmpty()) {
+ QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
+ if (svgFont)
+ fontStyle = new QSvgFontStyle(svgFont, doc);
+ }
+ if (!fontStyle)
+ fontStyle = new QSvgFontStyle;
+
+ if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT)
+ fontStyle->setFamily(attributes.fontFamily.toString().trimmed());
+
+ if (!attributes.fontSize.isEmpty() && attributes.fontSize != QT_INHERIT) {
+ // TODO: Support relative sizes 'larger' and 'smaller'.
+ QSvgHandler::LengthType dummy; // should always be pixel size
+ qreal size = 0;
+ static const qreal sizeTable[] = { qreal(6.9), qreal(8.3), qreal(10.0), qreal(12.0), qreal(14.4), qreal(17.3), qreal(20.7) };
+ enum AbsFontSize { XXSmall, XSmall, Small, Medium, Large, XLarge, XXLarge };
+ switch (attributes.fontSize.at(0).unicode()) {
+ case 'x':
+ if (attributes.fontSize == QLatin1String("xx-small"))
+ size = sizeTable[XXSmall];
+ else if (attributes.fontSize == QLatin1String("x-small"))
+ size = sizeTable[XSmall];
+ else if (attributes.fontSize == QLatin1String("x-large"))
+ size = sizeTable[XLarge];
+ else if (attributes.fontSize == QLatin1String("xx-large"))
+ size = sizeTable[XXLarge];
+ break;
+ case 's':
+ if (attributes.fontSize == QLatin1String("small"))
+ size = sizeTable[Small];
+ break;
+ case 'm':
+ if (attributes.fontSize == QLatin1String("medium"))
+ size = sizeTable[Medium];
+ break;
+ case 'l':
+ if (attributes.fontSize == QLatin1String("large"))
+ size = sizeTable[Large];
+ break;
+ default:
+ size = parseLength(attributes.fontSize.toString(), dummy, handler);
+ break;
+ }
+ fontStyle->setSize(size);
+ }
+
+ if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
+ if (attributes.fontStyle == QLatin1String("normal")) {
+ fontStyle->setStyle(QFont::StyleNormal);
+ } else if (attributes.fontStyle == QLatin1String("italic")) {
+ fontStyle->setStyle(QFont::StyleItalic);
+ } else if (attributes.fontStyle == QLatin1String("oblique")) {
+ fontStyle->setStyle(QFont::StyleOblique);
+ }
+ }
+
+ if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
+ bool ok = false;
+ int weightNum = attributes.fontWeight.toString().toInt(&ok);
+ if (ok) {
+ fontStyle->setWeight(weightNum);
+ } else {
+ if (attributes.fontWeight == QLatin1String("normal")) {
+ fontStyle->setWeight(400);
+ } else if (attributes.fontWeight == QLatin1String("bold")) {
+ fontStyle->setWeight(700);
+ } else if (attributes.fontWeight == QLatin1String("bolder")) {
+ fontStyle->setWeight(QSvgFontStyle::BOLDER);
+ } else if (attributes.fontWeight == QLatin1String("lighter")) {
+ fontStyle->setWeight(QSvgFontStyle::LIGHTER);
+ }
+ }
+ }
+
+ if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
+ if (attributes.fontVariant == QLatin1String("normal"))
+ fontStyle->setVariant(QFont::MixedCase);
+ else if (attributes.fontVariant == QLatin1String("small-caps"))
+ fontStyle->setVariant(QFont::SmallCaps);
+ }
+
+ if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
+ if (attributes.textAnchor == QLatin1String("start"))
+ fontStyle->setTextAnchor(Qt::AlignLeft);
+ if (attributes.textAnchor == QLatin1String("middle"))
+ fontStyle->setTextAnchor(Qt::AlignHCenter);
+ else if (attributes.textAnchor == QLatin1String("end"))
+ fontStyle->setTextAnchor(Qt::AlignRight);
+ }
+
+ node->appendStyleProperty(fontStyle, attributes.id);
+}
+
+static void parseTransform(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ if (attributes.transform.isEmpty())
+ return;
+ QMatrix matrix = parseTransformationMatrix(trimRef(attributes.transform));
+
+ if (!matrix.isIdentity()) {
+ node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), attributes.id);
+ }
+
+}
+
+static void parseVisibility(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ QSvgNode *parent = node->parent();
+
+ if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
+ node->setVisible(parent->isVisible());
+ else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
+ node->setVisible(false);
+ } else
+ node->setVisible(true);
+}
+
+static void pathArcSegment(QPainterPath &path,
+ qreal xc, qreal yc,
+ qreal th0, qreal th1,
+ qreal rx, qreal ry, qreal xAxisRotation)
+{
+ qreal sinTh, cosTh;
+ qreal a00, a01, a10, a11;
+ qreal x1, y1, x2, y2, x3, y3;
+ qreal t;
+ qreal thHalf;
+
+ sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
+ cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
+
+ a00 = cosTh * rx;
+ a01 = -sinTh * ry;
+ a10 = sinTh * rx;
+ a11 = cosTh * ry;
+
+ thHalf = 0.5 * (th1 - th0);
+ t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
+ x1 = xc + qCos(th0) - t * qSin(th0);
+ y1 = yc + qSin(th0) + t * qCos(th0);
+ x3 = xc + qCos(th1);
+ y3 = yc + qSin(th1);
+ x2 = x3 + t * qSin(th1);
+ y2 = y3 - t * qCos(th1);
+
+ path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+ a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+ a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+// the arc handling code underneath is from XSVG (BSD license)
+/*
+ * Copyright 2002 USC/Information Sciences Institute
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Information Sciences Institute not be used in advertising or
+ * publicity pertaining to distribution of the software without
+ * specific, written prior permission. Information Sciences Institute
+ * makes no representations about the suitability of this software for
+ * any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
+ * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+ * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+static void pathArc(QPainterPath &path,
+ qreal rx,
+ qreal ry,
+ qreal x_axis_rotation,
+ int large_arc_flag,
+ int sweep_flag,
+ qreal x,
+ qreal y,
+ qreal curx, qreal cury)
+{
+ qreal sin_th, cos_th;
+ qreal a00, a01, a10, a11;
+ qreal x0, y0, x1, y1, xc, yc;
+ qreal d, sfactor, sfactor_sq;
+ qreal th0, th1, th_arc;
+ int i, n_segs;
+ qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
+
+ rx = qAbs(rx);
+ ry = qAbs(ry);
+
+ sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
+ cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
+
+ dx = (curx - x) / 2.0;
+ dy = (cury - y) / 2.0;
+ dx1 = cos_th * dx + sin_th * dy;
+ dy1 = -sin_th * dx + cos_th * dy;
+ Pr1 = rx * rx;
+ Pr2 = ry * ry;
+ Px = dx1 * dx1;
+ Py = dy1 * dy1;
+ /* Spec : check if radii are large enough */
+ check = Px / Pr1 + Py / Pr2;
+ if (check > 1) {
+ rx = rx * qSqrt(check);
+ ry = ry * qSqrt(check);
+ }
+
+ a00 = cos_th / rx;
+ a01 = sin_th / rx;
+ a10 = -sin_th / ry;
+ a11 = cos_th / ry;
+ x0 = a00 * curx + a01 * cury;
+ y0 = a10 * curx + a11 * cury;
+ x1 = a00 * x + a01 * y;
+ y1 = a10 * x + a11 * y;
+ /* (x0, y0) is current point in transformed coordinate space.
+ (x1, y1) is new point in transformed coordinate space.
+
+ The arc fits a unit-radius circle in this space.
+ */
+ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+ sfactor_sq = 1.0 / d - 0.25;
+ if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactor = qSqrt(sfactor_sq);
+ if (sweep_flag == large_arc_flag) sfactor = -sfactor;
+ xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+ yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+ /* (xc, yc) is center of the circle. */
+
+ th0 = qAtan2(y0 - yc, x0 - xc);
+ th1 = qAtan2(y1 - yc, x1 - xc);
+
+ th_arc = th1 - th0;
+ if (th_arc < 0 && sweep_flag)
+ th_arc += 2 * Q_PI;
+ else if (th_arc > 0 && !sweep_flag)
+ th_arc -= 2 * Q_PI;
+
+ n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
+
+ for (i = 0; i < n_segs; i++) {
+ pathArcSegment(path, xc, yc,
+ th0 + i * th_arc / n_segs,
+ th0 + (i + 1) * th_arc / n_segs,
+ rx, ry, x_axis_rotation);
+ }
+}
+
+static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
+{
+ qreal x0 = 0, y0 = 0; // starting point
+ qreal x = 0, y = 0; // current point
+ char lastMode = 0;
+ QPointF ctrlPt;
+ const QChar *str = dataStr.constData();
+ const QChar *end = str + dataStr.size();
+
+ while (str != end) {
+ while (str->isSpace())
+ ++str;
+ QChar pathElem = *str;
+ ++str;
+ QChar endc = *end;
+ *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
+ QVarLengthArray<qreal, 8> arg;
+ parseNumbersArray(str, arg);
+ *const_cast<QChar *>(end) = endc;
+ if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
+ arg.append(0);//dummy
+ const qreal *num = arg.constData();
+ int count = arg.count();
+ while (count > 0) {
+ qreal offsetX = x; // correction offsets
+ qreal offsetY = y; // for relative commands
+ switch (pathElem.unicode()) {
+ case 'm': {
+ if (count < 2) {
+ num++;
+ count--;
+ break;
+ }
+ x = x0 = num[0] + offsetX;
+ y = y0 = num[1] + offsetY;
+ num += 2;
+ count -= 2;
+ path.moveTo(x0, y0);
+
+ // As per 1.2 spec 8.3.2 The "moveto" commands
+ // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+ // the subsequent pairs shall be treated as implicit 'lineto' commands.
+ pathElem = QLatin1Char('l');
+ }
+ break;
+ case 'M': {
+ if (count < 2) {
+ num++;
+ count--;
+ break;
+ }
+ x = x0 = num[0];
+ y = y0 = num[1];
+ num += 2;
+ count -= 2;
+ path.moveTo(x0, y0);
+
+ // As per 1.2 spec 8.3.2 The "moveto" commands
+ // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+ // the subsequent pairs shall be treated as implicit 'lineto' commands.
+ pathElem = QLatin1Char('L');
+ }
+ break;
+ case 'z':
+ case 'Z': {
+ x = x0;
+ y = y0;
+ count--; // skip dummy
+ num++;
+ path.closeSubpath();
+ }
+ break;
+ case 'l': {
+ if (count < 2) {
+ num++;
+ count--;
+ break;
+ }
+ x = num[0] + offsetX;
+ y = num[1] + offsetY;
+ num += 2;
+ count -= 2;
+ path.lineTo(x, y);
+
+ }
+ break;
+ case 'L': {
+ if (count < 2) {
+ num++;
+ count--;
+ break;
+ }
+ x = num[0];
+ y = num[1];
+ num += 2;
+ count -= 2;
+ path.lineTo(x, y);
+ }
+ break;
+ case 'h': {
+ x = num[0] + offsetX;
+ num++;
+ count--;
+ path.lineTo(x, y);
+ }
+ break;
+ case 'H': {
+ x = num[0];
+ num++;
+ count--;
+ path.lineTo(x, y);
+ }
+ break;
+ case 'v': {
+ y = num[0] + offsetY;
+ num++;
+ count--;
+ path.lineTo(x, y);
+ }
+ break;
+ case 'V': {
+ y = num[0];
+ num++;
+ count--;
+ path.lineTo(x, y);
+ }
+ break;
+ case 'c': {
+ if (count < 6) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c1(num[0] + offsetX, num[1] + offsetY);
+ QPointF c2(num[2] + offsetX, num[3] + offsetY);
+ QPointF e(num[4] + offsetX, num[5] + offsetY);
+ num += 6;
+ count -= 6;
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'C': {
+ if (count < 6) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c1(num[0], num[1]);
+ QPointF c2(num[2], num[3]);
+ QPointF e(num[4], num[5]);
+ num += 6;
+ count -= 6;
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 's': {
+ if (count < 4) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c1;
+ if (lastMode == 'c' || lastMode == 'C' ||
+ lastMode == 's' || lastMode == 'S')
+ c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c1 = QPointF(x, y);
+ QPointF c2(num[0] + offsetX, num[1] + offsetY);
+ QPointF e(num[2] + offsetX, num[3] + offsetY);
+ num += 4;
+ count -= 4;
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'S': {
+ if (count < 4) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c1;
+ if (lastMode == 'c' || lastMode == 'C' ||
+ lastMode == 's' || lastMode == 'S')
+ c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c1 = QPointF(x, y);
+ QPointF c2(num[0], num[1]);
+ QPointF e(num[2], num[3]);
+ num += 4;
+ count -= 4;
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'q': {
+ if (count < 4) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c(num[0] + offsetX, num[1] + offsetY);
+ QPointF e(num[2] + offsetX, num[3] + offsetY);
+ num += 4;
+ count -= 4;
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'Q': {
+ if (count < 4) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF c(num[0], num[1]);
+ QPointF e(num[2], num[3]);
+ num += 4;
+ count -= 4;
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 't': {
+ if (count < 2) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF e(num[0] + offsetX, num[1] + offsetY);
+ num += 2;
+ count -= 2;
+ QPointF c;
+ if (lastMode == 'q' || lastMode == 'Q' ||
+ lastMode == 't' || lastMode == 'T')
+ c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c = QPointF(x, y);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'T': {
+ if (count < 2) {
+ num += count;
+ count = 0;
+ break;
+ }
+ QPointF e(num[0], num[1]);
+ num += 2;
+ count -= 2;
+ QPointF c;
+ if (lastMode == 'q' || lastMode == 'Q' ||
+ lastMode == 't' || lastMode == 'T')
+ c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
+ else
+ c = QPointF(x, y);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ break;
+ }
+ case 'a': {
+ if (count < 7) {
+ num += count;
+ count = 0;
+ break;
+ }
+ qreal rx = (*num++);
+ qreal ry = (*num++);
+ qreal xAxisRotation = (*num++);
+ qreal largeArcFlag = (*num++);
+ qreal sweepFlag = (*num++);
+ qreal ex = (*num++) + offsetX;
+ qreal ey = (*num++) + offsetY;
+ count -= 7;
+ qreal curx = x;
+ qreal cury = y;
+ pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+ int(sweepFlag), ex, ey, curx, cury);
+
+ x = ex;
+ y = ey;
+ }
+ break;
+ case 'A': {
+ if (count < 7) {
+ num += count;
+ count = 0;
+ break;
+ }
+ qreal rx = (*num++);
+ qreal ry = (*num++);
+ qreal xAxisRotation = (*num++);
+ qreal largeArcFlag = (*num++);
+ qreal sweepFlag = (*num++);
+ qreal ex = (*num++);
+ qreal ey = (*num++);
+ count -= 7;
+ qreal curx = x;
+ qreal cury = y;
+ pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+ int(sweepFlag), ex, ey, curx, cury);
+
+ x = ex;
+ y = ey;
+ }
+ break;
+ default:
+ return false;
+ }
+ lastMode = pathElem.toLatin1();
+ }
+ }
+ return true;
+}
+
+static bool parseStyle(QSvgNode *node,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *);
+
+static bool parseStyle(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *);
+
+static void parseCSStoXMLAttrs(const QVector<QCss::Declaration> &declarations,
+ QXmlStreamAttributes &attributes)
+{
+ for (int i = 0; i < declarations.count(); ++i) {
+ const QCss::Declaration &decl = declarations.at(i);
+ if (decl.d->property.isEmpty())
+ continue;
+ QCss::Value val = decl.d->values.first();
+ QString valueStr;
+ if (decl.d->values.count() != 1) {
+ for (int i=0; i<decl.d->values.count(); ++i) {
+ const QString &value = decl.d->values[i].toString();
+ if (value.isEmpty())
+ valueStr += QLatin1Char(',');
+ else
+ valueStr += value;
+ }
+ } else {
+ valueStr = val.toString();
+ }
+ if (val.type == QCss::Value::Uri) {
+ valueStr.prepend(QLatin1String("url("));
+ valueStr.append(QLatin1Char(')'));
+ } else if (val.type == QCss::Value::Function) {
+ QStringList lst = val.variant.toStringList();
+ valueStr.append(lst.at(0));
+ valueStr.append(QLatin1Char('('));
+ for (int i = 1; i < lst.count(); ++i) {
+ valueStr.append(lst.at(i));
+ if ((i +1) < lst.count())
+ valueStr.append(QLatin1Char(','));
+ }
+ valueStr.append(QLatin1Char(')'));
+ } else if (val.type == QCss::Value::KnownIdentifier) {
+ switch (val.variant.toInt()) {
+ case QCss::Value_None:
+ valueStr = QLatin1String("none");
+ break;
+ default:
+ break;
+ }
+ }
+
+ attributes.append(QString(), decl.d->property, valueStr);
+ }
+}
+
+void QSvgHandler::parseCSStoXMLAttrs(QString css, QVector<QSvgCssAttribute> *attributes)
+{
+ // preprocess (for unicode escapes), tokenize and remove comments
+ m_cssParser.init(css);
+ QString key;
+
+ attributes->reserve(10);
+
+ while (m_cssParser.hasNext()) {
+ m_cssParser.skipSpace();
+
+ if (!m_cssParser.hasNext())
+ break;
+ m_cssParser.next();
+
+ QStringRef name;
+ if (m_cssParser.hasEscapeSequences) {
+ key = m_cssParser.lexem();
+ name = QStringRef(&key, 0, key.length());
+ } else {
+ const QCss::Symbol &sym = m_cssParser.symbol();
+ name = QStringRef(&sym.text, sym.start, sym.len);
+ }
+
+ m_cssParser.skipSpace();
+ if (!m_cssParser.test(QCss::COLON))
+ break;
+
+ m_cssParser.skipSpace();
+ if (!m_cssParser.hasNext())
+ break;
+
+ QSvgCssAttribute attribute;
+ attribute.name = QXmlStreamStringRef(name);
+
+ const int firstSymbol = m_cssParser.index;
+ int symbolCount = 0;
+ do {
+ m_cssParser.next();
+ ++symbolCount;
+ } while (m_cssParser.hasNext() && !m_cssParser.test(QCss::SEMICOLON));
+
+ bool canExtractValueByRef = !m_cssParser.hasEscapeSequences;
+ if (canExtractValueByRef) {
+ int len = m_cssParser.symbols.at(firstSymbol).len;
+ for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
+ len += m_cssParser.symbols.at(i).len;
+
+ if (m_cssParser.symbols.at(i - 1).start + m_cssParser.symbols.at(i - 1).len
+ != m_cssParser.symbols.at(i).start) {
+ canExtractValueByRef = false;
+ break;
+ }
+ }
+ if (canExtractValueByRef) {
+ const QCss::Symbol &sym = m_cssParser.symbols.at(firstSymbol);
+ attribute.value = QXmlStreamStringRef(QStringRef(&sym.text, sym.start, len));
+ }
+ }
+ if (!canExtractValueByRef) {
+ QString value;
+ for (int i = firstSymbol; i < m_cssParser.index - 1; ++i)
+ value += m_cssParser.symbols.at(i).lexem();
+ attribute.value = QXmlStreamStringRef(QStringRef(&value, 0, value.length()));
+ }
+
+ attributes->append(attribute);
+
+ m_cssParser.skipSpace();
+ }
+}
+
+static void cssStyleLookup(QSvgNode *node,
+ QSvgHandler *handler,
+ QSvgStyleSelector *selector)
+{
+ QCss::StyleSelector::NodePtr cssNode;
+ cssNode.ptr = node;
+ QVector<QCss::Declaration> decls = selector->declarationsForNode(cssNode);
+
+ QXmlStreamAttributes attributes;
+ parseCSStoXMLAttrs(decls, attributes);
+ parseStyle(node, attributes, handler);
+}
+
+static inline QStringList stringToList(const QString &str)
+{
+ QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts);
+ return lst;
+}
+
+static bool parseCoreNode(QSvgNode *node,
+ const QXmlStreamAttributes &attributes)
+{
+ QStringList features;
+ QStringList extensions;
+ QStringList languages;
+ QStringList formats;
+ QStringList fonts;
+ QString xmlClassStr;
+
+ for (int i = 0; i < attributes.count(); ++i) {
+ const QXmlStreamAttribute &attribute = attributes.at(i);
+ QStringRef name = attribute.qualifiedName();
+ if (name.isEmpty())
+ continue;
+ QStringRef value = attribute.value();
+ switch (name.at(0).unicode()) {
+ case 'c':
+ if (name == QLatin1String("class"))
+ xmlClassStr = value.toString();
+ break;
+ case 'r':
+ if (name == QLatin1String("requiredFeatures"))
+ features = stringToList(value.toString());
+ else if (name == QLatin1String("requiredExtensions"))
+ extensions = stringToList(value.toString());
+ else if (name == QLatin1String("requiredFormats"))
+ formats = stringToList(value.toString());
+ else if (name == QLatin1String("requiredFonts"))
+ fonts = stringToList(value.toString());
+ break;
+ case 's':
+ if (name == QLatin1String("systemLanguage"))
+ languages = stringToList(value.toString());
+ break;
+ default:
+ break;
+ }
+ }
+
+ node->setRequiredFeatures(features);
+ node->setRequiredExtensions(extensions);
+ node->setRequiredLanguages(languages);
+ node->setRequiredFormats(formats);
+ node->setRequiredFonts(fonts);
+ node->setNodeId(someId(attributes));
+ node->setXmlClass(xmlClassStr);
+
+ return true;
+}
+
+static void parseOpacity(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ if (attributes.opacity.isEmpty())
+ return;
+
+ const QString value = attributes.opacity.toString().trimmed();
+
+ bool ok = false;
+ qreal op = value.toDouble(&ok);
+
+ if (ok) {
+ QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
+ node->appendStyleProperty(opacity, attributes.id);
+ }
+}
+
+static QPainter::CompositionMode svgToQtCompositionMode(const QString &op)
+{
+#define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
+ if (op == QLatin1String("clear")) {
+ return QPainter::CompositionMode_Clear;
+ } else if (op == QLatin1String("src")) {
+ return QPainter::CompositionMode_Source;
+ } else if (op == QLatin1String("dst")) {
+ return QPainter::CompositionMode_Destination;
+ } else if (op == QLatin1String("src-over")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (op == QLatin1String("dst-over")) {
+ return QPainter::CompositionMode_DestinationOver;
+ } else if (op == QLatin1String("src-in")) {
+ return QPainter::CompositionMode_SourceIn;
+ } else if (op == QLatin1String("dst-in")) {
+ return QPainter::CompositionMode_DestinationIn;
+ } else if (op == QLatin1String("src-out")) {
+ return QPainter::CompositionMode_SourceOut;
+ } else if (op == QLatin1String("dst-out")) {
+ return QPainter::CompositionMode_DestinationOut;
+ } else if (op == QLatin1String("src-atop")) {
+ return QPainter::CompositionMode_SourceAtop;
+ } else if (op == QLatin1String("dst-atop")) {
+ return QPainter::CompositionMode_DestinationAtop;
+ } else if (op == QLatin1String("xor")) {
+ return QPainter::CompositionMode_Xor;
+ } else if (op == QLatin1String("plus")) {
+ return QPainter::CompositionMode_Plus;
+ } else if (op == QLatin1String("multiply")) {
+ return QPainter::CompositionMode_Multiply;
+ } else if (op == QLatin1String("screen")) {
+ return QPainter::CompositionMode_Screen;
+ } else if (op == QLatin1String("overlay")) {
+ return QPainter::CompositionMode_Overlay;
+ } else if (op == QLatin1String("darken")) {
+ return QPainter::CompositionMode_Darken;
+ } else if (op == QLatin1String("lighten")) {
+ return QPainter::CompositionMode_Lighten;
+ } else if (op == QLatin1String("color-dodge")) {
+ return QPainter::CompositionMode_ColorDodge;
+ } else if (op == QLatin1String("color-burn")) {
+ return QPainter::CompositionMode_ColorBurn;
+ } else if (op == QLatin1String("hard-light")) {
+ return QPainter::CompositionMode_HardLight;
+ } else if (op == QLatin1String("soft-light")) {
+ return QPainter::CompositionMode_SoftLight;
+ } else if (op == QLatin1String("difference")) {
+ return QPainter::CompositionMode_Difference;
+ } else if (op == QLatin1String("exclusion")) {
+ return QPainter::CompositionMode_Exclusion;
+ } else {
+ NOOP;
+ }
+
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static void parseCompOp(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ if (attributes.compOp.isEmpty())
+ return;
+ QString value = attributes.compOp.toString().trimmed();
+
+ if (!value.isEmpty()) {
+ QSvgCompOpStyle *compop = new QSvgCompOpStyle(svgToQtCompositionMode(value));
+ node->appendStyleProperty(compop, attributes.id);
+ }
+}
+
+static inline QSvgNode::DisplayMode displayStringToEnum(const QString &str)
+{
+ if (str == QLatin1String("inline")) {
+ return QSvgNode::InlineMode;
+ } else if (str == QLatin1String("block")) {
+ return QSvgNode::BlockMode;
+ } else if (str == QLatin1String("list-item")) {
+ return QSvgNode::ListItemMode;
+ } else if (str == QLatin1String("run-in")) {
+ return QSvgNode::RunInMode;
+ } else if (str == QLatin1String("compact")) {
+ return QSvgNode::CompactMode;
+ } else if (str == QLatin1String("marker")) {
+ return QSvgNode::MarkerMode;
+ } else if (str == QLatin1String("table")) {
+ return QSvgNode::TableMode;
+ } else if (str == QLatin1String("inline-table")) {
+ return QSvgNode::InlineTableMode;
+ } else if (str == QLatin1String("table-row")) {
+ return QSvgNode::TableRowGroupMode;
+ } else if (str == QLatin1String("table-header-group")) {
+ return QSvgNode::TableHeaderGroupMode;
+ } else if (str == QLatin1String("table-footer-group")) {
+ return QSvgNode::TableFooterGroupMode;
+ } else if (str == QLatin1String("table-row")) {
+ return QSvgNode::TableRowMode;
+ } else if (str == QLatin1String("table-column-group")) {
+ return QSvgNode::TableColumnGroupMode;
+ } else if (str == QLatin1String("table-column")) {
+ return QSvgNode::TableColumnMode;
+ } else if (str == QLatin1String("table-cell")) {
+ return QSvgNode::TableCellMode;
+ } else if (str == QLatin1String("table-caption")) {
+ return QSvgNode::TableCaptionMode;
+ } else if (str == QLatin1String("none")) {
+ return QSvgNode::NoneMode;
+ } else if (str == QT_INHERIT) {
+ return QSvgNode::InheritMode;
+ }
+ return QSvgNode::BlockMode;
+}
+
+static void parseOthers(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ if (attributes.display.isEmpty())
+ return;
+ QString displayStr = attributes.display.toString().trimmed();
+
+ if (!displayStr.isEmpty()) {
+ node->setDisplayMode(displayStringToEnum(displayStr));
+ }
+}
+
+static bool parseStyle(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ parseColor(node, attributes, handler);
+ parseBrush(node, attributes, handler);
+ parsePen(node, attributes, handler);
+ parseFont(node, attributes, handler);
+ parseTransform(node, attributes, handler);
+ parseVisibility(node, attributes, handler);
+ parseOpacity(node, attributes, handler);
+ parseCompOp(node, attributes, handler);
+ parseOthers(node, attributes, handler);
+#if 0
+ value = attributes.value("audio-level");
+
+ value = attributes.value("color-rendering");
+
+ value = attributes.value("display-align");
+
+ value = attributes.value("image-rendering");
+
+ value = attributes.value("line-increment");
+
+ value = attributes.value("pointer-events");
+
+ value = attributes.value("shape-rendering");
+
+ value = attributes.value("solid-color");
+
+ value = attributes.value("solid-opacity");
+
+ value = attributes.value("text-rendering");
+
+ value = attributes.value("vector-effect");
+
+ value = attributes.value("viewport-fill");
+
+ value = attributes.value("viewport-fill-opacity");
+#endif
+ return true;
+}
+
+static bool parseStyle(QSvgNode *node,
+ const QXmlStreamAttributes &attrs,
+ QSvgHandler *handler)
+{
+ return parseStyle(node, QSvgAttributes(attrs, handler), handler);
+}
+
+static bool parseAnchorNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseAnimateNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseAnimateColorNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString typeStr = attributes.value(QLatin1String("type")).toString();
+ QStringRef fromStr = attributes.value(QLatin1String("from"));
+ QStringRef toStr = attributes.value(QLatin1String("to"));
+ QString valuesStr = attributes.value(QLatin1String("values")).toString();
+ QString beginStr = attributes.value(QLatin1String("begin")).toString();
+ QString durStr = attributes.value(QLatin1String("dur")).toString();
+ QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
+ QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
+ QString fillStr = attributes.value(QLatin1String("fill")).toString();
+
+ QList<QColor> colors;
+ if (valuesStr.isEmpty()) {
+ QColor startColor, endColor;
+ resolveColor(fromStr, startColor, handler);
+ resolveColor(toStr, endColor, handler);
+ colors.append(startColor);
+ colors.append(endColor);
+ } else {
+ QStringList str = valuesStr.split(QLatin1Char(';'));
+ QStringList::const_iterator itr;
+ for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
+ QColor color;
+ QString str = *itr;
+ resolveColor(QStringRef(&str), color, handler);
+ colors.append(color);
+ }
+ }
+
+ int ms = 1000;
+ beginStr = beginStr.trimmed();
+ if (beginStr.endsWith(QLatin1String("ms"))) {
+ beginStr.chop(2);
+ ms = 1;
+ } else if (beginStr.endsWith(QLatin1String("s"))) {
+ beginStr.chop(1);
+ }
+ durStr = durStr.trimmed();
+ if (durStr.endsWith(QLatin1String("ms"))) {
+ durStr.chop(2);
+ ms = 1;
+ } else if (durStr.endsWith(QLatin1String("s"))) {
+ durStr.chop(1);
+ }
+ int begin = static_cast<int>(toDouble(beginStr) * ms);
+ int end = static_cast<int>((toDouble(durStr) + begin) * ms);
+
+ QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
+ anim->setArgs((targetStr == QLatin1String("fill")), colors);
+ anim->setFreeze(fillStr == QLatin1String("freeze"));
+ anim->setRepeatCount(
+ (repeatStr == QLatin1String("indefinite")) ? -1 :
+ (repeatStr == QLatin1String("")) ? 1 : toDouble(repeatStr));
+
+ parent->appendStyleProperty(anim, someId(attributes));
+ parent->document()->setAnimated(true);
+ handler->setAnimPeriod(begin, end);
+ return true;
+}
+
+static bool parseAimateMotionNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static void parseNumberTriplet(QVector<qreal> &values, const QChar *&s)
+{
+ QVector<qreal> list = parseNumbersList(s);
+ values << list;
+ for (int i = 3 - list.size(); i > 0; --i)
+ values.append(0.0);
+}
+
+static bool parseAnimateTransformNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString typeStr = attributes.value(QLatin1String("type")).toString();
+ QString values = attributes.value(QLatin1String("values")).toString();
+ QString beginStr = attributes.value(QLatin1String("begin")).toString();
+ QString durStr = attributes.value(QLatin1String("dur")).toString();
+ QString targetStr = attributes.value(QLatin1String("attributeName")).toString();
+ QString repeatStr = attributes.value(QLatin1String("repeatCount")).toString();
+ QString fillStr = attributes.value(QLatin1String("fill")).toString();
+ QString fromStr = attributes.value(QLatin1String("from")).toString();
+ QString toStr = attributes.value(QLatin1String("to")).toString();
+ QString byStr = attributes.value(QLatin1String("by")).toString();
+ QString addtv = attributes.value(QLatin1String("additive")).toString();
+
+ QSvgAnimateTransform::Additive additive = QSvgAnimateTransform::Replace;
+ if (addtv == QLatin1String("sum"))
+ additive = QSvgAnimateTransform::Sum;
+
+ QVector<qreal> vals;
+ if (values.isEmpty()) {
+ const QChar *s;
+ if (fromStr.isEmpty()) {
+ if (!byStr.isEmpty()) {
+ // By-animation.
+ additive = QSvgAnimateTransform::Sum;
+ vals.append(0.0);
+ vals.append(0.0);
+ vals.append(0.0);
+ parseNumberTriplet(vals, s = byStr.constData());
+ } else {
+ // To-animation not defined.
+ return false;
+ }
+ } else {
+ if (!toStr.isEmpty()) {
+ // From-to-animation.
+ parseNumberTriplet(vals, s = fromStr.constData());
+ parseNumberTriplet(vals, s = toStr.constData());
+ } else if (!byStr.isEmpty()) {
+ // From-by-animation.
+ parseNumberTriplet(vals, s = fromStr.constData());
+ parseNumberTriplet(vals, s = byStr.constData());
+ for (int i = vals.size() - 3; i < vals.size(); ++i)
+ vals[i] += vals[i - 3];
+ } else {
+ return false;
+ }
+ }
+ } else {
+ const QChar *s = values.constData();
+ while (s && *s != QLatin1Char(0)) {
+ parseNumberTriplet(vals, s);
+ if (*s == QLatin1Char(0))
+ break;
+ ++s;
+ }
+ }
+
+ int ms = 1000;
+ beginStr = beginStr.trimmed();
+ if (beginStr.endsWith(QLatin1String("ms"))) {
+ beginStr.chop(2);
+ ms = 1;
+ } else if (beginStr.endsWith(QLatin1String("s"))) {
+ beginStr.chop(1);
+ }
+ int begin = static_cast<int>(toDouble(beginStr) * ms);
+ durStr = durStr.trimmed();
+ if (durStr.endsWith(QLatin1String("ms"))) {
+ durStr.chop(2);
+ ms = 1;
+ } else if (durStr.endsWith(QLatin1String("s"))) {
+ durStr.chop(1);
+ ms = 1000;
+ }
+ int end = static_cast<int>(toDouble(durStr)*ms) + begin;
+
+ QSvgAnimateTransform::TransformType type = QSvgAnimateTransform::Empty;
+ if (typeStr == QLatin1String("translate")) {
+ type = QSvgAnimateTransform::Translate;
+ } else if (typeStr == QLatin1String("scale")) {
+ type = QSvgAnimateTransform::Scale;
+ } else if (typeStr == QLatin1String("rotate")) {
+ type = QSvgAnimateTransform::Rotate;
+ } else if (typeStr == QLatin1String("skewX")) {
+ type = QSvgAnimateTransform::SkewX;
+ } else if (typeStr == QLatin1String("skewY")) {
+ type = QSvgAnimateTransform::SkewY;
+ } else {
+ return false;
+ }
+
+ QSvgAnimateTransform *anim = new QSvgAnimateTransform(begin, end, 0);
+ anim->setArgs(type, additive, vals);
+ anim->setFreeze(fillStr == QLatin1String("freeze"));
+ anim->setRepeatCount(
+ (repeatStr == QLatin1String("indefinite"))? -1 :
+ (repeatStr == QLatin1String(""))? 1 : toDouble(repeatStr));
+
+ parent->appendStyleProperty(anim, someId(attributes));
+ parent->document()->setAnimated(true);
+ handler->setAnimPeriod(begin, end);
+ return true;
+}
+
+static QSvgNode * createAnimationNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return 0;
+}
+
+static bool parseAudioNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createCircleNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString cx = attributes.value(QLatin1String("cx")).toString();
+ QString cy = attributes.value(QLatin1String("cy")).toString();
+ QString r = attributes.value(QLatin1String("r")).toString();
+ qreal ncx = toDouble(cx);
+ qreal ncy = toDouble(cy);
+ qreal nr = toDouble(r);
+
+ QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
+ QSvgNode *circle = new QSvgCircle(parent, rect);
+ return circle;
+}
+
+static QSvgNode *createDefsNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(attributes);
+ QSvgDefs *defs = new QSvgDefs(parent);
+ return defs;
+}
+
+static bool parseDescNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseDiscardNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createEllipseNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString cx = attributes.value(QLatin1String("cx")).toString();
+ QString cy = attributes.value(QLatin1String("cy")).toString();
+ QString rx = attributes.value(QLatin1String("rx")).toString();
+ QString ry = attributes.value(QLatin1String("ry")).toString();
+ qreal ncx = toDouble(cx);
+ qreal ncy = toDouble(cy);
+ qreal nrx = toDouble(rx);
+ qreal nry = toDouble(ry);
+
+ QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
+ QSvgNode *ellipse = new QSvgEllipse(parent, rect);
+ return ellipse;
+}
+
+static QSvgStyleProperty *createFontNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString hax = attributes.value(QLatin1String("horiz-adv-x")).toString();
+ QString myId = someId(attributes);
+
+ qreal horizAdvX = toDouble(hax);
+
+ while (parent && parent->type() != QSvgNode::DOC) {
+ parent = parent->parent();
+ }
+
+ if (parent) {
+ QSvgTinyDocument *doc = static_cast<QSvgTinyDocument*>(parent);
+ QSvgFont *font = new QSvgFont(horizAdvX);
+ font->setFamilyName(myId);
+ if (!font->familyName().isEmpty()) {
+ if (!doc->svgFont(font->familyName()))
+ doc->addSvgFont(font);
+ }
+ return new QSvgFontStyle(font, doc);
+ }
+ return 0;
+}
+
+static bool parseFontFaceNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ if (parent->type() != QSvgStyleProperty::FONT) {
+ return false;
+ }
+
+ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
+ QSvgFont *font = style->svgFont();
+ QString name = attributes.value(QLatin1String("font-family")).toString();
+ QString unitsPerEmStr = attributes.value(QLatin1String("units-per-em")).toString();
+
+ qreal unitsPerEm = toDouble(unitsPerEmStr);
+ if (!unitsPerEm)
+ unitsPerEm = 1000;
+
+ if (!name.isEmpty())
+ font->setFamilyName(name);
+ font->setUnitsPerEm(unitsPerEm);
+
+ if (!font->familyName().isEmpty())
+ if (!style->doc()->svgFont(font->familyName()))
+ style->doc()->addSvgFont(font);
+
+ return true;
+}
+
+static bool parseFontFaceNameNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ if (parent->type() != QSvgStyleProperty::FONT) {
+ return false;
+ }
+
+ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
+ QSvgFont *font = style->svgFont();
+ QString name = attributes.value(QLatin1String("name")).toString();
+
+ if (!name.isEmpty())
+ font->setFamilyName(name);
+
+ if (!font->familyName().isEmpty())
+ if (!style->doc()->svgFont(font->familyName()))
+ style->doc()->addSvgFont(font);
+
+ return true;
+}
+
+static bool parseFontFaceSrcNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseFontFaceUriNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseForeignObjectNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createGNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(attributes);
+ QSvgG *node = new QSvgG(parent);
+ return node;
+}
+
+static bool parseGlyphNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ if (parent->type() != QSvgStyleProperty::FONT) {
+ return false;
+ }
+
+ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
+ QSvgFont *font = style->svgFont();
+ createSvgGlyph(font, attributes);
+ return true;
+}
+
+static bool parseHandlerNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseHkernNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createImageNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString x = attributes.value(QLatin1String("x")).toString();
+ QString y = attributes.value(QLatin1String("y")).toString();
+ QString width = attributes.value(QLatin1String("width")).toString();
+ QString height = attributes.value(QLatin1String("height")).toString();
+ QString filename = attributes.value(QLatin1String("xlink:href")).toString();
+ qreal nx = toDouble(x);
+ qreal ny = toDouble(y);
+ QSvgHandler::LengthType type;
+ qreal nwidth = parseLength(width, type, handler);
+ nwidth = convertToPixels(nwidth, true, type);
+
+ qreal nheight = parseLength(height, type, handler);
+ nheight = convertToPixels(nheight, false, type);
+
+
+ filename = filename.trimmed();
+ QImage image;
+ if (filename.startsWith(QLatin1String("data"))) {
+ int idx = filename.lastIndexOf(QLatin1String("base64,"));
+ if (idx != -1) {
+ idx += 7;
+ QString dataStr = filename.mid(idx);
+ QByteArray data = QByteArray::fromBase64(dataStr.toAscii());
+ image = QImage::fromData(data);
+ } else {
+ qDebug()<<"QSvgHandler::createImageNode: Unrecognized inline image format!";
+ }
+ } else
+ image = QImage(filename);
+
+ if (image.isNull()) {
+ qDebug()<<"couldn't create image from "<<filename;
+ return 0;
+ }
+
+ if (image.format() == QImage::Format_ARGB32)
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
+ QSvgNode *img = new QSvgImage(parent,
+ image,
+ QRect(int(nx),
+ int(ny),
+ int(nwidth),
+ int(nheight)));
+ return img;
+}
+
+static QSvgNode *createLineNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString x1 = attributes.value(QLatin1String("x1")).toString();
+ QString y1 = attributes.value(QLatin1String("y1")).toString();
+ QString x2 = attributes.value(QLatin1String("x2")).toString();
+ QString y2 = attributes.value(QLatin1String("y2")).toString();
+ qreal nx1 = toDouble(x1);
+ qreal ny1 = toDouble(y1);
+ qreal nx2 = toDouble(x2);
+ qreal ny2 = toDouble(y2);
+
+ QLineF lineBounds(nx1, ny1, nx2, ny2);
+ QSvgNode *line = new QSvgLine(parent, lineBounds);
+ return line;
+}
+
+
+static void parseBaseGradient(QSvgNode *node,
+ const QXmlStreamAttributes &attributes,
+ QSvgGradientStyle *gradProp,
+ QSvgHandler *handler)
+{
+ QString link = attributes.value(QLatin1String("xlink:href")).toString();
+ QStringRef trans = attributes.value(QLatin1String("gradientTransform"));
+ QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
+ QString units = attributes.value(QLatin1String("gradientUnits")).toString();
+ QStringRef colorStr = attributes.value(QLatin1String("color"));
+ QStringRef colorOpacityStr = attributes.value(QLatin1String("color-opacity"));
+
+ QColor color;
+ if (constructColor(colorStr, colorOpacityStr, color, handler)) {
+ handler->popColor();
+ handler->pushColor(color);
+ }
+
+ QMatrix matrix;
+ QGradient *grad = gradProp->qgradient();
+ if (!link.isEmpty()) {
+ QSvgStyleProperty *prop = node->styleProperty(link);
+ //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
+ if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
+ QSvgGradientStyle *inherited =
+ static_cast<QSvgGradientStyle*>(prop);
+ if (!inherited->stopLink().isEmpty()) {
+ gradProp->setStopLink(inherited->stopLink(), handler->document());
+ } else {
+ grad->setStops(inherited->qgradient()->stops());
+ gradProp->setGradientStopsSet(inherited->gradientStopsSet());
+ }
+
+ matrix = inherited->qmatrix();
+ } else {
+ gradProp->setStopLink(link, handler->document());
+ }
+ }
+
+ if (!trans.isEmpty()) {
+ matrix = parseTransformationMatrix(trans);
+ gradProp->setMatrix(matrix);
+ } else if (!matrix.isIdentity()) {
+ gradProp->setMatrix(matrix);
+ }
+
+ if (!spread.isEmpty()) {
+ if (spread == QLatin1String("pad")) {
+ grad->setSpread(QGradient::PadSpread);
+ } else if (spread == QLatin1String("reflect")) {
+ grad->setSpread(QGradient::ReflectSpread);
+ } else if (spread == QLatin1String("repeat")) {
+ grad->setSpread(QGradient::RepeatSpread);
+ }
+ }
+
+ if (units.isEmpty() || units == QLatin1String("objectBoundingBox")) {
+ grad->setCoordinateMode(QGradient::ObjectBoundingMode);
+ }
+}
+
+static QSvgStyleProperty *createLinearGradientNode(QSvgNode *node,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString x1 = attributes.value(QLatin1String("x1")).toString();
+ QString y1 = attributes.value(QLatin1String("y1")).toString();
+ QString x2 = attributes.value(QLatin1String("x2")).toString();
+ QString y2 = attributes.value(QLatin1String("y2")).toString();
+
+ qreal nx1 = 0.0;
+ qreal ny1 = 0.0;
+ qreal nx2 = 1.0;
+ qreal ny2 = 0.0;
+
+ if (!x1.isEmpty())
+ nx1 = convertToNumber(x1, handler);
+ if (!y1.isEmpty())
+ ny1 = convertToNumber(y1, handler);
+ if (!x2.isEmpty())
+ nx2 = convertToNumber(x2, handler);
+ if (!y2.isEmpty())
+ ny2 = convertToNumber(y2, handler);
+
+ QSvgNode *itr = node;
+ while (itr && itr->type() != QSvgNode::DOC) {
+ itr = itr->parent();
+ }
+
+ QLinearGradient *grad = new QLinearGradient(nx1, ny1, nx2, ny2);
+ grad->setInterpolationMode(QGradient::ComponentInterpolation);
+ QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
+ parseBaseGradient(node, attributes, prop, handler);
+
+ return prop;
+}
+
+static bool parseMetadataNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseMissingGlyphNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ if (parent->type() != QSvgStyleProperty::FONT) {
+ return false;
+ }
+
+ QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
+ QSvgFont *font = style->svgFont();
+ createSvgGlyph(font, attributes);
+ return true;
+}
+
+static bool parseMpathNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createPathNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QStringRef data = attributes.value(QLatin1String("d"));
+
+ QPainterPath qpath;
+ qpath.setFillRule(Qt::WindingFill);
+ //XXX do error handling
+ parsePathDataFast(data, qpath);
+
+ QSvgNode *path = new QSvgPath(parent, qpath);
+ return path;
+}
+
+static QSvgNode *createPolygonNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString pointsStr = attributes.value(QLatin1String("points")).toString();
+
+ //same QPolygon parsing is in createPolylineNode
+ const QChar *s = pointsStr.constData();
+ QVector<qreal> points = parseNumbersList(s);
+ QPolygonF poly(points.count()/2);
+ for (int i = 0; i < poly.size(); ++i)
+ poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
+ QSvgNode *polygon = new QSvgPolygon(parent, poly);
+ return polygon;
+}
+
+static QSvgNode *createPolylineNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ QString pointsStr = attributes.value(QLatin1String("points")).toString();
+
+ //same QPolygon parsing is in createPolygonNode
+ const QChar *s = pointsStr.constData();
+ QVector<qreal> points = parseNumbersList(s);
+ QPolygonF poly(points.count()/2);
+ for (int i = 0; i < poly.size(); ++i)
+ poly[i] = QPointF(points.at(2 * i), points.at(2 * i + 1));
+
+ QSvgNode *line = new QSvgPolyline(parent, poly);
+ return line;
+}
+
+static bool parsePrefetchNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString cx = attributes.value(QLatin1String("cx")).toString();
+ QString cy = attributes.value(QLatin1String("cy")).toString();
+ QString r = attributes.value(QLatin1String("r")).toString();
+ QString fx = attributes.value(QLatin1String("fx")).toString();
+ QString fy = attributes.value(QLatin1String("fy")).toString();
+
+ qreal ncx = 0.5;
+ qreal ncy = 0.5;
+ qreal nr = 0.5;
+ if (!cx.isEmpty())
+ ncx = toDouble(cx);
+ if (!cy.isEmpty())
+ ncy = toDouble(cy);
+ if (!r.isEmpty())
+ nr = toDouble(r);
+
+ qreal nfx = ncx;
+ if (!fx.isEmpty())
+ nfx = toDouble(fx);
+ qreal nfy = ncy;
+ if (!fy.isEmpty())
+ nfy = toDouble(fy);
+
+ QRadialGradient *grad = new QRadialGradient(ncx, ncy, nr, nfx, nfy);
+ grad->setInterpolationMode(QGradient::ComponentInterpolation);
+
+ QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
+ parseBaseGradient(node, attributes, prop, handler);
+
+ return prop;
+}
+
+static QSvgNode *createRectNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString x = attributes.value(QLatin1String("x")).toString();
+ QString y = attributes.value(QLatin1String("y")).toString();
+ QString width = attributes.value(QLatin1String("width")).toString();
+ QString height = attributes.value(QLatin1String("height")).toString();
+ QString rx = attributes.value(QLatin1String("rx")).toString();
+ QString ry = attributes.value(QLatin1String("ry")).toString();
+
+ QSvgHandler::LengthType type;
+ qreal nwidth = parseLength(width, type, handler);
+ nwidth = convertToPixels(nwidth, true, type);
+
+ qreal nheight = parseLength(height, type, handler);
+ nheight = convertToPixels(nheight, true, type);
+ qreal nrx = toDouble(rx);
+ qreal nry = toDouble(ry);
+
+ QRectF bounds(toDouble(x), toDouble(y),
+ nwidth, nheight);
+
+ //9.2 The 'rect' element clearly specifies it
+ // but the case might in fact be handled because
+ // we draw rounded rectangles differently
+ if (nrx > bounds.width()/2)
+ nrx = bounds.width()/2;
+ if (nry > bounds.height()/2)
+ nry = bounds.height()/2;
+
+ if (!rx.isEmpty() && ry.isEmpty())
+ nry = nrx;
+ else if (!ry.isEmpty() && rx.isEmpty())
+ nrx = nry;
+
+ //we draw rounded rect from 0...99
+ //svg from 0...bounds.width()/2 so we're adjusting the
+ //coordinates
+ nrx *= (100/(bounds.width()/2));
+ nry *= (100/(bounds.height()/2));
+
+ QSvgNode *rect = new QSvgRect(parent, bounds,
+ int(nrx),
+ int(nry));
+ return rect;
+}
+
+static bool parseScriptNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseSetNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgStyleProperty *createSolidColorNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ QStringRef solidColorStr = attributes.value(QLatin1String("solid-color"));
+ QStringRef solidOpacityStr = attributes.value(QLatin1String("solid-opacity"));
+
+ if (solidOpacityStr.isEmpty())
+ solidOpacityStr = attributes.value(QLatin1String("opacity"));
+
+ QColor color;
+ if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
+ return 0;
+ QSvgSolidColorStyle *style = new QSvgSolidColorStyle(color);
+ return style;
+}
+
+static bool parseStopNode(QSvgStyleProperty *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ if (parent->type() != QSvgStyleProperty::GRADIENT)
+ return false;
+ QString nodeIdStr = someId(attributes);
+ QString xmlClassStr = attributes.value(QLatin1String("class")).toString();
+
+ //### nasty hack because stop gradients are not in the rendering tree
+ // we force a dummy node with the same id and class into a rendering
+ // tree to figure out whether the selector has a style for it
+ // QSvgStyleSelector should be coded in a way that could avoid it
+ QSvgAnimation anim;
+ anim.setNodeId(nodeIdStr);
+ anim.setXmlClass(xmlClassStr);
+
+ QCss::StyleSelector::NodePtr cssNode;
+ cssNode.ptr = &anim;
+ QVector<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
+
+ QXmlStreamAttributes xmlAttr = attributes;
+ for (int i = 0; i < decls.count(); ++i) {
+ const QCss::Declaration &decl = decls.at(i);
+
+ if (decl.d->property.isEmpty())
+ continue;
+ if (decl.d->values.count() != 1)
+ continue;
+ QCss::Value val = decl.d->values.first();
+ QString valueStr = val.toString();
+ if (val.type == QCss::Value::Uri) {
+ valueStr.prepend(QLatin1String("url("));
+ valueStr.append(QLatin1Char(')'));
+ }
+ xmlAttr.append(QString(), decl.d->property, valueStr);
+ }
+ QSvgAttributes attrs(xmlAttr, handler);
+
+ QSvgGradientStyle *style =
+ static_cast<QSvgGradientStyle*>(parent);
+ QString offsetStr = attrs.offset.toString();
+ QStringRef colorStr = attrs.stopColor;
+ QColor color;
+
+ bool ok = true;
+ qreal offset = convertToNumber(offsetStr, handler, &ok);
+ if (!ok)
+ offset = 0.0;
+ QString black = QString::fromLatin1("#000000");
+ if (colorStr.isEmpty()) {
+ colorStr = QStringRef(&black);
+ }
+
+ constructColor(colorStr, attrs.stopOpacity, color, handler);
+
+ QGradient *grad = style->qgradient();
+
+ offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
+ QGradientStops stops;
+ if (style->gradientStopsSet()) {
+ stops = grad->stops();
+ // If the stop offset equals the one previously added, add an epsilon to make it greater.
+ if (offset <= stops.back().first)
+ offset = stops.back().first + FLT_EPSILON;
+ }
+
+ // If offset is greater than one, it must be clamped to one.
+ if (offset > 1.0) {
+ if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
+ stops.back().first = 1.0 - FLT_EPSILON;
+ grad->setStops(stops);
+ }
+ offset = 1.0;
+ }
+
+ grad->setColorAt(offset, color);
+ style->setGradientStopsSet(true);
+ return true;
+}
+
+static bool parseStyleNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ Q_UNUSED(parent);
+ QString type = attributes.value(QLatin1String("type")).toString();
+ type = type.toLower();
+
+ if (type == QLatin1String("text/css")) {
+ handler->setInStyle(true);
+ }
+
+ return true;
+}
+
+static QSvgNode *createSvgNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+
+ QString baseProfile = attributes.value(QLatin1String("baseProfile")).toString();
+#if 0
+ if (baseProfile.isEmpty() && baseProfile != QLatin1String("tiny")) {
+ qWarning("Profile is %s while we only support tiny!",
+ qPrintable(baseProfile));
+ }
+#endif
+
+ QSvgTinyDocument *node = new QSvgTinyDocument();
+ QString widthStr = attributes.value(QLatin1String("width")).toString();
+ QString heightStr = attributes.value(QLatin1String("height")).toString();
+ QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
+
+ QSvgHandler::LengthType type = QSvgHandler::LT_PX; // FIXME: is the default correct?
+ qreal width = 0;
+ if (!widthStr.isEmpty()) {
+ width = parseLength(widthStr, type, handler);
+ if (type != QSvgHandler::LT_PT)
+ width = convertToPixels(width, true, type);
+ node->setWidth(int(width), type == QSvgHandler::LT_PERCENT);
+ }
+ qreal height = 0;
+ if (!heightStr.isEmpty()) {
+ height = parseLength(heightStr, type, handler);
+ if (type != QSvgHandler::LT_PT)
+ height = convertToPixels(height, false, type);
+ node->setHeight(int(height), type == QSvgHandler::LT_PERCENT);
+ }
+
+ QStringList viewBoxValues;
+ if (!viewBoxStr.isEmpty()) {
+ viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
+ viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
+ viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
+ viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
+ viewBoxValues = viewBoxStr.split(QLatin1Char(','), QString::SkipEmptyParts);
+ }
+ if (viewBoxValues.count() == 4) {
+ QString xStr = viewBoxValues.at(0).trimmed();
+ QString yStr = viewBoxValues.at(1).trimmed();
+ QString widthStr = viewBoxValues.at(2).trimmed();
+ QString heightStr = viewBoxValues.at(3).trimmed();
+
+ QSvgHandler::LengthType lt;
+ qreal x = parseLength(xStr, lt, handler);
+ qreal y = parseLength(yStr, lt, handler);
+ qreal w = parseLength(widthStr, lt, handler);
+ qreal h = parseLength(heightStr, lt, handler);
+
+ node->setViewBox(QRectF(x, y, w, h));
+
+ } else if (width && height) {
+ if (type == QSvgHandler::LT_PT) {
+ width = convertToPixels(width, false, type);
+ height = convertToPixels(height, false, type);
+ }
+ node->setViewBox(QRectF(0, 0, width, height));
+ }
+ handler->setDefaultCoordinateSystem(QSvgHandler::LT_PX);
+
+ return node;
+}
+
+static QSvgNode *createSwitchNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(attributes);
+ QSvgSwitch *node = new QSvgSwitch(parent);
+ return node;
+}
+
+static bool parseTbreakNode(QSvgNode *parent,
+ const QXmlStreamAttributes &,
+ QSvgHandler *)
+{
+ if (parent->type() != QSvgNode::TEXTAREA)
+ return false;
+ static_cast<QSvgText*>(parent)->addLineBreak();
+ return true;
+}
+
+static QSvgNode *createTextNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString x = attributes.value(QLatin1String("x")).toString();
+ QString y = attributes.value(QLatin1String("y")).toString();
+ //### editable and rotate not handled
+ QSvgHandler::LengthType type;
+ qreal nx = parseLength(x, type, handler);
+ qreal ny = parseLength(y, type, handler);
+
+ QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
+ return text;
+}
+
+static QSvgNode *createTextAreaNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
+ if (node) {
+ QSvgHandler::LengthType type;
+ qreal width = parseLength(attributes.value(QLatin1String("width")).toString(), type, handler);
+ qreal height = parseLength(attributes.value(QLatin1String("height")).toString(), type, handler);
+ node->setTextArea(QSizeF(width, height));
+ }
+ return node;
+}
+
+static QSvgNode *createTspanNode(QSvgNode *parent,
+ const QXmlStreamAttributes &,
+ QSvgHandler *)
+{
+ return new QSvgTspan(parent);
+}
+
+static bool parseTitleNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static QSvgNode *createUseNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
+ QString xStr = attributes.value(QLatin1String("x")).toString();
+ QString yStr = attributes.value(QLatin1String("y")).toString();
+ QSvgStructureNode *group = 0;
+
+ if (linkId.isEmpty())
+ linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
+ switch (parent->type()) {
+ case QSvgNode::DOC:
+ case QSvgNode::DEFS:
+ case QSvgNode::G:
+ case QSvgNode::SWITCH:
+ group = static_cast<QSvgStructureNode*>(parent);
+ break;
+ default:
+ break;
+ }
+
+ if (group) {
+ QSvgNode *link = group->scopeNode(linkId);
+ if (link) {
+ QPointF pt;
+ if (!xStr.isNull() || !yStr.isNull()) {
+ QSvgHandler::LengthType type;
+ qreal nx = parseLength(xStr, type, handler);
+ nx = convertToPixels(nx, true, type);
+
+ qreal ny = parseLength(yStr, type, handler);
+ ny = convertToPixels(ny, true, type);
+ pt = QPointF(nx, ny);
+ }
+
+ //delay link resolving till the first draw call on
+ //use nodes, link 2might have not been created yet
+ QSvgUse *node = new QSvgUse(pt, parent, link);
+ return node;
+ }
+ }
+
+ qWarning("link %s hasn't been detected!", qPrintable(linkId));
+ return 0;
+}
+
+static QSvgNode *createVideoNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return 0;
+}
+
+typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
+
+static FactoryMethod findGroupFactory(const QString &name)
+{
+ if (name.isEmpty())
+ return 0;
+
+ QStringRef ref(&name, 1, name.length() - 1);
+ switch (name.at(0).unicode()) {
+ case 'd':
+ if (ref == QLatin1String("efs")) return createDefsNode;
+ break;
+ case 'g':
+ if (ref.isEmpty()) return createGNode;
+ break;
+ case 's':
+ if (ref == QLatin1String("vg")) return createSvgNode;
+ if (ref == QLatin1String("witch")) return createSwitchNode;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static FactoryMethod findGraphicsFactory(const QString &name)
+{
+ if (name.isEmpty())
+ return 0;
+
+ QStringRef ref(&name, 1, name.length() - 1);
+ switch (name.at(0).unicode()) {
+ case 'a':
+ if (ref == QLatin1String("nimation")) return createAnimationNode;
+ break;
+ case 'c':
+ if (ref == QLatin1String("ircle")) return createCircleNode;
+ break;
+ case 'e':
+ if (ref == QLatin1String("llipse")) return createEllipseNode;
+ break;
+ case 'i':
+ if (ref == QLatin1String("mage")) return createImageNode;
+ break;
+ case 'l':
+ if (ref == QLatin1String("ine")) return createLineNode;
+ break;
+ case 'p':
+ if (ref == QLatin1String("ath")) return createPathNode;
+ if (ref == QLatin1String("olygon")) return createPolygonNode;
+ if (ref == QLatin1String("olyline")) return createPolylineNode;
+ break;
+ case 'r':
+ if (ref == QLatin1String("ect")) return createRectNode;
+ break;
+ case 't':
+ if (ref == QLatin1String("ext")) return createTextNode;
+ if (ref == QLatin1String("extArea")) return createTextAreaNode;
+ if (ref == QLatin1String("span")) return createTspanNode;
+ break;
+ case 'u':
+ if (ref == QLatin1String("se")) return createUseNode;
+ break;
+ case 'v':
+ if (ref == QLatin1String("ideo")) return createVideoNode;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+typedef bool (*ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
+
+static ParseMethod findUtilFactory(const QString &name)
+{
+ if (name.isEmpty())
+ return 0;
+
+ QStringRef ref(&name, 1, name.length() - 1);
+ switch (name.at(0).unicode()) {
+ case 'a':
+ if (ref.isEmpty()) return parseAnchorNode;
+ if (ref == QLatin1String("nimate")) return parseAnimateNode;
+ if (ref == QLatin1String("nimateColor")) return parseAnimateColorNode;
+ if (ref == QLatin1String("nimateMotion")) return parseAimateMotionNode;
+ if (ref == QLatin1String("nimateTransform")) return parseAnimateTransformNode;
+ if (ref == QLatin1String("udio")) return parseAudioNode;
+ break;
+ case 'd':
+ if (ref == QLatin1String("esc")) return parseDescNode;
+ if (ref == QLatin1String("iscard")) return parseDiscardNode;
+ break;
+ case 'f':
+ if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
+ break;
+ case 'h':
+ if (ref == QLatin1String("andler")) return parseHandlerNode;
+ if (ref == QLatin1String("kern")) return parseHkernNode;
+ break;
+ case 'm':
+ if (ref == QLatin1String("etadata")) return parseMetadataNode;
+ if (ref == QLatin1String("path")) return parseMpathNode;
+ break;
+ case 'p':
+ if (ref == QLatin1String("refetch")) return parsePrefetchNode;
+ break;
+ case 's':
+ if (ref == QLatin1String("cript")) return parseScriptNode;
+ if (ref == QLatin1String("et")) return parseSetNode;
+ if (ref == QLatin1String("tyle")) return parseStyleNode;
+ break;
+ case 't':
+ if (ref == QLatin1String("break")) return parseTbreakNode;
+ if (ref == QLatin1String("itle")) return parseTitleNode;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
+ const QXmlStreamAttributes &,
+ QSvgHandler *);
+
+static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
+{
+ if (name.isEmpty())
+ return 0;
+
+ QStringRef ref(&name, 1, name.length() - 1);
+ switch (name.at(0).unicode()) {
+ case 'f':
+ if (ref == QLatin1String("ont")) return createFontNode;
+ break;
+ case 'l':
+ if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
+ break;
+ case 'r':
+ if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
+ break;
+ case 's':
+ if (ref == QLatin1String("olidColor")) return createSolidColorNode;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+typedef bool (*StyleParseMethod)(QSvgStyleProperty *,
+ const QXmlStreamAttributes &,
+ QSvgHandler *);
+
+static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
+{
+ if (name.isEmpty())
+ return 0;
+
+ QStringRef ref(&name, 1, name.length() - 1);
+ switch (name.at(0).unicode()) {
+ case 'f':
+ if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
+ if (ref == QLatin1String("ont-face-name")) return parseFontFaceNameNode;
+ if (ref == QLatin1String("ont-face-src")) return parseFontFaceSrcNode;
+ if (ref == QLatin1String("ont-face-uri")) return parseFontFaceUriNode;
+ break;
+ case 'g':
+ if (ref == QLatin1String("lyph")) return parseGlyphNode;
+ break;
+ case 'm':
+ if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
+ break;
+ case 's':
+ if (ref == QLatin1String("top")) return parseStopNode;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+QSvgHandler::QSvgHandler(QIODevice *device) : xml(new QXmlStreamReader(device))
+ , m_ownsReader(true)
+{
+ init();
+}
+
+QSvgHandler::QSvgHandler(const QByteArray &data) : xml(new QXmlStreamReader(data))
+ , m_ownsReader(true)
+{
+ init();
+}
+
+QSvgHandler::QSvgHandler(QXmlStreamReader *const reader) : xml(reader)
+ , m_ownsReader(false)
+{
+ init();
+}
+
+void QSvgHandler::init()
+{
+ m_doc = 0;
+ m_style = 0;
+ m_animEnd = 0;
+ m_defaultCoords = LT_PX;
+ m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
+ m_defaultPen.setMiterLimit(4);
+ parse();
+}
+
+void QSvgHandler::parse()
+{
+ xml->setNamespaceProcessing(false);
+ m_selector = new QSvgStyleSelector;
+ m_inStyle = false;
+ bool done = false;
+ while (!xml->atEnd() && !done) {
+ switch (xml->readNext()) {
+ case QXmlStreamReader::StartElement:
+ // he we could/should verify the namespaces, and simply
+ // call m_skipNodes(Unknown) if we don't know the
+ // namespace. We do support http://www.w3.org/2000/svg
+ // but also http://www.w3.org/2000/svg-20000303-stylable
+ // And if the document uses an external dtd, the reported
+ // namespaceUri is empty. The only possible strategy at
+ // this point is to do what everyone else seems to do and
+ // ignore the reported namespaceUri completely.
+ if (!startElement(xml->name().toString(), xml->attributes())) {
+ delete m_doc;
+ m_doc = 0;
+ return;
+ }
+ break;
+ case QXmlStreamReader::EndElement:
+ endElement(xml->name());
+ // if we are using somebody else's qxmlstreamreader
+ // we should not read until the end of the stream
+ done = !m_ownsReader && (xml->name() == QLatin1String("svg"));
+ break;
+ case QXmlStreamReader::Characters:
+ characters(xml->text());
+ break;
+ case QXmlStreamReader::ProcessingInstruction:
+ processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
+ break;
+ default:
+ break;
+ }
+ }
+ resolveGradients(m_doc);
+}
+
+bool QSvgHandler::startElement(const QString &localName,
+ const QXmlStreamAttributes &attributes)
+{
+ QSvgNode *node = 0;
+
+ pushColorCopy();
+
+ /* The xml:space attribute may appear on any element. We do
+ * a lookup by the qualified name here, but this is namespace aware, since
+ * the XML namespace can only be bound to prefix "xml." */
+ const QStringRef xmlSpace(attributes.value(QLatin1String("xml:space")));
+ if (xmlSpace.isNull()) {
+ // This element has no xml:space attribute.
+ m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
+ } else if (xmlSpace == QLatin1String("preserve")) {
+ m_whitespaceMode.push(QSvgText::Preserve);
+ } else if (xmlSpace == QLatin1String("default")) {
+ m_whitespaceMode.push(QSvgText::Default);
+ } else {
+ qWarning() << QString::fromLatin1("\"%1\" is an invalid value for attribute xml:space. "
+ "Valid values are \"preserve\" and \"default\".").arg(xmlSpace.toString());
+ m_whitespaceMode.push(QSvgText::Default);
+ }
+
+ if (!m_doc && localName != QLatin1String("svg"))
+ return false;
+
+ if (FactoryMethod method = findGroupFactory(localName)) {
+ //group
+ node = method(m_doc ? m_nodes.top() : 0, attributes, this);
+ Q_ASSERT(node);
+ if (!m_doc) {
+ Q_ASSERT(node->type() == QSvgNode::DOC);
+ m_doc = static_cast<QSvgTinyDocument*>(node);
+ } else {
+ switch (m_nodes.top()->type()) {
+ case QSvgNode::DOC:
+ case QSvgNode::G:
+ case QSvgNode::DEFS:
+ case QSvgNode::SWITCH:
+ {
+ QSvgStructureNode *group =
+ static_cast<QSvgStructureNode*>(m_nodes.top());
+ group->addChild(node, someId(attributes));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ parseCoreNode(node, attributes);
+ cssStyleLookup(node, this, m_selector);
+ parseStyle(node, attributes, this);
+ } else if (FactoryMethod method = findGraphicsFactory(localName)) {
+ //rendering element
+ Q_ASSERT(!m_nodes.isEmpty());
+ node = method(m_nodes.top(), attributes, this);
+ if (node) {
+ switch (m_nodes.top()->type()) {
+ case QSvgNode::DOC:
+ case QSvgNode::G:
+ case QSvgNode::DEFS:
+ case QSvgNode::SWITCH:
+ {
+ QSvgStructureNode *group =
+ static_cast<QSvgStructureNode*>(m_nodes.top());
+ group->addChild(node, someId(attributes));
+ }
+ break;
+ case QSvgNode::TEXT:
+ case QSvgNode::TEXTAREA:
+ if (node->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
+ } else {
+ qWarning("\'text\' or \'textArea\' element contains invalid element type.");
+ delete node;
+ node = 0;
+ }
+ break;
+ default:
+ qWarning("Could not add child element to parent element because the types are incorrect.");
+ delete node;
+ node = 0;
+ break;
+ }
+
+ if (node) {
+ parseCoreNode(node, attributes);
+ cssStyleLookup(node, this, m_selector);
+ parseStyle(node, attributes, this);
+ if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) {
+ static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
+ } else if (node->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
+ }
+ }
+ }
+ } else if (ParseMethod method = findUtilFactory(localName)) {
+ Q_ASSERT(!m_nodes.isEmpty());
+ if (!method(m_nodes.top(), attributes, this)) {
+ qWarning("Problem parsing %s", qPrintable(localName));
+ }
+ } else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
+ QSvgStyleProperty *prop = method(m_nodes.top(), attributes, this);
+ if (prop) {
+ m_style = prop;
+ m_nodes.top()->appendStyleProperty(prop, someId(attributes));
+ } else {
+ qWarning("Could not parse node: %s", qPrintable(localName));
+ }
+ } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
+ if (m_style) {
+ if (!method(m_style, attributes, this)) {
+ qWarning("Problem parsing %s", qPrintable(localName));
+ }
+ }
+ } else {
+ //qWarning()<<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
+ m_skipNodes.push(Unknown);
+ return true;
+ }
+
+ if (node) {
+ m_nodes.push(node);
+ m_skipNodes.push(Graphics);
+ } else {
+ //qDebug()<<"Skipping "<<localName;
+ m_skipNodes.push(Style);
+ }
+ return true;
+}
+
+bool QSvgHandler::endElement(const QStringRef &localName)
+{
+ CurrentNode node = m_skipNodes.top();
+ m_skipNodes.pop();
+ m_whitespaceMode.pop();
+
+ popColor();
+
+ if (node == Unknown) {
+ return true;
+ }
+
+ if (m_inStyle && localName == QLatin1String("style"))
+ m_inStyle = false;
+
+ if (node == Graphics)
+ m_nodes.pop();
+ else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
+ m_style = 0;
+
+ return true;
+}
+
+void QSvgHandler::resolveGradients(QSvgNode *node)
+{
+ if (!node || (node->type() != QSvgNode::DOC && node->type() != QSvgNode::G
+ && node->type() != QSvgNode::DEFS && node->type() != QSvgNode::SWITCH)) {
+ return;
+ }
+ QSvgStructureNode *structureNode = static_cast<QSvgStructureNode *>(node);
+
+ QList<QSvgNode *> ren = structureNode->renderers();
+ for (QList<QSvgNode *>::iterator it = ren.begin(); it != ren.end(); ++it) {
+ QSvgFillStyle *fill = static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
+ if (fill && !fill->isGradientResolved()) {
+ QString id = fill->gradientId();
+ QSvgFillStyleProperty *style = structureNode->styleProperty(id);
+ if (style) {
+ fill->setFillStyle(style);
+ } else {
+ qWarning("Could not resolve property : %s", qPrintable(id));
+ fill->setBrush(Qt::NoBrush);
+ }
+ }
+
+ QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
+ if (stroke && !stroke->isGradientResolved()) {
+ QString id = stroke->gradientId();
+ QSvgFillStyleProperty *style = structureNode->styleProperty(id);
+ if (style) {
+ stroke->setStyle(style);
+ } else {
+ qWarning("Could not resolve property : %s", qPrintable(id));
+ stroke->setStroke(Qt::NoBrush);
+ }
+ }
+
+ resolveGradients(*it);
+ }
+}
+
+bool QSvgHandler::characters(const QStringRef &str)
+{
+ if (m_inStyle) {
+ QString css = str.toString();
+ QCss::StyleSheet sheet;
+ QCss::Parser(css).parse(&sheet);
+ m_selector->styleSheets.append(sheet);
+ return true;
+ } else if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown || m_nodes.isEmpty())
+ return true;
+
+ if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
+ static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
+ } else if (m_nodes.top()->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
+ }
+
+ return true;
+}
+
+QSvgTinyDocument * QSvgHandler::document() const
+{
+ return m_doc;
+}
+
+QSvgHandler::LengthType QSvgHandler::defaultCoordinateSystem() const
+{
+ return m_defaultCoords;
+}
+
+void QSvgHandler::setDefaultCoordinateSystem(LengthType type)
+{
+ m_defaultCoords = type;
+}
+
+void QSvgHandler::pushColor(const QColor &color)
+{
+ m_colorStack.push(color);
+ m_colorTagCount.push(1);
+}
+
+void QSvgHandler::pushColorCopy()
+{
+ if (m_colorTagCount.count())
+ ++m_colorTagCount.top();
+ else
+ pushColor(Qt::black);
+}
+
+void QSvgHandler::popColor()
+{
+ if (m_colorTagCount.count()) {
+ if (!--m_colorTagCount.top()) {
+ m_colorStack.pop();
+ m_colorTagCount.pop();
+ }
+ }
+}
+
+QColor QSvgHandler::currentColor() const
+{
+ if (!m_colorStack.isEmpty())
+ return m_colorStack.top();
+ else
+ return QColor(0, 0, 0);
+}
+
+void QSvgHandler::setInStyle(bool b)
+{
+ m_inStyle = b;
+}
+
+bool QSvgHandler::inStyle() const
+{
+ return m_inStyle;
+}
+
+QSvgStyleSelector * QSvgHandler::selector() const
+{
+ return m_selector;
+}
+
+bool QSvgHandler::processingInstruction(const QString &target, const QString &data)
+{
+ if (target == QLatin1String("xml-stylesheet")) {
+ QRegExp rx(QLatin1String("type=\\\"(.+)\\\""));
+ rx.setMinimal(true);
+ bool isCss = false;
+ int pos = 0;
+ while ((pos = rx.indexIn(data, pos)) != -1) {
+ QString type = rx.cap(1);
+ if (type.toLower() == QLatin1String("text/css")) {
+ isCss = true;
+ }
+ pos += rx.matchedLength();
+ }
+
+ if (isCss) {
+ QRegExp rx(QLatin1String("href=\\\"(.+)\\\""));
+ rx.setMinimal(true);
+ pos = 0;
+ pos = rx.indexIn(data, pos);
+ QString addr = rx.cap(1);
+ QFileInfo fi(addr);
+ //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
+ if (fi.exists()) {
+ QFile file(fi.absoluteFilePath());
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return true;
+ }
+ QByteArray cssData = file.readAll();
+ QString css = QString::fromUtf8(cssData);
+
+ QCss::StyleSheet sheet;
+ QCss::Parser(css).parse(&sheet);
+ m_selector->styleSheets.append(sheet);
+ }
+
+ }
+ }
+
+ return true;
+}
+
+void QSvgHandler::setAnimPeriod(int start, int end)
+{
+ Q_UNUSED(start);
+ m_animEnd = qMax(end, m_animEnd);
+}
+
+int QSvgHandler::animationDuration() const
+{
+ return m_animEnd;
+}
+
+QSvgHandler::~QSvgHandler()
+{
+ delete m_selector;
+ m_selector = 0;
+
+ if(m_ownsReader)
+ delete xml;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvghandler_p.h b/src/svg/qsvghandler_p.h
new file mode 100644
index 0000000..1d1712e
--- /dev/null
+++ b/src/svg/qsvghandler_p.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGHANDLER_P_H
+#define QSVGHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtXml/qxmlstream.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtCore/qhash.h"
+#include "QtCore/qstack.h"
+#include "qsvgstyle_p.h"
+#include "private/qcssparser_p.h"
+#include "private/qsvggraphics_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSvgNode;
+class QSvgTinyDocument;
+class QSvgHandler;
+class QColor;
+class QSvgStyleSelector;
+class QXmlStreamReader;
+
+struct QSvgCssAttribute
+{
+ QXmlStreamStringRef name;
+ QXmlStreamStringRef value;
+};
+
+class QSvgHandler
+{
+public:
+ enum LengthType {
+ LT_PERCENT,
+ LT_PX,
+ LT_PC,
+ LT_PT,
+ LT_MM,
+ LT_CM,
+ LT_IN,
+ LT_OTHER
+ };
+
+public:
+ QSvgHandler(QIODevice *device);
+ QSvgHandler(const QByteArray &data);
+ QSvgHandler(QXmlStreamReader *const data);
+ ~QSvgHandler();
+
+ QSvgTinyDocument *document() const;
+
+ inline bool ok() const {
+ return document() != 0 && !xml->hasError();
+ }
+
+ inline QString errorString() const { return xml->errorString(); }
+ inline int lineNumber() const { return xml->lineNumber(); }
+
+ void setDefaultCoordinateSystem(LengthType type);
+ LengthType defaultCoordinateSystem() const;
+
+ void pushColor(const QColor &color);
+ void pushColorCopy();
+ void popColor();
+ QColor currentColor() const;
+
+ void setInStyle(bool b);
+ bool inStyle() const;
+
+ QSvgStyleSelector *selector() const;
+
+ void setAnimPeriod(int start, int end);
+ int animationDuration() const;
+
+ void parseCSStoXMLAttrs(QString css, QVector<QSvgCssAttribute> *attributes);
+
+ inline QPen defaultPen() const
+ { return m_defaultPen; }
+
+public:
+ bool startElement(const QString &localName, const QXmlStreamAttributes &attributes);
+ bool endElement(const QStringRef &localName);
+ bool characters(const QStringRef &str);
+ bool processingInstruction(const QString &target, const QString &data);
+
+private:
+ void init();
+
+ QSvgTinyDocument *m_doc;
+ QStack<QSvgNode*> m_nodes;
+
+ QList<QSvgNode*> m_resolveNodes;
+
+ enum CurrentNode
+ {
+ Unknown,
+ Graphics,
+ Style
+ };
+ QStack<CurrentNode> m_skipNodes;
+
+ /*!
+ Follows the depths of elements. The top is current xml:space
+ value that applies for a given element.
+ */
+ QStack<QSvgText::WhitespaceMode> m_whitespaceMode;
+
+ QSvgRefCounter<QSvgStyleProperty> m_style;
+
+ LengthType m_defaultCoords;
+
+ QStack<QColor> m_colorStack;
+ QStack<int> m_colorTagCount;
+
+ bool m_inStyle;
+
+ QSvgStyleSelector *m_selector;
+
+ int m_animEnd;
+
+ QXmlStreamReader *const xml;
+ QCss::Parser m_cssParser;
+ void parse();
+ void resolveGradients(QSvgNode *node);
+
+ QPen m_defaultPen;
+ /**
+ * Whether we own the variable xml, and hence whether
+ * we need to delete it.
+ */
+ const bool m_ownsReader;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGHANDLER_P_H
diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
new file mode 100644
index 0000000..5751f80
--- /dev/null
+++ b/src/svg/qsvgnode.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgnode_p.h"
+#include "qsvgtinydocument_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qdebug.h"
+#include "qstack.h"
+
+QT_BEGIN_NAMESPACE
+
+QSvgNode::QSvgNode(QSvgNode *parent)
+ : m_parent(parent),
+ m_visible(true),
+ m_displayMode(BlockMode)
+{
+}
+
+QSvgNode::~QSvgNode()
+{
+
+}
+
+void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id)
+{
+ //qDebug()<<"appending "<<prop->type()<< " ("<< id <<") "<<"to "<<this<<this->type();
+ QSvgTinyDocument *doc;
+ switch (prop->type()) {
+ case QSvgStyleProperty::QUALITY:
+ m_style.quality = static_cast<QSvgQualityStyle*>(prop);
+ break;
+ case QSvgStyleProperty::FILL:
+ m_style.fill = static_cast<QSvgFillStyle*>(prop);
+ break;
+ case QSvgStyleProperty::VIEWPORT_FILL:
+ m_style.viewportFill = static_cast<QSvgViewportFillStyle*>(prop);
+ break;
+ case QSvgStyleProperty::FONT:
+ m_style.font = static_cast<QSvgFontStyle*>(prop);
+ break;
+ case QSvgStyleProperty::STROKE:
+ m_style.stroke = static_cast<QSvgStrokeStyle*>(prop);
+ break;
+ case QSvgStyleProperty::SOLID_COLOR:
+ m_style.solidColor = static_cast<QSvgSolidColorStyle*>(prop);
+ doc = document();
+ if (doc && !id.isEmpty())
+ doc->addNamedStyle(id, m_style.solidColor);
+ break;
+ case QSvgStyleProperty::GRADIENT:
+ m_style.gradient = static_cast<QSvgGradientStyle*>(prop);
+ doc = document();
+ if (doc && !id.isEmpty())
+ doc->addNamedStyle(id, m_style.gradient);
+ break;
+ case QSvgStyleProperty::TRANSFORM:
+ m_style.transform = static_cast<QSvgTransformStyle*>(prop);
+ break;
+ case QSvgStyleProperty::ANIMATE_COLOR:
+ m_style.animateColor = static_cast<QSvgAnimateColor*>(prop);
+ break;
+ case QSvgStyleProperty::ANIMATE_TRANSFORM:
+ m_style.animateTransforms.append(
+ static_cast<QSvgAnimateTransform*>(prop));
+ break;
+ case QSvgStyleProperty::OPACITY:
+ m_style.opacity = static_cast<QSvgOpacityStyle*>(prop);
+ break;
+ case QSvgStyleProperty::COMP_OP:
+ m_style.compop = static_cast<QSvgCompOpStyle*>(prop);
+ break;
+ default:
+ qDebug("QSvgNode: Trying to append unknown property!");
+ break;
+ }
+}
+
+void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) const
+{
+ m_style.apply(p, this, states);
+}
+
+void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) const
+{
+ m_style.revert(p, states);
+}
+
+QSvgStyleProperty * QSvgNode::styleProperty(QSvgStyleProperty::Type type) const
+{
+ const QSvgNode *node = this;
+ while (node) {
+ switch (type) {
+ case QSvgStyleProperty::QUALITY:
+ if (node->m_style.quality)
+ return node->m_style.quality;
+ break;
+ case QSvgStyleProperty::FILL:
+ if (node->m_style.fill)
+ return node->m_style.fill;
+ break;
+ case QSvgStyleProperty::VIEWPORT_FILL:
+ if (m_style.viewportFill)
+ return node->m_style.viewportFill;
+ break;
+ case QSvgStyleProperty::FONT:
+ if (node->m_style.font)
+ return node->m_style.font;
+ break;
+ case QSvgStyleProperty::STROKE:
+ if (node->m_style.stroke)
+ return node->m_style.stroke;
+ break;
+ case QSvgStyleProperty::SOLID_COLOR:
+ if (node->m_style.solidColor)
+ return node->m_style.solidColor;
+ break;
+ case QSvgStyleProperty::GRADIENT:
+ if (node->m_style.gradient)
+ return node->m_style.gradient;
+ break;
+ case QSvgStyleProperty::TRANSFORM:
+ if (node->m_style.transform)
+ return node->m_style.transform;
+ break;
+ case QSvgStyleProperty::ANIMATE_COLOR:
+ if (node->m_style.animateColor)
+ return node->m_style.animateColor;
+ break;
+ case QSvgStyleProperty::ANIMATE_TRANSFORM:
+ if (!node->m_style.animateTransforms.isEmpty())
+ return node->m_style.animateTransforms.first();
+ break;
+ case QSvgStyleProperty::OPACITY:
+ if (node->m_style.opacity)
+ return node->m_style.opacity;
+ break;
+ case QSvgStyleProperty::COMP_OP:
+ if (node->m_style.compop)
+ return node->m_style.compop;
+ break;
+ default:
+ break;
+ }
+ node = node->parent();
+ }
+
+ return 0;
+}
+
+QSvgFillStyleProperty * QSvgNode::styleProperty(const QString &id) const
+{
+ QString rid = id;
+ if (rid.startsWith(QLatin1Char('#')))
+ rid.remove(0, 1);
+ QSvgTinyDocument *doc = document();
+ return doc ? doc->namedStyle(rid) : 0;
+}
+
+QRectF QSvgNode::bounds(QPainter *, QSvgExtraStates &) const
+{
+ return QRectF(0, 0, 0, 0);
+}
+
+QRectF QSvgNode::transformedBounds() const
+{
+ if (!m_cachedBounds.isEmpty())
+ return m_cachedBounds;
+
+ QImage dummy(1, 1, QImage::Format_RGB32);
+ QPainter p(&dummy);
+ QSvgExtraStates states;
+
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p.setPen(pen);
+
+ QStack<QSvgNode*> parentApplyStack;
+ QSvgNode *parent = m_parent;
+ while (parent) {
+ parentApplyStack.push(parent);
+ parent = parent->parent();
+ }
+
+ for (int i = parentApplyStack.size() - 1; i >= 0; --i)
+ parentApplyStack[i]->applyStyle(&p, states);
+
+ p.setWorldTransform(QTransform());
+
+ m_cachedBounds = transformedBounds(&p, states);
+ return m_cachedBounds;
+}
+
+QSvgTinyDocument * QSvgNode::document() const
+{
+ QSvgTinyDocument *doc = 0;
+ QSvgNode *node = const_cast<QSvgNode*>(this);
+ while (node && node->type() != QSvgNode::DOC) {
+ node = node->parent();
+ }
+ doc = static_cast<QSvgTinyDocument*>(node);
+
+ return doc;
+}
+
+void QSvgNode::setRequiredFeatures(const QStringList &lst)
+{
+ m_requiredFeatures = lst;
+}
+
+const QStringList & QSvgNode::requiredFeatures() const
+{
+ return m_requiredFeatures;
+}
+
+void QSvgNode::setRequiredExtensions(const QStringList &lst)
+{
+ m_requiredExtensions = lst;
+}
+
+const QStringList & QSvgNode::requiredExtensions() const
+{
+ return m_requiredExtensions;
+}
+
+void QSvgNode::setRequiredLanguages(const QStringList &lst)
+{
+ m_requiredLanguages = lst;
+}
+
+const QStringList & QSvgNode::requiredLanguages() const
+{
+ return m_requiredLanguages;
+}
+
+void QSvgNode::setRequiredFormats(const QStringList &lst)
+{
+ m_requiredFormats = lst;
+}
+
+const QStringList & QSvgNode::requiredFormats() const
+{
+ return m_requiredFormats;
+}
+
+void QSvgNode::setRequiredFonts(const QStringList &lst)
+{
+ m_requiredFonts = lst;
+}
+
+const QStringList & QSvgNode::requiredFonts() const
+{
+ return m_requiredFonts;
+}
+
+void QSvgNode::setVisible(bool visible)
+{
+ //propagate visibility change of true to the parent
+ //not propagating false is just a small performance
+ //degradation since we'll iterate over children without
+ //drawing any of them
+ if (m_parent && visible && !m_parent->isVisible())
+ m_parent->setVisible(true);
+
+ m_visible = visible;
+}
+
+QRectF QSvgNode::transformedBounds(QPainter *p, QSvgExtraStates &states) const
+{
+ applyStyle(p, states);
+ QRectF rect = bounds(p, states);
+ revertStyle(p, states);
+ return rect;
+}
+
+void QSvgNode::setNodeId(const QString &i)
+{
+ m_id = i;
+}
+
+void QSvgNode::setXmlClass(const QString &str)
+{
+ m_class = str;
+}
+
+void QSvgNode::setDisplayMode(DisplayMode mode)
+{
+ m_displayMode = mode;
+}
+
+QSvgNode::DisplayMode QSvgNode::displayMode() const
+{
+ return m_displayMode;
+}
+
+qreal QSvgNode::strokeWidth(QPainter *p)
+{
+ QPen pen = p->pen();
+ if (pen.style() == Qt::NoPen || pen.brush().style() == Qt::NoBrush || pen.isCosmetic())
+ return 0;
+ return pen.widthF();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h
new file mode 100644
index 0000000..9d06988
--- /dev/null
+++ b/src/svg/qsvgnode_p.h
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGNODE_P_H
+#define QSVGNODE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsvgstyle_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtCore/qstring.h"
+#include "QtCore/qhash.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+class QSvgTinyDocument;
+
+class QSvgNode
+{
+public:
+ enum Type
+ {
+ DOC,
+ G,
+ DEFS,
+ SWITCH,
+ ANIMATION,
+ ARC,
+ CIRCLE,
+ ELLIPSE,
+ IMAGE,
+ LINE,
+ PATH,
+ POLYGON,
+ POLYLINE,
+ RECT,
+ TEXT,
+ TEXTAREA,
+ TSPAN,
+ USE,
+ VIDEO
+ };
+ enum DisplayMode {
+ InlineMode,
+ BlockMode,
+ ListItemMode,
+ RunInMode,
+ CompactMode,
+ MarkerMode,
+ TableMode,
+ InlineTableMode,
+ TableRowGroupMode,
+ TableHeaderGroupMode,
+ TableFooterGroupMode,
+ TableRowMode,
+ TableColumnGroupMode,
+ TableColumnMode,
+ TableCellMode,
+ TableCaptionMode,
+ NoneMode,
+ InheritMode
+ };
+public:
+ QSvgNode(QSvgNode *parent=0);
+ virtual ~QSvgNode();
+ virtual void draw(QPainter *p, QSvgExtraStates &states) =0;
+
+ QSvgNode *parent() const;
+
+ void appendStyleProperty(QSvgStyleProperty *prop, const QString &id);
+ void applyStyle(QPainter *p, QSvgExtraStates &states) const;
+ void revertStyle(QPainter *p, QSvgExtraStates &states) const;
+ QSvgStyleProperty *styleProperty(QSvgStyleProperty::Type type) const;
+ QSvgFillStyleProperty *styleProperty(const QString &id) const;
+
+ QSvgTinyDocument *document() const;
+
+ virtual Type type() const =0;
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+ virtual QRectF transformedBounds(QPainter *p, QSvgExtraStates &states) const;
+ QRectF transformedBounds() const;
+
+ void setRequiredFeatures(const QStringList &lst);
+ const QStringList & requiredFeatures() const;
+
+ void setRequiredExtensions(const QStringList &lst);
+ const QStringList & requiredExtensions() const;
+
+ void setRequiredLanguages(const QStringList &lst);
+ const QStringList & requiredLanguages() const;
+
+ void setRequiredFormats(const QStringList &lst);
+ const QStringList & requiredFormats() const;
+
+ void setRequiredFonts(const QStringList &lst);
+ const QStringList & requiredFonts() const;
+
+ void setVisible(bool visible);
+ bool isVisible() const;
+
+ void setDisplayMode(DisplayMode display);
+ DisplayMode displayMode() const;
+
+ QString nodeId() const;
+ void setNodeId(const QString &i);
+
+ QString xmlClass() const;
+ void setXmlClass(const QString &str);
+protected:
+ mutable QSvgStyle m_style;
+
+ static qreal strokeWidth(QPainter *p);
+private:
+ QSvgNode *m_parent;
+
+ QStringList m_requiredFeatures;
+ QStringList m_requiredExtensions;
+ QStringList m_requiredLanguages;
+ QStringList m_requiredFormats;
+ QStringList m_requiredFonts;
+
+ bool m_visible;
+
+ QString m_id;
+ QString m_class;
+
+ DisplayMode m_displayMode;
+ mutable QRectF m_cachedBounds;
+
+ friend class QSvgTinyDocument;
+};
+
+inline QSvgNode *QSvgNode::parent() const
+{
+ return m_parent;
+}
+
+inline bool QSvgNode::isVisible() const
+{
+ return m_visible;
+}
+
+inline QString QSvgNode::nodeId() const
+{
+ return m_id;
+}
+
+inline QString QSvgNode::xmlClass() const
+{
+ return m_class;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGNODE_P_H
diff --git a/src/svg/qsvgrenderer.cpp b/src/svg/qsvgrenderer.cpp
new file mode 100644
index 0000000..9cc6b0c
--- /dev/null
+++ b/src/svg/qsvgrenderer.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgrenderer.h"
+
+#ifndef QT_NO_SVGRENDERER
+
+#include "qsvgtinydocument_p.h"
+
+#include "qbytearray.h"
+#include "qtimer.h"
+#include "qdebug.h"
+#include "private/qobject_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSvgRenderer
+ \ingroup painting
+
+ \brief The QSvgRenderer class is used to draw the contents of SVG files onto paint devices.
+ \since 4.1
+ \reentrant
+
+ Using QSvgRenderer, Scalable Vector Graphics (SVG) can be rendered onto any QPaintDevice
+ subclass, including QWidget, QImage, and QGLWidget.
+
+ QSvgRenderer provides an API that supports basic features of SVG rendering, such as loading
+ and rendering of static drawings, and more interactive features like animation. Since the
+ rendering is performed using QPainter, SVG drawings can be rendered on any subclass of
+ QPaintDevice.
+
+ SVG drawings are either loaded when an QSvgRenderer is constructed, or loaded later
+ using the load() functions. Data is either supplied directly as serialized XML, or
+ indirectly using a file name. If a valid file has been loaded, either when the renderer
+ is constructed or at some later time, isValid() returns true; otherwise it returns false.
+ QSvgRenderer provides the render() slot to render the current document, or the current
+ frame of an animated document, using a given painter.
+
+ The defaultSize() function provides information about the amount of space that is required
+ to render the currently loaded SVG file. This is useful for paint devices, such as QWidget,
+ that often need to supply a size hint to their parent layout.
+ The default size of a drawing may differ from its visible area, found using the \l viewBox
+ property.
+
+ Animated SVG drawings are supported, and can be controlled with a simple collection of
+ functions and properties:
+
+ \list
+ \o The animated() function indicates whether a drawing contains animation information.
+ \omit
+ \o The animationDuration() function provides the duration in milliseconds of the
+ animation, without taking any looping into account.
+ \o The \l currentFrame property contains the current frame of the animation.
+ \endomit
+ \o The \l framesPerSecond property contains the rate at which the animation plays.
+ \endlist
+
+ Finally, the QSvgRenderer class provides the repaintNeeded() signal which is emitted
+ whenever the rendering of the document needs to be updated.
+
+ \sa QSvgWidget, {QtSvg Module}, {SVG Viewer Example}, QPicture
+*/
+
+class QSvgRendererPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSvgRenderer)
+public:
+ explicit QSvgRendererPrivate()
+ : QObjectPrivate(),
+ render(0), timer(0),
+ fps(30)
+ {}
+ ~QSvgRendererPrivate()
+ {
+ delete render;
+ }
+
+ static void callRepaintNeeded(QSvgRenderer *const q);
+
+ QSvgTinyDocument *render;
+ QTimer *timer;
+ int fps;
+};
+
+/*!
+ Constructs a new renderer with the given \a parent.
+*/
+QSvgRenderer::QSvgRenderer(QObject *parent)
+ : QObject(*(new QSvgRendererPrivate), parent)
+{
+}
+
+/*!
+ Constructs a new renderer with the given \a parent and loads the contents of the
+ SVG file with the specified \a filename.
+*/
+QSvgRenderer::QSvgRenderer(const QString &filename, QObject *parent)
+ : QObject(*new QSvgRendererPrivate, parent)
+{
+ load(filename);
+}
+
+/*!
+ Constructs a new renderer with the given \a parent and loads the SVG data
+ from the byte array specified by \a contents.
+*/
+QSvgRenderer::QSvgRenderer(const QByteArray &contents, QObject *parent)
+ : QObject(*new QSvgRendererPrivate, parent)
+{
+ load(contents);
+}
+
+/*!
+ \since 4.5
+
+ Constructs a new renderer with the given \a parent and loads the SVG data
+ using the stream reader specified by \a contents.
+*/
+QSvgRenderer::QSvgRenderer(QXmlStreamReader *contents, QObject *parent)
+ : QObject(*new QSvgRendererPrivate, parent)
+{
+ load(contents);
+}
+
+/*!
+ Destroys the renderer.
+*/
+QSvgRenderer::~QSvgRenderer()
+{
+
+}
+
+/*!
+ Returns true if there is a valid current document; otherwise returns false.
+*/
+bool QSvgRenderer::isValid() const
+{
+ Q_D(const QSvgRenderer);
+ return d->render;
+}
+
+/*!
+ Returns the default size of the document contents.
+*/
+QSize QSvgRenderer::defaultSize() const
+{
+ Q_D(const QSvgRenderer);
+ if (d->render)
+ return d->render->size();
+ else
+ return QSize();
+}
+
+/*!
+ Returns viewBoxF().toRect().
+
+ \sa viewBoxF()
+*/
+QRect QSvgRenderer::viewBox() const
+{
+ Q_D(const QSvgRenderer);
+ if (d->render)
+ return d->render->viewBox().toRect();
+ else
+ return QRect();
+}
+
+/*!
+ \property QSvgRenderer::viewBox
+ \brief the rectangle specifying the visible area of the document in logical coordinates
+ \since 4.2
+*/
+void QSvgRenderer::setViewBox(const QRect &viewbox)
+{
+ Q_D(QSvgRenderer);
+ if (d->render)
+ d->render->setViewBox(viewbox);
+}
+
+/*!
+ Returns true if the current document contains animated elements; otherwise
+ returns false.
+
+ \sa framesPerSecond()
+*/
+bool QSvgRenderer::animated() const
+{
+ Q_D(const QSvgRenderer);
+ if (d->render)
+ return d->render->animated();
+ else
+ return false;
+}
+
+/*!
+ \property QSvgRenderer::framesPerSecond
+ \brief the number of frames per second to be shown
+
+ The number of frames per second is 0 if the current document is not animated.
+
+ \sa animated()
+*/
+int QSvgRenderer::framesPerSecond() const
+{
+ Q_D(const QSvgRenderer);
+ return d->fps;
+}
+
+void QSvgRenderer::setFramesPerSecond(int num)
+{
+ Q_D(QSvgRenderer);
+ if (num < 0) {
+ qWarning("QSvgRenderer::setFramesPerSecond: Cannot set negative value %d", num);
+ return;
+ }
+ d->fps = num;
+}
+
+/*!
+ \property QSvgRenderer::currentFrame
+ \brief the current frame of the document's animation, or 0 if the document is not animated
+ \internal
+
+ \sa animationDuration(), framesPerSecond, animated()
+*/
+
+/*!
+ \internal
+*/
+int QSvgRenderer::currentFrame() const
+{
+ Q_D(const QSvgRenderer);
+ return d->render->currentFrame();
+}
+
+/*!
+ \internal
+*/
+void QSvgRenderer::setCurrentFrame(int frame)
+{
+ Q_D(QSvgRenderer);
+ d->render->setCurrentFrame(frame);
+}
+
+/*!
+ \internal
+
+ Returns the number of frames in the animation, or 0 if the current document is not
+ animated.
+
+ \sa animated(), framesPerSecond
+*/
+int QSvgRenderer::animationDuration() const
+{
+ Q_D(const QSvgRenderer);
+ return d->render->animationDuration();
+}
+
+/*!
+ \internal
+ \since 4.5
+
+ We can't have template functions, that's loadDocument(), as friends, for this
+ code, so we let this function be a friend of QSvgRenderer instead.
+ */
+void QSvgRendererPrivate::callRepaintNeeded(QSvgRenderer *const q)
+{
+ q->repaintNeeded();
+}
+
+template<typename TInputType>
+static bool loadDocument(QSvgRenderer *const q,
+ QSvgRendererPrivate *const d,
+ const TInputType &in)
+{
+ delete d->render;
+ d->render = QSvgTinyDocument::load(in);
+ if (d->render && d->render->animated() && d->fps > 0) {
+ if (!d->timer)
+ d->timer = new QTimer(q);
+ else
+ d->timer->stop();
+ q->connect(d->timer, SIGNAL(timeout()),
+ q, SIGNAL(repaintNeeded()));
+ d->timer->start(1000/d->fps);
+ } else if (d->timer) {
+ d->timer->stop();
+ }
+
+ //force first update
+ QSvgRendererPrivate::callRepaintNeeded(q);
+
+ return d->render;
+}
+
+/*!
+ Loads the SVG file specified by \a filename, returning true if the content
+ was successfully parsed; otherwise returns false.
+*/
+bool QSvgRenderer::load(const QString &filename)
+{
+ Q_D(QSvgRenderer);
+ return loadDocument(this, d, filename);
+}
+
+/*!
+ Loads the specified SVG format \a contents, returning true if the content
+ was successfully parsed; otherwise returns false.
+*/
+bool QSvgRenderer::load(const QByteArray &contents)
+{
+ Q_D(QSvgRenderer);
+ return loadDocument(this, d, contents);
+}
+
+/*!
+ Loads the specified SVG in \a contents, returning true if the content
+ was successfully parsed; otherwise returns false.
+
+ The reader will be used from where it currently is positioned. If \a contents
+ is \c null, behavior is undefined.
+
+ \since 4.5
+*/
+bool QSvgRenderer::load(QXmlStreamReader *contents)
+{
+ Q_D(QSvgRenderer);
+ return loadDocument(this, d, contents);
+}
+
+/*!
+ Renders the current document, or the current frame of an animated
+ document, using the given \a painter.
+*/
+void QSvgRenderer::render(QPainter *painter)
+{
+ Q_D(QSvgRenderer);
+ if (d->render) {
+ d->render->draw(painter);
+ }
+}
+
+/*!
+ \fn void QSvgRenderer::repaintNeeded()
+
+ This signal is emitted whenever the rendering of the document
+ needs to be updated, usually for the purposes of animation.
+*/
+
+/*!
+ Renders the given element with \a elementId using the given \a painter
+ on the specified \a bounds. If the bounding rectangle is not specified
+ the SVG element is mapped to the whole paint device.
+*/
+void QSvgRenderer::render(QPainter *painter, const QString &elementId,
+ const QRectF &bounds)
+{
+ Q_D(QSvgRenderer);
+ if (d->render) {
+ d->render->draw(painter, elementId, bounds);
+ }
+}
+
+/*!
+ Renders the current document, or the current frame of an animated
+ document, using the given \a painter on the specified \a bounds within
+ the painter. If the bounding rectangle is not specified
+ the SVG file is mapped to the whole paint device.
+*/
+void QSvgRenderer::render(QPainter *painter, const QRectF &bounds)
+{
+ Q_D(QSvgRenderer);
+ if (d->render) {
+ d->render->draw(painter, bounds);
+ }
+}
+
+QRectF QSvgRenderer::viewBoxF() const
+{
+ Q_D(const QSvgRenderer);
+ if (d->render)
+ return d->render->viewBox();
+ else
+ return QRect();
+}
+
+void QSvgRenderer::setViewBox(const QRectF &viewbox)
+{
+ Q_D(QSvgRenderer);
+ if (d->render)
+ d->render->setViewBox(viewbox);
+}
+
+/*!
+ \since 4.2
+
+ Returns bounding rectangle of the item with the given \a id.
+ The transformation matrix of parent elements is not affecting
+ the bounds of the element.
+
+ \sa matrixForElement()
+*/
+QRectF QSvgRenderer::boundsOnElement(const QString &id) const
+{
+ Q_D(const QSvgRenderer);
+ QRectF bounds;
+ if (d->render)
+ bounds = d->render->boundsOnElement(id);
+ return bounds;
+}
+
+
+/*!
+ \since 4.2
+
+ Returns true if the element with the given \a id exists
+ in the currently parsed SVG file and is a renderable
+ element.
+
+ Note: this method returns true only for elements that
+ can be rendered. Which implies that elements that are considered
+ part of the fill/stroke style properties, e.g. radialGradients
+ even tough marked with "id" attributes will not be found by this
+ method.
+*/
+bool QSvgRenderer::elementExists(const QString &id) const
+{
+ Q_D(const QSvgRenderer);
+ bool exists = false;
+ if (d->render)
+ exists = d->render->elementExists(id);
+ return exists;
+}
+
+/*!
+ \since 4.2
+
+ Returns the transformation matrix for the element
+ with the given \a id. The matrix is a product of
+ the transformation of the element's parents. The transformation of
+ the element itself is not included.
+
+ To find the bounding rectangle of the element in logical coordinates,
+ you can apply the matrix on the rectangle returned from boundsOnElement().
+
+ \sa boundsOnElement()
+*/
+QMatrix QSvgRenderer::matrixForElement(const QString &id) const
+{
+ Q_D(const QSvgRenderer);
+ QMatrix mat;
+ if (d->render)
+ mat = d->render->matrixForElement(id);
+ return mat;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsvgrenderer.cpp"
+
+#endif // QT_NO_SVGRENDERER
diff --git a/src/svg/qsvgrenderer.h b/src/svg/qsvgrenderer.h
new file mode 100644
index 0000000..b1a20d2
--- /dev/null
+++ b/src/svg/qsvgrenderer.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGRENDERER_H
+#define QSVGRENDERER_H
+
+#include <QtGui/qmatrix.h>
+
+#ifndef QT_NO_SVGRENDERER
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qxmlstream.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Svg)
+
+class QSvgRendererPrivate;
+class QPainter;
+class QByteArray;
+
+class Q_SVG_EXPORT QSvgRenderer : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QRectF viewBox READ viewBoxF WRITE setViewBox)
+ Q_PROPERTY(int framesPerSecond READ framesPerSecond WRITE setFramesPerSecond)
+ Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame)
+public:
+ QSvgRenderer(QObject *parent=0);
+ QSvgRenderer(const QString &filename, QObject *parent=0);
+ QSvgRenderer(const QByteArray &contents, QObject *parent=0);
+ QSvgRenderer(QXmlStreamReader *contents, QObject *parent=0);
+ ~QSvgRenderer();
+
+ bool isValid() const;
+
+ QSize defaultSize() const;
+
+ QRect viewBox() const;
+ QRectF viewBoxF() const;
+ void setViewBox(const QRect &viewbox);
+ void setViewBox(const QRectF &viewbox);
+
+ bool animated() const;
+ int framesPerSecond() const;
+ void setFramesPerSecond(int num);
+ int currentFrame() const;
+ void setCurrentFrame(int);
+ int animationDuration() const;//in seconds
+
+ QRectF boundsOnElement(const QString &id) const;
+ bool elementExists(const QString &id) const;
+ QMatrix matrixForElement(const QString &id) const;
+
+public Q_SLOTS:
+ bool load(const QString &filename);
+ bool load(const QByteArray &contents);
+ bool load(QXmlStreamReader *contents);
+ void render(QPainter *p);
+ void render(QPainter *p, const QRectF &bounds);
+
+ void render(QPainter *p, const QString &elementId,
+ const QRectF &bounds=QRectF());
+
+Q_SIGNALS:
+ void repaintNeeded();
+
+private:
+ Q_DECLARE_PRIVATE(QSvgRenderer)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SVGRENDERER
+#endif // QSVGRENDERER_H
diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp
new file mode 100644
index 0000000..71fc8d2
--- /dev/null
+++ b/src/svg/qsvgstructure.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgstructure_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgnode_p.h"
+#include "qsvgstyle_p.h"
+#include "qsvgtinydocument_p.h"
+
+#include "qpainter.h"
+#include "qlocale.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+QSvgG::QSvgG(QSvgNode *parent)
+ : QSvgStructureNode(parent)
+{
+
+}
+
+QSvgStructureNode::~QSvgStructureNode()
+{
+ qDeleteAll(m_renderers);
+}
+
+void QSvgG::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QList<QSvgNode*>::iterator itr = m_renderers.begin();
+ applyStyle(p, states);
+
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
+ node->draw(p, states);
+ ++itr;
+ }
+ revertStyle(p, states);
+}
+
+QSvgNode::Type QSvgG::type() const
+{
+ return G;
+}
+
+QSvgStructureNode::QSvgStructureNode(QSvgNode *parent)
+ :QSvgNode(parent)
+{
+
+}
+
+QSvgNode * QSvgStructureNode::scopeNode(const QString &id) const
+{
+ QSvgTinyDocument *doc = document();
+ return doc ? doc->namedNode(id) : 0;
+}
+
+void QSvgStructureNode::addChild(QSvgNode *child, const QString &id)
+{
+ m_renderers.append(child);
+
+ if (id.isEmpty())
+ return; //we can't add it to scope without id
+
+ QSvgTinyDocument *doc = document();
+ if (doc)
+ doc->addNamedNode(id, child);
+}
+
+QSvgDefs::QSvgDefs(QSvgNode *parent)
+ : QSvgStructureNode(parent)
+{
+}
+
+void QSvgDefs::draw(QPainter *, QSvgExtraStates &)
+{
+ //noop
+}
+
+QSvgNode::Type QSvgDefs::type() const
+{
+ return DEFS;
+}
+
+/*
+ Below is a lookup function based on the gperf output using the following set:
+
+ http://www.w3.org/Graphics/SVG/feature/1.2/#SVG
+ http://www.w3.org/Graphics/SVG/feature/1.2/#SVG-static
+ http://www.w3.org/Graphics/SVG/feature/1.2/#CoreAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Structure
+ http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessing
+ http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessingAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Image
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Prefetch
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Shape
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Text
+ http://www.w3.org/Graphics/SVG/feature/1.2/#PaintAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#OpacityAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#GraphicsAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Gradient
+ http://www.w3.org/Graphics/SVG/feature/1.2/#SolidColor
+ http://www.w3.org/Graphics/SVG/feature/1.2/#XlinkAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#ExternalResourcesRequiredAttribute
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Font
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Hyperlinking
+ http://www.w3.org/Graphics/SVG/feature/1.2/#Extensibility
+*/
+
+// ----- begin of generated code -----
+
+/* C code produced by gperf version 3.0.2 */
+/* Command-line: gperf -c -L c svg */
+/* Computed positions: -k'45-46' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+enum {
+ TOTAL_KEYWORDS = 20,
+ MIN_WORD_LENGTH = 47,
+ MAX_WORD_LENGTH = 78,
+ MIN_HASH_VALUE = 48,
+ MAX_HASH_VALUE = 88
+};
+/* maximum key range = 41, duplicates = 0 */
+
+inline static bool isSupportedSvgFeature(const QString &str)
+{
+ static const unsigned char asso_values[] = {
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 0, 89, 5,
+ 15, 5, 0, 10, 89, 89, 89, 89, 89, 0,
+ 15, 89, 89, 0, 0, 89, 5, 89, 0, 89,
+ 89, 89, 89, 89, 89, 89, 89, 0, 89, 89,
+ 89, 0, 89, 89, 0, 89, 89, 89, 0, 5,
+ 89, 0, 0, 89, 5, 89, 0, 89, 89, 89,
+ 5, 0, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89
+ };
+
+ static const char * wordlist[] = {
+ "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "",
+ "", "", "", "", "", "", "", "", "",
+ "", "", "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Text",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Shape",
+ "", "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#SVG",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Structure",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#SolidColor",
+ "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Hyperlinking",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#CoreAttribute",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#XlinkAttribute",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#SVG-static",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#OpacityAttribute",
+ "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Gradient",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Font",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Image",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessing",
+ "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Extensibility",
+ "", "", "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#GraphicsAttribute",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#Prefetch",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#PaintAttribute",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#ConditionalProcessingAttribute",
+ "", "", "", "", "", "", "", "", "",
+ "", "", "", "",
+ "http://www.w3.org/Graphics/SVG/feature/1.2/#ExternalResourcesRequiredAttribute"
+ };
+
+ if (str.length() <= MAX_WORD_LENGTH && str.length() >= MIN_WORD_LENGTH) {
+ const int key = str.length()
+ + asso_values[str.at(45).unicode()]
+ + asso_values[str.at(44).unicode()];
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ return str == QLatin1String(wordlist[key]);
+ }
+ return false;
+}
+
+// ----- end of generated code -----
+
+static inline bool isSupportedSvgExtension(const QString &)
+{
+ return false;
+}
+
+
+QSvgSwitch::QSvgSwitch(QSvgNode *parent)
+ : QSvgStructureNode(parent)
+{
+ init();
+}
+
+void QSvgSwitch::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QList<QSvgNode*>::iterator itr = m_renderers.begin();
+ applyStyle(p, states);
+
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
+ const QStringList &features = node->requiredFeatures();
+ const QStringList &extensions = node->requiredExtensions();
+ const QStringList &languages = node->requiredLanguages();
+ const QStringList &formats = node->requiredFormats();
+ const QStringList &fonts = node->requiredFonts();
+
+ bool okToRender = true;
+ if (!features.isEmpty()) {
+ QStringList::const_iterator sitr = features.constBegin();
+ for (; sitr != features.constEnd(); ++sitr) {
+ if (!isSupportedSvgFeature(*sitr)) {
+ okToRender = false;
+ break;
+ }
+ }
+ }
+
+ if (okToRender && !extensions.isEmpty()) {
+ QStringList::const_iterator sitr = extensions.constBegin();
+ for (; sitr != extensions.constEnd(); ++sitr) {
+ if (!isSupportedSvgExtension(*sitr)) {
+ okToRender = false;
+ break;
+ }
+ }
+ }
+
+ if (okToRender && !languages.isEmpty()) {
+ QStringList::const_iterator sitr = languages.constBegin();
+ okToRender = false;
+ for (; sitr != languages.constEnd(); ++sitr) {
+ if ((*sitr).startsWith(m_systemLanguagePrefix)) {
+ okToRender = true;
+ break;
+ }
+ }
+ }
+
+ if (okToRender && !formats.isEmpty()) {
+ okToRender = false;
+ }
+
+ if (okToRender && !fonts.isEmpty()) {
+ okToRender = false;
+ }
+
+ if (okToRender) {
+ node->draw(p, states);
+ break;
+ }
+ }
+ ++itr;
+ }
+ revertStyle(p, states);
+}
+
+QSvgNode::Type QSvgSwitch::type() const
+{
+ return SWITCH;
+}
+
+void QSvgSwitch::init()
+{
+ QLocale locale;
+ m_systemLanguage = locale.name().replace(QLatin1Char('_'), QLatin1Char('-'));
+ int idx = m_systemLanguage.indexOf(QLatin1Char('-'));
+ m_systemLanguagePrefix = m_systemLanguage.mid(0, idx);
+}
+
+QRectF QSvgStructureNode::bounds(QPainter *p, QSvgExtraStates &states) const
+{
+ QRectF bounds;
+ foreach(QSvgNode *node, m_renderers)
+ bounds |= node->transformedBounds(p, states);
+ return bounds;
+}
+
+QSvgNode * QSvgStructureNode::previousSiblingNode(QSvgNode *n) const
+{
+ QSvgNode *prev = 0;
+ QList<QSvgNode*>::const_iterator itr = m_renderers.constBegin();
+ while (itr != m_renderers.constEnd()) {
+ QSvgNode *node = *itr;
+ if (node == n)
+ return prev;
+ prev = node;
+ }
+ return prev;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvgstructure_p.h b/src/svg/qsvgstructure_p.h
new file mode 100644
index 0000000..f511c02
--- /dev/null
+++ b/src/svg/qsvgstructure_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGSTRUCTURE_P_H
+#define QSVGSTRUCTURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsvgnode_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtCore/qlist.h"
+#include "QtCore/qhash.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSvgTinyDocument;
+class QSvgNode;
+class QPainter;
+class QSvgDefs;
+
+class QSvgStructureNode : public QSvgNode
+{
+public:
+ QSvgStructureNode(QSvgNode *parent);
+ ~QSvgStructureNode();
+ QSvgNode *scopeNode(const QString &id) const;
+ void addChild(QSvgNode *child, const QString &id);
+ virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const;
+ QSvgNode *previousSiblingNode(QSvgNode *n) const;
+ QList<QSvgNode*> renderers() const { return m_renderers; }
+protected:
+ QList<QSvgNode*> m_renderers;
+ QHash<QString, QSvgNode*> m_scope;
+ QList<QSvgStructureNode*> m_linkedScopes;
+};
+
+class QSvgG : public QSvgStructureNode
+{
+public:
+ QSvgG(QSvgNode *parent);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ Type type() const;
+};
+
+class QSvgDefs : public QSvgStructureNode
+{
+public:
+ QSvgDefs(QSvgNode *parent);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ Type type() const;
+};
+
+class QSvgSwitch : public QSvgStructureNode
+{
+public:
+ QSvgSwitch(QSvgNode *parent);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ Type type() const;
+private:
+ void init();
+private:
+ QString m_systemLanguage;
+ QString m_systemLanguagePrefix;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGSTRUCTURE_P_H
diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp
new file mode 100644
index 0000000..06adec5
--- /dev/null
+++ b/src/svg/qsvgstyle.cpp
@@ -0,0 +1,958 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgstyle_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgfont_p.h"
+#include "qsvggraphics_p.h"
+#include "qsvgnode_p.h"
+#include "qsvgtinydocument_p.h"
+
+#include "qpainter.h"
+#include "qpair.h"
+#include "qcolor.h"
+#include "qdebug.h"
+#include "qmath.h"
+#include "qnumeric.h"
+
+QT_BEGIN_NAMESPACE
+
+QSvgExtraStates::QSvgExtraStates()
+ : fillOpacity(1.0)
+ , strokeOpacity(1.0)
+ , svgFont(0)
+ , textAnchor(Qt::AlignLeft)
+ , fontWeight(400)
+ , fillRule(Qt::WindingFill)
+ , strokeDashOffset(0)
+ , vectorEffect(false)
+{
+}
+
+QSvgStyleProperty::~QSvgStyleProperty()
+{
+}
+
+void QSvgFillStyleProperty::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+
+QSvgQualityStyle::QSvgQualityStyle(int color)
+ : m_colorRendering(color)
+{
+
+}
+void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
+{
+
+}
+void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
+{
+
+}
+
+QSvgFillStyle::QSvgFillStyle()
+ : m_style(0)
+ , m_fillRule(Qt::WindingFill)
+ , m_oldFillRule(Qt::WindingFill)
+ , m_fillOpacity(1.0)
+ , m_oldFillOpacity(0)
+ , m_gradientResolved(1)
+ , m_fillRuleSet(0)
+ , m_fillOpacitySet(0)
+ , m_fillSet(0)
+{
+}
+
+void QSvgFillStyle::setFillRule(Qt::FillRule f)
+{
+ m_fillRuleSet = 1;
+ m_fillRule = f;
+}
+
+void QSvgFillStyle::setFillOpacity(qreal opacity)
+{
+ m_fillOpacitySet = 1;
+ m_fillOpacity = opacity;
+}
+
+void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
+{
+ m_style = style;
+ m_fillSet = 1;
+}
+
+void QSvgFillStyle::setBrush(QBrush brush)
+{
+ m_fill = brush;
+ m_style = 0;
+ m_fillSet = 1;
+}
+
+void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldFill = p->brush();
+ m_oldFillRule = states.fillRule;
+ m_oldFillOpacity = states.fillOpacity;
+
+ if (m_fillRuleSet)
+ states.fillRule = m_fillRule;
+ if (m_fillSet) {
+ if (m_style)
+ p->setBrush(m_style->brush(p, states));
+ else
+ p->setBrush(m_fill);
+ }
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_fillOpacity;
+}
+
+void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_oldFillOpacity;
+ if (m_fillSet)
+ p->setBrush(m_oldFill);
+ if (m_fillRuleSet)
+ states.fillRule = m_oldFillRule;
+}
+
+QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
+ : m_viewportFill(brush)
+{
+}
+
+void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldFill = p->brush();
+ p->setBrush(m_viewportFill);
+}
+
+void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setBrush(m_oldFill);
+}
+
+QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc)
+ : m_svgFont(font)
+ , m_doc(doc)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+QSvgFontStyle::QSvgFontStyle()
+ : m_svgFont(0)
+ , m_doc(0)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+int QSvgFontStyle::SVGToQtWeight(int weight) {
+ switch (weight) {
+ case 100:
+ case 200:
+ return QFont::Light;
+ case 300:
+ case 400:
+ return QFont::Normal;
+ case 500:
+ case 600:
+ return QFont::DemiBold;
+ case 700:
+ case 800:
+ return QFont::Bold;
+ case 900:
+ return QFont::Black;
+ }
+ return QFont::Normal;
+}
+
+void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldQFont = p->font();
+ m_oldSvgFont = states.svgFont;
+ m_oldTextAnchor = states.textAnchor;
+ m_oldWeight = states.fontWeight;
+
+ if (m_textAnchorSet)
+ states.textAnchor = m_textAnchor;
+
+ QFont font = m_oldQFont;
+ if (m_familySet) {
+ states.svgFont = m_svgFont;
+ font.setFamily(m_qfont.family());
+ }
+
+ if (m_sizeSet)
+ font.setPointSizeF(m_qfont.pointSizeF());
+
+ if (m_styleSet)
+ font.setStyle(m_qfont.style());
+
+ if (m_variantSet)
+ font.setCapitalization(m_qfont.capitalization());
+
+ if (m_weightSet) {
+ if (m_weight == BOLDER) {
+ states.fontWeight = qMin(states.fontWeight + 100, 900);
+ } else if (m_weight == LIGHTER) {
+ states.fontWeight = qMax(states.fontWeight - 100, 100);
+ } else {
+ states.fontWeight = m_weight;
+ }
+ font.setWeight(SVGToQtWeight(states.fontWeight));
+ }
+
+ p->setFont(font);
+}
+
+void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ p->setFont(m_oldQFont);
+ states.svgFont = m_oldSvgFont;
+ states.textAnchor = m_oldTextAnchor;
+ states.fontWeight = m_oldWeight;
+}
+
+QSvgStrokeStyle::QSvgStrokeStyle()
+ : m_strokeOpacity(1.0)
+ , m_oldStrokeOpacity(0.0)
+ , m_strokeDashOffset(0)
+ , m_oldStrokeDashOffset(0)
+ , m_style(0)
+ , m_gradientResolved(1)
+ , m_vectorEffect(0)
+ , m_oldVectorEffect(0)
+ , m_strokeSet(0)
+ , m_strokeDashArraySet(0)
+ , m_strokeDashOffsetSet(0)
+ , m_strokeLineCapSet(0)
+ , m_strokeLineJoinSet(0)
+ , m_strokeMiterLimitSet(0)
+ , m_strokeOpacitySet(0)
+ , m_strokeWidthSet(0)
+ , m_vectorEffectSet(0)
+{
+}
+
+void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldStroke = p->pen();
+ m_oldStrokeOpacity = states.strokeOpacity;
+ m_oldStrokeDashOffset = states.strokeDashOffset;
+ m_oldVectorEffect = states.vectorEffect;
+
+ QPen pen = p->pen();
+
+ qreal oldWidth = pen.widthF();
+ qreal width = m_stroke.widthF();
+ if (oldWidth == 0)
+ oldWidth = 1;
+ if (width == 0)
+ width = 1;
+ qreal scale = oldWidth / width;
+
+ if (m_strokeOpacitySet)
+ states.strokeOpacity = m_strokeOpacity;
+
+ if (m_vectorEffectSet)
+ states.vectorEffect = m_vectorEffect;
+
+ if (m_strokeSet) {
+ if (m_style)
+ pen.setBrush(m_style->brush(p, states));
+ else
+ pen.setBrush(m_stroke.brush());
+ }
+
+ if (m_strokeWidthSet)
+ pen.setWidthF(m_stroke.widthF());
+
+ bool setDashOffsetNeeded = false;
+
+ if (m_strokeDashOffsetSet) {
+ states.strokeDashOffset = m_strokeDashOffset;
+ setDashOffsetNeeded = true;
+ }
+
+ if (m_strokeDashArraySet) {
+ if (m_stroke.style() == Qt::SolidLine) {
+ pen.setStyle(Qt::SolidLine);
+ } else if (m_strokeWidthSet || oldWidth == 1) {
+ // If both width and dash array was set, the dash array is already scaled correctly.
+ pen.setDashPattern(m_stroke.dashPattern());
+ setDashOffsetNeeded = true;
+ } else {
+ // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width.
+ QVector<qreal> dashes = m_stroke.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] /= oldWidth;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
+ }
+ } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) {
+ // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width.
+ QVector<qreal> dashes = pen.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] *= scale;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
+ }
+
+ if (m_strokeLineCapSet)
+ pen.setCapStyle(m_stroke.capStyle());
+ if (m_strokeLineJoinSet)
+ pen.setJoinStyle(m_stroke.joinStyle());
+ if (m_strokeMiterLimitSet)
+ pen.setMiterLimit(m_stroke.miterLimit());
+
+ // You can have dash offset on solid strokes in SVG files, but not in Qt.
+ // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine,
+ // so don't call the method if the pen is solid.
+ if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) {
+ qreal currentWidth = pen.widthF();
+ if (currentWidth == 0)
+ currentWidth = 1;
+ pen.setDashOffset(states.strokeDashOffset / currentWidth);
+ }
+
+ pen.setCosmetic(states.vectorEffect);
+
+ p->setPen(pen);
+}
+
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ p->setPen(m_oldStroke);
+ states.strokeOpacity = m_oldStrokeOpacity;
+ states.strokeDashOffset = m_oldStrokeDashOffset;
+ states.vectorEffect = m_oldVectorEffect;
+}
+
+void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes)
+{
+ if (m_strokeWidthSet) {
+ QVector<qreal> d = dashes;
+ qreal w = m_stroke.widthF();
+ if (w != 0 && w != 1) {
+ for (int i = 0; i < d.size(); ++i)
+ d[i] /= w;
+ }
+ m_stroke.setDashPattern(d);
+ } else {
+ m_stroke.setDashPattern(dashes);
+ }
+ m_strokeDashArraySet = 1;
+}
+
+QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
+ : m_solidColor(color)
+{
+}
+
+QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
+ : m_gradient(grad), m_gradientStopsSet(false)
+{
+}
+
+QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &)
+{
+ if (!m_link.isEmpty()) {
+ resolveStops();
+ }
+
+ // If the gradient is marked as empty, insert transparent black
+ if (!m_gradientStopsSet) {
+ m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
+ m_gradientStopsSet = true;
+ }
+
+ QBrush b(*m_gradient);
+
+ if (!m_matrix.isIdentity())
+ b.setMatrix(m_matrix);
+
+ return b;
+}
+
+
+void QSvgGradientStyle::setMatrix(const QMatrix &mat)
+{
+ m_matrix = mat;
+}
+
+QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
+ : m_transform(trans)
+{
+}
+
+void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldWorldTransform = p->worldTransform();
+ p->setWorldTransform(m_transform, true);
+}
+
+void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+}
+
+QSvgStyleProperty::Type QSvgQualityStyle::type() const
+{
+ return QUALITY;
+}
+
+QSvgStyleProperty::Type QSvgFillStyle::type() const
+{
+ return FILL;
+}
+
+QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
+{
+ return VIEWPORT_FILL;
+}
+
+QSvgStyleProperty::Type QSvgFontStyle::type() const
+{
+ return FONT;
+}
+
+QSvgStyleProperty::Type QSvgStrokeStyle::type() const
+{
+ return STROKE;
+}
+
+QSvgStyleProperty::Type QSvgSolidColorStyle::type() const
+{
+ return SOLID_COLOR;
+}
+
+QSvgStyleProperty::Type QSvgGradientStyle::type() const
+{
+ return GRADIENT;
+}
+
+QSvgStyleProperty::Type QSvgTransformStyle::type() const
+{
+ return TRANSFORM;
+}
+
+
+QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
+ : m_mode(mode)
+{
+
+}
+
+void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldMode = p->compositionMode();
+ p->setCompositionMode(m_mode);
+}
+
+void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setCompositionMode(m_oldMode);
+}
+
+QSvgStyleProperty::Type QSvgCompOpStyle::type() const
+{
+ return COMP_OP;
+}
+
+QSvgStyle::~QSvgStyle()
+{
+}
+
+void QSvgStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
+{
+ if (quality) {
+ quality->apply(p, node, states);
+ }
+
+ if (fill) {
+ fill->apply(p, node, states);
+ }
+
+ if (viewportFill) {
+ viewportFill->apply(p, node, states);
+ }
+
+ if (font) {
+ font->apply(p, node, states);
+ }
+
+ if (stroke) {
+ stroke->apply(p, node, states);
+ }
+
+ if (transform) {
+ transform->apply(p, node, states);
+ }
+
+ if (animateColor) {
+ animateColor->apply(p, node, states);
+ }
+
+ //animated transforms have to be applied
+ //_after_ the original object transformations
+ if (!animateTransforms.isEmpty()) {
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ // Find the last animateTransform with additive="replace", since this will override all
+ // previous animateTransforms.
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd();
+ do {
+ --itr;
+ if ((*itr)->animActive(totalTimeElapsed)
+ && (*itr)->additiveType() == QSvgAnimateTransform::Replace) {
+ // An animateTransform with additive="replace" will replace the transform attribute.
+ if (transform)
+ transform->revert(p, states);
+ break;
+ }
+ } while (itr != animateTransforms.constBegin());
+
+ // Apply the animateTransforms after and including the last one with additive="replace".
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->animActive(totalTimeElapsed))
+ (*itr)->apply(p, node, states);
+ }
+ }
+
+ if (opacity) {
+ opacity->apply(p, node, states);
+ }
+
+ if (compop) {
+ compop->apply(p, node, states);
+ }
+}
+
+void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ if (quality) {
+ quality->revert(p, states);
+ }
+
+ if (fill) {
+ fill->revert(p, states);
+ }
+
+ if (viewportFill) {
+ viewportFill->revert(p, states);
+ }
+
+ if (font) {
+ font->revert(p, states);
+ }
+
+ if (stroke) {
+ stroke->revert(p, states);
+ }
+
+ //animated transforms need to be reverted _before_
+ //the native transforms
+ if (!animateTransforms.isEmpty()) {
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin();
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->transformApplied()) {
+ (*itr)->revert(p, states);
+ break;
+ }
+ }
+ for (; itr != animateTransforms.constEnd(); ++itr)
+ (*itr)->clearTransformApplied();
+ }
+
+ if (transform) {
+ transform->revert(p, states);
+ }
+
+ if (animateColor) {
+ animateColor->revert(p, states);
+ }
+
+ if (opacity) {
+ opacity->revert(p, states);
+ }
+
+ if (compop) {
+ compop->revert(p, states);
+ }
+}
+
+QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs )
+ : QSvgStyleProperty(),
+ m_from(startMs), m_to(endMs), m_by(byMs),
+ m_type(Empty), m_additive(Replace), m_count(0), m_finished(false), m_transformApplied(false)
+{
+ m_totalRunningTime = m_to - m_from;
+}
+
+void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args)
+{
+ m_type = type;
+ m_args = args;
+ m_additive = additive;
+ Q_ASSERT(!(args.count()%3));
+ m_count = args.count() / 3;
+}
+
+void QSvgAnimateTransform::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
+{
+ m_oldWorldTransform = p->worldTransform();
+ resolveMatrix(node);
+ p->setWorldTransform(m_transform, true);
+ m_transformApplied = true;
+}
+
+void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+ m_transformApplied = false;
+}
+
+void QSvgAnimateTransform::resolveMatrix(const QSvgNode *node)
+{
+ static const qreal deg2rad = qreal(0.017453292519943295769);
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0) {
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+
+ if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
+ m_finished = true;
+ animationFrame = m_repeatCount;
+ }
+ }
+
+ qreal percentOfAnimation = animationFrame;
+ if (percentOfAnimation > 1) {
+ percentOfAnimation -= ((int)percentOfAnimation);
+ }
+
+ qreal currentPosition = percentOfAnimation * (m_count - 1);
+ int startElem = qFloor(currentPosition);
+ int endElem = qCeil(currentPosition);
+
+ switch(m_type)
+ {
+ case Translate: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2;
+ qreal to1, to2;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ qreal transYDiff = (to2-from2) * percentOfAnimation;
+ qreal transY = from2 + transYDiff;
+ m_transform = QTransform();
+ m_transform.translate(transX, transY);
+ break;
+ }
+ case Scale: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2;
+ qreal to1, to2;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ qreal transYDiff = (to2-from2) * percentOfAnimation;
+ qreal transY = from2 + transYDiff;
+ if (transY == 0)
+ transY = transX;
+ m_transform = QTransform();
+ m_transform.scale(transX, transY);
+ break;
+ }
+ case Rotate: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2, from3;
+ qreal to1, to2, to3;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ from3 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+ to3 = m_args[endElem++];
+
+ qreal rotationDiff = (to1 - from1) * percentOfAnimation;
+ //qreal rotation = from1 + rotationDiff;
+
+ qreal transXDiff = (to2-from2) * percentOfAnimation;
+ qreal transX = from2 + transXDiff;
+ qreal transYDiff = (to3-from3) * percentOfAnimation;
+ qreal transY = from3 + transYDiff;
+ m_transform = QTransform();
+ m_transform.translate(transX, transY);
+ m_transform.rotate(rotationDiff);
+ m_transform.translate(-transX, -transY);
+ break;
+ }
+ case SkewX: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1;
+ qreal to1;
+ from1 = m_args[startElem++];
+ to1 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ m_transform = QTransform();
+ m_transform.shear(qTan(transX * deg2rad), 0);
+ break;
+ }
+ case SkewY: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1;
+ qreal to1;
+ from1 = m_args[startElem++];
+ to1 = m_args[endElem++];
+
+
+ qreal transYDiff = (to1 - from1) * percentOfAnimation;
+ qreal transY = from1 + transYDiff;
+ m_transform = QTransform();
+ m_transform.shear(0, qTan(transY * deg2rad));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QSvgStyleProperty::Type QSvgAnimateTransform::type() const
+{
+ return ANIMATE_TRANSFORM;
+}
+
+void QSvgAnimateTransform::setFreeze(bool freeze)
+{
+ m_freeze = freeze;
+}
+
+void QSvgAnimateTransform::setRepeatCount(qreal repeatCount)
+{
+ m_repeatCount = repeatCount;
+}
+
+QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs)
+ : QSvgStyleProperty(),
+ m_from(startMs), m_to(endMs), m_by(byMs),
+ m_finished(false)
+{
+ m_totalRunningTime = m_to - m_from;
+}
+
+void QSvgAnimateColor::setArgs(bool fill,
+ const QList<QColor> &colors)
+{
+ m_fill = fill;
+ m_colors = colors;
+}
+
+void QSvgAnimateColor::setFreeze(bool freeze)
+{
+ m_freeze = freeze;
+}
+
+void QSvgAnimateColor::setRepeatCount(qreal repeatCount)
+{
+ m_repeatCount = repeatCount;
+}
+
+void QSvgAnimateColor::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
+{
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0)
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+
+ if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
+ m_finished = true;
+ animationFrame = m_repeatCount;
+ }
+
+ qreal percentOfAnimation = animationFrame;
+ if (percentOfAnimation > 1) {
+ percentOfAnimation -= ((int)percentOfAnimation);
+ }
+
+ qreal currentPosition = percentOfAnimation * (m_colors.count() - 1);
+
+ int startElem = qFloor(currentPosition);
+ int endElem = qCeil(currentPosition);
+ QColor start = m_colors[startElem];
+ QColor end = m_colors[endElem];
+
+ qreal percentOfColorMorph = currentPosition;
+ if (percentOfColorMorph > 1) {
+ percentOfColorMorph -= ((int)percentOfColorMorph);
+ }
+
+ // Interpolate between the two fixed colors start and end
+ qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph;
+ qreal rDiff = (end.red() - start.red()) * percentOfColorMorph;
+ qreal gDiff = (end.green() - start.green()) * percentOfColorMorph;
+ qreal bDiff = (end.blue() - start.blue()) * percentOfColorMorph;
+
+ int alpha = int(start.alpha() + aDiff);
+ int red = int(start.red() + rDiff);
+ int green = int(start.green() + gDiff);
+ int blue = int(start.blue() + bDiff);
+
+ QColor color(red, green, blue, alpha);
+
+ if (m_fill) {
+ QBrush b = p->brush();
+ m_oldBrush = b;
+ b.setColor(color);
+ p->setBrush(b);
+ } else {
+ QPen pen = p->pen();
+ m_oldPen = pen;
+ pen.setColor(color);
+ p->setPen(pen);
+ }
+}
+
+void QSvgAnimateColor::revert(QPainter *p, QSvgExtraStates &)
+{
+ if (m_fill) {
+ p->setBrush(m_oldBrush);
+ } else {
+ p->setPen(m_oldPen);
+ }
+}
+
+QSvgStyleProperty::Type QSvgAnimateColor::type() const
+{
+ return ANIMATE_COLOR;
+}
+
+QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
+ : m_opacity(opacity), m_oldOpacity(0)
+{
+
+}
+
+void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldOpacity = p->opacity();
+ p->setOpacity(m_opacity * m_oldOpacity);
+}
+
+void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setOpacity(m_oldOpacity);
+}
+
+QSvgStyleProperty::Type QSvgOpacityStyle::type() const
+{
+ return OPACITY;
+}
+
+void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc)
+{
+ m_link = link;
+ m_doc = doc;
+}
+
+void QSvgGradientStyle::resolveStops()
+{
+ if (!m_link.isEmpty() && m_doc) {
+ QSvgStyleProperty *prop = m_doc->styleProperty(m_link);
+ if (prop) {
+ if (prop->type() == QSvgStyleProperty::GRADIENT) {
+ QSvgGradientStyle *st =
+ static_cast<QSvgGradientStyle*>(prop);
+ st->resolveStops();
+ m_gradient->setStops(st->qgradient()->stops());
+ m_gradientStopsSet = st->gradientStopsSet();
+ }
+ } else {
+ qWarning("Could not resolve property : %s", qPrintable(m_link));
+ }
+ m_link = QString();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h
new file mode 100644
index 0000000..e53ab48
--- /dev/null
+++ b/src/svg/qsvgstyle_p.h
@@ -0,0 +1,826 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGSTYLE_P_H
+#define QSVGSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtGui/qpainter.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtGui/qpen.h"
+#include "QtGui/qbrush.h"
+#include "QtGui/qmatrix.h"
+#include "QtGui/qcolor.h"
+#include "QtGui/qfont.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+class QSvgNode;
+class QSvgFont;
+class QSvgTinyDocument;
+
+template <class T> class QSvgRefCounter
+{
+public:
+ QSvgRefCounter() { t = 0; }
+ QSvgRefCounter(T *_t)
+ {
+ t = _t;
+ if (t)
+ t->ref();
+ }
+ QSvgRefCounter(const QSvgRefCounter &other)
+ {
+ t = other.t;
+ if (t)
+ t->ref();
+ }
+ QSvgRefCounter &operator =(T *_t)
+ {
+ if(_t)
+ _t->ref();
+ if (t)
+ t->deref();
+ t = _t;
+ return *this;
+ }
+ QSvgRefCounter &operator =(const QSvgRefCounter &other)
+ {
+ if(other.t)
+ other.t->ref();
+ if (t)
+ t->deref();
+ t = other.t;
+ return *this;
+ }
+ ~QSvgRefCounter()
+ {
+ if (t)
+ t->deref();
+ }
+
+ inline T *operator->() const { return t; }
+ inline operator T*() const { return t; }
+
+private:
+ T *t;
+};
+
+class QSvgRefCounted
+{
+public:
+ QSvgRefCounted() { _ref = 0; }
+ virtual ~QSvgRefCounted() {}
+ void ref() {
+ ++_ref;
+// qDebug() << this << ": adding ref, now " << _ref;
+ }
+ void deref() {
+// qDebug() << this << ": removing ref, now " << _ref;
+ if(!--_ref) {
+// qDebug(" deleting");
+ delete this;
+ }
+ }
+private:
+ int _ref;
+};
+
+struct QSvgExtraStates
+{
+ QSvgExtraStates();
+
+ qreal fillOpacity;
+ qreal strokeOpacity;
+ QSvgFont *svgFont;
+ Qt::Alignment textAnchor;
+ int fontWeight;
+ Qt::FillRule fillRule;
+ qreal strokeDashOffset;
+ bool vectorEffect; // true if pen is cosmetic
+};
+
+class QSvgStyleProperty : public QSvgRefCounted
+{
+public:
+ enum Type
+ {
+ QUALITY,
+ FILL,
+ VIEWPORT_FILL,
+ FONT,
+ STROKE,
+ SOLID_COLOR,
+ GRADIENT,
+ TRANSFORM,
+ ANIMATE_TRANSFORM,
+ ANIMATE_COLOR,
+ OPACITY,
+ COMP_OP
+ };
+public:
+ virtual ~QSvgStyleProperty();
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) = 0;
+ virtual void revert(QPainter *p, QSvgExtraStates &states) =0;
+ virtual Type type() const=0;
+};
+
+class QSvgFillStyleProperty : public QSvgStyleProperty
+{
+public:
+ virtual QBrush brush(QPainter *p, QSvgExtraStates &states) = 0;
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+};
+
+class QSvgQualityStyle : public QSvgStyleProperty
+{
+public:
+ QSvgQualityStyle(int color);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+private:
+ // color-render ing v v 'auto' | 'optimizeSpeed' |
+ // 'optimizeQuality' | 'inherit'
+ int m_colorRendering;
+
+ // shape-rendering v v 'auto' | 'optimizeSpeed' | 'crispEdges' |
+ // 'geometricPrecision' | 'inherit'
+ //QSvgShapeRendering m_shapeRendering;
+
+
+ // text-rendering v v 'auto' | 'optimizeSpeed' | 'optimizeLegibility'
+ // | 'geometricPrecision' | 'inherit'
+ //QSvgTextRendering m_textRendering;
+
+
+ // vector-effect v x 'default' | 'non-scaling-stroke' | 'inherit'
+ //QSvgVectorEffect m_vectorEffect;
+
+ // image-rendering v v 'auto' | 'optimizeSpeed' | 'optimizeQuality' |
+ // 'inherit'
+ //QSvgImageRendering m_imageRendering;
+};
+
+
+
+class QSvgOpacityStyle : public QSvgStyleProperty
+{
+public:
+ QSvgOpacityStyle(qreal opacity);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+private:
+ qreal m_opacity;
+ qreal m_oldOpacity;
+};
+
+class QSvgFillStyle : public QSvgStyleProperty
+{
+public:
+ QSvgFillStyle();
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void setFillRule(Qt::FillRule f);
+ void setFillOpacity(qreal opacity);
+ void setFillStyle(QSvgFillStyleProperty* style);
+ void setBrush(QBrush brush);
+
+ const QBrush & qbrush() const
+ {
+ return m_fill;
+ }
+
+ qreal fillOpacity() const
+ {
+ return m_fillOpacity;
+ }
+
+ Qt::FillRule fillRule() const
+ {
+ return m_fillRule;
+ }
+
+ QSvgFillStyleProperty* style() const
+ {
+ return m_style;
+ }
+
+ void setGradientId(const QString &Id)
+ {
+ m_gradientId = Id;
+ }
+
+ QString gradientId() const
+ {
+ return m_gradientId;
+ }
+
+ void setGradientResolved(bool resolved)
+ {
+ m_gradientResolved = resolved;
+ }
+
+ bool isGradientResolved() const
+ {
+ return m_gradientResolved;
+ }
+
+private:
+ // fill v v 'inherit' | <Paint.datatype>
+ // fill-opacity v v 'inherit' | <OpacityValue.datatype>
+ QBrush m_fill;
+ QBrush m_oldFill;
+ QSvgFillStyleProperty *m_style;
+
+ Qt::FillRule m_fillRule;
+ Qt::FillRule m_oldFillRule;
+ qreal m_fillOpacity;
+ qreal m_oldFillOpacity;
+
+ QString m_gradientId;
+ uint m_gradientResolved : 1;
+
+ uint m_fillRuleSet : 1;
+ uint m_fillOpacitySet : 1;
+ uint m_fillSet : 1;
+};
+
+class QSvgViewportFillStyle : public QSvgStyleProperty
+{
+public:
+ QSvgViewportFillStyle(const QBrush &brush);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ const QBrush & qbrush() const
+ {
+ return m_viewportFill;
+ }
+private:
+ // viewport-fill v x 'inherit' | <Paint.datatype>
+ // viewport-fill-opacity v x 'inherit' | <OpacityValue.datatype>
+ QBrush m_viewportFill;
+
+ QBrush m_oldFill;
+};
+
+class QSvgFontStyle : public QSvgStyleProperty
+{
+public:
+ static const int LIGHTER = -1;
+ static const int BOLDER = 1;
+
+ QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc);
+ QSvgFontStyle();
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void setSize(qreal size)
+ {
+ // Store the _pixel_ size in the font. Since QFont::setPixelSize() only takes an int, call
+ // QFont::SetPointSize() instead. Set proper font size just before rendering.
+ m_qfont.setPointSizeF(size);
+ m_sizeSet = 1;
+ }
+
+ void setTextAnchor(Qt::Alignment anchor)
+ {
+ m_textAnchor = anchor;
+ m_textAnchorSet = 1;
+ }
+
+ void setFamily(const QString &family)
+ {
+ m_qfont.setFamily(family);
+ m_familySet = 1;
+ }
+
+ void setStyle(QFont::Style fontStyle) {
+ m_qfont.setStyle(fontStyle);
+ m_styleSet = 1;
+ }
+
+ void setVariant(QFont::Capitalization fontVariant)
+ {
+ m_qfont.setCapitalization(fontVariant);
+ m_variantSet = 1;
+ }
+
+ static int SVGToQtWeight(int weight);
+
+ void setWeight(int weight)
+ {
+ m_weight = weight;
+ m_weightSet = 1;
+ }
+
+ QSvgFont * svgFont() const
+ {
+ return m_svgFont;
+ }
+
+ const QFont &qfont() const
+ {
+ return m_qfont;
+ }
+
+ QSvgTinyDocument *doc() const {return m_doc;}
+
+private:
+ QSvgFont *m_svgFont;
+ QSvgTinyDocument *m_doc;
+ QFont m_qfont;
+
+ int m_weight;
+ Qt::Alignment m_textAnchor;
+
+ QSvgFont *m_oldSvgFont;
+ QFont m_oldQFont;
+ Qt::Alignment m_oldTextAnchor;
+ int m_oldWeight;
+
+ uint m_familySet : 1;
+ uint m_sizeSet : 1;
+ uint m_styleSet : 1;
+ uint m_variantSet : 1;
+ uint m_weightSet : 1;
+ uint m_textAnchorSet : 1;
+};
+
+class QSvgStrokeStyle : public QSvgStyleProperty
+{
+public:
+ QSvgStrokeStyle();
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void setStroke(QBrush brush)
+ {
+ m_stroke.setBrush(brush);
+ m_style = 0;
+ m_strokeSet = 1;
+ }
+
+ void setStyle(QSvgFillStyleProperty *style)
+ {
+ m_style = style;
+ m_strokeSet = 1;
+ }
+
+ void setDashArray(const QVector<qreal> &dashes);
+
+ void setDashArrayNone()
+ {
+ m_stroke.setStyle(Qt::SolidLine);
+ m_strokeDashArraySet = 1;
+ }
+
+ void setDashOffset(qreal offset)
+ {
+ m_strokeDashOffset = offset;
+ m_strokeDashOffsetSet = 1;
+ }
+
+ void setLineCap(Qt::PenCapStyle cap)
+ {
+ m_stroke.setCapStyle(cap);
+ m_strokeLineCapSet = 1;
+ }
+
+ void setLineJoin(Qt::PenJoinStyle join)
+ {
+ m_stroke.setJoinStyle(join);
+ m_strokeLineJoinSet = 1;
+ }
+
+ void setMiterLimit(qreal limit)
+ {
+ m_stroke.setMiterLimit(limit);
+ m_strokeMiterLimitSet = 1;
+ }
+
+ void setOpacity(qreal opacity)
+ {
+ m_strokeOpacity = opacity;
+ m_strokeOpacitySet = 1;
+ }
+
+ void setWidth(qreal width)
+ {
+ m_stroke.setWidthF(width);
+ m_strokeWidthSet = 1;
+ Q_ASSERT(!m_strokeDashArraySet); // set width before dash array.
+ }
+
+ qreal width()
+ {
+ return m_stroke.widthF();
+ }
+
+ void setVectorEffect(bool nonScalingStroke)
+ {
+ m_vectorEffect = nonScalingStroke;
+ m_vectorEffectSet = 1;
+ }
+
+ QSvgFillStyleProperty* style() const
+ {
+ return m_style;
+ }
+
+ void setGradientId(const QString &Id)
+ {
+ m_gradientId = Id;
+ }
+
+ QString gradientId() const
+ {
+ return m_gradientId;
+ }
+
+ void setGradientResolved(bool resolved)
+ {
+ m_gradientResolved = resolved;
+ }
+
+ bool isGradientResolved() const
+ {
+ return m_gradientResolved;
+ }
+
+ QPen stroke() const
+ {
+ return m_stroke;
+ }
+
+private:
+ // stroke v v 'inherit' | <Paint.datatype>
+ // stroke-dasharray v v 'inherit' | <StrokeDashArrayValue.datatype>
+ // stroke-dashoffset v v 'inherit' | <StrokeDashOffsetValue.datatype>
+ // stroke-linecap v v 'butt' | 'round' | 'square' | 'inherit'
+ // stroke-linejoin v v 'miter' | 'round' | 'bevel' | 'inherit'
+ // stroke-miterlimit v v 'inherit' | <StrokeMiterLimitValue.datatype>
+ // stroke-opacity v v 'inherit' | <OpacityValue.datatype>
+ // stroke-width v v 'inherit' | <StrokeWidthValue.datatype>
+ QPen m_stroke;
+ QPen m_oldStroke;
+ qreal m_strokeOpacity;
+ qreal m_oldStrokeOpacity;
+ qreal m_strokeDashOffset;
+ qreal m_oldStrokeDashOffset;
+
+ QSvgFillStyleProperty *m_style;
+ QString m_gradientId;
+ uint m_gradientResolved : 1;
+ uint m_vectorEffect : 1;
+ uint m_oldVectorEffect : 1;
+
+ uint m_strokeSet : 1;
+ uint m_strokeDashArraySet : 1;
+ uint m_strokeDashOffsetSet : 1;
+ uint m_strokeLineCapSet : 1;
+ uint m_strokeLineJoinSet : 1;
+ uint m_strokeMiterLimitSet : 1;
+ uint m_strokeOpacitySet : 1;
+ uint m_strokeWidthSet : 1;
+ uint m_vectorEffectSet : 1;
+};
+
+class QSvgSolidColorStyle : public QSvgFillStyleProperty
+{
+public:
+ QSvgSolidColorStyle(const QColor &color);
+ virtual Type type() const;
+
+ const QColor & qcolor() const
+ {
+ return m_solidColor;
+ }
+
+ QBrush brush(QPainter *, QSvgExtraStates &)
+ {
+ return m_solidColor;
+ }
+
+private:
+ // solid-color v x 'inherit' | <SVGColor.datatype>
+ // solid-opacity v x 'inherit' | <OpacityValue.datatype>
+ QColor m_solidColor;
+
+ QBrush m_oldFill;
+ QPen m_oldStroke;
+};
+
+class QSvgGradientStyle : public QSvgFillStyleProperty
+{
+public:
+ QSvgGradientStyle(QGradient *grad);
+ ~QSvgGradientStyle() { delete m_gradient; }
+ virtual Type type() const;
+
+ void setStopLink(const QString &link, QSvgTinyDocument *doc);
+ QString stopLink() const { return m_link; }
+ void resolveStops();
+
+ void setMatrix(const QMatrix &matrix);
+ QMatrix qmatrix() const
+ {
+ return m_matrix;
+ }
+
+ QGradient *qgradient() const
+ {
+ return m_gradient;
+ }
+
+ bool gradientStopsSet() const
+ {
+ return m_gradientStopsSet;
+ }
+
+ void setGradientStopsSet(bool set)
+ {
+ m_gradientStopsSet = set;
+ }
+
+ QBrush brush(QPainter *, QSvgExtraStates &);
+private:
+ QGradient *m_gradient;
+ QMatrix m_matrix;
+
+ QSvgTinyDocument *m_doc;
+ QString m_link;
+ bool m_gradientStopsSet;
+};
+
+class QSvgTransformStyle : public QSvgStyleProperty
+{
+public:
+ QSvgTransformStyle(const QTransform &transform);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ const QTransform & qtransform() const
+ {
+ return m_transform;
+ }
+private:
+ //7.6 The transform attribute
+ QTransform m_transform;
+ QTransform m_oldWorldTransform;
+};
+
+
+class QSvgAnimateTransform : public QSvgStyleProperty
+{
+public:
+ enum TransformType
+ {
+ Empty,
+ Translate,
+ Scale,
+ Rotate,
+ SkewX,
+ SkewY
+ };
+ enum Additive
+ {
+ Sum,
+ Replace
+ };
+public:
+ QSvgAnimateTransform(int startMs, int endMs, int by = 0);
+ void setArgs(TransformType type, Additive additive, const QVector<qreal> &args);
+ void setFreeze(bool freeze);
+ void setRepeatCount(qreal repeatCount);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ QSvgAnimateTransform::Additive additiveType() const
+ {
+ return m_additive;
+ }
+
+ bool animActive(qreal totalTimeElapsed)
+ {
+ if (totalTimeElapsed < m_from)
+ return false;
+ if (m_freeze || m_repeatCount < 0) // fill="freeze" or repeat="indefinite"
+ return true;
+ if (m_totalRunningTime == 0)
+ return false;
+ qreal animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+ if (animationFrame > m_repeatCount)
+ return false;
+ return true;
+ }
+
+ bool transformApplied() const
+ {
+ return m_transformApplied;
+ }
+
+ // Call this instead of revert if you know that revert is unnecessary.
+ void clearTransformApplied()
+ {
+ m_transformApplied = false;
+ }
+
+protected:
+ void resolveMatrix(const QSvgNode *node);
+private:
+ qreal m_from, m_to, m_by;
+ qreal m_totalRunningTime;
+ TransformType m_type;
+ Additive m_additive;
+ QVector<qreal> m_args;
+ int m_count;
+ QTransform m_transform;
+ QTransform m_oldWorldTransform;
+ bool m_finished;
+ bool m_freeze;
+ qreal m_repeatCount;
+ bool m_transformApplied;
+};
+
+
+class QSvgAnimateColor : public QSvgStyleProperty
+{
+public:
+ QSvgAnimateColor(int startMs, int endMs, int by = 0);
+ void setArgs(bool fill, const QList<QColor> &colors);
+ void setFreeze(bool freeze);
+ void setRepeatCount(qreal repeatCount);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+private:
+ qreal m_from, m_to, m_by;
+ qreal m_totalRunningTime;
+ QList<QColor> m_colors;
+ QBrush m_oldBrush;
+ QPen m_oldPen;
+ bool m_fill;
+ bool m_finished;
+ bool m_freeze;
+ qreal m_repeatCount;
+};
+
+
+class QSvgCompOpStyle : public QSvgStyleProperty
+{
+public:
+ QSvgCompOpStyle(QPainter::CompositionMode mode);
+ virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ const QPainter::CompositionMode & compOp() const
+ {
+ return m_mode;
+ }
+private:
+ //comp-op attribute
+ QPainter::CompositionMode m_mode;
+
+ QPainter::CompositionMode m_oldMode;
+};
+
+
+class QSvgStyle
+{
+public:
+ QSvgStyle()
+ : quality(0),
+ fill(0),
+ viewportFill(0),
+ font(0),
+ stroke(0),
+ solidColor(0),
+ gradient(0),
+ transform(0),
+ animateColor(0),
+ opacity(0),
+ compop(0)
+ {}
+ ~QSvgStyle();
+
+ void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states);
+ void revert(QPainter *p, QSvgExtraStates &states);
+ QSvgRefCounter<QSvgQualityStyle> quality;
+ QSvgRefCounter<QSvgFillStyle> fill;
+ QSvgRefCounter<QSvgViewportFillStyle> viewportFill;
+ QSvgRefCounter<QSvgFontStyle> font;
+ QSvgRefCounter<QSvgStrokeStyle> stroke;
+ QSvgRefCounter<QSvgSolidColorStyle> solidColor;
+ QSvgRefCounter<QSvgGradientStyle> gradient;
+ QSvgRefCounter<QSvgTransformStyle> transform;
+ QSvgRefCounter<QSvgAnimateColor> animateColor;
+ QList<QSvgRefCounter<QSvgAnimateTransform> > animateTransforms;
+ QSvgRefCounter<QSvgOpacityStyle> opacity;
+ QSvgRefCounter<QSvgCompOpStyle> compop;
+};
+
+/********************************************************/
+// NOT implemented:
+
+// color v v 'inherit' | <Color.datatype>
+//QColor m_color;
+
+// display v x 'inline' | 'block' | 'list-item'
+// | 'run-in' | 'compact' | 'marker' |
+// 'table' | 'inline-table' |
+// 'table-row-group' | 'table-header-group' |
+// 'table-footer-group' | 'table-row' |
+// 'table-column-group' | 'table-column' |
+// 'table-cell' | 'table-caption' |
+// 'none' | 'inherit'
+//QSvgDisplayStyle m_display;
+
+// display-align v v 'auto' | 'before' | 'center' | 'after' | 'inherit'
+//QSvgDisplayAlign m_displayAlign;
+
+// line-increment v v 'auto' | 'inherit' | <Number.datatype>
+//int m_lineIncrement;
+
+// text-anchor v v 'start' | 'middle' | 'end' | 'inherit'
+//QSvgTextAnchor m_textAnchor;
+
+// visibility v v 'visible' | 'hidden' | 'inherit'
+//QSvgVisibility m_visibility;
+
+/******************************************************/
+// the following do not make sense for us
+
+// pointer-events v v 'visiblePainted' | 'visibleFill' | 'visibleStroke' |
+// 'visible' | 'painted' | 'fill' | 'stroke' | 'all' |
+// 'none' | 'inherit'
+//QSvgPointEvents m_pointerEvents;
+
+// audio-level v x 'inherit' | <Number.datatype>
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGSTYLE_P_H
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp
new file mode 100644
index 0000000..7bd8004
--- /dev/null
+++ b/src/svg/qsvgtinydocument.cpp
@@ -0,0 +1,492 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgtinydocument_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvghandler_p.h"
+#include "qsvgfont_p.h"
+
+#include "qpainter.h"
+#include "qfile.h"
+#include "qbuffer.h"
+#include "qbytearray.h"
+#include "qqueue.h"
+#include "qstack.h"
+#include "qdebug.h"
+
+#ifndef QT_NO_COMPRESS
+#include <zlib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QSvgTinyDocument::QSvgTinyDocument()
+ : QSvgStructureNode(0)
+ , m_widthPercent(false)
+ , m_heightPercent(false)
+ , m_animated(false)
+ , m_animationDuration(0)
+ , m_fps(30)
+{
+}
+
+QSvgTinyDocument::~QSvgTinyDocument()
+{
+}
+
+#ifndef QT_NO_COMPRESS
+# ifdef QT_BUILD_INTERNAL
+Q_AUTOTEST_EXPORT QByteArray qt_inflateGZipDataFrom(QIODevice *device);
+# else
+static QByteArray qt_inflateGZipDataFrom(QIODevice *device);
+# endif
+
+QByteArray qt_inflateGZipDataFrom(QIODevice *device)
+{
+ if (!device)
+ return QByteArray();
+
+ if (!device->isOpen())
+ device->open(QIODevice::ReadOnly);
+
+ Q_ASSERT(device->isOpen() && device->isReadable());
+
+ static const int CHUNK_SIZE = 4096;
+ int zlibResult = Z_OK;
+
+ QByteArray source;
+ QByteArray destination;
+
+ // Initialize zlib stream struct
+ z_stream zlibStream;
+ zlibStream.next_in = Z_NULL;
+ zlibStream.avail_in = 0;
+ zlibStream.avail_out = 0;
+ zlibStream.zalloc = Z_NULL;
+ zlibStream.zfree = Z_NULL;
+ zlibStream.opaque = Z_NULL;
+
+ // Adding 16 to the window size gives us gzip decoding
+ if (inflateInit2(&zlibStream, MAX_WBITS + 16) != Z_OK) {
+ qWarning("Cannot initialize zlib, because: %s",
+ (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
+ return QByteArray();
+ }
+
+ bool stillMoreWorkToDo = true;
+ while (stillMoreWorkToDo) {
+
+ if (!zlibStream.avail_in) {
+ source = device->read(CHUNK_SIZE);
+
+ if (source.isEmpty())
+ break;
+
+ zlibStream.avail_in = source.size();
+ zlibStream.next_in = reinterpret_cast<Bytef*>(source.data());
+ }
+
+ do {
+ // Prepare the destination buffer
+ int oldSize = destination.size();
+ destination.resize(oldSize + CHUNK_SIZE);
+ zlibStream.next_out = reinterpret_cast<Bytef*>(
+ destination.data() + oldSize - zlibStream.avail_out);
+ zlibStream.avail_out += CHUNK_SIZE;
+
+ zlibResult = inflate(&zlibStream, Z_NO_FLUSH);
+ switch (zlibResult) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ case Z_STREAM_ERROR:
+ case Z_MEM_ERROR: {
+ inflateEnd(&zlibStream);
+ qWarning("Error while inflating gzip file: %s",
+ (zlibStream.msg != NULL ? zlibStream.msg : "Unknown error"));
+ destination.chop(zlibStream.avail_out);
+ return destination;
+ }
+ }
+
+ // If the output buffer still has more room after calling inflate
+ // it means we have to provide more data, so exit the loop here
+ } while (!zlibStream.avail_out);
+
+ if (zlibResult == Z_STREAM_END) {
+ // Make sure there are no more members to process before exiting
+ if (!(zlibStream.avail_in && inflateReset(&zlibStream) == Z_OK))
+ stillMoreWorkToDo = false;
+ }
+ }
+
+ // Chop off trailing space in the buffer
+ destination.chop(zlibStream.avail_out);
+
+ inflateEnd(&zlibStream);
+ return destination;
+}
+#endif
+
+QSvgTinyDocument * QSvgTinyDocument::load(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly)) {
+ qWarning("Cannot open file '%s', because: %s",
+ qPrintable(fileName), qPrintable(file.errorString()));
+ return 0;
+ }
+
+#ifndef QT_NO_COMPRESS
+ if (fileName.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)
+ || fileName.endsWith(QLatin1String(".svg.gz"), Qt::CaseInsensitive)) {
+ return load(qt_inflateGZipDataFrom(&file));
+ }
+#endif
+
+ QSvgTinyDocument *doc = 0;
+ QSvgHandler handler(&file);
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ } else {
+ qWarning("Cannot read file '%s', because: %s (line %d)",
+ qPrintable(fileName), qPrintable(handler.errorString()), handler.lineNumber());
+ }
+ return doc;
+}
+
+QSvgTinyDocument * QSvgTinyDocument::load(const QByteArray &contents)
+{
+#ifndef QT_NO_COMPRESS
+ // Check for gzip magic number and inflate if appropriate
+ if (contents.startsWith("\x1f\x8b")) {
+ QBuffer buffer(const_cast<QByteArray *>(&contents));
+ return load(qt_inflateGZipDataFrom(&buffer));
+ }
+#endif
+
+ QSvgHandler handler(contents);
+
+ QSvgTinyDocument *doc = 0;
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ }
+ return doc;
+}
+
+QSvgTinyDocument * QSvgTinyDocument::load(QXmlStreamReader *contents)
+{
+ QSvgHandler handler(contents);
+
+ QSvgTinyDocument *doc = 0;
+ if (handler.ok()) {
+ doc = handler.document();
+ doc->m_animationDuration = handler.animationDuration();
+ }
+ return doc;
+}
+
+void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
+{
+ if (m_time.isNull()) {
+ m_time.start();
+ }
+
+ if (displayMode() == QSvgNode::NoneMode)
+ return;
+
+ p->save();
+ //sets default style on the painter
+ //### not the most optimal way
+ mapSourceToTarget(p, bounds);
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
+ p->setBrush(Qt::black);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+ QList<QSvgNode*>::iterator itr = m_renderers.begin();
+ applyStyle(p, m_states);
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
+ node->draw(p, m_states);
+ ++itr;
+ }
+ revertStyle(p, m_states);
+ p->restore();
+}
+
+
+void QSvgTinyDocument::draw(QPainter *p, const QString &id,
+ const QRectF &bounds)
+{
+ QSvgNode *node = scopeNode(id);
+
+ if (!node) {
+ qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
+ return;
+ }
+ if (m_time.isNull()) {
+ m_time.start();
+ }
+
+ if (node->displayMode() == QSvgNode::NoneMode)
+ return;
+
+ p->save();
+
+ const QRectF elementBounds = node->transformedBounds();
+
+ mapSourceToTarget(p, bounds, elementBounds);
+ QTransform originalTransform = p->worldTransform();
+
+ //XXX set default style on the painter
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
+ p->setBrush(Qt::black);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ QStack<QSvgNode*> parentApplyStack;
+ QSvgNode *parent = node->parent();
+ while (parent) {
+ parentApplyStack.push(parent);
+ parent = parent->parent();
+ }
+
+ for (int i = parentApplyStack.size() - 1; i >= 0; --i)
+ parentApplyStack[i]->applyStyle(p, m_states);
+
+ // Reset the world transform so that our parents don't affect
+ // the position
+ QTransform currentTransform = p->worldTransform();
+ p->setWorldTransform(originalTransform);
+
+ node->draw(p, m_states);
+
+ p->setWorldTransform(currentTransform);
+
+ for (int i = 0; i < parentApplyStack.size(); ++i)
+ parentApplyStack[i]->revertStyle(p, m_states);
+
+ //p->fillRect(bounds.adjusted(-5, -5, 5, 5), QColor(0, 0, 255, 100));
+
+ p->restore();
+}
+
+
+QSvgNode::Type QSvgTinyDocument::type() const
+{
+ return DOC;
+}
+
+void QSvgTinyDocument::setWidth(int len, bool percent)
+{
+ m_size.setWidth(len);
+ m_widthPercent = percent;
+}
+
+void QSvgTinyDocument::setHeight(int len, bool percent)
+{
+ m_size.setHeight(len);
+ m_heightPercent = percent;
+}
+
+void QSvgTinyDocument::setViewBox(const QRectF &rect)
+{
+ m_viewBox = rect;
+}
+
+void QSvgTinyDocument::addSvgFont(QSvgFont *font)
+{
+ m_fonts.insert(font->familyName(), font);
+}
+
+QSvgFont * QSvgTinyDocument::svgFont(const QString &family) const
+{
+ return m_fonts[family];
+}
+
+void QSvgTinyDocument::addNamedNode(const QString &id, QSvgNode *node)
+{
+ m_namedNodes.insert(id, node);
+}
+
+QSvgNode *QSvgTinyDocument::namedNode(const QString &id) const
+{
+ return m_namedNodes.value(id);
+}
+
+void QSvgTinyDocument::addNamedStyle(const QString &id, QSvgFillStyleProperty *style)
+{
+ m_namedStyles.insert(id, style);
+}
+
+QSvgFillStyleProperty *QSvgTinyDocument::namedStyle(const QString &id) const
+{
+ return m_namedStyles.value(id);
+}
+
+void QSvgTinyDocument::restartAnimation()
+{
+ m_time.restart();
+}
+
+bool QSvgTinyDocument::animated() const
+{
+ return m_animated;
+}
+
+void QSvgTinyDocument::setAnimated(bool a)
+{
+ m_animated = a;
+}
+
+void QSvgTinyDocument::draw(QPainter *p)
+{
+ draw(p, QRectF());
+}
+
+void QSvgTinyDocument::draw(QPainter *p, QSvgExtraStates &)
+{
+ draw(p);
+}
+
+void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect)
+{
+ QRectF target = targetRect;
+ if (target.isNull()) {
+ QPaintDevice *dev = p->device();
+ QRectF deviceRect(0, 0, dev->width(), dev->height());
+ if (deviceRect.isNull()) {
+ if (sourceRect.isNull())
+ target = QRectF(QPointF(0, 0), size());
+ else
+ target = QRectF(QPointF(0, 0), sourceRect.size());
+ } else {
+ target = deviceRect;
+ }
+ }
+
+ QRectF source = sourceRect;
+ if (source.isNull())
+ source = viewBox();
+
+ if (source != target && !source.isNull()) {
+ QTransform transform;
+ transform.scale(target.width() / source.width(),
+ target.height() / source.height());
+ QRectF c2 = transform.mapRect(source);
+ p->translate(target.x() - c2.x(),
+ target.y() - c2.y());
+ p->scale(target.width() / source.width(),
+ target.height() / source.height());
+ }
+}
+
+QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const
+{
+ const QSvgNode *node = scopeNode(id);
+ if (!node)
+ node = this;
+ return node->transformedBounds();
+}
+
+bool QSvgTinyDocument::elementExists(const QString &id) const
+{
+ QSvgNode *node = scopeNode(id);
+
+ return (node!=0);
+}
+
+QMatrix QSvgTinyDocument::matrixForElement(const QString &id) const
+{
+ QSvgNode *node = scopeNode(id);
+
+ if (!node) {
+ qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
+ return QMatrix();
+ }
+
+ QTransform t;
+
+ node = node->parent();
+ while (node) {
+ if (node->m_style.transform)
+ t *= node->m_style.transform->qtransform();
+ node = node->parent();
+ }
+
+ return t.toAffine();
+}
+
+int QSvgTinyDocument::currentFrame() const
+{
+ double runningPercentage = qMin(m_time.elapsed()/double(m_animationDuration), 1.);
+
+ int totalFrames = m_fps * m_animationDuration;
+
+ return int(runningPercentage * totalFrames);
+}
+
+void QSvgTinyDocument::setCurrentFrame(int frame)
+{
+ int totalFrames = m_fps * m_animationDuration;
+ double framePercentage = frame/double(totalFrames);
+ double timeForFrame = m_animationDuration * framePercentage; //in S
+ timeForFrame *= 1000; //in ms
+ int timeToAdd = int(timeForFrame - m_time.elapsed());
+ m_time = m_time.addMSecs(timeToAdd);
+}
+
+void QSvgTinyDocument::setFramesPerSecond(int num)
+{
+ m_fps = num;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h
new file mode 100644
index 0000000..e54a41f
--- /dev/null
+++ b/src/svg/qsvgtinydocument_p.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGTINYDOCUMENT_P_H
+#define QSVGTINYDOCUMENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsvgstructure_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "QtCore/qrect.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qhash.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qxmlstream.h"
+#include "qsvgstyle_p.h"
+#include "qsvgfont_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QPainter;
+class QByteArray;
+class QSvgFont;
+
+class Q_SVG_EXPORT QSvgTinyDocument : public QSvgStructureNode
+{
+public:
+ static QSvgTinyDocument * load(const QString &file);
+ static QSvgTinyDocument * load(const QByteArray &contents);
+ static QSvgTinyDocument * load(QXmlStreamReader *contents);
+public:
+ QSvgTinyDocument();
+ ~QSvgTinyDocument();
+ Type type() const;
+
+ QSize size() const;
+ void setWidth(int len, bool percent);
+ void setHeight(int len, bool percent);
+ int width() const;
+ int height() const;
+ bool widthPercent() const;
+ bool heightPercent() const;
+
+ bool preserveAspectRatio() const;
+
+ QRectF viewBox() const;
+ void setViewBox(const QRectF &rect);
+
+ virtual void draw(QPainter *p, QSvgExtraStates &);//from the QSvgNode
+
+ void draw(QPainter *p);
+ void draw(QPainter *p, const QRectF &bounds);
+ void draw(QPainter *p, const QString &id,
+ const QRectF &bounds=QRectF());
+
+ QMatrix matrixForElement(const QString &id) const;
+ QRectF boundsOnElement(const QString &id) const;
+ bool elementExists(const QString &id) const;
+
+ void addSvgFont(QSvgFont *);
+ QSvgFont *svgFont(const QString &family) const;
+ void addNamedNode(const QString &id, QSvgNode *node);
+ QSvgNode *namedNode(const QString &id) const;
+ void addNamedStyle(const QString &id, QSvgFillStyleProperty *style);
+ QSvgFillStyleProperty *namedStyle(const QString &id) const;
+
+ void restartAnimation();
+ int currentElapsed() const;
+ bool animated() const;
+ void setAnimated(bool a);
+ int animationDuration() const;
+ int currentFrame() const;
+ void setCurrentFrame(int);
+ void setFramesPerSecond(int num);
+private:
+ void mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect = QRectF());
+private:
+ QSize m_size;
+ bool m_widthPercent;
+ bool m_heightPercent;
+
+ mutable QRectF m_viewBox;
+
+ QHash<QString, QSvgRefCounter<QSvgFont> > m_fonts;
+ QHash<QString, QSvgNode *> m_namedNodes;
+ QHash<QString, QSvgRefCounter<QSvgFillStyleProperty> > m_namedStyles;
+
+ QTime m_time;
+ bool m_animated;
+ int m_animationDuration;
+ int m_fps;
+
+ QSvgExtraStates m_states;
+};
+
+inline QSize QSvgTinyDocument::size() const
+{
+ if (m_size.isEmpty()) {
+ return viewBox().size().toSize();
+ } else {
+ return m_size;
+ }
+}
+
+inline int QSvgTinyDocument::width() const
+{
+ return size().width();
+}
+
+inline int QSvgTinyDocument::height() const
+{
+ return size().height();
+}
+
+inline bool QSvgTinyDocument::widthPercent() const
+{
+ return m_widthPercent;
+}
+
+inline bool QSvgTinyDocument::heightPercent() const
+{
+ return m_heightPercent;
+}
+
+inline QRectF QSvgTinyDocument::viewBox() const
+{
+ if (m_viewBox.isNull())
+ m_viewBox = transformedBounds();
+
+ return m_viewBox;
+}
+
+inline bool QSvgTinyDocument::preserveAspectRatio() const
+{
+ return false;
+}
+
+inline int QSvgTinyDocument::currentElapsed() const
+{
+ return m_time.elapsed();
+}
+
+inline int QSvgTinyDocument::animationDuration() const
+{
+ return m_animationDuration;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG
+#endif // QSVGTINYDOCUMENT_P_H
diff --git a/src/svg/qsvgwidget.cpp b/src/svg/qsvgwidget.cpp
new file mode 100644
index 0000000..d43db92
--- /dev/null
+++ b/src/svg/qsvgwidget.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgwidget.h"
+
+#ifndef QT_NO_SVGWIDGET
+
+#include "qsvgrenderer.h"
+
+#include "qpainter.h"
+#include "private/qwidget_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSvgWidget
+ \ingroup painting
+
+ \brief The QSvgWidget class provides a widget that is used to display the contents of
+ Scalable Vector Graphics (SVG) files.
+ \since 4.1
+
+ This class enables developers to display SVG drawings alongside standard widgets, and
+ is used in much the same way as QLabel is used for displaying text and bitmap images.
+
+ Since QSvgWidget is a subclass of QWidget, SVG drawings are rendered using the properties
+ of the display. More control can be exercised over the rendering process with the
+ QSvgRenderer class, as this can be used to paint onto other paint devices, such as QImage
+ and QGLWidget. The renderer used by the widget can be obtained with the renderer()
+ function.
+
+ Each QSvgWidget can be constructed with the file name of a SVG file, or they can be
+ constructed without a specific file to render and one can be supplied later. The load()
+ functions provide two different ways to load an SVG file: they accept either the file name
+ of an SVG file or a QByteArray containing the serialized XML representation of an SVG file.
+
+ By default, the widget provides a size hint to reflect the size of the drawing that it
+ displays. If no data has been loaded, the widget provides the default QWidget size hint.
+ Subclass this class and reimplement sizeHint() if you need to customize this behavior.
+
+ \sa QSvgRenderer, {QtSvg Module}, QPicture
+*/
+
+class QSvgWidgetPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QSvgWidget)
+public:
+ QSvgRenderer *renderer;
+};
+
+/*!
+ Constructs a new SVG display widget with the given \a parent.
+*/
+QSvgWidget::QSvgWidget(QWidget *parent)
+ : QWidget(*new QSvgWidgetPrivate, parent, 0)
+{
+ d_func()->renderer = new QSvgRenderer(this);
+ QObject::connect(d_func()->renderer, SIGNAL(repaintNeeded()),
+ this, SLOT(update()));
+}
+
+/*!
+ Constructs a new SVG display widget with the given \a parent and loads the contents
+ of the specified \a file.
+*/
+QSvgWidget::QSvgWidget(const QString &file, QWidget *parent)
+ : QWidget(*new QSvgWidgetPrivate, parent, 0)
+{
+ d_func()->renderer = new QSvgRenderer(file, this);
+ QObject::connect(d_func()->renderer, SIGNAL(repaintNeeded()),
+ this, SLOT(update()));
+}
+
+/*!
+ Destroys the widget.
+*/
+QSvgWidget::~QSvgWidget()
+{
+
+}
+
+/*!
+ Returns the renderer used to display the contents of the widget.
+*/
+QSvgRenderer * QSvgWidget::renderer() const
+{
+ Q_D(const QSvgWidget);
+ return d->renderer;
+}
+
+
+/*!
+ \reimp
+*/
+QSize QSvgWidget::sizeHint() const
+{
+ Q_D(const QSvgWidget);
+ if (d->renderer->isValid())
+ return d->renderer->defaultSize();
+ else
+ return QSize(128, 64);
+}
+
+
+/*!
+ \reimp
+*/
+void QSvgWidget::paintEvent(QPaintEvent *)
+{
+ Q_D(QSvgWidget);
+ QPainter p(this);
+ d->renderer->render(&p);
+}
+
+/*!
+ Loads the contents of the specified SVG \a file and updates the widget.
+*/
+void QSvgWidget::load(const QString &file)
+{
+ Q_D(const QSvgWidget);
+ d->renderer->load(file);
+}
+
+/*!
+ Loads the specified SVG format \a contents and updates the widget.
+*/
+void QSvgWidget::load(const QByteArray &contents)
+{
+ Q_D(const QSvgWidget);
+ d->renderer->load(contents);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVGWIDGET
diff --git a/src/svg/qsvgwidget.h b/src/svg/qsvgwidget.h
new file mode 100644
index 0000000..d404e36
--- /dev/null
+++ b/src/svg/qsvgwidget.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSVGWIDGET_H
+#define QSVGWIDGET_H
+
+#include <QtGui/qwidget.h>
+
+#ifndef QT_NO_SVGWIDGET
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Svg)
+
+class QSvgWidgetPrivate;
+class QPaintEvent;
+class QSvgRenderer;
+
+class Q_SVG_EXPORT QSvgWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QSvgWidget(QWidget *parent=0);
+ QSvgWidget(const QString &file, QWidget *parent=0);
+ ~QSvgWidget();
+
+ QSvgRenderer *renderer() const;
+
+ QSize sizeHint() const;
+public Q_SLOTS:
+ void load(const QString &file);
+ void load(const QByteArray &contents);
+protected:
+ void paintEvent(QPaintEvent *event);
+private:
+ Q_DISABLE_COPY(QSvgWidget)
+ Q_DECLARE_PRIVATE(QSvgWidget)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QT_NO_SVGWIDGET
+#endif // QSVGWIDGET_H
diff --git a/src/svg/svg.pro b/src/svg/svg.pro
new file mode 100644
index 0000000..79f284a
--- /dev/null
+++ b/src/svg/svg.pro
@@ -0,0 +1,45 @@
+TARGET = QtSvg
+QPRO_PWD = $$PWD
+QT = core gui
+DEFINES += QT_BUILD_SVG_LIB
+DEFINES += QT_NO_USING_NAMESPACE
+win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000
+solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2
+
+unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
+
+include(../qbase.pri)
+
+
+HEADERS += \
+ qsvggraphics_p.h \
+ qsvghandler_p.h \
+ qsvgnode_p.h \
+ qsvgstructure_p.h \
+ qsvgstyle_p.h \
+ qsvgfont_p.h \
+ qsvgtinydocument_p.h \
+ qsvgrenderer.h \
+ qsvgwidget.h \
+ qgraphicssvgitem.h \
+ qsvggenerator.h
+
+
+SOURCES += \
+ qsvggraphics.cpp \
+ qsvghandler.cpp \
+ qsvgnode.cpp \
+ qsvgstructure.cpp \
+ qsvgstyle.cpp \
+ qsvgfont.cpp \
+ qsvgtinydocument.cpp \
+ qsvgrenderer.cpp \
+ qsvgwidget.cpp \
+ qgraphicssvgitem.cpp \
+ qsvggenerator.cpp
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src
+
+symbian:TARGET.UID3=0x2001B2E2
+
+include(../3rdparty/zlib_dependency.pri)