~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/text/qtexthtmlparser.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the text module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qtexthtmlparser_p.h"
 
30
 
 
31
#include <qbytearray.h>
 
32
#include <qtextcodec.h>
 
33
#include <qapplication.h>
 
34
#include <qstack.h>
 
35
#include <qdebug.h>
 
36
 
 
37
#include "qtextdocument.h"
 
38
#include "qtextformat_p.h"
 
39
#include "qtextdocument_p.h"
 
40
#include "qtextcursor.h"
 
41
 
 
42
const int DefaultFontSize = 12;
 
43
 
 
44
#define MAX_ENTITY 259
 
45
static const struct QTextHtmlEntity { const char *name; quint16 code; } entities[MAX_ENTITY]= {
 
46
    { "AElig", 0x00c6 },
 
47
    { "Aacute", 0x00c1 },
 
48
    { "Acirc", 0x00c2 },
 
49
    { "Agrave", 0x00c0 },
 
50
    { "Alpha", 0x0391 },
 
51
    { "AMP", 38 },
 
52
    { "Aring", 0x00c5 },
 
53
    { "Atilde", 0x00c3 },
 
54
    { "Auml", 0x00c4 },
 
55
    { "Beta", 0x0392 },
 
56
    { "Ccedil", 0x00c7 },
 
57
    { "Chi", 0x03a7 },
 
58
    { "Dagger", 0x2021 },
 
59
    { "Delta", 0x0394 },
 
60
    { "ETH", 0x00d0 },
 
61
    { "Eacute", 0x00c9 },
 
62
    { "Ecirc", 0x00ca },
 
63
    { "Egrave", 0x00c8 },
 
64
    { "Epsilon", 0x0395 },
 
65
    { "Eta", 0x0397 },
 
66
    { "Euml", 0x00cb },
 
67
    { "Gamma", 0x0393 },
 
68
    { "GT", 62 },
 
69
    { "Iacute", 0x00cd },
 
70
    { "Icirc", 0x00ce },
 
71
    { "Igrave", 0x00cc },
 
72
    { "Iota", 0x0399 },
 
73
    { "Iuml", 0x00cf },
 
74
    { "Kappa", 0x039a },
 
75
    { "Lambda", 0x039b },
 
76
    { "LT", 60 },
 
77
    { "Mu", 0x039c },
 
78
    { "Ntilde", 0x00d1 },
 
79
    { "Nu", 0x039d },
 
80
    { "OElig", 0x0152 },
 
81
    { "Oacute", 0x00d3 },
 
82
    { "Ocirc", 0x00d4 },
 
83
    { "Ograve", 0x00d2 },
 
84
    { "Omega", 0x03a9 },
 
85
    { "Omicron", 0x039f },
 
86
    { "Oslash", 0x00d8 },
 
87
    { "Otilde", 0x00d5 },
 
88
    { "Ouml", 0x00d6 },
 
89
    { "Phi", 0x03a6 },
 
90
    { "Pi", 0x03a0 },
 
91
    { "Prime", 0x2033 },
 
92
    { "Psi", 0x03a8 },
 
93
    { "QUOT", 34 },
 
94
    { "Rho", 0x03a1 },
 
95
    { "Scaron", 0x0160 },
 
96
    { "Sigma", 0x03a3 },
 
97
    { "THORN", 0x00de },
 
98
    { "Tau", 0x03a4 },
 
99
    { "Theta", 0x0398 },
 
100
    { "Uacute", 0x00da },
 
101
    { "Ucirc", 0x00db },
 
102
    { "Ugrave", 0x00d9 },
 
103
    { "Upsilon", 0x03a5 },
 
104
    { "Uuml", 0x00dc },
 
105
    { "Xi", 0x039e },
 
106
    { "Yacute", 0x00dd },
 
107
    { "Yuml", 0x0178 },
 
108
    { "Zeta", 0x0396 },
 
109
    { "aacute", 0x00e1 },
 
110
    { "acirc", 0x00e2 },
 
111
    { "acute", 0x00b4 },
 
112
    { "aelig", 0x00e6 },
 
113
    { "agrave", 0x00e0 },
 
114
    { "alefsym", 0x2135 },
 
115
    { "alpha", 0x03b1 },
 
116
    { "amp", 38 },
 
117
    { "and", 0x22a5 },
 
118
    { "ang", 0x2220 },
 
119
    { "apos", 0x0027 },
 
120
    { "aring", 0x00e5 },
 
121
    { "asymp", 0x2248 },
 
122
    { "atilde", 0x00e3 },
 
123
    { "auml", 0x00e4 },
 
124
    { "bdquo", 0x201e },
 
125
    { "beta", 0x03b2 },
 
126
    { "brvbar", 0x00a6 },
 
127
    { "bull", 0x2022 },
 
128
    { "cap", 0x2229 },
 
129
    { "ccedil", 0x00e7 },
 
130
    { "cedil", 0x00b8 },
 
131
    { "cent", 0x00a2 },
 
132
    { "chi", 0x03c7 },
 
133
    { "circ", 0x02c6 },
 
134
    { "clubs", 0x2663 },
 
135
    { "cong", 0x2245 },
 
136
    { "copy", 0x00a9 },
 
137
    { "crarr", 0x21b5 },
 
138
    { "cup", 0x222a },
 
139
    { "curren", 0x00a4 },
 
140
    { "dArr", 0x21d3 },
 
141
    { "dagger", 0x2020 },
 
142
    { "darr", 0x2193 },
 
143
    { "deg", 0x00b0 },
 
144
    { "delta", 0x03b4 },
 
145
    { "diams", 0x2666 },
 
146
    { "divide", 0x00f7 },
 
147
    { "eacute", 0x00e9 },
 
148
    { "ecirc", 0x00ea },
 
149
    { "egrave", 0x00e8 },
 
150
    { "empty", 0x2205 },
 
151
    { "emsp", 0x2003 },
 
152
    { "ensp", 0x2002 },
 
153
    { "epsilon", 0x03b5 },
 
154
    { "equiv", 0x2261 },
 
155
    { "eta", 0x03b7 },
 
156
    { "eth", 0x00f0 },
 
157
    { "euml", 0x00eb },
 
158
    { "euro", 0x20ac },
 
159
    { "exist", 0x2203 },
 
160
    { "fnof", 0x0192 },
 
161
    { "forall", 0x2200 },
 
162
    { "frac12", 0x00bd },
 
163
    { "frac14", 0x00bc },
 
164
    { "frac34", 0x00be },
 
165
    { "frasl", 0x2044 },
 
166
    { "gamma", 0x03b3 },
 
167
    { "ge", 0x2265 },
 
168
    { "gt", 62 },
 
169
    { "hArr", 0x21d4 },
 
170
    { "harr", 0x2194 },
 
171
    { "hearts", 0x2665 },
 
172
    { "hellip", 0x2026 },
 
173
    { "iacute", 0x00ed },
 
174
    { "icirc", 0x00ee },
 
175
    { "iexcl", 0x00a1 },
 
176
    { "igrave", 0x00ec },
 
177
    { "image", 0x2111 },
 
178
    { "infin", 0x221e },
 
179
    { "int", 0x222b },
 
180
    { "iota", 0x03b9 },
 
181
    { "iquest", 0x00bf },
 
182
    { "isin", 0x2208 },
 
183
    { "iuml", 0x00ef },
 
184
    { "kappa", 0x03ba },
 
185
    { "lArr", 0x21d0 },
 
186
    { "lambda", 0x03bb },
 
187
    { "lang", 0x2329 },
 
188
    { "laquo", 0x00ab },
 
189
    { "larr", 0x2190 },
 
190
    { "lceil", 0x2308 },
 
191
    { "ldquo", 0x201c },
 
192
    { "le", 0x2264 },
 
193
    { "lfloor", 0x230a },
 
194
    { "lowast", 0x2217 },
 
195
    { "loz", 0x25ca },
 
196
    { "lrm", 0x200e },
 
197
    { "lsaquo", 0x2039 },
 
198
    { "lsquo", 0x2018 },
 
199
    { "lt", 60 },
 
200
    { "macr", 0x00af },
 
201
    { "mdash", 0x2014 },
 
202
    { "micro", 0x00b5 },
 
203
    { "middot", 0x00b7 },
 
204
    { "minus", 0x2212 },
 
205
    { "mu", 0x03bc },
 
206
    { "nabla", 0x2207 },
 
207
    { "nbsp", 0x00a0 },
 
208
    { "ndash", 0x2013 },
 
209
    { "ne", 0x2260 },
 
210
    { "ni", 0x220b },
 
211
    { "not", 0x00ac },
 
212
    { "notin", 0x2209 },
 
213
    { "nsub", 0x2284 },
 
214
    { "ntilde", 0x00f1 },
 
215
    { "nu", 0x03bd },
 
216
    { "oacute", 0x00f3 },
 
217
    { "ocirc", 0x00f4 },
 
218
    { "oelig", 0x0153 },
 
219
    { "ograve", 0x00f2 },
 
220
    { "oline", 0x203e },
 
221
    { "omega", 0x03c9 },
 
222
    { "omicron", 0x03bf },
 
223
    { "oplus", 0x2295 },
 
224
    { "or", 0x22a6 },
 
225
    { "ordf", 0x00aa },
 
226
    { "ordm", 0x00ba },
 
227
    { "oslash", 0x00f8 },
 
228
    { "otilde", 0x00f5 },
 
229
    { "otimes", 0x2297 },
 
230
    { "ouml", 0x00f6 },
 
231
    { "para", 0x00b6 },
 
232
    { "part", 0x2202 },
 
233
    { "percnt", 0x0025 },
 
234
    { "permil", 0x2030 },
 
235
    { "perp", 0x22a5 },
 
236
    { "phi", 0x03c6 },
 
237
    { "pi", 0x03c0 },
 
238
    { "piv", 0x03d6 },
 
239
    { "plusmn", 0x00b1 },
 
240
    { "pound", 0x00a3 },
 
241
    { "prime", 0x2032 },
 
242
    { "prod", 0x220f },
 
243
    { "prop", 0x221d },
 
244
    { "psi", 0x03c8 },
 
245
    { "quot", 34 },
 
246
    { "rArr", 0x21d2 },
 
247
    { "radic", 0x221a },
 
248
    { "rang", 0x232a },
 
249
    { "raquo", 0x00bb },
 
250
    { "rarr", 0x2192 },
 
251
    { "rceil", 0x2309 },
 
252
    { "rdquo", 0x201d },
 
253
    { "real", 0x211c },
 
254
    { "reg", 0x00ae },
 
255
    { "rfloor", 0x230b },
 
256
    { "rho", 0x03c1 },
 
257
    { "rlm", 0x200f },
 
258
    { "rsaquo", 0x203a },
 
259
    { "rsquo", 0x2019 },
 
260
    { "sbquo", 0x201a },
 
261
    { "scaron", 0x0161 },
 
262
    { "sdot", 0x22c5 },
 
263
    { "sect", 0x00a7 },
 
264
    { "shy", 0x00ad },
 
265
    { "sigma", 0x03c3 },
 
266
    { "sigmaf", 0x03c2 },
 
267
    { "sim", 0x223c },
 
268
    { "spades", 0x2660 },
 
269
    { "sub", 0x2282 },
 
270
    { "sube", 0x2286 },
 
271
    { "sum", 0x2211 },
 
272
    { "sup1", 0x00b9 },
 
273
    { "sup2", 0x00b2 },
 
274
    { "sup3", 0x00b3 },
 
275
    { "sup", 0x2283 },
 
276
    { "supe", 0x2287 },
 
277
    { "szlig", 0x00df },
 
278
    { "tau", 0x03c4 },
 
279
    { "there4", 0x2234 },
 
280
    { "theta", 0x03b8 },
 
281
    { "thetasym", 0x03d1 },
 
282
    { "thinsp", 0x2009 },
 
283
    { "thorn", 0x00fe },
 
284
    { "tilde", 0x02dc },
 
285
    { "times", 0x00d7 },
 
286
    { "trade", 0x2122 },
 
287
    { "uArr", 0x21d1 },
 
288
    { "uacute", 0x00fa },
 
289
    { "uarr", 0x2191 },
 
290
    { "ucirc", 0x00fb },
 
291
    { "ugrave", 0x00f9 },
 
292
    { "uml", 0x00a8 },
 
293
    { "upsih", 0x03d2 },
 
294
    { "upsilon", 0x03c5 },
 
295
    { "uuml", 0x00fc },
 
296
    { "weierp", 0x2118 },
 
297
    { "xi", 0x03be },
 
298
    { "yacute", 0x00fd },
 
299
    { "yen", 0x00a5 },
 
300
    { "yuml", 0x00ff },
 
301
    { "zeta", 0x03b6 },
 
302
    { "zwj", 0x200d },
 
303
    { "zwnj", 0x200c },
 
304
    { 0, 0 }
 
305
};
 
306
 
 
307
static bool operator<(const QString &entityStr, const QTextHtmlEntity &entity)
 
308
{
 
309
    return entityStr < QLatin1String(entity.name);
 
310
}
 
311
 
 
312
static bool operator<(const QTextHtmlEntity &entity, const QString &entityStr)
 
313
{
 
314
    return QLatin1String(entity.name) < entityStr;
 
315
}
 
316
 
 
317
static QChar resolveEntity(const QString &entity)
 
318
{
 
319
    const QTextHtmlEntity *start = &entities[0];
 
320
    const QTextHtmlEntity *end = &entities[MAX_ENTITY-1];
 
321
    const QTextHtmlEntity *e = qBinaryFind(start, end, entity);
 
322
    if (!e->name)
 
323
        return QChar();
 
324
    return e->code;
 
325
}
 
326
 
 
327
// the displayMode value is according to the what are blocks in the piecetable, not
 
328
// what the w3c defines.
 
329
static const QTextHtmlElement elements[Html_NumElements+1]= {
 
330
    { "a", Html_a, QTextHtmlElement::DisplayInline },
 
331
    { "b", Html_b, QTextHtmlElement::DisplayInline },
 
332
    { "big", Html_big, QTextHtmlElement::DisplayInline },
 
333
    { "blockquote", Html_blockquote, QTextHtmlElement::DisplayBlock },
 
334
    { "body", Html_body, QTextHtmlElement::DisplayBlock },
 
335
    { "br", Html_br, QTextHtmlElement::DisplayInline },
 
336
    { "center", Html_center, QTextHtmlElement::DisplayBlock },
 
337
    { "code", Html_code, QTextHtmlElement::DisplayInline },
 
338
    { "dd", Html_dd, QTextHtmlElement::DisplayBlock },
 
339
    { "div", Html_div, QTextHtmlElement::DisplayBlock },
 
340
    { "dl", Html_dl, QTextHtmlElement::DisplayBlock },
 
341
    { "dt", Html_dt, QTextHtmlElement::DisplayBlock },
 
342
    { "em", Html_em, QTextHtmlElement::DisplayInline },
 
343
    { "font", Html_font, QTextHtmlElement::DisplayInline },
 
344
    { "h1", Html_h1, QTextHtmlElement::DisplayBlock },
 
345
    { "h2", Html_h2, QTextHtmlElement::DisplayBlock },
 
346
    { "h3", Html_h3, QTextHtmlElement::DisplayBlock },
 
347
    { "h4", Html_h4, QTextHtmlElement::DisplayBlock },
 
348
    { "h5", Html_h5, QTextHtmlElement::DisplayBlock },
 
349
    { "h6", Html_h6, QTextHtmlElement::DisplayBlock },
 
350
    { "head", Html_head, QTextHtmlElement::DisplayNone },
 
351
    { "hr", Html_hr, QTextHtmlElement::DisplayInline },
 
352
    { "html", Html_html, QTextHtmlElement::DisplayInline },
 
353
    { "i", Html_i, QTextHtmlElement::DisplayInline },
 
354
    { "img", Html_img, QTextHtmlElement::DisplayInline },
 
355
    { "li", Html_li, QTextHtmlElement::DisplayBlock },
 
356
    { "meta", Html_meta, QTextHtmlElement::DisplayNone },
 
357
    { "nobr", Html_nobr, QTextHtmlElement::DisplayInline },
 
358
    { "ol", Html_ol, QTextHtmlElement::DisplayBlock },
 
359
    { "p", Html_p, QTextHtmlElement::DisplayBlock },
 
360
    { "pre", Html_pre, QTextHtmlElement::DisplayBlock },
 
361
    { "qt", Html_qt, QTextHtmlElement::DisplayBlock },
 
362
    { "s", Html_s, QTextHtmlElement::DisplayInline },
 
363
    { "small", Html_small, QTextHtmlElement::DisplayInline },
 
364
    { "span", Html_span, QTextHtmlElement::DisplayInline },
 
365
    { "strong", Html_strong, QTextHtmlElement::DisplayInline },
 
366
    { "style", Html_style, QTextHtmlElement::DisplayNone },
 
367
    { "sub", Html_sub, QTextHtmlElement::DisplayInline },
 
368
    { "sup", Html_sup, QTextHtmlElement::DisplayInline },
 
369
    { "table", Html_table, QTextHtmlElement::DisplayBlock },
 
370
    { "td", Html_td, QTextHtmlElement::DisplayBlock },
 
371
    { "th", Html_th, QTextHtmlElement::DisplayBlock },
 
372
    { "title", Html_title, QTextHtmlElement::DisplayNone },
 
373
    { "tr", Html_tr, QTextHtmlElement::DisplayBlock },
 
374
    { "tt", Html_tt, QTextHtmlElement::DisplayInline },
 
375
    { "u", Html_u, QTextHtmlElement::DisplayInline },
 
376
    { "ul", Html_ul, QTextHtmlElement::DisplayBlock },
 
377
    { 0, 0, QTextHtmlElement::DisplayNone }
 
378
};
 
379
 
 
380
 
 
381
static bool operator<(const QString &str, const QTextHtmlElement &e)
 
382
{
 
383
    return str < QLatin1String(e.name);
 
384
}
 
385
 
 
386
static bool operator<(const QTextHtmlElement &e, const QString &str)
 
387
{
 
388
    return QLatin1String(e.name) < str;
 
389
}
 
390
 
 
391
static const QTextHtmlElement *lookupElement(const QString &element)
 
392
{
 
393
    const QTextHtmlElement *start = &elements[0];
 
394
    const QTextHtmlElement *end = &elements[Html_NumElements];
 
395
    const QTextHtmlElement *e = qBinaryFind(start, end, element);
 
396
    Q_ASSERT(!e->name || e->name == element);
 
397
    return e;
 
398
}
 
399
 
 
400
int QTextHtmlParser::lookupElement(const QString &element)
 
401
{
 
402
    const QTextHtmlElement *e = ::lookupElement(element);
 
403
    if (!e->name)
 
404
        return -1;
 
405
    return e->id;
 
406
}
 
407
 
 
408
static int scaleFontPointSize(int fontPointSize, int logicalFontSize, int logicalFontSizeStep = 0)
 
409
{
 
410
    if (logicalFontSize != -1 || logicalFontSizeStep) {
 
411
        int logical = logicalFontSize;
 
412
        if (logical < 0)
 
413
            logical = 3;
 
414
        logical += logicalFontSizeStep;
 
415
        if (logical < 0)
 
416
            logical = 0;
 
417
        else if (logical > 7)
 
418
            logical = 8;
 
419
        switch (logical) {
 
420
        case 1:
 
421
            fontPointSize =  (7 * fontPointSize) / 10;
 
422
            break;
 
423
        case 2:
 
424
            fontPointSize = (8 * fontPointSize) / 10;
 
425
            break;
 
426
        case 4:
 
427
            fontPointSize =  (12 * fontPointSize) / 10;
 
428
            break;
 
429
        case 5:
 
430
            fontPointSize = (15 * fontPointSize) / 10;
 
431
            break;
 
432
        case 6:
 
433
            fontPointSize = 2 * fontPointSize;
 
434
            break;
 
435
        case 7:
 
436
            fontPointSize = (24 * fontPointSize) / 10;
 
437
            break;
 
438
        };
 
439
    }
 
440
    return fontPointSize;
 
441
}
 
442
 
 
443
// quotes newlines as "\\n"
 
444
static QString quoteNewline(const QString &s)
 
445
{
 
446
    QString n = s;
 
447
    n.replace('\n', "\\n");
 
448
    return n;
 
449
}
 
450
 
 
451
QTextHtmlParserNode::QTextHtmlParserNode()
 
452
    : parent(0), id(-1), isBlock(false), isListItem(false), isListStart(false), isTableCell(false), isAnchor(false),
 
453
      fontItalic(false), fontUnderline(false), fontOverline(false), fontStrikeOut(false), fontFixedPitch(false),
 
454
      cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), hasFontPointSize(false),
 
455
      hasCssBlockIndent(false), hasCssListIndent(false), isEmptyParagraph(false), direction(3),
 
456
      displayMode(QTextHtmlElement::DisplayInline), fontPointSize(DefaultFontSize),
 
457
      fontWeight(QFont::Normal), alignment(0), verticalAlignment(QTextCharFormat::AlignNormal),
 
458
      listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
 
459
      tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0), cssBlockIndent(0),
 
460
      cssListIndent(0), text_indent(0), wsm(WhiteSpaceModeUndefined)
 
461
{
 
462
    margin[QTextHtmlParser::MarginLeft] = 0;
 
463
    margin[QTextHtmlParser::MarginRight] = 0;
 
464
    margin[QTextHtmlParser::MarginTop] = 0;
 
465
    margin[QTextHtmlParser::MarginBottom] = 0;
 
466
}
 
467
 
 
468
QTextCharFormat QTextHtmlParserNode::charFormat() const
 
469
{
 
470
    QTextCharFormat format;
 
471
 
 
472
    format.setFontItalic(fontItalic);
 
473
    format.setFontUnderline(fontUnderline);
 
474
    format.setFontOverline(fontOverline);
 
475
    format.setFontStrikeOut(fontStrikeOut);
 
476
    format.setFontFixedPitch(fontFixedPitch);
 
477
    if (fontFamily.size())
 
478
        format.setFontFamily(fontFamily);
 
479
    if (hasFontPointSize)
 
480
        format.setFontPointSize(fontPointSize);
 
481
    format.setFontWeight(fontWeight);
 
482
    if (color.isValid())
 
483
        format.setForeground(QBrush(color));
 
484
    if (verticalAlignment != QTextCharFormat::AlignNormal)
 
485
        format.setVerticalAlignment(verticalAlignment);
 
486
    if (isAnchor) {
 
487
        format.setAnchor(true);
 
488
        format.setAnchorHref(anchorHref);
 
489
        format.setAnchorName(anchorName);
 
490
    }
 
491
 
 
492
    return format;
 
493
}
 
494
 
 
495
QTextBlockFormat QTextHtmlParserNode::blockFormat() const
 
496
{
 
497
    QTextBlockFormat format;
 
498
 
 
499
    if (alignment)
 
500
        format.setAlignment(alignment);
 
501
    if (direction < 2)
 
502
        format.setLayoutDirection(Qt::LayoutDirection(direction));
 
503
 
 
504
    if (hasCssBlockIndent)
 
505
        format.setIndent(cssBlockIndent);
 
506
    if (text_indent != 0.)
 
507
        format.setTextIndent(text_indent);
 
508
 
 
509
    return format;
 
510
}
 
511
 
 
512
void QTextHtmlParser::dumpHtml()
 
513
{
 
514
    for (int i = 0; i < count(); ++i) {
 
515
        qDebug().nospace() << qPrintable(QString(depth(i)*4, ' '))
 
516
                           << qPrintable(at(i).tag) << ":"
 
517
                           << quoteNewline(at(i).text);
 
518
            ;
 
519
    }
 
520
}
 
521
 
 
522
QTextHtmlParserNode *QTextHtmlParser::newNode(int parent)
 
523
{
 
524
    QTextHtmlParserNode *lastNode = &nodes.last();
 
525
    QTextHtmlParserNode *newNode = 0;
 
526
 
 
527
    bool reuseLastNode = true;
 
528
 
 
529
    if (nodes.count() == 1) {
 
530
        reuseLastNode = false;
 
531
    } else if (lastNode->tag.isEmpty()) {
 
532
 
 
533
        if (lastNode->text.isEmpty()) {
 
534
            reuseLastNode = true;
 
535
        } else { // last node is a text node (empty tag) with some text
 
536
 
 
537
            if (lastNode->text == QLatin1String(" ")) {
 
538
 
 
539
                // re-use last node if whitspace'ed, unless it's part of a
 
540
                // <span>Foo</span> <span>Bar</span> alike sequence
 
541
                const QTextHtmlParserNode *secondLastNode = &at(count() - 2);
 
542
                if (secondLastNode->displayMode == QTextHtmlElement::DisplayInline
 
543
                    && secondLastNode->parent == lastNode->parent) {
 
544
                    reuseLastNode = false;
 
545
                } else {
 
546
                    reuseLastNode = true;
 
547
                }
 
548
            } else {
 
549
                // text node with real (non-whitespace) text -> nothing to re-use
 
550
                reuseLastNode = false;
 
551
            }
 
552
 
 
553
        }
 
554
 
 
555
    } else {
 
556
        // last node had a proper tag -> nothing to re-use
 
557
        reuseLastNode = false;
 
558
    }
 
559
 
 
560
    if (reuseLastNode) {
 
561
        newNode = lastNode;
 
562
        newNode->tag.clear();
 
563
        newNode->text.clear();
 
564
        newNode->id = -1;
 
565
    } else {
 
566
        nodes.resize(nodes.size() + 1);
 
567
        newNode = &nodes.last();
 
568
    }
 
569
 
 
570
    newNode->parent = parent;
 
571
    return newNode;
 
572
}
 
573
 
 
574
void QTextHtmlParser::parse(const QString &text)
 
575
{
 
576
    nodes.clear();
 
577
    nodes.resize(1);
 
578
    txt = text;
 
579
    pos = 0;
 
580
    len = txt.length();
 
581
    textEditMode = false;
 
582
    parse();
 
583
    //dumpHtml();
 
584
}
 
585
 
 
586
int QTextHtmlParser::depth(int i) const
 
587
{
 
588
    int depth = 0;
 
589
    while (i) {
 
590
        i = at(i).parent;
 
591
        ++depth;
 
592
    }
 
593
    return depth;
 
594
}
 
595
 
 
596
int QTextHtmlParser::margin(int i, int mar) const {
 
597
    int m = 0;
 
598
    const QTextHtmlParserNode *node;
 
599
    if (mar == MarginLeft
 
600
        || mar == MarginRight) {
 
601
        while (i) {
 
602
            node = &at(i);
 
603
            if (!node->isBlock)
 
604
                return 0;
 
605
            m += node->margin[mar];
 
606
            i = node->parent;
 
607
        }
 
608
    }
 
609
    return m;
 
610
}
 
611
 
 
612
int QTextHtmlParser::topMargin(int i) const
 
613
{
 
614
    int m = 0;
 
615
    const QTextHtmlParserNode *node;
 
616
    while (i) {
 
617
        node = &at(i);
 
618
        if (!node->isBlock)
 
619
            return 0;
 
620
        m = qMax(m, node->margin[MarginTop]);
 
621
 
 
622
        // collapsing margins across table cells makes no sense
 
623
        if (node->isTableCell)
 
624
            break;
 
625
 
 
626
        // don't collapse margins across list items
 
627
        // (the top margin of the list is merged as part of the block
 
628
        // merging in documentfragment.cpp)
 
629
        if (node->isListItem)
 
630
            break;
 
631
 
 
632
        // <ul>
 
633
        //  ..
 
634
        //  <ul> <-- this one should not take the first <ul>'s margin into account
 
635
        if (node->isNestedList(this))
 
636
            break;
 
637
 
 
638
        // get previous block
 
639
        while (i-1 && !at(i-1).isBlock)
 
640
            --i;
 
641
        if (i && node->parent == at(i).parent)
 
642
            break;
 
643
        i = node->parent;
 
644
    }
 
645
    return m;
 
646
}
 
647
 
 
648
int QTextHtmlParser::bottomMargin(int i) const
 
649
{
 
650
    int m = 0;
 
651
    const QTextHtmlParserNode *node;
 
652
    while (i) {
 
653
        node = &at(i);
 
654
        if (!node->isBlock)
 
655
            return 0;
 
656
        m = qMax(m, node->margin[MarginBottom]);
 
657
 
 
658
        // collapsing margins across table cells makes no sense
 
659
        if (node->isTableCell)
 
660
            break;
 
661
 
 
662
        // don't collapse margins across list items
 
663
        if (node->isListItem)
 
664
            break;
 
665
 
 
666
        // <ul>
 
667
        //  ..
 
668
        //  <ul> <-- this one should not take the first <ul>'s margin into account
 
669
        if (node->isNestedList(this))
 
670
            break;
 
671
 
 
672
        // get next block
 
673
        while (i+1 < count() && !at(i+1).isBlock)
 
674
            ++i;
 
675
        if (i && node->parent == at(i).parent)
 
676
            break;
 
677
        i = node->parent;
 
678
    }
 
679
    return m;
 
680
}
 
681
 
 
682
void QTextHtmlParser::eatSpace()
 
683
{
 
684
    while (pos < len && txt.at(pos).isSpace() && txt.at(pos) != QChar::ParagraphSeparator)
 
685
        pos++;
 
686
}
 
687
 
 
688
void QTextHtmlParser::parse() {
 
689
    QTextHtmlParserNode::WhiteSpaceMode wsm = QTextHtmlParserNode::WhiteSpaceNormal;
 
690
    while (pos < len) {        
 
691
        QChar c = txt.at(pos++);
 
692
        if (c == QLatin1Char('<')) {
 
693
            parseTag();
 
694
            wsm = nodes.last().wsm;
 
695
        } else if (c == QLatin1Char('&')) {
 
696
            nodes.last().text += parseEntity();
 
697
        } else {
 
698
            if (c.isSpace() && c != QChar(QChar::Nbsp) && c != QChar::ParagraphSeparator) {
 
699
                if (wsm == QTextHtmlParserNode::WhiteSpacePre
 
700
                    || textEditMode) {
 
701
                    if (c == QLatin1Char('\n'))
 
702
                        c = QChar::LineSeparator;
 
703
                    else if (c == QLatin1Char('\r'))
 
704
                        continue;
 
705
 
 
706
                    if (textEditMode
 
707
                        && c == QChar::LineSeparator)
 
708
                        continue;
 
709
                } else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) { // non-pre mode: collapse whitespace except nbsp
 
710
                    while (pos < len && txt.at(pos).isSpace()
 
711
                           && txt.at(pos) != QChar::Nbsp)
 
712
                        pos++;
 
713
                    if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
 
714
                        c = QChar::Nbsp;
 
715
                    else
 
716
                        c = QLatin1Char(' ');
 
717
                }
 
718
            }
 
719
            nodes.last().text += c;
 
720
        }
 
721
    }
 
722
}
 
723
 
 
724
// parses a tag after "<"
 
725
void QTextHtmlParser::parseTag()
 
726
{
 
727
    eatSpace();
 
728
 
 
729
    // handle comments and other exclamation mark declarations
 
730
    if (hasPrefix(QLatin1Char('!'))) {
 
731
        parseExclamationTag();
 
732
        eatSpace();
 
733
        return;
 
734
    }
 
735
 
 
736
    // if close tag just close
 
737
    if (hasPrefix(QLatin1Char('/'))) {
 
738
        parseCloseTag();
 
739
        return;
 
740
    }
 
741
 
 
742
    int p = last();
 
743
    while (p && at(p).tag.size() == 0)
 
744
        p = at(p).parent;
 
745
 
 
746
    QTextHtmlParserNode *node = newNode(p);
 
747
 
 
748
    // parse tag name
 
749
    node->tag = parseWord().toLower();
 
750
 
 
751
    const QTextHtmlElement *elem = ::lookupElement(node->tag);
 
752
    if (elem->name) {
 
753
        node->id = elem->id;
 
754
        node->isBlock = (elem->displayMode == QTextHtmlElement::DisplayBlock);
 
755
        node->displayMode = elem->displayMode;
 
756
    } else {
 
757
        node->id = -1;
 
758
    }
 
759
 
 
760
    node->isListItem = (node->id == Html_li);
 
761
    node->isListStart = (node->id == Html_ol || node->id == Html_ul);
 
762
    node->isTableCell = (node->id == Html_td || node->id == Html_th);
 
763
 
 
764
    resolveParent();
 
765
    resolveNode();
 
766
    // _need_ at least one space after the tag name, otherwise there can't be attributes
 
767
    if (pos < len && txt.at(pos).isSpace())
 
768
        parseAttributes();
 
769
 
 
770
    // special handling for anchors with href attribute (hyperlinks)
 
771
    if (node->isAnchor && !node->anchorHref.isEmpty()) {
 
772
        node->fontUnderline = true; // ####
 
773
        node->color = Qt::blue; // ####
 
774
    }
 
775
 
 
776
    // finish tag
 
777
    bool tagClosed = false;
 
778
    while (pos < len && txt.at(pos) != QLatin1Char('>')) {
 
779
        if (txt.at(pos) == QLatin1Char('/')) 
 
780
            tagClosed = true;
 
781
        
 
782
 
 
783
        pos++;
 
784
    }
 
785
    pos++;
 
786
 
 
787
    if (node->wsm != QTextHtmlParserNode::WhiteSpacePre
 
788
        && node->wsm != QTextHtmlParserNode::WhiteSpacePreWrap)
 
789
        eatSpace();
 
790
 
 
791
    if (node->mayNotHaveChildren() || tagClosed) {
 
792
        newNode(node->parent);
 
793
        resolveNode();
 
794
    }
 
795
}
 
796
 
 
797
// parses a tag beginning with "/"
 
798
void QTextHtmlParser::parseCloseTag()
 
799
{
 
800
    ++pos;
 
801
    QString tag = parseWord().toLower().trimmed();
 
802
    while (pos < len) {
 
803
        QChar c = txt.at(pos++);
 
804
        if (c == QLatin1Char('>'))
 
805
            break;
 
806
    }
 
807
 
 
808
    // find corresponding open node
 
809
    int p = last();
 
810
    if (p > 0
 
811
        && at(p - 1).tag == tag
 
812
        && at(p - 1).mayNotHaveChildren())
 
813
        p--;
 
814
 
 
815
    while (p && at(p).tag != tag)
 
816
        p = at(p).parent;
 
817
 
 
818
    newNode(at(p).parent);
 
819
    resolveNode();
 
820
}
 
821
 
 
822
// parses a tag beginning with "!"
 
823
void QTextHtmlParser::parseExclamationTag()
 
824
{
 
825
    ++pos;
 
826
    if (hasPrefix(QLatin1Char('-'),1) && hasPrefix(QLatin1Char('-'),2)) {
 
827
        pos += 3;
 
828
        // eat comments
 
829
        int end = txt.indexOf(QLatin1String("-->"), pos);
 
830
        pos = (end >= 0 ? end + 3 : len);
 
831
    } else {
 
832
        // eat internal tags
 
833
        while (pos < len) {
 
834
            QChar c = txt.at(pos++);
 
835
            if (c == QLatin1Char('>'))
 
836
                break;
 
837
        }
 
838
    }
 
839
}
 
840
 
 
841
// parses an entity after "&", and returns it
 
842
QString QTextHtmlParser::parseEntity()
 
843
{
 
844
    int recover = pos;
 
845
    QString entity;
 
846
    while (pos < len) {
 
847
        QChar c = txt.at(pos++);
 
848
        if (c.isSpace() || pos - recover > 8) {
 
849
            goto error;
 
850
        }
 
851
        if (c == QLatin1Char(';'))
 
852
            break;
 
853
        entity += c;
 
854
    }
 
855
    {
 
856
        QChar resolved = resolveEntity(entity);
 
857
        if (!resolved.isNull())
 
858
            return QString(resolved);
 
859
    }
 
860
    if (entity.length() > 1 && entity.at(0) == QLatin1Char('#')) {
 
861
        entity.remove(0, 1); // removing leading #
 
862
 
 
863
        int base = 10;
 
864
        bool ok = false;
 
865
 
 
866
        if (entity.at(0).toLower() == QLatin1Char('x')) { // hex entity?
 
867
            entity.remove(0, 1);
 
868
            base = 16;
 
869
        }
 
870
 
 
871
        int uc = entity.toInt(&ok, base);
 
872
        if (ok) {
 
873
            if (uc == 151) // ### hack for designer manual
 
874
                uc = '-';
 
875
            QString str;
 
876
            if (uc > 0xffff) {
 
877
                // surrogate pair
 
878
                uc -= 0x10000;
 
879
                ushort high = uc/0x400 + 0xd800;
 
880
                ushort low = uc%0x400 + 0xdc00;
 
881
                str.append(QChar(high));
 
882
                str.append(QChar(low));
 
883
            } else {
 
884
                str.append(QChar(uc));
 
885
            }
 
886
            return str;
 
887
        }
 
888
    }
 
889
error:
 
890
    pos = recover;
 
891
    return QLatin1String("&");
 
892
}
 
893
 
 
894
// parses one word, possibly quoted, and returns it
 
895
QString QTextHtmlParser::parseWord()
 
896
{
 
897
    QString word;
 
898
    if (hasPrefix(QLatin1Char('\"'))) { // double quotes
 
899
        ++pos;
 
900
        while (pos < len) {
 
901
            QChar c = txt.at(pos++);
 
902
            if (c == QLatin1Char('\"'))
 
903
                break;
 
904
            else if (c == QLatin1Char('&'))
 
905
                word += parseEntity();
 
906
            else
 
907
                word += c;
 
908
        }
 
909
    } else if (hasPrefix(QLatin1Char('\''))) { // single quotes
 
910
        ++pos;
 
911
        while (pos < len) {
 
912
            QChar c = txt.at(pos++);
 
913
            if (c == QLatin1Char('\''))
 
914
                break;
 
915
            else
 
916
                word += c;
 
917
        }
 
918
    } else { // normal text
 
919
        while (pos < len) {
 
920
            QChar c = txt.at(pos++);
 
921
            if (c == QLatin1Char('>')
 
922
                || (c == '/' && hasPrefix(QLatin1Char('>'), 1))
 
923
                || c == QLatin1Char('<')
 
924
                || c == QLatin1Char('=')
 
925
                || c.isSpace()) {
 
926
                --pos;
 
927
                break;
 
928
            }
 
929
            if (c == QLatin1Char('&'))
 
930
                word += parseEntity();
 
931
            else
 
932
                word += c;
 
933
        }
 
934
    }
 
935
    return word;
 
936
}
 
937
 
 
938
// gives the new node the right parent
 
939
void QTextHtmlParser::resolveParent()
 
940
{
 
941
    QTextHtmlParserNode *node = &nodes.last();
 
942
    int p = node->parent;
 
943
 
 
944
    // block elements close inline elements
 
945
    // ... with the exception of the font element ... grmbl ...
 
946
    if (node->isBlock)
 
947
        while (p
 
948
               && !at(p).isBlock
 
949
               && at(p).id != Html_font) {
 
950
            p = at(p).parent;
 
951
        }
 
952
 
 
953
    // some elements are not self nesting
 
954
    if (node->tag == at(p).tag) {
 
955
        if (node->isNotSelfNesting())
 
956
            p = at(p).parent;
 
957
    }
 
958
 
 
959
    // some elements are not allowed in certain contexts
 
960
    while (p && !node->allowedInContext(at(p).id)
 
961
           // ### make new styles aware of empty tags
 
962
           || at(p).id == Html_hr
 
963
           || at(p).id == Html_br
 
964
           || at(p).id == Html_img
 
965
       ) {
 
966
        p = at(p).parent;
 
967
    }
 
968
 
 
969
    node->parent = p;
 
970
 
 
971
    // makes it easier to traverse the tree, later
 
972
    nodes[p].children.append(nodes.count() - 1);
 
973
}
 
974
 
 
975
// sets all properties on the new node
 
976
void QTextHtmlParser::resolveNode()
 
977
{
 
978
    QTextHtmlParserNode *node = &nodes.last();
 
979
    const QTextHtmlParserNode *parent = &nodes.at(node->parent);
 
980
    node->initializeProperties(parent, this);
 
981
}
 
982
 
 
983
bool QTextHtmlParserNode::isNestedList(const QTextHtmlParser *parser) const
 
984
{
 
985
    if (!isListStart)
 
986
        return false;
 
987
 
 
988
    int p = parent;
 
989
    while (p) {
 
990
        if (parser->at(p).isListStart)
 
991
            return true;
 
992
        p = parser->at(p).parent;
 
993
    }
 
994
    return false;
 
995
}
 
996
 
 
997
void QTextHtmlParserNode::initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser)
 
998
{
 
999
    // inherit properties from parent element
 
1000
    isAnchor = parent->isAnchor;
 
1001
    fontItalic = parent->fontItalic;
 
1002
    fontUnderline = parent->fontUnderline;
 
1003
    fontOverline = parent->fontOverline;
 
1004
    fontStrikeOut = parent->fontStrikeOut;
 
1005
    fontFixedPitch = parent->fontFixedPitch;
 
1006
    fontFamily = parent->fontFamily;
 
1007
    hasFontPointSize = parent->hasFontPointSize;
 
1008
    fontPointSize = parent->fontPointSize;
 
1009
    fontWeight = parent->fontWeight;
 
1010
    color = parent->color;
 
1011
    verticalAlignment = parent->verticalAlignment;
 
1012
 
 
1013
    if (parent->id != Html_table) {
 
1014
        alignment = parent->alignment;
 
1015
    }
 
1016
    // we don't paint per-row background colors, yet. so as an
 
1017
    // exception inherit the background color here
 
1018
    if (parent->id == Html_tr && isTableCell) {
 
1019
        bgColor = parent->bgColor;
 
1020
    }
 
1021
 
 
1022
    listStyle = parent->listStyle;
 
1023
    anchorHref = parent->anchorHref;
 
1024
    // makes no sense to inherit that property, a named anchor is a single point
 
1025
    // in the document, which is set by the DocumentFragment
 
1026
    //anchorName = parent->anchorName;
 
1027
    wsm = parent->wsm;
 
1028
 
 
1029
    // initialize remaining properties
 
1030
    margin[QTextHtmlParser::MarginLeft] = 0;
 
1031
    margin[QTextHtmlParser::MarginRight] = 0;
 
1032
    margin[QTextHtmlParser::MarginTop] = 0;
 
1033
    margin[QTextHtmlParser::MarginBottom] = 0;
 
1034
    cssFloat = QTextFrameFormat::InFlow;
 
1035
 
 
1036
    const int oldFontPointSize = fontPointSize;
 
1037
 
 
1038
    // set element specific attributes
 
1039
    switch (id) {
 
1040
        case Html_a:
 
1041
            isAnchor = true;
 
1042
            break;
 
1043
        case Html_em:
 
1044
        case Html_i:
 
1045
            fontItalic = true;
 
1046
            break;
 
1047
        case Html_big:
 
1048
            fontPointSize = scaleFontPointSize(fontPointSize, -1 /*logical*/, 1 /*step*/);
 
1049
            break;
 
1050
        case Html_small:
 
1051
            fontPointSize = scaleFontPointSize(fontPointSize, -1 /*logical*/, -1 /*step*/);
 
1052
            break;
 
1053
        case Html_strong:
 
1054
        case Html_b:
 
1055
            fontWeight = QFont::Bold;
 
1056
            break;
 
1057
        case Html_h1:
 
1058
            fontWeight = QFont::Bold;
 
1059
            fontPointSize = scaleFontPointSize(DefaultFontSize, 6);
 
1060
            margin[QTextHtmlParser::MarginTop] = 18;
 
1061
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1062
            break;
 
1063
        case Html_h2:
 
1064
            fontWeight = QFont::Bold;
 
1065
            fontPointSize = scaleFontPointSize(DefaultFontSize, 5);
 
1066
            margin[QTextHtmlParser::MarginTop] = 16;
 
1067
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1068
            break;
 
1069
        case Html_h3:
 
1070
            fontWeight = QFont::Bold;
 
1071
            fontPointSize = scaleFontPointSize(DefaultFontSize, 4);
 
1072
            margin[QTextHtmlParser::MarginTop] = 14;
 
1073
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1074
            break;
 
1075
        case Html_h4:
 
1076
            fontWeight = QFont::Bold;
 
1077
            fontPointSize = scaleFontPointSize(DefaultFontSize, 3);
 
1078
            margin[QTextHtmlParser::MarginTop] = 12;
 
1079
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1080
            break;
 
1081
        case Html_h5:
 
1082
            fontWeight = QFont::Bold;
 
1083
            fontPointSize = scaleFontPointSize(DefaultFontSize, 2);
 
1084
            margin[QTextHtmlParser::MarginTop] = 12;
 
1085
            margin[QTextHtmlParser::MarginBottom] = 4;
 
1086
            break;
 
1087
        case Html_p:
 
1088
            margin[QTextHtmlParser::MarginTop] = 12;
 
1089
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1090
            break;
 
1091
        case Html_center:
 
1092
            alignment = Qt::AlignCenter;
 
1093
            break;
 
1094
        case Html_ul:
 
1095
            listStyle = QTextListFormat::ListDisc;
 
1096
            // nested lists don't have margins, except for the toplevel one
 
1097
            if (!isNestedList(parser)) {
 
1098
                margin[QTextHtmlParser::MarginTop] = 12;
 
1099
                margin[QTextHtmlParser::MarginBottom] = 12;
 
1100
            }
 
1101
            // no left margin as we use indenting instead
 
1102
            break;
 
1103
        case Html_ol:
 
1104
            listStyle = QTextListFormat::ListDecimal;
 
1105
            // nested lists don't have margins, except for the toplevel one
 
1106
            if (!isNestedList(parser)) {
 
1107
                margin[QTextHtmlParser::MarginTop] = 12;
 
1108
                margin[QTextHtmlParser::MarginBottom] = 12;
 
1109
            }
 
1110
            // no left margin as we use indenting instead
 
1111
            break;
 
1112
        case Html_code:
 
1113
        case Html_tt:
 
1114
            fontFamily = QString::fromLatin1("Courier New,courier");
 
1115
            // <tt> uses a fixed font, so set the property
 
1116
            fontFixedPitch = true;
 
1117
            break;
 
1118
        case Html_br:
 
1119
            text = QChar(QChar::LineSeparator);
 
1120
            break;
 
1121
        // ##### sub / sup
 
1122
        case Html_pre:
 
1123
            fontFamily = QString::fromLatin1("Courier New,courier");
 
1124
            wsm = WhiteSpacePre;
 
1125
            margin[QTextHtmlParser::MarginTop] = 12;
 
1126
            margin[QTextHtmlParser::MarginBottom] = 12;
 
1127
            // <pre> uses a fixed font
 
1128
            fontFixedPitch = true;
 
1129
            break;
 
1130
        case Html_blockquote:
 
1131
            margin[QTextHtmlParser::MarginLeft] = 40;
 
1132
            margin[QTextHtmlParser::MarginRight] = 40;
 
1133
            break;
 
1134
        case Html_dl:
 
1135
            margin[QTextHtmlParser::MarginTop] = 8;
 
1136
            margin[QTextHtmlParser::MarginBottom] = 8;
 
1137
            break;
 
1138
        case Html_dd:
 
1139
            margin[QTextHtmlParser::MarginLeft] = 30;
 
1140
            break;
 
1141
        case Html_u:
 
1142
            fontUnderline = true;
 
1143
            break;
 
1144
        case Html_s:
 
1145
            fontStrikeOut = true;
 
1146
            break;
 
1147
        case Html_nobr:
 
1148
            wsm = WhiteSpaceNoWrap;
 
1149
            break;
 
1150
        case Html_th:
 
1151
            fontWeight = QFont::Bold;
 
1152
            alignment = Qt::AlignCenter;
 
1153
            break;
 
1154
        case Html_td:
 
1155
            alignment = Qt::AlignLeft;
 
1156
            break;
 
1157
        case Html_sub:
 
1158
            verticalAlignment = QTextCharFormat::AlignSubScript;
 
1159
            break;
 
1160
        case Html_sup:
 
1161
            verticalAlignment = QTextCharFormat::AlignSuperScript;
 
1162
            break;
 
1163
        default: break;
 
1164
    }
 
1165
 
 
1166
    if (fontPointSize != oldFontPointSize)
 
1167
        hasFontPointSize = true;
 
1168
}
 
1169
 
 
1170
static bool setIntAttribute(int *destination, const QString &value)
 
1171
{
 
1172
    bool ok = false;
 
1173
    int val = value.toInt(&ok);
 
1174
    if (ok)
 
1175
        *destination = val;
 
1176
 
 
1177
    return ok;
 
1178
}
 
1179
 
 
1180
static void setWidthAttribute(QTextLength *width, QString value)
 
1181
{
 
1182
    int intVal;
 
1183
    bool ok = false;
 
1184
    intVal = value.toInt(&ok);
 
1185
    if (ok) {
 
1186
        *width = QTextLength(QTextLength::FixedLength, intVal);
 
1187
    } else {
 
1188
        value = value.trimmed();
 
1189
        if (!value.isEmpty() && value.at(value.length() - 1) == QLatin1Char('%')) {
 
1190
            value.chop(1);
 
1191
            intVal = value.toInt(&ok);
 
1192
            if (ok)
 
1193
                *width = QTextLength(QTextLength::PercentageLength, intVal);
 
1194
        }
 
1195
    }
 
1196
}
 
1197
 
 
1198
static QTextHtmlParserNode::WhiteSpaceMode stringToWhiteSpaceMode(const QString &s)
 
1199
{
 
1200
    if (s == QLatin1String("normal"))
 
1201
        return QTextHtmlParserNode::WhiteSpaceNormal;
 
1202
    else if (s == QLatin1String("pre"))
 
1203
        return QTextHtmlParserNode::WhiteSpacePre;
 
1204
    else if (s == QLatin1String("nowrap"))
 
1205
        return QTextHtmlParserNode::WhiteSpaceNoWrap;
 
1206
    else if (s == QLatin1String("pre-wrap"))
 
1207
        return QTextHtmlParserNode::WhiteSpacePreWrap;
 
1208
 
 
1209
    return QTextHtmlParserNode::WhiteSpaceModeUndefined;
 
1210
}
 
1211
 
 
1212
void QTextHtmlParser::parseAttributes()
 
1213
{
 
1214
    // local state variable for qt3 textedit mode
 
1215
    bool seenQt3Richtext = false;
 
1216
 
 
1217
    QTextHtmlParserNode *node = &nodes.last();
 
1218
    while (pos < len) {
 
1219
        eatSpace();
 
1220
        if (hasPrefix(QLatin1Char('>')) || hasPrefix(QLatin1Char('/')))
 
1221
            break;
 
1222
        QString key = parseWord().toLower();
 
1223
        QString value = QLatin1String("1");
 
1224
        if (key.size() == 0)
 
1225
            break;
 
1226
        eatSpace();
 
1227
        if (hasPrefix(QLatin1Char('='))){
 
1228
            pos++;
 
1229
            eatSpace();
 
1230
            value = parseWord();
 
1231
        }
 
1232
        if (value.size() == 0)
 
1233
            continue;
 
1234
        if (node->id == Html_font) {
 
1235
            // the infamous font tag
 
1236
            if (key == QLatin1String("size") && value.size()) {
 
1237
                int n = value.toInt();
 
1238
                if (value.at(0) == QLatin1Char('+') || value.at(0) == QLatin1Char('-'))
 
1239
                    n += 3;
 
1240
                node->fontPointSize = scaleFontPointSize(DefaultFontSize, n);
 
1241
                node->hasFontPointSize = true;
 
1242
            } else if (key == QLatin1String("face")) {
 
1243
                node->fontFamily = value;
 
1244
            } else if (key == QLatin1String("color")) {
 
1245
                node->color.setNamedColor(value);
 
1246
            }
 
1247
        } else if (node->id == Html_ol
 
1248
                   || node->id == Html_ul) {
 
1249
            if (key == QLatin1String("type")) {
 
1250
                node->hasOwnListStyle = true;
 
1251
                if (value == QLatin1String("1")) {
 
1252
                    node->listStyle = QTextListFormat::ListDecimal;
 
1253
                } else if (value == QLatin1String("a")) {
 
1254
                    node->listStyle = QTextListFormat::ListLowerAlpha;
 
1255
                } else if (value == QLatin1String("A")) {
 
1256
                    node->listStyle = QTextListFormat::ListUpperAlpha;
 
1257
                } else {
 
1258
                    value = value.toLower();
 
1259
                    if (value == QLatin1String("square"))
 
1260
                        node->listStyle = QTextListFormat::ListSquare;
 
1261
                    else if (value == QLatin1String("disc"))
 
1262
                        node->listStyle = QTextListFormat::ListDisc;
 
1263
                    else if (value == QLatin1String("circle"))
 
1264
                        node->listStyle = QTextListFormat::ListCircle;
 
1265
                }
 
1266
            }
 
1267
        } else if (node->isAnchor) {
 
1268
            if (key == QLatin1String("href"))
 
1269
                node->anchorHref = value;
 
1270
            else if (key == QLatin1String("name"))
 
1271
                node->anchorName = value;
 
1272
        } else if (node->id == Html_img) {
 
1273
            if (key == QLatin1String("src") || key == QLatin1String("source")) {
 
1274
                node->imageName = value;
 
1275
            } else if (key == QLatin1String("width")) {
 
1276
                setIntAttribute(&node->imageWidth, value);
 
1277
            } else if (key == QLatin1String("height")) {
 
1278
                setIntAttribute(&node->imageHeight, value);
 
1279
            }
 
1280
        } else if (node->id == Html_tr || node->id == Html_body) {
 
1281
            if (key == QLatin1String("bgcolor"))
 
1282
                node->bgColor.setNamedColor(value);
 
1283
        } else if (node->isTableCell) {
 
1284
            if (key == QLatin1String("width")) {
 
1285
                setWidthAttribute(&node->width, value);
 
1286
            } else if (key == QLatin1String("bgcolor")) {
 
1287
                node->bgColor.setNamedColor(value);
 
1288
            } else if (key == QLatin1String("rowspan")) {
 
1289
                setIntAttribute(&node->tableCellRowSpan, value);
 
1290
            } else if (key == QLatin1String("colspan")) {
 
1291
                setIntAttribute(&node->tableCellColSpan, value);
 
1292
            }
 
1293
        } else if (node->id == Html_table) {
 
1294
            if (key == QLatin1String("border")) {
 
1295
                setIntAttribute(&node->tableBorder, value);
 
1296
            } else if (key == QLatin1String("bgcolor")) {
 
1297
                node->bgColor.setNamedColor(value);
 
1298
            } else if (key == QLatin1String("cellspacing")) {
 
1299
                setIntAttribute(&node->tableCellSpacing, value);
 
1300
            } else if (key == QLatin1String("cellpadding")) {
 
1301
                setIntAttribute(&node->tableCellPadding, value);
 
1302
            } else if (key == QLatin1String("width")) {
 
1303
                setWidthAttribute(&node->width, value);
 
1304
            }
 
1305
        } else if (node->id == Html_meta) {
 
1306
            if (key == QLatin1String("name")
 
1307
                && value == QLatin1String("qrichtext")) {
 
1308
                seenQt3Richtext = true;
 
1309
            }
 
1310
 
 
1311
            if (key == QLatin1String("content")
 
1312
                && value == QLatin1String("1")
 
1313
                && seenQt3Richtext) {
 
1314
 
 
1315
                textEditMode = true;
 
1316
            }
 
1317
        }
 
1318
 
 
1319
        if (key == QLatin1String("style")) {
 
1320
            // style parser taken from Qt 3
 
1321
            QString a = value;
 
1322
            int count = a.count(';')+1;
 
1323
            for (int s = 0; s < count; s++) {
 
1324
                QString style = a.section(';', s, s).trimmed();
 
1325
                if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
 
1326
                    node->fontPointSize = int(style.mid(10, style.length() - 12).trimmed().toDouble());
 
1327
                    node->hasFontPointSize = true;
 
1328
                } if (style.startsWith(QLatin1String("font-style:"))) {
 
1329
                    QString s = style.mid(11).trimmed();
 
1330
                    if (s == QLatin1String("normal"))
 
1331
                        node->fontItalic = false;
 
1332
                    else if (s == QLatin1String("italic") || s == QLatin1String("oblique"))
 
1333
                        node->fontItalic = true;
 
1334
                } else if (style.startsWith(QLatin1String("font-weight:"))) {
 
1335
                    QString s = style.mid(12);
 
1336
                    bool ok = true;
 
1337
                    int n = s.toInt(&ok);
 
1338
                    if (ok)
 
1339
                        node->fontWeight = n/8;
 
1340
                } else if (style.startsWith(QLatin1String("font-family:"))) {
 
1341
                    node->fontFamily = style.mid(12).trimmed();
 
1342
                } else if (style.startsWith(QLatin1String("text-decoration:"))) {
 
1343
                    QString s = style.mid(16);
 
1344
                    node->fontUnderline = static_cast<bool>(s.contains("underline"));
 
1345
                    node->fontOverline = static_cast<bool>(s.contains("overline"));
 
1346
                    node->fontStrikeOut = static_cast<bool>(s.contains("line-through"));
 
1347
#if 0
 
1348
                } else if (style.startsWith(QLatin1String("vertical-align:"))) {
 
1349
                    QString s = style.mid(15).trimmed();
 
1350
                    if (s == QLatin1String("sub"))
 
1351
                        format.setVAlign(QTextFormat::AlignSubScript);
 
1352
                    else if (s == QLatin1String("super"))
 
1353
                        format.setVAlign(QTextFormat::AlignSuperScript);
 
1354
                    else
 
1355
                        format.setVAlign(QTextFormat::AlignNormal);
 
1356
#endif
 
1357
                } else if (style.startsWith(QLatin1String("color:"))) {
 
1358
                    QString s = style.mid(6).trimmed();
 
1359
                    if (s.startsWith(QLatin1String("rgb("))
 
1360
                        && s.at(s.length() - 1) == QLatin1Char(')')) {
 
1361
 
 
1362
                        s.chop(1);
 
1363
                        s.remove(0, 4);
 
1364
 
 
1365
                        const QStringList rgb = s.split(',');
 
1366
                        if (rgb.count() == 3)
 
1367
                            node->color.setRgb(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt());
 
1368
                        else
 
1369
                            node->color = QColor();
 
1370
                    } else {
 
1371
                        node->color.setNamedColor(style.mid(6));
 
1372
                    }
 
1373
                } else if (style.startsWith(QLatin1String("float:"))) {
 
1374
                    QString s = style.mid(6).trimmed();
 
1375
                    node->cssFloat = QTextFrameFormat::InFlow;
 
1376
                    if (s == QLatin1String("left"))
 
1377
                        node->cssFloat = QTextFrameFormat::FloatLeft;
 
1378
                    else if (s == QLatin1String("right"))
 
1379
                        node->cssFloat = QTextFrameFormat::FloatRight;
 
1380
                } else if (style.startsWith(QLatin1String("-qt-block-indent:"))) {
 
1381
                    const QString s = style.mid(17).trimmed();
 
1382
                    if (setIntAttribute(&node->cssBlockIndent, s))
 
1383
                        node->hasCssBlockIndent = true;
 
1384
                } else if (style.startsWith(QLatin1String("text-indent:")) && style.endsWith(QLatin1String("px"))) {
 
1385
                    node->text_indent = style.mid(12, style.length() - 14).trimmed().toDouble();
 
1386
                } else if (style.startsWith(QLatin1String("-qt-list-indent:"))) {
 
1387
                    const QString s = style.mid(16).trimmed();
 
1388
                    if (setIntAttribute(&node->cssListIndent, s)) {
 
1389
                        node->hasCssListIndent = true;
 
1390
                    }
 
1391
                } else if (style.startsWith(QLatin1String("-qt-paragraph-type:"))) {
 
1392
                    const QString s = style.mid(19).trimmed().toLower();
 
1393
                    if (s == QLatin1String("empty"))
 
1394
                        node->isEmptyParagraph = true;
 
1395
                } else if (style.startsWith(QLatin1String("white-space:"))) {
 
1396
                    const QString s = style.mid(12).trimmed().toLower();
 
1397
                    QTextHtmlParserNode::WhiteSpaceMode ws = stringToWhiteSpaceMode(s);
 
1398
                    if (ws != QTextHtmlParserNode::WhiteSpaceModeUndefined)
 
1399
                        node->wsm = ws;
 
1400
                } else if (style.startsWith(QLatin1String("margin-top:")) && style.endsWith("px")) {
 
1401
                    const QString s = style.mid(11, style.length() - 13).trimmed();
 
1402
                    setIntAttribute(&node->margin[MarginTop], s);
 
1403
                } else if (style.startsWith(QLatin1String("margin-bottom:")) && style.endsWith("px")) {
 
1404
                    const QString s = style.mid(14, style.length() - 16).trimmed();
 
1405
                    setIntAttribute(&node->margin[MarginBottom], s);
 
1406
                } else if (style.startsWith(QLatin1String("margin-left:")) && style.endsWith("px")) {
 
1407
                    const QString s = style.mid(12, style.length() - 14).trimmed();
 
1408
                    setIntAttribute(&node->margin[MarginLeft], s);
 
1409
                } else if (style.startsWith(QLatin1String("margin-right:")) && style.endsWith("px")) {
 
1410
                    const QString s = style.mid(13, style.length() - 15).trimmed();
 
1411
                    setIntAttribute(&node->margin[MarginRight], s);
 
1412
                } else if (style.startsWith(QLatin1String("vertical-align:"))) {
 
1413
                    const QString s = style.mid(15, style.length() - 15).trimmed();
 
1414
                    if (s == "sub")
 
1415
                        node->verticalAlignment = QTextCharFormat::AlignSubScript;
 
1416
                    else if (s == "super")
 
1417
                        node->verticalAlignment = QTextCharFormat::AlignSuperScript;
 
1418
                    else
 
1419
                        node->verticalAlignment = QTextCharFormat::AlignNormal;
 
1420
                }
 
1421
            }
 
1422
        } else if (key == QLatin1String("align")) {
 
1423
            if (value == QLatin1String("left"))
 
1424
                node->alignment = Qt::AlignLeft|Qt::AlignAbsolute;
 
1425
            else if (value == QLatin1String("right"))
 
1426
                node->alignment = Qt::AlignRight|Qt::AlignAbsolute;
 
1427
            else if (value == QLatin1String("center"))
 
1428
                node->alignment = Qt::AlignHCenter;
 
1429
            else if (value == QLatin1String("justify"))
 
1430
                node->alignment = Qt::AlignJustify;
 
1431
 
 
1432
            // HTML4 compat
 
1433
            if (node->id == Html_img) {
 
1434
                if (node->alignment == Qt::AlignLeft)
 
1435
                    node->cssFloat = QTextFrameFormat::FloatLeft;
 
1436
                else if (node->alignment == Qt::AlignRight)
 
1437
                    node->cssFloat = QTextFrameFormat::FloatRight;
 
1438
            }
 
1439
        } else if (key == QLatin1String("dir")) {
 
1440
            if (value == QLatin1String("ltr"))
 
1441
                node->direction = Qt::LeftToRight;
 
1442
            else if (value == QLatin1String("rtl"))
 
1443
                node->direction = Qt::RightToLeft;
 
1444
        }
 
1445
    }
 
1446
}
 
1447