diff options
-rw-r--r-- | src/svg/qsvghandler.cpp | 244 | ||||
-rw-r--r-- | src/svg/qsvgnode.cpp | 2 | ||||
-rw-r--r-- | src/svg/qsvgstyle.cpp | 8 | ||||
-rw-r--r-- | src/svg/qsvgstyle_p.h | 12 | ||||
-rw-r--r-- | tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 124 |
5 files changed, 294 insertions, 96 deletions
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index 345dcf3..5950fac 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -908,103 +908,155 @@ static void parsePen(QSvgNode *node, //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(); + /* All the below checks needed because g (or any container element) can have only one of these attributes. + * If it doesn't has any one of these, then processing below not needed */ + + if (!value.isEmpty() || !width.isEmpty() || !dashArray.isEmpty() || !linecap.isEmpty() || + !linejoin.isEmpty() || !miterlimit.isEmpty() || !opacity.isEmpty() || !dashOffset.isEmpty() ) { + //if (value != QLatin1String("none")) { + /* If stroke = "none" then also, we need to parse below, because elements like 'g' may + have other defined stroke attributes, which will be inherited by its children */ + QSvgStrokeStyle *inherited = + static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty( + QSvgStyleProperty::STROKE)); + + QPen pen(handler->defaultPen()); + bool stroke = false; + if (inherited) { + pen = inherited->qpen(); + stroke = inherited->strokePresent(); + } - if (!value.isEmpty()) { - if (value.startsWith(QLatin1String("url"))) { - value = value.remove(0, 3); - QSvgStyleProperty *style = styleFromUrl(node, value); - if (style) { - if (style->type() == QSvgStyleProperty::GRADIENT) { - QBrush b(*((QSvgGradientStyle*)style)->qgradient()); - pen.setBrush(b); - } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) { - pen.setColor( - ((QSvgSolidColorStyle*)style)->qcolor()); - } - } else { - qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value); + // stroke-opacity attribute handling + qreal strokeAlpha; + if (!opacity.isEmpty() && opacity != QLatin1String("inherit")) { + strokeAlpha = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))); + } else { + strokeAlpha = pen.color().alphaF(); + } + + //stroke attribute handling + if (!value.isEmpty() && value != QLatin1String("inherit")) { + if (value.startsWith(QLatin1String("url"))) { + value = value.remove(0, 3); + QSvgStyleProperty *style = styleFromUrl(node, value); + if (style) { + if (style->type() == QSvgStyleProperty::GRADIENT) { + QBrush b(*((QSvgGradientStyle*)style)->qgradient()); + pen.setBrush(b); + } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) { + pen.setColor( + ((QSvgSolidColorStyle*)style)->qcolor()); } + pen.setStyle(Qt::SolidLine); + stroke = true; } else { - QColor color; - if (constructColor(value, opacity, color, handler)) - pen.setColor(color); + qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value); } - //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; + } else if (value == QLatin1String("none")) { + QColor color; //### fixme: dafalut value for color. + color.setAlphaF(strokeAlpha); + pen.setColor(color); + stroke = false; // This is required because, parent may have a valid stroke but the child may have stroke = "none" + } else { + QColor color; + if (resolveColor(value, color, handler)) { + color.setAlphaF(strokeAlpha); + pen.setColor(color); } - 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); + stroke = true; } + } else { + QColor color = pen.color(); + color.setAlphaF(strokeAlpha); + pen.setColor(color); + } - 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 + if (!width.isEmpty() && width != QLatin1String("inherit")) { + qreal widthF; + QSvgHandler::LengthType lt; + widthF = parseLength(width, lt, handler); + pen.setWidthF(widthF); + } else if (inherited){ + qreal widthF = inherited->qpen().widthF(); + pen.setWidthF(widthF); + } - qreal penw = pen.widthF(); - if (!dashArray.isEmpty()) { - const QChar *s = dashArray.constData(); - QVector<qreal> dashes = parseNumbersList(s); - qreal *d = dashes.data(); - if(penw != 0) - for (int i = 0; i < dashes.size(); ++i) { - *d /= penw; - ++d; - } + //stroke-linejoin attribute handling + 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 the dash count is odd the dashes should be duplicated - if (dashes.size() % 2 != 0) - dashes << QVector<qreal>(dashes); + //stroke-linecap attribute handling + 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); + } - pen.setDashPattern(dashes); - } - if (!dashOffset.isEmpty()) { - qreal doffset = toDouble(dashOffset); - if (penw != 0) - doffset /= penw; - pen.setDashOffset(doffset); + //strok-dasharray attribute handling + qreal penw = pen.widthF(); + if (!dashArray.isEmpty() && dashArray != QLatin1String("inherit")) { + const QChar *s = dashArray.constData(); + QVector<qreal> dashes = parseNumbersList(s); + qreal *d = dashes.data(); + if (penw != 0) + for (int i = 0; i < dashes.size(); ++i) { + *d /= penw; + ++d; + } + // if the dash count is odd the dashes should be duplicated + if (dashes.size() % 2 != 0) + dashes << QVector<qreal>(dashes); + pen.setDashPattern(dashes); + } else if (inherited) { + QVector<qreal> dashes(inherited->qpen().dashPattern()); + qreal *d = dashes.data(); + if (!penw) + penw = 1.0; + qreal inheritpenw = inherited->qpen().widthF(); + if (!inheritpenw) + inheritpenw = 1.0; + for ( int i = 0; i < dashes.size(); ++i) { + *d *= (inheritpenw/ penw); + ++d; } - if (!miterlimit.isEmpty()) - pen.setMiterLimit(toDouble(miterlimit)); + pen.setDashPattern(dashes); + } - node->appendStyleProperty(new QSvgStrokeStyle(pen), myId); - } else { - QPen pen(handler->defaultPen()); - pen.setStyle(Qt::NoPen); - node->appendStyleProperty(new QSvgStrokeStyle(pen), myId); + + //stroke-dashoffset attribute handling + if (!dashOffset.isEmpty() && dashOffset != QLatin1String("inherit")) { + qreal doffset = toDouble(dashOffset); + if (penw != 0) + doffset /= penw; + pen.setDashOffset(doffset); + } else if (inherited) { + qreal doffset = pen.dashOffset(); + if (!penw) + penw = 1.0; + qreal inheritpenw = inherited->qpen().widthF(); + if (!inheritpenw) + inheritpenw = 1.0; + doffset *= (inheritpenw/ penw); + pen.setDashOffset(doffset); } + + if (!miterlimit.isEmpty() && miterlimit != QLatin1String("inherit")) + pen.setMiterLimit(toDouble(miterlimit)); + + QSvgStrokeStyle *prop = new QSvgStrokeStyle(pen); + prop->setStroke(stroke); + node->appendStyleProperty(prop, myId); } } @@ -1071,10 +1123,14 @@ static bool parseQFont(const QSvgAttributes &attributes, 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; + if (size == QLatin1String("inherit")) { + //inherited already + } else { + 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()) { @@ -1154,9 +1210,13 @@ static void parseFont(QSvgNode *node, font = inherited->qfont(); fontSize = inherited->pointSize(); } - if (parseQFont(attributes, font, fontSize, handler)) { + // group or any container element can have only text-anchor and should + // be processed, because its children can inherit from it. + // So checking for text-anchor before parseQfont() + QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); + if (parseQFont(attributes, font, fontSize, handler) || (!anchor.isEmpty())) { QString myId = someId(attributes); - QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); + //QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); QSvgTinyDocument *doc = node->document(); QSvgFontStyle *fontStyle = 0; QString family = (font.family().isEmpty())?myId:font.family(); @@ -1865,7 +1925,7 @@ static bool parseDefaultTextStyle(QSvgNode *node, if (fontStyle) { font = fontStyle->qfont(); fontSize = fontStyle->pointSize(); - if (anchor.isEmpty()) + if (anchor.isEmpty() || anchor == QLatin1String("inherit")) anchor = fontStyle->textAnchor(); } @@ -3395,7 +3455,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(); } diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp index 75c01a9..ce8b3fa 100644 --- a/src/svg/qsvgnode.cpp +++ b/src/svg/qsvgnode.cpp @@ -320,7 +320,7 @@ qreal QSvgNode::strokeWidth() const { QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>( styleProperty(QSvgStyleProperty::STROKE)); - if (!stroke || stroke->qpen().style() == Qt::NoPen) + if (!stroke || !stroke->strokePresent()) return 0; return stroke->qpen().widthF(); } diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index fec6231..556201b 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -195,14 +195,18 @@ void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &) } QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen) - : m_stroke(pen) + : m_stroke(pen), m_strokePresent(true) { } void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) { m_oldStroke = p->pen(); - p->setPen(m_stroke); + if (!m_strokePresent || !m_stroke.widthF() || !m_stroke.color().alphaF()) { + p->setPen(Qt::NoPen); + } else { + p->setPen(m_stroke); + } } void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &) diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index e65b6f5..f1d0811 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -345,6 +345,16 @@ public: virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; + void setStroke(bool stroke) + { + m_strokePresent = stroke; + } + + bool strokePresent() const + { + return m_strokePresent; + } + const QPen & qpen() const { return m_stroke; @@ -359,8 +369,8 @@ private: // stroke-opacity v v 'inherit' | <OpacityValue.datatype> // stroke-width v v 'inherit' | <StrokeWidthValue.datatype> QPen m_stroke; - QPen m_oldStroke; + bool m_strokePresent; }; diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index f11aff9..7b62eae 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -80,6 +80,7 @@ private slots: void opacity(); void paths(); void displayMode(); + void strokeInherit(); #ifndef QT_NO_COMPRESS void testGzLoading(); @@ -925,5 +926,128 @@ void tst_QSvgRenderer::displayMode() } } +void tst_QSvgRenderer::strokeInherit() +{ + static const char *svgs[] = { + // Reference. + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\"/>" + " </g>" + "</svg>", + // stroke + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"none\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke=\"blue\"/>" + " </g>" + " <g stroke=\"yellow\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\" stroke=\"green\"/>" + " </g>" + "</svg>", + // stroke-width + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"0\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-width=\"20\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"10\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\" stroke-width=\"0\"/>" + " </g>" + "</svg>", + // stroke-linecap + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"round\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-linecap=\"butt\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\"/>" + " </g>" + "</svg>", + // stroke-linejoin + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"round\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-linejoin=\"miter\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\"/>" + " </g>" + "</svg>", + // stroke-miterlimit + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"2\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-miterlimit=\"1\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\"/>" + " </g>" + "</svg>", + // stroke-dasharray + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"1,1,1,1,1,1,3,1,3,1,3,1,1,1,1,1,1,3\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-dasharray=\"20,10\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"none\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\" stroke-dasharray=\"3,3,1\"/>" + " </g>" + "</svg>", + // stroke-dashoffset + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"0\" stroke-opacity=\"0.5\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-dashoffset=\"10\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"0\">" + " <polyline fill=\"none\" points=\"10 25 80 25\" stroke-dashoffset=\"4.5\"/>" + " </g>" + "</svg>", + // stroke-opacity + "<svg viewBox=\"0 0 200 30\">" + " <g stroke=\"blue\" stroke-width=\"20\" stroke-linecap=\"butt\"" + " stroke-linejoin=\"miter\" stroke-miterlimit=\"1\" stroke-dasharray=\"20,10\"" + " stroke-dashoffset=\"10\" stroke-opacity=\"0\">" + " <polyline fill=\"none\" points=\"10 10 100 10 100 20 190 20\" stroke-opacity=\"0.5\"/>" + " </g>" + " <g stroke=\"green\" stroke-width=\"0\" stroke-dasharray=\"3,3,1\" stroke-dashoffset=\"4.5\">" + " <polyline fill=\"none\" points=\"10 25 80 25\"/>" + " </g>" + "</svg>" + }; + + const int COUNT = sizeof(svgs) / sizeof(svgs[0]); + QImage images[COUNT]; + QPainter p; + + for (int i = 0; i < COUNT; ++i) { + QByteArray data(svgs[i]); + QSvgRenderer renderer(data); + QVERIFY(renderer.isValid()); + images[i] = QImage(200, 30, QImage::Format_ARGB32_Premultiplied); + images[i].fill(-1); + p.begin(&images[i]); + renderer.render(&p); + p.end(); + if (i != 0) { + QCOMPARE(images[0], images[i]); + } + } +} + QTEST_MAIN(tst_QSvgRenderer) #include "tst_qsvgrenderer.moc" |