| author | Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> | 
| Tue, 06 Jul 2010 15:10:48 +0300 | |
| changeset 30 | 5dc02b23752f | 
| parent 18 | 2f34d5167611 | 
| permissions | -rw-r--r-- | 
| 0 | 1 | /**************************************************************************** | 
| 2 | ** | |
| 18 
2f34d5167611
Revision: 201011
 Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> parents: 
0diff
changeset | 3 | ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). | 
| 0 | 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 |