tests/auto/qtextlayout/tst_qtextlayout.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qtextlayout/tst_qtextlayout.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1311 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*
+    !!!!!! 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 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();
+
+    // QTextLine stuff
+    void setNumColumnsWrapAtWordBoundaryOrAnywhere();
+    void setNumColumnsWordWrap();
+    void smallTextLengthNoWrap();
+    void smallTextLengthWordWrap();
+    void smallTextLengthWrapAtWordBoundaryOrAnywhere();
+    void testLineBreakingAllSpaces();
+
+
+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;
+}
+
+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::cursorMovementInsideSpaces()
+{
+    QTextLayout layout("ABC            DEF", testFont);
+
+    QCOMPARE(layout.previousCursorPosition(6, QTextLayout::SkipWords), 0);
+    QCOMPARE(layout.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;
+    tab.type = QTextOption::CenterTab;
+    tab.position = 150;
+    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;
+    tab.type = QTextOption::DelimiterTab;
+    tab.delimiter = QChar('.');
+    tab.position = 100;
+    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 = 200;
+    tabs.append(tab);
+
+    tab.position = 400;
+    tab.type = QTextOption::RightTab;
+    tabs.append(tab);
+
+    tab.position = 600;
+    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();
+    }
+
+}
+
+
+QTEST_MAIN(tst_QTextLayout)
+#include "tst_qtextlayout.moc"