summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/text
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/gui/text')
-rw-r--r--tests/auto/gui/text/qabstracttextdocumentlayout/.gitignore1
-rw-r--r--tests/auto/gui/text/qabstracttextdocumentlayout/qabstracttextdocumentlayout.pro9
-rw-r--r--tests/auto/gui/text/qabstracttextdocumentlayout/tst_qabstracttextdocumentlayout.cpp159
-rw-r--r--tests/auto/gui/text/qcssparser/.gitignore1
-rw-r--r--tests/auto/gui/text/qcssparser/qcssparser.pro17
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments/output4
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output12
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output4
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output3
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output5
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/simple/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/simple/output9
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input1
-rw-r--r--tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output3
-rw-r--r--tests/auto/gui/text/qcssparser/tst_qcssparser.cpp1718
-rw-r--r--tests/auto/gui/text/qfont/.gitignore1
-rw-r--r--tests/auto/gui/text/qfont/qfont.pro5
-rw-r--r--tests/auto/gui/text/qfont/tst_qfont.cpp629
-rw-r--r--tests/auto/gui/text/qfontdatabase/.gitignore1
-rw-r--r--tests/auto/gui/text/qfontdatabase/FreeMono.ttfbin0 -> 267400 bytes
-rw-r--r--tests/auto/gui/text/qfontdatabase/qfontdatabase.pro10
-rw-r--r--tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp294
-rw-r--r--tests/auto/gui/text/qfontmetrics/.gitignore1
-rw-r--r--tests/auto/gui/text/qfontmetrics/qfontmetrics.pro4
-rw-r--r--tests/auto/gui/text/qfontmetrics/testfont.qrc5
-rw-r--r--tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp323
-rw-r--r--tests/auto/gui/text/qfontmetrics/ucs4font.ttfbin0 -> 3076 bytes
-rw-r--r--tests/auto/gui/text/qglyphrun/qglyphrun.pro11
-rw-r--r--tests/auto/gui/text/qglyphrun/test.ttfbin0 -> 3712 bytes
-rw-r--r--tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp679
-rw-r--r--tests/auto/gui/text/qrawfont/qrawfont.pro16
-rw-r--r--tests/auto/gui/text/qrawfont/testfont.ttfbin0 -> 63212 bytes
-rw-r--r--tests/auto/gui/text/qrawfont/testfont_bold_italic.ttfbin0 -> 49760 bytes
-rw-r--r--tests/auto/gui/text/qrawfont/tst_qrawfont.cpp897
-rw-r--r--tests/auto/gui/text/qstatictext/qstatictext.pro6
-rw-r--r--tests/auto/gui/text/qstatictext/tst_qstatictext.cpp870
-rw-r--r--tests/auto/gui/text/qsyntaxhighlighter/.gitignore1
-rw-r--r--tests/auto/gui/text/qsyntaxhighlighter/qsyntaxhighlighter.pro4
-rw-r--r--tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp549
-rw-r--r--tests/auto/gui/text/qtextblock/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextblock/qtextblock.pro9
-rw-r--r--tests/auto/gui/text/qtextblock/tst_qtextblock.cpp197
-rw-r--r--tests/auto/gui/text/qtextcursor/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextcursor/qtextcursor.pro5
-rw-r--r--tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp1862
-rw-r--r--tests/auto/gui/text/qtextdocument/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextdocument/common.h93
-rw-r--r--tests/auto/gui/text/qtextdocument/qtextdocument.pro6
-rw-r--r--tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp2788
-rw-r--r--tests/auto/gui/text/qtextdocumentfragment/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextdocumentfragment/qtextdocumentfragment.pro8
-rw-r--r--tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp4030
-rw-r--r--tests/auto/gui/text/qtextdocumentlayout/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextdocumentlayout/qtextdocumentlayout.pro5
-rw-r--r--tests/auto/gui/text/qtextdocumentlayout/tst_qtextdocumentlayout.cpp276
-rw-r--r--tests/auto/gui/text/qtextformat/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextformat/qtextformat.pro9
-rw-r--r--tests/auto/gui/text/qtextformat/tst_qtextformat.cpp374
-rw-r--r--tests/auto/gui/text/qtextlayout/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextlayout/qtextlayout.pro12
-rw-r--r--tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp1506
-rw-r--r--tests/auto/gui/text/qtextlist/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextlist/qtextlist.pro9
-rw-r--r--tests/auto/gui/text/qtextlist/tst_qtextlist.cpp453
-rw-r--r--tests/auto/gui/text/qtextobject/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextobject/qtextobject.pro9
-rw-r--r--tests/auto/gui/text/qtextobject/tst_qtextobject.cpp128
-rw-r--r--tests/auto/gui/text/qtextodfwriter/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextodfwriter/qtextodfwriter.pro6
-rw-r--r--tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp426
-rw-r--r--tests/auto/gui/text/qtextpiecetable/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextpiecetable/qtextpiecetable.pro9
-rw-r--r--tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp1155
-rw-r--r--tests/auto/gui/text/qtextscriptengine/.gitignore1
-rw-r--r--tests/auto/gui/text/qtextscriptengine/generate/generate.pro14
-rw-r--r--tests/auto/gui/text/qtextscriptengine/generate/main.cpp129
-rw-r--r--tests/auto/gui/text/qtextscriptengine/qtextscriptengine.pro7
-rw-r--r--tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp1304
-rw-r--r--tests/auto/gui/text/qtexttable/.gitignore1
-rw-r--r--tests/auto/gui/text/qtexttable/qtexttable.pro6
-rw-r--r--tests/auto/gui/text/qtexttable/tst_qtexttable.cpp1004
-rw-r--r--tests/auto/gui/text/qzip/.gitignore1
-rw-r--r--tests/auto/gui/text/qzip/qzip.pro12
-rw-r--r--tests/auto/gui/text/qzip/testdata/symlink.zipbin0 -> 289 bytes
-rw-r--r--tests/auto/gui/text/qzip/testdata/test.zipbin0 -> 286 bytes
-rw-r--r--tests/auto/gui/text/qzip/tst_qzip.cpp167
-rw-r--r--tests/auto/gui/text/text.pro33
92 files changed, 22323 insertions, 0 deletions
diff --git a/tests/auto/gui/text/qabstracttextdocumentlayout/.gitignore b/tests/auto/gui/text/qabstracttextdocumentlayout/.gitignore
new file mode 100644
index 0000000000..d747466790
--- /dev/null
+++ b/tests/auto/gui/text/qabstracttextdocumentlayout/.gitignore
@@ -0,0 +1 @@
+tst_qabstracttextdocumentlayout
diff --git a/tests/auto/gui/text/qabstracttextdocumentlayout/qabstracttextdocumentlayout.pro b/tests/auto/gui/text/qabstracttextdocumentlayout/qabstracttextdocumentlayout.pro
new file mode 100644
index 0000000000..22b013e37e
--- /dev/null
+++ b/tests/auto/gui/text/qabstracttextdocumentlayout/qabstracttextdocumentlayout.pro
@@ -0,0 +1,9 @@
+############################################################
+# Project file for autotest for file qabstracttextdocumentlayout.h
+############################################################
+
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qabstracttextdocumentlayout.cpp
+
+
diff --git a/tests/auto/gui/text/qabstracttextdocumentlayout/tst_qabstracttextdocumentlayout.cpp b/tests/auto/gui/text/qabstracttextdocumentlayout/tst_qabstracttextdocumentlayout.cpp
new file mode 100644
index 0000000000..90f212e832
--- /dev/null
+++ b/tests/auto/gui/text/qabstracttextdocumentlayout/tst_qabstracttextdocumentlayout.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qimage.h>
+#include <qtextobject.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QAbstractTextDocumentLayout : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QAbstractTextDocumentLayout();
+ virtual ~tst_QAbstractTextDocumentLayout();
+
+private slots:
+ void getSetCheck();
+ void maximumBlockCount();
+};
+
+tst_QAbstractTextDocumentLayout::tst_QAbstractTextDocumentLayout()
+{
+}
+
+tst_QAbstractTextDocumentLayout::~tst_QAbstractTextDocumentLayout()
+{
+}
+
+class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout
+{
+ Q_OBJECT
+public:
+ MyAbstractTextDocumentLayout(QTextDocument *doc)
+ : QAbstractTextDocumentLayout(doc)
+ , gotFullLayout(false)
+ , blockCount(0)
+ , changeEvents(0)
+ {
+ }
+
+ void draw(QPainter *, const PaintContext &) {}
+ int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; }
+ int pageCount() const { return 0; }
+ QSizeF documentSize() const { return QSizeF(); }
+ QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
+ QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
+ void documentChanged(int from, int /* oldLength */, int length) {
+ ++changeEvents;
+
+ QTextBlock last = document()->lastBlock();
+ int lastPos = last.position() + last.length() - 1;
+ if (from == 0 && length == lastPos)
+ gotFullLayout = true;
+ }
+
+ bool gotFullLayout;
+ int blockCount;
+ int changeEvents;
+
+public slots:
+ void blockCountChanged(int bc) { blockCount = bc; }
+};
+
+// Testing get/set functions
+void tst_QAbstractTextDocumentLayout::getSetCheck()
+{
+ QTextDocument doc;
+ MyAbstractTextDocumentLayout obj1(&doc);
+ // QPaintDevice * QAbstractTextDocumentLayout::paintDevice()
+ // void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *)
+ QImage *var1 = new QImage(QSize(10,10), QImage::Format_ARGB32_Premultiplied);
+ obj1.setPaintDevice(var1);
+ QCOMPARE(static_cast<QPaintDevice *>(var1), obj1.paintDevice());
+ obj1.setPaintDevice((QPaintDevice *)0);
+ QCOMPARE(static_cast<QPaintDevice *>(0), obj1.paintDevice());
+ delete var1;
+}
+
+void tst_QAbstractTextDocumentLayout::maximumBlockCount()
+{
+ QTextDocument doc;
+ doc.setMaximumBlockCount(10);
+
+ MyAbstractTextDocumentLayout layout(&doc);
+ doc.setDocumentLayout(&layout);
+ QObject::connect(&doc, SIGNAL(blockCountChanged(int)), &layout, SLOT(blockCountChanged(int)));
+
+ QTextCursor cursor(&doc);
+ for (int i = 0; i < 10; ++i) {
+ cursor.insertBlock();
+ cursor.insertText("bla");
+ }
+
+ QCOMPARE(layout.blockCount, 10);
+
+ layout.gotFullLayout = false;
+ layout.changeEvents = 0;
+ cursor.insertBlock();
+ QCOMPARE(layout.changeEvents, 2);
+ cursor.insertText("foo");
+ QCOMPARE(layout.changeEvents, 3);
+ cursor.insertBlock();
+ QCOMPARE(layout.changeEvents, 5);
+ cursor.insertText("foo");
+ QCOMPARE(layout.changeEvents, 6);
+
+ QVERIFY(!layout.gotFullLayout);
+
+ QCOMPARE(layout.blockCount, 10);
+}
+
+QTEST_MAIN(tst_QAbstractTextDocumentLayout)
+#include "tst_qabstracttextdocumentlayout.moc"
diff --git a/tests/auto/gui/text/qcssparser/.gitignore b/tests/auto/gui/text/qcssparser/.gitignore
new file mode 100644
index 0000000000..6cc99800e0
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/.gitignore
@@ -0,0 +1 @@
+tst_qcssparser
diff --git a/tests/auto/gui/text/qcssparser/qcssparser.pro b/tests/auto/gui/text/qcssparser/qcssparser.pro
new file mode 100644
index 0000000000..16fa265231
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/qcssparser.pro
@@ -0,0 +1,17 @@
+load(qttest_p4)
+SOURCES += tst_qcssparser.cpp
+QT += xml gui-private
+
+requires(contains(QT_CONFIG,private_tests))
+!symbian: {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+wince*|symbian: {
+ addFiles.files = testdata
+ addFiles.path = .
+ timesFont.files = C:/Windows/Fonts/times.ttf
+ timesFont.path = .
+ DEPLOYMENT += addFiles timesFont
+}
+
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments/input b/tests/auto/gui/text/qcssparser/testdata/scanner/comments/input
new file mode 100644
index 0000000000..af2b659a5b
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments/input
@@ -0,0 +1 @@
+/* let's see if comments actually work *//*foo*/ "it /*should be preserved \"in strings*/ though"
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments/output b/tests/auto/gui/text/qcssparser/testdata/scanner/comments/output
new file mode 100644
index 0000000000..80ede0ba22
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments/output
@@ -0,0 +1,4 @@
+S|/* let's see if comments actually work */
+S|/*foo*/
+S|
+STRING|"it /*should be preserved "in strings*/ though"
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input b/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input
new file mode 100644
index 0000000000..3135acd78c
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input
@@ -0,0 +1 @@
+/*foo*/{/*foo*/+/*foo*/>/*foo*/,/*foo*/}/*foo*/-
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output b/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output
new file mode 100644
index 0000000000..d1f1259869
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output
@@ -0,0 +1,12 @@
+S|/*foo*/
+LBRACE|{
+S|/*foo*/
+PLUS|+
+S|/*foo*/
+GREATER|>
+S|/*foo*/
+COMMA|,
+S|/*foo*/
+RBRACE|}
+S|/*foo*/
+MINUS|-
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input b/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input
new file mode 100644
index 0000000000..8634543a8f
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input
@@ -0,0 +1 @@
+url(/*comment*/"www.kde.org")
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output b/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output
new file mode 100644
index 0000000000..af7bad752a
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output
@@ -0,0 +1,4 @@
+FUNCTION|url(
+S|/*comment*/
+STRING|"www.kde.org"
+RPAREN|)
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input b/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input
new file mode 100644
index 0000000000..62d039b00c
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input
@@ -0,0 +1 @@
+!/*hmm*/important
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output b/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output
new file mode 100644
index 0000000000..eb86e7bd15
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output
@@ -0,0 +1,3 @@
+EXCLAMATION_SYM|!
+S|/*hmm*/
+IDENT|important
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input b/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input
new file mode 100644
index 0000000000..deae3a8dab
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input
@@ -0,0 +1 @@
+background: 'test_bug.png';
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output b/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output
new file mode 100644
index 0000000000..ed52419594
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output
@@ -0,0 +1,5 @@
+IDENT|background
+COLON|:
+S|
+STRING|'test_bug.png'
+SEMICOLON|;
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/simple/input b/tests/auto/gui/text/qcssparser/testdata/scanner/simple/input
new file mode 100644
index 0000000000..b37e587661
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/simple/input
@@ -0,0 +1 @@
+p { display:block; }
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/simple/output b/tests/auto/gui/text/qcssparser/testdata/scanner/simple/output
new file mode 100644
index 0000000000..71c60ec9bf
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/simple/output
@@ -0,0 +1,9 @@
+IDENT|p
+LBRACE| {
+S|
+IDENT|display
+COLON|:
+IDENT|block
+SEMICOLON|;
+S|
+RBRACE|}
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input b/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input
new file mode 100644
index 0000000000..2c33f7be97
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input
@@ -0,0 +1 @@
+\41"\7E"\00006Df
diff --git a/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output b/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output
new file mode 100644
index 0000000000..0829c37e28
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output
@@ -0,0 +1,3 @@
+IDENT|A
+STRING|"~"
+IDENT|mf
diff --git a/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp
new file mode 100644
index 0000000000..1e82431a48
--- /dev/null
+++ b/tests/auto/gui/text/qcssparser/tst_qcssparser.cpp
@@ -0,0 +1,1718 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QtXml/QtXml>
+#if defined(Q_OS_WINCE)
+#include <QtGui/QFontDatabase>
+#endif
+
+//TESTED_CLASS=QCss
+//TESTED_FILES=gui/text/qcssparser.cpp gui/text/qcssparser_p.h
+
+#include "private/qcssparser_p.h"
+
+class tst_QCssParser : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+private slots:
+ void scanner_data();
+ void scanner();
+ void term_data();
+ void term();
+ void expr_data();
+ void expr();
+ void import();
+ void media();
+ void page();
+ void ruleset();
+ void selector_data();
+ void selector();
+ void prio();
+ void escapes();
+ void malformedDeclarations_data();
+ void malformedDeclarations();
+ void invalidAtKeywords();
+ void marginValue();
+ void marginValue_data();
+ void colorValue_data();
+ void colorValue();
+ void styleSelector_data();
+ void styleSelector();
+ void specificity_data();
+ void specificity();
+ void specificitySort_data();
+ void specificitySort();
+ void rulesForNode_data();
+ void rulesForNode();
+ void shorthandBackgroundProperty_data();
+ void shorthandBackgroundProperty();
+ void pseudoElement_data();
+ void pseudoElement();
+ void gradient_data();
+ void gradient();
+ void extractFontFamily_data();
+ void extractFontFamily();
+ void extractBorder_data();
+ void extractBorder();
+ void noTextDecoration();
+ void quotedAndUnquotedIdentifiers();
+
+private:
+#if defined(Q_OS_WINCE)
+ int m_timesFontId;
+#endif
+};
+
+void tst_QCssParser::initTestCase()
+{
+#if defined(Q_OS_WINCE)
+ QFontDatabase fontDB;
+ m_timesFontId = -1;
+ if (!fontDB.families().contains("Times New Roman")) {
+ m_timesFontId = QFontDatabase::addApplicationFont("times.ttf");
+ QVERIFY(m_timesFontId != -1);
+ }
+#endif
+}
+
+void tst_QCssParser::cleanupTestCase()
+{
+#if defined(Q_OS_WINCE)
+ if (m_timesFontId != -1)
+ QFontDatabase::removeApplicationFont(m_timesFontId);
+#endif
+}
+
+void tst_QCssParser::scanner_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("output");
+
+#if !defined(Q_OS_IRIX) && !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+ QDir d(SRCDIR);
+#else
+ QDir d(QDir::current());
+#endif
+ d.cd("testdata");
+ d.cd("scanner");
+ foreach (QFileInfo test, d.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ QString dir = test.absoluteFilePath() + QDir::separator();
+ QTest::newRow(qPrintable(test.baseName()))
+ << dir + "input"
+ << dir + "output"
+ ;
+ }
+}
+
+
+static const char *tokenName(QCss::TokenType t)
+{
+ switch (t) {
+ case QCss::NONE: return "NONE";
+ case QCss::S: return "S";
+ case QCss::CDO: return "CDO";
+ case QCss::CDC: return "CDC";
+ case QCss::INCLUDES: return "INCLUDES";
+ case QCss::DASHMATCH: return "DASHMATCH";
+ case QCss::LBRACE: return "LBRACE";
+ case QCss::PLUS: return "PLUS";
+ case QCss::GREATER: return "GREATER";
+ case QCss::COMMA: return "COMMA";
+ case QCss::STRING: return "STRING";
+ case QCss::INVALID: return "INVALID";
+ case QCss::IDENT: return "IDENT";
+ case QCss::HASH: return "HASH";
+ case QCss::ATKEYWORD_SYM: return "ATKEYWORD_SYM";
+ case QCss::EXCLAMATION_SYM: return "EXCLAMATION_SYM";
+ case QCss::LENGTH: return "LENGTH";
+ case QCss::PERCENTAGE: return "PERCENTAGE";
+ case QCss::NUMBER: return "NUMBER";
+ case QCss::FUNCTION: return "FUNCTION";
+ case QCss::COLON: return "COLON";
+ case QCss::SEMICOLON: return "SEMICOLON";
+ case QCss::RBRACE: return "RBRACE";
+ case QCss::SLASH: return "SLASH";
+ case QCss::MINUS: return "MINUS";
+ case QCss::DOT: return "DOT";
+ case QCss::STAR: return "STAR";
+ case QCss::LBRACKET: return "LBRACKET";
+ case QCss::RBRACKET: return "RBRACKET";
+ case QCss::EQUAL: return "EQUAL";
+ case QCss::LPAREN: return "LPAREN";
+ case QCss::RPAREN: return "RPAREN";
+ case QCss::OR: return "OR";
+ }
+ return "";
+}
+
+static void debug(const QVector<QCss::Symbol> &symbols, int index = -1)
+{
+ qDebug() << "all symbols:";
+ for (int i = 0; i < symbols.count(); ++i)
+ qDebug() << "(" << i << "); Token:" << tokenName(symbols.at(i).token) << "; Lexem:" << symbols.at(i).lexem();
+ if (index != -1)
+ qDebug() << "failure at index" << index;
+}
+
+//static void debug(const QCss::Parser &p) { debug(p.symbols); }
+
+void tst_QCssParser::scanner()
+{
+ QFETCH(QString, input);
+ QFETCH(QString, output);
+
+ QFile inputFile(input);
+ QVERIFY(inputFile.open(QIODevice::ReadOnly|QIODevice::Text));
+ QVector<QCss::Symbol> symbols;
+ QCss::Scanner::scan(QCss::Scanner::preprocess(QString::fromUtf8(inputFile.readAll())), &symbols);
+
+ QVERIFY(symbols.count() > 1);
+ QVERIFY(symbols.last().token == QCss::S);
+ QVERIFY(symbols.last().lexem() == QLatin1String("\n"));
+ symbols.remove(symbols.count() - 1, 1);
+
+ QFile outputFile(output);
+ QVERIFY(outputFile.open(QIODevice::ReadOnly|QIODevice::Text));
+ QStringList lines;
+ while (!outputFile.atEnd()) {
+ QString line = QString::fromUtf8(outputFile.readLine());
+ if (line.endsWith(QLatin1Char('\n')))
+ line.chop(1);
+ lines.append(line);
+ }
+
+ if (lines.count() != symbols.count()) {
+ debug(symbols);
+ QCOMPARE(lines.count(), symbols.count());
+ }
+
+ for (int i = 0; i < lines.count(); ++i) {
+ QStringList l = lines.at(i).split(QChar::fromLatin1('|'));
+ QCOMPARE(l.count(), 2);
+ const QString expectedToken = l.at(0);
+ const QString expectedLexem = l.at(1);
+ QString actualToken = QString::fromLatin1(tokenName(symbols.at(i).token));
+ if (actualToken != expectedToken) {
+ debug(symbols, i);
+ QCOMPARE(actualToken, expectedToken);
+ }
+ if (symbols.at(i).lexem() != expectedLexem) {
+ debug(symbols, i);
+ QCOMPARE(symbols.at(i).lexem(), expectedLexem);
+ }
+ }
+}
+
+Q_DECLARE_METATYPE(QCss::Value)
+
+void tst_QCssParser::term_data()
+{
+ QTest::addColumn<bool>("parseSuccess");
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QCss::Value>("expectedValue");
+
+ QCss::Value val;
+
+ val.type = QCss::Value::Percentage;
+ val.variant = QVariant(double(200));
+ QTest::newRow("percentage") << true << "200%" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10px");
+ QTest::newRow("px") << true << "10px" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10cm");
+ QTest::newRow("cm") << true << "10cm" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10mm");
+ QTest::newRow("mm") << true << "10mm" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10pt");
+ QTest::newRow("pt") << true << "10pt" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10pc");
+ QTest::newRow("pc") << true << "10pc" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("42in");
+ QTest::newRow("inch") << true << "42in" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10deg");
+ QTest::newRow("deg") << true << "10deg" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10rad");
+ QTest::newRow("rad") << true << "10rad" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10grad");
+ QTest::newRow("grad") << true << "10grad" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10ms");
+ QTest::newRow("time") << true << "10ms" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10s");
+ QTest::newRow("times") << true << "10s" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10hz");
+ QTest::newRow("hz") << true << "10hz" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10khz");
+ QTest::newRow("khz") << true << "10khz" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10myunit");
+ QTest::newRow("dimension") << true << "10myunit" << val;
+
+ val.type = QCss::Value::Percentage;
+
+ val.type = QCss::Value::Percentage;
+ val.variant = QVariant(double(-200));
+ QTest::newRow("minuspercentage") << true << "-200%" << val;
+
+ val.type = QCss::Value::Length;
+ val.variant = QString("10em");
+ QTest::newRow("ems") << true << "10em" << val;
+
+ val.type = QCss::Value::String;
+ val.variant = QVariant(QString("foo"));
+ QTest::newRow("string") << true << "\"foo\"" << val;
+
+ val.type = QCss::Value::Function;
+ val.variant = QVariant(QStringList() << "myFunc" << "23, (nested text)");
+ QTest::newRow("function") << true << "myFunc(23, (nested text))" << val;
+
+ QTest::newRow("function_failure") << false << "myFunction((blah)" << val;
+ QTest::newRow("function_failure2") << false << "+myFunc(23, (nested text))" << val;
+
+ val.type = QCss::Value::Color;
+ val.variant = QVariant(QColor("#12ff34"));
+ QTest::newRow("hexcolor") << true << "#12ff34" << val;
+
+ val.type = QCss::Value::Color;
+ val.variant = QVariant(QColor("#ffbb00"));
+ QTest::newRow("hexcolor2") << true << "#fb0" << val;
+
+ QTest::ignoreMessage(QtWarningMsg, "QCssParser::parseHexColor: Unknown color name '#cafebabe'");
+ QTest::newRow("hexcolor_failure") << false << "#cafebabe" << val;
+
+ val.type = QCss::Value::Uri;
+ val.variant = QString("www.kde.org");
+ QTest::newRow("uri1") << true << "url(\"www.kde.org\")" << val;
+
+ QTest::newRow("uri2") << true << "url(www.kde.org)" << val;
+
+ val.type = QCss::Value::KnownIdentifier;
+ val.variant = int(QCss::Value_Italic);
+ QTest::newRow("italic") << true << "italic" << val;
+
+ val.type = QCss::Value::KnownIdentifier;
+ val.variant = int(QCss::Value_Italic);
+ QTest::newRow("ItaLIc") << true << "ItaLIc" << val;
+}
+
+void tst_QCssParser::term()
+{
+ QFETCH(bool, parseSuccess);
+ QFETCH(QString, css);
+ QFETCH(QCss::Value, expectedValue);
+
+ QCss::Parser parser(css);
+ QCss::Value val;
+ QVERIFY(parser.testTerm());
+ QCOMPARE(parser.parseTerm(&val), parseSuccess);
+ if (parseSuccess) {
+ QCOMPARE(int(val.type), int(expectedValue.type));
+ if (val.variant != expectedValue.variant) {
+ qDebug() << "val.variant:" << val.variant << "expectedValue.variant:" << expectedValue.variant;
+ QCOMPARE(val.variant, expectedValue.variant);
+ }
+ }
+}
+
+Q_DECLARE_METATYPE(QVector<QCss::Value>)
+
+void tst_QCssParser::expr_data()
+{
+ QTest::addColumn<bool>("parseSuccess");
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QVector<QCss::Value> >("expectedValues");
+
+ QVector<QCss::Value> values;
+ QCss::Value val;
+
+ QCss::Value comma;
+ comma.type = QCss::Value::TermOperatorComma;
+
+ val.type = QCss::Value::Identifier;
+ val.variant = QLatin1String("foo");
+ values << val;
+ values << comma;
+ val.variant = QLatin1String("bar");
+ values << val;
+ values << comma;
+ val.variant = QLatin1String("baz");
+ values << val;
+ QTest::newRow("list") << true << "foo, bar, baz" << values;
+ values.clear();
+}
+
+void tst_QCssParser::expr()
+{
+ QFETCH(bool, parseSuccess);
+ QFETCH(QString, css);
+ QFETCH(QVector<QCss::Value>, expectedValues);
+
+ QCss::Parser parser(css);
+ QVector<QCss::Value> values;
+ QVERIFY(parser.testExpr());
+ QCOMPARE(parser.parseExpr(&values), parseSuccess);
+ if (parseSuccess) {
+ QCOMPARE(values.count(), expectedValues.count());
+
+ for (int i = 0; i < values.count(); ++i) {
+ QCOMPARE(int(values.at(i).type), int(expectedValues.at(i).type));
+ QCOMPARE(values.at(i).variant, expectedValues.at(i).variant);
+ }
+ }
+}
+
+void tst_QCssParser::import()
+{
+ QCss::Parser parser("@import \"plainstring\";");
+ QVERIFY(parser.testImport());
+ QCss::ImportRule rule;
+ QVERIFY(parser.parseImport(&rule));
+ QCOMPARE(rule.href, QString("plainstring"));
+
+ parser = QCss::Parser("@import url(\"www.kde.org\") print/*comment*/,screen;");
+ QVERIFY(parser.testImport());
+ QVERIFY(parser.parseImport(&rule));
+ QCOMPARE(rule.href, QString("www.kde.org"));
+ QCOMPARE(rule.media.count(), 2);
+ QCOMPARE(rule.media.at(0), QString("print"));
+ QCOMPARE(rule.media.at(1), QString("screen"));
+}
+
+void tst_QCssParser::media()
+{
+ QCss::Parser parser("@media print/*comment*/,screen /*comment to ignore*/{ }");
+ QVERIFY(parser.testMedia());
+ QCss::MediaRule rule;
+ QVERIFY(parser.parseMedia(&rule));
+ QCOMPARE(rule.media.count(), 2);
+ QCOMPARE(rule.media.at(0), QString("print"));
+ QCOMPARE(rule.media.at(1), QString("screen"));
+ QVERIFY(rule.styleRules.isEmpty());
+}
+
+void tst_QCssParser::page()
+{
+ QCss::Parser parser("@page :first/*comment to ignore*/{ }");
+ QVERIFY(parser.testPage());
+ QCss::PageRule rule;
+ QVERIFY(parser.parsePage(&rule));
+ QCOMPARE(rule.selector, QString("first"));
+ QVERIFY(rule.declarations.isEmpty());
+}
+
+void tst_QCssParser::ruleset()
+{
+ {
+ QCss::Parser parser("p/*foo*/{ }");
+ QVERIFY(parser.testRuleset());
+ QCss::StyleRule rule;
+ QVERIFY(parser.parseRuleset(&rule));
+ QCOMPARE(rule.selectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p"));
+ QVERIFY(rule.declarations.isEmpty());
+ }
+
+ {
+ QCss::Parser parser("p/*comment*/,div{ }");
+ QVERIFY(parser.testRuleset());
+ QCss::StyleRule rule;
+ QVERIFY(parser.parseRuleset(&rule));
+ QCOMPARE(rule.selectors.count(), 2);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p"));
+ QCOMPARE(rule.selectors.at(1).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).elementName, QString("div"));
+ QVERIFY(rule.declarations.isEmpty());
+ }
+
+ {
+ QCss::Parser parser(":before, :after { }");
+ QVERIFY(parser.testRuleset());
+ QCss::StyleRule rule;
+ QVERIFY(parser.parseRuleset(&rule));
+ QCOMPARE(rule.selectors.count(), 2);
+
+ QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).pseudos.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).pseudos.at(0).name, QString("before"));
+
+ QCOMPARE(rule.selectors.at(1).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).pseudos.count(), 1);
+ QCOMPARE(rule.selectors.at(1).basicSelectors.at(0).pseudos.at(0).name, QString("after"));
+
+ QVERIFY(rule.declarations.isEmpty());
+ }
+
+}
+
+Q_DECLARE_METATYPE(QCss::Selector)
+
+void tst_QCssParser::selector_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QCss::Selector>("expectedSelector");
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "p";
+ basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfPreceeds;
+ sel.basicSelectors << basic;
+
+ basic = QCss::BasicSelector();
+ basic.elementName = "div";
+ sel.basicSelectors << basic;
+
+ QTest::newRow("comment") << QString("p/* */+ div") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = QString();
+ sel.basicSelectors << basic;
+
+ QTest::newRow("any") << QString("*") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ sel.basicSelectors << basic;
+
+ QTest::newRow("element") << QString("e") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfAncestor;
+ sel.basicSelectors << basic;
+
+ basic.elementName = "f";
+ basic.relationToNext = QCss::BasicSelector::NoRelation;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("descendant") << QString("e f") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfParent;
+ sel.basicSelectors << basic;
+
+ basic.elementName = "f";
+ basic.relationToNext = QCss::BasicSelector::NoRelation;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("parent") << QString("e > f") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::Pseudo pseudo;
+ pseudo.name = "first-child";
+ basic.pseudos.append(pseudo);
+ sel.basicSelectors << basic;
+
+ QTest::newRow("first-child") << QString("e:first-child") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::Pseudo pseudo;
+ pseudo.name = "c";
+ pseudo.function = "lang";
+ basic.pseudos.append(pseudo);
+ sel.basicSelectors << basic;
+
+ QTest::newRow("lang") << QString("e:lang(c)") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ basic.relationToNext = QCss::BasicSelector::MatchNextSelectorIfPreceeds;
+ sel.basicSelectors << basic;
+
+ basic.elementName = "f";
+ basic.relationToNext = QCss::BasicSelector::NoRelation;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("precede") << QString("e + f") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::AttributeSelector attrSel;
+ attrSel.name = "foo";
+ basic.attributeSelectors << attrSel;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("attr") << QString("e[foo]") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::AttributeSelector attrSel;
+ attrSel.name = "foo";
+ attrSel.value = "warning";
+ attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchEqual;
+ basic.attributeSelectors << attrSel;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("attr-equal") << QString("e[foo=\"warning\"]") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::AttributeSelector attrSel;
+ attrSel.name = "foo";
+ attrSel.value = "warning";
+ attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchContains;
+ basic.attributeSelectors << attrSel;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("attr-contains") << QString("e[foo~=\"warning\"]") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ QCss::AttributeSelector attrSel;
+ attrSel.name = "lang";
+ attrSel.value = "en";
+ attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchBeginsWith;
+ basic.attributeSelectors << attrSel;
+ sel.basicSelectors << basic;
+
+ QTest::newRow("attr-contains") << QString("e[lang|=\"en\"]") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "div";
+
+ QCss::AttributeSelector attrSel;
+ attrSel.name = "class";
+ attrSel.valueMatchCriterium = QCss::AttributeSelector::MatchContains;
+ attrSel.value = "warning";
+ basic.attributeSelectors.append(attrSel);
+
+ attrSel.value = "foo";
+ basic.attributeSelectors.append(attrSel);
+
+ sel.basicSelectors << basic;
+
+ QTest::newRow("class") << QString("div.warning.foo") << sel;
+ }
+
+ {
+ QCss::Selector sel;
+ QCss::BasicSelector basic;
+
+ basic.elementName = "e";
+ basic.ids << "myid";
+ sel.basicSelectors << basic;
+
+ QTest::newRow("id") << QString("e#myid") << sel;
+ }
+}
+
+void tst_QCssParser::selector()
+{
+ QFETCH(QString, css);
+ QFETCH(QCss::Selector, expectedSelector);
+
+ QCss::Parser parser(css);
+ QVERIFY(parser.testSelector());
+ QCss::Selector selector;
+ QVERIFY(parser.parseSelector(&selector));
+
+ QCOMPARE(selector.basicSelectors.count(), expectedSelector.basicSelectors.count());
+ for (int i = 0; i < selector.basicSelectors.count(); ++i) {
+ const QCss::BasicSelector sel = selector.basicSelectors.at(i);
+ const QCss::BasicSelector expectedSel = expectedSelector.basicSelectors.at(i);
+ QCOMPARE(sel.elementName, expectedSel.elementName);
+ QCOMPARE(int(sel.relationToNext), int(expectedSel.relationToNext));
+
+ QCOMPARE(sel.pseudos.count(), expectedSel.pseudos.count());
+ for (int i = 0; i < sel.pseudos.count(); ++i) {
+ QCOMPARE(sel.pseudos.at(i).name, expectedSel.pseudos.at(i).name);
+ QCOMPARE(sel.pseudos.at(i).function, expectedSel.pseudos.at(i).function);
+ }
+
+ QCOMPARE(sel.attributeSelectors.count(), expectedSel.attributeSelectors.count());
+ for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
+ QCOMPARE(sel.attributeSelectors.at(i).name, expectedSel.attributeSelectors.at(i).name);
+ QCOMPARE(sel.attributeSelectors.at(i).value, expectedSel.attributeSelectors.at(i).value);
+ QCOMPARE(int(sel.attributeSelectors.at(i).valueMatchCriterium), int(expectedSel.attributeSelectors.at(i).valueMatchCriterium));
+ }
+ }
+}
+
+void tst_QCssParser::prio()
+{
+ {
+ QCss::Parser parser("!important");
+ QVERIFY(parser.testPrio());
+ }
+ {
+ QCss::Parser parser("!impOrTAnt");
+ QVERIFY(parser.testPrio());
+ }
+ {
+ QCss::Parser parser("!\"important\"");
+ QVERIFY(!parser.testPrio());
+ QCOMPARE(parser.index, 0);
+ }
+ {
+ QCss::Parser parser("!importbleh");
+ QVERIFY(!parser.testPrio());
+ QCOMPARE(parser.index, 0);
+ }
+}
+
+void tst_QCssParser::escapes()
+{
+ QCss::Parser parser("\\hello");
+ parser.test(QCss::IDENT);
+ QCOMPARE(parser.lexem(), QString("hello"));
+}
+
+void tst_QCssParser::malformedDeclarations_data()
+{
+ QTest::addColumn<QString>("css");
+
+ QTest::newRow("1") << QString("p { color:green }");
+ QTest::newRow("2") << QString("p { color:green; color } /* malformed declaration missing ':', value */");
+ QTest::newRow("3") << QString("p { color:red; color; color:green } /* same with expected recovery */");
+ QTest::newRow("4") << QString("p { color:green; color: } /* malformed declaration missing value */");
+ QTest::newRow("5") << QString("p { color:red; color:; color:green } /* same with expected recovery */");
+ QTest::newRow("6") << QString("p { color:green; color{;color:maroon} } /* unexpected tokens { } */");
+ QTest::newRow("7") << QString("p { color:red; color{;color:maroon}; color:green } /* same with recovery */");
+}
+
+void tst_QCssParser::malformedDeclarations()
+{
+ QFETCH(QString, css);
+ QCss::Parser parser(css);
+ QVERIFY(parser.testRuleset());
+ QCss::StyleRule rule;
+ QVERIFY(parser.parseRuleset(&rule));
+
+ QCOMPARE(rule.selectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("p"));
+
+ QVERIFY(rule.declarations.count() >= 1);
+ QCOMPARE(int(rule.declarations.last().d->propertyId), int(QCss::Color));
+ QCOMPARE(rule.declarations.last().d->values.count(), 1);
+ QCOMPARE(int(rule.declarations.last().d->values.at(0).type), int(QCss::Value::Identifier));
+ QCOMPARE(rule.declarations.last().d->values.at(0).variant.toString(), QString("green"));
+}
+
+void tst_QCssParser::invalidAtKeywords()
+{
+ QCss::Parser parser(""
+ "@three-dee {"
+ " @background-lighting {"
+ " azimuth: 30deg;"
+ " elevation: 190deg;"
+ " }"
+ " h1 { color: red }"
+ "}"
+ "h1 { color: blue }");
+
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
+ sheet.styleRules.at(0) : *sheet.nameIndex.begin();
+
+ QCOMPARE(rule.selectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.count(), 1);
+ QCOMPARE(rule.selectors.at(0).basicSelectors.at(0).elementName, QString("h1"));
+
+ QCOMPARE(rule.declarations.count(), 1);
+ QCOMPARE(int(rule.declarations.at(0).d->propertyId), int(QCss::Color));
+ QCOMPARE(rule.declarations.at(0).d->values.count(), 1);
+ QCOMPARE(int(rule.declarations.at(0).d->values.at(0).type), int(QCss::Value::Identifier));
+ QCOMPARE(rule.declarations.at(0).d->values.at(0).variant.toString(), QString("blue"));
+}
+
+Q_DECLARE_METATYPE(QColor)
+
+void tst_QCssParser::colorValue_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QColor>("expectedColor");
+
+ QTest::newRow("identifier") << "color: black" << QColor("black");
+ QTest::newRow("string") << "color: \"green\"" << QColor("green");
+ QTest::newRow("hexcolor") << "color: #12af0e" << QColor(0x12, 0xaf, 0x0e);
+ QTest::newRow("functional1") << "color: rgb(21, 45, 73)" << QColor(21, 45, 73);
+ QTest::newRow("functional2") << "color: rgb(100%, 0%, 100%)" << QColor(0xff, 0, 0xff);
+ QTest::newRow("rgba") << "color: rgba(10, 20, 30, 40)" << QColor(10, 20, 30, 40);
+ QTest::newRow("rgb") << "color: rgb(10, 20, 30, 40)" << QColor(10, 20, 30, 40);
+ QTest::newRow("hsl") << "color: hsv(10, 20, 30)" << QColor::fromHsv(10, 20, 30, 255);
+ QTest::newRow("hsla") << "color: hsva(10, 20, 30, 40)" << QColor::fromHsv(10, 20, 30, 40);
+ QTest::newRow("invalid1") << "color: rgb(why, does, it, always, rain, on, me)" << QColor();
+ QTest::newRow("invalid2") << "color: rgba(i, meant, norway)" << QColor();
+ QTest::newRow("role") << "color: palette(base)" << qApp->palette().color(QPalette::Base);
+ QTest::newRow("role2") << "color: palette( window-text ) " << qApp->palette().color(QPalette::WindowText);
+ QTest::newRow("transparent") << "color: transparent" << QColor(Qt::transparent);
+}
+
+void tst_QCssParser::colorValue()
+{
+ QFETCH(QString, css);
+ QFETCH(QColor, expectedColor);
+
+ QCss::Parser parser(css);
+ QCss::Declaration decl;
+ QVERIFY(parser.parseNextDeclaration(&decl));
+ const QColor col = decl.colorValue();
+ QVERIFY(expectedColor.isValid() == col.isValid());
+ QCOMPARE(col, expectedColor);
+}
+
+class DomStyleSelector : public QCss::StyleSelector
+{
+public:
+ inline DomStyleSelector(const QDomDocument &doc, const QCss::StyleSheet &sheet)
+ : doc(doc)
+ {
+ styleSheets.append(sheet);
+ }
+
+ virtual QStringList nodeNames(NodePtr node) const { return QStringList(reinterpret_cast<QDomElement *>(node.ptr)->tagName()); }
+ virtual QString attribute(NodePtr node, const QString &name) const { return reinterpret_cast<QDomElement *>(node.ptr)->attribute(name); }
+ virtual bool hasAttribute(NodePtr node, const QString &name) const { return reinterpret_cast<QDomElement *>(node.ptr)->hasAttribute(name); }
+ virtual bool hasAttributes(NodePtr node) const { return reinterpret_cast<QDomElement *>(node.ptr)->hasAttributes(); }
+
+ virtual bool isNullNode(NodePtr node) const {
+ return reinterpret_cast<QDomElement *>(node.ptr)->isNull();
+ }
+ virtual NodePtr parentNode(NodePtr node) const {
+ NodePtr parent;
+ parent.ptr = new QDomElement(reinterpret_cast<QDomElement *>(node.ptr)->parentNode().toElement());
+ return parent;
+ }
+ virtual NodePtr duplicateNode(NodePtr node) const {
+ NodePtr n;
+ n.ptr = new QDomElement(*reinterpret_cast<QDomElement *>(node.ptr));
+ return n;
+ }
+ virtual NodePtr previousSiblingNode(NodePtr node) const {
+ NodePtr sibling;
+ sibling.ptr = new QDomElement(reinterpret_cast<QDomElement *>(node.ptr)->previousSiblingElement());
+ return sibling;
+ }
+ virtual void freeNode(NodePtr node) const {
+ delete reinterpret_cast<QDomElement *>(node.ptr);
+ }
+
+private:
+ QDomDocument doc;
+};
+
+Q_DECLARE_METATYPE(QDomDocument)
+
+void tst_QCssParser::marginValue_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QString>("expectedMargin");
+
+ QFont f;
+ int ex = QFontMetrics(f).xHeight();
+ int em = QFontMetrics(f).height();
+
+ QTest::newRow("one value") << "margin: 1px" << "1 1 1 1";
+ QTest::newRow("two values") << "margin: 1px 2px" << "1 2 1 2";
+ QTest::newRow("three value") << "margin: 1px 2px 3px" << "1 2 3 2";
+ QTest::newRow("four values") << "margin: 1px 2px 3px 4px" << "1 2 3 4";
+ QTest::newRow("default px") << "margin: 1 2 3 4" << "1 2 3 4";
+ QTest::newRow("no unit") << "margin: 1 2 3 4" << "1 2 3 4";
+ QTest::newRow("em") << "margin: 1ex 2ex 3ex 4ex" << QString("%1 %2 %3 %4").arg(ex).arg(2*ex).arg(3*ex).arg(4*ex);
+ QTest::newRow("ex") << "margin: 1 2em 3px 4ex" << QString("%1 %2 %3 %4").arg(1).arg(2*em).arg(3).arg(4*ex);
+
+ f.setPointSize(20);
+ f.setBold(true);
+ ex = QFontMetrics(f).xHeight();
+ em = QFontMetrics(f).height();
+ QTest::newRow("em2") << "font: bold 20pt; margin: 1ex 2ex 3ex 4ex" << QString("%1 %2 %3 %4").arg(ex).arg(2*ex).arg(3*ex).arg(4*ex);
+ QTest::newRow("ex2") << "margin: 1 2em 3px 4ex; font-size: 20pt; font-weight: bold;" << QString("%1 %2 %3 %4").arg(1).arg(2*em).arg(3).arg(4*ex);
+
+ QTest::newRow("crap") << "margin: crap" << "0 0 0 0";
+}
+
+void tst_QCssParser::marginValue()
+{
+ QFETCH(QString, css);
+ QFETCH(QString, expectedMargin);
+
+ QDomDocument doc;
+ QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>")));
+
+ css.prepend("dummy {");
+ css.append("}");
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(n);
+ QVector<QCss::Declaration> decls = rules.at(0).declarations;
+ QCss::ValueExtractor v(decls);
+
+ {
+ int m[4];
+ int p[4];
+ int spacing;
+ v.extractBox(m, p, &spacing);
+ QString str = QString("%1 %2 %3 %4").arg(m[0]).arg(m[1]).arg(m[2]).arg(m[3]);
+ QCOMPARE(str, expectedMargin);
+ }
+}
+
+void tst_QCssParser::styleSelector_data()
+{
+ QTest::addColumn<bool>("match");
+ QTest::addColumn<QString>("selector");
+ QTest::addColumn<QString>("xml");
+ QTest::addColumn<QString>("elementToCheck");
+
+ QTest::newRow("plain") << true << QString("p") << QString("<p />") << QString();
+ QTest::newRow("noplain") << false << QString("bar") << QString("<p />") << QString();
+
+ QTest::newRow("class") << true << QString(".foo") << QString("<p class=\"foo\" />") << QString();
+ QTest::newRow("noclass") << false << QString(".bar") << QString("<p class=\"foo\" />") << QString();
+
+ QTest::newRow("attrset") << true << QString("[justset]") << QString("<p justset=\"bar\" />") << QString();
+ QTest::newRow("notattrset") << false << QString("[justset]") << QString("<p otherattribute=\"blub\" />") << QString();
+
+ QTest::newRow("attrmatch") << true << QString("[foo=bar]") << QString("<p foo=\"bar\" />") << QString();
+ QTest::newRow("noattrmatch") << false << QString("[foo=bar]") << QString("<p foo=\"xyz\" />") << QString();
+
+ QTest::newRow("contains") << true << QString("[foo~=bar]") << QString("<p foo=\"baz bleh bar\" />") << QString();
+ QTest::newRow("notcontains") << false << QString("[foo~=bar]") << QString("<p foo=\"test\" />") << QString();
+
+ QTest::newRow("beingswith") << true << QString("[foo|=bar]") << QString("<p foo=\"bar-bleh\" />") << QString();
+ QTest::newRow("notbeingswith") << false << QString("[foo|=bar]") << QString("<p foo=\"bleh-bar\" />") << QString();
+
+ QTest::newRow("attr2") << true << QString("[bar=foo]") << QString("<p bleh=\"bar\" bar=\"foo\" />") << QString();
+
+ QTest::newRow("universal1") << true << QString("*") << QString("<p />") << QString();
+
+ QTest::newRow("universal3") << false << QString("*[foo=bar]") << QString("<p foo=\"bleh\" />") << QString();
+ QTest::newRow("universal4") << true << QString("*[foo=bar]") << QString("<p foo=\"bar\" />") << QString();
+
+ QTest::newRow("universal5") << false << QString("[foo=bar]") << QString("<p foo=\"bleh\" />") << QString();
+ QTest::newRow("universal6") << true << QString("[foo=bar]") << QString("<p foo=\"bar\" />") << QString();
+
+ QTest::newRow("universal7") << true << QString(".charfmt1") << QString("<p class=\"charfmt1\" />") << QString();
+
+ QTest::newRow("id") << true << QString("#blub") << QString("<p id=\"blub\" />") << QString();
+ QTest::newRow("noid") << false << QString("#blub") << QString("<p id=\"other\" />") << QString();
+
+ QTest::newRow("childselector") << true << QString("parent > child")
+ << QString("<parent><child /></parent>")
+ << QString("parent/child");
+
+ QTest::newRow("nochildselector2") << false << QString("parent > child")
+ << QString("<child><parent /></child>")
+ << QString("child/parent");
+
+ QTest::newRow("nochildselector3") << false << QString("parent > child")
+ << QString("<parent><intermediate><child /></intermediate></parent>")
+ << QString("parent/intermediate/child");
+
+ QTest::newRow("childselector2") << true << QString("parent[foo=bar] > child")
+ << QString("<parent foo=\"bar\"><child /></parent>")
+ << QString("parent/child");
+
+ QTest::newRow("nochildselector4") << false << QString("parent[foo=bar] > child")
+ << QString("<parent><child /></parent>")
+ << QString("parent/child");
+
+ QTest::newRow("nochildselector5") << false << QString("parent[foo=bar] > child")
+ << QString("<parent foo=\"bar\"><parent><child /></parent></parent>")
+ << QString("parent/parent/child");
+
+ QTest::newRow("childselectors") << true << QString("grandparent > parent > child")
+ << QString("<grandparent><parent><child /></parent></grandparent>")
+ << QString("grandparent/parent/child");
+
+ QTest::newRow("descendant") << true << QString("grandparent child")
+ << QString("<grandparent><parent><child /></parent></grandparent>")
+ << QString("grandparent/parent/child");
+
+ QTest::newRow("nodescendant") << false << QString("grandparent child")
+ << QString("<other><parent><child /></parent></other>")
+ << QString("other/parent/child");
+
+ QTest::newRow("descendant2") << true << QString("grandgrandparent grandparent child")
+ << QString("<grandgrandparent><inbetween><grandparent><parent><child /></parent></grandparent></inbetween></grandgrandparent>")
+ << QString("grandgrandparent/inbetween/grandparent/parent/child");
+
+ QTest::newRow("combined") << true << QString("grandparent parent > child")
+ << QString("<grandparent><inbetween><parent><child /></parent></inbetween></grandparent>")
+ << QString("grandparent/inbetween/parent/child");
+
+ QTest::newRow("combined2") << true << QString("grandparent > parent child")
+ << QString("<grandparent><parent><inbetween><child /></inbetween></parent></grandparent>")
+ << QString("grandparent/parent/inbetween/child");
+
+ QTest::newRow("combined3") << true << QString("grandparent > parent child")
+ << QString("<grandparent><parent><inbetween><child /></inbetween></parent></grandparent>")
+ << QString("grandparent/parent/inbetween/child");
+
+ QTest::newRow("nocombined") << false << QString("grandparent parent > child")
+ << QString("<inbetween><parent><child /></parent></inbetween>")
+ << QString("inbetween/parent/child");
+
+ QTest::newRow("nocombined2") << false << QString("grandparent parent > child")
+ << QString("<parent><child /></parent>")
+ << QString("parent/child");
+
+ QTest::newRow("previoussibling") << true << QString("p1 + p2")
+ << QString("<p1 /><p2 />")
+ << QString("p2");
+
+ QTest::newRow("noprevioussibling") << false << QString("p2 + p1")
+ << QString("<p1 /><p2 />")
+ << QString("p2");
+
+ QTest::newRow("ancestry_firstmismatch") << false << QString("parent child[foo=bar]")
+ << QString("<parent><child /></parent>")
+ << QString("parent/child");
+
+ QTest::newRow("unknown-pseudo") << false << QString("p:enabled:foobar") << QString("<p/>") << QString();
+}
+
+void tst_QCssParser::styleSelector()
+{
+ QFETCH(bool, match);
+ QFETCH(QString, selector);
+ QFETCH(QString, xml);
+ QFETCH(QString, elementToCheck);
+
+ QString css = QString("%1 { background-color: green }").arg(selector);
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QDomDocument doc;
+ xml.prepend("<!DOCTYPE test><test>");
+ xml.append("</test>");
+ QVERIFY(doc.setContent(xml));
+
+ DomStyleSelector testSelector(doc, sheet);
+
+ QDomElement e = doc.documentElement();
+ if (elementToCheck.isEmpty()) {
+ e = e.firstChildElement();
+ } else {
+ QStringList path = elementToCheck.split(QLatin1Char('/'));
+ do {
+ e = e.namedItem(path.takeFirst()).toElement();
+ } while (!path.isEmpty());
+ }
+ QVERIFY(!e.isNull());
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::Declaration> decls = testSelector.declarationsForNode(n);
+
+ if (match) {
+ QCOMPARE(decls.count(), 1);
+ QCOMPARE(int(decls.at(0).d->propertyId), int(QCss::BackgroundColor));
+ QCOMPARE(decls.at(0).d->values.count(), 1);
+ QCOMPARE(int(decls.at(0).d->values.at(0).type), int(QCss::Value::Identifier));
+ QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), QString("green"));
+ } else {
+ QVERIFY(decls.isEmpty());
+ }
+}
+
+void tst_QCssParser::specificity_data()
+{
+ QTest::addColumn<QString>("selector");
+ QTest::addColumn<int>("specificity");
+
+ QTest::newRow("universal") << QString("*") << 0;
+
+ QTest::newRow("elements+pseudos1") << QString("foo") << 1;
+ QTest::newRow("elements+pseudos2") << QString("foo *[blah]") << 1 + (1 * 0x10);
+
+ // should strictly speaking be '2', but we don't support pseudo-elements yet,
+ // only pseudo-classes
+ QTest::newRow("elements+pseudos3") << QString("li:first-line") << 1 + (1 * 0x10);
+
+ QTest::newRow("elements+pseudos4") << QString("ul li") << 2;
+ QTest::newRow("elements+pseudos5") << QString("ul ol+li") << 3;
+ QTest::newRow("elements+pseudos6") << QString("h1 + *[rel=up]") << 1 + (1 * 0x10);
+
+ QTest::newRow("elements+pseudos7") << QString("ul ol li.red") << 3 + (1 * 0x10);
+ QTest::newRow("elements+pseudos8") << QString("li.red.level") << 1 + (2 * 0x10);
+ QTest::newRow("id") << QString("#x34y") << 1 * 0x100;
+}
+
+void tst_QCssParser::specificity()
+{
+ QFETCH(QString, selector);
+
+ QString css = QString("%1 { }").arg(selector);
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count() + sheet.idIndex.count() , 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ? sheet.styleRules.at(0)
+ : (!sheet.nameIndex.isEmpty()) ? *sheet.nameIndex.begin()
+ : *sheet.idIndex.begin();
+ QCOMPARE(rule.selectors.count(), 1);
+ QTEST(rule.selectors.at(0).specificity(), "specificity");
+}
+
+void tst_QCssParser::specificitySort_data()
+{
+ QTest::addColumn<QString>("firstSelector");
+ QTest::addColumn<QString>("secondSelector");
+ QTest::addColumn<QString>("xml");
+
+ QTest::newRow("universal1") << QString("*") << QString("p") << QString("<p />");
+ QTest::newRow("attr") << QString("p") << QString("p[foo=bar]") << QString("<p foo=\"bar\" />");
+ QTest::newRow("id") << QString("p") << QString("#hey") << QString("<p id=\"hey\" />");
+ QTest::newRow("id2") << QString("[id=hey]") << QString("#hey") << QString("<p id=\"hey\" />");
+ QTest::newRow("class") << QString("p") << QString(".hey") << QString("<p class=\"hey\" />");
+}
+
+void tst_QCssParser::specificitySort()
+{
+ QFETCH(QString, firstSelector);
+ QFETCH(QString, secondSelector);
+ QFETCH(QString, xml);
+
+ firstSelector.append(" { color: green; }");
+ secondSelector.append(" { color: red; }");
+
+ QDomDocument doc;
+ xml.prepend("<!DOCTYPE test><test>");
+ xml.append("</test>");
+ QVERIFY(doc.setContent(xml));
+
+ for (int i = 0; i < 2; ++i) {
+ QString css;
+ if (i == 0)
+ css = firstSelector + secondSelector;
+ else
+ css = secondSelector + firstSelector;
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::Declaration> decls = testSelector.declarationsForNode(n);
+
+ QCOMPARE(decls.count(), 2);
+
+ QCOMPARE(int(decls.at(0).d->propertyId), int(QCss::Color));
+ QCOMPARE(decls.at(0).d->values.count(), 1);
+ QCOMPARE(int(decls.at(0).d->values.at(0).type), int(QCss::Value::Identifier));
+ QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), QString("green"));
+
+ QCOMPARE(int(decls.at(1).d->propertyId), int(QCss::Color));
+ QCOMPARE(decls.at(1).d->values.count(), 1);
+ QCOMPARE(int(decls.at(1).d->values.at(0).type), int(QCss::Value::Identifier));
+ QCOMPARE(decls.at(1).d->values.at(0).variant.toString(), QString("red"));
+ }
+}
+
+void tst_QCssParser::rulesForNode_data()
+{
+ QTest::addColumn<QString>("xml");
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<quint64>("pseudoClass");
+ QTest::addColumn<int>("declCount");
+ QTest::addColumn<QString>("value0");
+ QTest::addColumn<QString>("value1");
+
+ QTest::newRow("universal1") << QString("<p/>") << QString("* { color: red }")
+ << (quint64)QCss::PseudoClass_Unspecified << 1 << "red" << "";
+
+ QTest::newRow("basic") << QString("<p/>") << QString("p:enabled { color: red; bg:blue; }")
+ << (quint64)QCss::PseudoClass_Enabled << 2 << "red" << "blue";
+
+ QTest::newRow("single") << QString("<p/>")
+ << QString("p:enabled { color: red; } *:hover { color: white }")
+ << (quint64)QCss::PseudoClass_Hover << 1 << "white" << "";
+
+ QTest::newRow("multisel") << QString("<p/>")
+ << QString("p:enabled { color: red; } p:hover { color: gray } *:hover { color: white } ")
+ << (quint64)QCss::PseudoClass_Hover << 2 << "white" << "gray";
+
+ QTest::newRow("multisel2") << QString("<p/>")
+ << QString("p:enabled { color: red; } p:hover:focus { color: gray } *:hover { color: white } ")
+ << quint64(QCss::PseudoClass_Hover|QCss::PseudoClass_Focus) << 2 << "white" << "gray";
+
+ QTest::newRow("multisel3-diffspec") << QString("<p/>")
+ << QString("p:enabled { color: red; } p:hover:focus { color: gray } *:hover { color: white } ")
+ << quint64(QCss::PseudoClass_Hover) << 1 << "white" << "";
+
+ QTest::newRow("!-1") << QString("<p/>")
+ << QString("p:checked:!hover { color: red; } p:checked:hover { color: gray } p:checked { color: white }")
+ << quint64(QCss::PseudoClass_Hover|QCss::PseudoClass_Checked) << 2 << "white" << "gray";
+
+ QTest::newRow("!-2") << QString("<p/>")
+ << QString("p:checked:!hover:!pressed { color: red; } p:!checked:hover { color: gray } p:!focus { color: blue }")
+ << quint64(QCss::PseudoClass_Focus) << 0 << "" << "";
+
+ QTest::newRow("!-3") << QString("<p/>")
+ << QString("p:checked:!hover:!pressed { color: red; } p:!checked:hover { color: gray } p:!focus { color: blue; }")
+ << quint64(QCss::PseudoClass_Pressed) << 1 << "blue" << "";
+}
+
+void tst_QCssParser::rulesForNode()
+{
+ QFETCH(QString, xml);
+ QFETCH(QString, css);
+ QFETCH(quint64, pseudoClass);
+ QFETCH(int, declCount);
+ QFETCH(QString, value0);
+ QFETCH(QString, value1);
+
+ QDomDocument doc;
+ xml.prepend("<!DOCTYPE test><test>");
+ xml.append("</test>");
+ QVERIFY(doc.setContent(xml));
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(n);
+
+ QVector<QCss::Declaration> decls;
+ for (int i = 0; i < rules.count(); i++) {
+ const QCss::Selector &selector = rules.at(i).selectors.at(0);
+ quint64 negated = 0;
+ quint64 cssClass = selector.pseudoClass(&negated);
+ if ((cssClass == QCss::PseudoClass_Unspecified)
+ || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0)))
+ decls += rules.at(i).declarations;
+ }
+
+ QVERIFY(decls.count() == declCount);
+
+ if (declCount > 0)
+ QCOMPARE(decls.at(0).d->values.at(0).variant.toString(), value0);
+ if (declCount > 1)
+ QCOMPARE(decls.at(1).d->values.at(0).variant.toString(), value1);
+}
+
+void tst_QCssParser::shorthandBackgroundProperty_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QBrush>("expectedBrush");
+ QTest::addColumn<QString>("expectedImage");
+ QTest::addColumn<int>("expectedRepeatValue");
+ QTest::addColumn<int>("expectedAlignment");
+
+ QTest::newRow("simple color") << "background: red" << QBrush(QColor("red")) << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop);
+ QTest::newRow("plain color") << "background-color: red" << QBrush(QColor("red")) << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop);
+ QTest::newRow("palette color") << "background-color: palette(mid)" << qApp->palette().mid() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop);
+ QTest::newRow("multiple") << "background: url(chess.png) blue repeat-y" << QBrush(QColor("blue")) << QString("chess.png") << int(QCss::Repeat_Y) << int(Qt::AlignLeft | Qt::AlignTop);
+ QTest::newRow("plain alignment") << "background-position: center" << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignCenter);
+ QTest::newRow("plain alignment2") << "background-position: left top" << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignTop);
+ QTest::newRow("plain alignment3") << "background-position: left" << QBrush() << QString() << int(QCss::Repeat_XY) << int(Qt::AlignLeft | Qt::AlignVCenter);
+ QTest::newRow("multi") << "background: left url(blah.png) repeat-x" << QBrush() << QString("blah.png") << int(QCss::Repeat_X) << int(Qt::AlignLeft | Qt::AlignVCenter);
+ QTest::newRow("multi2") << "background: url(blah.png) repeat-x top" << QBrush() << QString("blah.png") << int(QCss::Repeat_X) << int(Qt::AlignTop | Qt::AlignHCenter);
+ QTest::newRow("multi3") << "background: url(blah.png) top right" << QBrush() << QString("blah.png") << int(QCss::Repeat_XY) << int(Qt::AlignTop | Qt::AlignRight);
+}
+
+void tst_QCssParser::shorthandBackgroundProperty()
+{
+ QFETCH(QString, css);
+
+ QDomDocument doc;
+ QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>")));
+
+ css.prepend("dummy {");
+ css.append("}");
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(n);
+ QVector<QCss::Declaration> decls = rules.at(0).declarations;
+ QCss::ValueExtractor v(decls);
+
+ QBrush brush;
+ QString image;
+ QCss::Repeat repeat = QCss::Repeat_XY;
+ Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
+ QCss::Origin origin = QCss::Origin_Padding;
+ QCss::Attachment attachment;
+ QCss::Origin ignoredOrigin;
+ v.extractBackground(&brush, &image, &repeat, &alignment, &origin, &attachment, &ignoredOrigin);
+
+ QFETCH(QBrush, expectedBrush);
+ QVERIFY(expectedBrush.color() == brush.color());
+
+ QTEST(image, "expectedImage");
+ QTEST(int(repeat), "expectedRepeatValue");
+ QTEST(int(alignment), "expectedAlignment");
+
+ //QTBUG-9674 : a second evaluation should give the same results
+ QVERIFY(v.extractBackground(&brush, &image, &repeat, &alignment, &origin, &attachment, &ignoredOrigin));
+ QVERIFY(expectedBrush.color() == brush.color());
+ QTEST(image, "expectedImage");
+ QTEST(int(repeat), "expectedRepeatValue");
+ QTEST(int(alignment), "expectedAlignment");
+}
+
+void tst_QCssParser::pseudoElement_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QString>("pseudoElement");
+ QTest::addColumn<int>("declCount");
+
+ // QComboBox::dropDown { border-image: blah; }
+ QTest::newRow("no pseudo-elements") << QString("dummy:hover { color: red }") << "" << 1;
+ QTest::newRow("no pseudo-elements") << QString("dummy:hover { color: red }") << "pe" << 0;
+
+ QTest::newRow("1 pseudo-element (1)") << QString("dummy::pe:hover { color: red }") << "pe" << 1;
+ QTest::newRow("1 pseudo-element (2)") << QString("dummy::pe:hover { color: red }") << "x" << 0;
+ QTest::newRow("1 pseudo-element (2)") << QString("whatever::pe:hover { color: red }") << "pe" << 0;
+
+ QTest::newRow("1 pseudo-element (3)")
+ << QString("dummy { color: white; } dummy::pe:hover { color: red }") << "x" << 0;
+ QTest::newRow("1 pseudo-element (4)")
+ << QString("dummy { color: white; } dummy::pe:hover { color: red } dummy { x:y }") << "" << 2;
+ QTest::newRow("1 pseudo-element (5)")
+ << QString("dummy { color: white; } dummy::pe:hover { color: red }") << "pe" << 1;
+ QTest::newRow("1 pseudo-element (6)")
+ << QString("dummy { color: white; } dummy::pe:hover { color: red } dummy::pe:checked { x: y} ") << "pe" << 2;
+
+ QTest::newRow("2 pseudo-elements (1)")
+ << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} ")
+ << "" << 1;
+ QTest::newRow("2 pseudo-elements (1)")
+ << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} ")
+ << "pe1" << 1;
+ QTest::newRow("2 pseudo-elements (2)")
+ << QString("dummy { color: white; } dummy::pe1:hover { color: red } dummy::pe2:checked { x: y} ")
+ << "pe2" << 1;
+}
+
+void tst_QCssParser::pseudoElement()
+{
+ QFETCH(QString, css);
+ QFETCH(QString, pseudoElement);
+ QFETCH(int, declCount);
+
+ QDomDocument doc;
+ QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>")));
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(n);
+ QVector<QCss::Declaration> decls;
+ for (int i = 0; i < rules.count(); i++) {
+ const QCss::Selector& selector = rules.at(i).selectors.at(0);
+ if (pseudoElement.compare(selector.pseudoElement(), Qt::CaseInsensitive) != 0)
+ continue;
+ decls += rules.at(i).declarations;
+
+ }
+ QVERIFY(decls.count() == declCount);
+}
+
+void tst_QCssParser::gradient_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QString>("type");
+ QTest::addColumn<QPointF>("start");
+ QTest::addColumn<QPointF>("finalStop");
+ QTest::addColumn<int>("spread");
+ QTest::addColumn<qreal>("stop0");
+ QTest::addColumn<QColor>("color0");
+ QTest::addColumn<qreal>("stop1");
+ QTest::addColumn<QColor>("color1");
+
+ QTest::newRow("color-string") <<
+ "selection-background-color: qlineargradient(x1:1, y1:2, x2:3, y2:4, "
+ "stop:0.2 red, stop:0.5 green)" << "linear" << QPointF(1, 2) << QPointF(3, 4)
+ << 0 << qreal(0.2) << QColor("red") << qreal(0.5) << QColor("green");
+
+ QTest::newRow("color-#") <<
+ "selection-background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
+ "spread: reflect, stop:0.2 #123, stop:0.5 #456)" << "linear" << QPointF(0, 0) << QPointF(0, 1)
+ << 1 << qreal(0.2) << QColor("#123") << qreal(0.5) << QColor("#456");
+
+ QTest::newRow("color-rgb") <<
+ "selection-background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
+ "spread: reflect, stop:0.2 rgb(1, 2, 3), stop:0.5 rgba(1, 2, 3, 4))" << "linear" << QPointF(0, 0) << QPointF(0, 1)
+ << 1 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4);
+
+ QTest::newRow("color-spaces") <<
+ "selection-background-color: qlineargradient(x1: 0, y1 :0,x2:0, y2 : 1 , "
+ "spread: reflect, stop:0.2 rgb(1, 2, 3), stop: 0.5 rgba(1, 2, 3, 4))" << "linear" << QPointF(0, 0) << QPointF(0, 1)
+ << 1 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4);
+
+ QTest::newRow("conical gradient") <<
+ "selection-background-color: qconicalgradient(cx: 4, cy : 2, angle: 23, "
+ "spread: repeat, stop:0.2 rgb(1, 2, 3), stop:0.5 rgba(1, 2, 3, 4))" << "conical" << QPointF(4, 2) << QPointF()
+ << 2 << qreal(0.2) << QColor(1, 2, 3) << qreal(0.5) << QColor(1, 2, 3, 4);
+
+ /* wont pass: stop values are expected to be sorted
+ QTest::newRow("unsorted-stop") <<
+ "selection-background: lineargradient(x1:0, y1:0, x2:0, y2:1, "
+ "stop:0.5 green, stop:0.2 red)" << QPointF(0, 0) << QPointF(0, 1)
+ 0 << 0.2 << QColor("red") << 0.5 << QColor("green");
+ */
+}
+
+void tst_QCssParser::gradient()
+{
+ QFETCH(QString, css);
+ QFETCH(QString, type);
+ QFETCH(QPointF, finalStop);
+ QFETCH(QPointF, start);
+ QFETCH(int, spread);
+ QFETCH(qreal, stop0); QFETCH(QColor, color0);
+ QFETCH(qreal, stop1); QFETCH(QColor, color1);
+
+ QDomDocument doc;
+ QVERIFY(doc.setContent(QLatin1String("<!DOCTYPE test><test> <dummy/> </test>")));
+
+ css.prepend("dummy {");
+ css.append("}");
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ DomStyleSelector testSelector(doc, sheet);
+ QDomElement e = doc.documentElement().firstChildElement();
+ QCss::StyleSelector::NodePtr n;
+ n.ptr = &e;
+ QVector<QCss::StyleRule> rules = testSelector.styleRulesForNode(n);
+ QVector<QCss::Declaration> decls = rules.at(0).declarations;
+ QCss::ValueExtractor ve(decls);
+ QBrush fg, sfg;
+ QBrush sbg, abg;
+ QVERIFY(ve.extractPalette(&fg, &sfg, &sbg, &abg));
+ if (type == "linear") {
+ QVERIFY(sbg.style() == Qt::LinearGradientPattern);
+ const QLinearGradient *lg = static_cast<const QLinearGradient *>(sbg.gradient());
+ QCOMPARE(lg->start(), start);
+ QCOMPARE(lg->finalStop(), finalStop);
+ } else if (type == "conical") {
+ QVERIFY(sbg.style() == Qt::ConicalGradientPattern);
+ const QConicalGradient *cg = static_cast<const QConicalGradient *>(sbg.gradient());
+ QCOMPARE(cg->center(), start);
+ }
+ const QGradient *g = sbg.gradient();
+ QCOMPARE(g->spread(), QGradient::Spread(spread));
+ QVERIFY(g->stops().at(0).first == stop0);
+ QVERIFY(g->stops().at(0).second == color0);
+ QVERIFY(g->stops().at(1).first == stop1);
+ QVERIFY(g->stops().at(1).second == color1);
+}
+
+void tst_QCssParser::extractFontFamily_data()
+{
+ if (QFontInfo(QFont("Times New Roman")).family() != "Times New Roman")
+ QSKIP("'Times New Roman' font not found ", SkipAll);
+
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<QString>("expectedFamily");
+
+ QTest::newRow("quoted-family-name") << "font-family: 'Times New Roman'" << QString("Times New Roman");
+ QTest::newRow("unquoted-family-name") << "font-family: Times New Roman" << QString("Times New Roman");
+ QTest::newRow("unquoted-family-name2") << "font-family: Times New Roman" << QString("Times New Roman");
+ QTest::newRow("multiple") << "font-family: Times New Roman , foobar, 'baz'" << QString("Times New Roman");
+ QTest::newRow("multiple2") << "font-family: invalid, Times New Roman " << QString("Times New Roman");
+ QTest::newRow("invalid") << "font-family: invalid" << QFontInfo(QFont("invalid font")).family();
+ QTest::newRow("shorthand") << "font: 12pt Times New Roman" << QString("Times New Roman");
+ QTest::newRow("shorthand multiple quote") << "font: 12pt invalid, \"Times New Roman\" " << QString("Times New Roman");
+ QTest::newRow("shorthand multiple") << "font: 12pt invalid, Times New Roman " << QString("Times New Roman");
+ QTest::newRow("invalid spaces") << "font-family: invalid spaces, Times New Roman " << QString("Times New Roman");
+ QTest::newRow("invalid spaces quotes") << "font-family: 'invalid spaces', 'Times New Roman' " << QString("Times New Roman");
+}
+
+
+void tst_QCssParser::extractFontFamily()
+{
+ QFETCH(QString, css);
+ css.prepend("dummy {");
+ css.append("}");
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
+ sheet.styleRules.at(0) : *sheet.nameIndex.begin();
+
+ const QVector<QCss::Declaration> decls = rule.declarations;
+ QVERIFY(!decls.isEmpty());
+ QCss::ValueExtractor extractor(decls);
+
+ int adjustment = 0;
+ QFont fnt;
+ extractor.extractFont(&fnt, &adjustment);
+ QFontInfo info(fnt);
+
+#ifdef Q_WS_QPA
+ // Note, we have to QSKIP rather than QEXPECT_FAIL because font lookup is broken
+ // such that it may work or not work depending on the order in which fonts were
+ // loaded from disk
+ QSKIP("QTBUG-20986 may fail on qpa", SkipSingle);
+#endif
+
+ QTEST(info.family(), "expectedFamily");
+}
+
+void tst_QCssParser::extractBorder_data()
+{
+ QTest::addColumn<QString>("css");
+ QTest::addColumn<int>("expectedTopWidth");
+ QTest::addColumn<int>("expectedTopStyle");
+ QTest::addColumn<QColor>("expectedTopColor");
+
+ QTest::newRow("all values") << "border: 2px solid green" << 2 << (int)QCss::BorderStyle_Solid << QColor("green");
+ QTest::newRow("palette") << "border: 2px solid palette(highlight)" << 2 << (int)QCss::BorderStyle_Solid << qApp->palette().color(QPalette::Highlight);
+ QTest::newRow("just width") << "border: 2px" << 2 << (int)QCss::BorderStyle_None << QColor();
+ QTest::newRow("just style") << "border: solid" << 0 << (int)QCss::BorderStyle_Solid << QColor();
+ QTest::newRow("just color") << "border: green" << 0 << (int)QCss::BorderStyle_None << QColor("green");
+ QTest::newRow("width+style") << "border: 2px solid" << 2 << (int)QCss::BorderStyle_Solid << QColor();
+ QTest::newRow("style+color") << "border: solid green" << 0 << (int)QCss::BorderStyle_Solid << QColor("green");
+ QTest::newRow("width+color") << "border: 3px green" << 3 << (int)QCss::BorderStyle_None << QColor("green");
+ QTest::newRow("groove style") << "border: groove" << 0 << (int)QCss::BorderStyle_Groove << QColor();
+ QTest::newRow("ridge style") << "border: ridge" << 0 << (int)QCss::BorderStyle_Ridge << QColor();
+ QTest::newRow("double style") << "border: double" << 0 << (int)QCss::BorderStyle_Double << QColor();
+ QTest::newRow("inset style") << "border: inset" << 0 << (int)QCss::BorderStyle_Inset << QColor();
+ QTest::newRow("outset style") << "border: outset" << 0 << (int)QCss::BorderStyle_Outset << QColor();
+ QTest::newRow("dashed style") << "border: dashed" << 0 << (int)QCss::BorderStyle_Dashed << QColor();
+ QTest::newRow("dotted style") << "border: dotted" << 0 << (int)QCss::BorderStyle_Dotted << QColor();
+ QTest::newRow("dot-dash style") << "border: dot-dash" << 0 << (int)QCss::BorderStyle_DotDash << QColor();
+ QTest::newRow("dot-dot-dash style") << "border: dot-dot-dash" << 0 << (int)QCss::BorderStyle_DotDotDash << QColor();
+
+ QTest::newRow("top-width+color") << "border-top: 3px green" << 3 << (int)QCss::BorderStyle_None << QColor("green");
+}
+
+void tst_QCssParser::extractBorder()
+{
+ QFETCH(QString, css);
+ QFETCH(int, expectedTopWidth);
+ QFETCH(int, expectedTopStyle);
+ QFETCH(QColor, expectedTopColor);
+
+ css.prepend("dummy {");
+ css.append("}");
+
+ QCss::Parser parser(css);
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
+ sheet.styleRules.at(0) : *sheet.nameIndex.begin();
+ const QVector<QCss::Declaration> decls = rule.declarations;
+ QVERIFY(!decls.isEmpty());
+ QCss::ValueExtractor extractor(decls);
+
+ int widths[4];
+ QBrush colors[4];
+ QCss::BorderStyle styles[4];
+ QSize radii[4];
+
+ extractor.extractBorder(widths, colors, styles, radii);
+ QVERIFY(widths[QCss::TopEdge] == expectedTopWidth);
+ QVERIFY(styles[QCss::TopEdge] == expectedTopStyle);
+ QVERIFY(colors[QCss::TopEdge] == expectedTopColor);
+
+ //QTBUG-9674 : a second evaluation should give the same results
+ QVERIFY(extractor.extractBorder(widths, colors, styles, radii));
+ QVERIFY(widths[QCss::TopEdge] == expectedTopWidth);
+ QVERIFY(styles[QCss::TopEdge] == expectedTopStyle);
+ QVERIFY(colors[QCss::TopEdge] == expectedTopColor);
+}
+
+void tst_QCssParser::noTextDecoration()
+{
+ QCss::Parser parser("dummy { text-decoration: none; }");
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
+ sheet.styleRules.at(0) : *sheet.nameIndex.begin();
+ const QVector<QCss::Declaration> decls = rule.declarations;
+ QVERIFY(!decls.isEmpty());
+ QCss::ValueExtractor extractor(decls);
+
+ int adjustment = 0;
+ QFont f;
+ f.setUnderline(true);
+ f.setOverline(true);
+ f.setStrikeOut(true);
+ QVERIFY(extractor.extractFont(&f, &adjustment));
+
+ QVERIFY(!f.underline());
+ QVERIFY(!f.overline());
+ QVERIFY(!f.strikeOut());
+}
+
+void tst_QCssParser::quotedAndUnquotedIdentifiers()
+{
+ QCss::Parser parser("foo { font-style: \"italic\"; font-weight: bold }");
+ QCss::StyleSheet sheet;
+ QVERIFY(parser.parse(&sheet));
+
+ QCOMPARE(sheet.styleRules.count() + sheet.nameIndex.count(), 1);
+ QCss::StyleRule rule = (!sheet.styleRules.isEmpty()) ?
+ sheet.styleRules.at(0) : *sheet.nameIndex.begin();
+ const QVector<QCss::Declaration> decls = rule.declarations;
+ QCOMPARE(decls.size(), 2);
+
+ QCOMPARE(decls.at(0).d->values.first().type, QCss::Value::String);
+ QCOMPARE(decls.at(0).d->property, QLatin1String("font-style"));
+ QCOMPARE(decls.at(0).d->values.first().toString(), QLatin1String("italic"));
+
+ QCOMPARE(decls.at(1).d->values.first().type, QCss::Value::KnownIdentifier);
+ QCOMPARE(decls.at(1).d->property, QLatin1String("font-weight"));
+ QCOMPARE(decls.at(1).d->values.first().toString(), QLatin1String("bold"));
+}
+
+QTEST_MAIN(tst_QCssParser)
+#include "tst_qcssparser.moc"
+
diff --git a/tests/auto/gui/text/qfont/.gitignore b/tests/auto/gui/text/qfont/.gitignore
new file mode 100644
index 0000000000..61aa3df16c
--- /dev/null
+++ b/tests/auto/gui/text/qfont/.gitignore
@@ -0,0 +1 @@
+tst_qfont
diff --git a/tests/auto/gui/text/qfont/qfont.pro b/tests/auto/gui/text/qfont/qfont.pro
new file mode 100644
index 0000000000..891cb0a093
--- /dev/null
+++ b/tests/auto/gui/text/qfont/qfont.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qfont.cpp
+
+
diff --git a/tests/auto/gui/text/qfont/tst_qfont.cpp b/tests/auto/gui/text/qfont/tst_qfont.cpp
new file mode 100644
index 0000000000..a564e71e19
--- /dev/null
+++ b/tests/auto/gui/text/qfont/tst_qfont.cpp
@@ -0,0 +1,629 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qfont.h>
+#include <qfontdatabase.h>
+#include <qfontinfo.h>
+#include <qstringlist.h>
+#include <qapplication.h>
+#include <qwidget.h>
+#include <qlist.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QFont : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QFont();
+ virtual ~tst_QFont();
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void getSetCheck();
+ void exactMatch();
+ void compare();
+ void resolve();
+ void resetFont();
+ void isCopyOf();
+ void setFontRaw();
+ void italicOblique();
+ void insertAndRemoveSubstitutions();
+ void serializeSpacing();
+ void lastResortFont();
+ void styleName();
+};
+
+// Testing get/set functions
+void tst_QFont::getSetCheck()
+{
+ QFont obj1;
+ // Style QFont::style()
+ // void QFont::setStyle(Style)
+ obj1.setStyle(QFont::Style(QFont::StyleNormal));
+ QCOMPARE(QFont::Style(QFont::StyleNormal), obj1.style());
+ obj1.setStyle(QFont::Style(QFont::StyleItalic));
+ QCOMPARE(QFont::Style(QFont::StyleItalic), obj1.style());
+ obj1.setStyle(QFont::Style(QFont::StyleOblique));
+ QCOMPARE(QFont::Style(QFont::StyleOblique), obj1.style());
+
+ // StyleStrategy QFont::styleStrategy()
+ // void QFont::setStyleStrategy(StyleStrategy)
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferDefault), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferBitmap));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferBitmap), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDevice));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferDevice), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferOutline));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferOutline), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::ForceOutline));
+ QCOMPARE(QFont::StyleStrategy(QFont::ForceOutline), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferMatch));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferMatch), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferQuality));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferQuality), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferAntialias));
+ QCOMPARE(QFont::StyleStrategy(QFont::PreferAntialias), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::NoAntialias));
+ QCOMPARE(QFont::StyleStrategy(QFont::NoAntialias), obj1.styleStrategy());
+ obj1.setStyleStrategy(QFont::StyleStrategy(QFont::OpenGLCompatible));
+ QCOMPARE(QFont::StyleStrategy(QFont::OpenGLCompatible), obj1.styleStrategy());
+}
+
+tst_QFont::tst_QFont()
+{
+}
+
+tst_QFont::~tst_QFont()
+{
+
+}
+
+void tst_QFont::init()
+{
+// TODO: Add initialization code here.
+// This will be executed immediately before each test is run.
+}
+
+void tst_QFont::cleanup()
+{
+// TODO: Add cleanup code here.
+// This will be executed immediately after each test is run.
+}
+
+void tst_QFont::exactMatch()
+{
+ QFont font;
+
+ // Check if a non-existing font hasn't an exact match
+ font = QFont( "BogusFont", 33 );
+ QVERIFY( !font.exactMatch() );
+
+#ifdef Q_WS_WIN
+ QSKIP("Exact matching on windows misses a lot because of the sample chars", SkipAll);
+ return;
+#endif
+
+#ifdef Q_WS_X11
+ QVERIFY(QFont("sans").exactMatch());
+ QVERIFY(QFont("sans-serif").exactMatch());
+ QVERIFY(QFont("serif").exactMatch());
+ QVERIFY(QFont("monospace").exactMatch());
+#endif
+
+ QSKIP("This test is bogus on Unix with support for font aliases in fontconfig", SkipAll);
+ return;
+
+ QFontDatabase fdb;
+
+ QList<QFontDatabase::WritingSystem> systems = fdb.writingSystems();
+ for (int system = 0; system < systems.count(); ++system) {
+ QStringList families = fdb.families(systems[system]);
+ if (families.isEmpty())
+ return;
+
+ QStringList::ConstIterator f_it, f_end = families.end();
+ for (f_it = families.begin(); f_it != f_end; ++f_it) {
+ const QString &family = *f_it;
+ if (family.contains('['))
+ continue;
+
+ QStringList styles = fdb.styles(family);
+ QVERIFY(!styles.isEmpty());
+ QStringList::ConstIterator s_it, s_end = styles.end();
+ for (s_it = styles.begin(); s_it != s_end; ++s_it) {
+ const QString &style = *s_it;
+
+ if (fdb.isSmoothlyScalable(family, style)) {
+ // smoothly scalable font... don't need to load every pointsize
+ font = fdb.font(family, style, 12);
+ QFontInfo fontinfo(font);
+
+ if (! fontinfo.exactMatch()) {
+ // Unfortunately, this can fail, since
+ // QFontDatabase does not fill in all font
+ // properties. Check to make sure that the
+ // test didn't fail for obvious reasons
+
+ if (fontinfo.family().isEmpty()
+ && fontinfo.pointSize() == 0) {
+ // this is a box rendering engine... this can happen from
+ // time to time, especially on X11 with iso10646-1 or
+ // unknown font encodings
+ continue;
+ }
+
+#ifdef Q_WS_WIN32
+ if (font.family().startsWith("MS ") || fontinfo.family().startsWith("MS ")) {
+ /* qDebug("Family matching skipped for MS-Alias font: %s, fontinfo: %s",
+ font.family().latin1(), fontinfo.family().latin1());
+ */
+ } else
+#endif
+ {
+ if (!(font.family() == fontinfo.family()
+ || fontinfo.family().contains(font.family())
+ || fontinfo.family().isEmpty())) {
+ qDebug("Test about to fail for font: %s, fontinfo: %s",
+ font.family().toLatin1().constData(),
+ fontinfo.family().toLatin1().constData());
+ }
+ QVERIFY(font.family() == fontinfo.family()
+ || fontinfo.family().contains(font.family())
+ || fontinfo.family().isEmpty());
+ }
+ if (font.pointSize() != -1) {
+ QVERIFY(font.pointSize() == fontinfo.pointSize());
+ } else {
+ QVERIFY(font.pixelSize() == fontinfo.pixelSize());
+ }
+ QVERIFY(font.italic() == fontinfo.italic());
+ if (font.weight() != fontinfo.weight()) {
+ qDebug("font is %s", font.toString().toLatin1().constData());
+ }
+ QVERIFY(font.weight() == fontinfo.weight());
+ } else {
+ font.setFixedPitch(!fontinfo.fixedPitch());
+ QFontInfo fontinfo1(font);
+ QVERIFY( !fontinfo1.exactMatch() );
+
+ font.setFixedPitch(fontinfo.fixedPitch());
+ QFontInfo fontinfo2(font);
+ QVERIFY( fontinfo2.exactMatch() );
+ }
+ }
+#if 0
+ // ############## can only work if we have float point sizes in QFD
+ else {
+ QList<int> sizes = fdb.pointSizes(family, style);
+ QVERIFY(!sizes.isEmpty());
+ QList<int>::ConstIterator z_it, z_end = sizes.end();
+ for (z_it = sizes.begin(); z_it != z_end; ++z_it) {
+ const int size = *z_it;
+
+ // Initialize the font, and check if it is an exact match
+ font = fdb.font(family, style, size);
+ QFontInfo fontinfo(font, (QFont::Script) script);
+
+ if (! fontinfo.exactMatch()) {
+ // Unfortunately, this can fail, since
+ // QFontDatabase does not fill in all font
+ // properties. Check to make sure that the
+ // test didn't fail for obvious reasons
+
+ if (fontinfo.family().isEmpty()
+ && fontinfo.pointSize() == 0) {
+ // this is a box rendering engine... this can happen from
+ // time to time, especially on X11 with iso10646-1 or
+ // unknown font encodings
+ continue;
+ }
+
+ // no need to skip MS-fonts here it seems
+ if (!(font.family() == fontinfo.family()
+ || fontinfo.family().contains(font.family())
+ || fontinfo.family().isEmpty())) {
+ qDebug("Test about to fail for font: %s, fontinfo: %s",
+ font.family().latin1(), fontinfo.family().latin1());
+ }
+ QVERIFY(font.family() == fontinfo.family()
+ || fontinfo.family().contains(font.family())
+ || fontinfo.family().isEmpty());
+ if (font.pointSize() != -1) {
+ QVERIFY(font.pointSize() == fontinfo.pointSize());
+ } else {
+ QVERIFY(font.pixelSize() == fontinfo.pixelSize());
+ }
+ QVERIFY(font.italic() == fontinfo.italic());
+ QVERIFY(font.weight() == fontinfo.weight());
+ } else {
+ font.setFixedPitch(!fontinfo.fixedPitch());
+ QFontInfo fontinfo1(font, (QFont::Script) script);
+ QVERIFY( !fontinfo1.exactMatch() );
+
+ font.setFixedPitch(fontinfo.fixedPitch());
+ QFontInfo fontinfo2(font, (QFont::Script) script);
+ QVERIFY( fontinfo2.exactMatch() );
+ }
+ }
+ }
+#endif
+ }
+ }
+ }
+}
+
+void tst_QFont::italicOblique()
+{
+ QFontDatabase fdb;
+
+ QStringList families = fdb.families();
+ if (families.isEmpty())
+ return;
+
+ QStringList::ConstIterator f_it, f_end = families.end();
+ for (f_it = families.begin(); f_it != f_end; ++f_it) {
+
+ QString family = *f_it;
+ QStringList styles = fdb.styles(family);
+ QVERIFY(!styles.isEmpty());
+ QStringList::ConstIterator s_it, s_end = styles.end();
+ for (s_it = styles.begin(); s_it != s_end; ++s_it) {
+ QString style = *s_it;
+
+ if (fdb.isSmoothlyScalable(family, style)) {
+ if (style.contains("Oblique")) {
+ style.replace("Oblique", "Italic");
+ } else if (style.contains("Italic")) {
+ style.replace("Italic", "Oblique");
+ } else {
+ continue;
+ }
+ QFont f = fdb.font(family, style, 12);
+ QVERIFY(f.italic());
+ }
+ }
+ }
+}
+
+void tst_QFont::compare()
+{
+ QFont font;
+ {
+ QFont font2 = font;
+ font2.setPointSize( 24 );
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ }
+ {
+ QFont font2 = font;
+ font2.setPixelSize( 24 );
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ }
+
+ font.setPointSize(12);
+ font.setItalic(false);
+ font.setWeight(QFont::Normal);
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setOverline(false);
+ {
+ QFont font2 = font;
+ font2.setPointSize( 24 );
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ }
+ {
+ QFont font2 = font;
+ font2.setPixelSize( 24 );
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ }
+ {
+ QFont font2 = font;
+
+ font2.setItalic(true);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font2.setItalic(false);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+
+ font2.setWeight(QFont::Bold);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font2.setWeight(QFont::Normal);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+
+ font.setUnderline(true);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font.setUnderline(false);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+
+ font.setStrikeOut(true);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font.setStrikeOut(false);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+
+ font.setOverline(true);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font.setOverline(false);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+
+ font.setCapitalization(QFont::SmallCaps);
+ QVERIFY( font != font2 );
+ QCOMPARE(font < font2,!(font2 < font));
+ font.setCapitalization(QFont::MixedCase);
+ QVERIFY( font == font2 );
+ QVERIFY(!(font < font2));
+ }
+
+#if defined(Q_WS_X11)
+ {
+ QFont font1, font2;
+ font1.setRawName("-Adobe-Helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1");
+ font2.setRawName("-Adobe-Helvetica-medium-r-normal--24-240-75-75-p-130-iso8859-1");
+ QVERIFY(font1 != font2);
+ }
+#endif
+}
+
+void tst_QFont::resolve()
+{
+ QFont font;
+ font.setPointSize(font.pointSize() * 2);
+ font.setItalic(false);
+ font.setWeight(QFont::Normal);
+ font.setUnderline(false);
+ font.setStrikeOut(false);
+ font.setOverline(false);
+ font.setStretch(QFont::Unstretched);
+
+ QFont font1;
+ font1.setWeight(QFont::Bold);
+ QFont font2 = font1.resolve(font);
+
+ QVERIFY(font2.weight() == font1.weight());
+
+ QVERIFY(font2.pointSize() == font.pointSize());
+ QVERIFY(font2.italic() == font.italic());
+ QVERIFY(font2.underline() == font.underline());
+ QVERIFY(font2.overline() == font.overline());
+ QVERIFY(font2.strikeOut() == font.strikeOut());
+ QVERIFY(font2.stretch() == font.stretch());
+
+ QFont font3;
+ font3.setStretch(QFont::UltraCondensed);
+ QFont font4 = font3.resolve(font1).resolve(font);
+
+ QVERIFY(font4.stretch() == font3.stretch());
+
+ QVERIFY(font4.weight() == font.weight());
+ QVERIFY(font4.pointSize() == font.pointSize());
+ QVERIFY(font4.italic() == font.italic());
+ QVERIFY(font4.underline() == font.underline());
+ QVERIFY(font4.overline() == font.overline());
+ QVERIFY(font4.strikeOut() == font.strikeOut());
+
+
+ QFont f1,f2,f3;
+ f2.setPointSize(45);
+ f3.setPointSize(55);
+
+ QFont f4 = f1.resolve(f2);
+ QCOMPARE(f4.pointSize(), 45);
+ f4 = f4.resolve(f3);
+ QCOMPARE(f4.pointSize(), 55);
+}
+
+void tst_QFont::resetFont()
+{
+ QWidget parent;
+ QFont parentFont = parent.font();
+ parentFont.setPointSize(parentFont.pointSize() + 2);
+ parent.setFont(parentFont);
+
+ QWidget *child = new QWidget(&parent);
+
+ QFont childFont = child->font();
+ childFont.setBold(!childFont.bold());
+ child->setFont(childFont);
+
+ QVERIFY(parentFont.resolve() != 0);
+ QVERIFY(childFont.resolve() != 0);
+ QVERIFY(childFont != parentFont);
+
+ child->setFont(QFont()); // reset font
+
+ QVERIFY(child->font().resolve() == 0);
+ QVERIFY(child->font().pointSize() == parent.font().pointSize());
+ QVERIFY(parent.font().resolve() != 0);
+}
+
+void tst_QFont::isCopyOf()
+{
+ QFont font;
+ QVERIFY(font.isCopyOf(QApplication::font()));
+
+ QFont font2("bogusfont", 23);
+ QVERIFY(! font2.isCopyOf(QApplication::font()));
+
+ QFont font3 = font;
+ QVERIFY(font3.isCopyOf(font));
+
+ font3.setPointSize(256);
+ QVERIFY(!font3.isCopyOf(font));
+ font3.setPointSize(font.pointSize());
+ QVERIFY(!font3.isCopyOf(font));
+}
+
+void tst_QFont::setFontRaw()
+{
+#ifndef Q_WS_X11
+ QSKIP("Only tested on X11", SkipAll);
+#else
+ QFont f;
+ f.setRawName("-*-fixed-bold-r-normal--0-0-*-*-*-0-iso8859-1");
+// qDebug("font family: %s", f.family().utf8());
+ QFontDatabase fdb;
+ QStringList families = fdb.families();
+ bool found = false;
+ for (int i = 0; i < families.size(); ++i) {
+ QString str = families.at(i);
+ if (str.contains('['))
+ str = str.left(str.indexOf('[')-1);
+ if (str.toLower() == "fixed")
+ found = true;
+ }
+ if (!found) {
+ QSKIP("Fixed font not available.", SkipSingle);
+ }
+ QCOMPARE(QFontInfo(f).family().left(5).toLower(), QString("fixed"));
+#endif
+}
+
+void tst_QFont::insertAndRemoveSubstitutions()
+{
+ QFont::removeSubstitution("BogusFontFamily");
+ // make sure it is empty before we start
+ QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
+ QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
+
+ // inserting Foo
+ QFont::insertSubstitution("BogusFontFamily", "Foo");
+ QCOMPARE(QFont::substitutes("BogusFontFamily").count(), 1);
+ QCOMPARE(QFont::substitutes("bogusfontfamily").count(), 1);
+
+ // inserting Bar and Baz
+ QStringList moreFonts;
+ moreFonts << "Bar" << "Baz";
+ QFont::insertSubstitutions("BogusFontFamily", moreFonts);
+ QCOMPARE(QFont::substitutes("BogusFontFamily").count(), 3);
+ QCOMPARE(QFont::substitutes("bogusfontfamily").count(), 3);
+
+ QFont::removeSubstitution("BogusFontFamily");
+ // make sure it is empty again
+ QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
+ QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
+}
+
+
+static QFont copyFont(const QFont &font1) // copy using a QDataStream
+{
+ QBuffer buffer;
+ buffer.open(QIODevice::WriteOnly);
+ QDataStream ds(&buffer);
+ ds << font1;
+ buffer.close();
+
+ buffer.open(QIODevice::ReadOnly);
+ QFont font2;
+ ds >> font2;
+ return font2;
+}
+
+void tst_QFont::serializeSpacing()
+{
+ QFont font;
+ QCOMPARE(font.letterSpacing(), 0.);
+ QCOMPARE(font.wordSpacing(), 0.);
+
+ font.setLetterSpacing(QFont::AbsoluteSpacing, 105);
+ QCOMPARE(font.letterSpacing(), 105.);
+ QCOMPARE(font.letterSpacingType(), QFont::AbsoluteSpacing);
+ QCOMPARE(font.wordSpacing(), 0.);
+ QFont font2 = copyFont(font);
+ QCOMPARE(font2.letterSpacing(), 105.);
+ QCOMPARE(font2.letterSpacingType(), QFont::AbsoluteSpacing);
+ QCOMPARE(font2.wordSpacing(), 0.);
+
+ font.setWordSpacing(50.0);
+ QCOMPARE(font.letterSpacing(), 105.);
+ QCOMPARE(font.wordSpacing(), 50.);
+
+ QFont font3 = copyFont(font);
+ QCOMPARE(font3.letterSpacing(), 105.);
+ QCOMPARE(font3.letterSpacingType(), QFont::AbsoluteSpacing);
+ QCOMPARE(font3.wordSpacing(), 50.);
+}
+
+void tst_QFont::lastResortFont()
+{
+#if defined(Q_WS_QWS) || defined(Q_WS_QPA)
+ QSKIP("QFont::lastResortFont() may abort with qFatal() on QWS/QPA", SkipAll);
+ // ...if absolutely no font is found. Just as ducumented for QFont::lastResortFont().
+ // This happens on our CI machines which run QWS autotests.
+#endif
+ QFont font;
+ QVERIFY(!font.lastResortFont().isEmpty());
+}
+
+void tst_QFont::styleName()
+{
+#if !defined(Q_WS_MAC)
+ QSKIP("Only tested on Mac", SkipAll);
+#else
+ QFont font("Helvetica Neue");
+ font.setStyleName("UltraLight");
+
+ QCOMPARE(QFontInfo(font).styleName(), QString("UltraLight"));
+#endif
+}
+
+QTEST_MAIN(tst_QFont)
+#include "tst_qfont.moc"
diff --git a/tests/auto/gui/text/qfontdatabase/.gitignore b/tests/auto/gui/text/qfontdatabase/.gitignore
new file mode 100644
index 0000000000..65edf9b279
--- /dev/null
+++ b/tests/auto/gui/text/qfontdatabase/.gitignore
@@ -0,0 +1 @@
+tst_qfontdatabase
diff --git a/tests/auto/gui/text/qfontdatabase/FreeMono.ttf b/tests/auto/gui/text/qfontdatabase/FreeMono.ttf
new file mode 100644
index 0000000000..d7ce52ddc7
--- /dev/null
+++ b/tests/auto/gui/text/qfontdatabase/FreeMono.ttf
Binary files differ
diff --git a/tests/auto/gui/text/qfontdatabase/qfontdatabase.pro b/tests/auto/gui/text/qfontdatabase/qfontdatabase.pro
new file mode 100644
index 0000000000..e7dfc3c73d
--- /dev/null
+++ b/tests/auto/gui/text/qfontdatabase/qfontdatabase.pro
@@ -0,0 +1,10 @@
+load(qttest_p4)
+SOURCES += tst_qfontdatabase.cpp
+!symbian:DEFINES += SRCDIR=\\\"$$PWD\\\"
+
+wince*|symbian {
+ additionalFiles.files = FreeMono.ttf
+ additionalFiles.path = .
+ DEPLOYMENT += additionalFiles
+}
+
diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
new file mode 100644
index 0000000000..1df61d0a06
--- /dev/null
+++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
@@ -0,0 +1,294 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qfontdatabase.h>
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR "."
+#endif
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QFontDatabase : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QFontDatabase();
+ virtual ~tst_QFontDatabase();
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void styles_data();
+ void styles();
+
+ void fixedPitch_data();
+ void fixedPitch();
+
+#ifdef Q_WS_MAC
+ void trickyFonts_data();
+ void trickyFonts();
+#endif
+
+ void widthTwoTimes_data();
+ void widthTwoTimes();
+
+ void addAppFont_data();
+ void addAppFont();
+};
+
+tst_QFontDatabase::tst_QFontDatabase()
+{
+#ifndef Q_OS_IRIX
+ QDir::setCurrent(SRCDIR);
+#endif
+}
+
+tst_QFontDatabase::~tst_QFontDatabase()
+{
+
+}
+
+void tst_QFontDatabase::init()
+{
+// TODO: Add initialization code here.
+// This will be executed immediately before each test is run.
+}
+
+void tst_QFontDatabase::cleanup()
+{
+// TODO: Add cleanup code here.
+// This will be executed immediately after each test is run.
+}
+
+void tst_QFontDatabase::styles_data()
+{
+ QTest::addColumn<QString>("font");
+
+ QTest::newRow( "data0" ) << QString( "Times New Roman" );
+}
+
+void tst_QFontDatabase::styles()
+{
+ QFETCH( QString, font );
+
+ QFontDatabase fdb;
+ QStringList styles = fdb.styles( font );
+ QStringList::Iterator it = styles.begin();
+ while ( it != styles.end() ) {
+ QString style = *it;
+ QString trimmed = style.trimmed();
+ ++it;
+
+ QCOMPARE( style, trimmed );
+ }
+}
+
+void tst_QFontDatabase::fixedPitch_data()
+{
+ QTest::addColumn<QString>("font");
+ QTest::addColumn<bool>("fixedPitch");
+
+ QTest::newRow( "Times New Roman" ) << QString( "Times New Roman" ) << false;
+ QTest::newRow( "Arial" ) << QString( "Arial" ) << false;
+ QTest::newRow( "Andale Mono" ) << QString( "Andale Mono" ) << true;
+ QTest::newRow( "Courier" ) << QString( "Courier" ) << true;
+ QTest::newRow( "Courier New" ) << QString( "Courier New" ) << true;
+#ifndef Q_WS_MAC
+ QTest::newRow( "Script" ) << QString( "Script" ) << false;
+ QTest::newRow( "Lucida Console" ) << QString( "Lucida Console" ) << true;
+#else
+ QTest::newRow( "Menlo" ) << QString( "Menlo" ) << true;
+ QTest::newRow( "Monaco" ) << QString( "Monaco" ) << true;
+#endif
+}
+
+void tst_QFontDatabase::fixedPitch()
+{
+#ifdef Q_WS_QWS
+ QSKIP("fixedPitch not implemented for Qtopia Core", SkipAll);
+#endif
+ QFETCH(QString, font);
+ QFETCH(bool, fixedPitch);
+
+ QFontDatabase fdb;
+ if (!fdb.families().contains(font))
+ QSKIP( "Font not installed", SkipSingle);
+
+#ifdef Q_WS_QPA
+ if (fixedPitch) {
+ // fixedPitch() never returns true on qpa
+ QEXPECT_FAIL("", "QTBUG-20754 fails on qpa", Abort);
+ }
+#endif
+
+ QCOMPARE(fdb.isFixedPitch(font), fixedPitch);
+
+ QFont qfont(font);
+ QFontInfo fi(qfont);
+ QCOMPARE(fi.fixedPitch(), fixedPitch);
+}
+
+#ifdef Q_WS_MAC
+void tst_QFontDatabase::trickyFonts_data()
+{
+ QTest::addColumn<QString>("font");
+
+ QTest::newRow( "Geeza Pro" ) << QString( "Geeza Pro" );
+}
+
+void tst_QFontDatabase::trickyFonts()
+{
+ QFETCH(QString, font);
+
+ QFontDatabase fdb;
+ if (!fdb.families().contains(font))
+ QSKIP( "Font not installed", SkipSingle);
+
+ QFont qfont(font);
+ QFontInfo fi(qfont);
+ QCOMPARE(fi.family(), font);
+}
+#endif
+
+void tst_QFontDatabase::widthTwoTimes_data()
+{
+ QTest::addColumn<QString>("font");
+ QTest::addColumn<int>("pixelSize");
+ QTest::addColumn<QString>("text");
+
+ QTest::newRow("Arial") << QString("Arial") << 1000 << QString("Some text");
+}
+
+void tst_QFontDatabase::widthTwoTimes()
+{
+ QFETCH(QString, font);
+ QFETCH(int, pixelSize);
+ QFETCH(QString, text);
+
+ QFont f;
+ f.setFamily(font);
+ f.setPixelSize(pixelSize);
+
+ QFontMetrics fm(f);
+ int w1 = fm.charWidth(text, 0);
+ int w2 = fm.charWidth(text, 0);
+
+ QCOMPARE(w1, w2);
+}
+
+void tst_QFontDatabase::addAppFont_data()
+{
+ QTest::addColumn<bool>("useMemoryFont");
+ QTest::newRow("font file") << false;
+ QTest::newRow("memory font") << true;
+}
+
+void tst_QFontDatabase::addAppFont()
+{
+ QFETCH(bool, useMemoryFont);
+ QSignalSpy fontDbChangedSpy(QApplication::instance(), SIGNAL(fontDatabaseChanged()));
+
+ QFontDatabase db;
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("memory font", "QTBUG-20754 fails on qpa", Abort);
+#endif
+
+ const QStringList oldFamilies = db.families();
+ QVERIFY(!oldFamilies.isEmpty());
+
+ fontDbChangedSpy.clear();
+
+ int id;
+ if (useMemoryFont) {
+ QFile fontfile("FreeMono.ttf");
+ fontfile.open(QIODevice::ReadOnly);
+ QByteArray fontdata = fontfile.readAll();
+ QVERIFY(!fontdata.isEmpty());
+ id = QFontDatabase::addApplicationFontFromData(fontdata);
+ } else {
+ id = QFontDatabase::addApplicationFont("FreeMono.ttf");
+ }
+#if defined(Q_OS_HPUX) && defined(QT_NO_FONTCONFIG)
+ // Documentation says that X11 systems that don't have fontconfig
+ // don't support application fonts.
+ QCOMPARE(id, -1);
+ return;
+#endif
+ QCOMPARE(fontDbChangedSpy.count(), 1);
+// addApplicationFont is supported on Mac, don't skip the test if it breaks.
+#ifndef Q_WS_MAC
+ if (id == -1) {
+ QSKIP("Skip the test since app fonts are not supported on this system", SkipSingle);
+ return;
+ }
+#endif
+
+ const QStringList addedFamilies = QFontDatabase::applicationFontFamilies(id);
+ QVERIFY(!addedFamilies.isEmpty());
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("font file", "QTBUG-20754 fails on qpa", Abort);
+#endif
+
+ const QStringList newFamilies = db.families();
+ QVERIFY(!newFamilies.isEmpty());
+ QVERIFY(newFamilies.count() >= oldFamilies.count());
+
+ for (int i = 0; i < addedFamilies.count(); ++i)
+ QVERIFY(newFamilies.contains(addedFamilies.at(i)));
+
+ QVERIFY(QFontDatabase::removeApplicationFont(id));
+ QCOMPARE(fontDbChangedSpy.count(), 2);
+
+ QVERIFY(db.families() == oldFamilies);
+}
+
+QTEST_MAIN(tst_QFontDatabase)
+#include "tst_qfontdatabase.moc"
diff --git a/tests/auto/gui/text/qfontmetrics/.gitignore b/tests/auto/gui/text/qfontmetrics/.gitignore
new file mode 100644
index 0000000000..0b428672a3
--- /dev/null
+++ b/tests/auto/gui/text/qfontmetrics/.gitignore
@@ -0,0 +1 @@
+tst_qfontmetrics
diff --git a/tests/auto/gui/text/qfontmetrics/qfontmetrics.pro b/tests/auto/gui/text/qfontmetrics/qfontmetrics.pro
new file mode 100644
index 0000000000..c0dc1abbe6
--- /dev/null
+++ b/tests/auto/gui/text/qfontmetrics/qfontmetrics.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qfontmetrics.cpp
+RESOURCES += testfont.qrc
+
diff --git a/tests/auto/gui/text/qfontmetrics/testfont.qrc b/tests/auto/gui/text/qfontmetrics/testfont.qrc
new file mode 100644
index 0000000000..bc0c0b0959
--- /dev/null
+++ b/tests/auto/gui/text/qfontmetrics/testfont.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/fonts">
+ <file>ucs4font.ttf</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp
new file mode 100644
index 0000000000..982cfc11d9
--- /dev/null
+++ b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qfontdatabase.h>
+#include <qstringlist.h>
+#include <qlist.h>
+
+
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QFontMetrics : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QFontMetrics();
+ virtual ~tst_QFontMetrics();
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void same();
+ void metrics();
+ void boundingRect();
+ void elidedText_data();
+ void elidedText();
+ void veryNarrowElidedText();
+ void averageCharWidth();
+ void bypassShaping();
+ void elidedMultiLength();
+ void elidedMultiLengthF();
+ void inFontUcs4();
+ void lineWidth();
+};
+
+tst_QFontMetrics::tst_QFontMetrics()
+
+{
+}
+
+tst_QFontMetrics::~tst_QFontMetrics()
+{
+
+}
+
+void tst_QFontMetrics::init()
+{
+}
+
+void tst_QFontMetrics::cleanup()
+{
+}
+
+void tst_QFontMetrics::same()
+{
+ QFont font;
+ font.setBold(true);
+ QFontMetrics fm(font);
+ const QString text = QLatin1String("Some stupid STRING");
+ QCOMPARE(fm.size(0, text), fm.size(0, text)) ;
+
+ {
+ QImage image;
+ QFontMetrics fm2(font, &image);
+ QString text2 = QLatin1String("Foo Foo");
+ QCOMPARE(fm2.size(0, text2), fm2.size(0, text2)); //used to crash
+ }
+
+ {
+ QImage image;
+ QFontMetricsF fm3(font, &image);
+ QString text2 = QLatin1String("Foo Foo");
+ QCOMPARE(fm3.size(0, text2), fm3.size(0, text2)); //used to crash
+ }
+}
+
+
+void tst_QFontMetrics::metrics()
+{
+ QFont font;
+ QFontDatabase fdb;
+
+ // Query the QFontDatabase for a specific font, store the
+ // result in family, style and size.
+ QStringList families = fdb.families();
+ if (families.isEmpty())
+ return;
+
+ QStringList::ConstIterator f_it, f_end = families.end();
+ for (f_it = families.begin(); f_it != f_end; ++f_it) {
+ const QString &family = *f_it;
+
+ QStringList styles = fdb.styles(family);
+ QStringList::ConstIterator s_it, s_end = styles.end();
+ for (s_it = styles.begin(); s_it != s_end; ++s_it) {
+ const QString &style = *s_it;
+
+ if (fdb.isSmoothlyScalable(family, style)) {
+ // smoothly scalable font... don't need to load every pointsize
+ font = fdb.font(family, style, 12);
+
+ QFontMetrics fontmetrics(font);
+ QCOMPARE(fontmetrics.ascent() + fontmetrics.descent() + 1,
+ fontmetrics.height());
+
+ QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
+ fontmetrics.lineSpacing());
+ } else {
+ QList<int> sizes = fdb.pointSizes(family, style);
+ QVERIFY(!sizes.isEmpty());
+ QList<int>::ConstIterator z_it, z_end = sizes.end();
+ for (z_it = sizes.begin(); z_it != z_end; ++z_it) {
+ const int size = *z_it;
+
+ // Initialize the font, and check if it is an exact match
+ font = fdb.font(family, style, size);
+
+ QFontMetrics fontmetrics(font);
+ QCOMPARE(fontmetrics.ascent() + fontmetrics.descent() + 1,
+ fontmetrics.height());
+ QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
+ fontmetrics.lineSpacing());
+ }
+ }
+ }
+ }
+}
+
+void tst_QFontMetrics::boundingRect()
+{
+ QFont f;
+ f.setPointSize(24);
+ QFontMetrics fm(f);
+ QRect r = fm.boundingRect(QChar('Y'));
+ QVERIFY(r.top() < 0);
+ r = fm.boundingRect(QString("Y"));
+ QVERIFY(r.top() < 0);
+}
+
+void tst_QFontMetrics::elidedText_data()
+{
+ QTest::addColumn<QFont>("font");
+ QTest::addColumn<QString>("text");
+
+ QTest::newRow("helvetica hello") << QFont("helvetica",10) << QString("hello") ;
+ QTest::newRow("helvetica hello &Bye") << QFont("helvetica",10) << QString("hello&Bye") ;
+}
+
+
+void tst_QFontMetrics::elidedText()
+{
+ QFETCH(QFont, font);
+ QFETCH(QString, text);
+ QFontMetrics fm(font);
+ int w = fm.width(text);
+ QString newtext = fm.elidedText(text,Qt::ElideRight,w+1, 0);
+ QCOMPARE(text,newtext); // should not elide
+ newtext = fm.elidedText(text,Qt::ElideRight,w-1, 0);
+ QVERIFY(text != newtext); // should elide
+}
+
+void tst_QFontMetrics::veryNarrowElidedText()
+{
+ QFont f;
+ QFontMetrics fm(f);
+ QString text("hello");
+ QCOMPARE(fm.elidedText(text, Qt::ElideRight, 0), QString());
+}
+
+void tst_QFontMetrics::averageCharWidth()
+{
+ QFont f;
+ QFontMetrics fm(f);
+ QVERIFY(fm.averageCharWidth() != 0);
+ QFontMetricsF fmf(f);
+ QVERIFY(fmf.averageCharWidth() != 0);
+}
+
+void tst_QFontMetrics::bypassShaping()
+{
+ QFont f;
+ f.setStyleStrategy(QFont::ForceIntegerMetrics);
+ QFontMetrics fm(f);
+ QString text = " A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z";
+ int textWidth = fm.width(text, -1, Qt::TextBypassShaping);
+ QVERIFY(textWidth != 0);
+ int charsWidth = 0;
+ for (int i = 0; i < text.size(); ++i)
+ charsWidth += fm.width(text[i]);
+ // This assertion is needed in QtWebKit's WebCore::Font::offsetForPositionForSimpleText
+ QCOMPARE(textWidth, charsWidth);
+}
+
+template<class FontMetrics> void elidedMultiLength_helper()
+{
+ QString text1 = "Long Text 1\x9cShorter\x9csmall";
+ QString text1_long = "Long Text 1";
+ QString text1_short = "Shorter";
+ QString text1_small = "small";
+ FontMetrics fm = FontMetrics(QFont());
+ int width_long = fm.size(0, text1_long).width();
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, 8000), text1_long);
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long + 1), text1_long);
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long - 1), text1_short);
+ int width_short = fm.size(0, text1_short).width();
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short + 1), text1_short);
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short - 1), text1_small);
+
+ // Not even wide enough for "small" - should use ellipsis
+ QChar ellipsisChar(0x2026);
+ QString text1_el = QString::fromLatin1("s") + ellipsisChar;
+ int width_small = fm.width(text1_el);
+ QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_small + 1), text1_el);
+}
+
+void tst_QFontMetrics::elidedMultiLength()
+{
+ elidedMultiLength_helper<QFontMetrics>();
+}
+
+void tst_QFontMetrics::elidedMultiLengthF()
+{
+ elidedMultiLength_helper<QFontMetricsF>();
+}
+
+void tst_QFontMetrics::inFontUcs4()
+{
+ int id = QFontDatabase::addApplicationFont(":/fonts/ucs4font.ttf");
+ QVERIFY(id >= 0);
+
+ QFont font("QtTestUcs4");
+ {
+ QFontMetrics fm(font);
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20759 fails for qpa", Continue);
+#endif
+
+ QVERIFY(fm.inFontUcs4(0x1D7FF));
+ }
+
+ {
+ QFontMetricsF fm(font);
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20759 fails for qpa", Continue);
+#endif
+
+ QVERIFY(fm.inFontUcs4(0x1D7FF));
+ }
+
+ QFontDatabase::removeApplicationFont(id);
+}
+
+void tst_QFontMetrics::lineWidth()
+{
+ // QTBUG-13009, QTBUG-13011
+ QFont smallFont;
+ smallFont.setPointSize(8);
+ smallFont.setWeight(QFont::Light);
+ const QFontMetrics smallFontMetrics(smallFont);
+
+ QFont bigFont;
+ bigFont.setPointSize(40);
+ bigFont.setWeight(QFont::Black);
+ const QFontMetrics bigFontMetrics(bigFont);
+
+ QVERIFY(smallFontMetrics.lineWidth() >= 1);
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20759 fails for qpa", Continue);
+#endif
+
+ QVERIFY(smallFontMetrics.lineWidth() < bigFontMetrics.lineWidth());
+}
+
+QTEST_MAIN(tst_QFontMetrics)
+#include "tst_qfontmetrics.moc"
diff --git a/tests/auto/gui/text/qfontmetrics/ucs4font.ttf b/tests/auto/gui/text/qfontmetrics/ucs4font.ttf
new file mode 100644
index 0000000000..31b6997779
--- /dev/null
+++ b/tests/auto/gui/text/qfontmetrics/ucs4font.ttf
Binary files differ
diff --git a/tests/auto/gui/text/qglyphrun/qglyphrun.pro b/tests/auto/gui/text/qglyphrun/qglyphrun.pro
new file mode 100644
index 0000000000..480ad5b9a4
--- /dev/null
+++ b/tests/auto/gui/text/qglyphrun/qglyphrun.pro
@@ -0,0 +1,11 @@
+load(qttest_p4)
+QT = core gui
+
+SOURCES += \
+ tst_qglyphrun.cpp
+
+wince*|symbian*: {
+ DEFINES += SRCDIR=\\\"\\\"
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD/\\\"
+}
diff --git a/tests/auto/gui/text/qglyphrun/test.ttf b/tests/auto/gui/text/qglyphrun/test.ttf
new file mode 100644
index 0000000000..9043a576ef
--- /dev/null
+++ b/tests/auto/gui/text/qglyphrun/test.ttf
Binary files differ
diff --git a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp
new file mode 100644
index 0000000000..e84915428f
--- /dev/null
+++ b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp
@@ -0,0 +1,679 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <qglyphrun.h>
+#include <qpainter.h>
+#include <qtextlayout.h>
+#include <qfontdatabase.h>
+
+// #define DEBUG_SAVE_IMAGE
+
+class tst_QGlyphRun: public QObject
+{
+ Q_OBJECT
+
+#if !defined(QT_NO_RAWFONT)
+private slots:
+ void initTestCase();
+ void init();
+ void cleanupTestCase();
+
+ void constructionAndDestruction();
+ void copyConstructor();
+ void assignment();
+ void equalsOperator_data();
+ void equalsOperator();
+ void textLayoutGlyphIndexes();
+ void drawExistingGlyphs();
+ void drawNonExistentGlyphs();
+ void drawMultiScriptText1();
+ void drawMultiScriptText2();
+ void drawStruckOutText();
+ void drawOverlinedText();
+ void drawUnderlinedText();
+ void drawRightToLeft();
+ void detach();
+ void setRawData();
+ void setRawDataAndGetAsVector();
+
+private:
+ int m_testFontId;
+ QFont m_testFont;
+ bool m_testFont_ok;
+#endif // QT_NO_RAWFONT
+
+};
+
+#if !defined(QT_NO_RAWFONT)
+
+Q_DECLARE_METATYPE(QGlyphRun);
+
+void tst_QGlyphRun::initTestCase()
+{
+ m_testFont_ok = false;
+
+ m_testFontId = QFontDatabase::addApplicationFont(SRCDIR "test.ttf");
+ QVERIFY(m_testFontId >= 0);
+
+ m_testFont = QFont("QtsSpecialTestFont");
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20760 fails on qpa", Abort);
+#endif
+
+ QCOMPARE(QFontInfo(m_testFont).family(), QString::fromLatin1("QtsSpecialTestFont"));
+
+ m_testFont_ok = true;
+}
+
+void tst_QGlyphRun::init()
+{
+ if (!m_testFont_ok) {
+ QSKIP("Test font is not working correctly", SkipAll);
+ }
+}
+
+void tst_QGlyphRun::cleanupTestCase()
+{
+ QFontDatabase::removeApplicationFont(m_testFontId);
+}
+
+void tst_QGlyphRun::constructionAndDestruction()
+{
+ QGlyphRun glyphIndexes;
+}
+
+static QGlyphRun make_dummy_indexes()
+{
+ QGlyphRun glyphs;
+
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+ QFont font;
+ font.setPointSize(18);
+
+ glyphIndexes.append(1);
+ glyphIndexes.append(2);
+ glyphIndexes.append(3);
+
+ positions.append(QPointF(1, 2));
+ positions.append(QPointF(3, 4));
+ positions.append(QPointF(5, 6));
+
+ glyphs.setRawFont(QRawFont::fromFont(font));
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+
+ return glyphs;
+}
+
+void tst_QGlyphRun::copyConstructor()
+{
+ QGlyphRun glyphs;
+
+ {
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+ QFont font;
+ font.setPointSize(18);
+
+ glyphIndexes.append(1);
+ glyphIndexes.append(2);
+ glyphIndexes.append(3);
+
+ positions.append(QPointF(1, 2));
+ positions.append(QPointF(3, 4));
+ positions.append(QPointF(5, 6));
+
+ glyphs.setRawFont(QRawFont::fromFont(font));
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+ }
+
+ QGlyphRun otherGlyphs(glyphs);
+ QCOMPARE(otherGlyphs.rawFont(), glyphs.rawFont());
+ QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
+ QCOMPARE(glyphs.positions(), otherGlyphs.positions());
+}
+
+void tst_QGlyphRun::assignment()
+{
+ QGlyphRun glyphs(make_dummy_indexes());
+
+ QGlyphRun otherGlyphs = glyphs;
+ QCOMPARE(otherGlyphs.rawFont(), glyphs.rawFont());
+ QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
+ QCOMPARE(glyphs.positions(), otherGlyphs.positions());
+}
+
+void tst_QGlyphRun::equalsOperator_data()
+{
+ QTest::addColumn<QGlyphRun>("one");
+ QTest::addColumn<QGlyphRun>("two");
+ QTest::addColumn<bool>("equals");
+
+ QGlyphRun one(make_dummy_indexes());
+ QGlyphRun two(make_dummy_indexes());
+
+ QTest::newRow("Identical") << one << two << true;
+
+ {
+ QGlyphRun busted(two);
+
+ QVector<QPointF> positions = busted.positions();
+ positions[2] += QPointF(1, 1);
+ busted.setPositions(positions);
+
+
+ QTest::newRow("Different positions") << one << busted << false;
+ }
+
+ {
+ QGlyphRun busted(two);
+
+ QFont font;
+ font.setPixelSize(busted.rawFont().pixelSize() * 2);
+ busted.setRawFont(QRawFont::fromFont(font));
+
+ QTest::newRow("Different fonts") << one << busted << false;
+ }
+
+ {
+ QGlyphRun busted(two);
+
+ QVector<quint32> glyphIndexes = busted.glyphIndexes();
+ glyphIndexes[2] += 1;
+ busted.setGlyphIndexes(glyphIndexes);
+
+ QTest::newRow("Different glyph indexes") << one << busted << false;
+ }
+
+}
+
+void tst_QGlyphRun::equalsOperator()
+{
+ QFETCH(QGlyphRun, one);
+ QFETCH(QGlyphRun, two);
+ QFETCH(bool, equals);
+
+ QCOMPARE(one == two, equals);
+ QCOMPARE(one != two, !equals);
+}
+
+
+void tst_QGlyphRun::textLayoutGlyphIndexes()
+{
+ QString s;
+ s.append(QLatin1Char('A'));
+ s.append(QChar(0xe000));
+
+ QTextLayout layout(s);
+ layout.setFont(m_testFont);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QList<QGlyphRun> listOfGlyphs = layout.glyphRuns();
+
+ QCOMPARE(listOfGlyphs.size(), 1);
+
+ QGlyphRun glyphs = listOfGlyphs.at(0);
+
+ QCOMPARE(glyphs.glyphIndexes().size(), 2);
+ QCOMPARE(glyphs.glyphIndexes().at(0), quint32(2));
+ QCOMPARE(glyphs.glyphIndexes().at(1), quint32(1));
+}
+
+void tst_QGlyphRun::drawExistingGlyphs()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s;
+ s.append(QLatin1Char('A'));
+ s.append(QChar(0xe000));
+
+ QTextLayout layout(s);
+ layout.setFont(m_testFont);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphRun glyphs = layout.glyphRuns().size() > 0
+ ? layout.glyphRuns().at(0)
+ : QGlyphRun();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawExistingGlyphs_textLayoutDraw.png");
+ drawGlyphs.save("drawExistingGlyphs_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphRun::setRawData()
+{
+ QGlyphRun glyphRun;
+ glyphRun.setRawFont(QRawFont::fromFont(m_testFont));
+ glyphRun.setGlyphIndexes(QVector<quint32>() << 2 << 2 << 2);
+ glyphRun.setPositions(QVector<QPointF>() << QPointF(2, 3) << QPointF(20, 3) << QPointF(10, 20));
+
+ QPixmap baseline(100, 50);
+ baseline.fill(Qt::white);
+ {
+ QPainter p(&baseline);
+ p.drawGlyphRun(QPointF(3, 2), glyphRun);
+ }
+
+ QGlyphRun baselineCopied = glyphRun;
+
+ quint32 glyphIndexArray[3] = { 2, 2, 2 };
+ QPointF glyphPositionArray[3] = { QPointF(2, 3), QPointF(20, 3), QPointF(10, 20) };
+
+ glyphRun.setRawData(glyphIndexArray, glyphPositionArray, 3);
+
+ QPixmap rawDataGlyphs(100, 50);
+ rawDataGlyphs.fill(Qt::white);
+ {
+ QPainter p(&rawDataGlyphs);
+ p.drawGlyphRun(QPointF(3, 2), glyphRun);
+ }
+
+ quint32 otherGlyphIndexArray[1] = { 2 };
+ QPointF otherGlyphPositionArray[1] = { QPointF(2, 3) };
+
+ glyphRun.setRawData(otherGlyphIndexArray, otherGlyphPositionArray, 1);
+
+ QPixmap baselineCopiedPixmap(100, 50);
+ baselineCopiedPixmap.fill(Qt::white);
+ {
+ QPainter p(&baselineCopiedPixmap);
+ p.drawGlyphRun(QPointF(3, 2), baselineCopied);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ baseline.save("setRawData_baseline.png");
+ rawDataGlyphs.save("setRawData_rawDataGlyphs.png");
+ baselineCopiedPixmap.save("setRawData_baselineCopiedPixmap.png");
+#endif
+
+ QCOMPARE(rawDataGlyphs, baseline);
+ QCOMPARE(baselineCopiedPixmap, baseline);
+}
+
+void tst_QGlyphRun::setRawDataAndGetAsVector()
+{
+ QVector<quint32> glyphIndexArray;
+ glyphIndexArray << 3 << 2 << 1 << 4;
+
+ QVector<QPointF> glyphPositionArray;
+ glyphPositionArray << QPointF(1, 2) << QPointF(3, 4) << QPointF(5, 6) << QPointF(7, 8);
+
+ QGlyphRun glyphRun;
+ glyphRun.setRawData(glyphIndexArray.constData(), glyphPositionArray.constData(), 4);
+
+ QVector<quint32> glyphIndexes = glyphRun.glyphIndexes();
+ QVector<QPointF> glyphPositions = glyphRun.positions();
+
+ QCOMPARE(glyphIndexes.size(), 4);
+ QCOMPARE(glyphPositions.size(), 4);
+
+ QCOMPARE(glyphIndexes, glyphIndexArray);
+ QCOMPARE(glyphPositions, glyphPositionArray);
+
+ QGlyphRun otherGlyphRun;
+ otherGlyphRun.setGlyphIndexes(glyphIndexArray);
+ otherGlyphRun.setPositions(glyphPositionArray);
+
+ QCOMPARE(glyphRun, otherGlyphRun);
+}
+
+void tst_QGlyphRun::drawNonExistentGlyphs()
+{
+ QVector<quint32> glyphIndexes;
+ glyphIndexes.append(3);
+
+ QVector<QPointF> glyphPositions;
+ glyphPositions.append(QPointF(0, 0));
+
+ QGlyphRun glyphs;
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(glyphPositions);
+ glyphs.setRawFont(QRawFont::fromFont(m_testFont));
+
+ QPixmap image(1000, 1000);
+ image.fill(Qt::white);
+
+ QPixmap imageBefore = image;
+ {
+ QPainter p(&image);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ image.save("drawNonExistentGlyphs.png");
+#endif
+
+ QCOMPARE(image, imageBefore); // Should be unchanged
+}
+
+void tst_QGlyphRun::drawMultiScriptText1()
+{
+ QString text;
+ text += QChar(0x03D0); // Greek, beta
+
+ QTextLayout textLayout(text);
+ textLayout.beginLayout();
+ textLayout.createLine();
+ textLayout.endLayout();
+
+ QPixmap textLayoutDraw(1000, 1000);
+ textLayoutDraw.fill(Qt::white);
+
+ QPixmap drawGlyphs(1000, 1000);
+ drawGlyphs.fill(Qt::white);
+
+ QList<QGlyphRun> glyphsList = textLayout.glyphRuns();
+ QCOMPARE(glyphsList.size(), 1);
+
+ {
+ QPainter p(&textLayoutDraw);
+ textLayout.draw(&p, QPointF(50, 50));
+ }
+
+ {
+ QPainter p(&drawGlyphs);
+ foreach (QGlyphRun glyphs, glyphsList)
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawMultiScriptText1_textLayoutDraw.png");
+ drawGlyphs.save("drawMultiScriptText1_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(drawGlyphs, textLayoutDraw);
+}
+
+
+void tst_QGlyphRun::drawMultiScriptText2()
+{
+ QString text;
+ text += QChar(0x0621); // Arabic, Hamza
+ text += QChar(0x03D0); // Greek, beta
+
+ QTextLayout textLayout(text);
+ textLayout.beginLayout();
+ textLayout.createLine();
+ textLayout.endLayout();
+
+ QPixmap textLayoutDraw(1000, 1000);
+ textLayoutDraw.fill(Qt::white);
+
+ QPixmap drawGlyphs(1000, 1000);
+ drawGlyphs.fill(Qt::white);
+
+ QList<QGlyphRun> glyphsList = textLayout.glyphRuns();
+ QCOMPARE(glyphsList.size(), 2);
+
+ {
+ QPainter p(&textLayoutDraw);
+ textLayout.draw(&p, QPointF(50, 50));
+ }
+
+ {
+ QPainter p(&drawGlyphs);
+ foreach (QGlyphRun glyphs, glyphsList)
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawMultiScriptText2_textLayoutDraw.png");
+ drawGlyphs.save("drawMultiScriptText2_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(drawGlyphs, textLayoutDraw);
+}
+
+void tst_QGlyphRun::detach()
+{
+ QGlyphRun glyphs;
+
+ glyphs.setGlyphIndexes(QVector<quint32>() << 1 << 2 << 3);
+
+ QGlyphRun otherGlyphs;
+ otherGlyphs = glyphs;
+
+ QCOMPARE(otherGlyphs.glyphIndexes(), glyphs.glyphIndexes());
+
+ otherGlyphs.setGlyphIndexes(QVector<quint32>() << 4 << 5 << 6);
+
+ QCOMPARE(otherGlyphs.glyphIndexes(), QVector<quint32>() << 4 << 5 << 6);
+ QCOMPARE(glyphs.glyphIndexes(), QVector<quint32>() << 1 << 2 << 3);
+}
+
+void tst_QGlyphRun::drawStruckOutText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setStrikeOut(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphRun glyphs = layout.glyphRuns().size() > 0
+ ? layout.glyphRuns().at(0)
+ : QGlyphRun();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawStruckOutText_textLayoutDraw.png");
+ drawGlyphs.save("drawStruckOutText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphRun::drawOverlinedText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setOverline(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphRun glyphs = layout.glyphRuns().size() > 0
+ ? layout.glyphRuns().at(0)
+ : QGlyphRun();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawOverlineText_textLayoutDraw.png");
+ drawGlyphs.save("drawOverlineText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphRun::drawUnderlinedText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setUnderline(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphRun glyphs = layout.glyphRuns().size() > 0
+ ? layout.glyphRuns().at(0)
+ : QGlyphRun();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawUnderlineText_textLayoutDraw.png");
+ drawGlyphs.save("drawUnderlineText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphRun::drawRightToLeft()
+{
+ QString s;
+ s.append(QChar(1575));
+ s.append(QChar(1578));
+
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QFont font;
+ font.setUnderline(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphRun glyphs = layout.glyphRuns().size() > 0
+ ? layout.glyphRuns().at(0)
+ : QGlyphRun();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphRun(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawRightToLeft_textLayoutDraw.png");
+ drawGlyphs.save("drawRightToLeft_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+
+}
+
+#endif // QT_NO_RAWFONT
+
+QTEST_MAIN(tst_QGlyphRun)
+#include "tst_qglyphrun.moc"
+
diff --git a/tests/auto/gui/text/qrawfont/qrawfont.pro b/tests/auto/gui/text/qrawfont/qrawfont.pro
new file mode 100644
index 0000000000..1c08299699
--- /dev/null
+++ b/tests/auto/gui/text/qrawfont/qrawfont.pro
@@ -0,0 +1,16 @@
+load(qttest_p4)
+
+QT = core core-private gui gui-private
+
+SOURCES += \
+ tst_qrawfont.cpp
+
+INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src
+
+wince*|symbian*: {
+ DEFINES += SRCDIR=\\\"\\\"
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD/\\\"
+}
+
+CONFIG += insignificant_test # QTBUG-21402
diff --git a/tests/auto/gui/text/qrawfont/testfont.ttf b/tests/auto/gui/text/qrawfont/testfont.ttf
new file mode 100644
index 0000000000..d6042d2e58
--- /dev/null
+++ b/tests/auto/gui/text/qrawfont/testfont.ttf
Binary files differ
diff --git a/tests/auto/gui/text/qrawfont/testfont_bold_italic.ttf b/tests/auto/gui/text/qrawfont/testfont_bold_italic.ttf
new file mode 100644
index 0000000000..9f65ac8df7
--- /dev/null
+++ b/tests/auto/gui/text/qrawfont/testfont_bold_italic.ttf
Binary files differ
diff --git a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp
new file mode 100644
index 0000000000..1c18f2f1e8
--- /dev/null
+++ b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp
@@ -0,0 +1,897 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <qrawfont.h>
+#include <private/qrawfont_p.h>
+
+class tst_QRawFont: public QObject
+{
+ Q_OBJECT
+#if !defined(QT_NO_RAWFONT)
+private slots:
+ void init();
+
+ void invalidRawFont();
+
+ void explicitRawFontNotLoadedInDatabase_data();
+ void explicitRawFontNotLoadedInDatabase();
+
+ void explicitRawFontNotAvailableInSystem_data();
+ void explicitRawFontNotAvailableInSystem();
+
+ void correctFontData_data();
+ void correctFontData();
+
+ void glyphIndices();
+
+ void advances_data();
+ void advances();
+
+ void textLayout();
+
+ void fontTable_data();
+ void fontTable();
+
+ void supportedWritingSystems_data();
+ void supportedWritingSystems();
+
+ void supportsCharacter_data();
+ void supportsCharacter();
+
+ void supportsUcs4Character_data();
+ void supportsUcs4Character();
+
+ void fromFont_data();
+ void fromFont();
+
+ void copyConstructor_data();
+ void copyConstructor();
+
+ void detach_data();
+ void detach();
+
+ void unsupportedWritingSystem_data();
+ void unsupportedWritingSystem();
+
+ void rawFontSetPixelSize_data();
+ void rawFontSetPixelSize();
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ void multipleRawFontsFromData();
+#endif
+
+#endif // QT_NO_RAWFONT
+};
+
+#if !defined(QT_NO_RAWFONT)
+Q_DECLARE_METATYPE(QFont::HintingPreference)
+Q_DECLARE_METATYPE(QFont::Style)
+Q_DECLARE_METATYPE(QFont::Weight)
+Q_DECLARE_METATYPE(QFontDatabase::WritingSystem)
+
+void tst_QRawFont::init()
+{
+#ifdef Q_WS_QPA
+ // Loading fonts from a QByteArray seems unimplemented for all qpa plugins at time of writing;
+ // almost all testfunctions fail on qpa due to this, except these few:
+ const QByteArray func = QTest::currentTestFunction();
+ if (func != "invalidRawFont"
+ && func != "explicitRawFontNotAvailableInSystem"
+ && func != "fromFont"
+ && func != "textLayout")
+ QEXPECT_FAIL("", "QTBUG-20976 fails on qpa", Abort);
+#endif
+}
+
+void tst_QRawFont::invalidRawFont()
+{
+ QRawFont font;
+ QVERIFY(!font.isValid());
+ QCOMPARE(font.pixelSize(), 0.0);
+ QVERIFY(font.familyName().isEmpty());
+ QCOMPARE(font.style(), QFont::StyleNormal);
+ QCOMPARE(font.weight(), -1);
+ QCOMPARE(font.ascent(), 0.0);
+ QCOMPARE(font.descent(), 0.0);
+ QVERIFY(font.glyphIndexesForString(QLatin1String("Test")).isEmpty());
+}
+
+void tst_QRawFont::explicitRawFontNotLoadedInDatabase_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::explicitRawFontNotLoadedInDatabase()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QRawFont font(QLatin1String(SRCDIR "testfont.ttf"), 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QVERIFY(!QFontDatabase().families().contains(font.familyName()));
+}
+
+void tst_QRawFont::explicitRawFontNotAvailableInSystem_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::explicitRawFontNotAvailableInSystem()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QRawFont rawfont(QLatin1String(SRCDIR "testfont.ttf"), 10, hintingPreference);
+
+ {
+ QFont font(rawfont.familyName(), 10);
+
+ QVERIFY(!font.exactMatch());
+ QVERIFY(font.family() != QFontInfo(font).family());
+ }
+}
+
+void tst_QRawFont::correctFontData_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QString>("expectedFamilyName");
+ QTest::addColumn<QFont::Style>("style");
+ QTest::addColumn<QFont::Weight>("weight");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+ QTest::addColumn<qreal>("unitsPerEm");
+ QTest::addColumn<qreal>("pixelSize");
+
+ int hintingPreferences[] = {
+ int(QFont::PreferDefaultHinting),
+ int(QFont::PreferNoHinting),
+ int(QFont::PreferVerticalHinting),
+ int(QFont::PreferFullHinting),
+ -1
+ };
+ int *hintingPreference = hintingPreferences;
+
+ while (*hintingPreference >= 0) {
+ QString fileName = QLatin1String(SRCDIR "testfont.ttf");
+ QString title = fileName
+ + QLatin1String(": hintingPreference=")
+ + QString::number(*hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QString::fromLatin1("QtBidiTestFont")
+ << QFont::StyleNormal
+ << QFont::Normal
+ << QFont::HintingPreference(*hintingPreference)
+ << 1000.0
+ << 10.0;
+
+ fileName = QLatin1String(SRCDIR "testfont_bold_italic.ttf");
+ title = fileName
+ + QLatin1String(": hintingPreference=")
+ + QString::number(*hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QString::fromLatin1("QtBidiTestFont")
+ << QFont::StyleItalic
+ << QFont::Bold
+ << QFont::HintingPreference(*hintingPreference)
+ << 1000.0
+ << 10.0;
+
+ ++hintingPreference;
+ }
+}
+
+void tst_QRawFont::correctFontData()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QString, expectedFamilyName);
+ QFETCH(QFont::Style, style);
+ QFETCH(QFont::Weight, weight);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+ QFETCH(qreal, unitsPerEm);
+ QFETCH(qreal, pixelSize);
+
+ QRawFont font(fileName, 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QCOMPARE(font.familyName(), expectedFamilyName);
+ QCOMPARE(font.style(), style);
+ QCOMPARE(font.weight(), int(weight));
+ QCOMPARE(font.hintingPreference(), hintingPreference);
+ QCOMPARE(font.unitsPerEm(), unitsPerEm);
+ QCOMPARE(font.pixelSize(), pixelSize);
+}
+
+void tst_QRawFont::glyphIndices()
+{
+ QRawFont font(QLatin1String(SRCDIR "testfont.ttf"), 10);
+ QVERIFY(font.isValid());
+
+ QVector<quint32> glyphIndices = font.glyphIndexesForString(QLatin1String("Foobar"));
+ QVector<quint32> expectedGlyphIndices;
+ expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86;
+
+ QCOMPARE(glyphIndices, expectedGlyphIndices);
+}
+
+void tst_QRawFont::advances_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::advances()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QRawFont font(QLatin1String(SRCDIR "testfont.ttf"), 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QRawFontPrivate *font_d = QRawFontPrivate::get(font);
+ QVERIFY(font_d->fontEngine != 0);
+
+ QVector<quint32> glyphIndices;
+ glyphIndices << 44 << 83 << 83 << 70 << 69 << 86; // "Foobar"
+
+ bool supportsSubPixelPositions = font_d->fontEngine->supportsSubPixelPositions();
+ QVector<QPointF> advances = font.advancesForGlyphIndexes(glyphIndices);
+ for (int i=0; i<glyphIndices.size(); ++i) {
+ QVERIFY(qFuzzyCompare(qRound(advances.at(i).x()), 8.0));
+ if (supportsSubPixelPositions)
+ QVERIFY(advances.at(i).x() > 8.0);
+
+ QVERIFY(qFuzzyIsNull(advances.at(i).y()));
+ }
+}
+
+void tst_QRawFont::textLayout()
+{
+ QFontDatabase fontDatabase;
+ int id = fontDatabase.addApplicationFont(SRCDIR "testfont.ttf");
+ QVERIFY(id >= 0);
+
+ QString familyName = QString::fromLatin1("QtBidiTestFont");
+ QFont font(familyName);
+ font.setPixelSize(18.0);
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20976 fails on qpa", Abort);
+#endif
+ QCOMPARE(QFontInfo(font).family(), familyName);
+
+ QTextLayout layout(QLatin1String("Foobar"));
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QList<QGlyphRun> glyphRuns = layout.glyphRuns();
+ QCOMPARE(glyphRuns.size(), 1);
+
+ QGlyphRun glyphs = glyphRuns.at(0);
+
+ QRawFont rawFont = glyphs.rawFont();
+ QVERIFY(rawFont.isValid());
+ QCOMPARE(rawFont.familyName(), familyName);
+ QCOMPARE(rawFont.pixelSize(), 18.0);
+
+ QVector<quint32> expectedGlyphIndices;
+ expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86;
+
+ QCOMPARE(glyphs.glyphIndexes(), expectedGlyphIndices);
+
+ QVERIFY(fontDatabase.removeApplicationFont(id));
+}
+
+void tst_QRawFont::fontTable_data()
+{
+ QTest::addColumn<QByteArray>("tagName");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+ QTest::addColumn<int>("offset");
+ QTest::addColumn<quint32>("expectedValue");
+
+ QTest::newRow("Head table, magic number, default hinting")
+ << QByteArray("head")
+ << QFont::PreferDefaultHinting
+ << 12
+ << (QSysInfo::ByteOrder == QSysInfo::BigEndian
+ ? 0x5F0F3CF5
+ : 0xF53C0F5F);
+
+ QTest::newRow("Head table, magic number, no hinting")
+ << QByteArray("head")
+ << QFont::PreferNoHinting
+ << 12
+ << (QSysInfo::ByteOrder == QSysInfo::BigEndian
+ ? 0x5F0F3CF5
+ : 0xF53C0F5F);
+
+ QTest::newRow("Head table, magic number, vertical hinting")
+ << QByteArray("head")
+ << QFont::PreferVerticalHinting
+ << 12
+ << (QSysInfo::ByteOrder == QSysInfo::BigEndian
+ ? 0x5F0F3CF5
+ : 0xF53C0F5F);
+
+ QTest::newRow("Head table, magic number, full hinting")
+ << QByteArray("head")
+ << QFont::PreferFullHinting
+ << 12
+ << (QSysInfo::ByteOrder == QSysInfo::BigEndian
+ ? 0x5F0F3CF5
+ : 0xF53C0F5F);
+}
+
+void tst_QRawFont::fontTable()
+{
+ QFETCH(QByteArray, tagName);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+ QFETCH(int, offset);
+ QFETCH(quint32, expectedValue);
+
+ QRawFont font(QString::fromLatin1(SRCDIR "testfont.ttf"), 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QByteArray table = font.fontTable(tagName);
+ QVERIFY(!table.isEmpty());
+
+ const quint32 *value = reinterpret_cast<const quint32 *>(table.constData() + offset);
+ QCOMPARE(*value, expectedValue);
+}
+
+typedef QList<QFontDatabase::WritingSystem> WritingSystemList;
+Q_DECLARE_METATYPE(WritingSystemList)
+
+void tst_QRawFont::supportedWritingSystems_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<WritingSystemList>("writingSystems");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ for (int hintingPreference=QFont::PreferDefaultHinting;
+ hintingPreference<=QFont::PreferFullHinting;
+ ++hintingPreference) {
+
+ QTest::newRow(qPrintable(QString::fromLatin1("testfont.ttf, hintingPreference=%1")
+ .arg(hintingPreference)))
+ << QString::fromLatin1(SRCDIR "testfont.ttf")
+ << (QList<QFontDatabase::WritingSystem>()
+ << QFontDatabase::Latin
+ << QFontDatabase::Hebrew
+ << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin
+ << QFont::HintingPreference(hintingPreference);
+
+ QTest::newRow(qPrintable(QString::fromLatin1("testfont_bold_italic.ttf, hintingPreference=%1")
+ .arg(hintingPreference)))
+ << QString::fromLatin1(SRCDIR "testfont_bold_italic.ttf")
+ << (QList<QFontDatabase::WritingSystem>()
+ << QFontDatabase::Latin
+ << QFontDatabase::Hebrew
+ << QFontDatabase::Devanagari
+ << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin
+ << QFont::HintingPreference(hintingPreference);
+ }
+}
+
+void tst_QRawFont::supportedWritingSystems()
+{
+ QFETCH(QString, fileName);
+ QFETCH(WritingSystemList, writingSystems);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QRawFont font(fileName, 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ WritingSystemList actualWritingSystems = font.supportedWritingSystems();
+ QCOMPARE(actualWritingSystems.size(), writingSystems.size());
+
+ foreach (QFontDatabase::WritingSystem writingSystem, writingSystems)
+ QVERIFY(actualWritingSystems.contains(writingSystem));
+}
+
+void tst_QRawFont::supportsCharacter_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+ QTest::addColumn<QChar>("character");
+ QTest::addColumn<bool>("shouldBeSupported");
+
+ const char *fileNames[2] = {
+ SRCDIR "testfont.ttf",
+ SRCDIR "testfont_bold_italic.ttf"
+ };
+
+ for (int hintingPreference=QFont::PreferDefaultHinting;
+ hintingPreference<=QFont::PreferFullHinting;
+ ++hintingPreference) {
+
+ for (int i=0; i<2; ++i) {
+ QString fileName = QLatin1String(fileNames[i]);
+
+ // Latin text
+ for (char ch='!'; ch<='~'; ++ch) {
+ QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3")
+ .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(hintingPreference)
+ << QChar::fromLatin1(ch)
+ << true;
+ }
+
+ // Hebrew text
+ for (quint16 ch=0x05D0; ch<=0x05EA; ++ch) {
+ QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3")
+ .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(hintingPreference)
+ << QChar(ch)
+ << true;
+ }
+
+ QTest::newRow(qPrintable(QString::fromLatin1("Missing character, %1, hintingPreference=%2")
+ .arg(fileName).arg(hintingPreference)))
+ << fileName
+ << QFont::HintingPreference(hintingPreference)
+ << QChar(0xD8)
+ << false;
+ }
+ }
+}
+
+void tst_QRawFont::supportsCharacter()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+ QFETCH(QChar, character);
+ QFETCH(bool, shouldBeSupported);
+
+ QRawFont font(fileName, 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QCOMPARE(font.supportsCharacter(character), shouldBeSupported);
+}
+
+void tst_QRawFont::supportsUcs4Character_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+ QTest::addColumn<quint32>("ucs4");
+ QTest::addColumn<bool>("shouldBeSupported");
+
+ // Gothic text
+ for (int hintingPreference=QFont::PreferDefaultHinting;
+ hintingPreference<=QFont::PreferFullHinting;
+ ++hintingPreference) {
+ for (quint32 ch=0x10330; ch<=0x1034A; ++ch) {
+ {
+ QString fileName = QString::fromLatin1(SRCDIR "testfont.ttf");
+ QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3")
+ .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(hintingPreference)
+ << ch
+ << true;
+ }
+
+ {
+ QString fileName = QString::fromLatin1(SRCDIR "testfont_bold_italic.ttf");
+ QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3")
+ .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference);
+
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(hintingPreference)
+ << ch
+ << false;
+ }
+ }
+ }
+}
+
+void tst_QRawFont::supportsUcs4Character()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+ QFETCH(quint32, ucs4);
+ QFETCH(bool, shouldBeSupported);
+
+ QRawFont font(fileName, 10, hintingPreference);
+ QVERIFY(font.isValid());
+
+ QCOMPARE(font.supportsCharacter(ucs4), shouldBeSupported);
+}
+
+void tst_QRawFont::fromFont_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+ QTest::addColumn<QString>("familyName");
+ QTest::addColumn<QFontDatabase::WritingSystem>("writingSystem");
+
+ for (int i=QFont::PreferDefaultHinting; i<=QFont::PreferFullHinting; ++i) {
+ QString titleBase = QString::fromLatin1("%2, hintingPreference=%1, writingSystem=%3")
+ .arg(i);
+ {
+ QString fileName = QString::fromLatin1(SRCDIR "testfont.ttf");
+ QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any;
+
+ QString title = titleBase.arg(fileName).arg(writingSystem);
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(i)
+ << "QtBidiTestFont"
+ << writingSystem;
+ }
+
+ {
+ QString fileName = QString::fromLatin1(SRCDIR "testfont.ttf");
+ QFontDatabase::WritingSystem writingSystem = QFontDatabase::Hebrew;
+
+ QString title = titleBase.arg(fileName).arg(writingSystem);
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(i)
+ << "QtBidiTestFont"
+ << writingSystem;
+ }
+
+ {
+ QString fileName = QString::fromLatin1(SRCDIR "testfont.ttf");
+ QFontDatabase::WritingSystem writingSystem = QFontDatabase::Latin;
+
+ QString title = titleBase.arg(fileName).arg(writingSystem);
+ QTest::newRow(qPrintable(title))
+ << fileName
+ << QFont::HintingPreference(i)
+ << "QtBidiTestFont"
+ << writingSystem;
+ }
+ }
+}
+
+void tst_QRawFont::fromFont()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QFont::HintingPreference, hintingPreference);
+ QFETCH(QString, familyName);
+ QFETCH(QFontDatabase::WritingSystem, writingSystem);
+
+ QFontDatabase fontDatabase;
+ int id = fontDatabase.addApplicationFont(fileName);
+ QVERIFY(id >= 0);
+
+ QFont font(familyName);
+ font.setHintingPreference(hintingPreference);
+ font.setPixelSize(26.0);
+
+ QRawFont rawFont = QRawFont::fromFont(font, writingSystem);
+ QVERIFY(rawFont.isValid());
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20976 fails on qpa", Abort);
+#endif
+
+ QCOMPARE(rawFont.familyName(), familyName);
+ QCOMPARE(rawFont.pixelSize(), 26.0);
+
+ QVERIFY(fontDatabase.removeApplicationFont(id));
+}
+
+void tst_QRawFont::copyConstructor_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting preference") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting preference") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::copyConstructor()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ {
+ QString rawFontFamilyName;
+ qreal rawFontPixelSize;
+ qreal rawFontAscent;
+ qreal rawFontDescent;
+ int rawFontTableSize;
+
+ QRawFont outerRawFont;
+ {
+ QRawFont rawFont(QString::fromLatin1(SRCDIR "testfont.ttf"), 11, hintingPreference);
+ QVERIFY(rawFont.isValid());
+
+ rawFontFamilyName = rawFont.familyName();
+ rawFontPixelSize = rawFont.pixelSize();
+ rawFontAscent = rawFont.ascent();
+ rawFontDescent = rawFont.descent();
+ rawFontTableSize = rawFont.fontTable("glyf").size();
+ QVERIFY(rawFontTableSize > 0);
+
+ {
+ QRawFont otherRawFont(rawFont);
+ QVERIFY(otherRawFont.isValid());
+ QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(otherRawFont.ascent(), rawFontAscent);
+ QCOMPARE(otherRawFont.descent(), rawFontDescent);
+ QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+
+ {
+ QRawFont otherRawFont = rawFont;
+ QVERIFY(otherRawFont.isValid());
+ QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(otherRawFont.ascent(), rawFontAscent);
+ QCOMPARE(otherRawFont.descent(), rawFontDescent);
+ QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+
+ outerRawFont = rawFont;
+ }
+
+ QVERIFY(outerRawFont.isValid());
+ QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(outerRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(outerRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(outerRawFont.ascent(), rawFontAscent);
+ QCOMPARE(outerRawFont.descent(), rawFontDescent);
+ QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+}
+
+void tst_QRawFont::detach_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting preference") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting preference") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::detach()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ {
+ QString rawFontFamilyName;
+ qreal rawFontPixelSize;
+ qreal rawFontAscent;
+ qreal rawFontDescent;
+ int rawFontTableSize;
+
+ QRawFont outerRawFont;
+ {
+ QRawFont rawFont(QString::fromLatin1(SRCDIR "testfont.ttf"), 11, hintingPreference);
+ QVERIFY(rawFont.isValid());
+
+ rawFontFamilyName = rawFont.familyName();
+ rawFontPixelSize = rawFont.pixelSize();
+ rawFontAscent = rawFont.ascent();
+ rawFontDescent = rawFont.descent();
+ rawFontTableSize = rawFont.fontTable("glyf").size();
+ QVERIFY(rawFontTableSize > 0);
+
+ {
+ QRawFont otherRawFont(rawFont);
+
+ otherRawFont.loadFromFile(QLatin1String(SRCDIR "testfont.ttf"),
+ rawFontPixelSize, hintingPreference);
+
+ QVERIFY(otherRawFont.isValid());
+ QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(otherRawFont.ascent(), rawFontAscent);
+ QCOMPARE(otherRawFont.descent(), rawFontDescent);
+ QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+
+ {
+ QRawFont otherRawFont = rawFont;
+
+ otherRawFont.loadFromFile(QLatin1String(SRCDIR "testfont.ttf"),
+ rawFontPixelSize, hintingPreference);
+
+ QVERIFY(otherRawFont.isValid());
+ QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(otherRawFont.ascent(), rawFontAscent);
+ QCOMPARE(otherRawFont.descent(), rawFontDescent);
+ QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+
+ outerRawFont = rawFont;
+
+ rawFont.loadFromFile(QLatin1String(SRCDIR "testfont.ttf"), rawFontPixelSize,
+ hintingPreference);
+ }
+
+ QVERIFY(outerRawFont.isValid());
+ QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize);
+ QCOMPARE(outerRawFont.familyName(), rawFontFamilyName);
+ QCOMPARE(outerRawFont.hintingPreference(), hintingPreference);
+ QCOMPARE(outerRawFont.ascent(), rawFontAscent);
+ QCOMPARE(outerRawFont.descent(), rawFontDescent);
+ QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize);
+ }
+}
+
+void tst_QRawFont::unsupportedWritingSystem_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting preference") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting preference") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::unsupportedWritingSystem()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QFontDatabase fontDatabase;
+ int id = fontDatabase.addApplicationFont(QLatin1String(SRCDIR "testfont.ttf"));
+
+ QFont font("QtBidiTestFont");
+ font.setHintingPreference(hintingPreference);
+ font.setPixelSize(12.0);
+
+ QRawFont rawFont = QRawFont::fromFont(font, QFontDatabase::Any);
+ QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont"));
+ QCOMPARE(rawFont.pixelSize(), 12.0);
+
+ rawFont = QRawFont::fromFont(font, QFontDatabase::Hebrew);
+ QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont"));
+ QCOMPARE(rawFont.pixelSize(), 12.0);
+
+ QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic);
+
+ QTextLayout layout;
+ layout.setFont(font);
+ layout.setText(arabicText);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QList<QGlyphRun> glyphRuns = layout.glyphRuns();
+ QCOMPARE(glyphRuns.size(), 1);
+
+ QGlyphRun glyphs = glyphRuns.at(0);
+ QRawFont layoutFont = glyphs.rawFont();
+ QVERIFY(layoutFont.familyName() != QString::fromLatin1("QtBidiTestFont"));
+ QCOMPARE(layoutFont.pixelSize(), 12.0);
+
+ rawFont = QRawFont::fromFont(font, QFontDatabase::Arabic);
+ QCOMPARE(rawFont.familyName(), layoutFont.familyName());
+ QCOMPARE(rawFont.pixelSize(), 12.0);
+
+ fontDatabase.removeApplicationFont(id);
+}
+
+void tst_QRawFont::rawFontSetPixelSize_data()
+{
+ QTest::addColumn<QFont::HintingPreference>("hintingPreference");
+
+ QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting;
+ QTest::newRow("No hinting preference") << QFont::PreferNoHinting;
+ QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting;
+ QTest::newRow("Full hinting preference") << QFont::PreferFullHinting;
+}
+
+void tst_QRawFont::rawFontSetPixelSize()
+{
+ QFETCH(QFont::HintingPreference, hintingPreference);
+
+ QTextLayout layout("Foobar");
+
+ QFont font = layout.font();
+ font.setHintingPreference(hintingPreference);
+ font.setPixelSize(12);
+ layout.setFont(font);
+
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QGlyphRun glyphs = layout.glyphRuns().at(0);
+ QRawFont rawFont = glyphs.rawFont();
+ QCOMPARE(rawFont.pixelSize(), 12.0);
+
+ rawFont.setPixelSize(24);
+ QCOMPARE(rawFont.pixelSize(), 24.0);
+}
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+void tst_QRawFont::multipleRawFontsFromData()
+{
+ QFile file(QString::fromLatin1(SRCDIR "testfont.ttf"));
+ QRawFont testFont;
+ if (file.open(QIODevice::ReadOnly)) {
+ testFont.loadFromData(file.readAll(), 11, QFont::PreferDefaultHinting);
+ file.close();
+ }
+ file.setFileName(QLatin1String(SRCDIR "testfont_bold_italic.ttf"));
+ QRawFont testFontBoldItalic;
+ if (file.open(QIODevice::ReadOnly))
+ testFontBoldItalic.loadFromData(file.readAll(), 11, QFont::PreferDefaultHinting);
+
+ QVERIFY(testFont.familyName() != (testFontBoldItalic.familyName())
+ || testFont.styleName() != (testFontBoldItalic.styleName()));
+}
+#endif
+
+#endif // QT_NO_RAWFONT
+
+QTEST_MAIN(tst_QRawFont)
+#include "tst_qrawfont.moc"
+
diff --git a/tests/auto/gui/text/qstatictext/qstatictext.pro b/tests/auto/gui/text/qstatictext/qstatictext.pro
new file mode 100644
index 0000000000..a8398dccf9
--- /dev/null
+++ b/tests/auto/gui/text/qstatictext/qstatictext.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+QT += widgets widgets-private
+QT += core core-private gui gui-private
+SOURCES += tst_qstatictext.cpp
+
+CONFIG += insignificant_test # QTBUG-21290 - crashes on qpa, xcb
diff --git a/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp b/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp
new file mode 100644
index 0000000000..79cbd692ea
--- /dev/null
+++ b/tests/auto/gui/text/qstatictext/tst_qstatictext.cpp
@@ -0,0 +1,870 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtWidgets/QApplication>
+#include <QtGui/QPainter>
+#include <QtGui/QImage>
+
+#include <qstatictext.h>
+#include <qpaintengine.h>
+
+#include <private/qstatictext_p.h>
+#include <private/qapplication_p.h>
+
+// #define DEBUG_SAVE_IMAGE
+
+class tst_QStaticText: public QObject
+{
+ Q_OBJECT
+public:
+ tst_QStaticText() {}
+
+private slots:
+ void initTestCase();
+
+ void init();
+ void cleanup();
+
+ void constructionAndDestruction();
+ void drawToPoint_data();
+ void drawToPoint();
+ void drawToRect_data();
+ void drawToRect();
+ void setFont();
+ void setTextWidth();
+ void prepareToCorrectData();
+ void prepareToWrongData();
+
+ void copyConstructor();
+
+ void translatedPainter();
+ void rotatedPainter();
+ void scaledPainter();
+ void projectedPainter();
+#if 0
+ void rotatedScaledAndTranslatedPainter_data();
+ void rotatedScaledAndTranslatedPainter();
+#endif
+ void transformationChanged();
+
+ void plainTextVsRichText();
+
+ void setPenPlainText();
+ void setPenRichText();
+ void richTextOverridesPen();
+
+ void drawStruckOutText();
+ void drawOverlinedText();
+ void drawUnderlinedText();
+
+ void unprintableCharacter_qtbug12614();
+
+ void underlinedColor_qtbug20159();
+ void textDocumentColor();
+
+private:
+ bool supportsTransformations() const;
+
+ QImage const m_whiteSquare;
+};
+
+void tst_QStaticText::initTestCase()
+{
+ // a "blank" square; we compare against in our testfunctions to verify
+ // that we have actually painted something
+ QPixmap pm(1000, 1000);
+ pm.fill(Qt::white);
+ const_cast<QImage&>(m_whiteSquare) = pm.toImage();
+}
+
+void tst_QStaticText::init()
+{
+}
+
+void tst_QStaticText::cleanup()
+{
+}
+
+void tst_QStaticText::constructionAndDestruction()
+{
+ QStaticText text("My text");
+}
+
+void tst_QStaticText::copyConstructor()
+{
+ QStaticText text(QLatin1String("My text"));
+
+ QTextOption textOption(Qt::AlignRight);
+ text.setTextOption(textOption);
+
+ text.setPerformanceHint(QStaticText::AggressiveCaching);
+ text.setTextWidth(123.456);
+ text.setTextFormat(Qt::PlainText);
+
+ QStaticText copiedText(text);
+ copiedText.setText(QLatin1String("Other text"));
+
+ QCOMPARE(copiedText.textOption().alignment(), Qt::AlignRight);
+ QCOMPARE(copiedText.performanceHint(), QStaticText::AggressiveCaching);
+ QCOMPARE(copiedText.textWidth(), 123.456);
+ QCOMPARE(copiedText.textFormat(), Qt::PlainText);
+
+ QStaticText otherCopiedText(copiedText);
+ otherCopiedText.setTextWidth(789);
+
+ QCOMPARE(otherCopiedText.text(), QString::fromLatin1("Other text"));
+}
+
+Q_DECLARE_METATYPE(QStaticText::PerformanceHint)
+void tst_QStaticText::drawToPoint_data()
+{
+ QTest::addColumn<QStaticText::PerformanceHint>("performanceHint");
+
+ QTest::newRow("Moderate caching") << QStaticText::ModerateCaching;
+ QTest::newRow("Aggressive caching") << QStaticText::AggressiveCaching;
+}
+
+void tst_QStaticText::drawToPoint()
+{
+ QFETCH(QStaticText::PerformanceHint, performanceHint);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+ text.setPerformanceHint(performanceHint);
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::drawToRect_data()
+{
+ QTest::addColumn<QStaticText::PerformanceHint>("performanceHint");
+
+ QTest::newRow("Moderate caching") << QStaticText::ModerateCaching;
+ QTest::newRow("Aggressive caching") << QStaticText::AggressiveCaching;
+}
+
+void tst_QStaticText::drawToRect()
+{
+ QFETCH(QStaticText::PerformanceHint, performanceHint);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.drawText(QRectF(11, 12, 10, 500), "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextWidth(10),
+ p.setClipRect(QRectF(11, 12, 10, 500));
+ text.setPerformanceHint(performanceHint);
+ text.setTextFormat(Qt::PlainText);
+ p.drawStaticText(QPointF(11, 12), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("drawToRect_imageDrawText.png");
+ imageDrawStaticText.save("drawToRect_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::prepareToCorrectData()
+{
+ QTransform transform;
+ transform.scale(2.0, 2.0);
+ transform.rotate(90, Qt::ZAxis);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.setTransform(transform);
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.setTransform(transform);
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.prepare(transform, p.font());
+ text.setTextFormat(Qt::PlainText);
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("prepareToCorrectData_imageDrawText.png");
+ imageDrawStaticText.save("prepareToCorrectData_imageDrawStaticText.png");
+#endif
+
+#ifdef Q_WS_QPA
+ QEXPECT_FAIL("", "QTBUG-20977 fails on qpa", Abort);
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+
+ if (!supportsTransformations())
+ QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::prepareToWrongData()
+{
+ QTransform transform;
+ transform.scale(2.0, 2.0);
+ transform.rotate(90, Qt::ZAxis);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.prepare(transform, p.font());
+ text.setTextFormat(Qt::PlainText);
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+
+void tst_QStaticText::setFont()
+{
+ QFont font = QApplication::font();
+ font.setBold(true);
+ font.setPointSize(28);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+
+ p.setFont(font);
+ p.drawText(QRectF(11, 120, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+
+ QStaticText text;
+ text.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(0, 0, text);
+
+ p.setFont(font);
+ p.drawStaticText(11, 120, text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("setFont_imageDrawText.png");
+ imageDrawStaticText.save("setFont_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::setTextWidth()
+{
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.drawText(QRectF(11, 12, 10, 500), "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextWidth(10);
+ p.setClipRect(QRectF(11, 12, 10, 500));
+ p.drawStaticText(QPointF(11, 12), text);
+ }
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::translatedPainter()
+{
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.translate(100, 200);
+
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.translate(100, 200);
+
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+bool tst_QStaticText::supportsTransformations() const
+{
+ QPixmap pm(10, 10);
+ QPainter p(&pm);
+ QPaintEngine *engine = p.paintEngine();
+
+ QPaintEngine::Type type = engine->type();
+
+ if (type == QPaintEngine::OpenGL
+#if !defined(Q_WS_WIN) && !defined(Q_WS_X11) && !defined(Q_WS_MAC)
+ || type == QPaintEngine::Raster
+#endif
+ )
+ return false;
+
+ return true;
+}
+
+void tst_QStaticText::rotatedPainter()
+{
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.rotate(30.0);
+ p.drawText(QRectF(0, 0, 1000, 100), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ QPainter p(&imageDrawStaticText);
+ p.rotate(30.0);
+ p.drawStaticText(QPoint(0, 0), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("rotatedPainter_imageDrawText.png");
+ imageDrawStaticText.save("rotatedPainter_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+
+ if (!supportsTransformations())
+ QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::scaledPainter()
+{
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.scale(2.0, 0.2);
+
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.scale(2.0, 0.2);
+
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+
+ if (!supportsTransformations())
+ QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::projectedPainter()
+{
+ QTransform transform;
+ transform.rotate(90, Qt::XAxis);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.setTransform(transform);
+
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.setTransform(transform);
+
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+#if 0
+void tst_QStaticText::rotatedScaledAndTranslatedPainter_data()
+{
+ QTest::addColumn<qreal>("offset");
+
+ for (int i=0; i<100; ++i) {
+ qreal offset = 300 + i / 100.;
+ QTest::newRow(QByteArray::number(offset).constData()) << offset;
+ }
+}
+
+void tst_QStaticText::rotatedScaledAndTranslatedPainter()
+{
+ QFETCH(qreal, offset);
+
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.translate(offset, 0);
+ p.rotate(45.0);
+ p.scale(2.0, 2.0);
+ p.translate(100, 200);
+
+ p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.translate(offset, 0);
+ p.rotate(45.0);
+ p.scale(2.0, 2.0);
+ p.translate(100, 200);
+
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("rotatedScaledAndPainter_imageDrawText.png");
+ imageDrawStaticText.save("rotatedScaledAndPainter_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+
+ if (!supportsTransformations())
+ QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+#endif
+
+void tst_QStaticText::transformationChanged()
+{
+ QPixmap imageDrawText(1000, 1000);
+ imageDrawText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawText);
+ p.rotate(33.0);
+ p.scale(0.5, 0.7);
+
+ p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+
+ p.scale(2.0, 2.5);
+ p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ }
+
+ QPixmap imageDrawStaticText(1000, 1000);
+ imageDrawStaticText.fill(Qt::white);
+ {
+ QPainter p(&imageDrawStaticText);
+ p.rotate(33.0);
+ p.scale(0.5, 0.7);
+
+ QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
+ text.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(QPointF(0, 0), text);
+
+ p.scale(2.0, 2.5);
+ p.drawStaticText(QPointF(0, 0), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("transformationChanged_imageDrawText.png");
+ imageDrawStaticText.save("transformationChanged_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+
+ if (!supportsTransformations())
+ QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
+ QCOMPARE(imageDrawStaticText, imageDrawText);
+}
+
+void tst_QStaticText::plainTextVsRichText()
+{
+ QPixmap imagePlainText(1000, 1000);
+ imagePlainText.fill(Qt::white);
+ {
+ QPainter p(&imagePlainText);
+
+ QStaticText staticText;
+ staticText.setText("FOObar");
+ staticText.setTextFormat(Qt::PlainText);
+
+ p.drawStaticText(10, 10, staticText);
+ }
+
+ QPixmap imageRichText(1000, 1000);
+ imageRichText.fill(Qt::white);
+ {
+ QPainter p(&imageRichText);
+
+ QStaticText staticText;
+ staticText.setText("<html><body>FOObar</body></html>");
+ staticText.setTextFormat(Qt::RichText);
+
+ p.drawStaticText(10, 10, staticText);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imagePlainText.save("plainTextVsRichText_imagePlainText.png");
+ imageRichText.save("plainTextVsRichText_imageRichText.png");
+#endif
+
+ QVERIFY(imagePlainText.toImage() != m_whiteSquare);
+ QCOMPARE(imagePlainText, imageRichText);
+}
+
+void tst_QStaticText::setPenPlainText()
+{
+ QFont font = QApplication::font();
+ font.setStyleStrategy(QFont::NoAntialias);
+
+ QFontMetricsF fm(font);
+ QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height()));
+ image.fill(Qt::white);
+ {
+ QPainter p(&image);
+ p.setFont(font);
+ p.setPen(Qt::green);
+
+ QStaticText staticText("XXXXX");
+ staticText.setTextFormat(Qt::PlainText);
+ p.drawStaticText(0, 0, staticText);
+ }
+
+ QImage img = image.toImage();
+ for (int x=0; x<img.width(); ++x) {
+ for (int y=0; y<img.height(); ++y) {
+ QRgb pixel = img.pixel(x, y);
+ QVERIFY(pixel == QColor(Qt::white).rgba()
+ || pixel == QColor(Qt::green).rgba());
+ }
+ }
+}
+
+void tst_QStaticText::setPenRichText()
+{
+ QFont font = QApplication::font();
+ font.setStyleStrategy(QFont::NoAntialias);
+
+ QFontMetricsF fm(font);
+ QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height()));
+ image.fill(Qt::white);
+ {
+ QPainter p(&image);
+ p.setFont(font);
+ p.setPen(Qt::green);
+
+ QStaticText staticText;
+ staticText.setText("<html><body>XXXXX</body></html>");
+ staticText.setTextFormat(Qt::RichText);
+ p.drawStaticText(0, 0, staticText);
+ }
+
+ QImage img = image.toImage();
+ for (int x=0; x<img.width(); ++x) {
+ for (int y=0; y<img.height(); ++y) {
+ QRgb pixel = img.pixel(x, y);
+ QVERIFY(pixel == QColor(Qt::white).rgba()
+ || pixel == QColor(Qt::green).rgba());
+ }
+ }
+}
+
+void tst_QStaticText::richTextOverridesPen()
+{
+ QFont font = QApplication::font();
+ font.setStyleStrategy(QFont::NoAntialias);
+
+ QFontMetricsF fm(font);
+ QPixmap image(qCeil(fm.width("XXXXX")), qCeil(fm.height()));
+ image.fill(Qt::white);
+ {
+ QPainter p(&image);
+ p.setFont(font);
+ p.setPen(Qt::green);
+
+ QStaticText staticText;
+ staticText.setText("<html><body><font color=\"#ff0000\">XXXXX</font></body></html>");
+ staticText.setTextFormat(Qt::RichText);
+ p.drawStaticText(0, 0, staticText);
+ }
+
+ QImage img = image.toImage();
+ for (int x=0; x<img.width(); ++x) {
+ for (int y=0; y<img.height(); ++y) {
+ QRgb pixel = img.pixel(x, y);
+ QVERIFY(pixel == QColor(Qt::white).rgba()
+ || pixel == QColor(Qt::red).rgba());
+ }
+ }
+}
+
+void tst_QStaticText::drawStruckOutText()
+{
+ QPixmap imageDrawText(1000, 1000);
+ QPixmap imageDrawStaticText(1000, 1000);
+
+ imageDrawText.fill(Qt::white);
+ imageDrawStaticText.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setStrikeOut(true);
+
+ {
+ QPainter p(&imageDrawText);
+ p.setFont(font);
+ p.drawText(QPointF(50, 50), s);
+ }
+
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text = QStaticText(s);
+ p.setFont(font);
+ p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("drawStruckOutText_imageDrawText.png");
+ imageDrawStaticText.save("drawStruckOutText_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawText, imageDrawStaticText);
+}
+
+void tst_QStaticText::drawOverlinedText()
+{
+ QPixmap imageDrawText(1000, 1000);
+ QPixmap imageDrawStaticText(1000, 1000);
+
+ imageDrawText.fill(Qt::white);
+ imageDrawStaticText.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setOverline(true);
+
+ {
+ QPainter p(&imageDrawText);
+ p.setFont(font);
+ p.drawText(QPointF(50, 50), s);
+ }
+
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text = QStaticText(s);
+ p.setFont(font);
+ p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("drawOverlinedText_imageDrawText.png");
+ imageDrawStaticText.save("drawOverlinedText_imageDrawStaticText.png");
+#endif
+
+ QVERIFY(imageDrawText.toImage() != m_whiteSquare);
+ QCOMPARE(imageDrawText, imageDrawStaticText);
+}
+
+void tst_QStaticText::drawUnderlinedText()
+{
+ QPixmap imageDrawText(1000, 1000);
+ QPixmap imageDrawStaticText(1000, 1000);
+
+ imageDrawText.fill(Qt::white);
+ imageDrawStaticText.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setUnderline(true);
+
+ {
+ QPainter p(&imageDrawText);
+ p.setFont(font);
+ p.drawText(QPointF(50, 50), s);
+ }
+
+ {
+ QPainter p(&imageDrawStaticText);
+ QStaticText text = QStaticText(s);
+ p.setFont(font);
+ p.drawStaticText(QPointF(50, 50 - QFontMetricsF(p.font()).ascent()), text);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ imageDrawText.save("drawUnderlinedText_imageDrawText.png");
+ imageDrawStaticText.save("drawUnderlinedText_imageDrawStaticText.png");
+#endif
+
+ QCOMPARE(imageDrawText, imageDrawStaticText);
+}
+
+void tst_QStaticText::unprintableCharacter_qtbug12614()
+{
+ QString s(QChar(0x200B)); // U+200B, ZERO WIDTH SPACE
+
+ QStaticText staticText(s);
+
+ QVERIFY(staticText.size().isValid()); // Force layout. Should not crash.
+}
+
+void tst_QStaticText::underlinedColor_qtbug20159()
+{
+ QString multiScriptText;
+ multiScriptText += QChar(0x0410); // Cyrillic 'A'
+ multiScriptText += QLatin1Char('A');
+
+ QStaticText staticText(multiScriptText);
+
+ QFont font;
+ font.setUnderline(true);
+
+ staticText.prepare(QTransform(), font);
+
+ QStaticTextPrivate *d = QStaticTextPrivate::get(&staticText);
+ QCOMPARE(d->itemCount, 2);
+
+ // The pen should not be marked as dirty when drawing the underline
+ QVERIFY(!d->items[0].color.isValid());
+ QVERIFY(!d->items[1].color.isValid());
+}
+
+void tst_QStaticText::textDocumentColor()
+{
+ QStaticText staticText("A<font color=\"red\">B</font>");
+ staticText.setTextFormat(Qt::RichText);
+ staticText.prepare();
+
+ QStaticTextPrivate *d = QStaticTextPrivate::get(&staticText);
+ QCOMPARE(d->itemCount, 2);
+
+ // The pen should not be marked as dirty when drawing the underline
+ QVERIFY(!d->items[0].color.isValid());
+ QVERIFY(d->items[1].color.isValid());
+
+ QCOMPARE(d->items[1].color, QColor(Qt::red));
+}
+
+QTEST_MAIN(tst_QStaticText)
+#include "tst_qstatictext.moc"
diff --git a/tests/auto/gui/text/qsyntaxhighlighter/.gitignore b/tests/auto/gui/text/qsyntaxhighlighter/.gitignore
new file mode 100644
index 0000000000..3efe6efbe5
--- /dev/null
+++ b/tests/auto/gui/text/qsyntaxhighlighter/.gitignore
@@ -0,0 +1 @@
+tst_qsyntaxhighlighter
diff --git a/tests/auto/gui/text/qsyntaxhighlighter/qsyntaxhighlighter.pro b/tests/auto/gui/text/qsyntaxhighlighter/qsyntaxhighlighter.pro
new file mode 100644
index 0000000000..30fb3a5283
--- /dev/null
+++ b/tests/auto/gui/text/qsyntaxhighlighter/qsyntaxhighlighter.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qsyntaxhighlighter.cpp
+
+
diff --git a/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp b/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp
new file mode 100644
index 0000000000..24ade2dc91
--- /dev/null
+++ b/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp
@@ -0,0 +1,549 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include <QTextDocument>
+#include <QTextLayout>
+#include <QDebug>
+#include <QAbstractTextDocumentLayout>
+#include <QSyntaxHighlighter>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+//
+class QTestDocumentLayout : public QAbstractTextDocumentLayout
+{
+ Q_OBJECT
+public:
+ inline QTestDocumentLayout(QTextDocument *doc)
+ : QAbstractTextDocumentLayout(doc), documentChangedCalled(false) {}
+
+ virtual void draw(QPainter *, const QAbstractTextDocumentLayout::PaintContext &) {}
+
+ virtual int hitTest(const QPointF &, Qt::HitTestAccuracy ) const { return 0; }
+
+ virtual void documentChanged(int, int, int) { documentChangedCalled = true; }
+
+ virtual int pageCount() const { return 1; }
+
+ virtual QSizeF documentSize() const { return QSize(); }
+
+ virtual QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
+ virtual QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
+
+ bool documentChangedCalled;
+};
+
+class tst_QSyntaxHighlighter : public QObject
+{
+ Q_OBJECT
+public:
+ inline tst_QSyntaxHighlighter() {}
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void basic();
+ void basicTwo();
+ void removeFormatsOnDelete();
+ void emptyBlocks();
+ void setCharFormat();
+ void highlightOnInit();
+ void stopHighlightingWhenStateDoesNotChange();
+ void unindent();
+ void highlightToEndOfDocument();
+ void highlightToEndOfDocument2();
+ void preservePreeditArea();
+ void task108530();
+ void avoidUnnecessaryRehighlight();
+ void noContentsChangedDuringHighlight();
+ void rehighlight();
+ void rehighlightBlock();
+
+private:
+ QTextDocument *doc;
+ QTestDocumentLayout *lout;
+ QTextCursor cursor;
+};
+
+void tst_QSyntaxHighlighter::init()
+{
+ doc = new QTextDocument;
+ lout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(lout);
+ cursor = QTextCursor(doc);
+}
+
+void tst_QSyntaxHighlighter::cleanup()
+{
+ delete doc;
+ doc = 0;
+}
+
+class TestHighlighter : public QSyntaxHighlighter
+{
+public:
+ inline TestHighlighter(const QList<QTextLayout::FormatRange> &fmts, QTextDocument *parent)
+ : QSyntaxHighlighter(parent), formats(fmts), highlighted(false), callCount(0) {}
+ inline TestHighlighter(QTextDocument *parent)
+ : QSyntaxHighlighter(parent), highlighted(false), callCount(0) {}
+
+ virtual void highlightBlock(const QString &text)
+ {
+ for (int i = 0; i < formats.count(); ++i) {
+ const QTextLayout::FormatRange &range = formats.at(i);
+ setFormat(range.start, range.length, range.format);
+ }
+ highlighted = true;
+ highlightedText += text;
+ ++callCount;
+ }
+
+ QList<QTextLayout::FormatRange> formats;
+ bool highlighted;
+ int callCount;
+ QString highlightedText;
+};
+
+QT_BEGIN_NAMESPACE
+bool operator==(const QTextLayout::FormatRange &lhs, const QTextLayout::FormatRange &rhs)
+{
+ return lhs.start == rhs.start
+ && lhs.length == rhs.length
+ && lhs.format == rhs.format;
+}
+QT_END_NAMESPACE
+
+void tst_QSyntaxHighlighter::basic()
+{
+ QList<QTextLayout::FormatRange> formats;
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 2;
+ range.format.setForeground(Qt::blue);
+ formats.append(range);
+
+ range.start = 4;
+ range.length = 2;
+ range.format.setFontItalic(true);
+ formats.append(range);
+
+ range.start = 9;
+ range.length = 2;
+ range.format.setFontUnderline(true);
+ formats.append(range);
+
+ TestHighlighter *hl = new TestHighlighter(formats, doc);
+
+ lout->documentChangedCalled = false;
+ doc->setPlainText("Hello World");
+ QVERIFY(hl->highlighted);
+ QVERIFY(lout->documentChangedCalled);
+
+ QVERIFY(doc->begin().layout()->additionalFormats() == formats);
+}
+
+class CommentTestHighlighter : public QSyntaxHighlighter
+{
+public:
+ inline CommentTestHighlighter(QTextDocument *parent)
+ : QSyntaxHighlighter(parent), highlighted(false) {}
+
+ inline void reset()
+ {
+ highlighted = false;
+ }
+
+ virtual void highlightBlock(const QString &text)
+ {
+ QTextCharFormat commentFormat;
+ commentFormat.setForeground(Qt::darkGreen);
+ commentFormat.setFontWeight(QFont::StyleItalic);
+ commentFormat.setFontFixedPitch(true);
+ int textLength = text.length();
+
+ if (text.startsWith(QLatin1Char(';'))){
+ // The entire line is a comment
+ setFormat(0, textLength, commentFormat);
+ highlighted = true;
+ }
+ }
+ bool highlighted;
+};
+
+
+void tst_QSyntaxHighlighter::basicTwo()
+{
+ // Done for task 104409
+ CommentTestHighlighter *hl = new CommentTestHighlighter(doc);
+ doc->setPlainText("; a test");
+ QVERIFY(hl->highlighted);
+ QVERIFY(lout->documentChangedCalled);
+}
+
+void tst_QSyntaxHighlighter::removeFormatsOnDelete()
+{
+ QList<QTextLayout::FormatRange> formats;
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 9;
+ range.format.setForeground(Qt::blue);
+ formats.append(range);
+
+ TestHighlighter *hl = new TestHighlighter(formats, doc);
+
+ lout->documentChangedCalled = false;
+ doc->setPlainText("Hello World");
+ QVERIFY(hl->highlighted);
+ QVERIFY(lout->documentChangedCalled);
+
+ lout->documentChangedCalled = false;
+ QVERIFY(!doc->begin().layout()->additionalFormats().isEmpty());
+ delete hl;
+ QVERIFY(doc->begin().layout()->additionalFormats().isEmpty());
+ QVERIFY(lout->documentChangedCalled);
+}
+
+void tst_QSyntaxHighlighter::emptyBlocks()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+
+ cursor.insertText("Foo");
+ cursor.insertBlock();
+ cursor.insertBlock();
+ hl->highlighted = false;
+ cursor.insertBlock();
+ QVERIFY(hl->highlighted);
+}
+
+void tst_QSyntaxHighlighter::setCharFormat()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+
+ cursor.insertText("FooBar");
+ cursor.insertBlock();
+ cursor.insertText("Blah");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ QTextCharFormat fmt;
+ fmt.setFontItalic(true);
+ hl->highlighted = false;
+ hl->callCount = 0;
+ cursor.mergeCharFormat(fmt);
+ QVERIFY(hl->highlighted);
+ QCOMPARE(hl->callCount, 2);
+}
+
+void tst_QSyntaxHighlighter::highlightOnInit()
+{
+ cursor.insertText("Hello");
+ cursor.insertBlock();
+ cursor.insertText("World");
+
+ TestHighlighter *hl = new TestHighlighter(doc);
+ QTest::qWait(100);
+ QVERIFY(hl->highlighted);
+}
+
+class StateTestHighlighter : public QSyntaxHighlighter
+{
+public:
+ inline StateTestHighlighter(QTextDocument *parent)
+ : QSyntaxHighlighter(parent), state(0), highlighted(false) {}
+
+ inline void reset()
+ {
+ highlighted = false;
+ state = 0;
+ }
+
+ virtual void highlightBlock(const QString &text)
+ {
+ highlighted = true;
+ if (text == QLatin1String("changestate"))
+ setCurrentBlockState(state++);
+ }
+
+ int state;
+ bool highlighted;
+};
+
+void tst_QSyntaxHighlighter::stopHighlightingWhenStateDoesNotChange()
+{
+ cursor.insertText("state");
+ cursor.insertBlock();
+ cursor.insertText("changestate");
+ cursor.insertBlock();
+ cursor.insertText("keepstate");
+ cursor.insertBlock();
+ cursor.insertText("changestate");
+ cursor.insertBlock();
+ cursor.insertText("changestate");
+
+ StateTestHighlighter *hl = new StateTestHighlighter(doc);
+ QTest::qWait(100);
+ QVERIFY(hl->highlighted);
+
+ hl->reset();
+
+ // turn the text of the first block into 'changestate'
+ cursor.movePosition(QTextCursor::Start);
+ cursor.insertText("change");
+
+ // verify that we highlighted only to the 'keepstate' block,
+ // not beyond
+ QCOMPARE(hl->state, 2);
+}
+
+void tst_QSyntaxHighlighter::unindent()
+{
+ const QString spaces(" ");
+ const QString text("Foobar");
+ QString plainText;
+ for (int i = 0; i < 5; ++i) {
+ cursor.insertText(spaces + text);
+ cursor.insertBlock();
+
+ plainText += spaces;
+ plainText += text;
+ plainText += QLatin1Char('\n');
+ }
+ QCOMPARE(doc->toPlainText(), plainText);
+
+ TestHighlighter *hl = new TestHighlighter(doc);
+ hl->callCount = 0;
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.beginEditBlock();
+
+ plainText.clear();
+ for (int i = 0; i < 5; ++i) {
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
+ cursor.removeSelectedText();
+ cursor.movePosition(QTextCursor::NextBlock);
+
+ plainText += text;
+ plainText += QLatin1Char('\n');
+ }
+
+ cursor.endEditBlock();
+ QCOMPARE(doc->toPlainText(), plainText);
+ QCOMPARE(hl->callCount, 5);
+}
+
+void tst_QSyntaxHighlighter::highlightToEndOfDocument()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+ hl->callCount = 0;
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.beginEditBlock();
+
+ cursor.insertText("Hello");
+ cursor.insertBlock();
+ cursor.insertBlock();
+ cursor.insertText("World");
+ cursor.insertBlock();
+
+ cursor.endEditBlock();
+
+ QCOMPARE(hl->callCount, 4);
+}
+
+void tst_QSyntaxHighlighter::highlightToEndOfDocument2()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+ hl->callCount = 0;
+
+ cursor.movePosition(QTextCursor::End);
+ cursor.beginEditBlock();
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::AlignLeft);
+ cursor.setBlockFormat(fmt);
+ cursor.insertText("Three\nLines\nHere");
+ cursor.endEditBlock();
+
+ QCOMPARE(hl->callCount, 3);
+}
+
+void tst_QSyntaxHighlighter::preservePreeditArea()
+{
+ QList<QTextLayout::FormatRange> formats;
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 8;
+ range.format.setForeground(Qt::blue);
+ formats << range;
+ range.start = 9;
+ range.length = 1;
+ range.format.setForeground(Qt::red);
+ formats << range;
+ TestHighlighter *hl = new TestHighlighter(formats, doc);
+
+ doc->setPlainText("Hello World");
+ cursor.movePosition(QTextCursor::Start);
+
+ QTextLayout *layout = cursor.block().layout();
+
+ layout->setPreeditArea(5, QString("foo"));
+ range.start = 5;
+ range.length = 3;
+ range.format.setFontUnderline(true);
+ formats.clear();
+ formats << range;
+
+ hl->callCount = 0;
+
+ cursor.beginEditBlock();
+ layout->setAdditionalFormats(formats);
+ cursor.endEditBlock();
+
+ QCOMPARE(hl->callCount, 1);
+
+ formats = layout->additionalFormats();
+ QCOMPARE(formats.count(), 3);
+
+ range = formats.at(0);
+
+ QCOMPARE(range.start, 5);
+ QCOMPARE(range.length, 3);
+ QVERIFY(range.format.fontUnderline());
+
+ range = formats.at(1);
+ QCOMPARE(range.start, 0);
+ QCOMPARE(range.length, 8 + 3);
+
+ range = formats.at(2);
+ QCOMPARE(range.start, 9 + 3);
+ QCOMPARE(range.length, 1);
+}
+
+void tst_QSyntaxHighlighter::task108530()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+
+ cursor.insertText("test");
+ hl->callCount = 0;
+ hl->highlightedText.clear();
+ cursor.movePosition(QTextCursor::Start);
+ cursor.insertBlock();
+
+ QCOMPARE(hl->highlightedText, QString("test"));
+ QCOMPARE(hl->callCount, 2);
+}
+
+void tst_QSyntaxHighlighter::avoidUnnecessaryRehighlight()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+ QVERIFY(!hl->highlighted);
+
+ doc->setPlainText("Hello World");
+ QVERIFY(hl->highlighted);
+
+ hl->highlighted = false;
+ QTest::qWait(100);
+ QVERIFY(!hl->highlighted);
+}
+
+void tst_QSyntaxHighlighter::noContentsChangedDuringHighlight()
+{
+ QList<QTextLayout::FormatRange> formats;
+ QTextLayout::FormatRange range;
+ range.start = 0;
+ range.length = 10;
+ range.format.setForeground(Qt::blue);
+ formats.append(range);
+
+ TestHighlighter *hl = new TestHighlighter(formats, doc);
+
+ lout->documentChangedCalled = false;
+ QTextCursor cursor(doc);
+
+ QSignalSpy contentsChangedSpy(doc, SIGNAL(contentsChanged()));
+ cursor.insertText("Hello World");
+
+ QCOMPARE(contentsChangedSpy.count(), 1);
+ QVERIFY(hl->highlighted);
+ QVERIFY(lout->documentChangedCalled);
+}
+
+void tst_QSyntaxHighlighter::rehighlight()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+ hl->callCount = 0;
+ doc->setPlainText("Hello");
+ hl->callCount = 0;
+ hl->rehighlight();
+ QCOMPARE(hl->callCount, 1);
+}
+
+void tst_QSyntaxHighlighter::rehighlightBlock()
+{
+ TestHighlighter *hl = new TestHighlighter(doc);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.beginEditBlock();
+ cursor.insertText("Hello");
+ cursor.insertBlock();
+ cursor.insertText("World");
+ cursor.endEditBlock();
+
+ hl->callCount = 0;
+ hl->highlightedText.clear();
+ QTextBlock block = doc->begin();
+ hl->rehighlightBlock(block);
+
+ QCOMPARE(hl->highlightedText, QString("Hello"));
+ QCOMPARE(hl->callCount, 1);
+
+ hl->callCount = 0;
+ hl->highlightedText.clear();
+ hl->rehighlightBlock(block.next());
+
+ QCOMPARE(hl->highlightedText, QString("World"));
+ QCOMPARE(hl->callCount, 1);
+}
+
+QTEST_MAIN(tst_QSyntaxHighlighter)
+#include "tst_qsyntaxhighlighter.moc"
diff --git a/tests/auto/gui/text/qtextblock/.gitignore b/tests/auto/gui/text/qtextblock/.gitignore
new file mode 100644
index 0000000000..648a522140
--- /dev/null
+++ b/tests/auto/gui/text/qtextblock/.gitignore
@@ -0,0 +1 @@
+tst_qtextblock
diff --git a/tests/auto/gui/text/qtextblock/qtextblock.pro b/tests/auto/gui/text/qtextblock/qtextblock.pro
new file mode 100644
index 0000000000..d50ef5ca78
--- /dev/null
+++ b/tests/auto/gui/text/qtextblock/qtextblock.pro
@@ -0,0 +1,9 @@
+load(qttest_p4)
+
+QT += widgets widgets-private
+QT += core-private gui-private
+
+SOURCES += tst_qtextblock.cpp
+
+
+
diff --git a/tests/auto/gui/text/qtextblock/tst_qtextblock.cpp b/tests/auto/gui/text/qtextblock/tst_qtextblock.cpp
new file mode 100644
index 0000000000..b04a6f5559
--- /dev/null
+++ b/tests/auto/gui/text/qtextblock/tst_qtextblock.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#define protected public
+#include <qtextdocument.h>
+#undef protected
+#include <qdebug.h>
+#ifndef Q_WS_WIN
+#include <private/qtextdocument_p.h>
+#endif
+
+
+
+#include <qtextobject.h>
+#include <qtextcursor.h>
+
+
+//TESTED_FILES=
+
+QT_FORWARD_DECLARE_CLASS(QTextDocument)
+
+class tst_QTextBlock : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextBlock();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void fragmentOverBlockBoundaries();
+ void excludeParagraphSeparatorFragment();
+ void backwardsBlockIterator();
+ void previousBlock_qtbug18026();
+ void removedBlock_qtbug18500();
+
+private:
+ QTextDocument *doc;
+ QTextCursor cursor;
+};
+
+tst_QTextBlock::tst_QTextBlock()
+{}
+
+void tst_QTextBlock::init()
+{
+ doc = new QTextDocument;
+ cursor = QTextCursor(doc);
+}
+
+void tst_QTextBlock::cleanup()
+{
+ cursor = QTextCursor();
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextBlock::fragmentOverBlockBoundaries()
+{
+ /* this creates two fragments in the piecetable:
+ * 1) 'hello<parag separator here>world'
+ * 2) '<parag separator>'
+ * (they are not united because the former was interested after the latter,
+ * hence their position in the pt buffer is the other way around)
+ */
+ cursor.insertText("Hello");
+ cursor.insertBlock();
+ cursor.insertText("World");
+
+ cursor.movePosition(QTextCursor::Start);
+
+ const QTextDocument *doc = cursor.block().document();
+ QVERIFY(doc);
+ // Block separators are always a fragment of their self. Thus:
+ // |Hello|\b|World|\b|
+#if !defined(Q_WS_WIN) && !defined(Q_WS_S60)
+ QVERIFY(doc->docHandle()->fragmentMap().numNodes() == 4);
+#endif
+ QCOMPARE(cursor.block().text(), QString("Hello"));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString("World"));
+}
+
+void tst_QTextBlock::excludeParagraphSeparatorFragment()
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ cursor.insertText("Hello", fmt);
+
+ QTextBlock block = doc->begin();
+ QVERIFY(block.isValid());
+
+ QTextBlock::Iterator it = block.begin();
+
+ QTextFragment fragment = it.fragment();
+ QVERIFY(fragment.isValid());
+ QCOMPARE(fragment.text(), QString("Hello"));
+
+ ++it;
+ QVERIFY(it.atEnd());
+ QVERIFY(it == block.end());
+}
+
+void tst_QTextBlock::backwardsBlockIterator()
+{
+ QTextCharFormat fmt;
+
+ fmt.setForeground(Qt::magenta);
+ cursor.insertText("A", fmt);
+
+ fmt.setForeground(Qt::red);
+ cursor.insertText("A", fmt);
+
+ fmt.setForeground(Qt::magenta);
+ cursor.insertText("A", fmt);
+
+ QTextBlock block = doc->begin();
+ QVERIFY(block.isValid());
+
+ QTextBlock::Iterator it = block.begin();
+ QCOMPARE(it.fragment().position(), 0);
+ ++it;
+ QCOMPARE(it.fragment().position(), 1);
+ ++it;
+
+ QCOMPARE(it.fragment().position(), 2);
+
+ --it;
+ QCOMPARE(it.fragment().position(), 1);
+ --it;
+ QCOMPARE(it.fragment().position(), 0);
+}
+
+void tst_QTextBlock::previousBlock_qtbug18026()
+{
+ QTextBlock last = doc->end().previous();
+ QVERIFY(last.isValid());
+}
+
+void tst_QTextBlock::removedBlock_qtbug18500()
+{
+ cursor.insertText("line 1\nline 2\nline 3 \nline 4\n");
+ cursor.setPosition(7);
+ QTextBlock block = cursor.block();
+ cursor.setPosition(21, QTextCursor::KeepAnchor);
+
+ cursor.removeSelectedText();
+ QVERIFY(!block.isValid());
+}
+
+QTEST_MAIN(tst_QTextBlock)
+#include "tst_qtextblock.moc"
diff --git a/tests/auto/gui/text/qtextcursor/.gitignore b/tests/auto/gui/text/qtextcursor/.gitignore
new file mode 100644
index 0000000000..b9b1f8e7d6
--- /dev/null
+++ b/tests/auto/gui/text/qtextcursor/.gitignore
@@ -0,0 +1 @@
+tst_qtextcursor
diff --git a/tests/auto/gui/text/qtextcursor/qtextcursor.pro b/tests/auto/gui/text/qtextcursor/qtextcursor.pro
new file mode 100644
index 0000000000..828b90ca16
--- /dev/null
+++ b/tests/auto/gui/text/qtextcursor/qtextcursor.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+SOURCES += tst_qtextcursor.cpp
+
+
+
diff --git a/tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp b/tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp
new file mode 100644
index 0000000000..2b0ba422e0
--- /dev/null
+++ b/tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp
@@ -0,0 +1,1862 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qtextdocument.h>
+#include <qtexttable.h>
+#include <qvariant.h>
+#include <qtextdocumentfragment.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qtextlayout.h>
+#include <qtextcursor.h>
+#include <qdebug.h>
+
+//TESTED_FILES=gui/text/qtextcursor.cpp gui/text/qtextcursor_p.h
+
+QT_FORWARD_DECLARE_CLASS(QTextDocument)
+
+class tst_QTextCursor : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextCursor();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void navigation1();
+ void navigation2_data();
+ void navigation2();
+ void navigation3();
+ void navigation4();
+ void navigation5();
+ void navigation6();
+ void navigation7();
+ void navigation8();
+ void navigation9();
+ void navigation10();
+ void movePositionEndOfLine();
+ void insertBlock();
+ void insertWithBlockSeparator1();
+ void insertWithBlockSeparator2();
+ void insertWithBlockSeparator3();
+ void insertWithBlockSeparator4();
+ void clearObjectType1();
+ void clearObjectType2();
+ void clearObjectType3();
+ void comparisonOperators1();
+ void comparisonOperators2();
+ void selection1();
+ void dontCopyTableAttributes();
+
+ void checkFrame1();
+ void checkFrame2();
+
+ void tableMovement();
+ void selectionsInTable();
+
+ void insertBlockToUseCharFormat();
+
+ void selectedText();
+
+ void insertBlockShouldRemoveSelection();
+ void insertBlockShouldRemoveSelection2();
+ void mergeCellShouldUpdateSelection();
+
+ void joinPreviousEditBlock();
+
+ void setBlockFormatInTable();
+
+ void blockCharFormat();
+ void blockCharFormat2();
+ void blockCharFormat3();
+ void blockCharFormatOnSelection();
+
+ void anchorInitialized1();
+ void anchorInitialized2();
+ void anchorInitialized3();
+
+ void selectWord();
+ void selectWordWithSeparators_data();
+ void selectWordWithSeparators();
+ void startOfWord();
+ void selectBlock();
+ void selectVisually();
+
+ void insertText();
+
+ void insertFragmentShouldUseCurrentCharFormat();
+
+ void endOfLine();
+
+ void editBlocksDuringRemove();
+ void selectAllDuringRemove();
+
+ void update_data();
+ void update();
+
+ void disallowSettingObjectIndicesOnCharFormats();
+
+ void blockAndColumnNumber();
+
+ void clearCells();
+
+ void task244408_wordUnderCursor_data();
+ void task244408_wordUnderCursor();
+
+ void adjustCursorsOnInsert();
+
+ void cursorPositionWithBlockUndoAndRedo();
+ void cursorPositionWithBlockUndoAndRedo2();
+ void cursorPositionWithBlockUndoAndRedo3();
+
+private:
+ int blockCount();
+
+ QTextDocument *doc;
+ QTextCursor cursor;
+};
+
+Q_DECLARE_METATYPE(QList<QVariant>)
+
+tst_QTextCursor::tst_QTextCursor()
+{}
+
+void tst_QTextCursor::init()
+{
+ doc = new QTextDocument;
+ cursor = QTextCursor(doc);
+}
+
+void tst_QTextCursor::cleanup()
+{
+ cursor = QTextCursor();
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextCursor::navigation1()
+{
+
+ cursor.insertText("Hello World");
+ QVERIFY(doc->toPlainText() == "Hello World");
+
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.position() == 11);
+ cursor.deletePreviousChar();
+ QVERIFY(cursor.position() == 10);
+ cursor.deletePreviousChar();
+ cursor.deletePreviousChar();
+ cursor.deletePreviousChar();
+ cursor.deletePreviousChar();
+ cursor.deletePreviousChar();
+ QVERIFY(doc->toPlainText() == "Hello");
+
+ QTextCursor otherCursor(doc);
+ otherCursor.movePosition(QTextCursor::Start);
+ otherCursor.movePosition(QTextCursor::Right);
+ cursor = otherCursor;
+ cursor.movePosition(QTextCursor::Right);
+ QVERIFY(cursor != otherCursor);
+ otherCursor.insertText("Hey");
+ QVERIFY(cursor.position() == 5);
+
+ doc->undo();
+ QVERIFY(cursor.position() == 2);
+ doc->redo();
+ QVERIFY(cursor.position() == 5);
+
+ doc->undo();
+
+ doc->undo();
+ QVERIFY(doc->toPlainText() == "Hello World");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 6);
+ QVERIFY(cursor.position() == 6);
+ otherCursor = cursor;
+ otherCursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+ otherCursor.deletePreviousChar();
+ otherCursor.deletePreviousChar();
+ otherCursor.deletePreviousChar();
+ QVERIFY(cursor.position() == 5);
+
+ cursor.movePosition(QTextCursor::End);
+ cursor.insertBlock();
+ {
+ int oldPos = cursor.position();
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.position() == oldPos);
+ }
+ QVERIFY(cursor.atBlockStart());
+ QVERIFY(cursor.position() == 9);
+
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ cursor.insertText("Test", fmt);
+ QVERIFY(fmt == cursor.charFormat());
+ QVERIFY(cursor.position() == 13);
+}
+
+void tst_QTextCursor::navigation2_data()
+{
+ QTest::addColumn<QStringList>("sl");
+ QTest::addColumn<QList<QVariant> >("movement");
+ QTest::addColumn<int>("finalPos");
+
+ QTest::newRow("startBlock1") << QStringList("Happy happy happy joy joy joy")
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)) << 0;
+ QTest::newRow("endBlock1") << QStringList("Happy happy happy joy joy joy")
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::EndOfBlock)) << 29;
+ QTest::newRow("startBlock2") << QStringList("Happy happy happy joy joy joy")
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::EndOfBlock)
+ << QVariant(QTextCursor::StartOfBlock)) << 0;
+ QTest::newRow("endBlock2") << QStringList("Happy happy happy joy joy joy")
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::EndOfBlock)
+ << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::EndOfBlock)
+ ) << 29;
+ QTest::newRow("multiBlock1") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock))
+ << 18;
+ QTest::newRow("multiBlock2") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::EndOfBlock))
+ << 29;
+ QTest::newRow("multiBlock3") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::StartOfBlock)
+ << QVariant(QTextCursor::StartOfBlock))
+ << 18;
+ QTest::newRow("multiBlock4") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::Start)
+ << QVariant(QTextCursor::EndOfBlock))
+ << 17;
+ QTest::newRow("multiBlock5") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::Start)
+ << QVariant(QTextCursor::EndOfBlock)
+ << QVariant(QTextCursor::EndOfBlock))
+ << 17;
+ QTest::newRow("multiBlock6") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::End)
+ << QVariant(QTextCursor::StartOfBlock))
+ << 18;
+ QTest::newRow("multiBlock7") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousBlock))
+ << 0;
+ QTest::newRow("multiBlock8") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousBlock)
+ << QVariant(QTextCursor::EndOfBlock))
+ << 17;
+ QTest::newRow("multiBlock9") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousBlock)
+ << QVariant(QTextCursor::NextBlock))
+ << 18;
+ QTest::newRow("multiBlock10") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousBlock)
+ << QVariant(QTextCursor::NextBlock)
+ << QVariant(QTextCursor::NextBlock))
+ << 18;
+ QTest::newRow("multiBlock11") << (QStringList() << QString("Happy happy happy")
+ << QString("Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousBlock)
+ << QVariant(QTextCursor::NextBlock)
+ << QVariant(QTextCursor::EndOfBlock))
+ << 29;
+ QTest::newRow("PreviousWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousWord))
+ << 26;
+ QTest::newRow("PreviousWord2") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::PreviousWord))
+ << 22;
+ QTest::newRow("EndWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::EndOfWord))
+ << 25;
+ QTest::newRow("NextWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::NextWord))
+ << 26;
+ QTest::newRow("NextWord2") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::Start)
+ << QVariant(QTextCursor::NextWord)
+ << QVariant(QTextCursor::EndOfWord))
+ << 11;
+ QTest::newRow("StartWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::PreviousWord)
+ << QVariant(QTextCursor::StartOfWord))
+ << 22;
+ QTest::newRow("StartWord3") << (QStringList() << QString("Happy happy happy Joy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::Start)
+ << QVariant(QTextCursor::NextWord)
+ << QVariant(QTextCursor::EndOfWord)
+ << QVariant(QTextCursor::StartOfWord))
+ << 6;
+
+ QTest::newRow("PreviousCharacter") << (QStringList() << QString("Happy happy Joy Joy"))
+ << (QList<QVariant>() << QVariant(QTextCursor::PreviousCharacter)
+ << QVariant(QTextCursor::PreviousCharacter))
+ << 17;
+}
+
+void tst_QTextCursor::navigation2()
+{
+ QFETCH(QStringList, sl);
+ QFETCH(QList<QVariant>, movement);
+ int i;
+ for (i = 0; i < sl.size(); ++i) {
+ cursor.insertText(sl.at(i));
+ if (i < sl.size() - 1)
+ cursor.insertBlock();
+ }
+
+ for (i = 0; i < movement.size(); ++i)
+ cursor.movePosition(QTextCursor::MoveOperation(movement.at(i).toInt()));
+ QTEST(cursor.position(), "finalPos");
+}
+
+void tst_QTextCursor::navigation3()
+{
+ cursor.insertText("a");
+ cursor.deletePreviousChar();
+ QCOMPARE(cursor.position(), 0);
+ QVERIFY(doc->toPlainText().isEmpty());
+}
+
+void tst_QTextCursor::navigation4()
+{
+ cursor.insertText(" Test ");
+
+ cursor.setPosition(4);
+ cursor.movePosition(QTextCursor::EndOfWord);
+ QCOMPARE(cursor.position(), 6);
+}
+
+void tst_QTextCursor::navigation5()
+{
+ cursor.insertText("Test");
+ cursor.insertBlock();
+ cursor.insertText("Test");
+
+ cursor.setPosition(0);
+ cursor.movePosition(QTextCursor::EndOfBlock);
+ QCOMPARE(cursor.position(), 4);
+}
+
+void tst_QTextCursor::navigation6()
+{
+ // triger creation of document layout, so that QTextLines are there
+ doc->documentLayout();
+ doc->setTextWidth(1000);
+
+ cursor.insertText("Test ");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::EndOfLine);
+ QCOMPARE(cursor.position(), 8);
+}
+
+void tst_QTextCursor::navigation7()
+{
+ QVERIFY(doc->isEmpty());
+ for (int i = QTextCursor::Start; i <= QTextCursor::WordRight; ++i)
+ QVERIFY(!cursor.movePosition(QTextCursor::MoveOperation(i)));
+
+ doc->setPlainText("Hello World");
+ cursor.movePosition(QTextCursor::Start);
+ do {
+ } while (cursor.movePosition(QTextCursor::NextCharacter));
+ QVERIFY(true /*reached*/);
+}
+
+void tst_QTextCursor::navigation8()
+{
+ cursor.insertList(QTextListFormat::ListDecimal);
+ QCOMPARE(cursor.position(), 1);
+ cursor.insertText("foo");
+ QCOMPARE(cursor.position(), 4);
+
+ cursor.insertList(QTextListFormat::ListCircle);
+ QCOMPARE(cursor.position(), 5);
+ cursor.insertText("something");
+ QCOMPARE(cursor.position(), 14);
+
+ cursor.movePosition(QTextCursor::PreviousCharacter);
+ QCOMPARE(cursor.position(), 13);
+
+ cursor.setPosition(2);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QCOMPARE(cursor.position(), 3);
+}
+
+void tst_QTextCursor::navigation9()
+{
+ cursor.insertText("Hello &-=+\t World");
+ cursor.movePosition(QTextCursor::PreviousWord);
+ QCOMPARE(cursor.position(), 15);
+ cursor.movePosition(QTextCursor::PreviousWord);
+ QCOMPARE(cursor.position(), 7);
+ cursor.movePosition(QTextCursor::PreviousWord);
+ QCOMPARE(cursor.position(), 0);
+ cursor.movePosition(QTextCursor::NextWord);
+ QCOMPARE(cursor.position(), 7);
+ cursor.movePosition(QTextCursor::NextWord);
+ QCOMPARE(cursor.position(), 15);
+}
+
+void tst_QTextCursor::navigation10()
+{
+ doc->setHtml("<html><p>just a simple paragraph.</p>"
+ "<table>"
+ "<tr><td>Cell number 1</td><td>another cell</td><td></td><td>previous</br>is</br>empty</td></tr>"
+ "<tr><td>row 2</td><td colspan=\"2\">foo bar</td><td>last cell</td></tr>"
+ "<tr><td colspan=\"3\">row 3</td><td>a</td></tr>"
+ "</table></html");
+ QCOMPARE(cursor.position(), 101); // end of document
+ cursor.setPosition(0);
+ QCOMPARE(cursor.position(), 0);
+ bool ok = cursor.movePosition(QTextCursor::EndOfLine);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 24);
+ ok = cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.position(), 25); // cell 1
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 39); // another..
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 52); // empty
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 53); // last on row 1
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 69); // row 2
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 75);
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 83);
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 93); // row 3
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 99);
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok == false);
+ QCOMPARE(cursor.position(), 99); // didn't move.
+ QVERIFY(cursor.currentTable());
+
+ // same thing in reverse...
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 93);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 83);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 75);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 69);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 53);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 52);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 39);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 25);
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(!ok);
+ QCOMPARE(cursor.position(), 25); // can't leave the table
+
+ ok = cursor.movePosition(QTextCursor::NextRow);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 69);
+ ok = cursor.movePosition(QTextCursor::NextRow);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 93);
+ ok = cursor.movePosition(QTextCursor::NextRow);
+ QVERIFY(!ok);
+ QCOMPARE(cursor.position(), 93); // didn't move
+
+ ok = cursor.movePosition(QTextCursor::PreviousRow);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 83); // last col in row 2
+ ok = cursor.movePosition(QTextCursor::PreviousRow);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 53); // last col in row 1
+ ok = cursor.movePosition(QTextCursor::PreviousRow);
+ QVERIFY(!ok);
+ QCOMPARE(cursor.position(), 53);
+
+ // test usecase of jumping over a cell
+ doc->clear();
+ doc->setHtml("<html><table>tr><td rowspan=\"2\">a</td><td>b</td></tr><tr><td>c</td></tr></table></html>");
+ cursor.setPosition(1); // a
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 3); // b
+ ok = cursor.movePosition(QTextCursor::NextCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 5); // c
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 3); // b
+ ok = cursor.movePosition(QTextCursor::PreviousCell);
+ QVERIFY(ok);
+ QCOMPARE(cursor.position(), 1); // a
+}
+
+void tst_QTextCursor::insertBlock()
+{
+ QTextBlockFormat fmt;
+ fmt.setTopMargin(100);
+ cursor.insertBlock(fmt);
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(cursor.blockFormat() == fmt);
+}
+
+void tst_QTextCursor::insertWithBlockSeparator1()
+{
+ QString text = "Hello" + QString(QChar::ParagraphSeparator) + "World";
+
+ cursor.insertText(text);
+
+ cursor.movePosition(QTextCursor::PreviousBlock);
+ QVERIFY(cursor.position() == 0);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 6);
+}
+
+void tst_QTextCursor::insertWithBlockSeparator2()
+{
+ cursor.insertText(QString(QChar::ParagraphSeparator));
+ QVERIFY(cursor.position() == 1);
+}
+
+void tst_QTextCursor::insertWithBlockSeparator3()
+{
+ cursor.insertText(QString(QChar::ParagraphSeparator) + "Hi" + QString(QChar::ParagraphSeparator));
+ QVERIFY(cursor.position() == 4);
+}
+
+void tst_QTextCursor::insertWithBlockSeparator4()
+{
+ cursor.insertText(QString(QChar::ParagraphSeparator) + QString(QChar::ParagraphSeparator));
+ QVERIFY(cursor.position() == 2);
+}
+
+void tst_QTextCursor::clearObjectType1()
+{
+ cursor.insertImage("test.png");
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(cursor.charFormat().isImageFormat());
+ cursor.insertText("Hey");
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(!cursor.charFormat().isImageFormat());
+}
+
+void tst_QTextCursor::clearObjectType2()
+{
+ cursor.insertImage("test.png");
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(cursor.charFormat().isImageFormat());
+ cursor.insertBlock();
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(!cursor.charFormat().isImageFormat());
+}
+
+void tst_QTextCursor::clearObjectType3()
+{
+ // like clearObjectType2 but tests different insertBlock overload
+ cursor.insertImage("test.png");
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(cursor.charFormat().isImageFormat());
+ QTextBlockFormat bfmt;
+ bfmt.setAlignment(Qt::AlignRight);
+ cursor.insertBlock(bfmt);
+ QVERIFY(cursor.charFormat().isValid());
+ QVERIFY(!cursor.charFormat().isImageFormat());
+}
+
+void tst_QTextCursor::comparisonOperators1()
+{
+ cursor.insertText("Hello World");
+
+ cursor.movePosition(QTextCursor::PreviousWord);
+
+ QTextCursor startCursor = cursor;
+ startCursor.movePosition(QTextCursor::Start);
+
+ QVERIFY(startCursor < cursor);
+
+ QTextCursor midCursor = startCursor;
+ midCursor.movePosition(QTextCursor::NextWord);
+
+ QVERIFY(midCursor <= cursor);
+ QVERIFY(midCursor == cursor);
+ QVERIFY(midCursor >= cursor);
+
+ QVERIFY(midCursor > startCursor);
+
+ QVERIFY(midCursor != startCursor);
+ QVERIFY(!(midCursor == startCursor));
+
+ QTextCursor nullCursor;
+
+ QVERIFY(!(startCursor < nullCursor));
+ QVERIFY(!(nullCursor < nullCursor));
+ QVERIFY(nullCursor < startCursor);
+
+ QVERIFY(nullCursor <= startCursor);
+ QVERIFY(!(startCursor <= nullCursor));
+
+ QVERIFY(!(nullCursor >= startCursor));
+ QVERIFY(startCursor >= nullCursor);
+
+ QVERIFY(!(nullCursor > startCursor));
+ QVERIFY(!(nullCursor > nullCursor));
+ QVERIFY(startCursor > nullCursor);
+}
+
+void tst_QTextCursor::comparisonOperators2()
+{
+ QTextDocument doc1;
+ QTextDocument doc2;
+
+ QTextCursor cursor1(&doc1);
+ QTextCursor cursor2(&doc2);
+
+ QVERIFY(cursor1 != cursor2);
+ QVERIFY(cursor1 == QTextCursor(&doc1));
+}
+
+void tst_QTextCursor::selection1()
+{
+ cursor.insertText("Hello World");
+
+ cursor.setPosition(0);
+ cursor.clearSelection();
+ cursor.setPosition(4, QTextCursor::KeepAnchor);
+
+ QCOMPARE(cursor.selectionStart(), 0);
+ QCOMPARE(cursor.selectionEnd(), 4);
+}
+
+void tst_QTextCursor::dontCopyTableAttributes()
+{
+ /* when pressing 'enter' inside a cell it shouldn't
+ * enlarge the table by adding another cell but just
+ * extend the cell */
+ QTextTable *table = cursor.insertTable(2, 2);
+ QVERIFY(cursor == table->cellAt(0, 0).firstCursorPosition());
+ cursor.insertBlock();
+ QCOMPARE(table->columns(), 2);
+}
+
+void tst_QTextCursor::checkFrame1()
+{
+ QVERIFY(cursor.position() == 0);
+ QPointer<QTextFrame> frame = cursor.insertFrame(QTextFrameFormat());
+ QVERIFY(frame != 0);
+
+ QTextFrame *root = frame->parentFrame();
+ QVERIFY(root != 0);
+
+ QVERIFY(frame->firstPosition() == 1);
+ QVERIFY(frame->lastPosition() == 1);
+ QVERIFY(frame->parentFrame() != 0);
+ QVERIFY(root->childFrames().size() == 1);
+
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(cursor.selectionStart() == 1);
+ QVERIFY(cursor.selectionEnd() == 1);
+
+ doc->undo();
+
+ QVERIFY(!frame);
+ QVERIFY(root->childFrames().size() == 0);
+
+ QVERIFY(cursor.position() == 0);
+ QVERIFY(cursor.selectionStart() == 0);
+ QVERIFY(cursor.selectionEnd() == 0);
+
+ doc->redo();
+
+ frame = doc->frameAt(1);
+
+ QVERIFY(frame);
+ QVERIFY(frame->firstPosition() == 1);
+ QVERIFY(frame->lastPosition() == 1);
+ QVERIFY(frame->parentFrame() != 0);
+ QVERIFY(root->childFrames().size() == 1);
+
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(cursor.selectionStart() == 1);
+ QVERIFY(cursor.selectionEnd() == 1);
+
+// cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
+// QVERIFY(cursor.position() == 2);
+// QVERIFY(cursor.selectionStart() == 0);
+// QVERIFY(cursor.selectionEnd() == 2);
+}
+
+void tst_QTextCursor::checkFrame2()
+{
+ QVERIFY(cursor.position() == 0);
+ cursor.insertText("A");
+ QVERIFY(cursor.position() == 1);
+ cursor.movePosition(QTextCursor::Start, QTextCursor::KeepAnchor);
+
+ QPointer<QTextFrame> frame = cursor.insertFrame(QTextFrameFormat());
+ QTextFrame *root = frame->parentFrame();
+
+ QVERIFY(frame->firstPosition() == 1);
+ QVERIFY(frame->lastPosition() == 2);
+ QVERIFY(frame->parentFrame() != 0);
+ QVERIFY(root->childFrames().size() == 1);
+
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(cursor.selectionStart() == 1);
+ QVERIFY(cursor.selectionEnd() == 2);
+
+ doc->undo();
+
+ QVERIFY(!frame);
+ QVERIFY(root->childFrames().size() == 0);
+
+ QVERIFY(cursor.position() == 0);
+ QVERIFY(cursor.selectionStart() == 0);
+ QVERIFY(cursor.selectionEnd() == 1);
+
+ doc->redo();
+
+ frame = doc->frameAt(1);
+
+ QVERIFY(frame);
+ QVERIFY(frame->firstPosition() == 1);
+ QVERIFY(frame->lastPosition() == 2);
+ QVERIFY(frame->parentFrame() != 0);
+ QVERIFY(root->childFrames().size() == 1);
+
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(cursor.selectionStart() == 1);
+ QVERIFY(cursor.selectionEnd() == 2);
+
+ cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
+ QVERIFY(cursor.position() == 0);
+ QVERIFY(cursor.selectionStart() == 0);
+ QVERIFY(cursor.selectionEnd() == 3);
+}
+
+void tst_QTextCursor::insertBlockToUseCharFormat()
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ cursor.insertText("Hello", fmt);
+ QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue));
+
+ cursor.insertBlock();
+ QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue));
+
+ fmt.setForeground(Qt::red);
+ cursor.insertText("Hello\nWorld", fmt);
+ cursor.insertText("Blah");
+ QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::red));
+
+ // ### we might want a testcase for createTable, too, as it calls insertBlock, too,
+ // and we might want to have the char format copied (the one that gets inserted
+ // as table separators, that are undeletable)
+}
+
+void tst_QTextCursor::tableMovement()
+{
+ QVERIFY(cursor.position() == 0);
+ cursor.insertText("AA");
+ QVERIFY(cursor.position() == 2);
+ cursor.movePosition(QTextCursor::Left);
+
+ cursor.insertTable(3, 3);
+ QCOMPARE(cursor.position(), 2);
+
+ cursor.movePosition(QTextCursor::Down);
+ QCOMPARE(cursor.position(), 5);
+
+ cursor.movePosition(QTextCursor::Right);
+ QCOMPARE(cursor.position(), 6);
+
+ cursor.movePosition(QTextCursor::Up);
+ QCOMPARE(cursor.position(), 3);
+
+ cursor.movePosition(QTextCursor::Right);
+ QCOMPARE(cursor.position(), 4);
+
+ cursor.movePosition(QTextCursor::Right);
+ QCOMPARE(cursor.position(), 5);
+
+ cursor.movePosition(QTextCursor::Up);
+ QCOMPARE(cursor.position(), 2);
+
+ cursor.movePosition(QTextCursor::Up);
+ QCOMPARE(cursor.position(), 0);
+
+}
+
+void tst_QTextCursor::selectionsInTable()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+
+ cursor = table->cellAt(0, 0).lastCursorPosition();
+ QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor));
+ QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor) == false);
+
+ cursor = table->cellAt(1, 0).lastCursorPosition();
+ QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor));
+ QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor) == false);
+
+ cursor = table->cellAt(0, 1).firstCursorPosition();
+ QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor));
+ QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor) == false);
+
+ cursor = table->cellAt(1, 1).firstCursorPosition();
+ QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor));
+ QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor) == false);
+}
+
+void tst_QTextCursor::selectedText()
+{
+ cursor.insertText("Hello World");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+
+ QCOMPARE(cursor.selectedText(), QString("Hello World"));
+}
+
+void tst_QTextCursor::insertBlockShouldRemoveSelection()
+{
+ cursor.insertText("Hello World");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectedText(), QString("Hello"));
+
+ cursor.insertBlock();
+
+ QVERIFY(!cursor.hasSelection());
+ QVERIFY(doc->toPlainText().indexOf("Hello") == -1);
+}
+
+void tst_QTextCursor::insertBlockShouldRemoveSelection2()
+{
+ cursor.insertText("Hello World");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectedText(), QString("Hello"));
+
+ QTextBlockFormat fmt = cursor.blockFormat();
+ cursor.insertBlock(fmt);
+
+ QVERIFY(!cursor.hasSelection());
+ QVERIFY(doc->toPlainText().indexOf("Hello") == -1);
+}
+
+void tst_QTextCursor::mergeCellShouldUpdateSelection()
+{
+ QTextTable *table = cursor.insertTable(4, 4);
+ cursor.setPosition(table->cellAt(0, 0).firstPosition());
+ cursor.setPosition(table->cellAt(3, 0).firstPosition(), QTextCursor::KeepAnchor); // aka bottom left
+ int firstRow, numRows, firstColumn, numColumns;
+ cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
+ QCOMPARE(firstRow, 0);
+ QCOMPARE(numRows, 4);
+ QCOMPARE(firstColumn, 0);
+ QCOMPARE(numColumns, 1);
+
+ table->removeColumns(firstColumn, numColumns);
+
+ QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition());
+ QCOMPARE(cursor.position(), table->cellAt(0, 0).firstPosition());
+ QCOMPARE(cursor.position(), cursor.anchor()); // empty. I don't really care where it ends up.
+
+ // prepare for another test with multiple cursors.
+ // note we have a 4 rows, 3 cols table now.
+ cursor.setPosition(table->cellAt(0, 0).firstPosition());
+ cursor.setPosition(table->cellAt(0, 2).firstPosition(), QTextCursor::KeepAnchor);
+
+ // now create a selection of a whole row.
+ QTextCursor c2 = table->cellAt(2, 0).firstCursorPosition();
+ c2.setPosition(table->cellAt(2, 2).firstPosition(), QTextCursor::KeepAnchor);
+
+ // just for good measure, another one for a block of cells.
+ QTextCursor c3 = table->cellAt(2, 1).firstCursorPosition();
+ c3.setPosition(table->cellAt(3, 2).firstPosition(), QTextCursor::KeepAnchor);
+
+ table->removeRows(2, 1);
+
+ QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition());
+ QCOMPARE(cursor.position(), table->cellAt(0, 2).firstPosition());
+
+ QCOMPARE(c2.position(), c2.anchor()); // empty. I don't really care where it ends up.
+
+ QCOMPARE(c3.anchor(), table->cellAt(2, 1).firstPosition());
+ QCOMPARE(c3.position(), table->cellAt(2, 2).firstPosition());
+
+
+ // prepare for another test where we remove a column
+ // note we have a 3 rows, 3 cols table now.
+ cursor.setPosition(table->cellAt(0, 0).firstPosition());
+ cursor.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor);
+
+ c2.setPosition(table->cellAt(0, 1).firstPosition());
+ c2.setPosition(table->cellAt(2, 2).firstPosition(), QTextCursor::KeepAnchor);
+
+ table->removeColumns(1, 1);
+
+ QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition());
+ QCOMPARE(cursor.position(), table->cellAt(2, 0).firstPosition());
+
+ QCOMPARE(c2.anchor(), table->cellAt(0, 1).firstPosition());
+ QCOMPARE(c2.position(), table->cellAt(2, 1).firstPosition());
+
+ // test for illegal cursor positions.
+ // note we have a 3 rows, 2 cols table now.
+ cursor.setPosition(table->cellAt(2, 0).firstPosition());
+ cursor.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor);
+
+ c2.setPosition(table->cellAt(0, 0).firstPosition());
+ c2.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor);
+
+ c3.setPosition(table->cellAt(2, 1).firstPosition());
+
+ table->removeRows(2, 1);
+
+ QCOMPARE(cursor.anchor(), table->cellAt(1, 1).lastPosition()+1);
+ QCOMPARE(cursor.position(), cursor.anchor());
+
+ QCOMPARE(c2.anchor(), table->cellAt(0, 0).firstPosition());
+ QCOMPARE(c2.position(), table->cellAt(1, 1).firstPosition());
+
+ QCOMPARE(c3.anchor(), table->cellAt(1, 1).firstPosition());
+ QCOMPARE(c3.position(), table->cellAt(1, 1).firstPosition());
+}
+
+void tst_QTextCursor::joinPreviousEditBlock()
+{
+ cursor.beginEditBlock();
+ cursor.insertText("Hello");
+ cursor.insertText("World");
+ cursor.endEditBlock();
+ QVERIFY(doc->toPlainText().startsWith("HelloWorld"));
+
+ cursor.joinPreviousEditBlock();
+ cursor.insertText("Hey");
+ cursor.endEditBlock();
+ QVERIFY(doc->toPlainText().startsWith("HelloWorldHey"));
+
+ doc->undo();
+ QVERIFY(!doc->toPlainText().contains("HelloWorldHey"));
+}
+
+void tst_QTextCursor::setBlockFormatInTable()
+{
+ // someone reported this on qt4-preview-feedback
+ QTextBlockFormat fmt;
+ fmt.setBackground(Qt::blue);
+ cursor.setBlockFormat(fmt);
+
+ QTextTable *table = cursor.insertTable(2, 2);
+ cursor = table->cellAt(0, 0).firstCursorPosition();
+ fmt.setBackground(Qt::red);
+ cursor.setBlockFormat(fmt);
+
+ cursor.movePosition(QTextCursor::Start);
+ QVERIFY(cursor.blockFormat().background().color() == Qt::blue);
+}
+
+void tst_QTextCursor::blockCharFormat2()
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::green);
+ cursor.mergeBlockCharFormat(fmt);
+
+ fmt.setForeground(Qt::red);
+
+ cursor.insertText("Test", fmt);
+ cursor.movePosition(QTextCursor::Start);
+ cursor.insertText("Red");
+ cursor.movePosition(QTextCursor::PreviousCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
+}
+
+void tst_QTextCursor::blockCharFormat3()
+{
+ QVERIFY(cursor.atBlockStart());
+ QVERIFY(cursor.atBlockEnd());
+ QVERIFY(cursor.atStart());
+
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::green);
+ cursor.setBlockCharFormat(fmt);
+ cursor.insertText("Test");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::green);
+
+ cursor.movePosition(QTextCursor::Start);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::green);
+
+ fmt.setForeground(Qt::red);
+ cursor.setBlockCharFormat(fmt);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red);
+
+ cursor.movePosition(QTextCursor::End);
+ cursor.movePosition(QTextCursor::Start);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::green);
+
+ cursor.insertText("Test");
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::green);
+
+ cursor.select(QTextCursor::Document);
+ cursor.removeSelectedText();
+ QVERIFY(cursor.atBlockStart());
+ QVERIFY(cursor.atBlockEnd());
+ QVERIFY(cursor.atStart());
+
+ cursor.insertText("Test");
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
+}
+
+void tst_QTextCursor::blockCharFormat()
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+ cursor.insertText("Hm");
+
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue);
+
+ fmt.setForeground(Qt::red);
+
+ cursor.setBlockCharFormat(fmt);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red);
+}
+
+void tst_QTextCursor::blockCharFormatOnSelection()
+{
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+
+ fmt.setForeground(Qt::green);
+ cursor.insertText("Hm", fmt);
+
+ fmt.setForeground(Qt::red);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+ cursor.insertText("Ah");
+
+ fmt.setForeground(Qt::white);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+ cursor.insertText("bleh");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::white);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
+
+ fmt.setForeground(Qt::cyan);
+ cursor.setBlockCharFormat(fmt);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::cyan);
+
+ cursor.movePosition(QTextCursor::Right);
+ cursor.movePosition(QTextCursor::Right);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::green);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::cyan);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::white);
+}
+
+void tst_QTextCursor::anchorInitialized1()
+{
+ cursor.insertBlock();
+ cursor = QTextCursor(cursor.block());
+ QCOMPARE(cursor.position(), 1);
+ QCOMPARE(cursor.anchor(), 1);
+ QCOMPARE(cursor.selectionStart(), 1);
+ QCOMPARE(cursor.selectionEnd(), 1);
+}
+
+void tst_QTextCursor::anchorInitialized2()
+{
+ cursor.insertBlock();
+ cursor = QTextCursor(cursor.block().docHandle(), 1);
+ QCOMPARE(cursor.position(), 1);
+ QCOMPARE(cursor.anchor(), 1);
+ QCOMPARE(cursor.selectionStart(), 1);
+ QCOMPARE(cursor.selectionEnd(), 1);
+}
+
+void tst_QTextCursor::anchorInitialized3()
+{
+ QTextFrame *frame = cursor.insertFrame(QTextFrameFormat());
+ cursor = QTextCursor(frame);
+ QCOMPARE(cursor.position(), 1);
+ QCOMPARE(cursor.anchor(), 1);
+ QCOMPARE(cursor.selectionStart(), 1);
+ QCOMPARE(cursor.selectionEnd(), 1);
+}
+
+void tst_QTextCursor::selectWord()
+{
+ cursor.insertText("first second third");
+ cursor.insertBlock();
+ cursor.insertText("words in second paragraph");
+
+ cursor.setPosition(9);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 6);
+ QCOMPARE(cursor.selectionEnd(), 12);
+
+ cursor.setPosition(5);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 0);
+ QCOMPARE(cursor.selectionEnd(), 5);
+
+ cursor.setPosition(6);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 6);
+ QCOMPARE(cursor.selectionEnd(), 12);
+
+ cursor.setPosition(14);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 6);
+ QCOMPARE(cursor.selectionEnd(), 12);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 0);
+ QCOMPARE(cursor.selectionEnd(), 5);
+
+ cursor.movePosition(QTextCursor::EndOfBlock);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QVERIFY(cursor.hasSelection());
+ QCOMPARE(cursor.selectionStart(), 17);
+ QCOMPARE(cursor.selectionEnd(), 22);
+}
+
+void tst_QTextCursor::selectWordWithSeparators_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("initialPosition");
+ QTest::addColumn<QString>("expectedSelectedText");
+
+ QTest::newRow("dereference") << QString::fromLatin1("foo->bar()") << 1 << QString::fromLatin1("foo");
+ QTest::newRow("funcsignature") << QString::fromLatin1("bar(int x);") << 1 << QString::fromLatin1("bar");
+ QTest::newRow("def") << QString::fromLatin1("foo *f;") << 1 << QString::fromLatin1("foo");
+}
+
+void tst_QTextCursor::selectWordWithSeparators()
+{
+ QFETCH(QString, text);
+ QFETCH(int, initialPosition);
+ QFETCH(QString, expectedSelectedText);
+
+ cursor.insertText(text);
+ cursor.setPosition(initialPosition);
+ cursor.select(QTextCursor::WordUnderCursor);
+
+ QCOMPARE(cursor.selectedText(), expectedSelectedText);
+}
+
+void tst_QTextCursor::startOfWord()
+{
+ cursor.insertText("first second");
+ cursor.setPosition(7);
+ cursor.movePosition(QTextCursor::StartOfWord);
+ QCOMPARE(cursor.position(), 0);
+}
+
+void tst_QTextCursor::selectBlock()
+{
+ cursor.insertText("foobar");
+ QTextBlockFormat blockFmt;
+ blockFmt.setAlignment(Qt::AlignHCenter);
+ cursor.insertBlock(blockFmt);
+ cursor.insertText("blah");
+ cursor.insertBlock(QTextBlockFormat());
+
+ cursor.movePosition(QTextCursor::PreviousBlock);
+ QCOMPARE(cursor.block().text(), QString("blah"));
+
+ cursor.select(QTextCursor::BlockUnderCursor);
+ QVERIFY(cursor.hasSelection());
+
+ QTextDocumentFragment fragment(cursor);
+ doc->clear();
+ cursor.insertFragment(fragment);
+ QCOMPARE(blockCount(), 2);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter);
+ QCOMPARE(cursor.block().text(), QString("blah"));
+}
+
+void tst_QTextCursor::selectVisually()
+{
+ cursor.insertText("Foo\nlong line which is probably going to be cut in two when shown in a widget\nparagraph 3\n");
+
+ cursor.setPosition(6); // somewhere in the long paragraph.
+ cursor.select(QTextCursor::LineUnderCursor);
+ // since we are not yet laid-out, we expect the whole paragraph to be selected.
+ QCOMPARE(cursor.position(), 77);
+ QCOMPARE(cursor.anchor(), 4);
+}
+
+void tst_QTextCursor::insertText()
+{
+ QString txt = "Foo\nBar\r\nMeep";
+ txt += QChar::LineSeparator;
+ txt += "Baz";
+ txt += QChar::ParagraphSeparator;
+ txt += "yoyodyne";
+ cursor.insertText(txt);
+ QCOMPARE(blockCount(), 4);
+ cursor.movePosition(QTextCursor::Start);
+ QCOMPARE(cursor.block().text(), QString("Foo"));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString("Bar"));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString(QString("Meep") + QChar(QChar::LineSeparator) + QString("Baz")));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString("yoyodyne"));
+}
+
+void tst_QTextCursor::insertFragmentShouldUseCurrentCharFormat()
+{
+ QTextDocumentFragment fragment = QTextDocumentFragment::fromPlainText("Hello World");
+ QTextCharFormat fmt;
+ fmt.setFontUnderline(true);
+
+ cursor.clearSelection();
+ cursor.setCharFormat(fmt);
+ cursor.insertFragment(fragment);
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat() == fmt);
+}
+
+int tst_QTextCursor::blockCount()
+{
+ int cnt = 0;
+ for (QTextBlock blk = doc->begin(); blk.isValid(); blk = blk.next())
+ ++cnt;
+ return cnt;
+}
+
+void tst_QTextCursor::endOfLine()
+{
+ doc->setPageSize(QSizeF(100000, INT_MAX));
+
+ QString text("First Line \nSecond Line ");
+ text.replace(QLatin1Char('\n'), QChar(QChar::LineSeparator));
+ cursor.insertText(text);
+
+ // ensure layouted
+ doc->documentLayout()->documentSize();
+
+ cursor.movePosition(QTextCursor::Start);
+
+ QCOMPARE(cursor.block().layout()->lineCount(), 2);
+
+ cursor.movePosition(QTextCursor::EndOfLine);
+ QCOMPARE(cursor.position(), 14);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QCOMPARE(cursor.position(), 15);
+ cursor.movePosition(QTextCursor::EndOfLine);
+ QCOMPARE(cursor.position(), 28);
+}
+
+class CursorListener : public QObject
+{
+ Q_OBJECT
+public:
+ CursorListener(QTextCursor *_cursor) : lastRecordedPosition(-1), lastRecordedAnchor(-1), recordingCount(0), cursor(_cursor) {}
+
+ int lastRecordedPosition;
+ int lastRecordedAnchor;
+ int recordingCount;
+
+public slots:
+ void recordCursorPosition()
+ {
+ lastRecordedPosition = cursor->position();
+ lastRecordedAnchor = cursor->anchor();
+ ++recordingCount;
+ }
+
+ void selectAllContents()
+ {
+ // Only test the first time
+ if (!recordingCount) {
+ recordingCount++;
+ cursor->select(QTextCursor::Document);
+ lastRecordedPosition = cursor->position();
+ lastRecordedAnchor = cursor->anchor();
+ }
+ }
+
+private:
+ QTextCursor *cursor;
+};
+
+void tst_QTextCursor::editBlocksDuringRemove()
+{
+ CursorListener listener(&cursor);
+
+ cursor.insertText("Hello World");
+ cursor.movePosition(QTextCursor::Start, QTextCursor::KeepAnchor);
+ QCOMPARE(cursor.selectedText(), QString("Hello World"));
+
+ connect(doc, SIGNAL(contentsChanged()), &listener, SLOT(recordCursorPosition()));
+ listener.recordingCount = 0;
+ cursor.deleteChar();
+
+ QCOMPARE(listener.recordingCount, 1);
+ QCOMPARE(listener.lastRecordedPosition, 0);
+ QCOMPARE(listener.lastRecordedAnchor, 0);
+
+ QVERIFY(doc->toPlainText().isEmpty());
+}
+
+void tst_QTextCursor::selectAllDuringRemove()
+{
+ CursorListener listener(&cursor);
+
+ cursor.insertText("Hello World");
+ cursor.movePosition(QTextCursor::End);
+
+ connect(doc, SIGNAL(contentsChanged()), &listener, SLOT(selectAllContents()));
+ listener.recordingCount = 0;
+ QTextCursor localCursor = cursor;
+ localCursor.deletePreviousChar();
+
+ QCOMPARE(listener.lastRecordedPosition, 10);
+ QCOMPARE(listener.lastRecordedAnchor, 0);
+}
+
+void tst_QTextCursor::update_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("position");
+ QTest::addColumn<int>("anchor");
+ QTest::addColumn<int>("modifyPosition");
+ QTest::addColumn<int>("modifyAnchor");
+ QTest::addColumn<QString>("insertText");
+ QTest::addColumn<int>("expectedPosition");
+ QTest::addColumn<int>("expectedAnchor");
+
+ QString text("Hello big world");
+ int charsToDelete = 3;
+ QTest::newRow("removeInsideSelection")
+ << text
+ << /*position*/ 0
+ << /*anchor*/ text.length()
+ // delete 'big'
+ << 6
+ << 6 + charsToDelete
+ << QString() // don't insert anything, just remove
+ << /*expectedPosition*/ 0
+ << /*expectedAnchor*/ text.length() - charsToDelete
+ ;
+
+ text = "Hello big world";
+ charsToDelete = 3;
+ QTest::newRow("removeInsideSelectionWithSwappedAnchorAndPosition")
+ << text
+ << /*position*/ text.length()
+ << /*anchor*/ 0
+ // delete 'big'
+ << 6
+ << 6 + charsToDelete
+ << QString() // don't insert anything, just remove
+ << /*expectedPosition*/ text.length() - charsToDelete
+ << /*expectedAnchor*/ 0
+ ;
+
+
+ text = "Hello big world";
+ charsToDelete = 3;
+ QString textToInsert("small");
+ QTest::newRow("replaceInsideSelection")
+ << text
+ << /*position*/ 0
+ << /*anchor*/ text.length()
+ // delete 'big' ...
+ << 6
+ << 6 + charsToDelete
+ << textToInsert // ... and replace 'big' with 'small'
+ << /*expectedPosition*/ 0
+ << /*expectedAnchor*/ text.length() - charsToDelete + textToInsert.length()
+ ;
+
+ text = "Hello big world";
+ charsToDelete = 3;
+ textToInsert = "small";
+ QTest::newRow("replaceInsideSelectionWithSwappedAnchorAndPosition")
+ << text
+ << /*position*/ text.length()
+ << /*anchor*/ 0
+ // delete 'big' ...
+ << 6
+ << 6 + charsToDelete
+ << textToInsert // ... and replace 'big' with 'small'
+ << /*expectedPosition*/ text.length() - charsToDelete + textToInsert.length()
+ << /*expectedAnchor*/ 0
+ ;
+
+
+ text = "Hello big world";
+ charsToDelete = 3;
+ QTest::newRow("removeBeforeSelection")
+ << text
+ << /*position*/ text.length() - 5
+ << /*anchor*/ text.length()
+ // delete 'big'
+ << 6
+ << 6 + charsToDelete
+ << QString() // don't insert anything, just remove
+ << /*expectedPosition*/ text.length() - 5 - charsToDelete
+ << /*expectedAnchor*/ text.length() - charsToDelete
+ ;
+
+ text = "Hello big world";
+ charsToDelete = 3;
+ QTest::newRow("removeAfterSelection")
+ << text
+ << /*position*/ 0
+ << /*anchor*/ 4
+ // delete 'big'
+ << 6
+ << 6 + charsToDelete
+ << QString() // don't insert anything, just remove
+ << /*expectedPosition*/ 0
+ << /*expectedAnchor*/ 4
+ ;
+
+}
+
+void tst_QTextCursor::update()
+{
+ QFETCH(QString, text);
+
+ doc->setPlainText(text);
+
+ QFETCH(int, position);
+ QFETCH(int, anchor);
+
+ cursor.setPosition(anchor);
+ cursor.setPosition(position, QTextCursor::KeepAnchor);
+
+ QCOMPARE(cursor.position(), position);
+ QCOMPARE(cursor.anchor(), anchor);
+
+ QFETCH(int, modifyPosition);
+ QFETCH(int, modifyAnchor);
+
+ QTextCursor modifyCursor = cursor;
+ modifyCursor.setPosition(modifyAnchor);
+ modifyCursor.setPosition(modifyPosition, QTextCursor::KeepAnchor);
+
+ QCOMPARE(modifyCursor.position(), modifyPosition);
+ QCOMPARE(modifyCursor.anchor(), modifyAnchor);
+
+ QFETCH(QString, insertText);
+ modifyCursor.insertText(insertText);
+
+ QFETCH(int, expectedPosition);
+ QFETCH(int, expectedAnchor);
+
+ QCOMPARE(cursor.position(), expectedPosition);
+ QCOMPARE(cursor.anchor(), expectedAnchor);
+}
+
+void tst_QTextCursor::disallowSettingObjectIndicesOnCharFormats()
+{
+ QTextCharFormat fmt;
+ fmt.setObjectIndex(42);
+ cursor.insertText("Hey", fmt);
+ QCOMPARE(cursor.charFormat().objectIndex(), -1);
+
+ cursor.select(QTextCursor::Document);
+ cursor.mergeCharFormat(fmt);
+ QCOMPARE(doc->begin().begin().fragment().charFormat().objectIndex(), -1);
+
+ cursor.select(QTextCursor::Document);
+ cursor.setCharFormat(fmt);
+ QCOMPARE(doc->begin().begin().fragment().charFormat().objectIndex(), -1);
+
+ cursor.setBlockCharFormat(fmt);
+ QCOMPARE(cursor.blockCharFormat().objectIndex(), -1);
+
+ cursor.movePosition(QTextCursor::End);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+ QCOMPARE(cursor.blockCharFormat().objectIndex(), -1);
+
+ doc->clear();
+
+ QTextTable *table = cursor.insertTable(1, 1);
+ cursor.select(QTextCursor::Document);
+ cursor.setCharFormat(fmt);
+
+ cursor = table->cellAt(0, 0).firstCursorPosition();
+ QVERIFY(!cursor.isNull());
+ QCOMPARE(cursor.blockCharFormat().objectIndex(), table->objectIndex());
+}
+
+void tst_QTextCursor::blockAndColumnNumber()
+{
+ QCOMPARE(QTextCursor().columnNumber(), 0);
+ QCOMPARE(QTextCursor().blockNumber(), 0);
+
+ QCOMPARE(cursor.columnNumber(), 0);
+ QCOMPARE(cursor.blockNumber(), 0);
+ cursor.insertText("Hello");
+ QCOMPARE(cursor.columnNumber(), 5);
+ QCOMPARE(cursor.blockNumber(), 0);
+
+ cursor.insertBlock();
+ QCOMPARE(cursor.columnNumber(), 0);
+ QCOMPARE(cursor.blockNumber(), 1);
+ cursor.insertText("Blah");
+ QCOMPARE(cursor.blockNumber(), 1);
+
+ // trigger a layout
+ doc->documentLayout();
+
+ cursor.insertBlock();
+ QCOMPARE(cursor.columnNumber(), 0);
+ QCOMPARE(cursor.blockNumber(), 2);
+ cursor.insertText("Test");
+ QCOMPARE(cursor.columnNumber(), 4);
+ QCOMPARE(cursor.blockNumber(), 2);
+ cursor.insertText(QString(QChar(QChar::LineSeparator)));
+ QCOMPARE(cursor.columnNumber(), 0);
+ QCOMPARE(cursor.blockNumber(), 2);
+ cursor.insertText("A");
+ QCOMPARE(cursor.columnNumber(), 1);
+ QCOMPARE(cursor.blockNumber(), 2);
+}
+
+void tst_QTextCursor::movePositionEndOfLine()
+{
+ cursor.insertText("blah\nblah\n");
+ // Select part of the second line ("la")
+ cursor.setPosition(6);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
+ QCOMPARE(cursor.selectedText(), QLatin1String("la"));
+
+ // trigger a layout
+ doc->documentLayout();
+
+ // Remove "la" and append "something" to the end in one undo operation
+ cursor.beginEditBlock();
+ cursor.removeSelectedText();
+ QTextCursor c2(doc);
+ c2.setPosition(7);
+ c2.insertText("foo"); // append to doc without touching the cursor.
+
+ QCOMPARE(cursor.position(), 6);
+ cursor.movePosition(QTextCursor::EndOfLine); // in an edit block visual movement is moved to the end of the paragraph
+ QCOMPARE(cursor.position(), 10);
+ cursor.endEditBlock();
+}
+
+void tst_QTextCursor::clearCells()
+{
+ QTextTable *table = cursor.insertTable(3, 5);
+ cursor.setPosition(table->cellAt(0,0).firstPosition()); // select cell 1 and cell 2
+ cursor.setPosition(table->cellAt(0,1).firstPosition(), QTextCursor::KeepAnchor);
+ cursor.deleteChar(); // should clear the cells, and not crash ;)
+}
+
+void tst_QTextCursor::task244408_wordUnderCursor_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("expected");
+ QTest::newRow("trailingSpace") << QString::fromLatin1("foo ") << QString::fromLatin1("");
+ QTest::newRow("noTrailingSpace") << QString::fromLatin1("foo") << QString::fromLatin1("foo");
+}
+
+void tst_QTextCursor::task244408_wordUnderCursor()
+{
+ QFETCH(QString, input);
+ QFETCH(QString, expected);
+ cursor.insertText(input);
+ cursor.movePosition(QTextCursor::End);
+ cursor.select(QTextCursor::WordUnderCursor);
+ QCOMPARE(cursor.selectedText(), expected);
+}
+
+void tst_QTextCursor::adjustCursorsOnInsert()
+{
+ cursor.insertText("Some text before ");
+ int posBefore = cursor.position();
+ cursor.insertText("selected text");
+ int posAfter = cursor.position();
+ cursor.insertText(" some text afterwards");
+
+ QTextCursor selection = cursor;
+ selection.setPosition(posBefore);
+ selection.setPosition(posAfter, QTextCursor::KeepAnchor);
+
+ cursor.setPosition(posBefore-1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.anchor(), posBefore+1);
+ QCOMPARE(selection.position(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posBefore);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.anchor(), posBefore+1);
+ QCOMPARE(selection.position(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posBefore+1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.anchor(), posBefore);
+ QCOMPARE(selection.position(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posAfter-1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.anchor(), posBefore);
+ QCOMPARE(selection.position(), posAfter+1);
+ doc->undo();
+
+ selection.setKeepPositionOnInsert(true);
+ cursor.setPosition(posAfter);
+ cursor.insertText(QLatin1String("x"));
+ selection.setKeepPositionOnInsert(false);
+ QCOMPARE(selection.anchor(), posBefore);
+ QCOMPARE(selection.position(), posAfter);
+ doc->undo();
+
+ cursor.setPosition(posAfter+1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.anchor(), posBefore);
+ QCOMPARE(selection.position(), posAfter);
+ doc->undo();
+
+ selection.setPosition(posAfter);
+ selection.setPosition(posBefore, QTextCursor::KeepAnchor);
+
+ cursor.setPosition(posBefore-1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore+1);
+ QCOMPARE(selection.anchor(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posBefore);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore+1);
+ QCOMPARE(selection.anchor(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posBefore+1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore);
+ QCOMPARE(selection.anchor(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posAfter-1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore);
+ QCOMPARE(selection.anchor(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posAfter);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore);
+ QCOMPARE(selection.anchor(), posAfter+1);
+ doc->undo();
+
+ cursor.setPosition(posAfter+1);
+ cursor.insertText(QLatin1String("x"));
+ QCOMPARE(selection.position(), posBefore);
+ QCOMPARE(selection.anchor(), posAfter);
+ doc->undo();
+
+}
+void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo()
+{
+ cursor.insertText("AAAABBBBCCCCDDDD");
+ cursor.setPosition(12);
+ int cursorPositionBefore = cursor.position();
+ cursor.beginEditBlock();
+ cursor.insertText("*");
+ cursor.setPosition(8);
+ cursor.insertText("*");
+ cursor.setPosition(4);
+ cursor.insertText("*");
+ cursor.setPosition(0);
+ cursor.insertText("*");
+ int cursorPositionAfter = cursor.position();
+ cursor.endEditBlock();
+
+ QVERIFY(doc->toPlainText() == "*AAAA*BBBB*CCCC*DDDD");
+ QCOMPARE(12, cursorPositionBefore);
+ QCOMPARE(1, cursorPositionAfter);
+
+ doc->undo(&cursor);
+ QVERIFY(doc->toPlainText() == "AAAABBBBCCCCDDDD");
+ QCOMPARE(cursor.position(), cursorPositionBefore);
+ doc->redo(&cursor);
+ QVERIFY(doc->toPlainText() == "*AAAA*BBBB*CCCC*DDDD");
+ QCOMPARE(cursor.position(), cursorPositionAfter);
+}
+
+void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo2()
+{
+ cursor.insertText("AAAABBBB");
+ int cursorPositionBefore = cursor.position();
+ cursor.setPosition(0, QTextCursor::KeepAnchor);
+ cursor.beginEditBlock();
+ cursor.removeSelectedText();
+ cursor.insertText("AAAABBBBCCCCDDDD");
+ cursor.endEditBlock();
+ doc->undo(&cursor);
+ QVERIFY(doc->toPlainText() == "AAAABBBB");
+ QCOMPARE(cursor.position(), cursorPositionBefore);
+
+ cursor.insertText("CCCC");
+ QVERIFY(doc->toPlainText() == "AAAABBBBCCCC");
+
+ cursorPositionBefore = cursor.position();
+ cursor.setPosition(0, QTextCursor::KeepAnchor);
+ cursor.beginEditBlock();
+ cursor.removeSelectedText();
+ cursor.insertText("AAAABBBBCCCCDDDD");
+ cursor.endEditBlock();
+
+ /* this undo now implicitely reinserts two segments, first "CCCCC", then
+ "AAAABBBB". The test ensures that the two are combined in order to
+ reconstruct the correct cursor position */
+ doc->undo(&cursor);
+
+
+ QVERIFY(doc->toPlainText() == "AAAABBBBCCCC");
+ QCOMPARE(cursor.position(), cursorPositionBefore);
+}
+
+void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo3()
+{
+ // verify that it's the position of the beginEditBlock that counts, and not the last edit position
+ cursor.insertText("AAAABBBB");
+ int cursorPositionBefore = cursor.position();
+ cursor.beginEditBlock();
+ cursor.setPosition(4);
+ QVERIFY(cursor.position() != cursorPositionBefore);
+ cursor.insertText("*");
+ cursor.endEditBlock();
+ QCOMPARE(cursor.position(), 5);
+ doc->undo(&cursor);
+ QCOMPARE(cursor.position(), cursorPositionBefore);
+}
+
+QTEST_MAIN(tst_QTextCursor)
+#include "tst_qtextcursor.moc"
diff --git a/tests/auto/gui/text/qtextdocument/.gitignore b/tests/auto/gui/text/qtextdocument/.gitignore
new file mode 100644
index 0000000000..c14f0e2422
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocument/.gitignore
@@ -0,0 +1 @@
+tst_qtextdocument
diff --git a/tests/auto/gui/text/qtextdocument/common.h b/tests/auto/gui/text/qtextdocument/common.h
new file mode 100644
index 0000000000..1eaefdbfbe
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocument/common.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QAbstractTextDocumentLayout>
+#include <private/qtextdocument_p.h>
+
+#ifndef COMMON_H
+#define COMMON_H
+
+class QTestDocumentLayout : public QAbstractTextDocumentLayout
+{
+ Q_OBJECT
+public:
+ QTestDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc), f(-1), called(false) {}
+ virtual void draw(QPainter *, const PaintContext &) {}
+ virtual int hitTest(const QPointF &, Qt::HitTestAccuracy ) const { return 0; }
+
+ virtual void documentChanged(int from, int oldLength, int length)
+ {
+ called = true;
+ lastDocumentLengths.append(document()->docHandle()->length());
+
+ if (f < 0)
+ return;
+
+ if(from != f ||
+ o != oldLength ||
+ l != length) {
+ qDebug("checkDocumentChanged: got %d %d %d, expected %d %d %d", from, oldLength, length, f, o, l);
+ error = true;
+ }
+ }
+
+ virtual int pageCount() const { return 1; }
+ virtual QSizeF documentSize() const { return QSizeF(); }
+
+ virtual QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
+ virtual QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
+
+ int f;
+ int o;
+ int l;
+
+ void expect(int from, int oldLength, int length) {
+ f = from;
+ o = oldLength;
+ l = length;
+ error = false;
+ called = false;
+ }
+ bool error;
+ bool called;
+ QList<int> lastDocumentLengths;
+};
+
+#endif
diff --git a/tests/auto/gui/text/qtextdocument/qtextdocument.pro b/tests/auto/gui/text/qtextdocument/qtextdocument.pro
new file mode 100644
index 0000000000..69517589cc
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocument/qtextdocument.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+QT += core-private gui-private xml
+HEADERS += common.h
+SOURCES += tst_qtextdocument.cpp
+
+
diff --git a/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
new file mode 100644
index 0000000000..c98a703acc
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
@@ -0,0 +1,2788 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qtextdocument.h>
+#include <qdebug.h>
+
+#include <qtextcursor.h>
+#include <qtextdocumentfragment.h>
+#include <qtextformat.h>
+#include <qtextobject.h>
+#include <qtexttable.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qtextlist.h>
+#include <qtextcodec.h>
+#include <qurl.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qtextlayout.h>
+#include <QDomDocument>
+#include "common.h"
+
+
+QT_FORWARD_DECLARE_CLASS(QTextDocument)
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTextDocument : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextDocument();
+ virtual ~tst_QTextDocument();
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void getSetCheck();
+ void isEmpty();
+ void find_data();
+ void find();
+ void find2();
+ void findWithRegExp_data();
+ void findWithRegExp();
+ void findMultiple();
+ void basicIsModifiedChecks();
+ void moreIsModified();
+ void isModified2();
+ void isModified3();
+ void isModified4();
+ void noundo_basicIsModifiedChecks();
+ void noundo_moreIsModified();
+ void noundo_isModified2();
+ void noundo_isModified3();
+ void mightBeRichText();
+ void mightBeRichText_data();
+
+ void task240325();
+
+ void stylesheetFont_data();
+ void stylesheetFont();
+
+ void toHtml_data();
+ void toHtml();
+ void toHtml2();
+
+ void setFragmentMarkersInHtmlExport();
+
+ void toHtmlBodyBgColor();
+ void toHtmlRootFrameProperties();
+ void capitalizationHtmlInExport();
+ void wordspacingHtmlExport();
+
+ void cursorPositionChanged();
+ void cursorPositionChangedOnSetText();
+
+ void textFrameIterator();
+
+ void codecForHtml();
+
+ void markContentsDirty();
+
+ void clonePreservesMetaInformation();
+ void clonePreservesPageSize();
+ void clonePreservesPageBreakPolicies();
+ void clonePreservesDefaultFont();
+ void clonePreservesRootFrameFormat();
+ void clonePreservesResources();
+ void clonePreservesUserStates();
+ void clonePreservesIndentWidth();
+ void blockCount();
+ void defaultStyleSheet();
+
+ void resolvedFontInEmptyFormat();
+
+ void defaultRootFrameMargin();
+
+ void clearResources();
+
+ void setPlainText();
+ void toPlainText();
+
+ void deleteTextObjectsOnClear();
+
+ void maximumBlockCount();
+ void adjustSize();
+ void initialUserData();
+
+ void html_defaultFont();
+
+ void blockCountChanged();
+
+ void nonZeroDocumentLengthOnClear();
+
+ void setTextPreservesUndoRedoEnabled();
+
+ void firstLast();
+
+ void backgroundImage_toHtml();
+ void backgroundImage_toHtml2();
+ void backgroundImage_clone();
+ void backgroundImage_copy();
+
+ void documentCleanup();
+
+ void characterAt();
+ void revisions();
+ void revisionWithUndoCompressionAndUndo();
+
+ void testUndoCommandAdded();
+
+ void testUndoBlocks();
+
+ void receiveCursorPositionChangedAfterContentsChange();
+ void escape_data();
+ void escape();
+
+ void copiedFontSize();
+
+ void htmlExportImportBlockCount();
+
+private:
+ void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
+
+ QTextDocument *doc;
+ QTextCursor cursor;
+ QFont defaultFont;
+ QString htmlHead;
+ QString htmlTail;
+};
+
+class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout
+{
+public:
+ MyAbstractTextDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc) {}
+ void draw(QPainter *, const PaintContext &) {}
+ int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; }
+ int pageCount() const { return 0; }
+ QSizeF documentSize() const { return QSizeF(); }
+ QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
+ QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
+ void documentChanged(int, int, int) {}
+};
+
+// Testing get/set functions
+void tst_QTextDocument::getSetCheck()
+{
+ QTextDocument obj1;
+ // QAbstractTextDocumentLayout * QTextDocument::documentLayout()
+ // void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *)
+ QPointer<MyAbstractTextDocumentLayout> var1 = new MyAbstractTextDocumentLayout(0);
+ obj1.setDocumentLayout(var1);
+ QCOMPARE(static_cast<QAbstractTextDocumentLayout *>(var1), obj1.documentLayout());
+ obj1.setDocumentLayout((QAbstractTextDocumentLayout *)0);
+ QVERIFY(var1.isNull());
+ QVERIFY(obj1.documentLayout());
+
+ // bool QTextDocument::useDesignMetrics()
+ // void QTextDocument::setUseDesignMetrics(bool)
+ obj1.setUseDesignMetrics(false);
+ QCOMPARE(false, obj1.useDesignMetrics());
+ obj1.setUseDesignMetrics(true);
+ QCOMPARE(true, obj1.useDesignMetrics());
+}
+
+tst_QTextDocument::tst_QTextDocument()
+{
+ QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
+ img.save("foo.png");
+}
+
+tst_QTextDocument::~tst_QTextDocument()
+{
+ QFile::remove(QLatin1String("foo.png"));
+}
+
+void tst_QTextDocument::init()
+{
+ doc = new QTextDocument;
+ cursor = QTextCursor(doc);
+ defaultFont = QFont();
+
+ htmlHead = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+ "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+ "p, li { white-space: pre-wrap; }\n"
+ "</style></head>"
+ "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n");
+ htmlHead = htmlHead.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
+
+ htmlTail = QString("</body></html>");
+}
+
+void tst_QTextDocument::cleanup()
+{
+ cursor = QTextCursor();
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextDocument::isEmpty()
+{
+ QVERIFY(doc->isEmpty());
+}
+
+void tst_QTextDocument::find_data()
+{
+ QTest::addColumn<QString>("haystack");
+ QTest::addColumn<QString>("needle");
+ QTest::addColumn<int>("flags");
+ QTest::addColumn<int>("from");
+ QTest::addColumn<int>("anchor");
+ QTest::addColumn<int>("position");
+
+ QTest::newRow("1") << "Hello World" << "World" << int(QTextDocument::FindCaseSensitively) << 0 << 6 << 11;
+
+ QTest::newRow("2") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
+ << "World" << int(QTextDocument::FindCaseSensitively) << 1 << 6 << 11;
+
+ QTest::newRow("3") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
+ << "Hello" << int(QTextDocument::FindCaseSensitively | QTextDocument::FindBackward) << 10 << 0 << 5;
+ QTest::newRow("4wholewords") << QString::fromAscii("Hello Blah World")
+ << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 6 << 10;
+ QTest::newRow("5wholewords") << QString::fromAscii("HelloBlahWorld")
+ << "Blah" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
+ QTest::newRow("6wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
+ << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 15 << 19;
+ QTest::newRow("7wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
+ << "Blah" << int(QTextDocument::FindWholeWords | QTextDocument::FindBackward) << 23 << 15 << 19;
+ QTest::newRow("8wholewords") << QString::fromAscii("Hello: World\n")
+ << "orld" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
+
+ QTest::newRow("across-paragraphs") << QString::fromAscii("First Parag\nSecond Parag with a lot more text")
+ << "Parag" << int(QTextDocument::FindBackward)
+ << 15 << 6 << 11;
+
+ QTest::newRow("nbsp") << "Hello" + QString(QChar(QChar::Nbsp)) +"World" << " " << int(QTextDocument::FindCaseSensitively) << 0 << 5 << 6;
+}
+
+void tst_QTextDocument::find()
+{
+ QFETCH(QString, haystack);
+ QFETCH(QString, needle);
+ QFETCH(int, flags);
+ QFETCH(int, from);
+ QFETCH(int, anchor);
+ QFETCH(int, position);
+
+ cursor.insertText(haystack);
+ cursor = doc->find(needle, from, QTextDocument::FindFlags(flags));
+
+ if (anchor != -1) {
+ QCOMPARE(cursor.anchor(), anchor);
+ QCOMPARE(cursor.position(), position);
+ } else {
+ QVERIFY(cursor.isNull());
+ }
+
+ //search using a regular expression
+ QRegExp expr(needle);
+ expr.setPatternSyntax(QRegExp::FixedString);
+ QTextDocument::FindFlags flg(flags);
+ expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ cursor = doc->find(expr, from, flg);
+
+ if (anchor != -1) {
+ QCOMPARE(cursor.anchor(), anchor);
+ QCOMPARE(cursor.position(), position);
+ } else {
+ QVERIFY(cursor.isNull());
+ }
+}
+
+void tst_QTextDocument::findWithRegExp_data()
+{
+ QTest::addColumn<QString>("haystack");
+ QTest::addColumn<QString>("needle");
+ QTest::addColumn<int>("flags");
+ QTest::addColumn<int>("from");
+ QTest::addColumn<int>("anchor");
+ QTest::addColumn<int>("position");
+
+ // match integers 0 to 99
+ QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2;
+ // match ampersands but not &amp;
+ QTest::newRow("2") << "His &amp; hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16;
+ //backward search
+ QTest::newRow("3") << QString::fromAscii("HelloBlahWorld Blah Hah")
+ << "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9;
+
+}
+
+void tst_QTextDocument::findWithRegExp()
+{
+ QFETCH(QString, haystack);
+ QFETCH(QString, needle);
+ QFETCH(int, flags);
+ QFETCH(int, from);
+ QFETCH(int, anchor);
+ QFETCH(int, position);
+
+ cursor.insertText(haystack);
+ //search using a regular expression
+ QRegExp expr(needle);
+ QTextDocument::FindFlags flg(flags);
+ expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ cursor = doc->find(expr, from, flg);
+
+ if (anchor != -1) {
+ QCOMPARE(cursor.anchor(), anchor);
+ QCOMPARE(cursor.position(), position);
+ } else {
+ QVERIFY(cursor.isNull());
+ }
+}
+
+void tst_QTextDocument::find2()
+{
+ doc->setPlainText("aaa");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
+ QTextCursor hit = doc->find("a", cursor);
+ QCOMPARE(hit.position(), 2);
+ QCOMPARE(hit.anchor(), 1);
+}
+
+void tst_QTextDocument::findMultiple()
+{
+ const QString text("foo bar baz foo bar baz");
+ doc->setPlainText(text);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor = doc->find("bar", cursor);
+ QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+ cursor = doc->find("bar", cursor);
+ QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+
+ cursor.movePosition(QTextCursor::End);
+ cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
+ QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+ cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
+ QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+
+
+ QRegExp expr("bar");
+ expr.setPatternSyntax(QRegExp::FixedString);
+
+ cursor.movePosition(QTextCursor::End);
+ cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
+ QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+ cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
+ QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor = doc->find(expr, cursor);
+ QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+ cursor = doc->find(expr, cursor);
+ QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
+ QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
+}
+
+void tst_QTextDocument::basicIsModifiedChecks()
+{
+ QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
+
+ QVERIFY(!doc->isModified());
+ cursor.insertText("Hello World");
+ QVERIFY(doc->isModified());
+ QCOMPARE(spy.count(), 1);
+ QVERIFY(spy.takeFirst().at(0).toBool());
+
+ doc->undo();
+ QVERIFY(!doc->isModified());
+ QCOMPARE(spy.count(), 1);
+ QVERIFY(!spy.takeFirst().at(0).toBool());
+
+ doc->redo();
+ QVERIFY(doc->isModified());
+ QCOMPARE(spy.count(), 1);
+ QVERIFY(spy.takeFirst().at(0).toBool());
+}
+
+void tst_QTextDocument::moreIsModified()
+{
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+
+ doc->undo();
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+
+ doc->undo();
+ QVERIFY(!doc->isModified());
+}
+
+void tst_QTextDocument::isModified2()
+{
+ // reported on qt4-preview-feedback
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+
+ doc->setModified(false);
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+}
+
+void tst_QTextDocument::isModified3()
+{
+ QVERIFY(!doc->isModified());
+
+ doc->setUndoRedoEnabled(false);
+ doc->setUndoRedoEnabled(true);
+
+ cursor.insertText("Hello");
+
+ QVERIFY(doc->isModified());
+ doc->undo();
+ QVERIFY(!doc->isModified());
+}
+
+void tst_QTextDocument::isModified4()
+{
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ cursor.insertText("World");
+
+ doc->setModified(false);
+
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Again");
+ QVERIFY(doc->isModified());
+
+ doc->undo();
+ QVERIFY(!doc->isModified());
+ doc->undo();
+ QVERIFY(doc->isModified());
+
+ doc->redo();
+ QVERIFY(!doc->isModified());
+ doc->redo();
+ QVERIFY(doc->isModified());
+
+ doc->undo();
+ QVERIFY(!doc->isModified());
+ doc->undo();
+ QVERIFY(doc->isModified());
+
+ //task 197769
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+}
+
+void tst_QTextDocument::noundo_basicIsModifiedChecks()
+{
+ doc->setUndoRedoEnabled(false);
+ QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
+
+ QVERIFY(!doc->isModified());
+ cursor.insertText("Hello World");
+ QVERIFY(doc->isModified());
+ QCOMPARE(spy.count(), 1);
+ QVERIFY(spy.takeFirst().at(0).toBool());
+
+ doc->undo();
+ QVERIFY(doc->isModified());
+ QCOMPARE(spy.count(), 0);
+
+ doc->redo();
+ QVERIFY(doc->isModified());
+ QCOMPARE(spy.count(), 0);
+}
+
+void tst_QTextDocument::task240325()
+{
+ doc->setHtml("<html><img width=\"100\" height=\"100\" align=\"right\"/>Foobar Foobar Foobar Foobar</html>");
+
+ QImage img(1000, 7000, QImage::Format_ARGB32_Premultiplied);
+ QPainter p(&img);
+ QFontMetrics fm(p.font());
+
+ // Set page size to contain image and one "Foobar"
+ doc->setPageSize(QSize(100 + fm.width("Foobar")*2, 1000));
+
+ // Force layout
+ doc->drawContents(&p);
+
+ QCOMPARE(doc->blockCount(), 1);
+ for (QTextBlock block = doc->begin() ; block!=doc->end() ; block = block.next()) {
+ QTextLayout *layout = block.layout();
+ QCOMPARE(layout->lineCount(), 4);
+ for (int lineIdx=0;lineIdx<layout->lineCount();++lineIdx) {
+ QTextLine line = layout->lineAt(lineIdx);
+
+ QString text = block.text().mid(line.textStart(), line.textLength()).trimmed();
+
+ // Remove start token
+ if (lineIdx == 0)
+ text = text.mid(1);
+
+ QCOMPARE(text, QString::fromLatin1("Foobar"));
+ }
+ }
+}
+
+void tst_QTextDocument::stylesheetFont_data()
+{
+ QTest::addColumn<QString>("stylesheet");
+ QTest::addColumn<QFont>("font");
+
+ {
+ QFont font;
+ font.setBold(true);
+ font.setPixelSize(64);
+
+ QTest::newRow("Regular font specification")
+ << "font-size: 64px; font-weight: bold;"
+ << font;
+ }
+
+
+ {
+ QFont font;
+ font.setBold(true);
+ font.setPixelSize(64);
+
+ QTest::newRow("Shorthand font specification")
+ << "font: normal bold 64px Arial;"
+ << font;
+ }
+
+}
+
+void tst_QTextDocument::stylesheetFont()
+{
+ QFETCH(QString, stylesheet);
+ QFETCH(QFont, font);
+
+ QString html = QString::fromLatin1("<html>"
+ "<body>"
+ "<div style=\"%1\" >"
+ "Foobar"
+ "</div>"
+ "</body>"
+ "</html>").arg(stylesheet);
+
+ qDebug() << html;
+ doc->setHtml(html);
+ QCOMPARE(doc->blockCount(), 1);
+
+ // First and only block
+ QTextBlock block = doc->firstBlock();
+
+ QString text = block.text();
+ QCOMPARE(text, QString::fromLatin1("Foobar"));
+
+ QFont actualFont = block.charFormat().font();
+
+ QCOMPARE(actualFont.bold(), font.bold());
+ QCOMPARE(actualFont.pixelSize(), font.pixelSize());
+}
+
+void tst_QTextDocument::noundo_moreIsModified()
+{
+ doc->setUndoRedoEnabled(false);
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+
+ doc->undo();
+ QVERIFY(doc->isModified());
+
+ cursor.insertText("Hello");
+
+ doc->undo();
+ QVERIFY(doc->isModified());
+}
+
+void tst_QTextDocument::noundo_isModified2()
+{
+ // reported on qt4-preview-feedback
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+
+ doc->setModified(false);
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+ QVERIFY(doc->isModified());
+}
+
+void tst_QTextDocument::noundo_isModified3()
+{
+ doc->setUndoRedoEnabled(false);
+ QVERIFY(!doc->isModified());
+
+ cursor.insertText("Hello");
+
+ QVERIFY(doc->isModified());
+ doc->undo();
+ QVERIFY(doc->isModified());
+}
+
+void tst_QTextDocument::mightBeRichText_data()
+{
+ const char qtDocuHeader[] = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+ "<!DOCTYPE html\n"
+ " PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">";
+ QVERIFY(Qt::mightBeRichText(QString::fromLatin1(qtDocuHeader)));
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<bool>("result");
+
+ QTest::newRow("documentation-header") << QString("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+ "<!DOCTYPE html\n"
+ " PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">")
+ << true;
+ QTest::newRow("br-nospace") << QString("Test <br/> new line") << true;
+ QTest::newRow("br-space") << QString("Test <br /> new line") << true;
+ QTest::newRow("br-invalidspace") << QString("Test <br/ > new line") << false;
+ QTest::newRow("invalid closing tag") << QString("Test <br/ line") << false;
+}
+
+void tst_QTextDocument::mightBeRichText()
+{
+ QFETCH(QString, input);
+ QFETCH(bool, result);
+ QVERIFY(result == Qt::mightBeRichText(input));
+}
+
+Q_DECLARE_METATYPE(QTextDocumentFragment)
+
+#define CREATE_DOC_AND_CURSOR() \
+ QTextDocument doc; \
+ doc.setDefaultFont(defaultFont); \
+ QTextCursor cursor(&doc);
+
+void tst_QTextDocument::toHtml_data()
+{
+ QTest::addColumn<QTextDocumentFragment>("input");
+ QTest::addColumn<QString>("expectedOutput");
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("Blah");
+
+ QTest::newRow("simple") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("&<>");
+
+ QTest::newRow("entities") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>&amp;&lt;&gt;</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontFamily("Times");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-family") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Times';\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontFamily("Foo's Family");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-family-with-quotes1") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:&quot;Foo's Family&quot;;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontFamily("Foo\"s Family");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-family-with-quotes2") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Foo&quot;s Family';\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setNonBreakableLines(true);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("pre") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<pre DEFAULTBLOCKSTYLE>Blah</pre>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontPointSize(40);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-size") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:40pt;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("logical-font-size") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:x-large;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("Foo");
+
+ QTextCharFormat fmt;
+ fmt.setFontPointSize(40);
+ cursor.insertBlock(QTextBlockFormat(), fmt);
+
+ fmt.clearProperty(QTextFormat::FontPointSize);
+ cursor.insertText("Blub", fmt);
+
+ QTest::newRow("no-font-size") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE>Foo</p>\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blub</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setLayoutDirection(Qt::RightToLeft);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("rtl") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p dir='rtl' DEFAULTBLOCKSTYLE>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::AlignJustify);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("blockalign") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p align=\"justify\" DEFAULTBLOCKSTYLE>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::AlignCenter);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("blockalign2") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p align=\"center\" DEFAULTBLOCKSTYLE>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("blockalign3") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p align=\"right\" DEFAULTBLOCKSTYLE>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setBackground(QColor("#0000ff"));
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("bgcolor") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK") +
+ QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\">Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontWeight(40);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-weight") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-weight:320;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontItalic(true);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("font-italic") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-style:italic;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setFontUnderline(true);
+ fmt.setFontOverline(false);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("text-decoration-1") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" text-decoration: underline;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#00ff00"));
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("color") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:#00ff00;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setBackground(QColor("#00ff00"));
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("span-bgcolor") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setVerticalAlignment(QTextCharFormat::AlignSubScript);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("valign-sub") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:sub;\">Blah</span></p>");
+
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("valign-super") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:super;\">Blah</span></p>");
+
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setAnchor(true);
+ fmt.setAnchorName("blub");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("named anchor") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><a name=\"blub\"></a>Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setAnchor(true);
+ fmt.setAnchorHref("http://www.kde.org/");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("href anchor") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/\">Blah</a></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setAnchor(true);
+ fmt.setAnchorHref("http://www.kde.org/?a=1&b=2");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("href anchor with &") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a=1&amp;b=2\">Blah</a></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setAnchor(true);
+ fmt.setAnchorHref("http://www.kde.org/?a='&b=\"");
+ cursor.insertText("Blah", fmt);
+
+ QTest::newRow("href anchor with ' and \"") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a='&amp;b=&quot;\">Blah</a></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertTable(2, 2);
+
+ QTest::newRow("simpletable") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(1, 4);
+ table->mergeCells(0, 0, 1, 2);
+ table->mergeCells(0, 2, 1, 2);
+
+ QTest::newRow("tablespans") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td colspan=\"2\"></td>\n<td colspan=\"2\"></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTableFormat fmt;
+ fmt.setBorder(1);
+ fmt.setCellSpacing(3);
+ fmt.setCellPadding(3);
+ fmt.setBackground(QColor("#ff00ff"));
+ fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
+ fmt.setAlignment(Qt::AlignHCenter);
+ fmt.setPosition(QTextFrameFormat::FloatRight);
+ cursor.insertTable(2, 2, fmt);
+
+ QTest::newRow("tableattrs") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" style=\" float: right;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTableFormat fmt;
+ fmt.setBorder(1);
+ fmt.setCellSpacing(3);
+ fmt.setCellPadding(3);
+ fmt.setBackground(QColor("#ff00ff"));
+ fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
+ fmt.setAlignment(Qt::AlignHCenter);
+ fmt.setPosition(QTextFrameFormat::FloatRight);
+ fmt.setLeftMargin(25);
+ fmt.setBottomMargin(35);
+ cursor.insertTable(2, 2, fmt);
+
+ QTest::newRow("tableattrs2") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" style=\" float: right; margin-top:0px; margin-bottom:35px; margin-left:25px; margin-right:0px;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTableFormat fmt;
+ fmt.setHeaderRowCount(2);
+ cursor.insertTable(4, 2, fmt);
+
+ QTest::newRow("tableheader") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "<thead>\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr></thead>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(2, 2);
+ QTextTable *subTable = table->cellAt(0, 1).firstCursorPosition().insertTable(1, 1);
+ subTable->cellAt(0, 0).firstCursorPosition().insertText("Hey");
+
+ QTest::newRow("nestedtable") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td></td>\n<td>\n<table border=\"1\" cellspacing=\"2\">\n<tr>\n<td>\n<p DEFAULTBLOCKSTYLE>Hey</p></td></tr></table></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTableFormat fmt;
+ QVector<QTextLength> widths;
+ widths.append(QTextLength());
+ widths.append(QTextLength(QTextLength::PercentageLength, 30));
+ widths.append(QTextLength(QTextLength::FixedLength, 40));
+ fmt.setColumnWidthConstraints(widths);
+ cursor.insertTable(1, 3, fmt);
+
+ QTest::newRow("colwidths") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td></td>\n<td width=\"30%\"></td>\n<td width=\"40\"></td></tr>"
+ "</table>");
+ }
+
+ // ### rowspan/colspan tests, once texttable api for that is back again
+ //
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(1, 1);
+ QTextCursor cellCurs = table->cellAt(0, 0).firstCursorPosition();
+ QTextCharFormat fmt;
+ fmt.setBackground(QColor("#ffffff"));
+ cellCurs.mergeBlockCharFormat(fmt);
+
+ QTest::newRow("cellproperties") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td bgcolor=\"#ffffff\"></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ // ### fixme: use programmatic api as soon as we can create floats through it
+ const char html[] = "<html><body>Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</body></html>";
+
+ QTest::newRow("image") << QTextDocumentFragment::fromHtml(QString::fromLatin1(html))
+ << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextImageFormat fmt;
+ fmt.setName("foo");
+ fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
+ cursor.insertImage(fmt);
+
+ QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: middle;\" /></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextImageFormat fmt;
+ fmt.setName("foo");
+ fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
+ cursor.insertImage(fmt);
+
+ QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: top;\" /></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextImageFormat fmt;
+ fmt.setName("foo");
+ cursor.insertImage(fmt);
+ cursor.insertImage(fmt);
+
+ QTest::newRow("2images") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" /><img src=\"foo\" /></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QString txt = QLatin1String("Blah");
+ txt += QChar::LineSeparator;
+ txt += QLatin1String("Bar");
+ cursor.insertText(txt);
+
+ QTest::newRow("linebreaks") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE>Blah<br />Bar</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setTopMargin(10);
+ fmt.setBottomMargin(20);
+ fmt.setLeftMargin(30);
+ fmt.setRightMargin(40);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("blockmargins") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p style=\" margin-top:10px; margin-bottom:20px; margin-left:30px; margin-right:40px; -qt-block-indent:0; text-indent:0px;\">Blah</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
+ cursor.insertText("Blubb");
+ cursor.insertBlock();
+ cursor.insertText("Blah");
+ QCOMPARE(list->count(), 2);
+
+ QTest::newRow("lists") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li DEFAULTBLOCKSTYLE>Blah</li></ul>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
+ cursor.insertText("Blubb");
+
+ cursor.insertBlock();
+
+ QTextCharFormat blockCharFmt;
+ blockCharFmt.setForeground(QColor("#0000ff"));
+ cursor.mergeBlockCharFormat(blockCharFmt);
+
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#ff0000"));
+ cursor.insertText("Blah", fmt);
+ QCOMPARE(list->count(), 2);
+
+ QTest::newRow("charfmt-for-list-item") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blubb</li>\n<li style=\" color:#0000ff;\" DEFAULTBLOCKSTYLE><span style=\" color:#ff0000;\">Blah</span></li></ul>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setIndent(3);
+ fmt.setTextIndent(30);
+ cursor.insertBlock(fmt);
+ cursor.insertText("Test");
+
+ QTest::newRow("block-indent") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:3; text-indent:30px;\">Test</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListDisc);
+ fmt.setIndent(4);
+ cursor.insertList(fmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("list-indent") << QTextDocumentFragment(&doc)
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertBlock();
+
+
+ QTest::newRow("emptyblock") << QTextDocumentFragment(&doc)
+ // after insertBlock() we /do/ have two blocks in the document, so also expect
+ // these in the html output
+ << QString("EMPTYBLOCK") + QString("EMPTYBLOCK");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ // if you press enter twice in an empty textedit and then insert 'Test'
+ // you actually get three visible paragraphs, two empty leading ones and
+ // a third with the actual text. the corresponding html representation
+ // therefore should also contain three paragraphs.
+
+ cursor.insertBlock();
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#00ff00"));
+ fmt.setProperty(QTextFormat::FontSizeIncrement, 1);
+ cursor.mergeBlockCharFormat(fmt);
+
+ fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
+ cursor.insertText("Test", fmt);
+
+ QTest::newRow("blockcharfmt") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:x-large; color:#00ff00;\">Test</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#00ff00"));
+ cursor.setBlockCharFormat(fmt);
+ fmt.setForeground(QColor("#0000ff"));
+ cursor.insertText("Test", fmt);
+
+ QTest::newRow("blockcharfmt2") << QTextDocumentFragment(&doc)
+ << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" color:#0000ff;\">Test</span></p>");
+ }
+
+ {
+ QTest::newRow("horizontal-ruler") << QTextDocumentFragment::fromHtml("<hr />")
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<hr />");
+ }
+ {
+ QTest::newRow("horizontal-ruler-with-width") << QTextDocumentFragment::fromHtml("<hr width=\"50%\"/>")
+ <<
+ QString("EMPTYBLOCK") +
+ QString("<hr width=\"50%\"/>");
+ }
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextFrame *mainFrame = cursor.currentFrame();
+
+ 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");
+ cursor = mainFrame->lastCursorPosition();
+
+ QTest::newRow("frame") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" style=\"-qt-table-type: frame; float: right; margin-top:2px; margin-bottom:2px; margin-left:2px; margin-right:2px;\" width=\"100\" height=\"50\" bgcolor=\"#00ff00\">\n<tr>\n<td style=\"border: none;\">\n<p DEFAULTBLOCKSTYLE>Hello World</p></td></tr></table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#00ff00"));
+// fmt.setBackground(QColor("#0000ff"));
+ cursor.setBlockCharFormat(fmt);
+
+ fmt.setForeground(QBrush());
+// fmt.setBackground(QBrush());
+ cursor.insertText("Test", fmt);
+
+// QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; color:#00ff00; -qt-blockcharfmt-background-color:#0000ff;\">Test</p>");
+ QTest::newRow("nostylebrush") << QTextDocumentFragment(&doc) << QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Test</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->mergeCells(0, 0, 1, 2);
+ QTextTableFormat fmt = table->format();
+ QVector<QTextLength> widths;
+ widths.append(QTextLength(QTextLength::FixedLength, 20));
+ widths.append(QTextLength(QTextLength::FixedLength, 40));
+ fmt.setColumnWidthConstraints(widths);
+ table->setFormat(fmt);
+
+ QTest::newRow("mergedtablecolwidths") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td colspan=\"2\"></td></tr>"
+ "\n<tr>\n<td width=\"20\"></td>\n<td width=\"40\"></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextCharFormat fmt;
+
+ cursor.insertText("Blah\nGreen yellow green");
+ cursor.setPosition(0);
+ cursor.setPosition(23, QTextCursor::KeepAnchor);
+ fmt.setBackground(Qt::green);
+ cursor.mergeCharFormat(fmt);
+ cursor.clearSelection();
+ cursor.setPosition(11);
+ cursor.setPosition(17, QTextCursor::KeepAnchor);
+ fmt.setBackground(Qt::yellow);
+ cursor.mergeCharFormat(fmt);
+ cursor.clearSelection();
+
+ QTest::newRow("multiparagraph-bgcolor") << QTextDocumentFragment(&doc)
+ << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>\n"
+ "<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Green </span>"
+ "<span style=\" background-color:#ffff00;\">yellow</span>"
+ "<span style=\" background-color:#00ff00;\"> green</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat fmt;
+ fmt.setBackground(QColor("#0000ff"));
+ cursor.insertBlock(fmt);
+
+ QTextCharFormat charfmt;
+ charfmt.setBackground(QColor("#0000ff"));
+ cursor.insertText("Blah", charfmt);
+
+ QTest::newRow("nospan-bgcolor") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK") +
+ QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\"><span style=\" background-color:#0000ff;\">Blah</span></p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(2, 2);
+ QTextCharFormat fmt = table->cellAt(0, 0).format();
+ fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
+ table->cellAt(0, 0).setFormat(fmt);
+ fmt = table->cellAt(0, 1).format();
+ fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
+ table->cellAt(0, 1).setFormat(fmt);
+ fmt = table->cellAt(1, 0).format();
+ fmt.setVerticalAlignment(QTextCharFormat::AlignBottom);
+ table->cellAt(1, 0).setFormat(fmt);
+
+ table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
+
+ QTest::newRow("table-vertical-alignment") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td style=\" vertical-align:middle;\">\n"
+ "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
+ "\n<td style=\" vertical-align:top;\"></td></tr>"
+ "\n<tr>\n<td style=\" vertical-align:bottom;\"></td>"
+ "\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTable *table = cursor.insertTable(2, 2);
+ QTextTableCellFormat fmt = table->cellAt(0, 0).format().toTableCellFormat();
+ fmt.setLeftPadding(1);
+ table->cellAt(0, 0).setFormat(fmt);
+ fmt = table->cellAt(0, 1).format().toTableCellFormat();
+ fmt.setRightPadding(1);
+ table->cellAt(0, 1).setFormat(fmt);
+ fmt = table->cellAt(1, 0).format().toTableCellFormat();
+ fmt.setTopPadding(1);
+ table->cellAt(1, 0).setFormat(fmt);
+ fmt = table->cellAt(1, 1).format().toTableCellFormat();
+ fmt.setBottomPadding(1);
+ table->cellAt(1, 1).setFormat(fmt);
+
+ table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
+
+ QTest::newRow("table-cell-paddings") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" cellspacing=\"2\">"
+ "\n<tr>\n<td style=\" padding-left:1;\">\n"
+ "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
+ "\n<td style=\" padding-right:1;\"></td></tr>"
+ "\n<tr>\n<td style=\" padding-top:1;\"></td>"
+ "\n<td style=\" padding-bottom:1;\"></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextTableFormat fmt;
+ fmt.setBorderBrush(QColor("#0000ff"));
+ fmt.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
+ cursor.insertTable(2, 2, fmt);
+
+ QTest::newRow("tableborder") << QTextDocumentFragment(&doc)
+ << QString("<table border=\"1\" style=\" border-color:#0000ff; border-style:solid;\" cellspacing=\"2\">"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "\n<tr>\n<td></td>\n<td></td></tr>"
+ "</table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertBlock();
+ cursor.insertText("Foo");
+
+ cursor.block().setUserState(42);
+
+ QTest::newRow("userstate") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK") +
+ QString("<p OPENDEFAULTBLOCKSTYLE -qt-user-state:42;\">Foo</p>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextBlockFormat blockFmt;
+ blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
+
+ cursor.insertBlock(blockFmt);
+ cursor.insertText("Foo");
+
+ blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore | QTextFormat::PageBreak_AlwaysAfter);
+
+ cursor.insertBlock(blockFmt);
+ cursor.insertText("Bar");
+
+ QTextTableFormat tableFmt;
+ tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
+
+ cursor.insertTable(1, 1, tableFmt);
+
+ QTest::newRow("pagebreak") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK") +
+ QString("<p OPENDEFAULTBLOCKSTYLE page-break-before:always;\">Foo</p>"
+ "\n<p OPENDEFAULTBLOCKSTYLE page-break-before:always; page-break-after:always;\">Bar</p>"
+ "\n<table border=\"1\" style=\" page-break-after:always;\" cellspacing=\"2\">\n<tr>\n<td></td></tr></table>");
+ }
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ QTextListFormat listFmt;
+ listFmt.setStyle(QTextListFormat::ListDisc);
+
+ cursor.insertList(listFmt);
+ cursor.insertText("Blah");
+
+ QTest::newRow("list-ul-margin") << QTextDocumentFragment(&doc)
+ << QString("EMPTYBLOCK") +
+ QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
+ }
+}
+
+void tst_QTextDocument::toHtml()
+{
+ QFETCH(QTextDocumentFragment, input);
+ QFETCH(QString, expectedOutput);
+
+ cursor.insertFragment(input);
+
+ expectedOutput.prepend(htmlHead);
+
+ expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;");
+ expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
+ expectedOutput.replace("EMPTYBLOCK", "<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p>\n");
+ if (expectedOutput.endsWith(QLatin1Char('\n')))
+ expectedOutput.chop(1);
+ expectedOutput.append(htmlTail);
+
+ QString output = doc->toHtml();
+
+ QCOMPARE(output, expectedOutput);
+
+ QDomDocument document;
+ QVERIFY2(document.setContent(output), "Output was not valid XML");
+}
+
+void tst_QTextDocument::toHtml2()
+{
+ QTextDocument doc;
+ doc.setHtml("<p>text <img src=\"\"> text</p>"); // 4 spaces before the second 'text'
+ QTextBlock block = doc.firstBlock();
+ QTextBlock::Iterator iter = block.begin();
+ QTextFragment f = iter.fragment();
+ QVERIFY(f.isValid());
+ QCOMPARE(f.position(), 0);
+ QCOMPARE(f.length(), 5);
+ //qDebug() << block.text().mid(f.position(), f.length());
+
+ iter++;
+ f = iter.fragment();
+ QVERIFY(f.isValid());
+ QCOMPARE(f.position(), 5);
+ QCOMPARE(f.length(), 1);
+ //qDebug() << block.text().mid(f.position(), f.length());
+
+ iter++;
+ f = iter.fragment();
+ //qDebug() << block.text().mid(f.position(), f.length());
+ QVERIFY(f.isValid());
+ QCOMPARE(f.position(), 6);
+ QCOMPARE(f.length(), 5); // 1 space should be preserved.
+ QCOMPARE(block.text().mid(f.position(), f.length()), QString(" text"));
+
+ doc.setHtml("<table><tr><td> foo</td></tr></table> text"); // 4 spaces before the second 'text'
+ block = doc.firstBlock().next();
+ //qDebug() << block.text();
+ QCOMPARE(block.text(), QString("foo"));
+
+ block = block.next();
+ //qDebug() << block.text();
+ QCOMPARE(block.text(), QString("text"));
+}
+
+void tst_QTextDocument::setFragmentMarkersInHtmlExport()
+{
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("Leadin");
+ const int startPos = cursor.position();
+
+ cursor.insertText("Test");
+ QTextCharFormat fmt;
+ fmt.setForeground(QColor("#00ff00"));
+ cursor.insertText("Blah", fmt);
+
+ const int endPos = cursor.position();
+ cursor.insertText("Leadout", QTextCharFormat());
+
+ cursor.setPosition(startPos);
+ cursor.setPosition(endPos, QTextCursor::KeepAnchor);
+ QTextDocumentFragment fragment(cursor);
+
+ QString expected = htmlHead;
+ expected.replace(QRegExp("<body.*>"), QString("<body>"));
+ expected += QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><!--StartFragment-->Test<span style=\" color:#00ff00;\">Blah</span><!--EndFragment--></p>") + htmlTail;
+ QCOMPARE(fragment.toHtml(), expected);
+ }
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("Leadin");
+ const int startPos = cursor.position();
+
+ cursor.insertText("Test");
+
+ const int endPos = cursor.position();
+ cursor.insertText("Leadout", QTextCharFormat());
+
+ cursor.setPosition(startPos);
+ cursor.setPosition(endPos, QTextCursor::KeepAnchor);
+ QTextDocumentFragment fragment(cursor);
+
+ QString expected = htmlHead;
+ expected.replace(QRegExp("<body.*>"), QString("<body>"));
+ expected += QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><!--StartFragment-->Test<!--EndFragment--></p>") + htmlTail;
+ QCOMPARE(fragment.toHtml(), expected);
+ }
+}
+
+void tst_QTextDocument::toHtmlBodyBgColor()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertText("Blah");
+
+ QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
+ fmt.setBackground(QColor("#0000ff"));
+ doc.rootFrame()->setFrameFormat(fmt);
+
+ QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+ "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+ "p, li { white-space: pre-wrap; }\n"
+ "</style></head>"
+ "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\""
+ " bgcolor=\"#0000ff\">\n"
+ "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
+ "</body></html>");
+
+ expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
+
+ QCOMPARE(doc.toHtml(), expectedHtml);
+}
+
+void tst_QTextDocument::toHtmlRootFrameProperties()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
+ fmt.setTopMargin(10);
+ fmt.setLeftMargin(10);
+ fmt.setBorder(2);
+ doc.rootFrame()->setFrameFormat(fmt);
+
+ cursor.insertText("Blah");
+
+ QString expectedOutput("<table border=\"2\" style=\"-qt-table-type: root; margin-top:10px; "
+ "margin-bottom:4px; margin-left:10px; margin-right:4px;\">\n"
+ "<tr>\n<td style=\"border: none;\">\n"
+ "<p DEFAULTBLOCKSTYLE>Blah</p></td></tr></table>");
+
+ expectedOutput.prepend(htmlHead);
+ expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
+ expectedOutput.append(htmlTail);
+
+ QCOMPARE(doc.toHtml(), expectedOutput);
+}
+
+void tst_QTextDocument::capitalizationHtmlInExport()
+{
+ doc->setPlainText("Test");
+
+ QRegExp re(".*span style=\"(.*)\">Test.*");
+ QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
+
+ QTextCursor cursor(doc);
+ cursor.setPosition(4, QTextCursor::KeepAnchor);
+ QTextCharFormat cf;
+ cf.setFontCapitalization(QFont::SmallCaps);
+ cursor.mergeCharFormat(cf);
+
+ const QString smallcaps = doc->toHtml();
+ QVERIFY(re.exactMatch(doc->toHtml()));
+ QCOMPARE(re.captureCount(), 1);
+ QCOMPARE(re.cap(1).trimmed(), QString("font-variant:small-caps;"));
+
+ cf.setFontCapitalization(QFont::AllUppercase);
+ cursor.mergeCharFormat(cf);
+ const QString uppercase = doc->toHtml();
+ QVERIFY(re.exactMatch(doc->toHtml()));
+ QCOMPARE(re.captureCount(), 1);
+ QCOMPARE(re.cap(1).trimmed(), QString("text-transform:uppercase;"));
+
+ cf.setFontCapitalization(QFont::AllLowercase);
+ cursor.mergeCharFormat(cf);
+ const QString lowercase = doc->toHtml();
+ QVERIFY(re.exactMatch(doc->toHtml()));
+ QCOMPARE(re.captureCount(), 1);
+ QCOMPARE(re.cap(1).trimmed(), QString("text-transform:lowercase;"));
+
+ doc->setHtml(smallcaps);
+ cursor.setPosition(1);
+ QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::SmallCaps);
+ doc->setHtml(uppercase);
+ QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllUppercase);
+ doc->setHtml(lowercase);
+ QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllLowercase);
+}
+
+void tst_QTextDocument::wordspacingHtmlExport()
+{
+ doc->setPlainText("Test");
+
+ QRegExp re(".*span style=\"(.*)\">Test.*");
+ QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
+
+ QTextCursor cursor(doc);
+ cursor.setPosition(4, QTextCursor::KeepAnchor);
+ QTextCharFormat cf;
+ cf.setFontWordSpacing(4);
+ cursor.mergeCharFormat(cf);
+
+ QVERIFY(re.exactMatch(doc->toHtml()));
+ QCOMPARE(re.captureCount(), 1);
+ QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:4px;"));
+
+ cf.setFontWordSpacing(-8.5);
+ cursor.mergeCharFormat(cf);
+
+ QVERIFY(re.exactMatch(doc->toHtml()));
+ QCOMPARE(re.captureCount(), 1);
+ QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:-8.5px;"));
+}
+
+class CursorPosSignalSpy : public QObject
+{
+ Q_OBJECT
+public:
+ CursorPosSignalSpy(QTextDocument *doc)
+ {
+ calls = 0;
+ connect(doc, SIGNAL(cursorPositionChanged(const QTextCursor &)),
+ this, SLOT(cursorPositionChanged(const QTextCursor &)));
+ }
+
+ int calls;
+
+private slots:
+ void cursorPositionChanged(const QTextCursor &)
+ {
+ ++calls;
+ }
+};
+
+void tst_QTextDocument::cursorPositionChanged()
+{
+ CursorPosSignalSpy spy(doc);
+
+ cursor.insertText("Test");
+ QCOMPARE(spy.calls, 1);
+
+ spy.calls = 0;
+ QTextCursor unrelatedCursor(doc);
+ unrelatedCursor.insertText("Blah");
+ QCOMPARE(spy.calls, 2);
+
+ spy.calls = 0;
+ cursor.insertText("Blah");
+ QCOMPARE(spy.calls, 1);
+
+ spy.calls = 0;
+ cursor.movePosition(QTextCursor::PreviousCharacter);
+ QCOMPARE(spy.calls, 0);
+}
+
+void tst_QTextDocument::cursorPositionChangedOnSetText()
+{
+ CursorPosSignalSpy spy(doc);
+
+ // doc has one QTextCursor stored in the
+ // cursor member variable, thus the signal
+ // gets emitted once.
+
+ doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah");
+
+ QCOMPARE(spy.calls, 1);
+
+ spy.calls = 0;
+ doc->setHtml("<p>Foo<p>Bar<p>Baz<p>Blah");
+
+ QCOMPARE(spy.calls, 1);
+}
+
+void tst_QTextDocument::textFrameIterator()
+{
+ cursor.insertTable(1, 1);
+
+ int blockCount = 0;
+ int frameCount = 0;
+
+ for (QTextFrame::Iterator frameIt = doc->rootFrame()->begin();
+ !frameIt.atEnd(); ++frameIt) {
+ if (frameIt.currentFrame())
+ ++frameCount;
+ else if (frameIt.currentBlock().isValid())
+ ++blockCount;
+
+ }
+
+ QEXPECT_FAIL("", "This is currently worked around in the html export but needs fixing!", Continue);
+ QCOMPARE(blockCount, 0);
+ QCOMPARE(frameCount, 1);
+}
+
+void tst_QTextDocument::codecForHtml()
+{
+ const QByteArray header("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;charset=utf-16\">");
+ QTextCodec *c = Qt::codecForHtml(header);
+ QVERIFY(c);
+ QCOMPARE(c->name(), QByteArray("UTF-16"));
+}
+
+class TestSyntaxHighlighter : public QObject
+{
+ Q_OBJECT
+public:
+ inline TestSyntaxHighlighter(QTextDocument *doc) : QObject(doc), ok(false) {}
+
+ bool ok;
+
+private slots:
+ inline void markBlockDirty(int from, int charsRemoved, int charsAdded)
+ {
+ Q_UNUSED(charsRemoved);
+ Q_UNUSED(charsAdded);
+ QTextDocument *doc = static_cast<QTextDocument *>(parent());
+ QTextBlock block = doc->findBlock(from);
+
+ QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
+ lout->called = false;
+
+ doc->markContentsDirty(block.position(), block.length());
+
+ ok = (lout->called == false);
+ }
+
+ inline void modifyBlockAgain(int from, int charsRemoved, int charsAdded)
+ {
+ Q_UNUSED(charsRemoved);
+ Q_UNUSED(charsAdded);
+ QTextDocument *doc = static_cast<QTextDocument *>(parent());
+ QTextBlock block = doc->findBlock(from);
+ QTextCursor cursor(block);
+
+ QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
+ lout->called = false;
+
+ cursor.insertText("Foo");
+
+ ok = (lout->called == true);
+ }
+};
+
+void tst_QTextDocument::markContentsDirty()
+{
+ QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(lout);
+ TestSyntaxHighlighter *highlighter = new TestSyntaxHighlighter(doc);
+ connect(doc, SIGNAL(contentsChange(int, int, int)),
+ highlighter, SLOT(markBlockDirty(int, int, int)));
+
+ highlighter->ok = false;
+ cursor.insertText("Some dummy text blah blah");
+ QVERIFY(highlighter->ok);
+
+ disconnect(doc, SIGNAL(contentsChange(int, int, int)),
+ highlighter, SLOT(markBlockDirty(int, int, int)));
+ connect(doc, SIGNAL(contentsChange(int, int, int)),
+ highlighter, SLOT(modifyBlockAgain(int, int, int)));
+ highlighter->ok = false;
+ cursor.insertText("FooBar");
+ QVERIFY(highlighter->ok);
+
+ lout->called = false;
+
+ doc->markContentsDirty(1, 4);
+
+ QVERIFY(lout->called);
+}
+
+void tst_QTextDocument::clonePreservesMetaInformation()
+{
+ const QString title("Foobar");
+ const QString url("about:blank");
+ doc->setHtml("<html><head><title>" + title + "</title></head><body>Hrm</body></html>");
+ doc->setMetaInformation(QTextDocument::DocumentUrl, url);
+ QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), title);
+ QCOMPARE(doc->metaInformation(QTextDocument::DocumentUrl), url);
+
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->metaInformation(QTextDocument::DocumentTitle), title);
+ QCOMPARE(clone->metaInformation(QTextDocument::DocumentUrl), url);
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesPageSize()
+{
+ QSizeF sz(100., 100.);
+ doc->setPageSize(sz);
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->pageSize(), sz);
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesPageBreakPolicies()
+{
+ QTextTableFormat tableFmt;
+ tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
+
+ QTextBlockFormat blockFmt;
+ blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
+
+ QTextCursor cursor(doc);
+
+ cursor.setBlockFormat(blockFmt);
+ cursor.insertText("foo");
+ cursor.insertTable(2, 2, tableFmt);
+
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore);
+ QVERIFY(!clone->rootFrame()->childFrames().isEmpty());
+ QCOMPARE(clone->rootFrame()->childFrames().first()->frameFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter);
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesDefaultFont()
+{
+ QFont f = doc->defaultFont();
+ QVERIFY(f.pointSize() != 100);
+ f.setPointSize(100);
+ doc->setDefaultFont(f);
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->defaultFont(), f);
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesResources()
+{
+ QUrl testUrl(":/foobar");
+ QVariant testResource("hello world");
+
+ doc->addResource(QTextDocument::ImageResource, testUrl, testResource);
+ QTextDocument *clone = doc->clone();
+ QVERIFY(clone->resource(QTextDocument::ImageResource, testUrl) == testResource);
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesUserStates()
+{
+ QTextCursor cursor(doc);
+ cursor.insertText("bla bla bla");
+ cursor.block().setUserState(1);
+ cursor.insertBlock();
+ cursor.insertText("foo bar");
+ cursor.block().setUserState(2);
+ cursor.insertBlock();
+ cursor.insertText("no user state");
+
+ QTextDocument *clone = doc->clone();
+ QTextBlock b1 = doc->begin(), b2 = clone->begin();
+ while (b1 != doc->end()) {
+ b1 = b1.next();
+ b2 = b2.next();
+ QCOMPARE(b1.userState(), b2.userState());
+ }
+ QVERIFY(b2 == clone->end());
+ delete clone;
+}
+
+void tst_QTextDocument::clonePreservesRootFrameFormat()
+{
+ doc->setPlainText("Hello");
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ fmt.setMargin(200);
+ doc->rootFrame()->setFrameFormat(fmt);
+ QCOMPARE(doc->rootFrame()->frameFormat().margin(), qreal(200));
+ QTextDocument *copy = doc->clone();
+ QCOMPARE(copy->rootFrame()->frameFormat().margin(), qreal(200));
+ delete copy;
+}
+
+void tst_QTextDocument::clonePreservesIndentWidth()
+{
+ doc->setIndentWidth(42);
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->indentWidth(), qreal(42));
+ delete clone;
+}
+
+void tst_QTextDocument::blockCount()
+{
+ QCOMPARE(doc->blockCount(), 1);
+ cursor.insertBlock();
+ QCOMPARE(doc->blockCount(), 2);
+ cursor.insertBlock();
+ QCOMPARE(doc->blockCount(), 3);
+ cursor.insertText("blah blah");
+ QCOMPARE(doc->blockCount(), 3);
+ doc->undo();
+ doc->undo();
+ QCOMPARE(doc->blockCount(), 2);
+ doc->undo();
+ QCOMPARE(doc->blockCount(), 1);
+}
+
+void tst_QTextDocument::resolvedFontInEmptyFormat()
+{
+ QFont font;
+ font.setPointSize(42);
+ doc->setDefaultFont(font);
+ QTextCharFormat fmt = doc->begin().charFormat();
+ QVERIFY(fmt.properties().isEmpty());
+ QVERIFY(fmt.font() == font);
+}
+
+void tst_QTextDocument::defaultRootFrameMargin()
+{
+ QCOMPARE(doc->rootFrame()->frameFormat().margin(), 4.0);
+}
+
+class TestDocument : public QTextDocument
+{
+public:
+ inline TestDocument(const QUrl &testUrl, const QString &testString)
+ : url(testUrl), string(testString), resourceLoaded(false) {}
+
+ bool hasResourceCached();
+
+protected:
+ virtual QVariant loadResource(int type, const QUrl &name);
+
+private:
+ QUrl url;
+ QString string;
+ bool resourceLoaded;
+};
+
+bool TestDocument::hasResourceCached()
+{
+ resourceLoaded = false;
+ resource(QTextDocument::ImageResource, url);
+ return !resourceLoaded;
+}
+
+QVariant TestDocument::loadResource(int type, const QUrl &name)
+{
+ if (type == QTextDocument::ImageResource
+ && name == url) {
+ resourceLoaded = true;
+ return string;
+ }
+ return QTextDocument::loadResource(type, name);
+}
+
+void tst_QTextDocument::clearResources()
+{
+ // regular resource for QTextDocument
+ QUrl testUrl(":/foobar");
+ QVariant testResource("hello world");
+
+ // implicitly cached resource, initially loaded through TestDocument::loadResource()
+ QUrl cacheUrl(":/blub");
+ QString cacheResource("mah");
+
+ TestDocument doc(cacheUrl, cacheResource);
+ doc.addResource(QTextDocument::ImageResource, testUrl, testResource);
+
+ QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
+
+ doc.setPlainText("Hah");
+ QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
+
+ doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
+ QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
+ QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
+
+ doc.clear();
+ QVERIFY(!doc.resource(QTextDocument::ImageResource, testUrl).isValid());
+ QVERIFY(!doc.hasResourceCached());
+ doc.clear();
+
+ doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
+ QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
+
+ doc.setPlainText("Foob");
+ QVERIFY(!doc.hasResourceCached());
+}
+
+void tst_QTextDocument::setPlainText()
+{
+ doc->setPlainText("Hello World");
+ QString s("");
+ doc->setPlainText(s);
+ QCOMPARE(doc->toPlainText(), s);
+}
+
+void tst_QTextDocument::toPlainText()
+{
+ doc->setHtml("Hello&nbsp;World");
+ QCOMPARE(doc->toPlainText(), QLatin1String("Hello World"));
+}
+
+void tst_QTextDocument::deleteTextObjectsOnClear()
+{
+ QPointer<QTextTable> table = cursor.insertTable(2, 2);
+ QVERIFY(!table.isNull());
+ doc->clear();
+ QVERIFY(table.isNull());
+}
+
+void tst_QTextDocument::defaultStyleSheet()
+{
+ const QString sheet("p { background-color: green; }");
+ QVERIFY(doc->defaultStyleSheet().isEmpty());
+ doc->setDefaultStyleSheet(sheet);
+ QCOMPARE(doc->defaultStyleSheet(), sheet);
+
+ cursor.insertHtml("<p>test");
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+
+ doc->clear();
+ cursor.insertHtml("<p>test");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+
+ QTextDocument *clone = doc->clone();
+ QCOMPARE(clone->defaultStyleSheet(), sheet);
+ cursor = QTextCursor(clone);
+ cursor.insertHtml("<p>test");
+ fmt = clone->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+ delete clone;
+
+ cursor = QTextCursor(doc);
+ cursor.insertHtml("<p>test");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+
+ doc->clear();
+ cursor.insertHtml("<style>p { background-color: red; }</style><p>test");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("red"));
+
+ doc->clear();
+ doc->setDefaultStyleSheet("invalid style sheet....");
+ cursor.insertHtml("<p>test");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() != QColor("green"));
+}
+
+void tst_QTextDocument::maximumBlockCount()
+{
+ QCOMPARE(doc->maximumBlockCount(), 0);
+ QVERIFY(doc->isUndoRedoEnabled());
+
+ cursor.insertBlock();
+ cursor.insertText("Blah");
+ cursor.insertBlock();
+ cursor.insertText("Foo");
+ QCOMPARE(doc->blockCount(), 3);
+ QCOMPARE(doc->toPlainText(), QString("\nBlah\nFoo"));
+
+ doc->setMaximumBlockCount(1);
+ QVERIFY(!doc->isUndoRedoEnabled());
+
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(doc->toPlainText(), QString("Foo"));
+
+ cursor.insertBlock();
+ cursor.insertText("Hello");
+ doc->setMaximumBlockCount(1);
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(doc->toPlainText(), QString("Hello"));
+
+ doc->setMaximumBlockCount(100);
+ for (int i = 0; i < 1000; ++i) {
+ cursor.insertBlock();
+ cursor.insertText("Blah)");
+ QVERIFY(doc->blockCount() <= 100);
+ }
+
+ cursor.movePosition(QTextCursor::End);
+ QCOMPARE(cursor.blockNumber(), 99);
+ QTextCharFormat fmt;
+ fmt.setFontItalic(true);
+ cursor.setBlockCharFormat(fmt);
+ cursor.movePosition(QTextCursor::Start);
+ QVERIFY(!cursor.blockCharFormat().fontItalic());
+
+ doc->setMaximumBlockCount(1);
+ QVERIFY(cursor.blockCharFormat().fontItalic());
+
+ cursor.insertTable(2, 2);
+ QCOMPARE(doc->blockCount(), 6);
+ cursor.insertBlock();
+ QCOMPARE(doc->blockCount(), 1);
+}
+
+void tst_QTextDocument::adjustSize()
+{
+ // avoid ugly tooltips like in task 125583
+ QString text("Test Text");
+ doc->setPlainText(text);
+ doc->rootFrame()->setFrameFormat(QTextFrameFormat());
+ doc->adjustSize();
+ QCOMPARE(doc->size().width(), doc->idealWidth());
+}
+
+void tst_QTextDocument::initialUserData()
+{
+ doc->setPlainText("Hello");
+ QTextBlock block = doc->begin();
+ block.setUserData(new QTextBlockUserData);
+ QVERIFY(block.userData());
+ doc->documentLayout();
+ QVERIFY(block.userData());
+ doc->setDocumentLayout(new QTestDocumentLayout(doc));
+ QVERIFY(!block.userData());
+}
+
+void tst_QTextDocument::html_defaultFont()
+{
+ QFont f;
+ f.setItalic(true);
+ f.setWeight(QFont::Bold);
+ doc->setDefaultFont(f);
+ doc->setPlainText("Test");
+
+ QString bodyPart = QString::fromLatin1("<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:italic;\">")
+ .arg(f.family()).arg(f.pointSizeF()).arg(f.weight() * 8);
+
+ QString html = doc->toHtml();
+ if (!html.contains(bodyPart)) {
+ qDebug() << "html:" << html;
+ qDebug() << "expected body:" << bodyPart;
+ QVERIFY(html.contains(bodyPart));
+ }
+
+ if (html.contains("span"))
+ qDebug() << "html:" << html;
+ QVERIFY(!html.contains("<span style"));
+}
+
+void tst_QTextDocument::blockCountChanged()
+{
+ QSignalSpy spy(doc, SIGNAL(blockCountChanged(int)));
+
+ doc->setPlainText("Foo");
+
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(spy.count(), 0);
+
+ spy.clear();
+
+ doc->setPlainText("Foo\nBar");
+ QCOMPARE(doc->blockCount(), 2);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).value(0).toInt(), 2);
+
+ spy.clear();
+
+ cursor.movePosition(QTextCursor::End);
+ cursor.insertText("Blahblah");
+
+ QCOMPARE(spy.count(), 0);
+
+ cursor.insertBlock();
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).value(0).toInt(), 3);
+
+ spy.clear();
+ doc->undo();
+
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).value(0).toInt(), 2);
+}
+
+void tst_QTextDocument::nonZeroDocumentLengthOnClear()
+{
+ QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(lout);
+
+ doc->clear();
+ QVERIFY(lout->called);
+ QVERIFY(!lout->lastDocumentLengths.contains(0));
+}
+
+void tst_QTextDocument::setTextPreservesUndoRedoEnabled()
+{
+ QVERIFY(doc->isUndoRedoEnabled());
+
+ doc->setPlainText("Test");
+
+ QVERIFY(doc->isUndoRedoEnabled());
+
+ doc->setUndoRedoEnabled(false);
+ QVERIFY(!doc->isUndoRedoEnabled());
+ doc->setPlainText("Test2");
+ QVERIFY(!doc->isUndoRedoEnabled());
+
+ doc->setHtml("<p>hello");
+ QVERIFY(!doc->isUndoRedoEnabled());
+}
+
+void tst_QTextDocument::firstLast()
+{
+ QCOMPARE(doc->blockCount(), 1);
+ QVERIFY(doc->firstBlock() == doc->lastBlock());
+
+ doc->setPlainText("Hello\nTest\nWorld");
+
+ QCOMPARE(doc->blockCount(), 3);
+ QVERIFY(doc->firstBlock() != doc->lastBlock());
+
+ QCOMPARE(doc->firstBlock().text(), QString("Hello"));
+ QCOMPARE(doc->lastBlock().text(), QString("World"));
+
+ // manual forward loop
+ QTextBlock block = doc->firstBlock();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("Hello"));
+
+ block = block.next();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("Test"));
+
+ block = block.next();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("World"));
+
+ block = block.next();
+ QVERIFY(!block.isValid());
+
+ // manual backward loop
+ block = doc->lastBlock();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("World"));
+
+ block = block.previous();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("Test"));
+
+ block = block.previous();
+
+ QVERIFY(block.isValid());
+ QCOMPARE(block.text(), QString("Hello"));
+
+ block = block.previous();
+ QVERIFY(!block.isValid());
+}
+
+const QString backgroundImage_html("<body><table><tr><td background=\"foo.png\">Blah</td></tr></table></body>");
+
+void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &doc)
+{
+ QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
+ "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
+ "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
+ "p, li { white-space: pre-wrap; }\n"
+ "</style></head>"
+ "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n"
+ "<table border=\"0\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;\" cellspacing=\"2\" cellpadding=\"0\">"
+ "\n<tr>\n<td background=\"foo.png\">"
+ "\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
+ "</td></tr></table></body></html>");
+
+ expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
+
+ QCOMPARE(doc.toHtml(), expectedHtml);
+}
+
+void tst_QTextDocument::backgroundImage_toHtml()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ doc.setHtml(backgroundImage_html);
+ backgroundImage_checkExpectedHtml(doc);
+}
+
+void tst_QTextDocument::backgroundImage_toHtml2()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertHtml(backgroundImage_html);
+ backgroundImage_checkExpectedHtml(doc);
+}
+
+void tst_QTextDocument::backgroundImage_clone()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ doc.setHtml(backgroundImage_html);
+ QTextDocument *clone = doc.clone();
+ backgroundImage_checkExpectedHtml(*clone);
+ delete clone;
+}
+
+void tst_QTextDocument::backgroundImage_copy()
+{
+ CREATE_DOC_AND_CURSOR();
+
+ doc.setHtml(backgroundImage_html);
+ QTextDocumentFragment fragment(&doc);
+
+ {
+ CREATE_DOC_AND_CURSOR();
+
+ cursor.insertFragment(fragment);
+ backgroundImage_checkExpectedHtml(doc);
+ }
+}
+
+void tst_QTextDocument::documentCleanup()
+{
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+ cursor.insertText("d\nfoo\nbar\n");
+ doc.documentLayout(); // forces relayout
+
+ // remove char 1
+ cursor.setPosition(0);
+ QSizeF size = doc.documentLayout()->documentSize();
+ cursor.deleteChar();
+ // the size should be unchanged.
+ QCOMPARE(doc.documentLayout()->documentSize(), size);
+}
+
+void tst_QTextDocument::characterAt()
+{
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+ QString text("12345\n67890");
+ cursor.insertText(text);
+ int length = doc.characterCount();
+ QCOMPARE(length, text.length() + 1);
+ QCOMPARE(doc.characterAt(length-1), QChar(QChar::ParagraphSeparator));
+ QCOMPARE(doc.characterAt(-1), QChar());
+ QCOMPARE(doc.characterAt(length), QChar());
+ QCOMPARE(doc.characterAt(length + 1), QChar());
+ for (int i = 0; i < text.length(); ++i) {
+ QChar c = text.at(i);
+ if (c == QLatin1Char('\n'))
+ c = QChar(QChar::ParagraphSeparator);
+ QCOMPARE(doc.characterAt(i), c);
+ }
+}
+
+void tst_QTextDocument::revisions()
+{
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+ QString text("Hello World");
+ QCOMPARE(doc.firstBlock().revision(), 0);
+ cursor.insertText(text);
+ QCOMPARE(doc.firstBlock().revision(), 1);
+ cursor.setPosition(6);
+ cursor.insertBlock();
+ QCOMPARE(cursor.block().previous().revision(), 2);
+ QCOMPARE(cursor.block().revision(), 2);
+ cursor.insertText("candle");
+ QCOMPARE(cursor.block().revision(), 3);
+ cursor.movePosition(QTextCursor::EndOfBlock);
+ cursor.insertBlock(); // we are at the block end
+ QCOMPARE(cursor.block().previous().revision(), 3);
+ QCOMPARE(cursor.block().revision(), 4);
+ cursor.insertText("lightbulb");
+ QCOMPARE(cursor.block().revision(), 5);
+ cursor.movePosition(QTextCursor::StartOfBlock);
+ cursor.insertBlock(); // we are the block start
+ QCOMPARE(cursor.block().previous().revision(), 6);
+ QCOMPARE(cursor.block().revision(), 5);
+}
+
+void tst_QTextDocument::revisionWithUndoCompressionAndUndo()
+{
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+ cursor.insertText("This is the beginning of it all.");
+ QCOMPARE(doc.firstBlock().revision(), 1);
+ QCOMPARE(doc.revision(), 1);
+ cursor.insertBlock();
+ QCOMPARE(doc.revision(), 2);
+ cursor.insertText("this");
+ QCOMPARE(doc.revision(), 3);
+ cursor.insertText("is");
+ QCOMPARE(doc.revision(), 4);
+ cursor.insertText("compressed");
+ QCOMPARE(doc.revision(), 5);
+ doc.undo();
+ QCOMPARE(doc.revision(), 6);
+ QCOMPARE(doc.toPlainText(), QString("This is the beginning of it all.\n")) ;
+ cursor.setPosition(0);
+ QCOMPARE(doc.firstBlock().revision(), 1);
+ cursor.insertText("Very beginnig");
+ QCOMPARE(doc.firstBlock().revision(), 7);
+ doc.undo();
+ QCOMPARE(doc.revision(), 8);
+ QCOMPARE(doc.firstBlock().revision(), 1);
+
+ cursor.beginEditBlock();
+ cursor.insertText("Hello");
+ cursor.insertBlock();
+ cursor.insertText("world");
+ cursor.endEditBlock();
+ QCOMPARE(doc.revision(), 9);
+ doc.undo();
+ QCOMPARE(doc.revision(), 10);
+
+
+}
+
+void tst_QTextDocument::testUndoCommandAdded()
+{
+ QVERIFY(doc);
+ QSignalSpy spy(doc, SIGNAL(undoCommandAdded()));
+ QVERIFY(spy.isValid());
+ QVERIFY(spy.isEmpty());
+
+ cursor.insertText("a");
+ QCOMPARE(spy.count(), 1);
+ cursor.insertText("b"); // should be merged
+ QCOMPARE(spy.count(), 1);
+ cursor.insertText("c"); // should be merged
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(doc->toPlainText(), QString("abc"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString(""));
+
+ doc->clear();
+ spy.clear();
+ cursor.insertText("aaa");
+ QCOMPARE(spy.count(), 1);
+
+ spy.clear();
+ cursor.insertText("aaaa\nbcd");
+ QCOMPARE(spy.count(), 1);
+
+ spy.clear();
+ cursor.beginEditBlock();
+ cursor.insertText("aa");
+ cursor.insertText("bbb\n");
+ cursor.setCharFormat(QTextCharFormat());
+ cursor.insertText("\nccc");
+ QVERIFY(spy.isEmpty());
+ cursor.endEditBlock();
+ QCOMPARE(spy.count(), 1);
+
+ spy.clear();
+ cursor.insertBlock();
+ QCOMPARE(spy.count(), 1);
+
+ spy.clear();
+ cursor.setPosition(5);
+ QVERIFY(spy.isEmpty());
+ cursor.setCharFormat(QTextCharFormat());
+ QVERIFY(spy.isEmpty());
+ cursor.setPosition(10, QTextCursor::KeepAnchor);
+ QVERIFY(spy.isEmpty());
+ QTextCharFormat cf;
+ cf.setFontItalic(true);
+ cursor.mergeCharFormat(cf);
+ QCOMPARE(spy.count(), 1);
+
+ spy.clear();
+ doc->undo();
+ QCOMPARE(spy.count(), 0);
+ doc->undo();
+ QCOMPARE(spy.count(), 0);
+ spy.clear();
+ doc->redo();
+ QCOMPARE(spy.count(), 0);
+ doc->redo();
+ QCOMPARE(spy.count(), 0);
+}
+
+void tst_QTextDocument::testUndoBlocks()
+{
+ QVERIFY(doc);
+ cursor.insertText("Hello World");
+ cursor.insertText("period");
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString(""));
+ cursor.insertText("Hello World");
+ cursor.insertText("One\nTwo\nThree");
+ QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString("Hello World"));
+ cursor.insertText("One\nTwo\nThree");
+ cursor.insertText("Trailing text");
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString("Hello World"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString(""));
+
+ cursor.insertText("quod");
+ cursor.beginEditBlock();
+ cursor.insertText(" erat");
+ cursor.endEditBlock();
+ cursor.insertText(" demonstrandum");
+ QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString("quod erat"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString("quod"));
+ doc->undo();
+ QCOMPARE(doc->toPlainText(), QString(""));
+}
+
+class Receiver : public QObject
+{
+ Q_OBJECT
+ public:
+ QString first;
+ public slots:
+ void cursorPositionChanged() {
+ if (first.isEmpty())
+ first = QLatin1String("cursorPositionChanged");
+ }
+
+ void contentsChange() {
+ if (first.isEmpty())
+ first = QLatin1String("contentsChanged");
+ }
+};
+
+void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange()
+{
+ QVERIFY(doc);
+ doc->setDocumentLayout(new MyAbstractTextDocumentLayout(doc));
+ Receiver rec;
+ connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)),
+ &rec, SLOT(cursorPositionChanged()));
+ connect(doc, SIGNAL(contentsChange(int,int,int)),
+ &rec, SLOT(contentsChange()));
+ cursor.insertText("Hello World");
+ QCOMPARE(rec.first, QString("contentsChanged"));
+}
+
+void tst_QTextDocument::escape_data()
+{
+ QTest::addColumn<QString>("original");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("1") << "Hello World\n" << "Hello World\n";
+ QTest::newRow("2") << "#include <QtCore>" << "#include &lt;QtCore&gt;";
+ QTest::newRow("3") << "<p class=\"cool\"><a href=\"http://example.com/?foo=bar&amp;bar=foo\">plop --&gt; </a></p>"
+ << "&lt;p class=&quot;cool&quot;&gt;&lt;a href=&quot;http://example.com/?foo=bar&amp;amp;bar=foo&quot;&gt;plop --&amp;gt; &lt;/a&gt;&lt;/p&gt;";
+ QTest::newRow("4") << QString::fromUtf8("<\320\222\321\201>") << QString::fromUtf8("&lt;\320\222\321\201&gt;");
+}
+
+void tst_QTextDocument::escape()
+{
+ QFETCH(QString, original);
+ QFETCH(QString, expected);
+
+ QCOMPARE(Qt::escape(original), expected);
+}
+
+void tst_QTextDocument::copiedFontSize()
+{
+ QTextDocument documentInput;
+ QTextDocument documentOutput;
+
+ QFont fontInput;
+ fontInput.setPixelSize(24);
+
+ QTextCursor cursorInput(&documentInput);
+ QTextCharFormat formatInput = cursorInput.charFormat();
+ formatInput.setFont(fontInput);
+ cursorInput.insertText("Should be the same font", formatInput);
+ cursorInput.select(QTextCursor::Document);
+
+ QTextDocumentFragment fragmentInput(cursorInput);
+ QString html = fragmentInput.toHtml();
+
+ QTextCursor cursorOutput(&documentOutput);
+ QTextDocumentFragment fragmentOutput = QTextDocumentFragment::fromHtml(html);
+ cursorOutput.insertFragment(fragmentOutput);
+
+ QCOMPARE(cursorOutput.charFormat().font().pixelSize(), 24);
+}
+
+void tst_QTextDocument::htmlExportImportBlockCount()
+{
+ QTextDocument document;
+ {
+ QTextCursor cursor(&document);
+ cursor.insertText("Foo");
+ cursor.insertBlock();
+ cursor.insertBlock();
+ cursor.insertBlock();
+ cursor.insertBlock();
+ cursor.insertText("Bar");
+ }
+
+ QCOMPARE(document.blockCount(), 5);
+ QString html = document.toHtml();
+
+ document.clear();
+ document.setHtml(html);
+
+ QCOMPARE(document.blockCount(), 5);
+}
+
+QTEST_MAIN(tst_QTextDocument)
+#include "tst_qtextdocument.moc"
diff --git a/tests/auto/gui/text/qtextdocumentfragment/.gitignore b/tests/auto/gui/text/qtextdocumentfragment/.gitignore
new file mode 100644
index 0000000000..5c569834d3
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentfragment/.gitignore
@@ -0,0 +1 @@
+tst_qtextdocumentfragment
diff --git a/tests/auto/gui/text/qtextdocumentfragment/qtextdocumentfragment.pro b/tests/auto/gui/text/qtextdocumentfragment/qtextdocumentfragment.pro
new file mode 100644
index 0000000000..e6ddd45f85
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentfragment/qtextdocumentfragment.pro
@@ -0,0 +1,8 @@
+load(qttest_p4)
+
+QT += core-private gui-private
+
+SOURCES += tst_qtextdocumentfragment.cpp
+
+
+
diff --git a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp
new file mode 100644
index 0000000000..68c7a285ef
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp
@@ -0,0 +1,4030 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $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[] = "&#x00040;";
+ cursor.insertFragment(QTextDocumentFragment::fromHtml(html));
+ QCOMPARE(doc->begin().begin().fragment().text(), QString("@"));
+}
+
+void tst_QTextDocumentFragment::html_decEntities()
+{
+ const char html[] = "&#64;";
+ 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;\">&nbsp;</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"));
+
+ 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\n Bar;'>Test</div>");
+ QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar"));
+
+ setHtml("<div style='font-family: Foo\n Bar, serif, \"bar foo\";'>Test</div>");
+ QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar,serif,bar foo"));
+
+}
+
+void tst_QTextDocumentFragment::defaultFont()
+{
+ QFont f;
+ f.setFamily("Courier New");
+ f.setBold(true);
+ f.setItalic(true);
+ f.setStrikeOut(true); // set here but deliberately ignored for the html export
+ f.setPointSize(100);
+ doc->setDefaultFont(f);
+ doc->setPlainText("Hello World");
+ const QString html = doc->toHtml();
+ QLatin1String str("<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>");
+ 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&lt;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()
+{
+ {
+ setHtml("<html><body><div><b>Foo<div>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<html><body><p><b>Foo<p>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold);
+ }
+ {
+ setHtml("<html><body><b><center>Foo</center></b>");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<html><body><b><p>Foo");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<html><body><b><p>Foo<p>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<div><b>Foo<div>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<p><b>Foo<p>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold);
+ }
+ {
+ setHtml("<b><center>Foo</center></b>");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<b><p>Foo");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+ {
+ setHtml("<b><p>Foo<p>Bar");
+ QVERIFY(cursor.charFormat().fontWeight() == QFont::Bold);
+ QVERIFY(cursor.blockCharFormat().fontWeight() == QFont::Bold);
+ }
+}
+
+void tst_QTextDocumentFragment::html_tbody()
+{
+ setHtml("<table><thead><tr><td>First Cell</td></tr></thead><tbody><tr><td>Second Cell</td></tr></tbody></table>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->format().headerRowCount(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell"));
+}
+
+void tst_QTextDocumentFragment::html_nestedTables()
+{
+ setHtml("<table>"
+ " <tr><td>"
+ ""
+ " <table>"
+ " <tr><td>Hello</td></tr>"
+ " </table>"
+ ""
+ " <table>"
+ " <tr><td>World</td></tr>"
+ " </table>"
+ ""
+ " </td></tr>"
+ "</table>"
+ );
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 1);
+
+ cursor = table->cellAt(0, 0).firstCursorPosition();
+ cursor.movePosition(QTextCursor::NextBlock);
+
+ QTextTable *firstNestedTable = cursor.currentTable();
+ QVERIFY(firstNestedTable);
+ QVERIFY(firstNestedTable->parentFrame() == table);
+ QCOMPARE(firstNestedTable->rows(), 1);
+ QCOMPARE(firstNestedTable->columns(), 1);
+ QCOMPARE(firstNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hello"));
+
+ while (cursor.currentTable() == firstNestedTable
+ && cursor.movePosition(QTextCursor::NextBlock))
+ ;
+
+ QVERIFY(!cursor.isNull());
+ QVERIFY(cursor.currentTable() == table);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+
+ QTextTable *secondNestedTable = cursor.currentTable();
+ QVERIFY(secondNestedTable);
+ QVERIFY(secondNestedTable->parentFrame() == table);
+ QCOMPARE(secondNestedTable->rows(), 1);
+ QCOMPARE(secondNestedTable->columns(), 1);
+ QCOMPARE(secondNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("World"));
+}
+
+void tst_QTextDocumentFragment::html_rowSpans()
+{
+ setHtml(""
+ "<table border=\"1\" width=\"100%\">"
+ " <tr>"
+ " <td rowspan=\"2\">blah</td>"
+ " <td rowspan=\"2\">foo</td>"
+ " </tr>"
+ " <tr></tr>"
+ " <tr>"
+ " <td rowspan=\"2\">blubb</td>"
+ " <td rowspan=\"2\">baz</td>"
+ " </tr>"
+ " <tr></tr>"
+ "</table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 2);
+
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("blah"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("foo"));
+
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("blah"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("foo"));
+
+ QCOMPARE(table->cellAt(2, 0).firstCursorPosition().block().text(), QString("blubb"));
+ QCOMPARE(table->cellAt(2, 1).firstCursorPosition().block().text(), QString("baz"));
+
+ QCOMPARE(table->cellAt(3, 0).firstCursorPosition().block().text(), QString("blubb"));
+ QCOMPARE(table->cellAt(3, 1).firstCursorPosition().block().text(), QString("baz"));
+}
+
+void tst_QTextDocumentFragment::html_rowSpans2()
+{
+ setHtml(""
+ "<html><body>"
+ "<table border=\"1\">"
+ "<tr>"
+ "<td>Row 1 col 1</td>"
+ "</tr>"
+ "<tr>"
+ "<td rowspan=\"3\">Row 2 col 1, rowspan 3</td>"
+ "<td>Row 2 col 2</td>"
+ "</tr>"
+ "<tr>"
+ "<td rowspan=\"2\">Row 3 col 2, rowspan 2</td>"
+ "</tr>"
+ "<tr>"
+ "</tr>"
+ "</table>"
+ "</body></html>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 1).rowSpan(), 1);
+ QCOMPARE(table->cellAt(1, 0).rowSpan(), 3);
+ QCOMPARE(table->cellAt(2, 1).rowSpan(), 2);
+}
+
+void tst_QTextDocumentFragment::html_implicitParagraphs()
+{
+ setHtml("<p>foo</p>bar");
+ QCOMPARE(doc->blockCount(), 2);
+}
+
+void tst_QTextDocumentFragment::html_missingCloseTag()
+{
+ setHtml("<font color=\"red\"><span style=\"color:blue\">blue</span></span>&nbsp;red</font>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::blue);
+ cursor.movePosition(QTextCursor::NextWord);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
+}
+
+void tst_QTextDocumentFragment::html_anchorColor()
+{
+ setHtml("<span style=\"color: red;\">Red</span>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
+
+ setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\">Blue</a></span>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == QApplication::palette().link().color());
+
+ setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\" style=\"color: yellow;\">Green</a></span>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().foreground().color() == Qt::yellow);
+}
+
+void tst_QTextDocumentFragment::html_lastParagraphClosing()
+{
+ setHtml("<p>Foo<b>Bar</b>Baz");
+ QCOMPARE(doc->blockCount(), 1);
+}
+
+void tst_QTextDocumentFragment::html_tableHeaderBodyFootParent()
+{
+ // don't get confused by strange tags, keep tbody/thead/tfoot children of
+ // the table tag
+ setHtml("<table><col><col><col><tbody><tr><td>Hey</td></tr></tbody></table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
+
+ setHtml("<table><col><col><col><thead><tr><td>Hey</td></tr></thead></table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
+
+ setHtml("<table><col><col><col><tfoot><tr><td>Hey</td></tr></tfoot></table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
+}
+
+void tst_QTextDocumentFragment::html_columnWidths()
+{
+ setHtml("<table>"
+ " <tr>"
+ " <td colspan=\"2\">Foo</td>"
+ " </tr>"
+ " <tr>"
+ " <td>Bar</td>"
+ " <td width=\"1%\">Baz</td>"
+ " </tr>"
+ "</table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->rows(), 2);
+ QTextTableFormat fmt = table->format();
+
+ const QVector<QTextLength> columnWidths = fmt.columnWidthConstraints();
+ QCOMPARE(columnWidths.count(), 2);
+ QVERIFY(columnWidths.at(0).type() == QTextLength::VariableLength);
+ QVERIFY(columnWidths.at(1).type() == QTextLength::PercentageLength);
+ QVERIFY(columnWidths.at(1).rawValue() == 1);
+}
+
+void tst_QTextDocumentFragment::css_fontWeight()
+{
+ setHtml("<p style=\"font-weight:bold\">blah</p>");
+ QVERIFY(doc->begin().charFormat().fontWeight() == QFont::Bold);
+ setHtml("<p style=\"font-weight:600\">blah</p>");
+ QVERIFY(doc->begin().charFormat().fontWeight() == QFont::Bold);
+
+}
+
+void tst_QTextDocumentFragment::css_float()
+{
+ setHtml("<img src=\"foo\" style=\"float: right\">");
+ QTextCharFormat fmt = doc->begin().begin().fragment().charFormat();
+ QVERIFY(fmt.isImageFormat());
+ QTextObject *o = doc->objectForFormat(fmt);
+ QVERIFY(o);
+ QTextFormat f = o->format();
+ QVERIFY(f.isFrameFormat());
+ QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatRight);
+
+ setHtml("<img src=\"foo\" align=right>");
+ fmt = doc->begin().begin().fragment().charFormat();
+ QVERIFY(fmt.isImageFormat());
+ o = doc->objectForFormat(fmt);
+ QVERIFY(o);
+ f = o->format();
+ QVERIFY(f.isFrameFormat());
+ QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatRight);
+
+ setHtml("<img src=\"foo\" align=left>");
+ fmt = doc->begin().begin().fragment().charFormat();
+ QVERIFY(fmt.isImageFormat());
+ o = doc->objectForFormat(fmt);
+ QVERIFY(o);
+ f = o->format();
+ QVERIFY(f.isFrameFormat());
+ QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::FloatLeft);
+}
+
+void tst_QTextDocumentFragment::css_textIndent()
+{
+ setHtml("<p style=\"text-indent: 42px\">foo</p>");
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QCOMPARE(fmt.textIndent(), qreal(42));
+}
+
+void tst_QTextDocumentFragment::css_inline()
+{
+ setHtml(""
+ "<style>"
+ " p { background-color: green;}"
+ "</style>"
+ "<p>test</p>"
+ );
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::css_external()
+{
+ doc->addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("p { background-color: green; }"));
+ doc->setHtml(""
+ "<link href=\"test.css\" type=\"text/css\" />"
+ "<p>test</p>"
+ );
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::css_import()
+{
+ doc->addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("@import \"other.css\";"));
+ doc->addResource(QTextDocument::StyleSheetResource, QUrl("other.css"), QString("@import url(\"other2.css\");"));
+ doc->addResource(QTextDocument::StyleSheetResource, QUrl("other2.css"), QString("p { background-color: green; }"));
+ doc->setHtml(""
+ "<link href=\"test.css\" type=\"text/css\" />"
+ "<p>test</p>"
+ );
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+
+ doc->setHtml(""
+ "<style>@import \"test.css\" screen;</style>"
+ "<p>test</p>"
+ );
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::css_selectors_data()
+{
+ QTest::addColumn<bool>("match");
+ QTest::addColumn<QString>("selector");
+ QTest::addColumn<QString>("attributes");
+
+ QTest::newRow("plain") << true << QString() << QString();
+
+ QTest::newRow("class") << true << QString(".foo") << QString("class=foo");
+ QTest::newRow("notclass") << false << QString(".foo") << QString("class=bar");
+
+ QTest::newRow("attrset") << true << QString("[justset]") << QString("justset");
+ QTest::newRow("notattrset") << false << QString("[justset]") << QString("otherattribute");
+
+ QTest::newRow("attrmatch") << true << QString("[foo=bar]") << QString("foo=bar");
+ QTest::newRow("noattrmatch") << false << QString("[foo=bar]") << QString("foo=xyz");
+
+ QTest::newRow("contains") << true << QString("[foo~=bar]") << QString("foo=\"baz bleh bar\"");
+ QTest::newRow("notcontains") << false << QString("[foo~=bar]") << QString("foo=\"test\"");
+
+ QTest::newRow("beingswith") << true << QString("[foo|=bar]") << QString("foo=\"bar-bleh\"");
+ QTest::newRow("notbeingswith") << false << QString("[foo|=bar]") << QString("foo=\"bleh-bar\"");
+
+ QTest::newRow("attr2") << true << QString("[bar=foo]") << QString("bleh=bar bar=foo");
+}
+
+void tst_QTextDocumentFragment::css_selectors()
+{
+ QFETCH(bool, match);
+ QFETCH(QString, selector);
+ QFETCH(QString, attributes);
+
+ QString html = QString(""
+ "<style>"
+ " p { background-color: green }"
+ " p%1 { background-color: red }"
+ "</style>"
+ "<p %2>test</p>"
+ ).arg(selector).arg(attributes);
+ setHtml(html);
+
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ if (match)
+ QVERIFY(fmt.background().color() == QColor("red"));
+ else
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::css_nodeNameCaseInsensitivity()
+{
+ setHtml("<style>"
+ "P { background-color: green }"
+ "</style>"
+ "<p>test</p>");
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::css_textUnderlineStyle_data()
+{
+ QTest::addColumn<QString>("styleName");
+ QTest::addColumn<int>("expectedStyle");
+
+ QTest::newRow("none") << QString("none") << int(QTextCharFormat::NoUnderline);
+ QTest::newRow("solid") << QString("solid") << int(QTextCharFormat::SingleUnderline);
+ QTest::newRow("dash") << QString("dashed") << int(QTextCharFormat::DashUnderline);
+ QTest::newRow("dot") << QString("dotted") << int(QTextCharFormat::DotLine);
+ QTest::newRow("dashdot") << QString("dot-dash") << int(QTextCharFormat::DashDotLine);
+ QTest::newRow("dashdotdot") << QString("dot-dot-dash") << int(QTextCharFormat::DashDotDotLine);
+ QTest::newRow("wave") << QString("wave") << int(QTextCharFormat::WaveUnderline);
+}
+
+void tst_QTextDocumentFragment::css_textUnderlineStyle()
+{
+ QFETCH(QString, styleName);
+ QFETCH(int, expectedStyle);
+
+ QString html = QString::fromLatin1("<span style=\"text-underline-style: %1\">Blah</span>").arg(styleName);
+ doc->setHtml(html);
+
+ QTextFragment fragment = doc->begin().begin().fragment();
+ QVERIFY(fragment.isValid());
+ QCOMPARE(int(fragment.charFormat().underlineStyle()), expectedStyle);
+}
+
+void tst_QTextDocumentFragment::css_textUnderlineStyleAndDecoration()
+{
+ doc->setHtml("<span style=\"text-decoration: overline; text-underline-style: solid\">Test</span>");
+
+ QTextFragment fragment = doc->begin().begin().fragment();
+ QVERIFY(fragment.isValid());
+ QVERIFY(fragment.charFormat().underlineStyle() == QTextCharFormat::SingleUnderline);
+ QVERIFY(fragment.charFormat().fontOverline());
+
+ doc->setHtml("<span style=\"text-underline-style: solid; text-decoration: overline\">Test</span>");
+
+ fragment = doc->begin().begin().fragment();
+ QVERIFY(fragment.isValid());
+ QVERIFY(fragment.charFormat().underlineStyle() == QTextCharFormat::SingleUnderline);
+ QVERIFY(fragment.charFormat().fontOverline());
+}
+
+void tst_QTextDocumentFragment::css_listStyleType()
+{
+ doc->setHtml("<ol style=\"list-style-type: disc\"><li>Blah</li></ol>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDisc);
+
+ doc->setHtml("<ul style=\"list-style-type: square\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListSquare);
+
+ doc->setHtml("<ul style=\"list-style-type: circle\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListCircle);
+
+ doc->setHtml("<ul style=\"list-style-type: decimal\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDecimal);
+
+ doc->setHtml("<ul style=\"list-style-type: lower-alpha\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListLowerAlpha);
+
+ doc->setHtml("<ul style=\"list-style-type: upper-alpha\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListUpperAlpha);
+
+ doc->setHtml("<ul style=\"list-style-type: upper-roman\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListUpperRoman);
+
+ doc->setHtml("<ul style=\"list-style-type: lower-roman\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListLowerRoman);
+
+ // ignore the unsupported list-style-position inside the list-style shorthand property
+ doc->setHtml("<ul style=\"list-style: outside decimal\"><li>Blah</li></ul>");
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListDecimal);
+}
+
+void tst_QTextDocumentFragment::css_linkPseudo()
+{
+ doc->setHtml("<a href=\"foobar\">Blah</a>");
+ QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline());
+
+ doc->setHtml("<style>a { text-decoration: none; }</style><a href=\"foobar\">Blah</a>");
+ QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline());
+
+ doc->setHtml("<style>a:link { text-decoration: none; }</style><a href=\"foobar\">Blah</a>");
+ QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline());
+}
+
+void tst_QTextDocumentFragment::css_pageBreaks()
+{
+ doc->setHtml("<p>Foo</p>");
+ QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_Auto);
+
+ doc->setHtml("<p style=\" page-break-before:always;\">Foo</p>");
+ QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_AlwaysBefore);
+
+ doc->setHtml("<p style=\" page-break-after:always;\">Foo</p>");
+ QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == QTextFormat::PageBreak_AlwaysAfter);
+
+ doc->setHtml("<p style=\" page-break-before:always; page-break-after:always;\">Foo</p>");
+ QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == (QTextFormat::PageBreak_AlwaysAfter | QTextFormat::PageBreak_AlwaysBefore));
+}
+
+void tst_QTextDocumentFragment::universalSelectors_data()
+{
+ QTest::addColumn<bool>("match");
+ QTest::addColumn<QString>("selector");
+ QTest::addColumn<QString>("attributes");
+
+ QTest::newRow("1") << true << QString("*") << QString();
+ QTest::newRow("2") << false << QString() << QString(); // invalid totally empty selector
+
+ QTest::newRow("3") << false << QString("*[foo=bar]") << QString("foo=bleh");
+ QTest::newRow("4") << true << QString("*[foo=bar]") << QString("foo=bar");
+
+ QTest::newRow("5") << false << QString("[foo=bar]") << QString("foo=bleh");
+ QTest::newRow("6") << true << QString("[foo=bar]") << QString("foo=bar");
+
+ QTest::newRow("7") << true << QString(".charfmt1") << QString("class=charfmt1");
+}
+
+void tst_QTextDocumentFragment::universalSelectors()
+{
+ QFETCH(bool, match);
+ QFETCH(QString, selector);
+ QFETCH(QString, attributes);
+
+ QString html = QString(""
+ "<style>"
+ "%1 { background-color: green }"
+ "</style>"
+ "<p %2>test</p>"
+ ).arg(selector).arg(attributes);
+
+ setHtml(html);
+
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ if (match)
+ QVERIFY(fmt.background().color() == QColor("green"));
+ else
+ QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
+}
+
+void tst_QTextDocumentFragment::screenMedia()
+{
+ setHtml("<style>"
+ "@media screen {"
+ "p { background-color: green }"
+ "}"
+ "</style>"
+ "<p>test</p>"
+ "");
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+
+ setHtml("<style>"
+ "@media foobar {"
+ "p { background-color: green }"
+ "}"
+ "</style>"
+ "<p>test</p>"
+ "");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() != QColor("green"));
+
+ setHtml("<style>"
+ "@media sCrEeN {"
+ "p { background-color: green }"
+ "}"
+ "</style>"
+ "<p>test</p>"
+ "");
+ fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::htmlResourceLoading()
+{
+ const QString html("<link href=\"test.css\" type=\"text/css\" />"
+ "<p>test</p>");
+
+ QTextDocument tmp;
+ tmp.addResource(QTextDocument::StyleSheetResource, QUrl("test.css"), QString("p { background-color: green; }"));
+ QTextDocumentFragment frag = QTextDocumentFragment::fromHtml(html, &tmp);
+ doc->clear();
+ QTextCursor(doc).insertFragment(frag);
+ QTextBlockFormat fmt = doc->begin().blockFormat();
+ QVERIFY(fmt.background().color() == QColor("green"));
+}
+
+void tst_QTextDocumentFragment::someCaseInsensitiveAttributeValues()
+{
+ const char html1[] = "<ul type=sQUarE><li>Blah</li></ul>";
+ setHtml(QString::fromLatin1(html1));
+ cursor.movePosition(QTextCursor::End);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->format().style() == QTextListFormat::ListSquare);
+
+ const char html2[] = "<div align=ceNTeR><b>Hello World";
+ setHtml(html2);
+
+ QCOMPARE(doc->begin().blockFormat().alignment(), Qt::AlignHCenter);
+
+ const char html3[] = "<p dir=rTL><b>Hello World";
+ setHtml(html3);
+
+ QCOMPARE(doc->begin().blockFormat().layoutDirection(), Qt::RightToLeft);
+}
+
+class TestDocument : public QTextDocument
+{
+public:
+ inline TestDocument() {}
+
+ QPixmap testPixmap;
+
+ virtual QVariant loadResource(int type, const QUrl &name) {
+ if (name.toString() == QLatin1String("testPixmap")) {
+ return testPixmap;
+ }
+ return QTextDocument::loadResource(type, name);
+ }
+};
+
+void tst_QTextDocumentFragment::backgroundImage()
+{
+ TestDocument doc;
+ doc.testPixmap = QPixmap(100, 100);
+ doc.testPixmap.fill(Qt::blue);
+ doc.setHtml("<p style=\"background-image: url(testPixmap)\">Hello</p>");
+ QBrush bg = doc.begin().blockFormat().background();
+ QVERIFY(bg.style() == Qt::TexturePattern);
+ QVERIFY(bg.texture().serialNumber() == doc.testPixmap.serialNumber());
+}
+
+void tst_QTextDocumentFragment::dontMergePreAndNonPre()
+{
+ doc->setHtml("<pre>Pre text</pre>Text that should be wrapped");
+ QCOMPARE(doc->blockCount(), 2);
+ QCOMPARE(doc->begin().text(), QString("Pre text"));
+ QCOMPARE(doc->begin().next().text(), QString("Text that should be wrapped"));
+}
+
+void tst_QTextDocumentFragment::leftMarginInsideHtml()
+{
+ doc->setHtml("<html><dl><dd>Blah");
+ QCOMPARE(doc->blockCount(), 1);
+ QVERIFY(doc->begin().blockFormat().leftMargin() > 0);
+}
+
+void tst_QTextDocumentFragment::html_margins()
+{
+ doc->setHtml("<p style=\"margin-left: 42px\">Test");
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(doc->begin().blockFormat().topMargin(), 12.);
+ QCOMPARE(doc->begin().blockFormat().bottomMargin(), 12.);
+ QCOMPARE(doc->begin().blockFormat().leftMargin(), 42.);
+}
+
+void tst_QTextDocumentFragment::newlineInsidePreShouldBecomeNewParagraph()
+{
+ // rationale: we used to map newlines inside <pre> to QChar::LineSeparator, but
+ // if you display a lot of text inside pre it all ended up inside one single paragraph,
+ // which doesn't scale very well with our text engine. Paragraphs spanning thousands of
+ // lines are not a common use-case otherwise.
+
+ doc->setHtml("<pre>Foo\nBar</pre>");
+ QCOMPARE(doc->blockCount(), 2);
+ QTextBlock block = doc->begin();
+ QCOMPARE(block.blockFormat().topMargin(), qreal(12));
+ QVERIFY(qIsNull(block.blockFormat().bottomMargin()));
+
+ block = block.next();
+
+ QVERIFY(qIsNull(block.blockFormat().topMargin()));
+ QCOMPARE(block.blockFormat().bottomMargin(), qreal(12));
+
+ doc->setHtml("<pre style=\"margin-top: 32px; margin-bottom: 45px; margin-left: 50px\">Foo\nBar</pre>");
+ QCOMPARE(doc->blockCount(), 2);
+ block = doc->begin();
+ QCOMPARE(block.blockFormat().topMargin(), qreal(32));
+ QVERIFY(qIsNull(block.blockFormat().bottomMargin()));
+ QCOMPARE(block.blockFormat().leftMargin(), qreal(50));
+
+ block = block.next();
+
+ QVERIFY(qIsNull(block.blockFormat().topMargin()));
+ QCOMPARE(block.blockFormat().bottomMargin(), qreal(45));
+ QCOMPARE(block.blockFormat().leftMargin(), qreal(50));
+
+}
+
+void tst_QTextDocumentFragment::invalidColspan()
+{
+ doc->setHtml("<table><tr rowspan=-1><td colspan=-1>Blah</td></tr></table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->rows(), 1);
+}
+
+void tst_QTextDocumentFragment::html_brokenTableWithJustTr()
+{
+ doc->setHtml("<tr><td>First Cell</td><tr><td>Second Cell");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell"));
+
+ doc->setHtml(""
+ "<col width=286 style='mso-width-source:userset;mso-width-alt:10459;width:215pt'>"
+ "<col width=64 span=3 style='width:48pt'>"
+ "<tr height=17 style='height:12.75pt'>"
+ "<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>"
+ "<td width=64 style='width:48pt'>1b</td>"
+ "<td width=64 style='width:48pt'>1c</td>"
+ "<td width=64 style='width:48pt'>1d</td>"
+ "</tr>"
+ "<tr height=17 style='height:12.75pt'>"
+ "<td height=17 style='height:12.75pt'>|2a</td>"
+ "<td>2b</td>"
+ "<td>2c</td>"
+ "<td>2d</td>"
+ "</tr>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 4);
+}
+
+void tst_QTextDocumentFragment::html_brokenTableWithJustTd()
+{
+ doc->setHtml("<td>First Cell</td><td>Second Cell");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second Cell"));
+
+ doc->setHtml("<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>"
+ "<td width=64 style='width:48pt'>1b</td>"
+ "<td width=64 style='width:48pt'>1c</td>"
+ "<td width=64 style='width:48pt'>1d</td>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 4);
+}
+
+void tst_QTextDocumentFragment::html_preNewlineHandling_data()
+{
+ QTest::addColumn<QString>("html");
+ QTest::addColumn<QString>("expectedPlainText");
+
+ QTest::newRow("pre1") << QString("Foo<pre>Bar")
+ << QString("Foo\nBar");
+ QTest::newRow("pre2") << QString("Foo<pre>\nBar")
+ << QString("Foo\nBar");
+ QTest::newRow("pre2") << QString("Foo<pre>\n\nBar")
+ << QString("Foo\n\nBar");
+ QTest::newRow("pre4") << QString("<html>Foo<pre>\nBar")
+ << QString("Foo\nBar");
+ QTest::newRow("pre5") << QString("<pre>Foo\n</pre>\nBar")
+ << QString("Foo\nBar");
+ QTest::newRow("pre6") << QString("<pre>Foo<b>Bar</b>Blah\n</pre>\nMooh")
+ << QString("FooBarBlah\nMooh");
+ QTest::newRow("pre7") << QString("<pre>\nPara1\n</pre>\n<pre>\nPara2\n</pre>")
+ << QString("Para1\nPara2");
+}
+
+void tst_QTextDocumentFragment::html_preNewlineHandling()
+{
+ QFETCH(QString, html);
+
+ doc->setHtml(html);
+ QTEST(doc->toPlainText(), "expectedPlainText");
+}
+
+void tst_QTextDocumentFragment::html_br()
+{
+ doc->setHtml("Foo<br><br><br>Blah");
+ QCOMPARE(doc->toPlainText(), QString("Foo\n\n\nBlah"));
+}
+
+void tst_QTextDocumentFragment::html_dl()
+{
+ doc->setHtml("<dl><dt>term<dd>data</dl>Text afterwards");
+ QCOMPARE(doc->toPlainText(), QString("term\ndata\nText afterwards"));
+}
+
+void tst_QTextDocumentFragment::html_tableStrangeNewline()
+{
+ doc->setHtml("<table><tr><td>Foo</td></tr>\n</table>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 1);
+ const QTextTableCell cell = table->cellAt(0, 0);
+ QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
+ QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block());
+}
+
+void tst_QTextDocumentFragment::html_tableStrangeNewline2()
+{
+ doc->setHtml("<table><tr><td>Foo</td></tr><tr>\n<td/></tr></table>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 1);
+ const QTextTableCell cell = table->cellAt(0, 0);
+ QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
+ QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block());
+}
+
+void tst_QTextDocumentFragment::html_tableStrangeNewline3()
+{
+ doc->setHtml("<table border>"
+ "<tr>"
+ "<td>"
+ "<ul>"
+ "<li>Meh</li>"
+ "</ul>"
+ "</td>"
+ "<td>\n"
+ "<ul>"
+ "<li>Foo</li>"
+ "</ul>"
+ "</td>"
+ "</tr>"
+ "</table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 2);
+
+ QTextTableCell cell = table->cellAt(0, 0);
+ QCOMPARE(cell.firstCursorPosition().block().text(), QString("Meh"));
+ QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block());
+
+ cell = table->cellAt(0, 1);
+ QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
+ QVERIFY(cell.firstCursorPosition().block() == cell.lastCursorPosition().block());
+}
+
+void tst_QTextDocumentFragment::html_caption()
+{
+ doc->setHtml("<table border align=center>"
+ "<caption>This <b> is a</b> Caption!</caption>"
+ "<tr><td>Blah</td></tr>"
+ "</table>");
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+
+ QCOMPARE(cursor.block().text(), QString("This is a Caption!"));
+ QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 1);
+
+ QTextTableCell cell = table->cellAt(0, 0);
+ QCOMPARE(cell.firstCursorPosition().block().text(), QString("Blah"));
+}
+
+static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
+ 0x20ac, // 0x80
+ 0x0081, // 0x81 direct mapping
+ 0x201a, // 0x82
+ 0x0192, // 0x83
+ 0x201e, // 0x84
+ 0x2026, // 0x85
+ 0x2020, // 0x86
+ 0x2021, // 0x87
+ 0x02C6, // 0x88
+ 0x2030, // 0x89
+ 0x0160, // 0x8A
+ 0x2039, // 0x8B
+ 0x0152, // 0x8C
+ 0x008D, // 0x8D direct mapping
+ 0x017D, // 0x8E
+ 0x008F, // 0x8F directmapping
+ 0x0090, // 0x90 directmapping
+ 0x2018, // 0x91
+ 0x2019, // 0x92
+ 0x201C, // 0x93
+ 0X201D, // 0x94
+ 0x2022, // 0x95
+ 0x2013, // 0x96
+ 0x2014, // 0x97
+ 0x02DC, // 0x98
+ 0x2122, // 0x99
+ 0x0161, // 0x9A
+ 0x203A, // 0x9B
+ 0x0153, // 0x9C
+ 0x009D, // 0x9D direct mapping
+ 0x017E, // 0x9E
+ 0x0178 // 0x9F
+};
+
+void tst_QTextDocumentFragment::html_windowsEntities()
+{
+ for (uint i = 0; i < sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]); ++i) {
+ QString html = QString::number(i + 0x80);
+ html.prepend("<p>&#");
+ html.append(";");
+ doc->setHtml(html);
+ QCOMPARE(doc->toPlainText(), QString(QChar(windowsLatin1ExtendedCharacters[i])));
+ }
+}
+
+void tst_QTextDocumentFragment::html_eatenText()
+{
+ doc->setHtml("<h1>Test1</h1>\nTest2<h1>Test3</h1>");
+ cursor.movePosition(QTextCursor::Start);
+ QCOMPARE(cursor.block().text(), QString("Test1"));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString("Test2"));
+ cursor.movePosition(QTextCursor::NextBlock);
+ QCOMPARE(cursor.block().text(), QString("Test3"));
+}
+
+void tst_QTextDocumentFragment::html_hr()
+{
+ doc->setHtml("<hr />");
+ QCOMPARE(doc->blockCount(), 1);
+ QVERIFY(doc->begin().blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
+}
+
+void tst_QTextDocumentFragment::html_hrMargins()
+{
+ doc->setHtml("<p>Test<hr/>Blah");
+ QCOMPARE(doc->blockCount(), 3);
+
+ cursor.movePosition(QTextCursor::Start);
+ QTextBlock block = cursor.block();
+ QCOMPARE(block.text(), QString("Test"));
+ QVERIFY(block.blockFormat().bottomMargin() <= qreal(12.));
+ QTextBlock first = block;
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ block = cursor.block();
+ QTextBlock hr = block;
+ QVERIFY(qMax(first.blockFormat().bottomMargin(), block.blockFormat().topMargin()) > 0);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ block = cursor.block();
+
+ QCOMPARE(block.text(), QString("Blah"));
+}
+
+void tst_QTextDocumentFragment::html_blockQuoteMargins()
+{
+ doc->setHtml("<blockquote>Bar</blockquote>");
+ QCOMPARE(doc->blockCount(), 1);
+ cursor.movePosition(QTextCursor::Start);
+ QTextBlock block = cursor.block();
+ QCOMPARE(block.text(), QString("Bar"));
+ QCOMPARE(block.blockFormat().leftMargin(), qreal(40.));
+ QCOMPARE(block.blockFormat().rightMargin(), qreal(40.));
+ QCOMPARE(block.blockFormat().topMargin(), qreal(12.));
+ QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.));
+}
+
+void tst_QTextDocumentFragment::html_definitionListMargins()
+{
+ doc->setHtml("Foo<dl><dt>tag<dd>data</dl>Bar");
+ QCOMPARE(doc->blockCount(), 4);
+
+ cursor.movePosition(QTextCursor::Start);
+ QTextBlock block = cursor.block();
+ QCOMPARE(block.text(), QString("Foo"));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("tag"));
+ QCOMPARE(block.blockFormat().topMargin(), qreal(8.));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("data"));
+ QCOMPARE(block.blockFormat().bottomMargin(), qreal(8.));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("Bar"));
+}
+
+void tst_QTextDocumentFragment::html_listMargins()
+{
+ doc->setHtml("Foo<ol><li>First<li>Second</ol>Bar");
+ QCOMPARE(doc->blockCount(), 4);
+
+ cursor.movePosition(QTextCursor::Start);
+ QTextBlock block = cursor.block();
+ QCOMPARE(block.text(), QString("Foo"));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("First"));
+ QCOMPARE(block.blockFormat().topMargin(), qreal(12.));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("Second"));
+ QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.));
+
+ block = block.next();
+ QCOMPARE(block.text(), QString("Bar"));
+}
+
+void tst_QTextDocumentFragment::html_titleAttribute()
+{
+ doc->setHtml("<span title=\"this is my title\">Test</span>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QCOMPARE(cursor.charFormat().toolTip(), QString("this is my title"));
+}
+
+void tst_QTextDocumentFragment::html_compressDivs()
+{
+ doc->setHtml("<p/><div/><div/><div/><div/>Test");
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(doc->begin().text(), QString("Test"));
+}
+
+void tst_QTextDocumentFragment::completeToPlainText()
+{
+ doc->setPlainText("Hello\nWorld");
+ QCOMPARE(doc->toPlainText(), QString("Hello\nWorld"));
+ QTextDocumentFragment fragment(doc);
+ QCOMPARE(fragment.toPlainText(), QString("Hello\nWorld"));
+}
+
+void tst_QTextDocumentFragment::copyContents()
+{
+ doc->setPlainText("Hello");
+ QFont f;
+ doc->setDefaultFont(f);
+ QTextFragment fragment = doc->begin().begin().fragment();
+ QCOMPARE(fragment.text(), QString("Hello"));
+ QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize());
+
+ QTextDocumentFragment frag(doc);
+ doc->clear();
+ f.setPointSize(48);
+ doc->setDefaultFont(f);
+ QTextCursor(doc).insertFragment(QTextDocumentFragment::fromHtml(frag.toHtml()));
+ fragment = doc->begin().begin().fragment();
+ QCOMPARE(fragment.text(), QString("Hello"));
+ QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize());
+}
+
+void tst_QTextDocumentFragment::html_textAfterHr()
+{
+ doc->setHtml("<hr><nobr><b>After the centered text</b></nobr>");
+ QCOMPARE(doc->blockCount(), 2);
+ QTextBlock block = doc->begin();
+ QVERIFY(block.text().isEmpty());
+ QVERIFY(block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
+ block = block.next();
+
+ QString txt("After the centered text");
+ txt.replace(QLatin1Char(' '), QChar::Nbsp);
+ QCOMPARE(block.text(), txt);
+ QVERIFY(!block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
+}
+
+void tst_QTextDocumentFragment::blockTagClosing()
+{
+ doc->setHtml("<p>foo<p>bar<span>baz</span>");
+ QCOMPARE(doc->blockCount(), 2);
+ QTextBlock block = doc->begin();
+ QCOMPARE(block.text(), QString("foo"));
+ block = block.next();
+ QCOMPARE(block.text(), QString("barbaz"));
+}
+
+void tst_QTextDocumentFragment::isEmpty()
+{
+ QTextDocumentFragment frag;
+ QVERIFY(frag.isEmpty());
+ frag = QTextDocumentFragment::fromHtml("test");
+ QVERIFY(!frag.isEmpty());
+ frag = QTextDocumentFragment::fromHtml("<hr />");
+ QVERIFY(!frag.isEmpty());
+}
+
+void tst_QTextDocumentFragment::html_alignmentInheritance()
+{
+ doc->setHtml("<center>Centered text<hr></center><b>After the centered text</b>");
+ QCOMPARE(doc->blockCount(), 3);
+ QTextBlock block = doc->begin();
+ QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter);
+ block = block.next();
+ QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter);
+ block = block.next();
+ QVERIFY(!(block.blockFormat().alignment() & Qt::AlignHCenter));
+}
+
+void tst_QTextDocumentFragment::html_ignoreEmptyDivs()
+{
+ doc->setHtml("<p><div/><b>Foo</b>");
+ QCOMPARE(doc->blockCount(), 1);
+ QCOMPARE(doc->begin().text(), QString("Foo"));
+}
+
+void tst_QTextDocumentFragment::html_dontInheritAlignmentForFloatingImages()
+{
+ doc->setHtml("<p align=right><img align=unknownignored src=\"foo\" /></p>");
+ QTextCharFormat fmt = doc->begin().begin().fragment().charFormat();
+ QVERIFY(fmt.isImageFormat());
+ QTextObject *o = doc->objectForFormat(fmt);
+ QVERIFY(o);
+ QTextFormat f = o->format();
+ QVERIFY(f.isFrameFormat());
+ QVERIFY(f.toFrameFormat().position() == QTextFrameFormat::InFlow);
+}
+
+void tst_QTextDocumentFragment::html_verticalImageAlignment()
+{
+ doc->setHtml("<img src=\"foo\"/>");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().isImageFormat());
+ QTextImageFormat fmt = cursor.charFormat().toImageFormat();
+ QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignNormal);
+
+ doc->setHtml("<img src=\"foo\" align=middle />");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().isImageFormat());
+ fmt = cursor.charFormat().toImageFormat();
+ QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignMiddle);
+
+ doc->setHtml("<img src=\"foo\" style=\"vertical-align: middle\" />");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().isImageFormat());
+ fmt = cursor.charFormat().toImageFormat();
+ QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignMiddle);
+
+ doc->setHtml("<img src=\"foo\" align=top />");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().isImageFormat());
+ fmt = cursor.charFormat().toImageFormat();
+ QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignTop);
+
+ doc->setHtml("<img src=\"foo\" style=\"vertical-align: top\" />");
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextCharacter);
+ QVERIFY(cursor.charFormat().isImageFormat());
+ fmt = cursor.charFormat().toImageFormat();
+ QVERIFY(fmt.verticalAlignment() == QTextCharFormat::AlignTop);
+}
+
+void tst_QTextDocumentFragment::html_verticalCellAlignment()
+{
+ const char *alt[] =
+ {
+ // vertical-align property
+ "<table>"
+ "<tr>"
+ "<td style=\"vertical-align: middle\"></td>"
+ "<td style=\"vertical-align: top\"></td>"
+ "<td style=\"vertical-align: bottom\"></td>"
+ "</tr>"
+ "</table>",
+ // valign property
+ "<table>"
+ "<tr>"
+ "<td valign=\"middle\"></td>"
+ "<td valign=\"top\"></td>"
+ "<td valign=\"bottom\"></td>"
+ "</tr>"
+ "</table>",
+ // test td override of tr property
+ "<table>"
+ "<tr valign=\"bottom\">"
+ "<td valign=\"middle\"></td>"
+ "<td valign=\"top\"></td>"
+ "<td></td>"
+ "</tr>"
+ "</table>"
+ };
+
+ const int numTestCases = sizeof(alt) / sizeof(*alt);
+ for (int i = 0; i < numTestCases; ++i) {
+ doc->setHtml(alt[i]);
+
+ QTextTable *table = qobject_cast<QTextTable *>(doc->rootFrame()->childFrames().at(0));
+ QVERIFY(table);
+
+ QCOMPARE(table->cellAt(0, 0).format().verticalAlignment(), QTextCharFormat::AlignMiddle);
+ QCOMPARE(table->cellAt(0, 1).format().verticalAlignment(), QTextCharFormat::AlignTop);
+ QCOMPARE(table->cellAt(0, 2).format().verticalAlignment(), QTextCharFormat::AlignBottom);
+ }
+}
+
+void tst_QTextDocumentFragment::html_borderColor()
+{
+ const char html[] = "<table border=1 style=\"border-color:#0000ff;\"><tr><td>Foo</td></tr></table>";
+ cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html)));
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.currentTable());
+ QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Outset);
+ QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(QColor("#0000ff")));
+}
+
+void tst_QTextDocumentFragment::html_borderStyle()
+{
+ const char html[] = "<table border=1 style=\"border-style:solid;\"><tr><td>Foo</td></tr></table>";
+ cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html)));
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.currentTable());
+ QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Solid);
+ QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(Qt::darkGray));
+}
+
+void tst_QTextDocumentFragment::html_borderWidth()
+{
+ const char *html[2] = { "<table style=\"border-width:2;\"><tr><td>Foo</td></tr></table>",
+ "<table style=\"border-width:2px;\"><tr><td>Foo</td></tr></table>" };
+
+ for (int i = 0; i < 2; ++i) {
+ cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html[i])));
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.currentTable());
+ QCOMPARE(cursor.currentTable()->format().border(), qreal(2));
+ }
+}
+
+void tst_QTextDocumentFragment::html_userState()
+{
+ const char html[] = "<p style=\"-qt-user-state:42;\">A</p><p style=\"-qt-user-state:0;\">B</p><p>C</p>";
+ cursor.insertFragment(QTextDocumentFragment::fromHtml(QString::fromLatin1(html)));
+ QTextBlock block = doc->begin();
+ QCOMPARE(block.userState(), 42);
+ QCOMPARE(block.next().userState(), 0);
+ QCOMPARE(block.next().next().userState(), -1);
+}
+
+void tst_QTextDocumentFragment::html_rootFrameProperties()
+{
+ const char html[] = "<table border=1 style=\"-qt-table-type:root; margin-top:10px;\"><tr><td>Foo</tr></td>";
+ doc->setHtml(html);
+
+ QCOMPARE(doc->rootFrame()->childFrames().size(), 0);
+
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ QCOMPARE(fmt.topMargin(), qreal(10));
+ QCOMPARE(fmt.bottomMargin(), qreal(0));
+ QCOMPARE(fmt.leftMargin(), qreal(0));
+ QCOMPARE(fmt.rightMargin(), qreal(0));
+ QCOMPARE(fmt.border(), qreal(1));
+
+ QString normalFrameHtml = QLatin1String(html);
+ normalFrameHtml.replace(QLatin1String("root"), QLatin1String("frame"));
+
+ doc->setHtml(normalFrameHtml);
+ QCOMPARE(doc->rootFrame()->childFrames().size(), 1);
+}
+
+void tst_QTextDocumentFragment::html_appendList()
+{
+ appendHtml("<p>foo</p>");
+ appendHtml("<ul><li>Line 1</li><li>Line 2</li></ul>");
+
+ QCOMPARE(doc->blockCount(), 3);
+ QVERIFY(doc->begin().next().textList() != 0);
+}
+
+void tst_QTextDocumentFragment::html_appendList2()
+{
+ appendHtml("1");
+ appendHtml("<ul><li><img src=\"/foo/bar\" /></li></ul>");
+
+ QCOMPARE(doc->blockCount(), 2);
+ QVERIFY(doc->begin().next().textList() != 0);
+}
+
+void tst_QTextDocumentFragment::html_alignmentPropertySet()
+{
+ const char html[] = "<p>Test</p>";
+ setHtml(QString::fromLatin1(html));
+ QVERIFY(!doc->begin().blockFormat().hasProperty(QTextFormat::BlockAlignment));
+}
+
+void tst_QTextDocumentFragment::html_qt3RichtextWhitespaceMode()
+{
+ setHtml(QString::fromLatin1("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><p> line with whitespace</p><p> another line with whitespace</p></body></html>"));
+ QCOMPARE(doc->blockCount(), 2);
+
+ QTextBlock block = doc->begin();
+ QVERIFY(block.text().startsWith(" "));
+
+ block = block.next();
+ QVERIFY(block.text().startsWith(" "));
+}
+
+void tst_QTextDocumentFragment::html_brAfterHr()
+{
+ setHtml(QString::fromLatin1("Text A<br><hr><br>Text B<hr>"));
+
+ QCOMPARE(doc->blockCount(), 4);
+
+ QTextBlock block = doc->begin();
+ QCOMPARE(block.text(), QString("Text A") + QChar(QChar::LineSeparator));
+
+ block = block.next();
+ QVERIFY(block.text().isEmpty());
+
+ block = block.next();
+ QCOMPARE(block.text(), QChar(QChar::LineSeparator) + QString("Text B"));
+
+ block = block.next();
+ QVERIFY(block.text().isEmpty());
+}
+
+void tst_QTextDocumentFragment::html_unclosedHead()
+{
+ doc->setHtml(QString::fromLatin1("<html><head><title>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"
diff --git a/tests/auto/gui/text/qtextdocumentlayout/.gitignore b/tests/auto/gui/text/qtextdocumentlayout/.gitignore
new file mode 100644
index 0000000000..80671c70d0
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentlayout/.gitignore
@@ -0,0 +1 @@
+tst_qtextdocumentlayout
diff --git a/tests/auto/gui/text/qtextdocumentlayout/qtextdocumentlayout.pro b/tests/auto/gui/text/qtextdocumentlayout/qtextdocumentlayout.pro
new file mode 100644
index 0000000000..32b05ce061
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentlayout/qtextdocumentlayout.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qtextdocumentlayout.cpp
+
+
diff --git a/tests/auto/gui/text/qtextdocumentlayout/tst_qtextdocumentlayout.cpp b/tests/auto/gui/text/qtextdocumentlayout/tst_qtextdocumentlayout.cpp
new file mode 100644
index 0000000000..f5c72cd4d0
--- /dev/null
+++ b/tests/auto/gui/text/qtextdocumentlayout/tst_qtextdocumentlayout.cpp
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qtextdocument.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qdebug.h>
+#include <qpainter.h>
+#include <qtexttable.h>
+#include <qtextedit.h>
+#include <qscrollbar.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=gui/text/qtextdocumentlayout_p.h gui/text/qtextdocumentlayout.cpp
+
+class tst_QTextDocumentLayout : public QObject
+{
+ Q_OBJECT
+public:
+ inline tst_QTextDocumentLayout() {}
+ virtual ~tst_QTextDocumentLayout() {}
+
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void defaultPageSizeHandling();
+ void idealWidth();
+ void lineSeparatorFollowingTable();
+ void wrapAtWordBoundaryOrAnywhere();
+ void inlineImage();
+ void clippedTableCell();
+ void floatingTablePageBreak();
+
+private:
+ QTextDocument *doc;
+};
+
+void tst_QTextDocumentLayout::init()
+{
+ doc = new QTextDocument;
+}
+
+void tst_QTextDocumentLayout::cleanup()
+{
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextDocumentLayout::defaultPageSizeHandling()
+{
+ QAbstractTextDocumentLayout *layout = doc->documentLayout();
+ QVERIFY(layout);
+
+ QVERIFY(!doc->pageSize().isValid());
+ QSizeF docSize = layout->documentSize();
+ QVERIFY(docSize.width() > 0 && docSize.width() < 1000);
+ QVERIFY(docSize.height() > 0 && docSize.height() < 1000);
+
+ doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
+
+ docSize = layout->documentSize();
+ QVERIFY(docSize.isValid());
+ QVERIFY(docSize.width() != INT_MAX);
+ QVERIFY(docSize.height() != INT_MAX);
+}
+
+void tst_QTextDocumentLayout::idealWidth()
+{
+ doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
+ doc->setTextWidth(1000);
+ QCOMPARE(doc->textWidth(), qreal(1000));
+ QCOMPARE(doc->size().width(), doc->textWidth());
+ QVERIFY(doc->idealWidth() < doc->textWidth());
+ QVERIFY(doc->idealWidth() > 0);
+
+ QTextBlockFormat fmt;
+ fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
+ QTextCursor cursor(doc);
+ cursor.select(QTextCursor::Document);
+ cursor.mergeBlockFormat(fmt);
+
+ QCOMPARE(doc->textWidth(), qreal(1000));
+ QCOMPARE(doc->size().width(), doc->textWidth());
+ QVERIFY(doc->idealWidth() < doc->textWidth());
+ QVERIFY(doc->idealWidth() > 0);
+}
+
+// none of the QTextLine items in the document should intersect with the margin rect
+void tst_QTextDocumentLayout::lineSeparatorFollowingTable()
+{
+ QString html_begin("<html><table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table><br>");
+ QString html_text("bla bla bla bla bla bla bla bla<br>");
+ QString html_end("<table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table></html>");
+
+ QString html = html_begin;
+
+ for (int i = 0; i < 80; ++i)
+ html += html_text;
+
+ html += html_end;
+
+ doc->setHtml(html);
+
+ QTextCursor cursor(doc);
+ cursor.movePosition(QTextCursor::Start);
+
+ const int margin = 87;
+ const int pageWidth = 873;
+ const int pageHeight = 1358;
+
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ fmt.setMargin(margin);
+ doc->rootFrame()->setFrameFormat(fmt);
+
+ QFont font(doc->defaultFont());
+ font.setPointSize(10);
+ doc->setDefaultFont(font);
+ doc->setPageSize(QSizeF(pageWidth, pageHeight));
+
+ QRectF marginRect(QPointF(0, pageHeight - margin), QSizeF(pageWidth, 2 * margin));
+
+ // force layouting
+ doc->pageCount();
+
+ for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
+ QTextLayout *layout = block.layout();
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ QRectF rect = line.rect().translated(layout->position());
+ QVERIFY(!rect.intersects(marginRect));
+ }
+ }
+}
+
+void tst_QTextDocumentLayout::wrapAtWordBoundaryOrAnywhere()
+{
+ //task 150562
+ QTextEdit edit;
+ edit.setText("<table><tr><td>hello hello hello"
+ "thisisabigwordthisisabigwordthisisabigwordthisisabigwordthisisabigword"
+ "hello hello hello</td></tr></table>");
+ edit.setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ edit.resize(100, 100);
+ edit.show();
+ QVERIFY(!edit.horizontalScrollBar()->isVisible());
+}
+
+void tst_QTextDocumentLayout::inlineImage()
+{
+ doc->setPageSize(QSizeF(800, 500));
+
+ QImage img(400, 400, QImage::Format_RGB32);
+ QLatin1String name("bigImage");
+
+ doc->addResource(QTextDocument::ImageResource, QUrl(name), img);
+
+ QTextImageFormat imgFormat;
+ imgFormat.setName(name);
+ imgFormat.setWidth(img.width());
+
+ QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
+ qreal height = doc->pageSize().height() - fmt.topMargin() - fmt.bottomMargin();
+ imgFormat.setHeight(height);
+
+ QTextCursor cursor(doc);
+ cursor.insertImage(imgFormat);
+
+ QCOMPARE(doc->pageCount(), 1);
+}
+
+void tst_QTextDocumentLayout::clippedTableCell()
+{
+ const char *html =
+ "<table style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""
+ "border=\"0\" margin=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td></td></tr></table>";
+
+ doc->setHtml(html);
+ doc->pageSize();
+
+ QTextCursor cursor(doc);
+ cursor.movePosition(QTextCursor::Right);
+
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table);
+
+ QTextCursor cellCursor = table->cellAt(0, 0).firstCursorPosition();
+ QImage src(16, 16, QImage::Format_ARGB32_Premultiplied);
+ src.fill(0xffff0000);
+ cellCursor.insertImage(src);
+
+ QTextBlock block = cellCursor.block();
+ QRectF r = doc->documentLayout()->blockBoundingRect(block);
+
+ QRectF rect(0, 0, r.left() + 1, 64);
+
+ QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
+ img.fill(0x0);
+ QImage expected = img;
+ QPainter p(&img);
+ doc->drawContents(&p, rect);
+ p.end();
+ p.begin(&expected);
+ r.setWidth(1);
+ p.fillRect(r, Qt::red);
+ p.end();
+
+ img.save("img.png");
+ expected.save("expected.png");
+ QCOMPARE(img, expected);
+}
+
+void tst_QTextDocumentLayout::floatingTablePageBreak()
+{
+ doc->clear();
+
+ QTextCursor cursor(doc);
+
+ QTextTableFormat tableFormat;
+ tableFormat.setPosition(QTextFrameFormat::FloatLeft);
+ QTextTable *table = cursor.insertTable(50, 1, tableFormat);
+ Q_UNUSED(table);
+
+ // Make height of document 2/3 of the table, fitting the table into two pages
+ QSizeF documentSize = doc->size();
+ documentSize.rheight() *= 2.0 / 3.0;
+
+ doc->setPageSize(documentSize);
+
+ QCOMPARE(doc->pageCount(), 2);
+}
+
+
+QTEST_MAIN(tst_QTextDocumentLayout)
+#include "tst_qtextdocumentlayout.moc"
diff --git a/tests/auto/gui/text/qtextformat/.gitignore b/tests/auto/gui/text/qtextformat/.gitignore
new file mode 100644
index 0000000000..3e3be422fe
--- /dev/null
+++ b/tests/auto/gui/text/qtextformat/.gitignore
@@ -0,0 +1 @@
+tst_qtextformat
diff --git a/tests/auto/gui/text/qtextformat/qtextformat.pro b/tests/auto/gui/text/qtextformat/qtextformat.pro
new file mode 100644
index 0000000000..30f6e50c61
--- /dev/null
+++ b/tests/auto/gui/text/qtextformat/qtextformat.pro
@@ -0,0 +1,9 @@
+############################################################
+# Project file for autotest for file qtextformat.h
+############################################################
+
+load(qttest_p4)
+
+SOURCES += tst_qtextformat.cpp
+
+
diff --git a/tests/auto/gui/text/qtextformat/tst_qtextformat.cpp b/tests/auto/gui/text/qtextformat/tst_qtextformat.cpp
new file mode 100644
index 0000000000..b235b112b6
--- /dev/null
+++ b/tests/auto/gui/text/qtextformat/tst_qtextformat.cpp
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qsettings.h>
+#include <qtextformat.h>
+#include <qtextdocument.h>
+#include <qtextcursor.h>
+#include <qtextobject.h>
+#include <qtextlayout.h>
+#include <qabstracttextdocumentlayout.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTextFormat : public QObject
+{
+Q_OBJECT
+
+private slots:
+ void getSetCheck();
+ void defaultAlignment();
+ void testQTextCharFormat() const;
+ void testUnderlinePropertyPrecedence();
+ void toFormat();
+ void resolveFont();
+ void getSetTabs();
+ void testTabsUsed();
+ void testFontStyleSetters();
+};
+
+/*! \internal
+ This (used to) trigger a crash in:
+
+ QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
+
+ which is most easily produced through QSettings.
+ */
+void tst_QTextFormat::testQTextCharFormat() const
+{
+ QSettings settings("test", "test");
+ QTextCharFormat test;
+
+ settings.value("test", test);
+}
+
+// Testing get/set functions
+void tst_QTextFormat::getSetCheck()
+{
+ QTextFormat obj1;
+ // int QTextFormat::objectIndex()
+ // void QTextFormat::setObjectIndex(int)
+ obj1.setObjectIndex(0);
+ QCOMPARE(0, obj1.objectIndex());
+ obj1.setObjectIndex(INT_MIN);
+ QCOMPARE(INT_MIN, obj1.objectIndex());
+ obj1.setObjectIndex(INT_MAX);
+ QCOMPARE(INT_MAX, obj1.objectIndex());
+}
+
+void tst_QTextFormat::defaultAlignment()
+{
+ QTextBlockFormat fmt;
+ QVERIFY(!fmt.hasProperty(QTextFormat::BlockAlignment));
+ QCOMPARE(fmt.intProperty(QTextFormat::BlockAlignment), 0);
+ QVERIFY(fmt.alignment() == Qt::AlignLeft);
+}
+
+void tst_QTextFormat::testUnderlinePropertyPrecedence()
+{
+ QTextCharFormat format;
+ // use normal accessors and check internal state
+ format.setUnderlineStyle(QTextCharFormat::NoUnderline);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), false);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).toInt(), 0);
+
+ format = QTextCharFormat();
+ format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), true);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).toInt(), 1);
+
+ format = QTextCharFormat();
+ format.setUnderlineStyle(QTextCharFormat::DotLine);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), false);
+ QVERIFY(format.property(QTextFormat::TextUnderlineStyle).toInt() > 0);
+
+ // override accessors and use setProperty to create a false state.
+ // then check font()
+ format = QTextCharFormat();
+ format.setProperty(QTextCharFormat::FontUnderline, true);
+ QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
+ QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), true);
+ QCOMPARE(format.fontUnderline(), true);
+ QCOMPARE(format.font().underline(), true);
+
+ format = QTextCharFormat();
+ // create conflict. Should use the new property
+ format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
+ format.setProperty(QTextCharFormat::FontUnderline, false);
+ QCOMPARE(format.fontUnderline(), true);
+ QCOMPARE(format.font().underline(), true);
+
+ format = QTextCharFormat();
+ // create conflict. Should use the new property
+ format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::NoUnderline);
+ format.setProperty(QTextCharFormat::FontUnderline, true);
+ QCOMPARE(format.fontUnderline(), false);
+ QCOMPARE(format.font().underline(), false);
+
+ // do it again, but reverse the ordering (we use a QVector internally, so test a LOT ;)
+ // create conflict. Should use the new property
+ format.setProperty(QTextCharFormat::FontUnderline, false);
+ format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
+ QCOMPARE(format.fontUnderline(), true);
+ QCOMPARE(format.font().underline(), true);
+
+ format = QTextCharFormat();
+ // create conflict. Should use the new property
+ format.setProperty(QTextCharFormat::FontUnderline, true);
+ format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::NoUnderline);
+ QCOMPARE(format.fontUnderline(), false);
+ QCOMPARE(format.font().underline(), false);
+}
+
+void tst_QTextFormat::toFormat()
+{
+ {
+ QTextFormat fmt = QTextFrameFormat();
+ QCOMPARE(fmt.toFrameFormat().type(), int(QTextFormat::FrameFormat));
+ }
+
+ {
+ QTextFormat fmt = QTextTableFormat();
+ QCOMPARE(fmt.toTableFormat().type(), int(QTextFormat::FrameFormat));
+ QCOMPARE(fmt.toTableFormat().objectType(), int(QTextFormat::TableObject));
+ }
+
+ {
+ QTextFormat fmt = QTextBlockFormat();
+ QCOMPARE(fmt.toBlockFormat().type(), int(QTextFormat::BlockFormat));
+ }
+
+ {
+ QTextFormat fmt = QTextCharFormat();
+ QCOMPARE(fmt.toCharFormat().type(), int(QTextFormat::CharFormat));
+ }
+
+ {
+ QTextFormat fmt = QTextListFormat();
+ QCOMPARE(fmt.toListFormat().type(), int(QTextFormat::ListFormat));
+ }
+}
+
+void tst_QTextFormat::resolveFont()
+{
+ QTextDocument doc;
+
+ QTextCharFormat fmt;
+ fmt.setProperty(QTextFormat::ForegroundBrush, Qt::blue);
+ fmt.setProperty(QTextFormat::FontItalic, true);
+ QTextCursor(&doc).insertText("Test", fmt);
+
+ QVector<QTextFormat> formats = doc.allFormats();
+ QCOMPARE(formats.count(), 3);
+
+ QVERIFY(formats.at(2).type() == QTextFormat::CharFormat);
+ fmt = formats.at(2).toCharFormat();
+
+ QVERIFY(!fmt.font().underline());
+ QVERIFY(fmt.hasProperty(QTextFormat::ForegroundBrush));
+
+ QFont f;
+ f.setUnderline(true);
+ doc.setDefaultFont(f);
+ formats = doc.allFormats();
+ fmt = formats.at(2).toCharFormat();
+
+ QVERIFY(fmt.font().underline());
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+
+ // verify that deleting a non-existent property does not affect the font resolving
+
+ QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
+ fmt.clearProperty(QTextFormat::BackgroundBrush);
+ QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
+
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+ QVERIFY(fmt.font().underline());
+
+ // verify that deleting an existent but font _unrelated_ property does not affect the font resolving
+
+ QVERIFY(fmt.hasProperty(QTextFormat::ForegroundBrush));
+ fmt.clearProperty(QTextFormat::ForegroundBrush);
+ QVERIFY(!fmt.hasProperty(QTextFormat::ForegroundBrush));
+
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+ QVERIFY(fmt.font().underline());
+
+ // verify that removing a font property _does_ clear the resolving
+
+ QVERIFY(fmt.hasProperty(QTextFormat::FontItalic));
+ fmt.clearProperty(QTextFormat::FontItalic);
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontItalic));
+
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+ QVERIFY(!fmt.font().underline());
+ QVERIFY(!fmt.font().italic());
+
+ // reset
+ fmt = formats.at(2).toCharFormat();
+
+ QVERIFY(fmt.font().underline());
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+
+ // verify that _setting_ an unrelated property does _not_ affect the resolving
+
+ QVERIFY(!fmt.hasProperty(QTextFormat::IsAnchor));
+ fmt.setProperty(QTextFormat::IsAnchor, true);
+ QVERIFY(fmt.hasProperty(QTextFormat::IsAnchor));
+
+ QVERIFY(fmt.font().underline());
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+
+ // verify that setting a _related_ font property does affect the resolving
+ //
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontStrikeOut));
+ fmt.setProperty(QTextFormat::FontStrikeOut, true);
+ QVERIFY(fmt.hasProperty(QTextFormat::FontStrikeOut));
+
+ QVERIFY(!fmt.font().underline());
+ QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
+ QVERIFY(fmt.font().strikeOut());
+}
+
+void tst_QTextFormat::getSetTabs()
+{
+ class Comparator {
+ public:
+ Comparator(const QList<QTextOption::Tab> &tabs, const QList<QTextOption::Tab> &tabs2)
+ {
+ QCOMPARE(tabs.count(), tabs2.count());
+ for(int i=0; i < tabs.count(); i++) {
+ QTextOption::Tab t1 = tabs[i];
+ QTextOption::Tab t2 = tabs2[i];
+ QCOMPARE(t1.position, t2.position);
+ QCOMPARE(t1.type, t2.type);
+ QCOMPARE(t1.delimiter, t2.delimiter);
+ }
+ }
+ };
+
+ QList<QTextOption::Tab> tabs;
+ QTextBlockFormat format;
+ format.setTabPositions(tabs);
+ Comparator c1(tabs, format.tabPositions());
+
+ QTextOption::Tab tab1;
+ tab1.position = 1234;
+ tabs.append(tab1);
+ format.setTabPositions(tabs);
+ Comparator c2(tabs, format.tabPositions());
+
+ QTextOption::Tab tab2(3456, QTextOption::RightTab, QChar('x'));
+ tabs.append(tab2);
+ format.setTabPositions(tabs);
+ Comparator c3(tabs, format.tabPositions());
+}
+
+void tst_QTextFormat::testTabsUsed()
+{
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+
+ QList<QTextOption::Tab> tabs;
+ QTextBlockFormat format;
+ QTextOption::Tab tab;
+ tab.position = 100;
+ tabs.append(tab);
+ format.setTabPositions(tabs);
+ cursor.mergeBlockFormat(format);
+ cursor.insertText("foo\tbar");
+ //doc.setPageSize(QSizeF(200, 200));
+ doc.documentLayout()->pageCount(); // force layout;
+
+ QTextBlock block = doc.begin();
+ QTextLayout *layout = block.layout();
+ QVERIFY(layout);
+ QCOMPARE(layout->lineCount(), 1);
+ QTextLine line = layout->lineAt(0);
+ QCOMPARE(line.cursorToX(4), 100.);
+
+ QTextOption option = layout->textOption();
+ QCOMPARE(option.tabs().count(), tabs.count());
+
+}
+
+void tst_QTextFormat::testFontStyleSetters()
+{
+ QTextCharFormat format;
+
+ // test the setters
+ format.setFontStyleHint(QFont::Serif);
+ QCOMPARE(format.font().styleHint(), QFont::Serif);
+ QCOMPARE(format.font().styleStrategy(), QFont::PreferDefault);
+ format.setFontStyleStrategy(QFont::PreferOutline);
+ QCOMPARE(format.font().styleStrategy(), QFont::PreferOutline);
+
+ // test setting properties through setFont()
+ QFont font;
+ font.setStyleHint(QFont::SansSerif, QFont::PreferAntialias);
+ format.setFont(font);
+ QCOMPARE(format.font().styleHint(), QFont::SansSerif);
+ QCOMPARE(format.font().styleStrategy(), QFont::PreferAntialias);
+
+ // test kerning
+ format.setFontKerning(false);
+ QCOMPARE(format.font().kerning(), false);
+ format.setFontKerning(true);
+ QCOMPARE(format.font().kerning(), true);
+ font.setKerning(false);
+ format.setFont(font);
+ QCOMPARE(format.font().kerning(), false);
+}
+
+QTEST_MAIN(tst_QTextFormat)
+#include "tst_qtextformat.moc"
diff --git a/tests/auto/gui/text/qtextlayout/.gitignore b/tests/auto/gui/text/qtextlayout/.gitignore
new file mode 100644
index 0000000000..11758969db
--- /dev/null
+++ b/tests/auto/gui/text/qtextlayout/.gitignore
@@ -0,0 +1 @@
+tst_qtextlayout
diff --git a/tests/auto/gui/text/qtextlayout/qtextlayout.pro b/tests/auto/gui/text/qtextlayout/qtextlayout.pro
new file mode 100644
index 0000000000..6bf0065e4f
--- /dev/null
+++ b/tests/auto/gui/text/qtextlayout/qtextlayout.pro
@@ -0,0 +1,12 @@
+load(qttest_p4)
+QT += core-private gui-private
+HEADERS +=
+SOURCES += tst_qtextlayout.cpp
+DEFINES += QT_COMPILES_IN_HARFBUZZ
+INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src
+
+symbian {
+ TARGET.EPOCHEAPSIZE = 100000 20000000
+}
+
+qpa:contains(QT_CONFIG,qpa):CONFIG+=insignificant_test # QTBUG-20979
diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
new file mode 100644
index 0000000000..2414ab3e37
--- /dev/null
+++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
@@ -0,0 +1,1506 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*
+ !!!!!! Warning !!!!!
+ Please don't save this file in emacs. It contains utf8 text sequences emacs will
+ silently convert to a series of question marks.
+ */
+#include <QtTest/QtTest>
+
+
+
+#include <private/qtextengine_p.h>
+#include <qtextlayout.h>
+
+#include <qdebug.h>
+
+
+#define TESTFONT_SIZE 12
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTextLayout : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextLayout();
+ virtual ~tst_QTextLayout();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void getSetCheck();
+ void lineBreaking();
+ void simpleBoundingRect();
+ void threeLineBoundingRect();
+ void boundingRectWithLongLineAndNoWrap();
+ void forcedBreaks();
+ void breakAny();
+ void noWrap();
+ void cursorToXForInlineObjects();
+ void cursorToXForSetColumns();
+ void defaultWordSeparators_data();
+ void defaultWordSeparators();
+ void cursorMovementFromInvalidPositions();
+ void cursorMovementInsideSpaces();
+ void charWordStopOnLineSeparator();
+ void xToCursorAtEndOfLine();
+ void boundingRectTopLeft();
+ void charStopForSurrogatePairs();
+ void tabStops();
+ void integerOverflow();
+ void testDefaultTabs();
+ void testTabs();
+ void testMultilineTab();
+ void testRightTab();
+ void testTabsInAlignedParag();
+ void testCenteredTab();
+ void testDelimiterTab();
+ void testMultiTab();
+ void testTabDPIScale();
+ void tabsForRtl();
+ void tabHeight();
+ void capitalization_allUpperCase();
+ void capitalization_allLowerCase();
+ void capitalization_smallCaps();
+ void capitalization_capitalize();
+ void longText();
+ void widthOfTabs();
+ void columnWrapWithTabs();
+ void boundingRectForUnsetLineWidth();
+ void boundingRectForSetLineWidth();
+ void glyphLessItems();
+
+ // QTextLine stuff
+ void setNumColumnsWrapAtWordBoundaryOrAnywhere();
+ void setNumColumnsWordWrap();
+ void smallTextLengthNoWrap();
+ void smallTextLengthWordWrap();
+ void smallTextLengthWrapAtWordBoundaryOrAnywhere();
+ void testLineBreakingAllSpaces();
+ void lineWidthFromBOM();
+ void textWidthVsWIdth();
+ void textWithSurrogates_qtbug15679();
+ void textWidthWithStackedTextEngine();
+ void textWidthWithLineSeparator();
+ void cursorInLigatureWithMultipleLines();
+ void xToCursorForLigatures();
+
+private:
+ QFont testFont;
+};
+
+// Testing get/set functions
+void tst_QTextLayout::getSetCheck()
+{
+ QString str("Bogus text");
+ QTextLayout layout(str, testFont);
+ layout.beginLayout();
+ QTextEngine *engine = layout.engine();
+ QTextInlineObject obj1(0, engine);
+ // qreal QTextInlineObject::width()
+ // void QTextInlineObject::setWidth(qreal)
+ obj1.setWidth(0.0);
+ QCOMPARE(0.0, obj1.width());
+ obj1.setWidth(1.2);
+ QVERIFY(1.0 < obj1.width());
+
+ // qreal QTextInlineObject::ascent()
+ // void QTextInlineObject::setAscent(qreal)
+ obj1.setAscent(0.0);
+ QCOMPARE(0.0, obj1.ascent());
+ obj1.setAscent(1.2);
+ QVERIFY(1.0 < obj1.ascent());
+
+ // qreal QTextInlineObject::descent()
+ // void QTextInlineObject::setDescent(qreal)
+ obj1.setDescent(0.0);
+ QCOMPARE(0.0, obj1.descent());
+ obj1.setDescent(1.2);
+ QVERIFY(1.0 < obj1.descent());
+
+ QTextLayout obj2;
+ // bool QTextLayout::cacheEnabled()
+ // void QTextLayout::setCacheEnabled(bool)
+ obj2.setCacheEnabled(false);
+ QCOMPARE(false, obj2.cacheEnabled());
+ obj2.setCacheEnabled(true);
+ QCOMPARE(true, obj2.cacheEnabled());
+}
+
+QT_BEGIN_NAMESPACE
+extern void qt_setQtEnableTestFont(bool value);
+QT_END_NAMESPACE
+
+tst_QTextLayout::tst_QTextLayout()
+{
+ qt_setQtEnableTestFont(true);
+}
+
+tst_QTextLayout::~tst_QTextLayout()
+{
+}
+
+void tst_QTextLayout::init()
+{
+ testFont = QFont();
+ testFont.setFamily("__Qt__Box__Engine__");
+ testFont.setPixelSize(TESTFONT_SIZE);
+ testFont.setWeight(QFont::Normal);
+ QCOMPARE(QFontMetrics(testFont).width('a'), testFont.pixelSize());
+}
+
+void tst_QTextLayout::cleanup()
+{
+ testFont = QFont();
+}
+
+
+void tst_QTextLayout::lineBreaking()
+{
+#if defined(Q_WS_X11)
+ struct Breaks {
+ const char *utf8;
+ uchar breaks[32];
+ };
+ Breaks brks[] = {
+ { "11", { false, 0xff } },
+ { "aa", { false, 0xff } },
+ { "++", { false, 0xff } },
+ { "--", { false, 0xff } },
+ { "((", { false, 0xff } },
+ { "))", { false, 0xff } },
+ { "..", { false, 0xff } },
+ { "\"\"", { false, 0xff } },
+ { "$$", { false, 0xff } },
+ { "!!", { false, 0xff } },
+ { "??", { false, 0xff } },
+ { ",,", { false, 0xff } },
+
+ { ")()", { true, false, 0xff } },
+ { "?!?", { false, false, 0xff } },
+ { ".,.", { false, false, 0xff } },
+ { "+-+", { false, false, 0xff } },
+ { "+=+", { false, false, 0xff } },
+ { "+(+", { false, false, 0xff } },
+ { "+)+", { false, false, 0xff } },
+
+ { "a b", { false, true, 0xff } },
+ { "a(b", { false, false, 0xff } },
+ { "a)b", { false, false, 0xff } },
+ { "a-b", { false, true, 0xff } },
+ { "a.b", { false, false, 0xff } },
+ { "a+b", { false, false, 0xff } },
+ { "a?b", { false, false, 0xff } },
+ { "a!b", { false, false, 0xff } },
+ { "a$b", { false, false, 0xff } },
+ { "a,b", { false, false, 0xff } },
+ { "a/b", { false, false, 0xff } },
+ { "1/2", { false, false, 0xff } },
+ { "./.", { false, false, 0xff } },
+ { ",/,", { false, false, 0xff } },
+ { "!/!", { false, false, 0xff } },
+ { "\\/\\", { false, false, 0xff } },
+ { "1 2", { false, true, 0xff } },
+ { "1(2", { false, false, 0xff } },
+ { "1)2", { false, false, 0xff } },
+ { "1-2", { false, false, 0xff } },
+ { "1.2", { false, false, 0xff } },
+ { "1+2", { false, false, 0xff } },
+ { "1?2", { false, true, 0xff } },
+ { "1!2", { false, true, 0xff } },
+ { "1$2", { false, false, 0xff } },
+ { "1,2", { false, false, 0xff } },
+ { "1/2", { false, false, 0xff } },
+ { "\330\260\331\216\331\204\331\220\331\203\331\216", { false, false, false, false, false, 0xff } },
+ { "\330\247\331\204\331\205 \330\247\331\204\331\205", { false, false, false, true, false, false, 0xff } },
+ { "1#2", { false, false, 0xff } },
+ { "!#!", { false, false, 0xff } },
+ { 0, {} }
+ };
+ Breaks *b = brks;
+ while (b->utf8) {
+ QString str = QString::fromUtf8(b->utf8);
+ QTextEngine engine(str, QFont());
+ const HB_CharAttributes *attrs = engine.attributes();
+ int i;
+ for (i = 0; i < (int)str.length() - 1; ++i) {
+ QVERIFY(b->breaks[i] != 0xff);
+ if ( (attrs[i].lineBreakType != HB_NoBreak) != (bool)b->breaks[i] ) {
+ qDebug("test case \"%s\" failed at char %d; break type: %d", b->utf8, i, attrs[i].lineBreakType);
+ QCOMPARE( (attrs[i].lineBreakType != HB_NoBreak), (bool)b->breaks[i] );
+ }
+ }
+ QVERIFY(attrs[i].lineBreakType == HB_ForcedBreak);
+ QCOMPARE(b->breaks[i], (uchar)0xff);
+ ++b;
+ }
+#else
+ QSKIP("This test can not be run on non-X11 platforms", SkipAll);
+#endif
+}
+
+void tst_QTextLayout::simpleBoundingRect()
+{
+ /* just check if boundingRect() gives sane values. The text is not broken. */
+
+ QString hello("hello world");
+
+ const int width = hello.length() * testFont.pixelSize();
+
+ QTextLayout layout(hello, testFont);
+ layout.beginLayout();
+
+ QTextLine line = layout.createLine();
+ line.setLineWidth(width);
+ QCOMPARE(line.textLength(), hello.length());
+ QCOMPARE(layout.boundingRect(), QRectF(0, 0, width, QFontMetrics(testFont).height()));
+}
+
+void tst_QTextLayout::threeLineBoundingRect()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("QTestFontEngine on the mac does not support logclusters at the moment", SkipAll);
+#endif
+ /* stricter check. break text into three lines */
+
+ QString firstWord("hello");
+ QString secondWord("world");
+ QString thirdWord("test");
+ QString text(firstWord + ' ' + secondWord + ' ' + thirdWord);
+
+ const int firstLineWidth = firstWord.length() * testFont.pixelSize();
+ const int secondLineWidth = secondWord.length() * testFont.pixelSize();
+ const int thirdLineWidth = thirdWord.length() * testFont.pixelSize();
+
+ const int longestLine = qMax(firstLineWidth, qMax(secondLineWidth, thirdLineWidth));
+
+ QTextLayout layout(text, testFont);
+ layout.beginLayout();
+
+ int pos = 0;
+ int y = 0;
+ QTextLine line = layout.createLine();
+ line.setLineWidth(firstLineWidth);
+ line.setPosition(QPoint(0, y));
+ QCOMPARE(line.textStart(), pos);
+ // + 1 for trailing space
+ QCOMPARE(line.textLength(), firstWord.length() + 1);
+ QCOMPARE(qRound(line.naturalTextWidth()), firstLineWidth);
+
+ pos += line.textLength();
+ y += qRound(line.ascent() + line.descent());
+
+ line = layout.createLine();
+ line.setLineWidth(secondLineWidth);
+ line.setPosition(QPoint(0, y));
+ // + 1 for trailing space
+ QCOMPARE(line.textStart(), pos);
+ QCOMPARE(line.textLength(), secondWord.length() + 1);
+ QCOMPARE(qRound(line.naturalTextWidth()), secondLineWidth);
+
+ pos += line.textLength();
+ y += qRound(line.ascent() + line.descent());
+
+ line = layout.createLine();
+ line.setLineWidth(secondLineWidth);
+ line.setPosition(QPoint(0, y));
+ // no trailing space here!
+ QCOMPARE(line.textStart(), pos);
+ QCOMPARE(line.textLength(), thirdWord.length());
+ QCOMPARE(qRound(line.naturalTextWidth()), thirdLineWidth);
+ y += qRound(line.ascent() + line.descent());
+
+ QCOMPARE(layout.boundingRect(), QRectF(0, 0, longestLine, y + 1));
+}
+
+void tst_QTextLayout::boundingRectWithLongLineAndNoWrap()
+{
+ QString longString("thisisaverylongstringthatcannotbewrappedatallitjustgoesonandonlikeonebigword");
+
+ const int width = longString.length() * testFont.pixelSize() / 20; // very small widthx
+
+ QTextLayout layout(longString, testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(width);
+
+ QVERIFY(layout.boundingRect().width() >= line.width());
+ QCOMPARE(layout.boundingRect().width(), line.naturalTextWidth());
+}
+
+void tst_QTextLayout::forcedBreaks()
+{
+ QString text = "A\n\nB\nC";
+ text.replace('\n', QChar::LineSeparator);
+
+ QTextLayout layout(text, testFont);
+
+ layout.beginLayout();
+
+ int pos = 0;
+
+ QTextLine line = layout.createLine();
+ line.setLineWidth(0x10000);
+ QCOMPARE(line.textStart(), pos);
+ QCOMPARE(line.textLength(),2);
+ QCOMPARE(qRound(line.naturalTextWidth()),testFont.pixelSize());
+ QCOMPARE((int) line.height(), testFont.pixelSize() + 1); // + 1 baseline
+ QCOMPARE(line.xToCursor(0), line.textStart());
+ pos += line.textLength();
+
+ line = layout.createLine();
+ line.setLineWidth(0x10000);
+ QCOMPARE(line.textStart(),pos);
+ QCOMPARE(line.textLength(),1);
+ QCOMPARE(qRound(line.naturalTextWidth()), 0);
+ QCOMPARE((int) line.height(), testFont.pixelSize() + 1); // + 1 baseline
+ QCOMPARE(line.xToCursor(0), line.textStart());
+ pos += line.textLength();
+
+ line = layout.createLine();
+ line.setLineWidth(0x10000);
+ QCOMPARE(line.textStart(),pos);
+ QCOMPARE(line.textLength(),2);
+ QCOMPARE(qRound(line.naturalTextWidth()),testFont.pixelSize());
+ QCOMPARE(qRound(line.height()), testFont.pixelSize() + 1); // + 1 baseline
+ QCOMPARE(line.xToCursor(0), line.textStart());
+ pos += line.textLength();
+
+ line = layout.createLine();
+ line.setLineWidth(0x10000);
+ QCOMPARE(line.textStart(),pos);
+ QCOMPARE(line.textLength(),1);
+ QCOMPARE(qRound(line.naturalTextWidth()), testFont.pixelSize());
+ QCOMPARE((int) line.height(), testFont.pixelSize() + 1); // + 1 baseline
+ QCOMPARE(line.xToCursor(0), line.textStart());
+}
+
+void tst_QTextLayout::breakAny()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("QTestFontEngine on the mac does not support logclusters at the moment", SkipAll);
+#endif
+ QString text = "ABCD";
+
+ QTextLayout layout(text, testFont);
+ QTextLine line;
+
+ QTextOption opt;
+ opt.setWrapMode(QTextOption::WrapAnywhere);
+ layout.setTextOption(opt);
+ layout.beginLayout();
+
+ line = layout.createLine();
+ line.setLineWidth(testFont.pixelSize() * 2);
+ QCOMPARE(line.textStart(), 0);
+ QCOMPARE(line.textLength(), 2);
+
+ line = layout.createLine();
+ line.setLineWidth(testFont.pixelSize() * 2);
+ QCOMPARE(line.textStart(), 2);
+ QCOMPARE(line.textLength(), 2);
+
+ line = layout.createLine();
+ QVERIFY(!line.isValid());
+
+ layout.endLayout();
+
+ text = "ABCD EFGH";
+ layout.setText(text);
+ layout.beginLayout();
+
+ line = layout.createLine();
+ line.setLineWidth(testFont.pixelSize() * 7);
+ QCOMPARE(line.textStart(), 0);
+ QCOMPARE(line.textLength(), 7);
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::noWrap()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("QTestFontEngine on the mac does not support logclusters at the moment", SkipAll);
+#endif
+ QString text = "AB CD";
+
+ QTextLayout layout(text, testFont);
+ QTextLine line;
+
+ QTextOption opt;
+ opt.setWrapMode(QTextOption::NoWrap);
+ layout.setTextOption(opt);
+ layout.beginLayout();
+
+ line = layout.createLine();
+ line.setLineWidth(testFont.pixelSize() * 2);
+ QCOMPARE(line.textStart(), 0);
+ QCOMPARE(line.textLength(), 5);
+
+ line = layout.createLine();
+ QVERIFY(!line.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::cursorToXForInlineObjects()
+{
+ QChar ch(QChar::ObjectReplacementCharacter);
+ QString text(ch);
+ QTextLayout layout(text, testFont);
+ layout.beginLayout();
+
+ QTextEngine *engine = layout.engine();
+ const int item = engine->findItem(0);
+ engine->layoutData->items[item].width = 32;
+
+ QTextLine line = layout.createLine();
+ line.setLineWidth(0x10000);
+
+ QCOMPARE(line.cursorToX(0), qreal(0));
+ QCOMPARE(line.cursorToX(1), qreal(32));
+}
+
+void tst_QTextLayout::cursorToXForSetColumns()
+{
+ QTextLayout lay("abc", testFont);
+ QTextOption o = lay.textOption();
+ o.setWrapMode(QTextOption::WrapAnywhere);
+
+ // enable/disable this line for full effect ;)
+ o.setAlignment(Qt::AlignHCenter);
+
+ lay.setTextOption(o);
+ lay.beginLayout();
+ QTextLine line = lay.createLine();
+ line.setNumColumns(1);
+ lay.endLayout();
+ QCOMPARE(line.cursorToX(0), 0.);
+ QCOMPARE(line.cursorToX(1), (qreal) TESTFONT_SIZE);
+}
+
+void tst_QTextLayout::defaultWordSeparators_data()
+{
+ QTest::addColumn<QString>("text");
+ QTest::addColumn<int>("startPos");
+ QTest::addColumn<int>("endPos");
+
+ QString separators(".,:;-<>[](){}=/+%&^*");
+ separators += QLatin1String("!?");
+ for (int i = 0; i < separators.count(); ++i) {
+ QTest::newRow(QString::number(i).toAscii().data())
+ << QString::fromLatin1("abcd") + separators.at(i) + QString::fromLatin1("efgh")
+ << 0 << 4;
+ }
+
+ QTest::newRow("nbsp")
+ << QString::fromLatin1("abcd") + QString(QChar::Nbsp) + QString::fromLatin1("efgh")
+ << 0 << 5;
+
+ QTest::newRow("tab")
+ << QString::fromLatin1("abcd") + QString::fromLatin1("\t") + QString::fromLatin1("efgh")
+ << 0 << 5;
+
+ QTest::newRow("lineseparator")
+ << QString::fromLatin1("abcd") + QString(QChar::LineSeparator) + QString::fromLatin1("efgh")
+ << 0 << 5;
+
+ QTest::newRow("empty")
+ << QString()
+ << 0 << 0;
+}
+
+void tst_QTextLayout::defaultWordSeparators()
+{
+ QFETCH(QString, text);
+ QFETCH(int, startPos);
+ QFETCH(int, endPos);
+ QTextLayout layout(text, testFont);
+
+ QCOMPARE(layout.nextCursorPosition(startPos, QTextLayout::SkipWords), endPos);
+ QCOMPARE(layout.previousCursorPosition(endPos, QTextLayout::SkipWords), startPos);
+}
+
+void tst_QTextLayout::cursorMovementFromInvalidPositions()
+{
+ int badpos = 10000;
+
+ QTextLayout layout("ABC", testFont);
+
+ QCOMPARE(layout.previousCursorPosition(-badpos, QTextLayout::SkipCharacters), -badpos);
+ QCOMPARE(layout.nextCursorPosition(-badpos, QTextLayout::SkipCharacters), -badpos);
+
+ QCOMPARE(layout.previousCursorPosition(badpos, QTextLayout::SkipCharacters), badpos);
+ QCOMPARE(layout.nextCursorPosition(badpos, QTextLayout::SkipCharacters), badpos);
+}
+
+void tst_QTextLayout::cursorMovementInsideSpaces()
+{
+ QTextLayout layout("ABC DEF", testFont);
+
+ QCOMPARE(layout.previousCursorPosition(6, QTextLayout::SkipWords), 0);
+ QCOMPARE(layout.nextCursorPosition(6, QTextLayout::SkipWords), 15);
+
+
+ QTextLayout layout2("ABC\t\t\t\t\t\t\t\t\t\t\t\tDEF", testFont);
+
+ QCOMPARE(layout2.previousCursorPosition(6, QTextLayout::SkipWords), 0);
+ QCOMPARE(layout2.nextCursorPosition(6, QTextLayout::SkipWords), 15);
+}
+
+void tst_QTextLayout::charWordStopOnLineSeparator()
+{
+ const QChar lineSeparator(QChar::LineSeparator);
+ QString txt;
+ txt.append(lineSeparator);
+ txt.append(lineSeparator);
+ QTextLayout layout(txt, testFont);
+ QTextEngine *engine = layout.engine();
+ const HB_CharAttributes *attrs = engine->attributes();
+ QVERIFY(attrs);
+ QVERIFY(attrs[1].charStop);
+}
+
+void tst_QTextLayout::xToCursorAtEndOfLine()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("QTestFontEngine on the mac does not support logclusters at the moment", SkipAll);
+#endif
+ QString text = "FirstLine SecondLine";
+ text.replace('\n', QChar::LineSeparator);
+
+ const qreal firstLineWidth = QString("FirstLine").length() * testFont.pixelSize();
+
+ QTextLayout layout(text, testFont);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ QVERIFY(line.isValid());
+ line.setLineWidth(firstLineWidth);
+ QVERIFY(layout.createLine().isValid());
+ QVERIFY(!layout.createLine().isValid());
+ layout.endLayout();
+
+ line = layout.lineAt(0);
+ QCOMPARE(line.xToCursor(100000), 9);
+ line = layout.lineAt(1);
+ QCOMPARE(line.xToCursor(100000), 20);
+}
+
+void tst_QTextLayout::boundingRectTopLeft()
+{
+ QString text = "FirstLine\nSecondLine";
+ text.replace('\n', QChar::LineSeparator);
+
+ QTextLayout layout(text, testFont);
+
+ layout.beginLayout();
+ QTextLine firstLine = layout.createLine();
+ QVERIFY(firstLine.isValid());
+ firstLine.setPosition(QPointF(10, 10));
+ QTextLine secondLine = layout.createLine();
+ QVERIFY(secondLine.isValid());
+ secondLine.setPosition(QPointF(20, 20));
+ layout.endLayout();
+
+ QCOMPARE(layout.boundingRect().topLeft(), firstLine.position());
+}
+
+void tst_QTextLayout::charStopForSurrogatePairs()
+{
+ QString txt;
+ txt.append("a");
+ txt.append(0xd87e);
+ txt.append(0xdc25);
+ txt.append("b");
+ QTextLayout layout(txt, testFont);
+ QTextEngine *engine = layout.engine();
+ const HB_CharAttributes *attrs = engine->attributes();
+ QVERIFY(attrs);
+ QVERIFY(attrs[0].charStop);
+ QVERIFY(attrs[1].charStop);
+ QVERIFY(!attrs[2].charStop);
+ QVERIFY(attrs[3].charStop);
+}
+
+void tst_QTextLayout::tabStops()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("QTestFontEngine on the mac does not support logclusters at the moment", SkipAll);
+#endif
+ QString txt("Hello there\tworld");
+ QTextLayout layout(txt, testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+
+ QVERIFY(line.isValid());
+ line.setNumColumns(11);
+ QCOMPARE(line.textLength(), TESTFONT_SIZE);
+
+ line = layout.createLine();
+ QVERIFY(line.isValid());
+ line.setNumColumns(5);
+ QCOMPARE(line.textLength(), 5);
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::integerOverflow()
+{
+ QString txt("Hello world... ");
+
+ for (int i = 0; i < 8; ++i)
+ txt += txt;
+
+ QTextLayout layout(txt, testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+
+ QVERIFY(line.isValid());
+ line.setLineWidth(INT_MAX);
+ QCOMPARE(line.textLength(), txt.length());
+
+ QVERIFY(!layout.createLine().isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::setNumColumnsWrapAtWordBoundaryOrAnywhere()
+{
+ QString txt("This is a small test text");
+ QTextLayout layout(txt, testFont);
+ QTextOption option = layout.textOption();
+ option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ QVERIFY(line1.isValid());
+ line1.setNumColumns(1);
+
+ // qDebug() << line1.naturalTextWidth();
+ QCOMPARE(line1.textLength(), 1);
+ QVERIFY(line1.naturalTextWidth() == testFont.pixelSize()); // contains only one character
+
+ QTextLine line2 = layout.createLine();
+ QVERIFY(line2.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::setNumColumnsWordWrap()
+{
+ QString txt("This is a small test text");
+ QTextLayout layout(txt, testFont);
+ QTextOption option = layout.textOption();
+ option.setWrapMode(QTextOption::WordWrap);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ QVERIFY(line1.isValid());
+ line1.setNumColumns(1);
+
+ // qDebug() << line1.naturalTextWidth();
+ QCOMPARE(line1.textLength(), 5);
+ QVERIFY(line1.naturalTextWidth() > 20.0); // contains the whole first word.
+
+ QTextLine line2 = layout.createLine();
+ QVERIFY(line2.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::smallTextLengthNoWrap()
+{
+ QString txt("This is a small test text");
+ QTextLayout layout(txt, testFont);
+ QTextOption option = layout.textOption();
+ option.setWrapMode(QTextOption::NoWrap);
+ layout.setTextOption(option);
+
+ /// NoWrap
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ QVERIFY(line1.isValid());
+ line1.setLineWidth(5); // most certainly too short for the word 'This' to fit.
+
+ QCOMPARE(line1.width(), 5.0);
+ QVERIFY(line1.naturalTextWidth() > 70); // contains all the text.
+
+ QTextLine line2 = layout.createLine();
+ QVERIFY(! line2.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::smallTextLengthWordWrap()
+{
+ QString txt("This is a small test text");
+ QTextLayout layout(txt, testFont);
+ QTextOption option = layout.textOption();
+ option.setWrapMode(QTextOption::WordWrap);
+ layout.setTextOption(option);
+
+ /// WordWrap
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ QVERIFY(line1.isValid());
+ line1.setLineWidth(5); // most certainly too short for the word 'This' to fit.
+
+ QCOMPARE(line1.width(), 5.0);
+ QVERIFY(line1.naturalTextWidth() > 20.0); // contains the whole first word.
+ QCOMPARE(line1.textLength(), 5);
+
+ QTextLine line2 = layout.createLine();
+ QVERIFY(line2.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::smallTextLengthWrapAtWordBoundaryOrAnywhere()
+{
+ QString txt("This is a small test text");
+ QTextLayout layout(txt, testFont);
+ QTextOption option = layout.textOption();
+ option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ QVERIFY(line1.isValid());
+ line1.setLineWidth(5); // most certainly too short for the word 'This' to fit.
+
+ QCOMPARE(line1.width(), 5.0);
+ // qDebug() << line1.naturalTextWidth();
+ QCOMPARE(line1.textLength(), 1);
+ QVERIFY(line1.naturalTextWidth() == testFont.pixelSize()); // contains just the characters that fit.
+
+ QTextLine line2 = layout.createLine();
+ QVERIFY(line2.isValid());
+
+ layout.endLayout();
+}
+
+void tst_QTextLayout::testDefaultTabs()
+{
+ QTextLayout layout("Foo\tBar\ta slightly longer text\tend.", testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(1000);
+ layout.endLayout();
+
+ //qDebug() << "After the tab: " << line.cursorToX(4);
+ QCOMPARE(line.cursorToX(4), 80.); // default tab is 80
+ QCOMPARE(line.cursorToX(8), 160.);
+ QCOMPARE(line.cursorToX(31), 480.);
+
+ QTextOption option = layout.textOption();
+ option.setTabStop(90);
+ layout.setTextOption(option);
+ layout.beginLayout();
+ line = layout.createLine();
+ line.setLineWidth(1000);
+ layout.endLayout();
+
+ QCOMPARE(line.cursorToX(4), 90.);
+ QCOMPARE(line.cursorToX(8), 180.);
+ QCOMPARE(line.cursorToX(31), 450.);
+
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab;
+ tab.position = 110; // set one tab to 110, but since the rest is unset they will be at the normal interval again.
+ tabs.append(tab);
+ option.setTabs(tabs);
+ layout.setTextOption(option);
+ layout.beginLayout();
+ line = layout.createLine();
+ line.setLineWidth(1000);
+ layout.endLayout();
+
+ QCOMPARE(line.cursorToX(4), 110.);
+ QCOMPARE(line.cursorToX(8), 180.);
+ QCOMPARE(line.cursorToX(31), 450.);
+}
+
+void tst_QTextLayout::testTabs()
+{
+ QTextLayout layout("Foo\tBar.", testFont);
+ QTextOption option = layout.textOption();
+ option.setTabStop(150);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(200.);
+ layout.endLayout();
+
+ QVERIFY(line.naturalTextWidth() > 150);
+ QCOMPARE(line.cursorToX(4), 150.);
+}
+
+void tst_QTextLayout::testMultilineTab()
+{
+ QTextLayout layout("Lorem ipsum dolor sit\tBar.", testFont);
+ // test if this works on the second line.
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(220.); // moves 'sit' to next line.
+ line = layout.createLine();
+ line.setLineWidth(220.);
+ layout.endLayout();
+
+ QCOMPARE(line.cursorToX(22), 80.);
+}
+
+void tst_QTextLayout::testMultiTab()
+{
+ QTextLayout layout("Foo\t\t\tBar.", testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(1000.);
+ layout.endLayout();
+
+ QCOMPARE(line.cursorToX(6), 80. * 3);
+}
+
+void tst_QTextLayout::testTabsInAlignedParag()
+{
+ QTextLayout layout("Foo\tsome more words", testFont);
+ QTextOption option = layout.textOption();
+ // right
+ option.setAlignment(Qt::AlignRight);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(300.);
+ layout.endLayout();
+
+ const qreal textWidth = 80 + 15 * TESTFONT_SIZE; // 15 chars right of the tab
+ QCOMPARE(line.naturalTextWidth(), textWidth);
+ QCOMPARE(line.cursorToX(0), 300. - textWidth);
+
+ // centered
+ option.setAlignment(Qt::AlignCenter);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ line = layout.createLine();
+ line.setLineWidth(300.);
+ layout.endLayout();
+
+ QCOMPARE(line.naturalTextWidth(), textWidth);
+ QCOMPARE(line.cursorToX(0), (300. - textWidth) / 2.);
+
+ // justified
+ option.setAlignment(Qt::AlignJustify);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ line = layout.createLine();
+ line.setLineWidth(textWidth - 10); // make the last word slip to the next line so justification actually happens.
+ layout.endLayout();
+
+ QCOMPARE(line.cursorToX(0), 0.);
+ QCOMPARE(line.cursorToX(4), 80.);
+
+ //QTextLayout layout2("Foo\tUt wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis", testFont); // means it will be more then one line long.
+}
+
+void tst_QTextLayout::testRightTab()
+{
+ QTextLayout layout("Foo\tLorem ipsum te sit\tBar baz\tText\tEnd", testFont);
+ /* ^ a ^ b ^ c ^ d
+ a = 4, b = 22, c = 30, d = 35 (position)
+
+ I expect the output to be:
+ Foo Lorem ipsum te
+ sit Bar Baz
+ Text End
+
+ a) tab replaced with a single space due to the text not fitting before the tab.
+ b) tab takes space so the text until the 3th tab fits to the tab pos.
+ c) tab is after last tab (both auto and defined) and thus moves text to start of next line.
+ d) tab takes space so text until enter fits to tab pos.
+ */
+
+ QTextOption option = layout.textOption();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab;
+ tab.type = QTextOption::RightTab;
+ tab.position = 190; // which means only 15(.8) chars of our test font fit left of it.
+ tabs.append(tab);
+ option.setTabs(tabs);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ line1.setLineWidth(220.);
+ // qDebug() << "=====line 2";
+ QTextLine line2 = layout.createLine();
+ QVERIFY(line2.isValid());
+ line2.setLineWidth(220.);
+ // qDebug() << "=====line 3";
+ QTextLine line3 = layout.createLine();
+ QVERIFY(line3.isValid());
+ line3.setLineWidth(220.);
+ // qDebug() << "=====line 4";
+ QTextLine line4 = layout.createLine();
+ QVERIFY(! line4.isValid());
+ layout.endLayout();
+ // qDebug() << "--------";
+
+ QCOMPARE(line1.cursorToX(4), 3. * TESTFONT_SIZE ); // a
+ QCOMPARE(line1.textLength(), 19);
+ QCOMPARE(line2.cursorToX(23), 190. - 7. * TESTFONT_SIZE); // b
+ QCOMPARE(line2.textLength(), 12);
+ QCOMPARE(line3.cursorToX(31), 0.); // c
+ QCOMPARE(line3.cursorToX(36), 190 - 3. * TESTFONT_SIZE); // d
+}
+
+void tst_QTextLayout::testCenteredTab()
+{
+ QTextLayout layout("Foo\tBar", testFont);
+ // test if centering the tab works. We expect the center of 'Bar.' to be at the tab point.
+ QTextOption option = layout.textOption();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab(150, QTextOption::CenterTab);
+ tabs.append(tab);
+ option.setTabs(tabs);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(200.);
+ layout.endLayout();
+
+ const qreal wordLength = 3 * TESTFONT_SIZE; // the length of 'Bar'
+ QCOMPARE(line.cursorToX(4), 150 - wordLength / 2.);
+}
+
+void tst_QTextLayout::testDelimiterTab()
+{
+ QTextLayout layout("Foo\tBar. Barrabas", testFont);
+ // try the different delimiter characters to see if the alignment works there.
+ QTextOption option = layout.textOption();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab(100, QTextOption::DelimiterTab, QChar('.'));
+ tabs.append(tab);
+ option.setTabs(tabs);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(200.);
+ layout.endLayout();
+
+ const qreal distanceBeforeTab = 3.5 * TESTFONT_SIZE; // the length of 'bar' and half the width of the dot.
+ QCOMPARE(line.cursorToX(4), 100 - distanceBeforeTab);
+}
+
+void tst_QTextLayout::testLineBreakingAllSpaces()
+{
+ QTextLayout layout(" 123", testFont); // thats 20 spaces
+ const qreal firstLineWidth = 17 * TESTFONT_SIZE;
+ layout.beginLayout();
+ QTextLine line1 = layout.createLine();
+ line1.setLineWidth(firstLineWidth);
+ QTextLine line2 = layout.createLine();
+ line2.setLineWidth(1000); // the rest
+ layout.endLayout();
+ QCOMPARE(line1.width(), firstLineWidth);
+ QCOMPARE(line1.naturalTextWidth(), 0.); // spaces don't take space
+ QCOMPARE(line1.textLength(), 20);
+ QCOMPARE(line2.textLength(), 3);
+ QCOMPARE(line2.naturalTextWidth(), 3. * TESTFONT_SIZE);
+}
+
+void tst_QTextLayout::tabsForRtl()
+{
+ QString word(QChar(0x5e9)); // a hebrew character
+ word = word + word + word; // 3 hebrew characters ;)
+
+ QTextLayout layout(word +'\t'+ word +'\t'+ word +'\t'+ word, testFont);
+//QTextLayout layout(word +' '+ word +' '+ word +' '+ word, testFont);// tester ;)
+ /* ^ a ^ b ^ c
+ a = 4, b = 8, c = 12, d = 16 (position)
+
+ a) Left tab in RTL is a righ tab; so a is at width - 80
+ b) Like a
+ c) right tab on RTL is a left tab; so its at width - 240
+ d) center tab is still a centered tab.
+ */
+
+ QTextOption option = layout.textOption();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab;
+ tab.position = 80;
+ tabs.append(tab);
+ tab.position = 160;
+ tabs.append(tab);
+ tab.position = 240;
+ tab.type = QTextOption::RightTab;
+ tabs.append(tab);
+ option.setTabs(tabs);
+ option.setTextDirection(Qt::RightToLeft);
+ option.setAlignment(Qt::AlignRight);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ const qreal WIDTH = 400.;
+ line.setLineWidth(WIDTH);
+ layout.endLayout();
+
+//qDebug() << "layout ended --------------";
+
+ QCOMPARE(line.cursorToX(0), WIDTH);
+ QCOMPARE(line.cursorToX(1), WIDTH - TESTFONT_SIZE); // check its right-aligned
+ QCOMPARE(line.cursorToX(4), WIDTH - 80 + 3 * TESTFONT_SIZE);
+ QCOMPARE(line.cursorToX(8), WIDTH - 160 + 3 * TESTFONT_SIZE);
+ QCOMPARE(line.cursorToX(12), WIDTH - 240);
+}
+
+QT_BEGIN_NAMESPACE
+extern int qt_defaultDpiY();
+QT_END_NAMESPACE
+
+void tst_QTextLayout::testTabDPIScale()
+{
+ class MyPaintDevice : public QPaintDevice {
+ QPaintEngine *paintEngine () const { return 0; }
+ int metric (QPaintDevice::PaintDeviceMetric metric) const {
+ switch(metric) {
+ case QPaintDevice::PdmWidth:
+ case QPaintDevice::PdmHeight:
+ case QPaintDevice::PdmWidthMM:
+ case QPaintDevice::PdmHeightMM:
+ case QPaintDevice::PdmNumColors:
+ return INT_MAX;
+ case QPaintDevice::PdmDepth:
+ return 32;
+ case QPaintDevice::PdmDpiX:
+ case QPaintDevice::PdmDpiY:
+ case QPaintDevice::PdmPhysicalDpiX:
+ case QPaintDevice::PdmPhysicalDpiY:
+ return 72;
+ }
+ return 0;
+ }
+ };
+
+ MyPaintDevice pd;
+
+ QTextLayout layout("text1\ttext2\ttext3\tend", testFont, &pd);
+
+ QTextOption option = layout.textOption();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab;
+ tab.position = 300;
+ tabs.append(tab);
+
+ tab.position = 600;
+ tab.type = QTextOption::RightTab;
+ tabs.append(tab);
+
+ tab.position = 800;
+ tab.type = QTextOption::CenterTab;
+ tabs.append(tab);
+ option.setTabs(tabs);
+ layout.setTextOption(option);
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(1500.);
+ layout.endLayout();
+ QCOMPARE(line.cursorToX(0), 0.);
+ QCOMPARE(line.cursorToX(1), (double) TESTFONT_SIZE); // check that the font does not resize
+ qreal scale = 72 / (qreal) qt_defaultDpiY();
+ // lets do the transformation of deminishing resolution that QFixed has as effect.
+ int fixedScale = (int)( scale * qreal(64)); // into a QFixed
+ scale = ((qreal)fixedScale)/(qreal)64; // and out of a QFixed
+
+ QCOMPARE(line.cursorToX(6), tabs.at(0).position * scale);
+ QCOMPARE(line.cursorToX(12), tabs.at(1).position * scale - TESTFONT_SIZE * 5);
+ QCOMPARE(line.cursorToX(18), tabs.at(2).position * scale - TESTFONT_SIZE * 3 / 2.0);
+}
+
+void tst_QTextLayout::tabHeight()
+{
+ QTextLayout layout("\t", testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QCOMPARE(qRound(line.ascent()), QFontMetrics(testFont).ascent());
+ QCOMPARE(qRound(line.descent()), QFontMetrics(testFont).descent());
+}
+
+void tst_QTextLayout::capitalization_allUpperCase()
+{
+ QFont font(testFont);
+ font.setCapitalization(QFont::AllUppercase);
+ QTextLayout layout("Test", font);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QTextEngine *engine = layout.engine();
+ engine->itemize();
+ QCOMPARE(engine->layoutData->items.count(), 1);
+ QVERIFY(engine->layoutData->items.at(0).analysis.flags == QScriptAnalysis::Uppercase);
+}
+
+void tst_QTextLayout::capitalization_allLowerCase()
+{
+ QFont font(testFont);
+ font.setCapitalization(QFont::AllLowercase);
+ QTextLayout layout("Test", font);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QTextEngine *engine = layout.engine();
+ engine->itemize();
+ QCOMPARE(engine->layoutData->items.count(), 1);
+ QVERIFY(engine->layoutData->items.at(0).analysis.flags == QScriptAnalysis::Lowercase);
+}
+
+void tst_QTextLayout::capitalization_smallCaps()
+{
+ QFont font(testFont);
+ font.setCapitalization(QFont::SmallCaps);
+ QTextLayout layout("Test", font);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QTextEngine *engine = layout.engine();
+ engine->itemize();
+ QCOMPARE(engine->layoutData->items.count(), 2);
+ QVERIFY(engine->layoutData->items.at(0).analysis.flags == QScriptAnalysis::None);
+ QVERIFY(engine->layoutData->items.at(1).analysis.flags == QScriptAnalysis::SmallCaps);
+}
+
+void tst_QTextLayout::capitalization_capitalize()
+{
+ QFont font(testFont);
+ font.setCapitalization(QFont::Capitalize);
+ QTextLayout layout("hello\tworld", font);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QTextEngine *engine = layout.engine();
+ engine->itemize();
+ QCOMPARE(engine->layoutData->items.count(), 5);
+ QVERIFY(engine->layoutData->items.at(0).analysis.flags == QScriptAnalysis::Uppercase);
+ QVERIFY(engine->layoutData->items.at(1).analysis.flags == QScriptAnalysis::None);
+ QVERIFY(engine->layoutData->items.at(2).analysis.flags == QScriptAnalysis::Tab);
+ QVERIFY(engine->layoutData->items.at(3).analysis.flags == QScriptAnalysis::Uppercase);
+ QVERIFY(engine->layoutData->items.at(4).analysis.flags == QScriptAnalysis::None);
+}
+
+void tst_QTextLayout::longText()
+{
+ QString longText(128000, 'a');
+
+ {
+ QTextLayout layout(longText, testFont);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ QVERIFY(line.isValid());
+ QVERIFY(line.cursorToX(line.textLength() - 1) > 0);
+ }
+
+ for (int cap = QFont::MixedCase; cap < QFont::Capitalize + 1; ++cap) {
+ QFont f(testFont);
+ f.setCapitalization(QFont::Capitalization(cap));
+ QTextLayout layout(longText, f);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ QVERIFY(line.isValid());
+ QVERIFY(line.cursorToX(line.textLength() - 1) > 0);
+ }
+
+ {
+ QTextLayout layout(longText, testFont);
+ layout.setFlags(Qt::TextForceLeftToRight);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ QVERIFY(line.isValid());
+ QVERIFY(line.cursorToX(line.textLength() - 1) > 0);
+ }
+
+ {
+ QTextLayout layout(longText, testFont);
+ layout.setFlags(Qt::TextForceRightToLeft);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ QVERIFY(line.isValid());
+ QVERIFY(line.cursorToX(line.textLength() - 1) > 0);
+ }
+}
+
+void tst_QTextLayout::widthOfTabs()
+{
+ QTextEngine engine("ddd\t\t", testFont);
+ engine.ignoreBidi = true;
+ engine.itemize();
+ QCOMPARE(qRound(engine.width(0, 5)), qRound(engine.boundingBox(0, 5).width));
+}
+
+void tst_QTextLayout::columnWrapWithTabs()
+{
+ QTextLayout textLayout;
+ {
+ QTextOption textOption;
+ textOption.setWrapMode(QTextOption::WordWrap);
+ textLayout.setTextOption(textOption);
+ }
+
+ // Make sure string with spaces does not break
+ {
+ QString text = "Foo bar foo bar foo bar";
+ textLayout.setText(text);
+
+ textLayout.beginLayout();
+ QTextLine line = textLayout.createLine();
+ line.setNumColumns(30);
+ QCOMPARE(line.textLength(), text.length());
+ textLayout.endLayout();
+ }
+
+ // Make sure string with tabs breaks
+ {
+ QString text = "Foo\tbar\tfoo\tbar\tfoo\tbar";
+ textLayout.setText(text);
+ textLayout.beginLayout();
+ QTextLine line = textLayout.createLine();
+ line.setNumColumns(30);
+ QVERIFY(line.textLength() < text.length());
+ textLayout.endLayout();
+ }
+
+}
+
+void tst_QTextLayout::boundingRectForUnsetLineWidth()
+{
+ QTextLayout layout("FOOBAR");
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QCOMPARE(layout.boundingRect().width(), line.naturalTextWidth());
+}
+
+void tst_QTextLayout::boundingRectForSetLineWidth()
+{
+ QTextLayout layout("FOOBAR");
+
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(QFIXED_MAX - 1);
+ layout.endLayout();
+
+ QCOMPARE(layout.boundingRect().width(), qreal(QFIXED_MAX - 1));
+}
+
+void tst_QTextLayout::lineWidthFromBOM()
+{
+ const QString string(QChar(0xfeff)); // BYTE ORDER MARK
+ QTextLayout layout(string);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(INT_MAX / 256);
+ layout.endLayout();
+
+ // Don't spin into an infinite loop
+ }
+
+void tst_QTextLayout::glyphLessItems()
+{
+ {
+ QTextLayout layout;
+ layout.setText("\t\t");
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+ }
+
+ {
+ QTextLayout layout;
+ layout.setText(QString::fromLatin1("AA") + QChar(QChar::LineSeparator));
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+ }
+}
+
+void tst_QTextLayout::textWidthVsWIdth()
+{
+ QTextLayout layout;
+ QTextOption opt;
+ opt.setWrapMode(QTextOption::WrapAnywhere);
+ layout.setTextOption(opt);
+ layout.setText(QString::fromLatin1(
+ "g++ -c -m64 -pipe -g -fvisibility=hidden -fvisibility-inlines-hidden -Wall -W -D_REENTRANT -fPIC -DCORE_LIBRARY -DIDE_LIBRARY_BASENAME=\"lib\" -DWITH_TESTS "
+ "-DQT_NO_CAST_TO_ASCII -DQT_USE_FAST_OPERATOR_PLUS -DQT_USE_FAST_CONCATENATION -DQT_PLUGIN -DQT_TESTLIB_LIB -DQT_SCRIPT_LIB -DQT_SVG_LIB -DQT_SQL_LIB -DQT_XM"
+ "L_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_SHARED -I../../../../qt-qml/mkspecs/linux-g++-64 -I. -I../../../../qt-qml/include/QtCore -I../../../."
+ "./qt-qml/include/QtNetwork -I../../../../qt-qml/include/QtGui -I../../../../qt-qml/include/QtXml -I../../../../qt-qml/include/QtSql -I../../../../qt-qml/inc"
+ "lude/QtSvg -I../../../../qt-qml/include/QtScript -I../../../../qt-qml/include/QtTest -I../../../../qt-qml/include -I../../../../qt-qml/include/QtHelp -I../."
+ "./libs -I/home/ettrich/dev/creator/tools -I../../plugins -I../../shared/scriptwrapper -I../../libs/3rdparty/botan/build -Idialogs -Iactionmanager -Ieditorma"
+ "nager -Iprogressmanager -Iscriptmanager -I.moc/debug-shared -I.uic -o .obj/debug-shared/sidebar.o sidebar.cpp"));
+
+ // textWidth includes right bearing, but it should never be LARGER than width if there is space for at least one character
+ for (int width = 100; width < 1000; ++width) {
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(width);
+ layout.endLayout();
+
+ qreal textWidthIsLargerBy = qMax(qreal(0), line.naturalTextWidth() - line.width());
+ qreal thisMustBeZero = 0;
+ QCOMPARE(textWidthIsLargerBy, thisMustBeZero);
+ }
+}
+
+void tst_QTextLayout::textWithSurrogates_qtbug15679()
+{
+ QString str = QString::fromUtf8("🀀a🀀");
+ QTextLayout layout(str);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ qreal x[6];
+ for (int i = 0; i < 6; i++)
+ x[i] = line.cursorToX(i);
+
+ // If the first and third character are using the same
+ // font, they must have the same advance (since they
+ // are surrogate pairs, we need to add two for each
+ // character)
+ QCOMPARE(x[2] - x[0], x[5] - x[3]);
+}
+
+void tst_QTextLayout::textWidthWithStackedTextEngine()
+{
+ QString text = QString::fromUtf8("คลิก ถัดไป เพื่อดำเนินการต่อ");
+ QTextLayout layout(text);
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+ QFontMetricsF fm(layout.font());
+ QCOMPARE(line.naturalTextWidth(), fm.width(text));
+}
+
+void tst_QTextLayout::textWidthWithLineSeparator()
+{
+ QString s1("Save Project"), s2("Save Project\ntest");
+ s2.replace('\n', QChar::LineSeparator);
+
+ QTextLayout layout1(s1), layout2(s2);
+ layout1.beginLayout();
+ layout2.beginLayout();
+
+ QTextLine line1 = layout1.createLine();
+ QTextLine line2 = layout2.createLine();
+ line1.setLineWidth(0x1000);
+ line2.setLineWidth(0x1000);
+ QCOMPARE(line1.naturalTextWidth(), line2.naturalTextWidth());
+}
+
+void tst_QTextLayout::cursorInLigatureWithMultipleLines()
+{
+#if !defined(Q_WS_MAC)
+ QSKIP("This test can not be run on Mac", SkipAll);
+#endif
+ QTextLayout layout("first line finish", QFont("Times", 20));
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ line.setLineWidth(70);
+ line = layout.createLine();
+ layout.endLayout();
+
+ // The second line will be "finish", with "fi" as a ligature
+ QVERIFY(line.cursorToX(0) != line.cursorToX(1));
+}
+
+void tst_QTextLayout::xToCursorForLigatures()
+{
+#if !defined(Q_WS_MAC)
+ QSKIP("This test can not be run on Mac", SkipAll);
+#endif
+ QTextLayout layout("fi", QFont("Times", 20));
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QVERIFY(line.xToCursor(0) != line.xToCursor(line.naturalTextWidth() / 2));
+
+ // U+0061 U+0308
+ QTextLayout layout2(QString::fromUtf8("\x61\xCC\x88"), QFont("Times", 20));
+
+ layout2.beginLayout();
+ line = layout2.createLine();
+ layout2.endLayout();
+
+ qreal width = line.naturalTextWidth();
+ QVERIFY(line.xToCursor(0) == line.xToCursor(width / 2) ||
+ line.xToCursor(width) == line.xToCursor(width / 2));
+}
+
+QTEST_MAIN(tst_QTextLayout)
+#include "tst_qtextlayout.moc"
diff --git a/tests/auto/gui/text/qtextlist/.gitignore b/tests/auto/gui/text/qtextlist/.gitignore
new file mode 100644
index 0000000000..f1c4f6acab
--- /dev/null
+++ b/tests/auto/gui/text/qtextlist/.gitignore
@@ -0,0 +1 @@
+tst_qtextlist
diff --git a/tests/auto/gui/text/qtextlist/qtextlist.pro b/tests/auto/gui/text/qtextlist/qtextlist.pro
new file mode 100644
index 0000000000..f66fb96dd0
--- /dev/null
+++ b/tests/auto/gui/text/qtextlist/qtextlist.pro
@@ -0,0 +1,9 @@
+load(qttest_p4)
+
+QT += core-private gui-private
+
+SOURCES += tst_qtextlist.cpp
+HEADERS += ../qtextdocument/common.h
+
+
+
diff --git a/tests/auto/gui/text/qtextlist/tst_qtextlist.cpp b/tests/auto/gui/text/qtextlist/tst_qtextlist.cpp
new file mode 100644
index 0000000000..164dd6fc68
--- /dev/null
+++ b/tests/auto/gui/text/qtextlist/tst_qtextlist.cpp
@@ -0,0 +1,453 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qtextdocument.h>
+#include <qtextdocumentfragment.h>
+#include <qtextlist.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qtextcursor.h>
+#include "../qtextdocument/common.h"
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTextList : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextList();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void item();
+ void autoNumbering();
+ void autoNumberingRTL();
+ void autoNumberingPrefixAndSuffix();
+ void autoNumberingPrefixAndSuffixRTL();
+ void autoNumberingPrefixAndSuffixHtmlExportImport();
+ void romanNumbering();
+ void romanNumberingLimit();
+ void formatChange();
+ void cursorNavigation();
+ void partialRemoval();
+ void formatReferenceChange();
+ void ensureItemOrder();
+ void add();
+ void defaultIndent();
+ void blockUpdate();
+ void numbering_data();
+ void numbering();
+
+private:
+ QTextDocument *doc;
+ QTextCursor cursor;
+ QTestDocumentLayout *layout;
+};
+
+tst_QTextList::tst_QTextList()
+{}
+
+void tst_QTextList::init()
+{
+ doc = new QTextDocument();
+ layout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(layout);
+ cursor = QTextCursor(doc);
+}
+
+void tst_QTextList::cleanup()
+{
+ cursor = QTextCursor();
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextList::item()
+{
+ // this is basically a test for the key() + 1 in QTextList::item.
+ QTextList *list = cursor.createList(QTextListFormat());
+ QVERIFY(list->item(0).blockFormat().objectIndex() != -1);
+}
+
+void tst_QTextList::autoNumbering()
+{
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListLowerAlpha);
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 0; i < 27; ++i)
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 28);
+
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->itemNumber(cursor.block()) == 27);
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == "ab.");
+}
+
+void tst_QTextList::autoNumberingPrefixAndSuffix()
+{
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListLowerAlpha);
+ fmt.setNumberPrefix("-");
+ fmt.setNumberSuffix(")");
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 0; i < 27; ++i)
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 28);
+
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->itemNumber(cursor.block()) == 27);
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == "-ab)");
+}
+
+void tst_QTextList::autoNumberingPrefixAndSuffixRTL()
+{
+ QTextBlockFormat bfmt;
+ bfmt.setLayoutDirection(Qt::RightToLeft);
+ cursor.setBlockFormat(bfmt);
+
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListUpperAlpha);
+ fmt.setNumberPrefix("-");
+ fmt.setNumberSuffix("*");
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 2);
+
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == "*B-");
+}
+
+void tst_QTextList::autoNumberingPrefixAndSuffixHtmlExportImport()
+{
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListLowerAlpha);
+ fmt.setNumberPrefix("\"");
+ fmt.setNumberSuffix("#");
+ fmt.setIndent(10);
+ // FIXME: Would like to test "'" but there's a problem in the css parser (Scanner::preprocess
+ // is called before the values are being parsed), so the quoting does not work.
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 0; i < 27; ++i)
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 28);
+
+ QString htmlExport = doc->toHtml();
+ QTextDocument importDoc;
+ importDoc.setHtml(htmlExport);
+
+ QTextCursor importCursor(&importDoc);
+ for (int i = 0; i < 27; ++i)
+ importCursor.movePosition(QTextCursor::NextBlock);
+
+ QVERIFY(importCursor.currentList());
+ QVERIFY(importCursor.currentList()->itemNumber(importCursor.block()) == 27);
+ QVERIFY(importCursor.currentList()->itemText(importCursor.block()) == "\"ab#");
+ QVERIFY(importCursor.currentList()->format().indent() == 10);
+}
+
+void tst_QTextList::autoNumberingRTL()
+{
+ QTextBlockFormat bfmt;
+ bfmt.setLayoutDirection(Qt::RightToLeft);
+ cursor.setBlockFormat(bfmt);
+
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListUpperAlpha);
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 2);
+
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == ".B");
+}
+
+void tst_QTextList::romanNumbering()
+{
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListUpperRoman);
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 0; i < 4998; ++i)
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 4999);
+
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->itemNumber(cursor.block()) == 4998);
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == "MMMMCMXCIX.");
+}
+
+void tst_QTextList::romanNumberingLimit()
+{
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::ListLowerRoman);
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 0; i < 4999; ++i)
+ cursor.insertBlock();
+
+ QVERIFY(list->count() == 5000);
+
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->itemNumber(cursor.block()) == 4999);
+ QVERIFY(cursor.currentList()->itemText(cursor.block()) == "?.");
+}
+
+void tst_QTextList::formatChange()
+{
+ // testing the formatChanged slot in QTextListManager
+
+ /* <initial block>
+ * 1.
+ * 2.
+ */
+ QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
+ QTextList *firstList = list;
+ cursor.insertBlock();
+
+ QVERIFY(list && list->count() == 2);
+
+ QTextBlockFormat bfmt = cursor.blockFormat();
+// QVERIFY(bfmt.object() == list);
+
+ bfmt.setObjectIndex(-1);
+ cursor.setBlockFormat(bfmt);
+
+ QVERIFY(firstList->count() == 1);
+}
+
+void tst_QTextList::cursorNavigation()
+{
+ // testing some cursor list methods
+
+ /* <initial block>
+ * 1.
+ * 2.
+ */
+ cursor.insertList(QTextListFormat::ListDecimal);
+ cursor.insertBlock();
+
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.currentList());
+ cursor.movePosition(QTextCursor::PreviousBlock);
+ QVERIFY(cursor.currentList());
+ QVERIFY(cursor.currentList()->itemNumber(cursor.block()) == 0);
+}
+
+void tst_QTextList::partialRemoval()
+{
+ /* this is essentially a test for PieceTable::removeBlock to not miss any
+ blocks with the blockChanged signal emission that actually get removed.
+
+ It creates two lists, like this:
+
+ 1. Hello World
+ a. Foobar
+
+ and then removes from within the 'Hello World' into the 'Foobar' .
+ There used to be no emission for the removal of the second (a.) block,
+ causing list inconsistencies.
+
+ */
+
+ QTextList *firstList = cursor.insertList(QTextListFormat::ListDecimal);
+
+ QTextCursor selStart = cursor;
+ selStart.movePosition(QTextCursor::PreviousCharacter);
+
+ cursor.insertText("Hello World");
+
+ // position it well into the 'hello world' text.
+ selStart.movePosition(QTextCursor::NextCharacter);
+ selStart.movePosition(QTextCursor::NextCharacter);
+ selStart.clearSelection();
+
+ QPointer<QTextList> secondList = cursor.insertList(QTextListFormat::ListCircle);
+ cursor.insertText("Foobar");
+
+ // position it into the 'foo bar' text.
+ cursor.movePosition(QTextCursor::PreviousCharacter);
+ QTextCursor selEnd = cursor;
+
+ // this creates a selection that includes parts of both text-fragments and also the list item of the second list.
+ QTextCursor selection = selStart;
+ selection.setPosition(selEnd.position(), QTextCursor::KeepAnchor);
+
+ selection.deleteChar(); // deletes the second list
+
+ QVERIFY(!secondList);
+ QVERIFY(!firstList->isEmpty());
+
+ doc->undo();
+}
+
+void tst_QTextList::formatReferenceChange()
+{
+ QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
+ cursor.insertText("Some Content...");
+ cursor.insertBlock(QTextBlockFormat());
+
+ cursor.setPosition(list->item(0).position());
+ int listItemStartPos = cursor.position();
+ cursor.movePosition(QTextCursor::NextBlock);
+ int listItemLen = cursor.position() - listItemStartPos;
+ layout->expect(listItemStartPos, listItemLen, listItemLen);
+
+ QTextListFormat fmt = list->format();
+ fmt.setStyle(QTextListFormat::ListCircle);
+ list->setFormat(fmt);
+
+ QVERIFY(layout->called);
+ QVERIFY(!layout->error);
+}
+
+void tst_QTextList::ensureItemOrder()
+{
+ /*
+ * Insert a new list item before the first one and verify the blocks
+ * are sorted after that.
+ */
+ QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
+
+ QTextBlockFormat fmt = cursor.blockFormat();
+ cursor.movePosition(QTextCursor::Start);
+ cursor.insertBlock(fmt);
+
+ QCOMPARE(list->item(0).position(), 1);
+ QCOMPARE(list->item(1).position(), 2);
+}
+
+void tst_QTextList::add()
+{
+ QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
+ cursor.insertBlock(QTextBlockFormat());
+ QCOMPARE(list->count(), 1);
+ cursor.insertBlock(QTextBlockFormat());
+ list->add(cursor.block());
+ QCOMPARE(list->count(), 2);
+}
+
+// Task #72036
+void tst_QTextList::defaultIndent()
+{
+ QTextListFormat fmt;
+ QCOMPARE(fmt.indent(), 1);
+}
+
+void tst_QTextList::blockUpdate()
+{
+ // three items
+ QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
+ cursor.insertBlock();
+ cursor.insertBlock();
+
+ // remove second, needs also update on the third
+ // since the numbering might have changed
+ const int len = cursor.position() + cursor.block().length() - 1;
+ layout->expect(1, len, len);
+ list->remove(list->item(1));
+ QVERIFY(!layout->error);
+}
+
+void tst_QTextList::numbering_data()
+{
+ QTest::addColumn<int>("format");
+ QTest::addColumn<int>("number");
+ QTest::addColumn<QString>("result");
+
+ QTest::newRow("E.") << int(QTextListFormat::ListUpperAlpha) << 5 << "E.";
+ QTest::newRow("abc.") << int(QTextListFormat::ListLowerAlpha) << (26 + 2) * 26 + 3 << "abc.";
+ QTest::newRow("12.") << int(QTextListFormat::ListDecimal) << 12 << "12.";
+ QTest::newRow("XXIV.") << int(QTextListFormat::ListUpperRoman) << 24 << "XXIV.";
+ QTest::newRow("VIII.") << int(QTextListFormat::ListUpperRoman) << 8 << "VIII.";
+ QTest::newRow("xxx.") << int(QTextListFormat::ListLowerRoman) << 30 << "xxx.";
+ QTest::newRow("xxix.") << int(QTextListFormat::ListLowerRoman) << 29 << "xxix.";
+// QTest::newRow("xxx. alpha") << int(QTextListFormat::ListLowerAlpha) << (24 * 26 + 24) * 26 + 24 << "xxx."; //Too slow
+}
+
+void tst_QTextList::numbering()
+{
+ QFETCH(int, format);
+ QFETCH(int, number);
+ QFETCH(QString, result);
+
+
+ QTextListFormat fmt;
+ fmt.setStyle(QTextListFormat::Style(format));
+ QTextList *list = cursor.createList(fmt);
+ QVERIFY(list);
+
+ for (int i = 1; i < number; ++i)
+ cursor.insertBlock();
+
+ QCOMPARE(list->count(), number);
+
+ QVERIFY(cursor.currentList());
+ QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), number - 1);
+ QCOMPARE(cursor.currentList()->itemText(cursor.block()), result);
+}
+
+QTEST_MAIN(tst_QTextList)
+#include "tst_qtextlist.moc"
diff --git a/tests/auto/gui/text/qtextobject/.gitignore b/tests/auto/gui/text/qtextobject/.gitignore
new file mode 100644
index 0000000000..6a3da09549
--- /dev/null
+++ b/tests/auto/gui/text/qtextobject/.gitignore
@@ -0,0 +1 @@
+tst_qtextobject
diff --git a/tests/auto/gui/text/qtextobject/qtextobject.pro b/tests/auto/gui/text/qtextobject/qtextobject.pro
new file mode 100644
index 0000000000..e87a364ab6
--- /dev/null
+++ b/tests/auto/gui/text/qtextobject/qtextobject.pro
@@ -0,0 +1,9 @@
+############################################################
+# Project file for autotest for file qtextobject.h
+############################################################
+
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qtextobject.cpp
+
+
diff --git a/tests/auto/gui/text/qtextobject/tst_qtextobject.cpp b/tests/auto/gui/text/qtextobject/tst_qtextobject.cpp
new file mode 100644
index 0000000000..0d1773e3fa
--- /dev/null
+++ b/tests/auto/gui/text/qtextobject/tst_qtextobject.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdebug.h>
+#include <qtextobject.h>
+#include <qtextdocument.h>
+#include <qtextedit.h>
+#include <qtextcursor.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QTextObject : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QTextObject();
+ virtual ~tst_QTextObject();
+
+private slots:
+ void getSetCheck();
+ void testStandAloneTextObject();
+};
+
+tst_QTextObject::tst_QTextObject()
+{
+}
+
+tst_QTextObject::~tst_QTextObject()
+{
+}
+
+// Testing get/set functions
+void tst_QTextObject::getSetCheck()
+{
+ QTextEdit edit;
+ QTextFrame obj1(edit.document());
+ // QTextFrameLayoutData * QTextFrame::layoutData()
+ // void QTextFrame::setLayoutData(QTextFrameLayoutData *)
+ QTextFrameLayoutData *var1 = new QTextFrameLayoutData;
+ obj1.setLayoutData(var1);
+ QCOMPARE(var1, obj1.layoutData());
+ obj1.setLayoutData((QTextFrameLayoutData *)0);
+ QCOMPARE((QTextFrameLayoutData *)0, obj1.layoutData());
+ // delete var1; // No delete, since QTextFrame takes ownership
+
+ QTextBlock obj2 = edit.textCursor().block();
+ // QTextBlockUserData * QTextBlock::userData()
+ // void QTextBlock::setUserData(QTextBlockUserData *)
+ QTextBlockUserData *var2 = new QTextBlockUserData;
+ obj2.setUserData(var2);
+ QCOMPARE(var2, obj2.userData());
+ obj2.setUserData((QTextBlockUserData *)0);
+ QCOMPARE((QTextBlockUserData *)0, obj2.userData());
+
+ // int QTextBlock::userState()
+ // void QTextBlock::setUserState(int)
+ obj2.setUserState(0);
+ QCOMPARE(0, obj2.userState());
+ obj2.setUserState(INT_MIN);
+ QCOMPARE(INT_MIN, obj2.userState());
+ obj2.setUserState(INT_MAX);
+ QCOMPARE(INT_MAX, obj2.userState());
+}
+
+class TestTextObject : public QTextObject
+{
+public:
+ TestTextObject(QTextDocument *document) : QTextObject(document) {}
+};
+
+void tst_QTextObject::testStandAloneTextObject()
+{
+ QTextDocument document;
+ TestTextObject textObject(&document);
+
+ QCOMPARE(textObject.document(), &document);
+ // don't crash
+ textObject.format();
+ textObject.formatIndex();
+ QCOMPARE(textObject.objectIndex(), -1);
+}
+
+QTEST_MAIN(tst_QTextObject)
+#include "tst_qtextobject.moc"
diff --git a/tests/auto/gui/text/qtextodfwriter/.gitignore b/tests/auto/gui/text/qtextodfwriter/.gitignore
new file mode 100644
index 0000000000..791445d7a9
--- /dev/null
+++ b/tests/auto/gui/text/qtextodfwriter/.gitignore
@@ -0,0 +1 @@
+tst_qtextodfwriter
diff --git a/tests/auto/gui/text/qtextodfwriter/qtextodfwriter.pro b/tests/auto/gui/text/qtextodfwriter/qtextodfwriter.pro
new file mode 100644
index 0000000000..25bb5a5ed0
--- /dev/null
+++ b/tests/auto/gui/text/qtextodfwriter/qtextodfwriter.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+QT += core-private gui-private
+SOURCES += tst_qtextodfwriter.cpp
+
+!symbian:DEFINES += SRCDIR=\\\"$$PWD\\\"
+symbian:INCLUDEPATH+=$$[QT_INSTALL_PREFIX]/include/QtGui/private
diff --git a/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp b/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp
new file mode 100644
index 0000000000..765afce66e
--- /dev/null
+++ b/tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <QTextDocument>
+#include <QTextCursor>
+#include <QTextBlock>
+#include <QTextList>
+#include <QTextTable>
+#include <QBuffer>
+#include <QDebug>
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR "."
+#endif
+
+#include <private/qtextodfwriter_p.h>
+
+class tst_QTextOdfWriter : public QObject
+{
+ Q_OBJECT
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void testWriteParagraph_data();
+ void testWriteParagraph();
+ void testWriteStyle1_data();
+ void testWriteStyle1();
+ void testWriteStyle2();
+ void testWriteList();
+ void testWriteList2();
+ void createArchive();
+ void testWriteAll();
+ void testWriteSection();
+ void testWriteTable();
+
+private:
+ /// closes the document and returns the part of the XML stream that the test wrote
+ QString getContentFromXml();
+
+private:
+ QTextDocument *document;
+ QXmlStreamWriter *xmlWriter;
+ QTextOdfWriter *odfWriter;
+ QBuffer *buffer;
+};
+
+void tst_QTextOdfWriter::init()
+{
+ document = new QTextDocument();
+ odfWriter = new QTextOdfWriter(*document, 0);
+
+ buffer = new QBuffer();
+ buffer->open(QIODevice::WriteOnly);
+ xmlWriter = new QXmlStreamWriter(buffer);
+ xmlWriter->writeNamespace(odfWriter->officeNS, "office");
+ xmlWriter->writeNamespace(odfWriter->textNS, "text");
+ xmlWriter->writeNamespace(odfWriter->styleNS, "style");
+ xmlWriter->writeNamespace(odfWriter->foNS, "fo");
+ xmlWriter->writeNamespace(odfWriter->tableNS, "table");
+ xmlWriter->writeStartDocument();
+ xmlWriter->writeStartElement("dummy");
+}
+
+void tst_QTextOdfWriter::cleanup()
+{
+ delete document;
+ delete odfWriter;
+ delete xmlWriter;
+ delete buffer;
+}
+
+QString tst_QTextOdfWriter::getContentFromXml()
+{
+ xmlWriter->writeEndDocument();
+ buffer->close();
+ QString stringContent = QString::fromUtf8(buffer->data());
+ QString ret;
+ int index = stringContent.indexOf("<dummy");
+ if (index > 0) {
+ index = stringContent.indexOf('>', index);
+ if (index > 0)
+ ret = stringContent.mid(index+1, stringContent.length() - index - 10);
+ }
+ return ret;
+}
+
+void tst_QTextOdfWriter::testWriteParagraph_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("xml");
+
+ QTest::newRow("empty") << "" <<
+ "<text:p text:style-name=\"p1\"/>";
+ QTest::newRow("spaces") << "foobar word" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word</text:span></text:p>";
+ QTest::newRow("starting spaces") << " starting spaces" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:s text:c=\"2\"/>starting spaces</text:span></text:p>";
+ QTest::newRow("trailing spaces") << "trailing spaces " <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">trailing spaces <text:s/></text:span></text:p>";
+ QTest::newRow("tab") << "word\ttab x" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/>tab x</text:span></text:p>";
+ QTest::newRow("tab2") << "word\t\ttab\tx" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/><text:tab/>tab<text:tab/>x</text:span></text:p>";
+ QTest::newRow("misc") << "foobar word\ttab x" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word<text:tab/>tab x</text:span></text:p>";
+ QTest::newRow("misc2") << "\t \tFoo" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:tab/> <text:s text:c=\"4\"/><text:tab/>Foo</text:span></text:p>";
+ QTest::newRow("linefeed") << QString("line1%1line2").arg(QChar(0x2028)) <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">line1<text:line-break/>line2</text:span></text:p>";
+ QTest::newRow("spaces") << "The quick brown fox jumped over the lazy dog" <<
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">The quick brown fox jumped over the lazy dog</text:span></text:p>";
+}
+
+void tst_QTextOdfWriter::testWriteParagraph()
+{
+ QFETCH(QString, input);
+ QFETCH(QString, xml);
+
+ QTextCursor cursor(document);
+ cursor.insertText(input);
+
+ odfWriter->writeBlock(*xmlWriter, document->begin());
+ QCOMPARE( getContentFromXml(), xml);
+}
+
+void tst_QTextOdfWriter::testWriteStyle1_data()
+{
+ QTest::addColumn<QString>("htmlInput");
+ QTest::addColumn<int>("cursorPosition");
+ QTest::addColumn<QString>("xml");
+
+ QString text1 = "Normal<b>bold</b><i>italic</i><b><i>Bold/Italic</i></b>";
+ QTest::newRow("normal") << text1 << 2 <<
+ "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\"/></style:style>";
+ QTest::newRow("bold") << text1 << 10 <<
+ "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
+ QTest::newRow("italic") << text1 << 14 <<
+ "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-family=\"Sans\"/></style:style>";
+ QTest::newRow("bold+italic") << text1 << 25 <<
+ "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
+ QString colorText = "<span style=\"color: #00FF00; background-color: #FF0000;\"> Color Text </span>";
+ QTest::newRow("green/red") << colorText << 3 <<
+ "<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\" fo:color=\"#00ff00\" fo:background-color=\"#ff0000\"/></style:style>";
+
+}
+
+void tst_QTextOdfWriter::testWriteStyle1()
+{
+ QFETCH(QString, htmlInput);
+ QFETCH(int, cursorPosition);
+ QFETCH(QString, xml);
+ document->setHtml(htmlInput);
+
+ QTextCursor cursor(document);
+ cursor.setPosition(cursorPosition);
+ odfWriter->writeCharacterFormat(*xmlWriter, cursor.charFormat(), 4);
+ QCOMPARE( getContentFromXml(), xml);
+}
+
+void tst_QTextOdfWriter::testWriteStyle2()
+{
+ QTextBlockFormat bf; // = cursor.blockFormat();
+ QList<QTextOption::Tab> tabs;
+ QTextOption::Tab tab1(40, QTextOption::RightTab);
+ tabs << tab1;
+ QTextOption::Tab tab2(80, QTextOption::DelimiterTab, 'o');
+ tabs << tab2;
+ bf.setTabPositions(tabs);
+
+ odfWriter->writeBlockFormat(*xmlWriter, bf, 1);
+ QString xml = QString::fromLatin1(
+ "<style:style style:name=\"p1\" style:family=\"paragraph\">"
+ "<style:paragraph-properties>"
+ "<style:tab-stops>"
+ "<style:tab-stop style:position=\"30pt\" style:type=\"right\"/>"
+ "<style:tab-stop style:position=\"60pt\" style:type=\"char\" style:char=\"o\"/>"
+ "</style:tab-stops>"
+ "</style:paragraph-properties>"
+ "</style:style>");
+ QCOMPARE(getContentFromXml(), xml);
+}
+
+void tst_QTextOdfWriter::testWriteList()
+{
+ QTextCursor cursor(document);
+ QTextList *list = cursor.createList(QTextListFormat::ListDisc);
+ cursor.insertText("ListItem 1");
+ list->add(cursor.block());
+ cursor.insertBlock();
+ cursor.insertText("ListItem 2");
+ list->add(cursor.block());
+
+ odfWriter->writeBlock(*xmlWriter, cursor.block());
+ QString xml = QString::fromLatin1(
+ "<text:list text:style-name=\"L2\">"
+ "<text:list-item>"
+ //"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
+ //"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
+ "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">ListItem 2</text:span></text:p>"
+ "</text:list-item>"
+ "</text:list>");
+
+ QCOMPARE(getContentFromXml(), xml);
+}
+
+void tst_QTextOdfWriter::testWriteList2()
+{
+ QTextCursor cursor(document);
+ QTextList *list = cursor.createList(QTextListFormat::ListDisc);
+ cursor.insertText("Cars");
+ list->add(cursor.block());
+ cursor.insertBlock();
+ QTextListFormat level2;
+ level2.setStyle(QTextListFormat::ListSquare);
+ level2.setIndent(2);
+ QTextList *list2 = cursor.createList(level2);
+ cursor.insertText("Model T");
+ list2->add(cursor.block());
+ cursor.insertBlock();
+ cursor.insertText("Kitt");
+ list2->add(cursor.block());
+ cursor.insertBlock();
+ cursor.insertText("Animals");
+ list->add(cursor.block());
+
+ cursor.insertBlock(QTextBlockFormat(), QTextCharFormat()); // start a new completely unrelated list.
+ QTextList *list3 = cursor.createList(QTextListFormat::ListDecimal);
+ cursor.insertText("Foo");
+ list3->add(cursor.block());
+
+ // and another block thats NOT in a list.
+ cursor.insertBlock(QTextBlockFormat(), QTextCharFormat());
+ cursor.insertText("Bar");
+
+ odfWriter->writeFrame(*xmlWriter, document->rootFrame());
+ QString xml = QString::fromLatin1(
+ "<text:list text:style-name=\"L2\">"
+ "<text:list-item>"
+ //"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
+ //"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
+ "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Cars</text:span></text:p>"
+ "</text:list-item>"
+ "<text:list-item>"
+ "<text:list text:style-name=\"L4\">"
+ "<text:list-item>"
+ "<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Model T</text:span></text:p>"
+ "</text:list-item>"
+ "<text:list-item>"
+ "<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Kitt</text:span></text:p>"
+ "</text:list-item>"
+ "</text:list>"
+ "</text:list-item>"
+ "<text:list-item>"
+ "<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Animals</text:span></text:p>"
+ "</text:list-item>"
+ "</text:list>"
+ "<text:list text:style-name=\"L6\">"
+ "<text:list-item>"
+ "<text:p text:style-name=\"p7\"><text:span text:style-name=\"c0\">Foo</text:span></text:p>"
+ "</text:list-item>"
+ "</text:list>"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>");
+
+ // QString x = getContentFromXml();
+ // for (int i=0; i < x.length(); i+=150) qDebug() << x.mid(i, 150);
+ QCOMPARE(getContentFromXml(), xml);
+}
+
+
+void tst_QTextOdfWriter::createArchive()
+{
+ document->setPlainText("a"); // simple doc is enough ;)
+ QTextOdfWriter writer(*document, buffer);
+ QCOMPARE(writer.createArchive(), true); // default
+ writer.writeAll();
+/*
+QFile file("createArchive-odt");
+file.open(QIODevice::WriteOnly);
+file.write(buffer->data());
+file.close();
+*/
+ QVERIFY(buffer->data().length() > 80);
+ QCOMPARE(buffer->data()[0], 'P'); // its a zip :)
+ QCOMPARE(buffer->data()[1], 'K');
+ QString mimetype(buffer->data().mid(38, 39));
+ QCOMPARE(mimetype, QString::fromLatin1("application/vnd.oasis.opendocument.text"));
+}
+
+void tst_QTextOdfWriter::testWriteAll()
+{
+ document->setPlainText("a"); // simple doc is enough ;)
+ QTextOdfWriter writer(*document, buffer);
+ QCOMPARE(writer.createArchive(), true);
+ writer.setCreateArchive(false);
+ writer.writeAll();
+ QString result = QString(buffer->data());
+ // details we check elsewhere, all we have to do is check availability.
+ QVERIFY(result.indexOf("office:automatic-styles") >= 0);
+ QVERIFY(result.indexOf("<style:style style:name=\"p1\"") >= 0);
+ QVERIFY(result.indexOf("<style:style style:name=\"c0\"") >= 0);
+ QVERIFY(result.indexOf("office:body") >= 0);
+ QVERIFY(result.indexOf("office:text") >= 0);
+ QVERIFY(result.indexOf("style:style") >= 0);
+}
+
+void tst_QTextOdfWriter::testWriteSection()
+{
+ QTextCursor cursor(document);
+ cursor.insertText("foo\nBar");
+ QTextFrameFormat ff;
+ cursor.insertFrame(ff);
+ cursor.insertText("baz");
+
+ odfWriter->writeFrame(*xmlWriter, document->rootFrame());
+ QString xml = QString::fromLatin1(
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foo</text:span></text:p>"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>"
+ "<text:section>"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">baz</text:span></text:p>"
+ "</text:section>"
+ "<text:p text:style-name=\"p1\"/>");
+
+ QCOMPARE(getContentFromXml(), xml);
+}
+
+void tst_QTextOdfWriter::testWriteTable()
+{
+ // create table with merged cells
+ QTextCursor cursor(document);
+ QTextTable * table = cursor.insertTable(3, 3);
+ table->mergeCells(1, 0, 2, 2);
+ table->mergeCells(0, 1, 1, 2);
+ cursor = table->cellAt(0, 0).firstCursorPosition();
+ cursor.insertText("a");
+ cursor.movePosition(QTextCursor::NextCell);
+ cursor.insertText("b");
+ cursor.movePosition(QTextCursor::NextCell);
+ cursor.insertText("c");
+ cursor.movePosition(QTextCursor::NextCell);
+ cursor.insertText("d");
+ cursor.movePosition(QTextCursor::NextCell);
+ cursor.insertText("e");
+ /*
+ +-+---+
+ |a|b |
+ +-+-+-+
+ |c |d|
+ + +-+
+ | |e|
+ +-+-+-+
+ */
+
+ odfWriter->writeFrame(*xmlWriter, document->rootFrame());
+ QString xml = QString::fromLatin1(
+ "<text:p text:style-name=\"p1\"/>"
+ "<table:table>"
+ "<table:table-column table:number-columns-repeated=\"3\"/>"
+ "<table:table-row>"
+ "<table:table-cell table:style-name=\"T3\">"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">a</text:span></text:p>"
+ "</table:table-cell>"
+ "<table:table-cell table:number-columns-spanned=\"2\" table:style-name=\"T6\">"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c7\">b</text:span></text:p>"
+ "</table:table-cell>"
+ "</table:table-row>"
+ "<table:table-row>"
+ "<table:table-cell table:number-columns-spanned=\"2\" table:number-rows-spanned=\"2\" table:style-name=\"T5\">"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c8\">c</text:span></text:p>"
+ "</table:table-cell>"
+ "<table:table-cell table:style-name=\"T3\">"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">d</text:span></text:p>"
+ "</table:table-cell>"
+ "</table:table-row>"
+ "<table:table-row>"
+ "<table:table-cell table:style-name=\"T3\">"
+ "<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">e</text:span></text:p>"
+ "</table:table-cell>"
+ "</table:table-row>"
+ "</table:table>"
+ "<text:p text:style-name=\"p1\"/>");
+
+ QCOMPARE(getContentFromXml(), xml);
+}
+
+QTEST_MAIN(tst_QTextOdfWriter)
+#include "tst_qtextodfwriter.moc"
diff --git a/tests/auto/gui/text/qtextpiecetable/.gitignore b/tests/auto/gui/text/qtextpiecetable/.gitignore
new file mode 100644
index 0000000000..720b01f79f
--- /dev/null
+++ b/tests/auto/gui/text/qtextpiecetable/.gitignore
@@ -0,0 +1 @@
+tst_qtextpiecetable
diff --git a/tests/auto/gui/text/qtextpiecetable/qtextpiecetable.pro b/tests/auto/gui/text/qtextpiecetable/qtextpiecetable.pro
new file mode 100644
index 0000000000..cfbbe2bca5
--- /dev/null
+++ b/tests/auto/gui/text/qtextpiecetable/qtextpiecetable.pro
@@ -0,0 +1,9 @@
+load(qttest_p4)
+QT += widgets widgets-private
+QT += core-private gui-private
+SOURCES += tst_qtextpiecetable.cpp
+HEADERS += ../qtextdocument/common.h
+
+requires(!win32)
+requires(contains(QT_CONFIG,private_tests))
+
diff --git a/tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp b/tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp
new file mode 100644
index 0000000000..ddaf84134d
--- /dev/null
+++ b/tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp
@@ -0,0 +1,1155 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#define protected public
+
+#include <qtextdocument.h>
+#undef protected
+#include <private/qtextdocument_p.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qtextobject.h>
+#include <qdebug.h>
+#include <stdlib.h>
+#include <qtextcursor.h>
+#include "../qtextdocument/common.h"
+
+//TESTED_FILES=gui/text/qtextdocument_p.cpp gui/text/qtextdocument_p.h
+
+class tst_QTextPieceTable : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextPieceTable();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void insertion1();
+ void insertion2();
+ void insertion3();
+ void insertion4();
+ void insertion5();
+
+ void removal1();
+ void removal2();
+ void removal3();
+ void removal4();
+
+ void undoRedo1();
+ void undoRedo2();
+ void undoRedo3();
+ void undoRedo4();
+ void undoRedo5();
+ void undoRedo6();
+ void undoRedo7();
+ void undoRedo8();
+ void undoRedo9();
+ void undoRedo10();
+ void undoRedo11();
+
+ void checkDocumentChanged();
+ void checkDocumentChanged2();
+ void setBlockFormat();
+
+ void blockInsertion();
+ void blockInsertion2();
+
+ void blockRemoval1();
+ void blockRemoval2();
+ void blockRemoval3();
+ void blockRemoval4();
+ void blockRemoval5();
+
+ void checkBlockSeparation();
+
+ void checkFrames1();
+ void removeFrameDirect();
+ void removeWithChildFrame();
+ void clearWithFrames();
+
+private:
+ QTextDocument *doc;
+ QTextDocumentPrivate *table;
+ int blockFormatIndex;
+ int charFormatIndex;
+};
+
+tst_QTextPieceTable::tst_QTextPieceTable()
+{ doc = 0; table = 0; }
+
+
+void tst_QTextPieceTable::init()
+{
+ doc = new QTextDocument(0);
+ table = doc->docHandle();
+ blockFormatIndex = table->formatCollection()->indexForFormat(QTextBlockFormat());
+ charFormatIndex = table->formatCollection()->indexForFormat(QTextCharFormat());
+}
+
+void tst_QTextPieceTable::cleanup()
+{
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextPieceTable::insertion1()
+{
+ table->insert(0, "aacc", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("aacc"));
+ table->insert(2, "bb", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("aabbcc"));
+ table->insert(1, "1", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("a1abbcc"));
+ table->insert(6, "d", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("a1abbcdc"));
+ table->insert(8, "z", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("a1abbcdcz"));
+}
+
+void tst_QTextPieceTable::insertion2()
+{
+ table->insert(0, "bb", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("bb"));
+}
+
+void tst_QTextPieceTable::insertion3()
+{
+ QString compare;
+ for (int i = 0; i < 20000; ++i) {
+ int pos = rand() % (i+1);
+ QChar c((unsigned short)(i & 0xff) + 1);
+ QString str;
+ str += c;
+ table->insert(pos, str, charFormatIndex);
+ compare.insert(pos, str);
+ }
+ QVERIFY(table->plainText() == compare);
+}
+
+void tst_QTextPieceTable::insertion4()
+{
+ QString compare;
+ for (int i = 0; i < 20000; ++i) {
+ int pos = rand() % (i+1);
+ QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
+ QString str;
+ str += c;
+ str += c;
+ table->insert(pos, str, charFormatIndex);
+ compare.insert(pos, str);
+ // if (table->text() != compare) {
+ // qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->text().latin1());
+ // exit(12);
+ // }
+ }
+ QVERIFY(table->plainText() == compare);
+}
+
+void tst_QTextPieceTable::insertion5()
+{
+ QString compare;
+ for (int i = 0; i < 20000; ++i) {
+ int pos = rand() % (i+1);
+ QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
+ QString str;
+ str += c;
+ str += c;
+ if (c == 'a') {
+ table->insertBlock(pos, blockFormatIndex, charFormatIndex);
+ str = QChar(QChar::ParagraphSeparator);
+ } else {
+ table->insert(pos, str, charFormatIndex);
+ }
+ compare.insert(pos, str);
+ }
+ QVERIFY(table->plainText() == compare);
+ for (QTextBlock it = table->blocksBegin(); it != table->blocksEnd(); it = it.next()) {
+ QTextDocumentPrivate::FragmentIterator fit = table->find(it.position());
+ QVERIFY(fit.position() == it.position());
+ }
+}
+
+void tst_QTextPieceTable::removal1()
+{
+ table->insert(0, "abbccc", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("abbccc"));
+ table->remove(1, 2);
+ QCOMPARE(table->plainText(), QString("accc"));
+ table->insert(1, "1", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("a1ccc"));
+ table->remove(4, 1);
+ QCOMPARE(table->plainText(), QString("a1cc"));
+ table->insert(4, "z", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("a1ccz"));
+}
+
+void tst_QTextPieceTable::removal2()
+{
+ table->insert(0, "bb", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("bb"));
+ table->remove(0, 2);
+ QCOMPARE(table->plainText(), QString(""));
+ table->insertBlock(0, blockFormatIndex, charFormatIndex);
+ QCOMPARE(table->plainText(), QString(QChar(QChar::ParagraphSeparator)));
+ table->remove(0, 1);
+ QCOMPARE(table->plainText(), QString(""));
+
+ table->insert(0, "bb", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("bb"));
+ table->insertBlock(1, blockFormatIndex, charFormatIndex);
+ QCOMPARE(table->plainText(), QString("b") + QString(QChar(QChar::ParagraphSeparator)) + QString("b"));
+ table->remove(1, 1);
+ QCOMPARE(table->plainText(), QString("bb"));
+}
+
+void tst_QTextPieceTable::removal3()
+{
+ QString compare;
+ int l = 0;
+ for (int i = 0; i < 20000; ++i) {
+ bool remove = l && (rand() % 2);
+ int pos = rand() % (remove ? l : (l+1));
+ QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
+ QString str;
+ str += c;
+ str += c;
+ if (remove && pos < table->length()) {
+ compare.remove(pos, 1);
+ table->remove(pos, 1);
+ } else {
+ compare.insert(pos, str);
+ table->insert(pos, str, charFormatIndex);
+ }
+ l += remove ? -1 : 2;
+ // if (table->text() != compare) {
+ // qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->text().latin1());
+ // exit(12);
+ // }
+ }
+ QVERIFY(table->plainText() == compare);
+}
+
+void tst_QTextPieceTable::removal4()
+{
+ QString compare;
+ int l = 0;
+ for (int i = 0; i < 20000; ++i) {
+ bool remove = l && (rand() % 2);
+ int pos = (l > 1) ? rand() % (remove ? l-1 : l) : 0;
+ QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
+ QString str;
+ if (c != 'a') {
+ str += c;
+ str += c;
+ } else {
+ str = QChar(QChar::ParagraphSeparator);
+ }
+ if (remove && pos < table->length() - 1) {
+ compare.remove(pos, 1);
+ table->remove(pos, 1);
+ } else {
+ if (str[0] == QChar(QChar::ParagraphSeparator))
+ table->insertBlock(pos, blockFormatIndex, charFormatIndex);
+ else
+ table->insert(pos, str, charFormatIndex);
+ compare.insert(pos, str);
+ }
+ l += remove ? -1 : 2;
+// if (table->plainText() != compare) {
+// qDebug("compare failed: i=%d (current char=%c) insert at %d\nexpected '%s'\ngot '%s'", i, (i % 26) + (i>25?'A':'a'), pos, compare.latin1(), table->plainText().latin1());
+// exit(12);
+// }
+ }
+ QVERIFY(table->plainText() == compare);
+}
+
+void tst_QTextPieceTable::undoRedo1()
+{
+ table->insert(0, "01234567", charFormatIndex);
+ table->insert(0, "a", charFormatIndex);
+ table->insert(1, "b", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("ab01234567"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("01234567"));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("ab01234567"));
+ table->undo();
+ table->insert(1, "c", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("0c1234567"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("01234567"));
+ table->undo();
+ QVERIFY(table->plainText().isEmpty());
+}
+
+void tst_QTextPieceTable::undoRedo2()
+{
+ table->insert(0, "01", charFormatIndex);
+ table->insert(1, "a", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("0a1"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("01"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString(""));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("01"));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("0a1"));
+}
+
+void tst_QTextPieceTable::undoRedo3()
+{
+ table->insert(0, "01", charFormatIndex);
+ table->insert(2, "ab", charFormatIndex);
+ table->remove(2, 1);
+ QCOMPARE(table->plainText(), QString("01b"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("01ab"));
+ table->undo();
+ QVERIFY(table->plainText().isEmpty());
+ table->redo();
+ QCOMPARE(table->plainText(), QString("01ab"));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("01b"));
+}
+
+void tst_QTextPieceTable::undoRedo4()
+{
+ table->insert(0, "01", charFormatIndex);
+ table->insert(0, "ab", charFormatIndex);
+ table->remove(0, 1);
+ QCOMPARE(table->plainText(), QString("b01"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("ab01"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString("01"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString(""));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("01"));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("ab01"));
+ table->redo();
+ QCOMPARE(table->plainText(), QString("b01"));
+}
+
+void tst_QTextPieceTable::undoRedo5()
+{
+ table->beginEditBlock();
+ table->insert(0, "01", charFormatIndex);
+ table->remove(1, 1);
+ table->endEditBlock();
+ QCOMPARE(table->plainText(), QString("0"));
+ table->undo();
+ QCOMPARE(table->plainText(), QString(""));
+}
+
+void tst_QTextPieceTable::undoRedo6()
+{
+ // this is essentially a test for the undoStack[undoPosition - 1].block = false in PieceTable::endUndoBlock()
+ QTextDocument doc;
+ QTextCursor cursor(&doc);
+ cursor.insertText("Hello World");
+
+ cursor.insertBlock();
+ cursor.insertText("Hello World2");
+
+ cursor.movePosition(QTextCursor::Start);
+ QTextBlockFormat bfmt;
+ bfmt.setAlignment(Qt::AlignHCenter);
+ cursor.setBlockFormat(bfmt);
+ QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter);
+
+ QTextCursor range = cursor;
+ range.clearSelection();
+ range.movePosition(QTextCursor::Start);
+ range.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+
+ QTextCharFormat modifier;
+ modifier.setFontItalic(true);
+ range.mergeCharFormat(modifier);
+
+ cursor.movePosition(QTextCursor::Start);
+ QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter);
+
+ doc.undo();
+
+ QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter);
+}
+
+void tst_QTextPieceTable::undoRedo7()
+{
+ table->insert(0, "a", charFormatIndex);
+ table->insert(1, "b", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("ab"));
+
+ table->undo();
+ QVERIFY(table->plainText().isEmpty());
+}
+
+void tst_QTextPieceTable::undoRedo8()
+{
+ table->insert(0, "a", charFormatIndex);
+ table->insert(1, "b", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("ab"));
+
+ table->remove(0, 1);
+ table->remove(0, 1);
+
+ QVERIFY(table->plainText().isEmpty());
+ table->undo();
+ QCOMPARE(table->plainText(), QString("ab"));
+}
+
+void tst_QTextPieceTable::undoRedo9()
+{
+ table->insert(0, "a", charFormatIndex);
+ table->insert(1, "b", charFormatIndex);
+ QCOMPARE(table->plainText(), QString("ab"));
+
+ table->remove(1, 1);
+ table->remove(0, 1);
+
+ QVERIFY(table->plainText().isEmpty());
+ table->undo();
+ QCOMPARE(table->plainText(), QString("ab"));
+}
+
+void tst_QTextPieceTable::undoRedo10()
+{
+ // testcase for the beginUndoBlock/endUndoBlock calls being surrounded by an if (undoEnabled)
+ QTextCharFormat cf;
+ cf.setForeground(Qt::blue);
+ int cfIdx = table->formatCollection()->indexForFormat(cf);
+
+ QTextBlockFormat f;
+ int idx = table->formatCollection()->indexForFormat(f);
+
+ table->insert(0, "a", cfIdx);
+ table->insertBlock(1, idx, cfIdx);
+ table->insert(1, "b", cfIdx);
+
+ cf.setForeground(Qt::red);
+ int newCfIdx = table->formatCollection()->indexForFormat(cf);
+
+ table->setCharFormat(0, 3, cf, QTextDocumentPrivate::MergeFormat);
+
+ QCOMPARE(table->find(0).value()->format, newCfIdx);
+
+ table->undo();
+
+ QCOMPARE(table->find(0).value()->format, cfIdx);
+}
+
+void tst_QTextPieceTable::undoRedo11()
+{
+ srand(3);
+ const int loops = 20;
+ QString compare;
+ int l = 0;
+ for (int i = 0; i < loops; ++i) {
+ bool remove = l && (rand() % 2);
+ int pos = (l > 1) ? rand() % (remove ? l-1 : l) : 0;
+ QChar c((unsigned short)((i % 26) + (i>25?'A':'a')));
+ QString str;
+ str += c;
+ str += c;
+ if (remove) {
+ compare.remove(pos, 1);
+ table->remove(pos, 1);
+ } else {
+ compare.insert(pos, str);
+ table->insert(pos, str, charFormatIndex);
+ }
+ l += remove ? -1 : 2;
+ }
+ QVERIFY(table->plainText() == compare);
+ for (int i = 0; i < loops; ++i)
+ table->undo();
+ QVERIFY(table->plainText() == QString(""));
+ for (int i = 0; i < loops; ++i)
+ table->redo();
+ QVERIFY(table->plainText() == compare);
+}
+
+
+void tst_QTextPieceTable::checkDocumentChanged()
+{
+ table->enableUndoRedo(false);
+ QTestDocumentLayout *layout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(layout);
+
+ // single insert
+ layout->expect(0, 0, 15);
+ table->insert(0, "012345678901234", charFormatIndex);
+ QVERIFY(!layout->error);
+
+ // single remove
+ layout->expect(0, 5, 0);
+ table->remove(0, 5);
+ QVERIFY(!layout->error);
+
+ // symmetric insert/remove
+ layout->expect(0, 0, 0);
+ table->beginEditBlock();
+ table->insert(0, "01234", charFormatIndex);
+ table->remove(0, 5);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 5, 5);
+ table->beginEditBlock();
+ table->remove(0, 5);
+ table->insert(0, "01234", charFormatIndex);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ // replace
+ layout->expect(0, 3, 5);
+ table->beginEditBlock();
+ table->remove(0, 3);
+ table->insert(0, "01234", charFormatIndex);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 0, 2);
+ table->beginEditBlock();
+ table->insert(0, "01234", charFormatIndex);
+ table->remove(0, 3);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ // insert + remove inside insert block
+ layout->expect(0, 0, 2);
+ table->beginEditBlock();
+ table->insert(0, "01234", charFormatIndex);
+ table->remove(1, 3);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 0, 2);
+ table->beginEditBlock();
+ table->insert(0, "01234", charFormatIndex);
+ table->remove(2, 3);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ // insert + remove partly outside
+ layout->expect(0, 1, 0);
+ table->beginEditBlock();
+ table->insert(1, "0", charFormatIndex);
+ table->remove(0, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 1, 1);
+ table->beginEditBlock();
+ table->insert(1, "01", charFormatIndex);
+ table->remove(0, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 1, 2);
+ table->beginEditBlock();
+ table->insert(1, "012", charFormatIndex);
+ table->remove(0, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(1, 1, 0);
+ table->beginEditBlock();
+ table->insert(1, "0", charFormatIndex);
+ table->remove(1, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(1, 1, 1);
+ table->beginEditBlock();
+ table->insert(1, "01", charFormatIndex);
+ table->remove(2, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(1, 1, 2);
+ table->beginEditBlock();
+ table->insert(1, "012", charFormatIndex);
+ table->remove(3, 2);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ // insert + remove non overlapping
+ layout->expect(0, 1, 1);
+ table->beginEditBlock();
+ table->insert(1, "0", charFormatIndex);
+ table->remove(0, 1);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 2, 2);
+ table->beginEditBlock();
+ table->insert(2, "1", charFormatIndex);
+ table->remove(0, 1);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 2, 2);
+ table->beginEditBlock();
+ table->remove(0, 1);
+ table->insert(1, "0", charFormatIndex);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ layout->expect(0, 3, 3);
+ table->beginEditBlock();
+ table->remove(0, 1);
+ table->insert(2, "1", charFormatIndex);
+ table->endEditBlock();
+
+
+ layout->expect(0, 3, 3);
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ table->beginEditBlock();
+ table->setCharFormat(0, 1, fmt);
+ table->setCharFormat(2, 1, fmt);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+}
+
+void tst_QTextPieceTable::checkDocumentChanged2()
+{
+ QTestDocumentLayout *layout = new QTestDocumentLayout(doc);
+ doc->setDocumentLayout(layout);
+
+ QTextCharFormat fmt;
+ fmt.setForeground(Qt::blue);
+ int anotherCharFormatIndex = table->formatCollection()->indexForFormat(fmt);
+
+ layout->expect(0, 0, 12);
+ table->beginEditBlock();
+ table->insert(0, "0123", charFormatIndex);
+ table->insert(4, "4567", anotherCharFormatIndex);
+ table->insert(8, "8901", charFormatIndex);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+
+ fmt.setFontItalic(true);
+
+ layout->expect(1, 10, 10);
+ table->beginEditBlock();
+ table->setCharFormat(8, 3, fmt);
+ table->setCharFormat(4, 4, fmt);
+ table->setCharFormat(1, 3, fmt);
+ table->endEditBlock();
+ QVERIFY(!layout->error);
+}
+
+void tst_QTextPieceTable::setBlockFormat()
+{
+ QTextBlockFormat bfmt;
+ int index = table->formatCollection()->indexForFormat(bfmt);
+
+ table->insertBlock(0, index, charFormatIndex);
+ table->insertBlock(0, index, charFormatIndex);
+ table->insertBlock(0, index, charFormatIndex);
+
+ QTextBlockFormat newbfmt = bfmt;
+ newbfmt.setAlignment(Qt::AlignRight);
+ index = table->formatCollection()->indexForFormat(bfmt);
+ QTextBlock b = table->blocksFind(1);
+ table->setBlockFormat(b, b, newbfmt);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == bfmt);
+ QVERIFY(table->blocksFind(1).blockFormat() == newbfmt);
+ QVERIFY(table->blocksFind(2).blockFormat() == bfmt);
+}
+
+
+void tst_QTextPieceTable::blockInsertion()
+{
+ QTextBlockFormat fmt;
+ fmt.setTopMargin(100);
+ int idx = table->formatCollection()->indexForFormat(fmt);
+ int charFormat = table->formatCollection()->indexForFormat(QTextCharFormat());
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+
+ table->insertBlock(0, idx, charFormat);
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).blockFormat() == fmt);
+
+ table->undo();
+ QVERIFY(table->blockMap().length() == 1);
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+
+ table->redo();
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).blockFormat() == fmt);
+}
+
+void tst_QTextPieceTable::blockInsertion2()
+{
+ // caused evil failing assertion in fragmentmap
+ int pos = 0;
+ table->insertBlock(pos, blockFormatIndex, charFormatIndex);
+ pos += 1;
+ table->insert(pos, "a", charFormatIndex);
+ pos += 1;
+
+ pos -= 1;
+ table->insertBlock(pos, blockFormatIndex, charFormatIndex);
+ QCOMPARE(table->blocksFind(0).position(), 0);
+ QCOMPARE(table->blocksFind(1).position(), 1);
+ QCOMPARE(table->blocksFind(2).position(), 2);
+}
+
+/*
+ Tests correct removal behaviour when deleting over block boundaries or complete blocks.
+*/
+
+void tst_QTextPieceTable::blockRemoval1()
+{
+ QTextBlockFormat fmt1;
+ fmt1.setTopMargin(100);
+ QTextBlockFormat fmt2;
+ fmt2.setAlignment(Qt::AlignRight);
+ int idx1 = table->formatCollection()->indexForFormat(fmt1);
+ int idx2 = table->formatCollection()->indexForFormat(fmt2);
+
+ table->insert(0, "0123", charFormatIndex);
+ table->insertBlock(4, idx1, charFormatIndex);
+ table->insert(5, "5678", charFormatIndex);
+ table->insertBlock(9, idx2, charFormatIndex);
+ table->insert(10, "0123", charFormatIndex);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->beginEditBlock();
+ table->remove(5, 5);
+ table->endEditBlock();
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(4).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 5);
+
+ table->undo();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->redo();
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(4).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 5);
+}
+
+void tst_QTextPieceTable::blockRemoval2()
+{
+ QTextBlockFormat fmt1;
+ fmt1.setTopMargin(100);
+ QTextBlockFormat fmt2;
+ fmt2.setAlignment(Qt::AlignRight);
+ int idx1 = table->formatCollection()->indexForFormat(fmt1);
+ int idx2 = table->formatCollection()->indexForFormat(fmt2);
+
+ table->insert(0, "0123", charFormatIndex);
+ table->insertBlock(4, idx1, charFormatIndex);
+ table->insert(5, "5678", charFormatIndex);
+ table->insertBlock(9, idx2, charFormatIndex);
+ table->insert(10, "0123", charFormatIndex);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->remove(4, 1);
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(6).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 0);
+
+ table->undo();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->redo();
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(6).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 0);
+}
+
+void tst_QTextPieceTable::blockRemoval3()
+{
+ QTextBlockFormat fmt1;
+ fmt1.setTopMargin(100);
+ QTextBlockFormat fmt2;
+ fmt2.setAlignment(Qt::AlignRight);
+ int idx1 = table->formatCollection()->indexForFormat(fmt1);
+ int idx2 = table->formatCollection()->indexForFormat(fmt2);
+
+ table->insert(0, "0123", charFormatIndex);
+ table->insertBlock(4, idx1, charFormatIndex);
+ table->insert(5, "5678", charFormatIndex);
+ table->insertBlock(9, idx2, charFormatIndex);
+ table->insert(10, "0123", charFormatIndex);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->beginEditBlock();
+ table->remove(3, 4);
+ table->endEditBlock();
+
+ QVERIFY(table->blocksFind(1).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+
+ table->undo();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->redo();
+ QVERIFY(table->blocksFind(1).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+}
+
+void tst_QTextPieceTable::blockRemoval4()
+{
+#if 0
+ QTextBlockFormat fmt1;
+ fmt1.setTopMargin(100);
+ QTextBlockFormat fmt2;
+ fmt2.setAlignment(Qt::AlignRight);
+ int idx1 = table->formatCollection()->indexForFormat(fmt1);
+ int idx2 = table->formatCollection()->indexForFormat(fmt2);
+
+ table->insert(0, "0123", charFormatIndex);
+ table->insertBlock(4, idx1, charFormatIndex);
+ table->insert(5, "5678", charFormatIndex);
+ table->insertBlock(9, idx2, charFormatIndex);
+ table->insert(10, "0123", charFormatIndex);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->remove(3, 7);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+ QVERIFY(table->blocksFind(1).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+
+ table->undo();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->redo();
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+ QVERIFY(table->blocksFind(1).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+#endif
+}
+
+void tst_QTextPieceTable::blockRemoval5()
+{
+ QTextBlockFormat fmt1;
+ fmt1.setTopMargin(100);
+ QTextBlockFormat fmt2;
+ fmt2.setAlignment(Qt::AlignRight);
+ int idx1 = table->formatCollection()->indexForFormat(fmt1);
+ int idx2 = table->formatCollection()->indexForFormat(fmt2);
+
+ table->insert(0, "0123", charFormatIndex);
+ table->insertBlock(4, idx1, charFormatIndex);
+ table->insert(5, "5678", charFormatIndex);
+ table->insertBlock(9, idx2, charFormatIndex);
+ table->insert(10, "0123", charFormatIndex);
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->beginEditBlock();
+ table->remove(3, 8);
+ table->endEditBlock();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+
+ table->undo();
+
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(4).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == fmt1);
+ QVERIFY(table->blocksFind(10).blockFormat() == fmt2);
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(6).position() == 5);
+ QVERIFY(table->blocksFind(11).position() == 10);
+
+ table->redo();
+ QVERIFY(table->blocksFind(0).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(5).blockFormat() == QTextBlockFormat());
+ QVERIFY(table->blocksFind(1).position() == 0);
+ QVERIFY(table->blocksFind(5).position() == 0);
+}
+
+
+void tst_QTextPieceTable::checkBlockSeparation()
+{
+ table->insertBlock(0, blockFormatIndex, charFormatIndex);
+ table->insertBlock(1, blockFormatIndex, charFormatIndex);
+
+ QVERIFY(table->find(0) != table->find(1));
+}
+
+void tst_QTextPieceTable::checkFrames1()
+{
+ QTextFrameFormat ffmt;
+ table->insert(0, "Hello", charFormatIndex);
+ QPointer<QTextFrame> frame = table->insertFrame(1, 3, ffmt);
+ QTextFrame *root = table->rootFrame();
+
+ QVERIFY(root == frame->parentFrame());
+
+ QVERIFY(root);
+ QVERIFY(root->parentFrame() == 0);
+
+ QVERIFY(root->childFrames().count() == 1);
+ QVERIFY(frame->format() == ffmt);
+ QVERIFY(frame->firstPosition() == 2);
+ QVERIFY(frame->lastPosition() == 4);
+
+
+ QPointer<QTextFrame> frame2 = table->insertFrame(2, 3, ffmt);
+
+ QVERIFY(root->childFrames().count() == 1);
+ QVERIFY(root->childFrames().at(0) == frame);
+ QVERIFY(frame->childFrames().count() == 1);
+ QVERIFY(frame2->childFrames().count() == 0);
+ QVERIFY(frame2->parentFrame() == frame);
+ QVERIFY(frame2->firstPosition() == 3);
+ QVERIFY(frame2->lastPosition() == 4);
+
+ QVERIFY(frame->format() == ffmt);
+ QVERIFY(frame->firstPosition() == 2);
+ QVERIFY(frame->lastPosition() == 6);
+
+ table->removeFrame(frame);
+
+ QVERIFY(root->childFrames().count() == 1);
+ QVERIFY(root->childFrames().at(0) == frame2);
+ QVERIFY(!frame);
+ QVERIFY(frame2->childFrames().count() == 0);
+ QVERIFY(frame2->parentFrame() == root);
+ QVERIFY(frame2->firstPosition() == 2);
+ QVERIFY(frame2->lastPosition() == 3);
+
+ table->undo();
+
+ frame = table->frameAt(2);
+
+ QVERIFY(root->childFrames().count() == 1);
+ QVERIFY(root->childFrames().at(0) == frame);
+ QVERIFY(frame->childFrames().count() == 1);
+ QVERIFY(frame->childFrames().at(0) == frame2);
+ QVERIFY(frame2->childFrames().count() == 0);
+ QVERIFY(frame2->parentFrame() == frame);
+ QVERIFY(frame2->firstPosition() == 3);
+ QVERIFY(frame2->lastPosition() == 4);
+
+ QVERIFY(frame->firstPosition() == 2);
+ QVERIFY(frame->lastPosition() == 6);
+
+ table->undo();
+
+ QVERIFY(root->childFrames().count() == 1);
+ QVERIFY(root->childFrames().at(0) == frame);
+ QVERIFY(frame->childFrames().count() == 0);
+ QVERIFY(!frame2);
+
+ QVERIFY(frame->firstPosition() == 2);
+ QVERIFY(frame->lastPosition() == 4);
+}
+
+void tst_QTextPieceTable::removeFrameDirect()
+{
+ QTextFrameFormat ffmt;
+ table->insert(0, "Hello", charFormatIndex);
+
+ QTextFrame *frame = table->insertFrame(1, 5, ffmt);
+
+ QVERIFY(frame->parentFrame() == table->rootFrame());
+
+ const int start = frame->firstPosition() - 1;
+ const int end = frame->lastPosition();
+ const int length = end - start + 1;
+
+ table->remove(start, length);
+}
+
+void tst_QTextPieceTable::removeWithChildFrame()
+{
+ /*
+ The piecetable layout is:
+
+ ...
+ 1 BeginningOfFrame(first frame)
+ 2 text
+ 3 BeginningOfFrame(second frame)
+ 4 text
+ 5 text
+ 6 EndOfFrame(second frame)
+ 7 text
+ 8 text
+ 9 EndOfFrame(first frame)
+ ...
+
+ The idea is to remove from [2] until [6], basically some trailing text and the second frame.
+ In this case frameAt(2) != frameAt(6), so the assertion in remove() needed an adjustement.
+ */
+ QTextFrameFormat ffmt;
+ table->insert(0, "Hello World", charFormatIndex);
+
+ QTextFrame *frame = table->insertFrame(1, 6, ffmt);
+ QTextFrame *childFrame = table->insertFrame(3, 5, ffmt);
+ Q_UNUSED(frame);
+ Q_UNUSED(childFrame);
+
+ // used to give a failing assertion
+ table->remove(2, 5);
+ QVERIFY(true);
+}
+
+void tst_QTextPieceTable::clearWithFrames()
+{
+ /*
+ The piecetable layout is:
+
+ ...
+ 1 BeginningOfFrame(first frame)
+ 2 text
+ 3 EndOfFrame(first frame)
+ 4 BeginningOfFrame(second frame)
+ 5 text
+ 6 text
+ 7 EndOfFrame(second frame)
+ ...
+
+ The idea is to remove from [1] until [7].
+ */
+ QTextFrameFormat ffmt;
+ table->insert(0, "Hello World", charFormatIndex);
+
+ QTextFrame *firstFrame = table->insertFrame(1, 2, ffmt);
+ QTextFrame *secondFrame = table->insertFrame(4, 6, ffmt);
+
+ const int start = firstFrame->firstPosition() - 1;
+ const int end = secondFrame->lastPosition();
+ const int length = end - start + 1;
+ // used to give a failing assertion
+ table->remove(start, length);
+ QVERIFY(true);
+}
+
+QTEST_MAIN(tst_QTextPieceTable)
+
+
+#include "tst_qtextpiecetable.moc"
+
diff --git a/tests/auto/gui/text/qtextscriptengine/.gitignore b/tests/auto/gui/text/qtextscriptengine/.gitignore
new file mode 100644
index 0000000000..e51a335099
--- /dev/null
+++ b/tests/auto/gui/text/qtextscriptengine/.gitignore
@@ -0,0 +1 @@
+tst_qtextscriptengine
diff --git a/tests/auto/gui/text/qtextscriptengine/generate/generate.pro b/tests/auto/gui/text/qtextscriptengine/generate/generate.pro
new file mode 100644
index 0000000000..354e0e5cdf
--- /dev/null
+++ b/tests/auto/gui/text/qtextscriptengine/generate/generate.pro
@@ -0,0 +1,14 @@
+######################################################################
+# Automatically generated by qmake (1.07a) Fri Sep 30 15:20:45 2005
+######################################################################
+
+TEMPLATE = app
+CONFIG -= moc
+INCLUDEPATH += . /usr/include/freetype2
+INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src
+
+# Input
+SOURCES += main.cpp
+CONFIG += qt warn_on debug thread create_prl link_prl
+
+
diff --git a/tests/auto/gui/text/qtextscriptengine/generate/main.cpp b/tests/auto/gui/text/qtextscriptengine/generate/main.cpp
new file mode 100644
index 0000000000..06caa34b4d
--- /dev/null
+++ b/tests/auto/gui/text/qtextscriptengine/generate/main.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QApplication>
+#include <QTextEdit>
+#include <QLineEdit>
+#include <QVBoxLayout>
+#include <QFontDialog>
+#include <QPushButton>
+
+#define private public
+#include <qfont.h>
+#include <private/qtextengine_p.h>
+#include <private/qfontengine_p.h>
+#include <qtextlayout.h>
+#undef private
+
+
+class MyEdit : public QTextEdit {
+ Q_OBJECT
+public:
+ MyEdit(QWidget *p) : QTextEdit(p) { setReadOnly(true); }
+public slots:
+ void setText(const QString &str);
+ void changeFont();
+public:
+ QLineEdit *lineEdit;
+};
+
+void MyEdit::setText(const QString &str)
+{
+ if (str.isEmpty()) {
+ clear();
+ return;
+ }
+ QTextLayout layout(str, lineEdit->font());
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ QString result;
+ result = "Using font '" + e->fontEngine(e->layoutData->items[0])->fontDef.family + "'\n\n";
+
+ result += "{ { ";
+ for (int i = 0; i < str.length(); ++i)
+ result += "0x" + QString::number(str.at(i).unicode(), 16) + ", ";
+ result += "0x0 },\n { ";
+ for (int i = 0; i < e->layoutData->items[0].num_glyphs; ++i)
+ result += "0x" + QString::number(e->layoutData->glyphLayout.glyphs[i], 16) + ", ";
+ result += "0x0 } }";
+
+ setPlainText(result);
+}
+
+void MyEdit::changeFont()
+{
+ bool ok;
+ QFont f = QFontDialog::getFont(&ok, lineEdit->font(), topLevelWidget());
+ if (ok)
+ lineEdit->setFont(f);
+}
+
+int main(int argc, char **argv)
+{
+ QApplication a(argc, argv);
+
+ QWidget *mw = new QWidget(0);
+ QVBoxLayout *l = new QVBoxLayout(mw);
+
+ QLineEdit *le = new QLineEdit(mw);
+ l->addWidget(le);
+
+ MyEdit *view = new MyEdit(mw);
+ view->lineEdit = le;
+ l->addWidget(view);
+
+ QPushButton *button = new QPushButton("Change Font", mw);
+ l->addWidget(button);
+
+ QObject::connect(le, SIGNAL(textChanged(QString)), view, SLOT(setText(QString)));
+ QObject::connect(button, SIGNAL(clicked()), view, SLOT(changeFont()));
+
+ mw->resize(500, 300);
+ mw->show();
+
+ return a.exec();
+}
+
+
+#include <main.moc>
diff --git a/tests/auto/gui/text/qtextscriptengine/qtextscriptengine.pro b/tests/auto/gui/text/qtextscriptengine/qtextscriptengine.pro
new file mode 100644
index 0000000000..0f5076e2ed
--- /dev/null
+++ b/tests/auto/gui/text/qtextscriptengine/qtextscriptengine.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+
+QT += core-private gui-private
+
+HEADERS +=
+SOURCES += tst_qtextscriptengine.cpp
+INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src
diff --git a/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp b/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp
new file mode 100644
index 0000000000..cbed675cb7
--- /dev/null
+++ b/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp
@@ -0,0 +1,1304 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+
+#ifdef Q_WS_X11
+#define private public
+#endif
+
+// cannot do private -> public on windows since it seems to mess up some stl headers
+#include <qfont.h>
+
+#ifdef Q_WS_X11
+#undef private
+#endif
+
+#include <QtTest/QtTest>
+
+
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+#define private public
+#include <private/qtextengine_p.h>
+#include <qtextlayout.h>
+#undef private
+#else
+#include <private/qtextengine_p.h>
+#include <qtextlayout.h>
+#endif
+
+#include <qfontdatabase.h>
+#include <qfontinfo.h>
+
+
+
+
+
+//TESTED_CLASS=
+//TESTED_FILES= gui/text/qscriptengine.cpp
+
+class tst_QTextScriptEngine : public QObject
+{
+Q_OBJECT
+
+public:
+ tst_QTextScriptEngine();
+ virtual ~tst_QTextScriptEngine();
+
+
+public slots:
+ void initTestCase();
+ void init();
+ void cleanup();
+private slots:
+ void devanagari();
+ void bengali();
+ void gurmukhi();
+ // gujarati missing
+ void oriya();
+ void tamil();
+ void telugu();
+ void kannada();
+ void malayalam();
+ void sinhala();
+ void greek();
+
+ void khmer();
+ void linearB();
+ void controlInSyllable_qtbug14204();
+ void combiningMarks_qtbug15675();
+
+ void mirroredChars_data();
+ void mirroredChars();
+
+private:
+ bool haveTestFonts;
+};
+
+tst_QTextScriptEngine::tst_QTextScriptEngine()
+ : haveTestFonts(qgetenv("QT_HAVE_TEST_FONTS") == QByteArray("1"))
+{
+}
+
+tst_QTextScriptEngine::~tst_QTextScriptEngine()
+{
+}
+
+void tst_QTextScriptEngine::initTestCase()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ qWarning(
+ "Some of these tests depend on the internals of some test fonts which are not freely "
+ "distributable.\n"
+ "These tests will be skipped.\n"
+ "If you have the fonts available, set QT_HAVE_TEST_FONTS=1 in your environment and "
+ "run the test again."
+ );
+ }
+#endif
+}
+
+void tst_QTextScriptEngine::init()
+{
+}
+
+void tst_QTextScriptEngine::cleanup()
+{
+}
+
+struct ShapeTable {
+ unsigned short unicode[16];
+ unsigned short glyphs[16];
+};
+
+#if defined(Q_WS_X11)
+static bool shaping( const QFont &f, const ShapeTable *s)
+{
+ QString str = QString::fromUtf16( s->unicode );
+ QTextLayout layout(str, f);
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ int nglyphs = 0;
+ const unsigned short *g = s->glyphs;
+ while ( *g ) {
+ nglyphs++;
+ g++;
+ }
+
+ if( nglyphs != e->layoutData->items[0].num_glyphs )
+ goto error;
+
+ for (int i = 0; i < nglyphs; ++i) {
+ if ((e->layoutData->glyphLayout.glyphs[i] & 0xffffff) != s->glyphs[i])
+ goto error;
+ }
+ return true;
+ error:
+ str = "";
+ const unsigned short *uc = s->unicode;
+ while (*uc) {
+ str += QString("%1 ").arg(*uc, 4, 16);
+ ++uc;
+ }
+ qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
+ f.family().toLatin1().constData(),
+ str.toLatin1().constData(),
+ e->layoutData->items[0].num_glyphs, nglyphs);
+
+ str = "";
+ int i = 0;
+ while (i < e->layoutData->items[0].num_glyphs) {
+ str += QString("%1 ").arg(e->layoutData->glyphLayout.glyphs[i], 4, 16);
+ ++i;
+ }
+ qDebug(" glyph result = %s", str.toLatin1().constData());
+ return false;
+}
+#endif
+
+void tst_QTextScriptEngine::devanagari()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Devanagari).contains("Raghindi")) {
+ QFont f("Raghindi");
+ const ShapeTable shape_table [] = {
+ // Ka
+ { { 0x0915, 0x0 },
+ { 0x0080, 0x0 } },
+ // Ka Halant
+ { { 0x0915, 0x094d, 0x0 },
+ { 0x0080, 0x0051, 0x0 } },
+ // Ka Halant Ka
+ { { 0x0915, 0x094d, 0x0915, 0x0 },
+ { 0x00c8, 0x0080, 0x0 } },
+ // Ka MatraI
+ { { 0x0915, 0x093f, 0x0 },
+ { 0x01d1, 0x0080, 0x0 } },
+ // Ra Halant Ka
+ { { 0x0930, 0x094d, 0x0915, 0x0 },
+ { 0x0080, 0x005b, 0x0 } },
+ // Ra Halant Ka MatraI
+ { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
+ { 0x01d1, 0x0080, 0x005b, 0x0 } },
+ // MatraI
+ { { 0x093f, 0x0 },
+ { 0x01d4, 0x029c, 0x0 } },
+ // Ka Nukta
+ { { 0x0915, 0x093c, 0x0 },
+ { 0x00a4, 0x0 } },
+ // Ka Halant Ra
+ { { 0x0915, 0x094d, 0x0930, 0x0 },
+ { 0x0110, 0x0 } },
+ // Ka Halant Ra Halant Ka
+ { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
+ { 0x0158, 0x0080, 0x0 } },
+ { { 0x0930, 0x094d, 0x200d, 0x0 },
+ { 0x00e2, 0x0 } },
+ { { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
+ { 0x0158, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Raghindi", SkipAll);
+ }
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Devanagari).contains("Mangal")) {
+ QFont f("Mangal");
+ const ShapeTable shape_table [] = {
+ // Ka
+ { { 0x0915, 0x0 },
+ { 0x0080, 0x0 } },
+ // Ka Halant
+ { { 0x0915, 0x094d, 0x0 },
+ { 0x0080, 0x0051, 0x0 } },
+ // Ka Halant Ka
+ { { 0x0915, 0x094d, 0x0915, 0x0 },
+ { 0x00c8, 0x0080, 0x0 } },
+ // Ka MatraI
+ { { 0x0915, 0x093f, 0x0 },
+ { 0x01d1, 0x0080, 0x0 } },
+ // Ra Halant Ka
+ { { 0x0930, 0x094d, 0x0915, 0x0 },
+ { 0x0080, 0x005b, 0x0 } },
+ // Ra Halant Ka MatraI
+ { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
+ { 0x01d1, 0x0080, 0x005b, 0x0 } },
+ // MatraI
+ { { 0x093f, 0x0 },
+ { 0x01d4, 0x029c, 0x0 } },
+ // Ka Nukta
+ { { 0x0915, 0x093c, 0x0 },
+ { 0x00a4, 0x0 } },
+ // Ka Halant Ra
+ { { 0x0915, 0x094d, 0x0930, 0x0 },
+ { 0x0110, 0x0 } },
+ // Ka Halant Ra Halant Ka
+ { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
+ { 0x0158, 0x0080, 0x0 } },
+
+ { { 0x92b, 0x94d, 0x930, 0x0 },
+ { 0x125, 0x0 } },
+ { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
+ { 0x149, 0x0 } },
+ { {0}, {0} }
+ };
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find mangal", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::bengali()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Bengali).contains("Akaash")) {
+ QFont f("Akaash");
+ const ShapeTable shape_table [] = {
+ // Ka
+ { { 0x0995, 0x0 },
+ { 0x0151, 0x0 } },
+ // Ka Halant
+ { { 0x0995, 0x09cd, 0x0 },
+ { 0x0151, 0x017d, 0x0 } },
+ // Ka Halant Ka
+ { { 0x0995, 0x09cd, 0x0995, 0x0 },
+ { 0x019b, 0x0 } },
+ // Ka MatraI
+ { { 0x0995, 0x09bf, 0x0 },
+ { 0x0173, 0x0151, 0x0 } },
+ // Ra Halant Ka
+ { { 0x09b0, 0x09cd, 0x0995, 0x0 },
+ { 0x0151, 0x0276, 0x0 } },
+ // Ra Halant Ka MatraI
+ { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
+ { 0x0173, 0x0151, 0x0276, 0x0 } },
+ // Ka Nukta
+ { { 0x0995, 0x09bc, 0x0 },
+ { 0x0151, 0x0171, 0x0 } },
+ // Ka Halant Ra
+ { { 0x0995, 0x09cd, 0x09b0, 0x0 },
+ { 0x01f4, 0x0 } },
+ // Ka Halant Ra Halant Ka
+ { { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
+ { 0x025c, 0x0276, 0x0151, 0x0 } },
+ // Ya + Halant
+ { { 0x09af, 0x09cd, 0x0 },
+ { 0x016a, 0x017d, 0x0 } },
+ // Da Halant Ya -> Da Ya-Phala
+ { { 0x09a6, 0x09cd, 0x09af, 0x0 },
+ { 0x01e5, 0x0 } },
+ // A Halant Ya -> A Ya-phala
+ { { 0x0985, 0x09cd, 0x09af, 0x0 },
+ { 0x0145, 0x01cf, 0x0 } },
+ // Na Halant Ka
+ { { 0x09a8, 0x09cd, 0x0995, 0x0 },
+ { 0x026f, 0x0151, 0x0 } },
+ // Na Halant ZWNJ Ka
+ { { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
+ { 0x0164, 0x017d, 0x0151, 0x0 } },
+ // Na Halant ZWJ Ka
+ { { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
+ { 0x026f, 0x0151, 0x0 } },
+ // Ka Halant ZWNJ Ka
+ { { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
+ { 0x0151, 0x017d, 0x0151, 0x0 } },
+ // Ka Halant ZWJ Ka
+ { { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
+ { 0x025c, 0x0151, 0x0 } },
+ // Na Halant Ra
+ { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
+ { 0x0207, 0x0 } },
+ // Na Halant ZWNJ Ra
+ { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
+ { 0x0164, 0x017d, 0x016b, 0x0 } },
+ // Na Halant ZWJ Ra
+ { { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
+ { 0x026f, 0x016b, 0x0 } },
+ // Na Halant Ba
+ { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
+ { 0x022f, 0x0 } },
+ // Na Halant ZWNJ Ba
+ { { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
+ { 0x0164, 0x017d, 0x0167, 0x0 } },
+ // Na Halant ZWJ Ba
+ { { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
+ { 0x026f, 0x0167, 0x0 } },
+ // Na Halant Dha
+ { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
+ { 0x01d3, 0x0 } },
+ // Na Halant ZWNJ Dha
+ { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
+ { 0x0164, 0x017d, 0x0163, 0x0 } },
+ // Na Halant ZWJ Dha
+ { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
+ { 0x026f, 0x0163, 0x0 } },
+ // Ra Halant Ka MatraAU
+ { { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
+ { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
+ // Ra Halant Ba Halant Ba
+ { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
+ { 0x0232, 0x0276, 0x0 } },
+ { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
+ { 0x151, 0x276, 0x172, 0x143, 0x0 } },
+ { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
+ { 0x151, 0x276, 0x172, 0x144, 0x0 } },
+ // test decomposed two parts matras
+ { { 0x995, 0x9c7, 0x9be, 0x0 },
+ { 0x179, 0x151, 0x172, 0x0 } },
+ { { 0x995, 0x9c7, 0x9d7, 0x0 },
+ { 0x179, 0x151, 0x17e, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Akaash", SkipAll);
+ }
+ }
+ {
+ if (QFontDatabase().families(QFontDatabase::Bengali).contains("Mukti Narrow")) {
+ QFont f("Mukti Narrow");
+ const ShapeTable shape_table [] = {
+ // Ka
+ { { 0x0995, 0x0 },
+ { 0x0073, 0x0 } },
+ // Ka Halant
+ { { 0x0995, 0x09cd, 0x0 },
+ { 0x00b9, 0x0 } },
+ // Ka Halant Ka
+ { { 0x0995, 0x09cd, 0x0995, 0x0 },
+ { 0x0109, 0x0 } },
+ // Ka MatraI
+ { { 0x0995, 0x09bf, 0x0 },
+ { 0x0095, 0x0073, 0x0 } },
+ // Ra Halant Ka
+ { { 0x09b0, 0x09cd, 0x0995, 0x0 },
+ { 0x0073, 0x00e1, 0x0 } },
+ // Ra Halant Ka MatraI
+ { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
+ { 0x0095, 0x0073, 0x00e1, 0x0 } },
+ // MatraI
+ { { 0x09bf, 0x0 },
+ { 0x0095, 0x01c8, 0x0 } },
+ // Ka Nukta
+ { { 0x0995, 0x09bc, 0x0 },
+ { 0x0073, 0x0093, 0x0 } },
+ // Ka Halant Ra
+ { { 0x0995, 0x09cd, 0x09b0, 0x0 },
+ { 0x00e5, 0x0 } },
+ // Ka Halant Ra Halant Ka
+ { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
+ { 0x234, 0x24e, 0x73, 0x0 } },
+ // Ya + Halant
+ { { 0x09af, 0x09cd, 0x0 },
+ { 0x00d2, 0x0 } },
+ // Da Halant Ya -> Da Ya-Phala
+ { { 0x09a6, 0x09cd, 0x09af, 0x0 },
+ { 0x0084, 0x00e2, 0x0 } },
+ // A Halant Ya -> A Ya-phala
+ { { 0x0985, 0x09cd, 0x09af, 0x0 },
+ { 0x0067, 0x00e2, 0x0 } },
+ // Na Halant Ka
+ { { 0x09a8, 0x09cd, 0x0995, 0x0 },
+ { 0x0188, 0x0 } },
+ // Na Halant ZWNJ Ka
+ { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
+ { 0xcc, 0x73, 0x0 } },
+ // Na Halant ZWJ Ka
+ { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+ { 0x247, 0x73, 0x0 } },
+ // Ka Halant ZWNJ Ka
+ { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+ { 0x247, 0x73, 0x0 } },
+ // Ka Halant ZWJ Ka
+ { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+ { 0x247, 0x73, 0x0 } },
+ // Na Halant Ra
+ { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
+ { 0x00f8, 0x0 } },
+ // Na Halant ZWNJ Ra
+ { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
+ { 0xcc, 0x8d, 0x0 } },
+ // Na Halant ZWJ Ra
+ { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
+ { 0x247, 0x8d, 0x0 } },
+ // Na Halant Ba
+ { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
+ { 0x0139, 0x0 } },
+ // Na Halant ZWNJ Ba
+ { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
+ { 0xcc, 0x89, 0x0 } },
+ // Na Halant ZWJ Ba
+ { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
+ { 0x247, 0x89, 0x0 } },
+ // Na Halant Dha
+ { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
+ { 0x0145, 0x0 } },
+ // Na Halant ZWNJ Dha
+ { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
+ { 0xcc, 0x85, 0x0 } },
+ // Na Halant ZWJ Dha
+ { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
+ { 0x247, 0x85, 0x0 } },
+ // Ra Halant Ka MatraAU
+ { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
+ { 0x232, 0x73, 0xe1, 0xa0, 0x0 } },
+ // Ra Halant Ba Halant Ba
+ { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
+ { 0x013b, 0x00e1, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Mukti", SkipAll);
+ }
+ }
+ {
+ if (QFontDatabase().families(QFontDatabase::Bengali).contains("Likhan")) {
+ QFont f("Likhan");
+ const ShapeTable shape_table [] = {
+ { { 0x9a8, 0x9cd, 0x9af, 0x0 },
+ { 0x1ca, 0x0 } },
+ { { 0x09b8, 0x09cd, 0x09af, 0x0 },
+ { 0x020e, 0x0 } },
+ { { 0x09b6, 0x09cd, 0x09af, 0x0 },
+ { 0x01f4, 0x0 } },
+ { { 0x09b7, 0x09cd, 0x09af, 0x0 },
+ { 0x01fe, 0x0 } },
+ { { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
+ { 0x10b, 0x167, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Likhan", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::gurmukhi()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Gurmukhi).contains("Lohit Punjabi")) {
+ QFont f("Lohit Punjabi");
+ const ShapeTable shape_table [] = {
+ { { 0xA15, 0xA4D, 0xa39, 0x0 },
+ { 0x3b, 0x8b, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Lohit Punjabi", SkipAll);
+ }
+ }
+#endif
+}
+
+void tst_QTextScriptEngine::oriya()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Oriya).contains("utkal")) {
+ QFont f("utkal");
+ const ShapeTable shape_table [] = {
+ { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+ { 0x150, 0x125, 0x0 } },
+ { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+ { 0x151, 0x120, 0x0 } },
+ { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+ { 0x152, 0x120, 0x0 } },
+ { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+ { 0x152, 0x120, 0x0 } },
+ { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+ { 0x176, 0x0 } },
+ { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+ { 0x177, 0x0 } },
+ { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
+ { 0x176, 0x124, 0x0 } },
+ { {0}, {0} }
+
+ };
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find utkal", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+
+void tst_QTextScriptEngine::tamil()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Tamil).contains("AkrutiTml1")) {
+ QFont f("AkrutiTml1");
+ const ShapeTable shape_table [] = {
+ { { 0x0b95, 0x0bc2, 0x0 },
+ { 0x004e, 0x0 } },
+ { { 0x0bae, 0x0bc2, 0x0 },
+ { 0x009e, 0x0 } },
+ { { 0x0b9a, 0x0bc2, 0x0 },
+ { 0x0058, 0x0 } },
+ { { 0x0b99, 0x0bc2, 0x0 },
+ { 0x0053, 0x0 } },
+ { { 0x0bb0, 0x0bc2, 0x0 },
+ { 0x00a8, 0x0 } },
+ { { 0x0ba4, 0x0bc2, 0x0 },
+ { 0x008e, 0x0 } },
+ { { 0x0b9f, 0x0bc2, 0x0 },
+ { 0x0062, 0x0 } },
+ { { 0x0b95, 0x0bc6, 0x0 },
+ { 0x000a, 0x0031, 0x0 } },
+ { { 0x0b95, 0x0bca, 0x0 },
+ { 0x000a, 0x0031, 0x0007, 0x0 } },
+ { { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
+ { 0x000a, 0x0031, 0x007, 0x0 } },
+ { { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
+ { 0x0049, 0x0 } },
+ { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
+ { 0x000a, 0x0049, 0x007, 0x0 } },
+ { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
+ { 0x000a, 0x0049, 0x007, 0x0 } },
+ { { 0x0b9f, 0x0bbf, 0x0 },
+ { 0x005f, 0x0 } },
+ { { 0x0b9f, 0x0bc0, 0x0 },
+ { 0x0060, 0x0 } },
+ { { 0x0bb2, 0x0bc0, 0x0 },
+ { 0x00ab, 0x0 } },
+ { { 0x0bb2, 0x0bbf, 0x0 },
+ { 0x00aa, 0x0 } },
+ { { 0x0bb0, 0x0bcd, 0x0 },
+ { 0x00a4, 0x0 } },
+ { { 0x0bb0, 0x0bbf, 0x0 },
+ { 0x00a5, 0x0 } },
+ { { 0x0bb0, 0x0bc0, 0x0 },
+ { 0x00a6, 0x0 } },
+ { { 0x0b83, 0x0 },
+ { 0x0025, 0x0 } },
+ { { 0x0b83, 0x0b95, 0x0 },
+ { 0x0025, 0x0031, 0x0 } },
+ { { 0xb95, 0xbc6, 0xbbe, 0x0 },
+ { 0xa, 0x31, 0x7, 0x0 } },
+ { { 0xb95, 0xbc7, 0xbbe, 0x0 },
+ { 0xb, 0x31, 0x7, 0x0 } },
+ { { 0xb95, 0xbc6, 0xbd7, 0x0 },
+ { 0xa, 0x31, 0x40, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find AkrutiTml1", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+
+void tst_QTextScriptEngine::telugu()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Telugu).contains("Pothana2000")) {
+ QFont f("Pothana2000");
+ const ShapeTable shape_table [] = {
+ { { 0xc15, 0xc4d, 0x0 },
+ { 0xbb, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc37, 0x0 },
+ { 0x4b, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
+ { 0xe0, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
+ { 0x4b, 0x91, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc30, 0x0 },
+ { 0x5a, 0xb2, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
+ { 0xbb, 0xb2, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
+ { 0x5a, 0xb2, 0x83, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
+ { 0xe2, 0xb2, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
+ { 0xe6, 0xb3, 0x83, 0x0 } },
+ { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
+ { 0xe6, 0xb3, 0x9f, 0x0 } },
+ { { 0xc15, 0xc46, 0xc56, 0x0 },
+ { 0xe6, 0xb3, 0x0 } },
+ { {0}, {0} }
+
+ };
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Pothana2000", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+
+void tst_QTextScriptEngine::kannada()
+{
+#if defined(Q_WS_X11)
+ {
+ if (QFontDatabase().families(QFontDatabase::Kannada).contains("Sampige")) {
+ QFont f("Sampige");
+ const ShapeTable shape_table [] = {
+ { { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
+ { 0x0049, 0x00ba, 0x0 } },
+ { { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
+ { 0x0049, 0x00b3, 0x0 } },
+ { { 0x0caf, 0x0cc2, 0x0 },
+ { 0x004f, 0x005d, 0x0 } },
+ { { 0x0ce0, 0x0 },
+ { 0x006a, 0x0 } },
+ { { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
+ { 0x006b, 0x006c, 0x006d, 0x0 } },
+ { { 0x0cb5, 0x0ccb, 0x0 },
+ { 0x015f, 0x0067, 0x0 } },
+ { { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
+ { 0x004e, 0x0082, 0x0 } },
+ { { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
+ { 0x0036, 0x0082, 0x0 } },
+ { { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
+ { 0x0036, 0x00c1, 0x0 } },
+ { { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
+ { 0x0050, 0x00a7, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Sampige", SkipAll);
+ }
+ }
+ {
+ if (QFontDatabase().families(QFontDatabase::Kannada).contains("Tunga")) {
+ QFont f("Tunga");
+ const ShapeTable shape_table [] = {
+ { { 0x0cb7, 0x0cc6, 0x0 },
+ { 0x00b0, 0x006c, 0x0 } },
+ { { 0x0cb7, 0x0ccd, 0x0 },
+ { 0x0163, 0x0 } },
+ { { 0xc95, 0xcbf, 0xcd5, 0x0 },
+ { 0x114, 0x73, 0x0 } },
+ { { 0xc95, 0xcc6, 0xcd5, 0x0 },
+ { 0x90, 0x6c, 0x73, 0x0 } },
+ { { 0xc95, 0xcc6, 0xcd6, 0x0 },
+ { 0x90, 0x6c, 0x74, 0x0 } },
+ { { 0xc95, 0xcc6, 0xcc2, 0x0 },
+ { 0x90, 0x6c, 0x69, 0x0 } },
+ { { 0xc95, 0xcca, 0xcd5, 0x0 },
+ { 0x90, 0x6c, 0x69, 0x73, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Tunga", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+
+
+void tst_QTextScriptEngine::malayalam()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Malayalam).contains("AkrutiMal2")) {
+ QFont f("AkrutiMal2");
+ const ShapeTable shape_table [] = {
+ { { 0x0d15, 0x0d46, 0x0 },
+ { 0x005e, 0x0034, 0x0 } },
+ { { 0x0d15, 0x0d47, 0x0 },
+ { 0x005f, 0x0034, 0x0 } },
+ { { 0x0d15, 0x0d4b, 0x0 },
+ { 0x005f, 0x0034, 0x0058, 0x0 } },
+ { { 0x0d15, 0x0d48, 0x0 },
+ { 0x0060, 0x0034, 0x0 } },
+ { { 0x0d15, 0x0d4a, 0x0 },
+ { 0x005e, 0x0034, 0x0058, 0x0 } },
+ { { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
+ { 0x009e, 0x0034, 0x0 } },
+ { { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
+ { 0x0034, 0x007a, 0x0 } },
+ { { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
+ { 0x0034, 0x00a2, 0x0 } },
+ { { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
+ { 0x0069, 0x0 } },
+ { { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
+ { 0x0074, 0x0 } },
+ { { 0x0d30, 0x0d4d, 0x0 },
+ { 0x009e, 0x0 } },
+ { { 0x0d30, 0x0d4d, 0x200c, 0x0 },
+ { 0x009e, 0x0 } },
+ { { 0x0d30, 0x0d4d, 0x200d, 0x0 },
+ { 0x009e, 0x0 } },
+ { { 0xd15, 0xd46, 0xd3e, 0x0 },
+ { 0x5e, 0x34, 0x58, 0x0 } },
+ { { 0xd15, 0xd47, 0xd3e, 0x0 },
+ { 0x5f, 0x34, 0x58, 0x0 } },
+ { { 0xd15, 0xd46, 0xd57, 0x0 },
+ { 0x5e, 0x34, 0x65, 0x0 } },
+ { { 0xd15, 0xd57, 0x0 },
+ { 0x34, 0x65, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find AkrutiMal2", SkipAll);
+ }
+ }
+ {
+ if (QFontDatabase().families(QFontDatabase::Malayalam).contains("Rachana")) {
+ QFont f("Rachana");
+ const ShapeTable shape_table [] = {
+ { { 0xd37, 0xd4d, 0xd1f, 0xd4d, 0xd30, 0xd40, 0x0 },
+ { 0x385, 0xa3, 0x0 } },
+ { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
+ { 0x2ff, 0x0 } },
+ { { 0xd33, 0xd4d, 0xd33, 0x0 },
+ { 0x3f8, 0x0 } },
+ { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
+ { 0x2ff, 0x0 } },
+ { { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 },
+ { 0xf3, 0x350, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Rachana", SkipAll);
+ }
+ }
+
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::sinhala()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Sinhala).contains("Malithi Web")) {
+ QFont f("Malithi Web");
+ const ShapeTable shape_table [] = {
+ { { 0xd9a, 0xdd9, 0xdcf, 0x0 },
+ { 0x4a, 0x61, 0x42, 0x0 } },
+ { { 0xd9a, 0xdd9, 0xddf, 0x0 },
+ { 0x4a, 0x61, 0x50, 0x0 } },
+ { { 0xd9a, 0xdd9, 0xdca, 0x0 },
+ { 0x4a, 0x62, 0x0 } },
+ { { 0xd9a, 0xddc, 0xdca, 0x0 },
+ { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
+ { { 0xd9a, 0xdda, 0x0 },
+ { 0x4a, 0x62, 0x0 } },
+ { { 0xd9a, 0xddd, 0x0 },
+ { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Malithi Web", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+
+void tst_QTextScriptEngine::khmer()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Khmer).contains("Khmer OS")) {
+ QFont f("Khmer OS");
+ const ShapeTable shape_table [] = {
+ { { 0x179a, 0x17cd, 0x0 },
+ { 0x24c, 0x27f, 0x0 } },
+ { { 0x179f, 0x17c5, 0x0 },
+ { 0x273, 0x203, 0x0 } },
+ { { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
+ { 0x275, 0x242, 0x182, 0x0 } },
+ { { 0x179a, 0x0 },
+ { 0x24c, 0x0 } },
+ { { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
+ { 0x274, 0x233, 0x197, 0x0 } },
+ { { 0x1798, 0x17b6, 0x0 },
+ { 0x1cb, 0x0 } },
+ { { 0x179a, 0x17b8, 0x0 },
+ { 0x24c, 0x26a, 0x0 } },
+ { { 0x1787, 0x17b6, 0x0 },
+ { 0x1ba, 0x0 } },
+ { { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
+ { 0x24a, 0x195, 0x26d, 0x0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Khmer OS", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::linearB()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Any).contains("Penuturesu")) {
+ QFont f("Penuturesu");
+ const ShapeTable shape_table [] = {
+ { { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 },
+ { 0x5, 0x6, 0x7, 0 } },
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find Penuturesu", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+#if defined(Q_WS_X11)
+static bool decomposedShaping( const QFont &f, const QChar &ch)
+{
+ QString str = QString().append(ch);
+ QTextLayout layout(str, f);
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ QTextLayout decomposed(str.normalized(QString::NormalizationForm_D), f);
+ QTextEngine *de = decomposed.d;
+ de->itemize();
+ de->shape(0);
+
+ if( e->layoutData->items[0].num_glyphs != de->layoutData->items[0].num_glyphs )
+ goto error;
+
+ for (int i = 0; i < e->layoutData->items[0].num_glyphs; ++i) {
+ if ((e->layoutData->glyphLayout.glyphs[i] & 0xffffff) != (de->layoutData->glyphLayout.glyphs[i] & 0xffffff))
+ goto error;
+ }
+ return true;
+ error:
+ qDebug("%s: decomposedShaping of char %4x failed, nglyphs=%d, decomposed nglyphs %d",
+ f.family().toLatin1().constData(),
+ ch.unicode(),
+ e->layoutData->items[0].num_glyphs,
+ de->layoutData->items[0].num_glyphs);
+
+ str = "";
+ int i = 0;
+ while (i < e->layoutData->items[0].num_glyphs) {
+ str += QString("%1 ").arg(e->layoutData->glyphLayout.glyphs[i], 4, 16);
+ ++i;
+ }
+ qDebug(" composed glyph result = %s", str.toLatin1().constData());
+ str = "";
+ i = 0;
+ while (i < de->layoutData->items[0].num_glyphs) {
+ str += QString("%1 ").arg(de->layoutData->glyphLayout.glyphs[i], 4, 16);
+ ++i;
+ }
+ qDebug(" decomposed glyph result = %s", str.toLatin1().constData());
+ return false;
+}
+#endif
+
+
+void tst_QTextScriptEngine::greek()
+{
+#if defined(Q_WS_X11)
+ if (!haveTestFonts) {
+ QSKIP("Test fonts are not available", SkipAll);
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Any).contains("DejaVu Sans")) {
+ QFont f("DejaVu Sans");
+ for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
+ QString str;
+ str.append(uc);
+ if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
+ //qDebug() << "skipping" << hex << uc;
+ continue;
+ }
+ if (uc == 0x1fc1 || uc == 0x1fed)
+ continue;
+ QVERIFY( decomposedShaping(f, QChar(uc)) );
+ }
+ } else {
+ QSKIP("couldn't find DejaVu Sans", SkipAll);
+ }
+ }
+
+ {
+ if (QFontDatabase().families(QFontDatabase::Any).contains("SBL Greek")) {
+ QFont f("SBL Greek");
+ for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
+ QString str;
+ str.append(uc);
+ if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
+ //qDebug() << "skipping" << hex << uc;
+ continue;
+ }
+ if (uc == 0x1fc1 || uc == 0x1fed)
+ continue;
+ QVERIFY( decomposedShaping(f, QChar(uc) ) );
+
+ }
+
+ const ShapeTable shape_table [] = {
+ { { 0x3b1, 0x300, 0x313, 0x0 },
+ { 0xb8, 0x3d3, 0x3c7, 0x0 } },
+ { { 0x3b1, 0x313, 0x300, 0x0 },
+ { 0xd4, 0x0 } },
+
+ { {0}, {0} }
+ };
+
+
+ const ShapeTable *s = shape_table;
+ while (s->unicode[0]) {
+ QVERIFY( shaping(f, s) );
+ ++s;
+ }
+ } else {
+ QSKIP("couldn't find SBL_grk", SkipAll);
+ }
+ }
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::controlInSyllable_qtbug14204()
+{
+#if defined(Q_WS_X11)
+ QString s;
+ s.append(QChar(0x0915));
+ s.append(QChar(0x094d));
+ s.append(QChar(0x200d));
+ s.append(QChar(0x0915));
+
+ QTextLayout layout(s);
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ QVERIFY(e->layoutData->items[0].num_glyphs == 2);
+ QVERIFY(e->layoutData->glyphLayout.advances_x[1] != 0);
+#else
+ QSKIP("X11 specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::combiningMarks_qtbug15675()
+{
+#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
+ QString s;
+ s.append(QChar(0x0061));
+ s.append(QChar(0x0062));
+ s.append(QChar(0x0300));
+ s.append(QChar(0x0063));
+
+ QFont font("Monaco");
+ QTextLayout layout(s, font);
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ QVERIFY(e->layoutData->items[0].num_glyphs == 4);
+ QVERIFY(e->layoutData->glyphLayout.advances_y[2] > 0);
+#elif defined(Q_WS_X11)
+ QFontDatabase db;
+
+ if (!db.families().contains("DejaVu Sans Mono")) {
+ QSKIP("Required font (DejaVu Sans Mono) doesn't exist, skip test.", SkipAll);
+ return;
+ }
+
+ QString s;
+ s.append(QChar(0x0062));
+ s.append(QChar(0x0332));
+ s.append(QChar(0x0063));
+
+ QTextLayout layout(s, QFont("DejaVu Sans Mono"));
+ QTextEngine *e = layout.d;
+ e->itemize();
+ e->shape(0);
+
+ QVERIFY(e->layoutData->items[0].num_glyphs == 3);
+ QVERIFY(e->layoutData->glyphLayout.advances_x[1] == 0);
+#else
+ QSKIP("X11/Mac specific test", SkipAll);
+#endif
+}
+
+void tst_QTextScriptEngine::mirroredChars_data()
+{
+ QTest::addColumn<int>("hintingPreference");
+
+ QTest::newRow("Default hinting") << int(QFont::PreferDefaultHinting);
+ QTest::newRow("No hinting") << int(QFont::PreferNoHinting);
+ QTest::newRow("Vertical hinting") << int(QFont::PreferVerticalHinting);
+ QTest::newRow("Full hinting") << int(QFont::PreferFullHinting);
+}
+
+void tst_QTextScriptEngine::mirroredChars()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("Not supported on Mac", SkipAll);
+#endif
+ QFETCH(int, hintingPreference);
+
+ QFont font;
+ font.setHintingPreference(QFont::HintingPreference(hintingPreference));
+
+ QString s;
+ s.append(QLatin1Char('('));
+ s.append(QLatin1Char(')'));
+
+ HB_Glyph leftParenthesis;
+ HB_Glyph rightParenthesis;
+ {
+ QTextLayout layout(s);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QTextEngine *e = layout.engine();
+ e->itemize();
+ e->shape(0);
+ QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2));
+
+ const QGlyphLayout &glyphLayout = e->layoutData->glyphLayout;
+ leftParenthesis = glyphLayout.glyphs[0];
+ rightParenthesis = glyphLayout.glyphs[1];
+ }
+
+ {
+ QTextLayout layout(s);
+ layout.setFlags(Qt::TextForceRightToLeft);
+
+ QTextEngine *e = layout.engine();
+ e->itemize();
+ e->shape(0);
+ QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2));
+
+ const QGlyphLayout &glyphLayout = e->layoutData->glyphLayout;
+ QCOMPARE(glyphLayout.glyphs[0], rightParenthesis);
+ QCOMPARE(glyphLayout.glyphs[1], leftParenthesis);
+ }
+}
+
+QTEST_MAIN(tst_QTextScriptEngine)
+#include "tst_qtextscriptengine.moc"
diff --git a/tests/auto/gui/text/qtexttable/.gitignore b/tests/auto/gui/text/qtexttable/.gitignore
new file mode 100644
index 0000000000..876aa75c0a
--- /dev/null
+++ b/tests/auto/gui/text/qtexttable/.gitignore
@@ -0,0 +1 @@
+tst_qtexttable
diff --git a/tests/auto/gui/text/qtexttable/qtexttable.pro b/tests/auto/gui/text/qtexttable/qtexttable.pro
new file mode 100644
index 0000000000..611b706fc4
--- /dev/null
+++ b/tests/auto/gui/text/qtexttable/qtexttable.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+QT += widgets
+SOURCES += tst_qtexttable.cpp
+
+
+
diff --git a/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp
new file mode 100644
index 0000000000..83343811d8
--- /dev/null
+++ b/tests/auto/gui/text/qtexttable/tst_qtexttable.cpp
@@ -0,0 +1,1004 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+
+#include <qtextdocument.h>
+#include <qtextdocumentfragment.h>
+#include <qtexttable.h>
+#include <qdebug.h>
+#include <qtextcursor.h>
+#include <qtextdocument.h>
+#include <qtextedit.h>
+
+//TESTED_FILES=
+
+typedef QList<int> IntList;
+Q_DECLARE_METATYPE(IntList)
+
+QT_FORWARD_DECLARE_CLASS(QTextDocument)
+
+class tst_QTextTable : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTextTable();
+
+
+public slots:
+ void init();
+ void cleanup();
+private slots:
+ void cursorPositioning();
+ void variousTableModifications();
+ void tableShrinking();
+ void spans();
+ void variousModifications2();
+ void tableManager_undo();
+ void tableManager_removeCell();
+ void rowAt();
+ void rowAtWithSpans();
+ void multiBlockCells();
+ void insertRows();
+ void deleteInTable();
+ void mergeCells();
+ void mergeAndInsert();
+ void splitCells();
+ void blocksForTableShouldHaveEmptyFormat();
+ void removeTableByRemoveRows();
+ void removeTableByRemoveColumns();
+ void setCellFormat();
+ void removeRows1();
+ void removeRows2();
+ void removeRows3();
+ void removeRows4();
+ void removeRows5();
+ void removeColumns1();
+ void removeColumns2();
+ void removeColumns3();
+ void removeColumns4();
+ void removeColumns5();
+ void removeColumnsInTableWithMergedRows();
+ void QTBUG11282_insertBeforeMergedEnding_data();
+ void QTBUG11282_insertBeforeMergedEnding();
+
+private:
+ QTextTable *create2x2Table();
+ QTextTable *create4x4Table();
+
+ QTextTable *createTable(int rows, int cols);
+
+ QTextDocument *doc;
+ QTextCursor cursor;
+};
+
+tst_QTextTable::tst_QTextTable()
+{}
+
+void tst_QTextTable::init()
+{
+ doc = new QTextDocument;
+ cursor = QTextCursor(doc);
+}
+
+void tst_QTextTable::cleanup()
+{
+ cursor = QTextCursor();
+ delete doc;
+ doc = 0;
+}
+
+void tst_QTextTable::cursorPositioning()
+{
+ // ensure the cursor is placed at the beginning of the first cell upon
+ // table creation
+ QTextTable *table = cursor.insertTable(2, 2);
+
+ QVERIFY(cursor == table->cellAt(0, 0).firstCursorPosition());
+ QVERIFY(table->cellAt(0, 0).firstPosition() == table->firstPosition());
+}
+
+void tst_QTextTable::variousTableModifications()
+{
+ QTextTableFormat tableFmt;
+
+ QTextTable *tab = cursor.insertTable(2, 2, tableFmt);
+ QVERIFY(doc->toPlainText().length() == 5);
+ QVERIFY(tab == cursor.currentTable());
+ QVERIFY(tab->columns() == 2);
+ QVERIFY(tab->rows() == 2);
+
+ QVERIFY(cursor.position() == 1);
+ QTextCharFormat fmt = cursor.charFormat();
+ QVERIFY(fmt.objectIndex() == -1);
+ QTextTableCell cell = tab->cellAt(cursor);
+ QVERIFY(cell.isValid());
+ QVERIFY(cell.row() == 0);
+ QVERIFY(cell.column() == 0);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 2);
+ fmt = cursor.charFormat();
+ QVERIFY(fmt.objectIndex() == -1);
+ cell = tab->cellAt(cursor);
+ QVERIFY(cell.isValid());
+ QVERIFY(cell.row() == 0);
+ QVERIFY(cell.column() == 1);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 3);
+ fmt = cursor.charFormat();
+ QVERIFY(fmt.objectIndex() == -1);
+ cell = tab->cellAt(cursor);
+ QVERIFY(cell.isValid());
+ QVERIFY(cell.row() == 1);
+ QVERIFY(cell.column() == 0);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 4);
+ fmt = cursor.charFormat();
+ QVERIFY(fmt.objectIndex() == -1);
+ cell = tab->cellAt(cursor);
+ QVERIFY(cell.isValid());
+ QVERIFY(cell.row() == 1);
+ QVERIFY(cell.column() == 1);
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 5);
+ fmt = cursor.charFormat();
+ QVERIFY(fmt.objectIndex() == -1);
+ cell = tab->cellAt(cursor);
+ QVERIFY(!cell.isValid());
+
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 5);
+
+ // check we can't delete the cells with the cursor
+ cursor.movePosition(QTextCursor::Start);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 1);
+ cursor.deleteChar();
+ QVERIFY(doc->toPlainText().length() == 5);
+ cursor.movePosition(QTextCursor::NextBlock);
+ QVERIFY(cursor.position() == 2);
+ cursor.deleteChar();
+ QVERIFY(doc->toPlainText().length() == 5);
+ cursor.deletePreviousChar();
+ QVERIFY(cursor.position() == 2);
+ QVERIFY(doc->toPlainText().length() == 5);
+
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 2);
+
+ table->insertRows(2, 1);
+ QVERIFY(table->rows() == 3);
+ QVERIFY(table->columns() == 2);
+ QVERIFY(doc->toPlainText().length() == 7);
+ table->insertColumns(2, 2);
+ QVERIFY(table->rows() == 3);
+ QVERIFY(table->columns() == 4);
+ QVERIFY(doc->toPlainText().length() == 13);
+
+ table->resize(4, 5);
+ QVERIFY(table->rows() == 4);
+ QVERIFY(table->columns() == 5);
+ QVERIFY(doc->toPlainText().length() == 21);
+}
+
+void tst_QTextTable::tableShrinking()
+{
+ QTextTableFormat tableFmt;
+
+ cursor.insertTable(3, 4, tableFmt);
+ QVERIFY(doc->toPlainText().length() == 13);
+
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table->rows() == 3);
+ QVERIFY(table->columns() == 4);
+
+ table->removeRows(1, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 4);
+ QVERIFY(doc->toPlainText().length() == 9);
+ table->removeColumns(1, 2);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 2);
+ QVERIFY(doc->toPlainText().length() == 5);
+
+ table->resize(1, 1);
+ QVERIFY(table->rows() == 1);
+ QVERIFY(table->columns() == 1);
+ QVERIFY(doc->toPlainText().length() == 2);
+}
+
+void tst_QTextTable::spans()
+{
+ QTextTableFormat tableFmt;
+
+ cursor.insertTable(2, 2, tableFmt);
+
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(table->cellAt(0, 0) != table->cellAt(0, 1));
+ table->mergeCells(0, 0, 1, 2);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 2);
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(0, 1));
+ table->mergeCells(0, 0, 2, 2);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 2);
+}
+
+void tst_QTextTable::variousModifications2()
+{
+ QTextTableFormat tableFmt;
+
+ cursor.insertTable(2, 5, tableFmt);
+ QVERIFY(doc->toPlainText().length() == 11);
+ QTextTable *table = cursor.currentTable();
+ QVERIFY(cursor.position() == 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 5);
+
+ table->insertColumns(0, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 6);
+ table->insertColumns(6, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 7);
+
+ table->insertRows(0, 1);
+ QVERIFY(table->rows() == 3);
+ QVERIFY(table->columns() == 7);
+ table->insertRows(3, 1);
+ QVERIFY(table->rows() == 4);
+ QVERIFY(table->columns() == 7);
+
+ table->removeRows(0, 1);
+ QVERIFY(table->rows() == 3);
+ QVERIFY(table->columns() == 7);
+ table->removeRows(2, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 7);
+
+ table->removeColumns(0, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 6);
+ table->removeColumns(5, 1);
+ QVERIFY(table->rows() == 2);
+ QVERIFY(table->columns() == 5);
+
+ tableFmt = table->format();
+ table->insertColumns(2, 1);
+ table->setFormat(tableFmt);
+ table->insertColumns(2, 1);
+ QVERIFY(table->columns() == 7);
+}
+
+void tst_QTextTable::tableManager_undo()
+{
+ QTextTableFormat fmt;
+ fmt.setBorder(10);
+ QTextTable *table = cursor.insertTable(2, 2, fmt);
+ QVERIFY(table);
+
+ QVERIFY(table->format().border() == 10);
+
+ fmt.setBorder(20);
+ table->setFormat(fmt);
+
+ QVERIFY(table->format().border() == 20);
+
+ doc->undo();
+
+ QVERIFY(table->format().border() == 10);
+}
+
+void tst_QTextTable::tableManager_removeCell()
+{
+ // essentially a test for TableManager::removeCell, in particular to remove empty items from the rowlist.
+ // If it fails it'll triger assertions inside TableManager. Yeah, not pretty, should VERIFY here ;(
+ cursor.insertTable(2, 2, QTextTableFormat());
+ doc->undo();
+ // ###
+ QVERIFY(true);
+}
+
+void tst_QTextTable::rowAt()
+{
+ // test TablePrivate::rowAt
+ QTextTable *table = cursor.insertTable(4, 2);
+
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 2);
+
+ QTextCursor cell00Cursor = table->cellAt(0, 0).firstCursorPosition();
+ QTextCursor cell10Cursor = table->cellAt(1, 0).firstCursorPosition();
+ QTextCursor cell20Cursor = table->cellAt(2, 0).firstCursorPosition();
+ QTextCursor cell21Cursor = table->cellAt(2, 1).firstCursorPosition();
+ QTextCursor cell30Cursor = table->cellAt(3, 0).firstCursorPosition();
+ QVERIFY(table->cellAt(cell00Cursor).firstCursorPosition() == cell00Cursor);
+ QVERIFY(table->cellAt(cell10Cursor).firstCursorPosition() == cell10Cursor);
+ QVERIFY(table->cellAt(cell20Cursor).firstCursorPosition() == cell20Cursor);
+ QVERIFY(table->cellAt(cell30Cursor).firstCursorPosition() == cell30Cursor);
+
+ table->mergeCells(1, 0, 2, 1);
+
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 2);
+
+ QVERIFY(cell00Cursor == table->cellAt(0, 0).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(1, 0).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(2, 0).firstCursorPosition());
+ QVERIFY(cell21Cursor == table->cellAt(2, 1).firstCursorPosition());
+ QVERIFY(cell30Cursor == table->cellAt(3, 0).firstCursorPosition());
+
+ table->mergeCells(1, 0, 2, 2);
+
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 2);
+
+ QVERIFY(cell00Cursor == table->cellAt(0, 0).firstCursorPosition());
+ QVERIFY(cell00Cursor == table->cellAt(0, 0).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(1, 0).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(1, 1).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(2, 0).firstCursorPosition());
+ QVERIFY(cell10Cursor == table->cellAt(2, 1).firstCursorPosition());
+ QVERIFY(cell30Cursor == table->cellAt(3, 0).firstCursorPosition());
+}
+
+void tst_QTextTable::rowAtWithSpans()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+
+ table->mergeCells(0, 0, 2, 1);
+ QVERIFY(table->cellAt(0, 0).rowSpan() == 2);
+
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+
+ table->mergeCells(0, 0, 2, 2);
+ QVERIFY(table->cellAt(0, 0).columnSpan() == 2);
+
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+}
+
+void tst_QTextTable::multiBlockCells()
+{
+ // little testcase for multi-block cells
+ QTextTable *table = cursor.insertTable(2, 2);
+
+ QVERIFY(cursor == table->cellAt(0, 0).firstCursorPosition());
+
+ cursor.insertText("Hello");
+ cursor.insertBlock(QTextBlockFormat());
+ cursor.insertText("World");
+
+ cursor.movePosition(QTextCursor::Left);
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(cursor));
+}
+
+void tst_QTextTable::insertRows()
+{
+ // little testcase for multi-block cells
+ QTextTable *table = cursor.insertTable(2, 2);
+
+ QVERIFY(cursor == table->cellAt(0, 0).firstCursorPosition());
+
+ table->insertRows(0, 1);
+ QVERIFY(table->rows() == 3);
+
+ table->insertRows(1, 1);
+ QVERIFY(table->rows() == 4);
+
+ table->insertRows(-1, 1);
+ QVERIFY(table->rows() == 5);
+
+ table->insertRows(5, 2);
+ QVERIFY(table->rows() == 7);
+
+}
+
+void tst_QTextTable::deleteInTable()
+{
+ 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");
+
+ 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);
+
+ cursor.removeSelectedText();
+
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->rows(), 2);
+
+ // verify table is still all in shape. Only the text inside should get deleted
+ for (int row = 0; row < table->rows(); ++row)
+ for (int col = 0; col < table->columns(); ++col) {
+ const QTextTableCell cell = table->cellAt(row, col);
+ QVERIFY(cell.isValid());
+ QCOMPARE(cell.rowSpan(), 1);
+ QCOMPARE(cell.columnSpan(), 1);
+ }
+}
+
+QTextTable *tst_QTextTable::create2x2Table()
+{
+ cleanup();
+ init();
+ 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");
+ return table;
+}
+
+QTextTable *tst_QTextTable::create4x4Table()
+{
+ cleanup();
+ init();
+ QTextTable *table = cursor.insertTable(4, 4);
+ 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");
+ return table;
+}
+
+QTextTable *tst_QTextTable::createTable(int rows, int cols)
+{
+ cleanup();
+ init();
+ QTextTable *table = cursor.insertTable(rows, cols);
+ return table;
+}
+
+void tst_QTextTable::mergeCells()
+{
+ QTextTable *table = create4x4Table();
+
+ table->mergeCells(1, 1, 1, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table = create4x4Table();
+
+ table->mergeCells(1, 1, 2, 1);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table = create4x4Table();
+
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ // should do nothing
+ table->mergeCells(1, 1, 1, 1);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table = create2x2Table();
+
+ table->mergeCells(0, 1, 2, 1);
+ table->mergeCells(0, 0, 2, 2);
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(0, 1));
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(1, 0));
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(1, 1));
+
+ QTextBlock block = table->cellAt(0, 0).firstCursorPosition().block();
+
+ QVERIFY(block.text() == "Blah Foo");
+ QVERIFY(block.next().text() == "Hah");
+ QVERIFY(block.next().next().text() == "Bar");
+
+ table = create4x4Table();
+
+ QTextCursor cursor = table->cellAt(3, 3).firstCursorPosition();
+ QTextTable *t2 = cursor.insertTable(2, 2);
+ t2->cellAt(0, 0).firstCursorPosition().insertText("Test");
+
+ table->mergeCells(2, 2, 2, 2);
+ cursor = table->cellAt(2, 2).firstCursorPosition();
+
+ QTextFrame *frame = cursor.currentFrame();
+
+ QTextFrame::iterator it = frame->begin();
+
+ // find the embedded table
+ while (it != frame->end() && !it.currentFrame())
+ ++it;
+
+ table = qobject_cast<QTextTable *>(it.currentFrame());
+
+ QVERIFY(table);
+
+ if (table) {
+ cursor = table->cellAt(0, 0).firstCursorPosition();
+
+ QVERIFY(cursor.block().text() == "Test");
+ }
+
+ table = create2x2Table();
+
+ table->mergeCells(0, 1, 2, 1);
+
+ QVERIFY(table->cellAt(0, 0) != table->cellAt(0, 1));
+ QVERIFY(table->cellAt(0, 1) == table->cellAt(1, 1));
+
+ // should do nothing
+ table->mergeCells(0, 0, 1, 2);
+
+ QVERIFY(table->cellAt(0, 0) != table->cellAt(0, 1));
+ QVERIFY(table->cellAt(0, 1) == table->cellAt(1, 1));
+}
+
+void tst_QTextTable::mergeAndInsert()
+{
+ QTextTable *table = cursor.insertTable(4,3);
+ table->mergeCells(0,1,3,2);
+ table->mergeCells(3,0,1,3);
+ //Don't crash !
+ table->insertColumns(1,2);
+ QCOMPARE(table->columns(), 5);
+}
+
+void tst_QTextTable::splitCells()
+{
+ QTextTable *table = create4x4Table();
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table->splitCell(1, 1, 1, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 2));
+
+ table->splitCell(1, 1, 1, 1);
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 2));
+
+
+ table = create4x4Table();
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table->splitCell(1, 1, 2, 1);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 2));
+
+ table->splitCell(1, 1, 1, 1);
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 2));
+
+
+ table = create4x4Table();
+ table->mergeCells(1, 1, 2, 2);
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) == table->cellAt(2, 2));
+
+ table->splitCell(1, 1, 1, 1);
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(1, 2));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 1));
+ QVERIFY(table->cellAt(1, 1) != table->cellAt(2, 2));
+
+ table = createTable(2, 5);
+ table->mergeCells(0, 0, 2, 1);
+ table->mergeCells(0, 1, 2, 1);
+ QVERIFY(table->cellAt(0, 0) == table->cellAt(1, 0));
+ QVERIFY(table->cellAt(0, 1) == table->cellAt(1, 1));
+ table->splitCell(0, 0, 1, 1);
+ QVERIFY(table->cellAt(0, 0) != table->cellAt(1, 0));
+ QVERIFY(table->cellAt(0, 1) == table->cellAt(1, 1));
+
+ table = createTable(2, 5);
+ table->mergeCells(0, 4, 2, 1);
+ QVERIFY(table->cellAt(0, 4) == table->cellAt(1, 4));
+
+ table->splitCell(0, 4, 1, 1);
+ QVERIFY(table->cellAt(0, 4) != table->cellAt(1, 4));
+}
+
+void tst_QTextTable::blocksForTableShouldHaveEmptyFormat()
+{
+ QTextBlockFormat fmt;
+ fmt.setProperty(QTextFormat::UserProperty, true);
+ cursor.insertBlock(fmt);
+ QVERIFY(cursor.blockFormat().hasProperty(QTextFormat::UserProperty));
+
+ QTextTable *table = cursor.insertTable(1, 1);
+ QVERIFY(!table->cellAt(0, 0).firstCursorPosition().blockFormat().hasProperty(QTextFormat::UserProperty));
+
+ int userPropCount = 0;
+ for (QTextBlock block = doc->begin();
+ block.isValid(); block = block.next()) {
+ if (block.blockFormat().hasProperty(QTextFormat::UserProperty))
+ userPropCount++;
+ }
+ QCOMPARE(userPropCount, 1);
+}
+
+void tst_QTextTable::removeTableByRemoveRows()
+{
+ QPointer<QTextTable> table1 = QTextCursor(cursor).insertTable(4, 4);
+ QPointer<QTextTable> table2 = QTextCursor(cursor).insertTable(4, 4);
+ QPointer<QTextTable> table3 = QTextCursor(cursor).insertTable(4, 4);
+
+ QVERIFY(table1);
+ QVERIFY(table2);
+ QVERIFY(table3);
+
+ table2->removeRows(1, 1);
+
+ QVERIFY(table1);
+ QVERIFY(table2);
+ QVERIFY(table3);
+
+ table2->removeRows(0, table2->rows());
+
+ QVERIFY(table1);
+ QVERIFY(!table2);
+ QVERIFY(table3);
+}
+
+void tst_QTextTable::removeTableByRemoveColumns()
+{
+ QPointer<QTextTable> table1 = QTextCursor(cursor).insertTable(4, 4);
+ QPointer<QTextTable> table2 = QTextCursor(cursor).insertTable(4, 4);
+ QPointer<QTextTable> table3 = QTextCursor(cursor).insertTable(4, 4);
+
+ QVERIFY(table1);
+ QVERIFY(table2);
+ QVERIFY(table3);
+
+ table2->removeColumns(1, 1);
+
+ QVERIFY(table1);
+ QVERIFY(table2);
+ QVERIFY(table3);
+
+ table2->removeColumns(0, table2->columns());
+
+ QVERIFY(table1);
+ QVERIFY(!table2);
+ QVERIFY(table3);
+}
+
+void tst_QTextTable::setCellFormat()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ QTextTableCell cell = table->cellAt(0, 0);
+ QTextCharFormat fmt;
+ fmt.setObjectIndex(23);
+ fmt.setBackground(Qt::blue);
+ fmt.setTableCellColumnSpan(25);
+ fmt.setTableCellRowSpan(42);
+ cell.setFormat(fmt);
+ QVERIFY(cell.format().background().color() == QColor(Qt::blue));
+ QCOMPARE(cell.format().tableCellColumnSpan(), 1);
+ QCOMPARE(cell.format().tableCellRowSpan(), 1);
+}
+
+void tst_QTextTable::removeRows1()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->removeRows(0, 1);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Third"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Fourth"));
+}
+
+void tst_QTextTable::removeRows2()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->removeRows(1, 1);
+ QCOMPARE(table->rows(), 1);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second"));
+}
+
+void tst_QTextTable::removeRows3()
+{
+ QTextTable *table = cursor.insertTable(3, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->cellAt(2, 0).firstCursorPosition().insertText("Fifth");
+ table->cellAt(2, 1).firstCursorPosition().insertText("Sixth");
+ table->removeRows(1, 1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Fifth"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Sixth"));
+}
+
+void tst_QTextTable::removeRows4()
+{
+ QTextTable *table = cursor.insertTable(4, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->cellAt(2, 0).firstCursorPosition().insertText("Fifth");
+ table->cellAt(2, 1).firstCursorPosition().insertText("Sixth");
+ table->cellAt(3, 0).firstCursorPosition().insertText("Seventh");
+ table->cellAt(3, 1).firstCursorPosition().insertText("Eighth");
+ table->removeRows(1, 2);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Seventh"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Eighth"));
+}
+
+void tst_QTextTable::removeRows5()
+{
+ QTextTable *table = cursor.insertTable(2,2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->insertRows(1,1);
+ table->mergeCells(1,0,1,2);
+ table->removeRows(1,1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Third"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Fourth"));
+}
+
+void tst_QTextTable::removeColumns1()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->removeColumns(0, 1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Second"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Fourth"));
+}
+
+void tst_QTextTable::removeColumns2()
+{
+ QTextTable *table = cursor.insertTable(2, 2);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fourth");
+ table->removeColumns(1, 1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 1);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Third"));
+}
+
+void tst_QTextTable::removeColumns3()
+{
+ QTextTable *table = cursor.insertTable(2, 3);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(0, 2).firstCursorPosition().insertText("Third");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Fourth");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Fifth");
+ table->cellAt(1, 2).firstCursorPosition().insertText("Sixth");
+ table->removeColumns(1, 1);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Third"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Fourth"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Sixth"));
+}
+
+void tst_QTextTable::removeColumns4()
+{
+ QTextTable *table = cursor.insertTable(2, 4);
+ table->cellAt(0, 0).firstCursorPosition().insertText("First");
+ table->cellAt(0, 1).firstCursorPosition().insertText("Second");
+ table->cellAt(0, 2).firstCursorPosition().insertText("Third");
+ table->cellAt(0, 3).firstCursorPosition().insertText("Fourth");
+ table->cellAt(1, 0).firstCursorPosition().insertText("Fifth");
+ table->cellAt(1, 1).firstCursorPosition().insertText("Sixth");
+ table->cellAt(1, 2).firstCursorPosition().insertText("Seventh");
+ table->cellAt(1, 3).firstCursorPosition().insertText("Eighth");
+ table->removeColumns(1, 2);
+ QCOMPARE(table->rows(), 2);
+ QCOMPARE(table->columns(), 2);
+ QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First"));
+ QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Fourth"));
+ QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Fifth"));
+ QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Eighth"));
+}
+
+void tst_QTextTable::removeColumns5()
+{
+ QTextTable *table = cursor.insertTable(4, 4);
+ QTextCursor tc (doc);
+ tc.setPosition(table->cellAt(2,0).firstPosition());
+ tc.setPosition(table->cellAt(3,1).firstPosition(), QTextCursor::KeepAnchor);
+ table->mergeCells(tc);
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 4);
+ QCOMPARE(table->cellAt(0, 0).firstPosition(), 1);
+ QCOMPARE(table->cellAt(0, 1).firstPosition(), 2);
+ QCOMPARE(table->cellAt(0, 2).firstPosition(), 3);
+ QCOMPARE(table->cellAt(0, 3).firstPosition(), 4);
+ QCOMPARE(table->cellAt(1, 0).firstPosition(), 5);
+ QCOMPARE(table->cellAt(1, 1).firstPosition(), 6);
+ QCOMPARE(table->cellAt(1, 2).firstPosition(), 7);
+ QCOMPARE(table->cellAt(1, 3).firstPosition(), 8);
+ QCOMPARE(table->cellAt(2, 0).firstPosition(), 9);
+ QCOMPARE(table->cellAt(2, 0).rowSpan(), 2);
+ QCOMPARE(table->cellAt(2, 0).columnSpan(), 2);
+ QCOMPARE(table->cellAt(2, 1).firstPosition(), 9);
+ QCOMPARE(table->cellAt(2, 2).firstPosition(), 10);
+ QCOMPARE(table->cellAt(2, 3).firstPosition(), 11);
+ QCOMPARE(table->cellAt(3, 0).firstPosition(), 9);
+ QCOMPARE(table->cellAt(3, 1).firstPosition(), 9);
+ QCOMPARE(table->cellAt(3, 2).firstPosition(), 12);
+ QCOMPARE(table->cellAt(3, 3).firstPosition(), 13);
+
+ table->removeColumns(1, 1);
+ QCOMPARE(table->rows(), 4);
+ QCOMPARE(table->columns(), 3);
+ QCOMPARE(table->cellAt(0, 0).firstPosition(), 1);
+ QCOMPARE(table->cellAt(0, 1).firstPosition(), 2);
+ QCOMPARE(table->cellAt(0, 2).firstPosition(), 3);
+ QCOMPARE(table->cellAt(1, 0).firstPosition(), 4);
+ QCOMPARE(table->cellAt(1, 1).firstPosition(), 5);
+ QCOMPARE(table->cellAt(1, 2).firstPosition(), 6);
+ QCOMPARE(table->cellAt(2, 0).firstPosition(), 7);
+ QCOMPARE(table->cellAt(2, 0).rowSpan(), 2);
+ QCOMPARE(table->cellAt(2, 0).columnSpan(), 1);
+ QCOMPARE(table->cellAt(2, 1).firstPosition(), 8);
+ QCOMPARE(table->cellAt(2, 2).firstPosition(), 9);
+ QCOMPARE(table->cellAt(3, 0).firstPosition(), 7);
+ QCOMPARE(table->cellAt(3, 1).firstPosition(), 10);
+ QCOMPARE(table->cellAt(3, 2).firstPosition(), 11);
+}
+
+void tst_QTextTable::removeColumnsInTableWithMergedRows()
+{
+ QTextTable *table = cursor.insertTable(3, 4);
+ table->mergeCells(0, 0, 1, 4);
+ QCOMPARE(table->rows(), 3);
+ QCOMPARE(table->columns(), 4);
+
+ table->removeColumns(0, table->columns() - 1);
+
+ QCOMPARE(table->rows(), 3);
+ QCOMPARE(table->columns(), 1);
+}
+
+void tst_QTextTable::QTBUG11282_insertBeforeMergedEnding_data()
+{
+ QTest::addColumn<int>("rows");
+ QTest::addColumn<int>("columns");
+ QTest::addColumn<QList<int> >("merge");
+ QTest::addColumn<QList<int> >("insert");
+
+ QTest::newRow("2x3, merge two, insert one") << 2 << 3 << (QList<int>() << 1 << 2 << 2)
+ << (QList<int>() << 1 << 1) ;
+ QTest::newRow("3x4, merge three, insert one") << 3 << 4 << (QList<int>() << 1 << 3 << 3)
+ << (QList<int>() << 1 << 1) ;
+ QTest::newRow("4x3, merge two, insert two") << 4 << 3 << (QList<int>() << 1 << 4 << 2)
+ << (QList<int>() << 1 << 2) ;
+ QTest::newRow("4x4, merge middle two, insert one") << 4 << 4 << (QList<int>() << 1 << 4 << 2)
+ << (QList<int>() << 1 << 1) ;
+}
+
+void tst_QTextTable::QTBUG11282_insertBeforeMergedEnding()
+{
+ QFETCH(int, rows);
+ QFETCH(int, columns);
+ QFETCH(QList<int>, merge);
+ QFETCH(QList<int>, insert);
+ QTextTable *table = cursor.insertTable(rows, columns);
+ QTextEdit *textEdit = new QTextEdit;
+ textEdit->setDocument(doc);
+ textEdit->show();
+ QTest::qWaitForWindowShown(textEdit);
+ table->mergeCells(0,merge.at(0), merge.at(1), merge.at(2));
+ //Don't crash !
+ table->insertColumns(insert.at(0), insert.at(1));
+ //Check that the final size is what we expected
+ QCOMPARE(table->rows(), rows);
+ QCOMPARE(table->columns(), columns + insert.at(1));
+ delete textEdit;
+}
+
+QTEST_MAIN(tst_QTextTable)
+#include "tst_qtexttable.moc"
diff --git a/tests/auto/gui/text/qzip/.gitignore b/tests/auto/gui/text/qzip/.gitignore
new file mode 100644
index 0000000000..2d7dfbe70c
--- /dev/null
+++ b/tests/auto/gui/text/qzip/.gitignore
@@ -0,0 +1 @@
+tst_qzip
diff --git a/tests/auto/gui/text/qzip/qzip.pro b/tests/auto/gui/text/qzip/qzip.pro
new file mode 100644
index 0000000000..7631a3e93a
--- /dev/null
+++ b/tests/auto/gui/text/qzip/qzip.pro
@@ -0,0 +1,12 @@
+load(qttest_p4)
+QT += gui-private
+SOURCES += tst_qzip.cpp
+
+wince*|symbian: {
+ addFiles.files = testdata
+ addFiles.path = .
+ DEPLOYMENT += addFiles
+ !symbian:DEFINES += SRCDIR=\\\".\\\"
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
diff --git a/tests/auto/gui/text/qzip/testdata/symlink.zip b/tests/auto/gui/text/qzip/testdata/symlink.zip
new file mode 100644
index 0000000000..027f96477a
--- /dev/null
+++ b/tests/auto/gui/text/qzip/testdata/symlink.zip
Binary files differ
diff --git a/tests/auto/gui/text/qzip/testdata/test.zip b/tests/auto/gui/text/qzip/testdata/test.zip
new file mode 100644
index 0000000000..a57ba4e2a9
--- /dev/null
+++ b/tests/auto/gui/text/qzip/testdata/test.zip
Binary files differ
diff --git a/tests/auto/gui/text/qzip/tst_qzip.cpp b/tests/auto/gui/text/qzip/tst_qzip.cpp
new file mode 100644
index 0000000000..0955ac069f
--- /dev/null
+++ b/tests/auto/gui/text/qzip/tst_qzip.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QDebug>
+#include <private/qzipwriter_p.h>
+#include <private/qzipreader_p.h>
+
+#ifdef Q_OS_SYMBIAN
+#define SRCDIR "."
+#endif
+
+class tst_QZip : public QObject
+{
+ Q_OBJECT
+public slots:
+ void init();
+ void cleanup();
+
+private slots:
+ void basicUnpack();
+ void symlinks();
+ void readTest();
+ void createArchive();
+};
+
+void tst_QZip::init()
+{
+}
+
+void tst_QZip::cleanup()
+{
+}
+
+void tst_QZip::basicUnpack()
+{
+ QZipReader zip(QString(SRCDIR) + "/testdata/test.zip", QIODevice::ReadOnly);
+ QList<QZipReader::FileInfo> files = zip.fileInfoList();
+ QCOMPARE(files.count(), 2);
+
+ QZipReader::FileInfo fi = files.at(0);
+ QVERIFY(fi.isValid());
+ QCOMPARE(fi.filePath, QString("test/"));
+ QCOMPARE(uint(fi.isDir), (uint) 1);
+ QCOMPARE(uint(fi.isFile), (uint) 0);
+ QCOMPARE(uint(fi.isSymLink), (uint) 0);
+
+ QCOMPARE(fi.permissions,QFile::Permissions( QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
+ | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser ));
+
+ QCOMPARE(fi.lastModified, QDateTime::fromString("2005.11.11 13:08:02", "yyyy.MM.dd HH:mm:ss"));
+
+ fi = files.at(1);
+ QVERIFY(fi.isValid());
+ QCOMPARE(fi.filePath, QString("test/test.txt"));
+ QCOMPARE(uint(fi.isDir), (uint) 0);
+ QCOMPARE(uint(fi.isFile), (uint) 1);
+ QCOMPARE(uint(fi.isSymLink), (uint) 0);
+
+ QVERIFY(fi.permissions == QFile::Permissions( QFile::ReadOwner | QFile::WriteOwner
+ | QFile::ReadUser | QFile::WriteUser ));
+
+ QCOMPARE(fi.lastModified, QDateTime::fromString("2005.11.11 13:08:02", "yyyy.MM.dd HH:mm:ss"));
+
+ QCOMPARE(zip.fileData("test/test.txt"), QByteArray("content\n"));
+
+ fi = files.at(-1);
+ QVERIFY(!fi.isValid());
+}
+
+void tst_QZip::symlinks()
+{
+ QZipReader zip(QString(SRCDIR) + "/testdata/symlink.zip", QIODevice::ReadOnly);
+ QList<QZipReader::FileInfo> files = zip.fileInfoList();
+ QCOMPARE(files.count(), 2);
+
+ QZipReader::FileInfo fi = files.at(0);
+ QVERIFY(fi.isValid());
+ QCOMPARE(fi.filePath, QString("symlink"));
+ QVERIFY(!fi.isDir);
+ QVERIFY(!fi.isFile);
+ QVERIFY(fi.isSymLink);
+
+ QCOMPARE(zip.fileData("symlink"), QByteArray("destination"));
+
+ fi = files.at(1);
+ QVERIFY(fi.isValid());
+ QCOMPARE(fi.filePath, QString("destination"));
+ QVERIFY(!fi.isDir);
+ QVERIFY(fi.isFile);
+ QVERIFY(!fi.isSymLink);
+}
+
+void tst_QZip::readTest()
+{
+ QZipReader zip("foobar.zip", QIODevice::ReadOnly); // non existing file.
+ QList<QZipReader::FileInfo> files = zip.fileInfoList();
+ QCOMPARE(files.count(), 0);
+ QByteArray b = zip.fileData("foobar");
+ QCOMPARE(b.size(), 0);
+}
+
+void tst_QZip::createArchive()
+{
+ QBuffer buffer;
+ QZipWriter zip(&buffer);
+ QByteArray fileContents("simple file contents\nline2\n");
+ zip.addFile("My Filename", fileContents);
+ zip.close();
+ QByteArray zipFile = buffer.buffer();
+
+ // QFile f("createArchiveTest.zip"); f.open(QIODevice::WriteOnly); f.write(zipFile); f.close();
+
+ QBuffer buffer2(&zipFile);
+ QZipReader zip2(&buffer2);
+ QList<QZipReader::FileInfo> files = zip2.fileInfoList();
+ QCOMPARE(files.count(), 1);
+ QZipReader::FileInfo file = files.at(0);
+ QCOMPARE(file.filePath, QString("My Filename"));
+ QCOMPARE(uint(file.isDir), (uint) 0);
+ QCOMPARE(uint(file.isFile), (uint) 1);
+ QCOMPARE(uint(file.isSymLink), (uint) 0);
+ QCOMPARE(file.permissions, QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser) );
+ QCOMPARE(file.size, (long long) 27);
+ QCOMPARE(zip2.fileData("My Filename"), fileContents);
+}
+
+QTEST_MAIN(tst_QZip)
+#include "tst_qzip.moc"
diff --git a/tests/auto/gui/text/text.pro b/tests/auto/gui/text/text.pro
new file mode 100644
index 0000000000..5055ab61a3
--- /dev/null
+++ b/tests/auto/gui/text/text.pro
@@ -0,0 +1,33 @@
+TEMPLATE=subdirs
+SUBDIRS=\
+ qabstracttextdocumentlayout \
+ qcssparser \
+ qfont \
+ qfontdatabase \
+ qfontmetrics \
+ qglyphrun \
+ qrawfont \
+ qstatictext \
+ qsyntaxhighlighter \
+ qtextblock \
+ qtextcursor \
+ qtextdocument \
+ qtextdocumentfragment \
+ qtextdocumentlayout \
+ qtextformat \
+ qtextlayout \
+ qtextlist \
+ qtextobject \
+ qtextpiecetable \
+ qtextscriptengine \
+ qtexttable \
+
+contains(QT_CONFIG, OdfWriter):SUBDIRS += qzip qtextodfwriter
+
+win32:SUBDIRS -= qtextpiecetable
+
+!contains(QT_CONFIG, private_tests): SUBDIRS -= \
+ qcssparser \
+ qstatictext \
+ qtextlayout \
+ qtextpiecetable \