src/svg/qsvghandler.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtSvg module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qplatformdefs.h"
       
    43 
       
    44 #include "qsvghandler_p.h"
       
    45 
       
    46 #ifndef QT_NO_SVG
       
    47 
       
    48 #include "qsvgtinydocument_p.h"
       
    49 #include "qsvgstructure_p.h"
       
    50 #include "qsvggraphics_p.h"
       
    51 #include "qsvgnode_p.h"
       
    52 #include "qsvgfont_p.h"
       
    53 
       
    54 #include "qapplication.h"
       
    55 #include "qwidget.h"
       
    56 #include "qpen.h"
       
    57 #include "qpainterpath.h"
       
    58 #include "qbrush.h"
       
    59 #include "qcolor.h"
       
    60 #include "qtextformat.h"
       
    61 #include "qvector.h"
       
    62 #include "qfileinfo.h"
       
    63 #include "qfile.h"
       
    64 #include "qdebug.h"
       
    65 #include "qmath.h"
       
    66 #include "qnumeric.h"
       
    67 #include "qvarlengtharray.h"
       
    68 #include "private/qmath_p.h"
       
    69 
       
    70 #include "float.h"
       
    71 
       
    72 QT_BEGIN_NAMESPACE
       
    73 
       
    74 static const char *qt_inherit_text = "inherit";
       
    75 #define QT_INHERIT QLatin1String(qt_inherit_text)
       
    76 
       
    77 double qstrtod(const char *s00, char const **se, bool *ok);
       
    78 
       
    79 // ======== duplicated from qcolor_p
       
    80 
       
    81 static inline int qsvg_h2i(char hex)
       
    82 {
       
    83     if (hex >= '0' && hex <= '9')
       
    84         return hex - '0';
       
    85     if (hex >= 'a' && hex <= 'f')
       
    86         return hex - 'a' + 10;
       
    87     if (hex >= 'A' && hex <= 'F')
       
    88         return hex - 'A' + 10;
       
    89     return -1;
       
    90 }
       
    91 
       
    92 static inline int qsvg_hex2int(const char *s)
       
    93 {
       
    94     return (qsvg_h2i(s[0]) << 4) | qsvg_h2i(s[1]);
       
    95 }
       
    96 
       
    97 static inline int qsvg_hex2int(char s)
       
    98 {
       
    99     int h = qsvg_h2i(s);
       
   100     return (h << 4) | h;
       
   101 }
       
   102 
       
   103 bool qsvg_get_hex_rgb(const char *name, QRgb *rgb)
       
   104 {
       
   105     if(name[0] != '#')
       
   106         return false;
       
   107     name++;
       
   108     int len = qstrlen(name);
       
   109     int r, g, b;
       
   110     if (len == 12) {
       
   111         r = qsvg_hex2int(name);
       
   112         g = qsvg_hex2int(name + 4);
       
   113         b = qsvg_hex2int(name + 8);
       
   114     } else if (len == 9) {
       
   115         r = qsvg_hex2int(name);
       
   116         g = qsvg_hex2int(name + 3);
       
   117         b = qsvg_hex2int(name + 6);
       
   118     } else if (len == 6) {
       
   119         r = qsvg_hex2int(name);
       
   120         g = qsvg_hex2int(name + 2);
       
   121         b = qsvg_hex2int(name + 4);
       
   122     } else if (len == 3) {
       
   123         r = qsvg_hex2int(name[0]);
       
   124         g = qsvg_hex2int(name[1]);
       
   125         b = qsvg_hex2int(name[2]);
       
   126     } else {
       
   127         r = g = b = -1;
       
   128     }
       
   129     if ((uint)r > 255 || (uint)g > 255 || (uint)b > 255) {
       
   130         *rgb = 0;
       
   131         return false;
       
   132     }
       
   133     *rgb = qRgb(r, g ,b);
       
   134     return true;
       
   135 }
       
   136 
       
   137 bool qsvg_get_hex_rgb(const QChar *str, int len, QRgb *rgb)
       
   138 {
       
   139     if (len > 13)
       
   140         return false;
       
   141     char tmp[16];
       
   142     for(int i = 0; i < len; ++i)
       
   143         tmp[i] = str[i].toLatin1();
       
   144     tmp[len] = 0;
       
   145     return qsvg_get_hex_rgb(tmp, rgb);
       
   146 }
       
   147 
       
   148 // ======== end of qcolor_p duplicate
       
   149 
       
   150 static bool parsePathDataFast(const QStringRef &data, QPainterPath &path);
       
   151 
       
   152 static inline QString someId(const QXmlStreamAttributes &attributes)
       
   153 {
       
   154     QString id = attributes.value(QLatin1String("id")).toString();
       
   155     if (id.isEmpty())
       
   156         id = attributes.value(QLatin1String("xml:id")).toString();
       
   157     return id;
       
   158 }
       
   159 
       
   160 struct QSvgAttributes
       
   161 {
       
   162     QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler);
       
   163 
       
   164     QString id;
       
   165 
       
   166     QStringRef color;
       
   167     QStringRef colorOpacity;
       
   168     QStringRef fill;
       
   169     QStringRef fillRule;
       
   170     QStringRef fillOpacity;
       
   171     QStringRef stroke;
       
   172     QStringRef strokeDashArray;
       
   173     QStringRef strokeDashOffset;
       
   174     QStringRef strokeLineCap;
       
   175     QStringRef strokeLineJoin;
       
   176     QStringRef strokeMiterLimit;
       
   177     QStringRef strokeOpacity;
       
   178     QStringRef strokeWidth;
       
   179     QStringRef vectorEffect;
       
   180     QStringRef fontFamily;
       
   181     QStringRef fontSize;
       
   182     QStringRef fontStyle;
       
   183     QStringRef fontWeight;
       
   184     QStringRef fontVariant;
       
   185     QStringRef textAnchor;
       
   186     QStringRef transform;
       
   187     QStringRef visibility;
       
   188     QStringRef opacity;
       
   189     QStringRef compOp;
       
   190     QStringRef display;
       
   191     QStringRef offset;
       
   192     QStringRef stopColor;
       
   193     QStringRef stopOpacity;
       
   194 
       
   195     QVector<QSvgCssAttribute> m_cssAttributes;
       
   196 };
       
   197 
       
   198 QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHandler *handler)
       
   199 {
       
   200     QStringRef style = xmlAttributes.value(QLatin1String("style"));
       
   201     if (!style.isEmpty()) {
       
   202         handler->parseCSStoXMLAttrs(style.toString(), &m_cssAttributes);
       
   203         for (int j = 0; j < m_cssAttributes.count(); ++j) {
       
   204             const QSvgCssAttribute &attribute = m_cssAttributes.at(j);
       
   205             QStringRef name = attribute.name;
       
   206             QStringRef value = attribute.value;
       
   207             if (name.isEmpty())
       
   208                 continue;
       
   209 
       
   210             switch (name.at(0).unicode()) {
       
   211 
       
   212             case 'c':
       
   213                 if (name == QLatin1String("color"))
       
   214                     color = value;
       
   215                 else if (name == QLatin1String("color-opacity"))
       
   216                     colorOpacity = value;
       
   217                 else if (name == QLatin1String("comp-op"))
       
   218                     compOp = value;
       
   219                 break;
       
   220 
       
   221             case 'd':
       
   222                 if (name == QLatin1String("display"))
       
   223                     display = value;
       
   224                 break;
       
   225 
       
   226             case 'f':
       
   227                 if (name == QLatin1String("fill"))
       
   228                     fill = value;
       
   229                 else if (name == QLatin1String("fill-rule"))
       
   230                     fillRule = value;
       
   231                 else if (name == QLatin1String("fill-opacity"))
       
   232                     fillOpacity = value;
       
   233                 else if (name == QLatin1String("font-family"))
       
   234                     fontFamily = value;
       
   235                 else if (name == QLatin1String("font-size"))
       
   236                     fontSize = value;
       
   237                 else if (name == QLatin1String("font-style"))
       
   238                     fontStyle = value;
       
   239                 else if (name == QLatin1String("font-weight"))
       
   240                     fontWeight = value;
       
   241                 else if (name == QLatin1String("font-variant"))
       
   242                     fontVariant = value;
       
   243                 break;
       
   244 
       
   245             case 'o':
       
   246                 if (name == QLatin1String("opacity"))
       
   247                     opacity = value;
       
   248                 else if (name == QLatin1String("offset"))
       
   249                     offset = value;
       
   250                 break;
       
   251 
       
   252             case 's':
       
   253                 if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
       
   254                     QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
       
   255                     if (strokeRef.isEmpty())
       
   256                         stroke = value;
       
   257                     else if (strokeRef == QLatin1String("-dasharray"))
       
   258                         strokeDashArray = value;
       
   259                     else if (strokeRef == QLatin1String("-dashoffset"))
       
   260                         strokeDashOffset = value;
       
   261                     else if (strokeRef == QLatin1String("-linecap"))
       
   262                         strokeLineCap = value;
       
   263                     else if (strokeRef == QLatin1String("-linejoin"))
       
   264                         strokeLineJoin = value;
       
   265                     else if (strokeRef == QLatin1String("-miterlimit"))
       
   266                         strokeMiterLimit = value;
       
   267                     else if (strokeRef == QLatin1String("-opacity"))
       
   268                         strokeOpacity = value;
       
   269                     else if (strokeRef == QLatin1String("-width"))
       
   270                         strokeWidth = value;
       
   271                 }
       
   272                 else if (name == QLatin1String("stop-color"))
       
   273                     stopColor = value;
       
   274                 else if (name == QLatin1String("stop-opacity"))
       
   275                     stopOpacity = value;
       
   276                 break;
       
   277 
       
   278             case 't':
       
   279                 if (name == QLatin1String("text-anchor"))
       
   280                     textAnchor = value;
       
   281                 else if (name == QLatin1String("transform"))
       
   282                     transform = value;
       
   283                 break;
       
   284 
       
   285             case 'v':
       
   286                 if (name == QLatin1String("vector-effect"))
       
   287                     vectorEffect = value;
       
   288                 else if (name == QLatin1String("visibility"))
       
   289                     visibility = value;
       
   290                 break;
       
   291 
       
   292             default:
       
   293                 break;
       
   294            }
       
   295         }
       
   296     }
       
   297 
       
   298     for (int i = 0; i < xmlAttributes.count(); ++i) {
       
   299         const QXmlStreamAttribute &attribute = xmlAttributes.at(i);
       
   300         QStringRef name = attribute.qualifiedName();
       
   301         if (name.isEmpty())
       
   302             continue;
       
   303         QStringRef value = attribute.value();
       
   304 
       
   305         switch (name.at(0).unicode()) {
       
   306 
       
   307         case 'c':
       
   308             if (name == QLatin1String("color"))
       
   309                 color = value;
       
   310             else if (name == QLatin1String("color-opacity"))
       
   311                 colorOpacity = value;
       
   312             else if (name == QLatin1String("comp-op"))
       
   313                 compOp = value;
       
   314             break;
       
   315 
       
   316         case 'd':
       
   317             if (name == QLatin1String("display"))
       
   318                 display = value;
       
   319             break;
       
   320 
       
   321         case 'f':
       
   322             if (name == QLatin1String("fill"))
       
   323                 fill = value;
       
   324             else if (name == QLatin1String("fill-rule"))
       
   325                 fillRule = value;
       
   326             else if (name == QLatin1String("fill-opacity"))
       
   327                 fillOpacity = value;
       
   328             else if (name == QLatin1String("font-family"))
       
   329                 fontFamily = value;
       
   330             else if (name == QLatin1String("font-size"))
       
   331                 fontSize = value;
       
   332             else if (name == QLatin1String("font-style"))
       
   333                 fontStyle = value;
       
   334             else if (name == QLatin1String("font-weight"))
       
   335                 fontWeight = value;
       
   336             else if (name == QLatin1String("font-variant"))
       
   337                 fontVariant = value;
       
   338             break;
       
   339 
       
   340         case 'i':
       
   341             if (name == QLatin1String("id"))
       
   342                 id = value.toString();
       
   343             break;
       
   344 
       
   345         case 'o':
       
   346             if (name == QLatin1String("opacity"))
       
   347                 opacity = value;
       
   348             if (name == QLatin1String("offset"))
       
   349                 offset = value;
       
   350             break;
       
   351 
       
   352         case 's':
       
   353             if (name.length() > 5 && QStringRef(name.string(), name.position() + 1, 5) == QLatin1String("troke")) {
       
   354                 QStringRef strokeRef(name.string(), name.position() + 6, name.length() - 6);
       
   355                 if (strokeRef.isEmpty())
       
   356                     stroke = value;
       
   357                 else if (strokeRef == QLatin1String("-dasharray"))
       
   358                     strokeDashArray = value;
       
   359                 else if (strokeRef == QLatin1String("-dashoffset"))
       
   360                     strokeDashOffset = value;
       
   361                 else if (strokeRef == QLatin1String("-linecap"))
       
   362                     strokeLineCap = value;
       
   363                 else if (strokeRef == QLatin1String("-linejoin"))
       
   364                     strokeLineJoin = value;
       
   365                 else if (strokeRef == QLatin1String("-miterlimit"))
       
   366                     strokeMiterLimit = value;
       
   367                 else if (strokeRef == QLatin1String("-opacity"))
       
   368                     strokeOpacity = value;
       
   369                 else if (strokeRef == QLatin1String("-width"))
       
   370                     strokeWidth = value;
       
   371             }
       
   372             else if (name == QLatin1String("stop-color"))
       
   373                 stopColor = value;
       
   374             else if (name == QLatin1String("stop-opacity"))
       
   375                 stopOpacity = value;
       
   376             break;
       
   377 
       
   378         case 't':
       
   379             if (name == QLatin1String("text-anchor"))
       
   380                 textAnchor = value;
       
   381             else if (name == QLatin1String("transform"))
       
   382                 transform = value;
       
   383             break;
       
   384 
       
   385         case 'v':
       
   386             if (name == QLatin1String("vector-effect"))
       
   387                 vectorEffect = value;
       
   388             else if (name == QLatin1String("visibility"))
       
   389                 visibility = value;
       
   390             break;
       
   391 
       
   392         case 'x':
       
   393             if (name == QLatin1String("xml:id") && id.isEmpty())
       
   394                 id = value.toString();
       
   395             break;
       
   396 
       
   397         default:
       
   398             break;
       
   399         }
       
   400     }
       
   401 
       
   402 }
       
   403 
       
   404 static const char * QSvgStyleSelector_nodeString[] = {
       
   405     "svg",
       
   406     "g",
       
   407     "defs",
       
   408     "switch",
       
   409     "animation",
       
   410     "arc",
       
   411     "circle",
       
   412     "ellipse",
       
   413     "image",
       
   414     "line",
       
   415     "path",
       
   416     "polygon",
       
   417     "polyline",
       
   418     "rect",
       
   419     "text",
       
   420     "textarea",
       
   421     "use",
       
   422     "video"
       
   423 };
       
   424 
       
   425 class QSvgStyleSelector : public QCss::StyleSelector
       
   426 {
       
   427 public:
       
   428     QSvgStyleSelector()
       
   429     {
       
   430         nameCaseSensitivity = Qt::CaseInsensitive;
       
   431     }
       
   432     virtual ~QSvgStyleSelector()
       
   433     {
       
   434     }
       
   435 
       
   436     inline QString nodeToName(QSvgNode *node) const
       
   437     {
       
   438         return QLatin1String(QSvgStyleSelector_nodeString[node->type()]);
       
   439     }
       
   440 
       
   441     inline QSvgNode *svgNode(NodePtr node) const
       
   442     {
       
   443         return (QSvgNode*)node.ptr;
       
   444     }
       
   445     inline QSvgStructureNode *nodeToStructure(QSvgNode *n) const
       
   446     {
       
   447         if (n &&
       
   448             (n->type() == QSvgNode::DOC ||
       
   449              n->type() == QSvgNode::G ||
       
   450              n->type() == QSvgNode::DEFS ||
       
   451              n->type() == QSvgNode::SWITCH)) {
       
   452             return (QSvgStructureNode*)n;
       
   453         }
       
   454         return 0;
       
   455     }
       
   456 
       
   457     inline QSvgStructureNode *svgStructure(NodePtr node) const
       
   458     {
       
   459         QSvgNode *n = svgNode(node);
       
   460         QSvgStructureNode *st = nodeToStructure(n);
       
   461         return st;
       
   462     }
       
   463 
       
   464     virtual bool nodeNameEquals(NodePtr node, const QString& nodeName) const
       
   465     {
       
   466         QSvgNode *n = svgNode(node);
       
   467         if (!n)
       
   468             return false;
       
   469         QString name = nodeToName(n);
       
   470         return QString::compare(name, nodeName, Qt::CaseInsensitive) == 0;
       
   471     }
       
   472     virtual QString attribute(NodePtr node, const QString &name) const
       
   473     {
       
   474         QSvgNode *n = svgNode(node);
       
   475         if ((!n->nodeId().isEmpty() && (name == QLatin1String("id") ||
       
   476                                         name == QLatin1String("xml:id"))))
       
   477             return n->nodeId();
       
   478         if (!n->xmlClass().isEmpty() && name == QLatin1String("class"))
       
   479             return n->xmlClass();
       
   480         return QString();
       
   481     }
       
   482     virtual bool hasAttributes(NodePtr node) const
       
   483     {
       
   484         QSvgNode *n = svgNode(node);
       
   485         return (n &&
       
   486                 (!n->nodeId().isEmpty() || !n->xmlClass().isEmpty()));
       
   487     }
       
   488 
       
   489     virtual QStringList nodeIds(NodePtr node) const
       
   490     {
       
   491         QSvgNode *n = svgNode(node);
       
   492         QString nid;
       
   493         if (n)
       
   494             nid = n->nodeId();
       
   495         QStringList lst; lst.append(nid);
       
   496         return lst;
       
   497     }
       
   498 
       
   499     virtual QStringList nodeNames(NodePtr node) const
       
   500     {
       
   501         QSvgNode *n = svgNode(node);
       
   502         if (n)
       
   503            return QStringList(nodeToName(n));
       
   504         return QStringList();
       
   505     }
       
   506 
       
   507     virtual bool isNullNode(NodePtr node) const
       
   508     {
       
   509         return !node.ptr;
       
   510     }
       
   511 
       
   512     virtual NodePtr parentNode(NodePtr node) const
       
   513     {
       
   514         QSvgNode *n = svgNode(node);
       
   515         NodePtr newNode;
       
   516         newNode.ptr = 0;
       
   517         newNode.id = 0;
       
   518         if (n) {
       
   519             QSvgNode *svgParent = n->parent();
       
   520             if (svgParent) {
       
   521                 newNode.ptr = svgParent;
       
   522             }
       
   523         }
       
   524         return newNode;
       
   525     }
       
   526     virtual NodePtr previousSiblingNode(NodePtr node) const
       
   527     {
       
   528         NodePtr newNode;
       
   529         newNode.ptr = 0;
       
   530         newNode.id = 0;
       
   531 
       
   532         QSvgNode *n = svgNode(node);
       
   533         if (!n)
       
   534             return newNode;
       
   535         QSvgStructureNode *svgParent = nodeToStructure(n->parent());
       
   536 
       
   537         if (svgParent) {
       
   538             newNode.ptr = svgParent->previousSiblingNode(n);
       
   539         }
       
   540         return newNode;
       
   541     }
       
   542     virtual NodePtr duplicateNode(NodePtr node) const
       
   543     {
       
   544         NodePtr n;
       
   545         n.ptr = node.ptr;
       
   546         n.id  = node.id;
       
   547         return n;
       
   548     }
       
   549     virtual void freeNode(NodePtr node) const
       
   550     {
       
   551         Q_UNUSED(node);
       
   552     }
       
   553 };
       
   554 
       
   555 // '0' is 0x30 and '9' is 0x39
       
   556 static inline bool isDigit(ushort ch)
       
   557 {
       
   558     static quint16 magic = 0x3ff;
       
   559     return ((ch >> 4) == 3) && (magic >> (ch & 15));
       
   560 }
       
   561 
       
   562 static qreal toDouble(const QChar *&str)
       
   563 {
       
   564     const int maxLen = 255;//technically doubles can go til 308+ but whatever
       
   565     char temp[maxLen+1];
       
   566     int pos = 0;
       
   567 
       
   568     if (*str == QLatin1Char('-')) {
       
   569         temp[pos++] = '-';
       
   570         ++str;
       
   571     } else if (*str == QLatin1Char('+')) {
       
   572         ++str;
       
   573     }
       
   574     while (isDigit(str->unicode()) && pos < maxLen) {
       
   575         temp[pos++] = str->toLatin1();
       
   576         ++str;
       
   577     }
       
   578     if (*str == QLatin1Char('.') && pos < maxLen) {
       
   579         temp[pos++] = '.';
       
   580         ++str;
       
   581     }
       
   582     while (isDigit(str->unicode()) && pos < maxLen) {
       
   583         temp[pos++] = str->toLatin1();
       
   584         ++str;
       
   585     }
       
   586     bool exponent = false;
       
   587     if (*str == QLatin1Char('e') && pos < maxLen) {
       
   588         exponent = true;
       
   589         temp[pos++] = 'e';
       
   590         ++str;
       
   591         if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
       
   592             temp[pos++] = str->toLatin1();
       
   593             ++str;
       
   594         }
       
   595         while (isDigit(str->unicode()) && pos < maxLen) {
       
   596             temp[pos++] = str->toLatin1();
       
   597             ++str;
       
   598         }
       
   599     }
       
   600 
       
   601     temp[pos] = '\0';
       
   602 
       
   603     qreal val;
       
   604     if (!exponent && pos < 10) {
       
   605         int ival = 0;
       
   606         const char *t = temp;
       
   607         bool neg = false;
       
   608         if(*t == '-') {
       
   609             neg = true;
       
   610             ++t;
       
   611         }
       
   612         while(*t && *t != '.') {
       
   613             ival *= 10;
       
   614             ival += (*t) - '0';
       
   615             ++t;
       
   616         }
       
   617         if(*t == '.') {
       
   618             ++t;
       
   619             int div = 1;
       
   620             while(*t) {
       
   621                 ival *= 10;
       
   622                 ival += (*t) - '0';
       
   623                 div *= 10;
       
   624                 ++t;
       
   625             }
       
   626             val = ((qreal)ival)/((qreal)div);
       
   627         } else {
       
   628             val = ival;
       
   629         }
       
   630         if (neg)
       
   631             val = -val;
       
   632     } else {
       
   633 #if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
       
   634         if(sizeof(qreal) == sizeof(float))
       
   635             val = strtof(temp, 0);
       
   636         else
       
   637 #endif
       
   638         {
       
   639             bool ok = false;
       
   640             val = qstrtod(temp, 0, &ok);
       
   641         }
       
   642     }
       
   643     return val;
       
   644 
       
   645 }
       
   646 static qreal toDouble(const QString &str, bool *ok = NULL)
       
   647 {
       
   648     const QChar *c = str.constData();
       
   649     qreal res = toDouble(c);
       
   650     if (ok) {
       
   651         *ok = ((*c) == QLatin1Char('\0'));
       
   652     }
       
   653     return res;
       
   654 }
       
   655 
       
   656 static qreal toDouble(const QStringRef &str, bool *ok = NULL)
       
   657 {
       
   658     const QChar *c = str.constData();
       
   659     qreal res = toDouble(c);
       
   660     if (ok) {
       
   661         *ok = (c == (str.constData() + str.length()));
       
   662     }
       
   663     return res;
       
   664 }
       
   665 
       
   666 static QVector<qreal> parseNumbersList(const QChar *&str)
       
   667 {
       
   668     QVector<qreal> points;
       
   669     if (!str)
       
   670         return points;
       
   671     points.reserve(32);
       
   672 
       
   673     while (str->isSpace())
       
   674         ++str;
       
   675     while (isDigit(str->unicode()) ||
       
   676            *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
       
   677            *str == QLatin1Char('.')) {
       
   678 
       
   679         points.append(toDouble(str));
       
   680 
       
   681         while (str->isSpace())
       
   682             ++str;
       
   683         if (*str == QLatin1Char(','))
       
   684             ++str;
       
   685 
       
   686         //eat the rest of space
       
   687         while (str->isSpace())
       
   688             ++str;
       
   689     }
       
   690 
       
   691     return points;
       
   692 }
       
   693 
       
   694 static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
       
   695 {
       
   696     while (str->isSpace())
       
   697         ++str;
       
   698     while (isDigit(str->unicode()) ||
       
   699            *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
       
   700            *str == QLatin1Char('.')) {
       
   701 
       
   702         points.append(toDouble(str));
       
   703 
       
   704         while (str->isSpace())
       
   705             ++str;
       
   706         if (*str == QLatin1Char(','))
       
   707             ++str;
       
   708 
       
   709         //eat the rest of space
       
   710         while (str->isSpace())
       
   711             ++str;
       
   712     }
       
   713 }
       
   714 
       
   715 static QVector<qreal> parsePercentageList(const QChar *&str)
       
   716 {
       
   717     QVector<qreal> points;
       
   718     if (!str)
       
   719         return points;
       
   720 
       
   721     while (str->isSpace())
       
   722         ++str;
       
   723     while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
       
   724            *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
       
   725            *str == QLatin1Char('.')) {
       
   726 
       
   727         points.append(toDouble(str));
       
   728 
       
   729         while (str->isSpace())
       
   730             ++str;
       
   731         if (*str == QLatin1Char('%'))
       
   732             ++str;
       
   733         while (str->isSpace())
       
   734             ++str;
       
   735         if (*str == QLatin1Char(','))
       
   736             ++str;
       
   737 
       
   738         //eat the rest of space
       
   739         while (str->isSpace())
       
   740             ++str;
       
   741     }
       
   742 
       
   743     return points;
       
   744 }
       
   745 
       
   746 static QString idFromUrl(const QString &url)
       
   747 {
       
   748     QString::const_iterator itr = url.constBegin();
       
   749     while ((*itr).isSpace())
       
   750         ++itr;
       
   751     if ((*itr) == QLatin1Char('('))
       
   752         ++itr;
       
   753     while ((*itr).isSpace())
       
   754         ++itr;
       
   755     if ((*itr) == QLatin1Char('#'))
       
   756         ++itr;
       
   757     QString id;
       
   758     while ((*itr) != QLatin1Char(')')) {
       
   759         id += *itr;
       
   760         ++itr;
       
   761     }
       
   762     return id;
       
   763 }
       
   764 
       
   765 static inline QStringRef trimRef(const QStringRef &str)
       
   766 {
       
   767     if (str.isEmpty())
       
   768         return QStringRef();
       
   769     const QChar *s = str.string()->constData() + str.position();
       
   770     int end = str.length() - 1;
       
   771     if (!s[0].isSpace() && !s[end].isSpace())
       
   772         return str;
       
   773 
       
   774     int start = 0;
       
   775     while (start<=end && s[start].isSpace())  // skip white space from start
       
   776         start++;
       
   777     if (start <= end) {                          // only white space
       
   778         while (s[end].isSpace())           // skip white space from end
       
   779             end--;
       
   780     }
       
   781     int l = end - start + 1;
       
   782     if (l <= 0)
       
   783         return QStringRef();
       
   784     return QStringRef(str.string(), str.position() + start, l);
       
   785 }
       
   786 
       
   787 /**
       
   788  * returns true when successfuly set the color. false signifies
       
   789  * that the color should be inherited
       
   790  */
       
   791 static bool resolveColor(const QStringRef &colorStr, QColor &color, QSvgHandler *handler)
       
   792 {
       
   793     QStringRef colorStrTr = trimRef(colorStr);
       
   794     if (colorStrTr.isEmpty())
       
   795         return false;
       
   796 
       
   797     switch(colorStrTr.at(0).unicode()) {
       
   798 
       
   799         case '#':
       
   800             {
       
   801                 // #rrggbb is very very common, so let's tackle it here
       
   802                 // rather than falling back to QColor
       
   803                 QRgb rgb;
       
   804                 bool ok = qsvg_get_hex_rgb(colorStrTr.unicode(), colorStrTr.length(), &rgb);
       
   805                 if (ok)
       
   806                     color.setRgb(rgb);
       
   807                 return ok;
       
   808             }
       
   809             break;
       
   810 
       
   811         case 'r':
       
   812             {
       
   813                 // starts with "rgb(", ends with ")" and consists of at least 7 characters "rgb(,,)"
       
   814                 if (colorStrTr.length() >= 7 && colorStrTr.at(colorStrTr.length() - 1) == QLatin1Char(')')
       
   815                     && QStringRef(colorStrTr.string(), colorStrTr.position(), 4) == QLatin1String("rgb(")) {
       
   816                     const QChar *s = colorStrTr.constData() + 4;
       
   817                     QVector<qreal> compo = parseNumbersList(s);
       
   818                     //1 means that it failed after reaching non-parsable
       
   819                     //character which is going to be "%"
       
   820                     if (compo.size() == 1) {
       
   821                         s = colorStrTr.constData() + 4;
       
   822                         compo = parsePercentageList(s);
       
   823                         for (int i = 0; i < compo.size(); ++i)
       
   824                             compo[i] *= (qreal)2.55;
       
   825                     }
       
   826 
       
   827                     if (compo.size() == 3) {
       
   828                         color = QColor(int(compo[0]),
       
   829                                        int(compo[1]),
       
   830                                        int(compo[2]));
       
   831                         return true;
       
   832                     }
       
   833                     return false;
       
   834                 }
       
   835             }
       
   836             break;
       
   837 
       
   838         case 'c':
       
   839             if (colorStrTr == QLatin1String("currentColor")) {
       
   840                 color = handler->currentColor();
       
   841                 return true;
       
   842             }
       
   843             break;
       
   844         case 'i':
       
   845             if (colorStrTr == QT_INHERIT)
       
   846                 return false;
       
   847             break;
       
   848         default:
       
   849             break;
       
   850     }
       
   851 
       
   852     color = QColor(colorStrTr.toString());
       
   853     return color.isValid();
       
   854 }
       
   855 
       
   856 static bool constructColor(const QStringRef &colorStr, const QStringRef &opacity,
       
   857                            QColor &color, QSvgHandler *handler)
       
   858 {
       
   859     if (!resolveColor(colorStr, color, handler))
       
   860         return false;
       
   861     if (!opacity.isEmpty()) {
       
   862         bool ok = true;
       
   863         qreal op = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity, &ok)));
       
   864         if (!ok)
       
   865             op = 1.0;
       
   866         color.setAlphaF(op);
       
   867     }
       
   868     return true;
       
   869 }
       
   870 
       
   871 static qreal parseLength(const QString &str, QSvgHandler::LengthType &type,
       
   872                          QSvgHandler *handler, bool *ok = NULL)
       
   873 {
       
   874     QString numStr = str.trimmed();
       
   875 
       
   876     if (numStr.endsWith(QLatin1Char('%'))) {
       
   877         numStr.chop(1);
       
   878         type = QSvgHandler::LT_PERCENT;
       
   879     } else if (numStr.endsWith(QLatin1String("px"))) {
       
   880         numStr.chop(2);
       
   881         type = QSvgHandler::LT_PX;
       
   882     } else if (numStr.endsWith(QLatin1String("pc"))) {
       
   883         numStr.chop(2);
       
   884         type = QSvgHandler::LT_PC;
       
   885     } else if (numStr.endsWith(QLatin1String("pt"))) {
       
   886         numStr.chop(2);
       
   887         type = QSvgHandler::LT_PT;
       
   888     } else if (numStr.endsWith(QLatin1String("mm"))) {
       
   889         numStr.chop(2);
       
   890         type = QSvgHandler::LT_MM;
       
   891     } else if (numStr.endsWith(QLatin1String("cm"))) {
       
   892         numStr.chop(2);
       
   893         type = QSvgHandler::LT_CM;
       
   894     } else if (numStr.endsWith(QLatin1String("in"))) {
       
   895         numStr.chop(2);
       
   896         type = QSvgHandler::LT_IN;
       
   897     } else {
       
   898         type = handler->defaultCoordinateSystem();
       
   899         //type = QSvgHandler::LT_OTHER;
       
   900     }
       
   901     qreal len = toDouble(numStr, ok);
       
   902     //qDebug()<<"len is "<<len<<", from '"<<numStr << "'";
       
   903     return len;
       
   904 }
       
   905 
       
   906 static inline qreal convertToNumber(const QString &str, QSvgHandler *handler, bool *ok = NULL)
       
   907 {
       
   908     QSvgHandler::LengthType type;
       
   909     qreal num = parseLength(str, type, handler, ok);
       
   910     if (type == QSvgHandler::LT_PERCENT) {
       
   911         num = num/100.0;
       
   912     }
       
   913     return num;
       
   914 }
       
   915 
       
   916 static bool createSvgGlyph(QSvgFont *font, const QXmlStreamAttributes &attributes)
       
   917 {
       
   918     QStringRef uncStr = attributes.value(QLatin1String("unicode"));
       
   919     QStringRef havStr = attributes.value(QLatin1String("horiz-adv-x"));
       
   920     QStringRef pathStr = attributes.value(QLatin1String("d"));
       
   921 
       
   922     QChar unicode = (uncStr.isEmpty()) ? 0 : uncStr.at(0);
       
   923     qreal havx = (havStr.isEmpty()) ? -1 : toDouble(havStr);
       
   924     QPainterPath path;
       
   925     path.setFillRule(Qt::WindingFill);
       
   926     parsePathDataFast(pathStr, path);
       
   927 
       
   928     font->addGlyph(unicode, path, havx);
       
   929 
       
   930     return true;
       
   931 }
       
   932 
       
   933 // this should really be called convertToDefaultCoordinateSystem
       
   934 // and convert when type != QSvgHandler::defaultCoordinateSystem
       
   935 static qreal convertToPixels(qreal len, bool , QSvgHandler::LengthType type)
       
   936 {
       
   937 
       
   938     switch (type) {
       
   939     case QSvgHandler::LT_PERCENT:
       
   940         break;
       
   941     case QSvgHandler::LT_PX:
       
   942         break;
       
   943     case QSvgHandler::LT_PC:
       
   944         break;
       
   945     case QSvgHandler::LT_PT:
       
   946         return len * 1.25;
       
   947         break;
       
   948     case QSvgHandler::LT_MM:
       
   949         return len * 3.543307;
       
   950         break;
       
   951     case QSvgHandler::LT_CM:
       
   952         return len * 35.43307;
       
   953         break;
       
   954     case QSvgHandler::LT_IN:
       
   955         return len * 90;
       
   956         break;
       
   957     case QSvgHandler::LT_OTHER:
       
   958         break;
       
   959     default:
       
   960         break;
       
   961     }
       
   962     return len;
       
   963 }
       
   964 
       
   965 static void parseColor(QSvgNode *,
       
   966                        const QSvgAttributes &attributes,
       
   967                        QSvgHandler *handler)
       
   968 {
       
   969     QColor color;
       
   970     if (constructColor(attributes.color, attributes.colorOpacity, color, handler)) {
       
   971         handler->popColor();
       
   972         handler->pushColor(color);
       
   973     }
       
   974 }
       
   975 
       
   976 static QSvgStyleProperty *styleFromUrl(QSvgNode *node, const QString &url)
       
   977 {
       
   978     return node ? node->styleProperty(idFromUrl(url)) : 0;
       
   979 }
       
   980 
       
   981 static void parseBrush(QSvgNode *node,
       
   982                        const QSvgAttributes &attributes,
       
   983                        QSvgHandler *handler)
       
   984 {
       
   985     if (!attributes.fill.isEmpty() || !attributes.fillRule.isEmpty() || !attributes.fillOpacity.isEmpty()) {
       
   986         QSvgFillStyle *prop = new QSvgFillStyle;
       
   987 
       
   988         //fill-rule attribute handling
       
   989         if (!attributes.fillRule.isEmpty() && attributes.fillRule != QT_INHERIT) {
       
   990             if (attributes.fillRule == QLatin1String("evenodd"))
       
   991                 prop->setFillRule(Qt::OddEvenFill);
       
   992             else if (attributes.fillRule == QLatin1String("nonzero"))
       
   993                 prop->setFillRule(Qt::WindingFill);
       
   994         }
       
   995 
       
   996         //fill-opacity atttribute handling
       
   997         if (!attributes.fillOpacity.isEmpty() && attributes.fillOpacity != QT_INHERIT) {
       
   998             prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.fillOpacity))));
       
   999         }
       
  1000 
       
  1001         //fill attribute handling
       
  1002         if ((!attributes.fill.isEmpty()) && (attributes.fill != QT_INHERIT) ) {
       
  1003             if (attributes.fill.length() > 3 &&
       
  1004                 QStringRef(attributes.fill.string(), attributes.fill.position(), 3) == QLatin1String("url")) {
       
  1005                 QStringRef urlRef(attributes.fill.string(), attributes.fill.position() + 3, attributes.fill.length() - 3);
       
  1006                 QString value = urlRef.toString();
       
  1007                 QSvgStyleProperty *style = styleFromUrl(node, value);
       
  1008                 if (style) {
       
  1009                     if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
       
  1010                         prop->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
       
  1011                 } else {
       
  1012                     QString id = idFromUrl(value);
       
  1013                     prop->setGradientId(id);
       
  1014                     prop->setGradientResolved(false);
       
  1015                 }
       
  1016             } else if (attributes.fill != QLatin1String("none")) {
       
  1017                 QColor color;
       
  1018                 if (resolveColor(attributes.fill, color, handler))
       
  1019                     prop->setBrush(QBrush(color));
       
  1020             } else {
       
  1021                 prop->setBrush(QBrush(Qt::NoBrush));
       
  1022             }
       
  1023         }
       
  1024         node->appendStyleProperty(prop, attributes.id);
       
  1025     }
       
  1026 }
       
  1027 
       
  1028 
       
  1029 
       
  1030 static QMatrix parseTransformationMatrix(const QStringRef &value)
       
  1031 {
       
  1032     if (value.isEmpty())
       
  1033         return QMatrix();
       
  1034 
       
  1035     QMatrix matrix;
       
  1036     const QChar *str = value.constData();
       
  1037     const QChar *end = str + value.length();
       
  1038 
       
  1039     while (str < end) {
       
  1040         if (str->isSpace() || *str == QLatin1Char(',')) {
       
  1041             ++str;
       
  1042             continue;
       
  1043         }
       
  1044         enum State {
       
  1045             Matrix,
       
  1046             Translate,
       
  1047             Rotate,
       
  1048             Scale,
       
  1049             SkewX,
       
  1050             SkewY
       
  1051         };
       
  1052         State state = Matrix;
       
  1053         if (*str == QLatin1Char('m')) {  //matrix
       
  1054             const char *ident = "atrix";
       
  1055             for (int i = 0; i < 5; ++i)
       
  1056                 if (*(++str) != QLatin1Char(ident[i]))
       
  1057                     goto error;
       
  1058             ++str;
       
  1059             state = Matrix;
       
  1060         } else if (*str == QLatin1Char('t')) { //translate
       
  1061             const char *ident = "ranslate";
       
  1062             for (int i = 0; i < 8; ++i)
       
  1063                 if (*(++str) != QLatin1Char(ident[i]))
       
  1064                     goto error;
       
  1065             ++str;
       
  1066             state = Translate;
       
  1067         } else if (*str == QLatin1Char('r')) { //rotate
       
  1068             const char *ident = "otate";
       
  1069             for (int i = 0; i < 5; ++i)
       
  1070                 if (*(++str) != QLatin1Char(ident[i]))
       
  1071                     goto error;
       
  1072             ++str;
       
  1073             state = Rotate;
       
  1074         } else if (*str == QLatin1Char('s')) { //scale, skewX, skewY
       
  1075             ++str;
       
  1076             if (*str == QLatin1Char('c')) {
       
  1077                 const char *ident = "ale";
       
  1078                 for (int i = 0; i < 3; ++i)
       
  1079                     if (*(++str) != QLatin1Char(ident[i]))
       
  1080                         goto error;
       
  1081                 ++str;
       
  1082                 state = Scale;
       
  1083             } else if (*str == QLatin1Char('k')) {
       
  1084                 if (*(++str) != QLatin1Char('e'))
       
  1085                     goto error;
       
  1086                 if (*(++str) != QLatin1Char('w'))
       
  1087                     goto error;
       
  1088                 ++str;
       
  1089                 if (*str == QLatin1Char('X'))
       
  1090                     state = SkewX;
       
  1091                 else if (*str == QLatin1Char('Y'))
       
  1092                     state = SkewY;
       
  1093                 else
       
  1094                     goto error;
       
  1095                 ++str;
       
  1096             } else {
       
  1097                 goto error;
       
  1098             }
       
  1099         } else {
       
  1100             goto error;
       
  1101         }
       
  1102 
       
  1103 
       
  1104         while (str < end && str->isSpace())
       
  1105             ++str;
       
  1106         if (*str != QLatin1Char('('))
       
  1107             goto error;
       
  1108         ++str;
       
  1109         QVarLengthArray<qreal, 8> points;
       
  1110         parseNumbersArray(str, points);
       
  1111         if (*str != QLatin1Char(')'))
       
  1112             goto error;
       
  1113         ++str;
       
  1114 
       
  1115         if(state == Matrix) {
       
  1116             if(points.count() != 6)
       
  1117                 goto error;
       
  1118             matrix = matrix * QMatrix(points[0], points[1],
       
  1119                                       points[2], points[3],
       
  1120                                       points[4], points[5]);
       
  1121         } else if (state == Translate) {
       
  1122             if (points.count() == 1)
       
  1123                 matrix.translate(points[0], 0);
       
  1124             else if (points.count() == 2)
       
  1125                 matrix.translate(points[0], points[1]);
       
  1126             else
       
  1127                 goto error;
       
  1128         } else if (state == Rotate) {
       
  1129             if(points.count() == 1) {
       
  1130                 matrix.rotate(points[0]);
       
  1131             } else if (points.count() == 3) {
       
  1132                 matrix.translate(points[1], points[2]);
       
  1133                 matrix.rotate(points[0]);
       
  1134                 matrix.translate(-points[1], -points[2]);
       
  1135             } else {
       
  1136                 goto error;
       
  1137             }
       
  1138         } else if (state == Scale) {
       
  1139             if (points.count() < 1 || points.count() > 2)
       
  1140                 goto error;
       
  1141             qreal sx = points[0];
       
  1142             qreal sy = sx;
       
  1143             if(points.count() == 2)
       
  1144                 sy = points[1];
       
  1145             matrix.scale(sx, sy);
       
  1146         } else if (state == SkewX) {
       
  1147             if (points.count() != 1)
       
  1148                 goto error;
       
  1149             const qreal deg2rad = qreal(0.017453292519943295769);
       
  1150             matrix.shear(tan(points[0]*deg2rad), 0);
       
  1151         } else if (state == SkewY) {
       
  1152             if (points.count() != 1)
       
  1153                 goto error;
       
  1154             const qreal deg2rad = qreal(0.017453292519943295769);
       
  1155             matrix.shear(0, tan(points[0]*deg2rad));
       
  1156         }
       
  1157     }
       
  1158   error:
       
  1159     return matrix;
       
  1160 }
       
  1161 
       
  1162 static void parsePen(QSvgNode *node,
       
  1163                      const QSvgAttributes &attributes,
       
  1164                      QSvgHandler *handler)
       
  1165 {
       
  1166     //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
       
  1167 
       
  1168     if (!attributes.stroke.isEmpty() || !attributes.strokeDashArray.isEmpty() || !attributes.strokeDashOffset.isEmpty() || !attributes.strokeLineCap.isEmpty()
       
  1169         || !attributes.strokeLineJoin.isEmpty() || !attributes.strokeMiterLimit.isEmpty() || !attributes.strokeOpacity.isEmpty() || !attributes.strokeWidth.isEmpty()
       
  1170         || !attributes.vectorEffect.isEmpty()) {
       
  1171 
       
  1172         QSvgStrokeStyle *prop = new QSvgStrokeStyle;
       
  1173 
       
  1174         //stroke attribute handling
       
  1175         if ((!attributes.stroke.isEmpty()) && (attributes.stroke != QT_INHERIT) ) {
       
  1176             if (attributes.stroke.length() > 3 &&
       
  1177                  QStringRef(attributes.stroke.string(), attributes.stroke.position(), 3) == QLatin1String("url")) {
       
  1178                  QStringRef urlRef(attributes.stroke.string(), attributes.stroke.position() + 3, attributes.stroke.length() - 3);
       
  1179                  QString value = urlRef.toString();
       
  1180                     QSvgStyleProperty *style = styleFromUrl(node, value);
       
  1181                     if (style) {
       
  1182                         if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
       
  1183                             prop->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
       
  1184                     } else {
       
  1185                         QString id = idFromUrl(value);
       
  1186                         prop->setGradientId(id);
       
  1187                         prop->setGradientResolved(false);
       
  1188                     }
       
  1189             } else if (attributes.stroke != QLatin1String("none")) {
       
  1190                 QColor color;
       
  1191                 if (resolveColor(attributes.stroke, color, handler))
       
  1192                     prop->setStroke(QBrush(color));
       
  1193             } else {
       
  1194                 prop->setStroke(QBrush(Qt::NoBrush));
       
  1195             }
       
  1196         }
       
  1197 
       
  1198         //stroke-width handling
       
  1199         if (!attributes.strokeWidth.isEmpty() && attributes.strokeWidth != QT_INHERIT) {
       
  1200             QSvgHandler::LengthType lt;
       
  1201             prop->setWidth(parseLength(attributes.strokeWidth.toString(), lt, handler));
       
  1202         }
       
  1203 
       
  1204         //stroke-dasharray
       
  1205         if (!attributes.strokeDashArray.isEmpty() && attributes.strokeDashArray != QT_INHERIT) {
       
  1206             if (attributes.strokeDashArray == QLatin1String("none")) {
       
  1207                 prop->setDashArrayNone();
       
  1208             } else {
       
  1209                 QString dashArray  = attributes.strokeDashArray.toString();
       
  1210                 const QChar *s = dashArray.constData();
       
  1211                 QVector<qreal> dashes = parseNumbersList(s);
       
  1212                 // if the dash count is odd the dashes should be duplicated
       
  1213                 if ((dashes.size() & 1) != 0)
       
  1214                     dashes << QVector<qreal>(dashes);
       
  1215                 prop->setDashArray(dashes);
       
  1216             }
       
  1217         }
       
  1218 
       
  1219         //stroke-linejoin attribute handling
       
  1220         if (!attributes.strokeLineJoin.isEmpty()) {
       
  1221             if (attributes.strokeLineJoin == QLatin1String("miter"))
       
  1222                 prop->setLineJoin(Qt::SvgMiterJoin);
       
  1223             else if (attributes.strokeLineJoin == QLatin1String("round"))
       
  1224                 prop->setLineJoin(Qt::RoundJoin);
       
  1225             else if (attributes.strokeLineJoin == QLatin1String("bevel"))
       
  1226                 prop->setLineJoin(Qt::BevelJoin);
       
  1227         }
       
  1228 
       
  1229         //stroke-linecap attribute handling
       
  1230         if (!attributes.strokeLineCap.isEmpty()) {
       
  1231             if (attributes.strokeLineCap == QLatin1String("butt"))
       
  1232                 prop->setLineCap(Qt::FlatCap);
       
  1233             else if (attributes.strokeLineCap == QLatin1String("round"))
       
  1234                 prop->setLineCap(Qt::RoundCap);
       
  1235             else if (attributes.strokeLineCap == QLatin1String("square"))
       
  1236                 prop->setLineCap(Qt::SquareCap);
       
  1237         }
       
  1238 
       
  1239         //stroke-dashoffset attribute handling
       
  1240         if (!attributes.strokeDashOffset.isEmpty() && attributes.strokeDashOffset != QT_INHERIT)
       
  1241             prop->setDashOffset(toDouble(attributes.strokeDashOffset));
       
  1242 
       
  1243         //vector-effect attribute handling
       
  1244         if (!attributes.vectorEffect.isEmpty()) {
       
  1245             if (attributes.vectorEffect == QLatin1String("non-scaling-stroke"))
       
  1246                 prop->setVectorEffect(true);
       
  1247             else if (attributes.vectorEffect == QLatin1String("none"))
       
  1248                 prop->setVectorEffect(false);
       
  1249         }
       
  1250 
       
  1251         //stroke-miterlimit
       
  1252         if (!attributes.strokeMiterLimit.isEmpty() && attributes.strokeMiterLimit != QT_INHERIT)
       
  1253             prop->setMiterLimit(toDouble(attributes.strokeMiterLimit));
       
  1254 
       
  1255         //stroke-opacity atttribute handling
       
  1256         if (!attributes.strokeOpacity.isEmpty() && attributes.strokeOpacity != QT_INHERIT)
       
  1257             prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(attributes.strokeOpacity))));
       
  1258 
       
  1259         node->appendStyleProperty(prop, attributes.id);
       
  1260     }
       
  1261 }
       
  1262 
       
  1263 static void parseFont(QSvgNode *node,
       
  1264                       const QSvgAttributes &attributes,
       
  1265                       QSvgHandler *handler)
       
  1266 {
       
  1267     if (attributes.fontFamily.isEmpty() && attributes.fontSize.isEmpty() && attributes.fontStyle.isEmpty() &&
       
  1268         attributes.fontWeight.isEmpty() && attributes.fontVariant.isEmpty() && attributes.textAnchor.isEmpty())
       
  1269         return;
       
  1270 
       
  1271     QSvgTinyDocument *doc = node->document();
       
  1272     QSvgFontStyle *fontStyle = 0;
       
  1273     if (!attributes.fontFamily.isEmpty()) {
       
  1274         QSvgFont *svgFont = doc->svgFont(attributes.fontFamily.toString());
       
  1275         if (svgFont)
       
  1276             fontStyle = new QSvgFontStyle(svgFont, doc);
       
  1277     }
       
  1278     if (!fontStyle)
       
  1279         fontStyle = new QSvgFontStyle;
       
  1280 
       
  1281     if (!attributes.fontFamily.isEmpty() && attributes.fontFamily != QT_INHERIT)
       
  1282         fontStyle->setFamily(attributes.fontFamily.toString().trimmed());
       
  1283 
       
  1284     if (!attributes.fontSize.isEmpty() && attributes.fontSize != QT_INHERIT) {
       
  1285         QSvgHandler::LengthType dummy; // should always be pixel size
       
  1286         fontStyle->setSize(parseLength(attributes.fontSize.toString(), dummy, handler));
       
  1287     }
       
  1288 
       
  1289     if (!attributes.fontStyle.isEmpty() && attributes.fontStyle != QT_INHERIT) {
       
  1290         if (attributes.fontStyle == QLatin1String("normal")) {
       
  1291             fontStyle->setStyle(QFont::StyleNormal);
       
  1292         } else if (attributes.fontStyle == QLatin1String("italic")) {
       
  1293             fontStyle->setStyle(QFont::StyleItalic);
       
  1294         } else if (attributes.fontStyle == QLatin1String("oblique")) {
       
  1295             fontStyle->setStyle(QFont::StyleOblique);
       
  1296         }
       
  1297     }
       
  1298 
       
  1299     if (!attributes.fontWeight.isEmpty() && attributes.fontWeight != QT_INHERIT) {
       
  1300         bool ok = false;
       
  1301         int weightNum = attributes.fontWeight.toString().toInt(&ok);
       
  1302         if (ok) {
       
  1303             fontStyle->setWeight(weightNum);
       
  1304         } else {
       
  1305             if (attributes.fontWeight == QLatin1String("normal")) {
       
  1306                 fontStyle->setWeight(400);
       
  1307             } else if (attributes.fontWeight == QLatin1String("bold")) {
       
  1308                 fontStyle->setWeight(700);
       
  1309             } else if (attributes.fontWeight == QLatin1String("bolder")) {
       
  1310                 fontStyle->setWeight(QSvgFontStyle::BOLDER);
       
  1311             } else if (attributes.fontWeight == QLatin1String("lighter")) {
       
  1312                 fontStyle->setWeight(QSvgFontStyle::LIGHTER);
       
  1313             }
       
  1314         }
       
  1315     }
       
  1316 
       
  1317     if (!attributes.fontVariant.isEmpty() && attributes.fontVariant != QT_INHERIT) {
       
  1318         if (attributes.fontVariant == QLatin1String("normal"))
       
  1319             fontStyle->setVariant(QFont::MixedCase);
       
  1320         else if (attributes.fontVariant == QLatin1String("small-caps"))
       
  1321             fontStyle->setVariant(QFont::SmallCaps);
       
  1322     }
       
  1323 
       
  1324     if (!attributes.textAnchor.isEmpty() && attributes.textAnchor != QT_INHERIT) {
       
  1325         if (attributes.textAnchor == QLatin1String("start"))
       
  1326             fontStyle->setTextAnchor(Qt::AlignLeft);
       
  1327         if (attributes.textAnchor == QLatin1String("middle"))
       
  1328            fontStyle->setTextAnchor(Qt::AlignHCenter);
       
  1329         else if (attributes.textAnchor == QLatin1String("end"))
       
  1330            fontStyle->setTextAnchor(Qt::AlignRight);
       
  1331     }
       
  1332 
       
  1333     node->appendStyleProperty(fontStyle, attributes.id);
       
  1334 }
       
  1335 
       
  1336 static void parseTransform(QSvgNode *node,
       
  1337                            const QSvgAttributes &attributes,
       
  1338                            QSvgHandler *)
       
  1339 {
       
  1340     if (attributes.transform.isEmpty())
       
  1341         return;
       
  1342     QMatrix matrix = parseTransformationMatrix(trimRef(attributes.transform));
       
  1343 
       
  1344     if (!matrix.isIdentity()) {
       
  1345         node->appendStyleProperty(new QSvgTransformStyle(QTransform(matrix)), attributes.id);
       
  1346     }
       
  1347 
       
  1348 }
       
  1349 
       
  1350 static void parseVisibility(QSvgNode *node,
       
  1351                             const QSvgAttributes &attributes,
       
  1352                             QSvgHandler *)
       
  1353 {
       
  1354     QSvgNode *parent = node->parent();
       
  1355 
       
  1356     if (parent && (attributes.visibility.isEmpty() || attributes.visibility == QT_INHERIT))
       
  1357         node->setVisible(parent->isVisible());
       
  1358     else if (attributes.visibility == QLatin1String("hidden") || attributes.visibility == QLatin1String("collapse")) {
       
  1359         node->setVisible(false);
       
  1360     } else
       
  1361         node->setVisible(true);
       
  1362 }
       
  1363 
       
  1364 static void pathArcSegment(QPainterPath &path,
       
  1365                            qreal xc, qreal yc,
       
  1366                            qreal th0, qreal th1,
       
  1367                            qreal rx, qreal ry, qreal xAxisRotation)
       
  1368 {
       
  1369     qreal sinTh, cosTh;
       
  1370     qreal a00, a01, a10, a11;
       
  1371     qreal x1, y1, x2, y2, x3, y3;
       
  1372     qreal t;
       
  1373     qreal thHalf;
       
  1374 
       
  1375     sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
       
  1376     cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
       
  1377 
       
  1378     a00 =  cosTh * rx;
       
  1379     a01 = -sinTh * ry;
       
  1380     a10 =  sinTh * rx;
       
  1381     a11 =  cosTh * ry;
       
  1382 
       
  1383     thHalf = 0.5 * (th1 - th0);
       
  1384     t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
       
  1385     x1 = xc + qCos(th0) - t * qSin(th0);
       
  1386     y1 = yc + qSin(th0) + t * qCos(th0);
       
  1387     x3 = xc + qCos(th1);
       
  1388     y3 = yc + qSin(th1);
       
  1389     x2 = x3 + t * qSin(th1);
       
  1390     y2 = y3 - t * qCos(th1);
       
  1391 
       
  1392     path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
       
  1393                  a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
       
  1394                  a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
       
  1395 }
       
  1396 
       
  1397 // the arc handling code underneath is from XSVG (BSD license)
       
  1398 /*
       
  1399  * Copyright  2002 USC/Information Sciences Institute
       
  1400  *
       
  1401  * Permission to use, copy, modify, distribute, and sell this software
       
  1402  * and its documentation for any purpose is hereby granted without
       
  1403  * fee, provided that the above copyright notice appear in all copies
       
  1404  * and that both that copyright notice and this permission notice
       
  1405  * appear in supporting documentation, and that the name of
       
  1406  * Information Sciences Institute not be used in advertising or
       
  1407  * publicity pertaining to distribution of the software without
       
  1408  * specific, written prior permission.  Information Sciences Institute
       
  1409  * makes no representations about the suitability of this software for
       
  1410  * any purpose.  It is provided "as is" without express or implied
       
  1411  * warranty.
       
  1412  *
       
  1413  * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
       
  1414  * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
       
  1415  * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
       
  1416  * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
       
  1417  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
       
  1418  * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
       
  1419  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       
  1420  * PERFORMANCE OF THIS SOFTWARE.
       
  1421  *
       
  1422  */
       
  1423 static void pathArc(QPainterPath &path,
       
  1424                     qreal               rx,
       
  1425                     qreal               ry,
       
  1426                     qreal               x_axis_rotation,
       
  1427                     int         large_arc_flag,
       
  1428                     int         sweep_flag,
       
  1429                     qreal               x,
       
  1430                     qreal               y,
       
  1431                     qreal curx, qreal cury)
       
  1432 {
       
  1433     qreal sin_th, cos_th;
       
  1434     qreal a00, a01, a10, a11;
       
  1435     qreal x0, y0, x1, y1, xc, yc;
       
  1436     qreal d, sfactor, sfactor_sq;
       
  1437     qreal th0, th1, th_arc;
       
  1438     int i, n_segs;
       
  1439     qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
       
  1440 
       
  1441     rx = qAbs(rx);
       
  1442     ry = qAbs(ry);
       
  1443 
       
  1444     sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
       
  1445     cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
       
  1446 
       
  1447     dx = (curx - x) / 2.0;
       
  1448     dy = (cury - y) / 2.0;
       
  1449     dx1 =  cos_th * dx + sin_th * dy;
       
  1450     dy1 = -sin_th * dx + cos_th * dy;
       
  1451     Pr1 = rx * rx;
       
  1452     Pr2 = ry * ry;
       
  1453     Px = dx1 * dx1;
       
  1454     Py = dy1 * dy1;
       
  1455     /* Spec : check if radii are large enough */
       
  1456     check = Px / Pr1 + Py / Pr2;
       
  1457     if (check > 1) {
       
  1458         rx = rx * qSqrt(check);
       
  1459         ry = ry * qSqrt(check);
       
  1460     }
       
  1461 
       
  1462     a00 =  cos_th / rx;
       
  1463     a01 =  sin_th / rx;
       
  1464     a10 = -sin_th / ry;
       
  1465     a11 =  cos_th / ry;
       
  1466     x0 = a00 * curx + a01 * cury;
       
  1467     y0 = a10 * curx + a11 * cury;
       
  1468     x1 = a00 * x + a01 * y;
       
  1469     y1 = a10 * x + a11 * y;
       
  1470     /* (x0, y0) is current point in transformed coordinate space.
       
  1471        (x1, y1) is new point in transformed coordinate space.
       
  1472 
       
  1473        The arc fits a unit-radius circle in this space.
       
  1474     */
       
  1475     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
       
  1476     sfactor_sq = 1.0 / d - 0.25;
       
  1477     if (sfactor_sq < 0) sfactor_sq = 0;
       
  1478     sfactor = qSqrt(sfactor_sq);
       
  1479     if (sweep_flag == large_arc_flag) sfactor = -sfactor;
       
  1480     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
       
  1481     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
       
  1482     /* (xc, yc) is center of the circle. */
       
  1483 
       
  1484     th0 = atan2(y0 - yc, x0 - xc);
       
  1485     th1 = atan2(y1 - yc, x1 - xc);
       
  1486 
       
  1487     th_arc = th1 - th0;
       
  1488     if (th_arc < 0 && sweep_flag)
       
  1489         th_arc += 2 * Q_PI;
       
  1490     else if (th_arc > 0 && !sweep_flag)
       
  1491         th_arc -= 2 * Q_PI;
       
  1492 
       
  1493     n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
       
  1494 
       
  1495     for (i = 0; i < n_segs; i++) {
       
  1496         pathArcSegment(path, xc, yc,
       
  1497                        th0 + i * th_arc / n_segs,
       
  1498                        th0 + (i + 1) * th_arc / n_segs,
       
  1499                        rx, ry, x_axis_rotation);
       
  1500     }
       
  1501 }
       
  1502 
       
  1503 static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
       
  1504 {
       
  1505     qreal x0 = 0, y0 = 0;              // starting point
       
  1506     qreal x = 0, y = 0;                // current point
       
  1507     char lastMode = 0;
       
  1508     QPointF ctrlPt;
       
  1509     const QChar *str = dataStr.constData();
       
  1510     const QChar *end = str + dataStr.size();
       
  1511 
       
  1512     while (str != end) {
       
  1513         while (str->isSpace())
       
  1514             ++str;
       
  1515         QChar pathElem = *str;
       
  1516         ++str;
       
  1517         QChar endc = *end;
       
  1518         *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
       
  1519         QVarLengthArray<qreal, 8> arg;
       
  1520         parseNumbersArray(str, arg);
       
  1521         *const_cast<QChar *>(end) = endc;
       
  1522         if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
       
  1523             arg.append(0);//dummy
       
  1524         const qreal *num = arg.constData();
       
  1525         int count = arg.count();
       
  1526         while (count > 0) {
       
  1527             qreal offsetX = x;        // correction offsets
       
  1528             qreal offsetY = y;        // for relative commands
       
  1529             switch (pathElem.unicode()) {
       
  1530             case 'm': {
       
  1531                 if (count < 2) {
       
  1532                     num++;
       
  1533                     count--;
       
  1534                     break;
       
  1535                 }
       
  1536                 x = x0 = num[0] + offsetX;
       
  1537                 y = y0 = num[1] + offsetY;
       
  1538                 num += 2;
       
  1539                 count -= 2;
       
  1540                 path.moveTo(x0, y0);
       
  1541 
       
  1542                  // As per 1.2  spec 8.3.2 The "moveto" commands
       
  1543                  // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
       
  1544                  // the subsequent pairs shall be treated as implicit 'lineto' commands.
       
  1545                  pathElem = QLatin1Char('l');
       
  1546             }
       
  1547                 break;
       
  1548             case 'M': {
       
  1549                 if (count < 2) {
       
  1550                     num++;
       
  1551                     count--;
       
  1552                     break;
       
  1553                 }
       
  1554                 x = x0 = num[0];
       
  1555                 y = y0 = num[1];
       
  1556                 num += 2;
       
  1557                 count -= 2;
       
  1558                 path.moveTo(x0, y0);
       
  1559 
       
  1560                 // As per 1.2  spec 8.3.2 The "moveto" commands
       
  1561                 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
       
  1562                 // the subsequent pairs shall be treated as implicit 'lineto' commands.
       
  1563                 pathElem = QLatin1Char('L');
       
  1564             }
       
  1565                 break;
       
  1566             case 'z':
       
  1567             case 'Z': {
       
  1568                 x = x0;
       
  1569                 y = y0;
       
  1570                 count--; // skip dummy
       
  1571                 num++;
       
  1572                 path.closeSubpath();
       
  1573             }
       
  1574                 break;
       
  1575             case 'l': {
       
  1576                 if (count < 2) {
       
  1577                     num++;
       
  1578                     count--;
       
  1579                     break;
       
  1580                 }
       
  1581                 x = x0 = num[0] + offsetX;
       
  1582                 y = y0 = num[1] + offsetY;
       
  1583                 num += 2;
       
  1584                 count -= 2;
       
  1585                 path.lineTo(x, y);
       
  1586 
       
  1587             }
       
  1588                 break;
       
  1589             case 'L': {
       
  1590                 if (count < 2) {
       
  1591                     num++;
       
  1592                     count--;
       
  1593                     break;
       
  1594                 }
       
  1595                 x = x0 = num[0];
       
  1596                 y = y0 = num[1];
       
  1597                 num += 2;
       
  1598                 count -= 2;
       
  1599                 path.lineTo(x, y);
       
  1600             }
       
  1601                 break;
       
  1602             case 'h': {
       
  1603                 x = num[0] + offsetX;
       
  1604                 num++;
       
  1605                 count--;
       
  1606                 path.lineTo(x, y);
       
  1607             }
       
  1608                 break;
       
  1609             case 'H': {
       
  1610                 x = num[0];
       
  1611                 num++;
       
  1612                 count--;
       
  1613                 path.lineTo(x, y);
       
  1614             }
       
  1615                 break;
       
  1616             case 'v': {
       
  1617                 y = num[0] + offsetY;
       
  1618                 num++;
       
  1619                 count--;
       
  1620                 path.lineTo(x, y);
       
  1621             }
       
  1622                 break;
       
  1623             case 'V': {
       
  1624                 y = num[0];
       
  1625                 num++;
       
  1626                 count--;
       
  1627                 path.lineTo(x, y);
       
  1628             }
       
  1629                 break;
       
  1630             case 'c': {
       
  1631                 if (count < 6) {
       
  1632                     num += count;
       
  1633                     count = 0;
       
  1634                     break;
       
  1635                 }
       
  1636                 QPointF c1(num[0] + offsetX, num[1] + offsetY);
       
  1637                 QPointF c2(num[2] + offsetX, num[3] + offsetY);
       
  1638                 QPointF e(num[4] + offsetX, num[5] + offsetY);
       
  1639                 num += 6;
       
  1640                 count -= 6;
       
  1641                 path.cubicTo(c1, c2, e);
       
  1642                 ctrlPt = c2;
       
  1643                 x = e.x();
       
  1644                 y = e.y();
       
  1645                 break;
       
  1646             }
       
  1647             case 'C': {
       
  1648                 if (count < 6) {
       
  1649                     num += count;
       
  1650                     count = 0;
       
  1651                     break;
       
  1652                 }
       
  1653                 QPointF c1(num[0], num[1]);
       
  1654                 QPointF c2(num[2], num[3]);
       
  1655                 QPointF e(num[4], num[5]);
       
  1656                 num += 6;
       
  1657                 count -= 6;
       
  1658                 path.cubicTo(c1, c2, e);
       
  1659                 ctrlPt = c2;
       
  1660                 x = e.x();
       
  1661                 y = e.y();
       
  1662                 break;
       
  1663             }
       
  1664             case 's': {
       
  1665                 if (count < 4) {
       
  1666                     num += count;
       
  1667                     count = 0;
       
  1668                     break;
       
  1669                 }
       
  1670                 QPointF c1;
       
  1671                 if (lastMode == 'c' || lastMode == 'C' ||
       
  1672                     lastMode == 's' || lastMode == 'S')
       
  1673                     c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
       
  1674                 else
       
  1675                     c1 = QPointF(x, y);
       
  1676                 QPointF c2(num[0] + offsetX, num[1] + offsetY);
       
  1677                 QPointF e(num[2] + offsetX, num[3] + offsetY);
       
  1678                 num += 4;
       
  1679                 count -= 4;
       
  1680                 path.cubicTo(c1, c2, e);
       
  1681                 ctrlPt = c2;
       
  1682                 x = e.x();
       
  1683                 y = e.y();
       
  1684                 break;
       
  1685             }
       
  1686             case 'S': {
       
  1687                 if (count < 4) {
       
  1688                     num += count;
       
  1689                     count = 0;
       
  1690                     break;
       
  1691                 }
       
  1692                 QPointF c1;
       
  1693                 if (lastMode == 'c' || lastMode == 'C' ||
       
  1694                     lastMode == 's' || lastMode == 'S')
       
  1695                     c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
       
  1696                 else
       
  1697                     c1 = QPointF(x, y);
       
  1698                 QPointF c2(num[0], num[1]);
       
  1699                 QPointF e(num[2], num[3]);
       
  1700                 num += 4;
       
  1701                 count -= 4;
       
  1702                 path.cubicTo(c1, c2, e);
       
  1703                 ctrlPt = c2;
       
  1704                 x = e.x();
       
  1705                 y = e.y();
       
  1706                 break;
       
  1707             }
       
  1708             case 'q': {
       
  1709                 if (count < 4) {
       
  1710                     num += count;
       
  1711                     count = 0;
       
  1712                     break;
       
  1713                 }
       
  1714                 QPointF c(num[0] + offsetX, num[1] + offsetY);
       
  1715                 QPointF e(num[2] + offsetX, num[3] + offsetY);
       
  1716                 num += 4;
       
  1717                 count -= 4;
       
  1718                 path.quadTo(c, e);
       
  1719                 ctrlPt = c;
       
  1720                 x = e.x();
       
  1721                 y = e.y();
       
  1722                 break;
       
  1723             }
       
  1724             case 'Q': {
       
  1725                 if (count < 4) {
       
  1726                     num += count;
       
  1727                     count = 0;
       
  1728                     break;
       
  1729                 }
       
  1730                 QPointF c(num[0], num[1]);
       
  1731                 QPointF e(num[2], num[3]);
       
  1732                 num += 4;
       
  1733                 count -= 4;
       
  1734                 path.quadTo(c, e);
       
  1735                 ctrlPt = c;
       
  1736                 x = e.x();
       
  1737                 y = e.y();
       
  1738                 break;
       
  1739             }
       
  1740             case 't': {
       
  1741                 if (count < 2) {
       
  1742                     num += count;
       
  1743                     count = 0;
       
  1744                     break;
       
  1745                 }
       
  1746                 QPointF e(num[0] + offsetX, num[1] + offsetY);
       
  1747                 num += 2;
       
  1748                 count -= 2;
       
  1749                 QPointF c;
       
  1750                 if (lastMode == 'q' || lastMode == 'Q' ||
       
  1751                     lastMode == 't' || lastMode == 'T')
       
  1752                     c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
       
  1753                 else
       
  1754                     c = QPointF(x, y);
       
  1755                 path.quadTo(c, e);
       
  1756                 ctrlPt = c;
       
  1757                 x = e.x();
       
  1758                 y = e.y();
       
  1759                 break;
       
  1760             }
       
  1761             case 'T': {
       
  1762                 if (count < 2) {
       
  1763                     num += count;
       
  1764                     count = 0;
       
  1765                     break;
       
  1766                 }
       
  1767                 QPointF e(num[0], num[1]);
       
  1768                 num += 2;
       
  1769                 count -= 2;
       
  1770                 QPointF c;
       
  1771                 if (lastMode == 'q' || lastMode == 'Q' ||
       
  1772                     lastMode == 't' || lastMode == 'T')
       
  1773                     c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
       
  1774                 else
       
  1775                     c = QPointF(x, y);
       
  1776                 path.quadTo(c, e);
       
  1777                 ctrlPt = c;
       
  1778                 x = e.x();
       
  1779                 y = e.y();
       
  1780                 break;
       
  1781             }
       
  1782             case 'a': {
       
  1783                 if (count < 7) {
       
  1784                     num += count;
       
  1785                     count = 0;
       
  1786                     break;
       
  1787                 }
       
  1788                 qreal rx = (*num++);
       
  1789                 qreal ry = (*num++);
       
  1790                 qreal xAxisRotation = (*num++);
       
  1791                 qreal largeArcFlag  = (*num++);
       
  1792                 qreal sweepFlag = (*num++);
       
  1793                 qreal ex = (*num++) + offsetX;
       
  1794                 qreal ey = (*num++) + offsetY;
       
  1795                 count -= 7;
       
  1796                 qreal curx = x;
       
  1797                 qreal cury = y;
       
  1798                 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
       
  1799                         int(sweepFlag), ex, ey, curx, cury);
       
  1800 
       
  1801                 x = ex;
       
  1802                 y = ey;
       
  1803             }
       
  1804                 break;
       
  1805             case 'A': {
       
  1806                 if (count < 7) {
       
  1807                     num += count;
       
  1808                     count = 0;
       
  1809                     break;
       
  1810                 }
       
  1811                 qreal rx = (*num++);
       
  1812                 qreal ry = (*num++);
       
  1813                 qreal xAxisRotation = (*num++);
       
  1814                 qreal largeArcFlag  = (*num++);
       
  1815                 qreal sweepFlag = (*num++);
       
  1816                 qreal ex = (*num++);
       
  1817                 qreal ey = (*num++);
       
  1818                 count -= 7;
       
  1819                 qreal curx = x;
       
  1820                 qreal cury = y;
       
  1821                 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
       
  1822                         int(sweepFlag), ex, ey, curx, cury);
       
  1823 
       
  1824                 x = ex;
       
  1825                 y = ey;
       
  1826             }
       
  1827                 break;
       
  1828             default:
       
  1829                 return false;
       
  1830             }
       
  1831             lastMode = pathElem.toLatin1();
       
  1832         }
       
  1833     }
       
  1834     return true;
       
  1835 }
       
  1836 
       
  1837 static bool parseStyle(QSvgNode *node,
       
  1838                        const QXmlStreamAttributes &attributes,
       
  1839                        QSvgHandler *);
       
  1840 
       
  1841 static bool parseStyle(QSvgNode *node,
       
  1842                        const QSvgAttributes &attributes,
       
  1843                        QSvgHandler *);
       
  1844 
       
  1845 static void parseCSStoXMLAttrs(const QVector<QCss::Declaration> &declarations,
       
  1846                                QXmlStreamAttributes &attributes)
       
  1847 {
       
  1848     for (int i = 0; i < declarations.count(); ++i) {
       
  1849         const QCss::Declaration &decl = declarations.at(i);
       
  1850         if (decl.d->property.isEmpty())
       
  1851             continue;
       
  1852         QCss::Value val = decl.d->values.first();
       
  1853         QString valueStr;
       
  1854         if (decl.d->values.count() != 1) {
       
  1855             for (int i=0; i<decl.d->values.count(); ++i) {
       
  1856                 const QString &value = decl.d->values[i].toString();
       
  1857                 if (value.isEmpty())
       
  1858                     valueStr += QLatin1Char(',');
       
  1859                 else
       
  1860                     valueStr += value;
       
  1861             }
       
  1862         } else {
       
  1863             valueStr = val.toString();
       
  1864         }
       
  1865         if (val.type == QCss::Value::Uri) {
       
  1866             valueStr.prepend(QLatin1String("url("));
       
  1867             valueStr.append(QLatin1Char(')'));
       
  1868         } else if (val.type == QCss::Value::Function) {
       
  1869             QStringList lst = val.variant.toStringList();
       
  1870             valueStr.append(lst.at(0));
       
  1871             valueStr.append(QLatin1Char('('));
       
  1872             for (int i = 1; i < lst.count(); ++i) {
       
  1873                 valueStr.append(lst.at(i));
       
  1874                 if ((i +1) < lst.count())
       
  1875                     valueStr.append(QLatin1Char(','));
       
  1876             }
       
  1877             valueStr.append(QLatin1Char(')'));
       
  1878         } else if (val.type == QCss::Value::KnownIdentifier) {
       
  1879             switch (val.variant.toInt()) {
       
  1880             case QCss::Value_None:
       
  1881                 valueStr = QLatin1String("none");
       
  1882                 break;
       
  1883             default:
       
  1884                 break;
       
  1885             }
       
  1886         }
       
  1887 
       
  1888         attributes.append(QString(), decl.d->property, valueStr);
       
  1889     }
       
  1890 }
       
  1891 
       
  1892 void QSvgHandler::parseCSStoXMLAttrs(QString css, QVector<QSvgCssAttribute> *attributes)
       
  1893 {
       
  1894     // preprocess (for unicode escapes), tokenize and remove comments
       
  1895     m_cssParser.init(css);
       
  1896     QString key;
       
  1897 
       
  1898     attributes->reserve(10);
       
  1899 
       
  1900     while (m_cssParser.hasNext()) {
       
  1901         m_cssParser.skipSpace();
       
  1902 
       
  1903         if (!m_cssParser.hasNext())
       
  1904             break;
       
  1905         m_cssParser.next();
       
  1906 
       
  1907         QStringRef name;
       
  1908         if (m_cssParser.hasEscapeSequences) {
       
  1909             key = m_cssParser.lexem();
       
  1910             name = QStringRef(&key, 0, key.length());
       
  1911         } else {
       
  1912             const QCss::Symbol &sym = m_cssParser.symbol();
       
  1913             name = QStringRef(&sym.text, sym.start, sym.len);
       
  1914         }
       
  1915 
       
  1916         m_cssParser.skipSpace();
       
  1917         if (!m_cssParser.test(QCss::COLON))
       
  1918             break;
       
  1919 
       
  1920         m_cssParser.skipSpace();
       
  1921         if (!m_cssParser.hasNext())
       
  1922             break;
       
  1923 
       
  1924         QSvgCssAttribute attribute;
       
  1925         attribute.name = QXmlStreamStringRef(name);
       
  1926 
       
  1927         const int firstSymbol = m_cssParser.index;
       
  1928         int symbolCount = 0;
       
  1929         do {
       
  1930             m_cssParser.next();
       
  1931             ++symbolCount;
       
  1932         } while (m_cssParser.hasNext() && !m_cssParser.test(QCss::SEMICOLON));
       
  1933 
       
  1934         bool canExtractValueByRef = !m_cssParser.hasEscapeSequences;
       
  1935         if (canExtractValueByRef) {
       
  1936             int len = m_cssParser.symbols.at(firstSymbol).len;
       
  1937             for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
       
  1938                 len += m_cssParser.symbols.at(i).len;
       
  1939 
       
  1940                 if (m_cssParser.symbols.at(i - 1).start + m_cssParser.symbols.at(i - 1).len
       
  1941                         != m_cssParser.symbols.at(i).start) {
       
  1942                     canExtractValueByRef = false;
       
  1943                     break;
       
  1944                 }
       
  1945             }
       
  1946             if (canExtractValueByRef) {
       
  1947                 const QCss::Symbol &sym = m_cssParser.symbols.at(firstSymbol);
       
  1948                 attribute.value = QXmlStreamStringRef(QStringRef(&sym.text, sym.start, len));
       
  1949             }
       
  1950         }
       
  1951         if (!canExtractValueByRef) {
       
  1952             QString value;
       
  1953             for (int i = firstSymbol; i < m_cssParser.index - 1; ++i)
       
  1954                 value += m_cssParser.symbols.at(i).lexem();
       
  1955             attribute.value = QXmlStreamStringRef(QStringRef(&value, 0, value.length()));
       
  1956         }
       
  1957 
       
  1958         attributes->append(attribute);
       
  1959 
       
  1960         m_cssParser.skipSpace();
       
  1961     }
       
  1962 }
       
  1963 
       
  1964 static void cssStyleLookup(QSvgNode *node,
       
  1965                            QSvgHandler *handler,
       
  1966                            QSvgStyleSelector *selector)
       
  1967 {
       
  1968     QCss::StyleSelector::NodePtr cssNode;
       
  1969     cssNode.ptr = node;
       
  1970     QVector<QCss::Declaration> decls = selector->declarationsForNode(cssNode);
       
  1971 
       
  1972     QXmlStreamAttributes attributes;
       
  1973     parseCSStoXMLAttrs(decls, attributes);
       
  1974     parseStyle(node, attributes, handler);
       
  1975 }
       
  1976 
       
  1977 static inline QStringList stringToList(const QString &str)
       
  1978 {
       
  1979     QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts);
       
  1980     return lst;
       
  1981 }
       
  1982 
       
  1983 static bool parseCoreNode(QSvgNode *node,
       
  1984                           const QXmlStreamAttributes &attributes)
       
  1985 {
       
  1986     QStringList features;
       
  1987     QStringList extensions;
       
  1988     QStringList languages;
       
  1989     QStringList formats;
       
  1990     QStringList fonts;
       
  1991     QString xmlClassStr;
       
  1992 
       
  1993     for (int i = 0; i < attributes.count(); ++i) {
       
  1994         const QXmlStreamAttribute &attribute = attributes.at(i);
       
  1995         QStringRef name = attribute.qualifiedName();
       
  1996         if (name.isEmpty())
       
  1997             continue;
       
  1998         QStringRef value = attribute.value();
       
  1999         switch (name.at(0).unicode()) {
       
  2000         case 'c':
       
  2001             if (name == QLatin1String("class"))
       
  2002                 xmlClassStr = value.toString();
       
  2003             break;
       
  2004         case 'r':
       
  2005             if (name == QLatin1String("requiredFeatures"))
       
  2006                 features = stringToList(value.toString());
       
  2007             else if (name == QLatin1String("requiredExtensions"))
       
  2008                 extensions = stringToList(value.toString());
       
  2009             else if (name == QLatin1String("requiredFormats"))
       
  2010                 formats = stringToList(value.toString());
       
  2011             else if (name == QLatin1String("requiredFonts"))
       
  2012                 fonts = stringToList(value.toString());
       
  2013             break;
       
  2014         case 's':
       
  2015             if (name == QLatin1String("systemLanguage"))
       
  2016                 languages = stringToList(value.toString());
       
  2017             break;
       
  2018         default:
       
  2019             break;
       
  2020         }
       
  2021     }
       
  2022 
       
  2023     node->setRequiredFeatures(features);
       
  2024     node->setRequiredExtensions(extensions);
       
  2025     node->setRequiredLanguages(languages);
       
  2026     node->setRequiredFormats(formats);
       
  2027     node->setRequiredFonts(fonts);
       
  2028     node->setNodeId(someId(attributes));
       
  2029     node->setXmlClass(xmlClassStr);
       
  2030 
       
  2031     return true;
       
  2032 }
       
  2033 
       
  2034 static void parseOpacity(QSvgNode *node,
       
  2035                          const QSvgAttributes &attributes,
       
  2036                          QSvgHandler *)
       
  2037 {
       
  2038     if (attributes.opacity.isEmpty())
       
  2039         return;
       
  2040 
       
  2041     const QString value = attributes.opacity.toString().trimmed();
       
  2042 
       
  2043     bool ok = false;
       
  2044     qreal op = value.toDouble(&ok);
       
  2045 
       
  2046     if (ok) {
       
  2047         QSvgOpacityStyle *opacity = new QSvgOpacityStyle(qBound(qreal(0.0), op, qreal(1.0)));
       
  2048         node->appendStyleProperty(opacity, attributes.id);
       
  2049     }
       
  2050 }
       
  2051 
       
  2052 static QPainter::CompositionMode svgToQtCompositionMode(const QString &op)
       
  2053 {
       
  2054 #define NOOP qDebug()<<"Operation: "<<op<<" is not implemented"
       
  2055     if (op == QLatin1String("clear")) {
       
  2056         return QPainter::CompositionMode_Clear;
       
  2057     } else if (op == QLatin1String("src")) {
       
  2058         return QPainter::CompositionMode_Source;
       
  2059     } else if (op == QLatin1String("dst")) {
       
  2060         return QPainter::CompositionMode_Destination;
       
  2061     } else if (op == QLatin1String("src-over")) {
       
  2062         return QPainter::CompositionMode_SourceOver;
       
  2063     } else if (op == QLatin1String("dst-over")) {
       
  2064         return QPainter::CompositionMode_DestinationOver;
       
  2065     } else if (op == QLatin1String("src-in")) {
       
  2066         return QPainter::CompositionMode_SourceIn;
       
  2067     } else if (op == QLatin1String("dst-in")) {
       
  2068         return QPainter::CompositionMode_DestinationIn;
       
  2069     } else if (op == QLatin1String("src-out")) {
       
  2070         return QPainter::CompositionMode_SourceOut;
       
  2071     } else if (op == QLatin1String("dst-out")) {
       
  2072         return QPainter::CompositionMode_DestinationOut;
       
  2073     } else if (op == QLatin1String("src-atop")) {
       
  2074         return QPainter::CompositionMode_SourceAtop;
       
  2075     } else if (op == QLatin1String("dst-atop")) {
       
  2076         return QPainter::CompositionMode_DestinationAtop;
       
  2077     } else if (op == QLatin1String("xor")) {
       
  2078         return QPainter::CompositionMode_Xor;
       
  2079     } else if (op == QLatin1String("plus")) {
       
  2080         return QPainter::CompositionMode_Plus;
       
  2081     } else if (op == QLatin1String("multiply")) {
       
  2082         return QPainter::CompositionMode_Multiply;
       
  2083     } else if (op == QLatin1String("screen")) {
       
  2084         return QPainter::CompositionMode_Screen;
       
  2085     } else if (op == QLatin1String("overlay")) {
       
  2086         return QPainter::CompositionMode_Overlay;
       
  2087     } else if (op == QLatin1String("darken")) {
       
  2088         return QPainter::CompositionMode_Darken;
       
  2089     } else if (op == QLatin1String("lighten")) {
       
  2090         return QPainter::CompositionMode_Lighten;
       
  2091     } else if (op == QLatin1String("color-dodge")) {
       
  2092         return QPainter::CompositionMode_ColorDodge;
       
  2093     } else if (op == QLatin1String("color-burn")) {
       
  2094         return QPainter::CompositionMode_ColorBurn;
       
  2095     } else if (op == QLatin1String("hard-light")) {
       
  2096         return QPainter::CompositionMode_HardLight;
       
  2097     } else if (op == QLatin1String("soft-light")) {
       
  2098         return QPainter::CompositionMode_SoftLight;
       
  2099     } else if (op == QLatin1String("difference")) {
       
  2100         return QPainter::CompositionMode_Difference;
       
  2101     } else if (op == QLatin1String("exclusion")) {
       
  2102         return QPainter::CompositionMode_Exclusion;
       
  2103     } else {
       
  2104         NOOP;
       
  2105     }
       
  2106 
       
  2107     return QPainter::CompositionMode_SourceOver;
       
  2108 }
       
  2109 
       
  2110 static void parseCompOp(QSvgNode *node,
       
  2111                         const QSvgAttributes &attributes,
       
  2112                         QSvgHandler *)
       
  2113 {
       
  2114     if (attributes.compOp.isEmpty())
       
  2115         return;
       
  2116     QString value = attributes.compOp.toString().trimmed();
       
  2117 
       
  2118     if (!value.isEmpty()) {
       
  2119         QSvgCompOpStyle *compop = new QSvgCompOpStyle(svgToQtCompositionMode(value));
       
  2120         node->appendStyleProperty(compop, attributes.id);
       
  2121     }
       
  2122 }
       
  2123 
       
  2124 static inline QSvgNode::DisplayMode displayStringToEnum(const QString &str)
       
  2125 {
       
  2126     if (str == QLatin1String("inline")) {
       
  2127         return QSvgNode::InlineMode;
       
  2128     } else if (str == QLatin1String("block")) {
       
  2129         return QSvgNode::BlockMode;
       
  2130     } else if (str == QLatin1String("list-item")) {
       
  2131         return QSvgNode::ListItemMode;
       
  2132     } else if (str == QLatin1String("run-in")) {
       
  2133         return QSvgNode::RunInMode;
       
  2134     } else if (str == QLatin1String("compact")) {
       
  2135         return QSvgNode::CompactMode;
       
  2136     } else if (str == QLatin1String("marker")) {
       
  2137         return QSvgNode::MarkerMode;
       
  2138     } else if (str == QLatin1String("table")) {
       
  2139         return QSvgNode::TableMode;
       
  2140     } else if (str == QLatin1String("inline-table")) {
       
  2141         return QSvgNode::InlineTableMode;
       
  2142     } else if (str == QLatin1String("table-row")) {
       
  2143         return QSvgNode::TableRowGroupMode;
       
  2144     } else if (str == QLatin1String("table-header-group")) {
       
  2145         return QSvgNode::TableHeaderGroupMode;
       
  2146     } else if (str == QLatin1String("table-footer-group")) {
       
  2147         return QSvgNode::TableFooterGroupMode;
       
  2148     } else if (str == QLatin1String("table-row")) {
       
  2149         return QSvgNode::TableRowMode;
       
  2150     } else if (str == QLatin1String("table-column-group")) {
       
  2151         return QSvgNode::TableColumnGroupMode;
       
  2152     } else if (str == QLatin1String("table-column")) {
       
  2153         return QSvgNode::TableColumnMode;
       
  2154     } else if (str == QLatin1String("table-cell")) {
       
  2155         return QSvgNode::TableCellMode;
       
  2156     } else if (str == QLatin1String("table-caption")) {
       
  2157         return QSvgNode::TableCaptionMode;
       
  2158     } else if (str == QLatin1String("none")) {
       
  2159         return QSvgNode::NoneMode;
       
  2160     } else if (str == QT_INHERIT) {
       
  2161         return QSvgNode::InheritMode;
       
  2162     }
       
  2163     return QSvgNode::BlockMode;
       
  2164 }
       
  2165 
       
  2166 static void parseOthers(QSvgNode *node,
       
  2167                         const QSvgAttributes &attributes,
       
  2168                         QSvgHandler *)
       
  2169 {
       
  2170     if (attributes.display.isEmpty())
       
  2171         return;
       
  2172     QString displayStr = attributes.display.toString().trimmed();
       
  2173 
       
  2174     if (!displayStr.isEmpty()) {
       
  2175         node->setDisplayMode(displayStringToEnum(displayStr));
       
  2176     }
       
  2177 }
       
  2178 
       
  2179 static bool parseStyle(QSvgNode *node,
       
  2180                        const QSvgAttributes &attributes,
       
  2181                        QSvgHandler *handler)
       
  2182 {
       
  2183     parseColor(node, attributes, handler);
       
  2184     parseBrush(node, attributes, handler);
       
  2185     parsePen(node, attributes, handler);
       
  2186     parseFont(node, attributes, handler);
       
  2187     parseTransform(node, attributes, handler);
       
  2188     parseVisibility(node, attributes, handler);
       
  2189     parseOpacity(node, attributes, handler);
       
  2190     parseCompOp(node, attributes, handler);
       
  2191     parseOthers(node, attributes, handler);
       
  2192 #if 0
       
  2193     value = attributes.value("audio-level");
       
  2194 
       
  2195     value = attributes.value("color-rendering");
       
  2196 
       
  2197     value = attributes.value("display-align");
       
  2198 
       
  2199     value = attributes.value("image-rendering");
       
  2200 
       
  2201     value = attributes.value("line-increment");
       
  2202 
       
  2203     value = attributes.value("pointer-events");
       
  2204 
       
  2205     value = attributes.value("shape-rendering");
       
  2206 
       
  2207     value = attributes.value("solid-color");
       
  2208 
       
  2209     value = attributes.value("solid-opacity");
       
  2210 
       
  2211     value = attributes.value("text-rendering");
       
  2212 
       
  2213     value = attributes.value("vector-effect");
       
  2214 
       
  2215     value = attributes.value("viewport-fill");
       
  2216 
       
  2217     value = attributes.value("viewport-fill-opacity");
       
  2218 #endif
       
  2219     return true;
       
  2220 }
       
  2221 
       
  2222 static bool parseStyle(QSvgNode *node,
       
  2223                        const QXmlStreamAttributes &attrs,
       
  2224                        QSvgHandler *handler)
       
  2225 {
       
  2226     return parseStyle(node, QSvgAttributes(attrs, handler), handler);
       
  2227 }
       
  2228 
       
  2229 static bool parseAnchorNode(QSvgNode *parent,
       
  2230                             const QXmlStreamAttributes &attributes,
       
  2231                             QSvgHandler *)
       
  2232 {
       
  2233     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2234     return true;
       
  2235 }
       
  2236 
       
  2237 static bool parseAnimateNode(QSvgNode *parent,
       
  2238                              const QXmlStreamAttributes &attributes,
       
  2239                              QSvgHandler *)
       
  2240 {
       
  2241     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2242     return true;
       
  2243 }
       
  2244 
       
  2245 static bool parseAnimateColorNode(QSvgNode *parent,
       
  2246                                   const QXmlStreamAttributes &attributes,
       
  2247                                   QSvgHandler *handler)
       
  2248 {
       
  2249     QString typeStr    = attributes.value(QLatin1String("type")).toString();
       
  2250     QStringRef fromStr    = attributes.value(QLatin1String("from"));
       
  2251     QStringRef toStr      = attributes.value(QLatin1String("to"));
       
  2252     QString valuesStr  = attributes.value(QLatin1String("values")).toString();
       
  2253     QString beginStr   = attributes.value(QLatin1String("begin")).toString();
       
  2254     QString durStr     = attributes.value(QLatin1String("dur")).toString();
       
  2255     QString targetStr  = attributes.value(QLatin1String("attributeName")).toString();
       
  2256     QString repeatStr  = attributes.value(QLatin1String("repeatCount")).toString();
       
  2257     QString fillStr    = attributes.value(QLatin1String("fill")).toString();
       
  2258 
       
  2259     QList<QColor> colors;
       
  2260     if (valuesStr.isEmpty()) {
       
  2261         QColor startColor, endColor;
       
  2262         resolveColor(fromStr, startColor, handler);
       
  2263         resolveColor(toStr, endColor, handler);
       
  2264         colors.append(startColor);
       
  2265         colors.append(endColor);
       
  2266     } else {
       
  2267         QStringList str = valuesStr.split(QLatin1Char(';'));
       
  2268         QStringList::const_iterator itr;
       
  2269         for (itr = str.constBegin(); itr != str.constEnd(); ++itr) {
       
  2270             QColor color;
       
  2271             QString str = *itr;
       
  2272             resolveColor(QStringRef(&str), color, handler);
       
  2273             colors.append(color);
       
  2274         }
       
  2275     }
       
  2276 
       
  2277     int ms = 1000;
       
  2278     beginStr = beginStr.trimmed();
       
  2279     if (beginStr.endsWith(QLatin1String("ms"))) {
       
  2280         beginStr.chop(2);
       
  2281         ms = 1;
       
  2282     } else if (beginStr.endsWith(QLatin1String("s"))) {
       
  2283         beginStr.chop(1);
       
  2284     }
       
  2285     durStr = durStr.trimmed();
       
  2286     if (durStr.endsWith(QLatin1String("ms"))) {
       
  2287         durStr.chop(2);
       
  2288         ms = 1;
       
  2289     } else if (durStr.endsWith(QLatin1String("s"))) {
       
  2290         durStr.chop(1);
       
  2291     }
       
  2292     int begin = static_cast<int>(toDouble(beginStr) * ms);
       
  2293     int end   = static_cast<int>((toDouble(durStr) + begin) * ms);
       
  2294 
       
  2295     QSvgAnimateColor *anim = new QSvgAnimateColor(begin, end, 0);
       
  2296     anim->setArgs((targetStr == QLatin1String("fill")), colors);
       
  2297     anim->setFreeze(fillStr == QLatin1String("freeze"));
       
  2298     anim->setRepeatCount(
       
  2299         (repeatStr == QLatin1String("indefinite")) ? -1 :
       
  2300             (repeatStr == QLatin1String("")) ? 1 : toDouble(repeatStr));
       
  2301 
       
  2302     parent->appendStyleProperty(anim, someId(attributes));
       
  2303     parent->document()->setAnimated(true);
       
  2304     handler->setAnimPeriod(begin, end);
       
  2305     return true;
       
  2306 }
       
  2307 
       
  2308 static bool parseAimateMotionNode(QSvgNode *parent,
       
  2309                                   const QXmlStreamAttributes &attributes,
       
  2310                                   QSvgHandler *)
       
  2311 {
       
  2312     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2313     return true;
       
  2314 }
       
  2315 
       
  2316 static void parseNumberTriplet(QVector<qreal> &values, const QChar *&s)
       
  2317 {
       
  2318     QVector<qreal> list = parseNumbersList(s);
       
  2319     values << list;
       
  2320     for (int i = 3 - list.size(); i > 0; --i)
       
  2321         values.append(0.0);
       
  2322 }
       
  2323 
       
  2324 static bool parseAnimateTransformNode(QSvgNode *parent,
       
  2325                                       const QXmlStreamAttributes &attributes,
       
  2326                                       QSvgHandler *handler)
       
  2327 {
       
  2328     QString typeStr    = attributes.value(QLatin1String("type")).toString();
       
  2329     QString values     = attributes.value(QLatin1String("values")).toString();
       
  2330     QString beginStr   = attributes.value(QLatin1String("begin")).toString();
       
  2331     QString durStr     = attributes.value(QLatin1String("dur")).toString();
       
  2332     QString targetStr  = attributes.value(QLatin1String("attributeName")).toString();
       
  2333     QString repeatStr  = attributes.value(QLatin1String("repeatCount")).toString();
       
  2334     QString fillStr    = attributes.value(QLatin1String("fill")).toString();
       
  2335     QString fromStr    = attributes.value(QLatin1String("from")).toString();
       
  2336     QString toStr      = attributes.value(QLatin1String("to")).toString();
       
  2337     QString byStr      = attributes.value(QLatin1String("by")).toString();
       
  2338     QString addtv      = attributes.value(QLatin1String("additive")).toString();
       
  2339 
       
  2340     QSvgAnimateTransform::Additive additive = QSvgAnimateTransform::Replace;
       
  2341     if (addtv == QLatin1String("sum"))
       
  2342         additive = QSvgAnimateTransform::Sum;
       
  2343 
       
  2344     QVector<qreal> vals;
       
  2345     if (values.isEmpty()) {
       
  2346         const QChar *s;
       
  2347         if (fromStr.isEmpty()) {
       
  2348             if (!byStr.isEmpty()) {
       
  2349                 // By-animation.
       
  2350                 additive = QSvgAnimateTransform::Sum;
       
  2351                 vals.append(0.0);
       
  2352                 vals.append(0.0);
       
  2353                 vals.append(0.0);
       
  2354                 parseNumberTriplet(vals, s = byStr.constData());
       
  2355             } else {
       
  2356                 // To-animation not defined.
       
  2357                 return false;
       
  2358             }
       
  2359         } else {
       
  2360             if (!toStr.isEmpty()) {
       
  2361                 // From-to-animation.
       
  2362                 parseNumberTriplet(vals, s = fromStr.constData());
       
  2363                 parseNumberTriplet(vals, s = toStr.constData());
       
  2364             } else if (!byStr.isEmpty()) {
       
  2365                 // From-by-animation.
       
  2366                 parseNumberTriplet(vals, s = fromStr.constData());
       
  2367                 parseNumberTriplet(vals, s = byStr.constData());
       
  2368                 for (int i = vals.size() - 3; i < vals.size(); ++i)
       
  2369                     vals[i] += vals[i - 3];
       
  2370             } else {
       
  2371                 return false;
       
  2372             }
       
  2373         }
       
  2374     } else {
       
  2375         const QChar *s = values.constData();
       
  2376         while (s && *s != QLatin1Char(0)) {
       
  2377             parseNumberTriplet(vals, s);
       
  2378             if (*s == QLatin1Char(0))
       
  2379                 break;
       
  2380             ++s;
       
  2381         }
       
  2382     }
       
  2383 
       
  2384     int ms = 1000;
       
  2385     beginStr = beginStr.trimmed();
       
  2386     if (beginStr.endsWith(QLatin1String("ms"))) {
       
  2387         beginStr.chop(2);
       
  2388         ms = 1;
       
  2389     } else if (beginStr.endsWith(QLatin1String("s"))) {
       
  2390         beginStr.chop(1);
       
  2391     }
       
  2392     int begin = static_cast<int>(toDouble(beginStr) * ms);
       
  2393     durStr = durStr.trimmed();
       
  2394     if (durStr.endsWith(QLatin1String("ms"))) {
       
  2395         durStr.chop(2);
       
  2396         ms = 1;
       
  2397     } else if (durStr.endsWith(QLatin1String("s"))) {
       
  2398         durStr.chop(1);
       
  2399         ms = 1000;
       
  2400     }
       
  2401     int end = static_cast<int>(toDouble(durStr)*ms) + begin;
       
  2402 
       
  2403     QSvgAnimateTransform::TransformType type = QSvgAnimateTransform::Empty;
       
  2404     if (typeStr == QLatin1String("translate")) {
       
  2405         type = QSvgAnimateTransform::Translate;
       
  2406     } else if (typeStr == QLatin1String("scale")) {
       
  2407         type = QSvgAnimateTransform::Scale;
       
  2408     } else if (typeStr == QLatin1String("rotate")) {
       
  2409         type = QSvgAnimateTransform::Rotate;
       
  2410     } else if (typeStr == QLatin1String("skewX")) {
       
  2411         type = QSvgAnimateTransform::SkewX;
       
  2412     } else if (typeStr == QLatin1String("skewY")) {
       
  2413         type = QSvgAnimateTransform::SkewY;
       
  2414     } else {
       
  2415         return false;
       
  2416     }
       
  2417 
       
  2418     QSvgAnimateTransform *anim = new QSvgAnimateTransform(begin, end, 0);
       
  2419     anim->setArgs(type, additive, vals);
       
  2420     anim->setFreeze(fillStr == QLatin1String("freeze"));
       
  2421     anim->setRepeatCount(
       
  2422             (repeatStr == QLatin1String("indefinite"))? -1 :
       
  2423             (repeatStr == QLatin1String(""))? 1 : toDouble(repeatStr));
       
  2424 
       
  2425     parent->appendStyleProperty(anim, someId(attributes));
       
  2426     parent->document()->setAnimated(true);
       
  2427     handler->setAnimPeriod(begin, end);
       
  2428     return true;
       
  2429 }
       
  2430 
       
  2431 static QSvgNode * createAnimationNode(QSvgNode *parent,
       
  2432                                       const QXmlStreamAttributes &attributes,
       
  2433                                       QSvgHandler *)
       
  2434 {
       
  2435     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2436     return 0;
       
  2437 }
       
  2438 
       
  2439 static bool parseAudioNode(QSvgNode *parent,
       
  2440                            const QXmlStreamAttributes &attributes,
       
  2441                            QSvgHandler *)
       
  2442 {
       
  2443     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2444     return true;
       
  2445 }
       
  2446 
       
  2447 static QSvgNode *createCircleNode(QSvgNode *parent,
       
  2448                                   const QXmlStreamAttributes &attributes,
       
  2449                                   QSvgHandler *)
       
  2450 {
       
  2451     QString cx      = attributes.value(QLatin1String("cx")).toString();
       
  2452     QString cy      = attributes.value(QLatin1String("cy")).toString();
       
  2453     QString r       = attributes.value(QLatin1String("r")).toString();
       
  2454     qreal ncx = toDouble(cx);
       
  2455     qreal ncy = toDouble(cy);
       
  2456     qreal nr  = toDouble(r);
       
  2457 
       
  2458     QRectF rect(ncx-nr, ncy-nr, nr*2, nr*2);
       
  2459     QSvgNode *circle = new QSvgCircle(parent, rect);
       
  2460     return circle;
       
  2461 }
       
  2462 
       
  2463 static QSvgNode *createDefsNode(QSvgNode *parent,
       
  2464                                 const QXmlStreamAttributes &attributes,
       
  2465                                 QSvgHandler *)
       
  2466 {
       
  2467     Q_UNUSED(attributes);
       
  2468     QSvgDefs *defs = new QSvgDefs(parent);
       
  2469     return defs;
       
  2470 }
       
  2471 
       
  2472 static bool parseDescNode(QSvgNode *parent,
       
  2473                           const QXmlStreamAttributes &attributes,
       
  2474                           QSvgHandler *)
       
  2475 {
       
  2476     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2477     return true;
       
  2478 }
       
  2479 
       
  2480 static bool parseDiscardNode(QSvgNode *parent,
       
  2481                              const QXmlStreamAttributes &attributes,
       
  2482                              QSvgHandler *)
       
  2483 {
       
  2484     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2485     return true;
       
  2486 }
       
  2487 
       
  2488 static QSvgNode *createEllipseNode(QSvgNode *parent,
       
  2489                                    const QXmlStreamAttributes &attributes,
       
  2490                                    QSvgHandler *)
       
  2491 {
       
  2492     QString cx      = attributes.value(QLatin1String("cx")).toString();
       
  2493     QString cy      = attributes.value(QLatin1String("cy")).toString();
       
  2494     QString rx      = attributes.value(QLatin1String("rx")).toString();
       
  2495     QString ry      = attributes.value(QLatin1String("ry")).toString();
       
  2496     qreal ncx = toDouble(cx);
       
  2497     qreal ncy = toDouble(cy);
       
  2498     qreal nrx = toDouble(rx);
       
  2499     qreal nry = toDouble(ry);
       
  2500 
       
  2501     QRectF rect(ncx-nrx, ncy-nry, nrx*2, nry*2);
       
  2502     QSvgNode *ellipse = new QSvgEllipse(parent, rect);
       
  2503     return ellipse;
       
  2504 }
       
  2505 
       
  2506 static QSvgStyleProperty *createFontNode(QSvgNode *parent,
       
  2507                                          const QXmlStreamAttributes &attributes,
       
  2508                                          QSvgHandler *)
       
  2509 {
       
  2510     QString hax      = attributes.value(QLatin1String("horiz-adv-x")).toString();
       
  2511     QString myId     = someId(attributes);
       
  2512 
       
  2513     qreal horizAdvX = toDouble(hax);
       
  2514 
       
  2515     while (parent && parent->type() != QSvgNode::DOC) {
       
  2516         parent = parent->parent();
       
  2517     }
       
  2518 
       
  2519     if (parent) {
       
  2520         QSvgTinyDocument *doc = static_cast<QSvgTinyDocument*>(parent);
       
  2521         QSvgFont *font = new QSvgFont(horizAdvX);
       
  2522         font->setFamilyName(myId);
       
  2523         if (!font->familyName().isEmpty()) {
       
  2524             if (!doc->svgFont(font->familyName()))
       
  2525                 doc->addSvgFont(font);
       
  2526         }
       
  2527         return new QSvgFontStyle(font, doc);
       
  2528     }
       
  2529     return 0;
       
  2530 }
       
  2531 
       
  2532 static bool parseFontFaceNode(QSvgStyleProperty *parent,
       
  2533                               const QXmlStreamAttributes &attributes,
       
  2534                               QSvgHandler *)
       
  2535 {
       
  2536     if (parent->type() != QSvgStyleProperty::FONT) {
       
  2537         return false;
       
  2538     }
       
  2539 
       
  2540     QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
       
  2541     QSvgFont *font = style->svgFont();
       
  2542     QString name   = attributes.value(QLatin1String("font-family")).toString();
       
  2543     QString unitsPerEmStr   = attributes.value(QLatin1String("units-per-em")).toString();
       
  2544 
       
  2545     qreal unitsPerEm = toDouble(unitsPerEmStr);
       
  2546     if (!unitsPerEm)
       
  2547         unitsPerEm = 1000;
       
  2548 
       
  2549     if (!name.isEmpty())
       
  2550         font->setFamilyName(name);
       
  2551     font->setUnitsPerEm(unitsPerEm);
       
  2552 
       
  2553     if (!font->familyName().isEmpty())
       
  2554         if (!style->doc()->svgFont(font->familyName()))
       
  2555             style->doc()->addSvgFont(font);
       
  2556 
       
  2557     return true;
       
  2558 }
       
  2559 
       
  2560 static bool parseFontFaceNameNode(QSvgStyleProperty *parent,
       
  2561                                   const QXmlStreamAttributes &attributes,
       
  2562                                   QSvgHandler *)
       
  2563 {
       
  2564     if (parent->type() != QSvgStyleProperty::FONT) {
       
  2565         return false;
       
  2566     }
       
  2567 
       
  2568     QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
       
  2569     QSvgFont *font = style->svgFont();
       
  2570     QString name   = attributes.value(QLatin1String("name")).toString();
       
  2571 
       
  2572     if (!name.isEmpty())
       
  2573         font->setFamilyName(name);
       
  2574 
       
  2575     if (!font->familyName().isEmpty())
       
  2576         if (!style->doc()->svgFont(font->familyName()))
       
  2577             style->doc()->addSvgFont(font);
       
  2578 
       
  2579     return true;
       
  2580 }
       
  2581 
       
  2582 static bool parseFontFaceSrcNode(QSvgStyleProperty *parent,
       
  2583                                  const QXmlStreamAttributes &attributes,
       
  2584                                  QSvgHandler *)
       
  2585 {
       
  2586     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2587     return true;
       
  2588 }
       
  2589 
       
  2590 static bool parseFontFaceUriNode(QSvgStyleProperty *parent,
       
  2591                                  const QXmlStreamAttributes &attributes,
       
  2592                                  QSvgHandler *)
       
  2593 {
       
  2594     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2595     return true;
       
  2596 }
       
  2597 
       
  2598 static bool parseForeignObjectNode(QSvgNode *parent,
       
  2599                                    const QXmlStreamAttributes &attributes,
       
  2600                                    QSvgHandler *)
       
  2601 {
       
  2602     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2603     return true;
       
  2604 }
       
  2605 
       
  2606 static QSvgNode *createGNode(QSvgNode *parent,
       
  2607                              const QXmlStreamAttributes &attributes,
       
  2608                              QSvgHandler *)
       
  2609 {
       
  2610     Q_UNUSED(attributes);
       
  2611     QSvgG *node = new QSvgG(parent);
       
  2612     return node;
       
  2613 }
       
  2614 
       
  2615 static bool parseGlyphNode(QSvgStyleProperty *parent,
       
  2616                            const QXmlStreamAttributes &attributes,
       
  2617                            QSvgHandler *)
       
  2618 {
       
  2619     if (parent->type() != QSvgStyleProperty::FONT) {
       
  2620         return false;
       
  2621     }
       
  2622 
       
  2623     QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
       
  2624     QSvgFont *font = style->svgFont();
       
  2625     createSvgGlyph(font, attributes);
       
  2626     return true;
       
  2627 }
       
  2628 
       
  2629 static bool parseHandlerNode(QSvgNode *parent,
       
  2630                              const QXmlStreamAttributes &attributes,
       
  2631                              QSvgHandler *)
       
  2632 {
       
  2633     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2634     return true;
       
  2635 }
       
  2636 
       
  2637 static bool parseHkernNode(QSvgNode *parent,
       
  2638                            const QXmlStreamAttributes &attributes,
       
  2639                            QSvgHandler *)
       
  2640 {
       
  2641     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2642     return true;
       
  2643 }
       
  2644 
       
  2645 static QSvgNode *createImageNode(QSvgNode *parent,
       
  2646                                  const QXmlStreamAttributes &attributes,
       
  2647                                  QSvgHandler *handler)
       
  2648 {
       
  2649     QString x = attributes.value(QLatin1String("x")).toString();
       
  2650     QString y = attributes.value(QLatin1String("y")).toString();
       
  2651     QString width  = attributes.value(QLatin1String("width")).toString();
       
  2652     QString height = attributes.value(QLatin1String("height")).toString();
       
  2653     QString filename = attributes.value(QLatin1String("xlink:href")).toString();
       
  2654     qreal nx = toDouble(x);
       
  2655     qreal ny = toDouble(y);
       
  2656     QSvgHandler::LengthType type;
       
  2657     qreal nwidth = parseLength(width, type, handler);
       
  2658     nwidth = convertToPixels(nwidth, true, type);
       
  2659 
       
  2660     qreal nheight = parseLength(height, type, handler);
       
  2661     nheight = convertToPixels(nheight, false, type);
       
  2662 
       
  2663 
       
  2664     filename = filename.trimmed();
       
  2665     QImage image;
       
  2666     if (filename.startsWith(QLatin1String("data"))) {
       
  2667         int idx = filename.lastIndexOf(QLatin1String("base64,"));
       
  2668         if (idx != -1) {
       
  2669             idx += 7;
       
  2670             QString dataStr = filename.mid(idx);
       
  2671             QByteArray data = QByteArray::fromBase64(dataStr.toAscii());
       
  2672             image = QImage::fromData(data);
       
  2673         } else {
       
  2674             qDebug()<<"QSvgHandler::createImageNode: Unrecognized inline image format!";
       
  2675         }
       
  2676     } else
       
  2677         image = QImage(filename);
       
  2678 
       
  2679     if (image.isNull()) {
       
  2680         qDebug()<<"couldn't create image from "<<filename;
       
  2681         return 0;
       
  2682     }
       
  2683 
       
  2684     if (image.format() == QImage::Format_ARGB32)
       
  2685         image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
       
  2686 
       
  2687     QSvgNode *img = new QSvgImage(parent,
       
  2688                                   image,
       
  2689                                   QRect(int(nx),
       
  2690                                         int(ny),
       
  2691                                         int(nwidth),
       
  2692                                         int(nheight)));
       
  2693     return img;
       
  2694 }
       
  2695 
       
  2696 static QSvgNode *createLineNode(QSvgNode *parent,
       
  2697                                 const QXmlStreamAttributes &attributes,
       
  2698                                 QSvgHandler *)
       
  2699 {
       
  2700     QString x1 = attributes.value(QLatin1String("x1")).toString();
       
  2701     QString y1 = attributes.value(QLatin1String("y1")).toString();
       
  2702     QString x2 = attributes.value(QLatin1String("x2")).toString();
       
  2703     QString y2 = attributes.value(QLatin1String("y2")).toString();
       
  2704     qreal nx1 = toDouble(x1);
       
  2705     qreal ny1 = toDouble(y1);
       
  2706     qreal nx2 = toDouble(x2);
       
  2707     qreal ny2 = toDouble(y2);
       
  2708 
       
  2709     QLineF lineBounds(nx1, ny1, nx2, ny2);
       
  2710     QSvgNode *line = new QSvgLine(parent, lineBounds);
       
  2711     return line;
       
  2712 }
       
  2713 
       
  2714 
       
  2715 static void parseBaseGradient(QSvgNode *node,
       
  2716                               const QXmlStreamAttributes &attributes,
       
  2717                               QSvgGradientStyle *gradProp,
       
  2718                               QSvgHandler *handler)
       
  2719 {
       
  2720     QString link   = attributes.value(QLatin1String("xlink:href")).toString();
       
  2721     QStringRef trans  = attributes.value(QLatin1String("gradientTransform"));
       
  2722     QString spread = attributes.value(QLatin1String("spreadMethod")).toString();
       
  2723     QString units = attributes.value(QLatin1String("gradientUnits")).toString();
       
  2724     QStringRef colorStr = attributes.value(QLatin1String("color"));
       
  2725     QStringRef colorOpacityStr = attributes.value(QLatin1String("color-opacity"));
       
  2726 
       
  2727     QColor color;
       
  2728     if (constructColor(colorStr, colorOpacityStr, color, handler)) {
       
  2729         handler->popColor();
       
  2730         handler->pushColor(color);
       
  2731     }
       
  2732 
       
  2733     QMatrix matrix;
       
  2734     QGradient *grad = gradProp->qgradient();
       
  2735     if (!link.isEmpty()) {
       
  2736         QSvgStyleProperty *prop = node->styleProperty(link);
       
  2737         //qDebug()<<"inherited "<<prop<<" ("<<link<<")";
       
  2738         if (prop && prop->type() == QSvgStyleProperty::GRADIENT) {
       
  2739             QSvgGradientStyle *inherited =
       
  2740                 static_cast<QSvgGradientStyle*>(prop);
       
  2741             if (!inherited->stopLink().isEmpty()) {
       
  2742                 gradProp->setStopLink(inherited->stopLink(), handler->document());
       
  2743             } else {
       
  2744                 grad->setStops(inherited->qgradient()->stops());
       
  2745                 gradProp->setGradientStopsSet(inherited->gradientStopsSet());
       
  2746             }
       
  2747 
       
  2748             matrix = inherited->qmatrix();
       
  2749         } else {
       
  2750             gradProp->setStopLink(link, handler->document());
       
  2751         }
       
  2752     }
       
  2753 
       
  2754     if (!trans.isEmpty()) {
       
  2755         matrix = parseTransformationMatrix(trans);
       
  2756         gradProp->setMatrix(matrix);
       
  2757     } else if (!matrix.isIdentity()) {
       
  2758         gradProp->setMatrix(matrix);
       
  2759     }
       
  2760 
       
  2761     if (!spread.isEmpty()) {
       
  2762         if (spread == QLatin1String("pad")) {
       
  2763             grad->setSpread(QGradient::PadSpread);
       
  2764         } else if (spread == QLatin1String("reflect")) {
       
  2765             grad->setSpread(QGradient::ReflectSpread);
       
  2766         } else if (spread == QLatin1String("repeat")) {
       
  2767             grad->setSpread(QGradient::RepeatSpread);
       
  2768         }
       
  2769     }
       
  2770 
       
  2771     if (units.isEmpty() || units == QLatin1String("objectBoundingBox")) {
       
  2772          grad->setCoordinateMode(QGradient::ObjectBoundingMode);
       
  2773     }
       
  2774 }
       
  2775 
       
  2776 static QSvgStyleProperty *createLinearGradientNode(QSvgNode *node,
       
  2777                                                    const QXmlStreamAttributes &attributes,
       
  2778                                                    QSvgHandler *handler)
       
  2779 {
       
  2780     QString x1 = attributes.value(QLatin1String("x1")).toString();
       
  2781     QString y1 = attributes.value(QLatin1String("y1")).toString();
       
  2782     QString x2 = attributes.value(QLatin1String("x2")).toString();
       
  2783     QString y2 = attributes.value(QLatin1String("y2")).toString();
       
  2784 
       
  2785     qreal nx1 = 0.0;
       
  2786     qreal ny1 = 0.0;
       
  2787     qreal nx2 = 1.0;
       
  2788     qreal ny2 = 0.0;
       
  2789 
       
  2790     if (!x1.isEmpty())
       
  2791         nx1 =  convertToNumber(x1, handler);
       
  2792     if (!y1.isEmpty())
       
  2793         ny1 =  convertToNumber(y1, handler);
       
  2794     if (!x2.isEmpty())
       
  2795         nx2 =  convertToNumber(x2, handler);
       
  2796     if (!y2.isEmpty())
       
  2797         ny2 =  convertToNumber(y2, handler);
       
  2798 
       
  2799     QSvgNode *itr = node;
       
  2800     while (itr && itr->type() != QSvgNode::DOC) {
       
  2801         itr = itr->parent();
       
  2802     }
       
  2803 
       
  2804     QLinearGradient *grad = new QLinearGradient(nx1, ny1, nx2, ny2);
       
  2805     grad->setInterpolationMode(QGradient::ComponentInterpolation);
       
  2806     QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
       
  2807     parseBaseGradient(node, attributes, prop, handler);
       
  2808 
       
  2809     return prop;
       
  2810 }
       
  2811 
       
  2812 static bool parseMetadataNode(QSvgNode *parent,
       
  2813                               const QXmlStreamAttributes &attributes,
       
  2814                               QSvgHandler *)
       
  2815 {
       
  2816     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2817     return true;
       
  2818 }
       
  2819 
       
  2820 static bool parseMissingGlyphNode(QSvgStyleProperty *parent,
       
  2821                                   const QXmlStreamAttributes &attributes,
       
  2822                                   QSvgHandler *)
       
  2823 {
       
  2824     if (parent->type() != QSvgStyleProperty::FONT) {
       
  2825         return false;
       
  2826     }
       
  2827 
       
  2828     QSvgFontStyle *style = static_cast<QSvgFontStyle*>(parent);
       
  2829     QSvgFont *font = style->svgFont();
       
  2830     createSvgGlyph(font, attributes);
       
  2831     return true;
       
  2832 }
       
  2833 
       
  2834 static bool parseMpathNode(QSvgNode *parent,
       
  2835                            const QXmlStreamAttributes &attributes,
       
  2836                            QSvgHandler *)
       
  2837 {
       
  2838     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2839     return true;
       
  2840 }
       
  2841 
       
  2842 static QSvgNode *createPathNode(QSvgNode *parent,
       
  2843                                 const QXmlStreamAttributes &attributes,
       
  2844                                 QSvgHandler *)
       
  2845 {
       
  2846     QStringRef data      = attributes.value(QLatin1String("d"));
       
  2847 
       
  2848     QPainterPath qpath;
       
  2849     qpath.setFillRule(Qt::WindingFill);
       
  2850     //XXX do error handling
       
  2851     parsePathDataFast(data, qpath);
       
  2852 
       
  2853     QSvgNode *path = new QSvgPath(parent, qpath);
       
  2854     return path;
       
  2855 }
       
  2856 
       
  2857 static QSvgNode *createPolygonNode(QSvgNode *parent,
       
  2858                                    const QXmlStreamAttributes &attributes,
       
  2859                                    QSvgHandler *)
       
  2860 {
       
  2861     QString pointsStr  = attributes.value(QLatin1String("points")).toString();
       
  2862 
       
  2863     //same QPolygon parsing is in createPolylineNode
       
  2864     const QChar *s = pointsStr.constData();
       
  2865     QVector<qreal> points = parseNumbersList(s);
       
  2866     QPolygonF poly(points.count()/2);
       
  2867     int i = 0;
       
  2868     QVector<qreal>::const_iterator itr = points.constBegin();
       
  2869     while (itr != points.constEnd()) {
       
  2870         qreal one = *itr; ++itr;
       
  2871         qreal two = *itr; ++itr;
       
  2872         poly[i] = QPointF(one, two);
       
  2873         ++i;
       
  2874     }
       
  2875     QSvgNode *polygon = new QSvgPolygon(parent, poly);
       
  2876     return polygon;
       
  2877 }
       
  2878 
       
  2879 static QSvgNode *createPolylineNode(QSvgNode *parent,
       
  2880                                     const QXmlStreamAttributes &attributes,
       
  2881                                     QSvgHandler *)
       
  2882 {
       
  2883     QString pointsStr  = attributes.value(QLatin1String("points")).toString();
       
  2884 
       
  2885     //same QPolygon parsing is in createPolygonNode
       
  2886     const QChar *s = pointsStr.constData();
       
  2887     QVector<qreal> points = parseNumbersList(s);
       
  2888     QPolygonF poly(points.count()/2);
       
  2889     int i = 0;
       
  2890     QVector<qreal>::const_iterator itr = points.constBegin();
       
  2891     while (itr != points.constEnd()) {
       
  2892         qreal one = *itr; ++itr;
       
  2893         qreal two = *itr; ++itr;
       
  2894         poly[i] = QPointF(one, two);
       
  2895         ++i;
       
  2896     }
       
  2897 
       
  2898     QSvgNode *line = new QSvgPolyline(parent, poly);
       
  2899     return line;
       
  2900 }
       
  2901 
       
  2902 static bool parsePrefetchNode(QSvgNode *parent,
       
  2903                               const QXmlStreamAttributes &attributes,
       
  2904                               QSvgHandler *)
       
  2905 {
       
  2906     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2907     return true;
       
  2908 }
       
  2909 
       
  2910 static QSvgStyleProperty *createRadialGradientNode(QSvgNode *node,
       
  2911                                                    const QXmlStreamAttributes &attributes,
       
  2912                                                    QSvgHandler *handler)
       
  2913 {
       
  2914     QString cx = attributes.value(QLatin1String("cx")).toString();
       
  2915     QString cy = attributes.value(QLatin1String("cy")).toString();
       
  2916     QString r  = attributes.value(QLatin1String("r")).toString();
       
  2917     QString fx = attributes.value(QLatin1String("fx")).toString();
       
  2918     QString fy = attributes.value(QLatin1String("fy")).toString();
       
  2919 
       
  2920     qreal ncx = 0.5;
       
  2921     qreal ncy = 0.5;
       
  2922     qreal nr  = 0.5;
       
  2923     if (!cx.isEmpty())
       
  2924         ncx = toDouble(cx);
       
  2925     if (!cy.isEmpty())
       
  2926         ncy = toDouble(cy);
       
  2927     if (!r.isEmpty())
       
  2928         nr = toDouble(r);
       
  2929 
       
  2930     qreal nfx = ncx;
       
  2931     if (!fx.isEmpty())
       
  2932         nfx = toDouble(fx);
       
  2933     qreal nfy = ncy;
       
  2934     if (!fy.isEmpty())
       
  2935         nfy = toDouble(fy);
       
  2936 
       
  2937     QRadialGradient *grad = new QRadialGradient(ncx, ncy, nr, nfx, nfy);
       
  2938     grad->setInterpolationMode(QGradient::ComponentInterpolation);
       
  2939 
       
  2940     QSvgGradientStyle *prop = new QSvgGradientStyle(grad);
       
  2941     parseBaseGradient(node, attributes, prop, handler);
       
  2942 
       
  2943     return prop;
       
  2944 }
       
  2945 
       
  2946 static QSvgNode *createRectNode(QSvgNode *parent,
       
  2947                                 const QXmlStreamAttributes &attributes,
       
  2948                                 QSvgHandler *handler)
       
  2949 {
       
  2950     QString x      = attributes.value(QLatin1String("x")).toString();
       
  2951     QString y      = attributes.value(QLatin1String("y")).toString();
       
  2952     QString width  = attributes.value(QLatin1String("width")).toString();
       
  2953     QString height = attributes.value(QLatin1String("height")).toString();
       
  2954     QString rx      = attributes.value(QLatin1String("rx")).toString();
       
  2955     QString ry      = attributes.value(QLatin1String("ry")).toString();
       
  2956 
       
  2957     QSvgHandler::LengthType type;
       
  2958     qreal nwidth = parseLength(width, type, handler);
       
  2959     nwidth = convertToPixels(nwidth, true, type);
       
  2960 
       
  2961     qreal nheight = parseLength(height, type, handler);
       
  2962     nheight = convertToPixels(nheight, true, type);
       
  2963     qreal nrx = toDouble(rx);
       
  2964     qreal nry = toDouble(ry);
       
  2965 
       
  2966     QRectF bounds(toDouble(x), toDouble(y),
       
  2967                   nwidth, nheight);
       
  2968 
       
  2969     //9.2 The 'rect'  element clearly specifies it
       
  2970     // but the case might in fact be handled because
       
  2971     // we draw rounded rectangles differently
       
  2972     if (nrx > bounds.width()/2)
       
  2973         nrx = bounds.width()/2;
       
  2974     if (nry > bounds.height()/2)
       
  2975         nry = bounds.height()/2;
       
  2976 
       
  2977     if (nrx && !nry)
       
  2978         nry = nrx;
       
  2979     else if (nry && !nrx)
       
  2980         nrx = nry;
       
  2981 
       
  2982     //we draw rounded rect from 0...99
       
  2983     //svg from 0...bounds.width()/2 so we're adjusting the
       
  2984     //coordinates
       
  2985     nrx *= (100/(bounds.width()/2));
       
  2986     nry *= (100/(bounds.height()/2));
       
  2987 
       
  2988     QSvgNode *rect = new QSvgRect(parent, bounds,
       
  2989                                   int(nrx),
       
  2990                                   int(nry));
       
  2991     return rect;
       
  2992 }
       
  2993 
       
  2994 static bool parseScriptNode(QSvgNode *parent,
       
  2995                             const QXmlStreamAttributes &attributes,
       
  2996                             QSvgHandler *)
       
  2997 {
       
  2998     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  2999     return true;
       
  3000 }
       
  3001 
       
  3002 static bool parseSetNode(QSvgNode *parent,
       
  3003                          const QXmlStreamAttributes &attributes,
       
  3004                          QSvgHandler *)
       
  3005 {
       
  3006     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  3007     return true;
       
  3008 }
       
  3009 
       
  3010 static QSvgStyleProperty *createSolidColorNode(QSvgNode *parent,
       
  3011                                                const QXmlStreamAttributes &attributes,
       
  3012                                                QSvgHandler *handler)
       
  3013 {
       
  3014     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  3015     QStringRef solidColorStr = attributes.value(QLatin1String("solid-color"));
       
  3016     QStringRef solidOpacityStr = attributes.value(QLatin1String("solid-opacity"));
       
  3017 
       
  3018     if (solidOpacityStr.isEmpty())
       
  3019         solidOpacityStr = attributes.value(QLatin1String("opacity"));
       
  3020 
       
  3021     QColor color;
       
  3022     if (!constructColor(solidColorStr, solidOpacityStr, color, handler))
       
  3023         return 0;
       
  3024     QSvgSolidColorStyle *style = new QSvgSolidColorStyle(color);
       
  3025     return style;
       
  3026 }
       
  3027 
       
  3028 static bool parseStopNode(QSvgStyleProperty *parent,
       
  3029                           const QXmlStreamAttributes &attributes,
       
  3030                           QSvgHandler *handler)
       
  3031 {
       
  3032     if (parent->type() != QSvgStyleProperty::GRADIENT)
       
  3033         return false;
       
  3034     QString nodeIdStr     = someId(attributes);
       
  3035     QString xmlClassStr   = attributes.value(QLatin1String("class")).toString();
       
  3036 
       
  3037     //### nasty hack because stop gradients are not in the rendering tree
       
  3038     //    we force a dummy node with the same id and class into a rendering
       
  3039     //    tree to figure out whether the selector has a style for it
       
  3040     //    QSvgStyleSelector should be coded in a way that could avoid it
       
  3041     QSvgAnimation anim;
       
  3042     anim.setNodeId(nodeIdStr);
       
  3043     anim.setXmlClass(xmlClassStr);
       
  3044 
       
  3045     QCss::StyleSelector::NodePtr cssNode;
       
  3046     cssNode.ptr = &anim;
       
  3047     QVector<QCss::Declaration> decls = handler->selector()->declarationsForNode(cssNode);
       
  3048 
       
  3049     QXmlStreamAttributes xmlAttr = attributes;
       
  3050     for (int i = 0; i < decls.count(); ++i) {
       
  3051         const QCss::Declaration &decl = decls.at(i);
       
  3052 
       
  3053         if (decl.d->property.isEmpty())
       
  3054             continue;
       
  3055         if (decl.d->values.count() != 1)
       
  3056             continue;
       
  3057         QCss::Value val = decl.d->values.first();
       
  3058         QString valueStr = val.toString();
       
  3059         if (val.type == QCss::Value::Uri) {
       
  3060             valueStr.prepend(QLatin1String("url("));
       
  3061             valueStr.append(QLatin1Char(')'));
       
  3062         }
       
  3063         xmlAttr.append(QString(), decl.d->property, valueStr);
       
  3064     }
       
  3065     QSvgAttributes attrs(xmlAttr, handler);
       
  3066 
       
  3067     QSvgGradientStyle *style =
       
  3068         static_cast<QSvgGradientStyle*>(parent);
       
  3069     QString offsetStr   = attrs.offset.toString();
       
  3070     QStringRef colorStr    = attrs.stopColor;
       
  3071     QColor color;
       
  3072 
       
  3073     bool ok = true;
       
  3074     qreal offset = convertToNumber(offsetStr, handler, &ok);
       
  3075     if (!ok)
       
  3076         offset = 0.0;
       
  3077     QString black = QString::fromLatin1("#000000");
       
  3078     if (colorStr.isEmpty()) {
       
  3079         colorStr = QStringRef(&black);
       
  3080     }
       
  3081 
       
  3082     constructColor(colorStr, attrs.stopOpacity, color, handler);
       
  3083 
       
  3084     QGradient *grad = style->qgradient();
       
  3085 
       
  3086     offset = qMin(qreal(1), qMax(qreal(0), offset)); // Clamp to range [0, 1]
       
  3087     QGradientStops stops;
       
  3088     if (style->gradientStopsSet()) {
       
  3089         stops = grad->stops();
       
  3090         // If the stop offset equals the one previously added, add an epsilon to make it greater.
       
  3091         if (offset <= stops.back().first)
       
  3092             offset = stops.back().first + FLT_EPSILON;
       
  3093     }
       
  3094 
       
  3095     // If offset is greater than one, it must be clamped to one.
       
  3096     if (offset > 1.0) {
       
  3097         if ((stops.size() == 1) || (stops.at(stops.size() - 2).first < 1.0 - FLT_EPSILON)) {
       
  3098             stops.back().first = 1.0 - FLT_EPSILON;
       
  3099             grad->setStops(stops);
       
  3100         }
       
  3101         offset = 1.0;
       
  3102     }
       
  3103 
       
  3104     grad->setColorAt(offset, color);
       
  3105     style->setGradientStopsSet(true);
       
  3106     return true;
       
  3107 }
       
  3108 
       
  3109 static bool parseStyleNode(QSvgNode *parent,
       
  3110                            const QXmlStreamAttributes &attributes,
       
  3111                            QSvgHandler *handler)
       
  3112 {
       
  3113     Q_UNUSED(parent);
       
  3114     QString type = attributes.value(QLatin1String("type")).toString();
       
  3115     type = type.toLower();
       
  3116 
       
  3117     if (type == QLatin1String("text/css")) {
       
  3118         handler->setInStyle(true);
       
  3119     }
       
  3120 
       
  3121     return true;
       
  3122 }
       
  3123 
       
  3124 static QSvgNode *createSvgNode(QSvgNode *parent,
       
  3125                                const QXmlStreamAttributes &attributes,
       
  3126                                QSvgHandler *handler)
       
  3127 {
       
  3128     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  3129 
       
  3130     QString baseProfile = attributes.value(QLatin1String("baseProfile")).toString();
       
  3131 #if 0
       
  3132     if (baseProfile.isEmpty() && baseProfile != QLatin1String("tiny")) {
       
  3133         qWarning("Profile is %s while we only support tiny!",
       
  3134                  qPrintable(baseProfile));
       
  3135     }
       
  3136 #endif
       
  3137 
       
  3138     QSvgTinyDocument *node = new QSvgTinyDocument();
       
  3139     QString widthStr  = attributes.value(QLatin1String("width")).toString();
       
  3140     QString heightStr = attributes.value(QLatin1String("height")).toString();
       
  3141     QString viewBoxStr = attributes.value(QLatin1String("viewBox")).toString();
       
  3142 
       
  3143     QSvgHandler::LengthType type = QSvgHandler::LT_PX; // FIXME: is the default correct?
       
  3144     qreal width = 0;
       
  3145     if (!widthStr.isEmpty()) {
       
  3146         width = parseLength(widthStr, type, handler);
       
  3147         if (type != QSvgHandler::LT_PT)
       
  3148             width = convertToPixels(width, true, type);
       
  3149         node->setWidth(int(width), type == QSvgHandler::LT_PERCENT);
       
  3150     }
       
  3151     qreal height = 0;
       
  3152     if (!heightStr.isEmpty()) {
       
  3153         height = parseLength(heightStr, type, handler);
       
  3154         if (type != QSvgHandler::LT_PT)
       
  3155             height = convertToPixels(height, false, type);
       
  3156         node->setHeight(int(height), type == QSvgHandler::LT_PERCENT);
       
  3157     }
       
  3158 
       
  3159     QStringList viewBoxValues;
       
  3160     if (!viewBoxStr.isEmpty()) {
       
  3161         viewBoxStr = viewBoxStr.replace(QLatin1Char(' '), QLatin1Char(','));
       
  3162         viewBoxStr = viewBoxStr.replace(QLatin1Char('\r'), QLatin1Char(','));
       
  3163         viewBoxStr = viewBoxStr.replace(QLatin1Char('\n'), QLatin1Char(','));
       
  3164         viewBoxStr = viewBoxStr.replace(QLatin1Char('\t'), QLatin1Char(','));
       
  3165         viewBoxValues = viewBoxStr.split(QLatin1Char(','), QString::SkipEmptyParts);
       
  3166     }
       
  3167     if (viewBoxValues.count() == 4) {
       
  3168         QString xStr      = viewBoxValues.at(0).trimmed();
       
  3169         QString yStr      = viewBoxValues.at(1).trimmed();
       
  3170         QString widthStr  = viewBoxValues.at(2).trimmed();
       
  3171         QString heightStr = viewBoxValues.at(3).trimmed();
       
  3172 
       
  3173         QSvgHandler::LengthType lt;
       
  3174         qreal x = parseLength(xStr, lt, handler);
       
  3175         qreal y = parseLength(yStr, lt, handler);
       
  3176         qreal w = parseLength(widthStr, lt, handler);
       
  3177         qreal h = parseLength(heightStr, lt, handler);
       
  3178 
       
  3179         node->setViewBox(QRectF(x, y, w, h));
       
  3180 
       
  3181     } else if (width && height) {
       
  3182         if (type == QSvgHandler::LT_PT) {
       
  3183             width = convertToPixels(width, false, type);
       
  3184             height = convertToPixels(height, false, type);
       
  3185         }
       
  3186         node->setViewBox(QRectF(0, 0, width, height));
       
  3187     }
       
  3188     handler->setDefaultCoordinateSystem(QSvgHandler::LT_PX);
       
  3189 
       
  3190     return node;
       
  3191 }
       
  3192 
       
  3193 static QSvgNode *createSwitchNode(QSvgNode *parent,
       
  3194                                   const QXmlStreamAttributes &attributes,
       
  3195                                   QSvgHandler *)
       
  3196 {
       
  3197     Q_UNUSED(attributes);
       
  3198     QSvgSwitch *node = new QSvgSwitch(parent);
       
  3199     return node;
       
  3200 }
       
  3201 
       
  3202 static bool parseTbreakNode(QSvgNode *parent,
       
  3203                             const QXmlStreamAttributes &,
       
  3204                             QSvgHandler *)
       
  3205 {
       
  3206     if (parent->type() != QSvgNode::TEXTAREA)
       
  3207         return false;
       
  3208     static_cast<QSvgText*>(parent)->addLineBreak();
       
  3209     return true;
       
  3210 }
       
  3211 
       
  3212 static QSvgNode *createTextNode(QSvgNode *parent,
       
  3213                                 const QXmlStreamAttributes &attributes,
       
  3214                                 QSvgHandler *handler)
       
  3215 {
       
  3216     QString x = attributes.value(QLatin1String("x")).toString();
       
  3217     QString y = attributes.value(QLatin1String("y")).toString();
       
  3218     //### editable and rotate not handled
       
  3219     QSvgHandler::LengthType type;
       
  3220     qreal nx = parseLength(x, type, handler);
       
  3221     qreal ny = parseLength(y, type, handler);
       
  3222 
       
  3223     QSvgNode *text = new QSvgText(parent, QPointF(nx, ny));
       
  3224     return text;
       
  3225 }
       
  3226 
       
  3227 static QSvgNode *createTextAreaNode(QSvgNode *parent,
       
  3228                                     const QXmlStreamAttributes &attributes,
       
  3229                                     QSvgHandler *handler)
       
  3230 {
       
  3231     QSvgText *node = static_cast<QSvgText *>(createTextNode(parent, attributes, handler));
       
  3232     if (node) {
       
  3233         QSvgHandler::LengthType type;
       
  3234         qreal width = parseLength(attributes.value(QLatin1String("width")).toString(), type, handler);
       
  3235         qreal height = parseLength(attributes.value(QLatin1String("height")).toString(), type, handler);
       
  3236         node->setTextArea(QSizeF(width, height));
       
  3237     }
       
  3238     return node;
       
  3239 }
       
  3240 
       
  3241 static QSvgNode *createTspanNode(QSvgNode *parent,
       
  3242                                     const QXmlStreamAttributes &,
       
  3243                                     QSvgHandler *)
       
  3244 {
       
  3245     return new QSvgTspan(parent);
       
  3246 }
       
  3247 
       
  3248 static bool parseTitleNode(QSvgNode *parent,
       
  3249                            const QXmlStreamAttributes &attributes,
       
  3250                            QSvgHandler *)
       
  3251 {
       
  3252     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  3253     return true;
       
  3254 }
       
  3255 
       
  3256 static QSvgNode *createUseNode(QSvgNode *parent,
       
  3257                                const QXmlStreamAttributes &attributes,
       
  3258                                QSvgHandler *handler)
       
  3259 {
       
  3260     QString linkId = attributes.value(QLatin1String("xlink:href")).toString().remove(0, 1);
       
  3261     QString xStr = attributes.value(QLatin1String("x")).toString();
       
  3262     QString yStr = attributes.value(QLatin1String("y")).toString();
       
  3263     QSvgStructureNode *group = 0;
       
  3264 
       
  3265     if (linkId.isEmpty())
       
  3266         linkId = attributes.value(QLatin1String("href")).toString().remove(0, 1);
       
  3267     switch (parent->type()) {
       
  3268     case QSvgNode::DOC:
       
  3269     case QSvgNode::DEFS:
       
  3270     case QSvgNode::G:
       
  3271     case QSvgNode::SWITCH:
       
  3272         group = static_cast<QSvgStructureNode*>(parent);
       
  3273         break;
       
  3274     default:
       
  3275         break;
       
  3276     }
       
  3277 
       
  3278     if (group) {
       
  3279         QSvgNode *link = group->scopeNode(linkId);
       
  3280         if (link) {
       
  3281             QPointF pt;
       
  3282             if (!xStr.isNull() || !yStr.isNull()) {
       
  3283                 QSvgHandler::LengthType type;
       
  3284                 qreal nx = parseLength(xStr, type, handler);
       
  3285                 nx = convertToPixels(nx, true, type);
       
  3286 
       
  3287                 qreal ny = parseLength(yStr, type, handler);
       
  3288                 ny = convertToPixels(ny, true, type);
       
  3289                 pt = QPointF(nx, ny);
       
  3290             }
       
  3291 
       
  3292             //delay link resolving till the first draw call on
       
  3293             //use nodes, link 2might have not been created yet
       
  3294             QSvgUse *node = new QSvgUse(pt, parent, link);
       
  3295             return node;
       
  3296         }
       
  3297     }
       
  3298 
       
  3299     qWarning("link %s hasn't been detected!", qPrintable(linkId));
       
  3300     return 0;
       
  3301 }
       
  3302 
       
  3303 static QSvgNode *createVideoNode(QSvgNode *parent,
       
  3304                                  const QXmlStreamAttributes &attributes,
       
  3305                                  QSvgHandler *)
       
  3306 {
       
  3307     Q_UNUSED(parent); Q_UNUSED(attributes);
       
  3308     return 0;
       
  3309 }
       
  3310 
       
  3311 typedef QSvgNode *(*FactoryMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
       
  3312 
       
  3313 static FactoryMethod findGroupFactory(const QString &name)
       
  3314 {
       
  3315     if (name.isEmpty())
       
  3316         return 0;
       
  3317 
       
  3318     QStringRef ref(&name, 1, name.length() - 1);
       
  3319     switch (name.at(0).unicode()) {
       
  3320     case 'd':
       
  3321         if (ref == QLatin1String("efs")) return createDefsNode;
       
  3322         break;
       
  3323     case 'g':
       
  3324         if (ref.isEmpty()) return createGNode;
       
  3325         break;
       
  3326     case 's':
       
  3327         if (ref == QLatin1String("vg")) return createSvgNode;
       
  3328         if (ref == QLatin1String("witch")) return createSwitchNode;
       
  3329         break;
       
  3330     default:
       
  3331         break;
       
  3332     }
       
  3333     return 0;
       
  3334 }
       
  3335 
       
  3336 static FactoryMethod findGraphicsFactory(const QString &name)
       
  3337 {
       
  3338     if (name.isEmpty())
       
  3339         return 0;
       
  3340 
       
  3341     QStringRef ref(&name, 1, name.length() - 1);
       
  3342     switch (name.at(0).unicode()) {
       
  3343     case 'a':
       
  3344         if (ref == QLatin1String("nimation")) return createAnimationNode;
       
  3345         break;
       
  3346     case 'c':
       
  3347         if (ref == QLatin1String("ircle")) return createCircleNode;
       
  3348         break;
       
  3349     case 'e':
       
  3350         if (ref == QLatin1String("llipse")) return createEllipseNode;
       
  3351         break;
       
  3352     case 'i':
       
  3353         if (ref == QLatin1String("mage")) return createImageNode;
       
  3354         break;
       
  3355     case 'l':
       
  3356         if (ref == QLatin1String("ine")) return createLineNode;
       
  3357         break;
       
  3358     case 'p':
       
  3359         if (ref == QLatin1String("ath")) return createPathNode;
       
  3360         if (ref == QLatin1String("olygon")) return createPolygonNode;
       
  3361         if (ref == QLatin1String("olyline")) return createPolylineNode;
       
  3362         break;
       
  3363     case 'r':
       
  3364         if (ref == QLatin1String("ect")) return createRectNode;
       
  3365         break;
       
  3366     case 't':
       
  3367         if (ref == QLatin1String("ext")) return createTextNode;
       
  3368         if (ref == QLatin1String("extArea")) return createTextAreaNode;
       
  3369         if (ref == QLatin1String("span")) return createTspanNode;
       
  3370         break;
       
  3371     case 'u':
       
  3372         if (ref == QLatin1String("se")) return createUseNode;
       
  3373         break;
       
  3374     case 'v':
       
  3375         if (ref == QLatin1String("ideo")) return createVideoNode;
       
  3376         break;
       
  3377     default:
       
  3378         break;
       
  3379     }
       
  3380     return 0;
       
  3381 }
       
  3382 
       
  3383 typedef bool (*ParseMethod)(QSvgNode *, const QXmlStreamAttributes &, QSvgHandler *);
       
  3384 
       
  3385 static ParseMethod findUtilFactory(const QString &name)
       
  3386 {
       
  3387     if (name.isEmpty())
       
  3388         return 0;
       
  3389 
       
  3390     QStringRef ref(&name, 1, name.length() - 1);
       
  3391     switch (name.at(0).unicode()) {
       
  3392     case 'a':
       
  3393         if (ref.isEmpty()) return parseAnchorNode;
       
  3394         if (ref == QLatin1String("nimate")) return parseAnimateNode;
       
  3395         if (ref == QLatin1String("nimateColor")) return parseAnimateColorNode;
       
  3396         if (ref == QLatin1String("nimateMotion")) return parseAimateMotionNode;
       
  3397         if (ref == QLatin1String("nimateTransform")) return parseAnimateTransformNode;
       
  3398         if (ref == QLatin1String("udio")) return parseAudioNode;
       
  3399         break;
       
  3400     case 'd':
       
  3401         if (ref == QLatin1String("esc")) return parseDescNode;
       
  3402         if (ref == QLatin1String("iscard")) return parseDiscardNode;
       
  3403         break;
       
  3404     case 'f':
       
  3405         if (ref == QLatin1String("oreignObject")) return parseForeignObjectNode;
       
  3406         break;
       
  3407     case 'h':
       
  3408         if (ref == QLatin1String("andler")) return parseHandlerNode;
       
  3409         if (ref == QLatin1String("kern")) return parseHkernNode;
       
  3410         break;
       
  3411     case 'm':
       
  3412         if (ref == QLatin1String("etadata")) return parseMetadataNode;
       
  3413         if (ref == QLatin1String("path")) return parseMpathNode;
       
  3414         break;
       
  3415     case 'p':
       
  3416         if (ref == QLatin1String("refetch")) return parsePrefetchNode;
       
  3417         break;
       
  3418     case 's':
       
  3419         if (ref == QLatin1String("cript")) return parseScriptNode;
       
  3420         if (ref == QLatin1String("et")) return parseSetNode;
       
  3421         if (ref == QLatin1String("tyle")) return parseStyleNode;
       
  3422         break;
       
  3423     case 't':
       
  3424         if (ref == QLatin1String("break")) return parseTbreakNode;
       
  3425         if (ref == QLatin1String("itle")) return parseTitleNode;
       
  3426         break;
       
  3427     default:
       
  3428         break;
       
  3429     }
       
  3430     return 0;
       
  3431 }
       
  3432 
       
  3433 typedef QSvgStyleProperty *(*StyleFactoryMethod)(QSvgNode *,
       
  3434                                                  const QXmlStreamAttributes &,
       
  3435                                                  QSvgHandler *);
       
  3436 
       
  3437 static StyleFactoryMethod findStyleFactoryMethod(const QString &name)
       
  3438 {
       
  3439     if (name.isEmpty())
       
  3440         return 0;
       
  3441 
       
  3442     QStringRef ref(&name, 1, name.length() - 1);
       
  3443     switch (name.at(0).unicode()) {
       
  3444     case 'f':
       
  3445         if (ref == QLatin1String("ont")) return createFontNode;
       
  3446         break;
       
  3447     case 'l':
       
  3448         if (ref == QLatin1String("inearGradient")) return createLinearGradientNode;
       
  3449         break;
       
  3450     case 'r':
       
  3451         if (ref == QLatin1String("adialGradient")) return createRadialGradientNode;
       
  3452         break;
       
  3453     case 's':
       
  3454         if (ref == QLatin1String("olidColor")) return createSolidColorNode;
       
  3455         break;
       
  3456     default:
       
  3457         break;
       
  3458     }
       
  3459     return 0;
       
  3460 }
       
  3461 
       
  3462 typedef bool (*StyleParseMethod)(QSvgStyleProperty *,
       
  3463                                  const QXmlStreamAttributes &,
       
  3464                                  QSvgHandler *);
       
  3465 
       
  3466 static StyleParseMethod findStyleUtilFactoryMethod(const QString &name)
       
  3467 {
       
  3468     if (name.isEmpty())
       
  3469         return 0;
       
  3470 
       
  3471     QStringRef ref(&name, 1, name.length() - 1);
       
  3472     switch (name.at(0).unicode()) {
       
  3473     case 'f':
       
  3474         if (ref == QLatin1String("ont-face")) return parseFontFaceNode;
       
  3475         if (ref == QLatin1String("ont-face-name")) return parseFontFaceNameNode;
       
  3476         if (ref == QLatin1String("ont-face-src")) return parseFontFaceSrcNode;
       
  3477         if (ref == QLatin1String("ont-face-uri")) return parseFontFaceUriNode;
       
  3478         break;
       
  3479     case 'g':
       
  3480         if (ref == QLatin1String("lyph")) return parseGlyphNode;
       
  3481         break;
       
  3482     case 'm':
       
  3483         if (ref == QLatin1String("issing-glyph")) return parseMissingGlyphNode;
       
  3484         break;
       
  3485     case 's':
       
  3486         if (ref == QLatin1String("top")) return parseStopNode;
       
  3487         break;
       
  3488     default:
       
  3489         break;
       
  3490     }
       
  3491     return 0;
       
  3492 }
       
  3493 
       
  3494 QSvgHandler::QSvgHandler(QIODevice *device) : xml(new QXmlStreamReader(device))
       
  3495                                              , m_ownsReader(true)
       
  3496 {
       
  3497     init();
       
  3498 }
       
  3499 
       
  3500 QSvgHandler::QSvgHandler(const QByteArray &data) : xml(new QXmlStreamReader(data))
       
  3501                                                  , m_ownsReader(true)
       
  3502 {
       
  3503     init();
       
  3504 }
       
  3505 
       
  3506 QSvgHandler::QSvgHandler(QXmlStreamReader *const reader) : xml(reader)
       
  3507                                                          , m_ownsReader(false)
       
  3508 {
       
  3509     init();
       
  3510 }
       
  3511 
       
  3512 void QSvgHandler::init()
       
  3513 {
       
  3514     m_doc = 0;
       
  3515     m_style = 0;
       
  3516     m_animEnd = 0;
       
  3517     m_defaultCoords = LT_PX;
       
  3518     m_defaultPen = QPen(Qt::black, 1, Qt::SolidLine, Qt::FlatCap, Qt::SvgMiterJoin);
       
  3519     m_defaultPen.setMiterLimit(4);
       
  3520     parse();
       
  3521 }
       
  3522 
       
  3523 void QSvgHandler::parse()
       
  3524 {
       
  3525     xml->setNamespaceProcessing(false);
       
  3526     m_selector = new QSvgStyleSelector;
       
  3527     m_inStyle = false;
       
  3528     bool done = false;
       
  3529     while (!xml->atEnd() && !done) {
       
  3530         switch (xml->readNext()) {
       
  3531         case QXmlStreamReader::StartElement:
       
  3532             // he we could/should verify the namespaces, and simply
       
  3533             // call m_skipNodes(Unknown) if we don't know the
       
  3534             // namespace.  We do support http://www.w3.org/2000/svg
       
  3535             // but also http://www.w3.org/2000/svg-20000303-stylable
       
  3536             // And if the document uses an external dtd, the reported
       
  3537             // namespaceUri is empty. The only possible strategy at
       
  3538             // this point is to do what everyone else seems to do and
       
  3539             // ignore the reported namespaceUri completely.
       
  3540             startElement(xml->name().toString(), xml->attributes());
       
  3541             break;
       
  3542         case QXmlStreamReader::EndElement:
       
  3543             endElement(xml->name());
       
  3544             // if we are using somebody else's qxmlstreamreader
       
  3545             // we should not read until the end of the stream
       
  3546             done = !m_ownsReader && (xml->name() == QLatin1String("svg"));
       
  3547             break;
       
  3548         case QXmlStreamReader::Characters:
       
  3549             characters(xml->text());
       
  3550             break;
       
  3551         case QXmlStreamReader::ProcessingInstruction:
       
  3552             processingInstruction(xml->processingInstructionTarget().toString(), xml->processingInstructionData().toString());
       
  3553             break;
       
  3554         default:
       
  3555             break;
       
  3556         }
       
  3557     }
       
  3558     resolveGradients(m_doc);
       
  3559 }
       
  3560 
       
  3561 bool QSvgHandler::startElement(const QString &localName,
       
  3562                                const QXmlStreamAttributes &attributes)
       
  3563 {
       
  3564     QSvgNode *node = 0;
       
  3565 
       
  3566     pushColorCopy();
       
  3567 
       
  3568     /* The xml:space attribute may appear on any element. We do
       
  3569      * a lookup by the qualified name here, but this is namespace aware, since
       
  3570      * the XML namespace can only be bound to prefix "xml." */
       
  3571     const QStringRef xmlSpace(attributes.value(QLatin1String("xml:space")));
       
  3572     if (xmlSpace.isNull()) {
       
  3573         // This element has no xml:space attribute.
       
  3574         m_whitespaceMode.push(m_whitespaceMode.isEmpty() ? QSvgText::Default : m_whitespaceMode.top());
       
  3575     } else if (xmlSpace == QLatin1String("preserve")) {
       
  3576         m_whitespaceMode.push(QSvgText::Preserve);
       
  3577     } else if (xmlSpace == QLatin1String("default")) {
       
  3578         m_whitespaceMode.push(QSvgText::Default);
       
  3579     } else {
       
  3580         qWarning() << QString::fromLatin1("\"%1\" is an invalid value for attribute xml:space. "
       
  3581                                           "Valid values are \"preserve\" and \"default\".").arg(xmlSpace.toString());
       
  3582         m_whitespaceMode.push(QSvgText::Default);
       
  3583     }
       
  3584 
       
  3585     if (FactoryMethod method = findGroupFactory(localName)) {
       
  3586         //group
       
  3587         node = method(m_doc ? m_nodes.top() : 0, attributes, this);
       
  3588         Q_ASSERT(node);
       
  3589         if (!m_doc) {
       
  3590             Q_ASSERT(node->type() == QSvgNode::DOC);
       
  3591             m_doc = static_cast<QSvgTinyDocument*>(node);
       
  3592         } else {
       
  3593             switch (m_nodes.top()->type()) {
       
  3594             case QSvgNode::DOC:
       
  3595             case QSvgNode::G:
       
  3596             case QSvgNode::DEFS:
       
  3597             case QSvgNode::SWITCH:
       
  3598             {
       
  3599                 QSvgStructureNode *group =
       
  3600                     static_cast<QSvgStructureNode*>(m_nodes.top());
       
  3601                 group->addChild(node, someId(attributes));
       
  3602             }
       
  3603                 break;
       
  3604             default:
       
  3605                 break;
       
  3606             }
       
  3607         }
       
  3608         parseCoreNode(node, attributes);
       
  3609         cssStyleLookup(node, this, m_selector);
       
  3610         parseStyle(node, attributes, this);
       
  3611     } else if (FactoryMethod method = findGraphicsFactory(localName)) {
       
  3612         //rendering element
       
  3613         Q_ASSERT(!m_nodes.isEmpty());
       
  3614         node = method(m_nodes.top(), attributes, this);
       
  3615         if (node) {
       
  3616             switch (m_nodes.top()->type()) {
       
  3617             case QSvgNode::DOC:
       
  3618             case QSvgNode::G:
       
  3619             case QSvgNode::DEFS:
       
  3620             case QSvgNode::SWITCH:
       
  3621             {
       
  3622                 QSvgStructureNode *group =
       
  3623                     static_cast<QSvgStructureNode*>(m_nodes.top());
       
  3624                 group->addChild(node, someId(attributes));
       
  3625             }
       
  3626                 break;
       
  3627             case QSvgNode::TEXT:
       
  3628             case QSvgNode::TEXTAREA:
       
  3629                 if (node->type() == QSvgNode::TSPAN) {
       
  3630                     static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node));
       
  3631                 } else {
       
  3632                     qWarning("\'text\' or \'textArea\' element contains invalid element type.");
       
  3633                     delete node;
       
  3634                     node = 0;
       
  3635                 }
       
  3636                 break;
       
  3637             default:
       
  3638                 qWarning("Could not add child element to parent element because the types are incorrect.");
       
  3639                 delete node;
       
  3640                 node = 0;
       
  3641                 break;
       
  3642             }
       
  3643 
       
  3644             if (node) {
       
  3645                 parseCoreNode(node, attributes);
       
  3646                 cssStyleLookup(node, this, m_selector);
       
  3647                 parseStyle(node, attributes, this);
       
  3648                 if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) {
       
  3649                     static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top());
       
  3650                 } else if (node->type() == QSvgNode::TSPAN) {
       
  3651                     static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top());
       
  3652                 }
       
  3653             }
       
  3654         }
       
  3655     } else if (ParseMethod method = findUtilFactory(localName)) {
       
  3656         Q_ASSERT(!m_nodes.isEmpty());
       
  3657         if (!method(m_nodes.top(), attributes, this)) {
       
  3658             qWarning("Problem parsing %s", qPrintable(localName));
       
  3659         }
       
  3660     } else if (StyleFactoryMethod method = findStyleFactoryMethod(localName)) {
       
  3661         QSvgStyleProperty *prop = method(m_nodes.top(), attributes, this);
       
  3662         if (prop) {
       
  3663             m_style = prop;
       
  3664             m_nodes.top()->appendStyleProperty(prop, someId(attributes));
       
  3665         } else {
       
  3666             qWarning("Could not parse node: %s", qPrintable(localName));
       
  3667         }
       
  3668     } else if (StyleParseMethod method = findStyleUtilFactoryMethod(localName)) {
       
  3669         if (m_style) {
       
  3670             if (!method(m_style, attributes, this)) {
       
  3671                 qWarning("Problem parsing %s", qPrintable(localName));
       
  3672             }
       
  3673         }
       
  3674     } else {
       
  3675         //qWarning()<<"Skipping unknown element!"<<namespaceURI<<"::"<<localName;
       
  3676         m_skipNodes.push(Unknown);
       
  3677         return true;
       
  3678     }
       
  3679 
       
  3680     if (node) {
       
  3681         m_nodes.push(node);
       
  3682         m_skipNodes.push(Graphics);
       
  3683     } else {
       
  3684         //qDebug()<<"Skipping "<<localName;
       
  3685         m_skipNodes.push(Style);
       
  3686     }
       
  3687     return true;
       
  3688 }
       
  3689 
       
  3690 bool QSvgHandler::endElement(const QStringRef &localName)
       
  3691 {
       
  3692     CurrentNode node = m_skipNodes.top();
       
  3693     m_skipNodes.pop();
       
  3694     m_whitespaceMode.pop();
       
  3695 
       
  3696     popColor();
       
  3697 
       
  3698     if (node == Unknown) {
       
  3699         return true;
       
  3700     }
       
  3701 
       
  3702     if (m_inStyle && localName == QLatin1String("style"))
       
  3703         m_inStyle = false;
       
  3704 
       
  3705     if (node == Graphics)
       
  3706         m_nodes.pop();
       
  3707     else if (m_style && !m_skipNodes.isEmpty() && m_skipNodes.top() != Style)
       
  3708         m_style = 0;
       
  3709 
       
  3710     return true;
       
  3711 }
       
  3712 
       
  3713 void QSvgHandler::resolveGradients(QSvgNode *node)
       
  3714 {
       
  3715     if (!node || (node->type() != QSvgNode::DOC && node->type() != QSvgNode::G
       
  3716         && node->type() != QSvgNode::DEFS && node->type() != QSvgNode::SWITCH)) {
       
  3717         return;
       
  3718     }
       
  3719     QSvgStructureNode *structureNode = static_cast<QSvgStructureNode *>(node);
       
  3720 
       
  3721     QList<QSvgNode *> ren = structureNode->renderers();
       
  3722     for (QList<QSvgNode *>::iterator it = ren.begin(); it != ren.end(); ++it) {
       
  3723         QSvgFillStyle *fill = static_cast<QSvgFillStyle *>((*it)->styleProperty(QSvgStyleProperty::FILL));
       
  3724         if (fill && !fill->isGradientResolved()) {
       
  3725             QString id = fill->gradientId();
       
  3726             QSvgFillStyleProperty *style = structureNode->styleProperty(id);
       
  3727             if (style) {
       
  3728                 fill->setFillStyle(style);
       
  3729             } else {
       
  3730                 qWarning("Could not resolve property : %s", qPrintable(id));
       
  3731                 fill->setBrush(Qt::NoBrush);
       
  3732             }
       
  3733         }
       
  3734 
       
  3735         QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle *>((*it)->styleProperty(QSvgStyleProperty::STROKE));
       
  3736         if (stroke && !stroke->isGradientResolved()) {
       
  3737             QString id = stroke->gradientId();
       
  3738             QSvgFillStyleProperty *style = structureNode->styleProperty(id);
       
  3739             if (style) {
       
  3740                 stroke->setStyle(style);
       
  3741             } else {
       
  3742                 qWarning("Could not resolve property : %s", qPrintable(id));
       
  3743                 stroke->setStroke(Qt::NoBrush);
       
  3744             }
       
  3745         }
       
  3746 
       
  3747         resolveGradients(*it);
       
  3748     }
       
  3749 }
       
  3750 
       
  3751 bool QSvgHandler::characters(const QStringRef &str)
       
  3752 {
       
  3753     if (m_inStyle) {
       
  3754         QString css = str.toString();
       
  3755         QCss::StyleSheet sheet;
       
  3756         QCss::Parser(css).parse(&sheet);
       
  3757         m_selector->styleSheets.append(sheet);
       
  3758         return true;
       
  3759     } else if (m_skipNodes.isEmpty() || m_skipNodes.top() == Unknown)
       
  3760         return true;
       
  3761 
       
  3762     if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) {
       
  3763         static_cast<QSvgText*>(m_nodes.top())->addText(str.toString());
       
  3764     } else if (m_nodes.top()->type() == QSvgNode::TSPAN) {
       
  3765         static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString());
       
  3766     }
       
  3767 
       
  3768     return true;
       
  3769 }
       
  3770 
       
  3771 QSvgTinyDocument * QSvgHandler::document() const
       
  3772 {
       
  3773     return m_doc;
       
  3774 }
       
  3775 
       
  3776 QSvgHandler::LengthType QSvgHandler::defaultCoordinateSystem() const
       
  3777 {
       
  3778     return m_defaultCoords;
       
  3779 }
       
  3780 
       
  3781 void QSvgHandler::setDefaultCoordinateSystem(LengthType type)
       
  3782 {
       
  3783     m_defaultCoords = type;
       
  3784 }
       
  3785 
       
  3786 void QSvgHandler::pushColor(const QColor &color)
       
  3787 {
       
  3788     m_colorStack.push(color);
       
  3789     m_colorTagCount.push(1);
       
  3790 }
       
  3791 
       
  3792 void QSvgHandler::pushColorCopy()
       
  3793 {
       
  3794     if (m_colorTagCount.count())
       
  3795         ++m_colorTagCount.top();
       
  3796     else
       
  3797         pushColor(Qt::black);
       
  3798 }
       
  3799 
       
  3800 void QSvgHandler::popColor()
       
  3801 {
       
  3802     if (m_colorTagCount.count()) {
       
  3803         if (!--m_colorTagCount.top()) {
       
  3804             m_colorStack.pop();
       
  3805             m_colorTagCount.pop();
       
  3806         }
       
  3807     }
       
  3808 }
       
  3809 
       
  3810 QColor QSvgHandler::currentColor() const
       
  3811 {
       
  3812     if (!m_colorStack.isEmpty())
       
  3813         return m_colorStack.top();
       
  3814     else
       
  3815         return QColor(0, 0, 0);
       
  3816 }
       
  3817 
       
  3818 void QSvgHandler::setInStyle(bool b)
       
  3819 {
       
  3820     m_inStyle = b;
       
  3821 }
       
  3822 
       
  3823 bool QSvgHandler::inStyle() const
       
  3824 {
       
  3825     return m_inStyle;
       
  3826 }
       
  3827 
       
  3828 QSvgStyleSelector * QSvgHandler::selector() const
       
  3829 {
       
  3830     return m_selector;
       
  3831 }
       
  3832 
       
  3833 bool QSvgHandler::processingInstruction(const QString &target, const QString &data)
       
  3834 {
       
  3835     if (target == QLatin1String("xml-stylesheet")) {
       
  3836         QRegExp rx(QLatin1String("type=\\\"(.+)\\\""));
       
  3837         rx.setMinimal(true);
       
  3838         bool isCss = false;
       
  3839         int pos = 0;
       
  3840         while ((pos = rx.indexIn(data, pos)) != -1) {
       
  3841             QString type =  rx.cap(1);
       
  3842             if (type.toLower() == QLatin1String("text/css")) {
       
  3843                 isCss = true;
       
  3844             }
       
  3845             pos += rx.matchedLength();
       
  3846         }
       
  3847 
       
  3848         if (isCss) {
       
  3849             QRegExp rx(QLatin1String("href=\\\"(.+)\\\""));
       
  3850             rx.setMinimal(true);
       
  3851             pos = 0;
       
  3852             pos = rx.indexIn(data, pos);
       
  3853             QString addr = rx.cap(1);
       
  3854             QFileInfo fi(addr);
       
  3855             //qDebug()<<"External CSS file "<<fi.absoluteFilePath()<<fi.exists();
       
  3856             if (fi.exists()) {
       
  3857                 QFile file(fi.absoluteFilePath());
       
  3858                 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
       
  3859                     return true;
       
  3860                 }
       
  3861                 QByteArray cssData = file.readAll();
       
  3862                 QString css = QString::fromUtf8(cssData);
       
  3863 
       
  3864                 QCss::StyleSheet sheet;
       
  3865                 QCss::Parser(css).parse(&sheet);
       
  3866                 m_selector->styleSheets.append(sheet);
       
  3867             }
       
  3868 
       
  3869         }
       
  3870     }
       
  3871 
       
  3872     return true;
       
  3873 }
       
  3874 
       
  3875 void QSvgHandler::setAnimPeriod(int start, int end)
       
  3876 {
       
  3877     Q_UNUSED(start);
       
  3878     m_animEnd   = qMax(end, m_animEnd);
       
  3879 }
       
  3880 
       
  3881 int QSvgHandler::animationDuration() const
       
  3882 {
       
  3883     return m_animEnd;
       
  3884 }
       
  3885 
       
  3886 QSvgHandler::~QSvgHandler()
       
  3887 {
       
  3888     delete m_selector;
       
  3889     m_selector = 0;
       
  3890 
       
  3891     if(m_ownsReader)
       
  3892         delete xml;
       
  3893 }
       
  3894 
       
  3895 QT_END_NAMESPACE
       
  3896 
       
  3897 #endif // QT_NO_SVG