diff options
Diffstat (limited to 'src/svg/qsvghandler.cpp')
-rw-r--r-- | src/svg/qsvghandler.cpp | 1744 |
1 files changed, 964 insertions, 780 deletions
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index bd8c504..e2c3d92 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,342 @@ #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); +// ======== duplicated from qcolor_p + +static inline int qsvg_h2i(char hex) +{ + if (hex >= '0' && hex <= '9') + return hex - '0'; + if (hex >= 'a' && hex <= 'f') + return hex - 'a' + 10; + if (hex >= 'A' && hex <= 'F') + return hex - 'A' + 10; + return -1; +} + +static inline int qsvg_hex2int(const char *s) +{ + return (qsvg_h2i(s[0]) << 4) | qsvg_h2i(s[1]); +} + +static inline int qsvg_hex2int(char s) +{ + int h = qsvg_h2i(s); + return (h << 4) | h; +} + +bool qsvg_get_hex_rgb(const char *name, QRgb *rgb) +{ + if(name[0] != '#') + return false; + name++; + int len = qstrlen(name); + int r, g, b; + if (len == 12) { + r = qsvg_hex2int(name); + g = qsvg_hex2int(name + 4); + b = qsvg_hex2int(name + 8); + } else if (len == 9) { + r = qsvg_hex2int(name); + g = qsvg_hex2int(name + 3); + b = qsvg_hex2int(name + 6); + } else if (len == 6) { + r = qsvg_hex2int(name); + g = qsvg_hex2int(name + 2); + b = qsvg_hex2int(name + 4); + } else if (len == 3) { + r = qsvg_hex2int(name[0]); + g = qsvg_hex2int(name[1]); + b = qsvg_hex2int(name[2]); + } else { + r = g = b = -1; + } + if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) { + *rgb = 0; + return false; + } + *rgb = qRgb(r, g ,b); + return true; +} + +bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb) +{ + if (len > 13) + return false; + char tmp[16]; + for(int i = 0; i < len; ++i) + tmp[i] = str[i].toLatin1(); + tmp[len] = 0; + return qsvg_get_hex_rgb(tmp, rgb); +} + +// ======== end of qcolor_p duplicate + static bool parsePathDataFast(const QStringRef &data, QPainterPath &path); +static inline QString someId(const QXmlStreamAttributes &attributes) +{ + QString id = attributes.value(QLatin1String("id")).toString(); + if (id.isEmpty()) + id = attributes.value(QLatin1String("xml:id")).toString(); + return id; +} + struct QSvgAttributes { QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler); - 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) { 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 'i': + if (name == QLatin1String("id")) + id = value.toString(); + break; + + case 'o': + if (name == QLatin1String("opacity")) + opacity = value; + if (name == QLatin1String("offset")) + offset = value; + break; + + case 's': + if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) { + QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6); + if (strokeRef.isEmpty()) + stroke = value; + else if (strokeRef == QLatin1String("-dasharray")) + strokeDashArray = value; + else if (strokeRef == QLatin1String("-dashoffset")) + strokeDashOffset = value; + else if (strokeRef == QLatin1String("-linecap")) + strokeLineCap = value; + else if (strokeRef == QLatin1String("-linejoin")) + strokeLineJoin = value; + else if (strokeRef == QLatin1String("-miterlimit")) + strokeMiterLimit = value; + else if (strokeRef == QLatin1String("-opacity")) + strokeOpacity = value; + else if (strokeRef == QLatin1String("-width")) + strokeWidth = value; } + else if (name == QLatin1String("stop-color")) + stopColor = value; + else if (name == QLatin1String("stop-opacity")) + stopOpacity = value; + break; + + case 't': + if (name == QLatin1String("text-anchor")) + textAnchor = value; + else if (name == QLatin1String("transform")) + transform = value; + break; + + case 'v': + if (name == QLatin1String("vector-effect")) + vectorEffect = value; + else if (name == QLatin1String("visibility")) + visibility = value; + break; + + case 'x': + if (name == QLatin1String("xml:id") && id.isEmpty()) + id = value.toString(); + break; + + default: + break; } } - return v; -} -static inline QString someId(const QXmlStreamAttributes &attributes) -{ - QString id = attributes.value(QLatin1String("id")).toString(); - if (id.isEmpty()) - id = attributes.value(QLatin1String("xml:id")).toString(); - return id; } -static inline QString someId(const QSvgAttributes &attributes) -{ return someId(attributes.m_xmlAttributes); } - - static const char * QSvgStyleSelector_nodeString[] = { "svg", @@ -282,6 +552,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 +571,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 +579,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 +592,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 +630,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 +643,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 +672,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 +691,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,58 +762,111 @@ 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 */ -static bool resolveColor(const QString &colorStr, QColor &color, QSvgHandler *handler) -{ - QString colorStrTr = colorStr.trimmed(); - if (colorStr.startsWith(QLatin1String("rgb("))) { - const QChar *s = colorStr.constData() + 4; - QVector<qreal> compo = parseNumbersList(s); - //1 means that it failed after reaching non-parsable - //character which is going to be "%" - if (compo.size() == 1) { - const QChar *s = colorStr.constData() + 4; - compo = parsePercentageList(s); - compo[0] *= (qreal)2.55; - compo[1] *= (qreal)2.55; - compo[2] *= (qreal)2.55; - } - - color = QColor(int(compo[0]), - int(compo[1]), - int(compo[2])); - return true; - } else if (colorStr == QLatin1String("inherited") || - colorStr == QLatin1String("inherit")) { +static bool resolveColor(const QStringRef &colorStr, QColor &color, QSvgHandler *handler) +{ + QStringRef colorStrTr = trimRef(colorStr); + if (colorStrTr.isEmpty()) return false; - } else if (colorStr == QLatin1String("currentColor")) { - color = handler->currentColor(); - return true; + + switch(colorStrTr.at(0).unicode()) { + + case '#': + { + // #rrggbb is very very common, so let's tackle it here + // rather than falling back to QColor + QRgb rgb; + bool ok = qsvg_get_hex_rgb(colorStrTr.unicode(), colorStrTr.length(), &rgb); + if (ok) + color.setRgb(rgb); + return ok; + } + break; + + case 'r': + { + // starts with "rgb(" + if (colorStrTr == QLatin1String("rgb(")) { + const QChar *s = colorStrTr.constData() + 4; + QVector<qreal> compo = parseNumbersList(s); + //1 means that it failed after reaching non-parsable + //character which is going to be "%" + if (compo.size() == 1) { + const QChar *s = colorStrTr.constData() + 4; + compo = parsePercentageList(s); + compo[0] *= (qreal)2.55; + compo[1] *= (qreal)2.55; + compo[2] *= (qreal)2.55; + } + + color = QColor(int(compo[0]), + int(compo[1]), + int(compo[2])); + return true; + } + } + break; + + case 'c': + if (colorStrTr == QLatin1String("currentColor")) { + color = handler->currentColor(); + return true; + } + break; + case 'i': + if (colorStrTr == QT_INHERIT) + return false; + break; + default: + break; } - color = QColor(colorStrTr); + color = QColor(colorStrTr.toString()); return color.isValid(); } -static bool constructColor(const QString &colorStr, const QString &opacity, +static bool constructColor(const QStringRef &colorStr, const QStringRef &opacity, QColor &color, QSvgHandler *handler) { if (!resolveColor(colorStr, color, handler)) return false; if (!opacity.isEmpty()) { - qreal op = toDouble(opacity); - if (op <= 1) - op *= 255; - color.setAlpha(int(op)); + 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 +895,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,10 +963,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(); QColor color; - if (constructColor(colorStr, opacity, color, handler)) { + if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) { handler->pushColor(color); } } @@ -628,144 +986,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, color, handler)) + prop->setBrush(QBrush(color)); + } else { + prop->setBrush(QBrush(Qt::NoBrush)); } - - } else { - pen.setStyle(Qt::NoPen); } + node->appendStyleProperty(prop, attributes.id); } } -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 +1105,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 +1167,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, 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); - } - if (!dashOffset.isEmpty()) { - qreal doffset = toDouble(dashOffset); - if (penw != 0) - doffset /= penw; - pen.setDashOffset(doffset); + prop->setDashArray(dashes); } - 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, attributes.id); } - - 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, attributes.id); } 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)), attributes.id); } } @@ -1194,12 +1356,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 +1520,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 +1678,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 +1700,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 +1761,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 +1782,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 +1805,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 +1979,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 +1988,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,15 +2040,17 @@ 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); - node->appendStyleProperty(opacity, someId(attributes)); + QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0))); + node->appendStyleProperty(opacity, attributes.id); } } @@ -2010,12 +2116,13 @@ 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)); - node->appendStyleProperty(compop, someId(attributes)); + node->appendStyleProperty(compop, attributes.id); } } @@ -2055,7 +2162,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 +2172,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)); @@ -2144,8 +2252,8 @@ static bool parseAnimateColorNode(QSvgNode *parent, QSvgHandler *handler) { QString typeStr = attributes.value(QLatin1String("type")).toString(); - QString fromStr = attributes.value(QLatin1String("from")).toString(); - QString toStr = attributes.value(QLatin1String("to")).toString(); + QStringRef fromStr = attributes.value(QLatin1String("from")); + QStringRef toStr = attributes.value(QLatin1String("to")); QString valuesStr = attributes.value(QLatin1String("values")).toString(); QString beginStr = attributes.value(QLatin1String("begin")).toString(); QString durStr = attributes.value(QLatin1String("dur")).toString(); @@ -2156,8 +2264,8 @@ static bool parseAnimateColorNode(QSvgNode *parent, QList<QColor> colors; if (valuesStr.isEmpty()) { QColor startColor, endColor; - constructColor(fromStr, QString(), startColor, handler); - constructColor(toStr, QString(), endColor, handler); + resolveColor(fromStr, startColor, handler); + resolveColor(toStr, endColor, handler); colors.append(startColor); colors.append(endColor); } else { @@ -2165,7 +2273,8 @@ static bool parseAnimateColorNode(QSvgNode *parent, QStringList::const_iterator itr; for (itr = str.constBegin(); itr != str.constEnd(); ++itr) { QColor color; - constructColor(*itr, QString(), color, handler); + QString str = *itr; + resolveColor(QStringRef(&str), color, handler); colors.append(color); } } @@ -2209,6 +2318,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 +2339,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 +2421,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 +2686,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 +2723,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(); @@ -2870,11 +3009,11 @@ static QSvgStyleProperty *createSolidColorNode(QSvgNode *parent, QSvgHandler *handler) { Q_UNUSED(parent); Q_UNUSED(attributes); - QString solidColorStr = attributes.value(QLatin1String("solid-color")).toString(); - QString solidOpacityStr = attributes.value(QLatin1String("solid-opacity")).toString(); + QStringRef solidColorStr = attributes.value(QLatin1String("solid-color")); + QStringRef solidOpacityStr = attributes.value(QLatin1String("solid-opacity")); if (solidOpacityStr.isEmpty()) - solidOpacityStr = attributes.value(QLatin1String("opacity")).toString(); + solidOpacityStr = attributes.value(QLatin1String("opacity")); QColor color; if (!constructColor(solidColorStr, solidOpacityStr, color, handler)) @@ -2904,8 +3043,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 +3057,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(); + QStringRef colorStr = attrs.stopColor; QColor color; - qreal offset = convertToNumber(offsetStr, handler); + + bool ok = true; + qreal offset = convertToNumber(offsetStr, handler, &ok); + if (!ok) + offset = 0.0; + QString black = QString::fromLatin1("#000000"); if (colorStr.isEmpty()) { - colorStr = QLatin1String("#000000"); + colorStr = QStringRef(&black); } - bool colorOK = constructColor(colorStr, opacityStr, color, handler); + constructColor(colorStr, attrs.stopOpacity, color, handler); QGradient *grad = style->qgradient(); @@ -2957,8 +3100,6 @@ static bool parseStopNode(QSvgStyleProperty *parent, grad->setColorAt(offset, color); style->setGradientStopsSet(true); - if (!colorOK) - style->addResolve(offset); return true; } @@ -3030,15 +3171,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 +3199,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 +3214,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 +3232,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 +3247,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 +3360,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 +3417,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 +3509,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 +3625,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 +3664,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 +3708,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 +3770,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; |