summaryrefslogtreecommitdiffstats
path: root/src/svg/qsvghandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg/qsvghandler.cpp')
-rw-r--r--src/svg/qsvghandler.cpp1744
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;