1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 
43 #include <QtTest/QtTest>
44 
45 
46 #include <qtextdocument.h>
47 #include <qdebug.h>
48 
49 #include <qtextcursor.h>
50 #include <qtextdocumentfragment.h>
51 #include <qtextformat.h>
52 #include <qtextobject.h>
53 #include <qtexttable.h>
54 #include <qabstracttextdocumentlayout.h>
55 #include <qtextlist.h>
56 #include <qtextcodec.h>
57 #include <qurl.h>
58 #include <qpainter.h>
59 #include <qfontmetrics.h>
60 #include <qimage.h>
61 #include <qtextlayout.h>
62 #include <QDomDocument>
63 #include "common.h"
64 
65 
66 QT_FORWARD_DECLARE_CLASS(QTextDocument)
67 
68 //TESTED_CLASS=
69 //TESTED_FILES=
70 
71 class tst_QTextDocument : public QObject
72 {
73     Q_OBJECT
74 
75 public:
76     tst_QTextDocument();
77     virtual ~tst_QTextDocument();
78 
79 public slots:
80     void init();
81     void cleanup();
82 private slots:
83     void getSetCheck();
84     void isEmpty();
85     void find_data();
86     void find();
87     void find2();
88     void findWithRegExp_data();
89     void findWithRegExp();
90     void findMultiple();
91     void basicIsModifiedChecks();
92     void moreIsModified();
93     void isModified2();
94     void isModified3();
95     void isModified4();
96     void noundo_basicIsModifiedChecks();
97     void noundo_moreIsModified();
98     void noundo_isModified2();
99     void noundo_isModified3();
100     void mightBeRichText();
101     void mightBeRichText_data();
102 
103     void task240325();
104 
105     void stylesheetFont_data();
106     void stylesheetFont();
107 
108     void toHtml_data();
109     void toHtml();
110     void toHtml2();
111 
112     void setFragmentMarkersInHtmlExport();
113 
114     void toHtmlBodyBgColor();
115     void toHtmlRootFrameProperties();
116     void capitalizationHtmlInExport();
117     void wordspacingHtmlExport();
118 
119     void cursorPositionChanged();
120     void cursorPositionChangedOnSetText();
121 
122     void textFrameIterator();
123 
124     void codecForHtml();
125 
126     void markContentsDirty();
127 
128     void clonePreservesMetaInformation();
129     void clonePreservesPageSize();
130     void clonePreservesPageBreakPolicies();
131     void clonePreservesDefaultFont();
132     void clonePreservesRootFrameFormat();
133     void clonePreservesResources();
134     void clonePreservesUserStates();
135     void clonePreservesIndentWidth();
136     void blockCount();
137     void defaultStyleSheet();
138 
139     void resolvedFontInEmptyFormat();
140 
141     void defaultRootFrameMargin();
142 
143     void clearResources();
144 
145     void setPlainText();
146     void toPlainText();
147 
148     void deleteTextObjectsOnClear();
149 
150     void maximumBlockCount();
151     void adjustSize();
152     void initialUserData();
153 
154     void html_defaultFont();
155 
156     void blockCountChanged();
157 
158     void nonZeroDocumentLengthOnClear();
159 
160     void setTextPreservesUndoRedoEnabled();
161 
162     void firstLast();
163 
164     void backgroundImage_toHtml();
165     void backgroundImage_toHtml2();
166     void backgroundImage_clone();
167     void backgroundImage_copy();
168 
169     void documentCleanup();
170 
171     void characterAt();
172     void revisions();
173     void revisionWithUndoCompressionAndUndo();
174 
175     void testUndoCommandAdded();
176 
177     void testUndoBlocks();
178 
179     void receiveCursorPositionChangedAfterContentsChange();
180     void escape_data();
181     void escape();
182 
183     void copiedFontSize();
184     void QTBUG25778_pixelSizeFromHtml();
185 
186     void htmlExportImportBlockCount();
187 
188 private:
189     void backgroundImage_checkExpectedHtml(const QTextDocument &doc);
190 
191     QTextDocument *doc;
192     QTextCursor cursor;
193     QFont defaultFont;
194     QString htmlHead;
195     QString htmlTail;
196 };
197 
198 class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout
199 {
200 public:
MyAbstractTextDocumentLayout(QTextDocument * doc)201     MyAbstractTextDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc) {}
draw(QPainter *,const PaintContext &)202     void draw(QPainter *, const PaintContext &) {}
hitTest(const QPointF &,Qt::HitTestAccuracy) const203     int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; }
pageCount() const204     int pageCount() const { return 0; }
documentSize() const205     QSizeF documentSize() const { return QSizeF(); }
frameBoundingRect(QTextFrame *) const206     QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); }
blockBoundingRect(const QTextBlock &) const207     QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); }
documentChanged(int,int,int)208     void documentChanged(int, int, int) {}
209 };
210 
211 // Testing get/set functions
getSetCheck()212 void tst_QTextDocument::getSetCheck()
213 {
214     QTextDocument obj1;
215     // QAbstractTextDocumentLayout * QTextDocument::documentLayout()
216     // void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *)
217     QPointer<MyAbstractTextDocumentLayout> var1 = new MyAbstractTextDocumentLayout(0);
218     obj1.setDocumentLayout(var1);
219     QCOMPARE(static_cast<QAbstractTextDocumentLayout *>(var1), obj1.documentLayout());
220     obj1.setDocumentLayout((QAbstractTextDocumentLayout *)0);
221     QVERIFY(var1.isNull());
222     QVERIFY(obj1.documentLayout());
223 
224     // bool QTextDocument::useDesignMetrics()
225     // void QTextDocument::setUseDesignMetrics(bool)
226     obj1.setUseDesignMetrics(false);
227     QCOMPARE(false, obj1.useDesignMetrics());
228     obj1.setUseDesignMetrics(true);
229     QCOMPARE(true, obj1.useDesignMetrics());
230 }
231 
tst_QTextDocument()232 tst_QTextDocument::tst_QTextDocument()
233 {
234     QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
235     img.save("foo.png");
236 }
237 
~tst_QTextDocument()238 tst_QTextDocument::~tst_QTextDocument()
239 {
240     QFile::remove(QLatin1String("foo.png"));
241 }
242 
init()243 void tst_QTextDocument::init()
244 {
245     doc = new QTextDocument;
246     cursor = QTextCursor(doc);
247     defaultFont = QFont();
248 
249     htmlHead = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
250             "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
251             "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
252             "p, li { white-space: pre-wrap; }\n"
253             "</style></head>"
254             "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n");
255     htmlHead = htmlHead.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
256 
257     htmlTail = QString("</body></html>");
258 }
259 
cleanup()260 void tst_QTextDocument::cleanup()
261 {
262     cursor = QTextCursor();
263     delete doc;
264     doc = 0;
265 }
266 
isEmpty()267 void tst_QTextDocument::isEmpty()
268 {
269     QVERIFY(doc->isEmpty());
270 }
271 
find_data()272 void tst_QTextDocument::find_data()
273 {
274     QTest::addColumn<QString>("haystack");
275     QTest::addColumn<QString>("needle");
276     QTest::addColumn<int>("flags");
277     QTest::addColumn<int>("from");
278     QTest::addColumn<int>("anchor");
279     QTest::addColumn<int>("position");
280 
281     QTest::newRow("1") << "Hello World" << "World" << int(QTextDocument::FindCaseSensitively) << 0 << 6 << 11;
282 
283     QTest::newRow("2") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
284                     << "World" << int(QTextDocument::FindCaseSensitively) << 1 << 6 << 11;
285 
286     QTest::newRow("3") << QString::fromAscii("Hello") + QString(QChar::ParagraphSeparator) + QString::fromAscii("World")
287                     << "Hello" << int(QTextDocument::FindCaseSensitively | QTextDocument::FindBackward) << 10 << 0 << 5;
288     QTest::newRow("4wholewords") << QString::fromAscii("Hello Blah World")
289                               << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 6 << 10;
290     QTest::newRow("5wholewords") << QString::fromAscii("HelloBlahWorld")
291                               << "Blah" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
292     QTest::newRow("6wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
293                               << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 15 << 19;
294     QTest::newRow("7wholewords") << QString::fromAscii("HelloBlahWorld Blah Hah")
295                               << "Blah" << int(QTextDocument::FindWholeWords | QTextDocument::FindBackward) << 23 << 15 << 19;
296     QTest::newRow("8wholewords") << QString::fromAscii("Hello: World\n")
297                               << "orld" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1;
298 
299     QTest::newRow("across-paragraphs") << QString::fromAscii("First Parag\nSecond Parag with a lot more text")
300                                        << "Parag" << int(QTextDocument::FindBackward)
301                                        << 15 << 6 << 11;
302 
303     QTest::newRow("nbsp") << "Hello" + QString(QChar(QChar::Nbsp)) +"World" << " " << int(QTextDocument::FindCaseSensitively) << 0 << 5 << 6;
304 }
305 
find()306 void tst_QTextDocument::find()
307 {
308     QFETCH(QString, haystack);
309     QFETCH(QString, needle);
310     QFETCH(int, flags);
311     QFETCH(int, from);
312     QFETCH(int, anchor);
313     QFETCH(int, position);
314 
315     cursor.insertText(haystack);
316     cursor = doc->find(needle, from, QTextDocument::FindFlags(flags));
317 
318     if (anchor != -1) {
319         QCOMPARE(cursor.anchor(), anchor);
320         QCOMPARE(cursor.position(), position);
321     } else {
322         QVERIFY(cursor.isNull());
323     }
324 
325     //search using a regular expression
326     QRegExp expr(needle);
327     expr.setPatternSyntax(QRegExp::FixedString);
328     QTextDocument::FindFlags flg(flags);
329     expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
330     cursor = doc->find(expr, from, flg);
331 
332     if (anchor != -1) {
333         QCOMPARE(cursor.anchor(), anchor);
334         QCOMPARE(cursor.position(), position);
335     } else {
336         QVERIFY(cursor.isNull());
337     }
338 }
339 
findWithRegExp_data()340 void tst_QTextDocument::findWithRegExp_data()
341 {
342     QTest::addColumn<QString>("haystack");
343     QTest::addColumn<QString>("needle");
344     QTest::addColumn<int>("flags");
345     QTest::addColumn<int>("from");
346     QTest::addColumn<int>("anchor");
347     QTest::addColumn<int>("position");
348 
349     // match integers 0 to 99
350     QTest::newRow("1") << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2;
351     // match ampersands but not &amp;
352     QTest::newRow("2") << "His &amp; hers & theirs" << "&(?!amp;)"<< int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16;
353     //backward search
354     QTest::newRow("3") << QString::fromAscii("HelloBlahWorld Blah Hah")
355                               << "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9;
356 
357 }
358 
findWithRegExp()359 void tst_QTextDocument::findWithRegExp()
360 {
361     QFETCH(QString, haystack);
362     QFETCH(QString, needle);
363     QFETCH(int, flags);
364     QFETCH(int, from);
365     QFETCH(int, anchor);
366     QFETCH(int, position);
367 
368     cursor.insertText(haystack);
369     //search using a regular expression
370     QRegExp expr(needle);
371     QTextDocument::FindFlags flg(flags);
372     expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive);
373     cursor = doc->find(expr, from, flg);
374 
375     if (anchor != -1) {
376         QCOMPARE(cursor.anchor(), anchor);
377         QCOMPARE(cursor.position(), position);
378     } else {
379         QVERIFY(cursor.isNull());
380     }
381 }
382 
find2()383 void tst_QTextDocument::find2()
384 {
385     doc->setPlainText("aaa");
386     cursor.movePosition(QTextCursor::Start);
387     cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
388     QTextCursor hit = doc->find("a", cursor);
389     QCOMPARE(hit.position(), 2);
390     QCOMPARE(hit.anchor(), 1);
391 }
392 
findMultiple()393 void tst_QTextDocument::findMultiple()
394 {
395     const QString text("foo bar baz foo bar baz");
396     doc->setPlainText(text);
397 
398     cursor.movePosition(QTextCursor::Start);
399     cursor = doc->find("bar", cursor);
400     QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
401     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
402     cursor = doc->find("bar", cursor);
403     QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
404     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
405 
406     cursor.movePosition(QTextCursor::End);
407     cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
408     QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
409     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
410     cursor = doc->find("bar", cursor, QTextDocument::FindBackward);
411     QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
412     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
413 
414 
415     QRegExp expr("bar");
416     expr.setPatternSyntax(QRegExp::FixedString);
417 
418     cursor.movePosition(QTextCursor::End);
419     cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
420     QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
421     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
422     cursor = doc->find(expr, cursor, QTextDocument::FindBackward);
423     QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
424     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
425 
426     cursor.movePosition(QTextCursor::Start);
427     cursor = doc->find(expr, cursor);
428     QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
429     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
430     cursor = doc->find(expr, cursor);
431     QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
432     QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
433 }
434 
basicIsModifiedChecks()435 void tst_QTextDocument::basicIsModifiedChecks()
436 {
437     QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
438 
439     QVERIFY(!doc->isModified());
440     cursor.insertText("Hello World");
441     QVERIFY(doc->isModified());
442     QCOMPARE(spy.count(), 1);
443     QVERIFY(spy.takeFirst().at(0).toBool());
444 
445     doc->undo();
446     QVERIFY(!doc->isModified());
447     QCOMPARE(spy.count(), 1);
448     QVERIFY(!spy.takeFirst().at(0).toBool());
449 
450     doc->redo();
451     QVERIFY(doc->isModified());
452     QCOMPARE(spy.count(), 1);
453     QVERIFY(spy.takeFirst().at(0).toBool());
454 }
455 
moreIsModified()456 void tst_QTextDocument::moreIsModified()
457 {
458     QVERIFY(!doc->isModified());
459 
460     cursor.insertText("Hello");
461     QVERIFY(doc->isModified());
462 
463     doc->undo();
464     QVERIFY(!doc->isModified());
465 
466     cursor.insertText("Hello");
467 
468     doc->undo();
469     QVERIFY(!doc->isModified());
470 }
471 
isModified2()472 void tst_QTextDocument::isModified2()
473 {
474     // reported on qt4-preview-feedback
475     QVERIFY(!doc->isModified());
476 
477     cursor.insertText("Hello");
478     QVERIFY(doc->isModified());
479 
480     doc->setModified(false);
481     QVERIFY(!doc->isModified());
482 
483     cursor.insertText("Hello");
484     QVERIFY(doc->isModified());
485 }
486 
isModified3()487 void tst_QTextDocument::isModified3()
488 {
489     QVERIFY(!doc->isModified());
490 
491     doc->setUndoRedoEnabled(false);
492     doc->setUndoRedoEnabled(true);
493 
494     cursor.insertText("Hello");
495 
496     QVERIFY(doc->isModified());
497     doc->undo();
498     QVERIFY(!doc->isModified());
499 }
500 
isModified4()501 void tst_QTextDocument::isModified4()
502 {
503     QVERIFY(!doc->isModified());
504 
505     cursor.insertText("Hello");
506     cursor.insertText("World");
507 
508     doc->setModified(false);
509 
510     QVERIFY(!doc->isModified());
511 
512     cursor.insertText("Again");
513     QVERIFY(doc->isModified());
514 
515     doc->undo();
516     QVERIFY(!doc->isModified());
517     doc->undo();
518     QVERIFY(doc->isModified());
519 
520     doc->redo();
521     QVERIFY(!doc->isModified());
522     doc->redo();
523     QVERIFY(doc->isModified());
524 
525     doc->undo();
526     QVERIFY(!doc->isModified());
527     doc->undo();
528     QVERIFY(doc->isModified());
529 
530     //task 197769
531     cursor.insertText("Hello");
532     QVERIFY(doc->isModified());
533 }
534 
noundo_basicIsModifiedChecks()535 void tst_QTextDocument::noundo_basicIsModifiedChecks()
536 {
537     doc->setUndoRedoEnabled(false);
538     QSignalSpy spy(doc, SIGNAL(modificationChanged(bool)));
539 
540     QVERIFY(!doc->isModified());
541     cursor.insertText("Hello World");
542     QVERIFY(doc->isModified());
543     QCOMPARE(spy.count(), 1);
544     QVERIFY(spy.takeFirst().at(0).toBool());
545 
546     doc->undo();
547     QVERIFY(doc->isModified());
548     QCOMPARE(spy.count(), 0);
549 
550     doc->redo();
551     QVERIFY(doc->isModified());
552     QCOMPARE(spy.count(), 0);
553 }
554 
task240325()555 void tst_QTextDocument::task240325()
556 {
557     doc->setHtml("<html><img width=\"100\" height=\"100\" align=\"right\"/>Foobar Foobar Foobar Foobar</html>");
558 
559     QImage img(1000, 7000, QImage::Format_ARGB32_Premultiplied);
560     QPainter p(&img);
561     QFontMetrics fm(p.font());
562 
563     // Set page size to contain image and one "Foobar"
564     doc->setPageSize(QSize(100 + fm.width("Foobar")*2, 1000));
565 
566     // Force layout
567     doc->drawContents(&p);
568 
569     QCOMPARE(doc->blockCount(), 1);
570     for (QTextBlock block = doc->begin() ; block!=doc->end() ; block = block.next()) {
571         QTextLayout *layout = block.layout();
572         QCOMPARE(layout->lineCount(), 4);
573         for (int lineIdx=0;lineIdx<layout->lineCount();++lineIdx) {
574             QTextLine line = layout->lineAt(lineIdx);
575 
576             QString text = block.text().mid(line.textStart(), line.textLength()).trimmed();
577 
578             // Remove start token
579             if (lineIdx == 0)
580                 text = text.mid(1);
581 
582             QCOMPARE(text, QString::fromLatin1("Foobar"));
583         }
584     }
585 }
586 
stylesheetFont_data()587 void tst_QTextDocument::stylesheetFont_data()
588 {
589     QTest::addColumn<QString>("stylesheet");
590     QTest::addColumn<QFont>("font");
591 
592     {
593         QFont font;
594         font.setBold(true);
595         font.setPixelSize(64);
596 
597         QTest::newRow("Regular font specification")
598                  << "font-size: 64px; font-weight: bold;"
599                  << font;
600     }
601 
602 
603     {
604         QFont font;
605         font.setBold(true);
606         font.setPixelSize(64);
607 
608         QTest::newRow("Shorthand font specification")
609                 << "font: normal bold 64px Arial;"
610                 << font;
611     }
612 
613 }
614 
stylesheetFont()615 void tst_QTextDocument::stylesheetFont()
616 {
617     QFETCH(QString, stylesheet);
618     QFETCH(QFont, font);
619 
620     QString html = QString::fromLatin1("<html>"
621                                        "<body>"
622                                        "<div style=\"%1\" >"
623                                        "Foobar"
624                                        "</div>"
625                                        "</body>"
626                                        "</html>").arg(stylesheet);
627 
628     qDebug() << html;
629     doc->setHtml(html);
630     QCOMPARE(doc->blockCount(), 1);
631 
632     // First and only block
633     QTextBlock block = doc->firstBlock();
634 
635     QString text = block.text();
636     QCOMPARE(text, QString::fromLatin1("Foobar"));
637 
638     QFont actualFont = block.charFormat().font();
639 
640     QCOMPARE(actualFont.bold(), font.bold());
641     QCOMPARE(actualFont.pixelSize(), font.pixelSize());
642 }
643 
noundo_moreIsModified()644 void tst_QTextDocument::noundo_moreIsModified()
645 {
646     doc->setUndoRedoEnabled(false);
647     QVERIFY(!doc->isModified());
648 
649     cursor.insertText("Hello");
650     QVERIFY(doc->isModified());
651 
652     doc->undo();
653     QVERIFY(doc->isModified());
654 
655     cursor.insertText("Hello");
656 
657     doc->undo();
658     QVERIFY(doc->isModified());
659 }
660 
noundo_isModified2()661 void tst_QTextDocument::noundo_isModified2()
662 {
663     // reported on qt4-preview-feedback
664     QVERIFY(!doc->isModified());
665 
666     cursor.insertText("Hello");
667     QVERIFY(doc->isModified());
668 
669     doc->setModified(false);
670     QVERIFY(!doc->isModified());
671 
672     cursor.insertText("Hello");
673     QVERIFY(doc->isModified());
674 }
675 
noundo_isModified3()676 void tst_QTextDocument::noundo_isModified3()
677 {
678     doc->setUndoRedoEnabled(false);
679     QVERIFY(!doc->isModified());
680 
681     cursor.insertText("Hello");
682 
683     QVERIFY(doc->isModified());
684     doc->undo();
685     QVERIFY(doc->isModified());
686 }
687 
mightBeRichText_data()688 void tst_QTextDocument::mightBeRichText_data()
689 {
690     const char qtDocuHeader[] = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
691                                 "<!DOCTYPE html\n"
692                                 "    PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
693                                 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">";
694     QVERIFY(Qt::mightBeRichText(QString::fromLatin1(qtDocuHeader)));
695     QTest::addColumn<QString>("input");
696     QTest::addColumn<bool>("result");
697 
698     QTest::newRow("documentation-header") << QString("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
699                                                      "<!DOCTYPE html\n"
700                                                      "    PUBLIC ""-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
701                                                      "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">")
702                                           << true;
703     QTest::newRow("br-nospace") << QString("Test <br/> new line") << true;
704     QTest::newRow("br-space") << QString("Test <br /> new line") << true;
705     QTest::newRow("br-invalidspace") << QString("Test <br/ > new line") << false;
706     QTest::newRow("invalid closing tag") << QString("Test <br/ line") << false;
707 }
708 
mightBeRichText()709 void tst_QTextDocument::mightBeRichText()
710 {
711     QFETCH(QString, input);
712     QFETCH(bool, result);
713     QVERIFY(result == Qt::mightBeRichText(input));
714 }
715 
Q_DECLARE_METATYPE(QTextDocumentFragment)716 Q_DECLARE_METATYPE(QTextDocumentFragment)
717 
718 #define CREATE_DOC_AND_CURSOR() \
719         QTextDocument doc; \
720         doc.setDefaultFont(defaultFont); \
721         QTextCursor cursor(&doc);
722 
723 void tst_QTextDocument::toHtml_data()
724 {
725     QTest::addColumn<QTextDocumentFragment>("input");
726     QTest::addColumn<QString>("expectedOutput");
727 
728     {
729         CREATE_DOC_AND_CURSOR();
730 
731         cursor.insertText("Blah");
732 
733         QTest::newRow("simple") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>Blah</p>");
734     }
735 
736     {
737         CREATE_DOC_AND_CURSOR();
738 
739         cursor.insertText("&<>");
740 
741         QTest::newRow("entities") << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>&amp;&lt;&gt;</p>");
742     }
743 
744     {
745         CREATE_DOC_AND_CURSOR();
746 
747         QTextCharFormat fmt;
748         fmt.setFontFamily("Times");
749         cursor.insertText("Blah", fmt);
750 
751         QTest::newRow("font-family") << QTextDocumentFragment(&doc)
752                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Times';\">Blah</span></p>");
753     }
754 
755     {
756         CREATE_DOC_AND_CURSOR();
757 
758         QTextCharFormat fmt;
759         fmt.setFontFamily("Foo's Family");
760         cursor.insertText("Blah", fmt);
761 
762         QTest::newRow("font-family-with-quotes1") << QTextDocumentFragment(&doc)
763                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:&quot;Foo's Family&quot;;\">Blah</span></p>");
764     }
765 
766     {
767         CREATE_DOC_AND_CURSOR();
768 
769         QTextCharFormat fmt;
770         fmt.setFontFamily("Foo\"s Family");
771         cursor.insertText("Blah", fmt);
772 
773         QTest::newRow("font-family-with-quotes2") << QTextDocumentFragment(&doc)
774                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Foo&quot;s Family';\">Blah</span></p>");
775     }
776 
777     {
778         CREATE_DOC_AND_CURSOR();
779 
780         QTextBlockFormat fmt;
781         fmt.setNonBreakableLines(true);
782         cursor.insertBlock(fmt);
783         cursor.insertText("Blah");
784 
785         QTest::newRow("pre") << QTextDocumentFragment(&doc)
786                           <<
787                              QString("EMPTYBLOCK") +
788                              QString("<pre DEFAULTBLOCKSTYLE>Blah</pre>");
789     }
790 
791     {
792         CREATE_DOC_AND_CURSOR();
793 
794         QTextCharFormat fmt;
795         fmt.setFontPointSize(40);
796         cursor.insertText("Blah", fmt);
797 
798         QTest::newRow("font-size") << QTextDocumentFragment(&doc)
799                                 << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:40pt;\">Blah</span></p>");
800     }
801 
802     {
803         CREATE_DOC_AND_CURSOR();
804 
805         QTextCharFormat fmt;
806         fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
807         cursor.insertText("Blah", fmt);
808 
809         QTest::newRow("logical-font-size") << QTextDocumentFragment(&doc)
810                                         << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:x-large;\">Blah</span></p>");
811     }
812 
813     {
814         CREATE_DOC_AND_CURSOR();
815 
816         cursor.insertText("Foo");
817 
818         QTextCharFormat fmt;
819         fmt.setFontPointSize(40);
820         cursor.insertBlock(QTextBlockFormat(), fmt);
821 
822         fmt.clearProperty(QTextFormat::FontPointSize);
823         cursor.insertText("Blub", fmt);
824 
825         QTest::newRow("no-font-size") << QTextDocumentFragment(&doc)
826                                    << 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>");
827     }
828 
829     {
830         CREATE_DOC_AND_CURSOR();
831 
832         QTextBlockFormat fmt;
833         fmt.setLayoutDirection(Qt::RightToLeft);
834         cursor.insertBlock(fmt);
835         cursor.insertText("Blah");
836 
837         QTest::newRow("rtl") << QTextDocumentFragment(&doc)
838                           <<
839                              QString("EMPTYBLOCK") +
840                              QString("<p dir='rtl' DEFAULTBLOCKSTYLE>Blah</p>");
841     }
842 
843     {
844         CREATE_DOC_AND_CURSOR();
845 
846         QTextBlockFormat fmt;
847         fmt.setAlignment(Qt::AlignJustify);
848         cursor.insertBlock(fmt);
849         cursor.insertText("Blah");
850 
851         QTest::newRow("blockalign") << QTextDocumentFragment(&doc)
852                                  <<
853                                     QString("EMPTYBLOCK") +
854                                     QString("<p align=\"justify\" DEFAULTBLOCKSTYLE>Blah</p>");
855     }
856 
857     {
858         CREATE_DOC_AND_CURSOR();
859 
860         QTextBlockFormat fmt;
861         fmt.setAlignment(Qt::AlignCenter);
862         cursor.insertBlock(fmt);
863         cursor.insertText("Blah");
864 
865         QTest::newRow("blockalign2") << QTextDocumentFragment(&doc)
866                                   <<
867                                     QString("EMPTYBLOCK") +
868                                     QString("<p align=\"center\" DEFAULTBLOCKSTYLE>Blah</p>");
869     }
870 
871     {
872         CREATE_DOC_AND_CURSOR();
873 
874         QTextBlockFormat fmt;
875         fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
876         cursor.insertBlock(fmt);
877         cursor.insertText("Blah");
878 
879         QTest::newRow("blockalign3") << QTextDocumentFragment(&doc)
880                                   <<
881                                     QString("EMPTYBLOCK") +
882                                     QString("<p align=\"right\" DEFAULTBLOCKSTYLE>Blah</p>");
883     }
884 
885     {
886         CREATE_DOC_AND_CURSOR();
887 
888         QTextBlockFormat fmt;
889         fmt.setBackground(QColor("#0000ff"));
890         cursor.insertBlock(fmt);
891         cursor.insertText("Blah");
892 
893         QTest::newRow("bgcolor") << QTextDocumentFragment(&doc)
894                                  << QString("EMPTYBLOCK") +
895                                     QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\">Blah</p>");
896     }
897 
898     {
899         CREATE_DOC_AND_CURSOR();
900 
901         QTextCharFormat fmt;
902         fmt.setFontWeight(40);
903         cursor.insertText("Blah", fmt);
904 
905         QTest::newRow("font-weight") << QTextDocumentFragment(&doc)
906                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-weight:320;\">Blah</span></p>");
907     }
908 
909     {
910         CREATE_DOC_AND_CURSOR();
911 
912         QTextCharFormat fmt;
913         fmt.setFontItalic(true);
914         cursor.insertText("Blah", fmt);
915 
916         QTest::newRow("font-italic") << QTextDocumentFragment(&doc)
917                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-style:italic;\">Blah</span></p>");
918     }
919 
920     {
921         CREATE_DOC_AND_CURSOR();
922 
923         QTextCharFormat fmt;
924         fmt.setFontUnderline(true);
925         fmt.setFontOverline(false);
926         cursor.insertText("Blah", fmt);
927 
928         QTest::newRow("text-decoration-1") << QTextDocumentFragment(&doc)
929                                   << QString("<p DEFAULTBLOCKSTYLE><span style=\" text-decoration: underline;\">Blah</span></p>");
930     }
931 
932     {
933         CREATE_DOC_AND_CURSOR();
934 
935         QTextCharFormat fmt;
936         fmt.setForeground(QColor("#00ff00"));
937         cursor.insertText("Blah", fmt);
938 
939         QTest::newRow("color") << QTextDocumentFragment(&doc)
940                             << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:#00ff00;\">Blah</span></p>");
941     }
942 
943     {
944         CREATE_DOC_AND_CURSOR();
945 
946         QTextCharFormat fmt;
947         fmt.setBackground(QColor("#00ff00"));
948         cursor.insertText("Blah", fmt);
949 
950         QTest::newRow("span-bgcolor") << QTextDocumentFragment(&doc)
951                             << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>");
952     }
953 
954     {
955         CREATE_DOC_AND_CURSOR();
956 
957         QTextCharFormat fmt;
958         fmt.setVerticalAlignment(QTextCharFormat::AlignSubScript);
959         cursor.insertText("Blah", fmt);
960 
961         QTest::newRow("valign-sub") << QTextDocumentFragment(&doc)
962                                  << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:sub;\">Blah</span></p>");
963 
964     }
965 
966     {
967         CREATE_DOC_AND_CURSOR();
968 
969         QTextCharFormat fmt;
970         fmt.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
971         cursor.insertText("Blah", fmt);
972 
973         QTest::newRow("valign-super") << QTextDocumentFragment(&doc)
974                                    << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:super;\">Blah</span></p>");
975 
976     }
977 
978     {
979         CREATE_DOC_AND_CURSOR();
980 
981         QTextCharFormat fmt;
982         fmt.setAnchor(true);
983         fmt.setAnchorName("blub");
984         cursor.insertText("Blah", fmt);
985 
986         QTest::newRow("named anchor") << QTextDocumentFragment(&doc)
987                                    << QString("<p DEFAULTBLOCKSTYLE><a name=\"blub\"></a>Blah</p>");
988     }
989 
990     {
991         CREATE_DOC_AND_CURSOR();
992 
993         QTextCharFormat fmt;
994         fmt.setAnchor(true);
995         fmt.setAnchorHref("http://www.kde.org/");
996         cursor.insertText("Blah", fmt);
997 
998         QTest::newRow("href anchor") << QTextDocumentFragment(&doc)
999                                   << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/\">Blah</a></p>");
1000     }
1001 
1002     {
1003         CREATE_DOC_AND_CURSOR();
1004 
1005         QTextCharFormat fmt;
1006         fmt.setAnchor(true);
1007         fmt.setAnchorHref("http://www.kde.org/?a=1&b=2");
1008         cursor.insertText("Blah", fmt);
1009 
1010         QTest::newRow("href anchor with &") << QTextDocumentFragment(&doc)
1011                                   << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a=1&amp;b=2\">Blah</a></p>");
1012     }
1013 
1014     {
1015         CREATE_DOC_AND_CURSOR();
1016 
1017         QTextCharFormat fmt;
1018         fmt.setAnchor(true);
1019         fmt.setAnchorHref("http://www.kde.org/?a='&b=\"");
1020         cursor.insertText("Blah", fmt);
1021 
1022         QTest::newRow("href anchor with ' and \"") << QTextDocumentFragment(&doc)
1023                                   << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a='&amp;b=&quot;\">Blah</a></p>");
1024     }
1025 
1026     {
1027         CREATE_DOC_AND_CURSOR();
1028 
1029         cursor.insertTable(2, 2);
1030 
1031         QTest::newRow("simpletable") << QTextDocumentFragment(&doc)
1032                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1033                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1034                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1035                                              "</table>");
1036     }
1037 
1038     {
1039         CREATE_DOC_AND_CURSOR();
1040 
1041         QTextTable *table = cursor.insertTable(1, 4);
1042         table->mergeCells(0, 0, 1, 2);
1043         table->mergeCells(0, 2, 1, 2);
1044 
1045         QTest::newRow("tablespans") << QTextDocumentFragment(&doc)
1046                                  << QString("<table border=\"1\" cellspacing=\"2\">"
1047                                              "\n<tr>\n<td colspan=\"2\"></td>\n<td colspan=\"2\"></td></tr>"
1048                                              "</table>");
1049     }
1050 
1051     {
1052         CREATE_DOC_AND_CURSOR();
1053 
1054         QTextTableFormat fmt;
1055         fmt.setBorder(1);
1056         fmt.setCellSpacing(3);
1057         fmt.setCellPadding(3);
1058         fmt.setBackground(QColor("#ff00ff"));
1059         fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
1060         fmt.setAlignment(Qt::AlignHCenter);
1061         fmt.setPosition(QTextFrameFormat::FloatRight);
1062         cursor.insertTable(2, 2, fmt);
1063 
1064         QTest::newRow("tableattrs") << QTextDocumentFragment(&doc)
1065                                   << QString("<table border=\"1\" style=\" float: right;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">"
1066                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1067                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1068                                              "</table>");
1069     }
1070 
1071     {
1072         CREATE_DOC_AND_CURSOR();
1073 
1074         QTextTableFormat fmt;
1075         fmt.setBorder(1);
1076         fmt.setCellSpacing(3);
1077         fmt.setCellPadding(3);
1078         fmt.setBackground(QColor("#ff00ff"));
1079         fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50));
1080         fmt.setAlignment(Qt::AlignHCenter);
1081         fmt.setPosition(QTextFrameFormat::FloatRight);
1082         fmt.setLeftMargin(25);
1083         fmt.setBottomMargin(35);
1084         cursor.insertTable(2, 2, fmt);
1085 
1086         QTest::newRow("tableattrs2") << QTextDocumentFragment(&doc)
1087                                   << 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\">"
1088                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1089                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1090                                              "</table>");
1091     }
1092 
1093     {
1094         CREATE_DOC_AND_CURSOR();
1095 
1096         QTextTableFormat fmt;
1097         fmt.setHeaderRowCount(2);
1098         cursor.insertTable(4, 2, fmt);
1099 
1100         QTest::newRow("tableheader") << QTextDocumentFragment(&doc)
1101                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1102                                              "<thead>\n<tr>\n<td></td>\n<td></td></tr>"
1103                                              "\n<tr>\n<td></td>\n<td></td></tr></thead>"
1104                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1105                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1106                                              "</table>");
1107     }
1108 
1109     {
1110         CREATE_DOC_AND_CURSOR();
1111 
1112         QTextTable *table = cursor.insertTable(2, 2);
1113         QTextTable *subTable = table->cellAt(0, 1).firstCursorPosition().insertTable(1, 1);
1114         subTable->cellAt(0, 0).firstCursorPosition().insertText("Hey");
1115 
1116         QTest::newRow("nestedtable") << QTextDocumentFragment(&doc)
1117                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1118                                              "\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>"
1119                                              "\n<tr>\n<td></td>\n<td></td></tr>"
1120                                              "</table>");
1121     }
1122 
1123     {
1124         CREATE_DOC_AND_CURSOR();
1125 
1126         QTextTableFormat fmt;
1127         QVector<QTextLength> widths;
1128         widths.append(QTextLength());
1129         widths.append(QTextLength(QTextLength::PercentageLength, 30));
1130         widths.append(QTextLength(QTextLength::FixedLength, 40));
1131         fmt.setColumnWidthConstraints(widths);
1132         cursor.insertTable(1, 3, fmt);
1133 
1134         QTest::newRow("colwidths") << QTextDocumentFragment(&doc)
1135                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1136                                              "\n<tr>\n<td></td>\n<td width=\"30%\"></td>\n<td width=\"40\"></td></tr>"
1137                                              "</table>");
1138     }
1139 
1140     // ### rowspan/colspan tests, once texttable api for that is back again
1141     //
1142     {
1143         CREATE_DOC_AND_CURSOR();
1144 
1145         QTextTable *table = cursor.insertTable(1, 1);
1146         QTextCursor cellCurs = table->cellAt(0, 0).firstCursorPosition();
1147         QTextCharFormat fmt;
1148         fmt.setBackground(QColor("#ffffff"));
1149         cellCurs.mergeBlockCharFormat(fmt);
1150 
1151         QTest::newRow("cellproperties") << QTextDocumentFragment(&doc)
1152                                      << QString("<table border=\"1\" cellspacing=\"2\">"
1153                                                 "\n<tr>\n<td bgcolor=\"#ffffff\"></td></tr>"
1154                                                 "</table>");
1155     }
1156 
1157     {
1158         CREATE_DOC_AND_CURSOR();
1159 
1160         // ### fixme: use programmatic api as soon as we can create floats through it
1161         const char html[] = "<html><body>Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</body></html>";
1162 
1163         QTest::newRow("image") << QTextDocumentFragment::fromHtml(QString::fromLatin1(html))
1164                             << 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>");
1165     }
1166 
1167     {
1168         CREATE_DOC_AND_CURSOR();
1169 
1170         QTextImageFormat fmt;
1171         fmt.setName("foo");
1172         fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1173         cursor.insertImage(fmt);
1174 
1175         QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
1176                             << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: middle;\" /></p>");
1177     }
1178 
1179     {
1180         CREATE_DOC_AND_CURSOR();
1181 
1182         QTextImageFormat fmt;
1183         fmt.setName("foo");
1184         fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
1185         cursor.insertImage(fmt);
1186 
1187         QTest::newRow("image-malign") << QTextDocumentFragment(&doc)
1188                             << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: top;\" /></p>");
1189     }
1190 
1191     {
1192         CREATE_DOC_AND_CURSOR();
1193 
1194         QTextImageFormat fmt;
1195         fmt.setName("foo");
1196         cursor.insertImage(fmt);
1197         cursor.insertImage(fmt);
1198 
1199         QTest::newRow("2images") << QTextDocumentFragment(&doc)
1200                             << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" /><img src=\"foo\" /></p>");
1201     }
1202 
1203     {
1204         CREATE_DOC_AND_CURSOR();
1205 
1206         QString txt = QLatin1String("Blah");
1207         txt += QChar::LineSeparator;
1208         txt += QLatin1String("Bar");
1209         cursor.insertText(txt);
1210 
1211         QTest::newRow("linebreaks") << QTextDocumentFragment(&doc)
1212                                  << QString("<p DEFAULTBLOCKSTYLE>Blah<br />Bar</p>");
1213     }
1214 
1215     {
1216         CREATE_DOC_AND_CURSOR();
1217 
1218         QTextBlockFormat fmt;
1219         fmt.setTopMargin(10);
1220         fmt.setBottomMargin(20);
1221         fmt.setLeftMargin(30);
1222         fmt.setRightMargin(40);
1223         cursor.insertBlock(fmt);
1224         cursor.insertText("Blah");
1225 
1226         QTest::newRow("blockmargins") << QTextDocumentFragment(&doc)
1227                           <<
1228                              QString("EMPTYBLOCK") +
1229                              QString("<p style=\" margin-top:10px; margin-bottom:20px; margin-left:30px; margin-right:40px; -qt-block-indent:0; text-indent:0px;\">Blah</p>");
1230     }
1231 
1232     {
1233         CREATE_DOC_AND_CURSOR();
1234 
1235         QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
1236         cursor.insertText("Blubb");
1237         cursor.insertBlock();
1238         cursor.insertText("Blah");
1239         QCOMPARE(list->count(), 2);
1240 
1241         QTest::newRow("lists") << QTextDocumentFragment(&doc)
1242                           <<
1243                              QString("EMPTYBLOCK") +
1244                              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>");
1245     }
1246 
1247     {
1248         CREATE_DOC_AND_CURSOR();
1249 
1250         QTextList *list = cursor.insertList(QTextListFormat::ListDisc);
1251         cursor.insertText("Blubb");
1252 
1253         cursor.insertBlock();
1254 
1255         QTextCharFormat blockCharFmt;
1256         blockCharFmt.setForeground(QColor("#0000ff"));
1257         cursor.mergeBlockCharFormat(blockCharFmt);
1258 
1259         QTextCharFormat fmt;
1260         fmt.setForeground(QColor("#ff0000"));
1261         cursor.insertText("Blah", fmt);
1262         QCOMPARE(list->count(), 2);
1263 
1264         QTest::newRow("charfmt-for-list-item") << QTextDocumentFragment(&doc)
1265                           <<
1266                              QString("EMPTYBLOCK") +
1267                              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>");
1268     }
1269 
1270     {
1271         CREATE_DOC_AND_CURSOR();
1272 
1273         QTextBlockFormat fmt;
1274         fmt.setIndent(3);
1275         fmt.setTextIndent(30);
1276         cursor.insertBlock(fmt);
1277         cursor.insertText("Test");
1278 
1279         QTest::newRow("block-indent") << QTextDocumentFragment(&doc)
1280                                    <<
1281                                     QString("EMPTYBLOCK") +
1282                                     QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:3; text-indent:30px;\">Test</p>");
1283     }
1284 
1285     {
1286         CREATE_DOC_AND_CURSOR();
1287 
1288         QTextListFormat fmt;
1289         fmt.setStyle(QTextListFormat::ListDisc);
1290         fmt.setIndent(4);
1291         cursor.insertList(fmt);
1292         cursor.insertText("Blah");
1293 
1294         QTest::newRow("list-indent") << QTextDocumentFragment(&doc)
1295                                   <<
1296                                     QString("EMPTYBLOCK") +
1297                                     QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
1298     }
1299 
1300     {
1301         CREATE_DOC_AND_CURSOR();
1302 
1303         cursor.insertBlock();
1304 
1305 
1306         QTest::newRow("emptyblock") << QTextDocumentFragment(&doc)
1307                                     // after insertBlock() we /do/ have two blocks in the document, so also expect
1308                                     // these in the html output
1309                                     << QString("EMPTYBLOCK") + QString("EMPTYBLOCK");
1310     }
1311 
1312     {
1313         CREATE_DOC_AND_CURSOR();
1314 
1315         // if you press enter twice in an empty textedit and then insert 'Test'
1316         // you actually get three visible paragraphs, two empty leading ones and
1317         // a third with the actual text. the corresponding html representation
1318         // therefore should also contain three paragraphs.
1319 
1320         cursor.insertBlock();
1321         QTextCharFormat fmt;
1322         fmt.setForeground(QColor("#00ff00"));
1323         fmt.setProperty(QTextFormat::FontSizeIncrement, 1);
1324         cursor.mergeBlockCharFormat(fmt);
1325 
1326         fmt.setProperty(QTextFormat::FontSizeIncrement, 2);
1327         cursor.insertText("Test", fmt);
1328 
1329         QTest::newRow("blockcharfmt") << QTextDocumentFragment(&doc)
1330                                    << 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>");
1331     }
1332 
1333     {
1334         CREATE_DOC_AND_CURSOR();
1335 
1336         QTextCharFormat fmt;
1337         fmt.setForeground(QColor("#00ff00"));
1338         cursor.setBlockCharFormat(fmt);
1339         fmt.setForeground(QColor("#0000ff"));
1340         cursor.insertText("Test", fmt);
1341 
1342         QTest::newRow("blockcharfmt2") << QTextDocumentFragment(&doc)
1343                                    << 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>");
1344     }
1345 
1346     {
1347         QTest::newRow("horizontal-ruler") << QTextDocumentFragment::fromHtml("<hr />")
1348                                        <<
1349                                           QString("EMPTYBLOCK") +
1350                                           QString("<hr />");
1351     }
1352     {
1353         QTest::newRow("horizontal-ruler-with-width") << QTextDocumentFragment::fromHtml("<hr width=\"50%\"/>")
1354                                                   <<
1355                                                       QString("EMPTYBLOCK") +
1356                                                       QString("<hr width=\"50%\"/>");
1357     }
1358     {
1359         CREATE_DOC_AND_CURSOR();
1360 
1361         QTextFrame *mainFrame = cursor.currentFrame();
1362 
1363         QTextFrameFormat ffmt;
1364         ffmt.setBorder(1);
1365         ffmt.setPosition(QTextFrameFormat::FloatRight);
1366         ffmt.setMargin(2);
1367         ffmt.setWidth(100);
1368         ffmt.setHeight(50);
1369         ffmt.setBackground(QColor("#00ff00"));
1370         cursor.insertFrame(ffmt);
1371         cursor.insertText("Hello World");
1372         cursor = mainFrame->lastCursorPosition();
1373 
1374         QTest::newRow("frame") << QTextDocumentFragment(&doc)
1375                             << 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>");
1376     }
1377 
1378     {
1379         CREATE_DOC_AND_CURSOR();
1380 
1381         QTextCharFormat fmt;
1382         fmt.setForeground(QColor("#00ff00"));
1383 //        fmt.setBackground(QColor("#0000ff"));
1384         cursor.setBlockCharFormat(fmt);
1385 
1386         fmt.setForeground(QBrush());
1387 //        fmt.setBackground(QBrush());
1388         cursor.insertText("Test", fmt);
1389 
1390 //        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>");
1391         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>");
1392     }
1393 
1394     {
1395         CREATE_DOC_AND_CURSOR();
1396 
1397         QTextTable *table = cursor.insertTable(2, 2);
1398         table->mergeCells(0, 0, 1, 2);
1399         QTextTableFormat fmt = table->format();
1400         QVector<QTextLength> widths;
1401         widths.append(QTextLength(QTextLength::FixedLength, 20));
1402         widths.append(QTextLength(QTextLength::FixedLength, 40));
1403         fmt.setColumnWidthConstraints(widths);
1404         table->setFormat(fmt);
1405 
1406         QTest::newRow("mergedtablecolwidths") << QTextDocumentFragment(&doc)
1407                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1408                                              "\n<tr>\n<td colspan=\"2\"></td></tr>"
1409                                              "\n<tr>\n<td width=\"20\"></td>\n<td width=\"40\"></td></tr>"
1410                                              "</table>");
1411     }
1412 
1413     {
1414         CREATE_DOC_AND_CURSOR();
1415 
1416         QTextCharFormat fmt;
1417 
1418         cursor.insertText("Blah\nGreen yellow green");
1419         cursor.setPosition(0);
1420         cursor.setPosition(23, QTextCursor::KeepAnchor);
1421         fmt.setBackground(Qt::green);
1422         cursor.mergeCharFormat(fmt);
1423         cursor.clearSelection();
1424         cursor.setPosition(11);
1425         cursor.setPosition(17, QTextCursor::KeepAnchor);
1426         fmt.setBackground(Qt::yellow);
1427         cursor.mergeCharFormat(fmt);
1428         cursor.clearSelection();
1429 
1430         QTest::newRow("multiparagraph-bgcolor") << QTextDocumentFragment(&doc)
1431                                  << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>\n"
1432                                             "<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Green </span>"
1433                                             "<span style=\" background-color:#ffff00;\">yellow</span>"
1434                                             "<span style=\" background-color:#00ff00;\"> green</span></p>");
1435     }
1436 
1437     {
1438         CREATE_DOC_AND_CURSOR();
1439 
1440         QTextBlockFormat fmt;
1441         fmt.setBackground(QColor("#0000ff"));
1442         cursor.insertBlock(fmt);
1443 
1444         QTextCharFormat charfmt;
1445         charfmt.setBackground(QColor("#0000ff"));
1446         cursor.insertText("Blah", charfmt);
1447 
1448         QTest::newRow("nospan-bgcolor") << QTextDocumentFragment(&doc)
1449                                  << QString("EMPTYBLOCK") +
1450                                     QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\"><span style=\" background-color:#0000ff;\">Blah</span></p>");
1451     }
1452 
1453     {
1454         CREATE_DOC_AND_CURSOR();
1455 
1456         QTextTable *table = cursor.insertTable(2, 2);
1457         QTextCharFormat fmt = table->cellAt(0, 0).format();
1458         fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle);
1459         table->cellAt(0, 0).setFormat(fmt);
1460         fmt = table->cellAt(0, 1).format();
1461         fmt.setVerticalAlignment(QTextCharFormat::AlignTop);
1462         table->cellAt(0, 1).setFormat(fmt);
1463         fmt = table->cellAt(1, 0).format();
1464         fmt.setVerticalAlignment(QTextCharFormat::AlignBottom);
1465         table->cellAt(1, 0).setFormat(fmt);
1466 
1467         table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
1468 
1469         QTest::newRow("table-vertical-alignment") << QTextDocumentFragment(&doc)
1470                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1471                                              "\n<tr>\n<td style=\" vertical-align:middle;\">\n"
1472                                              "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
1473                                              "\n<td style=\" vertical-align:top;\"></td></tr>"
1474                                              "\n<tr>\n<td style=\" vertical-align:bottom;\"></td>"
1475                                              "\n<td></td></tr>"
1476                                              "</table>");
1477     }
1478 
1479     {
1480         CREATE_DOC_AND_CURSOR();
1481 
1482         QTextTable *table = cursor.insertTable(2, 2);
1483         QTextTableCellFormat fmt = table->cellAt(0, 0).format().toTableCellFormat();
1484         fmt.setLeftPadding(1);
1485         table->cellAt(0, 0).setFormat(fmt);
1486         fmt = table->cellAt(0, 1).format().toTableCellFormat();
1487         fmt.setRightPadding(1);
1488         table->cellAt(0, 1).setFormat(fmt);
1489         fmt = table->cellAt(1, 0).format().toTableCellFormat();
1490         fmt.setTopPadding(1);
1491         table->cellAt(1, 0).setFormat(fmt);
1492         fmt = table->cellAt(1, 1).format().toTableCellFormat();
1493         fmt.setBottomPadding(1);
1494         table->cellAt(1, 1).setFormat(fmt);
1495 
1496         table->cellAt(0, 0).firstCursorPosition().insertText("Blah");
1497 
1498         QTest::newRow("table-cell-paddings") << QTextDocumentFragment(&doc)
1499                                   << QString("<table border=\"1\" cellspacing=\"2\">"
1500                                              "\n<tr>\n<td style=\" padding-left:1;\">\n"
1501                                              "<p DEFAULTBLOCKSTYLE>Blah</p></td>"
1502                                              "\n<td style=\" padding-right:1;\"></td></tr>"
1503                                              "\n<tr>\n<td style=\" padding-top:1;\"></td>"
1504                                              "\n<td style=\" padding-bottom:1;\"></td></tr>"
1505                                              "</table>");
1506     }
1507 
1508     {
1509         CREATE_DOC_AND_CURSOR();
1510 
1511         QTextTableFormat fmt;
1512         fmt.setBorderBrush(QColor("#0000ff"));
1513         fmt.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
1514         cursor.insertTable(2, 2, fmt);
1515 
1516         QTest::newRow("tableborder") << QTextDocumentFragment(&doc)
1517                                      << QString("<table border=\"1\" style=\" border-color:#0000ff; border-style:solid;\" cellspacing=\"2\">"
1518                                                 "\n<tr>\n<td></td>\n<td></td></tr>"
1519                                                 "\n<tr>\n<td></td>\n<td></td></tr>"
1520                                                 "</table>");
1521     }
1522 
1523     {
1524         CREATE_DOC_AND_CURSOR();
1525 
1526         cursor.insertBlock();
1527         cursor.insertText("Foo");
1528 
1529         cursor.block().setUserState(42);
1530 
1531         QTest::newRow("userstate") << QTextDocumentFragment(&doc)
1532                                    << QString("EMPTYBLOCK") +
1533                                       QString("<p OPENDEFAULTBLOCKSTYLE -qt-user-state:42;\">Foo</p>");
1534     }
1535 
1536     {
1537         CREATE_DOC_AND_CURSOR();
1538 
1539         QTextBlockFormat blockFmt;
1540         blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
1541 
1542         cursor.insertBlock(blockFmt);
1543         cursor.insertText("Foo");
1544 
1545         blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore | QTextFormat::PageBreak_AlwaysAfter);
1546 
1547         cursor.insertBlock(blockFmt);
1548         cursor.insertText("Bar");
1549 
1550         QTextTableFormat tableFmt;
1551         tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
1552 
1553         cursor.insertTable(1, 1, tableFmt);
1554 
1555         QTest::newRow("pagebreak") << QTextDocumentFragment(&doc)
1556                                    << QString("EMPTYBLOCK") +
1557                                       QString("<p OPENDEFAULTBLOCKSTYLE page-break-before:always;\">Foo</p>"
1558                                               "\n<p OPENDEFAULTBLOCKSTYLE page-break-before:always; page-break-after:always;\">Bar</p>"
1559                                               "\n<table border=\"1\" style=\" page-break-after:always;\" cellspacing=\"2\">\n<tr>\n<td></td></tr></table>");
1560     }
1561 
1562     {
1563         CREATE_DOC_AND_CURSOR();
1564 
1565         QTextListFormat listFmt;
1566         listFmt.setStyle(QTextListFormat::ListDisc);
1567 
1568         cursor.insertList(listFmt);
1569         cursor.insertText("Blah");
1570 
1571         QTest::newRow("list-ul-margin") << QTextDocumentFragment(&doc)
1572                                         << QString("EMPTYBLOCK") +
1573                                            QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>");
1574     }
1575 }
1576 
toHtml()1577 void tst_QTextDocument::toHtml()
1578 {
1579     QFETCH(QTextDocumentFragment, input);
1580     QFETCH(QString, expectedOutput);
1581 
1582     cursor.insertFragment(input);
1583 
1584     expectedOutput.prepend(htmlHead);
1585 
1586     expectedOutput.replace("OPENDEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;");
1587     expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
1588     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");
1589     if (expectedOutput.endsWith(QLatin1Char('\n')))
1590         expectedOutput.chop(1);
1591     expectedOutput.append(htmlTail);
1592 
1593     QString output = doc->toHtml();
1594 
1595     QCOMPARE(output, expectedOutput);
1596 
1597     QDomDocument document;
1598     QVERIFY2(document.setContent(output), "Output was not valid XML");
1599 }
1600 
toHtml2()1601 void tst_QTextDocument::toHtml2()
1602 {
1603     QTextDocument doc;
1604     doc.setHtml("<p>text <img src=\"\">    text</p>"); // 4 spaces before the second 'text'
1605     QTextBlock block = doc.firstBlock();
1606     QTextBlock::Iterator iter = block.begin();
1607     QTextFragment f = iter.fragment();
1608     QVERIFY(f.isValid());
1609     QCOMPARE(f.position(), 0);
1610     QCOMPARE(f.length(), 5);
1611     //qDebug() << block.text().mid(f.position(), f.length());
1612 
1613     iter++;
1614     f = iter.fragment();
1615     QVERIFY(f.isValid());
1616     QCOMPARE(f.position(), 5);
1617     QCOMPARE(f.length(), 1);
1618     //qDebug() << block.text().mid(f.position(), f.length());
1619 
1620     iter++;
1621     f = iter.fragment();
1622     //qDebug() << block.text().mid(f.position(), f.length());
1623     QVERIFY(f.isValid());
1624     QCOMPARE(f.position(), 6);
1625     QCOMPARE(f.length(), 5); // 1 space should be preserved.
1626     QCOMPARE(block.text().mid(f.position(), f.length()), QString(" text"));
1627 
1628     doc.setHtml("<table><tr><td>   foo</td></tr></table>    text"); // 4 spaces before the second 'text'
1629     block = doc.firstBlock().next();
1630     //qDebug() << block.text();
1631     QCOMPARE(block.text(), QString("foo"));
1632 
1633     block = block.next();
1634     //qDebug() << block.text();
1635     QCOMPARE(block.text(), QString("text"));
1636 }
1637 
setFragmentMarkersInHtmlExport()1638 void tst_QTextDocument::setFragmentMarkersInHtmlExport()
1639 {
1640     {
1641         CREATE_DOC_AND_CURSOR();
1642 
1643         cursor.insertText("Leadin");
1644         const int startPos = cursor.position();
1645 
1646         cursor.insertText("Test");
1647         QTextCharFormat fmt;
1648         fmt.setForeground(QColor("#00ff00"));
1649         cursor.insertText("Blah", fmt);
1650 
1651         const int endPos = cursor.position();
1652         cursor.insertText("Leadout", QTextCharFormat());
1653 
1654         cursor.setPosition(startPos);
1655         cursor.setPosition(endPos, QTextCursor::KeepAnchor);
1656         QTextDocumentFragment fragment(cursor);
1657 
1658         QString expected = htmlHead;
1659         expected.replace(QRegExp("<body.*>"), QString("<body>"));
1660         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;
1661         QCOMPARE(fragment.toHtml(), expected);
1662     }
1663     {
1664         CREATE_DOC_AND_CURSOR();
1665 
1666         cursor.insertText("Leadin");
1667         const int startPos = cursor.position();
1668 
1669         cursor.insertText("Test");
1670 
1671         const int endPos = cursor.position();
1672         cursor.insertText("Leadout", QTextCharFormat());
1673 
1674         cursor.setPosition(startPos);
1675         cursor.setPosition(endPos, QTextCursor::KeepAnchor);
1676         QTextDocumentFragment fragment(cursor);
1677 
1678         QString expected = htmlHead;
1679         expected.replace(QRegExp("<body.*>"), QString("<body>"));
1680         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;
1681         QCOMPARE(fragment.toHtml(), expected);
1682     }
1683 }
1684 
toHtmlBodyBgColor()1685 void tst_QTextDocument::toHtmlBodyBgColor()
1686 {
1687     CREATE_DOC_AND_CURSOR();
1688 
1689     cursor.insertText("Blah");
1690 
1691     QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
1692     fmt.setBackground(QColor("#0000ff"));
1693     doc.rootFrame()->setFrameFormat(fmt);
1694 
1695     QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
1696             "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
1697             "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
1698             "p, li { white-space: pre-wrap; }\n"
1699             "</style></head>"
1700             "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\""
1701             " bgcolor=\"#0000ff\">\n"
1702             "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
1703             "</body></html>");
1704 
1705     expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
1706 
1707     QCOMPARE(doc.toHtml(), expectedHtml);
1708 }
1709 
toHtmlRootFrameProperties()1710 void tst_QTextDocument::toHtmlRootFrameProperties()
1711 {
1712     CREATE_DOC_AND_CURSOR();
1713 
1714     QTextFrameFormat fmt = doc.rootFrame()->frameFormat();
1715     fmt.setTopMargin(10);
1716     fmt.setLeftMargin(10);
1717     fmt.setBorder(2);
1718     doc.rootFrame()->setFrameFormat(fmt);
1719 
1720     cursor.insertText("Blah");
1721 
1722     QString expectedOutput("<table border=\"2\" style=\"-qt-table-type: root; margin-top:10px; "
1723                            "margin-bottom:4px; margin-left:10px; margin-right:4px;\">\n"
1724                            "<tr>\n<td style=\"border: none;\">\n"
1725                            "<p DEFAULTBLOCKSTYLE>Blah</p></td></tr></table>");
1726 
1727     expectedOutput.prepend(htmlHead);
1728     expectedOutput.replace("DEFAULTBLOCKSTYLE", "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"");
1729     expectedOutput.append(htmlTail);
1730 
1731     QCOMPARE(doc.toHtml(), expectedOutput);
1732 }
1733 
capitalizationHtmlInExport()1734 void tst_QTextDocument::capitalizationHtmlInExport()
1735 {
1736     doc->setPlainText("Test");
1737 
1738     QRegExp re(".*span style=\"(.*)\">Test.*");
1739     QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
1740 
1741     QTextCursor cursor(doc);
1742     cursor.setPosition(4, QTextCursor::KeepAnchor);
1743     QTextCharFormat cf;
1744     cf.setFontCapitalization(QFont::SmallCaps);
1745     cursor.mergeCharFormat(cf);
1746 
1747     const QString smallcaps = doc->toHtml();
1748     QVERIFY(re.exactMatch(doc->toHtml()));
1749     QCOMPARE(re.captureCount(), 1);
1750     QCOMPARE(re.cap(1).trimmed(), QString("font-variant:small-caps;"));
1751 
1752     cf.setFontCapitalization(QFont::AllUppercase);
1753     cursor.mergeCharFormat(cf);
1754     const QString uppercase = doc->toHtml();
1755     QVERIFY(re.exactMatch(doc->toHtml()));
1756     QCOMPARE(re.captureCount(), 1);
1757     QCOMPARE(re.cap(1).trimmed(), QString("text-transform:uppercase;"));
1758 
1759     cf.setFontCapitalization(QFont::AllLowercase);
1760     cursor.mergeCharFormat(cf);
1761     const QString lowercase = doc->toHtml();
1762     QVERIFY(re.exactMatch(doc->toHtml()));
1763     QCOMPARE(re.captureCount(), 1);
1764     QCOMPARE(re.cap(1).trimmed(), QString("text-transform:lowercase;"));
1765 
1766     doc->setHtml(smallcaps);
1767     cursor.setPosition(1);
1768     QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::SmallCaps);
1769     doc->setHtml(uppercase);
1770     QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllUppercase);
1771     doc->setHtml(lowercase);
1772     QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllLowercase);
1773 }
1774 
wordspacingHtmlExport()1775 void tst_QTextDocument::wordspacingHtmlExport()
1776 {
1777     doc->setPlainText("Test");
1778 
1779     QRegExp re(".*span style=\"(.*)\">Test.*");
1780     QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span
1781 
1782     QTextCursor cursor(doc);
1783     cursor.setPosition(4, QTextCursor::KeepAnchor);
1784     QTextCharFormat cf;
1785     cf.setFontWordSpacing(4);
1786     cursor.mergeCharFormat(cf);
1787 
1788     QVERIFY(re.exactMatch(doc->toHtml()));
1789     QCOMPARE(re.captureCount(), 1);
1790     QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:4px;"));
1791 
1792     cf.setFontWordSpacing(-8.5);
1793     cursor.mergeCharFormat(cf);
1794 
1795     QVERIFY(re.exactMatch(doc->toHtml()));
1796     QCOMPARE(re.captureCount(), 1);
1797     QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:-8.5px;"));
1798 }
1799 
1800 class CursorPosSignalSpy : public QObject
1801 {
1802     Q_OBJECT
1803 public:
CursorPosSignalSpy(QTextDocument * doc)1804     CursorPosSignalSpy(QTextDocument *doc)
1805     {
1806         calls = 0;
1807         connect(doc, SIGNAL(cursorPositionChanged(const QTextCursor &)),
1808                 this, SLOT(cursorPositionChanged(const QTextCursor &)));
1809     }
1810 
1811     int calls;
1812 
1813 private slots:
cursorPositionChanged(const QTextCursor &)1814     void cursorPositionChanged(const QTextCursor &)
1815     {
1816         ++calls;
1817     }
1818 };
1819 
cursorPositionChanged()1820 void tst_QTextDocument::cursorPositionChanged()
1821 {
1822     CursorPosSignalSpy spy(doc);
1823 
1824     cursor.insertText("Test");
1825     QCOMPARE(spy.calls, 1);
1826 
1827     spy.calls = 0;
1828     QTextCursor unrelatedCursor(doc);
1829     unrelatedCursor.insertText("Blah");
1830     QCOMPARE(spy.calls, 2);
1831 
1832     spy.calls = 0;
1833     cursor.insertText("Blah");
1834     QCOMPARE(spy.calls, 1);
1835 
1836     spy.calls = 0;
1837     cursor.movePosition(QTextCursor::PreviousCharacter);
1838     QCOMPARE(spy.calls, 0);
1839 }
1840 
cursorPositionChangedOnSetText()1841 void tst_QTextDocument::cursorPositionChangedOnSetText()
1842 {
1843     CursorPosSignalSpy spy(doc);
1844 
1845     // doc has one QTextCursor stored in the
1846     // cursor member variable, thus the signal
1847     // gets emitted once.
1848 
1849     doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah");
1850 
1851     QCOMPARE(spy.calls, 1);
1852 
1853     spy.calls = 0;
1854     doc->setHtml("<p>Foo<p>Bar<p>Baz<p>Blah");
1855 
1856     QCOMPARE(spy.calls, 1);
1857 }
1858 
textFrameIterator()1859 void tst_QTextDocument::textFrameIterator()
1860 {
1861     cursor.insertTable(1, 1);
1862 
1863     int blockCount = 0;
1864     int frameCount = 0;
1865 
1866     for (QTextFrame::Iterator frameIt = doc->rootFrame()->begin();
1867          !frameIt.atEnd(); ++frameIt) {
1868         if (frameIt.currentFrame())
1869             ++frameCount;
1870         else if (frameIt.currentBlock().isValid())
1871             ++blockCount;
1872 
1873     }
1874 
1875     QEXPECT_FAIL("", "This is currently worked around in the html export but needs fixing!", Continue);
1876     QCOMPARE(blockCount, 0);
1877     QCOMPARE(frameCount, 1);
1878 }
1879 
codecForHtml()1880 void tst_QTextDocument::codecForHtml()
1881 {
1882     const QByteArray header("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;charset=utf-16\">");
1883     QTextCodec *c = Qt::codecForHtml(header);
1884     QVERIFY(c);
1885     QCOMPARE(c->name(), QByteArray("UTF-16"));
1886 }
1887 
1888 class TestSyntaxHighlighter : public QObject
1889 {
1890     Q_OBJECT
1891 public:
TestSyntaxHighlighter(QTextDocument * doc)1892     inline TestSyntaxHighlighter(QTextDocument *doc) : QObject(doc), ok(false) {}
1893 
1894     bool ok;
1895 
1896 private slots:
markBlockDirty(int from,int charsRemoved,int charsAdded)1897     inline void markBlockDirty(int from, int charsRemoved, int charsAdded)
1898     {
1899         Q_UNUSED(charsRemoved);
1900         Q_UNUSED(charsAdded);
1901         QTextDocument *doc = static_cast<QTextDocument *>(parent());
1902         QTextBlock block = doc->findBlock(from);
1903 
1904         QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
1905         lout->called = false;
1906 
1907         doc->markContentsDirty(block.position(), block.length());
1908 
1909         ok = (lout->called == false);
1910     }
1911 
modifyBlockAgain(int from,int charsRemoved,int charsAdded)1912     inline void modifyBlockAgain(int from, int charsRemoved, int charsAdded)
1913     {
1914         Q_UNUSED(charsRemoved);
1915         Q_UNUSED(charsAdded);
1916         QTextDocument *doc = static_cast<QTextDocument *>(parent());
1917         QTextBlock block = doc->findBlock(from);
1918         QTextCursor cursor(block);
1919 
1920         QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(doc->documentLayout());
1921         lout->called = false;
1922 
1923         cursor.insertText("Foo");
1924 
1925         ok = (lout->called == true);
1926     }
1927 };
1928 
markContentsDirty()1929 void tst_QTextDocument::markContentsDirty()
1930 {
1931     QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
1932     doc->setDocumentLayout(lout);
1933     TestSyntaxHighlighter *highlighter = new TestSyntaxHighlighter(doc);
1934     connect(doc, SIGNAL(contentsChange(int, int, int)),
1935             highlighter, SLOT(markBlockDirty(int, int, int)));
1936 
1937     highlighter->ok = false;
1938     cursor.insertText("Some dummy text blah blah");
1939     QVERIFY(highlighter->ok);
1940 
1941     disconnect(doc, SIGNAL(contentsChange(int, int, int)),
1942                highlighter, SLOT(markBlockDirty(int, int, int)));
1943     connect(doc, SIGNAL(contentsChange(int, int, int)),
1944             highlighter, SLOT(modifyBlockAgain(int, int, int)));
1945     highlighter->ok = false;
1946     cursor.insertText("FooBar");
1947     QVERIFY(highlighter->ok);
1948 
1949     lout->called = false;
1950 
1951     doc->markContentsDirty(1, 4);
1952 
1953     QVERIFY(lout->called);
1954 }
1955 
clonePreservesMetaInformation()1956 void tst_QTextDocument::clonePreservesMetaInformation()
1957 {
1958     const QString title("Foobar");
1959     const QString url("about:blank");
1960     doc->setHtml("<html><head><title>" + title + "</title></head><body>Hrm</body></html>");
1961     doc->setMetaInformation(QTextDocument::DocumentUrl, url);
1962     QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), title);
1963     QCOMPARE(doc->metaInformation(QTextDocument::DocumentUrl), url);
1964 
1965     QTextDocument *clone = doc->clone();
1966     QCOMPARE(clone->metaInformation(QTextDocument::DocumentTitle), title);
1967     QCOMPARE(clone->metaInformation(QTextDocument::DocumentUrl), url);
1968     delete clone;
1969 }
1970 
clonePreservesPageSize()1971 void tst_QTextDocument::clonePreservesPageSize()
1972 {
1973     QSizeF sz(100., 100.);
1974     doc->setPageSize(sz);
1975     QTextDocument *clone = doc->clone();
1976     QCOMPARE(clone->pageSize(), sz);
1977     delete clone;
1978 }
1979 
clonePreservesPageBreakPolicies()1980 void tst_QTextDocument::clonePreservesPageBreakPolicies()
1981 {
1982     QTextTableFormat tableFmt;
1983     tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
1984 
1985     QTextBlockFormat blockFmt;
1986     blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore);
1987 
1988     QTextCursor cursor(doc);
1989 
1990     cursor.setBlockFormat(blockFmt);
1991     cursor.insertText("foo");
1992     cursor.insertTable(2, 2, tableFmt);
1993 
1994     QTextDocument *clone = doc->clone();
1995     QCOMPARE(clone->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore);
1996     QVERIFY(!clone->rootFrame()->childFrames().isEmpty());
1997     QCOMPARE(clone->rootFrame()->childFrames().first()->frameFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter);
1998     delete clone;
1999 }
2000 
clonePreservesDefaultFont()2001 void tst_QTextDocument::clonePreservesDefaultFont()
2002 {
2003     QFont f = doc->defaultFont();
2004     QVERIFY(f.pointSize() != 100);
2005     f.setPointSize(100);
2006     doc->setDefaultFont(f);
2007     QTextDocument *clone = doc->clone();
2008     QCOMPARE(clone->defaultFont(), f);
2009     delete clone;
2010 }
2011 
clonePreservesResources()2012 void tst_QTextDocument::clonePreservesResources()
2013 {
2014     QUrl testUrl(":/foobar");
2015     QVariant testResource("hello world");
2016 
2017     doc->addResource(QTextDocument::ImageResource, testUrl, testResource);
2018     QTextDocument *clone = doc->clone();
2019     QVERIFY(clone->resource(QTextDocument::ImageResource, testUrl) == testResource);
2020     delete clone;
2021 }
2022 
clonePreservesUserStates()2023 void tst_QTextDocument::clonePreservesUserStates()
2024 {
2025     QTextCursor cursor(doc);
2026     cursor.insertText("bla bla bla");
2027     cursor.block().setUserState(1);
2028     cursor.insertBlock();
2029     cursor.insertText("foo bar");
2030     cursor.block().setUserState(2);
2031     cursor.insertBlock();
2032     cursor.insertText("no user state");
2033 
2034     QTextDocument *clone = doc->clone();
2035     QTextBlock b1 = doc->begin(), b2 = clone->begin();
2036     while (b1 != doc->end()) {
2037         b1 = b1.next();
2038         b2 = b2.next();
2039         QCOMPARE(b1.userState(), b2.userState());
2040     }
2041     QVERIFY(b2 == clone->end());
2042     delete clone;
2043 }
2044 
clonePreservesRootFrameFormat()2045 void tst_QTextDocument::clonePreservesRootFrameFormat()
2046 {
2047     doc->setPlainText("Hello");
2048     QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
2049     fmt.setMargin(200);
2050     doc->rootFrame()->setFrameFormat(fmt);
2051     QCOMPARE(doc->rootFrame()->frameFormat().margin(), qreal(200));
2052     QTextDocument *copy = doc->clone();
2053     QCOMPARE(copy->rootFrame()->frameFormat().margin(), qreal(200));
2054     delete copy;
2055 }
2056 
clonePreservesIndentWidth()2057 void tst_QTextDocument::clonePreservesIndentWidth()
2058 {
2059     doc->setIndentWidth(42);
2060     QTextDocument *clone = doc->clone();
2061     QCOMPARE(clone->indentWidth(), qreal(42));
2062     delete clone;
2063 }
2064 
blockCount()2065 void tst_QTextDocument::blockCount()
2066 {
2067     QCOMPARE(doc->blockCount(), 1);
2068     cursor.insertBlock();
2069     QCOMPARE(doc->blockCount(), 2);
2070     cursor.insertBlock();
2071     QCOMPARE(doc->blockCount(), 3);
2072     cursor.insertText("blah blah");
2073     QCOMPARE(doc->blockCount(), 3);
2074     doc->undo();
2075     doc->undo();
2076     QCOMPARE(doc->blockCount(), 2);
2077     doc->undo();
2078     QCOMPARE(doc->blockCount(), 1);
2079 }
2080 
resolvedFontInEmptyFormat()2081 void tst_QTextDocument::resolvedFontInEmptyFormat()
2082 {
2083     QFont font;
2084     font.setPointSize(42);
2085     doc->setDefaultFont(font);
2086     QTextCharFormat fmt = doc->begin().charFormat();
2087     QVERIFY(fmt.properties().isEmpty());
2088     QVERIFY(fmt.font() == font);
2089 }
2090 
defaultRootFrameMargin()2091 void tst_QTextDocument::defaultRootFrameMargin()
2092 {
2093     QCOMPARE(doc->rootFrame()->frameFormat().margin(), 4.0);
2094 }
2095 
2096 class TestDocument : public QTextDocument
2097 {
2098 public:
TestDocument(const QUrl & testUrl,const QString & testString)2099     inline TestDocument(const QUrl &testUrl, const QString &testString)
2100        : url(testUrl), string(testString), resourceLoaded(false) {}
2101 
2102     bool hasResourceCached();
2103 
2104 protected:
2105     virtual QVariant loadResource(int type, const QUrl &name);
2106 
2107 private:
2108     QUrl url;
2109     QString string;
2110     bool resourceLoaded;
2111 };
2112 
hasResourceCached()2113 bool TestDocument::hasResourceCached()
2114 {
2115     resourceLoaded = false;
2116     resource(QTextDocument::ImageResource, url);
2117     return !resourceLoaded;
2118 }
2119 
loadResource(int type,const QUrl & name)2120 QVariant TestDocument::loadResource(int type, const QUrl &name)
2121 {
2122     if (type == QTextDocument::ImageResource
2123         && name == url) {
2124         resourceLoaded = true;
2125         return string;
2126     }
2127     return QTextDocument::loadResource(type, name);
2128 }
2129 
clearResources()2130 void tst_QTextDocument::clearResources()
2131 {
2132     // regular resource for QTextDocument
2133     QUrl testUrl(":/foobar");
2134     QVariant testResource("hello world");
2135 
2136     // implicitly cached resource, initially loaded through TestDocument::loadResource()
2137     QUrl cacheUrl(":/blub");
2138     QString cacheResource("mah");
2139 
2140     TestDocument doc(cacheUrl, cacheResource);
2141     doc.addResource(QTextDocument::ImageResource, testUrl, testResource);
2142 
2143     QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2144 
2145     doc.setPlainText("Hah");
2146     QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2147 
2148     doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
2149     QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource);
2150     QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
2151 
2152     doc.clear();
2153     QVERIFY(!doc.resource(QTextDocument::ImageResource, testUrl).isValid());
2154     QVERIFY(!doc.hasResourceCached());
2155     doc.clear();
2156 
2157     doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>");
2158     QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource);
2159 
2160     doc.setPlainText("Foob");
2161     QVERIFY(!doc.hasResourceCached());
2162 }
2163 
setPlainText()2164 void tst_QTextDocument::setPlainText()
2165 {
2166     doc->setPlainText("Hello World");
2167     QString s("");
2168     doc->setPlainText(s);
2169     QCOMPARE(doc->toPlainText(), s);
2170 }
2171 
toPlainText()2172 void tst_QTextDocument::toPlainText()
2173 {
2174     doc->setHtml("Hello&nbsp;World");
2175     QCOMPARE(doc->toPlainText(), QLatin1String("Hello World"));
2176 }
2177 
deleteTextObjectsOnClear()2178 void tst_QTextDocument::deleteTextObjectsOnClear()
2179 {
2180     QPointer<QTextTable> table = cursor.insertTable(2, 2);
2181     QVERIFY(!table.isNull());
2182     doc->clear();
2183     QVERIFY(table.isNull());
2184 }
2185 
defaultStyleSheet()2186 void tst_QTextDocument::defaultStyleSheet()
2187 {
2188     const QString sheet("p { background-color: green; }");
2189     QVERIFY(doc->defaultStyleSheet().isEmpty());
2190     doc->setDefaultStyleSheet(sheet);
2191     QCOMPARE(doc->defaultStyleSheet(), sheet);
2192 
2193     cursor.insertHtml("<p>test");
2194     QTextBlockFormat fmt = doc->begin().blockFormat();
2195     QVERIFY(fmt.background().color() == QColor("green"));
2196 
2197     doc->clear();
2198     cursor.insertHtml("<p>test");
2199     fmt = doc->begin().blockFormat();
2200     QVERIFY(fmt.background().color() == QColor("green"));
2201 
2202     QTextDocument *clone = doc->clone();
2203     QCOMPARE(clone->defaultStyleSheet(), sheet);
2204     cursor = QTextCursor(clone);
2205     cursor.insertHtml("<p>test");
2206     fmt = clone->begin().blockFormat();
2207     QVERIFY(fmt.background().color() == QColor("green"));
2208     delete clone;
2209 
2210     cursor = QTextCursor(doc);
2211     cursor.insertHtml("<p>test");
2212     fmt = doc->begin().blockFormat();
2213     QVERIFY(fmt.background().color() == QColor("green"));
2214 
2215     doc->clear();
2216     cursor.insertHtml("<style>p { background-color: red; }</style><p>test");
2217     fmt = doc->begin().blockFormat();
2218     QVERIFY(fmt.background().color() == QColor("red"));
2219 
2220     doc->clear();
2221     doc->setDefaultStyleSheet("invalid style sheet....");
2222     cursor.insertHtml("<p>test");
2223     fmt = doc->begin().blockFormat();
2224     QVERIFY(fmt.background().color() != QColor("green"));
2225 }
2226 
maximumBlockCount()2227 void tst_QTextDocument::maximumBlockCount()
2228 {
2229     QCOMPARE(doc->maximumBlockCount(), 0);
2230     QVERIFY(doc->isUndoRedoEnabled());
2231 
2232     cursor.insertBlock();
2233     cursor.insertText("Blah");
2234     cursor.insertBlock();
2235     cursor.insertText("Foo");
2236     QCOMPARE(doc->blockCount(), 3);
2237     QCOMPARE(doc->toPlainText(), QString("\nBlah\nFoo"));
2238 
2239     doc->setMaximumBlockCount(1);
2240     QVERIFY(!doc->isUndoRedoEnabled());
2241 
2242     QCOMPARE(doc->blockCount(), 1);
2243     QCOMPARE(doc->toPlainText(), QString("Foo"));
2244 
2245     cursor.insertBlock();
2246     cursor.insertText("Hello");
2247     doc->setMaximumBlockCount(1);
2248     QCOMPARE(doc->blockCount(), 1);
2249     QCOMPARE(doc->toPlainText(), QString("Hello"));
2250 
2251     doc->setMaximumBlockCount(100);
2252     for (int i = 0; i < 1000; ++i) {
2253         cursor.insertBlock();
2254         cursor.insertText("Blah)");
2255         QVERIFY(doc->blockCount() <= 100);
2256     }
2257 
2258     cursor.movePosition(QTextCursor::End);
2259     QCOMPARE(cursor.blockNumber(), 99);
2260     QTextCharFormat fmt;
2261     fmt.setFontItalic(true);
2262     cursor.setBlockCharFormat(fmt);
2263     cursor.movePosition(QTextCursor::Start);
2264     QVERIFY(!cursor.blockCharFormat().fontItalic());
2265 
2266     doc->setMaximumBlockCount(1);
2267     QVERIFY(cursor.blockCharFormat().fontItalic());
2268 
2269     cursor.insertTable(2, 2);
2270     QCOMPARE(doc->blockCount(), 6);
2271     cursor.insertBlock();
2272     QCOMPARE(doc->blockCount(), 1);
2273 }
2274 
adjustSize()2275 void tst_QTextDocument::adjustSize()
2276 {
2277     // avoid ugly tooltips like in task 125583
2278     QString text("Test Text");
2279     doc->setPlainText(text);
2280     doc->rootFrame()->setFrameFormat(QTextFrameFormat());
2281     doc->adjustSize();
2282     QCOMPARE(doc->size().width(), doc->idealWidth());
2283 }
2284 
initialUserData()2285 void tst_QTextDocument::initialUserData()
2286 {
2287     doc->setPlainText("Hello");
2288     QTextBlock block = doc->begin();
2289     block.setUserData(new QTextBlockUserData);
2290     QVERIFY(block.userData());
2291     doc->documentLayout();
2292     QVERIFY(block.userData());
2293     doc->setDocumentLayout(new QTestDocumentLayout(doc));
2294     QVERIFY(!block.userData());
2295 }
2296 
html_defaultFont()2297 void tst_QTextDocument::html_defaultFont()
2298 {
2299     QFont f;
2300     f.setItalic(true);
2301     f.setWeight(QFont::Bold);
2302     doc->setDefaultFont(f);
2303     doc->setPlainText("Test");
2304 
2305     QString bodyPart = QString::fromLatin1("<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:italic;\">")
2306                        .arg(f.family()).arg(f.pointSizeF()).arg(f.weight() * 8);
2307 
2308     QString html = doc->toHtml();
2309     if (!html.contains(bodyPart)) {
2310         qDebug() << "html:" << html;
2311         qDebug() << "expected body:" << bodyPart;
2312         QVERIFY(html.contains(bodyPart));
2313     }
2314 
2315     if (html.contains("span"))
2316         qDebug() << "html:" << html;
2317     QVERIFY(!html.contains("<span style"));
2318 }
2319 
blockCountChanged()2320 void tst_QTextDocument::blockCountChanged()
2321 {
2322     QSignalSpy spy(doc, SIGNAL(blockCountChanged(int)));
2323 
2324     doc->setPlainText("Foo");
2325 
2326     QCOMPARE(doc->blockCount(), 1);
2327     QCOMPARE(spy.count(), 0);
2328 
2329     spy.clear();
2330 
2331     doc->setPlainText("Foo\nBar");
2332     QCOMPARE(doc->blockCount(), 2);
2333     QCOMPARE(spy.count(), 1);
2334     QCOMPARE(spy.at(0).value(0).toInt(), 2);
2335 
2336     spy.clear();
2337 
2338     cursor.movePosition(QTextCursor::End);
2339     cursor.insertText("Blahblah");
2340 
2341     QCOMPARE(spy.count(), 0);
2342 
2343     cursor.insertBlock();
2344     QCOMPARE(spy.count(), 1);
2345     QCOMPARE(spy.at(0).value(0).toInt(), 3);
2346 
2347     spy.clear();
2348     doc->undo();
2349 
2350     QCOMPARE(spy.count(), 1);
2351     QCOMPARE(spy.at(0).value(0).toInt(), 2);
2352 }
2353 
nonZeroDocumentLengthOnClear()2354 void tst_QTextDocument::nonZeroDocumentLengthOnClear()
2355 {
2356     QTestDocumentLayout *lout = new QTestDocumentLayout(doc);
2357     doc->setDocumentLayout(lout);
2358 
2359     doc->clear();
2360     QVERIFY(lout->called);
2361     QVERIFY(!lout->lastDocumentLengths.contains(0));
2362 }
2363 
setTextPreservesUndoRedoEnabled()2364 void tst_QTextDocument::setTextPreservesUndoRedoEnabled()
2365 {
2366     QVERIFY(doc->isUndoRedoEnabled());
2367 
2368     doc->setPlainText("Test");
2369 
2370     QVERIFY(doc->isUndoRedoEnabled());
2371 
2372     doc->setUndoRedoEnabled(false);
2373     QVERIFY(!doc->isUndoRedoEnabled());
2374     doc->setPlainText("Test2");
2375     QVERIFY(!doc->isUndoRedoEnabled());
2376 
2377     doc->setHtml("<p>hello");
2378     QVERIFY(!doc->isUndoRedoEnabled());
2379 }
2380 
firstLast()2381 void tst_QTextDocument::firstLast()
2382 {
2383     QCOMPARE(doc->blockCount(), 1);
2384     QVERIFY(doc->firstBlock() == doc->lastBlock());
2385 
2386     doc->setPlainText("Hello\nTest\nWorld");
2387 
2388     QCOMPARE(doc->blockCount(), 3);
2389     QVERIFY(doc->firstBlock() != doc->lastBlock());
2390 
2391     QCOMPARE(doc->firstBlock().text(), QString("Hello"));
2392     QCOMPARE(doc->lastBlock().text(), QString("World"));
2393 
2394     // manual forward loop
2395     QTextBlock block = doc->firstBlock();
2396 
2397     QVERIFY(block.isValid());
2398     QCOMPARE(block.text(), QString("Hello"));
2399 
2400     block = block.next();
2401 
2402     QVERIFY(block.isValid());
2403     QCOMPARE(block.text(), QString("Test"));
2404 
2405     block = block.next();
2406 
2407     QVERIFY(block.isValid());
2408     QCOMPARE(block.text(), QString("World"));
2409 
2410     block = block.next();
2411     QVERIFY(!block.isValid());
2412 
2413     // manual backward loop
2414     block = doc->lastBlock();
2415 
2416     QVERIFY(block.isValid());
2417     QCOMPARE(block.text(), QString("World"));
2418 
2419     block = block.previous();
2420 
2421     QVERIFY(block.isValid());
2422     QCOMPARE(block.text(), QString("Test"));
2423 
2424     block = block.previous();
2425 
2426     QVERIFY(block.isValid());
2427     QCOMPARE(block.text(), QString("Hello"));
2428 
2429     block = block.previous();
2430     QVERIFY(!block.isValid());
2431 }
2432 
2433 const QString backgroundImage_html("<body><table><tr><td background=\"foo.png\">Blah</td></tr></table></body>");
2434 
backgroundImage_checkExpectedHtml(const QTextDocument & doc)2435 void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &doc)
2436 {
2437     QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" "
2438             "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
2439             "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
2440             "p, li { white-space: pre-wrap; }\n"
2441             "</style></head>"
2442             "<body style=\" font-family:'%1'; font-size:%2pt; font-weight:%3; font-style:%4;\">\n"
2443             "<table border=\"0\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;\" cellspacing=\"2\" cellpadding=\"0\">"
2444             "\n<tr>\n<td background=\"foo.png\">"
2445             "\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>"
2446             "</td></tr></table></body></html>");
2447 
2448     expectedHtml = expectedHtml.arg(defaultFont.family()).arg(defaultFont.pointSizeF()).arg(defaultFont.weight() * 8).arg((defaultFont.italic() ? "italic" : "normal"));
2449 
2450     QCOMPARE(doc.toHtml(), expectedHtml);
2451 }
2452 
backgroundImage_toHtml()2453 void tst_QTextDocument::backgroundImage_toHtml()
2454 {
2455     CREATE_DOC_AND_CURSOR();
2456 
2457     doc.setHtml(backgroundImage_html);
2458     backgroundImage_checkExpectedHtml(doc);
2459 }
2460 
backgroundImage_toHtml2()2461 void tst_QTextDocument::backgroundImage_toHtml2()
2462 {
2463     CREATE_DOC_AND_CURSOR();
2464 
2465     cursor.insertHtml(backgroundImage_html);
2466     backgroundImage_checkExpectedHtml(doc);
2467 }
2468 
backgroundImage_clone()2469 void tst_QTextDocument::backgroundImage_clone()
2470 {
2471     CREATE_DOC_AND_CURSOR();
2472 
2473     doc.setHtml(backgroundImage_html);
2474     QTextDocument *clone = doc.clone();
2475     backgroundImage_checkExpectedHtml(*clone);
2476     delete clone;
2477 }
2478 
backgroundImage_copy()2479 void tst_QTextDocument::backgroundImage_copy()
2480 {
2481     CREATE_DOC_AND_CURSOR();
2482 
2483     doc.setHtml(backgroundImage_html);
2484     QTextDocumentFragment fragment(&doc);
2485 
2486     {
2487         CREATE_DOC_AND_CURSOR();
2488 
2489         cursor.insertFragment(fragment);
2490         backgroundImage_checkExpectedHtml(doc);
2491     }
2492 }
2493 
documentCleanup()2494 void tst_QTextDocument::documentCleanup()
2495 {
2496     QTextDocument doc;
2497     QTextCursor cursor(&doc);
2498     cursor.insertText("d\nfoo\nbar\n");
2499     doc.documentLayout(); // forces relayout
2500 
2501     // remove char 1
2502     cursor.setPosition(0);
2503     QSizeF size = doc.documentLayout()->documentSize();
2504     cursor.deleteChar();
2505     // the size should be unchanged.
2506     QCOMPARE(doc.documentLayout()->documentSize(), size);
2507 }
2508 
characterAt()2509 void tst_QTextDocument::characterAt()
2510 {
2511     QTextDocument doc;
2512     QTextCursor cursor(&doc);
2513     QString text("12345\n67890");
2514     cursor.insertText(text);
2515     int length = doc.characterCount();
2516     QCOMPARE(length, text.length() + 1);
2517     QCOMPARE(doc.characterAt(length-1), QChar(QChar::ParagraphSeparator));
2518     QCOMPARE(doc.characterAt(-1), QChar());
2519     QCOMPARE(doc.characterAt(length), QChar());
2520     QCOMPARE(doc.characterAt(length + 1), QChar());
2521     for (int i = 0; i < text.length(); ++i) {
2522         QChar c = text.at(i);
2523         if (c == QLatin1Char('\n'))
2524             c = QChar(QChar::ParagraphSeparator);
2525         QCOMPARE(doc.characterAt(i), c);
2526     }
2527 }
2528 
revisions()2529 void tst_QTextDocument::revisions()
2530 {
2531     QTextDocument doc;
2532     QTextCursor cursor(&doc);
2533     QString text("Hello World");
2534     QCOMPARE(doc.firstBlock().revision(), 0);
2535     cursor.insertText(text);
2536     QCOMPARE(doc.firstBlock().revision(), 1);
2537     cursor.setPosition(6);
2538     cursor.insertBlock();
2539     QCOMPARE(cursor.block().previous().revision(), 2);
2540     QCOMPARE(cursor.block().revision(), 2);
2541     cursor.insertText("candle");
2542     QCOMPARE(cursor.block().revision(), 3);
2543     cursor.movePosition(QTextCursor::EndOfBlock);
2544     cursor.insertBlock(); // we are at the block end
2545     QCOMPARE(cursor.block().previous().revision(), 3);
2546     QCOMPARE(cursor.block().revision(), 4);
2547     cursor.insertText("lightbulb");
2548     QCOMPARE(cursor.block().revision(), 5);
2549     cursor.movePosition(QTextCursor::StartOfBlock);
2550     cursor.insertBlock(); // we are the block start
2551     QCOMPARE(cursor.block().previous().revision(), 6);
2552     QCOMPARE(cursor.block().revision(), 5);
2553 }
2554 
revisionWithUndoCompressionAndUndo()2555 void tst_QTextDocument::revisionWithUndoCompressionAndUndo()
2556 {
2557     QTextDocument doc;
2558     QTextCursor cursor(&doc);
2559     cursor.insertText("This is the beginning of it all.");
2560     QCOMPARE(doc.firstBlock().revision(), 1);
2561     QCOMPARE(doc.revision(), 1);
2562     cursor.insertBlock();
2563     QCOMPARE(doc.revision(), 2);
2564     cursor.insertText("this");
2565     QCOMPARE(doc.revision(), 3);
2566     cursor.insertText("is");
2567     QCOMPARE(doc.revision(), 4);
2568     cursor.insertText("compressed");
2569     QCOMPARE(doc.revision(), 5);
2570     doc.undo();
2571     QCOMPARE(doc.revision(), 6);
2572     QCOMPARE(doc.toPlainText(), QString("This is the beginning of it all.\n"))  ;
2573     cursor.setPosition(0);
2574     QCOMPARE(doc.firstBlock().revision(), 1);
2575     cursor.insertText("Very beginnig");
2576     QCOMPARE(doc.firstBlock().revision(), 7);
2577     doc.undo();
2578     QCOMPARE(doc.revision(), 8);
2579     QCOMPARE(doc.firstBlock().revision(), 1);
2580 
2581     cursor.beginEditBlock();
2582     cursor.insertText("Hello");
2583     cursor.insertBlock();
2584     cursor.insertText("world");
2585     cursor.endEditBlock();
2586     QCOMPARE(doc.revision(), 9);
2587     doc.undo();
2588     QCOMPARE(doc.revision(), 10);
2589 
2590 
2591 }
2592 
testUndoCommandAdded()2593 void tst_QTextDocument::testUndoCommandAdded()
2594 {
2595     QVERIFY(doc);
2596     QSignalSpy spy(doc, SIGNAL(undoCommandAdded()));
2597     QVERIFY(spy.isValid());
2598     QVERIFY(spy.isEmpty());
2599 
2600     cursor.insertText("a");
2601     QCOMPARE(spy.count(), 1);
2602     cursor.insertText("b"); // should be merged
2603     QCOMPARE(spy.count(), 1);
2604     cursor.insertText("c"); // should be merged
2605     QCOMPARE(spy.count(), 1);
2606     QCOMPARE(doc->toPlainText(), QString("abc"));
2607     doc->undo();
2608     QCOMPARE(doc->toPlainText(), QString(""));
2609 
2610     doc->clear();
2611     spy.clear();
2612     cursor.insertText("aaa");
2613     QCOMPARE(spy.count(), 1);
2614 
2615     spy.clear();
2616     cursor.insertText("aaaa\nbcd");
2617     QCOMPARE(spy.count(), 1);
2618 
2619     spy.clear();
2620     cursor.beginEditBlock();
2621     cursor.insertText("aa");
2622     cursor.insertText("bbb\n");
2623     cursor.setCharFormat(QTextCharFormat());
2624     cursor.insertText("\nccc");
2625     QVERIFY(spy.isEmpty());
2626     cursor.endEditBlock();
2627     QCOMPARE(spy.count(), 1);
2628 
2629     spy.clear();
2630     cursor.insertBlock();
2631     QCOMPARE(spy.count(), 1);
2632 
2633     spy.clear();
2634     cursor.setPosition(5);
2635     QVERIFY(spy.isEmpty());
2636     cursor.setCharFormat(QTextCharFormat());
2637     QVERIFY(spy.isEmpty());
2638     cursor.setPosition(10, QTextCursor::KeepAnchor);
2639     QVERIFY(spy.isEmpty());
2640     QTextCharFormat cf;
2641     cf.setFontItalic(true);
2642     cursor.mergeCharFormat(cf);
2643     QCOMPARE(spy.count(), 1);
2644 
2645     spy.clear();
2646     doc->undo();
2647     QCOMPARE(spy.count(), 0);
2648     doc->undo();
2649     QCOMPARE(spy.count(), 0);
2650     spy.clear();
2651     doc->redo();
2652     QCOMPARE(spy.count(), 0);
2653     doc->redo();
2654     QCOMPARE(spy.count(), 0);
2655 }
2656 
testUndoBlocks()2657 void tst_QTextDocument::testUndoBlocks()
2658 {
2659     QVERIFY(doc);
2660     cursor.insertText("Hello World");
2661     cursor.insertText("period");
2662     doc->undo();
2663     QCOMPARE(doc->toPlainText(), QString(""));
2664     cursor.insertText("Hello World");
2665     cursor.insertText("One\nTwo\nThree");
2666     QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
2667     doc->undo();
2668     QCOMPARE(doc->toPlainText(), QString("Hello World"));
2669     cursor.insertText("One\nTwo\nThree");
2670     cursor.insertText("Trailing text");
2671     doc->undo();
2672     QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree"));
2673     doc->undo();
2674     QCOMPARE(doc->toPlainText(), QString("Hello World"));
2675     doc->undo();
2676     QCOMPARE(doc->toPlainText(), QString(""));
2677 
2678     cursor.insertText("quod");
2679     cursor.beginEditBlock();
2680     cursor.insertText(" erat");
2681     cursor.endEditBlock();
2682     cursor.insertText(" demonstrandum");
2683     QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum"));
2684     doc->undo();
2685     QCOMPARE(doc->toPlainText(), QString("quod erat"));
2686     doc->undo();
2687     QCOMPARE(doc->toPlainText(), QString("quod"));
2688     doc->undo();
2689     QCOMPARE(doc->toPlainText(), QString(""));
2690 }
2691 
2692 class Receiver : public QObject
2693 {
2694     Q_OBJECT
2695  public:
2696     QString first;
2697  public slots:
cursorPositionChanged()2698     void cursorPositionChanged() {
2699         if (first.isEmpty())
2700             first = QLatin1String("cursorPositionChanged");
2701     }
2702 
contentsChange()2703     void contentsChange() {
2704         if (first.isEmpty())
2705             first = QLatin1String("contentsChanged");
2706     }
2707 };
2708 
receiveCursorPositionChangedAfterContentsChange()2709 void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange()
2710 {
2711     QVERIFY(doc);
2712     doc->setDocumentLayout(new MyAbstractTextDocumentLayout(doc));
2713     Receiver rec;
2714     connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)),
2715             &rec, SLOT(cursorPositionChanged()));
2716     connect(doc, SIGNAL(contentsChange(int,int,int)),
2717             &rec, SLOT(contentsChange()));
2718     cursor.insertText("Hello World");
2719     QCOMPARE(rec.first, QString("contentsChanged"));
2720 }
2721 
escape_data()2722 void tst_QTextDocument::escape_data()
2723 {
2724     QTest::addColumn<QString>("original");
2725     QTest::addColumn<QString>("expected");
2726 
2727     QTest::newRow("1") << "Hello World\n" << "Hello World\n";
2728     QTest::newRow("2") << "#include <QtCore>" << "#include &lt;QtCore&gt;";
2729     QTest::newRow("3") << "<p class=\"cool\"><a href=\"http://example.com/?foo=bar&amp;bar=foo\">plop --&gt; </a></p>"
2730                        << "&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;";
2731     QTest::newRow("4") << QString::fromUtf8("<\320\222\321\201>") << QString::fromUtf8("&lt;\320\222\321\201&gt;");
2732 }
2733 
escape()2734 void tst_QTextDocument::escape()
2735 {
2736     QFETCH(QString, original);
2737     QFETCH(QString, expected);
2738 
2739     QCOMPARE(Qt::escape(original), expected);
2740 }
2741 
QTBUG25778_pixelSizeFromHtml()2742 void tst_QTextDocument::QTBUG25778_pixelSizeFromHtml()
2743 {
2744     QTextDocument document1;
2745     QTextDocument document2;
2746 
2747     document1.setHtml("<span style=\"font-size: 24px\">Foobar</span>");
2748     document2.setHtml(document1.toHtml());
2749 
2750     QTextCursor cursor(&document2);
2751     QCOMPARE(cursor.charFormat().font().pixelSize(), 24);
2752 }
2753 
copiedFontSize()2754 void tst_QTextDocument::copiedFontSize()
2755 {
2756     QTextDocument documentInput;
2757     QTextDocument documentOutput;
2758 
2759     QFont fontInput;
2760     fontInput.setPixelSize(24);
2761 
2762     QTextCursor cursorInput(&documentInput);
2763     QTextCharFormat formatInput = cursorInput.charFormat();
2764     formatInput.setFont(fontInput);
2765     cursorInput.insertText("Should be the same font", formatInput);
2766     cursorInput.select(QTextCursor::Document);
2767 
2768     QTextDocumentFragment fragmentInput(cursorInput);
2769     QString html =  fragmentInput.toHtml();
2770 
2771     QTextCursor cursorOutput(&documentOutput);
2772     QTextDocumentFragment fragmentOutput = QTextDocumentFragment::fromHtml(html);
2773     cursorOutput.insertFragment(fragmentOutput);
2774 
2775     QCOMPARE(cursorOutput.charFormat().font().pixelSize(), 24);
2776 }
2777 
htmlExportImportBlockCount()2778 void tst_QTextDocument::htmlExportImportBlockCount()
2779 {
2780     QTextDocument document;
2781     {
2782         QTextCursor cursor(&document);
2783         cursor.insertText("Foo");
2784         cursor.insertBlock();
2785         cursor.insertBlock();
2786         cursor.insertBlock();
2787         cursor.insertBlock();
2788         cursor.insertText("Bar");
2789     }
2790 
2791     QCOMPARE(document.blockCount(), 5);
2792     QString html = document.toHtml();
2793 
2794     document.clear();
2795     document.setHtml(html);
2796 
2797     QCOMPARE(document.blockCount(), 5);
2798 }
2799 
2800 QTEST_MAIN(tst_QTextDocument)
2801 #include "tst_qtextdocument.moc"
2802