qtextdocumentfragment.cpp

Absolute File Name:/home/qt/qt5_coco/qt5/qtbase/src/gui/text/qtextdocumentfragment.cpp
Source codeSwitch to Preprocessed file
LineSourceCount
1/****************************************************************************-
2**-
3** Copyright (C) 2016 The Qt Company Ltd.-
4** Contact: https://www.qt.io/licensing/-
5**-
6** This file is part of the QtGui module of the Qt Toolkit.-
7**-
8** $QT_BEGIN_LICENSE:LGPL$-
9** Commercial License Usage-
10** Licensees holding valid commercial Qt licenses may use this file in-
11** accordance with the commercial license agreement provided with the-
12** Software or, alternatively, in accordance with the terms contained in-
13** a written agreement between you and The Qt Company. For licensing terms-
14** and conditions see https://www.qt.io/terms-conditions. For further-
15** information use the contact form at https://www.qt.io/contact-us.-
16**-
17** GNU Lesser General Public License Usage-
18** Alternatively, this file may be used under the terms of the GNU Lesser-
19** General Public License version 3 as published by the Free Software-
20** Foundation and appearing in the file LICENSE.LGPL3 included in the-
21** packaging of this file. Please review the following information to-
22** ensure the GNU Lesser General Public License version 3 requirements-
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.-
24**-
25** GNU General Public License Usage-
26** Alternatively, this file may be used under the terms of the GNU-
27** General Public License version 2.0 or (at your option) the GNU General-
28** Public license version 3 or any later version approved by the KDE Free-
29** Qt Foundation. The licenses are as published by the Free Software-
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3-
31** included in the packaging of this file. Please review the following-
32** information to ensure the GNU General Public License requirements will-
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and-
34** https://www.gnu.org/licenses/gpl-3.0.html.-
35**-
36** $QT_END_LICENSE$-
37**-
38****************************************************************************/-
39-
40#include "qtextdocumentfragment.h"-
41#include "qtextdocumentfragment_p.h"-
42#include "qtextcursor_p.h"-
43#include "qtextlist.h"-
44-
45#include <qdebug.h>-
46#include <qtextcodec.h>-
47#include <qbytearray.h>-
48#include <qdatastream.h>-
49#include <qdatetime.h>-
50-
51QT_BEGIN_NAMESPACE-
52-
53QTextCopyHelper::QTextCopyHelper(const QTextCursor &_source, const QTextCursor &_destination, bool forceCharFormat, const QTextCharFormat &fmt)-
54#if defined(Q_CC_DIAB) // compiler bug-
55 : formatCollection(*_destination.d->priv->formatCollection()), originalText((const QString)_source.d->priv->buffer())-
56#else-
57 : formatCollection(*_destination.d->priv->formatCollection()), originalText(_source.d->priv->buffer())-
58#endif-
59{-
60 src = _source.d->priv;-
61 dst = _destination.d->priv;-
62 insertPos = _destination.position();-
63 this->forceCharFormat = forceCharFormat;-
64 primaryCharFormatIndex = convertFormatIndex(fmt);-
65 cursor = _source;-
66}
never executed: end of block
0
67-
68int QTextCopyHelper::convertFormatIndex(const QTextFormat &oldFormat, int objectIndexToSet)-
69{-
70 QTextFormat fmt = oldFormat;-
71 if (objectIndexToSet != -1) {
objectIndexToSet != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
72 fmt.setObjectIndex(objectIndexToSet);-
73 } else if (fmt.objectIndex() != -1) {
never executed: end of block
fmt.objectIndex() != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
74 int newObjectIndex = objectIndexMap.value(fmt.objectIndex(), -1);-
75 if (newObjectIndex == -1) {
newObjectIndex == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
76 QTextFormat objFormat = src->formatCollection()->objectFormat(fmt.objectIndex());-
77 Q_ASSERT(objFormat.objectIndex() == -1);-
78 newObjectIndex = formatCollection.createObjectIndex(objFormat);-
79 objectIndexMap.insert(fmt.objectIndex(), newObjectIndex);-
80 }
never executed: end of block
0
81 fmt.setObjectIndex(newObjectIndex);-
82 }
never executed: end of block
0
83 int idx = formatCollection.indexForFormat(fmt);-
84 Q_ASSERT(formatCollection.format(idx).type() == oldFormat.type());-
85 return idx;
never executed: return idx;
0
86}-
87-
88int QTextCopyHelper::appendFragment(int pos, int endPos, int objectIndex)-
89{-
90 QTextDocumentPrivate::FragmentIterator fragIt = src->find(pos);-
91 const QTextFragmentData * const frag = fragIt.value();-
92-
93 Q_ASSERT(objectIndex == -1-
94 || (frag->size_array[0] == 1 && src->formatCollection()->format(frag->format).objectIndex() != -1));-
95-
96 int charFormatIndex;-
97 if (forceCharFormat)
forceCharFormatDescription
TRUEnever evaluated
FALSEnever evaluated
0
98 charFormatIndex = primaryCharFormatIndex;
never executed: charFormatIndex = primaryCharFormatIndex;
0
99 else-
100 charFormatIndex = convertFormatIndex(frag->format, objectIndex);
never executed: charFormatIndex = convertFormatIndex(frag->format, objectIndex);
0
101-
102 const int inFragmentOffset = qMax(0, pos - fragIt.position());-
103 int charsToCopy = qMin(int(frag->size_array[0] - inFragmentOffset), endPos - pos);-
104-
105 QTextBlock nextBlock = src->blocksFind(pos + 1);-
106-
107 int blockIdx = -2;-
108 if (nextBlock.position() == pos + 1) {
nextBlock.posi...n() == pos + 1Description
TRUEnever evaluated
FALSEnever evaluated
0
109 blockIdx = convertFormatIndex(nextBlock.blockFormat());-
110 } else if (pos == 0 && insertPos == 0) {
never executed: end of block
pos == 0Description
TRUEnever evaluated
FALSEnever evaluated
insertPos == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
111 dst->setBlockFormat(dst->blocksBegin(), dst->blocksBegin(), convertFormat(src->blocksBegin().blockFormat()).toBlockFormat());-
112 dst->setCharFormat(-1, 1, convertFormat(src->blocksBegin().charFormat()).toCharFormat());-
113 }
never executed: end of block
0
114-
115 QString txtToInsert(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy);-
116 if (txtToInsert.length() == 1
txtToInsert.length() == 1Description
TRUEnever evaluated
FALSEnever evaluated
0
117 && (txtToInsert.at(0) == QChar::ParagraphSeparator
txtToInsert.at...graphSeparatorDescription
TRUEnever evaluated
FALSEnever evaluated
0
118 || txtToInsert.at(0) == QTextBeginningOfFrame
txtToInsert.at... QChar(0xfdd0)Description
TRUEnever evaluated
FALSEnever evaluated
0
119 || txtToInsert.at(0) == QTextEndOfFrame
txtToInsert.at... QChar(0xfdd1)Description
TRUEnever evaluated
FALSEnever evaluated
0
120 )-
121 ) {-
122 dst->insertBlock(txtToInsert.at(0), insertPos, blockIdx, charFormatIndex);-
123 ++insertPos;-
124 } else {
never executed: end of block
0
125 if (nextBlock.textList()) {
nextBlock.textList()Description
TRUEnever evaluated
FALSEnever evaluated
0
126 QTextBlock dstBlock = dst->blocksFind(insertPos);-
127 if (!dstBlock.textList()) {
!dstBlock.textList()Description
TRUEnever evaluated
FALSEnever evaluated
0
128 // insert a new text block with the block and char format from the-
129 // source block to make sure that the following text fragments-
130 // end up in a list as they should-
131 int listBlockFormatIndex = convertFormatIndex(nextBlock.blockFormat());-
132 int listCharFormatIndex = convertFormatIndex(nextBlock.charFormat());-
133 dst->insertBlock(insertPos, listBlockFormatIndex, listCharFormatIndex);-
134 ++insertPos;-
135 }
never executed: end of block
0
136 }
never executed: end of block
0
137 dst->insert(insertPos, txtToInsert, charFormatIndex);-
138 const int userState = nextBlock.userState();-
139 if (userState != -1)
userState != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
140 dst->blocksFind(insertPos).setUserState(userState);
never executed: dst->blocksFind(insertPos).setUserState(userState);
0
141 insertPos += txtToInsert.length();-
142 }
never executed: end of block
0
143-
144 return charsToCopy;
never executed: return charsToCopy;
0
145}-
146-
147void QTextCopyHelper::appendFragments(int pos, int endPos)-
148{-
149 Q_ASSERT(pos < endPos);-
150-
151 while (pos < endPos)
pos < endPosDescription
TRUEnever evaluated
FALSEnever evaluated
0
152 pos += appendFragment(pos, endPos);
never executed: pos += appendFragment(pos, endPos);
0
153}
never executed: end of block
0
154-
155void QTextCopyHelper::copy()-
156{-
157 if (cursor.hasComplexSelection()) {
cursor.hasComplexSelection()Description
TRUEnever evaluated
FALSEnever evaluated
0
158 QTextTable *table = cursor.currentTable();-
159 int row_start, col_start, num_rows, num_cols;-
160 cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);-
161-
162 QTextTableFormat tableFormat = table->format();-
163 tableFormat.setColumns(num_cols);-
164 tableFormat.clearColumnWidthConstraints();-
165 const int objectIndex = dst->formatCollection()->createObjectIndex(tableFormat);-
166-
167 Q_ASSERT(row_start != -1);-
168 for (int r = row_start; r < row_start + num_rows; ++r) {
r < row_start + num_rowsDescription
TRUEnever evaluated
FALSEnever evaluated
0
169 for (int c = col_start; c < col_start + num_cols; ++c) {
c < col_start + num_colsDescription
TRUEnever evaluated
FALSEnever evaluated
0
170 QTextTableCell cell = table->cellAt(r, c);-
171 const int rspan = cell.rowSpan();-
172 const int cspan = cell.columnSpan();-
173 if (rspan != 1) {
rspan != 1Description
TRUEnever evaluated
FALSEnever evaluated
0
174 int cr = cell.row();-
175 if (cr != r)
cr != rDescription
TRUEnever evaluated
FALSEnever evaluated
0
176 continue;
never executed: continue;
0
177 }
never executed: end of block
0
178 if (cspan != 1) {
cspan != 1Description
TRUEnever evaluated
FALSEnever evaluated
0
179 int cc = cell.column();-
180 if (cc != c)
cc != cDescription
TRUEnever evaluated
FALSEnever evaluated
0
181 continue;
never executed: continue;
0
182 }
never executed: end of block
0
183-
184 // add the QTextBeginningOfFrame-
185 QTextCharFormat cellFormat = cell.format();-
186 if (r + rspan >= row_start + num_rows) {
r + rspan >= r...art + num_rowsDescription
TRUEnever evaluated
FALSEnever evaluated
0
187 cellFormat.setTableCellRowSpan(row_start + num_rows - r);-
188 }
never executed: end of block
0
189 if (c + cspan >= col_start + num_cols) {
c + cspan >= c...art + num_colsDescription
TRUEnever evaluated
FALSEnever evaluated
0
190 cellFormat.setTableCellColumnSpan(col_start + num_cols - c);-
191 }
never executed: end of block
0
192 const int charFormatIndex = convertFormatIndex(cellFormat, objectIndex);-
193-
194 int blockIdx = -2;-
195 const int cellPos = cell.firstPosition();-
196 QTextBlock block = src->blocksFind(cellPos);-
197 if (block.position() == cellPos) {
block.position() == cellPosDescription
TRUEnever evaluated
FALSEnever evaluated
0
198 blockIdx = convertFormatIndex(block.blockFormat());-
199 }
never executed: end of block
0
200-
201 dst->insertBlock(QTextBeginningOfFrame, insertPos, blockIdx, charFormatIndex);-
202 ++insertPos;-
203-
204 // nothing to add for empty cells-
205 if (cell.lastPosition() > cellPos) {
cell.lastPosition() > cellPosDescription
TRUEnever evaluated
FALSEnever evaluated
0
206 // add the contents-
207 appendFragments(cellPos, cell.lastPosition());-
208 }
never executed: end of block
0
209 }
never executed: end of block
0
210 }
never executed: end of block
0
211-
212 // add end of table-
213 int end = table->lastPosition();-
214 appendFragment(end, end+1, objectIndex);-
215 } else {
never executed: end of block
0
216 appendFragments(cursor.selectionStart(), cursor.selectionEnd());-
217 }
never executed: end of block
0
218}-
219-
220QTextDocumentFragmentPrivate::QTextDocumentFragmentPrivate(const QTextCursor &_cursor)-
221 : ref(1), doc(new QTextDocument), importedFromPlainText(false)-
222{-
223 doc->setUndoRedoEnabled(false);-
224-
225 if (!_cursor.hasSelection())
!_cursor.hasSelection()Description
TRUEnever evaluated
FALSEnever evaluated
0
226 return;
never executed: return;
0
227-
228 doc->docHandle()->beginEditBlock();-
229 QTextCursor destCursor(doc);-
230 QTextCopyHelper(_cursor, destCursor).copy();-
231 doc->docHandle()->endEditBlock();-
232-
233 if (_cursor.d)
_cursor.dDescription
TRUEnever evaluated
FALSEnever evaluated
0
234 doc->docHandle()->mergeCachedResources(_cursor.d->priv);
never executed: doc->docHandle()->mergeCachedResources(_cursor.d->priv);
0
235}
never executed: end of block
0
236-
237void QTextDocumentFragmentPrivate::insert(QTextCursor &_cursor) const-
238{-
239 if (_cursor.isNull())
_cursor.isNull()Description
TRUEnever evaluated
FALSEnever evaluated
0
240 return;
never executed: return;
0
241-
242 QTextDocumentPrivate *destPieceTable = _cursor.d->priv;-
243 destPieceTable->beginEditBlock();-
244-
245 QTextCursor sourceCursor(doc);-
246 sourceCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);-
247 QTextCopyHelper(sourceCursor, _cursor, importedFromPlainText, _cursor.charFormat()).copy();-
248-
249 destPieceTable->endEditBlock();-
250}
never executed: end of block
0
251-
252/*!-
253 \class QTextDocumentFragment-
254 \reentrant-
255-
256 \inmodule QtGui-
257 \brief The QTextDocumentFragment class represents a piece of formatted text-
258 from a QTextDocument.-
259-
260 \ingroup richtext-processing-
261 \ingroup shared-
262-
263 A QTextDocumentFragment is a fragment of rich text, that can be inserted into-
264 a QTextDocument. A document fragment can be created from a-
265 QTextDocument, from a QTextCursor's selection, or from another-
266 document fragment. Document fragments can also be created by the-
267 static functions, fromPlainText() and fromHtml().-
268-
269 The contents of a document fragment can be obtained as plain text-
270 by using the toPlainText() function, or it can be obtained as HTML-
271 with toHtml().-
272*/-
273-
274-
275/*!-
276 Constructs an empty QTextDocumentFragment.-
277-
278 \sa isEmpty()-
279*/-
280QTextDocumentFragment::QTextDocumentFragment()-
281 : d(0)-
282{-
283}
never executed: end of block
0
284-
285/*!-
286 Converts the given \a document into a QTextDocumentFragment.-
287 Note that the QTextDocumentFragment only stores the document contents, not meta information-
288 like the document's title.-
289*/-
290QTextDocumentFragment::QTextDocumentFragment(const QTextDocument *document)-
291 : d(0)-
292{-
293 if (!document)
!documentDescription
TRUEnever evaluated
FALSEnever evaluated
0
294 return;
never executed: return;
0
295-
296 QTextCursor cursor(const_cast<QTextDocument *>(document));-
297 cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);-
298 d = new QTextDocumentFragmentPrivate(cursor);-
299}
never executed: end of block
0
300-
301/*!-
302 Creates a QTextDocumentFragment from the \a{cursor}'s selection.-
303 If the cursor doesn't have a selection, the created fragment is empty.-
304-
305 \sa isEmpty(), QTextCursor::selection()-
306*/-
307QTextDocumentFragment::QTextDocumentFragment(const QTextCursor &cursor)-
308 : d(0)-
309{-
310 if (!cursor.hasSelection())
!cursor.hasSelection()Description
TRUEnever evaluated
FALSEnever evaluated
0
311 return;
never executed: return;
0
312-
313 d = new QTextDocumentFragmentPrivate(cursor);-
314}
never executed: end of block
0
315-
316/*!-
317 \fn QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &other)-
318-
319 Copy constructor. Creates a copy of the \a other fragment.-
320*/-
321QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &rhs)-
322 : d(rhs.d)-
323{-
324 if (d)
dDescription
TRUEnever evaluated
FALSEnever evaluated
0
325 d->ref.ref();
never executed: d->ref.ref();
0
326}
never executed: end of block
0
327-
328/*!-
329 \fn QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &other)-
330-
331 Assigns the \a other fragment to this fragment.-
332*/-
333QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &rhs)-
334{-
335 if (rhs.d)
rhs.dDescription
TRUEnever evaluated
FALSEnever evaluated
0
336 rhs.d->ref.ref();
never executed: rhs.d->ref.ref();
0
337 if (d && !d->ref.deref())
dDescription
TRUEnever evaluated
FALSEnever evaluated
!d->ref.deref()Description
TRUEnever evaluated
FALSEnever evaluated
0
338 delete d;
never executed: delete d;
0
339 d = rhs.d;-
340 return *this;
never executed: return *this;
0
341}-
342-
343/*!-
344 Destroys the document fragment.-
345*/-
346QTextDocumentFragment::~QTextDocumentFragment()-
347{-
348 if (d && !d->ref.deref())
dDescription
TRUEnever evaluated
FALSEnever evaluated
!d->ref.deref()Description
TRUEnever evaluated
FALSEnever evaluated
0
349 delete d;
never executed: delete d;
0
350}
never executed: end of block
0
351-
352/*!-
353 Returns \c true if the fragment is empty; otherwise returns \c false.-
354*/-
355bool QTextDocumentFragment::isEmpty() const-
356{-
357 return !d || !d->doc || d->doc->docHandle()->length() <= 1;
never executed: return !d || !d->doc || d->doc->docHandle()->length() <= 1;
0
358}-
359-
360/*!-
361 Returns the document fragment's text as plain text (i.e. with no-
362 formatting information).-
363-
364 \sa toHtml()-
365*/-
366QString QTextDocumentFragment::toPlainText() const-
367{-
368 if (!d)
!dDescription
TRUEnever evaluated
FALSEnever evaluated
0
369 return QString();
never executed: return QString();
0
370-
371 return d->doc->toPlainText();
never executed: return d->doc->toPlainText();
0
372}-
373-
374#ifndef QT_NO_TEXTHTMLPARSER-
375-
376/*!-
377 \since 4.2-
378-
379 Returns the contents of the document fragment as HTML,-
380 using the specified \a encoding (e.g., "UTF-8", "ISO 8859-1").-
381-
382 \sa toPlainText(), QTextDocument::toHtml(), QTextCodec-
383*/-
384QString QTextDocumentFragment::toHtml(const QByteArray &encoding) const-
385{-
386 if (!d)
!dDescription
TRUEnever evaluated
FALSEnever evaluated
0
387 return QString();
never executed: return QString();
0
388-
389 return QTextHtmlExporter(d->doc).toHtml(encoding, QTextHtmlExporter::ExportFragment);
never executed: return QTextHtmlExporter(d->doc).toHtml(encoding, QTextHtmlExporter::ExportFragment);
0
390}-
391-
392#endif // QT_NO_TEXTHTMLPARSER-
393-
394/*!-
395 Returns a document fragment that contains the given \a plainText.-
396-
397 When inserting such a fragment into a QTextDocument the current char format of-
398 the QTextCursor used for insertion is used as format for the text.-
399*/-
400QTextDocumentFragment QTextDocumentFragment::fromPlainText(const QString &plainText)-
401{-
402 QTextDocumentFragment res;-
403-
404 res.d = new QTextDocumentFragmentPrivate;-
405 res.d->importedFromPlainText = true;-
406 QTextCursor cursor(res.d->doc);-
407 cursor.insertText(plainText);-
408 return res;
never executed: return res;
0
409}-
410-
411static QTextListFormat::Style nextListStyle(QTextListFormat::Style style)-
412{-
413 if (style == QTextListFormat::ListDisc)
style == QText...rmat::ListDiscDescription
TRUEnever evaluated
FALSEnever evaluated
0
414 return QTextListFormat::ListCircle;
never executed: return QTextListFormat::ListCircle;
0
415 else if (style == QTextListFormat::ListCircle)
style == QText...at::ListCircleDescription
TRUEnever evaluated
FALSEnever evaluated
0
416 return QTextListFormat::ListSquare;
never executed: return QTextListFormat::ListSquare;
0
417 return style;
never executed: return style;
0
418}-
419-
420#ifndef QT_NO_TEXTHTMLPARSER-
421-
422QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider)-
423 : indent(0), compressNextWhitespace(PreserveWhiteSpace), doc(_doc), importMode(mode)-
424{-
425 cursor = QTextCursor(doc);-
426 wsm = QTextHtmlParserNode::WhiteSpaceNormal;-
427-
428 QString html = _html;-
429 const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->"));-
430 if (startFragmentPos != -1) {
startFragmentPos != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
431 const QLatin1String qt3RichTextHeader("<meta name=\"qrichtext\" content=\"1\" />");-
432-
433 // Hack for Qt3-
434 const bool hasQtRichtextMetaTag = html.contains(qt3RichTextHeader);-
435-
436 const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->"));-
437 if (startFragmentPos < endFragmentPos)
startFragmentP...endFragmentPosDescription
TRUEnever evaluated
FALSEnever evaluated
0
438 html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
never executed: html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
0
439 else-
440 html = html.mid(startFragmentPos);
never executed: html = html.mid(startFragmentPos);
0
441-
442 if (hasQtRichtextMetaTag)
hasQtRichtextMetaTagDescription
TRUEnever evaluated
FALSEnever evaluated
0
443 html.prepend(qt3RichTextHeader);
never executed: html.prepend(qt3RichTextHeader);
0
444 }
never executed: end of block
0
445-
446 parse(html, resourceProvider ? resourceProvider : doc);-
447// dumpHtml();-
448}
never executed: end of block
0
449-
450void QTextHtmlImporter::import()-
451{-
452 cursor.beginEditBlock();-
453 hasBlock = true;-
454 forceBlockMerging = false;-
455 compressNextWhitespace = RemoveWhiteSpace;-
456 blockTagClosed = false;-
457 for (currentNodeIdx = 0; currentNodeIdx < count(); ++currentNodeIdx) {
currentNodeIdx < count()Description
TRUEnever evaluated
FALSEnever evaluated
0
458 currentNode = &at(currentNodeIdx);-
459 wsm = textEditMode ? QTextHtmlParserNode::WhiteSpacePreWrap : currentNode->wsm;
textEditModeDescription
TRUEnever evaluated
FALSEnever evaluated
0
460-
461 /*-
462 * process each node in three stages:-
463 * 1) check if the hierarchy changed and we therefore passed the-
464 * equivalent of a closing tag -> we may need to finish off-
465 * some structures like tables-
466 *-
467 * 2) check if the current node is a special node like a-
468 * <table>, <ul> or <img> tag that requires special processing-
469 *-
470 * 3) if the node should result in a QTextBlock create one and-
471 * finally insert text that may be attached to the node-
472 */-
473-
474 /* emit 'closing' table blocks or adjust current indent level-
475 * if we-
476 * 1) are beyond the first node-
477 * 2) the current node not being a child of the previous node-
478 * means there was a tag closing in the input html-
479 */-
480 if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) {
currentNodeIdx > 0Description
TRUEnever evaluated
FALSEnever evaluated
(currentNode->...ntNodeIdx - 1)Description
TRUEnever evaluated
FALSEnever evaluated
0
481 blockTagClosed = closeTag();-
482 // visually collapse subsequent block tags, but if the element after the closed block tag-
483 // is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting-
484 // hasBlock to false.-
485 if (blockTagClosed
blockTagClosedDescription
TRUEnever evaluated
FALSEnever evaluated
0
486 && !currentNode->isBlock()
!currentNode->isBlock()Description
TRUEnever evaluated
FALSEnever evaluated
0
487 && currentNode->id != Html_unknown)
currentNode->i...= Html_unknownDescription
TRUEnever evaluated
FALSEnever evaluated
0
488 {-
489 hasBlock = false;-
490 } else if (blockTagClosed && hasBlock) {
never executed: end of block
blockTagClosedDescription
TRUEnever evaluated
FALSEnever evaluated
hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
0
491 // when collapsing subsequent block tags we need to clear the block format-
492 QTextBlockFormat blockFormat = currentNode->blockFormat;-
493 blockFormat.setIndent(indent);-
494-
495 QTextBlockFormat oldFormat = cursor.blockFormat();-
496 if (oldFormat.hasProperty(QTextFormat::PageBreakPolicy)) {
oldFormat.hasP...geBreakPolicy)Description
TRUEnever evaluated
FALSEnever evaluated
0
497 QTextFormat::PageBreakFlags pageBreak = oldFormat.pageBreakPolicy();-
498 if (pageBreak == QTextFormat::PageBreak_AlwaysAfter)
pageBreak == Q...ak_AlwaysAfterDescription
TRUEnever evaluated
FALSEnever evaluated
0
499 /* We remove an empty paragrah that requested a page break after.-
500 moving that request to the next paragraph means we also need to make-
501 that a pagebreak before to keep the same visual appearance.-
502 */-
503 pageBreak = QTextFormat::PageBreak_AlwaysBefore;
never executed: pageBreak = QTextFormat::PageBreak_AlwaysBefore;
0
504 blockFormat.setPageBreakPolicy(pageBreak);-
505 }
never executed: end of block
0
506-
507 cursor.setBlockFormat(blockFormat);-
508 }
never executed: end of block
0
509 }
never executed: end of block
0
510-
511 if (currentNode->displayMode == QTextHtmlElement::DisplayNone) {
currentNode->d...t::DisplayNoneDescription
TRUEnever evaluated
FALSEnever evaluated
0
512 if (currentNode->id == Html_title)
currentNode->id == Html_titleDescription
TRUEnever evaluated
FALSEnever evaluated
0
513 doc->setMetaInformation(QTextDocument::DocumentTitle, currentNode->text);
never executed: doc->setMetaInformation(QTextDocument::DocumentTitle, currentNode->text);
0
514 // ignore explicitly 'invisible' elements-
515 continue;
never executed: continue;
0
516 }-
517-
518 if (processSpecialNodes() == ContinueWithNextNode)
processSpecial...ueWithNextNodeDescription
TRUEnever evaluated
FALSEnever evaluated
0
519 continue;
never executed: continue;
0
520-
521 // make sure there's a block for 'Blah' after <ul><li>foo</ul>Blah-
522 if (blockTagClosed
blockTagClosedDescription
TRUEnever evaluated
FALSEnever evaluated
0
523 && !hasBlock
!hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
0
524 && !currentNode->isBlock()
!currentNode->isBlock()Description
TRUEnever evaluated
FALSEnever evaluated
0
525 && !currentNode->text.isEmpty() && !currentNode->hasOnlyWhitespace()
!currentNode->text.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
!currentNode->...lyWhitespace()Description
TRUEnever evaluated
FALSEnever evaluated
0
526 && currentNode->displayMode == QTextHtmlElement::DisplayInline) {
currentNode->d...:DisplayInlineDescription
TRUEnever evaluated
FALSEnever evaluated
0
527-
528 QTextBlockFormat block = currentNode->blockFormat;-
529 block.setIndent(indent);-
530-
531 appendBlock(block, currentNode->charFormat);-
532-
533 hasBlock = true;-
534 }
never executed: end of block
0
535-
536 if (currentNode->isBlock()) {
currentNode->isBlock()Description
TRUEnever evaluated
FALSEnever evaluated
0
537 QTextHtmlImporter::ProcessNodeResult result = processBlockNode();-
538 if (result == ContinueWithNextNode) {
result == ContinueWithNextNodeDescription
TRUEnever evaluated
FALSEnever evaluated
0
539 continue;
never executed: continue;
0
540 } else if (result == ContinueWithNextSibling) {
result == Cont...ithNextSiblingDescription
TRUEnever evaluated
FALSEnever evaluated
0
541 currentNodeIdx += currentNode->children.size();-
542 continue;
never executed: continue;
0
543 }-
544 }
never executed: end of block
0
545-
546 if (currentNode->charFormat.isAnchor() && !currentNode->charFormat.anchorName().isEmpty()) {
currentNode->c...mat.isAnchor()Description
TRUEnever evaluated
FALSEnever evaluated
!currentNode->...me().isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
547 namedAnchors.append(currentNode->charFormat.anchorName());-
548 }
never executed: end of block
0
549-
550 if (appendNodeText())
appendNodeText()Description
TRUEnever evaluated
FALSEnever evaluated
0
551 hasBlock = false; // if we actually appended text then we don't
never executed: hasBlock = false;
0
552 // have an empty block anymore-
553 }
never executed: end of block
0
554-
555 cursor.endEditBlock();-
556}
never executed: end of block
0
557-
558bool QTextHtmlImporter::appendNodeText()-
559{-
560 const int initialCursorPosition = cursor.position();-
561 QTextCharFormat format = currentNode->charFormat;-
562-
563 if(wsm == QTextHtmlParserNode::WhiteSpacePre || wsm == QTextHtmlParserNode::WhiteSpacePreWrap)
wsm == QTextHt...:WhiteSpacePreDescription
TRUEnever evaluated
FALSEnever evaluated
wsm == QTextHt...teSpacePreWrapDescription
TRUEnever evaluated
FALSEnever evaluated
0
564 compressNextWhitespace = PreserveWhiteSpace;
never executed: compressNextWhitespace = PreserveWhiteSpace;
0
565-
566 QString text = currentNode->text;-
567-
568 QString textToInsert;-
569 textToInsert.reserve(text.size());-
570-
571 for (int i = 0; i < text.length(); ++i) {
i < text.length()Description
TRUEnever evaluated
FALSEnever evaluated
0
572 QChar ch = text.at(i);-
573-
574 if (ch.isSpace()
ch.isSpace()Description
TRUEnever evaluated
FALSEnever evaluated
0
575 && ch != QChar::Nbsp
ch != QChar::NbspDescription
TRUEnever evaluated
FALSEnever evaluated
0
576 && ch != QChar::ParagraphSeparator) {
ch != QChar::P...graphSeparatorDescription
TRUEnever evaluated
FALSEnever evaluated
0
577-
578 if (compressNextWhitespace == CollapseWhiteSpace)
compressNextWh...apseWhiteSpaceDescription
TRUEnever evaluated
FALSEnever evaluated
0
579 compressNextWhitespace = RemoveWhiteSpace; // allow this one, and remove the ones coming next.
never executed: compressNextWhitespace = RemoveWhiteSpace;
0
580 else if(compressNextWhitespace == RemoveWhiteSpace)
compressNextWh...moveWhiteSpaceDescription
TRUEnever evaluated
FALSEnever evaluated
0
581 continue;
never executed: continue;
0
582-
583 if (wsm == QTextHtmlParserNode::WhiteSpacePre
wsm == QTextHt...:WhiteSpacePreDescription
TRUEnever evaluated
FALSEnever evaluated
0
584 || textEditMode
textEditModeDescription
TRUEnever evaluated
FALSEnever evaluated
0
585 ) {-
586 if (ch == QLatin1Char('\n')) {
ch == QLatin1Char('\n')Description
TRUEnever evaluated
FALSEnever evaluated
0
587 if (textEditMode)
textEditModeDescription
TRUEnever evaluated
FALSEnever evaluated
0
588 continue;
never executed: continue;
0
589 } else if (ch == QLatin1Char('\r')) {
never executed: end of block
ch == QLatin1Char('\r')Description
TRUEnever evaluated
FALSEnever evaluated
0
590 continue;
never executed: continue;
0
591 }-
592 } else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) {
never executed: end of block
wsm != QTextHt...teSpacePreWrapDescription
TRUEnever evaluated
FALSEnever evaluated
0
593 compressNextWhitespace = RemoveWhiteSpace;-
594 if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap)
wsm == QTextHt...iteSpaceNoWrapDescription
TRUEnever evaluated
FALSEnever evaluated
0
595 ch = QChar::Nbsp;
never executed: ch = QChar::Nbsp;
0
596 else-
597 ch = QLatin1Char(' ');
never executed: ch = QLatin1Char(' ');
0
598 }-
599 } else {
never executed: end of block
0
600 compressNextWhitespace = PreserveWhiteSpace;-
601 }
never executed: end of block
0
602-
603 if (ch == QLatin1Char('\n')
ch == QLatin1Char('\n')Description
TRUEnever evaluated
FALSEnever evaluated
0
604 || ch == QChar::ParagraphSeparator) {
ch == QChar::P...graphSeparatorDescription
TRUEnever evaluated
FALSEnever evaluated
0
605-
606 if (!textToInsert.isEmpty()) {
!textToInsert.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
607 cursor.insertText(textToInsert, format);-
608 textToInsert.clear();-
609 }
never executed: end of block
0
610-
611 QTextBlockFormat fmt = cursor.blockFormat();-
612-
613 if (fmt.hasProperty(QTextFormat::BlockBottomMargin)) {
fmt.hasPropert...kBottomMargin)Description
TRUEnever evaluated
FALSEnever evaluated
0
614 QTextBlockFormat tmp = fmt;-
615 tmp.clearProperty(QTextFormat::BlockBottomMargin);-
616 cursor.setBlockFormat(tmp);-
617 }
never executed: end of block
0
618-
619 fmt.clearProperty(QTextFormat::BlockTopMargin);-
620 appendBlock(fmt, cursor.charFormat());-
621 } else {
never executed: end of block
0
622 if (!namedAnchors.isEmpty()) {
!namedAnchors.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
623 if (!textToInsert.isEmpty()) {
!textToInsert.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
624 cursor.insertText(textToInsert, format);-
625 textToInsert.clear();-
626 }
never executed: end of block
0
627-
628 format.setAnchor(true);-
629 format.setAnchorNames(namedAnchors);-
630 cursor.insertText(ch, format);-
631 namedAnchors.clear();-
632 format.clearProperty(QTextFormat::IsAnchor);-
633 format.clearProperty(QTextFormat::AnchorName);-
634 } else {
never executed: end of block
0
635 textToInsert += ch;-
636 }
never executed: end of block
0
637 }-
638 }-
639-
640 if (!textToInsert.isEmpty()) {
!textToInsert.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
641 cursor.insertText(textToInsert, format);-
642 }
never executed: end of block
0
643-
644 return cursor.position() != initialCursorPosition;
never executed: return cursor.position() != initialCursorPosition;
0
645}-
646-
647QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes()-
648{-
649 switch (currentNode->id) {-
650 case Html_body:
never executed: case Html_body:
0
651 if (currentNode->charFormat.background().style() != Qt::NoBrush) {
currentNode->c...!= Qt::NoBrushDescription
TRUEnever evaluated
FALSEnever evaluated
0
652 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();-
653 fmt.setBackground(currentNode->charFormat.background());-
654 doc->rootFrame()->setFrameFormat(fmt);-
655 const_cast<QTextHtmlParserNode *>(currentNode)->charFormat.clearProperty(QTextFormat::BackgroundBrush);-
656 }
never executed: end of block
0
657 compressNextWhitespace = RemoveWhiteSpace;-
658 break;
never executed: break;
0
659-
660 case Html_ol:
never executed: case Html_ol:
0
661 case Html_ul: {
never executed: case Html_ul:
0
662 QTextListFormat::Style style = currentNode->listStyle;-
663-
664 if (currentNode->id == Html_ul && !currentNode->hasOwnListStyle && currentNode->parent) {
currentNode->id == Html_ulDescription
TRUEnever evaluated
FALSEnever evaluated
!currentNode->hasOwnListStyleDescription
TRUEnever evaluated
FALSEnever evaluated
currentNode->parentDescription
TRUEnever evaluated
FALSEnever evaluated
0
665 const QTextHtmlParserNode *n = &at(currentNode->parent);-
666 while (n) {
nDescription
TRUEnever evaluated
FALSEnever evaluated
0
667 if (n->id == Html_ul) {
n->id == Html_ulDescription
TRUEnever evaluated
FALSEnever evaluated
0
668 style = nextListStyle(currentNode->listStyle);-
669 }
never executed: end of block
0
670 if (n->parent)
n->parentDescription
TRUEnever evaluated
FALSEnever evaluated
0
671 n = &at(n->parent);
never executed: n = &at(n->parent);
0
672 else-
673 n = 0;
never executed: n = 0;
0
674 }-
675 }
never executed: end of block
0
676-
677 QTextListFormat listFmt;-
678 listFmt.setStyle(style);-
679 if (!currentNode->textListNumberPrefix.isNull())
!currentNode->...refix.isNull()Description
TRUEnever evaluated
FALSEnever evaluated
0
680 listFmt.setNumberPrefix(currentNode->textListNumberPrefix);
never executed: listFmt.setNumberPrefix(currentNode->textListNumberPrefix);
0
681 if (!currentNode->textListNumberSuffix.isNull())
!currentNode->...uffix.isNull()Description
TRUEnever evaluated
FALSEnever evaluated
0
682 listFmt.setNumberSuffix(currentNode->textListNumberSuffix);
never executed: listFmt.setNumberSuffix(currentNode->textListNumberSuffix);
0
683-
684 ++indent;-
685 if (currentNode->hasCssListIndent)
currentNode->hasCssListIndentDescription
TRUEnever evaluated
FALSEnever evaluated
0
686 listFmt.setIndent(currentNode->cssListIndent);
never executed: listFmt.setIndent(currentNode->cssListIndent);
0
687 else-
688 listFmt.setIndent(indent);
never executed: listFmt.setIndent(indent);
0
689-
690 List l;-
691 l.format = listFmt;-
692 l.listNode = currentNodeIdx;-
693 lists.append(l);-
694 compressNextWhitespace = RemoveWhiteSpace;-
695-
696 // broken html: <ul>Text here<li>Foo-
697 const QString simpl = currentNode->text.simplified();-
698 if (simpl.isEmpty() || simpl.at(0).isSpace())
simpl.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
simpl.at(0).isSpace()Description
TRUEnever evaluated
FALSEnever evaluated
0
699 return ContinueWithNextNode;
never executed: return ContinueWithNextNode;
0
700 break;
never executed: break;
0
701 }-
702-
703 case Html_table: {
never executed: case Html_table:
0
704 Table t = scanTable(currentNodeIdx);-
705 tables.append(t);-
706 hasBlock = false;-
707 compressNextWhitespace = RemoveWhiteSpace;-
708 return ContinueWithNextNode;
never executed: return ContinueWithNextNode;
0
709 }-
710-
711 case Html_tr:
never executed: case Html_tr:
0
712 return ContinueWithNextNode;
never executed: return ContinueWithNextNode;
0
713-
714 case Html_img: {
never executed: case Html_img:
0
715 QTextImageFormat fmt;-
716 fmt.setName(currentNode->imageName);-
717-
718 fmt.merge(currentNode->charFormat);-
719-
720 if (currentNode->imageWidth != -1)
currentNode->imageWidth != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
721 fmt.setWidth(currentNode->imageWidth);
never executed: fmt.setWidth(currentNode->imageWidth);
0
722 if (currentNode->imageHeight != -1)
currentNode->imageHeight != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
723 fmt.setHeight(currentNode->imageHeight);
never executed: fmt.setHeight(currentNode->imageHeight);
0
724-
725 cursor.insertImage(fmt, QTextFrameFormat::Position(currentNode->cssFloat));-
726-
727 cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);-
728 cursor.mergeCharFormat(currentNode->charFormat);-
729 cursor.movePosition(QTextCursor::Right);-
730 compressNextWhitespace = CollapseWhiteSpace;-
731-
732 hasBlock = false;-
733 return ContinueWithNextNode;
never executed: return ContinueWithNextNode;
0
734 }-
735-
736 case Html_hr: {
never executed: case Html_hr:
0
737 QTextBlockFormat blockFormat = currentNode->blockFormat;-
738 blockFormat.setTopMargin(topMargin(currentNodeIdx));-
739 blockFormat.setBottomMargin(bottomMargin(currentNodeIdx));-
740 blockFormat.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, currentNode->width);-
741 if (hasBlock && importMode == ImportToDocument)
hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
importMode == ImportToDocumentDescription
TRUEnever evaluated
FALSEnever evaluated
0
742 cursor.mergeBlockFormat(blockFormat);
never executed: cursor.mergeBlockFormat(blockFormat);
0
743 else-
744 appendBlock(blockFormat);
never executed: appendBlock(blockFormat);
0
745 hasBlock = false;-
746 compressNextWhitespace = RemoveWhiteSpace;-
747 return ContinueWithNextNode;
never executed: return ContinueWithNextNode;
0
748 }-
749-
750 default: break;
never executed: break;
never executed: default:
0
751 }-
752 return ContinueWithCurrentNode;
never executed: return ContinueWithCurrentNode;
0
753}-
754-
755// returns true if a block tag was closed-
756bool QTextHtmlImporter::closeTag()-
757{-
758 const QTextHtmlParserNode *closedNode = &at(currentNodeIdx - 1);-
759 const int endDepth = depth(currentNodeIdx) - 1;-
760 int depth = this->depth(currentNodeIdx - 1);-
761 bool blockTagClosed = false;-
762-
763 while (depth > endDepth) {
depth > endDepthDescription
TRUEnever evaluated
FALSEnever evaluated
0
764 Table *t = 0;-
765 if (!tables.isEmpty())
!tables.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
766 t = &tables.last();
never executed: t = &tables.last();
0
767-
768 switch (closedNode->id) {-
769 case Html_tr:
never executed: case Html_tr:
0
770 if (t && !t->isTextFrame) {
tDescription
TRUEnever evaluated
FALSEnever evaluated
!t->isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
771 ++t->currentRow;-
772-
773 // for broken html with rowspans but missing tr tags-
774 while (!t->currentCell.atEnd() && t->currentCell.row < t->currentRow)
!t->currentCell.atEnd()Description
TRUEnever evaluated
FALSEnever evaluated
t->currentCell... t->currentRowDescription
TRUEnever evaluated
FALSEnever evaluated
0
775 ++t->currentCell;
never executed: ++t->currentCell;
0
776 }
never executed: end of block
0
777-
778 blockTagClosed = true;-
779 break;
never executed: break;
0
780-
781 case Html_table:
never executed: case Html_table:
0
782 if (!t)
!tDescription
TRUEnever evaluated
FALSEnever evaluated
0
783 break;
never executed: break;
0
784 indent = t->lastIndent;-
785-
786 tables.resize(tables.size() - 1);-
787 t = 0;-
788-
789 if (tables.isEmpty()) {
tables.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
790 cursor = doc->rootFrame()->lastCursorPosition();-
791 } else {
never executed: end of block
0
792 t = &tables.last();-
793 if (t->isTextFrame)
t->isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
794 cursor = t->frame->lastCursorPosition();
never executed: cursor = t->frame->lastCursorPosition();
0
795 else if (!t->currentCell.atEnd())
!t->currentCell.atEnd()Description
TRUEnever evaluated
FALSEnever evaluated
0
796 cursor = t->currentCell.cell().lastCursorPosition();
never executed: cursor = t->currentCell.cell().lastCursorPosition();
0
797 }
never executed: end of block
0
798-
799 // we don't need an extra block after tables, so we don't-
800 // claim to have closed one for the creation of a new one-
801 // in import()-
802 blockTagClosed = false;-
803 compressNextWhitespace = RemoveWhiteSpace;-
804 break;
never executed: break;
0
805-
806 case Html_th:
never executed: case Html_th:
0
807 case Html_td:
never executed: case Html_td:
0
808 if (t && !t->isTextFrame)
tDescription
TRUEnever evaluated
FALSEnever evaluated
!t->isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
809 ++t->currentCell;
never executed: ++t->currentCell;
0
810 blockTagClosed = true;-
811 compressNextWhitespace = RemoveWhiteSpace;-
812 break;
never executed: break;
0
813-
814 case Html_ol:
never executed: case Html_ol:
0
815 case Html_ul:
never executed: case Html_ul:
0
816 if (lists.isEmpty())
lists.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
817 break;
never executed: break;
0
818 lists.resize(lists.size() - 1);-
819 --indent;-
820 blockTagClosed = true;-
821 break;
never executed: break;
0
822-
823 case Html_br:
never executed: case Html_br:
0
824 compressNextWhitespace = RemoveWhiteSpace;-
825 break;
never executed: break;
0
826-
827 case Html_div:
never executed: case Html_div:
0
828 if (closedNode->children.isEmpty())
closedNode->children.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
829 break;
never executed: break;
0
830 // fall through-
831 default:
code before this statement never executed: default:
never executed: default:
0
832 if (closedNode->isBlock())
closedNode->isBlock()Description
TRUEnever evaluated
FALSEnever evaluated
0
833 blockTagClosed = true;
never executed: blockTagClosed = true;
0
834 break;
never executed: break;
0
835 }-
836-
837 closedNode = &at(closedNode->parent);-
838 --depth;-
839 }
never executed: end of block
0
840-
841 return blockTagClosed;
never executed: return blockTagClosed;
0
842}-
843-
844QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx)-
845{-
846 Table table;-
847 table.columns = 0;-
848-
849 QVector<QTextLength> columnWidths;-
850-
851 int tableHeaderRowCount = 0;-
852 QVector<int> rowNodes;-
853 rowNodes.reserve(at(tableNodeIdx).children.count());-
854 for (int row : at(tableNodeIdx).children) {-
855 switch (at(row).id) {-
856 case Html_tr:
never executed: case Html_tr:
0
857 rowNodes += row;-
858 break;
never executed: break;
0
859 case Html_thead:
never executed: case Html_thead:
0
860 case Html_tbody:
never executed: case Html_tbody:
0
861 case Html_tfoot:
never executed: case Html_tfoot:
0
862 for (int potentialRow : at(row).children) {-
863 if (at(potentialRow).id == Html_tr) {
at(potentialRow).id == Html_trDescription
TRUEnever evaluated
FALSEnever evaluated
0
864 rowNodes += potentialRow;-
865 if (at(row).id == Html_thead)
at(row).id == Html_theadDescription
TRUEnever evaluated
FALSEnever evaluated
0
866 ++tableHeaderRowCount;
never executed: ++tableHeaderRowCount;
0
867 }
never executed: end of block
0
868 }
never executed: end of block
0
869 break;
never executed: break;
0
870 default: break;
never executed: break;
never executed: default:
0
871 }-
872 }-
873-
874 QVector<RowColSpanInfo> rowColSpans;-
875 QVector<RowColSpanInfo> rowColSpanForColumn;-
876-
877 int effectiveRow = 0;-
878 for (int row : qAsConst(rowNodes)) {-
879 int colsInRow = 0;-
880-
881 for (int cell : at(row).children) {-
882 if (at(cell).isTableCell()) {
at(cell).isTableCell()Description
TRUEnever evaluated
FALSEnever evaluated
0
883 // skip all columns with spans from previous rows-
884 while (colsInRow < rowColSpanForColumn.size()) {
colsInRow < ro...rColumn.size()Description
TRUEnever evaluated
FALSEnever evaluated
0
885 const RowColSpanInfo &spanInfo = rowColSpanForColumn.at(colsInRow);-
886-
887 if (spanInfo.row + spanInfo.rowSpan > effectiveRow) {
spanInfo.row +...> effectiveRowDescription
TRUEnever evaluated
FALSEnever evaluated
0
888 Q_ASSERT(spanInfo.col == colsInRow);-
889 colsInRow += spanInfo.colSpan;-
890 } else
never executed: end of block
0
891 break;
never executed: break;
0
892 }-
893-
894 const QTextHtmlParserNode &c = at(cell);-
895 const int currentColumn = colsInRow;-
896 colsInRow += c.tableCellColSpan;-
897-
898 RowColSpanInfo spanInfo;-
899 spanInfo.row = effectiveRow;-
900 spanInfo.col = currentColumn;-
901 spanInfo.colSpan = c.tableCellColSpan;-
902 spanInfo.rowSpan = c.tableCellRowSpan;-
903 if (spanInfo.colSpan > 1 || spanInfo.rowSpan > 1)
spanInfo.colSpan > 1Description
TRUEnever evaluated
FALSEnever evaluated
spanInfo.rowSpan > 1Description
TRUEnever evaluated
FALSEnever evaluated
0
904 rowColSpans.append(spanInfo);
never executed: rowColSpans.append(spanInfo);
0
905-
906 columnWidths.resize(qMax(columnWidths.count(), colsInRow));-
907 rowColSpanForColumn.resize(columnWidths.size());-
908 for (int i = currentColumn; i < currentColumn + c.tableCellColSpan; ++i) {
i < currentCol...bleCellColSpanDescription
TRUEnever evaluated
FALSEnever evaluated
0
909 if (columnWidths.at(i).type() == QTextLength::VariableLength) {
columnWidths.a...VariableLengthDescription
TRUEnever evaluated
FALSEnever evaluated
0
910 QTextLength w = c.width;-
911 if (c.tableCellColSpan > 1 && w.type() != QTextLength::VariableLength)
c.tableCellColSpan > 1Description
TRUEnever evaluated
FALSEnever evaluated
w.type() != QT...VariableLengthDescription
TRUEnever evaluated
FALSEnever evaluated
0
912 w = QTextLength(w.type(), w.value(100.) / c.tableCellColSpan);
never executed: w = QTextLength(w.type(), w.value(100.) / c.tableCellColSpan);
0
913 columnWidths[i] = w;-
914 }
never executed: end of block
0
915 rowColSpanForColumn[i] = spanInfo;-
916 }
never executed: end of block
0
917 }
never executed: end of block
0
918 }
never executed: end of block
0
919-
920 table.columns = qMax(table.columns, colsInRow);-
921-
922 ++effectiveRow;-
923 }
never executed: end of block
0
924 table.rows = effectiveRow;-
925-
926 table.lastIndent = indent;-
927 indent = 0;-
928-
929 if (table.rows == 0 || table.columns == 0)
table.rows == 0Description
TRUEnever evaluated
FALSEnever evaluated
table.columns == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
930 return table;
never executed: return table;
0
931-
932 QTextFrameFormat fmt;-
933 const QTextHtmlParserNode &node = at(tableNodeIdx);-
934-
935 if (!node.isTextFrame) {
!node.isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
936 QTextTableFormat tableFmt;-
937 tableFmt.setCellSpacing(node.tableCellSpacing);-
938 tableFmt.setCellPadding(node.tableCellPadding);-
939 if (node.blockFormat.hasProperty(QTextFormat::BlockAlignment))
node.blockForm...lockAlignment)Description
TRUEnever evaluated
FALSEnever evaluated
0
940 tableFmt.setAlignment(node.blockFormat.alignment());
never executed: tableFmt.setAlignment(node.blockFormat.alignment());
0
941 tableFmt.setColumns(table.columns);-
942 tableFmt.setColumnWidthConstraints(columnWidths);-
943 tableFmt.setHeaderRowCount(tableHeaderRowCount);-
944 fmt = tableFmt;-
945 }
never executed: end of block
0
946-
947 fmt.setTopMargin(topMargin(tableNodeIdx));-
948 fmt.setBottomMargin(bottomMargin(tableNodeIdx));-
949 fmt.setLeftMargin(leftMargin(tableNodeIdx)-
950 + table.lastIndent * 40 // ##### not a good emulation-
951 );-
952 fmt.setRightMargin(rightMargin(tableNodeIdx));-
953-
954 // compatibility-
955 if (qFuzzyCompare(fmt.leftMargin(), fmt.rightMargin())
qFuzzyCompare(...rightMargin())Description
TRUEnever evaluated
FALSEnever evaluated
0
956 && qFuzzyCompare(fmt.leftMargin(), fmt.topMargin())
qFuzzyCompare(...t.topMargin())Description
TRUEnever evaluated
FALSEnever evaluated
0
957 && qFuzzyCompare(fmt.leftMargin(), fmt.bottomMargin()))
qFuzzyCompare(...ottomMargin())Description
TRUEnever evaluated
FALSEnever evaluated
0
958 fmt.setProperty(QTextFormat::FrameMargin, fmt.leftMargin());
never executed: fmt.setProperty(QTextFormat::FrameMargin, fmt.leftMargin());
0
959-
960 fmt.setBorderStyle(node.borderStyle);-
961 fmt.setBorderBrush(node.borderBrush);-
962 fmt.setBorder(node.tableBorder);-
963 fmt.setWidth(node.width);-
964 fmt.setHeight(node.height);-
965 if (node.blockFormat.hasProperty(QTextFormat::PageBreakPolicy))
node.blockForm...geBreakPolicy)Description
TRUEnever evaluated
FALSEnever evaluated
0
966 fmt.setPageBreakPolicy(node.blockFormat.pageBreakPolicy());
never executed: fmt.setPageBreakPolicy(node.blockFormat.pageBreakPolicy());
0
967-
968 if (node.blockFormat.hasProperty(QTextFormat::LayoutDirection))
node.blockForm...youtDirection)Description
TRUEnever evaluated
FALSEnever evaluated
0
969 fmt.setLayoutDirection(node.blockFormat.layoutDirection());
never executed: fmt.setLayoutDirection(node.blockFormat.layoutDirection());
0
970 if (node.charFormat.background().style() != Qt::NoBrush)
node.charForma...!= Qt::NoBrushDescription
TRUEnever evaluated
FALSEnever evaluated
0
971 fmt.setBackground(node.charFormat.background());
never executed: fmt.setBackground(node.charFormat.background());
0
972 fmt.setPosition(QTextFrameFormat::Position(node.cssFloat));-
973-
974 if (node.isTextFrame) {
node.isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
975 if (node.isRootFrame) {
node.isRootFrameDescription
TRUEnever evaluated
FALSEnever evaluated
0
976 table.frame = cursor.currentFrame();-
977 table.frame->setFrameFormat(fmt);-
978 } else
never executed: end of block
0
979 table.frame = cursor.insertFrame(fmt);
never executed: table.frame = cursor.insertFrame(fmt);
0
980-
981 table.isTextFrame = true;-
982 } else {
never executed: end of block
0
983 const int oldPos = cursor.position();-
984 QTextTable *textTable = cursor.insertTable(table.rows, table.columns, fmt.toTableFormat());-
985 table.frame = textTable;-
986-
987 for (int i = 0; i < rowColSpans.count(); ++i) {
i < rowColSpans.count()Description
TRUEnever evaluated
FALSEnever evaluated
0
988 const RowColSpanInfo &nfo = rowColSpans.at(i);-
989 textTable->mergeCells(nfo.row, nfo.col, nfo.rowSpan, nfo.colSpan);-
990 }
never executed: end of block
0
991-
992 table.currentCell = TableCellIterator(textTable);-
993 cursor.setPosition(oldPos); // restore for caption support which needs to be inserted right before the table-
994 }
never executed: end of block
0
995 return table;
never executed: return table;
0
996}-
997-
998QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode()-
999{-
1000 QTextBlockFormat block;-
1001 QTextCharFormat charFmt;-
1002 bool modifiedBlockFormat = true;-
1003 bool modifiedCharFormat = true;-
1004-
1005 if (currentNode->isTableCell() && !tables.isEmpty()) {
currentNode->isTableCell()Description
TRUEnever evaluated
FALSEnever evaluated
!tables.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
1006 Table &t = tables.last();-
1007 if (!t.isTextFrame && !t.currentCell.atEnd()) {
!t.isTextFrameDescription
TRUEnever evaluated
FALSEnever evaluated
!t.currentCell.atEnd()Description
TRUEnever evaluated
FALSEnever evaluated
0
1008 QTextTableCell cell = t.currentCell.cell();-
1009 if (cell.isValid()) {
cell.isValid()Description
TRUEnever evaluated
FALSEnever evaluated
0
1010 QTextTableCellFormat fmt = cell.format().toTableCellFormat();-
1011 if (topPadding(currentNodeIdx) >= 0)
topPadding(cur...tNodeIdx) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1012 fmt.setTopPadding(topPadding(currentNodeIdx));
never executed: fmt.setTopPadding(topPadding(currentNodeIdx));
0
1013 if (bottomPadding(currentNodeIdx) >= 0)
bottomPadding(...tNodeIdx) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1014 fmt.setBottomPadding(bottomPadding(currentNodeIdx));
never executed: fmt.setBottomPadding(bottomPadding(currentNodeIdx));
0
1015 if (leftPadding(currentNodeIdx) >= 0)
leftPadding(cu...tNodeIdx) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1016 fmt.setLeftPadding(leftPadding(currentNodeIdx));
never executed: fmt.setLeftPadding(leftPadding(currentNodeIdx));
0
1017 if (rightPadding(currentNodeIdx) >= 0)
rightPadding(c...tNodeIdx) >= 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1018 fmt.setRightPadding(rightPadding(currentNodeIdx));
never executed: fmt.setRightPadding(rightPadding(currentNodeIdx));
0
1019 cell.setFormat(fmt);-
1020-
1021 cursor.setPosition(cell.firstPosition());-
1022 }
never executed: end of block
0
1023 }
never executed: end of block
0
1024 hasBlock = true;-
1025 compressNextWhitespace = RemoveWhiteSpace;-
1026-
1027 if (currentNode->charFormat.background().style() != Qt::NoBrush) {
currentNode->c...!= Qt::NoBrushDescription
TRUEnever evaluated
FALSEnever evaluated
0
1028 charFmt.setBackground(currentNode->charFormat.background());-
1029 cursor.mergeBlockCharFormat(charFmt);-
1030 }
never executed: end of block
0
1031 }
never executed: end of block
0
1032-
1033 if (hasBlock) {
hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
0
1034 block = cursor.blockFormat();-
1035 charFmt = cursor.blockCharFormat();-
1036 modifiedBlockFormat = false;-
1037 modifiedCharFormat = false;-
1038 }
never executed: end of block
0
1039-
1040 // collapse-
1041 {-
1042 qreal tm = qreal(topMargin(currentNodeIdx));-
1043 if (tm > block.topMargin()) {
tm > block.topMargin()Description
TRUEnever evaluated
FALSEnever evaluated
0
1044 block.setTopMargin(tm);-
1045 modifiedBlockFormat = true;-
1046 }
never executed: end of block
0
1047 }-
1048-
1049 int bottomMargin = this->bottomMargin(currentNodeIdx);-
1050-
1051 // for list items we may want to collapse with the bottom margin of the-
1052 // list.-
1053 const QTextHtmlParserNode *parentNode = currentNode->parent ? &at(currentNode->parent) : 0;
currentNode->parentDescription
TRUEnever evaluated
FALSEnever evaluated
0
1054 if ((currentNode->id == Html_li || currentNode->id == Html_dt || currentNode->id == Html_dd)
currentNode->id == Html_liDescription
TRUEnever evaluated
FALSEnever evaluated
currentNode->id == Html_dtDescription
TRUEnever evaluated
FALSEnever evaluated
currentNode->id == Html_ddDescription
TRUEnever evaluated
FALSEnever evaluated
0
1055 && parentNode
parentNodeDescription
TRUEnever evaluated
FALSEnever evaluated
0
1056 && (parentNode->isListStart() || parentNode->id == Html_dl)
parentNode->isListStart()Description
TRUEnever evaluated
FALSEnever evaluated
parentNode->id == Html_dlDescription
TRUEnever evaluated
FALSEnever evaluated
0
1057 && (parentNode->children.last() == currentNodeIdx)) {
(parentNode->c...urrentNodeIdx)Description
TRUEnever evaluated
FALSEnever evaluated
0
1058 bottomMargin = qMax(bottomMargin, this->bottomMargin(currentNode->parent));-
1059 }
never executed: end of block
0
1060-
1061 if (block.bottomMargin() != bottomMargin) {
block.bottomMa...= bottomMarginDescription
TRUEnever evaluated
FALSEnever evaluated
0
1062 block.setBottomMargin(bottomMargin);-
1063 modifiedBlockFormat = true;-
1064 }
never executed: end of block
0
1065-
1066 {-
1067 const qreal lm = leftMargin(currentNodeIdx);-
1068 const qreal rm = rightMargin(currentNodeIdx);-
1069-
1070 if (block.leftMargin() != lm) {
block.leftMargin() != lmDescription
TRUEnever evaluated
FALSEnever evaluated
0
1071 block.setLeftMargin(lm);-
1072 modifiedBlockFormat = true;-
1073 }
never executed: end of block
0
1074 if (block.rightMargin() != rm) {
block.rightMargin() != rmDescription
TRUEnever evaluated
FALSEnever evaluated
0
1075 block.setRightMargin(rm);-
1076 modifiedBlockFormat = true;-
1077 }
never executed: end of block
0
1078 }-
1079-
1080 if (currentNode->id != Html_li
currentNode->id != Html_liDescription
TRUEnever evaluated
FALSEnever evaluated
0
1081 && indent != 0
indent != 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1082 && (lists.isEmpty()
lists.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
1083 || !hasBlock
!hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
0
1084 || !lists.constLast().list
!lists.constLast().listDescription
TRUEnever evaluated
FALSEnever evaluated
0
1085 || lists.constLast().list->itemNumber(cursor.block()) == -1
lists.constLas...block()) == -1Description
TRUEnever evaluated
FALSEnever evaluated
0
1086 )-
1087 ) {-
1088 block.setIndent(indent);-
1089 modifiedBlockFormat = true;-
1090 }
never executed: end of block
0
1091-
1092 if (currentNode->blockFormat.propertyCount() > 0) {
currentNode->b...rtyCount() > 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1093 modifiedBlockFormat = true;-
1094 block.merge(currentNode->blockFormat);-
1095 }
never executed: end of block
0
1096-
1097 if (currentNode->charFormat.propertyCount() > 0) {
currentNode->c...rtyCount() > 0Description
TRUEnever evaluated
FALSEnever evaluated
0
1098 modifiedCharFormat = true;-
1099 charFmt.merge(currentNode->charFormat);-
1100 }
never executed: end of block
0
1101-
1102 // ####################-
1103 // block.setFloatPosition(node->cssFloat);-
1104-
1105 if (wsm == QTextHtmlParserNode::WhiteSpacePre) {
wsm == QTextHt...:WhiteSpacePreDescription
TRUEnever evaluated
FALSEnever evaluated
0
1106 block.setNonBreakableLines(true);-
1107 modifiedBlockFormat = true;-
1108 }
never executed: end of block
0
1109-
1110 if (currentNode->charFormat.background().style() != Qt::NoBrush && !currentNode->isTableCell()) {
currentNode->c...!= Qt::NoBrushDescription
TRUEnever evaluated
FALSEnever evaluated
!currentNode->isTableCell()Description
TRUEnever evaluated
FALSEnever evaluated
0
1111 block.setBackground(currentNode->charFormat.background());-
1112 modifiedBlockFormat = true;-
1113 }
never executed: end of block
0
1114-
1115 if (hasBlock && (!currentNode->isEmptyParagraph || forceBlockMerging)) {
hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
!currentNode->isEmptyParagraphDescription
TRUEnever evaluated
FALSEnever evaluated
forceBlockMergingDescription
TRUEnever evaluated
FALSEnever evaluated
0
1116 if (modifiedBlockFormat)
modifiedBlockFormatDescription
TRUEnever evaluated
FALSEnever evaluated
0
1117 cursor.setBlockFormat(block);
never executed: cursor.setBlockFormat(block);
0
1118 if (modifiedCharFormat)
modifiedCharFormatDescription
TRUEnever evaluated
FALSEnever evaluated
0
1119 cursor.setBlockCharFormat(charFmt);
never executed: cursor.setBlockCharFormat(charFmt);
0
1120 } else {
never executed: end of block
0
1121 if (currentNodeIdx == 1 && cursor.position() == 0 && currentNode->isEmptyParagraph) {
currentNodeIdx == 1Description
TRUEnever evaluated
FALSEnever evaluated
cursor.position() == 0Description
TRUEnever evaluated
FALSEnever evaluated
currentNode->isEmptyParagraphDescription
TRUEnever evaluated
FALSEnever evaluated
0
1122 cursor.setBlockFormat(block);-
1123 cursor.setBlockCharFormat(charFmt);-
1124 } else {
never executed: end of block
0
1125 appendBlock(block, charFmt);-
1126 }
never executed: end of block
0
1127 }-
1128-
1129 if (currentNode->userState != -1)
currentNode->userState != -1Description
TRUEnever evaluated
FALSEnever evaluated
0
1130 cursor.block().setUserState(currentNode->userState);
never executed: cursor.block().setUserState(currentNode->userState);
0
1131-
1132 if (currentNode->id == Html_li && !lists.isEmpty()) {
currentNode->id == Html_liDescription
TRUEnever evaluated
FALSEnever evaluated
!lists.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
1133 List &l = lists.last();-
1134 if (l.list) {
l.listDescription
TRUEnever evaluated
FALSEnever evaluated
0
1135 l.list->add(cursor.block());-
1136 } else {
never executed: end of block
0
1137 l.list = cursor.createList(l.format);-
1138 const qreal listTopMargin = topMargin(l.listNode);-
1139 if (listTopMargin > block.topMargin()) {
listTopMargin ...ck.topMargin()Description
TRUEnever evaluated
FALSEnever evaluated
0
1140 block.setTopMargin(listTopMargin);-
1141 cursor.mergeBlockFormat(block);-
1142 }
never executed: end of block
0
1143 }
never executed: end of block
0
1144 if (hasBlock) {
hasBlockDescription
TRUEnever evaluated
FALSEnever evaluated
0
1145 QTextBlockFormat fmt;-
1146 fmt.setIndent(currentNode->blockFormat.indent());-
1147 cursor.mergeBlockFormat(fmt);-
1148 }
never executed: end of block
0
1149 }
never executed: end of block
0
1150-
1151 forceBlockMerging = false;-
1152 if (currentNode->id == Html_body || currentNode->id == Html_html)
currentNode->id == Html_bodyDescription
TRUEnever evaluated
FALSEnever evaluated
currentNode->id == Html_htmlDescription
TRUEnever evaluated
FALSEnever evaluated
0
1153 forceBlockMerging = true;
never executed: forceBlockMerging = true;
0
1154-
1155 if (currentNode->isEmptyParagraph) {
currentNode->isEmptyParagraphDescription
TRUEnever evaluated
FALSEnever evaluated
0
1156 hasBlock = false;-
1157 return ContinueWithNextSibling;
never executed: return ContinueWithNextSibling;
0
1158 }-
1159-
1160 hasBlock = true;-
1161 blockTagClosed = false;-
1162 return ContinueWithCurrentNode;
never executed: return ContinueWithCurrentNode;
0
1163}-
1164-
1165void QTextHtmlImporter::appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt)-
1166{-
1167 if (!namedAnchors.isEmpty()) {
!namedAnchors.isEmpty()Description
TRUEnever evaluated
FALSEnever evaluated
0
1168 charFmt.setAnchor(true);-
1169 charFmt.setAnchorNames(namedAnchors);-
1170 namedAnchors.clear();-
1171 }
never executed: end of block
0
1172-
1173 cursor.insertBlock(format, charFmt);-
1174-
1175 if (wsm != QTextHtmlParserNode::WhiteSpacePre && wsm != QTextHtmlParserNode::WhiteSpacePreWrap)
wsm != QTextHt...:WhiteSpacePreDescription
TRUEnever evaluated
FALSEnever evaluated
wsm != QTextHt...teSpacePreWrapDescription
TRUEnever evaluated
FALSEnever evaluated
0
1176 compressNextWhitespace = RemoveWhiteSpace;
never executed: compressNextWhitespace = RemoveWhiteSpace;
0
1177}
never executed: end of block
0
1178-
1179#endif // QT_NO_TEXTHTMLPARSER-
1180-
1181/*!-
1182 \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text)-
1183-
1184 Returns a QTextDocumentFragment based on the arbitrary piece of-
1185 HTML in the given \a text. The formatting is preserved as much as-
1186 possible; for example, "<b>bold</b>" will become a document-
1187 fragment with the text "bold" with a bold character format.-
1188*/-
1189-
1190#ifndef QT_NO_TEXTHTMLPARSER-
1191-
1192QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html)-
1193{-
1194 return fromHtml(html, 0);
never executed: return fromHtml(html, 0);
0
1195}-
1196-
1197/*!-
1198 \fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text, const QTextDocument *resourceProvider)-
1199 \since 4.2-
1200-
1201 Returns a QTextDocumentFragment based on the arbitrary piece of-
1202 HTML in the given \a text. The formatting is preserved as much as-
1203 possible; for example, "<b>bold</b>" will become a document-
1204 fragment with the text "bold" with a bold character format.-
1205-
1206 If the provided HTML contains references to external resources such as imported style sheets, then-
1207 they will be loaded through the \a resourceProvider.-
1208*/-
1209-
1210QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &html, const QTextDocument *resourceProvider)-
1211{-
1212 QTextDocumentFragment res;-
1213 res.d = new QTextDocumentFragmentPrivate;-
1214-
1215 QTextHtmlImporter importer(res.d->doc, html, QTextHtmlImporter::ImportToFragment, resourceProvider);-
1216 importer.import();-
1217 return res;
never executed: return res;
0
1218}-
1219-
1220QT_END_NAMESPACE-
1221#endif // QT_NO_TEXTHTMLPARSER-
Source codeSwitch to Preprocessed file

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9