summaryrefslogtreecommitdiffstats
path: root/src/svg
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg')
-rw-r--r--src/svg/qgraphicssvgitem.cpp3
-rw-r--r--src/svg/qgraphicssvgitem.h6
-rw-r--r--src/svg/qsvggenerator.cpp83
-rw-r--r--src/svg/qsvggenerator.h3
-rw-r--r--src/svg/qsvggraphics.cpp379
-rw-r--r--src/svg/qsvggraphics_p.h53
-rw-r--r--src/svg/qsvghandler.cpp1561
-rw-r--r--src/svg/qsvgnode.cpp6
-rw-r--r--src/svg/qsvgnode_p.h1
-rw-r--r--src/svg/qsvgrenderer.cpp2
-rw-r--r--src/svg/qsvgstructure.cpp98
-rw-r--r--src/svg/qsvgstyle.cpp448
-rw-r--r--src/svg/qsvgstyle_p.h329
-rw-r--r--src/svg/qsvgtinydocument.cpp30
-rw-r--r--src/svg/qsvgwidget.cpp2
-rw-r--r--src/svg/svg.pro4
16 files changed, 1772 insertions, 1236 deletions
diff --git a/src/svg/qgraphicssvgitem.cpp b/src/svg/qgraphicssvgitem.cpp
index 2c97ec6..cbb5b7a 100644
--- a/src/svg/qgraphicssvgitem.cpp
+++ b/src/svg/qgraphicssvgitem.cpp
@@ -99,7 +99,6 @@ public:
/*!
\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.
@@ -186,7 +185,7 @@ 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))
+ if (qFuzzyIsNull(qMax(murect.width(), murect.height())))
return;
const QRectF mbrect = painter->transform().mapRect(item->boundingRect());
diff --git a/src/svg/qgraphicssvgitem.h b/src/svg/qgraphicssvgitem.h
index d085fa8..e8065da 100644
--- a/src/svg/qgraphicssvgitem.h
+++ b/src/svg/qgraphicssvgitem.h
@@ -41,8 +41,8 @@
#ifndef QGRAPHICSSVGITEM_H
#define QGRAPHICSSVGITEM_H
-#include <QtGui/qgraphicsitem.h>
#include <QtCore/qobject.h>
+#include <QtGui/qgraphicsitem.h>
#ifndef QT_NO_GRAPHICSSVGITEM
@@ -89,9 +89,9 @@ private:
// Q_DECLARE_PRIVATE_WITH_BASE(QGraphicsSvgItem, QObject)
inline QGraphicsSvgItemPrivate *d_func()
- { return reinterpret_cast<QGraphicsSvgItemPrivate *>(QObject::d_ptr); }
+ { return reinterpret_cast<QGraphicsSvgItemPrivate *>(QObject::d_ptr.data()); }
inline const QGraphicsSvgItemPrivate *d_func() const
- { return reinterpret_cast<const QGraphicsSvgItemPrivate *>(QObject::d_ptr); }
+ { return reinterpret_cast<const QGraphicsSvgItemPrivate *>(QObject::d_ptr.data()); }
friend class QGraphicsSvgItemPrivate;
Q_PRIVATE_SLOT(d_func(), void _q_repaintItem())
diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp
index efc6c8f..d51704a 100644
--- a/src/svg/qsvggenerator.cpp
+++ b/src/svg/qsvggenerator.cpp
@@ -362,7 +362,7 @@ public:
translate_dashPattern(spen.dashPattern(), penWidth, &dashPattern);
// SVG uses absolute offset
- dashOffset = QString::fromLatin1("%1").arg(spen.dashOffset() * penWidth);
+ dashOffset = QString::number(spen.dashOffset() * penWidth);
d_func()->attributes.stroke = color;
d_func()->attributes.strokeOpacity = colorOpacity;
@@ -403,8 +403,8 @@ public:
}
switch (spen.joinStyle()) {
case Qt::MiterJoin:
- stream() << "stroke-linejoin=\"miter\" ";
- stream() << "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ stream() << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
break;
case Qt::BevelJoin:
stream() << "stroke-linejoin=\"bevel\" ";
@@ -413,8 +413,8 @@ public:
stream() << "stroke-linejoin=\"round\" ";
break;
case Qt::SvgMiterJoin:
- stream() << "stroke-linejoin=\"miter\" ";
- stream() << "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
+ stream() << "stroke-linejoin=\"miter\" "
+ "stroke-miterlimit=\""<<spen.miterLimit()<<"\" ";
break;
default:
qWarning("Unhandled join style");
@@ -427,8 +427,8 @@ public:
case Qt::SolidPattern: {
QString color, colorOpacity;
translate_color(sbrush.color(), &color, &colorOpacity);
- stream() << "fill=\"" << color << "\" ";
- stream() << "fill-opacity=\""
+ stream() << "fill=\"" << color << "\" "
+ "fill-opacity=\""
<< colorOpacity << "\" ";
d_func()->attributes.fill = color;
d_func()->attributes.fillOpacity = colorOpacity;
@@ -493,9 +493,9 @@ public:
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 << "\" "
+ "font-size=\"" << d->attributes.font_size << "\" "
+ "font-weight=\"" << d->attributes.font_weight << "\" "
+ "font-style=\"" << d->attributes.font_style << "\" "
<< endl;
}
};
@@ -511,7 +511,7 @@ public:
/*!
\class QSvgGenerator
- \ingroup multimedia
+ \ingroup painting
\since 4.3
\brief The QSvgGenerator class provides a paint device that is used to create SVG drawings.
\reentrant
@@ -567,7 +567,6 @@ QSvgGenerator::~QSvgGenerator()
if (d->owns_iodevice)
delete d->engine->outputDevice();
delete d->engine;
- delete d_ptr;
}
/*!
@@ -849,13 +848,13 @@ bool QSvgPaintEngine::begin(QPaintDevice *)
}
if (d->viewBox.isValid()) {
- *d->stream << " viewBox=\"" << d->viewBox.left() << " " << d->viewBox.top();
- *d->stream << " " << d->viewBox.width() << " " << d->viewBox.height() << "\"" << endl;
+ *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;
+ " 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;
@@ -918,10 +917,11 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
Q_UNUSED(sr);
Q_UNUSED(flags);
stream() << "<image ";
- stream() << "x=\""<<r.x()<<"\" ";
- stream() << "y=\""<<r.y()<<"\" ";
- stream() << "width=\""<<r.width()<<"\" ";
- stream() << "height=\""<<r.height()<<"\" ";
+ stream() << "x=\""<<r.x()<<"\" "
+ "y=\""<<r.y()<<"\" "
+ "width=\""<<r.width()<<"\" "
+ "height=\""<<r.height()<<"\" "
+ "preserveAspectRatio=\"none\" ";
QByteArray data;
QBuffer buffer(&data);
@@ -930,8 +930,7 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image,
buffer.close();
stream() << "xlink:href=\"data:image/png;base64,"
<< data.toBase64()
- <<"\" ";
- stream() << "/>\n";
+ <<"\" />\n";
}
void QSvgPaintEngine::updateState(const QPaintEngineState &state)
@@ -958,10 +957,10 @@ void QSvgPaintEngine::updateState(const QPaintEngineState &state)
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()
+ *d->stream << "transform=\"matrix(" << d->matrix.m11() << ','
+ << d->matrix.m12() << ','
+ << d->matrix.m21() << ',' << d->matrix.m22() << ','
+ << d->matrix.dx() << ',' << d->matrix.dy()
<< ")\""
<< endl;
}
@@ -971,11 +970,11 @@ void QSvgPaintEngine::updateState(const QPaintEngineState &state)
}
if (flags & QPaintEngine::DirtyOpacity) {
- if (!qFuzzyCompare(state.opacity(), 1))
+ if (!qFuzzyIsNull(state.opacity() - 1))
stream() << "opacity=\""<<state.opacity()<<"\" ";
}
- *d->stream << ">" << endl;
+ *d->stream << '>' << endl;
d->afterFirstUpdate = true;
}
@@ -984,10 +983,8 @@ void QSvgPaintEngine::drawPath(const QPainterPath &p)
{
Q_D(QSvgPaintEngine);
- *d->stream << "<path ";
-
-
- *d->stream << "fill-rule=";
+ *d->stream << "<path "
+ "fill-rule=";
if (p.fillRule() == Qt::OddEvenFill)
*d->stream << "\"evenodd\" ";
else
@@ -999,13 +996,13 @@ void QSvgPaintEngine::drawPath(const QPainterPath &p)
const QPainterPath::Element &e = p.elementAt(i);
switch (e.type) {
case QPainterPath::MoveToElement:
- *d->stream << "M" << e.x << "," << e.y;
+ *d->stream << 'M' << e.x << ',' << e.y;
break;
case QPainterPath::LineToElement:
- *d->stream << "L" << e.x << "," << e.y;
+ *d->stream << 'L' << e.x << ',' << e.y;
break;
case QPainterPath::CurveToElement:
- *d->stream << "C" << e.x << "," << e.y;
+ *d->stream << 'C' << e.x << ',' << e.y;
++i;
while (i < p.elementCount()) {
const QPainterPath::Element &e = p.elementAt(i);
@@ -1013,8 +1010,8 @@ void QSvgPaintEngine::drawPath(const QPainterPath &p)
--i;
break;
} else
- *d->stream << " ";
- *d->stream << e.x << "," << e.y;
+ *d->stream << ' ';
+ *d->stream << e.x << ',' << e.y;
++i;
}
break;
@@ -1022,7 +1019,7 @@ void QSvgPaintEngine::drawPath(const QPainterPath &p)
break;
}
if (i != p.elementCount() - 1) {
- *d->stream << " ";
+ *d->stream << ' ';
}
}
@@ -1044,7 +1041,7 @@ void QSvgPaintEngine::drawPolygon(const QPointF *points, int pointCount,
stream() << "<polyline fill=\"none\" points=\"";
for (int i = 0; i < pointCount; ++i) {
const QPointF &pt = points[i];
- stream() << pt.x() << "," << pt.y() << " ";
+ stream() << pt.x() << ',' << pt.y() << ' ';
}
stream() << "\" />" <<endl;
} else {
@@ -1063,10 +1060,10 @@ void QSvgPaintEngine::drawTextItem(const QPointF &pt, const QTextItem &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() << "\" ";
+ "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)
diff --git a/src/svg/qsvggenerator.h b/src/svg/qsvggenerator.h
index 255d398..1441802 100644
--- a/src/svg/qsvggenerator.h
+++ b/src/svg/qsvggenerator.h
@@ -49,6 +49,7 @@
#include <QtCore/qnamespace.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qobjectdefs.h>
+#include <QtCore/qscopedpointer.h>
QT_BEGIN_HEADER
@@ -100,7 +101,7 @@ protected:
int metric(QPaintDevice::PaintDeviceMetric metric) const;
private:
- QSvgGeneratorPrivate *d_ptr;
+ QScopedPointer<QSvgGeneratorPrivate> d_ptr;
};
QT_END_NAMESPACE
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
index 61363d3..7577859 100644
--- a/src/svg/qsvggraphics.cpp
+++ b/src/svg/qsvggraphics.cpp
@@ -56,20 +56,21 @@
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);
+#define QT_SVG_DRAW_SHAPE(command) \
+ qreal oldOpacity = p->opacity(); \
+ QBrush oldBrush = p->brush(); \
+ QPen oldPen = p->pen(); \
+ p->setPen(Qt::NoPen); \
+ p->setOpacity(oldOpacity * states.fillOpacity); \
+ command; \
+ p->setPen(oldPen); \
+ if (oldPen.widthF() != 0) { \
+ p->setOpacity(oldOpacity * states.strokeOpacity); \
+ p->setBrush(Qt::NoBrush); \
+ command; \
+ p->setBrush(oldBrush); \
+ } \
+ p->setOpacity(oldOpacity);
void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
@@ -94,7 +95,7 @@ QSvgCircle::QSvgCircle(QSvgNode *parent, const QRectF &rect)
QRectF QSvgCircle::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_bounds;
else {
QPainterPath path;
@@ -105,7 +106,9 @@ QRectF QSvgCircle::bounds() const
void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ revertStyle(p, states);
}
QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
@@ -117,7 +120,12 @@ QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
- p->drawPath(cubic);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPath(cubic);
+ p->setOpacity(oldOpacity);
+ }
revertStyle(p, states);
}
@@ -129,7 +137,7 @@ QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect)
QRectF QSvgEllipse::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_bounds;
else {
QPainterPath path;
@@ -140,7 +148,9 @@ QRectF QSvgEllipse::bounds() const
void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ revertStyle(p, states);
}
QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
@@ -171,27 +181,38 @@ QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
- p->drawLine(m_bounds);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawLine(m_bounds);
+ p->setOpacity(oldOpacity);
+ }
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)
{
+ applyStyle(p, states);
+ m_path.setFillRule(states.fillRule);
QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+ revertStyle(p, states);
}
QRectF QSvgPath::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw)) {
+ if (m_cachedBounds.isNull())
+ //m_cachedBounds = m_path.controlPointRect();
+ m_cachedBounds = m_path.boundingRect();
+
return m_cachedBounds;
+ }
else {
return boundsOnStroke(m_path, sw);
}
@@ -200,13 +221,12 @@ QRectF QSvgPath::bounds() const
QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
: QSvgNode(parent), m_poly(poly)
{
-
}
QRectF QSvgPolygon::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_poly.boundingRect();
else {
QPainterPath path;
@@ -217,7 +237,9 @@ QRectF QSvgPolygon::bounds() const
void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
{
- QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly));
+ applyStyle(p, states);
+ QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
+ revertStyle(p, states);
}
@@ -230,13 +252,19 @@ QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
if (p->brush().style() != Qt::NoBrush) {
QPen save = p->pen();
p->setPen(QPen(Qt::NoPen));
- p->drawPolygon(m_poly);
+ p->setOpacity(oldOpacity * states.fillOpacity);
+ p->drawPolygon(m_poly, states.fillRule);
p->setPen(save);
}
- p->drawPolyline(m_poly);
+ if (p->pen().widthF() != 0) {
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPolyline(m_poly);
+ }
+ p->setOpacity(oldOpacity);
revertStyle(p, states);
}
@@ -249,7 +277,7 @@ QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry)
QRectF QSvgRect::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_rect;
else {
QPainterPath path;
@@ -260,28 +288,32 @@ QRectF QSvgRect::bounds() const
void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
if (m_rx || m_ry) {
QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
} else {
QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
}
+ revertStyle(p, states);
}
+QSvgTspan * const QSvgText::LINEBREAK = 0;
+
QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord)
: QSvgNode(parent)
, m_coord(coord)
- , m_textAlignment(Qt::AlignLeft)
- , m_scale(1)
- , m_appendSpace(false)
, m_type(TEXT)
, m_size(0, 0)
+ , m_mode(Default)
{
- m_paragraphs.push_back(QString());
- m_formatRanges.push_back(QList<QTextLayout::FormatRange>());
}
QSvgText::~QSvgText()
{
+ for (int i = 0; i < m_tspans.size(); ++i) {
+ if (m_tspans[i] != LINEBREAK)
+ delete m_tspans[i];
+ }
}
void QSvgText::setTextArea(const QSizeF &size)
@@ -295,176 +327,177 @@ void QSvgText::setTextArea(const QSizeF &size)
void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.fillOpacity);
- 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;
- }
+ // Force the font to have a size of 100 pixels to avoid truncation problems
+ // when the font is very small.
+ qreal scale = 100.0 / p->font().pointSizeF();
+ Qt::Alignment alignment = states.textAnchor;
- // Scale the font to its correct size.
QTransform oldTransform = p->worldTransform();
- p->scale(1 / m_scale, 1 / m_scale);
+ p->scale(1 / scale, 1 / 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;
+ qreal px = m_coord.x() * scale;
+ qreal py = m_coord.y() * scale;
+ QSizeF scaledSize = m_size * scale;
if (m_type == TEXTAREA) {
- if (m_textAlignment == Qt::AlignHCenter)
+ if (alignment == Qt::AlignHCenter)
px += scaledSize.width() / 2;
- else if (m_textAlignment == Qt::AlignRight)
+ else if (alignment == 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;
+ bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used.
+
+ bool appendSpace = false;
+ QVector<QString> paragraphs;
+ QStack<QTextCharFormat> formats;
+ QVector<QList<QTextLayout::FormatRange> > formatRanges;
+ paragraphs.push_back(QString());
+ formatRanges.push_back(QList<QTextLayout::FormatRange>());
+
+ for (int i = 0; i < m_tspans.size(); ++i) {
+ if (m_tspans[i] == LINEBREAK) {
+ if (m_type == TEXTAREA) {
+ if (paragraphs.back().isEmpty()) {
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
+
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 1;
+ range.format.setFont(font);
+ formatRanges.back().append(range);
+
+ paragraphs.back().append(QLatin1Char(' '));;
+ }
+ appendSpace = false;
+ paragraphs.push_back(QString());
+ formatRanges.push_back(QList<QTextLayout::FormatRange>());
}
+ } else {
+ WhitespaceMode mode = m_tspans[i]->whitespaceMode();
+ m_tspans[i]->applyStyle(p, states);
- y += 1.1 * line.height();
- }
- tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
+ QFont font = p->font();
+ font.setPixelSize(font.pointSizeF() * scale);
- if (endOfBoundsReached)
- break;
- }
+ QString newText(m_tspans[i]->text());
+ newText.replace(QLatin1Char('\t'), QLatin1Char(' '));
+ newText.replace(QLatin1Char('\n'), QLatin1Char(' '));
- p->setWorldTransform(oldTransform, false);
- revertStyle(p, states);
-}
+ bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
+ if (appendSpace || prependSpace)
+ paragraphs.back().append(QLatin1Char(' '));
-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 appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
- bool prependSpace = !m_appendSpace && !isTSpan && (mode == Default) && !m_paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' '));
- if (m_appendSpace || prependSpace)
- m_paragraphs.back().append(QLatin1Char(' '));
+ if (mode == Default) {
+ newText = newText.simplified();
+ if (newText.isEmpty())
+ appendSpaceNext = false;
+ }
- bool appendSpaceNext = (!isTSpan && (mode == Default) && newText.endsWith(QLatin1Char(' ')));
+ QTextLayout::FormatRange range;
+ range.start = paragraphs.back().length();
+ range.length = newText.length();
+ range.format.setFont(font);
+ range.format.setTextOutline(p->pen());
+ range.format.setForeground(p->brush());
+
+ if (appendSpace) {
+ Q_ASSERT(!formatRanges.back().isEmpty());
+ ++formatRanges.back().back().length;
+ } else if (prependSpace) {
+ --range.start;
+ ++range.length;
+ }
+ formatRanges.back().append(range);
- if (mode == Default) {
- newText = newText.simplified();
- if (newText.isEmpty())
- appendSpaceNext = false;
- }
+ appendSpace = appendSpaceNext;
+ paragraphs.back() += newText;
- 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_tspans[i]->revertStyle(p, states);
}
- 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);
-}
+ if (states.svgFont) {
+ // SVG fonts not fully supported...
+ QString text = paragraphs.front();
+ for (int i = 1; i < paragraphs.size(); ++i) {
+ text.append(QLatin1Char('\n'));
+ text.append(paragraphs[i]);
+ }
+ states.svgFont->draw(p, m_coord, text, p->font().pointSizeF() * scale, states.textAnchor);
+ } else {
+ for (int i = 0; i < paragraphs.size(); ++i) {
+ QTextLayout tl(paragraphs[i]);
+ QTextOption op = tl.textOption();
+ op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ tl.setTextOption(op);
+ tl.setAdditionalFormats(formatRanges[i]);
+ tl.beginLayout();
+
+ forever {
+ QTextLine line = tl.createLine();
+ if (!line.isValid())
+ break;
+ if (m_size.width() != 0)
+ line.setLineWidth(scaledSize.width());
+ }
+ tl.endLayout();
+
+ bool endOfBoundsReached = false;
+ for (int i = 0; i < tl.lineCount(); ++i) {
+ QTextLine line = tl.lineAt(i);
+
+ qreal x = 0;
+ if (alignment == Qt::AlignHCenter)
+ x -= 0.5 * line.naturalTextWidth();
+ else if (alignment == Qt::AlignRight)
+ x -= line.naturalTextWidth();
+
+ if (initial && m_type == TEXT)
+ y -= line.ascent();
+ initial = false;
+
+ line.setPosition(QPointF(x, y));
+
+ // Check if the current line fits into the bounding rectangle.
+ if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width())
+ || (m_size.height() != 0 && y + line.height() > scaledSize.height())) {
+ // I need to set the bounds height to 'y-epsilon' to avoid drawing the current
+ // line. Since the font is scaled to 100 units, 1 should be a safe epsilon.
+ bounds.setHeight(y - 1);
+ endOfBoundsReached = true;
+ break;
+ }
+
+ y += 1.1 * line.height();
+ }
+ tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds);
-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>());
+ if (endOfBoundsReached)
+ break;
+ }
}
-}
-
-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();
+ p->setWorldTransform(oldTransform, false);
+ p->setOpacity(oldOpacity);
+ revertStyle(p, states);
}
-void QSvgText::setTextAlignment(const Qt::Alignment &alignment)
+void QSvgText::addText(const QString &text)
{
- m_textAlignment = alignment;
+ m_tspans.append(new QSvgTspan(this, false));
+ m_tspans.back()->setWhitespaceMode(m_mode);
+ m_tspans.back()->addText(text);
}
QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node)
@@ -596,7 +629,7 @@ QRectF QSvgUse::transformedBounds(const QTransform &transform) const
QRectF QSvgPolyline::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_poly.boundingRect();
else {
QPainterPath path;
@@ -608,7 +641,7 @@ QRectF QSvgPolyline::bounds() const
QRectF QSvgArc::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1))
+ if (qFuzzyIsNull(sw))
return m_cachedBounds;
else {
return boundsOnStroke(cubic, sw);
@@ -623,7 +656,7 @@ QRectF QSvgImage::bounds() const
QRectF QSvgLine::bounds() const
{
qreal sw = strokeWidth();
- if (qFuzzyCompare(sw + 1, 1)) {
+ if (qFuzzyIsNull(sw)) {
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());
diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h
index 8d0db0d..67232e5 100644
--- a/src/svg/qsvggraphics_p.h
+++ b/src/svg/qsvggraphics_p.h
@@ -145,7 +145,7 @@ public:
}
private:
QPainterPath m_path;
- QRectF m_cachedBounds;
+ mutable QRectF m_cachedBounds;
};
class QSvgPolygon : public QSvgNode
@@ -182,6 +182,8 @@ private:
int m_rx, m_ry;
};
+class QSvgTspan;
+
class QSvgText : public QSvgNode
{
public:
@@ -197,26 +199,47 @@ public:
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);
+
+ void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);}
+ void addText(const QString &text);
+ void addLineBreak() {m_tspans.append(LINEBREAK);}
+ void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;}
+
//virtual QRectF bounds() const;
private:
+ static QSvgTspan * const LINEBREAK;
+
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;
+ // 'm_tspans' is also used to store characters outside tspans and line breaks.
+ // If a 'm_tspan' item is null, it indicates a line break.
+ QVector<QSvgTspan *> m_tspans;
+
Type m_type;
QSizeF m_size;
+ WhitespaceMode m_mode;
+};
+
+class QSvgTspan : public QSvgNode
+{
+public:
+ // tspans are also used to store normal text, so the 'isProperTspan' is used to separate text from tspan.
+ QSvgTspan(QSvgNode *parent, bool isProperTspan = true)
+ : QSvgNode(parent), m_mode(QSvgText::Default), m_isTspan(isProperTspan)
+ {
+ }
+ ~QSvgTspan() { };
+ virtual Type type() const {return TSPAN;}
+ virtual void draw(QPainter *, QSvgExtraStates &) {Q_ASSERT(!"Tspans should be drawn through QSvgText::draw().");}
+ void addText(const QString &text) {m_text += text;}
+ const QString &text() const {return m_text;}
+ bool isTspan() const {return m_isTspan;}
+ void setWhitespaceMode(QSvgText::WhitespaceMode mode) {m_mode = mode;}
+ QSvgText::WhitespaceMode whitespaceMode() const {return m_mode;}
+private:
+ QString m_text;
+ QSvgText::WhitespaceMode m_mode;
+ bool m_isTspan;
};
class QSvgUse : public QSvgNode
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
index bd8c504..9683efd 100644
--- a/src/svg/qsvghandler.cpp
+++ b/src/svg/qsvghandler.cpp
@@ -39,6 +39,8 @@
**
****************************************************************************/
+#include "qplatformdefs.h"
+
#include "qsvghandler_p.h"
#ifndef QT_NO_SVG
@@ -62,74 +64,265 @@
#include "qdebug.h"
#include "qmath.h"
#include "qnumeric.h"
+#include "qvarlengtharray.h"
#include "private/qmath_p.h"
#include "float.h"
QT_BEGIN_NAMESPACE
+static const char *qt_inherit_text = "inherit";
+#define QT_INHERIT QLatin1String(qt_inherit_text)
+
double qstrtod(const char *s00, char const **se, bool *ok);
static bool parsePathDataFast(const QStringRef &data, QPainterPath &path);
+static inline QString someId(const QXmlStreamAttributes &attributes)
+{
+ QString id = attributes.value(QLatin1String("id")).toString();
+ if (id.isEmpty())
+ id = attributes.value(QLatin1String("xml:id")).toString();
+ return id;
+}
+
struct QSvgAttributes
{
QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler);
- QStringRef value(const QLatin1String &name) const;
- QStringRef value(const QString &namespaceUri, const QLatin1String &name) const;
+ QString id;
+
+ QStringRef color;
+ QStringRef colorOpacity;
+ QStringRef fill;
+ QStringRef fillRule;
+ QStringRef fillOpacity;
+ QStringRef stroke;
+ QStringRef strokeDashArray;
+ QStringRef strokeDashOffset;
+ QStringRef strokeLineCap;
+ QStringRef strokeLineJoin;
+ QStringRef strokeMiterLimit;
+ QStringRef strokeOpacity;
+ QStringRef strokeWidth;
+ QStringRef vectorEffect;
+ QStringRef fontFamily;
+ QStringRef fontSize;
+ QStringRef fontStyle;
+ QStringRef fontWeight;
+ QStringRef fontVariant;
+ QStringRef textAnchor;
+ QStringRef transform;
+ QStringRef visibility;
+ QStringRef opacity;
+ QStringRef compOp;
+ QStringRef display;
+ QStringRef offset;
+ QStringRef stopColor;
+ QStringRef stopOpacity;
- QXmlStreamAttributes m_xmlAttributes;
QVector<QSvgCssAttribute> m_cssAttributes;
};
QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
- : m_xmlAttributes(xmlAttributes)
{
+ id = someId(xmlAttributes);
QStringRef style = xmlAttributes.value(QLatin1String("style"));
- if (!style.isEmpty())
+ if (!style.isEmpty()) {
handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
-}
+ for (int j = 0; j < m_cssAttributes.count(); ++j) {
+ const QSvgCssAttribute &attribute = m_cssAttributes.at(j);
+ QStringRef name = attribute.name;
+ QStringRef value = attribute.value;
+ if (name.isEmpty())
+ continue;
+
+ switch (name.at(0).unicode()) {
+
+ case 'c':
+ if (name == QLatin1String("color"))
+ color = value;
+ else if (name == QLatin1String("color-opacity"))
+ colorOpacity = value;
+ else if (name == QLatin1String("comp-op"))
+ compOp = value;
+ break;
-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;
+ case 'd':
+ if (name == QLatin1String("display"))
+ display = value;
break;
- }
+
+ case 'f':
+ if (name == QLatin1String("fill"))
+ fill = value;
+ else if (name == QLatin1String("fill-rule"))
+ fillRule = value;
+ else if (name == QLatin1String("fill-opacity"))
+ fillOpacity = value;
+ else if (name == QLatin1String("font-family"))
+ fontFamily = value;
+ else if (name == QLatin1String("font-size"))
+ fontSize = value;
+ else if (name == QLatin1String("font-style"))
+ fontStyle = value;
+ else if (name == QLatin1String("font-weight"))
+ fontWeight = value;
+ else if (name == QLatin1String("font-variant"))
+ fontVariant = value;
+ break;
+
+ case 'o':
+ if (name == QLatin1String("opacity"))
+ opacity = value;
+ else if (name == QLatin1String("offset"))
+ offset = value;
+ break;
+
+ case 's':
+ if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
+ QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
+ if (strokeRef.isEmpty())
+ stroke = value;
+ else if (strokeRef == QLatin1String("-dasharray"))
+ strokeDashArray = value;
+ else if (strokeRef == QLatin1String("-dashoffset"))
+ strokeDashOffset = value;
+ else if (strokeRef == QLatin1String("-linecap"))
+ strokeLineCap = value;
+ else if (strokeRef == QLatin1String("-linejoin"))
+ strokeLineJoin = value;
+ else if (strokeRef == QLatin1String("-miterlimit"))
+ strokeMiterLimit = value;
+ else if (strokeRef == QLatin1String("-opacity"))
+ strokeOpacity = value;
+ else if (strokeRef == QLatin1String("-width"))
+ strokeWidth = value;
+ }
+ else if (name == QLatin1String("stop-color"))
+ stopColor = value;
+ else if (name == QLatin1String("stop-opacity"))
+ stopOpacity = value;
+ break;
+
+ case 't':
+ if (name == QLatin1String("text-anchor"))
+ textAnchor = value;
+ else if (name == QLatin1String("transform"))
+ transform = value;
+ break;
+
+ case 'v':
+ if (name == QLatin1String("vector-effect"))
+ vectorEffect = value;
+ else if (name == QLatin1String("visibility"))
+ visibility = value;
+ break;
+
+ default:
+ break;
+ }
}
}
- 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;
+ for (int i = 0; i < xmlAttributes.count(); ++i) {
+ const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
+ QStringRef name = attribute.qualifiedName();
+ if (name.isEmpty())
+ continue;
+ QStringRef value = attribute.value();
+
+ switch (name.at(0).unicode()) {
+
+ case 'c':
+ if (name == QLatin1String("color"))
+ color = value;
+ else if (name == QLatin1String("color-opacity"))
+ colorOpacity = value;
+ else if (name == QLatin1String("comp-op"))
+ compOp = value;
+ break;
+
+ case 'd':
+ if (name == QLatin1String("display"))
+ display = value;
+ break;
+
+ case 'f':
+ if (name == QLatin1String("fill"))
+ fill = value;
+ else if (name == QLatin1String("fill-rule"))
+ fillRule = value;
+ else if (name == QLatin1String("fill-opacity"))
+ fillOpacity = value;
+ else if (name == QLatin1String("font-family"))
+ fontFamily = value;
+ else if (name == QLatin1String("font-size"))
+ fontSize = value;
+ else if (name == QLatin1String("font-style"))
+ fontStyle = value;
+ else if (name == QLatin1String("font-weight"))
+ fontWeight = value;
+ else if (name == QLatin1String("font-variant"))
+ fontVariant = value;
+ break;
+
+ case 'o':
+ if (name == QLatin1String("opacity"))
+ opacity = value;
+ if (name == QLatin1String("offset"))
+ offset = value;
+ break;
+
+ case 's':
+ if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
+ QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
+ if (strokeRef.isEmpty())
+ stroke = value;
+ else if (strokeRef == QLatin1String("-dasharray"))
+ strokeDashArray = value;
+ else if (strokeRef == QLatin1String("-dashoffset"))
+ strokeDashOffset = value;
+ else if (strokeRef == QLatin1String("-linecap"))
+ strokeLineCap = value;
+ else if (strokeRef == QLatin1String("-linejoin"))
+ strokeLineJoin = value;
+ else if (strokeRef == QLatin1String("-miterlimit"))
+ strokeMiterLimit = value;
+ else if (strokeRef == QLatin1String("-opacity"))
+ strokeOpacity = value;
+ else if (strokeRef == QLatin1String("-width"))
+ strokeWidth = value;
}
+ else if (name == QLatin1String("stop-color"))
+ stopColor = value;
+ else if (name == QLatin1String("stop-opacity"))
+ stopOpacity = value;
+ break;
+
+ case 't':
+ if (name == QLatin1String("text-anchor"))
+ textAnchor = value;
+ else if (name == QLatin1String("transform"))
+ transform = value;
+ break;
+
+ case 'v':
+ if (name == QLatin1String("vector-effect"))
+ vectorEffect = value;
+ else if (name == QLatin1String("visibility"))
+ visibility = value;
+ break;
+
+ default:
+ 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 inline QString someId(const QSvgAttributes &attributes)
+{ return attributes.id; }
static const char * QSvgStyleSelector_nodeString[] = {
"svg",
@@ -282,6 +475,13 @@ public:
}
};
+// '0' is 0x30 and '9' is 0x39
+static inline bool isDigit(ushort ch)
+{
+ static quint16 magic = 0x3ff;
+ return ((ch >> 4) == 3) && (magic >> (ch & 15));
+}
+
static qreal toDouble(const QChar *&str)
{
const int maxLen = 255;//technically doubles can go til 308+ but whatever
@@ -294,7 +494,7 @@ static qreal toDouble(const QChar *&str)
} else if (*str == QLatin1Char('+')) {
++str;
}
- while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
+ while (isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
@@ -302,7 +502,7 @@ static qreal toDouble(const QChar *&str)
temp[pos++] = '.';
++str;
}
- while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
+ while (isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
@@ -315,11 +515,12 @@ static qreal toDouble(const QChar *&str)
temp[pos++] = str->toLatin1();
++str;
}
- while (*str >= QLatin1Char('0') && *str <= QLatin1Char('9') && pos < maxLen) {
+ while (isDigit(str->unicode()) && pos < maxLen) {
temp[pos++] = str->toLatin1();
++str;
}
}
+
temp[pos] = '\0';
qreal val;
@@ -352,7 +553,7 @@ static qreal toDouble(const QChar *&str)
if (neg)
val = -val;
} else {
-#ifdef Q_WS_QWS
+#if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
if(sizeof(qreal) == sizeof(float))
val = strtof(temp, 0);
else
@@ -365,16 +566,24 @@ static qreal toDouble(const QChar *&str)
return val;
}
-static qreal toDouble(const QString &str)
+static qreal toDouble(const QString &str, bool *ok = NULL)
{
const QChar *c = str.constData();
- return toDouble(c);
+ qreal res = toDouble(c);
+ if (ok) {
+ *ok = ((*c) == QLatin1Char('\0'));
+ }
+ return res;
}
-static qreal toDouble(const QStringRef &str)
+static qreal toDouble(const QStringRef &str, bool *ok = NULL)
{
const QChar *c = str.constData();
- return toDouble(c);
+ qreal res = toDouble(c);
+ if (ok) {
+ *ok = (c == (str.constData() + str.length()));
+ }
+ return res;
}
static QVector<qreal> parseNumbersList(const QChar *&str)
@@ -386,7 +595,7 @@ static QVector<qreal> parseNumbersList(const QChar *&str)
while (*str == QLatin1Char(' '))
++str;
- while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
+ while (isDigit(str->unicode()) ||
*str == QLatin1Char('-') || *str == QLatin1Char('+') ||
*str == QLatin1Char('.')) {
@@ -405,6 +614,27 @@ static QVector<qreal> parseNumbersList(const QChar *&str)
return points;
}
+static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
+{
+ while (*str == QLatin1Char(' '))
+ ++str;
+ while (isDigit(str->unicode()) ||
+ *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;
+ }
+}
+
static QVector<qreal> parsePercentageList(const QChar *&str)
{
QVector<qreal> points;
@@ -455,6 +685,28 @@ static QString idFromUrl(const QString &url)
return id;
}
+static inline QStringRef trimRef(const QStringRef &str)
+{
+ if (str.isEmpty())
+ return QStringRef();
+ const QChar *s = str.string()->constData() + str.position();
+ int end = str.length() - 1;
+ if (!s[0].isSpace() && !s[end].isSpace())
+ return str;
+
+ int start = 0;
+ while (start<=end && s[start].isSpace()) // skip white space from start
+ start++;
+ if (start <= end) { // only white space
+ while (s[end].isSpace()) // skip white space from end
+ end--;
+ }
+ int l = end - start + 1;
+ if (l <= 0)
+ return QStringRef();
+ return QStringRef(str.string(), str.position() + start, l);
+}
+
/**
* returns true when successfuly set the color. false signifies
* that the color should be inherited
@@ -479,8 +731,7 @@ static bool resolveColor(const QString &colorStr, QColor &color, QSvgHandler *ha
int(compo[1]),
int(compo[2]));
return true;
- } else if (colorStr == QLatin1String("inherited") ||
- colorStr == QLatin1String("inherit")) {
+ } else if (colorStr == QT_INHERIT) {
return false;
} else if (colorStr == QLatin1String("currentColor")) {
color = handler->currentColor();
@@ -497,16 +748,17 @@ static bool constructColor(const QString &colorStr, const QString &opacity,
if (!resolveColor(colorStr, color, handler))
return false;
if (!opacity.isEmpty()) {
- qreal op = toDouble(opacity);
- if (op <= 1)
- op *= 255;
- color.setAlpha(int(op));
+ bool ok = true;
+ qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));
+ if (!ok)
+ op = 1.0;
+ color.setAlphaF(op);
}
return true;
}
static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
- QSvgHandler *handler)
+ QSvgHandler *handler, bool *ok = NULL)
{
QString numStr = str.trimmed();
@@ -535,15 +787,15 @@ static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
type = handler->defaultCoordinateSystem();
//type = QSvgHandler::LT_OTHER;
}
- qreal len = toDouble(numStr);
+ qreal len = toDouble(numStr, ok);
//qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
return len;
}
-static inline qreal convertToNumber(const QString &str, QSvgHandler *handler)
+static inline qreal convertToNumber(const QString &str, QSvgHandler *handler, bool *ok = NULL)
{
QSvgHandler::LengthType type;
- qreal num = parseLength(str, type, handler);
+ qreal num = parseLength(str, type, handler, ok);
if (type == QSvgHandler::LT_PERCENT) {
num = num/100.0;
}
@@ -603,8 +855,8 @@ static void parseColor(QSvgNode *,
const QSvgAttributes &attributes,
QSvgHandler *handler)
{
- QString colorStr = attributes.value(QLatin1String("color")).toString();
- QString opacity = attributes.value(QLatin1String("color-opacity")).toString();
+ QString colorStr = attributes.color.toString();
+ QString opacity = attributes.colorOpacity.toString();
QColor color;
if (constructColor(colorStr, opacity, color, handler)) {
handler->pushColor(color);
@@ -628,144 +880,61 @@ 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);
+ if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
+ QSvgFillStyle *prop = new QSvgFillStyle;
+
+ //fill-rule attribute handling
+ if (!attributes.fillRule.isEmpty() && attributes.fillRule != QT_INHERIT) {
+ if (attributes.fillRule == QLatin1String("evenodd"))
+ prop->setFillRule(Qt::OddEvenFill);
+ else if (attributes.fillRule == QLatin1String("nonzero"))
+ prop->setFillRule(Qt::WindingFill);
}
- }
-}
-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);
- }
+ //fill-opacity atttribute handling
+ if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != QT_INHERIT) {
+ prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.fillOpacity))));
+ }
- 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;
+ //fill attribute handling
+ if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
+ if (attributes.fill.length() > 3 &&
+ QStringRef(attributes.fill.string(), attributes.fill.position(), 3) == QLatin1String("url")) {
+ QStringRef urlRef(attributes.fill.string(), attributes.fill.position() + 3, attributes.fill.length() - 3);
+ QString value = urlRef.toString();
+ QSvgStyleProperty *style = styleFromUrl(node, value);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ QString id = idFromUrl(value);
+ prop->setGradientId(id);
+ prop->setGradientResolved(false);
}
- pen.setDashPattern(dashes);
- }
- if (!dashOffset.isEmpty()) {
- pen.setDashOffset(toDouble(dashOffset));
+ } else if (attributes.fill != QLatin1String("none")) {
+ QColor color;
+ if (resolveColor(attributes.fill.toString(), color, handler))
+ prop->setBrush(QBrush(color));
+ } else {
+ prop->setBrush(QBrush(Qt::NoBrush));
}
-
- } else {
- pen.setStyle(Qt::NoPen);
}
+ node->appendStyleProperty(prop, someId(attributes));
}
}
-static QMatrix parseTransformationMatrix(const QString &value)
+
+
+static QMatrix parseTransformationMatrix(const QStringRef &value)
{
+ if (value.isEmpty())
+ return QMatrix();
+
QMatrix matrix;
const QChar *str = value.constData();
+ const QChar *end = str + value.length();
- while (*str != QLatin1Char(0)) {
+ while (str < end) {
if (str->isSpace() || *str == QLatin1Char(',')) {
++str;
continue;
@@ -830,12 +999,13 @@ static QMatrix parseTransformationMatrix(const QString &value)
}
- while (str->isSpace())
+ while (str < end && str->isSpace())
++str;
if (*str != QLatin1Char('('))
goto error;
++str;
- QVector<qreal> points = parseNumbersList(str);
+ QVarLengthArray<qreal, 8> points;
+ parseNumbersArray(str, points);
if (*str != QLatin1Char(')'))
goto error;
++str;
@@ -891,301 +1061,187 @@ 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);
+ if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
+ || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
+ || !attributes.vectorEffect.isEmpty()) {
+
+ QSvgStrokeStyle *prop = new QSvgStrokeStyle;
+
+ //stroke attribute handling
+ if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
+ if (attributes.stroke.length() > 3 &&
+ QStringRef(attributes.stroke.string(), attributes.stroke.position(), 3) == QLatin1String("url")) {
+ QStringRef urlRef(attributes.stroke.string(), attributes.stroke.position() + 3, attributes.stroke.length() - 3);
+ QString value = urlRef.toString();
QSvgStyleProperty *style = styleFromUrl(node, value);
if (style) {
- if (style->type() == QSvgStyleProperty::GRADIENT) {
- QBrush b(*((QSvgGradientStyle*)style)->qgradient());
- pen.setBrush(b);
- } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
- pen.setColor(
- ((QSvgSolidColorStyle*)style)->qcolor());
- }
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
} else {
- qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
+ QString id = idFromUrl(value);
+ prop->setGradientId(id);
+ prop->setGradientResolved(false);
}
- } 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);
+ } else if (attributes.stroke != QLatin1String("none")) {
+ QColor color;
+ if (resolveColor(attributes.stroke.toString(), color, handler))
+ prop->setStroke(QBrush(color));
+ } else {
+ prop->setStroke(QBrush(Qt::NoBrush));
}
+ }
- 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);
- }
+ //stroke-width handling
+ qreal w = 0;
+ if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != QT_INHERIT) {
+ QSvgHandler::LengthType lt;
+ prop->setWidth(w = parseLength(attributes.strokeWidth.toString(), lt, handler));
+ }
- qreal penw = pen.widthF();
- if (!dashArray.isEmpty()) {
+ //stroke-dasharray
+ if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != QT_INHERIT) {
+ if (attributes.strokeDashArray == QLatin1String("none")) {
+ prop->setDashArrayNone();
+ } else {
+ QString dashArray = attributes.strokeDashArray.toString();
const QChar *s = dashArray.constData();
QVector<qreal> dashes = parseNumbersList(s);
- 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)
+ if ((dashes.size() & 1) != 0)
dashes << QVector<qreal>(dashes);
-
- pen.setDashPattern(dashes);
+ prop->setDashArray(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);
+ //stroke-linejoin attribute handling
+ if (!attributes.strokeLineJoin.isEmpty()) {
+ if (attributes.strokeLineJoin == QLatin1String("miter"))
+ prop->setLineJoin(Qt::SvgMiterJoin);
+ else if (attributes.strokeLineJoin == QLatin1String("round"))
+ prop->setLineJoin(Qt::RoundJoin);
+ else if (attributes.strokeLineJoin == QLatin1String("bevel"))
+ prop->setLineJoin(Qt::BevelJoin);
}
- }
-}
+ //stroke-linecap attribute handling
+ if (!attributes.strokeLineCap.isEmpty()) {
+ if (attributes.strokeLineCap == QLatin1String("butt"))
+ prop->setLineCap(Qt::FlatCap);
+ else if (attributes.strokeLineCap == QLatin1String("round"))
+ prop->setLineCap(Qt::RoundCap);
+ else if (attributes.strokeLineCap == QLatin1String("square"))
+ prop->setLineCap(Qt::SquareCap);
+ }
-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();
+ //stroke-dashoffset attribute handling
+ if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != QT_INHERIT)
+ prop->setDashOffset(toDouble(attributes.strokeDashOffset));
- 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);
+ //vector-effect attribute handling
+ if (!attributes.vectorEffect.isEmpty()) {
+ if (attributes.vectorEffect == QLatin1String("non-scaling-stroke"))
+ prop->setVectorEffect(true);
+ else if (attributes.vectorEffect == QLatin1String("none"))
+ prop->setVectorEffect(false);
}
- 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();
+ //stroke-miterlimit
+ if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != QT_INHERIT)
+ prop->setMiterLimit(toDouble(attributes.strokeMiterLimit));
- if (!family.isEmpty() || !size.isEmpty() ||
- !style.isEmpty() || !weight.isEmpty()) {
+ //stroke-opacity atttribute handling
+ if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != QT_INHERIT)
+ prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.strokeOpacity))));
- 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;
+ node->appendStyleProperty(prop, someId(attributes));
}
-
- 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 (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
+ attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
+ return;
+
+ QSvgTinyDocument *doc = node->document();
+ QSvgFontStyle *fontStyle = 0;
+ if (!attributes.fontFamily.isEmpty()) {
+ QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
+ if (svgFont)
+ fontStyle = new QSvgFontStyle(svgFont, doc);
+ }
+ if (!fontStyle)
+ fontStyle = new QSvgFontStyle;
+
+ if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT)
+ fontStyle->setFamily(attributes.fontFamily.toString().trimmed());
+
+ if (!attributes.fontSize.isEmpty() && attributes.fontSize != QT_INHERIT) {
+ QSvgHandler::LengthType dummy; // should always be pixel size
+ fontStyle->setSize(parseLength(attributes.fontSize.toString(), dummy, handler));
+ }
+
+ if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
+ if (attributes.fontStyle == QLatin1String("normal")) {
+ fontStyle->setStyle(QFont::StyleNormal);
+ } else if (attributes.fontStyle == QLatin1String("italic")) {
+ fontStyle->setStyle(QFont::StyleItalic);
+ } else if (attributes.fontStyle == QLatin1String("oblique")) {
+ fontStyle->setStyle(QFont::StyleOblique);
}
- if (!fontStyle) {
- fontStyle = new QSvgFontStyle(font, node->document());
- fontStyle->setPointSize(fontSize);
+ }
+
+ if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
+ bool ok = false;
+ int weightNum = attributes.fontWeight.toString().toInt(&ok);
+ if (ok) {
+ fontStyle->setWeight(weightNum);
+ } else {
+ if (attributes.fontWeight == QLatin1String("normal")) {
+ fontStyle->setWeight(400);
+ } else if (attributes.fontWeight == QLatin1String("bold")) {
+ fontStyle->setWeight(700);
+ } else if (attributes.fontWeight == QLatin1String("bolder")) {
+ fontStyle->setWeight(QSvgFontStyle::BOLDER);
+ } else if (attributes.fontWeight == QLatin1String("lighter")) {
+ fontStyle->setWeight(QSvgFontStyle::LIGHTER);
+ }
}
- if (!anchor.isEmpty())
- fontStyle->setTextAnchor(anchor);
+ }
- node->appendStyleProperty(fontStyle, myId);
+ if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
+ if (attributes.fontVariant == QLatin1String("normal"))
+ fontStyle->setVariant(QFont::MixedCase);
+ else if (attributes.fontVariant == QLatin1String("small-caps"))
+ fontStyle->setVariant(QFont::SmallCaps);
}
+
+ if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
+ if (attributes.textAnchor == QLatin1String("start"))
+ fontStyle->setTextAnchor(Qt::AlignLeft);
+ if (attributes.textAnchor == QLatin1String("middle"))
+ fontStyle->setTextAnchor(Qt::AlignHCenter);
+ else if (attributes.textAnchor == QLatin1String("end"))
+ fontStyle->setTextAnchor(Qt::AlignRight);
+ }
+
+ node->appendStyleProperty(fontStyle, someId(attributes));
}
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())
+ if (attributes.transform.isEmpty())
return;
- QMatrix matrix = parseTransformationMatrix(value);
+ QMatrix matrix = parseTransformationMatrix(trimRef(attributes.transform));
if (!matrix.isIdentity()) {
- node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), myId);
+ node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), someId(attributes));
}
}
@@ -1194,12 +1250,11 @@ 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")))
+ if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
node->setVisible(parent->isVisible());
- else if (value == QLatin1String("hidden") || value == QLatin1String("collapse")) {
+ else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
node->setVisible(false);
} else
node->setVisible(true);
@@ -1359,132 +1414,156 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
+ QVarLengthArray<qreal, 8> arg;
+ parseNumbersArray(str, arg);
*const_cast<QChar *>(end) = endc;
if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
arg.append(0);//dummy
- while (!arg.isEmpty()) {
+ const qreal *num = arg.constData();
+ int count = arg.count();
+ while (count > 0) {
qreal offsetX = x; // correction offsets
qreal offsetY = y; // for relative commands
switch (pathElem.unicode()) {
case 'm': {
- if (arg.count() < 2) {
- arg.pop_front();
+ if (count < 2) {
+ num++;
+ count--;
break;
}
- x = x0 = arg[0] + offsetX;
- y = y0 = arg[1] + offsetY;
+ x = x0 = num[0] + offsetX;
+ y = y0 = num[1] + offsetY;
+ num += 2;
+ count -= 2;
path.moveTo(x0, y0);
- arg.pop_front(); arg.pop_front();
+
+ // As per 1.2 spec 8.3.2 The "moveto" commands
+ // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+ // the subsequent pairs shall be treated as implicit 'lineto' commands.
+ pathElem = QLatin1Char('l');
}
break;
case 'M': {
- if (arg.count() < 2) {
- arg.pop_front();
+ if (count < 2) {
+ num++;
+ count--;
break;
}
- x = x0 = arg[0];
- y = y0 = arg[1];
-
+ x = x0 = num[0];
+ y = y0 = num[1];
+ num += 2;
+ count -= 2;
path.moveTo(x0, y0);
- arg.pop_front(); arg.pop_front();
+
+ // As per 1.2 spec 8.3.2 The "moveto" commands
+ // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
+ // the subsequent pairs shall be treated as implicit 'lineto' commands.
+ pathElem = QLatin1Char('L');
}
break;
case 'z':
case 'Z': {
x = x0;
y = y0;
+ count--; // skip dummy
+ num++;
path.closeSubpath();
- arg.pop_front();//pop dummy
}
break;
case 'l': {
- if (arg.count() < 2) {
- arg.pop_front();
+ if (count < 2) {
+ num++;
+ count--;
break;
}
- x = arg.front() + offsetX;
- arg.pop_front();
- y = arg.front() + offsetY;
- arg.pop_front();
+ x = x0 = num[0] + offsetX;
+ y = y0 = num[1] + offsetY;
+ num += 2;
+ count -= 2;
path.lineTo(x, y);
}
break;
case 'L': {
- if (arg.count() < 2) {
- arg.pop_front();
+ if (count < 2) {
+ num++;
+ count--;
break;
}
- x = arg.front(); arg.pop_front();
- y = arg.front(); arg.pop_front();
+ x = x0 = num[0];
+ y = y0 = num[1];
+ num += 2;
+ count -= 2;
path.lineTo(x, y);
}
break;
case 'h': {
- x = arg.front() + offsetX; arg.pop_front();
+ x = num[0] + offsetX;
+ num++;
+ count--;
path.lineTo(x, y);
}
break;
case 'H': {
- x = arg[0];
+ x = num[0];
+ num++;
+ count--;
path.lineTo(x, y);
- arg.pop_front();
}
break;
case 'v': {
- y = arg[0] + offsetY;
+ y = num[0] + offsetY;
+ num++;
+ count--;
path.lineTo(x, y);
- arg.pop_front();
}
break;
case 'V': {
- y = arg[0];
+ y = num[0];
+ num++;
+ count--;
path.lineTo(x, y);
- arg.pop_front();
}
break;
case 'c': {
- if (arg.count() < 6) {
- while (arg.count())
- arg.pop_front();
+ if (count < 6) {
+ num += count;
+ count = 0;
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);
+ QPointF c1(num[0] + offsetX, num[1] + offsetY);
+ QPointF c2(num[2] + offsetX, num[3] + offsetY);
+ QPointF e(num[4] + offsetX, num[5] + offsetY);
+ num += 6;
+ count -= 6;
path.cubicTo(c1, c2, e);
ctrlPt = c2;
x = e.x();
y = e.y();
- 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();
+ if (count < 6) {
+ num += count;
+ count = 0;
break;
}
- QPointF c1(arg[0], arg[1]);
- QPointF c2(arg[2], arg[3]);
- QPointF e(arg[4], arg[5]);
+ QPointF c1(num[0], num[1]);
+ QPointF c2(num[2], num[3]);
+ QPointF e(num[4], num[5]);
+ num += 6;
+ count -= 6;
path.cubicTo(c1, c2, e);
ctrlPt = c2;
x = e.x();
y = e.y();
- 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();
+ if (count < 4) {
+ num += count;
+ count = 0;
break;
}
QPointF c1;
@@ -1493,20 +1572,20 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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);
+ QPointF c2(num[0] + offsetX, num[1] + offsetY);
+ QPointF e(num[2] + offsetX, num[3] + offsetY);
+ num += 4;
+ count -= 4;
path.cubicTo(c1, c2, e);
ctrlPt = c2;
x = e.x();
y = e.y();
- 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();
+ if (count < 4) {
+ num += count;
+ count = 0;
break;
}
QPointF c1;
@@ -1515,55 +1594,57 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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]);
+ QPointF c2(num[0], num[1]);
+ QPointF e(num[2], num[3]);
+ num += 4;
+ count -= 4;
path.cubicTo(c1, c2, e);
ctrlPt = c2;
x = e.x();
y = e.y();
- 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();
+ if (count < 4) {
+ num += count;
+ count = 0;
break;
}
- QPointF c(arg[0]+offsetX, arg[1]+offsetY);
- QPointF e(arg[2]+offsetX, arg[3]+offsetY);
+ QPointF c(num[0] + offsetX, num[1] + offsetY);
+ QPointF e(num[2] + offsetX, num[3] + offsetY);
+ num += 4;
+ count -= 4;
path.quadTo(c, e);
ctrlPt = c;
x = e.x();
y = e.y();
- 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();
+ if (count < 4) {
+ num += count;
+ count = 0;
break;
}
- QPointF c(arg[0], arg[1]);
- QPointF e(arg[2], arg[3]);
+ QPointF c(num[0], num[1]);
+ QPointF e(num[2], num[3]);
+ num += 4;
+ count -= 4;
path.quadTo(c, e);
ctrlPt = c;
x = e.x();
y = e.y();
- 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();
+ if (count < 2) {
+ num += count;
+ count = 0;
break;
}
- QPointF e(arg[0]+offsetX, arg[1]+offsetY);
+ QPointF e(num[0] + offsetX, num[1] + offsetY);
+ num += 2;
+ count -= 2;
QPointF c;
if (lastMode == 'q' || lastMode == 'Q' ||
lastMode == 't' || lastMode == 'T')
@@ -1574,16 +1655,17 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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();
+ if (count < 2) {
+ num += count;
+ count = 0;
break;
}
- QPointF e(arg[0], arg[1]);
+ QPointF e(num[0], num[1]);
+ num += 2;
+ count -= 2;
QPointF c;
if (lastMode == 'q' || lastMode == 'Q' ||
lastMode == 't' || lastMode == 'T')
@@ -1594,22 +1676,22 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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();
+ if (count < 7) {
+ num += count;
+ count = 0;
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 rx = (*num++);
+ qreal ry = (*num++);
+ qreal xAxisRotation = (*num++);
+ qreal largeArcFlag = (*num++);
+ qreal sweepFlag = (*num++);
+ qreal ex = (*num++) + offsetX;
+ qreal ey = (*num++) + offsetY;
+ count -= 7;
qreal curx = x;
qreal cury = y;
pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
@@ -1617,36 +1699,29 @@ static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
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();
+ if (count < 7) {
+ num += count;
+ count = 0;
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 rx = (*num++);
+ qreal ry = (*num++);
+ qreal xAxisRotation = (*num++);
+ qreal largeArcFlag = (*num++);
+ qreal sweepFlag = (*num++);
+ qreal ex = (*num++);
+ qreal ey = (*num++);
+ count -= 7;
qreal curx = x;
qreal cury = y;
pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
int(sweepFlag), ex, ey, curx, cury);
+
x = ex;
y = ey;
- arg.pop_front(); arg.pop_front();
- arg.pop_front(); arg.pop_front();
- arg.pop_front(); arg.pop_front();
- arg.pop_front();
}
break;
default:
@@ -1798,105 +1873,6 @@ static void cssStyleLookup(QSvgNode *node,
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);
@@ -1906,27 +1882,49 @@ static inline QStringList stringToList(const QString &str)
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);
+ QStringList features;
+ QStringList extensions;
+ QStringList languages;
+ QStringList formats;
+ QStringList fonts;
+ QString xmlClassStr;
+
+ for (int i = 0; i < attributes.count(); ++i) {
+ const QXmlStreamAttribute &attribute = attributes.at(i);
+ QStringRef name = attribute.qualifiedName();
+ if (name.isEmpty())
+ continue;
+ QStringRef value = attribute.value();
+ switch (name.at(0).unicode()) {
+ case 'c':
+ if (name == QLatin1String("class"))
+ xmlClassStr = value.toString();
+ break;
+ case 'r':
+ if (name == QLatin1String("requiredFeatures"))
+ features = stringToList(value.toString());
+ else if (name == QLatin1String("requiredExtensions"))
+ extensions = stringToList(value.toString());
+ else if (name == QLatin1String("requiredFormats"))
+ formats = stringToList(value.toString());
+ else if (name == QLatin1String("requiredFonts"))
+ fonts = stringToList(value.toString());
+ break;
+ case 's':
+ if (name == QLatin1String("systemLanguage"))
+ languages = stringToList(value.toString());
+ break;
+ default:
+ break;
+ }
+ }
node->setRequiredFeatures(features);
node->setRequiredExtensions(extensions);
node->setRequiredLanguages(languages);
node->setRequiredFormats(formats);
node->setRequiredFonts(fonts);
- node->setNodeId(nodeIdStr);
+ node->setNodeId(someId(attributes));
node->setXmlClass(xmlClassStr);
return true;
@@ -1936,14 +1934,16 @@ static void parseOpacity(QSvgNode *node,
const QSvgAttributes &attributes,
QSvgHandler *)
{
- QString value = attributes.value(QLatin1String("opacity")).toString();
- value = value.trimmed();
+ if (attributes.opacity.isEmpty())
+ return;
+
+ const QString value = attributes.opacity.toString().trimmed();
bool ok = false;
qreal op = value.toDouble(&ok);
if (ok) {
- QSvgOpacityStyle *opacity = new QSvgOpacityStyle(op);
+ QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
node->appendStyleProperty(opacity, someId(attributes));
}
}
@@ -2010,8 +2010,9 @@ static void parseCompOp(QSvgNode *node,
const QSvgAttributes &attributes,
QSvgHandler *)
{
- QString value = attributes.value(QLatin1String("comp-op")).toString();
- value = value.trimmed();
+ if (attributes.compOp.isEmpty())
+ return;
+ QString value = attributes.compOp.toString().trimmed();
if (!value.isEmpty()) {
QSvgCompOpStyle *compop = new QSvgCompOpStyle(svgToQtCompositionMode(value));
@@ -2055,7 +2056,7 @@ static inline QSvgNode::DisplayMode displayStringToEnum(const QString &str)
return QSvgNode::TableCaptionMode;
} else if (str == QLatin1String("none")) {
return QSvgNode::NoneMode;
- } else if (str == QLatin1String("inherit")) {
+ } else if (str == QT_INHERIT) {
return QSvgNode::InheritMode;
}
return QSvgNode::BlockMode;
@@ -2065,8 +2066,9 @@ static void parseOthers(QSvgNode *node,
const QSvgAttributes &attributes,
QSvgHandler *)
{
- QString displayStr = attributes.value(QLatin1String("display")).toString();
- displayStr = displayStr.trimmed();
+ if (attributes.display.isEmpty())
+ return;
+ QString displayStr = attributes.display.toString().trimmed();
if (!displayStr.isEmpty()) {
node->setDisplayMode(displayStringToEnum(displayStr));
@@ -2209,6 +2211,14 @@ static bool parseAimateMotionNode(QSvgNode *parent,
return true;
}
+static void parseNumberTriplet(QVector<qreal> &values, const QChar *&s)
+{
+ QVector<qreal> list = parseNumbersList(s);
+ values << list;
+ for (int i = 3 - list.size(); i > 0; --i)
+ values.append(0.0);
+}
+
static bool parseAnimateTransformNode(QSvgNode *parent,
const QXmlStreamAttributes &attributes,
QSvgHandler *handler)
@@ -2222,28 +2232,47 @@ static bool parseAnimateTransformNode(QSvgNode *parent,
QString fillStr = attributes.value(QLatin1String("fill")).toString();
QString fromStr = attributes.value(QLatin1String("from")).toString();
QString toStr = attributes.value(QLatin1String("to")).toString();
+ QString byStr = attributes.value(QLatin1String("by")).toString();
+ QString addtv = attributes.value(QLatin1String("additive")).toString();
+
+ QSvgAnimateTransform::Additive additive = QSvgAnimateTransform::Replace;
+ if (addtv == QLatin1String("sum"))
+ additive = QSvgAnimateTransform::Sum;
QVector<qreal> vals;
if (values.isEmpty()) {
- const QChar *s = 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;
+ const QChar *s;
+ if (fromStr.isEmpty()) {
+ if (!byStr.isEmpty()) {
+ // By-animation.
+ additive = QSvgAnimateTransform::Sum;
+ vals.append(0.0);
+ vals.append(0.0);
+ vals.append(0.0);
+ parseNumberTriplet(vals, s = byStr.constData());
+ } else {
+ // To-animation not defined.
+ return false;
+ }
+ } else {
+ if (!toStr.isEmpty()) {
+ // From-to-animation.
+ parseNumberTriplet(vals, s = fromStr.constData());
+ parseNumberTriplet(vals, s = toStr.constData());
+ } else if (!byStr.isEmpty()) {
+ // From-by-animation.
+ parseNumberTriplet(vals, s = fromStr.constData());
+ parseNumberTriplet(vals, s = byStr.constData());
+ for (int i = vals.size() - 3; i < vals.size(); ++i)
+ vals[i] += vals[i - 3];
+ } else {
+ return false;
+ }
+ }
} else {
const QChar *s = values.constData();
while (s && *s != QLatin1Char(0)) {
- QVector<qreal> tmpVals = parseNumbersList(s);
- while (tmpVals.count() < 3)
- tmpVals.append(0.0);
-
- vals << tmpVals;
+ parseNumberTriplet(vals, s);
if (*s == QLatin1Char(0))
break;
++s;
@@ -2285,7 +2314,7 @@ static bool parseAnimateTransformNode(QSvgNode *parent,
}
QSvgAnimateTransform *anim = new QSvgAnimateTransform(begin, end, 0);
- anim->setArgs(type, vals);
+ anim->setArgs(type, additive, vals);
anim->setFreeze(fillStr == QLatin1String("freeze"));
anim->setRepeatCount(
(repeatStr == QLatin1String("indefinite"))? -1 :
@@ -2550,6 +2579,9 @@ static QSvgNode *createImageNode(QSvgNode *parent,
return 0;
}
+ if (image.format() == QImage::Format_ARGB32)
+ image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+
QSvgNode *img = new QSvgImage(parent,
image,
QRect(int(nx),
@@ -2584,7 +2616,7 @@ static void parseBaseGradient(QSvgNode *node,
QSvgHandler *handler)
{
QString link = attributes.value(QLatin1String("xlink:href")).toString();
- QString trans = attributes.value(QLatin1String("gradientTransform")).toString();
+ QStringRef trans = attributes.value(QLatin1String("gradientTransform"));
QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
QString units = attributes.value(QLatin1String("gradientUnits")).toString();
@@ -2904,8 +2936,7 @@ static bool parseStopNode(QSvgStyleProperty *parent,
cssNode.ptr = &anim;
QVector<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
- QSvgAttributes attrs(attributes, handler);
-
+ QXmlStreamAttributes xmlAttr = attributes;
for (int i = 0; i < decls.count(); ++i) {
const QCss::Declaration &decl = decls.at(i);
@@ -2919,21 +2950,26 @@ static bool parseStopNode(QSvgStyleProperty *parent,
valueStr.prepend(QLatin1String("url("));
valueStr.append(QLatin1Char(')'));
}
- attrs.m_xmlAttributes.append(QString(), decl.d->property, valueStr);
+ xmlAttr.append(QString(), decl.d->property, valueStr);
}
+ QSvgAttributes attrs(xmlAttr, handler);
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();
+ QString offsetStr = attrs.offset.toString();
+ QString colorStr = attrs.stopColor.toString();
+ QString opacityStr = attrs.stopOpacity.toString();
QColor color;
- qreal offset = convertToNumber(offsetStr, handler);
+
+ bool ok = true;
+ qreal offset = convertToNumber(offsetStr, handler, &ok);
+ if (!ok)
+ offset = 0.0;
if (colorStr.isEmpty()) {
colorStr = QLatin1String("#000000");
}
- bool colorOK = constructColor(colorStr, opacityStr, color, handler);
+ constructColor(colorStr, opacityStr, color, handler);
QGradient *grad = style->qgradient();
@@ -2957,8 +2993,6 @@ static bool parseStopNode(QSvgStyleProperty *parent,
grad->setColorAt(offset, color);
style->setGradientStopsSet(true);
- if (!colorOK)
- style->addResolve(offset);
return true;
}
@@ -3030,15 +3064,14 @@ static QSvgNode *createSvgNode(QSvgNode *parent,
qreal h = parseLength(heightStr, lt, handler);
node->setViewBox(QRectF(x, y, w, h));
- } else if (width && height){
+
+ } 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;
@@ -3059,7 +3092,7 @@ static bool parseTbreakNode(QSvgNode *parent,
{
if (parent->type() != QSvgNode::TEXTAREA)
return false;
- static_cast<QSvgText*>(parent)->insertLineBreak();
+ static_cast<QSvgText*>(parent)->addLineBreak();
return true;
}
@@ -3074,12 +3107,6 @@ static QSvgNode *createTextNode(QSvgNode *parent,
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;
}
@@ -3098,6 +3125,13 @@ static QSvgNode *createTextAreaNode(QSvgNode *parent,
return node;
}
+static QSvgNode *createTspanNode(QSvgNode *parent,
+ const QXmlStreamAttributes &,
+ QSvgHandler *)
+{
+ return new QSvgTspan(parent);
+}
+
static bool parseTitleNode(QSvgNode *parent,
const QXmlStreamAttributes &attributes,
QSvgHandler *)
@@ -3106,15 +3140,6 @@ static bool parseTitleNode(QSvgNode *parent,
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)
@@ -3228,6 +3253,7 @@ static FactoryMethod findGraphicsFactory(const QString &name)
case 't':
if (ref == QLatin1String("ext")) return createTextNode;
if (ref == QLatin1String("extArea")) return createTextAreaNode;
+ if (ref == QLatin1String("span")) return createTspanNode;
break;
case 'u':
if (ref == QLatin1String("se")) return createUseNode;
@@ -3284,7 +3310,6 @@ static ParseMethod findUtilFactory(const QString &name)
case 't':
if (ref == QLatin1String("break")) return parseTbreakNode;
if (ref == QLatin1String("itle")) return parseTitleNode;
- if (ref == QLatin1String("span")) return parseTspanNode;
break;
default:
break;
@@ -3377,7 +3402,7 @@ void QSvgHandler::init()
m_style = 0;
m_animEnd = 0;
m_defaultCoords = LT_PX;
- m_defaultPen = QPen(Qt::black, 1, Qt::NoPen, Qt::FlatCap, Qt::SvgMiterJoin);
+ m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
m_defaultPen.setMiterLimit(4);
parse();
}
@@ -3493,16 +3518,33 @@ bool QSvgHandler::startElement(const QString &localName,
group->addChild(node, someId(attributes));
}
break;
+ case QSvgNode::TEXT:
+ case QSvgNode::TEXTAREA:
+ if (node->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
+ } else {
+ qWarning("\'text\' or \'textArea\' element contains invalid element type.");
+ delete node;
+ node = 0;
+ }
+ break;
default:
- Q_ASSERT(!"not a grouping element is the parent");
+ qWarning("Could not add child element to parent element because the types are incorrect.");
+ delete node;
+ node = 0;
+ break;
}
- parseCoreNode(node, attributes);
- cssStyleLookup(node, this, m_selector);
- if (node->type() != QSvgNode::TEXT && node->type() != QSvgNode::TEXTAREA)
+ if (node) {
+ parseCoreNode(node, attributes);
+ cssStyleLookup(node, this, m_selector);
parseStyle(node, attributes, this);
- else
- parseDefaultTextStyle(node, attributes, true, this);
+ if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) {
+ static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
+ } else if (node->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
+ }
+ }
}
} else if (ParseMethod method = findUtilFactory(localName)) {
Q_ASSERT(!m_nodes.isEmpty());
@@ -3515,7 +3557,7 @@ bool QSvgHandler::startElement(const QString &localName,
m_style = prop;
m_nodes.top()->appendStyleProperty(prop, someId(attributes), true);
} else {
- qWarning("Couldn't parse node: %s", qPrintable(localName));
+ qWarning("Could not parse node: %s", qPrintable(localName));
}
} else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
if (m_style) {
@@ -3559,16 +3601,50 @@ bool QSvgHandler::endElement(const QStringRef &localName)
return true;
}
- if (m_inStyle && localName == QLatin1String("style")) {
+ 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)
+ if (node == Graphics) {
+ // Iterate through the m_renderers to resolve any unresolved gradients.
+ QSvgNode* curNode = static_cast<QSvgNode*>(m_nodes.top());
+ if (curNode->type() == QSvgNode::DOC ||
+ curNode->type() == QSvgNode::G ||
+ curNode->type() == QSvgNode::DEFS ||
+ curNode->type() == QSvgNode::SWITCH) {
+ QSvgStructureNode* structureNode = static_cast<QSvgStructureNode*>(curNode);
+ QList<QSvgNode*> ren = structureNode->renderers();
+ QList<QSvgNode*>::iterator itr = ren.begin();
+ while (itr != ren.end()) {
+ QSvgNode *eleNode = *itr++;
+ QSvgFillStyle *fill = static_cast<QSvgFillStyle*>(eleNode->styleProperty(QSvgStyleProperty::FILL));
+ if (fill && !(fill->isGradientResolved())) {
+ QString id = fill->gradientId();
+ QSvgStyleProperty *style = structureNode->scopeStyle(id);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ fill->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ qWarning("Could not resolve property : %s",qPrintable(id));
+ fill->setBrush(QBrush(Qt::NoBrush));
+ }
+ }
+ QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>(eleNode->styleProperty(QSvgStyleProperty::STROKE));
+ if (stroke && !(stroke->isGradientResolved())) {
+ QString id = stroke->gradientId();
+ QSvgStyleProperty *style = structureNode->scopeStyle(id);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ stroke->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ qWarning("Could not resolve property : %s",qPrintable(id));
+ stroke->setStroke(QBrush(Qt::NoBrush));
+ }
+ }
+ }
+ }
m_nodes.pop();
+ }
+
else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
m_style = 0;
@@ -3587,8 +3663,9 @@ bool QSvgHandler::characters(const QStringRef &str)
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());
+ static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
+ } else if (m_nodes.top()->type() == QSvgNode::TSPAN) {
+ static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
}
return true;
diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
index 49800d5..92b913a 100644
--- a/src/svg/qsvgnode.cpp
+++ b/src/svg/qsvgnode.cpp
@@ -320,9 +320,11 @@ qreal QSvgNode::strokeWidth() const
{
QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>(
styleProperty(QSvgStyleProperty::STROKE));
- if (!stroke || stroke->qpen().style() == Qt::NoPen)
+ if (!stroke)
return 0;
- return stroke->qpen().widthF();
+ if (stroke->stroke().brush().style() == Qt::NoBrush)
+ return 0;
+ return stroke->width();
}
QT_END_NAMESPACE
diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h
index 11d0dbc..5485f30 100644
--- a/src/svg/qsvgnode_p.h
+++ b/src/svg/qsvgnode_p.h
@@ -86,6 +86,7 @@ public:
RECT,
TEXT,
TEXTAREA,
+ TSPAN,
USE,
VIDEO
};
diff --git a/src/svg/qsvgrenderer.cpp b/src/svg/qsvgrenderer.cpp
index 3bd4284..533b084 100644
--- a/src/svg/qsvgrenderer.cpp
+++ b/src/svg/qsvgrenderer.cpp
@@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QSvgRenderer
- \ingroup multimedia
+ \ingroup painting
\brief The QSvgRenderer class is used to draw the contents of SVG files onto paint devices.
\since 4.1
diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp
index 1adea29..d4e446a 100644
--- a/src/svg/qsvgstructure.cpp
+++ b/src/svg/qsvgstructure.cpp
@@ -68,13 +68,11 @@ 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;
- }
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
+ node->draw(p, states);
+ ++itr;
}
revertStyle(p, states);
}
@@ -321,63 +319,61 @@ 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;
- }
+ while (itr != m_renderers.end()) {
+ QSvgNode *node = *itr;
+ if (node->isVisible() && (node->displayMode() != QSvgNode::NoneMode)) {
+ const QStringList &features = node->requiredFeatures();
+ const QStringList &extensions = node->requiredExtensions();
+ const QStringList &languages = node->requiredLanguages();
+ const QStringList &formats = node->requiredFormats();
+ const QStringList &fonts = node->requiredFonts();
+
+ bool okToRender = true;
+ if (!features.isEmpty()) {
+ QStringList::const_iterator sitr = features.constBegin();
+ for (; sitr != features.constEnd(); ++sitr) {
+ if (!isSupportedSvgFeature(*sitr)) {
+ okToRender = false;
+ break;
}
}
+ }
- if (okToRender && !extensions.isEmpty()) {
- QStringList::const_iterator sitr = extensions.constBegin();
- for (; sitr != extensions.constEnd(); ++sitr) {
- if (!isSupportedSvgExtension(*sitr)) {
- okToRender = false;
- break;
- }
+ if (okToRender && !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 && !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 && !formats.isEmpty()) {
+ okToRender = false;
+ }
- if (okToRender && !fonts.isEmpty()) {
- okToRender = false;
- }
+ if (okToRender && !fonts.isEmpty()) {
+ okToRender = false;
+ }
- if (okToRender) {
- node->draw(p, states);
- break;
- }
+ if (okToRender) {
+ node->draw(p, states);
+ break;
}
- ++itr;
}
+ ++itr;
}
revertStyle(p, states);
}
diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp
index a19dab6..e3b75d0 100644
--- a/src/svg/qsvgstyle.cpp
+++ b/src/svg/qsvgstyle.cpp
@@ -59,6 +59,13 @@ QT_BEGIN_NAMESPACE
QSvgExtraStates::QSvgExtraStates()
: fillOpacity(1.0)
+ , strokeOpacity(1.0)
+ , svgFont(0)
+ , textAnchor(Qt::AlignLeft)
+ , fontWeight(400)
+ , fillRule(Qt::WindingFill)
+ , strokeDashOffset(0)
+ , vectorEffect(false)
{
}
@@ -66,6 +73,17 @@ QSvgStyleProperty::~QSvgStyleProperty()
{
}
+void QSvgFillStyleProperty::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+
QSvgQualityStyle::QSvgQualityStyle(int color)
: m_colorRendering(color)
{
@@ -80,65 +98,70 @@ void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
}
-QSvgFillStyle::QSvgFillStyle(const QBrush &brush)
- : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
- m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
-{
-}
-
-QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style)
- : m_style(style), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
- m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
+QSvgFillStyle::QSvgFillStyle()
+ : m_style(0)
+ , m_fillRule(Qt::WindingFill)
+ , m_oldFillRule(Qt::WindingFill)
+ , m_fillOpacity(1.0)
+ , m_oldFillOpacity(0)
+ , m_gradientResolved(1)
+ , m_fillRuleSet(0)
+ , m_fillOpacitySet(0)
+ , m_fillSet(0)
{
}
void QSvgFillStyle::setFillRule(Qt::FillRule f)
{
- m_fillRuleSet = true;
+ m_fillRuleSet = 1;
m_fillRule = f;
}
void QSvgFillStyle::setFillOpacity(qreal opacity)
{
- m_fillOpacitySet = true;
+ m_fillOpacitySet = 1;
m_fillOpacity = opacity;
}
-static void recursivelySetFill(QSvgNode *node, Qt::FillRule f)
+void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
{
- 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);
- }
- }
+ m_style = style;
+ m_fillSet = 1;
}
-void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
+
+void QSvgFillStyle::setBrush(QBrush brush)
+{
+ m_fill = brush;
+ m_style = 0;
+ m_fillSet = 1;
+}
+
+void QSvgFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
{
m_oldFill = p->brush();
- m_oldOpacity = states.fillOpacity;
+ m_oldFillRule = states.fillRule;
+ m_oldFillOpacity = states.fillOpacity;
- if (m_fillRuleSet) {
- recursivelySetFill(node, m_fillRule);
- m_fillRuleSet = false;//set it only on the first run
+ if (m_fillRuleSet)
+ states.fillRule = m_fillRule;
+ if (m_fillSet) {
+ if (m_style)
+ p->setBrush(m_style->brush(p, states));
+ else
+ p->setBrush(m_fill);
}
- 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;
+ states.fillOpacity = m_oldFillOpacity;
+ if (m_fillSet)
+ p->setBrush(m_oldFill);
+ if (m_fillRuleSet)
+ states.fillRule = m_oldFillRule;
}
QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
@@ -158,78 +181,227 @@ void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
}
QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc)
- : m_font(font), m_pointSize(24), m_doc(doc)
-{
-}
+ : m_svgFont(font)
+ , m_doc(doc)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+QSvgFontStyle::QSvgFontStyle()
+ : m_svgFont(0)
+ , m_doc(0)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+int QSvgFontStyle::SVGToQtWeight(int weight) {
+ switch (weight) {
+ case 100:
+ case 200:
+ return QFont::Light;
+ case 300:
+ case 400:
+ return QFont::Normal;
+ case 500:
+ case 600:
+ return QFont::DemiBold;
+ case 700:
+ case 800:
+ return QFont::Bold;
+ case 900:
+ return QFont::Black;
+ }
+ return QFont::Normal;
+}
+
+void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldQFont = p->font();
+ m_oldSvgFont = states.svgFont;
+ m_oldTextAnchor = states.textAnchor;
+ m_oldWeight = states.fontWeight;
+
+ if (m_textAnchorSet)
+ states.textAnchor = m_textAnchor;
+
+ QFont font = m_oldQFont;
+ if (m_familySet) {
+ states.svgFont = m_svgFont;
+ font.setFamily(m_qfont.family());
+ }
+
+ if (m_sizeSet)
+ font.setPointSize(m_qfont.pointSizeF());
+
+ if (m_styleSet)
+ font.setStyle(m_qfont.style());
+
+ if (m_variantSet)
+ font.setCapitalization(m_qfont.capitalization());
+
+ if (m_weightSet) {
+ if (m_weight == BOLDER) {
+ states.fontWeight = qMin(states.fontWeight + 100, 900);
+ } else if (m_weight == LIGHTER) {
+ states.fontWeight = qMax(states.fontWeight - 100, 100);
+ } else {
+ states.fontWeight = m_weight;
+ }
+ font.setWeight(SVGToQtWeight(states.fontWeight));
+ }
-QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc)
- : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font)
-{
+ p->setFont(font);
}
-
-void QSvgFontStyle::setPointSize(qreal size)
+void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
{
- m_pointSize = size;
+ p->setFont(m_oldQFont);
+ states.svgFont = m_oldSvgFont;
+ states.textAnchor = m_oldTextAnchor;
+ states.fontWeight = m_oldWeight;
}
-qreal QSvgFontStyle::pointSize() const
+QSvgStrokeStyle::QSvgStrokeStyle()
+ : m_strokeOpacity(1.0)
+ , m_oldStrokeOpacity(0.0)
+ , m_strokeDashOffset(0)
+ , m_oldStrokeDashOffset(0)
+ , m_style(0)
+ , m_gradientResolved(1)
+ , m_vectorEffect(0)
+ , m_oldVectorEffect(0)
+ , m_strokeSet(0)
+ , m_strokeDashArraySet(0)
+ , m_strokeDashOffsetSet(0)
+ , m_strokeLineCapSet(0)
+ , m_strokeLineJoinSet(0)
+ , m_strokeMiterLimitSet(0)
+ , m_strokeOpacitySet(0)
+ , m_strokeWidthSet(0)
+ , m_vectorEffectSet(0)
{
- return m_pointSize;
}
-void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
{
- if (!m_font) {
- m_oldFont = p->font();
- p->setFont(m_qfont);
+ m_oldStroke = p->pen();
+ m_oldStrokeOpacity = states.strokeOpacity;
+ m_oldStrokeDashOffset = states.strokeDashOffset;
+ m_oldVectorEffect = states.vectorEffect;
+
+ QPen pen = p->pen();
+
+ qreal oldWidth = pen.widthF();
+ qreal width = m_stroke.widthF();
+ if (oldWidth == 0)
+ oldWidth = 1;
+ if (width == 0)
+ width = 1;
+ qreal scale = oldWidth / width;
+
+ if (m_strokeOpacitySet)
+ states.strokeOpacity = m_strokeOpacity;
+
+ if (m_vectorEffectSet)
+ states.vectorEffect = m_vectorEffect;
+
+ if (m_strokeSet) {
+ if (m_style)
+ pen.setBrush(m_style->brush(p, states));
+ else
+ pen.setBrush(m_stroke.brush());
+ }
+
+ if (m_strokeWidthSet)
+ pen.setWidthF(m_stroke.widthF());
+
+ bool setDashOffsetNeeded = false;
+
+ if (m_strokeDashOffsetSet) {
+ states.strokeDashOffset = m_strokeDashOffset;
+ setDashOffsetNeeded = true;
+ }
+
+ if (m_strokeDashArraySet) {
+ if (m_stroke.style() == Qt::SolidLine) {
+ pen.setStyle(Qt::SolidLine);
+ } else if (m_strokeWidthSet || oldWidth == 1) {
+ // If both width and dash array was set, the dash array is already scaled correctly.
+ pen.setDashPattern(m_stroke.dashPattern());
+ setDashOffsetNeeded = true;
+ } else {
+ // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width.
+ QVector<qreal> dashes = m_stroke.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] /= oldWidth;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
+ }
+ } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) {
+ // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width.
+ QVector<qreal> dashes = pen.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] *= scale;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
}
-}
-void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &)
-{
- if (!m_font) {
- p->setFont(m_oldFont);
+ if (m_strokeLineCapSet)
+ pen.setCapStyle(m_stroke.capStyle());
+ if (m_strokeLineJoinSet)
+ pen.setJoinStyle(m_stroke.joinStyle());
+ if (m_strokeMiterLimitSet)
+ pen.setMiterLimit(m_stroke.miterLimit());
+
+ if (setDashOffsetNeeded) {
+ qreal currentWidth = pen.widthF();
+ if (currentWidth == 0)
+ currentWidth = 1;
+ pen.setDashOffset(states.strokeDashOffset / currentWidth);
}
-}
-QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen)
- : m_stroke(pen)
-{
-}
+ pen.setCosmetic(states.vectorEffect);
-void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
-{
- m_oldStroke = p->pen();
- p->setPen(m_stroke);
+ p->setPen(pen);
}
-void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &)
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
{
p->setPen(m_oldStroke);
+ states.strokeOpacity = m_oldStrokeOpacity;
+ states.strokeDashOffset = m_oldStrokeDashOffset;
+ states.vectorEffect = m_oldVectorEffect;
}
-QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
- : m_solidColor(color)
-{
-}
-
-void QSvgSolidColorStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes)
{
- 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);
+ if (m_strokeWidthSet) {
+ QVector<qreal> d = dashes;
+ qreal w = m_stroke.widthF();
+ if (w != 0 && w != 1) {
+ for (int i = 0; i < d.size(); ++i)
+ d[i] /= w;
+ }
+ m_stroke.setDashPattern(d);
+ } else {
+ m_stroke.setDashPattern(dashes);
+ }
+ m_strokeDashArraySet = 1;
}
-void QSvgSolidColorStyle::revert(QPainter *p, QSvgExtraStates &)
+QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
+ : m_solidColor(color)
{
- p->setBrush(m_oldFill);
- p->setPen(m_oldStroke);
}
QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
@@ -237,44 +409,24 @@ QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
{
}
-void QSvgGradientStyle::apply(QPainter *p, const QRectF &/*rect*/, QSvgNode *, QSvgExtraStates &)
+QBrush QSvgGradientStyle::brush(QPainter *, 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
if (!m_gradientStopsSet) {
m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
m_gradientStopsSet = true;
}
- QBrush brush;
- brush = QBrush(*m_gradient);
+ QBrush b(*m_gradient);
if (!m_matrix.isIdentity())
- brush.setMatrix(m_matrix);
-
- p->setBrush(brush);
-}
+ b.setMatrix(m_matrix);
-void QSvgGradientStyle::revert(QPainter *p, QSvgExtraStates &)
-{
- p->setBrush(m_oldFill);
+ return b;
}
@@ -283,11 +435,6 @@ 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)
{
@@ -393,14 +540,6 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra
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);
}
@@ -412,10 +551,25 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra
//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);
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ // Find the last animateTransform with additive="replace", since this will override all
+ // previous animateTransforms.
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd();
+ do {
+ --itr;
+ if ((*itr)->animActive(totalTimeElapsed)
+ && (*itr)->additiveType() == QSvgAnimateTransform::Replace) {
+ // An animateTransform with additive="replace" will replace the transform attribute.
+ if (transform)
+ transform->revert(p, states);
+ break;
+ }
+ } while (itr != animateTransforms.constBegin());
+
+ // Apply the animateTransforms after and including the last one with additive="replace".
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->animActive(totalTimeElapsed))
+ (*itr)->apply(p, rect, node, states);
}
}
@@ -450,24 +604,18 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
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);
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin();
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->transformApplied()) {
+ (*itr)->revert(p, states);
+ break;
+ }
}
+ for (; itr != animateTransforms.constEnd(); ++itr)
+ (*itr)->clearTransformApplied();
}
if (transform) {
@@ -490,15 +638,16 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &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_type(Empty), m_additive(Replace), m_count(0), m_finished(false), m_transformApplied(false)
{
m_totalRunningTime = m_to - m_from;
}
-void QSvgAnimateTransform::setArgs(TransformType type, const QVector<qreal> &args)
+void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args)
{
m_type = type;
m_args = args;
+ m_additive = additive;
Q_ASSERT(!(args.count()%3));
m_count = args.count() / 3;
}
@@ -507,15 +656,14 @@ void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QS
{
m_oldWorldTransform = p->worldTransform();
resolveMatrix(node);
- if (!m_finished || m_freeze)
- p->setWorldTransform(m_transform, true);
+ p->setWorldTransform(m_transform, true);
+ m_transformApplied = true;
}
void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
{
- if (!m_finished || m_freeze) {
- p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
- }
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+ m_transformApplied = false;
}
void QSvgAnimateTransform::resolveMatrix(QSvgNode *node)
@@ -701,7 +849,9 @@ void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgEx
if (totalTimeElapsed < m_from || m_finished)
return;
- qreal animationFrame = (totalTimeElapsed - m_from) / m_to;
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0)
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
m_finished = true;
@@ -765,18 +915,8 @@ 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)
+ : m_opacity(opacity), m_oldOpacity(0)
{
}
diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h
index fccf18c..b9ec654 100644
--- a/src/svg/qsvgstyle_p.h
+++ b/src/svg/qsvgstyle_p.h
@@ -141,7 +141,15 @@ private:
struct QSvgExtraStates
{
QSvgExtraStates();
+
qreal fillOpacity;
+ qreal strokeOpacity;
+ QSvgFont *svgFont;
+ Qt::Alignment textAnchor;
+ int fontWeight;
+ Qt::FillRule fillRule;
+ qreal strokeDashOffset;
+ bool vectorEffect; // true if pen is cosmetic
};
class QSvgStyleProperty : public QSvgRefCounted
@@ -169,6 +177,14 @@ public:
virtual Type type() const=0;
};
+class QSvgFillStyleProperty : public QSvgStyleProperty
+{
+public:
+ virtual QBrush brush(QPainter *p, QSvgExtraStates &states) = 0;
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+};
+
class QSvgQualityStyle : public QSvgStyleProperty
{
public:
@@ -216,31 +232,74 @@ private:
class QSvgFillStyle : public QSvgStyleProperty
{
public:
- QSvgFillStyle(const QBrush &brush);
- QSvgFillStyle(QSvgStyleProperty *style);
+ QSvgFillStyle();
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);
+ void setFillStyle(QSvgFillStyleProperty* style);
+ void setBrush(QBrush brush);
const QBrush & qbrush() const
{
return m_fill;
}
+
+ qreal fillOpacity() const
+ {
+ return m_fillOpacity;
+ }
+
+ Qt::FillRule fillRule() const
+ {
+ return m_fillRule;
+ }
+
+ QSvgFillStyleProperty* style() const
+ {
+ return m_style;
+ }
+
+ void setGradientId(const QString &Id)
+ {
+ m_gradientId = Id;
+ }
+
+ QString gradientId() const
+ {
+ return m_gradientId;
+ }
+
+ void setGradientResolved(bool resolved)
+ {
+ m_gradientResolved = resolved;
+ }
+
+ bool isGradientResolved() const
+ {
+ return m_gradientResolved;
+ }
+
private:
// fill v v 'inherit' | <Paint.datatype>
// fill-opacity v v 'inherit' | <OpacityValue.datatype>
QBrush m_fill;
QBrush m_oldFill;
- QSvgStyleProperty *m_style;
+ QSvgFillStyleProperty *m_style;
- bool m_fillRuleSet;
Qt::FillRule m_fillRule;
- bool m_fillOpacitySet;
+ Qt::FillRule m_oldFillRule;
qreal m_fillOpacity;
- qreal m_oldOpacity;
+ qreal m_oldFillOpacity;
+
+ QString m_gradientId;
+ uint m_gradientResolved : 1;
+
+ uint m_fillRuleSet : 1;
+ uint m_fillOpacitySet : 1;
+ uint m_fillSet : 1;
};
class QSvgViewportFillStyle : public QSvgStyleProperty
@@ -266,55 +325,194 @@ private:
class QSvgFontStyle : public QSvgStyleProperty
{
public:
+ static const int LIGHTER = -1;
+ static const int BOLDER = 1;
+
QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc);
- QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc);
+ QSvgFontStyle();
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;
+ void setSize(qreal size)
+ {
+ // Store the _pixel_ size in the font. Since QFont::setPixelSize() only takes an int, call
+ // QFont::SetPointSize() instead. Set proper font size just before rendering.
+ m_qfont.setPointSize(size);
+ m_sizeSet = 1;
+ }
- //### hack to avoid having a separate style element for text-anchor
- QString textAnchor() const;
- void setTextAnchor(const QString &anchor);
+ void setTextAnchor(Qt::Alignment anchor)
+ {
+ m_textAnchor = anchor;
+ m_textAnchorSet = 1;
+ }
- QSvgFont * svgFont() const
+ void setFamily(const QString &family)
{
- return m_font;
+ m_qfont.setFamily(family);
+ m_familySet = 1;
}
- QSvgTinyDocument *doc() const
+
+ void setStyle(QFont::Style fontStyle) {
+ m_qfont.setStyle(fontStyle);
+ m_styleSet = 1;
+ }
+
+ void setVariant(QFont::Capitalization fontVariant)
{
- return m_doc;
+ m_qfont.setCapitalization(fontVariant);
+ m_variantSet = 1;
}
- const QFont & qfont() const
+ static int SVGToQtWeight(int weight);
+
+ void setWeight(int weight)
+ {
+ m_weight = weight;
+ m_weightSet = 1;
+ }
+
+ QSvgFont * svgFont() const
+ {
+ return m_svgFont;
+ }
+
+ const QFont &qfont() const
{
return m_qfont;
}
+
+ QSvgTinyDocument *doc() const {return m_doc;}
+
private:
- QSvgFont *m_font;
- qreal m_pointSize;
+ QSvgFont *m_svgFont;
QSvgTinyDocument *m_doc;
+ QFont m_qfont;
- QString m_textAnchor;
+ int m_weight;
+ Qt::Alignment m_textAnchor;
- QFont m_qfont;
- QFont m_oldFont;
+ QSvgFont *m_oldSvgFont;
+ QFont m_oldQFont;
+ Qt::Alignment m_oldTextAnchor;
+ int m_oldWeight;
+
+ uint m_familySet : 1;
+ uint m_sizeSet : 1;
+ uint m_styleSet : 1;
+ uint m_variantSet : 1;
+ uint m_weightSet : 1;
+ uint m_textAnchorSet : 1;
};
class QSvgStrokeStyle : public QSvgStyleProperty
{
public:
- QSvgStrokeStyle(const QPen &pen);
+ QSvgStrokeStyle();
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
+ void setStroke(QBrush brush)
+ {
+ m_stroke.setBrush(brush);
+ m_style = 0;
+ m_strokeSet = 1;
+ }
+
+ void setStyle(QSvgFillStyleProperty *style)
+ {
+ m_style = style;
+ m_strokeSet = 1;
+ }
+
+ void setDashArray(const QVector<qreal> &dashes);
+
+ void setDashArrayNone()
+ {
+ m_stroke.setStyle(Qt::SolidLine);
+ m_strokeDashArraySet = 1;
+ }
+
+ void setDashOffset(qreal offset)
+ {
+ m_strokeDashOffset = offset;
+ m_strokeDashOffsetSet = 1;
+ }
+
+ void setLineCap(Qt::PenCapStyle cap)
+ {
+ m_stroke.setCapStyle(cap);
+ m_strokeLineCapSet = 1;
+ }
+
+ void setLineJoin(Qt::PenJoinStyle join)
+ {
+ m_stroke.setJoinStyle(join);
+ m_strokeLineJoinSet = 1;
+ }
+
+ void setMiterLimit(qreal limit)
+ {
+ m_stroke.setMiterLimit(limit);
+ m_strokeMiterLimitSet = 1;
+ }
+
+ void setOpacity(qreal opacity)
+ {
+ m_strokeOpacity = opacity;
+ m_strokeOpacitySet = 1;
+ }
+
+ void setWidth(qreal width)
+ {
+ m_stroke.setWidthF(width);
+ m_strokeWidthSet = 1;
+ Q_ASSERT(!m_strokeDashArraySet); // set width before dash array.
+ }
+
+ qreal width()
+ {
+ return m_stroke.widthF();
+ }
+
+ void setVectorEffect(bool nonScalingStroke)
+ {
+ m_vectorEffect = nonScalingStroke;
+ m_vectorEffectSet = 1;
+ }
+
+ QSvgFillStyleProperty* style() const
+ {
+ return m_style;
+ }
+
+ void setGradientId(const QString &Id)
+ {
+ m_gradientId = Id;
+ }
+
+ QString gradientId() const
+ {
+ return m_gradientId;
+ }
+
+ void setGradientResolved(bool resolved)
+ {
+ m_gradientResolved = resolved;
+ }
+
+ bool isGradientResolved() const
+ {
+ return m_gradientResolved;
+ }
+
+ QPen stroke() const
{
return m_stroke;
}
+
private:
// stroke v v 'inherit' | <Paint.datatype>
// stroke-dasharray v v 'inherit' | <StrokeDashArrayValue.datatype>
@@ -325,23 +523,45 @@ private:
// stroke-opacity v v 'inherit' | <OpacityValue.datatype>
// stroke-width v v 'inherit' | <StrokeWidthValue.datatype>
QPen m_stroke;
-
QPen m_oldStroke;
+ qreal m_strokeOpacity;
+ qreal m_oldStrokeOpacity;
+ qreal m_strokeDashOffset;
+ qreal m_oldStrokeDashOffset;
+
+ QSvgFillStyleProperty *m_style;
+ QString m_gradientId;
+ uint m_gradientResolved : 1;
+ uint m_vectorEffect : 1;
+ uint m_oldVectorEffect : 1;
+
+ uint m_strokeSet : 1;
+ uint m_strokeDashArraySet : 1;
+ uint m_strokeDashOffsetSet : 1;
+ uint m_strokeLineCapSet : 1;
+ uint m_strokeLineJoinSet : 1;
+ uint m_strokeMiterLimitSet : 1;
+ uint m_strokeOpacitySet : 1;
+ uint m_strokeWidthSet : 1;
+ uint m_vectorEffectSet : 1;
};
-
-class QSvgSolidColorStyle : public QSvgStyleProperty
+class QSvgSolidColorStyle : public QSvgFillStyleProperty
{
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;
}
+
+ QBrush brush(QPainter *, QSvgExtraStates &)
+ {
+ return m_solidColor;
+ }
+
private:
// solid-color v x 'inherit' | <SVGColor.datatype>
// solid-opacity v x 'inherit' | <OpacityValue.datatype>
@@ -351,13 +571,11 @@ private:
QPen m_oldStroke;
};
-class QSvgGradientStyle : public QSvgStyleProperty
+class QSvgGradientStyle : public QSvgFillStyleProperty
{
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);
@@ -375,8 +593,6 @@ public:
return m_gradient;
}
- void addResolve(qreal offset);
-
bool gradientStopsSet() const
{
return m_gradientStopsSet;
@@ -386,12 +602,10 @@ public:
{
m_gradientStopsSet = set;
}
+
+ QBrush brush(QPainter *, QSvgExtraStates &);
private:
QGradient *m_gradient;
- QList<qreal> m_resolvePoints;
-
- QBrush m_oldFill;
-
QMatrix m_matrix;
QSvgTinyDocument *m_doc;
@@ -430,20 +644,56 @@ public:
SkewX,
SkewY
};
+ enum Additive
+ {
+ Sum,
+ Replace
+ };
public:
QSvgAnimateTransform(int startMs, int endMs, int by = 0);
- void setArgs(TransformType type, const QVector<qreal> &args);
+ void setArgs(TransformType type, Additive additive, 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;
+ QSvgAnimateTransform::Additive additiveType() const
+ {
+ return m_additive;
+ }
+
+ bool animActive(qreal totalTimeElapsed)
+ {
+ if (totalTimeElapsed < m_from)
+ return false;
+ if (m_freeze || m_repeatCount < 0) // fill="freeze" or repeat="indefinite"
+ return true;
+ if (m_totalRunningTime == 0)
+ return false;
+ qreal animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+ if (animationFrame > m_repeatCount)
+ return false;
+ return true;
+ }
+
+ bool transformApplied() const
+ {
+ return m_transformApplied;
+ }
+
+ // Call this instead of revert if you know that revert is unnecessary.
+ void clearTransformApplied()
+ {
+ m_transformApplied = false;
+ }
+
protected:
void resolveMatrix(QSvgNode *node);
private:
qreal m_from, m_to, m_by;
qreal m_totalRunningTime;
TransformType m_type;
+ Additive m_additive;
QVector<qreal> m_args;
int m_count;
QTransform m_transform;
@@ -451,6 +701,7 @@ private:
bool m_finished;
bool m_freeze;
qreal m_repeatCount;
+ bool m_transformApplied;
};
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp
index e93d6de..54459d5 100644
--- a/src/svg/qsvgtinydocument.cpp
+++ b/src/svg/qsvgtinydocument.cpp
@@ -61,10 +61,12 @@
QT_BEGIN_NAMESPACE
QSvgTinyDocument::QSvgTinyDocument()
- : QSvgStructureNode(0),
- m_animated(false),
- m_animationDuration(0),
- m_fps(30)
+ : QSvgStructureNode(0)
+ , m_widthPercent(false)
+ , m_heightPercent(false)
+ , m_animated(false)
+ , m_animationDuration(0)
+ , m_fps(30)
{
}
@@ -231,12 +233,16 @@ void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
m_time.start();
}
- p->save();
+ if (displayMode() == QSvgNode::NoneMode)
+ return;
+ p->save();
//sets default style on the painter
//### not the most optimal way
mapSourceToTarget(p, bounds);
- p->setPen(Qt::NoPen);
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform);
@@ -244,7 +250,7 @@ void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
applyStyle(p, m_states);
while (itr != m_renderers.end()) {
QSvgNode *node = *itr;
- if (node->isVisible())
+ if ((node->isVisible()) && (node->displayMode() != QSvgNode::NoneMode))
node->draw(p, m_states);
++itr;
}
@@ -262,6 +268,12 @@ void QSvgTinyDocument::draw(QPainter *p, const QString &id,
qDebug("Couldn't find node %s. Skipping rendering.", qPrintable(id));
return;
}
+ if (m_time.isNull()) {
+ m_time.start();
+ }
+
+ if (node->displayMode() == QSvgNode::NoneMode)
+ return;
p->save();
@@ -271,7 +283,9 @@ void QSvgTinyDocument::draw(QPainter *p, const QString &id,
QTransform originalTransform = p->worldTransform();
//XXX set default style on the painter
- p->setPen(Qt::NoPen);
+ QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform);
diff --git a/src/svg/qsvgwidget.cpp b/src/svg/qsvgwidget.cpp
index c000b52..de72112 100644
--- a/src/svg/qsvgwidget.cpp
+++ b/src/svg/qsvgwidget.cpp
@@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QSvgWidget
- \ingroup multimedia
+ \ingroup painting
\brief The QSvgWidget class provides a widget that is used to display the contents of
Scalable Vector Graphics (SVG) files.
diff --git a/src/svg/svg.pro b/src/svg/svg.pro
index aef0786..d2a4227 100644
--- a/src/svg/svg.pro
+++ b/src/svg/svg.pro
@@ -40,9 +40,11 @@ SOURCES += \
INCLUDEPATH += ../3rdparty/harfbuzz/src
+symbian:TARGET.UID3=0x2001B2E2
+
#zlib support
contains(QT_CONFIG, zlib) {
INCLUDEPATH += ../3rdparty/zlib
} else:!contains(QT_CONFIG, no-zlib) {
- unix:LIBS += -lz
+ unix:LIBS_PRIVATE += -lz
}