src/gui/text/qtexthtmlparser.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
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 QtGui 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 "qtexthtmlparser_p.h"
       
    43 
       
    44 #include <qbytearray.h>
       
    45 #include <qtextcodec.h>
       
    46 #include <qapplication.h>
       
    47 #include <qstack.h>
       
    48 #include <qdebug.h>
       
    49 #include <qthread.h>
       
    50 
       
    51 #include "qtextdocument.h"
       
    52 #include "qtextformat_p.h"
       
    53 #include "qtextdocument_p.h"
       
    54 #include "qtextcursor.h"
       
    55 #include "qfont_p.h"
       
    56 #include "private/qunicodetables_p.h"
       
    57 #include "private/qfunctions_p.h"
       
    58 
       
    59 #ifndef QT_NO_TEXTHTMLPARSER
       
    60 
       
    61 QT_BEGIN_NAMESPACE
       
    62 
       
    63 // see also tst_qtextdocumentfragment.cpp
       
    64 #define MAX_ENTITY 258
       
    65 static const struct QTextHtmlEntity { const char *name; quint16 code; } entities[MAX_ENTITY]= {
       
    66     { "AElig", 0x00c6 },
       
    67     { "AMP", 38 },
       
    68     { "Aacute", 0x00c1 },
       
    69     { "Acirc", 0x00c2 },
       
    70     { "Agrave", 0x00c0 },
       
    71     { "Alpha", 0x0391 },
       
    72     { "Aring", 0x00c5 },
       
    73     { "Atilde", 0x00c3 },
       
    74     { "Auml", 0x00c4 },
       
    75     { "Beta", 0x0392 },
       
    76     { "Ccedil", 0x00c7 },
       
    77     { "Chi", 0x03a7 },
       
    78     { "Dagger", 0x2021 },
       
    79     { "Delta", 0x0394 },
       
    80     { "ETH", 0x00d0 },
       
    81     { "Eacute", 0x00c9 },
       
    82     { "Ecirc", 0x00ca },
       
    83     { "Egrave", 0x00c8 },
       
    84     { "Epsilon", 0x0395 },
       
    85     { "Eta", 0x0397 },
       
    86     { "Euml", 0x00cb },
       
    87     { "GT", 62 },
       
    88     { "Gamma", 0x0393 },
       
    89     { "Iacute", 0x00cd },
       
    90     { "Icirc", 0x00ce },
       
    91     { "Igrave", 0x00cc },
       
    92     { "Iota", 0x0399 },
       
    93     { "Iuml", 0x00cf },
       
    94     { "Kappa", 0x039a },
       
    95     { "LT", 60 },
       
    96     { "Lambda", 0x039b },
       
    97     { "Mu", 0x039c },
       
    98     { "Ntilde", 0x00d1 },
       
    99     { "Nu", 0x039d },
       
   100     { "OElig", 0x0152 },
       
   101     { "Oacute", 0x00d3 },
       
   102     { "Ocirc", 0x00d4 },
       
   103     { "Ograve", 0x00d2 },
       
   104     { "Omega", 0x03a9 },
       
   105     { "Omicron", 0x039f },
       
   106     { "Oslash", 0x00d8 },
       
   107     { "Otilde", 0x00d5 },
       
   108     { "Ouml", 0x00d6 },
       
   109     { "Phi", 0x03a6 },
       
   110     { "Pi", 0x03a0 },
       
   111     { "Prime", 0x2033 },
       
   112     { "Psi", 0x03a8 },
       
   113     { "QUOT", 34 },
       
   114     { "Rho", 0x03a1 },
       
   115     { "Scaron", 0x0160 },
       
   116     { "Sigma", 0x03a3 },
       
   117     { "THORN", 0x00de },
       
   118     { "Tau", 0x03a4 },
       
   119     { "Theta", 0x0398 },
       
   120     { "Uacute", 0x00da },
       
   121     { "Ucirc", 0x00db },
       
   122     { "Ugrave", 0x00d9 },
       
   123     { "Upsilon", 0x03a5 },
       
   124     { "Uuml", 0x00dc },
       
   125     { "Xi", 0x039e },
       
   126     { "Yacute", 0x00dd },
       
   127     { "Yuml", 0x0178 },
       
   128     { "Zeta", 0x0396 },
       
   129     { "aacute", 0x00e1 },
       
   130     { "acirc", 0x00e2 },
       
   131     { "acute", 0x00b4 },
       
   132     { "aelig", 0x00e6 },
       
   133     { "agrave", 0x00e0 },
       
   134     { "alefsym", 0x2135 },
       
   135     { "alpha", 0x03b1 },
       
   136     { "amp", 38 },
       
   137     { "and", 0x22a5 },
       
   138     { "ang", 0x2220 },
       
   139     { "apos", 0x0027 },
       
   140     { "aring", 0x00e5 },
       
   141     { "asymp", 0x2248 },
       
   142     { "atilde", 0x00e3 },
       
   143     { "auml", 0x00e4 },
       
   144     { "bdquo", 0x201e },
       
   145     { "beta", 0x03b2 },
       
   146     { "brvbar", 0x00a6 },
       
   147     { "bull", 0x2022 },
       
   148     { "cap", 0x2229 },
       
   149     { "ccedil", 0x00e7 },
       
   150     { "cedil", 0x00b8 },
       
   151     { "cent", 0x00a2 },
       
   152     { "chi", 0x03c7 },
       
   153     { "circ", 0x02c6 },
       
   154     { "clubs", 0x2663 },
       
   155     { "cong", 0x2245 },
       
   156     { "copy", 0x00a9 },
       
   157     { "crarr", 0x21b5 },
       
   158     { "cup", 0x222a },
       
   159     { "curren", 0x00a4 },
       
   160     { "dArr", 0x21d3 },
       
   161     { "dagger", 0x2020 },
       
   162     { "darr", 0x2193 },
       
   163     { "deg", 0x00b0 },
       
   164     { "delta", 0x03b4 },
       
   165     { "diams", 0x2666 },
       
   166     { "divide", 0x00f7 },
       
   167     { "eacute", 0x00e9 },
       
   168     { "ecirc", 0x00ea },
       
   169     { "egrave", 0x00e8 },
       
   170     { "empty", 0x2205 },
       
   171     { "emsp", 0x2003 },
       
   172     { "ensp", 0x2002 },
       
   173     { "epsilon", 0x03b5 },
       
   174     { "equiv", 0x2261 },
       
   175     { "eta", 0x03b7 },
       
   176     { "eth", 0x00f0 },
       
   177     { "euml", 0x00eb },
       
   178     { "euro", 0x20ac },
       
   179     { "exist", 0x2203 },
       
   180     { "fnof", 0x0192 },
       
   181     { "forall", 0x2200 },
       
   182     { "frac12", 0x00bd },
       
   183     { "frac14", 0x00bc },
       
   184     { "frac34", 0x00be },
       
   185     { "frasl", 0x2044 },
       
   186     { "gamma", 0x03b3 },
       
   187     { "ge", 0x2265 },
       
   188     { "gt", 62 },
       
   189     { "hArr", 0x21d4 },
       
   190     { "harr", 0x2194 },
       
   191     { "hearts", 0x2665 },
       
   192     { "hellip", 0x2026 },
       
   193     { "iacute", 0x00ed },
       
   194     { "icirc", 0x00ee },
       
   195     { "iexcl", 0x00a1 },
       
   196     { "igrave", 0x00ec },
       
   197     { "image", 0x2111 },
       
   198     { "infin", 0x221e },
       
   199     { "int", 0x222b },
       
   200     { "iota", 0x03b9 },
       
   201     { "iquest", 0x00bf },
       
   202     { "isin", 0x2208 },
       
   203     { "iuml", 0x00ef },
       
   204     { "kappa", 0x03ba },
       
   205     { "lArr", 0x21d0 },
       
   206     { "lambda", 0x03bb },
       
   207     { "lang", 0x2329 },
       
   208     { "laquo", 0x00ab },
       
   209     { "larr", 0x2190 },
       
   210     { "lceil", 0x2308 },
       
   211     { "ldquo", 0x201c },
       
   212     { "le", 0x2264 },
       
   213     { "lfloor", 0x230a },
       
   214     { "lowast", 0x2217 },
       
   215     { "loz", 0x25ca },
       
   216     { "lrm", 0x200e },
       
   217     { "lsaquo", 0x2039 },
       
   218     { "lsquo", 0x2018 },
       
   219     { "lt", 60 },
       
   220     { "macr", 0x00af },
       
   221     { "mdash", 0x2014 },
       
   222     { "micro", 0x00b5 },
       
   223     { "middot", 0x00b7 },
       
   224     { "minus", 0x2212 },
       
   225     { "mu", 0x03bc },
       
   226     { "nabla", 0x2207 },
       
   227     { "nbsp", 0x00a0 },
       
   228     { "ndash", 0x2013 },
       
   229     { "ne", 0x2260 },
       
   230     { "ni", 0x220b },
       
   231     { "not", 0x00ac },
       
   232     { "notin", 0x2209 },
       
   233     { "nsub", 0x2284 },
       
   234     { "ntilde", 0x00f1 },
       
   235     { "nu", 0x03bd },
       
   236     { "oacute", 0x00f3 },
       
   237     { "ocirc", 0x00f4 },
       
   238     { "oelig", 0x0153 },
       
   239     { "ograve", 0x00f2 },
       
   240     { "oline", 0x203e },
       
   241     { "omega", 0x03c9 },
       
   242     { "omicron", 0x03bf },
       
   243     { "oplus", 0x2295 },
       
   244     { "or", 0x22a6 },
       
   245     { "ordf", 0x00aa },
       
   246     { "ordm", 0x00ba },
       
   247     { "oslash", 0x00f8 },
       
   248     { "otilde", 0x00f5 },
       
   249     { "otimes", 0x2297 },
       
   250     { "ouml", 0x00f6 },
       
   251     { "para", 0x00b6 },
       
   252     { "part", 0x2202 },
       
   253     { "percnt", 0x0025 },
       
   254     { "permil", 0x2030 },
       
   255     { "perp", 0x22a5 },
       
   256     { "phi", 0x03c6 },
       
   257     { "pi", 0x03c0 },
       
   258     { "piv", 0x03d6 },
       
   259     { "plusmn", 0x00b1 },
       
   260     { "pound", 0x00a3 },
       
   261     { "prime", 0x2032 },
       
   262     { "prod", 0x220f },
       
   263     { "prop", 0x221d },
       
   264     { "psi", 0x03c8 },
       
   265     { "quot", 34 },
       
   266     { "rArr", 0x21d2 },
       
   267     { "radic", 0x221a },
       
   268     { "rang", 0x232a },
       
   269     { "raquo", 0x00bb },
       
   270     { "rarr", 0x2192 },
       
   271     { "rceil", 0x2309 },
       
   272     { "rdquo", 0x201d },
       
   273     { "real", 0x211c },
       
   274     { "reg", 0x00ae },
       
   275     { "rfloor", 0x230b },
       
   276     { "rho", 0x03c1 },
       
   277     { "rlm", 0x200f },
       
   278     { "rsaquo", 0x203a },
       
   279     { "rsquo", 0x2019 },
       
   280     { "sbquo", 0x201a },
       
   281     { "scaron", 0x0161 },
       
   282     { "sdot", 0x22c5 },
       
   283     { "sect", 0x00a7 },
       
   284     { "shy", 0x00ad },
       
   285     { "sigma", 0x03c3 },
       
   286     { "sigmaf", 0x03c2 },
       
   287     { "sim", 0x223c },
       
   288     { "spades", 0x2660 },
       
   289     { "sub", 0x2282 },
       
   290     { "sube", 0x2286 },
       
   291     { "sum", 0x2211 },
       
   292     { "sup", 0x2283 },
       
   293     { "sup1", 0x00b9 },
       
   294     { "sup2", 0x00b2 },
       
   295     { "sup3", 0x00b3 },
       
   296     { "supe", 0x2287 },
       
   297     { "szlig", 0x00df },
       
   298     { "tau", 0x03c4 },
       
   299     { "there4", 0x2234 },
       
   300     { "theta", 0x03b8 },
       
   301     { "thetasym", 0x03d1 },
       
   302     { "thinsp", 0x2009 },
       
   303     { "thorn", 0x00fe },
       
   304     { "tilde", 0x02dc },
       
   305     { "times", 0x00d7 },
       
   306     { "trade", 0x2122 },
       
   307     { "uArr", 0x21d1 },
       
   308     { "uacute", 0x00fa },
       
   309     { "uarr", 0x2191 },
       
   310     { "ucirc", 0x00fb },
       
   311     { "ugrave", 0x00f9 },
       
   312     { "uml", 0x00a8 },
       
   313     { "upsih", 0x03d2 },
       
   314     { "upsilon", 0x03c5 },
       
   315     { "uuml", 0x00fc },
       
   316     { "weierp", 0x2118 },
       
   317     { "xi", 0x03be },
       
   318     { "yacute", 0x00fd },
       
   319     { "yen", 0x00a5 },
       
   320     { "yuml", 0x00ff },
       
   321     { "zeta", 0x03b6 },
       
   322     { "zwj", 0x200d },
       
   323     { "zwnj", 0x200c }
       
   324 };
       
   325 
       
   326 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &entityStr, const QTextHtmlEntity &entity)
       
   327 {
       
   328     return entityStr < QLatin1String(entity.name);
       
   329 }
       
   330 
       
   331 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
       
   332 {
       
   333     return QLatin1String(entity.name) < entityStr;
       
   334 }
       
   335 
       
   336 static QChar resolveEntity(const QString &entity)
       
   337 {
       
   338     const QTextHtmlEntity *start = &entities[0];
       
   339     const QTextHtmlEntity *end = &entities[MAX_ENTITY];
       
   340     const QTextHtmlEntity *e = qBinaryFind(start, end, entity);
       
   341     if (e == end)
       
   342         return QChar();
       
   343     return e->code;
       
   344 }
       
   345 
       
   346 static const ushort windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
       
   347     0x20ac, // 0x80
       
   348     0x0081, // 0x81 direct mapping
       
   349     0x201a, // 0x82
       
   350     0x0192, // 0x83
       
   351     0x201e, // 0x84
       
   352     0x2026, // 0x85
       
   353     0x2020, // 0x86
       
   354     0x2021, // 0x87
       
   355     0x02C6, // 0x88
       
   356     0x2030, // 0x89
       
   357     0x0160, // 0x8A
       
   358     0x2039, // 0x8B
       
   359     0x0152, // 0x8C
       
   360     0x008D, // 0x8D direct mapping
       
   361     0x017D, // 0x8E
       
   362     0x008F, // 0x8F directmapping
       
   363     0x0090, // 0x90 directmapping
       
   364     0x2018, // 0x91
       
   365     0x2019, // 0x92
       
   366     0x201C, // 0x93
       
   367     0X201D, // 0x94
       
   368     0x2022, // 0x95
       
   369     0x2013, // 0x96
       
   370     0x2014, // 0x97
       
   371     0x02DC, // 0x98
       
   372     0x2122, // 0x99
       
   373     0x0161, // 0x9A
       
   374     0x203A, // 0x9B
       
   375     0x0153, // 0x9C
       
   376     0x009D, // 0x9D direct mapping
       
   377     0x017E, // 0x9E
       
   378     0x0178  // 0x9F
       
   379 };
       
   380 
       
   381 // the displayMode value is according to the what are blocks in the piecetable, not
       
   382 // what the w3c defines.
       
   383 static const QTextHtmlElement elements[Html_NumElements]= {
       
   384     { "a",          Html_a,          QTextHtmlElement::DisplayInline },
       
   385     { "address",    Html_address,    QTextHtmlElement::DisplayInline },
       
   386     { "b",          Html_b,          QTextHtmlElement::DisplayInline },
       
   387     { "big",        Html_big,        QTextHtmlElement::DisplayInline },
       
   388     { "blockquote", Html_blockquote, QTextHtmlElement::DisplayBlock },
       
   389     { "body",       Html_body,       QTextHtmlElement::DisplayBlock },
       
   390     { "br",         Html_br,         QTextHtmlElement::DisplayInline },
       
   391     { "caption",    Html_caption,    QTextHtmlElement::DisplayBlock },
       
   392     { "center",     Html_center,     QTextHtmlElement::DisplayBlock },
       
   393     { "cite",       Html_cite,       QTextHtmlElement::DisplayInline },
       
   394     { "code",       Html_code,       QTextHtmlElement::DisplayInline },
       
   395     { "dd",         Html_dd,         QTextHtmlElement::DisplayBlock },
       
   396     { "dfn",        Html_dfn,        QTextHtmlElement::DisplayInline },
       
   397     { "div",        Html_div,        QTextHtmlElement::DisplayBlock },
       
   398     { "dl",         Html_dl,         QTextHtmlElement::DisplayBlock },
       
   399     { "dt",         Html_dt,         QTextHtmlElement::DisplayBlock },
       
   400     { "em",         Html_em,         QTextHtmlElement::DisplayInline },
       
   401     { "font",       Html_font,       QTextHtmlElement::DisplayInline },
       
   402     { "h1",         Html_h1,         QTextHtmlElement::DisplayBlock },
       
   403     { "h2",         Html_h2,         QTextHtmlElement::DisplayBlock },
       
   404     { "h3",         Html_h3,         QTextHtmlElement::DisplayBlock },
       
   405     { "h4",         Html_h4,         QTextHtmlElement::DisplayBlock },
       
   406     { "h5",         Html_h5,         QTextHtmlElement::DisplayBlock },
       
   407     { "h6",         Html_h6,         QTextHtmlElement::DisplayBlock },
       
   408     { "head",       Html_head,       QTextHtmlElement::DisplayNone },
       
   409     { "hr",         Html_hr,         QTextHtmlElement::DisplayBlock },
       
   410     { "html",       Html_html,       QTextHtmlElement::DisplayInline },
       
   411     { "i",          Html_i,          QTextHtmlElement::DisplayInline },
       
   412     { "img",        Html_img,        QTextHtmlElement::DisplayInline },
       
   413     { "kbd",        Html_kbd,        QTextHtmlElement::DisplayInline },
       
   414     { "li",         Html_li,         QTextHtmlElement::DisplayBlock },
       
   415     { "link",       Html_link,       QTextHtmlElement::DisplayNone },
       
   416     { "meta",       Html_meta,       QTextHtmlElement::DisplayNone },
       
   417     { "nobr",       Html_nobr,       QTextHtmlElement::DisplayInline },
       
   418     { "ol",         Html_ol,         QTextHtmlElement::DisplayBlock },
       
   419     { "p",          Html_p,          QTextHtmlElement::DisplayBlock },
       
   420     { "pre",        Html_pre,        QTextHtmlElement::DisplayBlock },
       
   421     { "qt",         Html_body /*deliberate mapping*/, QTextHtmlElement::DisplayBlock },
       
   422     { "s",          Html_s,          QTextHtmlElement::DisplayInline },
       
   423     { "samp",       Html_samp,       QTextHtmlElement::DisplayInline },
       
   424     { "script",     Html_script,     QTextHtmlElement::DisplayNone },
       
   425     { "small",      Html_small,      QTextHtmlElement::DisplayInline },
       
   426     { "span",       Html_span,       QTextHtmlElement::DisplayInline },
       
   427     { "strong",     Html_strong,     QTextHtmlElement::DisplayInline },
       
   428     { "style",      Html_style,      QTextHtmlElement::DisplayNone },
       
   429     { "sub",        Html_sub,        QTextHtmlElement::DisplayInline },
       
   430     { "sup",        Html_sup,        QTextHtmlElement::DisplayInline },
       
   431     { "table",      Html_table,      QTextHtmlElement::DisplayTable },
       
   432     { "tbody",      Html_tbody,      QTextHtmlElement::DisplayTable },
       
   433     { "td",         Html_td,         QTextHtmlElement::DisplayBlock },
       
   434     { "tfoot",      Html_tfoot,      QTextHtmlElement::DisplayTable },
       
   435     { "th",         Html_th,         QTextHtmlElement::DisplayBlock },
       
   436     { "thead",      Html_thead,      QTextHtmlElement::DisplayTable },
       
   437     { "title",      Html_title,      QTextHtmlElement::DisplayNone },
       
   438     { "tr",         Html_tr,         QTextHtmlElement::DisplayTable },
       
   439     { "tt",         Html_tt,         QTextHtmlElement::DisplayInline },
       
   440     { "u",          Html_u,          QTextHtmlElement::DisplayInline },
       
   441     { "ul",         Html_ul,         QTextHtmlElement::DisplayBlock },
       
   442     { "var",        Html_var,        QTextHtmlElement::DisplayInline },
       
   443 };
       
   444 
       
   445 
       
   446 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &str, const QTextHtmlElement &e)
       
   447 {
       
   448     return str < QLatin1String(e.name);
       
   449 }
       
   450 
       
   451 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QTextHtmlElement &e, const QString &str)
       
   452 {
       
   453     return QLatin1String(e.name) < str;
       
   454 }
       
   455 
       
   456 static const QTextHtmlElement *lookupElementHelper(const QString &element)
       
   457 {
       
   458     const QTextHtmlElement *start = &elements[0];
       
   459     const QTextHtmlElement *end = &elements[Html_NumElements];
       
   460     const QTextHtmlElement *e = qBinaryFind(start, end, element);
       
   461     if (e == end)
       
   462         return 0;
       
   463     return e;
       
   464 }
       
   465 
       
   466 int QTextHtmlParser::lookupElement(const QString &element)
       
   467 {
       
   468     const QTextHtmlElement *e = lookupElementHelper(element);
       
   469     if (!e)
       
   470         return -1;
       
   471     return e->id;
       
   472 }
       
   473 
       
   474 // quotes newlines as "\\n"
       
   475 static QString quoteNewline(const QString &s)
       
   476 {
       
   477     QString n = s;
       
   478     n.replace(QLatin1Char('\n'), QLatin1String("\\n"));
       
   479     return n;
       
   480 }
       
   481 
       
   482 QTextHtmlParserNode::QTextHtmlParserNode()
       
   483     : parent(0), id(Html_unknown),
       
   484       cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false),
       
   485       hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false),
       
   486       displayMode(QTextHtmlElement::DisplayInline), hasHref(false),
       
   487       listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
       
   488       tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0),
       
   489       borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset),
       
   490       userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined)
       
   491 {
       
   492     margin[QTextHtmlParser::MarginLeft] = 0;
       
   493     margin[QTextHtmlParser::MarginRight] = 0;
       
   494     margin[QTextHtmlParser::MarginTop] = 0;
       
   495     margin[QTextHtmlParser::MarginBottom] = 0;
       
   496 }
       
   497 
       
   498 void QTextHtmlParser::dumpHtml()
       
   499 {
       
   500     for (int i = 0; i < count(); ++i) {
       
   501         qDebug().nospace() << qPrintable(QString(depth(i)*4, QLatin1Char(' ')))
       
   502                            << qPrintable(at(i).tag) << ':'
       
   503                            << quoteNewline(at(i).text);
       
   504             ;
       
   505     }
       
   506 }
       
   507 
       
   508 QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
       
   509 {
       
   510     QTextHtmlParserNode *lastNode = &nodes.last();
       
   511     QTextHtmlParserNode *newNode = 0;
       
   512 
       
   513     bool reuseLastNode = true;
       
   514 
       
   515     if (nodes.count() == 1) {
       
   516         reuseLastNode = false;
       
   517     } else if (lastNode->tag.isEmpty()) {
       
   518 
       
   519         if (lastNode->text.isEmpty()) {
       
   520             reuseLastNode = true;
       
   521         } else { // last node is a text node (empty tag) with some text
       
   522 
       
   523             if (lastNode->text.length() == 1 && lastNode->text.at(0).isSpace()) {
       
   524 
       
   525                 int lastSibling = count() - 2;
       
   526                 while (lastSibling
       
   527                        && at(lastSibling).parent != lastNode->parent
       
   528                        && at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
       
   529                     lastSibling = at(lastSibling).parent;
       
   530                 }
       
   531 
       
   532                 if (at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
       
   533                     reuseLastNode = false;
       
   534                 } else {
       
   535                     reuseLastNode = true;
       
   536                 }
       
   537             } else {
       
   538                 // text node with real (non-whitespace) text -> nothing to re-use
       
   539                 reuseLastNode = false;
       
   540             }
       
   541 
       
   542         }
       
   543 
       
   544     } else {
       
   545         // last node had a proper tag -> nothing to re-use
       
   546         reuseLastNode = false;
       
   547     }
       
   548 
       
   549     if (reuseLastNode) {
       
   550         newNode = lastNode;
       
   551         newNode->tag.clear();
       
   552         newNode->text.clear();
       
   553         newNode->id = Html_unknown;
       
   554     } else {
       
   555         nodes.resize(nodes.size() + 1);
       
   556         newNode = &nodes.last();
       
   557     }
       
   558 
       
   559     newNode->parent = parent;
       
   560     return newNode;
       
   561 }
       
   562 
       
   563 void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceProvider)
       
   564 {
       
   565     nodes.clear();
       
   566     nodes.resize(1);
       
   567     txt = text;
       
   568     pos = 0;
       
   569     len = txt.length();
       
   570     textEditMode = false;
       
   571     resourceProvider = _resourceProvider;
       
   572     parse();
       
   573     //dumpHtml();
       
   574 }
       
   575 
       
   576 int QTextHtmlParser::depth(int i) const
       
   577 {
       
   578     int depth = 0;
       
   579     while (i) {
       
   580         i = at(i).parent;
       
   581         ++depth;
       
   582     }
       
   583     return depth;
       
   584 }
       
   585 
       
   586 int QTextHtmlParser::margin(int i, int mar) const {
       
   587     int m = 0;
       
   588     const QTextHtmlParserNode *node;
       
   589     if (mar == MarginLeft
       
   590         || mar == MarginRight) {
       
   591         while (i) {
       
   592             node = &at(i);
       
   593             if (!node->isBlock() && node->id != Html_table)
       
   594                 break;
       
   595             if (node->isTableCell())
       
   596                 break;
       
   597             m += node->margin[mar];
       
   598             i = node->parent;
       
   599         }
       
   600     }
       
   601     return m;
       
   602 }
       
   603 
       
   604 int QTextHtmlParser::topMargin(int i) const
       
   605 {
       
   606     if (!i)
       
   607         return 0;
       
   608     return at(i).margin[MarginTop];
       
   609 }
       
   610 
       
   611 int QTextHtmlParser::bottomMargin(int i) const
       
   612 {
       
   613     if (!i)
       
   614         return 0;
       
   615     return at(i).margin[MarginBottom];
       
   616 }
       
   617 
       
   618 void QTextHtmlParser::eatSpace()
       
   619 {
       
   620     while (pos < len && txt.at(pos).isSpace() && txt.at(pos) != QChar::ParagraphSeparator)
       
   621         pos++;
       
   622 }
       
   623 
       
   624 void QTextHtmlParser::parse()
       
   625 {
       
   626     while (pos < len) {
       
   627         QChar c = txt.at(pos++);
       
   628         if (c == QLatin1Char('<')) {
       
   629             parseTag();
       
   630         } else if (c == QLatin1Char('&')) {
       
   631             nodes.last().text += parseEntity();
       
   632         } else {
       
   633             nodes.last().text += c;
       
   634         }
       
   635     }
       
   636 }
       
   637 
       
   638 // parses a tag after "<"
       
   639 void QTextHtmlParser::parseTag()
       
   640 {
       
   641     eatSpace();
       
   642 
       
   643     // handle comments and other exclamation mark declarations
       
   644     if (hasPrefix(QLatin1Char('!'))) {
       
   645         parseExclamationTag();
       
   646         if (nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePre
       
   647             && nodes.last().wsm != QTextHtmlParserNode::WhiteSpacePreWrap
       
   648             && !textEditMode)
       
   649             eatSpace();
       
   650         return;
       
   651     }
       
   652 
       
   653     // if close tag just close
       
   654     if (hasPrefix(QLatin1Char('/'))) {
       
   655         if (nodes.last().id == Html_style) {
       
   656 #ifndef QT_NO_CSSPARSER
       
   657             QCss::Parser parser(nodes.last().text);
       
   658             QCss::StyleSheet sheet;
       
   659             sheet.origin = QCss::StyleSheetOrigin_Author;
       
   660             parser.parse(&sheet, Qt::CaseInsensitive);
       
   661             inlineStyleSheets.append(sheet);
       
   662             resolveStyleSheetImports(sheet);
       
   663 #endif
       
   664         }
       
   665         parseCloseTag();
       
   666         return;
       
   667     }
       
   668 
       
   669     int p = last();
       
   670     while (p && at(p).tag.size() == 0)
       
   671         p = at(p).parent;
       
   672 
       
   673     QTextHtmlParserNode *node = newNode(p);
       
   674 
       
   675     // parse tag name
       
   676     node->tag = parseWord().toLower();
       
   677 
       
   678     const QTextHtmlElement *elem = lookupElementHelper(node->tag);
       
   679     if (elem) {
       
   680         node->id = elem->id;
       
   681         node->displayMode = elem->displayMode;
       
   682     } else {
       
   683         node->id = Html_unknown;
       
   684     }
       
   685 
       
   686     node->attributes.clear();
       
   687     // _need_ at least one space after the tag name, otherwise there can't be attributes
       
   688     if (pos < len && txt.at(pos).isSpace())
       
   689         node->attributes = parseAttributes();
       
   690 
       
   691     // resolveParent() may have to change the order in the tree and
       
   692     // insert intermediate nodes for buggy HTML, so re-initialize the 'node'
       
   693     // pointer through the return value
       
   694     node = resolveParent();
       
   695     resolveNode();
       
   696 
       
   697     const int nodeIndex = nodes.count() - 1; // this new node is always the last
       
   698 #ifndef QT_NO_CSSPARSER
       
   699     node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
       
   700 #endif
       
   701     applyAttributes(node->attributes);
       
   702 
       
   703     // finish tag
       
   704     bool tagClosed = false;
       
   705     while (pos < len && txt.at(pos) != QLatin1Char('>')) {
       
   706         if (txt.at(pos) == QLatin1Char('/'))
       
   707             tagClosed = true;
       
   708 
       
   709         pos++;
       
   710     }
       
   711     pos++;
       
   712 
       
   713     // in a white-space preserving environment strip off a initial newline
       
   714     // since the element itself already generates a newline
       
   715     if ((node->wsm == QTextHtmlParserNode::WhiteSpacePre
       
   716          || node->wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
       
   717         && node->isBlock()) {
       
   718         if (pos < len - 1 && txt.at(pos) == QLatin1Char('\n'))
       
   719             ++pos;
       
   720     }
       
   721 
       
   722     if (node->mayNotHaveChildren() || tagClosed) {
       
   723         newNode(node->parent);
       
   724         resolveNode();
       
   725     }
       
   726 }
       
   727 
       
   728 // parses a tag beginning with "/"
       
   729 void QTextHtmlParser::parseCloseTag()
       
   730 {
       
   731     ++pos;
       
   732     QString tag = parseWord().toLower().trimmed();
       
   733     while (pos < len) {
       
   734         QChar c = txt.at(pos++);
       
   735         if (c == QLatin1Char('>'))
       
   736             break;
       
   737     }
       
   738 
       
   739     // find corresponding open node
       
   740     int p = last();
       
   741     if (p > 0
       
   742         && at(p - 1).tag == tag
       
   743         && at(p - 1).mayNotHaveChildren())
       
   744         p--;
       
   745 
       
   746     while (p && at(p).tag != tag)
       
   747         p = at(p).parent;
       
   748 
       
   749     // simply ignore the tag if we can't find
       
   750     // a corresponding open node, for broken
       
   751     // html such as <font>blah</font></font>
       
   752     if (!p)
       
   753         return;
       
   754 
       
   755     // in a white-space preserving environment strip off a trailing newline
       
   756     // since the closing of the opening block element will automatically result
       
   757     // in a new block for elements following the <pre>
       
   758     // ...foo\n</pre><p>blah -> foo</pre><p>blah
       
   759     if ((at(p).wsm == QTextHtmlParserNode::WhiteSpacePre
       
   760          || at(p).wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
       
   761         && at(p).isBlock()) {
       
   762         if (at(last()).text.endsWith(QLatin1Char('\n')))
       
   763             nodes[last()].text.chop(1);
       
   764     }
       
   765 
       
   766     newNode(at(p).parent);
       
   767     resolveNode();
       
   768 }
       
   769 
       
   770 // parses a tag beginning with "!"
       
   771 void QTextHtmlParser::parseExclamationTag()
       
   772 {
       
   773     ++pos;
       
   774     if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
       
   775         pos += 3;
       
   776         // eat comments
       
   777         int end = txt.indexOf(QLatin1String("-->"), pos);
       
   778         pos = (end >= 0 ? end + 3 : len);
       
   779     } else {
       
   780         // eat internal tags
       
   781         while (pos < len) {
       
   782             QChar c = txt.at(pos++);
       
   783             if (c == QLatin1Char('>'))
       
   784                 break;
       
   785         }
       
   786     }
       
   787 }
       
   788 
       
   789 // parses an entity after "&", and returns it
       
   790 QString QTextHtmlParser::parseEntity()
       
   791 {
       
   792     int recover = pos;
       
   793     QString entity;
       
   794     while (pos < len) {
       
   795         QChar c = txt.at(pos++);
       
   796         if (c.isSpace() || pos - recover > 9) {
       
   797             goto error;
       
   798         }
       
   799         if (c == QLatin1Char(';'))
       
   800             break;
       
   801         entity += c;
       
   802     }
       
   803     {
       
   804         QChar resolved = resolveEntity(entity);
       
   805         if (!resolved.isNull())
       
   806             return QString(resolved);
       
   807     }
       
   808     if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
       
   809         entity.remove(0, 1); // removing leading #
       
   810 
       
   811         int base = 10;
       
   812         bool ok = false;
       
   813 
       
   814         if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
       
   815             entity.remove(0, 1);
       
   816             base = 16;
       
   817         }
       
   818 
       
   819         uint uc = entity.toUInt(&ok, base);
       
   820         if (ok) {
       
   821             if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
       
   822                 uc = windowsLatin1ExtendedCharacters[uc - 0x80];
       
   823             QString str;
       
   824             if (uc > 0xffff) {
       
   825                 // surrogate pair
       
   826                 uc -= 0x10000;
       
   827                 ushort high = uc/0x400 + 0xd800;
       
   828                 ushort low = uc%0x400 + 0xdc00;
       
   829                 str.append(QChar(high));
       
   830                 str.append(QChar(low));
       
   831             } else {
       
   832                 str.append(QChar(uc));
       
   833             }
       
   834             return str;
       
   835         }
       
   836     }
       
   837 error:
       
   838     pos = recover;
       
   839     return QLatin1String("&");
       
   840 }
       
   841 
       
   842 // parses one word, possibly quoted, and returns it
       
   843 QString QTextHtmlParser::parseWord()
       
   844 {
       
   845     QString word;
       
   846     if (hasPrefix(QLatin1Char('\"'))) { // double quotes
       
   847         ++pos;
       
   848         while (pos < len) {
       
   849             QChar c = txt.at(pos++);
       
   850             if (c == QLatin1Char('\"'))
       
   851                 break;
       
   852             else if (c == QLatin1Char('&'))
       
   853                 word += parseEntity();
       
   854             else
       
   855                 word += c;
       
   856         }
       
   857     } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
       
   858         ++pos;
       
   859         while (pos < len) {
       
   860             QChar c = txt.at(pos++);
       
   861             if (c == QLatin1Char('\''))
       
   862                 break;
       
   863             else
       
   864                 word += c;
       
   865         }
       
   866     } else { // normal text
       
   867         while (pos < len) {
       
   868             QChar c = txt.at(pos++);
       
   869             if (c == QLatin1Char('>')
       
   870                 || (c == QLatin1Char('/') && hasPrefix(QLatin1Char('>'), 1))
       
   871                 || c == QLatin1Char('<')
       
   872                 || c == QLatin1Char('=')
       
   873                 || c.isSpace()) {
       
   874                 --pos;
       
   875                 break;
       
   876             }
       
   877             if (c == QLatin1Char('&'))
       
   878                 word += parseEntity();
       
   879             else
       
   880                 word += c;
       
   881         }
       
   882     }
       
   883     return word;
       
   884 }
       
   885 
       
   886 // gives the new node the right parent
       
   887 QTextHtmlParserNode *QTextHtmlParser::resolveParent()
       
   888 {
       
   889     QTextHtmlParserNode *node = &nodes.last();
       
   890 
       
   891     int p = node->parent;
       
   892 
       
   893     // Excel gives us buggy HTML with just tr without surrounding table tags
       
   894     // or with just td tags
       
   895 
       
   896     if (node->id == Html_td) {
       
   897         int n = p;
       
   898         while (n && at(n).id != Html_tr)
       
   899             n = at(n).parent;
       
   900 
       
   901         if (!n) {
       
   902             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
       
   903             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
       
   904 
       
   905             QTextHtmlParserNode *table = &nodes[nodes.count() - 3];
       
   906             table->parent = p;
       
   907             table->id = Html_table;
       
   908             table->tag = QLatin1String("table");
       
   909             table->children.append(nodes.count() - 2); // add row as child
       
   910 
       
   911             QTextHtmlParserNode *row = &nodes[nodes.count() - 2];
       
   912             row->parent = nodes.count() - 3; // table as parent
       
   913             row->id = Html_tr;
       
   914             row->tag = QLatin1String("tr");
       
   915 
       
   916             p = nodes.count() - 2;
       
   917             node = &nodes.last(); // re-initialize pointer
       
   918         }
       
   919     }
       
   920 
       
   921     if (node->id == Html_tr) {
       
   922         int n = p;
       
   923         while (n && at(n).id != Html_table)
       
   924             n = at(n).parent;
       
   925 
       
   926         if (!n) {
       
   927             nodes.insert(nodes.count() - 1, QTextHtmlParserNode());
       
   928             QTextHtmlParserNode *table = &nodes[nodes.count() - 2];
       
   929             table->parent = p;
       
   930             table->id = Html_table;
       
   931             table->tag = QLatin1String("table");
       
   932             p = nodes.count() - 2;
       
   933             node = &nodes.last(); // re-initialize pointer
       
   934         }
       
   935     }
       
   936 
       
   937     // permit invalid html by letting block elements be children
       
   938     // of inline elements with the exception of paragraphs:
       
   939     //
       
   940     // a new paragraph closes parent inline elements (while loop),
       
   941     // unless they themselves are children of a non-paragraph block
       
   942     // element (if statement)
       
   943     //
       
   944     // For example:
       
   945     //
       
   946     // <body><p><b>Foo<p>Bar <-- second <p> implicitly closes <b> that
       
   947     //                           belongs to the first <p>. The self-nesting
       
   948     //                           check further down prevents the second <p>
       
   949     //                           from nesting into the first one then.
       
   950     //                           so Bar is not bold.
       
   951     //
       
   952     // <body><b><p>Foo <-- Foo should be bold.
       
   953     //
       
   954     // <body><b><p>Foo<p>Bar <-- Foo and Bar should be bold.
       
   955     //
       
   956     if (node->id == Html_p) {
       
   957         while (p && !at(p).isBlock())
       
   958             p = at(p).parent;
       
   959 
       
   960         if (!p || at(p).id != Html_p)
       
   961             p = node->parent;
       
   962     }
       
   963 
       
   964     // some elements are not self nesting
       
   965     if (node->id == at(p).id
       
   966         && node->isNotSelfNesting())
       
   967         p = at(p).parent;
       
   968 
       
   969     // some elements are not allowed in certain contexts
       
   970     while ((p && !node->allowedInContext(at(p).id))
       
   971            // ### make new styles aware of empty tags
       
   972            || at(p).mayNotHaveChildren()
       
   973        ) {
       
   974         p = at(p).parent;
       
   975     }
       
   976 
       
   977     node->parent = p;
       
   978 
       
   979     // makes it easier to traverse the tree, later
       
   980     nodes[p].children.append(nodes.count() - 1);
       
   981     return node;
       
   982 }
       
   983 
       
   984 // sets all properties on the new node
       
   985 void QTextHtmlParser::resolveNode()
       
   986 {
       
   987     QTextHtmlParserNode *node = &nodes.last();
       
   988     const QTextHtmlParserNode *parent = &nodes.at(node->parent);
       
   989     node->initializeProperties(parent, this);
       
   990 }
       
   991 
       
   992 bool QTextHtmlParserNode::isNestedList(const QTextHtmlParser *parser) const
       
   993 {
       
   994     if (!isListStart())
       
   995         return false;
       
   996 
       
   997     int p = parent;
       
   998     while (p) {
       
   999         if (parser->at(p).isListStart())
       
  1000             return true;
       
  1001         p = parser->at(p).parent;
       
  1002     }
       
  1003     return false;
       
  1004 }
       
  1005 
       
  1006 void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser)
       
  1007 {
       
  1008     // inherit properties from parent element
       
  1009     charFormat = parent->charFormat;
       
  1010 
       
  1011     if (id == Html_html)
       
  1012         blockFormat.setLayoutDirection(Qt::LeftToRight); // HTML default
       
  1013     else if (parent->blockFormat.hasProperty(QTextFormat::LayoutDirection))
       
  1014         blockFormat.setLayoutDirection(parent->blockFormat.layoutDirection());
       
  1015 
       
  1016     if (parent->displayMode == QTextHtmlElement::DisplayNone)
       
  1017         displayMode = QTextHtmlElement::DisplayNone;
       
  1018 
       
  1019     if (parent->id != Html_table || id == Html_caption) {
       
  1020         if (parent->blockFormat.hasProperty(QTextFormat::BlockAlignment))
       
  1021             blockFormat.setAlignment(parent->blockFormat.alignment());
       
  1022         else
       
  1023             blockFormat.clearProperty(QTextFormat::BlockAlignment);
       
  1024     }
       
  1025     // we don't paint per-row background colors, yet. so as an
       
  1026     // exception inherit the background color here
       
  1027     // we also inherit the background between inline elements
       
  1028     if ((parent->id != Html_tr || !isTableCell())
       
  1029         && (displayMode != QTextHtmlElement::DisplayInline || parent->displayMode != QTextHtmlElement::DisplayInline)) {
       
  1030         charFormat.clearProperty(QTextFormat::BackgroundBrush);
       
  1031     }
       
  1032 
       
  1033     listStyle = parent->listStyle;
       
  1034     // makes no sense to inherit that property, a named anchor is a single point
       
  1035     // in the document, which is set by the DocumentFragment
       
  1036     charFormat.clearProperty(QTextFormat::AnchorName);
       
  1037     wsm = parent->wsm;
       
  1038 
       
  1039     // initialize remaining properties
       
  1040     margin[QTextHtmlParser::MarginLeft] = 0;
       
  1041     margin[QTextHtmlParser::MarginRight] = 0;
       
  1042     margin[QTextHtmlParser::MarginTop] = 0;
       
  1043     margin[QTextHtmlParser::MarginBottom] = 0;
       
  1044     cssFloat = QTextFrameFormat::InFlow;
       
  1045 
       
  1046     for (int i = 0; i < 4; ++i)
       
  1047         padding[i] = -1;
       
  1048 
       
  1049     // set element specific attributes
       
  1050     switch (id) {
       
  1051         case Html_a:
       
  1052             charFormat.setAnchor(true);
       
  1053             for (int i = 0; i < attributes.count(); i += 2) {
       
  1054                 const QString key = attributes.at(i);
       
  1055                 if (key.compare(QLatin1String("href"), Qt::CaseInsensitive) == 0
       
  1056                     && !attributes.at(i + 1).isEmpty()) {
       
  1057                     hasHref = true;
       
  1058                     charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
       
  1059                     charFormat.setForeground(QApplication::palette().link());
       
  1060                 }
       
  1061             }
       
  1062 
       
  1063             break;
       
  1064         case Html_em:
       
  1065         case Html_i:
       
  1066         case Html_cite:
       
  1067         case Html_address:
       
  1068         case Html_var:
       
  1069         case Html_dfn:
       
  1070             charFormat.setFontItalic(true);
       
  1071             break;
       
  1072         case Html_big:
       
  1073             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
       
  1074             break;
       
  1075         case Html_small:
       
  1076             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
       
  1077             break;
       
  1078         case Html_strong:
       
  1079         case Html_b:
       
  1080             charFormat.setFontWeight(QFont::Bold);
       
  1081             break;
       
  1082         case Html_h1:
       
  1083             charFormat.setFontWeight(QFont::Bold);
       
  1084             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(3));
       
  1085             margin[QTextHtmlParser::MarginTop] = 18;
       
  1086             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1087             break;
       
  1088         case Html_h2:
       
  1089             charFormat.setFontWeight(QFont::Bold);
       
  1090             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(2));
       
  1091             margin[QTextHtmlParser::MarginTop] = 16;
       
  1092             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1093             break;
       
  1094         case Html_h3:
       
  1095             charFormat.setFontWeight(QFont::Bold);
       
  1096             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(1));
       
  1097             margin[QTextHtmlParser::MarginTop] = 14;
       
  1098             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1099             break;
       
  1100         case Html_h4:
       
  1101             charFormat.setFontWeight(QFont::Bold);
       
  1102             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(0));
       
  1103             margin[QTextHtmlParser::MarginTop] = 12;
       
  1104             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1105             break;
       
  1106         case Html_h5:
       
  1107             charFormat.setFontWeight(QFont::Bold);
       
  1108             charFormat.setProperty(QTextFormat::FontSizeAdjustment, int(-1));
       
  1109             margin[QTextHtmlParser::MarginTop] = 12;
       
  1110             margin[QTextHtmlParser::MarginBottom] = 4;
       
  1111             break;
       
  1112         case Html_p:
       
  1113             margin[QTextHtmlParser::MarginTop] = 12;
       
  1114             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1115             break;
       
  1116         case Html_center:
       
  1117             blockFormat.setAlignment(Qt::AlignCenter);
       
  1118             break;
       
  1119         case Html_ul:
       
  1120             listStyle = QTextListFormat::ListDisc;
       
  1121             // nested lists don't have margins, except for the toplevel one
       
  1122             if (!isNestedList(parser)) {
       
  1123                 margin[QTextHtmlParser::MarginTop] = 12;
       
  1124                 margin[QTextHtmlParser::MarginBottom] = 12;
       
  1125             }
       
  1126             // no left margin as we use indenting instead
       
  1127             break;
       
  1128         case Html_ol:
       
  1129             listStyle = QTextListFormat::ListDecimal;
       
  1130             // nested lists don't have margins, except for the toplevel one
       
  1131             if (!isNestedList(parser)) {
       
  1132                 margin[QTextHtmlParser::MarginTop] = 12;
       
  1133                 margin[QTextHtmlParser::MarginBottom] = 12;
       
  1134             }
       
  1135             // no left margin as we use indenting instead
       
  1136             break;
       
  1137         case Html_code:
       
  1138         case Html_tt:
       
  1139         case Html_kbd:
       
  1140         case Html_samp:
       
  1141             charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
       
  1142             // <tt> uses a fixed font, so set the property
       
  1143             charFormat.setFontFixedPitch(true);
       
  1144             break;
       
  1145         case Html_br:
       
  1146             text = QChar(QChar::LineSeparator);
       
  1147             wsm = QTextHtmlParserNode::WhiteSpacePre;
       
  1148             break;
       
  1149         // ##### sub / sup
       
  1150         case Html_pre:
       
  1151             charFormat.setFontFamily(QString::fromLatin1("Courier New,courier"));
       
  1152             wsm = WhiteSpacePre;
       
  1153             margin[QTextHtmlParser::MarginTop] = 12;
       
  1154             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1155             // <pre> uses a fixed font
       
  1156             charFormat.setFontFixedPitch(true);
       
  1157             break;
       
  1158         case Html_blockquote:
       
  1159             margin[QTextHtmlParser::MarginTop] = 12;
       
  1160             margin[QTextHtmlParser::MarginBottom] = 12;
       
  1161             margin[QTextHtmlParser::MarginLeft] = 40;
       
  1162             margin[QTextHtmlParser::MarginRight] = 40;
       
  1163             break;
       
  1164         case Html_dl:
       
  1165             margin[QTextHtmlParser::MarginTop] = 8;
       
  1166             margin[QTextHtmlParser::MarginBottom] = 8;
       
  1167             break;
       
  1168         case Html_dd:
       
  1169             margin[QTextHtmlParser::MarginLeft] = 30;
       
  1170             break;
       
  1171         case Html_u:
       
  1172             charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline);
       
  1173             break;
       
  1174         case Html_s:
       
  1175             charFormat.setFontStrikeOut(true);
       
  1176             break;
       
  1177         case Html_nobr:
       
  1178             wsm = WhiteSpaceNoWrap;
       
  1179             break;
       
  1180         case Html_th:
       
  1181             charFormat.setFontWeight(QFont::Bold);
       
  1182             blockFormat.setAlignment(Qt::AlignCenter);
       
  1183             break;
       
  1184         case Html_td:
       
  1185             blockFormat.setAlignment(Qt::AlignLeft);
       
  1186             break;
       
  1187         case Html_sub:
       
  1188             charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript);
       
  1189             break;
       
  1190         case Html_sup:
       
  1191             charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
       
  1192             break;
       
  1193         default: break;
       
  1194     }
       
  1195 }
       
  1196 
       
  1197 #ifndef QT_NO_CSSPARSER
       
  1198 void QTextHtmlParserNode::setListStyle(const QVector<QCss::Value> &cssValues)
       
  1199 {
       
  1200     for (int i = 0; i < cssValues.count(); ++i) {
       
  1201         if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
       
  1202             switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
       
  1203                 case QCss::Value_Disc: hasOwnListStyle = true; listStyle = QTextListFormat::ListDisc; break;
       
  1204                 case QCss::Value_Square: hasOwnListStyle = true; listStyle = QTextListFormat::ListSquare; break;
       
  1205                 case QCss::Value_Circle: hasOwnListStyle = true; listStyle = QTextListFormat::ListCircle; break;
       
  1206                 case QCss::Value_Decimal: hasOwnListStyle = true; listStyle = QTextListFormat::ListDecimal; break;
       
  1207                 case QCss::Value_LowerAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListLowerAlpha; break;
       
  1208                 case QCss::Value_UpperAlpha: hasOwnListStyle = true; listStyle = QTextListFormat::ListUpperAlpha; break;
       
  1209                 case QCss::Value_LowerRoman: hasOwnListStyle = true; listStyle = QTextListFormat::ListLowerRoman; break;
       
  1210                 case QCss::Value_UpperRoman: hasOwnListStyle = true; listStyle = QTextListFormat::ListUpperRoman; break;
       
  1211                 default: break;
       
  1212             }
       
  1213         }
       
  1214     }
       
  1215     // allow individual list items to override the style
       
  1216     if (id == Html_li && hasOwnListStyle)
       
  1217         blockFormat.setProperty(QTextFormat::ListStyle, listStyle);
       
  1218 }
       
  1219 
       
  1220 void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> &declarations, const QTextDocument *resourceProvider)
       
  1221 {
       
  1222     QCss::ValueExtractor extractor(declarations);
       
  1223     extractor.extractBox(margin, padding);
       
  1224 
       
  1225     for (int i = 0; i < declarations.count(); ++i) {
       
  1226         const QCss::Declaration &decl = declarations.at(i);
       
  1227         if (decl.d->values.isEmpty()) continue;
       
  1228 
       
  1229         QCss::KnownValue identifier = QCss::UnknownValue;
       
  1230         if (decl.d->values.first().type == QCss::Value::KnownIdentifier)
       
  1231             identifier = static_cast<QCss::KnownValue>(decl.d->values.first().variant.toInt());
       
  1232 
       
  1233         switch (decl.d->propertyId) {
       
  1234         case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
       
  1235         case QCss::BorderStyles:
       
  1236             if (decl.styleValue() != QCss::BorderStyle_Unknown && decl.styleValue() != QCss::BorderStyle_Native)
       
  1237                 borderStyle = static_cast<QTextFrameFormat::BorderStyle>(decl.styleValue() - 1);
       
  1238             break;
       
  1239         case QCss::BorderWidth:
       
  1240             tableBorder = extractor.lengthValue(decl);
       
  1241             break;
       
  1242         case QCss::Color: charFormat.setForeground(decl.colorValue()); break;
       
  1243         case QCss::Float:
       
  1244             cssFloat = QTextFrameFormat::InFlow;
       
  1245             switch (identifier) {
       
  1246             case QCss::Value_Left: cssFloat = QTextFrameFormat::FloatLeft; break;
       
  1247             case QCss::Value_Right: cssFloat = QTextFrameFormat::FloatRight; break;
       
  1248             default: break;
       
  1249             }
       
  1250             break;
       
  1251         case QCss::QtBlockIndent:
       
  1252             blockFormat.setIndent(decl.d->values.first().variant.toInt());
       
  1253             break;
       
  1254         case QCss::TextIndent: {
       
  1255             qreal indent = 0;
       
  1256             if (decl.realValue(&indent, "px"))
       
  1257                 blockFormat.setTextIndent(indent);
       
  1258             break; }
       
  1259         case QCss::QtListIndent:
       
  1260             if (decl.intValue(&cssListIndent))
       
  1261                 hasCssListIndent = true;
       
  1262             break;
       
  1263         case QCss::QtParagraphType:
       
  1264             if (decl.d->values.first().variant.toString().compare(QLatin1String("empty"), Qt::CaseInsensitive) == 0)
       
  1265                 isEmptyParagraph = true;
       
  1266             break;
       
  1267         case QCss::QtTableType:
       
  1268             if (decl.d->values.first().variant.toString().compare(QLatin1String("frame"), Qt::CaseInsensitive) == 0)
       
  1269                 isTextFrame = true;
       
  1270             else if (decl.d->values.first().variant.toString().compare(QLatin1String("root"), Qt::CaseInsensitive) == 0) {
       
  1271                 isTextFrame = true;
       
  1272                 isRootFrame = true;
       
  1273             }
       
  1274             break;
       
  1275         case QCss::QtUserState:
       
  1276             userState = decl.d->values.first().variant.toInt();
       
  1277             break;
       
  1278         case QCss::Whitespace:
       
  1279             switch (identifier) {
       
  1280             case QCss::Value_Normal: wsm = QTextHtmlParserNode::WhiteSpaceNormal; break;
       
  1281             case QCss::Value_Pre: wsm = QTextHtmlParserNode::WhiteSpacePre; break;
       
  1282             case QCss::Value_NoWrap: wsm = QTextHtmlParserNode::WhiteSpaceNoWrap; break;
       
  1283             case QCss::Value_PreWrap: wsm = QTextHtmlParserNode::WhiteSpacePreWrap; break;
       
  1284             default: break;
       
  1285             }
       
  1286             break;
       
  1287         case QCss::VerticalAlignment:
       
  1288             switch (identifier) {
       
  1289             case QCss::Value_Sub: charFormat.setVerticalAlignment(QTextCharFormat::AlignSubScript); break;
       
  1290             case QCss::Value_Super: charFormat.setVerticalAlignment(QTextCharFormat::AlignSuperScript); break;
       
  1291             case QCss::Value_Middle: charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle); break;
       
  1292             case QCss::Value_Top: charFormat.setVerticalAlignment(QTextCharFormat::AlignTop); break;
       
  1293             case QCss::Value_Bottom: charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom); break;
       
  1294             default: charFormat.setVerticalAlignment(QTextCharFormat::AlignNormal); break;
       
  1295             }
       
  1296             break;
       
  1297         case QCss::PageBreakBefore:
       
  1298             switch (identifier) {
       
  1299             case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysBefore); break;
       
  1300             case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysBefore); break;
       
  1301             default: break;
       
  1302             }
       
  1303             break;
       
  1304         case QCss::PageBreakAfter:
       
  1305             switch (identifier) {
       
  1306             case QCss::Value_Always: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() | QTextFormat::PageBreak_AlwaysAfter); break;
       
  1307             case QCss::Value_Auto: blockFormat.setPageBreakPolicy(blockFormat.pageBreakPolicy() & ~QTextFormat::PageBreak_AlwaysAfter); break;
       
  1308             default: break;
       
  1309             }
       
  1310             break;
       
  1311         case QCss::TextUnderlineStyle:
       
  1312             switch (identifier) {
       
  1313             case QCss::Value_None: charFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); break;
       
  1314             case QCss::Value_Solid: charFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); break;
       
  1315             case QCss::Value_Dashed: charFormat.setUnderlineStyle(QTextCharFormat::DashUnderline); break;
       
  1316             case QCss::Value_Dotted: charFormat.setUnderlineStyle(QTextCharFormat::DotLine); break;
       
  1317             case QCss::Value_DotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotLine); break;
       
  1318             case QCss::Value_DotDotDash: charFormat.setUnderlineStyle(QTextCharFormat::DashDotDotLine); break;
       
  1319             case QCss::Value_Wave: charFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); break;
       
  1320             default: break;
       
  1321             }
       
  1322             break;
       
  1323         case QCss::ListStyleType:
       
  1324         case QCss::ListStyle:
       
  1325             setListStyle(decl.d->values);
       
  1326             break;
       
  1327         default: break;
       
  1328         }
       
  1329     }
       
  1330 
       
  1331     QFont f;
       
  1332     int adjustment = -255;
       
  1333     extractor.extractFont(&f, &adjustment);
       
  1334     if (f.resolve() & QFont::SizeResolved) {
       
  1335         if (f.pointSize() > 0) {
       
  1336             charFormat.setFontPointSize(f.pointSize());
       
  1337         } else if (f.pixelSize() > 0) {
       
  1338             charFormat.setProperty(QTextFormat::FontPixelSize, f.pixelSize());
       
  1339         }
       
  1340     }
       
  1341     if (f.resolve() & QFont::StyleResolved)
       
  1342         charFormat.setFontItalic(f.style() != QFont::StyleNormal);
       
  1343 
       
  1344     if (f.resolve() & QFont::WeightResolved)
       
  1345         charFormat.setFontWeight(f.weight());
       
  1346 
       
  1347     if (f.resolve() & QFont::FamilyResolved)
       
  1348         charFormat.setFontFamily(f.family());
       
  1349 
       
  1350     if (f.resolve() & QFont::UnderlineResolved)
       
  1351         charFormat.setUnderlineStyle(f.underline() ? QTextCharFormat::SingleUnderline : QTextCharFormat::NoUnderline);
       
  1352 
       
  1353     if (f.resolve() & QFont::OverlineResolved)
       
  1354         charFormat.setFontOverline(f.overline());
       
  1355 
       
  1356     if (f.resolve() & QFont::StrikeOutResolved)
       
  1357         charFormat.setFontStrikeOut(f.strikeOut());
       
  1358 
       
  1359     if (f.resolve() & QFont::CapitalizationResolved)
       
  1360         charFormat.setFontCapitalization(f.capitalization());
       
  1361 
       
  1362     if (adjustment >= -1)
       
  1363         charFormat.setProperty(QTextFormat::FontSizeAdjustment, adjustment);
       
  1364 
       
  1365     {
       
  1366         Qt::Alignment ignoredAlignment;
       
  1367         QCss::Repeat ignoredRepeat;
       
  1368         QString bgImage;
       
  1369         QBrush bgBrush;
       
  1370         QCss::Origin ignoredOrigin, ignoredClip;
       
  1371         QCss::Attachment ignoredAttachment;
       
  1372         extractor.extractBackground(&bgBrush, &bgImage, &ignoredRepeat, &ignoredAlignment,
       
  1373                                     &ignoredOrigin, &ignoredAttachment, &ignoredClip);
       
  1374 
       
  1375         if (!bgImage.isEmpty() && resourceProvider) {
       
  1376             applyBackgroundImage(bgImage, resourceProvider);
       
  1377         } else if (bgBrush.style() != Qt::NoBrush) {
       
  1378             charFormat.setBackground(bgBrush);
       
  1379         }
       
  1380     }
       
  1381 }
       
  1382 
       
  1383 #endif // QT_NO_CSSPARSER
       
  1384 
       
  1385 void QTextHtmlParserNode::applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider)
       
  1386 {
       
  1387     if (!url.isEmpty() && resourceProvider) {
       
  1388         QVariant val = resourceProvider->resource(QTextDocument::ImageResource, url);
       
  1389 
       
  1390         if (qApp->thread() != QThread::currentThread()) {
       
  1391             // must use images in non-GUI threads
       
  1392             if (val.type() == QVariant::Image) {
       
  1393                 QImage image = qvariant_cast<QImage>(val);
       
  1394                 charFormat.setBackground(image);
       
  1395             } else if (val.type() == QVariant::ByteArray) {
       
  1396                 QImage image;
       
  1397                 if (image.loadFromData(val.toByteArray())) {
       
  1398                     charFormat.setBackground(image);
       
  1399                 }
       
  1400             }
       
  1401         } else {
       
  1402             if (val.type() == QVariant::Image || val.type() == QVariant::Pixmap) {
       
  1403                 charFormat.setBackground(qvariant_cast<QPixmap>(val));
       
  1404             } else if (val.type() == QVariant::ByteArray) {
       
  1405                 QPixmap pm;
       
  1406                 if (pm.loadFromData(val.toByteArray())) {
       
  1407                     charFormat.setBackground(pm);
       
  1408                 }
       
  1409             }
       
  1410         }
       
  1411     }
       
  1412     if (!url.isEmpty())
       
  1413         charFormat.setProperty(QTextFormat::BackgroundImageUrl, url);
       
  1414 }
       
  1415 
       
  1416 bool QTextHtmlParserNode::hasOnlyWhitespace() const
       
  1417 {
       
  1418     for (int i = 0; i < text.count(); ++i)
       
  1419         if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
       
  1420             return false;
       
  1421     return true;
       
  1422 }
       
  1423 
       
  1424 static bool setIntAttribute(int *destination, const QString &value)
       
  1425 {
       
  1426     bool ok = false;
       
  1427     int val = value.toInt(&ok);
       
  1428     if (ok)
       
  1429         *destination = val;
       
  1430 
       
  1431     return ok;
       
  1432 }
       
  1433 
       
  1434 static bool setFloatAttribute(qreal *destination, const QString &value)
       
  1435 {
       
  1436     bool ok = false;
       
  1437     qreal val = value.toDouble(&ok);
       
  1438     if (ok)
       
  1439         *destination = val;
       
  1440 
       
  1441     return ok;
       
  1442 }
       
  1443 
       
  1444 static void setWidthAttribute(QTextLength *width, QString value)
       
  1445 {
       
  1446     bool ok = false;
       
  1447     qreal realVal = value.toDouble(&ok);
       
  1448     if (ok) {
       
  1449         *width = QTextLength(QTextLength::FixedLength, realVal);
       
  1450     } else {
       
  1451         value = value.trimmed();
       
  1452         if (!value.isEmpty() && value.endsWith(QLatin1Char('%'))) {
       
  1453             value.chop(1);
       
  1454             realVal = value.toDouble(&ok);
       
  1455             if (ok)
       
  1456                 *width = QTextLength(QTextLength::PercentageLength, realVal);
       
  1457         }
       
  1458     }
       
  1459 }
       
  1460 
       
  1461 #ifndef QT_NO_CSSPARSER
       
  1462 void QTextHtmlParserNode::parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider)
       
  1463 {
       
  1464     QString css = value;
       
  1465     css.prepend(QLatin1String("* {"));
       
  1466     css.append(QLatin1Char('}'));
       
  1467     QCss::Parser parser(css);
       
  1468     QCss::StyleSheet sheet;
       
  1469     parser.parse(&sheet, Qt::CaseInsensitive);
       
  1470     if (sheet.styleRules.count() != 1) return;
       
  1471     applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
       
  1472 }
       
  1473 #endif
       
  1474 
       
  1475 QStringList QTextHtmlParser::parseAttributes()
       
  1476 {
       
  1477     QStringList attrs;
       
  1478 
       
  1479     while (pos < len) {
       
  1480         eatSpace();
       
  1481         if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
       
  1482             break;
       
  1483         QString key = parseWord().toLower();
       
  1484         QString value = QLatin1String("1");
       
  1485         if (key.size() == 0)
       
  1486             break;
       
  1487         eatSpace();
       
  1488         if (hasPrefix(QLatin1Char('='))){
       
  1489             pos++;
       
  1490             eatSpace();
       
  1491             value = parseWord();
       
  1492         }
       
  1493         if (value.size() == 0)
       
  1494             continue;
       
  1495         attrs << key << value;
       
  1496     }
       
  1497 
       
  1498     return attrs;
       
  1499 }
       
  1500 
       
  1501 void QTextHtmlParser::applyAttributes(const QStringList &attributes)
       
  1502 {
       
  1503     // local state variable for qt3 textedit mode
       
  1504     bool seenQt3Richtext = false;
       
  1505     QString linkHref;
       
  1506     QString linkType;
       
  1507 
       
  1508     if (attributes.count() % 2 == 1)
       
  1509         return;
       
  1510 
       
  1511     QTextHtmlParserNode *node = &nodes.last();
       
  1512 
       
  1513     for (int i = 0; i < attributes.count(); i += 2) {
       
  1514         QString key = attributes.at(i);
       
  1515         QString value = attributes.at(i + 1);
       
  1516 
       
  1517         switch (node->id) {
       
  1518             case Html_font:
       
  1519                 // the infamous font tag
       
  1520                 if (key == QLatin1String("size") && value.size()) {
       
  1521                     int n = value.toInt();
       
  1522                     if (value.at(0) != QLatin1Char('+') && value.at(0) != QLatin1Char('-'))
       
  1523                         n -= 3;
       
  1524                     node->charFormat.setProperty(QTextFormat::FontSizeAdjustment, n);
       
  1525                 } else if (key == QLatin1String("face")) {
       
  1526                     node->charFormat.setFontFamily(value);
       
  1527                 } else if (key == QLatin1String("color")) {
       
  1528                     QColor c; c.setNamedColor(value);
       
  1529                     if (!c.isValid())
       
  1530                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
       
  1531                     node->charFormat.setForeground(c);
       
  1532                 }
       
  1533                 break;
       
  1534             case Html_ol:
       
  1535             case Html_ul:
       
  1536                 if (key == QLatin1String("type")) {
       
  1537                     node->hasOwnListStyle = true;
       
  1538                     if (value == QLatin1String("1")) {
       
  1539                         node->listStyle = QTextListFormat::ListDecimal;
       
  1540                     } else if (value == QLatin1String("a")) {
       
  1541                         node->listStyle = QTextListFormat::ListLowerAlpha;
       
  1542                     } else if (value == QLatin1String("A")) {
       
  1543                         node->listStyle = QTextListFormat::ListUpperAlpha;
       
  1544                     } else if (value == QLatin1String("i")) {
       
  1545                         node->listStyle = QTextListFormat::ListLowerRoman;
       
  1546                     } else if (value == QLatin1String("I")) {
       
  1547                         node->listStyle = QTextListFormat::ListUpperRoman;
       
  1548                     } else {
       
  1549                         value = value.toLower();
       
  1550                         if (value == QLatin1String("square"))
       
  1551                             node->listStyle = QTextListFormat::ListSquare;
       
  1552                         else if (value == QLatin1String("disc"))
       
  1553                             node->listStyle = QTextListFormat::ListDisc;
       
  1554                         else if (value == QLatin1String("circle"))
       
  1555                             node->listStyle = QTextListFormat::ListCircle;
       
  1556                     }
       
  1557                 }
       
  1558                 break;
       
  1559             case Html_a:
       
  1560                 if (key == QLatin1String("href"))
       
  1561                     node->charFormat.setAnchorHref(value);
       
  1562                 else if (key == QLatin1String("name"))
       
  1563                     node->charFormat.setAnchorName(value);
       
  1564                 break;
       
  1565             case Html_img:
       
  1566                 if (key == QLatin1String("src") || key == QLatin1String("source")) {
       
  1567                     node->imageName = value;
       
  1568                 } else if (key == QLatin1String("width")) {
       
  1569                     node->imageWidth = -2; // register that there is a value for it.
       
  1570                     setFloatAttribute(&node->imageWidth, value);
       
  1571                 } else if (key == QLatin1String("height")) {
       
  1572                     node->imageHeight = -2; // register that there is a value for it.
       
  1573                     setFloatAttribute(&node->imageHeight, value);
       
  1574                 }
       
  1575                 break;
       
  1576             case Html_tr:
       
  1577             case Html_body:
       
  1578                 if (key == QLatin1String("bgcolor")) {
       
  1579                     QColor c; c.setNamedColor(value);
       
  1580                     if (!c.isValid())
       
  1581                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
       
  1582                     node->charFormat.setBackground(c);
       
  1583                 } else if (key == QLatin1String("background")) {
       
  1584                     node->applyBackgroundImage(value, resourceProvider);
       
  1585                 }
       
  1586                 break;
       
  1587             case Html_th:
       
  1588             case Html_td:
       
  1589                 if (key == QLatin1String("width")) {
       
  1590                     setWidthAttribute(&node->width, value);
       
  1591                 } else if (key == QLatin1String("bgcolor")) {
       
  1592                     QColor c; c.setNamedColor(value);
       
  1593                     if (!c.isValid())
       
  1594                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
       
  1595                     node->charFormat.setBackground(c);
       
  1596                 } else if (key == QLatin1String("background")) {
       
  1597                     node->applyBackgroundImage(value, resourceProvider);
       
  1598                 } else if (key == QLatin1String("rowspan")) {
       
  1599                     if (setIntAttribute(&node->tableCellRowSpan, value))
       
  1600                         node->tableCellRowSpan = qMax(1, node->tableCellRowSpan);
       
  1601                 } else if (key == QLatin1String("colspan")) {
       
  1602                     if (setIntAttribute(&node->tableCellColSpan, value))
       
  1603                         node->tableCellColSpan = qMax(1, node->tableCellColSpan);
       
  1604                 }
       
  1605                 break;
       
  1606             case Html_table:
       
  1607                 if (key == QLatin1String("border")) {
       
  1608                     setFloatAttribute(&node->tableBorder, value);
       
  1609                 } else if (key == QLatin1String("bgcolor")) {
       
  1610                     QColor c; c.setNamedColor(value);
       
  1611                     if (!c.isValid())
       
  1612                         qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
       
  1613                     node->charFormat.setBackground(c);
       
  1614                 } else if (key == QLatin1String("background")) {
       
  1615                     node->applyBackgroundImage(value, resourceProvider);
       
  1616                 } else if (key == QLatin1String("cellspacing")) {
       
  1617                     setFloatAttribute(&node->tableCellSpacing, value);
       
  1618                 } else if (key == QLatin1String("cellpadding")) {
       
  1619                     setFloatAttribute(&node->tableCellPadding, value);
       
  1620                 } else if (key == QLatin1String("width")) {
       
  1621                     setWidthAttribute(&node->width, value);
       
  1622                 } else if (key == QLatin1String("height")) {
       
  1623                     setWidthAttribute(&node->height, value);
       
  1624                 }
       
  1625                 break;
       
  1626             case Html_meta:
       
  1627                 if (key == QLatin1String("name")
       
  1628                     && value == QLatin1String("qrichtext")) {
       
  1629                     seenQt3Richtext = true;
       
  1630                 }
       
  1631 
       
  1632                 if (key == QLatin1String("content")
       
  1633                     && value == QLatin1String("1")
       
  1634                     && seenQt3Richtext) {
       
  1635 
       
  1636                     textEditMode = true;
       
  1637                 }
       
  1638                 break;
       
  1639             case Html_hr:
       
  1640                 if (key == QLatin1String("width"))
       
  1641                     setWidthAttribute(&node->width, value);
       
  1642                 break;
       
  1643             case Html_link:
       
  1644                 if (key == QLatin1String("href"))
       
  1645                     linkHref = value;
       
  1646                 else if (key == QLatin1String("type"))
       
  1647                     linkType = value;
       
  1648                 break;
       
  1649             default:
       
  1650                 break;
       
  1651         }
       
  1652 
       
  1653         if (key == QLatin1String("style")) {
       
  1654 #ifndef QT_NO_CSSPARSER
       
  1655             node->parseStyleAttribute(value, resourceProvider);
       
  1656 #endif
       
  1657         } else if (key == QLatin1String("align")) {
       
  1658             value = value.toLower();
       
  1659             bool alignmentSet = true;
       
  1660 
       
  1661             if (value == QLatin1String("left"))
       
  1662                 node->blockFormat.setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);
       
  1663             else if (value == QLatin1String("right"))
       
  1664                 node->blockFormat.setAlignment(Qt::AlignRight|Qt::AlignAbsolute);
       
  1665             else if (value == QLatin1String("center"))
       
  1666                 node->blockFormat.setAlignment(Qt::AlignHCenter);
       
  1667             else if (value == QLatin1String("justify"))
       
  1668                 node->blockFormat.setAlignment(Qt::AlignJustify);
       
  1669             else
       
  1670                 alignmentSet = false;
       
  1671 
       
  1672             if (node->id == Html_img) {
       
  1673                 // HTML4 compat
       
  1674                 if (alignmentSet) {
       
  1675                     if (node->blockFormat.alignment() & Qt::AlignLeft)
       
  1676                         node->cssFloat = QTextFrameFormat::FloatLeft;
       
  1677                     else if (node->blockFormat.alignment() & Qt::AlignRight)
       
  1678                         node->cssFloat = QTextFrameFormat::FloatRight;
       
  1679                 } else if (value == QLatin1String("middle")) {
       
  1680                     node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
       
  1681                 } else if (value == QLatin1String("top")) {
       
  1682                     node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
       
  1683                 }
       
  1684             }
       
  1685         } else if (key == QLatin1String("valign")) {
       
  1686             value = value.toLower();
       
  1687             if (value == QLatin1String("top"))
       
  1688                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignTop);
       
  1689             else if (value == QLatin1String("middle"))
       
  1690                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignMiddle);
       
  1691             else if (value == QLatin1String("bottom"))
       
  1692                 node->charFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);
       
  1693         } else if (key == QLatin1String("dir")) {
       
  1694             value = value.toLower();
       
  1695             if (value == QLatin1String("ltr"))
       
  1696                 node->blockFormat.setLayoutDirection(Qt::LeftToRight);
       
  1697             else if (value == QLatin1String("rtl"))
       
  1698                 node->blockFormat.setLayoutDirection(Qt::RightToLeft);
       
  1699         } else if (key == QLatin1String("title")) {
       
  1700             node->charFormat.setToolTip(value);
       
  1701         } else if (key == QLatin1String("id")) {
       
  1702             node->charFormat.setAnchor(true);
       
  1703             node->charFormat.setAnchorName(value);
       
  1704         }
       
  1705     }
       
  1706 
       
  1707 #ifndef QT_NO_CSSPARSER
       
  1708     if (resourceProvider && !linkHref.isEmpty() && linkType == QLatin1String("text/css"))
       
  1709         importStyleSheet(linkHref);
       
  1710 #endif
       
  1711 }
       
  1712 
       
  1713 #ifndef QT_NO_CSSPARSER
       
  1714 class QTextHtmlStyleSelector : public QCss::StyleSelector
       
  1715 {
       
  1716 public:
       
  1717     inline QTextHtmlStyleSelector(const QTextHtmlParser *parser)
       
  1718         : parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
       
  1719 
       
  1720     virtual QStringList nodeNames(NodePtr node) const;
       
  1721     virtual QString attribute(NodePtr node, const QString &name) const;
       
  1722     virtual bool hasAttributes(NodePtr node) const;
       
  1723     virtual bool isNullNode(NodePtr node) const;
       
  1724     virtual NodePtr parentNode(NodePtr node) const;
       
  1725     virtual NodePtr previousSiblingNode(NodePtr node) const;
       
  1726     virtual NodePtr duplicateNode(NodePtr node) const;
       
  1727     virtual void freeNode(NodePtr node) const;
       
  1728 
       
  1729 private:
       
  1730     const QTextHtmlParser *parser;
       
  1731 };
       
  1732 
       
  1733 QStringList QTextHtmlStyleSelector::nodeNames(NodePtr node) const
       
  1734 {
       
  1735     return QStringList(parser->at(node.id).tag.toLower());
       
  1736 }
       
  1737 
       
  1738 #endif // QT_NO_CSSPARSER
       
  1739 
       
  1740 static inline int findAttribute(const QStringList &attributes, const QString &name)
       
  1741 {
       
  1742     int idx = -1;
       
  1743     do {
       
  1744         idx = attributes.indexOf(name, idx + 1);
       
  1745     } while (idx != -1 && (idx % 2 == 1));
       
  1746     return idx;
       
  1747 }
       
  1748 
       
  1749 #ifndef QT_NO_CSSPARSER
       
  1750 
       
  1751 QString QTextHtmlStyleSelector::attribute(NodePtr node, const QString &name) const
       
  1752 {
       
  1753     const QStringList &attributes = parser->at(node.id).attributes;
       
  1754     const int idx = findAttribute(attributes, name);
       
  1755     if (idx == -1)
       
  1756         return QString();
       
  1757     return attributes.at(idx + 1);
       
  1758 }
       
  1759 
       
  1760 bool QTextHtmlStyleSelector::hasAttributes(NodePtr node) const
       
  1761 {
       
  1762    const QStringList &attributes = parser->at(node.id).attributes;
       
  1763    return !attributes.isEmpty();
       
  1764 }
       
  1765 
       
  1766 bool QTextHtmlStyleSelector::isNullNode(NodePtr node) const
       
  1767 {
       
  1768     return node.id == 0;
       
  1769 }
       
  1770 
       
  1771 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::parentNode(NodePtr node) const
       
  1772 {
       
  1773     NodePtr parent;
       
  1774     parent.id = 0;
       
  1775     if (node.id) {
       
  1776         parent.id = parser->at(node.id).parent;
       
  1777     }
       
  1778     return parent;
       
  1779 }
       
  1780 
       
  1781 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::duplicateNode(NodePtr node) const
       
  1782 {
       
  1783     return node;
       
  1784 }
       
  1785 
       
  1786 QCss::StyleSelector::NodePtr QTextHtmlStyleSelector::previousSiblingNode(NodePtr node) const
       
  1787 {
       
  1788     NodePtr sibling;
       
  1789     sibling.id = 0;
       
  1790     if (!node.id)
       
  1791         return sibling;
       
  1792     int parent = parser->at(node.id).parent;
       
  1793     if (!parent)
       
  1794         return sibling;
       
  1795     const int childIdx = parser->at(parent).children.indexOf(node.id);
       
  1796     if (childIdx <= 0)
       
  1797         return sibling;
       
  1798     sibling.id = parser->at(parent).children.at(childIdx - 1);
       
  1799     return sibling;
       
  1800 }
       
  1801 
       
  1802 void QTextHtmlStyleSelector::freeNode(NodePtr) const
       
  1803 {
       
  1804 }
       
  1805 
       
  1806 void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
       
  1807 {
       
  1808     for (int i = 0; i < sheet.importRules.count(); ++i) {
       
  1809         const QCss::ImportRule &rule = sheet.importRules.at(i);
       
  1810         if (rule.media.isEmpty()
       
  1811             || rule.media.contains(QLatin1String("screen"), Qt::CaseInsensitive))
       
  1812             importStyleSheet(rule.href);
       
  1813     }
       
  1814 }
       
  1815 
       
  1816 void QTextHtmlParser::importStyleSheet(const QString &href)
       
  1817 {
       
  1818     if (!resourceProvider)
       
  1819         return;
       
  1820     for (int i = 0; i < externalStyleSheets.count(); ++i)
       
  1821         if (externalStyleSheets.at(i).url == href)
       
  1822             return;
       
  1823 
       
  1824     QVariant res = resourceProvider->resource(QTextDocument::StyleSheetResource, href);
       
  1825     QString css;
       
  1826     if (res.type() == QVariant::String) {
       
  1827         css = res.toString();
       
  1828     } else if (res.type() == QVariant::ByteArray) {
       
  1829         // #### detect @charset
       
  1830         css = QString::fromUtf8(res.toByteArray());
       
  1831     }
       
  1832     if (!css.isEmpty()) {
       
  1833         QCss::Parser parser(css);
       
  1834         QCss::StyleSheet sheet;
       
  1835         parser.parse(&sheet, Qt::CaseInsensitive);
       
  1836         externalStyleSheets.append(ExternalStyleSheet(href, sheet));
       
  1837         resolveStyleSheetImports(sheet);
       
  1838     }
       
  1839 }
       
  1840 
       
  1841 QVector<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
       
  1842 {
       
  1843     QVector<QCss::Declaration> decls;
       
  1844 
       
  1845     QTextHtmlStyleSelector selector(this);
       
  1846 
       
  1847     int idx = 0;
       
  1848     selector.styleSheets.resize((resourceProvider ? 1 : 0)
       
  1849                                 + externalStyleSheets.count()
       
  1850                                 + inlineStyleSheets.count());
       
  1851     if (resourceProvider)
       
  1852         selector.styleSheets[idx++] = resourceProvider->docHandle()->parsedDefaultStyleSheet;
       
  1853 
       
  1854     for (int i = 0; i < externalStyleSheets.count(); ++i, ++idx)
       
  1855         selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
       
  1856 
       
  1857     for (int i = 0; i < inlineStyleSheets.count(); ++i, ++idx)
       
  1858         selector.styleSheets[idx] = inlineStyleSheets.at(i);
       
  1859 
       
  1860     selector.medium = QLatin1String("screen");
       
  1861 
       
  1862     QCss::StyleSelector::NodePtr n;
       
  1863     n.id = node;
       
  1864 
       
  1865     const char *extraPseudo = 0;
       
  1866     if (nodes.at(node).id == Html_a && nodes.at(node).hasHref)
       
  1867         extraPseudo = "link";
       
  1868     decls = selector.declarationsForNode(n, extraPseudo);
       
  1869 
       
  1870     return decls;
       
  1871 }
       
  1872 
       
  1873 bool QTextHtmlParser::nodeIsChildOf(int i, QTextHTMLElements id) const
       
  1874 {
       
  1875     while (i) {
       
  1876         if (at(i).id == id)
       
  1877             return true;
       
  1878         i = at(i).parent;
       
  1879     }
       
  1880     return false;
       
  1881 }
       
  1882 
       
  1883 QT_END_NAMESPACE
       
  1884 #endif // QT_NO_CSSPARSER
       
  1885 
       
  1886 #endif // QT_NO_TEXTHTMLPARSER