summaryrefslogtreecommitdiff
path: root/src/svg
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit983a4b61f1da719dac647a7190533d1edf2b2159 (patch)
tree3b52101d36c2b432540c20e0e188f62197c42c59 /src/svg
downloadqtsvg-983a4b61f1da719dac647a7190533d1edf2b2159.tar.gz
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/svg')
-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
23 files changed, 11361 insertions, 0 deletions
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)