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

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