/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QTextDocument) //TESTED_CLASS= //TESTED_FILES=gui/text/qtextdocumentfragment.h gui/text/qtextdocumentfragment.cpp gui/text/qtexthtmlparser.cpp gui/text/qtexthtmlparser_p.h class tst_QTextDocumentFragment : public QObject { Q_OBJECT public: tst_QTextDocumentFragment(); ~tst_QTextDocumentFragment(); public slots: void init(); void cleanup(); private slots: void listCopying(); void listZeroCopying(); void listCopying2(); void tableCopying(); void tableCopyingWithColSpans(); void tableColSpanAndWidth(); void tableImport(); void tableImport2(); void tableImport3(); void tableImport4(); void tableImport5(); void textCopy(); void copyWholeDocument(); void title(); void html_listIndents1(); void html_listIndents2(); void html_listIndents3(); void html_listIndents4(); void html_listIndents5(); void html_listIndents6(); void blockCharFormat(); void blockCharFormatCopied(); void initialBlock(); void clone(); void dontRemoveInitialBlockIfItHoldsObjectIndexedCharFormat(); void dosLineFeed(); void unorderedListEnumeration(); void resetHasBlockAfterClosedBlockTags(); void ignoreStyleTags(); void hrefAnchor(); void namedAnchorFragments(); void namedAnchorFragments2(); void namedAnchorFragments3(); void dontInheritAlignmentInTables(); void cellBlockCount(); void cellBlockCount2(); void emptyTable(); void emptyTable2(); void emptyTable3(); void doubleRowClose(); void mayNotHaveChildren(); void inheritAlignment(); void dontEmitEmptyNodeWhenEmptyTagIsFollowedByCloseTag(); void toPlainText(); void copyTableRow(); void copyTableColumn(); void copySubTable(); void html_textDecoration(); void html_infiniteLoop(); void html_blockIndent(); void html_listIndent(); void html_whitespace(); void html_whitespace_data(); void html_qt3Whitespace(); void html_qt3WhitespaceWithFragments(); void html_qt3WhitespaceAfterTags(); void html_listStart1(); void html_listStart2(); void html_cssMargin(); void html_hexEntities(); void html_decEntities(); void html_thCentered(); void orderedListNumbering(); void html_blockAfterList(); void html_subAndSuperScript(); void html_cssColors(); void obeyFragmentMarkersInImport(); void whitespaceWithFragmentMarkers(); void html_emptyParapgraphs1(); void html_emptyParapgraphs2(); void html_emptyParagraphs3(); void html_emptyParagraphs4(); void html_font(); void html_fontSize(); void html_fontSizeAdjustment(); void html_cssFontSize(); void html_cssShorthandFont(); void html_bodyBgColor(); void html_qtBgColor(); void html_blockLevelDiv(); void html_spanNesting(); void html_nestedLists(); void noSpecialCharactersInPlainText(); void html_doNotInheritBackground(); void html_inheritBackgroundToInlineElements(); void html_doNotInheritBackgroundFromBlockElements(); void html_nobr(); void fromPlainText(); void fromPlainText2(); void html_closingImageTag(); void html_emptyDocument(); void html_closingTag(); void html_anchorAroundImage(); void html_floatBorder(); void html_frameImport(); void html_frameImport2(); void html_dontAddMarginsAcrossTableCells(); void html_dontMergeCenterBlocks(); void html_tableCellBgColor(); void html_tableCellBgColor2(); void html_cellSkip(); void nonZeroMarginOnImport(); void html_charFormatPropertiesUnset(); void html_headings(); void html_quotedFontFamily(); void html_spanBackgroundColor(); void defaultFont(); void html_brokenTitle_data(); void html_brokenTitle(); void html_blockVsInline(); void html_tbody(); void html_nestedTables(); void html_rowSpans(); void html_rowSpans2(); void html_implicitParagraphs(); void html_missingCloseTag(); void html_anchorColor(); void html_lastParagraphClosing(); void html_tableHeaderBodyFootParent(); void html_columnWidths(); void html_bodyBackground(); void html_tableCellBackground(); void css_bodyBackground(); void css_tableCellBackground(); void css_fontWeight(); void css_float(); void css_textIndent(); void css_inline(); void css_external(); void css_import(); void css_selectors_data(); void css_selectors(); void css_nodeNameCaseInsensitivity(); void css_textUnderlineStyle_data(); void css_textUnderlineStyle(); void css_textUnderlineStyleAndDecoration(); void css_listStyleType(); void css_linkPseudo(); void css_pageBreaks(); void css_cellPaddings(); void universalSelectors_data(); void universalSelectors(); void screenMedia(); void htmlResourceLoading(); void someCaseInsensitiveAttributeValues(); void backgroundImage(); void dontMergePreAndNonPre(); void leftMarginInsideHtml(); void html_margins(); void newlineInsidePreShouldBecomeNewParagraph(); void invalidColspan(); void html_brokenTableWithJustTr(); void html_brokenTableWithJustTd(); void html_preNewlineHandling_data(); void html_preNewlineHandling(); void html_br(); void html_dl(); void html_tableStrangeNewline(); void html_tableStrangeNewline2(); void html_tableStrangeNewline3(); void html_caption(); void html_windowsEntities(); void html_eatenText(); void html_hr(); void html_hrMargins(); void html_blockQuoteMargins(); void html_definitionListMargins(); void html_listMargins(); void html_titleAttribute(); void html_compressDivs(); void completeToPlainText(); void copyContents(); void html_textAfterHr(); void blockTagClosing(); void isEmpty(); void html_alignmentInheritance(); void html_ignoreEmptyDivs(); void html_dontInheritAlignmentForFloatingImages(); void html_verticalImageAlignment(); void html_verticalCellAlignment(); void html_borderColor(); void html_borderStyle(); void html_borderWidth(); void html_userState(); void html_rootFrameProperties(); void html_alignmentPropertySet(); void html_appendList(); void html_appendList2(); void html_qt3RichtextWhitespaceMode(); void html_brAfterHr(); void html_unclosedHead(); void html_entities(); void html_entities_data(); void html_ignore_script(); void html_directionWithHtml(); void html_directionWithRichText(); void html_metaInBody(); void html_importImageWithoutAspectRatio(); void html_fromFirefox(); private: inline void setHtml(const QString &html) // don't take the shortcut in QTextDocument::setHtml { doc->clear(); QTextCursor(doc).insertFragment(QTextDocumentFragment::fromHtml(html)); } inline void appendHtml(const QString &html) { QTextCursor cursor(doc); cursor.movePosition(QTextCursor::End); cursor.insertHtml(html); } QTextDocument *doc; QTextCursor cursor; }; tst_QTextDocumentFragment::tst_QTextDocumentFragment() { QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); img.save("foo.png"); } tst_QTextDocumentFragment::~tst_QTextDocumentFragment() { QFile::remove(QLatin1String("foo.png")); } void tst_QTextDocumentFragment::init() { doc = new QTextDocument; cursor = QTextCursor(doc); } void tst_QTextDocumentFragment::cleanup() { cursor = QTextCursor(); delete doc; doc = 0; } #include #include static void dumpTable(const QTextDocumentPrivate *pt) { qDebug() << "---dump----"; qDebug() << "all text:" << pt->buffer(); for (QTextDocumentPrivate::FragmentIterator it = pt->begin(); !it.atEnd(); ++it) { qDebug() << "Fragment at text position" << it.position() << "; stringPosition" << it.value()->stringPosition << "; size" << it.value()->size_array[0] << "format :" << it.value()->format << "frag: " << it.n; qDebug() << " text:" << pt->buffer().mid(it.value()->stringPosition, it.value()->size_array[0]); } qDebug() << "----begin block dump----"; for (QTextBlock it = pt->blocksBegin(); it.isValid(); it = it.next()) qDebug() << "block at" << it.position() << "with length" << it.length() << "block alignment" << it.blockFormat().alignment(); qDebug() << "---dump----"; } static void dumpTable(QTextDocument *doc) { dumpTable(doc->docHandle()); } void tst_QTextDocumentFragment::listCopying() { cursor.insertList(QTextListFormat::ListDecimal); QTextFormat originalBlockFormat = cursor.blockFormat(); QVERIFY(originalBlockFormat.objectIndex() != -1); int originalListItemIdx = cursor.blockFormat().objectIndex(); cursor.insertText("Hello World"); QTextDocumentFragment fragment(doc); cursor.insertFragment(fragment); QVERIFY(cursor.currentList()); QVERIFY(cursor.blockFormat() != originalBlockFormat); QVERIFY(cursor.blockFormat().objectIndex() != originalListItemIdx); } void tst_QTextDocumentFragment::listZeroCopying() { // same testcase as above but using the zero-copying cursor.insertList(QTextListFormat::ListDecimal); QTextFormat originalBlockFormat = cursor.blockFormat(); int originalListItemIdx = cursor.blockFormat().objectIndex(); cursor.insertText("Hello World"); QTextDocumentFragment fragment(doc); cursor.insertFragment(fragment); QVERIFY(cursor.currentList()); QVERIFY(cursor.blockFormat() != originalBlockFormat); QVERIFY(cursor.blockFormat().objectIndex() != originalListItemIdx); } void tst_QTextDocumentFragment::listCopying2() { cursor.insertList(QTextListFormat::ListDecimal); cursor.insertText("Hello World"); cursor.insertList(QTextListFormat::ListDisc); cursor.insertText("Hello World"); QTextDocumentFragment fragment(doc); cursor.insertFragment(fragment); cursor.movePosition(QTextCursor::Start); int listItemCount = 0; do { if (cursor.currentList()) listItemCount++; } while (cursor.movePosition(QTextCursor::NextBlock)); QCOMPARE(listItemCount, 4); // we call this here because it used to cause a failing assertion in the // list manager. doc->undo(); } void tst_QTextDocumentFragment::tableCopying() { // this tests both, the fragment to use the direction insertion instead of using the // cursor, which might adjuts its position when inserting a table step by step, as well // as the pasiveness of the tablemanager. QTextDocumentFragment fragment; { QTextDocument doc; QTextCursor cursor(&doc); QTextTableFormat fmt; QTextTable *table = cursor.insertTable(2, 2, fmt); table->cellAt(0, 0).firstCursorPosition().insertText("First Cell"); table->cellAt(0, 1).firstCursorPosition().insertText("Second Cell"); table->cellAt(1, 0).firstCursorPosition().insertText("Third Cell"); table->cellAt(1, 1).firstCursorPosition().insertText("Fourth Cell"); fragment = QTextDocumentFragment(&doc); } { QTextDocument doc; QTextCursor cursor(&doc); cursor.insertText("FooBar"); cursor.insertBlock(); cursor.movePosition(QTextCursor::Left); cursor.insertFragment(fragment); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 2); QCOMPARE(table->columns(), 2); } } void tst_QTextDocumentFragment::tableCopyingWithColSpans() { const char html[] = "" "" " " " " " " " " " " " " "
First Cell" " Second Cell" "
Third Cell" "
Fourth Cell" " Fifth Cell" "
"; setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->columns() == 2 && table->rows() == 3); cursor = table->cellAt(2, 0).lastCursorPosition(); cursor.setPosition(table->cellAt(0, 0).firstPosition(), QTextCursor::KeepAnchor); QVERIFY(cursor.hasComplexSelection()); int firstRow = 0, numRows = 0, firstCol = 0, numCols = 0; cursor.selectedTableCells(&firstRow, &numRows, &firstCol, &numCols); QCOMPARE(firstRow, 0); QCOMPARE(numRows, 3); QCOMPARE(firstCol, 0); QCOMPARE(numCols, 1); QTextDocumentFragment frag = cursor.selection(); cleanup(); init(); cursor.insertFragment(frag); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->columns() == 1 && table->rows() == 3); } void tst_QTextDocumentFragment::tableColSpanAndWidth() { const char html[] = "" "" " " " " " " "
First Cell
"; setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->columns() == 4 && table->rows() == 1); // make sure its approx 400 and not a multiple due to the colspan QVERIFY(doc->size().width()> 398.); QVERIFY(doc->size().width() < 420.); } void tst_QTextDocumentFragment::tableImport() { // used to cause a failing assertion, as HTMLImporter::closeTag was // called twice with the last node. QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(QString::fromLatin1("
HeyBlah")); QVERIFY(!fragment.isEmpty()); } void tst_QTextDocumentFragment::tableImport2() { { const char html[] = "" "" "" "" "
First CellSecond Cell
Third CellFourth Cell
"; QTextDocument doc; QTextCursor cursor(&doc); cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); } { const char html[] = "" "" "" "" "
First CellSecond Cell
Third Cell" " " " " " " " " "
First Nested CellSecond Nested Cell
Third Nested CellFourth Nested Cell
Fifth Nested CellSixth Nested Cell
"; QTextDocument doc; QTextCursor cursor(&doc); cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); /* QTextCursor fourthCell = table->cellAt(1, 1).firstCursorPosition(); fourthCell.movePosition(QTextCursor::NextBlock); table = fourthCell.currentTable(); QVERIFY(table); QVERIFY(table != cursor.currentTable()); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 3); */ } { const char buggyHtml[] = "" "" "
First CellSecond Cell" "
Third CellFourth Cell" "
"; QTextDocument doc; QTextCursor cursor(&doc); cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(buggyHtml, sizeof(buggyHtml) / sizeof(buggyHtml[0])))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); } { const char buggyHtml[] = "" "" "
First CellSecond Cell" "
Third CellFourth Cell" "
"; QTextDocument doc; QTextCursor cursor(&doc); cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(buggyHtml, sizeof(buggyHtml) / sizeof(buggyHtml[0])))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); } } void tst_QTextDocumentFragment::tableImport3() { // ### would be better to have tree tests for QTextHtmlParser // make sure the p is a child of the td. If not the following td // ends up outside the table, causing an assertion const char html[] = "

"; QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(QString::fromLatin1(html)); QVERIFY(!fragment.isEmpty()); } void tst_QTextDocumentFragment::tableImport4() { const char html[] = "" "" "" "
blah
blahblah
"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->columns(), 2); } void tst_QTextDocumentFragment::tableImport5() { const char html[] = "" "" " " " " " " " " "" "" " " " " "" "
FooBarBlehBlub
AhhGah
"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->rows(), 2); QCOMPARE(cursor.currentTable()->columns(), 6); } void tst_QTextDocumentFragment::textCopy() { /* this test used to cause failing assertions in QTextDocumentFragment */ /* copy&paste 'lo\bwor' */ cursor.insertText("Hello"); cursor.insertBlock(); cursor.insertText("World"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, 3); cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 3); QTextDocumentFragment fragment(cursor); QVERIFY(!fragment.isEmpty()); cursor.insertFragment(fragment); } void tst_QTextDocumentFragment::copyWholeDocument() { // used to cause the famous currentBlock.position() == pos + 1 failing assertion cursor.insertText("\nHey\nBlah\n"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); fmt.setBackground(Qt::blue); doc->rootFrame()->setFrameFormat(fmt); QTextDocumentFragment fragment(cursor); QVERIFY(true); // good if we reach this point :) cleanup(); init(); fmt.setBackground(Qt::red); doc->rootFrame()->setFrameFormat(fmt); cursor.insertFragment(fragment); QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::red); } void tst_QTextDocumentFragment::title() { doc->setHtml(QString::fromLatin1("TestBlah")); QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), QString::fromLatin1("Test")); } void tst_QTextDocumentFragment::html_listIndents1() { const char html[] = "
  • Hey
  • Hah
"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); QCOMPARE(cursor.block().blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_listIndents2() { const char html[] = "
  • Hey

    Hah

"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); QCOMPARE(cursor.block().blockFormat().indent(), 0); cursor.movePosition(QTextCursor::NextBlock); QCOMPARE(cursor.block().blockFormat().indent(), 1); } void tst_QTextDocumentFragment::html_listIndents3() { const char html[] = "
  • Hah

"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); QCOMPARE(cursor.block().blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_listIndents4() { const char html[] = "
  • Foo

This should not have the same indent as Foo"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(!cursor.currentList()); QCOMPARE(cursor.blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_listIndents5() { const char html[] = "

  • Foo

  • Bar
"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentList() == list); QCOMPARE(cursor.blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_listIndents6() { const char html[] = "
  • Outer List
    • Nested Item 1
"; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); QTextList *list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 1); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentList() != list); list = cursor.currentList(); QVERIFY(list); QCOMPARE(list->format().indent(), 2); QCOMPARE(cursor.blockFormat().indent(), 0); } void tst_QTextDocumentFragment::blockCharFormat() { const char html[] = "

Test

"; setHtml(QString::fromLatin1(html)); QVERIFY(doc->begin().charFormat().fontItalic()); } void tst_QTextDocumentFragment::blockCharFormatCopied() { QTextCharFormat fmt; fmt.setForeground(Qt::green); cursor.setBlockCharFormat(fmt); cursor.insertText("Test", QTextCharFormat()); QTextDocumentFragment frag(doc); cleanup(); init(); cursor.insertFragment(frag); QVERIFY(cursor.blockCharFormat() == fmt); } void tst_QTextDocumentFragment::initialBlock() { const char html[] = "

Test

"; setHtml(QString::fromLatin1(html)); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocumentFragment::clone() { QTextBlockFormat mod; mod.setAlignment(Qt::AlignCenter); cursor.mergeBlockFormat(mod); cursor.insertText("Blah"); QVERIFY(cursor.blockFormat().alignment() == Qt::AlignCenter); QTextDocumentFragment frag(doc); cleanup(); init(); cursor.insertFragment(frag); cursor.movePosition(QTextCursor::Start); QVERIFY(cursor.blockFormat().alignment() == Qt::AlignCenter); } void tst_QTextDocumentFragment::dontRemoveInitialBlockIfItHoldsObjectIndexedCharFormat() { const char html[] = "
cell onecell two
cell threecell four
"; QVERIFY(doc->begin().charFormat().objectIndex() == -1); setHtml(QString::fromLatin1(html)); int cnt = 0; int objectIndexOfLast = -1; for (QTextBlock blk = doc->begin(); blk.isValid(); blk = blk.next()) { ++cnt; objectIndexOfLast = blk.charFormat().objectIndex(); } // beginning of frame for first cell // + beginning of frame for second cell // + beginning of frame for third cell // + beginning of frame for fourth cell // + end of frame // + initial block // ==> 6 QCOMPARE(cnt, 6); QVERIFY(objectIndexOfLast != -1); QVERIFY(doc->begin().next().charFormat().objectIndex() != -1); } void tst_QTextDocumentFragment::dosLineFeed() { const char html[] = "
Test\r\n
Bar"; setHtml(QString::fromLatin1(html)); QVERIFY(!doc->toPlainText().contains('\r')); QCOMPARE(doc->toPlainText(), QString("Test\nBar")); } void tst_QTextDocumentFragment::unorderedListEnumeration() { const char html[] = "
      • Blah
    "; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle); const char html2[] = "
        • Blah
      "; setHtml(QString::fromLatin1(html2)); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDisc); } void tst_QTextDocumentFragment::resetHasBlockAfterClosedBlockTags() { // when closing tags we have to make sure hasBlock in import() gets resetted const char html[] = "

      "; setHtml(QString::fromLatin1(html)); QVERIFY(!doc->isEmpty()); } void tst_QTextDocumentFragment::ignoreStyleTags() { const char html[] = "Hello"; setHtml(QString::fromLatin1(html)); QCOMPARE(doc->toPlainText(), QString("Hello")); } void tst_QTextDocumentFragment::hrefAnchor() { { const char html[] = "blah"; setHtml(QString::fromLatin1(html)); QVERIFY(doc->begin().begin().fragment().charFormat().isAnchor()); QCOMPARE(doc->begin().begin().fragment().charFormat().anchorHref(), QString::fromAscii("test")); QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline() == true); } { // only hyperlinks should have special formatting const char html[] = "blah"; setHtml(QString::fromLatin1(html)); QVERIFY(doc->begin().begin().fragment().charFormat().isAnchor()); QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline() == false); } } void tst_QTextDocumentFragment::namedAnchorFragments() { // named anchors should be 'invisible', but the fragment right after it should // hold the attribute const char html[] = "ablah"; setHtml(QString::fromLatin1(html)); QTextBlock firstBlock = doc->begin(); QVERIFY(firstBlock.isValid()); QTextBlock::Iterator it = firstBlock.begin(); QVERIFY(!it.atEnd()); // the 'a' QVERIFY(it.fragment().isValid()); QCOMPARE(it.fragment().text(), QString::fromAscii("a")); QVERIFY(it.fragment().charFormat().isAnchor() == false); // the 'b' of 'blah' as separate fragment with the anchor attribute ++it; QVERIFY(it.fragment().isValid()); QCOMPARE(it.fragment().text(), QString::fromAscii("b")); QVERIFY(it.fragment().charFormat().isAnchor()); // the 'lah' of 'blah' as remainder ++it; QVERIFY(it.fragment().isValid()); QVERIFY(it.fragment().text().startsWith("lah")); QVERIFY(it.fragment().charFormat().isAnchor() == false); } void tst_QTextDocumentFragment::namedAnchorFragments2() { const char html[] = "

      Hello"; setHtml(QString::fromLatin1(html)); QCOMPARE(doc->toPlainText(), QString("Hello")); QTextBlock::Iterator it = doc->begin().begin(); QVERIFY(!it.atEnd()); QCOMPARE(it.fragment().text(), QString::fromAscii("H")); QVERIFY(it.fragment().charFormat().isAnchor()); ++it; QCOMPARE(it.fragment().text(), QString::fromAscii("ello")); QVERIFY(!it.fragment().charFormat().isAnchor()); } void tst_QTextDocumentFragment::namedAnchorFragments3() { setHtml("Text"); QCOMPARE(doc->toPlainText(), QString("Text")); QTextBlock::Iterator it = doc->begin().begin(); QVERIFY(!it.atEnd()); QCOMPARE(it.fragment().text(), QString::fromAscii("T")); QVERIFY(it.fragment().charFormat().isAnchor()); QCOMPARE(it.fragment().charFormat().anchorName(), QString("target")); QStringList targets; targets << "target" << "target2"; QCOMPARE(it.fragment().charFormat().anchorNames(), targets); ++it; QCOMPARE(it.fragment().text(), QString::fromAscii("ext")); QVERIFY(!it.fragment().charFormat().isAnchor()); } void tst_QTextDocumentFragment::dontInheritAlignmentInTables() { const char html[] = "
      Hey
      "; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QVERIFY(cursor.currentTable()->cellAt(0, 0).isValid()); QVERIFY(cursor.currentTable()->cellAt(0, 0).firstCursorPosition().block().next().blockFormat().alignment() != Qt::AlignHCenter); } void tst_QTextDocumentFragment::cellBlockCount() { const char html[] = "
      Hey
      "; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QTextTableCell cell = cursor.currentTable()->cellAt(0, 0); QVERIFY(cell.isValid()); int blockCount = 0; for (QTextFrame::iterator it = cell.begin(); !it.atEnd(); ++it) { QVERIFY(it.currentFrame() == 0); QVERIFY(it.currentBlock().isValid()); ++blockCount; } QCOMPARE(blockCount, 1); } void tst_QTextDocumentFragment::cellBlockCount2() { const char html[] = "

      Hey

      "; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QTextTableCell cell = cursor.currentTable()->cellAt(0, 0); QVERIFY(cell.isValid()); int blockCount = 0; for (QTextFrame::iterator it = cell.begin(); !it.atEnd(); ++it) { QVERIFY(it.currentFrame() == 0); QVERIFY(it.currentBlock().isValid()); ++blockCount; } QCOMPARE(blockCount, 1); } void tst_QTextDocumentFragment::emptyTable() { const char html[] = "
      "; setHtml(QString::fromLatin1(html)); QVERIFY(true); // don't crash with a failing assertion } void tst_QTextDocumentFragment::emptyTable2() { const char html[] = "

      blah

      "; setHtml(QString::fromLatin1(html)); QVERIFY(true); // don't crash with a failing assertion } void tst_QTextDocumentFragment::emptyTable3() { const char html[] = "
      Foobar
      "; setHtml(QString::fromLatin1(html)); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 2); QTextTableCell cell = table->cellAt(0, 0); QVERIFY(cell.isValid()); QVERIFY(cell.firstPosition() == cell.lastPosition()); cell = table->cellAt(0, 1); QTextCursor cursor = cell.firstCursorPosition(); cursor.setPosition(cell.lastPosition(), QTextCursor::KeepAnchor); QCOMPARE(cursor.selectedText(), QString("Foobar")); } void tst_QTextDocumentFragment::doubleRowClose() { const char html[] = "
      Blah
      Hm
      "; setHtml(QString::fromLatin1(html)); QVERIFY(true); // don't crash with a failing assertion } void tst_QTextDocumentFragment::mayNotHaveChildren() { // make sure the Hey does not end up as tag text for the img tag const char html[] = "Hey"; setHtml(QString::fromLatin1(html)); QCOMPARE(doc->toPlainText().mid(1), QString::fromAscii("Hey")); } void tst_QTextDocumentFragment::inheritAlignment() { // make sure attributes from the body tag get inherited const char html[] = "

      Hey"; setHtml(QString::fromLatin1(html)); // html alignment is absolute QVERIFY(doc->begin().blockFormat().alignment() == Qt::Alignment(Qt::AlignRight|Qt::AlignAbsolute)); } void tst_QTextDocumentFragment::dontEmitEmptyNodeWhenEmptyTagIsFollowedByCloseTag() { // make sure the Hey does not end up as tag text for the img tag const char html[] = "

      Blah

      Hey"; setHtml(QString::fromLatin1(html)); QVERIFY(doc->begin().blockFormat().alignment() == Qt::Alignment(Qt::AlignLeft|Qt::AlignAbsolute)); QVERIFY(doc->begin().next().blockFormat().alignment() == Qt::Alignment(Qt::AlignRight|Qt::AlignAbsolute)); } void tst_QTextDocumentFragment::toPlainText() { QString input = "Hello\nWorld"; input += QChar::ParagraphSeparator; input += "Blah"; doc->setPlainText(input); QCOMPARE(doc->blockCount(), 3); } void tst_QTextDocumentFragment::copyTableRow() { QTextDocumentFragment frag; { QTextTable *table = cursor.insertTable(2, 2); table->cellAt(0, 0).firstCursorPosition().insertText("Blah"); table->cellAt(0, 1).firstCursorPosition().insertText("Foo"); table->cellAt(1, 0).firstCursorPosition().insertText("Bar"); table->cellAt(1, 1).firstCursorPosition().insertText("Hah"); // select second row cursor = table->cellAt(1, 1).firstCursorPosition(); cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::KeepAnchor); QCOMPARE(table->cellAt(cursor.position()).row(), 1); QCOMPARE(table->cellAt(cursor.position()).column(), 0); QCOMPARE(table->cellAt(cursor.anchor()).row(), 1); QCOMPARE(table->cellAt(cursor.anchor()).column(), 1); frag = QTextDocumentFragment(cursor); } { QTextDocument doc2; cursor = QTextCursor(&doc2); cursor.insertFragment(frag); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Bar")); QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Hah")); } } void tst_QTextDocumentFragment::copyTableColumn() { QTextDocumentFragment frag; { QTextTable *table = cursor.insertTable(2, 2); table->cellAt(0, 0).firstCursorPosition().insertText("Blah"); table->cellAt(0, 1).firstCursorPosition().insertText("Foo"); table->cellAt(1, 0).firstCursorPosition().insertText("Bar"); table->cellAt(1, 1).firstCursorPosition().insertText("Hah"); // select second column cursor = table->cellAt(0, 1).firstCursorPosition(); cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); QCOMPARE(table->cellAt(cursor.anchor()).row(), 0); QCOMPARE(table->cellAt(cursor.anchor()).column(), 1); QCOMPARE(table->cellAt(cursor.position()).row(), 1); QCOMPARE(table->cellAt(cursor.position()).column(), 1); frag = QTextDocumentFragment(cursor); } { QTextDocument doc2; cursor = QTextCursor(&doc2); cursor.insertFragment(frag); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 2); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Foo")); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Hah")); } } void tst_QTextDocumentFragment::copySubTable() { QTextDocumentFragment frag; { QTextTableFormat fmt; QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 16); constraints << QTextLength(QTextLength::PercentageLength, 28); constraints << QTextLength(QTextLength::PercentageLength, 28); constraints << QTextLength(QTextLength::PercentageLength, 28); fmt.setColumnWidthConstraints(constraints); QTextTable *table = cursor.insertTable(4, 4, fmt); for (int row = 0; row < 4; ++row) for (int col = 0; col < 4; ++col) table->cellAt(row, col).firstCursorPosition().insertText(QString("%1/%2").arg(row).arg(col)); QCOMPARE(table->format().columnWidthConstraints().count(), table->columns()); // select 2x2 subtable cursor = table->cellAt(1, 1).firstCursorPosition(); cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); QCOMPARE(table->cellAt(cursor.anchor()).row(), 1); QCOMPARE(table->cellAt(cursor.anchor()).column(), 1); QCOMPARE(table->cellAt(cursor.position()).row(), 2); QCOMPARE(table->cellAt(cursor.position()).column(), 2); frag = QTextDocumentFragment(cursor); } { QTextDocument doc2; cursor = QTextCursor(&doc2); cursor.insertFragment(frag); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->format().columnWidthConstraints().isEmpty()); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("1/1")); QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("1/2")); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("2/1")); QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("2/2")); } } void tst_QTextDocumentFragment::html_textDecoration() { const char html[] = "Blah"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().fontUnderline()); QVERIFY(cursor.charFormat().fontOverline()); QVERIFY(cursor.charFormat().fontStrikeOut()); } void tst_QTextDocumentFragment::html_infiniteLoop() { { // used to cause an infinite loop due to the lack of a space after the // tag name const char html[] = "Link"; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QVERIFY(true); } { const char html[] = "Test

      "; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QCOMPARE(cursor.blockFormat().indent(), 3); } void tst_QTextDocumentFragment::html_listIndent() { const char html[] = "
      • Blah
      "; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QVERIFY(cursor.currentList()); QCOMPARE(cursor.currentList()->format().indent(), 4); } void tst_QTextDocumentFragment::html_whitespace_data() { QTest::addColumn("html"); QTest::addColumn("expectedPlainText"); QTest::newRow("1") << QString("This is some test with spaces between words") << QString("This is some test with spaces between words"); QTest::newRow("2") << QString(" nowhitespacehereplease") << QString::fromLatin1("nowhitespacehereplease"); QTest::newRow("3") << QString(" white space here ") << QString::fromLatin1(" white space here "); QTest::newRow("4") << QString(" white space here ") << QString::fromLatin1(" white space here "); QTest::newRow("5") << QString("
      One Two Three\n" "Four") << QString::fromLatin1("One Two Three Four"); QTest::newRow("6") << QString("

      Testing: BoldItalic Italic

      ") << QString("Testing: BoldItalic Italic"); QTest::newRow("7") << QString("
      Blah
      Foo
      ") << QString("\nBlah\n\nFoo\n"); QTest::newRow("8") << QString("
      Blah
      Blub") << QString("\nBlah\nBlub"); QTest::newRow("task116492") << QString("

      a b c

      ") << QString("a b c"); QTest::newRow("task121653") << QString("abc def") << QString("abc def"); QTest::newRow("task122650") << QString("

      Foo

      Bar") << QString("Foo\nBar"); QTest::newRow("task122650-2") << QString("

      Foo

      Bar") << QString("Foo \nBar"); QTest::newRow("task122650-3") << QString("Before

      \nTest
      ") << QString("Before\nTest"); QTest::newRow("br-with-whitespace") << QString("Foo
      \nBlah") << QString("Foo\nBlah"); QTest::newRow("collapse-p-with-newline") << QString("Foo

      \n

      \n

      \n

      \n

      \n

      \nBar") << QString("Foo\nBar"); QTest::newRow("table") << QString("
      Blah
      \nTest") << QString("\nBlah\nTest"); QTest::newRow("table2") << QString("\n
      \nTest\n
      ") << QString("\nTest\n"); QTest::newRow("table3") << QString("
      \nTest\n
      \n \n
      ") << QString("\nTest \n"); } void tst_QTextDocumentFragment::html_whitespace() { QFETCH(QString, html); QFETCH(QString, expectedPlainText); setHtml(html); QCOMPARE(doc->toPlainText(), expectedPlainText); } void tst_QTextDocumentFragment::html_qt3Whitespace() { QString text = "This text has some whitespace" "\n and \nnewlines that \n should be ignored\n\n"; const QString html = QString("") + text + QString(""); cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); text.remove(QChar::fromLatin1('\n')); QCOMPARE(doc->toPlainText(), text); } void tst_QTextDocumentFragment::html_qt3WhitespaceWithFragments() { QString text = "This text has some whitespace" "\n and \nnewlines that \n should be ignored\n\n"; const QString html = QString("" "blah blah") + text + QString(""); cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); text.remove(QChar::fromLatin1('\n')); QCOMPARE(doc->toPlainText(), text); } void tst_QTextDocumentFragment::html_qt3WhitespaceAfterTags() { QString text = " This text has some whitespace "; const QString html = QString("") + text + QString(""); cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QCOMPARE(doc->toPlainText(), text); } void tst_QTextDocumentFragment::html_listStart1() { // don't create a block for the

        element, even if there's some whitespace between // it and the
      • const char html[] = "
        • list item
          • "; cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocumentFragment::html_listStart2() { // unlike with html_listStart1 we want a block showing the 'buggy' text here const char html[] = "
              buggy, but text should appear
            • list item
              • "; cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); QCOMPARE(doc->blockCount(), 2); } void tst_QTextDocumentFragment::html_cssMargin() { const char html[] = "

                Test

                "; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); const QTextBlockFormat fmt = cursor.blockFormat(); QCOMPARE(fmt.topMargin(), qreal(1)); QCOMPARE(fmt.bottomMargin(), qreal(2)); QCOMPARE(fmt.leftMargin(), qreal(3)); QCOMPARE(fmt.rightMargin(), qreal(4)); } void tst_QTextDocumentFragment::html_hexEntities() { const char html[] = "@"; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QCOMPARE(doc->begin().begin().fragment().text(), QString("@")); } void tst_QTextDocumentFragment::html_decEntities() { const char html[] = "@"; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); QCOMPARE(doc->begin().begin().fragment().text(), QString("@")); } void tst_QTextDocumentFragment::html_thCentered() { const char html[] = "
                This should be centered
                "; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); cursor.movePosition(QTextCursor::PreviousBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->cellAt(0, 0).begin().currentBlock().blockFormat().alignment() == Qt::AlignCenter); } void tst_QTextDocumentFragment::orderedListNumbering() { // Supporter issue 45941 - make sure _two_ separate lists // are imported, which have their own numbering const char html[] = "" "
                1. elem 1
                " "
                1. elem 1
                " ""; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); int numberOfLists = 0; cursor.movePosition(QTextCursor::Start); QTextList *lastList = 0; do { QTextList *list = cursor.currentList(); if (list && list != lastList) { lastList = list; ++numberOfLists; } } while (cursor.movePosition(QTextCursor::NextBlock)); QCOMPARE(numberOfLists, 2); } void tst_QTextDocumentFragment::html_blockAfterList() { const char html[] = "
                • Foo
                This should be a separate paragraph and not be indented at the same level as Foo"; cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); cursor.movePosition(QTextCursor::Start); QVERIFY(cursor.currentList()); QCOMPARE(cursor.currentList()->format().indent(), 1); QVERIFY(cursor.movePosition(QTextCursor::NextBlock)); QVERIFY(!cursor.currentList()); QCOMPARE(cursor.blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_subAndSuperScript() { const char subHtml[] = "Subby"; const char superHtml[] = "Super"; const char subHtmlCss[] = "Subby"; const char superHtmlCss[] = "Super"; const char alignmentInherited[] = "Subby"; setHtml(subHtml); QVERIFY(cursor.charFormat().verticalAlignment() == QTextCharFormat::AlignSubScript); setHtml(subHtmlCss); QVERIFY(cursor.charFormat().verticalAlignment() == QTextCharFormat::AlignSubScript); setHtml(superHtml); QVERIFY(cursor.charFormat().verticalAlignment() == QTextCharFormat::AlignSuperScript); setHtml(superHtmlCss); QVERIFY(cursor.charFormat().verticalAlignment() == QTextCharFormat::AlignSuperScript); setHtml(alignmentInherited); QVERIFY(cursor.charFormat().verticalAlignment() == QTextCharFormat::AlignSubScript); } void tst_QTextDocumentFragment::html_cssColors() { const char color[] = "Blue"; setHtml(color); QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); const char rgbColor[] = "Blue"; setHtml(rgbColor); QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); } void tst_QTextDocumentFragment::obeyFragmentMarkersInImport() { const char html[] = "This leading text should not appearTextThis text at the end should not appear"; setHtml(html); QCOMPARE(doc->toPlainText(), QString("Text")); } void tst_QTextDocumentFragment::whitespaceWithFragmentMarkers() { QString text(" text with leading and trailing whitespace "); const char html[] = "This leading text should not appear%1This text at the end should not appear"; setHtml(QString::fromLatin1(html).arg(text)); QString expected("text with leading and trailing whitespace "); QCOMPARE(doc->toPlainText(), expected); } void tst_QTextDocumentFragment::html_emptyParapgraphs1() { const char html[] = "

                 

                Two paragraphs

                "; setHtml(html); QCOMPARE(doc->blockCount(), 2); QVERIFY(doc->begin().text().isEmpty()); QCOMPARE(doc->begin().next().text(), QString("Two paragraphs")); } void tst_QTextDocumentFragment::html_emptyParapgraphs2() { const char html[] = "

                One paragraph

                "; setHtml(html); QCOMPARE(doc->blockCount(), 1); QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0)); const char html2[] = "

                One paragraph"; setHtml(html2); QCOMPARE(doc->blockCount(), 1); QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0)); const char html3[] = "

                Foo

                Two paragraphs"; setHtml(html3); QCOMPARE(doc->blockCount(), 2); cursor = QTextCursor(doc); QCOMPARE(cursor.blockFormat().leftMargin(), qreal(80)); QCOMPARE(cursor.block().next().blockFormat().leftMargin(), qreal(0)); } void tst_QTextDocumentFragment::html_emptyParagraphs3() { const char html[] = "

                  Foo

                Bar

                "; setHtml(html); QCOMPARE(doc->blockCount(), 2); cursor = QTextCursor(doc); QCOMPARE(cursor.block().next().blockFormat().indent(), 0); } void tst_QTextDocumentFragment::html_emptyParagraphs4() { const char html[] = "

                foo

                bar

                "; setHtml(html); QTextBlock block = doc->begin(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("foo")); block = block.next(); QVERIFY(block.isValid()); QTextBlockFormat bf = block.blockFormat(); QVERIFY(bf.hasProperty(QTextFormat::PageBreakPolicy)); QCOMPARE(bf.pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore); QCOMPARE(block.text(), QString("bar")); const char html2[] = "

                foo

                bar

                "; setHtml(html2); block = doc->begin(); QVERIFY(block.isValid()); QCOMPARE(block.text(), QString("foo")); block = block.next(); QVERIFY(block.isValid()); bf = block.blockFormat(); QVERIFY(bf.hasProperty(QTextFormat::PageBreakPolicy)); QCOMPARE(bf.pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore); // after the empty line means it should appear for 'bar' QCOMPARE(block.text(), QString("bar")); } void tst_QTextDocumentFragment::html_font() { const char html[] = "

                Hah

                "; setHtml(html); QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue); } void tst_QTextDocumentFragment::html_fontSize() { const char html[] = "Hah"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), -1); } void tst_QTextDocumentFragment::html_fontSizeAdjustment() { const char html[] = "Hah"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), 4); QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold)); } void tst_QTextDocumentFragment::html_cssFontSize() { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50); const char html2[] = "Foo"; setHtml(html2); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50); const char html3[] = "Foo"; setHtml(html3); QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), 1); } void tst_QTextDocumentFragment::html_cssShorthandFont() { { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50); QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif")); } { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50); QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif")); } { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7); QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("Times New Roman")); } { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontWeight).toInt(), int(QFont::Bold)); QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7); } { const char html[] = "Foo"; setHtml(html); QCOMPARE(cursor.charFormat().property(QTextFormat::FontWeight).toInt(), int(QFont::Bold)); QCOMPARE(cursor.charFormat().property(QTextFormat::FontItalic).toBool(), true); } } void tst_QTextDocumentFragment::html_bodyBgColor() { const char html[] = "Foo"; doc->setHtml(html); QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue); } void tst_QTextDocumentFragment::html_qtBgColor() { const char html[] = "Foo"; doc->setHtml(html); QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue); } void tst_QTextDocumentFragment::html_bodyBackground() { const char html[] = "Foo"; doc->setHtml(html); QVERIFY(doc->rootFrame()->frameFormat().background().style() == Qt::TexturePattern); } void tst_QTextDocumentFragment::html_tableCellBackground() { const char html[] = "
                Foo
                "; doc->setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QTextTableCell cell = table->cellAt(0, 0); QVERIFY(cell.format().background().style() == Qt::TexturePattern); } void tst_QTextDocumentFragment::css_bodyBackground() { const char html[] = "Foo"; doc->setHtml(html); QVERIFY(doc->rootFrame()->frameFormat().background().style() == Qt::TexturePattern); } void tst_QTextDocumentFragment::css_tableCellBackground() { const char html[] = "
                Foo
                "; doc->setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QTextTableCell cell = table->cellAt(0, 0); QVERIFY(cell.format().background().style() == Qt::TexturePattern); } void tst_QTextDocumentFragment::css_cellPaddings() { const char html[] = "" "" "
                Foo
                "; doc->setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QTextTableCell cell = table->cellAt(0, 0); QCOMPARE(cell.format().toTableCellFormat().leftPadding(), qreal(1)); cell = table->cellAt(0, 1); QCOMPARE(cell.format().toTableCellFormat().rightPadding(), qreal(1)); cell = table->cellAt(0, 2); QCOMPARE(cell.format().toTableCellFormat().topPadding(), qreal(10)); cell = table->cellAt(0, 3); QCOMPARE(cell.format().toTableCellFormat().bottomPadding(), qreal(5)); cell = table->cellAt(0, 4); QCOMPARE(cell.format().toTableCellFormat().leftPadding(), qreal(15)); QCOMPARE(cell.format().toTableCellFormat().rightPadding(), qreal(15)); QCOMPARE(cell.format().toTableCellFormat().topPadding(), qreal(15)); QCOMPARE(cell.format().toTableCellFormat().bottomPadding(), qreal(15)); } void tst_QTextDocumentFragment::html_blockLevelDiv() { const char html[] = "
                Hello World"; setHtml(html); QCOMPARE(doc->begin().blockFormat().alignment(), Qt::AlignRight|Qt::AlignAbsolute); QVERIFY(doc->begin().next() == doc->end()); } void tst_QTextDocumentFragment::html_spanNesting() { const char html[] = "abcd"; setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground() == Qt::black); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground() == Qt::red); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground() == Qt::black); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground() == Qt::black); } void tst_QTextDocumentFragment::html_nestedLists() { const char html[] = "

                • Foo
                  • In nested list
                • Last item

                "; setHtml(html); cursor.movePosition(QTextCursor::Start); QTextList *firstList = cursor.currentList(); QVERIFY(firstList); QCOMPARE(firstList->format().indent(), 1); cursor.movePosition(QTextCursor::NextBlock); QTextList *secondList = cursor.currentList(); QVERIFY(secondList); QVERIFY(secondList != firstList); QCOMPARE(cursor.currentList()->format().indent(), 2); cursor.movePosition(QTextCursor::NextBlock); QTextList *thirdList = cursor.currentList(); QVERIFY(thirdList); QVERIFY(thirdList == firstList); } void tst_QTextDocumentFragment::noSpecialCharactersInPlainText() { cursor.insertTable(2, 2); cursor.insertBlock(); cursor.insertText(QString(QChar::LineSeparator)); cursor.insertText(QString(QChar::Nbsp)); QString plain = doc->toPlainText(); QVERIFY(!plain.contains(QChar::ParagraphSeparator)); QVERIFY(!plain.contains(QChar::Nbsp)); QVERIFY(!plain.contains(QTextBeginningOfFrame)); QVERIFY(!plain.contains(QTextEndOfFrame)); QVERIFY(!plain.contains(QChar::LineSeparator)); plain = QTextDocumentFragment(doc).toPlainText(); QVERIFY(!plain.contains(QChar::ParagraphSeparator)); QVERIFY(!plain.contains(QChar::Nbsp)); QVERIFY(!plain.contains(QTextBeginningOfFrame)); QVERIFY(!plain.contains(QTextEndOfFrame)); QVERIFY(!plain.contains(QChar::LineSeparator)); } void tst_QTextDocumentFragment::html_doNotInheritBackground() { const char html[] = "

                Blah

                "; doc->setHtml(html); for (QTextBlock block = doc->begin(); block.isValid(); block = block.next()) { QVERIFY(block.blockFormat().hasProperty(QTextFormat::BackgroundBrush) == false); } QVERIFY(doc->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush)); QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue); } void tst_QTextDocumentFragment::html_inheritBackgroundToInlineElements() { const char html[] = "FooBar"; doc->setHtml(html); int fragmentCount = 0; QTextBlock block = doc->begin(); for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it, ++fragmentCount) { const QTextFragment fragment = it.fragment(); if (fragmentCount == 0) { QCOMPARE(fragment.text(), QString("FooBar")); QVERIFY(fragment.charFormat().background().color() == Qt::blue); } } QCOMPARE(fragmentCount, 1); } void tst_QTextDocumentFragment::html_doNotInheritBackgroundFromBlockElements() { const char html[] = "

                Foo"; doc->setHtml(html); int fragmentCount = 0; QTextBlock block = doc->begin(); for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it, ++fragmentCount) { const QTextFragment fragment = it.fragment(); if (fragmentCount == 0) { QCOMPARE(fragment.text(), QString("Foo")); QVERIFY(!fragment.charFormat().hasProperty(QTextFormat::BackgroundBrush)); } } QCOMPARE(fragmentCount, 1); } void tst_QTextDocumentFragment::html_nobr() { const QString input = "Blah Foo Bar"; const QString html = QString::fromLatin1("

                ") + input + QString::fromLatin1("

                "); setHtml(html); QString text = doc->begin().begin().fragment().text(); QString expectedText = input; expectedText.replace(QRegExp("\\s+"), QString(QChar::Nbsp)); QCOMPARE(text, expectedText); } void tst_QTextDocumentFragment::fromPlainText() { QString plainText; plainText = "Hello\nWorld\r\nBlub"; plainText += QChar::ParagraphSeparator; // TextEdit on OS 10 gives us OS 9 style linefeeds // when copy & pasteing multi-line plaintext. plainText += "OS9IsOldSchool\r"; plainText += "Last Parag"; doc->setPlainText(plainText); int blockCount = 0; for (QTextBlock block = doc->begin(); block.isValid(); block = block.next()) { QVERIFY(!block.text().contains(QLatin1Char('\n'))); QVERIFY(!block.text().contains(QLatin1Char('\r'))); QVERIFY(!block.text().contains(QChar::ParagraphSeparator)); if (blockCount == 0) QCOMPARE(block.text(), QString("Hello")); else if (blockCount == 1) QCOMPARE(block.text(), QString("World")); else if (blockCount == 2) QCOMPARE(block.text(), QString("Blub")); else if (blockCount == 3) QCOMPARE(block.text(), QString("OS9IsOldSchool")); else if (blockCount == 4) QCOMPARE(block.text(), QString("Last Parag")); ++blockCount; } QCOMPARE(blockCount, 5); } void tst_QTextDocumentFragment::fromPlainText2() { doc->setPlainText("Hello World"); QCOMPARE(QTextDocumentFragment(doc).toPlainText(), doc->toPlainText()); } void tst_QTextDocumentFragment::html_closingImageTag() { const char html[] = "BlahFoo"; setHtml(html); int fragmentCount = 0; QTextBlock block = doc->begin(); for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it, ++fragmentCount) { const QTextFragment fragment = it.fragment(); if (fragmentCount == 0) { QCOMPARE(fragment.text(), QString("Blah")); QCOMPARE(fragment.charFormat().fontPointSize(), qreal(40)); } else if (fragmentCount == 1) { QCOMPARE(fragment.text(), QString(QChar::ObjectReplacementCharacter)); } else if (fragmentCount == 2) { QCOMPARE(fragment.text(), QString("Foo")); QCOMPARE(fragment.charFormat().fontPointSize(), qreal(40)); } } QCOMPARE(fragmentCount, 3); } void tst_QTextDocumentFragment::html_emptyDocument() { const char html[] = "

                "; setHtml(html); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocumentFragment::html_closingTag() { const char html[] = "text"; setHtml(html); QVERIFY(!cursor.charFormat().fontItalic()); } void tst_QTextDocumentFragment::html_anchorAroundImage() { const char html[] = ""; setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QTextImageFormat fmt = cursor.charFormat().toImageFormat(); QCOMPARE(fmt.name(), QString("test.png")); QVERIFY(fmt.isAnchor()); QCOMPARE(fmt.anchorHref(), QString("http://www.troll.no")); } void tst_QTextDocumentFragment::html_floatBorder() { const char html[] = "
                Foo"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->format().border(), qreal(1.2)); } void tst_QTextDocumentFragment::html_frameImport() { QTextFrameFormat ffmt; ffmt.setBorder(1); ffmt.setPosition(QTextFrameFormat::FloatRight); ffmt.setMargin(2); ffmt.setWidth(100); ffmt.setHeight(50); ffmt.setBackground(QColor("#00ff00")); cursor.insertFrame(ffmt); cursor.insertText("Hello World"); QTextDocumentFragment frag(doc); cleanup(); init(); frag = QTextDocumentFragment::fromHtml(frag.toHtml()); cursor.insertFragment(frag); QList childFrames = doc->rootFrame()->childFrames(); QVERIFY(childFrames.count() == 1); QTextFrame *frame = childFrames.first(); QCOMPARE(frame->frameFormat().margin(), ffmt.margin()); QCOMPARE(frame->frameFormat().border(), ffmt.border()); } void tst_QTextDocumentFragment::html_frameImport2() { QTextFrameFormat ffmt; ffmt.setBorder(1); ffmt.setPosition(QTextFrameFormat::FloatRight); ffmt.setLeftMargin(200); ffmt.setTopMargin(100); ffmt.setBottomMargin(50); ffmt.setRightMargin(250); ffmt.setWidth(100); ffmt.setHeight(50); ffmt.setBackground(QColor("#00ff00")); cursor.insertFrame(ffmt); cursor.insertText("Hello World"); QTextDocumentFragment frag(doc); cleanup(); init(); frag = QTextDocumentFragment::fromHtml(frag.toHtml()); cursor.insertFragment(frag); QList childFrames = doc->rootFrame()->childFrames(); QVERIFY(childFrames.count() == 1); QTextFrame *frame = childFrames.first(); QCOMPARE(frame->frameFormat().topMargin(), ffmt.topMargin()); QCOMPARE(frame->frameFormat().bottomMargin(), ffmt.bottomMargin()); QCOMPARE(frame->frameFormat().leftMargin(), ffmt.leftMargin()); QCOMPARE(frame->frameFormat().rightMargin(), ffmt.rightMargin()); QCOMPARE(frame->frameFormat().border(), ffmt.border()); } void tst_QTextDocumentFragment::html_dontAddMarginsAcrossTableCells() { const char html[] = "

                Foo

                "; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); QList childFrames = doc->rootFrame()->childFrames(); QVERIFY(childFrames.count() == 1); QTextFrame *frame = childFrames.first(); cursor = frame->firstCursorPosition(); QCOMPARE(cursor.blockFormat().leftMargin(), qreal(50.0)); } void tst_QTextDocumentFragment::html_dontMergeCenterBlocks() { const char html[] = "
                This should be centered
                And this should not be centered anymore"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); QCOMPARE(doc->blockCount(), 2); QTextBlock blk = doc->begin(); QVERIFY(blk.blockFormat().alignment() == Qt::AlignCenter); blk = blk.next(); QVERIFY(blk.blockFormat().alignment() != Qt::AlignCenter); } void tst_QTextDocumentFragment::html_tableCellBgColor() { const char html[] = "
                Test

                Second Parag

                "; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QTextTableCell cell = table->cellAt(0, 0); QVERIFY(cell.format().background().color() == Qt::blue); } void tst_QTextDocumentFragment::html_tableCellBgColor2() { const char html[] = "
                Blah
                "; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QTextTableCell cell = table->cellAt(0, 0); QVERIFY(cell.format().background().color() == Qt::blue); QTextFrame::Iterator it = cell.begin(); QVERIFY(!it.atEnd()); QVERIFY(it.currentFrame() == 0); QVERIFY(it.currentBlock().isValid()); ++it; QVERIFY(!it.atEnd()); QVERIFY(it.currentFrame() != 0); QVERIFY(!it.currentBlock().isValid()); ++it; QVERIFY(!it.atEnd()); QVERIFY(it.currentFrame() == 0); QVERIFY(it.currentBlock().isValid()); QVERIFY(it.currentBlock().blockFormat().background() == QBrush(Qt::NoBrush)); ++it; QVERIFY(it.atEnd()); } void tst_QTextDocumentFragment::html_cellSkip() { const char html[] = "" "" " " " " " " " " " " " " " " "
                First Cell
                Second CellThird Cell
                "; setHtml(html); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QVERIFY(table->columns() == 2 && table->rows() == 2); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell")); QVERIFY(table->cellAt(0, 1).firstCursorPosition().block().text().isEmpty()); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell")); QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Third Cell")); } void tst_QTextDocumentFragment::nonZeroMarginOnImport() { // specify bgcolor so that the html import creates a root frame format setHtml("Hello World"); QVERIFY(doc->rootFrame()->frameFormat().margin() > 0.0); } void tst_QTextDocumentFragment::html_charFormatPropertiesUnset() { setHtml("Hello World"); QVERIFY(doc->begin().begin().fragment().charFormat().properties().isEmpty()); } void tst_QTextDocumentFragment::html_headings() { setHtml("

                foo

                bar"); QCOMPARE(doc->blockCount(), 2); } void tst_QTextDocumentFragment::html_quotedFontFamily() { setHtml("
                Test
                "); QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); setHtml("
                Test
                "); QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); setHtml("
                Test
                "); QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); setHtml("
                Test
                "); QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); setHtml("
                Test
                "); QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar,serif,bar foo")); } void tst_QTextDocumentFragment::defaultFont() { QFont f; f.setFamily("Courier New"); f.setBold(true); f.setItalic(true); f.setStrikeOut(true); // set here but deliberately ignored for the html export f.setPointSize(100); doc->setDefaultFont(f); doc->setPlainText("Hello World"); const QString html = doc->toHtml(); QLatin1String str(""); QVERIFY(html.contains(str)); } void tst_QTextDocumentFragment::html_spanBackgroundColor() { setHtml("Foo"); QVERIFY(doc->begin().begin().fragment().charFormat().background().color() == QColor(Qt::blue)); } void tst_QTextDocumentFragment::html_brokenTitle_data() { QTest::addColumn("html"); QTest::addColumn("expectedBody"); QTest::addColumn("expectedTitle"); QTest::newRow("brokentitle") << QString("Foo<b>bar</b>Blah") << QString("Blah") << QString("Foo"); QTest::newRow("brokentitle2") << QString("Foo<font color=red>i</font>t<font color=red>i</font>BlubBlah") << QString("Blah") << QString("Foo"); QTest::newRow("entities") << QString("Foo<barBlah") << QString("Blah") << QString("FooFoo</head><body>Blah</body></html>") << QString("Blah") << QString("Foo"); } void tst_QTextDocumentFragment::html_brokenTitle() { QFETCH(QString, html); QFETCH(QString, expectedBody); QFETCH(QString, expectedTitle); doc->setHtml(html); QCOMPARE(doc->toPlainText(), expectedBody); QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), expectedTitle); } void tst_QTextDocumentFragment::html_blockVsInline() { { setHtml("<html><body><div><b>Foo<div>Bar"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<html><body><p><b>Foo<p>Bar"); QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold); } { setHtml("<html><body><b><center>Foo</center></b>"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<html><body><b><p>Foo"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<html><body><b><p>Foo<p>Bar"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<div><b>Foo<div>Bar"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<p><b>Foo<p>Bar"); QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold); } { setHtml("<b><center>Foo</center></b>"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<b><p>Foo"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } { setHtml("<b><p>Foo<p>Bar"); QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold); QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold); } } void tst_QTextDocumentFragment::html_tbody() { setHtml("<table><thead><tr><td>First Cell</td></tr></thead><tbody><tr><td>Second Cell</td></tr></tbody></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 2); QCOMPARE(table->format().headerRowCount(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell")); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell")); } void tst_QTextDocumentFragment::html_nestedTables() { setHtml("<table>" " <tr><td>" "" " <table>" " <tr><td>Hello</td></tr>" " </table>" "" " <table>" " <tr><td>World</td></tr>" " </table>" "" " </td></tr>" "</table>" ); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 1); cursor = table->cellAt(0, 0).firstCursorPosition(); cursor.movePosition(QTextCursor::NextBlock); QTextTable *firstNestedTable = cursor.currentTable(); QVERIFY(firstNestedTable); QVERIFY(firstNestedTable->parentFrame() == table); QCOMPARE(firstNestedTable->rows(), 1); QCOMPARE(firstNestedTable->columns(), 1); QCOMPARE(firstNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hello")); while (cursor.currentTable() == firstNestedTable && cursor.movePosition(QTextCursor::NextBlock)) ; QVERIFY(!cursor.isNull()); QVERIFY(cursor.currentTable() == table); cursor.movePosition(QTextCursor::NextBlock); QTextTable *secondNestedTable = cursor.currentTable(); QVERIFY(secondNestedTable); QVERIFY(secondNestedTable->parentFrame() == table); QCOMPARE(secondNestedTable->rows(), 1); QCOMPARE(secondNestedTable->columns(), 1); QCOMPARE(secondNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("World")); } void tst_QTextDocumentFragment::html_rowSpans() { setHtml("" "<table border=\"1\" width=\"100%\">" " <tr>" " <td rowspan=\"2\">blah</td>" " <td rowspan=\"2\">foo</td>" " </tr>" " <tr></tr>" " <tr>" " <td rowspan=\"2\">blubb</td>" " <td rowspan=\"2\">baz</td>" " </tr>" " <tr></tr>" "</table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 4); QCOMPARE(table->columns(), 2); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("blah")); QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("foo")); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("blah")); QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("foo")); QCOMPARE(table->cellAt(2, 0).firstCursorPosition().block().text(), QString("blubb")); QCOMPARE(table->cellAt(2, 1).firstCursorPosition().block().text(), QString("baz")); QCOMPARE(table->cellAt(3, 0).firstCursorPosition().block().text(), QString("blubb")); QCOMPARE(table->cellAt(3, 1).firstCursorPosition().block().text(), QString("baz")); } void tst_QTextDocumentFragment::html_rowSpans2() { setHtml("" "<html><body>" "<table border=\"1\">" "<tr>" "<td>Row 1 col 1</td>" "</tr>" "<tr>" "<td rowspan=\"3\">Row 2 col 1, rowspan 3</td>" "<td>Row 2 col 2</td>" "</tr>" "<tr>" "<td rowspan=\"2\">Row 3 col 2, rowspan 2</td>" "</tr>" "<tr>" "</tr>" "</table>" "</body></html>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 4); QCOMPARE(table->columns(), 2); QCOMPARE(table->cellAt(0, 1).rowSpan(), 1); QCOMPARE(table->cellAt(1, 0).rowSpan(), 3); QCOMPARE(table->cellAt(2, 1).rowSpan(), 2); } void tst_QTextDocumentFragment::html_implicitParagraphs() { setHtml("<p>foo</p>bar"); QCOMPARE(doc->blockCount(), 2); } void tst_QTextDocumentFragment::html_missingCloseTag() { setHtml("<font color=\"red\"><span style=\"color:blue\">blue</span></span> red</font>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); cursor.movePosition(QTextCursor::NextWord); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground().color() == Qt::red); } void tst_QTextDocumentFragment::html_anchorColor() { setHtml("<span style=\"color: red;\">Red</span>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground().color() == Qt::red); setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\">Blue</a></span>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground().color() == QApplication::palette().link().color()); setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\" style=\"color: yellow;\">Green</a></span>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().foreground().color() == Qt::yellow); } void tst_QTextDocumentFragment::html_lastParagraphClosing() { setHtml("<p>Foo<b>Bar</b>Baz"); QCOMPARE(doc->blockCount(), 1); } void tst_QTextDocumentFragment::html_tableHeaderBodyFootParent() { // don't get confused by strange tags, keep tbody/thead/tfoot children of // the table tag setHtml("<table><col><col><col><tbody><tr><td>Hey</td></tr></tbody></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey")); setHtml("<table><col><col><col><thead><tr><td>Hey</td></tr></thead></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey")); setHtml("<table><col><col><col><tfoot><tr><td>Hey</td></tr></tfoot></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey")); } void tst_QTextDocumentFragment::html_columnWidths() { setHtml("<table>" " <tr>" " <td colspan=\"2\">Foo</td>" " </tr>" " <tr>" " <td>Bar</td>" " <td width=\"1%\">Baz</td>" " </tr>" "</table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 2); QCOMPARE(table->rows(), 2); QTextTableFormat fmt = table->format(); const QVector<QTextLength> columnWidths = fmt.columnWidthConstraints(); QCOMPARE(columnWidths.count(), 2); QVERIFY(columnWidths.at(0).type() == QTextLength::VariableLength); QVERIFY(columnWidths.at(1).type() == QTextLength::PercentageLength); QVERIFY(columnWidths.at(1).rawValue() == 1); } void tst_QTextDocumentFragment::css_fontWeight() { setHtml("<p style=\"font-weight:bold\">blah</p>"); QVERIFY(doc->begin().charFormat().fontWeight() == QFont::Bold); setHtml("<p style=\"font-weight:600\">blah</p>"); QVERIFY(doc->begin().charFormat().fontWeight() == QFont::Bold); } void tst_QTextDocumentFragment::css_float() { setHtml("<img src=\"foo\" style=\"float: right\">"); QTextCharFormat fmt = doc->begin().begin().fragment().charFormat(); QVERIFY(fmt.isImageFormat()); QTextObject *o = doc->objectForFormat(fmt); QVERIFY(o); QTextFormat f = o->format(); QVERIFY(f.isFrameFormat()); QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatRight); setHtml("<img src=\"foo\" align=right>"); fmt = doc->begin().begin().fragment().charFormat(); QVERIFY(fmt.isImageFormat()); o = doc->objectForFormat(fmt); QVERIFY(o); f = o->format(); QVERIFY(f.isFrameFormat()); QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatRight); setHtml("<img src=\"foo\" align=left>"); fmt = doc->begin().begin().fragment().charFormat(); QVERIFY(fmt.isImageFormat()); o = doc->objectForFormat(fmt); QVERIFY(o); f = o->format(); QVERIFY(f.isFrameFormat()); QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatLeft); } void tst_QTextDocumentFragment::css_textIndent() { setHtml("<p style=\"text-indent: 42px\">foo</p>"); QTextBlockFormat fmt = doc->begin().blockFormat(); QCOMPARE(fmt.textIndent(), qreal(42)); } void tst_QTextDocumentFragment::css_inline() { setHtml("" "<style>" " p { background-color: green;}" "</style>" "<p>test</p>" ); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::css_external() { doc->addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("p { background-color: green; }")); doc->setHtml("" "<link href=\"test.css\" type=\"text/css\" />" "<p>test</p>" ); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::css_import() { doc->addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("@import \"other.css\";")); doc->addResource(QTextDocument::StyleSheetResource, QUrl("other.css"), QString("@import url(\"other2.css\");")); doc->addResource(QTextDocument::StyleSheetResource, QUrl("other2.css"), QString("p { background-color: green; }")); doc->setHtml("" "<link href=\"test.css\" type=\"text/css\" />" "<p>test</p>" ); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); doc->setHtml("" "<style>@import \"test.css\" screen;</style>" "<p>test</p>" ); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::css_selectors_data() { QTest::addColumn<bool>("match"); QTest::addColumn<QString>("selector"); QTest::addColumn<QString>("attributes"); QTest::newRow("plain") << true << QString() << QString(); QTest::newRow("class") << true << QString(".foo") << QString("class=foo"); QTest::newRow("notclass") << false << QString(".foo") << QString("class=bar"); QTest::newRow("attrset") << true << QString("[justset]") << QString("justset"); QTest::newRow("notattrset") << false << QString("[justset]") << QString("otherattribute"); QTest::newRow("attrmatch") << true << QString("[foo=bar]") << QString("foo=bar"); QTest::newRow("noattrmatch") << false << QString("[foo=bar]") << QString("foo=xyz"); QTest::newRow("contains") << true << QString("[foo~=bar]") << QString("foo=\"baz bleh bar\""); QTest::newRow("notcontains") << false << QString("[foo~=bar]") << QString("foo=\"test\""); QTest::newRow("beingswith") << true << QString("[foo|=bar]") << QString("foo=\"bar-bleh\""); QTest::newRow("notbeingswith") << false << QString("[foo|=bar]") << QString("foo=\"bleh-bar\""); QTest::newRow("attr2") << true << QString("[bar=foo]") << QString("bleh=bar bar=foo"); } void tst_QTextDocumentFragment::css_selectors() { QFETCH(bool, match); QFETCH(QString, selector); QFETCH(QString, attributes); QString html = QString("" "<style>" " p { background-color: green }" " p%1 { background-color: red }" "</style>" "<p %2>test</p>" ).arg(selector).arg(attributes); setHtml(html); QTextBlockFormat fmt = doc->begin().blockFormat(); if (match) QVERIFY(fmt.background().color() == QColor("red")); else QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::css_nodeNameCaseInsensitivity() { setHtml("<style>" "P { background-color: green }" "</style>" "<p>test</p>"); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::css_textUnderlineStyle_data() { QTest::addColumn<QString>("styleName"); QTest::addColumn<int>("expectedStyle"); QTest::newRow("none") << QString("none") << int(QTextCharFormat::NoUnderline); QTest::newRow("solid") << QString("solid") << int(QTextCharFormat::SingleUnderline); QTest::newRow("dash") << QString("dashed") << int(QTextCharFormat::DashUnderline); QTest::newRow("dot") << QString("dotted") << int(QTextCharFormat::DotLine); QTest::newRow("dashdot") << QString("dot-dash") << int(QTextCharFormat::DashDotLine); QTest::newRow("dashdotdot") << QString("dot-dot-dash") << int(QTextCharFormat::DashDotDotLine); QTest::newRow("wave") << QString("wave") << int(QTextCharFormat::WaveUnderline); } void tst_QTextDocumentFragment::css_textUnderlineStyle() { QFETCH(QString, styleName); QFETCH(int, expectedStyle); QString html = QString::fromLatin1("<span style=\"text-underline-style: %1\">Blah</span>").arg(styleName); doc->setHtml(html); QTextFragment fragment = doc->begin().begin().fragment(); QVERIFY(fragment.isValid()); QCOMPARE(int(fragment.charFormat().underlineStyle()), expectedStyle); } void tst_QTextDocumentFragment::css_textUnderlineStyleAndDecoration() { doc->setHtml("<span style=\"text-decoration: overline; text-underline-style: solid\">Test</span>"); QTextFragment fragment = doc->begin().begin().fragment(); QVERIFY(fragment.isValid()); QVERIFY(fragment.charFormat().underlineStyle() == QTextCharFormat::SingleUnderline); QVERIFY(fragment.charFormat().fontOverline()); doc->setHtml("<span style=\"text-underline-style: solid; text-decoration: overline\">Test</span>"); fragment = doc->begin().begin().fragment(); QVERIFY(fragment.isValid()); QVERIFY(fragment.charFormat().underlineStyle() == QTextCharFormat::SingleUnderline); QVERIFY(fragment.charFormat().fontOverline()); } void tst_QTextDocumentFragment::css_listStyleType() { doc->setHtml("<ol style=\"list-style-type: disc\"><li>Blah</li></ol>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDisc); doc->setHtml("<ul style=\"list-style-type: square\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListSquare); doc->setHtml("<ul style=\"list-style-type: circle\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle); doc->setHtml("<ul style=\"list-style-type: decimal\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDecimal); doc->setHtml("<ul style=\"list-style-type: lower-alpha\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListLowerAlpha); doc->setHtml("<ul style=\"list-style-type: upper-alpha\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListUpperAlpha); doc->setHtml("<ul style=\"list-style-type: upper-roman\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListUpperRoman); doc->setHtml("<ul style=\"list-style-type: lower-roman\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListLowerRoman); // ignore the unsupported list-style-position inside the list-style shorthand property doc->setHtml("<ul style=\"list-style: outside decimal\"><li>Blah</li></ul>"); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDecimal); } void tst_QTextDocumentFragment::css_linkPseudo() { doc->setHtml("<a href=\"foobar\">Blah</a>"); QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline()); doc->setHtml("<style>a { text-decoration: none; }</style><a href=\"foobar\">Blah</a>"); QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline()); doc->setHtml("<style>a:link { text-decoration: none; }</style><a href=\"foobar\">Blah</a>"); QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline()); } void tst_QTextDocumentFragment::css_pageBreaks() { doc->setHtml("<p>Foo</p>"); QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_Auto); doc->setHtml("<p style=\" page-break-before:always;\">Foo</p>"); QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_AlwaysBefore); doc->setHtml("<p style=\" page-break-after:always;\">Foo</p>"); QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_AlwaysAfter); doc->setHtml("<p style=\" page-break-before:always; page-break-after:always;\">Foo</p>"); QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == (QTextFormat::PageBreak_AlwaysAfter | QTextFormat::PageBreak_AlwaysBefore)); } void tst_QTextDocumentFragment::universalSelectors_data() { QTest::addColumn<bool>("match"); QTest::addColumn<QString>("selector"); QTest::addColumn<QString>("attributes"); QTest::newRow("1") << true << QString("*") << QString(); QTest::newRow("2") << false << QString() << QString(); // invalid totally empty selector QTest::newRow("3") << false << QString("*[foo=bar]") << QString("foo=bleh"); QTest::newRow("4") << true << QString("*[foo=bar]") << QString("foo=bar"); QTest::newRow("5") << false << QString("[foo=bar]") << QString("foo=bleh"); QTest::newRow("6") << true << QString("[foo=bar]") << QString("foo=bar"); QTest::newRow("7") << true << QString(".charfmt1") << QString("class=charfmt1"); } void tst_QTextDocumentFragment::universalSelectors() { QFETCH(bool, match); QFETCH(QString, selector); QFETCH(QString, attributes); QString html = QString("" "<style>" "%1 { background-color: green }" "</style>" "<p %2>test</p>" ).arg(selector).arg(attributes); setHtml(html); QTextBlockFormat fmt = doc->begin().blockFormat(); if (match) QVERIFY(fmt.background().color() == QColor("green")); else QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush)); } void tst_QTextDocumentFragment::screenMedia() { setHtml("<style>" "@media screen {" "p { background-color: green }" "}" "</style>" "<p>test</p>" ""); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); setHtml("<style>" "@media foobar {" "p { background-color: green }" "}" "</style>" "<p>test</p>" ""); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() != QColor("green")); setHtml("<style>" "@media sCrEeN {" "p { background-color: green }" "}" "</style>" "<p>test</p>" ""); fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::htmlResourceLoading() { const QString html("<link href=\"test.css\" type=\"text/css\" />" "<p>test</p>"); QTextDocument tmp; tmp.addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("p { background-color: green; }")); QTextDocumentFragment frag = QTextDocumentFragment::fromHtml(html, &tmp); doc->clear(); QTextCursor(doc).insertFragment(frag); QTextBlockFormat fmt = doc->begin().blockFormat(); QVERIFY(fmt.background().color() == QColor("green")); } void tst_QTextDocumentFragment::someCaseInsensitiveAttributeValues() { const char html1[] = "<ul type=sQUarE><li>Blah</li></ul>"; setHtml(QString::fromLatin1(html1)); cursor.movePosition(QTextCursor::End); QVERIFY(cursor.currentList()); QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListSquare); const char html2[] = "<div align=ceNTeR><b>Hello World"; setHtml(html2); QCOMPARE(doc->begin().blockFormat().alignment(), Qt::AlignHCenter); const char html3[] = "<p dir=rTL><b>Hello World"; setHtml(html3); QCOMPARE(doc->begin().blockFormat().layoutDirection(), Qt::RightToLeft); } class TestDocument : public QTextDocument { public: inline TestDocument() {} QPixmap testPixmap; virtual QVariant loadResource(int type, const QUrl &name) { if (name.toString() == QLatin1String("testPixmap")) { return testPixmap; } return QTextDocument::loadResource(type, name); } }; void tst_QTextDocumentFragment::backgroundImage() { TestDocument doc; doc.testPixmap = QPixmap(100, 100); doc.testPixmap.fill(Qt::blue); doc.setHtml("<p style=\"background-image: url(testPixmap)\">Hello</p>"); QBrush bg = doc.begin().blockFormat().background(); QVERIFY(bg.style() == Qt::TexturePattern); QVERIFY(bg.texture().serialNumber() == doc.testPixmap.serialNumber()); } void tst_QTextDocumentFragment::dontMergePreAndNonPre() { doc->setHtml("<pre>Pre text</pre>Text that should be wrapped"); QCOMPARE(doc->blockCount(), 2); QCOMPARE(doc->begin().text(), QString("Pre text")); QCOMPARE(doc->begin().next().text(), QString("Text that should be wrapped")); } void tst_QTextDocumentFragment::leftMarginInsideHtml() { doc->setHtml("<html><dl><dd>Blah"); QCOMPARE(doc->blockCount(), 1); QVERIFY(doc->begin().blockFormat().leftMargin() > 0); } void tst_QTextDocumentFragment::html_margins() { doc->setHtml("<p style=\"margin-left: 42px\">Test"); QCOMPARE(doc->blockCount(), 1); QCOMPARE(doc->begin().blockFormat().topMargin(), 12.); QCOMPARE(doc->begin().blockFormat().bottomMargin(), 12.); QCOMPARE(doc->begin().blockFormat().leftMargin(), 42.); } void tst_QTextDocumentFragment::newlineInsidePreShouldBecomeNewParagraph() { // rationale: we used to map newlines inside <pre> to QChar::LineSeparator, but // if you display a lot of text inside pre it all ended up inside one single paragraph, // which doesn't scale very well with our text engine. Paragraphs spanning thousands of // lines are not a common use-case otherwise. doc->setHtml("<pre>Foo\nBar</pre>"); QCOMPARE(doc->blockCount(), 2); QTextBlock block = doc->begin(); QCOMPARE(block.blockFormat().topMargin(), qreal(12)); QVERIFY(qIsNull(block.blockFormat().bottomMargin())); block = block.next(); QVERIFY(qIsNull(block.blockFormat().topMargin())); QCOMPARE(block.blockFormat().bottomMargin(), qreal(12)); doc->setHtml("<pre style=\"margin-top: 32px; margin-bottom: 45px; margin-left: 50px\">Foo\nBar</pre>"); QCOMPARE(doc->blockCount(), 2); block = doc->begin(); QCOMPARE(block.blockFormat().topMargin(), qreal(32)); QVERIFY(qIsNull(block.blockFormat().bottomMargin())); QCOMPARE(block.blockFormat().leftMargin(), qreal(50)); block = block.next(); QVERIFY(qIsNull(block.blockFormat().topMargin())); QCOMPARE(block.blockFormat().bottomMargin(), qreal(45)); QCOMPARE(block.blockFormat().leftMargin(), qreal(50)); } void tst_QTextDocumentFragment::invalidColspan() { doc->setHtml("<table><tr rowspan=-1><td colspan=-1>Blah</td></tr></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->columns(), 1); QCOMPARE(table->rows(), 1); } void tst_QTextDocumentFragment::html_brokenTableWithJustTr() { doc->setHtml("<tr><td>First Cell</td><tr><td>Second Cell"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 2); QCOMPARE(table->columns(), 1); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell")); QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell")); doc->setHtml("" "<col width=286 style='mso-width-source:userset;mso-width-alt:10459;width:215pt'>" "<col width=64 span=3 style='width:48pt'>" "<tr height=17 style='height:12.75pt'>" "<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>" "<td width=64 style='width:48pt'>1b</td>" "<td width=64 style='width:48pt'>1c</td>" "<td width=64 style='width:48pt'>1d</td>" "</tr>" "<tr height=17 style='height:12.75pt'>" "<td height=17 style='height:12.75pt'>|2a</td>" "<td>2b</td>" "<td>2c</td>" "<td>2d</td>" "</tr>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 2); QCOMPARE(table->columns(), 4); } void tst_QTextDocumentFragment::html_brokenTableWithJustTd() { doc->setHtml("<td>First Cell</td><td>Second Cell"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 2); QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell")); QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second Cell")); doc->setHtml("<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>" "<td width=64 style='width:48pt'>1b</td>" "<td width=64 style='width:48pt'>1c</td>" "<td width=64 style='width:48pt'>1d</td>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 4); } void tst_QTextDocumentFragment::html_preNewlineHandling_data() { QTest::addColumn<QString>("html"); QTest::addColumn<QString>("expectedPlainText"); QTest::newRow("pre1") << QString("Foo<pre>Bar") << QString("Foo\nBar"); QTest::newRow("pre2") << QString("Foo<pre>\nBar") << QString("Foo\nBar"); QTest::newRow("pre2") << QString("Foo<pre>\n\nBar") << QString("Foo\n\nBar"); QTest::newRow("pre4") << QString("<html>Foo<pre>\nBar") << QString("Foo\nBar"); QTest::newRow("pre5") << QString("<pre>Foo\n</pre>\nBar") << QString("Foo\nBar"); QTest::newRow("pre6") << QString("<pre>Foo<b>Bar</b>Blah\n</pre>\nMooh") << QString("FooBarBlah\nMooh"); QTest::newRow("pre7") << QString("<pre>\nPara1\n</pre>\n<pre>\nPara2\n</pre>") << QString("Para1\nPara2"); } void tst_QTextDocumentFragment::html_preNewlineHandling() { QFETCH(QString, html); doc->setHtml(html); QTEST(doc->toPlainText(), "expectedPlainText"); } void tst_QTextDocumentFragment::html_br() { doc->setHtml("Foo<br><br><br>Blah"); QCOMPARE(doc->toPlainText(), QString("Foo\n\n\nBlah")); } void tst_QTextDocumentFragment::html_dl() { doc->setHtml("<dl><dt>term<dd>data</dl>Text afterwards"); QCOMPARE(doc->toPlainText(), QString("term\ndata\nText afterwards")); } void tst_QTextDocumentFragment::html_tableStrangeNewline() { doc->setHtml("<table><tr><td>Foo</td></tr>\n</table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 1); const QTextTableCell cell = table->cellAt(0, 0); QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo")); QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block()); } void tst_QTextDocumentFragment::html_tableStrangeNewline2() { doc->setHtml("<table><tr><td>Foo</td></tr><tr>\n<td/></tr></table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 2); QCOMPARE(table->columns(), 1); const QTextTableCell cell = table->cellAt(0, 0); QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo")); QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block()); } void tst_QTextDocumentFragment::html_tableStrangeNewline3() { doc->setHtml("<table border>" "<tr>" "<td>" "<ul>" "<li>Meh</li>" "</ul>" "</td>" "<td>\n" "<ul>" "<li>Foo</li>" "</ul>" "</td>" "</tr>" "</table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 2); QTextTableCell cell = table->cellAt(0, 0); QCOMPARE(cell.firstCursorPosition().block().text(), QString("Meh")); QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block()); cell = table->cellAt(0, 1); QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo")); QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block()); } void tst_QTextDocumentFragment::html_caption() { doc->setHtml("<table border align=center>" "<caption>This <b> is a</b> Caption!</caption>" "<tr><td>Blah</td></tr>" "</table>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QCOMPARE(cursor.block().text(), QString("This is a Caption!")); QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter); cursor.movePosition(QTextCursor::NextBlock); QTextTable *table = cursor.currentTable(); QVERIFY(table); QCOMPARE(table->rows(), 1); QCOMPARE(table->columns(), 1); QTextTableCell cell = table->cellAt(0, 0); QCOMPARE(cell.firstCursorPosition().block().text(), QString("Blah")); } static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = { 0x20ac, // 0x80 0x0081, // 0x81 direct mapping 0x201a, // 0x82 0x0192, // 0x83 0x201e, // 0x84 0x2026, // 0x85 0x2020, // 0x86 0x2021, // 0x87 0x02C6, // 0x88 0x2030, // 0x89 0x0160, // 0x8A 0x2039, // 0x8B 0x0152, // 0x8C 0x008D, // 0x8D direct mapping 0x017D, // 0x8E 0x008F, // 0x8F directmapping 0x0090, // 0x90 directmapping 0x2018, // 0x91 0x2019, // 0x92 0x201C, // 0x93 0X201D, // 0x94 0x2022, // 0x95 0x2013, // 0x96 0x2014, // 0x97 0x02DC, // 0x98 0x2122, // 0x99 0x0161, // 0x9A 0x203A, // 0x9B 0x0153, // 0x9C 0x009D, // 0x9D direct mapping 0x017E, // 0x9E 0x0178 // 0x9F }; void tst_QTextDocumentFragment::html_windowsEntities() { for (uint i = 0; i < sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]); ++i) { QString html = QString::number(i + 0x80); html.prepend("<p>&#"); html.append(";"); doc->setHtml(html); QCOMPARE(doc->toPlainText(), QString(QChar(windowsLatin1ExtendedCharacters[i]))); } } void tst_QTextDocumentFragment::html_eatenText() { doc->setHtml("<h1>Test1</h1>\nTest2<h1>Test3</h1>"); cursor.movePosition(QTextCursor::Start); QCOMPARE(cursor.block().text(), QString("Test1")); cursor.movePosition(QTextCursor::NextBlock); QCOMPARE(cursor.block().text(), QString("Test2")); cursor.movePosition(QTextCursor::NextBlock); QCOMPARE(cursor.block().text(), QString("Test3")); } void tst_QTextDocumentFragment::html_hr() { doc->setHtml("<hr />"); QCOMPARE(doc->blockCount(), 1); QVERIFY(doc->begin().blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)); } void tst_QTextDocumentFragment::html_hrMargins() { doc->setHtml("<p>Test<hr/>Blah"); QCOMPARE(doc->blockCount(), 3); cursor.movePosition(QTextCursor::Start); QTextBlock block = cursor.block(); QCOMPARE(block.text(), QString("Test")); QVERIFY(block.blockFormat().bottomMargin() <= qreal(12.)); QTextBlock first = block; cursor.movePosition(QTextCursor::NextBlock); block = cursor.block(); QTextBlock hr = block; QVERIFY(qMax(first.blockFormat().bottomMargin(), block.blockFormat().topMargin()) > 0); cursor.movePosition(QTextCursor::NextBlock); block = cursor.block(); QCOMPARE(block.text(), QString("Blah")); } void tst_QTextDocumentFragment::html_blockQuoteMargins() { doc->setHtml("<blockquote>Bar</blockquote>"); QCOMPARE(doc->blockCount(), 1); cursor.movePosition(QTextCursor::Start); QTextBlock block = cursor.block(); QCOMPARE(block.text(), QString("Bar")); QCOMPARE(block.blockFormat().leftMargin(), qreal(40.)); QCOMPARE(block.blockFormat().rightMargin(), qreal(40.)); QCOMPARE(block.blockFormat().topMargin(), qreal(12.)); QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.)); } void tst_QTextDocumentFragment::html_definitionListMargins() { doc->setHtml("Foo<dl><dt>tag<dd>data</dl>Bar"); QCOMPARE(doc->blockCount(), 4); cursor.movePosition(QTextCursor::Start); QTextBlock block = cursor.block(); QCOMPARE(block.text(), QString("Foo")); block = block.next(); QCOMPARE(block.text(), QString("tag")); QCOMPARE(block.blockFormat().topMargin(), qreal(8.)); block = block.next(); QCOMPARE(block.text(), QString("data")); QCOMPARE(block.blockFormat().bottomMargin(), qreal(8.)); block = block.next(); QCOMPARE(block.text(), QString("Bar")); } void tst_QTextDocumentFragment::html_listMargins() { doc->setHtml("Foo<ol><li>First<li>Second</ol>Bar"); QCOMPARE(doc->blockCount(), 4); cursor.movePosition(QTextCursor::Start); QTextBlock block = cursor.block(); QCOMPARE(block.text(), QString("Foo")); block = block.next(); QCOMPARE(block.text(), QString("First")); QCOMPARE(block.blockFormat().topMargin(), qreal(12.)); block = block.next(); QCOMPARE(block.text(), QString("Second")); QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.)); block = block.next(); QCOMPARE(block.text(), QString("Bar")); } void tst_QTextDocumentFragment::html_titleAttribute() { doc->setHtml("<span title=\"this is my title\">Test</span>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QCOMPARE(cursor.charFormat().toolTip(), QString("this is my title")); } void tst_QTextDocumentFragment::html_compressDivs() { doc->setHtml("<p/><div/><div/><div/><div/>Test"); QCOMPARE(doc->blockCount(), 1); QCOMPARE(doc->begin().text(), QString("Test")); } void tst_QTextDocumentFragment::completeToPlainText() { doc->setPlainText("Hello\nWorld"); QCOMPARE(doc->toPlainText(), QString("Hello\nWorld")); QTextDocumentFragment fragment(doc); QCOMPARE(fragment.toPlainText(), QString("Hello\nWorld")); } void tst_QTextDocumentFragment::copyContents() { doc->setPlainText("Hello"); QFont f; doc->setDefaultFont(f); QTextFragment fragment = doc->begin().begin().fragment(); QCOMPARE(fragment.text(), QString("Hello")); QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize()); QTextDocumentFragment frag(doc); doc->clear(); f.setPointSize(48); doc->setDefaultFont(f); QTextCursor(doc).insertFragment(QTextDocumentFragment::fromHtml(frag.toHtml())); fragment = doc->begin().begin().fragment(); QCOMPARE(fragment.text(), QString("Hello")); QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize()); } void tst_QTextDocumentFragment::html_textAfterHr() { doc->setHtml("<hr><nobr><b>After the centered text</b></nobr>"); QCOMPARE(doc->blockCount(), 2); QTextBlock block = doc->begin(); QVERIFY(block.text().isEmpty()); QVERIFY(block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)); block = block.next(); QString txt("After the centered text"); txt.replace(QLatin1Char(' '), QChar::Nbsp); QCOMPARE(block.text(), txt); QVERIFY(!block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)); } void tst_QTextDocumentFragment::blockTagClosing() { doc->setHtml("<p>foo<p>bar<span>baz</span>"); QCOMPARE(doc->blockCount(), 2); QTextBlock block = doc->begin(); QCOMPARE(block.text(), QString("foo")); block = block.next(); QCOMPARE(block.text(), QString("barbaz")); } void tst_QTextDocumentFragment::isEmpty() { QTextDocumentFragment frag; QVERIFY(frag.isEmpty()); frag = QTextDocumentFragment::fromHtml("test"); QVERIFY(!frag.isEmpty()); frag = QTextDocumentFragment::fromHtml("<hr />"); QVERIFY(!frag.isEmpty()); } void tst_QTextDocumentFragment::html_alignmentInheritance() { doc->setHtml("<center>Centered text<hr></center><b>After the centered text</b>"); QCOMPARE(doc->blockCount(), 3); QTextBlock block = doc->begin(); QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter); block = block.next(); QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter); block = block.next(); QVERIFY(!(block.blockFormat().alignment() & Qt::AlignHCenter)); } void tst_QTextDocumentFragment::html_ignoreEmptyDivs() { doc->setHtml("<p><div/><b>Foo</b>"); QCOMPARE(doc->blockCount(), 1); QCOMPARE(doc->begin().text(), QString("Foo")); } void tst_QTextDocumentFragment::html_dontInheritAlignmentForFloatingImages() { doc->setHtml("<p align=right><img align=unknownignored src=\"foo\" /></p>"); QTextCharFormat fmt = doc->begin().begin().fragment().charFormat(); QVERIFY(fmt.isImageFormat()); QTextObject *o = doc->objectForFormat(fmt); QVERIFY(o); QTextFormat f = o->format(); QVERIFY(f.isFrameFormat()); QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::InFlow); } void tst_QTextDocumentFragment::html_verticalImageAlignment() { doc->setHtml("<img src=\"foo\"/>"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); QTextImageFormat fmt = cursor.charFormat().toImageFormat(); QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignNormal); doc->setHtml("<img src=\"foo\" align=middle />"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignMiddle); doc->setHtml("<img src=\"foo\" style=\"vertical-align: middle\" />"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignMiddle); doc->setHtml("<img src=\"foo\" align=top />"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignTop); doc->setHtml("<img src=\"foo\" style=\"vertical-align: top\" />"); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignTop); } void tst_QTextDocumentFragment::html_verticalCellAlignment() { const char *alt[] = { // vertical-align property "<table>" "<tr>" "<td style=\"vertical-align: middle\"></td>" "<td style=\"vertical-align: top\"></td>" "<td style=\"vertical-align: bottom\"></td>" "</tr>" "</table>", // valign property "<table>" "<tr>" "<td valign=\"middle\"></td>" "<td valign=\"top\"></td>" "<td valign=\"bottom\"></td>" "</tr>" "</table>", // test td override of tr property "<table>" "<tr valign=\"bottom\">" "<td valign=\"middle\"></td>" "<td valign=\"top\"></td>" "<td></td>" "</tr>" "</table>" }; const int numTestCases = sizeof(alt) / sizeof(*alt); for (int i = 0; i < numTestCases; ++i) { doc->setHtml(alt[i]); QTextTable *table = qobject_cast<QTextTable *>(doc->rootFrame()->childFrames().at(0)); QVERIFY(table); QCOMPARE(table->cellAt(0, 0).format().verticalAlignment(), QTextCharFormat::AlignMiddle); QCOMPARE(table->cellAt(0, 1).format().verticalAlignment(), QTextCharFormat::AlignTop); QCOMPARE(table->cellAt(0, 2).format().verticalAlignment(), QTextCharFormat::AlignBottom); } } void tst_QTextDocumentFragment::html_borderColor() { const char html[] = "<table border=1 style=\"border-color:#0000ff;\"><tr><td>Foo</td></tr></table>"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Outset); QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(QColor("#0000ff"))); } void tst_QTextDocumentFragment::html_borderStyle() { const char html[] = "<table border=1 style=\"border-style:solid;\"><tr><td>Foo</td></tr></table>"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Solid); QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(Qt::darkGray)); } void tst_QTextDocumentFragment::html_borderWidth() { const char *html[2] = { "<table style=\"border-width:2;\"><tr><td>Foo</td></tr></table>", "<table style=\"border-width:2px;\"><tr><td>Foo</td></tr></table>" }; for (int i = 0; i < 2; ++i) { cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html[i]))); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextBlock); QVERIFY(cursor.currentTable()); QCOMPARE(cursor.currentTable()->format().border(), qreal(2)); } } void tst_QTextDocumentFragment::html_userState() { const char html[] = "<p style=\"-qt-user-state:42;\">A</p><p style=\"-qt-user-state:0;\">B</p><p>C</p>"; cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); QTextBlock block = doc->begin(); QCOMPARE(block.userState(), 42); QCOMPARE(block.next().userState(), 0); QCOMPARE(block.next().next().userState(), -1); } void tst_QTextDocumentFragment::html_rootFrameProperties() { const char html[] = "<table border=1 style=\"-qt-table-type:root; margin-top:10px;\"><tr><td>Foo</tr></td>"; doc->setHtml(html); QCOMPARE(doc->rootFrame()->childFrames().size(), 0); QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); QCOMPARE(fmt.topMargin(), qreal(10)); QCOMPARE(fmt.bottomMargin(), qreal(0)); QCOMPARE(fmt.leftMargin(), qreal(0)); QCOMPARE(fmt.rightMargin(), qreal(0)); QCOMPARE(fmt.border(), qreal(1)); QString normalFrameHtml = QLatin1String(html); normalFrameHtml.replace(QLatin1String("root"), QLatin1String("frame")); doc->setHtml(normalFrameHtml); QCOMPARE(doc->rootFrame()->childFrames().size(), 1); } void tst_QTextDocumentFragment::html_appendList() { appendHtml("<p>foo</p>"); appendHtml("<ul><li>Line 1</li><li>Line 2</li></ul>"); QCOMPARE(doc->blockCount(), 3); QVERIFY(doc->begin().next().textList() != 0); } void tst_QTextDocumentFragment::html_appendList2() { appendHtml("1"); appendHtml("<ul><li><img src=\"/foo/bar\" /></li></ul>"); QCOMPARE(doc->blockCount(), 2); QVERIFY(doc->begin().next().textList() != 0); } void tst_QTextDocumentFragment::html_alignmentPropertySet() { const char html[] = "<p>Test</p>"; setHtml(QString::fromLatin1(html)); QVERIFY(!doc->begin().blockFormat().hasProperty(QTextFormat::BlockAlignment)); } void tst_QTextDocumentFragment::html_qt3RichtextWhitespaceMode() { setHtml(QString::fromLatin1("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><p> line with whitespace</p><p> another line with whitespace</p></body></html>")); QCOMPARE(doc->blockCount(), 2); QTextBlock block = doc->begin(); QVERIFY(block.text().startsWith(" ")); block = block.next(); QVERIFY(block.text().startsWith(" ")); } void tst_QTextDocumentFragment::html_brAfterHr() { setHtml(QString::fromLatin1("Text A<br><hr><br>Text B<hr>")); QCOMPARE(doc->blockCount(), 4); QTextBlock block = doc->begin(); QCOMPARE(block.text(), QString("Text A") + QChar(QChar::LineSeparator)); block = block.next(); QVERIFY(block.text().isEmpty()); block = block.next(); QCOMPARE(block.text(), QChar(QChar::LineSeparator) + QString("Text B")); block = block.next(); QVERIFY(block.text().isEmpty()); } void tst_QTextDocumentFragment::html_unclosedHead() { doc->setHtml(QString::fromLatin1("<html><head><title>TestBlah")); QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), QString::fromLatin1("Test")); QCOMPARE(doc->toPlainText(), QString::fromLatin1("Blah")); } // duplicated from qtexthtmlparser.cpp #define MAX_ENTITY 258 static const struct { const char *name; quint16 code; } entities[MAX_ENTITY]= { { "AElig", 0x00c6 }, { "Aacute", 0x00c1 }, { "Acirc", 0x00c2 }, { "Agrave", 0x00c0 }, { "Alpha", 0x0391 }, { "AMP", 38 }, { "Aring", 0x00c5 }, { "Atilde", 0x00c3 }, { "Auml", 0x00c4 }, { "Beta", 0x0392 }, { "Ccedil", 0x00c7 }, { "Chi", 0x03a7 }, { "Dagger", 0x2021 }, { "Delta", 0x0394 }, { "ETH", 0x00d0 }, { "Eacute", 0x00c9 }, { "Ecirc", 0x00ca }, { "Egrave", 0x00c8 }, { "Epsilon", 0x0395 }, { "Eta", 0x0397 }, { "Euml", 0x00cb }, { "Gamma", 0x0393 }, { "GT", 62 }, { "Iacute", 0x00cd }, { "Icirc", 0x00ce }, { "Igrave", 0x00cc }, { "Iota", 0x0399 }, { "Iuml", 0x00cf }, { "Kappa", 0x039a }, { "Lambda", 0x039b }, { "LT", 60 }, { "Mu", 0x039c }, { "Ntilde", 0x00d1 }, { "Nu", 0x039d }, { "OElig", 0x0152 }, { "Oacute", 0x00d3 }, { "Ocirc", 0x00d4 }, { "Ograve", 0x00d2 }, { "Omega", 0x03a9 }, { "Omicron", 0x039f }, { "Oslash", 0x00d8 }, { "Otilde", 0x00d5 }, { "Ouml", 0x00d6 }, { "Phi", 0x03a6 }, { "Pi", 0x03a0 }, { "Prime", 0x2033 }, { "Psi", 0x03a8 }, { "QUOT", 34 }, { "Rho", 0x03a1 }, { "Scaron", 0x0160 }, { "Sigma", 0x03a3 }, { "THORN", 0x00de }, { "Tau", 0x03a4 }, { "Theta", 0x0398 }, { "Uacute", 0x00da }, { "Ucirc", 0x00db }, { "Ugrave", 0x00d9 }, { "Upsilon", 0x03a5 }, { "Uuml", 0x00dc }, { "Xi", 0x039e }, { "Yacute", 0x00dd }, { "Yuml", 0x0178 }, { "Zeta", 0x0396 }, { "aacute", 0x00e1 }, { "acirc", 0x00e2 }, { "acute", 0x00b4 }, { "aelig", 0x00e6 }, { "agrave", 0x00e0 }, { "alefsym", 0x2135 }, { "alpha", 0x03b1 }, { "amp", 38 }, { "and", 0x22a5 }, { "ang", 0x2220 }, { "apos", 0x0027 }, { "aring", 0x00e5 }, { "asymp", 0x2248 }, { "atilde", 0x00e3 }, { "auml", 0x00e4 }, { "bdquo", 0x201e }, { "beta", 0x03b2 }, { "brvbar", 0x00a6 }, { "bull", 0x2022 }, { "cap", 0x2229 }, { "ccedil", 0x00e7 }, { "cedil", 0x00b8 }, { "cent", 0x00a2 }, { "chi", 0x03c7 }, { "circ", 0x02c6 }, { "clubs", 0x2663 }, { "cong", 0x2245 }, { "copy", 0x00a9 }, { "crarr", 0x21b5 }, { "cup", 0x222a }, { "curren", 0x00a4 }, { "dArr", 0x21d3 }, { "dagger", 0x2020 }, { "darr", 0x2193 }, { "deg", 0x00b0 }, { "delta", 0x03b4 }, { "diams", 0x2666 }, { "divide", 0x00f7 }, { "eacute", 0x00e9 }, { "ecirc", 0x00ea }, { "egrave", 0x00e8 }, { "empty", 0x2205 }, { "emsp", 0x2003 }, { "ensp", 0x2002 }, { "epsilon", 0x03b5 }, { "equiv", 0x2261 }, { "eta", 0x03b7 }, { "eth", 0x00f0 }, { "euml", 0x00eb }, { "euro", 0x20ac }, { "exist", 0x2203 }, { "fnof", 0x0192 }, { "forall", 0x2200 }, { "frac12", 0x00bd }, { "frac14", 0x00bc }, { "frac34", 0x00be }, { "frasl", 0x2044 }, { "gamma", 0x03b3 }, { "ge", 0x2265 }, { "gt", 62 }, { "hArr", 0x21d4 }, { "harr", 0x2194 }, { "hearts", 0x2665 }, { "hellip", 0x2026 }, { "iacute", 0x00ed }, { "icirc", 0x00ee }, { "iexcl", 0x00a1 }, { "igrave", 0x00ec }, { "image", 0x2111 }, { "infin", 0x221e }, { "int", 0x222b }, { "iota", 0x03b9 }, { "iquest", 0x00bf }, { "isin", 0x2208 }, { "iuml", 0x00ef }, { "kappa", 0x03ba }, { "lArr", 0x21d0 }, { "lambda", 0x03bb }, { "lang", 0x2329 }, { "laquo", 0x00ab }, { "larr", 0x2190 }, { "lceil", 0x2308 }, { "ldquo", 0x201c }, { "le", 0x2264 }, { "lfloor", 0x230a }, { "lowast", 0x2217 }, { "loz", 0x25ca }, { "lrm", 0x200e }, { "lsaquo", 0x2039 }, { "lsquo", 0x2018 }, { "lt", 60 }, { "macr", 0x00af }, { "mdash", 0x2014 }, { "micro", 0x00b5 }, { "middot", 0x00b7 }, { "minus", 0x2212 }, { "mu", 0x03bc }, { "nabla", 0x2207 }, { "nbsp", 0x00a0 }, { "ndash", 0x2013 }, { "ne", 0x2260 }, { "ni", 0x220b }, { "not", 0x00ac }, { "notin", 0x2209 }, { "nsub", 0x2284 }, { "ntilde", 0x00f1 }, { "nu", 0x03bd }, { "oacute", 0x00f3 }, { "ocirc", 0x00f4 }, { "oelig", 0x0153 }, { "ograve", 0x00f2 }, { "oline", 0x203e }, { "omega", 0x03c9 }, { "omicron", 0x03bf }, { "oplus", 0x2295 }, { "or", 0x22a6 }, { "ordf", 0x00aa }, { "ordm", 0x00ba }, { "oslash", 0x00f8 }, { "otilde", 0x00f5 }, { "otimes", 0x2297 }, { "ouml", 0x00f6 }, { "para", 0x00b6 }, { "part", 0x2202 }, { "percnt", 0x0025 }, { "permil", 0x2030 }, { "perp", 0x22a5 }, { "phi", 0x03c6 }, { "pi", 0x03c0 }, { "piv", 0x03d6 }, { "plusmn", 0x00b1 }, { "pound", 0x00a3 }, { "prime", 0x2032 }, { "prod", 0x220f }, { "prop", 0x221d }, { "psi", 0x03c8 }, { "quot", 34 }, { "rArr", 0x21d2 }, { "radic", 0x221a }, { "rang", 0x232a }, { "raquo", 0x00bb }, { "rarr", 0x2192 }, { "rceil", 0x2309 }, { "rdquo", 0x201d }, { "real", 0x211c }, { "reg", 0x00ae }, { "rfloor", 0x230b }, { "rho", 0x03c1 }, { "rlm", 0x200f }, { "rsaquo", 0x203a }, { "rsquo", 0x2019 }, { "sbquo", 0x201a }, { "scaron", 0x0161 }, { "sdot", 0x22c5 }, { "sect", 0x00a7 }, { "shy", 0x00ad }, { "sigma", 0x03c3 }, { "sigmaf", 0x03c2 }, { "sim", 0x223c }, { "spades", 0x2660 }, { "sub", 0x2282 }, { "sube", 0x2286 }, { "sum", 0x2211 }, { "sup1", 0x00b9 }, { "sup2", 0x00b2 }, { "sup3", 0x00b3 }, { "sup", 0x2283 }, { "supe", 0x2287 }, { "szlig", 0x00df }, { "tau", 0x03c4 }, { "there4", 0x2234 }, { "theta", 0x03b8 }, { "thetasym", 0x03d1 }, { "thinsp", 0x2009 }, { "thorn", 0x00fe }, { "tilde", 0x02dc }, { "times", 0x00d7 }, { "trade", 0x2122 }, { "uArr", 0x21d1 }, { "uacute", 0x00fa }, { "uarr", 0x2191 }, { "ucirc", 0x00fb }, { "ugrave", 0x00f9 }, { "uml", 0x00a8 }, { "upsih", 0x03d2 }, { "upsilon", 0x03c5 }, { "uuml", 0x00fc }, { "weierp", 0x2118 }, { "xi", 0x03be }, { "yacute", 0x00fd }, { "yen", 0x00a5 }, { "yuml", 0x00ff }, { "zeta", 0x03b6 }, { "zwj", 0x200d }, { "zwnj", 0x200c } }; void tst_QTextDocumentFragment::html_entities_data() { QTest::addColumn("html"); QTest::addColumn("code"); for (int i = 0; i < MAX_ENTITY; ++i) { QTest::newRow(entities[i].name) << QString("
                &") + QString::fromLatin1(entities[i].name) + QString(";
                ") << entities[i].code; } } void tst_QTextDocumentFragment::html_entities() { QFETCH(QString, html); QFETCH(quint16, code); setHtml(html); QCOMPARE(doc->blockCount(), 1); QString txt = doc->begin().text(); QCOMPARE(txt.length(), 1); QCOMPARE(txt.at(0).unicode(), code); } void tst_QTextDocumentFragment::html_ignore_script() { doc->setHtml(QString::fromLatin1("Blah")); QCOMPARE(doc->toPlainText(), QString("Blah")); } void tst_QTextDocumentFragment::html_directionWithHtml() { doc->setHtml(QString::fromLatin1("

                Test

                RTL

                LTR")); QCOMPARE(doc->blockCount(), 3); QTextBlock block = doc->firstBlock(); QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); QVERIFY(block.blockFormat().layoutDirection() == Qt::LeftToRight); // HTML default block = block.next(); QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); QVERIFY(block.blockFormat().layoutDirection() == Qt::RightToLeft); block = block.next(); QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); QVERIFY(block.blockFormat().layoutDirection() == Qt::LeftToRight); } void tst_QTextDocumentFragment::html_directionWithRichText() { doc->setHtml(QString::fromLatin1("

                Test

                RTL

                LTR")); QCOMPARE(doc->blockCount(), 3); QTextBlock block = doc->firstBlock(); QVERIFY(!block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); block = block.next(); QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); QVERIFY(block.blockFormat().layoutDirection() == Qt::RightToLeft); block = block.next(); QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection)); QVERIFY(block.blockFormat().layoutDirection() == Qt::LeftToRight); } void tst_QTextDocumentFragment::html_metaInBody() { setHtml("HelloWorld"); QCOMPARE(doc->toPlainText(), QString("HelloWorld")); } void tst_QTextDocumentFragment::html_importImageWithoutAspectRatio() { doc->setHtml(""); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); QTextImageFormat fmt = cursor.charFormat().toImageFormat(); // qDebug() << fmt.width() << fmt.height(); QVERIFY (fmt.hasProperty(QTextFormat::ImageWidth)); QCOMPARE (fmt.height(), 43.); doc->setHtml(""); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY (! fmt.hasProperty(QTextFormat::ImageWidth)); QCOMPARE (fmt.height(), 43.); doc->setHtml(""); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::NextCharacter); QVERIFY(cursor.charFormat().isImageFormat()); fmt = cursor.charFormat().toImageFormat(); QVERIFY (! fmt.hasProperty(QTextFormat::ImageHeight)); QCOMPARE (fmt.width(), 200.); } void tst_QTextDocumentFragment::html_fromFirefox() { // if you have a html loaded in firefox like Test\nText then selecting all and copying will // result in the following text on the clipboard (for text/html) doc->setHtml(QString::fromLatin1("Test\nText\n\n")); QCOMPARE(doc->toPlainText(), QString::fromLatin1("Test Text ")); } QTEST_MAIN(tst_QTextDocumentFragment) #include "tst_qtextdocumentfragment.moc"