1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the text module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
30
#include "qtextdocumentfragment.h"
31
#include "qtextdocumentfragment_p.h"
32
#include "qtextcursor_p.h"
33
#include "qtexttable.h"
36
#include <qtextcodec.h>
37
#include <qbytearray.h>
38
#include <qdatastream.h>
40
QTextImportHelper::QTextImportHelper(QTextDocumentFragmentPrivate *docFragment, QTextDocumentPrivate *priv)
41
: formatCollection(docFragment->formatCollection), originalText(priv->buffer())
43
this->docFragment = docFragment;
47
int QTextImportHelper::convertFormatIndex(const QTextFormat &oldFormat, int objectIndexToSet)
49
QTextFormat fmt = oldFormat;
50
if (objectIndexToSet != -1) {
51
fmt.setObjectIndex(objectIndexToSet);
52
} else if (fmt.objectIndex() != -1) {
53
int newObjectIndex = objectIndexMap.value(fmt.objectIndex(), -1);
54
if (newObjectIndex == -1) {
55
QTextFormat objFormat = priv->formatCollection()->objectFormat(fmt.objectIndex());
56
Q_ASSERT(objFormat.objectIndex() == -1);
57
newObjectIndex = formatCollection.createObjectIndex(objFormat);
58
objectIndexMap.insert(fmt.objectIndex(), newObjectIndex);
60
fmt.setObjectIndex(newObjectIndex);
62
return formatCollection.indexForFormat(fmt);
65
int QTextImportHelper::appendFragment(int pos, int endPos, int objectIndex)
67
QTextDocumentPrivate::FragmentIterator fragIt = priv->find(pos);
68
const QTextFragmentData * const frag = fragIt.value();
70
Q_ASSERT(objectIndex == -1
71
|| (frag->size == 1 && priv->formatCollection()->format(frag->format).objectIndex() != -1));
73
const int charFormatIndex = convertFormatIndex(frag->format, objectIndex);
75
const int inFragmentOffset = qMax(0, pos - fragIt.position());
76
int charsToCopy = qMin(int(frag->size - inFragmentOffset), endPos - pos);
78
QTextBlock nextBlock = priv->blocksFind(pos + 1);
81
if (nextBlock.position() == pos + 1) {
82
blockIdx = convertFormatIndex(nextBlock.blockFormat());
83
} else // #### the initial paragraph doesn't have a dedicated text fragment, so
84
// we have to copy it manually. QTextDocumentFragmentPrivate::insert will take
85
// care of replacing an existing initial paragraph at insertion time with the one
86
// we create here. remove this hack as soon as the piecetable is fixed.
87
if (pos == 0 && docFragment->containsCompleteDocument) {
88
docFragment->appendText(QString(QChar::ParagraphSeparator),
89
charFormatIndex, convertFormatIndex(priv->blocksBegin().blockFormat()));
92
docFragment->appendText(QString(originalText.constData() + frag->stringPosition + inFragmentOffset, charsToCopy),
93
charFormatIndex, blockIdx);
97
void QTextImportHelper::appendFragments(int pos, int endPos)
99
Q_ASSERT(pos < endPos);
102
pos += appendFragment(pos, endPos);
105
QTextDocumentFragmentPrivate::QTextDocumentFragmentPrivate(const QTextCursor &cursor)
106
: hasTitle(false), containsCompleteDocument(false), setMarkerForHtmlExport(false)
108
if (!cursor.hasSelection())
111
QTextDocumentPrivate *priv = cursor.d->priv;
112
QTextImportHelper importHelper(this, priv);
114
if (cursor.selectionStart() == 0 && cursor.selectionEnd() == priv->length() - 1) {
115
containsCompleteDocument = true;
116
rootFrameFormat = priv->rootFrame()->frameFormat();
119
if (cursor.hasComplexSelection()) {
120
QTextTable *table = cursor.currentTable();
121
int row_start, col_start, num_rows, num_cols;
122
cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
124
QTextTableFormat tableFormat = table->format();
125
tableFormat.setColumns(num_cols);
126
tableFormat.clearColumnWidthConstraints();
127
const int objectIndex = formatCollection.createObjectIndex(tableFormat);
129
Q_ASSERT(row_start != -1);
130
for (int r = row_start; r < row_start + num_rows; ++r) {
131
for (int c = col_start; c < col_start + num_cols; ++c) {
132
QTextTableCell cell = table->cellAt(r, c);
133
const int rspan = cell.rowSpan();
134
const int cspan = cell.columnSpan();
141
int cc = cell.column();
146
// add the QTextBeginningOfFrame
147
QTextCharFormat cellFormat = cell.format();
148
if (r + rspan >= row_start + num_rows) {
149
cellFormat.setTableCellRowSpan(row_start + num_rows - r);
151
if (c + cspan >= col_start + num_cols) {
152
cellFormat.setTableCellColumnSpan(col_start + num_cols - c);
154
const int charFormatIndex = importHelper.convertFormatIndex(cellFormat, objectIndex);
157
const int cellPos = cell.firstPosition();
158
QTextBlock block = priv->blocksFind(cellPos);
159
if (block.position() == cellPos) {
160
blockIdx = importHelper.convertFormatIndex(block.blockFormat());
163
appendText(QString(QTextBeginningOfFrame), charFormatIndex, blockIdx);
165
// nothing to add for empty cells
166
if (cell.lastPosition() > cellPos) {
168
importHelper.appendFragments(cellPos, cell.lastPosition());
174
int end = table->lastPosition();
175
importHelper.appendFragment(end, end+1, objectIndex);
177
importHelper.appendFragments(cursor.selectionStart(), cursor.selectionEnd());
181
void QTextDocumentFragmentPrivate::insert(QTextCursor &cursor) const
186
QTextFormatCollection *formats = cursor.d->priv->formatCollection();
187
QMap<int, int> formatIndexMap = fillFormatCollection(formats);
189
QTextDocumentPrivate *destPieceTable = cursor.d->priv;
190
destPieceTable->beginEditBlock();
192
int defaultBlockFormat = formats->indexForFormat(cursor.blockFormat());
193
int defaultCharFormat = formats->indexForFormat(cursor.charFormat());
195
const bool documentWasEmpty = (destPieceTable->length() <= 1);
196
bool firstFragmentWasBlock = false;
198
for (int i = 0; i < fragments.count(); ++i) {
199
const TextFragment &f = fragments.at(i);
200
int blockFormatIdx = -2;
201
if (f.blockFormat >= 0)
202
blockFormatIdx = formatIndexMap.value(f.blockFormat, -1);
203
else if (f.blockFormat == -1)
204
blockFormatIdx = defaultBlockFormat;
206
if (f.charFormat != -1)
207
formatIdx = formatIndexMap.value(f.charFormat, -1);
209
formatIdx = defaultCharFormat;
211
if (setMarkerForHtmlExport
212
&& (i == 0 || i == fragments.count() - 1)) {
214
QTextCharFormat fmt = formats->charFormat(formatIdx);
218
flag |= FragmentStart;
219
if (i == fragments.count() - 1)
222
fmt.setProperty(QTextFormat::DocumentFragmentMark, flag);
223
formatIdx = formats->indexForFormat(fmt);
226
QString text(localBuffer.constData() + f.position, f.size);
228
if (blockFormatIdx == -2) {
229
destPieceTable->insert(cursor.position(), text, formatIdx);
231
destPieceTable->insertBlock(text.at(0), cursor.position(), blockFormatIdx, formatIdx);
233
firstFragmentWasBlock = true;
237
// if before the insertion the document was empty then we consider the
238
// insertion as a replacement and must now also remove the initial block
239
// that existed before, in case our fragment started with a block
240
if (documentWasEmpty && firstFragmentWasBlock) {
241
QTextCursor c = cursor;
243
c.movePosition(QTextCursor::Start);
247
if (containsCompleteDocument)
248
destPieceTable->rootFrame()->setFrameFormat(rootFrameFormat);
252
destPieceTable->document()->setMetaInformation(QTextDocument::DocumentTitle, title);
254
destPieceTable->endEditBlock();
257
void QTextDocumentFragmentPrivate::appendText(const QString &text, int formatIdx, int blockIdx)
260
f.position = localBuffer.length();
261
localBuffer.append(text);
262
f.size = text.length();
263
f.charFormat = formatIdx;
264
f.blockFormat = blockIdx;
268
QMap<int, int> QTextDocumentFragmentPrivate::fillFormatCollection(QTextFormatCollection *collection) const
270
QMap<int, int> formatIndexMap;
272
// maps from object index used in formats to real object index
273
QMap<int, int> insertedGroups;
275
const QVector<int> &objFormats = formatCollection.objFormats;
276
for (int i = 0; i < objFormats.size(); ++i) {
277
int objFormat = objFormats.at(i);
278
insertedGroups[i] = collection->createObjectIndex(formatCollection.format(objFormat));
281
const QVector<QTextFormat> &formats = formatCollection.formats;
282
for (int i = 0; i < formats.size(); ++i) {
283
QTextFormat format = formats.at(i);
285
int objectIndex = format.objectIndex();
286
if (objectIndex != -1) {
287
objectIndex = insertedGroups.value(objectIndex, -1);
288
format.setObjectIndex(objectIndex);
291
formatIndexMap[i] = collection->indexForFormat(format);
294
return formatIndexMap;
298
\class QTextDocumentFragment qtextdocumentfragment.h
299
\brief The QTextDocumentFragment class represents a piece of formatted text
300
from a QTextDocument.
304
A QTextDocumentFragment is a fragment of rich text, that can be inserted into
305
a QTextDocument. A document fragment can be created from a
306
QTextDocument, from a QTextCursor's selection, or from another
307
document fragment. Document fragments can also be created by the
308
static functions, fromPlainText() and fromHTML().
310
The contents of a document fragment can be obtained as plain text
311
by using the toPlainText() function, or it can be obtained as HTML
317
Constructs an empty QTextDocumentFragment.
321
QTextDocumentFragment::QTextDocumentFragment()
327
Converts the given \a document into a QTextDocumentFragment.
329
QTextDocumentFragment::QTextDocumentFragment(const QTextDocument *document)
335
QTextCursor cursor(const_cast<QTextDocument *>(document));
336
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
337
d = new QTextDocumentFragmentPrivate(cursor);
341
Creates a QTextDocumentFragment from the \a{cursor}'s selection.
342
If the cursor doesn't have a selection, the created fragment is empty.
344
\sa isEmpty() QTextCursor::selection()
346
QTextDocumentFragment::QTextDocumentFragment(const QTextCursor &cursor)
349
if (!cursor.hasSelection())
352
d = new QTextDocumentFragmentPrivate(cursor);
356
\fn QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &other)
358
Copy constructor. Creates a copy of the \a other fragment.
360
QTextDocumentFragment::QTextDocumentFragment(const QTextDocumentFragment &rhs)
367
\fn QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &other)
369
Assigns the \a other fragment to this fragment.
371
QTextDocumentFragment &QTextDocumentFragment::operator=(const QTextDocumentFragment &rhs)
373
if (&rhs == this || (!d && !rhs.d))
383
d = new QTextDocumentFragmentPrivate;
391
Destroys the document fragment.
393
QTextDocumentFragment::~QTextDocumentFragment()
399
Returns true if the fragment is empty; otherwise returns false.
401
bool QTextDocumentFragment::isEmpty() const
403
return !d || d->fragments.isEmpty();
406
// pull in from qtextdocument.cpp
407
void qt_replace_special_text_characters(QString *text);
410
Returns the document fragment's text as plain text (i.e. with no
411
formatting information).
415
QString QTextDocumentFragment::toPlainText() const
420
QString result = d->localBuffer;
422
// if we have a complete document that contains the initial paragraph
423
// at the beginning then we don't want to see that one in the plaintext
424
// output, as otherwise all plaintext output would always start with a
426
if (d->containsCompleteDocument
428
&& result.at(0) == QChar::ParagraphSeparator)
431
qt_replace_special_text_characters(&result);
436
Returns the contents of the document fragment as HTML.
440
QString QTextDocumentFragment::toHtml() const
446
QTextCursor cursor(&doc);
448
d->setMarkerForHtmlExport = (d->containsCompleteDocument == false);
449
cursor.insertFragment(*this);
450
d->setMarkerForHtmlExport = false;
455
Returns a document fragment that contains the given \a plainText.
457
When inserting such a fragment into a QTextDocument the current char format of
458
the QTextCursor used for insertion is used as format for the text.
460
QTextDocumentFragment QTextDocumentFragment::fromPlainText(const QString &plainText)
462
QTextDocumentFragment res;
464
res.d = new QTextDocumentFragmentPrivate;
466
bool seenCRLF = false;
469
for (int i = 0; i < plainText.length(); ++i) {
470
QChar ch = plainText.at(i);
471
if (ch == QLatin1Char('\n')
472
|| ch == QChar::ParagraphSeparator) {
474
const int textEnd = (seenCRLF ? i - 1 : i);
476
if (textEnd > textStart)
477
res.d->appendText(QString::fromRawData(plainText.unicode() + textStart, textEnd - textStart), -1);
480
res.d->appendText(QString(QChar::ParagraphSeparator), -1, -1);
483
} else if (ch == QLatin1Char('\r')
484
&& (i + 1) < plainText.length()
485
&& plainText.at(i + 1) == QLatin1Char('\n')) {
489
if (textStart < plainText.length())
490
res.d->appendText(QString::fromRawData(plainText.unicode() + textStart, plainText.length() - textStart), -1);
495
QTextHTMLImporter::QTextHTMLImporter(QTextDocumentFragmentPrivate *_d, const QString &html)
496
: d(_d), indent(0), setNamedAnchorInNextOutput(false)
502
static QTextListFormat::Style nextListStyle(QTextListFormat::Style style)
504
if (style == QTextListFormat::ListDisc)
505
return QTextListFormat::ListCircle;
506
else if (style == QTextListFormat::ListCircle)
507
return QTextListFormat::ListSquare;
511
void QTextHTMLImporter::import()
513
bool hasBlock = false;
514
bool forceBlockMerging = false;
515
for (int i = 0; i < count(); ++i) {
516
const QTextHtmlParserNode *node = &at(i);
519
* process each node in three stages:
520
* 1) check if the hierarchy changed and we therefore passed the
521
* equivalent of a closing tag -> we may need to finish off
522
* some structures like tables
524
* 2) check if the current node is a special node like a
525
* <table>, <ul> or <img> tag that requires special processing
527
* 3) if the node should result in a QTextBlock create one and
528
* finally insert text that may be attached to the node
531
/* emit 'closing' table blocks or adjust current indent level
533
* 1) are beyond the first node
534
* 2) the current node not being a child of the previous node
535
* means there was a tag closing in the input html
537
if (i > 0 && (node->parent != i - 1)) {
538
const bool blockTagClosed = closeTag(i);
539
if (hasBlock && blockTagClosed)
542
// make sure there's a block for 'Blah' after <ul><li>foo</ul>Blah
546
&& !node->text.isEmpty()
547
&& node->displayMode != QTextHtmlElement::DisplayNone) {
549
QTextBlockFormat block = node->blockFormat();
550
block.setIndent(indent);
552
appendBlock(block, node->charFormat());
558
if (node->displayMode == QTextHtmlElement::DisplayNone) {
559
if (node->id == Html_title) {
561
d->title = node->text;
563
// ignore explicitly 'invisible' elements
565
} else if (node->id == Html_body) {
566
d->containsCompleteDocument = true;
567
if (node->bgColor.isValid()) {
568
d->rootFrameFormat.setBackground(QBrush(node->bgColor));
569
const_cast<QTextHtmlParserNode *>(node)->bgColor = QColor();
571
} else if (node->isListStart) {
573
QTextListFormat::Style style = node->listStyle;
575
if (node->id == Html_ul && !node->hasOwnListStyle && node->parent) {
576
const QTextHtmlParserNode *n = &at(node->parent);
578
if (n->id == Html_ul) {
579
style = nextListStyle(node->listStyle);
588
QTextListFormat listFmt;
589
listFmt.setStyle(style);
592
if (node->hasCssListIndent)
593
listFmt.setIndent(node->cssListIndent);
595
listFmt.setIndent(indent);
597
listReferences.append(d->formatCollection.createObjectIndex(listFmt));
599
if (node->text.isEmpty())
601
} else if (node->id == Html_table) {
603
if (scanTable(i, &t)) {
608
} else if (node->id == Html_tr && !tables.isEmpty()) {
609
tables[tables.size() - 1].currentRow++;
611
} else if (node->id == Html_img) {
612
QTextImageFormat fmt;
613
fmt.setName(node->imageName);
615
if (node->imageWidth >= 0)
616
fmt.setWidth(node->imageWidth);
617
if (node->imageHeight >= 0)
618
fmt.setHeight(node->imageHeight);
619
QTextFrameFormat::Position f = QTextFrameFormat::Position(node->cssFloat);
620
QTextFrameFormat ffmt;
622
int objIndex = d->formatCollection.createObjectIndex(ffmt);
623
fmt.setObjectIndex(objIndex);
631
QTextBlockFormat block;
632
QTextCharFormat charFmt;
634
QChar separator = QChar::ParagraphSeparator;
637
Q_ASSERT(d->fragments.last().blockFormat >= 0);
638
block = d->formatCollection.blockFormat(d->fragments.last().blockFormat);
639
charFmt = d->formatCollection.charFormat(d->fragments.last().charFormat);
643
block.setTopMargin(qMax(block.topMargin(), (qreal)topMargin(i)));
645
int bottomMargin = this->bottomMargin(i);
647
// for list items we may want to collapse with the bottom margin of the
649
if (node->isListItem) {
650
if (node->parent && at(node->parent).isListStart) {
651
const int listId = node->parent;
652
const QTextHtmlParserNode *list = &at(listId);
653
if (list->children.last() == i /* == index of node */)
654
bottomMargin = qMax(bottomMargin, this->bottomMargin(listId));
658
block.setBottomMargin(bottomMargin);
660
block.setLeftMargin(leftMargin(i));
661
block.setRightMargin(rightMargin(i));
663
if (node->isListItem) {
664
if (!listReferences.isEmpty()) {
665
block.setObjectIndex(listReferences.last());
667
// qWarning("QTextDocumentFragment(html import): list item outside list found. bad html?");
669
} else if (indent && block.objectIndex() != listReferences.last()) {
670
block.setIndent(indent);
673
block.merge(node->blockFormat());
674
charFmt.merge(node->charFormat());
676
if (node->isTableCell && !tables.isEmpty()) {
678
charFmt.setObjectIndex(tables[tables.size() - 1].tableIndex);
680
if (node->bgColor.isValid())
681
charFmt.setBackground(QBrush(node->bgColor));
683
charFmt.setTableCellColumnSpan(node->tableCellColSpan);
684
charFmt.setTableCellRowSpan(node->tableCellRowSpan);
686
separator = QTextBeginningOfFrame;
688
tables[tables.size() - 1].currentColumnCount += node->tableCellColSpan;
693
// ####################
694
// block.setFloatPosition(node->cssFloat);
696
if (node->wsm == QTextHtmlParserNode::WhiteSpacePre)
697
block.setNonBreakableLines(true);
699
if (node->bgColor.isValid())
700
block.setBackground(QBrush(node->bgColor));
702
if (hasBlock && (!node->isEmptyParagraph || forceBlockMerging)) {
703
d->fragments.last().blockFormat = d->formatCollection.indexForFormat(block);
704
d->fragments.last().charFormat = d->formatCollection.indexForFormat(charFmt);
706
appendBlock(block, charFmt, separator);
709
forceBlockMerging = false;
710
if (node->id == Html_body || node->id == Html_html)
711
forceBlockMerging = true;
713
if (node->isEmptyParagraph)
719
if (node->isAnchor && !node->anchorName.isEmpty()) {
720
setNamedAnchorInNextOutput = true;
721
namedAnchor = node->anchorName;
723
if (node->text.size() == 0)
727
appendText(node->text, node->charFormat());
730
if (listReferences.size() || tables.size())
731
closeTag(count() - 1);
735
// returns true if a block tag was closed
736
bool QTextHTMLImporter::closeTag(int i)
738
const bool atLastNode = (i == count() - 1);
739
const QTextHtmlParserNode *closedNode = &at(i - 1);
740
const int endDepth = atLastNode ? - 1 : depth(i) - 1;
741
int depth = this->depth(i - 1);
742
bool blockTagClosed = false;
744
while (depth > endDepth) {
745
if (closedNode->id == Html_tr && !tables.isEmpty()) {
746
Table &t = tables[tables.size() -1];
748
QTextCharFormat charFmt;
749
charFmt.setObjectIndex(t.tableIndex);
751
const int rowSpanCells = t.rowSpanCellsPerRow.value(t.currentRow, 0);
753
while (t.currentColumnCount < t.columns - rowSpanCells) {
754
appendBlock(QTextBlockFormat(), charFmt, QTextBeginningOfFrame);
755
++t.currentColumnCount;
758
t.currentColumnCount = 0;
759
blockTagClosed = true;
760
} else if (closedNode->id == Html_table && !tables.isEmpty()) {
761
QTextCharFormat charFmt;
762
charFmt.setObjectIndex(tables[tables.size() - 1].tableIndex);
763
QTextBlockFormat fmt;
764
appendBlock(fmt, charFmt, QTextEndOfFrame);
765
tables.resize(tables.size() - 1);
766
// we don't need an extra block after tables, so we don't
767
// claim to have closed one for the creation of a new one
769
blockTagClosed = false;
770
} else if (closedNode->isListStart) {
772
Q_ASSERT(!listReferences.isEmpty());
774
listReferences.resize(listReferences.size() - 1);
776
blockTagClosed = true;
779
closedNode = &at(closedNode->parent);
783
return blockTagClosed;
786
bool QTextHTMLImporter::scanTable(int tableNodeIdx, Table *table)
790
QVector<QTextLength> columnWidths;
793
bool inFirstRow = true;
794
int effectiveRow = 0;
795
foreach (int row, at(tableNodeIdx).children) {
796
if (at(row).id == Html_tr) {
799
foreach (int cell, at(row).children)
800
if (at(cell).isTableCell) {
803
const QTextHtmlParserNode &c = at(cell);
804
colsInRow += c.tableCellColSpan;
806
if (c.tableCellRowSpan > 1) {
807
table->rowSpanCellsPerRow.resize(effectiveRow + c.tableCellRowSpan + 1);
809
for (int r = effectiveRow + 1; r < effectiveRow + c.tableCellRowSpan; ++r)
810
table->rowSpanCellsPerRow[r]++;
813
if (inFirstRow || colsInRow > columnWidths.count()) {
814
while (columnWidths.count() < colsInRow)
815
columnWidths << c.width;
819
table->columns = qMax(table->columns, colsInRow);
829
const QTextHtmlParserNode &node = at(tableNodeIdx);
830
QTextTableFormat fmt;
831
fmt.setBorder(node.tableBorder);
832
fmt.setWidth(node.width);
833
fmt.setCellSpacing(node.tableCellSpacing);
834
fmt.setCellPadding(node.tableCellPadding);
836
fmt.setAlignment(node.alignment);
837
if (node.direction < 2)
838
fmt.setLayoutDirection(Qt::LayoutDirection(node.direction));
839
if (node.bgColor.isValid())
840
fmt.setBackground(QBrush(node.bgColor));
842
fmt.clearBackground();
843
fmt.setPosition(QTextFrameFormat::Position(node.cssFloat));
845
fmt.setColumns(table->columns);
846
fmt.setColumnWidthConstraints(columnWidths);
847
table->tableIndex = d->formatCollection.createObjectIndex(fmt);
851
void QTextHTMLImporter::appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt, const QChar &separator)
853
if (setNamedAnchorInNextOutput) {
854
charFmt.setAnchor(true);
855
charFmt.setAnchorName(namedAnchor);
856
setNamedAnchorInNextOutput = false;
859
d->appendText(QString(separator), d->formatCollection.indexForFormat(charFmt), d->formatCollection.indexForFormat(format));
862
void QTextHTMLImporter::appendText(QString text, QTextCharFormat format)
864
if (setNamedAnchorInNextOutput && !text.isEmpty()) {
865
QTextCharFormat fmt = format;
867
fmt.setAnchorName(namedAnchor);
868
d->appendText(QString(text.at(0)), d->formatCollection.indexForFormat(fmt));
871
format.setAnchor(false);
872
format.setAnchorName(QString());
874
setNamedAnchorInNextOutput = false;
877
if (!text.isEmpty()) {
878
d->appendText(text, d->formatCollection.indexForFormat(format));
883
\fn QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &text)
885
Returns a QTextDocumentFragment based on the arbitrary piece of
886
HTML in the given \a text. The formatting is preserved as much as
887
possible; for example, "<b>bold</b>" will become a document
888
fragment with the text "bold" with a bold character format.
890
QTextDocumentFragment QTextDocumentFragment::fromHtml(const QString &_html)
892
QTextDocumentFragment res;
893
res.d = new QTextDocumentFragmentPrivate;
895
QString html = _html;
897
const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->"));
898
if (startFragmentPos != -1) {
899
const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->"));
900
if (startFragmentPos < endFragmentPos)
901
html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos);
903
html = html.mid(startFragmentPos);
905
html.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />"));
907
res.d->containsCompleteDocument = false;
909
res.d->containsCompleteDocument = true;
912
QTextHTMLImporter(res.d, html).import();