diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tests/auto/qtextdocumentfragment | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tests/auto/qtextdocumentfragment')
3 files changed, 4031 insertions, 0 deletions
diff --git a/tests/auto/qtextdocumentfragment/.gitignore b/tests/auto/qtextdocumentfragment/.gitignore new file mode 100644 index 0000000..5c56983 --- /dev/null +++ b/tests/auto/qtextdocumentfragment/.gitignore @@ -0,0 +1 @@ +tst_qtextdocumentfragment diff --git a/tests/auto/qtextdocumentfragment/qtextdocumentfragment.pro b/tests/auto/qtextdocumentfragment/qtextdocumentfragment.pro new file mode 100644 index 0000000..5df1937 --- /dev/null +++ b/tests/auto/qtextdocumentfragment/qtextdocumentfragment.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_qtextdocumentfragment.cpp + + + diff --git a/tests/auto/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/qtextdocumentfragment/tst_qtextdocumentfragment.cpp new file mode 100644 index 0000000..bace7b4 --- /dev/null +++ b/tests/auto/qtextdocumentfragment/tst_qtextdocumentfragment.cpp @@ -0,0 +1,4025 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + + +#include <qtextdocument.h> +#include <qtextdocumentfragment.h> +#include <qtexttable.h> +#include <qtextlist.h> +#include <qdebug.h> +#include <private/qtextdocument_p.h> + + +#include <qtextcursor.h> + +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 <private/qtextdocument_p.h> +#include <qdebug.h> +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[] = "" +"<table border>" +" <tr>" +" <td>First Cell" +" <td>Second Cell" +" </tr>" +" <tr>" +" <td colspan=\"2\">Third Cell" +" </tr>" +" <tr>" +" <td>Fourth Cell" +" <td>Fifth Cell" +" </tr>" +"</table>"; + 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[] = "" +"<table border=\"0\">" +" <tr>" +" <td colspan=\"4\" width=\"400\">First Cell</td>" +" </tr>" +"</table>"; + 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("<table><tr><td>Hey</td><td>Blah")); + QVERIFY(!fragment.isEmpty()); +} + +void tst_QTextDocumentFragment::tableImport2() +{ + { + const char html[] = "" + "<table>" + "<tr><td>First Cell</td><td>Second Cell</td></tr>" + "<tr><td>Third Cell</td><td>Fourth Cell</td></tr>" + "</table>"; + + 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[] = "" + "<table>" + "<tr><td>First Cell</td><td>Second Cell</td></tr>" + "<tr><td>Third Cell</td><td>" + " <table>" + " <tr><td>First Nested Cell</td><td>Second Nested Cell</td></tr>" + " <tr><td>Third Nested Cell</td><td>Fourth Nested Cell</td></tr>" + " <tr><td>Fifth Nested Cell</td><td>Sixth Nested Cell</td></tr>" + " </table></td></tr>" + "</table>"; + + 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[] = "" + "<table>" + "<tr><td>First Cell<td>Second Cell" + "<tr><td>Third Cell<td>Fourth Cell" + "</table>"; + + 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[] = "" + "<table>" + "<tr><th>First Cell<th>Second Cell" + "<tr><td>Third Cell<td>Fourth Cell" + "</table>"; + + 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[] = "<table><tr><td><p></p></td><td></td></tr></table>"; + QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(QString::fromLatin1(html)); + QVERIFY(!fragment.isEmpty()); +} + +void tst_QTextDocumentFragment::tableImport4() +{ + const char html[] = "<table>" + "<tr><td>blah</td></tr>" + "<tr><td>blah</td><td>blah</td></tr>" + "</table>"; + 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[] = "<table>" + "<tr>" + " <td>Foo</td>" + " <td>Bar</td>" + " <td>Bleh</td>" + " <td>Blub</td>" + "</tr>" + "<tr>" + " <td>Ahh</td>" + " <td colspan=5>Gah</td>" + "</tr>" + "</table>"; + + 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("<html><head><title>Test</title></head><body>Blah</body></html>")); + QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), QString::fromLatin1("Test")); +} + +void tst_QTextDocumentFragment::html_listIndents1() +{ + const char html[] = "<ul><li>Hey</li><li>Hah</li></ul>"; + 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[] = "<ul><li>Hey<p>Hah</ul>"; + 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[] = "<ul><li><p>Hah</ul>"; + 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[] = "<ul><li>Foo</ul><p>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[] = "<ul><li>Foo<p><li>Bar</li></ul>"; + 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[] = "<ul><li>Outer List<div class=\"testclass\"><ul><li>Nested Item 1</li></ul></div></li></ul>"; + 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[] = "<p style=\"font-style:italic\"><span style=\"font-style:normal\">Test</span></p>"; + 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[] = "<p>Test</p>"; + 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[] = "<table><tr><td>cell one<td>cell two</tr><tr><td>cell three<td>cell four</tr></table>"; + 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[] = "<pre>Test\r\n</pre>Bar"; + setHtml(QString::fromLatin1(html)); + QVERIFY(!doc->toPlainText().contains('\r')); + QCOMPARE(doc->toPlainText(), QString("Test\nBar")); +} + +void tst_QTextDocumentFragment::unorderedListEnumeration() +{ + const char html[] = "<ul><ul><ul><li>Blah</li></ul></ul>"; + setHtml(QString::fromLatin1(html)); + cursor.movePosition(QTextCursor::End); + QVERIFY(cursor.currentList()); + QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle); + + const char html2[] = "<ul><ul><ul type=disc><li>Blah</li></ul></ul>"; + 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[] = "<body><table><tr><td><td><p></table><p></body>"; + setHtml(QString::fromLatin1(html)); + QVERIFY(!doc->isEmpty()); +} + +void tst_QTextDocumentFragment::ignoreStyleTags() +{ + const char html[] = "<body><style>Blah</style>Hello</body>"; + setHtml(QString::fromLatin1(html)); + QCOMPARE(doc->toPlainText(), QString("Hello")); +} + +void tst_QTextDocumentFragment::hrefAnchor() +{ + { + const char html[] = "<a href=\"test\">blah</a>"; + 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[] = "<a>blah</a>"; + 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[] = "a<a name=\"test\" />blah"; + 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[] = "<p> <a name=\"foo\"> 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("<a name=\"target\" /><a name=\"target2\"/><span>Text</span>"); + + 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[] = "<table align=center><tr><td>Hey</td></tr></table>"; + 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[] = "<table><tr><td>Hey</td></tr></table>"; + 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[] = "<table><tr><td><p>Hey</p></td></tr></table>"; + 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[] = "<table></table>"; + setHtml(QString::fromLatin1(html)); + QVERIFY(true); // don't crash with a failing assertion +} + +void tst_QTextDocumentFragment::emptyTable2() +{ + const char html[] = "<table></td></tr></table><p>blah</p>"; + setHtml(QString::fromLatin1(html)); + QVERIFY(true); // don't crash with a failing assertion +} + +void tst_QTextDocumentFragment::emptyTable3() +{ + const char html[] = "<table><tr><td><table></table></td><td>Foobar</td></tr></table>"; + 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[] = "<table><tr><td>Blah</td></tr></tr><tr><td>Hm</td></tr></table>"; + 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[] = "<img />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[] = "<body align=right><p>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[] = "<body align=right><p align=left>Blah<img></img><p>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<QTextLength> 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[] = "<span style='text-decoration: overline line-through underline'>Blah</span>"; + 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[] = "<ahref=\"argl\">Link</a>"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); + QVERIFY(true); + } + + { + const char html[] = "<a href=\"\"a<"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); + QVERIFY(true); + } +} + +void tst_QTextDocumentFragment::html_blockIndent() +{ + const char html[] = "<p style=\"-qt-block-indent:3;\">Test</p>"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); + QCOMPARE(cursor.blockFormat().indent(), 3); +} + +void tst_QTextDocumentFragment::html_listIndent() +{ + const char html[] = "<ul style=\"-qt-list-indent:4;\"><li>Blah</ul>"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); + QVERIFY(cursor.currentList()); + QCOMPARE(cursor.currentList()->format().indent(), 4); +} + +void tst_QTextDocumentFragment::html_whitespace_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<QString>("expectedPlainText"); + + QTest::newRow("1") << QString("<span>This is some test</span><span> with spaces between words</span>") + << QString("This is some test with spaces between words"); + + QTest::newRow("2") << QString("<span> </span><span>nowhitespacehereplease</span>") + << QString::fromLatin1("nowhitespacehereplease"); + + QTest::newRow("3") << QString("<span style=\"white-space: pre;\"> white space here </span>") + << QString::fromLatin1(" white space here "); + + QTest::newRow("4") << QString("<span style=\"white-space: pre-wrap;\"> white space here </span>") + << QString::fromLatin1(" white space here "); + + QTest::newRow("5") << QString("<a href=\"One.html\">One</a> <a href=\"Two.html\">Two</a> <b>Three</b>\n" + "<b>Four</b>") + << QString::fromLatin1("One Two Three Four"); + + QTest::newRow("6") << QString("<p>Testing: <b><i><u>BoldItalic</u></i></b> <i>Italic</i></p>") + << QString("Testing: BoldItalic Italic"); + + QTest::newRow("7") << QString("<table><tr><td>Blah</td></tr></table> <table border><tr><td>Foo</td></tr></table>") + << QString("\nBlah\n\nFoo\n"); + + QTest::newRow("8") << QString("<table><tr><td><i>Blah</i></td></tr></table> <i>Blub</i>") + << QString("\nBlah\nBlub"); + + QTest::newRow("task116492") << QString("<p>a<font=\"Times\"> b </font>c</p>") + << QString("a b c"); + + QTest::newRow("task121653") << QString("abc<b> def</b>") + << QString("abc def"); + + QTest::newRow("task122650") << QString("<p>Foo</p> Bar") + << QString("Foo\nBar"); + + QTest::newRow("task122650-2") << QString("<p>Foo</p> <p> Bar") + << QString("Foo \nBar"); + + QTest::newRow("task122650-3") << QString("<html>Before<pre>\nTest</pre>") + << QString("Before\nTest"); + + QTest::newRow("br-with-whitespace") << QString("Foo<br>\nBlah") + << QString("Foo\nBlah"); + + QTest::newRow("collapse-p-with-newline") << QString("Foo<p>\n<p>\n<p>\n<p>\n<p>\n<p>\nBar") + << QString("Foo\nBar"); + + QTest::newRow("table") << QString("<table><tr><td>Blah</td></tr></table>\nTest") + << QString("\nBlah\nTest"); + + QTest::newRow("table2") << QString("<table><tr><td><pre>\nTest\n</pre></td>\n </tr></table>") + << QString("\nTest\n"); + + QTest::newRow("table3") << QString("<table><tr><td><pre>\nTest\n</pre> \n \n </td></tr></table>") + << 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("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>") + + text + + QString("</body></html>"); + + 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("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>" + "blah blah<!--StartFragment--><span>") + + text + + QString("</span><!--EndFragment--></body></html>"); + + 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("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body><span>") + + text + + QString("</span></body></html>"); + + cursor.insertFragment(QTextDocumentFragment::fromHtml(html)); + + QCOMPARE(doc->toPlainText(), text); +} + +void tst_QTextDocumentFragment::html_listStart1() +{ + // don't create a block for the <ul> element, even if there's some whitespace between + // it and the <li> + const char html[] = "<ul> <li>list item</li><ul>"; + 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[] = "<ul>buggy, but text should appear<li>list item</li><ul>"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(QByteArray::fromRawData(html, sizeof(html) / sizeof(html[0])))); + + QCOMPARE(doc->blockCount(), 2); +} + +void tst_QTextDocumentFragment::html_cssMargin() +{ + const char html[] = "<p style=\"margin-top: 1px; margin-bottom: 2px; margin-left: 3px; margin-right: 4px\">Test</p>"; + 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[] = "<table><tr><th>This should be centered</th></tr></table>"; + 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[] = "<html><body>" + "<ol><li>elem 1</li></ol>" + "<ol><li>elem 1</li></ol>" + "</body></html>"; + + 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[] = "<ul><li>Foo</ul>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[] = "<sub>Subby</sub>"; + const char superHtml[] = "<sup>Super</sup>"; + const char subHtmlCss[] = "<span style=\"vertical-align: sub\">Subby</span>"; + const char superHtmlCss[] = "<span style=\"vertical-align: super\">Super</span>"; + const char alignmentInherited[] = "<sub><font face=\"Verdana\">Subby</font></sub>"; + + 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[] = "<span style=\"color:red\"><span style=\"color:blue\">Blue</span></span>"; + setHtml(color); + QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); + + const char rgbColor[] = "<span style=\"color:red\"><span style=\"color:rgb(0, 0, 255)\">Blue</span></span>"; + setHtml(rgbColor); + QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); +} + +void tst_QTextDocumentFragment::obeyFragmentMarkersInImport() +{ + const char html[] = "This leading text should not appear<!--StartFragment--><span>Text</span><!--EndFragment-->This 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<!--StartFragment-->%1<!--EndFragment-->This 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[] = "<p style=\"-qt-paragraph-type:empty;\"> </p><p>Two paragraphs</p>"; + 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[] = "<p style=\"margin-left:80px\"></p><p>One paragraph</p>"; + setHtml(html); + + QCOMPARE(doc->blockCount(), 1); + QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0)); + + const char html2[] = "<p style=\"margin-left:80px\"></p>One paragraph"; + setHtml(html2); + QCOMPARE(doc->blockCount(), 1); + QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0)); + + const char html3[] = "<p style=\"margin-left:80px\">Foo</p><p></p>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[] = "<ul><p>Foo</p><p></p></ul><h4>Bar</h4>"; + + setHtml(html); + + QCOMPARE(doc->blockCount(), 2); + + cursor = QTextCursor(doc); + QCOMPARE(cursor.block().next().blockFormat().indent(), 0); +} + +void tst_QTextDocumentFragment::html_emptyParagraphs4() +{ + const char html[] = "<p>foo</p><p style=\"page-break-before: always\"></p><p>bar</p>"; + 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[] = "<p>foo</p><p style=\"page-break-after: always\"></p><p>bar</p>"; + 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[] = "<font color=\"blue\"><p>Hah</p></font>"; + setHtml(html); + + QVERIFY(cursor.charFormat().foreground().color() == Qt::blue); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue); +} + +void tst_QTextDocumentFragment::html_fontSize() +{ + const char html[] = "<font size=\"2\">Hah</font>"; + setHtml(html); + + QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), -1); +} + +void tst_QTextDocumentFragment::html_fontSizeAdjustment() +{ + const char html[] = "<font size=\"7\"><b>Hah</b></font>"; + 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[] = "<span style=\"font-size: 50pt\">Foo</span>"; + setHtml(html); + + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50); + + const char html2[] = "<span style=\"font-size: 50px\">Foo</span>"; + setHtml(html2); + + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50); + + const char html3[] = "<span style=\"font-size: large\">Foo</span>"; + setHtml(html3); + + QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), 1); +} + +void tst_QTextDocumentFragment::html_cssShorthandFont() +{ + { + const char html[] = "<span style=\"font: 50px sans-serif\">Foo</span>"; + setHtml(html); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif")); + } + { + const char html[] = "<span style=\"font: 50pt sans-serif\">Foo</span>"; + setHtml(html); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif")); + } + { + const char html[] = "<span style='font:7.0pt \"Times New Roman\"'>Foo</span>"; + setHtml(html); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("Times New Roman")); + } + { + const char html[] = "<span style='font:bold 7.0pt'>Foo</span>"; + setHtml(html); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontWeight).toInt(), int(QFont::Bold)); + QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7); + } + { + const char html[] = "<span style='font:bold italic 7.0pt'>Foo</span>"; + 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[] = "<body bgcolor=\"blue\">Foo</body>"; + doc->setHtml(html); + + QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue); +} + +void tst_QTextDocumentFragment::html_qtBgColor() +{ + const char html[] = "<qt bgcolor=\"blue\">Foo</qt>"; + doc->setHtml(html); + + QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue); +} + +void tst_QTextDocumentFragment::html_bodyBackground() +{ + const char html[] = "<body background=\"foo.png\">Foo</body>"; + doc->setHtml(html); + + QVERIFY(doc->rootFrame()->frameFormat().background().style() == Qt::TexturePattern); +} + +void tst_QTextDocumentFragment::html_tableCellBackground() +{ + const char html[] = "<body><table><tr><td background=\"foo.png\">Foo</td></tr></table></body>"; + 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[] = "<body style=\"background-image:url('foo.png')\">Foo</body>"; + doc->setHtml(html); + + QVERIFY(doc->rootFrame()->frameFormat().background().style() == Qt::TexturePattern); +} + +void tst_QTextDocumentFragment::css_tableCellBackground() +{ + const char html[] = "<body><table><tr><td style=\"background-image:url('foo.png')\">Foo</td></tr></table></body>"; + 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[] = "<body><table><tr><td style=\"padding-left:1\">Foo</td>" + "<td style=\"padding-right:1\"></td><td style=\"padding-top:10\"></td>" + "<td style=\"padding-bottom:5\"></td><td style=\"padding:15\"></td></tr></table></body>"; + 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[] = "<div align=right><b>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[] = "<span style=\"color:black\">a<span style=\"color:red\">b<span style=\"color:black\">c</span></span>d</span>"; + 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[] = "<p><ul><li>Foo<ul><li>In nested list</li></ul></li><li>Last item</li></ul></p>"; + 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[] = "<html><body bgcolor=\"blue\"><p>Blah</p></body></html>"; + 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[] = "<span style=\"background: blue\">Foo<span>Bar</span></span>"; + 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[] = "<p style=\"background: blue\"><span>Foo</span></span>"; + 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("<html><body><p><nobr>") + input + QString::fromLatin1("</nobr></p></body></html>"); + 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[] = "<span style=\"font-size: 10pt\"><span style=\"font-size: 40pt\">Blah<img src=\"blah\"></img>Foo</span></span>"; + 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[] = "<html><body><p style=\"-qt-paragraph-type:empty;\"></p></body></html>"; + setHtml(html); + QCOMPARE(doc->blockCount(), 1); +} + +void tst_QTextDocumentFragment::html_closingTag() +{ + const char html[] = "<i />text"; + setHtml(html); + + QVERIFY(!cursor.charFormat().fontItalic()); +} + +void tst_QTextDocumentFragment::html_anchorAroundImage() +{ + const char html[] = "<a href=\"http://www.troll.no\"><img src=test.png></a>"; + 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[] = "<table border=1.2><tr><td>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<QTextFrame *> 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<QTextFrame *> 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[] = "<table style=\"margin-left: 100px;\"><tr><td><p style=\"margin-left:50px;\">Foo</p></td></tr></table>"; + cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html))); + + QList<QTextFrame *> 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[] = "<center>This should be centered</center>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[] = "<table><tr><td bgcolor=\"blue\">Test<p>Second Parag</p></td></tr></table>"; + 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[] = "<table><tr><td bgcolor=\"blue\"><table><tr><td>Blah</td></tr></table></td></tr></table>"; + 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[] = "" +"<table border>" +" <tr>" +" <td>First Cell</td>" +" </tr>" +" <tr>" +" <td>Second Cell</td>" +" <td>Third Cell</td>" +" </tr>" +"</table>"; + + 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("<html><body bgcolor=\"#00ff00\"><b>Hello World</b></body></html>"); + 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("<h1>foo</h1>bar"); + QCOMPARE(doc->blockCount(), 2); +} + +void tst_QTextDocumentFragment::html_quotedFontFamily() +{ + setHtml("<div style=\"font-family: 'Foo Bar';\">Test</div>"); + QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); + + setHtml("<div style='font-family: \"Foo Bar\";'>Test</div>"); + QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); +} + +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("<body style=\" font-family:'Courier New'; font-size:100pt; font-weight:600; font-style:italic;\">"); + QVERIFY(html.contains(str)); +} + +void tst_QTextDocumentFragment::html_spanBackgroundColor() +{ + setHtml("<span style=\"background-color: blue\">Foo</span>"); +#if QT_VERSION <= 0x040100 + QEXPECT_FAIL("", "Fixed in >= 4.1.1", Continue); +#endif + QVERIFY(doc->begin().begin().fragment().charFormat().background().color() == QColor(Qt::blue)); +} + +void tst_QTextDocumentFragment::html_brokenTitle_data() +{ + QTest::addColumn<QString>("html"); + QTest::addColumn<QString>("expectedBody"); + QTest::addColumn<QString>("expectedTitle"); + + QTest::newRow("brokentitle") << QString("<html><head><title>Foo<b>bar</b></title></head><body>Blah</body></html>") + << QString("Blah") << QString("Foo"); + QTest::newRow("brokentitle2") << QString("<html><head><title>Foo<font color=red>i</font>t<font color=red>i</font>Blub</title></head><body>Blah</body></html>") + << QString("Blah") << QString("Foo"); + QTest::newRow("entities") << QString("<html><head><title>Foo<bar</title></head><body>Blah</body></html>") + << QString("Blah") << QString("Foo<bar"); + QTest::newRow("unclosedtitle") << QString("<html><head><title>Foo</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() +{ +#if QT_VERSION <= 0x040100 + QEXPECT_FAIL("", "Fixed in >= 4.1.1", Abort); +#endif + { + 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); +#if QT_VERSION <= 0x040100 + QEXPECT_FAIL("", "Fixed in >= 4.1.1", Abort); +#endif + QCOMPARE(table->rows(), 2); + +#if QT_VERISON >= 0x040200 + QVERIFY(table->format().headerRowCount(), 1); +#endif + + 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() +{ +#if QT_VERSION > 0x040100 + 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")); +#endif +} + +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); + + // 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>Test</title><body>Blah</body></html>")); + 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<QString>("html"); + QTest::addColumn<quint16>("code"); + + for (int i = 0; i < MAX_ENTITY; ++i) { + QTest::newRow(entities[i].name) << QString("<pre>&") + QString::fromLatin1(entities[i].name) + QString(";</pre>") + << 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("<html><script>Test</script><body>Blah</body></html>")); + QCOMPARE(doc->toPlainText(), QString("Blah")); +} + +void tst_QTextDocumentFragment::html_directionWithHtml() +{ + doc->setHtml(QString::fromLatin1("<html><body><p>Test<p dir=rtl>RTL<p dir=ltr>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("<p>Test<p dir=rtl>RTL<p dir=ltr>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("<body>Hello<meta>World</body>"); + QCOMPARE(doc->toPlainText(), QString("HelloWorld")); +} + +void tst_QTextDocumentFragment::html_importImageWithoutAspectRatio() +{ + doc->setHtml("<img src=\"foo\" width=\"100%\" height=\"43\">"); + 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("<img src=\"foo\" height=\"43\">"); + 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("<img src=\"foo\" width=\"200\">"); + 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 <html>Test\nText</html> then selecting all and copying will + // result in the following text on the clipboard (for text/html) + doc->setHtml(QString::fromLatin1("<!--StartFragment-->Test\nText\n\n<!--EndFragment-->")); + QCOMPARE(doc->toPlainText(), QString::fromLatin1("Test Text ")); +} + +QTEST_MAIN(tst_QTextDocumentFragment) +#include "tst_qtextdocumentfragment.moc" |