diff options
Diffstat (limited to 'src/svg')
-rw-r--r-- | src/svg/qgraphicssvgitem.cpp | 3 | ||||
-rw-r--r-- | src/svg/qgraphicssvgitem.h | 6 | ||||
-rw-r--r-- | src/svg/qsvggenerator.cpp | 83 | ||||
-rw-r--r-- | src/svg/qsvggenerator.h | 3 | ||||
-rw-r--r-- | src/svg/qsvggraphics.cpp | 379 | ||||
-rw-r--r-- | src/svg/qsvggraphics_p.h | 53 | ||||
-rw-r--r-- | src/svg/qsvghandler.cpp | 1561 | ||||
-rw-r--r-- | src/svg/qsvgnode.cpp | 6 | ||||
-rw-r--r-- | src/svg/qsvgnode_p.h | 1 | ||||
-rw-r--r-- | src/svg/qsvgrenderer.cpp | 2 | ||||
-rw-r--r-- | src/svg/qsvgstructure.cpp | 98 | ||||
-rw-r--r-- | src/svg/qsvgstyle.cpp | 448 | ||||
-rw-r--r-- | src/svg/qsvgstyle_p.h | 329 | ||||
-rw-r--r-- | src/svg/qsvgtinydocument.cpp | 30 | ||||
-rw-r--r-- | src/svg/qsvgwidget.cpp | 2 | ||||
-rw-r--r-- | src/svg/svg.pro | 4 |
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 } |