source: trunk/src/gui/text/qtexthtmlparser.cpp@ 869

Last change on this file since 869 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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