summaryrefslogtreecommitdiff
path: root/src/svg
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:18:55 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:18:55 +0100
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/svg
downloadqt4-tools-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Long live Qt 4.5!
Diffstat (limited to 'src/svg')
-rw-r--r--src/svg/qgraphicssvgitem.cpp376
-rw-r--r--src/svg/qgraphicssvgitem.h105
-rw-r--r--src/svg/qsvgfont.cpp142
-rw-r--r--src/svg/qsvgfont_p.h103
-rw-r--r--src/svg/qsvggenerator.cpp1052
-rw-r--r--src/svg/qsvggenerator.h111
-rw-r--r--src/svg/qsvggraphics.cpp642
-rw-r--r--src/svg/qsvggraphics_p.h247
-rw-r--r--src/svg/qsvghandler.cpp3697
-rw-r--r--src/svg/qsvghandler_p.h185
-rw-r--r--src/svg/qsvgnode.cpp330
-rw-r--r--src/svg/qsvgnode_p.h205
-rw-r--r--src/svg/qsvgrenderer.cpp501
-rw-r--r--src/svg/qsvgrenderer.h120
-rw-r--r--src/svg/qsvgstructure.cpp424
-rw-r--r--src/svg/qsvgstructure_p.h120
-rw-r--r--src/svg/qsvgstyle.cpp820
-rw-r--r--src/svg/qsvgstyle_p.h564
-rw-r--r--src/svg/qsvgtinydocument.cpp459
-rw-r--r--src/svg/qsvgtinydocument_p.h195
-rw-r--r--src/svg/qsvgwidget.cpp183
-rw-r--r--src/svg/qsvgwidget.h85
-rw-r--r--src/svg/svg.pro48
23 files changed, 10714 insertions, 0 deletions
diff --git a/src/svg/qgraphicssvgitem.cpp b/src/svg/qgraphicssvgitem.cpp
new file mode 100644
index 0000000000..e17df03b4b
--- /dev/null
+++ b/src/svg/qgraphicssvgitem.cpp
@@ -0,0 +1,376 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QGraphicsSvgItem)
+
+ QGraphicsSvgItemPrivate()
+ : renderer(0), shared(false)
+ {
+ }
+
+ void init()
+ {
+ Q_Q(QGraphicsSvgItem);
+ 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 multimedia
+ \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 setSize() method 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)
+ : QObject(*new QGraphicsSvgItemPrivate(), 0), QGraphicsItem(parent)
+{
+ Q_D(QGraphicsSvgItem);
+ d->init();
+}
+
+/*!
+ 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)
+ : QObject(*new QGraphicsSvgItemPrivate(), 0), QGraphicsItem(parent)
+{
+ Q_D(QGraphicsSvgItem);
+ d->init();
+ 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 (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1))
+ 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;
+}
+
+
+/*!
+ 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();
+}
+
+/*!
+ Sets the XML ID of the element that this item should render 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 renderer. 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 0000000000..c6ab0f70a8
--- /dev/null
+++ b/src/svg/qgraphicssvgitem.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QGRAPHICSSVGITEM_H
+#define QGRAPHICSSVGITEM_H
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtCore/qobject.h>
+
+#ifndef QT_NO_GRAPHICSSVGITEM
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Svg)
+
+class QSvgRenderer;
+class QGraphicsSvgItemPrivate;
+
+class Q_SVG_EXPORT QGraphicsSvgItem : public QObject, public QGraphicsItem
+{
+ Q_OBJECT
+
+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_WITH_BASE(QGraphicsSvgItem, QObject)
+ inline QGraphicsSvgItemPrivate *d_func()
+ { return reinterpret_cast<QGraphicsSvgItemPrivate *>(QObject::d_ptr); }
+ inline const QGraphicsSvgItemPrivate *d_func() const
+ { return reinterpret_cast<const QGraphicsSvgItemPrivate *>(QObject::d_ptr); }
+ friend class QGraphicsSvgItemPrivate;
+
+ 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 0000000000..cf90fb76b8
--- /dev/null
+++ b/src/svg/qsvgfont.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 0000000000..069d8beb21
--- /dev/null
+++ b/src/svg/qsvgfont_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 0000000000..f7b2ae8a4b
--- /dev/null
+++ b/src/svg/qsvggenerator.cpp
@@ -0,0 +1,1052 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 = 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("vector-effect=\"non-scaling-stroke\" ");
+ *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::fromLatin1("%1").arg(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) {
+ width = QLatin1String("1");
+ stream() << "vector-effect=\"non-scaling-stroke\" ";
+ }
+ else
+ width = QString::number(spen.widthF());
+ stream() <<"stroke-width=\""<<width<<"\" ";
+
+ 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\" ";
+ stream() << "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\" ";
+ stream() << "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 << "\" ";
+ stream() << "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 multimedia
+ \since 4.3
+ \brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
+ \reentrant
+
+ \sa QSvgRenderer, QSvgWidget
+*/
+
+/*!
+ 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;
+ delete d_ptr;
+}
+
+/*!
+ \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()<<"\" ";
+ stream() << "y=\""<<r.y()<<"\" ";
+ stream() << "width=\""<<r.width()<<"\" ";
+ stream() << "height=\""<<r.height()<<"\" ";
+
+ QByteArray data;
+ QBuffer buffer(&data);
+ buffer.open(QBuffer::ReadWrite);
+ image.save(&buffer, "PNG");
+ buffer.close();
+ stream() << "xlink:href=\"data:image/png;base64,"
+ << data.toBase64()
+ <<"\" ";
+ stream() << "/>\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 (!qFuzzyCompare(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 ";
+
+
+ *d->stream << "fill-rule=";
+ if (p.fillRule() == Qt::OddEvenFill)
+ *d->stream << "\"evenodd\" ";
+ else
+ *d->stream << "\"nonzero\" ";
+
+ *d->stream << "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\" 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\" "
+ << "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 0000000000..723a220cd6
--- /dev/null
+++ b/src/svg/qsvggenerator.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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>
+
+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:
+ 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 0000000000..9ff9c26387
--- /dev/null
+++ b/src/svg/qsvggraphics.cpp
@@ -0,0 +1,642 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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) \
+ applyStyle(p, states); \
+ qreal oldOpacity = p->opacity(); \
+ QBrush oldBrush = p->brush(); \
+ QPen oldPen = p->pen(); \
+ p->setPen(Qt::NoPen); \
+ p->setOpacity(oldOpacity * states.fillOpacity); \
+ command; \
+ p->setOpacity(oldOpacity); \
+ p->setPen(oldPen); \
+ p->setBrush(Qt::NoBrush); \
+ command; \
+ p->setBrush(oldBrush); \
+ revertStyle(p, states);
+
+
+void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
+{
+ qWarning("<animation> no implemented");
+}
+
+static inline QRectF boundsOnStroke(const QPainterPath &path, qreal width)
+{
+ QPainterPathStroker stroker;
+ stroker.setWidth(width);
+ QPainterPath stroke = stroker.createStroke(path);
+ return stroke.boundingRect();
+}
+
+QSvgCircle::QSvgCircle(QSvgNode *parent, const QRectF &rect)
+ : QSvgNode(parent), m_bounds(rect)
+{
+}
+
+
+QRectF QSvgCircle::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_bounds;
+ else {
+ QPainterPath path;
+ path.addRect(m_bounds);
+ return boundsOnStroke(path, sw);
+ }
+}
+
+void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+}
+
+QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
+ : QSvgNode(parent), cubic(path)
+{
+ m_cachedBounds = path.boundingRect();
+}
+
+void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ p->drawPath(cubic);
+ revertStyle(p, states);
+}
+
+QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
+ : QSvgNode(parent), m_bounds(rect)
+{
+}
+
+QRectF QSvgEllipse::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_bounds;
+ else {
+ QPainterPath path;
+ path.addEllipse(m_bounds);
+ return boundsOnStroke(path, sw);
+ }
+}
+
+void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+}
+
+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_bounds(line)
+{
+}
+
+
+void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ p->drawLine(m_bounds);
+ revertStyle(p, states);
+}
+
+QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
+ : QSvgNode(parent), m_path(qpath)
+{
+ //m_cachedBounds = m_path.controlPointRect();
+ m_cachedBounds = m_path.boundingRect();
+}
+
+void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+}
+
+QRectF QSvgPath::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_cachedBounds;
+ else {
+ return boundsOnStroke(m_path, sw);
+ }
+}
+
+QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
+ : QSvgNode(parent), m_poly(poly)
+{
+
+}
+
+QRectF QSvgPolygon::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_poly.boundingRect();
+ else {
+ QPainterPath path;
+ path.addPolygon(m_poly);
+ return boundsOnStroke(path, sw);
+ }
+}
+
+void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
+{
+ QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly));
+}
+
+
+QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
+ : QSvgNode(parent), m_poly(poly)
+{
+
+}
+
+void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+ if (p->brush().style() != Qt::NoBrush) {
+ QPen save = p->pen();
+ p->setPen(QPen(Qt::NoPen));
+ p->drawPolygon(m_poly);
+ p->setPen(save);
+ }
+ p->drawPolyline(m_poly);
+ 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() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_rect;
+ else {
+ QPainterPath path;
+ path.addRect(m_rect);
+ return boundsOnStroke(path, sw);
+ }
+}
+
+void QSvgRect::draw(QPainter *p, QSvgExtraStates &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));
+ }
+}
+
+QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
+ : QSvgNode(parent)
+ , m_coord(coord)
+ , m_textAlignment(Qt::AlignLeft)
+ , m_scale(1)
+ , m_appendSpace(false)
+ , m_type(TEXT)
+ , m_size(0, 0)
+{
+ m_paragraphs.push_back(QString());
+ m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
+}
+
+QSvgText::~QSvgText()
+{
+}
+
+void QSvgText::setTextArea(const QSizeF &size)
+{
+ m_size = size;
+ m_type = TEXTAREA;
+}
+
+//QRectF QSvgText::bounds() const {}
+
+void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
+{
+ applyStyle(p, states);
+
+ QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
+ styleProperty(QSvgStyleProperty::FONT));
+ if (fontStyle && fontStyle->svgFont()) {
+ // SVG fonts not fully supported...
+ QString text = m_paragraphs.front();
+ for (int i = 1; i < m_paragraphs.size(); ++i) {
+ text.append(QLatin1Char('\n'));
+ text.append(m_paragraphs[i]);
+ }
+ fontStyle->svgFont()->draw(p, m_coord, text, fontStyle->pointSize(), m_textAlignment);
+ revertStyle(p, states);
+ return;
+ }
+
+ // Scale the font to its correct size.
+ QTransform oldTransform = p->worldTransform();
+ p->scale(1 / m_scale, 1 / m_scale);
+
+ qreal y = 0;
+ bool initial = true;
+ qreal px = m_coord.x() * m_scale;
+ qreal py = m_coord.y() * m_scale;
+ QSizeF scaledSize = m_size * m_scale;
+
+ if (m_type == TEXTAREA) {
+ if (m_textAlignment == Qt::AlignHCenter)
+ px += scaledSize.width() / 2;
+ else if (m_textAlignment == Qt::AlignRight)
+ px += scaledSize.width();
+ }
+
+ QRectF bounds;
+ if (m_size.height() != 0)
+ bounds = QRectF(0, 0, 1, scaledSize.height());
+
+ for (int i = 0; i < m_paragraphs.size(); ++i) {
+ QTextLayout tl(m_paragraphs[i]);
+ QTextOption op = tl.textOption();
+ op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ tl.setTextOption(op);
+ tl.setAdditionalFormats(m_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 (m_textAlignment == Qt::AlignHCenter)
+ x -= line.naturalTextWidth() / 2;
+ else if (m_textAlignment == Qt::AlignRight)
+ x -= line.naturalTextWidth();
+
+ if (initial && m_type == TEXT)
+ y -= line.ascent();
+ initial = false;
+
+ line.setPosition(QPointF(x, y));
+ if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
+ || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
+ bounds.setHeight(y);
+ 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);
+ revertStyle(p, states);
+}
+
+void QSvgText::insertText(const QString &text, WhitespaceMode mode)
+{
+ bool isTSpan = (m_formats.count() == 2);
+ QString newText(text);
+ newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
+
+ bool prependSpace = !m_appendSpace && !isTSpan && (mode == Default) && !m_paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
+ if (m_appendSpace || prependSpace)
+ m_paragraphs.back().append(QLatin1Char(' '));
+
+ bool appendSpaceNext = (!isTSpan && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
+
+ if (mode == Default) {
+ newText = newText.simplified();
+ if (newText.isEmpty())
+ appendSpaceNext = false;
+ }
+
+ if (!m_formats.isEmpty()) {
+ QTextLayout::FormatRange range;
+ range.start = m_paragraphs.back().length();
+ range.length = newText.length();
+ range.format = m_formats.top();
+ if (m_appendSpace) {
+ Q_ASSERT(!m_formatRanges.back().isEmpty());
+ ++m_formatRanges.back().back().length;
+ } else if (prependSpace) {
+ --range.start;
+ ++range.length;
+ }
+ m_formatRanges.back().append(range);
+ }
+
+ m_appendSpace = appendSpaceNext;
+ m_paragraphs.back() += newText;
+}
+
+void QSvgText::insertFormat(const QTextCharFormat &format)
+{
+ QTextCharFormat mergedFormat = format;
+ if (!m_formats.isEmpty()) {
+ mergedFormat = m_formats.top();
+ mergedFormat.merge(format);
+ }
+ m_formats.push(mergedFormat);
+}
+
+void QSvgText::insertLineBreak()
+{
+ if (m_type == TEXTAREA) {
+ if (m_paragraphs.back().isEmpty())
+ insertText(QLatin1String(" "), Preserve);
+ m_appendSpace = false;
+ m_paragraphs.push_back(QString());
+ m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
+ }
+}
+
+void QSvgText::popFormat()
+{
+ if (m_formats.count() > 1)
+ m_formats.pop();
+}
+
+qreal QSvgText::scale() const
+{
+ return m_scale;
+}
+
+void QSvgText::setScale(qreal scale)
+{
+ m_scale = scale;
+}
+
+const QTextCharFormat &QSvgText::topFormat() const
+{
+ return m_formats.top();
+}
+
+void QSvgText::setTextAlignment(const Qt::Alignment &alignment)
+{
+ m_textAlignment = alignment;
+}
+
+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() const
+{
+ if (m_link && m_bounds.isEmpty()) {
+ m_bounds = m_link->bounds();
+ m_bounds = QRectF(m_bounds.x()+m_start.x(),
+ m_bounds.y()+m_start.y(),
+ m_bounds.width(),
+ m_bounds.height());
+
+ return m_bounds;
+ }
+ return m_bounds;
+}
+
+QRectF QSvgUse::transformedBounds(const QTransform &transform) const
+{
+ QRectF bounds;
+ QTransform t = transform;
+
+ if (m_link) {
+ QSvgTransformStyle *transStyle = m_style.transform;
+ if (transStyle) {
+ t = transStyle->qtransform() * t;
+ }
+ t.translate(m_start.x(), m_start.y());
+
+ bounds = m_link->transformedBounds(t);
+
+ return bounds;
+ }
+ return bounds;
+}
+
+QRectF QSvgPolyline::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_poly.boundingRect();
+ else {
+ QPainterPath path;
+ path.addPolygon(m_poly);
+ return boundsOnStroke(path, sw);
+ }
+}
+
+QRectF QSvgArc::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1))
+ return m_cachedBounds;
+ else {
+ return boundsOnStroke(cubic, sw);
+ }
+}
+
+QRectF QSvgImage::bounds() const
+{
+ return m_bounds;
+}
+
+QRectF QSvgLine::bounds() const
+{
+ qreal sw = strokeWidth();
+ if (qFuzzyCompare(sw + 1, 1)) {
+ qreal minX = qMin(m_bounds.x1(), m_bounds.x2());
+ qreal minY = qMin(m_bounds.y1(), m_bounds.y2());
+ qreal maxX = qMax(m_bounds.x1(), m_bounds.x2());
+ qreal maxY = qMax(m_bounds.y1(), m_bounds.y2());
+ return QRectF(minX, minY, maxX-minX, maxY-minY);
+ } else {
+ QPainterPath path;
+ path.moveTo(m_bounds.x1(), m_bounds.y1());
+ path.lineTo(m_bounds.x2(), m_bounds.y2());
+ return boundsOnStroke(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 0000000000..4d0d3182a0
--- /dev/null
+++ b/src/svg/qsvggraphics_p.h
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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() const;
+private:
+ QPainterPath cubic;
+ QRectF m_cachedBounds;
+};
+
+class QSvgCircle : public QSvgNode
+{
+public:
+ QSvgCircle(QSvgNode *parent, const QRectF &rect);
+ virtual void draw(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+ virtual QRectF bounds() const;
+private:
+ QRectF m_bounds;
+};
+
+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() const;
+private:
+ QRectF m_bounds;
+};
+
+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() 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() const;
+private:
+ QLineF m_bounds;
+};
+
+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() const;
+
+ QPainterPath *qpath() {
+ return &m_path;
+ }
+private:
+ QPainterPath m_path;
+ QRectF m_cachedBounds;
+};
+
+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() 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() 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() const;
+private:
+ QRectF m_rect;
+ int m_rx, m_ry;
+};
+
+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 insertText(const QString &text, WhitespaceMode mode);
+ void insertFormat(const QTextCharFormat &format);
+ void insertLineBreak();
+ void popFormat();
+ void setTextAlignment(const Qt::Alignment &alignment);
+ const QTextCharFormat &topFormat() const;
+ qreal scale() const;
+ void setScale(qreal scale);
+ //virtual QRectF bounds() const;
+private:
+ QPointF m_coord;
+
+ QVector<QString> m_paragraphs;
+ QStack<QTextCharFormat> m_formats;
+ Qt::Alignment m_textAlignment;
+ QVector<QList<QTextLayout::FormatRange> > m_formatRanges;
+ qreal m_scale;
+ bool m_appendSpace;
+ Type m_type;
+ QSizeF m_size;
+};
+
+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() const;
+ virtual QRectF transformedBounds(const QTransform &transform) const;
+
+private:
+ QSvgNode *m_link;
+ QPointF m_start;
+ mutable QRectF m_bounds;
+};
+
+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 0000000000..56dab5ffcf
--- /dev/null
+++ b/src/svg/qsvghandler.cpp
@@ -0,0 +1,3697 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#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 "private/qmath_p.h"
+
+#include "float.h"
+
+QT_BEGIN_NAMESPACE
+
+double qstrtod(const char *s00, char const **se, bool *ok);
+
+static bool parsePathDataFast(const QStringRef &data, QPainterPath &path);
+
+struct QSvgAttributes
+{
+ QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler);
+
+ QStringRef value(const QLatin1String &name) const;
+ QStringRef value(const QString &namespaceUri, const QLatin1String &name) const;
+
+ QXmlStreamAttributes m_xmlAttributes;
+ QVector<QSvgCssAttribute> m_cssAttributes;
+};
+
+QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
+ : m_xmlAttributes(xmlAttributes)
+{
+ QStringRef style = xmlAttributes.value(QLatin1String("style"));
+ if (!style.isEmpty())
+ handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
+}
+
+QStringRef QSvgAttributes::value(const QLatin1String &name) const
+{
+ QStringRef v = m_xmlAttributes.value(name);
+ if (v.isEmpty()) {
+ for (int i = 0; i < m_cssAttributes.count(); ++i) {
+ if (m_cssAttributes.at(i).name == name) {
+ v = m_cssAttributes.at(i).value;
+ break;
+ }
+ }
+ }
+ return v;
+}
+
+QStringRef QSvgAttributes::value(const QString &namespaceUri, const QLatin1String &name) const
+{
+ QStringRef v = m_xmlAttributes.value(namespaceUri, name);
+ if (v.isEmpty()) {
+ for (int i = 0; i < m_cssAttributes.count(); ++i) {
+ if (m_cssAttributes.at(i).name == name) {
+ v = m_cssAttributes.at(i).value;
+ break;
+ }
+ }
+ }
+ return v;
+}
+
+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;
+}
+static inline QString someId(const QSvgAttributes &attributes)
+{ return someId(attributes.m_xmlAttributes); }
+
+
+
+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);
+ }
+};
+
+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 (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ if (*str == QLatin1Char('.') && pos < maxLen) {
+ temp[pos++] = '.';
+ ++str;
+ }
+ while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ bool exponent = false;
+ if (*str == QLatin1Char('e') && pos < maxLen) {
+ exponent = true;
+ temp[pos++] = 'e';
+ ++str;
+ if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
+ temp[pos++] = str->toLatin1();
+ ++str;
+ }
+ while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && 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 {
+#ifdef Q_WS_QWS
+ 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)
+{
+ const QChar *c = str.constData();
+ return toDouble(c);
+}
+
+static qreal toDouble(const QStringRef &str)
+{
+ const QChar *c = str.constData();
+ return toDouble(c);
+}
+
+static QVector<qreal> parseNumbersList(const QChar *&str)
+{
+ QVector<qreal> points;
+ if (!str)
+ return points;
+ points.reserve(32);
+
+ while (*str == QLatin1Char(' '))
+ ++str;
+ while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
+ *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
+ *str == QLatin1Char('.')) {
+
+ points.append(toDouble(str));
+
+ while (*str == QLatin1Char(' '))
+ ++str;
+ if (*str == QLatin1Char(','))
+ ++str;
+
+ //eat the rest of space
+ while (*str == QLatin1Char(' '))
+ ++str;
+ }
+
+ return points;
+}
+
+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 == QLatin1Char(' '))
+ ++str;
+ if (*str == QLatin1Char('%'))
+ ++str;
+ while (*str == QLatin1Char(' '))
+ ++str;
+ if (*str == QLatin1Char(','))
+ ++str;
+
+ //eat the rest of space
+ while (*str == QLatin1Char(' '))
+ ++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;
+}
+
+/**
+ * returns true when successfuly set the color. false signifies
+ * that the color should be inherited
+ */
+static bool resolveColor(const QString &colorStr, QColor &color, QSvgHandler *handler)
+{
+ QString colorStrTr = colorStr.trimmed();
+ if (colorStr.startsWith(QLatin1String("rgb("))) {
+ const QChar *s = colorStr.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) {
+ const QChar *s = colorStr.constData() + 4;
+ compo = parsePercentageList(s);
+ compo[0] *= (qreal)2.55;
+ compo[1] *= (qreal)2.55;
+ compo[2] *= (qreal)2.55;
+ }
+
+ color = QColor(int(compo[0]),
+ int(compo[1]),
+ int(compo[2]));
+ return true;
+ } else if (colorStr == QLatin1String("inherited") ||
+ colorStr == QLatin1String("inherit")) {
+ return false;
+ } else if (colorStr == QLatin1String("currentColor")) {
+ color = handler->currentColor();
+ return true;
+ }
+
+ color = QColor(colorStrTr);
+ return color.isValid();
+}
+
+static bool constructColor(const QString &colorStr, const QString &opacity,
+ QColor &color, QSvgHandler *handler)
+{
+ if (!resolveColor(colorStr, color, handler))
+ return false;
+ if (!opacity.isEmpty()) {
+ qreal op = toDouble(opacity);
+ if (op <= 1)
+ op *= 255;
+ color.setAlpha(int(op));
+ }
+ return true;
+}
+
+static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
+ QSvgHandler *handler)
+{
+ 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);
+ //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
+ return len;
+}
+
+static inline qreal convertToNumber(const QString &str, QSvgHandler *handler)
+{
+ QSvgHandler::LengthType type;
+ qreal num = parseLength(str, type, handler);
+ 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)
+{
+ QString colorStr = attributes.value(QLatin1String("color")).toString();
+ QString opacity = attributes.value(QLatin1String("color-opacity")).toString();
+ QColor color;
+ if (constructColor(colorStr, opacity, color, handler)) {
+ handler->pushColor(color);
+ }
+}
+
+static QSvgStyleProperty *styleFromUrl(QSvgNode *node, const QString &url)
+{
+ while (node && (node->type() != QSvgNode::DOC &&
+ node->type() != QSvgNode::G &&
+ node->type() != QSvgNode::DEFS &&
+ node->type() != QSvgNode::SWITCH)) {
+ node = node->parent();
+ }
+ if (!node)
+ return 0;
+ return static_cast<QSvgStructureNode*>(node)->scopeStyle(idFromUrl(url));
+}
+
+static void parseBrush(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString value = attributes.value(QLatin1String("fill")).toString();
+ QString fillRule = attributes.value(QLatin1String("fill-rule")).toString();
+ QString opacity = attributes.value(QLatin1String("fill-opacity")).toString();
+ QString myId = someId(attributes);
+
+ value = value.trimmed();
+ fillRule = fillRule.trimmed();
+ if (!value.isEmpty() || !fillRule.isEmpty()) {
+ Qt::FillRule f = Qt::WindingFill;
+ if (fillRule == QLatin1String("evenodd"))
+ f = Qt::OddEvenFill;
+ if (value.startsWith(QLatin1String("url"))) {
+ value = value.remove(0, 3);
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ QSvgFillStyle *prop = new QSvgFillStyle(style);
+ if (!opacity.isEmpty())
+ prop->setFillOpacity(toDouble(opacity));
+ node->appendStyleProperty(prop, myId);
+ } else {
+ qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value)));
+ }
+ } else if (value != QLatin1String("none")) {
+ QColor color;
+ if (constructColor(value, opacity, color, handler)) {
+ QSvgFillStyle *prop = new QSvgFillStyle(QBrush(color));
+ if (!fillRule.isEmpty())
+ prop->setFillRule(f);
+ node->appendStyleProperty(prop, myId);
+ }
+ } else {
+ QSvgFillStyle *prop = new QSvgFillStyle(QBrush(Qt::NoBrush));
+ if (!fillRule.isEmpty())
+ prop->setFillRule(f);
+ node->appendStyleProperty(prop, myId);
+ }
+ }
+}
+
+static void parseQPen(QPen &pen, QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString value = attributes.value(QLatin1String("stroke")).toString();
+ QString dashArray = attributes.value(QLatin1String("stroke-dasharray")).toString();
+ QString dashOffset = attributes.value(QLatin1String("stroke-dashoffset")).toString();
+ QString linecap = attributes.value(QLatin1String("stroke-linecap")).toString();
+ QString linejoin = attributes.value(QLatin1String("stroke-linejoin")).toString();
+ QString miterlimit = attributes.value(QLatin1String("stroke-miterlimit")).toString();
+ QString opacity = attributes.value(QLatin1String("stroke-opacity")).toString();
+ QString width = attributes.value(QLatin1String("stroke-width")).toString();
+ QString myId = someId(attributes);
+
+ if (!value.isEmpty() || !width.isEmpty()) {
+ if (value != QLatin1String("none")) {
+ if (!value.isEmpty()) {
+ if (node && value.startsWith(QLatin1String("url"))) {
+ value = value.remove(0, 3);
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::GRADIENT) {
+ QBrush b(*((QSvgGradientStyle*)style)->qgradient());
+ pen.setBrush(b);
+ } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
+ pen.setColor(
+ ((QSvgSolidColorStyle*)style)->qcolor());
+ }
+ } else {
+ qWarning()<<"QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
+ }
+ } else {
+ QColor color;
+ if (constructColor(value, opacity, color, handler))
+ pen.setColor(color);
+ }
+ //since we could inherit stroke="none"
+ //we need to reset the style of our stroke to something
+ pen.setStyle(Qt::SolidLine);
+ }
+ if (!width.isEmpty()) {
+ QSvgHandler::LengthType lt;
+ qreal widthF = parseLength(width, lt, handler);
+ //### fixme
+ if (!widthF) {
+ pen.setStyle(Qt::NoPen);
+ return;
+ }
+ pen.setWidthF(widthF);
+ }
+ qreal penw = pen.widthF();
+
+ if (!linejoin.isEmpty()) {
+ if (linejoin == QLatin1String("miter"))
+ pen.setJoinStyle(Qt::SvgMiterJoin);
+ else if (linejoin == QLatin1String("round"))
+ pen.setJoinStyle(Qt::RoundJoin);
+ else if (linejoin == QLatin1String("bevel"))
+ pen.setJoinStyle(Qt::BevelJoin);
+ }
+ if (!miterlimit.isEmpty())
+ pen.setMiterLimit(toDouble(miterlimit));
+
+ if (!linecap.isEmpty()) {
+ if (linecap == QLatin1String("butt"))
+ pen.setCapStyle(Qt::FlatCap);
+ else if (linecap == QLatin1String("round"))
+ pen.setCapStyle(Qt::RoundCap);
+ else if (linecap == QLatin1String("square"))
+ pen.setCapStyle(Qt::SquareCap);
+ }
+
+ if (!dashArray.isEmpty()) {
+ const QChar *s = dashArray.constData();
+ QVector<qreal> dashes = parseNumbersList(s);
+ qreal *d = dashes.data();
+ if (penw != 0)
+ for (int i = 0; i < dashes.size(); ++i) {
+ *d /= penw;
+ ++d;
+ }
+ pen.setDashPattern(dashes);
+ }
+ if (!dashOffset.isEmpty()) {
+ pen.setDashOffset(toDouble(dashOffset));
+ }
+
+ } else {
+ pen.setStyle(Qt::NoPen);
+ }
+ }
+}
+
+static QMatrix parseTransformationMatrix(const QString &value)
+{
+ QMatrix matrix;
+ const QChar *str = value.constData();
+
+ while (*str != QLatin1Char(0)) {
+ 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->isSpace())
+ ++str;
+ if (*str != QLatin1Char('('))
+ goto error;
+ ++str;
+ QVector<qreal> points = parseNumbersList(str);
+ 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(tan(points[0]*deg2rad), 0);
+ } else if (state == SkewY) {
+ if (points.count() != 1)
+ goto error;
+ const qreal deg2rad = qreal(0.017453292519943295769);
+ matrix.shear(0, tan(points[0]*deg2rad));
+ }
+ }
+ error:
+ return matrix;
+}
+
+static void parsePen(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString value = attributes.value(QLatin1String("stroke")).toString();
+ QString dashArray = attributes.value(QLatin1String("stroke-dasharray")).toString();
+ QString dashOffset = attributes.value(QLatin1String("stroke-dashoffset")).toString();
+ QString linecap = attributes.value(QLatin1String("stroke-linecap")).toString();
+ QString linejoin = attributes.value(QLatin1String("stroke-linejoin")).toString();
+ QString miterlimit = attributes.value(QLatin1String("stroke-miterlimit")).toString();
+ QString opacity = attributes.value(QLatin1String("stroke-opacity")).toString();
+ QString width = attributes.value(QLatin1String("stroke-width")).toString();
+ QString myId = someId(attributes);
+
+ //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
+
+ if (!value.isEmpty() || !width.isEmpty() || !linecap.isEmpty() || !linejoin.isEmpty()) {
+ if (value != QLatin1String("none")) {
+ QSvgStrokeStyle *inherited =
+ static_cast<QSvgStrokeStyle*>(node->styleProperty(
+ QSvgStyleProperty::STROKE));
+ if (!inherited)
+ inherited = static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty(
+ QSvgStyleProperty::STROKE));
+ QPen pen(handler->defaultPen());
+ if (inherited)
+ pen = inherited->qpen();
+
+ if (!value.isEmpty()) {
+ if (value.startsWith(QLatin1String("url"))) {
+ value = value.remove(0, 3);
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::GRADIENT) {
+ QBrush b(*((QSvgGradientStyle*)style)->qgradient());
+ pen.setBrush(b);
+ } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
+ pen.setColor(
+ ((QSvgSolidColorStyle*)style)->qcolor());
+ }
+ } else {
+ qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
+ }
+ } else {
+ QColor color;
+ if (constructColor(value, opacity, color, handler))
+ pen.setColor(color);
+ }
+ //since we could inherit stroke="none"
+ //we need to reset the style of our stroke to something
+ pen.setStyle(Qt::SolidLine);
+ }
+ if (!width.isEmpty()) {
+ QSvgHandler::LengthType lt;
+ qreal widthF = parseLength(width, lt, handler);
+ //### fixme
+ if (!widthF) {
+ pen.setStyle(Qt::NoPen);
+ return;
+ }
+ pen.setWidthF(widthF);
+ }
+
+ if (!linejoin.isEmpty()) {
+ if (linejoin == QLatin1String("miter"))
+ pen.setJoinStyle(Qt::SvgMiterJoin);
+ else if (linejoin == QLatin1String("round"))
+ pen.setJoinStyle(Qt::RoundJoin);
+ else if (linejoin == QLatin1String("bevel"))
+ pen.setJoinStyle(Qt::BevelJoin);
+ }
+
+ if (!linecap.isEmpty()) {
+ if (linecap == QLatin1String("butt"))
+ pen.setCapStyle(Qt::FlatCap);
+ else if (linecap == QLatin1String("round"))
+ pen.setCapStyle(Qt::RoundCap);
+ else if (linecap == QLatin1String("square"))
+ pen.setCapStyle(Qt::SquareCap);
+ }
+
+ qreal penw = pen.widthF();
+ if (!dashArray.isEmpty()) {
+ const QChar *s = dashArray.constData();
+ QVector<qreal> dashes = parseNumbersList(s);
+ qreal *d = dashes.data();
+ if(penw != 0)
+ for (int i = 0; i < dashes.size(); ++i) {
+ *d /= penw;
+ ++d;
+ }
+
+ // if the dash count is odd the dashes should be duplicated
+ if (dashes.size() % 2 != 0)
+ dashes << QVector<qreal>(dashes);
+
+ pen.setDashPattern(dashes);
+ }
+ if (!dashOffset.isEmpty()) {
+ qreal doffset = toDouble(dashOffset);
+ if (penw != 0)
+ doffset /= penw;
+ pen.setDashOffset(doffset);
+ }
+ if (!miterlimit.isEmpty())
+ pen.setMiterLimit(toDouble(miterlimit));
+
+ node->appendStyleProperty(new QSvgStrokeStyle(pen), myId);
+ } else {
+ QPen pen(handler->defaultPen());
+ pen.setStyle(Qt::NoPen);
+ node->appendStyleProperty(new QSvgStrokeStyle(pen), myId);
+ }
+ }
+}
+
+
+static bool parseQBrush(const QSvgAttributes &attributes, QSvgNode *node,
+ QBrush &brush, QSvgHandler *handler)
+{
+ QString value = attributes.value(QLatin1String("fill")).toString();
+ QString opacity = attributes.value(QLatin1String("fill-opacity")).toString();
+
+ QColor color;
+ if (!value.isEmpty() || !opacity.isEmpty()) {
+ if (value.startsWith(QLatin1String("url"))) {
+ value = value.remove(0, 3);
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ switch (style->type()) {
+ case QSvgStyleProperty::FILL:
+ {
+ brush = static_cast<QSvgFillStyle*>(style)->qbrush();
+ break;
+ }
+ case QSvgStyleProperty::SOLID_COLOR:
+ {
+ brush = QBrush(static_cast<QSvgSolidColorStyle*>(style)->qcolor());
+ break;
+ }
+ case QSvgStyleProperty::GRADIENT:
+ {
+ brush = QBrush(*static_cast<QSvgGradientStyle*>(style)->qgradient());
+ break;
+ }
+ default:
+ qWarning("Cannot use property \"%s\" as brush.", qPrintable(idFromUrl(value)));
+ }
+ } else {
+ qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value)));
+ }
+ } else if (value != QLatin1String("none")) {
+ if (constructColor(value, opacity, color, handler)) {
+ brush.setStyle(Qt::SolidPattern);
+ brush.setColor(color);
+ }
+ } else {
+ brush = QBrush(Qt::NoBrush);
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool parseQFont(const QSvgAttributes &attributes,
+ QFont &font, qreal &fontSize, QSvgHandler *handler)
+{
+ QString family = attributes.value(QLatin1String("font-family")).toString();
+ QString size = attributes.value(QLatin1String("font-size")).toString();
+ QString style = attributes.value(QLatin1String("font-style")).toString();
+ QString weight = attributes.value(QLatin1String("font-weight")).toString();
+
+ if (!family.isEmpty() || !size.isEmpty() ||
+ !style.isEmpty() || !weight.isEmpty()) {
+
+ if (!family.isEmpty()) {
+ font.setFamily(family.trimmed());
+ }
+ if (!size.isEmpty()) {
+ QSvgHandler::LengthType dummy; // should always be pixel size
+ fontSize = parseLength(size, dummy, handler);
+ if (fontSize <= 0)
+ fontSize = 1;
+ font.setPixelSize(qMax(1, int(fontSize)));
+ }
+ if (!style.isEmpty()) {
+ if (style == QLatin1String("normal")) {
+ font.setStyle(QFont::StyleNormal);
+ } else if (style == QLatin1String("italic")) {
+ font.setStyle(QFont::StyleItalic);
+ } else if (style == QLatin1String("oblique")) {
+ font.setStyle(QFont::StyleOblique);
+ } else if (style == QLatin1String("inherit")) {
+ //inherited already
+ }
+ }
+ if (!weight.isEmpty()) {
+ bool ok = false;
+ int weightNum = weight.toInt(&ok);
+ if (ok) {
+ switch (weightNum) {
+ case 100:
+ case 200:
+ font.setWeight(QFont::Light);
+ break;
+ case 300:
+ case 400:
+ font.setWeight(QFont::Normal);
+ break;
+ case 500:
+ case 600:
+ font.setWeight(QFont::DemiBold);
+ break;
+ case 700:
+ case 800:
+ font.setWeight(QFont::Bold);
+ break;
+ case 900:
+ font.setWeight(QFont::Black);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (weight == QLatin1String("normal")) {
+ font.setWeight(QFont::Normal);
+ } else if (weight == QLatin1String("bold")) {
+ font.setWeight(QFont::Bold);
+ } else if (weight == QLatin1String("bolder")) {
+ font.setWeight(QFont::DemiBold);
+ } else if (weight == QLatin1String("lighter")) {
+ font.setWeight(QFont::Light);
+ }
+ }
+ }
+ // QFontInfo fi(font);
+ // font.setPointSize(fi.pointSize());
+ return true;
+ }
+
+ return false;
+}
+
+static void parseFont(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QFont font;
+ font.setPixelSize(12);
+ qreal fontSize = font.pixelSize();
+
+ QSvgFontStyle *inherited =
+ static_cast<QSvgFontStyle*>(node->styleProperty(
+ QSvgStyleProperty::FONT));
+ if (!inherited)
+ inherited =
+ static_cast<QSvgFontStyle*>(node->parent()->styleProperty(
+ QSvgStyleProperty::FONT));
+ if (inherited) {
+ font = inherited->qfont();
+ fontSize = inherited->pointSize();
+ }
+ if (parseQFont(attributes, font, fontSize, handler)) {
+ QString myId = someId(attributes);
+ QString anchor = attributes.value(QLatin1String("text-anchor")).toString();
+ QSvgTinyDocument *doc = node->document();
+ QSvgFontStyle *fontStyle = 0;
+ QString family = (font.family().isEmpty())?myId:font.family();
+ if (!family.isEmpty()) {
+ QSvgFont *svgFont = doc->svgFont(family);
+ if (svgFont) {
+ fontStyle = new QSvgFontStyle(svgFont, doc);
+ fontStyle->setPointSize(fontSize);
+ }
+ }
+ if (!fontStyle) {
+ fontStyle = new QSvgFontStyle(font, node->document());
+ fontStyle->setPointSize(fontSize);
+ }
+ if (!anchor.isEmpty())
+ fontStyle->setTextAnchor(anchor);
+
+ node->appendStyleProperty(fontStyle, myId);
+ }
+}
+
+static void parseTransform(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ QString value = attributes.value(QLatin1String("transform")).toString();
+ QString myId = someId(attributes);
+ value = value.trimmed();
+ if (value.isEmpty())
+ return;
+ QMatrix matrix = parseTransformationMatrix(value);
+
+ if (!matrix.isIdentity()) {
+ node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), myId);
+ }
+
+}
+
+static void parseVisibility(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ QString value = attributes.value(QLatin1String("visibility")).toString();
+ QSvgNode *parent = node->parent();
+
+ if (parent && (value.isEmpty() || value == QLatin1String("inherit")))
+ node->setVisible(parent->isVisible());
+ else if (value == QLatin1String("hidden") || value == 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 = atan2(y0 - yc, x0 - xc);
+ th1 = atan2(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 == QLatin1Char(' '))
+ ++str;
+ QChar pathElem = *str;
+ ++str;
+ QChar endc = *end;
+ *const_cast<QChar *>(end) = 0; // parseNumbersList requires 0-termination that QStringRef cannot guarantee
+ QVector<qreal> arg = parseNumbersList(str);
+ *const_cast<QChar *>(end) = endc;
+ if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
+ arg.append(0);//dummy
+ while (!arg.isEmpty()) {
+ qreal offsetX = x; // correction offsets
+ qreal offsetY = y; // for relative commands
+ switch (pathElem.unicode()) {
+ case 'm': {
+ if (arg.count() < 2) {
+ arg.pop_front();
+ break;
+ }
+ x = x0 = arg[0] + offsetX;
+ y = y0 = arg[1] + offsetY;
+ path.moveTo(x0, y0);
+ arg.pop_front(); arg.pop_front();
+ }
+ break;
+ case 'M': {
+ if (arg.count() < 2) {
+ arg.pop_front();
+ break;
+ }
+ x = x0 = arg[0];
+ y = y0 = arg[1];
+
+ path.moveTo(x0, y0);
+ arg.pop_front(); arg.pop_front();
+ }
+ break;
+ case 'z':
+ case 'Z': {
+ x = x0;
+ y = y0;
+ path.closeSubpath();
+ arg.pop_front();//pop dummy
+ }
+ break;
+ case 'l': {
+ if (arg.count() < 2) {
+ arg.pop_front();
+ break;
+ }
+ x = arg.front() + offsetX;
+ arg.pop_front();
+ y = arg.front() + offsetY;
+ arg.pop_front();
+ path.lineTo(x, y);
+
+ }
+ break;
+ case 'L': {
+ if (arg.count() < 2) {
+ arg.pop_front();
+ break;
+ }
+ x = arg.front(); arg.pop_front();
+ y = arg.front(); arg.pop_front();
+ path.lineTo(x, y);
+ }
+ break;
+ case 'h': {
+ x = arg.front() + offsetX; arg.pop_front();
+ path.lineTo(x, y);
+ }
+ break;
+ case 'H': {
+ x = arg[0];
+ path.lineTo(x, y);
+ arg.pop_front();
+ }
+ break;
+ case 'v': {
+ y = arg[0] + offsetY;
+ path.lineTo(x, y);
+ arg.pop_front();
+ }
+ break;
+ case 'V': {
+ y = arg[0];
+ path.lineTo(x, y);
+ arg.pop_front();
+ }
+ break;
+ case 'c': {
+ if (arg.count() < 6) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF c1(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF c2(arg[2]+offsetX, arg[3]+offsetY);
+ QPointF e(arg[4]+offsetX, arg[5]+offsetY);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'C': {
+ if (arg.count() < 6) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF c1(arg[0], arg[1]);
+ QPointF c2(arg[2], arg[3]);
+ QPointF e(arg[4], arg[5]);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 's': {
+ if (arg.count() < 4) {
+ while (arg.count())
+ arg.pop_front();
+ 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(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF e(arg[2]+offsetX, arg[3]+offsetY);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'S': {
+ if (arg.count() < 4) {
+ while (arg.count())
+ arg.pop_front();
+ 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(arg[0], arg[1]);
+ QPointF e(arg[2], arg[3]);
+ path.cubicTo(c1, c2, e);
+ ctrlPt = c2;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'q': {
+ if (arg.count() < 4) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF c(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF e(arg[2]+offsetX, arg[3]+offsetY);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'Q': {
+ if (arg.count() < 4) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF c(arg[0], arg[1]);
+ QPointF e(arg[2], arg[3]);
+ path.quadTo(c, e);
+ ctrlPt = c;
+ x = e.x();
+ y = e.y();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 't': {
+ if (arg.count() < 2) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF e(arg[0]+offsetX, arg[1]+offsetY);
+ 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();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'T': {
+ if (arg.count() < 2) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ QPointF e(arg[0], arg[1]);
+ 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();
+ arg.pop_front(); arg.pop_front();
+ break;
+ }
+ case 'a': {
+ if (arg.count() < 7) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ qreal rx = arg[0];
+ qreal ry = arg[1];
+ qreal xAxisRotation = arg[2];
+ qreal largeArcFlag = arg[3];
+ qreal sweepFlag = arg[4];
+ qreal ex = arg[5] + offsetX;
+ qreal ey = arg[6] + offsetY;
+ qreal curx = x;
+ qreal cury = y;
+ pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+ int(sweepFlag), ex, ey, curx, cury);
+
+ x = ex;
+ y = ey;
+
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front();
+ }
+ break;
+ case 'A': {
+ if (arg.count() < 7) {
+ while (arg.count())
+ arg.pop_front();
+ break;
+ }
+ qreal rx = arg[0];
+ qreal ry = arg[1];
+ qreal xAxisRotation = arg[2];
+ qreal largeArcFlag = arg[3];
+ qreal sweepFlag = arg[4];
+ qreal ex = arg[5];
+ qreal ey = arg[6];
+ qreal curx = x;
+ qreal cury = y;
+ pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
+ int(sweepFlag), ex, ey, curx, cury);
+ x = ex;
+ y = ey;
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front(); arg.pop_front();
+ arg.pop_front();
+ }
+ 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;
+ 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(')'));
+ } 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 bool parseDefaultTextStyle(QSvgNode *node,
+ const QXmlStreamAttributes &attributes,
+ bool initial,
+ QSvgHandler *handler)
+{
+ Q_ASSERT(node->type() == QSvgText::TEXT || node->type() == QSvgNode::TEXTAREA);
+ QSvgText *textNode = static_cast<QSvgText*>(node);
+
+ QSvgAttributes attrs(attributes, handler);
+
+ QString fontFamily = attrs.value(QString(), QLatin1String("font-family")).toString();
+
+ QString anchor = attrs.value(QString(), QLatin1String("text-anchor")).toString();
+
+ QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
+ node->styleProperty(QSvgStyleProperty::FONT));
+ if (fontStyle) {
+ QSvgTinyDocument *doc = fontStyle->doc();
+ if (doc && fontStyle->svgFont()) {
+ cssStyleLookup(node, handler, handler->selector());
+ parseStyle(node, attrs, handler);
+ return true;
+ }
+ } else if (!fontFamily.isEmpty()) {
+ QSvgTinyDocument *doc = node->document();
+ QSvgFont *svgFont = doc->svgFont(fontFamily);
+ if (svgFont) {
+ cssStyleLookup(node, handler, handler->selector());
+ parseStyle(node, attrs, handler);
+ return true;
+ }
+ }
+
+ QTextCharFormat format;
+ QBrush brush(QColor(0, 0, 0));
+ QFont font;
+ font.setPixelSize(12);
+ qreal fontSize = font.pixelSize();
+
+ if (!initial) {
+ font = textNode->topFormat().font();
+ fontSize = font.pixelSize() / textNode->scale();
+ brush = textNode->topFormat().foreground();
+ } else {
+ QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>(
+ node->styleProperty(QSvgStyleProperty::FONT));
+ if (!fontStyle)
+ fontStyle = static_cast<QSvgFontStyle*>(
+ node->parent()->styleProperty(QSvgStyleProperty::FONT));
+ if (fontStyle) {
+ font = fontStyle->qfont();
+ fontSize = fontStyle->pointSize();
+ if (anchor.isEmpty())
+ anchor = fontStyle->textAnchor();
+ }
+
+ Qt::Alignment align = Qt::AlignLeft;
+ if (anchor == QLatin1String("middle"))
+ align = Qt::AlignHCenter;
+ else if (anchor == QLatin1String("end"))
+ align = Qt::AlignRight;
+ textNode->setTextAlignment(align);
+
+ QSvgFillStyle *fillStyle = static_cast<QSvgFillStyle*>(
+ node->styleProperty(QSvgStyleProperty::FILL));
+ if (fillStyle)
+ brush = fillStyle->qbrush();
+ }
+
+ if (parseQFont(attrs, font, fontSize, handler) || initial) {
+ if (initial)
+ textNode->setScale(100 / fontSize);
+ font.setPixelSize(qMax(1, int(fontSize * textNode->scale())));
+ format.setFont(font);
+ }
+
+ if (parseQBrush(attrs, node, brush, handler) || initial) {
+ if (brush.style() != Qt::NoBrush || initial)
+ format.setForeground(brush);
+ }
+
+ QPen pen(Qt::NoPen);
+// QSvgStrokeStyle *inherited =
+// static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty(
+// QSvgStyleProperty::STROKE));
+// if (inherited)
+// pen = inherited->qpen();
+ parseQPen(pen, node, attrs, handler);
+ if (pen.style() != Qt::NoPen) {
+ format.setTextOutline(pen);
+ }
+
+ parseTransform(node, attrs, handler);
+
+ textNode->insertFormat(format);
+
+ return true;
+}
+
+static inline QStringList stringToList(const QString &str)
+{
+ QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts);
+ return lst;
+}
+
+static bool parseCoreNode(QSvgNode *node,
+ const QXmlStreamAttributes &attributes)
+{
+ QString featuresStr = attributes.value(QLatin1String("requiredFeatures")).toString();
+ QString extensionsStr = attributes.value(QLatin1String("requiredExtensions")).toString();
+ QString languagesStr = attributes.value(QLatin1String("systemLanguage")).toString();
+ QString formatsStr = attributes.value(QLatin1String("requiredFormats")).toString();
+ QString fontsStr = attributes.value(QLatin1String("requiredFonts")).toString();
+ QString nodeIdStr = someId(attributes);
+ QString xmlClassStr = attributes.value(QLatin1String("class")).toString();
+
+
+ QStringList features = stringToList(featuresStr);
+ QStringList extensions = stringToList(extensionsStr);
+ QStringList languages = stringToList(languagesStr);
+ QStringList formats = stringToList(formatsStr);
+ QStringList fonts = stringToList(fontsStr);
+
+ node->setRequiredFeatures(features);
+ node->setRequiredExtensions(extensions);
+ node->setRequiredLanguages(languages);
+ node->setRequiredFormats(formats);
+ node->setRequiredFonts(fonts);
+ node->setNodeId(nodeIdStr);
+ node->setXmlClass(xmlClassStr);
+
+ return true;
+}
+
+static void parseOpacity(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ QString value = attributes.value(QLatin1String("opacity")).toString();
+ value = value.trimmed();
+
+ bool ok = false;
+ qreal op = value.toDouble(&ok);
+
+ if (ok) {
+ QSvgOpacityStyle *opacity = new QSvgOpacityStyle(op);
+ node->appendStyleProperty(opacity, someId(attributes));
+ }
+}
+
+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 *)
+{
+ QString value = attributes.value(QLatin1String("comp-op")).toString();
+ value = value.trimmed();
+
+ if (!value.isEmpty()) {
+ QSvgCompOpStyle *compop = new QSvgCompOpStyle(svgToQtCompositionMode(value));
+ node->appendStyleProperty(compop, someId(attributes));
+ }
+}
+
+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 == QLatin1String("inherit")) {
+ return QSvgNode::InheritMode;
+ }
+ return QSvgNode::BlockMode;
+}
+
+static void parseOthers(QSvgNode *node,
+ const QSvgAttributes &attributes,
+ QSvgHandler *)
+{
+ QString displayStr = attributes.value(QLatin1String("display")).toString();
+ displayStr = displayStr.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();
+ QString fromStr = attributes.value(QLatin1String("from")).toString();
+ QString toStr = attributes.value(QLatin1String("to")).toString();
+ 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;
+ constructColor(fromStr, QString(), startColor, handler);
+ constructColor(toStr, QString(), 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;
+ constructColor(*itr, QString(), 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 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();
+
+ QVector<qreal> vals;
+ if (values.isEmpty()) {
+ const QChar *s = fromStr.constData();
+ QVector<qreal> lst = parseNumbersList(s);
+ while (lst.count() < 3)
+ lst.append(0.0);
+ vals << lst;
+
+ s = toStr.constData();
+ lst = parseNumbersList(s);
+ while (lst.count() < 3)
+ lst.append(0.0);
+ vals << lst;
+ } else {
+ const QChar *s = values.constData();
+ while (s && *s != QLatin1Char(0)) {
+ QVector<qreal> tmpVals = parseNumbersList(s);
+ while (tmpVals.count() < 3)
+ tmpVals.append(0.0);
+
+ vals << tmpVals;
+ 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, 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;
+ }
+
+ 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();
+ QString trans = attributes.value(QLatin1String("gradientTransform")).toString();
+ QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
+ QString units = attributes.value(QLatin1String("gradientUnits")).toString();
+
+ 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());
+
+ 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->setColorAt(qQNaN(), QColor());
+ 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);
+ int i = 0;
+ QVector<qreal>::const_iterator itr = points.constBegin();
+ while (itr != points.constEnd()) {
+ qreal one = *itr; ++itr;
+ qreal two = *itr; ++itr;
+ poly[i] = QPointF(one, two);
+ ++i;
+ }
+ 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);
+ int i = 0;
+ QVector<qreal>::const_iterator itr = points.constBegin();
+ while (itr != points.constEnd()) {
+ qreal one = *itr; ++itr;
+ qreal two = *itr; ++itr;
+ poly[i] = QPointF(one, two);
+ ++i;
+ }
+
+ 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->setColorAt(qQNaN(), QColor());
+ 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 (nrx && !nry)
+ nry = nrx;
+ else if (nry && !nrx)
+ 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);
+ QString solidColorStr = attributes.value(QLatin1String("solid-color")).toString();
+ QString solidOpacityStr = attributes.value(QLatin1String("solid-opacity")).toString();
+
+ if (solidOpacityStr.isEmpty())
+ solidOpacityStr = attributes.value(QLatin1String("opacity")).toString();
+
+ 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);
+
+ QSvgAttributes attrs(attributes, handler);
+
+ 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(')'));
+ }
+ attrs.m_xmlAttributes.append(QString(), decl.d->property, valueStr);
+ }
+
+ QSvgGradientStyle *style =
+ static_cast<QSvgGradientStyle*>(parent);
+ QString offsetStr = attrs.value(QString(), QLatin1String("offset")).toString();
+ QString colorStr = attrs.value(QString(), QLatin1String("stop-color")).toString();
+ QString opacityStr = attrs.value(QString(), QLatin1String("stop-opacity")).toString();
+ QColor color;
+ qreal offset = convertToNumber(offsetStr, handler);
+ if (colorStr.isEmpty()) {
+ colorStr = QLatin1String("#000000");
+ }
+
+ bool colorOK = constructColor(colorStr, opacityStr, color, handler);
+
+ QGradient *grad = style->qgradient();
+
+ offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
+ QGradientStops stops = grad->stops();
+ // Check if the gradient is marked as empty (marked with one single stop at NaN).
+ if ((stops.size() == 1) && qIsNaN(stops.at(0).first)) {
+ stops.clear();
+ grad->setStops(stops);
+ } else {
+ // 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);
+ if (!colorOK)
+ style->addResolve(offset);
+ 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);
+ }
+
+
+ if (!viewBoxStr.isEmpty()) {
+ QStringList lst = viewBoxStr.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ if (lst.count() != 4)
+ lst = viewBoxStr.split(QLatin1Char(','), QString::SkipEmptyParts);
+ QString xStr = lst.at(0).trimmed();
+ QString yStr = lst.at(1).trimmed();
+ QString widthStr = lst.at(2).trimmed();
+ QString heightStr = lst.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)->insertLineBreak();
+ 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);
+
+ //### not to pixels but to the default coordinate system
+ // and text should be already in the correct coordinate
+ // system here
+ //nx = convertToPixels(nx, true, type);
+ //ny = convertToPixels(ny, true, type);
+
+ 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 bool parseTitleNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *)
+{
+ Q_UNUSED(parent); Q_UNUSED(attributes);
+ return true;
+}
+
+static bool parseTspanNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+
+ cssStyleLookup(parent, handler, handler->selector());
+ return parseDefaultTextStyle(parent, attributes, false, handler);
+}
+
+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;
+ 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;
+ if (ref == QLatin1String("span")) return parseTspanNode;
+ 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_defaultCoords = LT_PX;
+ m_defaultPen = QPen(Qt::black, 1, Qt::NoPen, 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.
+ startElement(xml->name().toString(), xml->attributes());
+ 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:
+ ;
+ }
+ }
+}
+
+bool QSvgHandler::startElement(const QString &localName,
+ const QXmlStreamAttributes &attributes)
+{
+ QSvgNode *node = 0;
+
+ if (m_colorTagCount.count()) {
+ int top = m_colorTagCount.pop();
+ ++top;
+ m_colorTagCount.push(top);
+ }
+
+ /* 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(QSvgText::Default);
+ }
+ 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 (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;
+ default:
+ Q_ASSERT(!"not a grouping element is the parent");
+ }
+
+ parseCoreNode(node, attributes);
+ cssStyleLookup(node, this, m_selector);
+ if (node->type() != QSvgNode::TEXT && node->type() != QSvgNode::TEXTAREA)
+ parseStyle(node, attributes, this);
+ else
+ parseDefaultTextStyle(node, attributes, true, this);
+ }
+ } 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), true);
+ } else {
+ qWarning("Couldn't 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();
+
+ if (m_colorTagCount.count()) {
+ int top = m_colorTagCount.pop();
+ --top;
+ if (!top) {
+ m_colorStack.pop();
+ } else {
+ m_colorTagCount.push(top);
+ }
+ }
+
+ if (node == Unknown) {
+ return true;
+ }
+
+ if (m_inStyle && localName == QLatin1String("style")) {
+ m_inStyle = false;
+ } else if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
+ QSvgText *node = static_cast<QSvgText*>(m_nodes.top());
+ if (localName == QLatin1String("tspan"))
+ node->popFormat();
+ }
+
+ if (node == Graphics)
+ m_nodes.pop();
+ else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
+ m_style = 0;
+
+ return true;
+}
+
+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)
+ return true;
+
+ if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
+ QSvgText *node = static_cast<QSvgText*>(m_nodes.top());
+ node->insertText(str.toString(), m_whitespaceMode.top());
+ }
+
+ 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);
+}
+
+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 0000000000..c4c2f3c211
--- /dev/null
+++ b/src/svg/qsvghandler_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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);
+ 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();
+
+ 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 0000000000..f1c1b3863d
--- /dev/null
+++ b/src/svg/qsvgnode.cpp
@@ -0,0 +1,330 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgnode_p.h"
+#include "qsvgtinydocument_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qdebug.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,
+ bool justLink)
+{
+ //qDebug()<<"appending "<<prop->type()<< " ("<< id <<") "<<"to "<<this<<this->type();
+ if (!justLink) {
+ 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);
+ break;
+ case QSvgStyleProperty::GRADIENT:
+ m_style.gradient = static_cast<QSvgGradientStyle*>(prop);
+ 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;
+ }
+ }
+ if (!id.isEmpty()) {
+ m_styles.insert(id, prop);
+ }
+}
+
+void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states)
+{
+ m_style.apply(p, bounds(), this, states);
+}
+
+void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states)
+{
+ 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;
+}
+
+QSvgStyleProperty * QSvgNode::styleProperty(const QString &id) const
+{
+ QString rid = id;
+ if (rid.startsWith(QLatin1Char('#')))
+ rid.remove(0, 1);
+ const QSvgNode *node = this;
+ while (node) {
+ QSvgStyleProperty *style = node->m_styles[rid];
+ if (style)
+ return style;
+ node = node->parent();
+ }
+
+ return 0;
+}
+
+QRectF QSvgNode::bounds() const
+{
+ return QRectF(0, 0, 0, 0);
+}
+
+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(const QTransform &transform) const
+{
+ QTransform t = transform;
+
+ QSvgTransformStyle *transStyle = m_style.transform;
+ if (transStyle) {
+ t = transStyle->qtransform() * t;
+ }
+
+ QRectF rect = bounds();
+
+ rect = t.mapRect(rect);
+
+ 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() const
+{
+ QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>(
+ styleProperty(QSvgStyleProperty::STROKE));
+ if (!stroke || stroke->qpen().style() == Qt::NoPen)
+ return 0;
+ return stroke->qpen().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 0000000000..708b4c91eb
--- /dev/null
+++ b/src/svg/qsvgnode_p.h
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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,
+ 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,
+ bool justLink=false);
+ void applyStyle(QPainter *p, QSvgExtraStates &states);
+ void revertStyle(QPainter *p, QSvgExtraStates &states);
+ QSvgStyleProperty *styleProperty(QSvgStyleProperty::Type type) const;
+ QSvgStyleProperty *styleProperty(const QString &id) const;
+
+ QSvgTinyDocument *document() const;
+
+ virtual Type type() const =0;
+ virtual QRectF bounds() const;
+ virtual QRectF transformedBounds(const QTransform &transform) 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:
+ QSvgStyle m_style;
+
+ qreal strokeWidth() const;
+private:
+ QSvgNode *m_parent;
+ QHash<QString, QSvgRefCounter<QSvgStyleProperty> > m_styles;
+
+ 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;
+
+ 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 0000000000..d33b70c4bd
--- /dev/null
+++ b/src/svg/qsvgrenderer.cpp
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 multimedia
+
+ \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 0000000000..43d0cd4767
--- /dev/null
+++ b/src/svg/qsvgrenderer.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 0000000000..3551fecea1
--- /dev/null
+++ b/src/svg/qsvgstructure.cpp
@@ -0,0 +1,424 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgstructure_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgnode_p.h"
+#include "qsvgstyle_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);
+
+ if (displayMode() != QSvgNode::NoneMode) {
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if (node->isVisible())
+ 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
+{
+ const QSvgStructureNode *group = this;
+ while (group && group->type() != QSvgNode::DOC) {
+ group = static_cast<QSvgStructureNode*>(group->parent());
+ }
+ if (group)
+ return group->m_scope[id];
+ return 0;
+}
+
+void QSvgStructureNode::addChild(QSvgNode *child, const QString &id, bool def)
+{
+ if (!def)
+ m_renderers.append(child);
+
+ if (child->type() == QSvgNode::DEFS) {
+ QSvgDefs *defs =
+ static_cast<QSvgDefs*>(child);
+ m_linkedScopes.append(defs);
+ }
+
+ if (id.isEmpty())
+ return; //we can't add it to scope without id
+
+ QSvgStructureNode *group = this;
+ while (group && group->type() != QSvgNode::DOC) {
+ group = static_cast<QSvgStructureNode*>(group->parent());
+ }
+ if (group)
+ group->m_scope.insert(id, child);
+}
+
+QSvgDefs::QSvgDefs(QSvgNode *parent)
+ : QSvgStructureNode(parent)
+{
+}
+
+void QSvgDefs::draw(QPainter *, QSvgExtraStates &)
+{
+ //noop
+}
+
+QSvgNode::Type QSvgDefs::type() const
+{
+ return DEFS;
+}
+
+QSvgStyleProperty * QSvgStructureNode::scopeStyle(const QString &id) const
+{
+ const QSvgStructureNode *group = this;
+ while (group) {
+ QSvgStyleProperty *prop = group->styleProperty(id);
+ if (prop)
+ return prop;
+ QList<QSvgStructureNode*>::const_iterator itr = group->m_linkedScopes.constBegin();
+ while (itr != group->m_linkedScopes.constEnd()) {
+ prop = (*itr)->styleProperty(id);
+ if (prop)
+ return prop;
+ ++itr;
+ }
+ group = static_cast<QSvgStructureNode*>(group->parent());
+ }
+ return 0;
+}
+
+
+/*
+ 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);
+
+ if (displayMode() != QSvgNode::NoneMode) {
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if (node->isVisible()) {
+ 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() const
+{
+ if (m_bounds.isEmpty()) {
+ foreach(QSvgNode *node, m_renderers) {
+ m_bounds |= node->transformedBounds(QTransform());
+ }
+ }
+
+ return m_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 0000000000..7cfd280031
--- /dev/null
+++ b/src/svg/qsvgstructure_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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;
+ QSvgStyleProperty *scopeStyle(const QString &id) const;
+ void addChild(QSvgNode *child, const QString &id, bool def = false);
+ virtual QRectF bounds() 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;
+ mutable QRectF m_bounds;
+};
+
+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 0000000000..389f68f144
--- /dev/null
+++ b/src/svg/qsvgstyle.cpp
@@ -0,0 +1,820 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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)
+{
+}
+
+QSvgStyleProperty::~QSvgStyleProperty()
+{
+}
+
+QSvgQualityStyle::QSvgQualityStyle(int color)
+ : m_colorRendering(color)
+{
+
+}
+void QSvgQualityStyle::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+
+}
+void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
+{
+
+}
+
+QSvgFillStyle::QSvgFillStyle(const QBrush &brush)
+ : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillOpacitySet(false)
+{
+}
+
+QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style)
+ : m_style(style), m_fillRuleSet(false), m_fillOpacitySet(false)
+{
+}
+
+void QSvgFillStyle::setFillRule(Qt::FillRule f)
+{
+ m_fillRuleSet = true;
+ m_fillRule = f;
+}
+
+void QSvgFillStyle::setFillOpacity(qreal opacity)
+{
+ m_fillOpacitySet = true;
+ m_fillOpacity = opacity;
+}
+
+static void recursivelySetFill(QSvgNode *node, Qt::FillRule f)
+{
+ if (node->type() == QSvgNode::PATH) {
+ QSvgPath *path = static_cast<QSvgPath*>(node);
+ path->qpath()->setFillRule(f);
+ } else if (node->type() == QSvgNode::G) {
+ QList<QSvgNode*> renderers = static_cast<QSvgG*>(node)->renderers();
+ foreach(QSvgNode *n, renderers) {
+ recursivelySetFill(n, f);
+ }
+ }
+}
+void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
+{
+ m_oldFill = p->brush();
+ m_oldOpacity = states.fillOpacity;
+
+ if (m_fillRuleSet) {
+ recursivelySetFill(node, m_fillRule);
+ m_fillRuleSet = false;//set it only on the first run
+ }
+ p->setBrush(m_fill);
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_fillOpacity;
+ if (m_style)
+ m_style->apply(p, rect, node, states);
+}
+
+void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ if (m_style)
+ m_style->revert(p, states);
+ p->setBrush(m_oldFill);
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_oldOpacity;
+}
+
+QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
+ : m_viewportFill(brush)
+{
+}
+
+void QSvgViewportFillStyle::apply(QPainter *p, const QRectF &, 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_font(font), m_pointSize(24), m_doc(doc)
+{
+}
+
+QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc)
+ : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font)
+{
+}
+
+
+void QSvgFontStyle::setPointSize(qreal size)
+{
+ m_pointSize = size;
+}
+
+qreal QSvgFontStyle::pointSize() const
+{
+ return m_pointSize;
+}
+
+void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+ if (!m_font) {
+ m_oldFont = p->font();
+ p->setFont(m_qfont);
+ }
+}
+
+void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ if (!m_font) {
+ p->setFont(m_oldFont);
+ }
+}
+
+QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen)
+ : m_stroke(pen)
+{
+}
+
+void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+ m_oldStroke = p->pen();
+ p->setPen(m_stroke);
+}
+
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setPen(m_oldStroke);
+}
+
+QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
+ : m_solidColor(color)
+{
+}
+
+void QSvgSolidColorStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+ m_oldFill = p->brush();
+ m_oldStroke = p->pen();
+ QBrush b = m_oldFill;
+ b.setColor(m_solidColor);
+ p->setBrush(b);
+ QPen pen = m_oldStroke;
+ pen.setColor(m_solidColor);
+ p->setPen(pen);
+}
+
+void QSvgSolidColorStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setBrush(m_oldFill);
+ p->setPen(m_oldStroke);
+}
+
+QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
+ : m_gradient(grad)
+{
+}
+
+void QSvgGradientStyle::apply(QPainter *p, const QRectF &/*rect*/, QSvgNode *, QSvgExtraStates &)
+{
+ if (!m_link.isEmpty()) {
+ resolveStops();
+ }
+
+ m_oldFill = p->brush();
+
+ //resolving stop colors
+ if (!m_resolvePoints.isEmpty()) {
+ QColor color = p->brush().color();
+ if (!color.isValid())
+ color = p->pen().color();
+ QList<qreal>::const_iterator itr = m_resolvePoints.constBegin();
+ for (; itr != m_resolvePoints.constEnd(); ++itr) {
+ //qDebug()<<"resolving "<<(*itr)<<" to "<<color;
+ m_gradient->setColorAt(*itr, color);
+ }
+ }
+
+ // If the gradient is marked as empty, insert transparent black
+ QGradientStops stops = m_gradient->stops();
+ if (stops.size() == 1 && qIsNaN(stops.at(0).first))
+ m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
+
+ QGradient gradient = *m_gradient;
+
+ QBrush brush;
+ brush = QBrush(gradient);
+
+ if (!m_matrix.isIdentity())
+ brush.setMatrix(m_matrix);
+
+ p->setBrush(brush);
+}
+
+void QSvgGradientStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setBrush(m_oldFill);
+}
+
+
+void QSvgGradientStyle::setMatrix(const QMatrix &mat)
+{
+ m_matrix = mat;
+}
+
+void QSvgGradientStyle::addResolve(qreal offset)
+{
+ m_resolvePoints.append(offset);
+}
+
+QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
+ : m_transform(trans)
+{
+}
+
+void QSvgTransformStyle::apply(QPainter *p, const QRectF &, 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 QRectF &, 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 QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
+{
+ if (quality) {
+ quality->apply(p, rect, node, states);
+ }
+
+ if (fill) {
+ fill->apply(p, rect, node, states);
+ }
+
+ if (viewportFill) {
+ viewportFill->apply(p, rect, node, states);
+ }
+
+ if (font) {
+ font->apply(p, rect, node, states);
+ }
+
+ if (stroke) {
+ stroke->apply(p, rect, node, states);
+ }
+
+ if (solidColor) {
+ solidColor->apply(p, rect, node, states);
+ }
+
+ if (gradient) {
+ gradient->apply(p, rect, node, states);
+ }
+
+ if (transform) {
+ transform->apply(p, rect, node, states);
+ }
+
+ if (animateColor) {
+ animateColor->apply(p, rect, node, states);
+ }
+
+ //animated transforms have to be applied
+ //_after_ the original object transformations
+ if (!animateTransforms.isEmpty()) {
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
+ for (itr = animateTransforms.constBegin(); itr != animateTransforms.constEnd();
+ ++itr) {
+ (*itr)->apply(p, rect, node, states);
+ }
+ }
+
+ if (opacity) {
+ opacity->apply(p, rect, node, states);
+ }
+
+ if (compop) {
+ compop->apply(p, rect, 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);
+ }
+
+ if (solidColor) {
+ solidColor->revert(p, states);
+ }
+
+ if (gradient) {
+ gradient->revert(p, states);
+ }
+
+ //animated transforms need to be reverted _before_
+ //the native transforms
+ if (!animateTransforms.isEmpty()) {
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
+ itr = animateTransforms.constBegin();
+ //only need to rever the first one because that
+ //one has the original world matrix for the primitve
+ if (itr != animateTransforms.constEnd()) {
+ (*itr)->revert(p, states);
+ }
+ }
+
+ 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_count(0), m_finished(false)
+{
+ m_totalRunningTime = m_to - m_from;
+}
+
+void QSvgAnimateTransform::setArgs(TransformType type, const QVector<qreal> &args)
+{
+ m_type = type;
+ m_args = args;
+ Q_ASSERT(!(args.count()%3));
+ m_count = args.count() / 3;
+}
+
+void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &)
+{
+ m_oldWorldTransform = p->worldTransform();
+ resolveMatrix(node);
+ if (!m_finished || m_freeze)
+ p->setWorldTransform(m_transform, true);
+}
+
+void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
+{
+ if (!m_finished || m_freeze) {
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+ }
+}
+
+void QSvgAnimateTransform::resolveMatrix(QSvgNode *node)
+{
+ static const qreal deg2rad = qreal(0.017453292519943295769);
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = (totalTimeElapsed - m_from) / m_to;
+
+ 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, 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 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, 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 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, 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 transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ m_transform = QTransform();
+ m_transform.shear(tan(transX * deg2rad), 0);
+ break;
+ }
+ case SkewY: {
+ 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 transYDiff = (to1 - from1) * percentOfAnimation;
+ qreal transY = from1 + transYDiff;
+ m_transform = QTransform();
+ m_transform.shear(0, tan(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 QRectF &, QSvgNode *node, QSvgExtraStates &)
+{
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = (totalTimeElapsed - m_from) / m_to;
+
+ 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;
+}
+
+QString QSvgFontStyle::textAnchor() const
+{
+ return m_textAnchor;
+}
+
+void QSvgFontStyle::setTextAnchor(const QString &anchor)
+{
+ m_textAnchor = anchor;
+}
+
+QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
+ : m_opacity(opacity)
+{
+
+}
+
+void QSvgOpacityStyle::apply(QPainter *p, const QRectF &, 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->scopeStyle(m_link);
+ if (prop) {
+ if (prop->type() == QSvgStyleProperty::GRADIENT) {
+ QSvgGradientStyle *st =
+ static_cast<QSvgGradientStyle*>(prop);
+ st->resolveStops();
+ m_gradient->setStops(st->qgradient()->stops());
+ }
+ }
+ 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 0000000000..ff4058b265
--- /dev/null
+++ b/src/svg/qsvgstyle_p.h
@@ -0,0 +1,564 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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;
+};
+
+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 QRectF &, QSvgNode *node, QSvgExtraStates &states) =0;
+ virtual void revert(QPainter *p, QSvgExtraStates &states) =0;
+ virtual Type type() const=0;
+};
+
+class QSvgQualityStyle : public QSvgStyleProperty
+{
+public:
+ QSvgQualityStyle(int color);
+ virtual void apply(QPainter *p, const QRectF &, 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 QRectF &, 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(const QBrush &brush);
+ QSvgFillStyle(QSvgStyleProperty *style);
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void setFillRule(Qt::FillRule f);
+ void setFillOpacity(qreal opacity);
+
+ const QBrush & qbrush() const
+ {
+ return m_fill;
+ }
+private:
+ // fill v v 'inherit' | <Paint.datatype>
+ // fill-opacity v v 'inherit' | <OpacityValue.datatype>
+ QBrush m_fill;
+ QBrush m_oldFill;
+ QSvgStyleProperty *m_style;
+
+ bool m_fillRuleSet;
+ Qt::FillRule m_fillRule;
+ bool m_fillOpacitySet;
+ qreal m_fillOpacity;
+ qreal m_oldOpacity;
+};
+
+class QSvgViewportFillStyle : public QSvgStyleProperty
+{
+public:
+ QSvgViewportFillStyle(const QBrush &brush);
+ virtual void apply(QPainter *p, const QRectF &, 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:
+ QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc);
+ QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc);
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ void setPointSize(qreal size);
+ qreal pointSize() const;
+
+ //### hack to avoid having a separate style element for text-anchor
+ QString textAnchor() const;
+ void setTextAnchor(const QString &anchor);
+
+ QSvgFont * svgFont() const
+ {
+ return m_font;
+ }
+ QSvgTinyDocument *doc() const
+ {
+ return m_doc;
+ }
+
+ const QFont & qfont() const
+ {
+ return m_qfont;
+ }
+private:
+ QSvgFont *m_font;
+ qreal m_pointSize;
+ QSvgTinyDocument *m_doc;
+
+ QString m_textAnchor;
+
+ QFont m_qfont;
+ QFont m_oldFont;
+};
+
+class QSvgStrokeStyle : public QSvgStyleProperty
+{
+public:
+ QSvgStrokeStyle(const QPen &pen);
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ const QPen & qpen() 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;
+};
+
+
+class QSvgSolidColorStyle : public QSvgStyleProperty
+{
+public:
+ QSvgSolidColorStyle(const QColor &color);
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+
+ const QColor & qcolor() const
+ {
+ 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 QSvgStyleProperty
+{
+public:
+ QSvgGradientStyle(QGradient *grad);
+ ~QSvgGradientStyle() { delete m_gradient; }
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ 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;
+ }
+
+ void addResolve(qreal offset);
+private:
+ QGradient *m_gradient;
+ QList<qreal> m_resolvePoints;
+
+ QBrush m_oldFill;
+
+ QMatrix m_matrix;
+
+ QSvgTinyDocument *m_doc;
+ QString m_link;
+};
+
+class QSvgTransformStyle : public QSvgStyleProperty
+{
+public:
+ QSvgTransformStyle(const QTransform &transform);
+ virtual void apply(QPainter *p, const QRectF &, 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
+ };
+public:
+ QSvgAnimateTransform(int startMs, int endMs, int by = 0);
+ void setArgs(TransformType type, const QVector<qreal> &args);
+ void setFreeze(bool freeze);
+ void setRepeatCount(qreal repeatCount);
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+ virtual Type type() const;
+protected:
+ void resolveMatrix(QSvgNode *node);
+private:
+ qreal m_from, m_to, m_by;
+ qreal m_totalRunningTime;
+ TransformType m_type;
+ QVector<qreal> m_args;
+ int m_count;
+ QTransform m_transform;
+ QTransform m_oldWorldTransform;
+ bool m_finished;
+ bool m_freeze;
+ qreal m_repeatCount;
+};
+
+
+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 QRectF &, 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 QRectF &, 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 QRectF &rect, 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 0000000000..b6b8526090
--- /dev/null
+++ b/src/svg/qsvgtinydocument.cpp
@@ -0,0 +1,459 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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_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();
+ }
+
+ p->save();
+
+ //sets default style on the painter
+ //### not the most optimal way
+ mapSourceToTarget(p, bounds);
+ p->setPen(Qt::NoPen);
+ 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->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;
+ }
+
+ p->save();
+
+ const QRectF elementBounds = node->transformedBounds(QTransform());
+
+ mapSourceToTarget(p, bounds, elementBounds);
+ QTransform originalTransform = p->worldTransform();
+
+ //XXX set default style on the painter
+ p->setPen(Qt::NoPen);
+ 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::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(QTransform());
+}
+
+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 0000000000..0d54804c0f
--- /dev/null
+++ b/src/svg/qsvgtinydocument_p.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 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;
+
+ 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(QTransform());
+ }
+
+ 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 0000000000..a4200ca704
--- /dev/null
+++ b/src/svg/qsvgwidget.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 multimedia
+
+ \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:
+ QSvgWidgetPrivate()
+ : QWidgetPrivate()
+ {
+ Q_Q(QSvgWidget);
+ renderer = new QSvgRenderer(q);
+ }
+ QSvgWidgetPrivate(const QString &file)
+ : QWidgetPrivate()
+ {
+ Q_Q(QSvgWidget);
+ renderer = new QSvgRenderer(file, q);
+ }
+ QSvgRenderer *renderer;
+};
+
+/*!
+ Constructs a new SVG display widget with the given \a parent.
+*/
+QSvgWidget::QSvgWidget(QWidget *parent)
+ : QWidget(*new QSvgWidgetPrivate, parent, 0)
+{
+ 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(file), parent, 0)
+{
+ 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 0000000000..75eef32360
--- /dev/null
+++ b/src/svg/qsvgwidget.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@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 0000000000..aef0786e89
--- /dev/null
+++ b/src/svg/svg.pro
@@ -0,0 +1,48 @@
+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: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
+
+#zlib support
+contains(QT_CONFIG, zlib) {
+ INCLUDEPATH += ../3rdparty/zlib
+} else:!contains(QT_CONFIG, no-zlib) {
+ unix:LIBS += -lz
+}