Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtexthtmlparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtexthtmlparser_p.h"
5
6#include <qbytearray.h>
7#include <qstack.h>
8#include <qdebug.h>
9#include <qthread.h>
10#include <qguiapplication.h>
11
12#include "qtextdocument.h"
13#include "qtextformat_p.h"
14#include "qtextdocument_p.h"
15#include "qtextcursor.h"
16#include "qfont_p.h"
17
18#include <algorithm>
19
20#ifndef QT_NO_TEXTHTMLPARSER
21
23
24using namespace Qt::StringLiterals;
25
26// see also tst_qtextdocumentfragment.cpp
27#define MAX_ENTITY 258
28static const struct QTextHtmlEntity { const char name[9]; char16_t code; } entities[]= {
29 { "AElig", 0x00c6 },
30 { "AMP", 38 },
31 { "Aacute", 0x00c1 },
32 { "Acirc", 0x00c2 },
33 { "Agrave", 0x00c0 },
34 { "Alpha", 0x0391 },
35 { "Aring", 0x00c5 },
36 { "Atilde", 0x00c3 },
37 { "Auml", 0x00c4 },
38 { "Beta", 0x0392 },
39 { "Ccedil", 0x00c7 },
40 { "Chi", 0x03a7 },
41 { "Dagger", 0x2021 },
42 { "Delta", 0x0394 },
43 { "ETH", 0x00d0 },
44 { "Eacute", 0x00c9 },
45 { "Ecirc", 0x00ca },
46 { "Egrave", 0x00c8 },
47 { "Epsilon", 0x0395 },
48 { "Eta", 0x0397 },
49 { "Euml", 0x00cb },
50 { "GT", 62 },
51 { "Gamma", 0x0393 },
52 { "Iacute", 0x00cd },
53 { "Icirc", 0x00ce },
54 { "Igrave", 0x00cc },
55 { "Iota", 0x0399 },
56 { "Iuml", 0x00cf },
57 { "Kappa", 0x039a },
58 { "LT", 60 },
59 { "Lambda", 0x039b },
60 { "Mu", 0x039c },
61 { "Ntilde", 0x00d1 },
62 { "Nu", 0x039d },
63 { "OElig", 0x0152 },
64 { "Oacute", 0x00d3 },
65 { "Ocirc", 0x00d4 },
66 { "Ograve", 0x00d2 },
67 { "Omega", 0x03a9 },
68 { "Omicron", 0x039f },
69 { "Oslash", 0x00d8 },
70 { "Otilde", 0x00d5 },
71 { "Ouml", 0x00d6 },
72 { "Phi", 0x03a6 },
73 { "Pi", 0x03a0 },
74 { "Prime", 0x2033 },
75 { "Psi", 0x03a8 },
76 { "QUOT", 34 },
77 { "Rho", 0x03a1 },
78 { "Scaron", 0x0160 },
79 { "Sigma", 0x03a3 },
80 { "THORN", 0x00de },
81 { "Tau", 0x03a4 },
82 { "Theta", 0x0398 },
83 { "Uacute", 0x00da },
84 { "Ucirc", 0x00db },
85 { "Ugrave", 0x00d9 },
86 { "Upsilon", 0x03a5 },
87 { "Uuml", 0x00dc },
88 { "Xi", 0x039e },
89 { "Yacute", 0x00dd },
90 { "Yuml", 0x0178 },
91 { "Zeta", 0x0396 },
92 { "aacute", 0x00e1 },
93 { "acirc", 0x00e2 },
94 { "acute", 0x00b4 },
95 { "aelig", 0x00e6 },
96 { "agrave", 0x00e0 },
97 { "alefsym", 0x2135 },
98 { "alpha", 0x03b1 },
99 { "amp", 38 },
100 { "and", 0x22a5 },
101 { "ang", 0x2220 },
102 { "apos", 0x0027 },
103 { "aring", 0x00e5 },
104 { "asymp", 0x2248 },
105 { "atilde", 0x00e3 },
106 { "auml", 0x00e4 },
107 { "bdquo", 0x201e },
108 { "beta", 0x03b2 },
109 { "brvbar", 0x00a6 },
110 { "bull", 0x2022 },
111 { "cap", 0x2229 },
112 { "ccedil", 0x00e7 },
113 { "cedil", 0x00b8 },
114 { "cent", 0x00a2 },
115 { "chi", 0x03c7 },
116 { "circ", 0x02c6 },
117 { "clubs", 0x2663 },
118 { "cong", 0x2245 },
119 { "copy", 0x00a9 },
120 { "crarr", 0x21b5 },
121 { "cup", 0x222a },
122 { "curren", 0x00a4 },
123 { "dArr", 0x21d3 },
124 { "dagger", 0x2020 },
125 { "darr", 0x2193 },
126 { "deg", 0x00b0 },
127 { "delta", 0x03b4 },
128 { "diams", 0x2666 },
129 { "divide", 0x00f7 },
130 { "eacute", 0x00e9 },
131 { "ecirc", 0x00ea },
132 { "egrave", 0x00e8 },
133 { "empty", 0x2205 },
134 { "emsp", 0x2003 },
135 { "ensp", 0x2002 },
136 { "epsilon", 0x03b5 },
137 { "equiv", 0x2261 },
138 { "eta", 0x03b7 },
139 { "eth", 0x00f0 },
140 { "euml", 0x00eb },
141 { "euro", 0x20ac },
142 { "exist", 0x2203 },
143 { "fnof", 0x0192 },
144 { "forall", 0x2200 },
145 { "frac12", 0x00bd },
146 { "frac14", 0x00bc },
147 { "frac34", 0x00be },
148 { "frasl", 0x2044 },
149 { "gamma", 0x03b3 },
150 { "ge", 0x2265 },
151 { "gt", 62 },
152 { "hArr", 0x21d4 },
153 { "harr", 0x2194 },
154 { "hearts", 0x2665 },
155 { "hellip", 0x2026 },
156 { "iacute", 0x00ed },
157 { "icirc", 0x00ee },
158 { "iexcl", 0x00a1 },
159 { "igrave", 0x00ec },
160 { "image", 0x2111 },
161 { "infin", 0x221e },
162 { "int", 0x222b },
163 { "iota", 0x03b9 },
164 { "iquest", 0x00bf },
165 { "isin", 0x2208 },
166 { "iuml", 0x00ef },
167 { "kappa", 0x03ba },
168 { "lArr", 0x21d0 },
169 { "lambda", 0x03bb },
170 { "lang", 0x2329 },
171 { "laquo", 0x00ab },
172 { "larr", 0x2190 },
173 { "lceil", 0x2308 },
174 { "ldquo", 0x201c },
175 { "le", 0x2264 },
176 { "lfloor", 0x230a },
177 { "lowast", 0x2217 },
178 { "loz", 0x25ca },
179 { "lrm", 0x200e },
180 { "lsaquo", 0x2039 },
181 { "lsquo", 0x2018 },
182 { "lt", 60 },
183 { "macr", 0x00af },
184 { "mdash", 0x2014 },
185 { "micro", 0x00b5 },
186 { "middot", 0x00b7 },
187 { "minus", 0x2212 },
188 { "mu", 0x03bc },
189 { "nabla", 0x2207 },
190 { "nbsp", 0x00a0 },
191 { "ndash", 0x2013 },
192 { "ne", 0x2260 },
193 { "ni", 0x220b },
194 { "not", 0x00ac },
195 { "notin", 0x2209 },
196 { "nsub", 0x2284 },
197 { "ntilde", 0x00f1 },
198 { "nu", 0x03bd },
199 { "oacute", 0x00f3 },
200 { "ocirc", 0x00f4 },
201 { "oelig", 0x0153 },
202 { "ograve", 0x00f2 },
203 { "oline", 0x203e },
204 { "omega", 0x03c9 },
205 { "omicron", 0x03bf },
206 { "oplus", 0x2295 },
207 { "or", 0x22a6 },
208 { "ordf", 0x00aa },
209 { "ordm", 0x00ba },
210 { "oslash", 0x00f8 },
211 { "otilde", 0x00f5 },
212 { "otimes", 0x2297 },
213 { "ouml", 0x00f6 },
214 { "para", 0x00b6 },
215 { "part", 0x2202 },
216 { "percnt", 0x0025 },
217 { "permil", 0x2030 },
218 { "perp", 0x22a5 },
219 { "phi", 0x03c6 },
220 { "pi", 0x03c0 },
221 { "piv", 0x03d6 },
222 { "plusmn", 0x00b1 },
223 { "pound", 0x00a3 },
224 { "prime", 0x2032 },
225 { "prod", 0x220f },
226 { "prop", 0x221d },
227 { "psi", 0x03c8 },
228 { "quot", 34 },
229 { "rArr", 0x21d2 },
230 { "radic", 0x221a },
231 { "rang", 0x232a },
232 { "raquo", 0x00bb },
233 { "rarr", 0x2192 },
234 { "rceil", 0x2309 },
235 { "rdquo", 0x201d },
236 { "real", 0x211c },
237 { "reg", 0x00ae },
238 { "rfloor", 0x230b },
239 { "rho", 0x03c1 },
240 { "rlm", 0x200f },
241 { "rsaquo", 0x203a },
242 { "rsquo", 0x2019 },
243 { "sbquo", 0x201a },
244 { "scaron", 0x0161 },
245 { "sdot", 0x22c5 },
246 { "sect", 0x00a7 },
247 { "shy", 0x00ad },
248 { "sigma", 0x03c3 },
249 { "sigmaf", 0x03c2 },
250 { "sim", 0x223c },
251 { "spades", 0x2660 },
252 { "sub", 0x2282 },
253 { "sube", 0x2286 },
254 { "sum", 0x2211 },
255 { "sup", 0x2283 },
256 { "sup1", 0x00b9 },
257 { "sup2", 0x00b2 },
258 { "sup3", 0x00b3 },
259 { "supe", 0x2287 },
260 { "szlig", 0x00df },
261 { "tau", 0x03c4 },
262 { "there4", 0x2234 },
263 { "theta", 0x03b8 },
264 { "thetasym", 0x03d1 },
265 { "thinsp", 0x2009 },
266 { "thorn", 0x00fe },
267 { "tilde", 0x02dc },
268 { "times", 0x00d7 },
269 { "trade", 0x2122 },
270 { "uArr", 0x21d1 },
271 { "uacute", 0x00fa },
272 { "uarr", 0x2191 },
273 { "ucirc", 0x00fb },
274 { "ugrave", 0x00f9 },
275 { "uml", 0x00a8 },
276 { "upsih", 0x03d2 },
277 { "upsilon", 0x03c5 },
278 { "uuml", 0x00fc },
279 { "weierp", 0x2118 },
280 { "xi", 0x03be },
281 { "yacute", 0x00fd },
282 { "yen", 0x00a5 },
283 { "yuml", 0x00ff },
284 { "zeta", 0x03b6 },
285 { "zwj", 0x200d },
286 { "zwnj", 0x200c }
288static_assert(MAX_ENTITY == sizeof entities / sizeof *entities);
289
290#if defined(Q_CC_MSVC_ONLY) && _MSC_VER < 1600
291bool operator<(const QTextHtmlEntity &entity1, const QTextHtmlEntity &entity2)
292{
293 return QLatin1StringView(entity1.name) < QLatin1StringView(entity2.name);
294}
295#endif
296
297static bool operator<(QStringView entityStr, const QTextHtmlEntity &entity)
298{
299 return entityStr < QLatin1StringView(entity.name);
300}
301
302static bool operator<(const QTextHtmlEntity &entity, QStringView entityStr)
303{
304 return QLatin1StringView(entity.name) < entityStr;
305}
306
308{
309 const QTextHtmlEntity *start = &entities[0];
311 const QTextHtmlEntity *e = std::lower_bound(start, end, entity);
312 if (e == end || (entity < *e))
313 return QChar();
314 return e->code;
315}
316
317static const ushort windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
318 0x20ac, // 0x80
319 0x0081, // 0x81 direct mapping
320 0x201a, // 0x82
321 0x0192, // 0x83
322 0x201e, // 0x84
323 0x2026, // 0x85
324 0x2020, // 0x86
325 0x2021, // 0x87
326 0x02C6, // 0x88
327 0x2030, // 0x89
328 0x0160, // 0x8A
329 0x2039, // 0x8B
330 0x0152, // 0x8C
331 0x008D, // 0x8D direct mapping
332 0x017D, // 0x8E
333 0x008F, // 0x8F directmapping
334 0x0090, // 0x90 directmapping
335 0x2018, // 0x91
336 0x2019, // 0x92
337 0x201C, // 0x93
338 0X201D, // 0x94
339 0x2022, // 0x95
340 0x2013, // 0x96
341 0x2014, // 0x97
342 0x02DC, // 0x98
343 0x2122, // 0x99
344 0x0161, // 0x9A
345 0x203A, // 0x9B
346 0x0153, // 0x9C
347 0x009D, // 0x9D direct mapping
348 0x017E, // 0x9E
349 0x0178 // 0x9F
350};
351
352// the displayMode value is according to the what are blocks in the piecetable, not
353// what the w3c defines.
392 { "qt", Html_body /*deliberate mapping*/, QTextHtmlElement::DisplayBlock },
414};
415
417{
418 return str < QLatin1StringView(e.name);
419}
420
422{
423 return QLatin1StringView(e.name) < str;
424}
425
427{
428 const QTextHtmlElement *start = &elements[0];
430 const QTextHtmlElement *e = std::lower_bound(start, end, element);
431 if ((e == end) || (element < *e))
432 return nullptr;
433 return e;
434}
435
437{
438 const QTextHtmlElement *e = lookupElementHelper(element);
439 if (!e)
440 return -1;
441 return e->id;
442}
443
444// quotes newlines as "\\n"
446{
447 QString n = s;
448 n.replace(u'\n', "\\n"_L1);
449 return n;
450}
451
453 : parent(0), id(Html_unknown),
454 cssFloat(QTextFrameFormat::InFlow), hasOwnListStyle(false), hasOwnLineHeightType(false), hasLineHeightMultiplier(false),
455 hasCssListIndent(false), isEmptyParagraph(false), isTextFrame(false), isRootFrame(false),
456 displayMode(QTextHtmlElement::DisplayInline), hasHref(false),
457 listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0),
458 tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0),
459 borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset),
460 borderCollapse(false),
461 userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined)
462{
467
468 for (int i = 0; i < 4; ++i) {
470 tableCellBorder[i] = 0;
472 }
473}
474
476{
477 for (int i = 0; i < count(); ++i) {
478 qDebug().nospace() << qPrintable(QString(depth(i) * 4, u' '))
479 << qPrintable(at(i).tag) << ':'
480 << quoteNewline(at(i).text);
481 }
482}
483
485{
486 QTextHtmlParserNode *lastNode = nodes.last();
487 QTextHtmlParserNode *newNode = nullptr;
488
489 bool reuseLastNode = true;
490
491 if (nodes.size() == 1) {
492 reuseLastNode = false;
493 } else if (lastNode->tag.isEmpty()) {
494
495 if (lastNode->text.isEmpty()) {
496 reuseLastNode = true;
497 } else { // last node is a text node (empty tag) with some text
498
499 if (lastNode->text.size() == 1 && lastNode->text.at(0).isSpace()) {
500
501 int lastSibling = count() - 2;
502 while (lastSibling
503 && at(lastSibling).parent != lastNode->parent
504 && at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
505 lastSibling = at(lastSibling).parent;
506 }
507
508 if (at(lastSibling).displayMode == QTextHtmlElement::DisplayInline) {
509 reuseLastNode = false;
510 } else {
511 reuseLastNode = true;
512 }
513 } else {
514 // text node with real (non-whitespace) text -> nothing to re-use
515 reuseLastNode = false;
516 }
517
518 }
519
520 } else {
521 // last node had a proper tag -> nothing to re-use
522 reuseLastNode = false;
523 }
524
525 if (reuseLastNode) {
526 newNode = lastNode;
527 newNode->tag.clear();
528 newNode->text.clear();
530 } else {
532 newNode = nodes.last();
533 }
534
535 newNode->parent = parent;
536 return newNode;
537}
538
539void QTextHtmlParser::parse(const QString &text, const QTextDocument *_resourceProvider)
540{
542 nodes.clear();
544 txt = text;
545 pos = 0;
546 len = txt.size();
547 textEditMode = false;
548 resourceProvider = _resourceProvider;
549 parse();
550 //dumpHtml();
551}
552
554{
555 int depth = 0;
556 while (i) {
557 i = at(i).parent;
558 ++depth;
559 }
560 return depth;
561}
562
563int QTextHtmlParser::margin(int i, int mar) const {
564 int m = 0;
565 const QTextHtmlParserNode *node;
566 if (mar == MarginLeft
567 || mar == MarginRight) {
568 while (i) {
569 node = &at(i);
570 if (!node->isBlock() && node->id != Html_table)
571 break;
572 if (node->isTableCell())
573 break;
574 m += node->margin[mar];
575 i = node->parent;
576 }
577 }
578 return m;
579}
580
582{
583 if (!i)
584 return 0;
585 return at(i).margin[MarginTop];
586}
587
589{
590 if (!i)
591 return 0;
592 return at(i).margin[MarginBottom];
593}
594
596{
597 while (pos < len && txt.at(pos).isSpace() && txt.at(pos) != QChar::ParagraphSeparator)
598 pos++;
599}
600
602{
603 while (pos < len) {
604 QChar c = txt.at(pos++);
605 if (c == u'<') {
606 parseTag();
607 } else if (c == u'&') {
608 nodes.last()->text += parseEntity();
609 } else {
610 nodes.last()->text += c;
611 }
612 }
613}
614
615// parses a tag after "<"
617{
618 eatSpace();
619
620 // handle comments and other exclamation mark declarations
621 if (hasPrefix(u'!')) {
625 && !textEditMode)
626 eatSpace();
627 return;
628 }
629
630 // if close tag just close
631 if (hasPrefix(u'/')) {
632 if (nodes.last()->id == Html_style) {
633#ifndef QT_NO_CSSPARSER
634 QCss::Parser parser(nodes.constLast()->text);
635 QCss::StyleSheet sheet;
637 parser.parse(&sheet, Qt::CaseInsensitive);
638 inlineStyleSheets.append(sheet);
639 resolveStyleSheetImports(sheet);
640#endif
641 }
643 return;
644 }
645
646 int p = last();
647 while (p && at(p).tag.size() == 0)
648 p = at(p).parent;
649
651
652 // parse tag name
653 node->tag = parseWord().toLower();
654
655 const QTextHtmlElement *elem = lookupElementHelper(node->tag);
656 if (elem) {
657 node->id = elem->id;
658 node->displayMode = elem->displayMode;
659 } else {
660 node->id = Html_unknown;
661 }
662
663 node->attributes.clear();
664 // _need_ at least one space after the tag name, otherwise there can't be attributes
665 if (pos < len && txt.at(pos).isSpace())
666 node->attributes = parseAttributes();
667
668 // resolveParent() may have to change the order in the tree and
669 // insert intermediate nodes for buggy HTML, so re-initialize the 'node'
670 // pointer through the return value
671 node = resolveParent();
672 resolveNode();
673
674#ifndef QT_NO_CSSPARSER
675 const int nodeIndex = nodes.size() - 1; // this new node is always the last
676 node->applyCssDeclarations(declarationsForNode(nodeIndex), resourceProvider);
677#endif
679
680 // finish tag
681 bool tagClosed = false;
682 while (pos < len && txt.at(pos) != u'>') {
683 if (txt.at(pos) == u'/')
684 tagClosed = true;
685
686 pos++;
687 }
688 pos++;
689
690 // in a white-space preserving environment strip off a initial newline
691 // since the element itself already generates a newline
695 && node->isBlock()) {
696 if (pos < len - 1 && txt.at(pos) == u'\n')
697 ++pos;
698 }
699
700 if (node->mayNotHaveChildren() || tagClosed) {
701 newNode(node->parent);
702 resolveNode();
703 }
704}
705
706// parses a tag beginning with "/"
708{
709 ++pos;
711 while (pos < len) {
712 QChar c = txt.at(pos++);
713 if (c == u'>')
714 break;
715 }
716
717 // find corresponding open node
718 int p = last();
719 if (p > 0
720 && at(p - 1).tag == tag
721 && at(p - 1).mayNotHaveChildren())
722 p--;
723
724 while (p && at(p).tag != tag)
725 p = at(p).parent;
726
727 // simply ignore the tag if we can't find
728 // a corresponding open node, for broken
729 // html such as <font>blah</font></font>
730 if (!p)
731 return;
732
733 // in a white-space preserving environment strip off a trailing newline
734 // since the closing of the opening block element will automatically result
735 // in a new block for elements following the <pre>
736 // ...foo\n</pre><p>blah -> foo</pre><p>blah
740 && at(p).isBlock()) {
741 if (at(last()).text.endsWith(u'\n'))
742 nodes[last()]->text.chop(1);
743 }
744
745 newNode(at(p).parent);
746 resolveNode();
747}
748
749// parses a tag beginning with "!"
751{
752 ++pos;
753 if (hasPrefix(u'-') && hasPrefix(u'-', 1)) {
754 pos += 2;
755 // eat comments
756 int end = txt.indexOf("-->"_L1, pos);
757 pos = (end >= 0 ? end + 3 : len);
758 } else {
759 // eat internal tags
760 while (pos < len) {
761 QChar c = txt.at(pos++);
762 if (c == u'>')
763 break;
764 }
765 }
766}
767
769{
770 QChar resolved = resolveEntity(entity);
771 if (!resolved.isNull())
772 return QString(resolved);
773
774 if (entity.size() > 1 && entity.at(0) == u'#') {
775 entity = entity.mid(1); // removing leading #
776
777 int base = 10;
778 bool ok = false;
779
780 if (entity.at(0).toLower() == u'x') { // hex entity?
781 entity = entity.mid(1);
782 base = 16;
783 }
784
785 uint uc = entity.toUInt(&ok, base);
786 if (ok) {
787 if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0])))
788 uc = windowsLatin1ExtendedCharacters[uc - 0x80];
789 return QStringView{QChar::fromUcs4(uc)}.toString();
790 }
791 }
792 return {};
793}
794
795// parses an entity after "&", and returns it
797{
798 const int recover = pos;
799 int entityLen = 0;
800 while (pos < len) {
801 QChar c = txt.at(pos++);
802 if (c.isSpace() || pos - recover > 9) {
803 goto error;
804 }
805 if (c == u';')
806 break;
807 ++entityLen;
808 }
809 if (entityLen) {
810 const QStringView entity = QStringView(txt).mid(recover, entityLen);
811 QString parsedEntity = parseEntity(entity);
812 if (!parsedEntity.isNull()) {
813 return parsedEntity;
814 }
815 }
816error:
817 pos = recover;
818 return "&"_L1;
819}
820
821// parses one word, possibly quoted, and returns it
823{
824 QString word;
825 if (hasPrefix(u'\"')) { // double quotes
826 ++pos;
827 while (pos < len) {
828 QChar c = txt.at(pos++);
829 if (c == u'\"')
830 break;
831 else if (c == u'&')
832 word += parseEntity();
833 else
834 word += c;
835 }
836 } else if (hasPrefix(u'\'')) { // single quotes
837 ++pos;
838 while (pos < len) {
839 QChar c = txt.at(pos++);
840 // Allow for escaped single quotes as they may be part of the string
841 if (c == u'\'' && (txt.size() > 1 && txt.at(pos - 2) != u'\\'))
842 break;
843 else
844 word += c;
845 }
846 } else { // normal text
847 while (pos < len) {
848 QChar c = txt.at(pos++);
849 if (c == u'>' || (c == u'/' && hasPrefix(u'>'))
850 || c == u'<' || c == u'=' || c.isSpace()) {
851 --pos;
852 break;
853 }
854 if (c == u'&')
855 word += parseEntity();
856 else
857 word += c;
858 }
859 }
860 return word;
861}
862
863// gives the new node the right parent
865{
867
868 int p = node->parent;
869
870 // Excel gives us buggy HTML with just tr without surrounding table tags
871 // or with just td tags
872
873 if (node->id == Html_td) {
874 int n = p;
875 while (n && at(n).id != Html_tr)
876 n = at(n).parent;
877
878 if (!n) {
881
883 table->parent = p;
884 table->id = Html_table;
885 table->tag = "table"_L1;
886 table->children.append(nodes.size() - 2); // add row as child
887
889 row->parent = nodes.size() - 3; // table as parent
890 row->id = Html_tr;
891 row->tag = "tr"_L1;
892
893 p = nodes.size() - 2;
894 node = nodes.last(); // re-initialize pointer
895 }
896 }
897
898 if (node->id == Html_tr) {
899 int n = p;
900 while (n && at(n).id != Html_table)
901 n = at(n).parent;
902
903 if (!n) {
906 table->parent = p;
907 table->id = Html_table;
908 table->tag = "table"_L1;
909 p = nodes.size() - 2;
910 node = nodes.last(); // re-initialize pointer
911 }
912 }
913
914 // permit invalid html by letting block elements be children
915 // of inline elements with the exception of paragraphs:
916 //
917 // a new paragraph closes parent inline elements (while loop),
918 // unless they themselves are children of a non-paragraph block
919 // element (if statement)
920 //
921 // For example:
922 //
923 // <body><p><b>Foo<p>Bar <-- second <p> implicitly closes <b> that
924 // belongs to the first <p>. The self-nesting
925 // check further down prevents the second <p>
926 // from nesting into the first one then.
927 // so Bar is not bold.
928 //
929 // <body><b><p>Foo <-- Foo should be bold.
930 //
931 // <body><b><p>Foo<p>Bar <-- Foo and Bar should be bold.
932 //
933 if (node->id == Html_p) {
934 while (p && !at(p).isBlock())
935 p = at(p).parent;
936
937 if (!p || at(p).id != Html_p)
938 p = node->parent;
939 }
940
941 // some elements are not self nesting
942 if (node->id == at(p).id
943 && node->isNotSelfNesting())
944 p = at(p).parent;
945
946 // some elements are not allowed in certain contexts
947 while ((p && !node->allowedInContext(at(p).id))
948 // ### make new styles aware of empty tags
949 || at(p).mayNotHaveChildren()
950 ) {
951 p = at(p).parent;
952 }
953
954 node->parent = p;
955
956 // makes it easier to traverse the tree, later
957 nodes[p]->children.append(nodes.size() - 1);
958 return node;
959}
960
961// sets all properties on the new node
963{
965 const QTextHtmlParserNode *parent = nodes.at(node->parent);
966 node->initializeProperties(parent, this);
967}
968
970{
971 if (!isListStart())
972 return false;
973
974 int p = parent;
975 while (p) {
976 if (parser->at(p).isListStart())
977 return true;
978 p = parser->at(p).parent;
979 }
980 return false;
981}
982
984{
985 // inherit properties from parent element
986 charFormat = parent->charFormat;
987
988 if (id == Html_html)
990 else if (parent->blockFormat.hasProperty(QTextFormat::LayoutDirection))
991 blockFormat.setLayoutDirection(parent->blockFormat.layoutDirection());
992
993 if (parent->displayMode == QTextHtmlElement::DisplayNone)
995
996 if (parent->id != Html_table || id == Html_caption) {
997 if (parent->blockFormat.hasProperty(QTextFormat::BlockAlignment))
998 blockFormat.setAlignment(parent->blockFormat.alignment());
999 else
1001 }
1002 // we don't paint per-row background colors, yet. so as an
1003 // exception inherit the background color here
1004 // we also inherit the background between inline elements
1005 // we also inherit from non-body block elements since we merge them together
1006 if ((parent->id != Html_tr || !isTableCell())
1009 ) {
1011 }
1012
1013 listStyle = parent->listStyle;
1014 // makes no sense to inherit that property, a named anchor is a single point
1015 // in the document, which is set by the DocumentFragment
1017 wsm = parent->wsm;
1018
1019 // initialize remaining properties
1025
1026 for (int i = 0; i < 4; ++i)
1027 padding[i] = -1;
1028
1029 // set element specific attributes
1030 switch (id) {
1031 case Html_a:
1032 for (int i = 0; i < attributes.size(); i += 2) {
1033 const QString key = attributes.at(i);
1034 if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
1035 && !attributes.at(i + 1).isEmpty()) {
1036 hasHref = true;
1037 }
1038 }
1039 charFormat.setAnchor(true);
1040 break;
1041 case Html_big:
1043 break;
1044 case Html_small:
1046 break;
1047 case Html_h1:
1051 break;
1052 case Html_h2:
1056 break;
1057 case Html_h3:
1061 break;
1062 case Html_h4:
1066 break;
1067 case Html_h5:
1071 break;
1072 case Html_p:
1075 break;
1076 case Html_ul:
1077 // nested lists don't have margins, except for the toplevel one
1078 if (!isNestedList(parser)) {
1081 }
1082 // no left margin as we use indenting instead
1083 break;
1084 case Html_ol:
1085 // nested lists don't have margins, except for the toplevel one
1086 if (!isNestedList(parser)) {
1089 }
1090 // no left margin as we use indenting instead
1091 break;
1092 case Html_br:
1093 text = QChar(QChar::LineSeparator);
1094 break;
1095 case Html_pre:
1098 break;
1099 case Html_blockquote:
1105 break;
1106 case Html_dl:
1109 break;
1110 case Html_dd:
1112 break;
1113 default: break;
1114 }
1115}
1116
1117#ifndef QT_NO_CSSPARSER
1118void QTextHtmlParserNode::setListStyle(const QList<QCss::Value> &cssValues)
1119{
1120 for (int i = 0; i < cssValues.size(); ++i) {
1121 if (cssValues.at(i).type == QCss::Value::KnownIdentifier) {
1122 switch (static_cast<QCss::KnownValue>(cssValues.at(i).variant.toInt())) {
1132 default: break;
1133 }
1134 }
1135 }
1136 // allow individual list items to override the style
1137 if (id == Html_li && hasOwnListStyle)
1139}
1140
1142{
1143 switch (cssStyle) {
1169 break;
1170 // Intentionally no "default" to allow a compiler warning when extending the enum
1171 // without updating this here. clang gives such a warning.
1172 }
1173 // Must not happen, intentionally trigger undefined behavior which sanitizers will detect.
1174 // Having all cases covered in switch is not sufficient:
1175 // MSVC would warn when there is no "default".
1176 return static_cast<QTextFrameFormat::BorderStyle>(-1);
1177}
1178
1179void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &declarations, const QTextDocument *resourceProvider)
1180{
1181 QCss::ValueExtractor extractor(declarations);
1182 extractor.extractBox(margin, padding);
1183
1184 auto getBorderValues = [&extractor](qreal *borderWidth, QBrush *borderBrush, QTextFrameFormat::BorderStyle *borderStyles) {
1185 QCss::BorderStyle cssStyles[4];
1186 int cssBorder[4];
1187 QSize cssRadii[4]; // unused
1188 for (int i = 0; i < 4; ++i) {
1189 cssStyles[i] = QCss::BorderStyle_None;
1190 cssBorder[i] = 0;
1191 }
1192 // this will parse (and cache) "border-width" as a list so the
1193 // QCss::BorderWidth parsing below which expects a single value
1194 // will not work as expected - which in this case does not matter
1195 // because tableBorder is not relevant for cells.
1196 bool hit = extractor.extractBorder(cssBorder, borderBrush, cssStyles, cssRadii);
1197 for (int i = 0; i < 4; ++i) {
1198 borderStyles[i] = toQTextFrameFormat(cssStyles[i]);
1199 borderWidth[i] = static_cast<qreal>(cssBorder[i]);
1200 }
1201 return hit;
1202 };
1203
1204 if (id == Html_td || id == Html_th)
1206
1207 for (int i = 0; i < declarations.size(); ++i) {
1208 const QCss::Declaration &decl = declarations.at(i);
1209 if (decl.d->values.isEmpty()) continue;
1210
1212 if (decl.d->values.constFirst().type == QCss::Value::KnownIdentifier)
1213 identifier = static_cast<QCss::KnownValue>(decl.d->values.constFirst().variant.toInt());
1214
1215 switch (decl.d->propertyId) {
1216 case QCss::BorderColor: borderBrush = QBrush(decl.colorValue()); break;
1217 case QCss::BorderStyles:
1219 borderStyle = static_cast<QTextFrameFormat::BorderStyle>(decl.styleValue() - 1);
1220 break;
1221 case QCss::BorderWidth: {
1222 int borders[4];
1223 extractor.lengthValues(decl, borders);
1224 tableBorder = borders[0];
1225 }
1226 break;
1227 case QCss::Border: {
1228 qreal tblBorder[4];
1229 QBrush tblBorderBrush[4];
1230 QTextFrameFormat::BorderStyle tblBorderStyle[4];
1231 if (getBorderValues(tblBorder, tblBorderBrush, tblBorderStyle)) {
1232 tableBorder = tblBorder[0];
1233 if (tblBorderBrush[0].color().isValid())
1234 borderBrush = tblBorderBrush[0];
1235 if (tblBorderStyle[0] != static_cast<QTextFrameFormat::BorderStyle>(-1))
1236 borderStyle = tblBorderStyle[0];
1237 }
1238 }
1239 break;
1242 break;
1243 case QCss::Color: charFormat.setForeground(decl.colorValue()); break;
1244 case QCss::Float:
1246 switch (identifier) {
1249 default: break;
1250 }
1251 break;
1253 blockFormat.setIndent(decl.d->values.constFirst().variant.toInt());
1254 break;
1256 QString lineHeightTypeName = decl.d->values.constFirst().variant.toString();
1257 QTextBlockFormat::LineHeightTypes lineHeightType;
1258 if (lineHeightTypeName.compare("proportional"_L1, Qt::CaseInsensitive) == 0)
1259 lineHeightType = QTextBlockFormat::ProportionalHeight;
1260 else if (lineHeightTypeName.compare("fixed"_L1, Qt::CaseInsensitive) == 0)
1261 lineHeightType = QTextBlockFormat::FixedHeight;
1262 else if (lineHeightTypeName.compare("minimum"_L1, Qt::CaseInsensitive) == 0)
1263 lineHeightType = QTextBlockFormat::MinimumHeight;
1264 else if (lineHeightTypeName.compare("line-distance"_L1, Qt::CaseInsensitive) == 0)
1265 lineHeightType = QTextBlockFormat::LineDistanceHeight;
1266 else
1267 lineHeightType = QTextBlockFormat::SingleHeight;
1268
1270 qreal lineHeight = blockFormat.lineHeight() / 100.0;
1272 }
1273
1275 hasOwnLineHeightType = true;
1276 }
1277 break;
1278 case QCss::LineHeight: {
1279 qreal lineHeight;
1280 QTextBlockFormat::LineHeightTypes lineHeightType;
1281 if (decl.realValue(&lineHeight, "px")) {
1282 lineHeightType = QTextBlockFormat::MinimumHeight;
1283 } else {
1284 bool ok;
1285 QCss::Value cssValue = decl.d->values.constFirst();
1286 QString value = cssValue.toString();
1287 lineHeight = value.toDouble(&ok);
1288 if (ok) {
1289 if (!hasOwnLineHeightType && cssValue.type == QCss::Value::Number) {
1290 lineHeight *= 100.0;
1292 }
1293 lineHeightType = QTextBlockFormat::ProportionalHeight;
1294 } else {
1295 lineHeight = 0.0;
1296 lineHeightType = QTextBlockFormat::SingleHeight;
1297 }
1298 }
1299
1300 // Only override line height type if specified in same node
1303
1304 blockFormat.setLineHeight(lineHeight, lineHeightType);
1305 break;
1306 }
1307 case QCss::TextIndent: {
1308 qreal indent = 0;
1309 if (decl.realValue(&indent, "px"))
1310 blockFormat.setTextIndent(indent);
1311 break; }
1312 case QCss::QtListIndent:
1313 if (decl.intValue(&cssListIndent))
1314 hasCssListIndent = true;
1315 break;
1317 if (decl.d->values.constFirst().variant.toString().compare("empty"_L1, Qt::CaseInsensitive) == 0)
1318 isEmptyParagraph = true;
1319 break;
1320 case QCss::QtTableType:
1321 if (decl.d->values.constFirst().variant.toString().compare("frame"_L1, Qt::CaseInsensitive) == 0)
1322 isTextFrame = true;
1323 else if (decl.d->values.constFirst().variant.toString().compare("root"_L1, Qt::CaseInsensitive) == 0) {
1324 isTextFrame = true;
1325 isRootFrame = true;
1326 }
1327 break;
1328 case QCss::QtUserState:
1329 userState = decl.d->values.constFirst().variant.toInt();
1330 break;
1331 case QCss::Whitespace:
1332 switch (identifier) {
1338 default: break;
1339 }
1340 break;
1342 switch (identifier) {
1349 }
1350 break;
1352 switch (identifier) {
1355 default: break;
1356 }
1357 break;
1359 switch (identifier) {
1362 default: break;
1363 }
1364 break;
1366 switch (identifier) {
1374 default: break;
1375 }
1376 break;
1379 case QCss::ListStyle:
1380 setListStyle(decl.d->values);
1381 break;
1383 textListNumberPrefix = decl.d->values.constFirst().variant.toString();
1384 break;
1386 textListNumberSuffix = decl.d->values.constFirst().variant.toString();
1387 break;
1389 switch (identifier) {
1393 default: break;
1394 }
1395 break;
1396
1398 {
1399 if (resourceProvider != nullptr && QTextDocumentPrivate::get(resourceProvider) != nullptr) {
1400 bool ok;
1401 qint64 searchKey = decl.d->values.constFirst().variant.toLongLong(&ok);
1402 if (ok)
1403 applyForegroundImage(searchKey, resourceProvider);
1404 }
1405 break;
1406 }
1408 {
1409 QPen pen = charFormat.textOutline();
1411 pen.setColor(decl.colorValue());
1413 break;
1414 }
1416 {
1417 qreal width;
1418 if (decl.realValue(&width, "px")) {
1419 QPen pen = charFormat.textOutline();
1420 pen.setWidthF(width);
1422 }
1423 break;
1424 }
1426 {
1427 QPen pen = charFormat.textOutline();
1428 switch (identifier) {
1432 default: break;
1433 }
1435 break;
1436 }
1438 {
1439 QPen pen = charFormat.textOutline();
1440 switch (identifier) {
1445 default: break;
1446 }
1448 break;
1449 }
1451 {
1452 qreal miterLimit;
1453 if (decl.realValue(&miterLimit)) {
1454 QPen pen = charFormat.textOutline();
1455 pen.setMiterLimit(miterLimit);
1457 }
1458 break;
1459 }
1461 {
1462 QList<qreal> dashes = decl.dashArray();
1463 if (!dashes.empty()) {
1464 QPen pen = charFormat.textOutline();
1465 pen.setDashPattern(dashes);
1467 }
1468 break;
1469 }
1471 {
1472 qreal dashOffset;
1473 if (decl.realValue(&dashOffset)) {
1474 QPen pen = charFormat.textOutline();
1475 pen.setDashOffset(dashOffset);
1477 }
1478 break;
1479 }
1480 case QCss::QtForeground:
1481 {
1482 QBrush brush = decl.brushValue();
1484 break;
1485 }
1486 case QCss::MaximumWidth:
1487 if (id == Html_img) {
1488 auto imageFormat = charFormat.toImageFormat();
1489 imageFormat.setMaximumWidth(extractor.textLength(decl));
1490 charFormat = imageFormat;
1491 }
1492 break;
1493 default: break;
1494 }
1495 }
1496
1497 QFont f;
1498 int adjustment = -255;
1499 extractor.extractFont(&f, &adjustment);
1500 if (f.pixelSize() > INT32_MAX / 2)
1501 f.setPixelSize(INT32_MAX / 2); // avoid even more extreme values
1503
1504 if (adjustment >= -1)
1506
1507 {
1508 Qt::Alignment ignoredAlignment;
1509 QCss::Repeat ignoredRepeat;
1510 QString bgImage;
1511 QBrush bgBrush;
1512 QCss::Origin ignoredOrigin, ignoredClip;
1513 QCss::Attachment ignoredAttachment;
1514 extractor.extractBackground(&bgBrush, &bgImage, &ignoredRepeat, &ignoredAlignment,
1515 &ignoredOrigin, &ignoredAttachment, &ignoredClip);
1516
1517 if (!bgImage.isEmpty() && resourceProvider) {
1518 applyBackgroundImage(bgImage, resourceProvider);
1519 } else if (bgBrush.style() != Qt::NoBrush) {
1520 charFormat.setBackground(bgBrush);
1521 if (id == Html_hr)
1523 }
1524 }
1525}
1526
1527#endif // QT_NO_CSSPARSER
1528
1530{
1531 const QTextDocumentPrivate *priv = QTextDocumentPrivate::get(resourceProvider);
1532 for (int i = 0; i < priv->formats.numFormats(); ++i) {
1533 QTextCharFormat format = priv->formats.charFormat(i);
1534 if (format.isValid()) {
1535 QBrush brush = format.foreground();
1536 if (brush.style() == Qt::TexturePattern) {
1537 const bool isPixmap = qHasPixmapTexture(brush);
1538
1539 if (isPixmap && QCoreApplication::instance()->thread() != QThread::currentThread()) {
1540 qWarning("Can't apply QPixmap outside of GUI thread");
1541 return;
1542 }
1543
1544 const qint64 cacheKey = isPixmap ? brush.texture().cacheKey() : brush.textureImage().cacheKey();
1545 if (cacheKey == searchKey) {
1546 QBrush b;
1547 if (isPixmap)
1548 b.setTexture(brush.texture());
1549 else
1550 b.setTextureImage(brush.textureImage());
1551 b.setStyle(Qt::TexturePattern);
1553 }
1554 }
1555 }
1556 }
1557
1558}
1559
1561{
1562 if (!url.isEmpty() && resourceProvider) {
1563 QVariant val = resourceProvider->resource(QTextDocument::ImageResource, url);
1564
1566 // must use images in non-GUI threads
1567 if (val.userType() == QMetaType::QImage) {
1568 QImage image = qvariant_cast<QImage>(val);
1570 } else if (val.userType() == QMetaType::QByteArray) {
1571 QImage image;
1572 if (image.loadFromData(val.toByteArray())) {
1574 }
1575 }
1576 } else {
1577 if (val.userType() == QMetaType::QImage || val.userType() == QMetaType::QPixmap) {
1578 charFormat.setBackground(qvariant_cast<QPixmap>(val));
1579 } else if (val.userType() == QMetaType::QByteArray) {
1580 QPixmap pm;
1581 if (pm.loadFromData(val.toByteArray())) {
1583 }
1584 }
1585 }
1586 }
1587 if (!url.isEmpty())
1589}
1590
1592{
1593 for (int i = 0; i < text.size(); ++i)
1594 if (!text.at(i).isSpace() || text.at(i) == QChar::LineSeparator)
1595 return false;
1596 return true;
1597}
1598
1599static bool setIntAttribute(int *destination, const QString &value)
1600{
1601 bool ok = false;
1602 int val = value.toInt(&ok);
1603 if (ok)
1604 *destination = val;
1605
1606 return ok;
1607}
1608
1610{
1611 bool ok = false;
1612 qreal val = value.toDouble(&ok);
1613 if (ok)
1614 *destination = val;
1615
1616 return ok;
1617}
1618
1619static void setWidthAttribute(QTextLength *width, const QString &valueStr)
1620{
1621 bool ok = false;
1622 qreal realVal = valueStr.toDouble(&ok);
1623 if (ok) {
1625 } else {
1626 auto value = QStringView(valueStr).trimmed();
1627 if (!value.isEmpty() && value.endsWith(u'%')) {
1628 value.truncate(value.size() - 1);
1629 realVal = value.toDouble(&ok);
1630 if (ok)
1632 }
1633 }
1634}
1635
1636#ifndef QT_NO_CSSPARSER
1638{
1639 const QString css = "* {"_L1 + value + u'}';
1640 QCss::Parser parser(css);
1641 QCss::StyleSheet sheet;
1642 parser.parse(&sheet, Qt::CaseInsensitive);
1643 if (sheet.styleRules.size() != 1) return;
1644 applyCssDeclarations(sheet.styleRules.at(0).declarations, resourceProvider);
1645}
1646#endif
1647
1649{
1651
1652 while (pos < len) {
1653 eatSpace();
1654 if (hasPrefix(u'>') || hasPrefix(u'/'))
1655 break;
1657 QString value = "1"_L1;
1658 if (key.size() == 0)
1659 break;
1660 eatSpace();
1661 if (hasPrefix(u'=')){
1662 pos++;
1663 eatSpace();
1664 value = parseWord();
1665 }
1666 if (value.size() == 0)
1667 continue;
1668 attrs << key << value;
1669 }
1670
1671 return attrs;
1672}
1673
1675{
1676 // local state variable for qt3 textedit mode
1677 bool seenQt3Richtext = false;
1678 QString linkHref;
1679 QString linkType;
1680
1681 if (attributes.size() % 2 == 1)
1682 return;
1683
1684 QTextHtmlParserNode *node = nodes.last();
1685
1686 for (int i = 0; i < attributes.size(); i += 2) {
1687 QString key = attributes.at(i);
1688 QString value = attributes.at(i + 1);
1689
1690 switch (node->id) {
1691 case Html_font:
1692 // the infamous font tag
1693 if (key == "size"_L1 && value.size()) {
1694 int n = value.toInt();
1695 if (value.at(0) != u'+' && value.at(0) != u'-')
1696 n -= 3;
1698 } else if (key == "face"_L1) {
1699 if (value.contains(u',')) {
1700 QStringList families;
1701 for (auto family : value.tokenize(u','))
1702 families << family.trimmed().toString();
1703 node->charFormat.setFontFamilies(families);
1704 } else {
1706 }
1707 } else if (key == "color"_L1) {
1709 if (!c.isValid())
1710 qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1711 node->charFormat.setForeground(c);
1712 }
1713 break;
1714 case Html_ol:
1715 case Html_ul:
1716 if (key == "type"_L1) {
1717 node->hasOwnListStyle = true;
1718 if (value == "1"_L1) {
1720 } else if (value == "a"_L1) {
1722 } else if (value == "A"_L1) {
1724 } else if (value == "i"_L1) {
1726 } else if (value == "I"_L1) {
1728 } else {
1729 value = std::move(value).toLower();
1730 if (value == "square"_L1)
1732 else if (value == "disc"_L1)
1734 else if (value == "circle"_L1)
1736 else if (value == "none"_L1)
1738 }
1739 } else if (key == "start"_L1) {
1741 }
1742 break;
1743 case Html_li:
1744 if (key == "class"_L1) {
1745 if (value == "unchecked"_L1)
1747 else if (value == "checked"_L1)
1749 }
1750 break;
1751 case Html_a:
1752 if (key == "href"_L1)
1754 else if (key == "name"_L1)
1756 break;
1757 case Html_img:
1758 if (key == "src"_L1 || key == "source"_L1) {
1759 node->imageName = value;
1760 } else if (key == "width"_L1) {
1761 node->imageWidth = -2; // register that there is a value for it.
1763 } else if (key == "height"_L1) {
1764 node->imageHeight = -2; // register that there is a value for it.
1766 } else if (key == "alt"_L1) {
1767 node->imageAlt = value;
1768 } else if (key == "title"_L1) {
1769 node->text = value;
1770 }
1771 break;
1772 case Html_tr:
1773 case Html_body:
1774 if (key == "bgcolor"_L1) {
1776 if (!c.isValid())
1777 qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1778 node->charFormat.setBackground(c);
1779 } else if (key == "background"_L1) {
1781 }
1782 break;
1783 case Html_th:
1784 case Html_td:
1785 if (key == "width"_L1) {
1786 setWidthAttribute(&node->width, value);
1787 } else if (key == "bgcolor"_L1) {
1789 if (!c.isValid())
1790 qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1791 node->charFormat.setBackground(c);
1792 } else if (key == "background"_L1) {
1794 } else if (key == "rowspan"_L1) {
1796 node->tableCellRowSpan = qMax(1, node->tableCellRowSpan);
1797 } else if (key == "colspan"_L1) {
1799 node->tableCellColSpan = qBound(1, node->tableCellColSpan, 20480);
1800 }
1801 break;
1802 case Html_table:
1803 // If table border already set through css style, prefer that one otherwise consider this value
1804 if (key == "border"_L1 && !node->tableBorder) {
1806 } else if (key == "bgcolor"_L1) {
1808 if (!c.isValid())
1809 qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1810 node->charFormat.setBackground(c);
1811 } else if (key == "bordercolor"_L1) {
1813 if (!c.isValid())
1814 qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData());
1815 node->borderBrush = c;
1816 } else if (key == "background"_L1) {
1818 } else if (key == "cellspacing"_L1) {
1820 } else if (key == "cellpadding"_L1) {
1822 } else if (key == "width"_L1) {
1823 setWidthAttribute(&node->width, value);
1824 } else if (key == "height"_L1) {
1826 }
1827 break;
1828 case Html_meta:
1829 if (key == "name"_L1 && value == "qrichtext"_L1)
1830 seenQt3Richtext = true;
1831
1832 if (key == "content"_L1 && value == "1"_L1 && seenQt3Richtext)
1833 textEditMode = true;
1834 break;
1835 case Html_hr:
1836 if (key == "width"_L1)
1837 setWidthAttribute(&node->width, value);
1838 break;
1839 case Html_link:
1840 if (key == "href"_L1)
1841 linkHref = value;
1842 else if (key == "type"_L1)
1843 linkType = value;
1844 break;
1845 case Html_pre:
1846 if (key == "class"_L1 && value.startsWith("language-"_L1))
1848 break;
1849 default:
1850 break;
1851 }
1852
1853 if (key == "style"_L1) {
1854#ifndef QT_NO_CSSPARSER
1856#endif
1857 } else if (key == "align"_L1) {
1858 value = std::move(value).toLower();
1859 bool alignmentSet = true;
1860
1861 if (value == "left"_L1)
1863 else if (value == "right"_L1)
1865 else if (value == "center"_L1)
1867 else if (value == "justify"_L1)
1869 else
1870 alignmentSet = false;
1871
1872 if (node->id == Html_img) {
1873 // HTML4 compat
1874 if (alignmentSet) {
1875 if (node->blockFormat.alignment() & Qt::AlignLeft)
1877 else if (node->blockFormat.alignment() & Qt::AlignRight)
1879 } else if (value == "middle"_L1) {
1881 } else if (value == "top"_L1) {
1883 }
1884 }
1885 } else if (key == "valign"_L1) {
1886 value = std::move(value).toLower();
1887 if (value == "top"_L1)
1889 else if (value == "middle"_L1)
1891 else if (value == "bottom"_L1)
1893 } else if (key == "dir"_L1) {
1894 value = std::move(value).toLower();
1895 if (value == "ltr"_L1)
1897 else if (value == "rtl"_L1)
1899 } else if (key == "title"_L1) {
1901 } else if (key == "id"_L1) {
1902 node->charFormat.setAnchor(true);
1904 }
1905 }
1906
1907#ifndef QT_NO_CSSPARSER
1908 if (resourceProvider && !linkHref.isEmpty() && linkType == "text/css"_L1)
1909 importStyleSheet(linkHref);
1910#endif
1911}
1912
1913#ifndef QT_NO_CSSPARSER
1915{
1916public:
1918 : parser(parser) { nameCaseSensitivity = Qt::CaseInsensitive; }
1919
1920 QStringList nodeNames(NodePtr node) const override;
1921 QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const override;
1922 bool hasAttributes(NodePtr node) const override;
1923 bool isNullNode(NodePtr node) const override;
1924 NodePtr parentNode(NodePtr node) const override;
1925 NodePtr previousSiblingNode(NodePtr node) const override;
1926 NodePtr duplicateNode(NodePtr node) const override;
1927 void freeNode(NodePtr node) const override;
1928
1929private:
1930 const QTextHtmlParser *parser;
1931};
1932
1934{
1935 return QStringList(parser->at(node.id).tag.toLower());
1936}
1937
1938#endif // QT_NO_CSSPARSER
1939
1940#ifndef QT_NO_CSSPARSER
1941
1942static inline int findAttribute(const QStringList &attributes, const QString &name)
1943{
1944 int idx = -1;
1945 do {
1946 idx = attributes.indexOf(name, idx + 1);
1947 } while (idx != -1 && (idx % 2 == 1));
1948 return idx;
1949}
1950
1952{
1953 const QStringList &attributes = parser->at(node.id).attributes;
1954 const int idx = findAttribute(attributes, aSelector.name);
1955 if (idx == -1)
1956 return QString();
1957 return attributes.at(idx + 1);
1958}
1959
1961{
1962 const QStringList &attributes = parser->at(node.id).attributes;
1963 return !attributes.isEmpty();
1964}
1965
1967{
1968 return node.id == 0;
1969}
1970
1972{
1973 NodePtr parent;
1974 parent.id = 0;
1975 if (node.id) {
1976 parent.id = parser->at(node.id).parent;
1977 }
1978 return parent;
1979}
1980
1985
1987{
1988 NodePtr sibling;
1989 sibling.id = 0;
1990 if (!node.id)
1991 return sibling;
1992 int parent = parser->at(node.id).parent;
1993 if (!parent)
1994 return sibling;
1995 const int childIdx = parser->at(parent).children.indexOf(node.id);
1996 if (childIdx <= 0)
1997 return sibling;
1998 sibling.id = parser->at(parent).children.at(childIdx - 1);
1999 return sibling;
2000}
2001
2005
2006void QTextHtmlParser::resolveStyleSheetImports(const QCss::StyleSheet &sheet)
2007{
2008 for (int i = 0; i < sheet.importRules.size(); ++i) {
2009 const QCss::ImportRule &rule = sheet.importRules.at(i);
2010 if (rule.media.isEmpty() || rule.media.contains("screen"_L1, Qt::CaseInsensitive))
2011 importStyleSheet(rule.href);
2012 }
2013}
2014
2015void QTextHtmlParser::importStyleSheet(const QString &href)
2016{
2017 if (!resourceProvider)
2018 return;
2019 for (int i = 0; i < externalStyleSheets.size(); ++i)
2020 if (externalStyleSheets.at(i).url == href)
2021 return;
2022
2024 QString css;
2025 if (res.userType() == QMetaType::QString) {
2026 css = res.toString();
2027 } else if (res.userType() == QMetaType::QByteArray) {
2028 // #### detect @charset
2029 css = QString::fromUtf8(res.toByteArray());
2030 }
2031 if (!css.isEmpty()) {
2032 QCss::Parser parser(css);
2033 QCss::StyleSheet sheet;
2034 parser.parse(&sheet, Qt::CaseInsensitive);
2035 externalStyleSheets.append(ExternalStyleSheet(href, sheet));
2036 resolveStyleSheetImports(sheet);
2037 }
2038}
2039
2040QList<QCss::Declaration> standardDeclarationForNode(const QTextHtmlParserNode &node)
2041{
2042 QList<QCss::Declaration> decls;
2043 QCss::Declaration decl;
2045 switch (node.id) {
2046 case Html_a:
2047 case Html_u: {
2048 bool needsUnderline = (node.id == Html_u) ? true : false;
2049 if (node.id == Html_a) {
2050 for (int i = 0; i < node.attributes.size(); i += 2) {
2051 const QString key = node.attributes.at(i);
2052 if (key.compare("href"_L1, Qt::CaseInsensitive) == 0
2053 && !node.attributes.at(i + 1).isEmpty()) {
2054 needsUnderline = true;
2055 decl.d->property = "color"_L1;
2056 decl.d->propertyId = QCss::Color;
2058 val.variant = QStringList() << "palette"_L1 << "link"_L1;
2059 decl.d->values = QList<QCss::Value> { val };
2060 decl.d->inheritable = true;
2061 decls << decl;
2062 break;
2063 }
2064 }
2065 }
2066 if (needsUnderline) {
2067 decl = QCss::Declaration();
2068 decl.d->property = "text-decoration"_L1;
2069 decl.d->propertyId = QCss::TextDecoration;
2072 decl.d->values = QList<QCss::Value> { val };
2073 decl.d->inheritable = true;
2074 decls << decl;
2075 }
2076 break;
2077 }
2078 case Html_b:
2079 case Html_strong:
2080 case Html_h1:
2081 case Html_h2:
2082 case Html_h3:
2083 case Html_h4:
2084 case Html_h5:
2085 case Html_th:
2086 decl = QCss::Declaration();
2087 decl.d->property = "font-weight"_L1;
2088 decl.d->propertyId = QCss::FontWeight;
2090 val.variant = QVariant(QCss::Value_Bold);
2091 decl.d->values = QList<QCss::Value> { val };
2092 decl.d->inheritable = true;
2093 decls << decl;
2094 if (node.id == Html_b || node.id == Html_strong)
2095 break;
2096 Q_FALLTHROUGH();
2097 case Html_big:
2098 case Html_small:
2099 if (node.id != Html_th) {
2100 decl = QCss::Declaration();
2101 decl.d->property = "font-size"_L1;
2102 decl.d->propertyId = QCss::FontSize;
2103 decl.d->inheritable = false;
2105 switch (node.id) {
2106 case Html_h1: val.variant = QVariant(QCss::Value_XXLarge); break;
2107 case Html_h2: val.variant = QVariant(QCss::Value_XLarge); break;
2108 case Html_h3: case Html_big: val.variant = QVariant(QCss::Value_Large); break;
2109 case Html_h4: val.variant = QVariant(QCss::Value_Medium); break;
2110 case Html_h5: case Html_small: val.variant = QVariant(QCss::Value_Small); break;
2111 default: break;
2112 }
2113 decl.d->values = QList<QCss::Value> { val };
2114 decls << decl;
2115 break;
2116 }
2117 Q_FALLTHROUGH();
2118 case Html_center:
2119 case Html_td:
2120 decl = QCss::Declaration();
2121 decl.d->property = "text-align"_L1;
2122 decl.d->propertyId = QCss::TextAlignment;
2125 decl.d->values = QList<QCss::Value> { val };
2126 decl.d->inheritable = true;
2127 decls << decl;
2128 break;
2129 case Html_s:
2130 decl = QCss::Declaration();
2131 decl.d->property = "text-decoration"_L1;
2132 decl.d->propertyId = QCss::TextDecoration;
2135 decl.d->values = QList<QCss::Value> { val };
2136 decl.d->inheritable = true;
2137 decls << decl;
2138 break;
2139 case Html_em:
2140 case Html_i:
2141 case Html_cite:
2142 case Html_address:
2143 case Html_var:
2144 case Html_dfn:
2145 decl = QCss::Declaration();
2146 decl.d->property = "font-style"_L1;
2147 decl.d->propertyId = QCss::FontStyle;
2149 val.variant = QVariant(QCss::Value_Italic);
2150 decl.d->values = QList<QCss::Value> { val };
2151 decl.d->inheritable = true;
2152 decls << decl;
2153 break;
2154 case Html_sub:
2155 case Html_sup:
2156 decl = QCss::Declaration();
2157 decl.d->property = "vertical-align"_L1;
2158 decl.d->propertyId = QCss::VerticalAlignment;
2161 decl.d->values = QList<QCss::Value> { val };
2162 decl.d->inheritable = true;
2163 decls << decl;
2164 break;
2165 case Html_ul:
2166 case Html_ol:
2167 decl = QCss::Declaration();
2168 decl.d->property = "list-style"_L1;
2169 decl.d->propertyId = QCss::ListStyle;
2172 decl.d->values = QList<QCss::Value> { val };
2173 decl.d->inheritable = true;
2174 decls << decl;
2175 break;
2176 case Html_code:
2177 case Html_tt:
2178 case Html_kbd:
2179 case Html_samp:
2180 case Html_pre: {
2181 decl = QCss::Declaration();
2182 decl.d->property = "font-family"_L1;
2183 decl.d->propertyId = QCss::FontFamily;
2184 QList<QCss::Value> values;
2185 val.type = QCss::Value::String;
2186 val.variant = QFontDatabase::systemFont(QFontDatabase::FixedFont).families().constFirst();
2187 values << val;
2188 decl.d->values = values;
2189 decl.d->inheritable = true;
2190 decls << decl;
2191 }
2192 if (node.id != Html_pre)
2193 break;
2194 Q_FALLTHROUGH();
2195 case Html_br:
2196 case Html_nobr:
2197 decl = QCss::Declaration();
2198 decl.d->property = "whitespace"_L1;
2199 decl.d->propertyId = QCss::Whitespace;
2201 switch (node.id) {
2202 case Html_br: val.variant = QVariant(QCss::Value_PreWrap); break;
2203 case Html_nobr: val.variant = QVariant(QCss::Value_NoWrap); break;
2204 case Html_pre: val.variant = QVariant(QCss::Value_Pre); break;
2205 default: break;
2206 }
2207 decl.d->values = QList<QCss::Value> { val };
2208 decl.d->inheritable = true;
2209 decls << decl;
2210 break;
2211 default:
2212 break;
2213 }
2214 return decls;
2215}
2216
2217QList<QCss::Declaration> QTextHtmlParser::declarationsForNode(int node) const
2218{
2219 QList<QCss::Declaration> decls;
2220
2222
2223 int idx = 0;
2224 selector.styleSheets.resize((resourceProvider ? 1 : 0)
2225 + externalStyleSheets.size()
2226 + inlineStyleSheets.size());
2227 if (resourceProvider)
2228 selector.styleSheets[idx++] = QTextDocumentPrivate::get(resourceProvider)->parsedDefaultStyleSheet;
2229
2230 for (int i = 0; i < externalStyleSheets.size(); ++i, ++idx)
2231 selector.styleSheets[idx] = externalStyleSheets.at(i).sheet;
2232
2233 for (int i = 0; i < inlineStyleSheets.size(); ++i, ++idx)
2234 selector.styleSheets[idx] = inlineStyleSheets.at(i);
2235
2237
2239 n.id = node;
2240
2241 const char *extraPseudo = nullptr;
2242 if (nodes.at(node)->id == Html_a && nodes.at(node)->hasHref)
2243 extraPseudo = "link";
2244 // Ensure that our own style is taken into consideration
2245 decls = standardDeclarationForNode(*nodes.at(node));
2246 decls += selector.declarationsForNode(n, extraPseudo);
2247 n = selector.parentNode(n);
2248 while (!selector.isNullNode(n)) {
2249 QList<QCss::Declaration> inheritedDecls;
2250 inheritedDecls = selector.declarationsForNode(n, extraPseudo);
2251 for (int i = 0; i < inheritedDecls.size(); ++i) {
2252 const QCss::Declaration &decl = inheritedDecls.at(i);
2253 if (decl.d->inheritable)
2254 decls.prepend(decl);
2255 }
2256 n = selector.parentNode(n);
2257 }
2258 return decls;
2259}
2260
2262{
2263 while (i) {
2264 if (at(i).id == id)
2265 return true;
2266 i = at(i).parent;
2267 }
2268 return false;
2269}
2270
2272#endif // QT_NO_CSSPARSER
2273
2274#endif // QT_NO_TEXTHTMLPARSER
\inmodule QtGui
Definition qbrush.h:30
void setTexture(const QPixmap &pixmap)
Sets the brush pixmap to pixmap.
Definition qbrush.cpp:728
Qt::BrushStyle style() const
Returns the brush style.
Definition qbrush.h:120
\inmodule QtCore
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromString(QAnyStringView name) noexcept
Definition qcolor.cpp:980
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
bool parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity=Qt::CaseSensitive)
Qt::CaseSensitivity nameCaseSensitivity
static QFont systemFont(SystemFont type)
\reentrant
Definition qfont.h:22
\inmodule QtGui
Definition qimage.h:37
qsizetype size() const noexcept
Definition qlist.h:397
T & last()
Definition qlist.h:648
const T & constLast() const noexcept
Definition qlist.h:650
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
\inmodule QtGui
Definition qpen.h:28
void setCapStyle(Qt::PenCapStyle pcs)
Sets the pen's cap style to the given style.
Definition qpen.cpp:650
void setStyle(Qt::PenStyle)
[0]
void setWidthF(qreal width)
Sets the pen width to the given width in pixels with floating point precision.
Definition qpen.cpp:618
void setColor(const QColor &color)
Sets the color of this pen's brush to the given color.
Definition qpen.cpp:705
void setJoinStyle(Qt::PenJoinStyle pcs)
Sets the pen's join style to the given style.
Definition qpen.cpp:677
void setDashOffset(qreal doffset)
Sets the dash offset (the starting point on the dash pattern) for this pen to the offset specified.
Definition qpen.cpp:506
void setMiterLimit(qreal limit)
Sets the miter limit of this pen to the given limit.
Definition qpen.cpp:545
void setDashPattern(const QList< qreal > &pattern)
Sets the dash pattern for this pen to the given pattern.
Definition qpen.cpp:463
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
bool loadFromData(const uchar *buf, uint len, const char *format=nullptr, Qt::ImageConversionFlags flags=Qt::AutoColor)
Loads a pixmap from the len first bytes of the given binary data.
Definition qpixmap.cpp:761
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
constexpr void truncate(qsizetype n) noexcept
Truncates this string view to length length.
QString toString() const
Returns a deep copy of this string view's data as a QString.
Definition qstring.h:1121
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
QStringView trimmed() const noexcept
Strips leading and trailing whitespace and returns the result.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
double toDouble(bool *ok=nullptr) const
Returns the string converted to a double value.
Definition qstring.cpp:7904
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
QString toLower() const &
Definition qstring.h:435
QString trimmed() const &
Definition qstring.h:447
Qt::Alignment alignment() const
Returns the paragraph's alignment.
void setPageBreakPolicy(PageBreakFlags flags)
void setAlignment(Qt::Alignment alignment)
Sets the paragraph's alignment.
void setMarker(MarkerType marker)
void setLineHeight(qreal height, int heightType)
PageBreakFlags pageBreakPolicy() const
void setIndent(int indent)
Sets the paragraph's indentation.
qreal lineHeight(qreal scriptLineHeight, qreal scaling) const
int lineHeightType() const
void setTextIndent(qreal aindent)
Sets the indent for the first line in the block.
void setFontFamilies(const QStringList &families)
void setUnderlineStyle(UnderlineStyle style)
void setTextOutline(const QPen &pen)
Sets the pen used to draw the outlines of characters to the given pen.
void setVerticalAlignment(VerticalAlignment alignment)
Sets the vertical alignment used for the characters with this format to the alignment specified.
void setAnchor(bool anchor)
If anchor is true, text with this format represents an anchor, and is formatted in the appropriate wa...
void setAnchorNames(const QStringList &names)
void setToolTip(const QString &tip)
void setUnderlineColor(const QColor &color)
Sets the color used to draw underlines, overlines and strikeouts on the characters with this format t...
void setAnchorHref(const QString &value)
Sets the hypertext link for the text format to the given value.
QPen textOutline() const
Returns the pen used to draw the outlines of characters in this format.
void setFont(const QFont &font, FontPropertiesInheritanceBehavior behavior=FontPropertiesAll)
static const QTextDocumentPrivate * get(const QTextDocument *document)
\reentrant \inmodule QtGui
bool isEmpty() const
Returns true if the document is empty; otherwise returns false.
QVariant resource(int type, const QUrl &name) const
Returns data of the specified type from the resource with the given name.
QString metaInformation(MetaInformation info) const
Returns meta information about the document of the type specified by info.
void setForeground(const QBrush &brush)
Sets the foreground brush to the specified brush.
void setBackground(const QBrush &brush)
Sets the brush use to paint the document's background to the brush specified.
void setProperty(int propertyId, const QVariant &value)
Sets the property specified by the propertyId to the given value.
void setLayoutDirection(Qt::LayoutDirection direction)
Sets the document's layout direction to the specified direction.
QTextImageFormat toImageFormat() const
Returns this format as an image format.
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
@ PageBreak_AlwaysBefore
@ PageBreak_AlwaysAfter
bool hasPrefix(QChar c, int lookahead=0) const
bool nodeIsChildOf(int i, QTextHTMLElements id) const
QList< QTextHtmlParserNode * > nodes
QTextHtmlParserNode * resolveParent()
void applyAttributes(const QStringList &attributes)
int margin(int i, int mar) const
static int lookupElement(QStringView element)
int topMargin(int i) const
const QTextHtmlParserNode & at(int i) const
int depth(int i) const
QStringList parseAttributes()
int bottomMargin(int i) const
const QTextDocument * resourceProvider
QTextHtmlParserNode * newNode(int parent)
NodePtr previousSiblingNode(NodePtr node) const override
bool isNullNode(NodePtr node) const override
QString attributeValue(NodePtr node, const QCss::AttributeSelector &aSelector) const override
bool hasAttributes(NodePtr node) const override
NodePtr duplicateNode(NodePtr node) const override
QTextHtmlStyleSelector(const QTextHtmlParser *parser)
void freeNode(NodePtr node) const override
NodePtr parentNode(NodePtr node) const override
QStringList nodeNames(NodePtr node) const override
void setMaximumWidth(QTextLength maxWidth)
Sets the maximumWidth of the rectangle occupied by the image.
\reentrant
Definition qtextformat.h:45
static QThread * currentThread()
Definition qthread.cpp:1039
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
\inmodule QtCore
Definition qvariant.h:65
QString str
[2]
QString text
qDeleteAll(list.begin(), list.end())
@ Value_Disc
@ Value_Bottom
@ Value_Wave
@ Value_UpperRoman
@ Value_XXLarge
@ Value_Decimal
@ Value_PreWrap
@ Value_Left
@ Value_Circle
@ Value_Medium
@ Value_SvgMiterJoin
@ Value_Pre
@ Value_SquareCap
@ Value_Normal
@ Value_LineThrough
@ Value_DotDotDash
@ Value_Square
@ Value_Small
@ Value_LowerAlpha
@ Value_Solid
@ Value_Super
@ Value_NoWrap
@ Value_RoundJoin
@ Value_LowerRoman
@ Value_Top
@ Value_None
@ Value_Bold
@ UnknownValue
@ Value_Center
@ Value_Right
@ Value_Sub
@ Value_PreLine
@ Value_FlatCap
@ Value_Underline
@ Value_DotDash
@ Value_Auto
@ Value_Large
@ Value_XLarge
@ Value_MiterJoin
@ Value_RoundCap
@ Value_BevelJoin
@ Value_UpperAlpha
@ Value_Dashed
@ Value_Always
@ Value_Dotted
@ Value_Middle
@ Value_Italic
@ Whitespace
@ QtStrokeMiterLimit
@ QtStrokeDashArray
@ QtTableType
@ QtStrokeColor
@ QtStrokeDashOffset
@ TextUnderlineStyle
@ QtListIndent
@ QtForeground
@ QtListNumberPrefix
@ PageBreakAfter
@ FontWeight
@ QtForegroundTextureCacheKey
@ TextIndent
@ BorderColor
@ MaximumWidth
@ QtUserState
@ BorderCollapse
@ QtBlockIndent
@ QtStrokeWidth
@ FontStyle
@ FontFamily
@ BorderWidth
@ VerticalAlignment
@ LineHeight
@ QtParagraphType
@ BorderStyles
@ TextDecoration
@ ListStyleType
@ ListStyle
@ QtStrokeLineJoin
@ TextAlignment
@ TextDecorationColor
@ QtListNumberSuffix
@ FontSize
@ PageBreakBefore
@ QtLineHeightType
@ QtStrokeLineCap
@ StyleSheetOrigin_Author
@ BorderStyle_Dotted
@ BorderStyle_Solid
@ BorderStyle_Double
@ BorderStyle_DotDash
@ BorderStyle_Ridge
@ BorderStyle_Unknown
@ BorderStyle_Dashed
@ BorderStyle_Outset
@ BorderStyle_Groove
@ BorderStyle_Native
@ NumKnownBorderStyles
@ BorderStyle_None
@ BorderStyle_DotDotDash
@ BorderStyle_Inset
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ AlignRight
Definition qnamespace.h:146
@ AlignJustify
Definition qnamespace.h:149
@ AlignHCenter
Definition qnamespace.h:148
@ AlignCenter
Definition qnamespace.h:163
@ AlignAbsolute
Definition qnamespace.h:150
@ AlignLeft
Definition qnamespace.h:144
@ LeftToRight
@ RightToLeft
@ SolidLine
@ SvgMiterJoin
@ BevelJoin
@ MiterJoin
@ RoundJoin
@ CaseInsensitive
@ TexturePattern
@ NoBrush
@ SquareCap
@ RoundCap
@ FlatCap
Definition brush.cpp:5
Definition image.cpp:4
bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush &brush)
Definition qbrush.cpp:202
#define Q_FALLTHROUGH()
QList< QString > QStringList
Constructs a string list that contains the given string, str.
AudioChannelLayoutTag tag
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char * destination
DBusConnection const char * rule
DBusConnection const char DBusError * error
static struct AttrInfo attrs[]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static QByteArray cacheKey(Args &&...args)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
GLint GLenum GLsizei GLsizei GLsizei depth
const GLfloat * m
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint id
[7]
GLfloat GLfloat f
GLint GLsizei width
GLuint color
[2]
GLuint start
GLuint name
GLfloat n
GLint GLsizei GLsizei GLenum format
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLenum GLenum GLsizei void * row
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLenum GLenum GLsizei void * table
#define qPrintable(string)
Definition qstring.h:1531
static int findAttribute(const QStringList &attributes, const QString &name)
static bool operator<(QStringView entityStr, const QTextHtmlEntity &entity)
static const struct QTextHtmlEntity entities[]
QList< QCss::Declaration > standardDeclarationForNode(const QTextHtmlParserNode &node)
static bool setFloatAttribute(qreal *destination, const QString &value)
static const QTextHtmlElement * lookupElementHelper(QStringView element)
static QTextFrameFormat::BorderStyle toQTextFrameFormat(QCss::BorderStyle cssStyle)
static const QTextHtmlElement elements[Html_NumElements]
static void setWidthAttribute(QTextLength *width, const QString &valueStr)
static const ushort windowsLatin1ExtendedCharacters[0xA0 - 0x80]
static bool setIntAttribute(int *destination, const QString &value)
#define MAX_ENTITY
static QChar resolveEntity(QStringView entity)
static QString quoteNewline(const QString &s)
QTextHTMLElements
@ Html_h2
@ Html_dl
@ Html_meta
@ Html_samp
@ Html_em
@ Html_code
@ Html_th
@ Html_dd
@ Html_tr
@ Html_NumElements
@ Html_tbody
@ Html_nobr
@ Html_tfoot
@ Html_b
@ Html_h4
@ Html_a
@ Html_caption
@ Html_h5
@ Html_big
@ Html_title
@ Html_table
@ Html_address
@ Html_div
@ Html_var
@ Html_i
@ Html_u
@ Html_tt
@ Html_font
@ Html_p
@ Html_ol
@ Html_blockquote
@ Html_head
@ Html_ul
@ Html_span
@ Html_br
@ Html_script
@ Html_thead
@ Html_kbd
@ Html_pre
@ Html_body
@ Html_cite
@ Html_link
@ Html_s
@ Html_unknown
@ Html_dfn
@ Html_sub
@ Html_td
@ Html_hr
@ Html_li
@ Html_img
@ Html_html
@ Html_h6
@ Html_h1
@ Html_small
@ Html_sup
@ Html_h3
@ Html_dt
@ Html_center
@ Html_strong
@ Html_style
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
static const uint base
Definition qurlidna.cpp:20
QFileSelector selector
[1]
QUrl url("example.com")
[constructor-url-reference]
QAction * at
bool realValue(qreal *r, const char *unit=nullptr) const
QExplicitlySharedDataPointer< DeclarationData > d
QColor colorValue(const QPalette &=QPalette()) const
bool intValue(int *i, const char *unit=nullptr) const
QList< qreal > dashArray() const
BorderStyle styleValue() const
bool borderCollapseValue() const
QBrush brushValue(const QPalette &=QPalette()) const
StyleSheetOrigin origin
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962
const char name[11]
QTextHTMLElements id
bool isNotSelfNesting() const
void initializeProperties(const QTextHtmlParserNode *parent, const QTextHtmlParser *parser)
void parseStyleAttribute(const QString &value, const QTextDocument *resourceProvider)
QTextListFormat::Style listStyle
QTextFrameFormat::BorderStyle borderStyle
QTextCharFormat charFormat
QTextHTMLElements id
bool isNestedList(const QTextHtmlParser *parser) const
bool allowedInContext(int parentId) const
void applyBackgroundImage(const QString &url, const QTextDocument *resourceProvider)
void applyForegroundImage(qint64 cacheKey, const QTextDocument *resourceProvider)
QTextFrameFormat::BorderStyle tableCellBorderStyle[4]
QTextBlockFormat blockFormat
bool mayNotHaveChildren() const