1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | |
32 | |
33 | #include <qtextdocument.h> |
34 | #include <qdebug.h> |
35 | |
36 | #include <qtextcursor.h> |
37 | #include <qtextdocumentfragment.h> |
38 | #include <qtextformat.h> |
39 | #include <qtextobject.h> |
40 | #include <qtexttable.h> |
41 | #include <qabstracttextdocumentlayout.h> |
42 | #include <qtextlist.h> |
43 | #include <qtextcodec.h> |
44 | #include <qguiapplication.h> |
45 | #include <qurl.h> |
46 | #include <qpainter.h> |
47 | #include <qfontmetrics.h> |
48 | #include <qimage.h> |
49 | #include <qtextlayout.h> |
50 | #include <QDomDocument> |
51 | #include "common.h" |
52 | |
53 | // #define DEBUG_WRITE_OUTPUT |
54 | |
55 | QT_FORWARD_DECLARE_CLASS(QTextDocument) |
56 | |
57 | class tst_QTextDocument : public QObject |
58 | { |
59 | Q_OBJECT |
60 | |
61 | public: |
62 | tst_QTextDocument(); |
63 | |
64 | private slots: |
65 | void init(); |
66 | void cleanup(); |
67 | void cleanupTestCase(); |
68 | void getSetCheck(); |
69 | void isEmpty(); |
70 | void find_data(); |
71 | void find(); |
72 | void find2(); |
73 | void findWithRegExp_data(); |
74 | void findWithRegExp(); |
75 | void findWithRegularExpression_data(); |
76 | void findWithRegularExpression(); |
77 | void findMultiple(); |
78 | void basicIsModifiedChecks(); |
79 | void moreIsModified(); |
80 | void isModified2(); |
81 | void isModified3(); |
82 | void isModified4(); |
83 | void noundo_basicIsModifiedChecks(); |
84 | void noundo_moreIsModified(); |
85 | void noundo_isModified2(); |
86 | void noundo_isModified3(); |
87 | void mightBeRichText(); |
88 | void mightBeRichText_data(); |
89 | |
90 | void task240325(); |
91 | |
92 | void preFont(); |
93 | |
94 | void stylesheetFont_data(); |
95 | void stylesheetFont(); |
96 | |
97 | void toHtml_data(); |
98 | void toHtml(); |
99 | void toHtml2(); |
100 | |
101 | void setFragmentMarkersInHtmlExport(); |
102 | |
103 | void toHtmlBodyBgColor(); |
104 | void toHtmlBodyBgColorRgba(); |
105 | void toHtmlBodyBgColorTransparent(); |
106 | void toHtmlRootFrameProperties(); |
107 | void toHtmlLineHeightProperties(); |
108 | void toHtmlDefaultFontSpacingProperties(); |
109 | void capitalizationHtmlInExport(); |
110 | void wordspacingHtmlExport(); |
111 | |
112 | void cursorPositionChanged(); |
113 | void cursorPositionChangedOnSetText(); |
114 | |
115 | void textFrameIterator(); |
116 | |
117 | void codecForHtml(); |
118 | |
119 | void markContentsDirty(); |
120 | |
121 | void clonePreservesMetaInformation(); |
122 | void clonePreservesPageSize(); |
123 | void clonePreservesPageBreakPolicies(); |
124 | void clonePreservesDefaultFont(); |
125 | void clonePreservesRootFrameFormat(); |
126 | void clonePreservesResources(); |
127 | void clonePreservesUserStates(); |
128 | void clonePreservesIndentWidth(); |
129 | void clonePreservesFormatsWhenEmpty(); |
130 | void blockCount(); |
131 | void defaultStyleSheet(); |
132 | |
133 | void resolvedFontInEmptyFormat(); |
134 | |
135 | void defaultRootFrameMargin(); |
136 | |
137 | void clearResources(); |
138 | |
139 | void setPlainText(); |
140 | void toPlainText_data(); |
141 | void toPlainText(); |
142 | void toRawText(); |
143 | |
144 | void deleteTextObjectsOnClear(); |
145 | |
146 | void maximumBlockCount(); |
147 | void adjustSize(); |
148 | void initialUserData(); |
149 | |
150 | void html_defaultFont(); |
151 | |
152 | void blockCountChanged(); |
153 | |
154 | void nonZeroDocumentLengthOnClear(); |
155 | |
156 | void setTextPreservesUndoRedoEnabled(); |
157 | |
158 | void firstLast(); |
159 | |
160 | void backgroundImage_toHtml(); |
161 | void backgroundImage_toHtml2(); |
162 | void backgroundImage_clone(); |
163 | void backgroundImage_copy(); |
164 | |
165 | void documentCleanup(); |
166 | |
167 | void characterAt(); |
168 | void revisions(); |
169 | void revisionWithUndoCompressionAndUndo(); |
170 | |
171 | void testUndoCommandAdded(); |
172 | |
173 | void testUndoBlocks(); |
174 | |
175 | void receiveCursorPositionChangedAfterContentsChange(); |
176 | |
177 | void copiedFontSize(); |
178 | |
179 | void QTBUG25778_pixelSizeFromHtml(); |
180 | |
181 | void htmlExportImportBlockCount(); |
182 | |
183 | void QTBUG27354_spaceAndSoftSpace(); |
184 | void baseUrl_data(); |
185 | void baseUrl(); |
186 | |
187 | void QTBUG28998_linkColor(); |
188 | |
189 | void textCursorUsageWithinContentsChange(); |
190 | void cssInheritance(); |
191 | |
192 | void lineHeightType(); |
193 | void cssLineHeightMultiplier(); |
194 | |
195 | void fontTagFace(); |
196 | |
197 | void clearUndoRedoStacks(); |
198 | void mergeFontFamilies(); |
199 | |
200 | void contentsChangeIndices_data(); |
201 | void contentsChangeIndices(); |
202 | |
203 | void insertHtmlWithComments_data(); |
204 | void insertHtmlWithComments(); |
205 | |
206 | private: |
207 | void backgroundImage_checkExpectedHtml(const QTextDocument &doc); |
208 | void buildRegExpData(); |
209 | static QString cssFontSizeString(const QFont &font); |
210 | void writeActualAndExpected(const char* testTag, const QString &actual, const QString &expected); |
211 | |
212 | QTextDocument *doc; |
213 | QTextCursor cursor; |
214 | QFont defaultFont; |
215 | QString htmlHead; |
216 | QString htmlTail; |
217 | }; |
218 | |
219 | class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout |
220 | { |
221 | public: |
222 | MyAbstractTextDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc) {} |
223 | void draw(QPainter *, const PaintContext &) {} |
224 | int hitTest(const QPointF &, Qt::HitTestAccuracy) const { return 0; } |
225 | int pageCount() const { return 0; } |
226 | QSizeF documentSize() const { return QSizeF(); } |
227 | QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); } |
228 | QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); } |
229 | void documentChanged(int, int, int) {} |
230 | }; |
231 | |
232 | QString tst_QTextDocument::cssFontSizeString(const QFont &font) |
233 | { |
234 | return font.pointSize() >= 0 |
235 | ? QString::number(font.pointSizeF()) + QStringLiteral("pt" ) |
236 | : QString::number(font.pixelSize()) + QStringLiteral("px" ); |
237 | } |
238 | |
239 | void tst_QTextDocument::writeActualAndExpected(const char *testTag, const QString &actual, const QString &expected) |
240 | { |
241 | #ifdef DEBUG_WRITE_OUTPUT |
242 | { |
243 | QFile out(QDir::temp().absoluteFilePath(QLatin1String(testTag) + QLatin1String("-actual.html" ))); |
244 | out.open(QFile::WriteOnly); |
245 | out.write(actual.toUtf8()); |
246 | out.close(); |
247 | } { |
248 | QFile out(QDir::temp().absoluteFilePath(QLatin1String(testTag) + QLatin1String("-expected.html" ))); |
249 | out.open(QFile::WriteOnly); |
250 | out.write(expected.toUtf8()); |
251 | out.close(); |
252 | } |
253 | #else |
254 | Q_UNUSED(testTag) |
255 | Q_UNUSED(actual) |
256 | Q_UNUSED(expected) |
257 | #endif |
258 | } |
259 | |
260 | // Testing get/set functions |
261 | void tst_QTextDocument::getSetCheck() |
262 | { |
263 | QTextDocument obj1; |
264 | // QAbstractTextDocumentLayout * QTextDocument::documentLayout() |
265 | // void QTextDocument::setDocumentLayout(QAbstractTextDocumentLayout *) |
266 | QPointer<MyAbstractTextDocumentLayout> var1 = new MyAbstractTextDocumentLayout(0); |
267 | obj1.setDocumentLayout(var1); |
268 | QCOMPARE(static_cast<QAbstractTextDocumentLayout *>(var1), obj1.documentLayout()); |
269 | obj1.setDocumentLayout((QAbstractTextDocumentLayout *)0); |
270 | QVERIFY(var1.isNull()); |
271 | QVERIFY(obj1.documentLayout()); |
272 | |
273 | // bool QTextDocument::useDesignMetrics() |
274 | // void QTextDocument::setUseDesignMetrics(bool) |
275 | obj1.setUseDesignMetrics(false); |
276 | QCOMPARE(false, obj1.useDesignMetrics()); |
277 | obj1.setUseDesignMetrics(true); |
278 | QCOMPARE(true, obj1.useDesignMetrics()); |
279 | } |
280 | |
281 | tst_QTextDocument::tst_QTextDocument() |
282 | { |
283 | QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); |
284 | img.save(fileName: "foo.png" ); |
285 | } |
286 | |
287 | void tst_QTextDocument::init() |
288 | { |
289 | doc = new QTextDocument; |
290 | cursor = QTextCursor(doc); |
291 | defaultFont = QFont(); |
292 | |
293 | htmlHead = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
294 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
295 | "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" |
296 | "p, li { white-space: pre-wrap; }\n" |
297 | "</style></head>" |
298 | "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\">\n" ); |
299 | htmlHead = htmlHead |
300 | .arg(a: defaultFont.family()) |
301 | .arg(a: cssFontSizeString(font: defaultFont)) |
302 | .arg(a: defaultFont.weight() * 8) |
303 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
304 | |
305 | htmlTail = QString("</body></html>" ); |
306 | } |
307 | |
308 | void tst_QTextDocument::cleanup() |
309 | { |
310 | cursor = QTextCursor(); |
311 | delete doc; |
312 | doc = 0; |
313 | } |
314 | |
315 | void tst_QTextDocument::cleanupTestCase() |
316 | { |
317 | QFile::remove(fileName: QLatin1String("foo.png" )); |
318 | } |
319 | |
320 | void tst_QTextDocument::isEmpty() |
321 | { |
322 | QVERIFY(doc->isEmpty()); |
323 | } |
324 | |
325 | void tst_QTextDocument::find_data() |
326 | { |
327 | QTest::addColumn<QString>(name: "haystack" ); |
328 | QTest::addColumn<QString>(name: "needle" ); |
329 | QTest::addColumn<int>(name: "flags" ); |
330 | QTest::addColumn<int>(name: "from" ); |
331 | QTest::addColumn<int>(name: "anchor" ); |
332 | QTest::addColumn<int>(name: "position" ); |
333 | |
334 | QTest::newRow(dataTag: "1" ) << "Hello World" << "World" << int(QTextDocument::FindCaseSensitively) << 0 << 6 << 11; |
335 | |
336 | QTest::newRow(dataTag: "2" ) << QString::fromLatin1(str: "Hello" ) + QString(QChar::ParagraphSeparator) + QString::fromLatin1(str: "World" ) |
337 | << "World" << int(QTextDocument::FindCaseSensitively) << 1 << 6 << 11; |
338 | |
339 | QTest::newRow(dataTag: "3" ) << QString::fromLatin1(str: "Hello" ) + QString(QChar::ParagraphSeparator) + QString::fromLatin1(str: "World" ) |
340 | << "Hello" << int(QTextDocument::FindCaseSensitively | QTextDocument::FindBackward) << 10 << 0 << 5; |
341 | QTest::newRow(dataTag: "4wholewords" ) << QString::fromLatin1(str: "Hello Blah World" ) |
342 | << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 6 << 10; |
343 | QTest::newRow(dataTag: "5wholewords" ) << QString::fromLatin1(str: "HelloBlahWorld" ) |
344 | << "Blah" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1; |
345 | QTest::newRow(dataTag: "6wholewords" ) << QString::fromLatin1(str: "HelloBlahWorld Blah Hah" ) |
346 | << "Blah" << int(QTextDocument::FindWholeWords) << 0 << 15 << 19; |
347 | QTest::newRow(dataTag: "7wholewords" ) << QString::fromLatin1(str: "HelloBlahWorld Blah Hah" ) |
348 | << "Blah" << int(QTextDocument::FindWholeWords | QTextDocument::FindBackward) << 23 << 15 << 19; |
349 | QTest::newRow(dataTag: "8wholewords" ) << QString::fromLatin1(str: "Hello: World\n" ) |
350 | << "orld" << int(QTextDocument::FindWholeWords) << 0 << -1 << -1; |
351 | |
352 | QTest::newRow(dataTag: "across-paragraphs" ) << QString::fromLatin1(str: "First Parag\nSecond Parag with a lot more text" ) |
353 | << "Parag" << int(QTextDocument::FindBackward) |
354 | << 15 << 6 << 11; |
355 | |
356 | QTest::newRow(dataTag: "nbsp" ) << "Hello" + QString(QChar(QChar::Nbsp)) +"World" << " " << int(QTextDocument::FindCaseSensitively) << 0 << 5 << 6; |
357 | |
358 | QTest::newRow(dataTag: "from-the-end" ) << "Hello World" << "Hello World" << int(QTextDocument::FindCaseSensitively| QTextDocument::FindBackward) << 11 << 0 << 11; |
359 | |
360 | QTest::newRow(dataTag: "bw-cross-paras-1" ) << "a1\na2\nb1" << "a" << int(QTextDocument::FindBackward) << 7 << 3 << 4; |
361 | QTest::newRow(dataTag: "bw-cross-paras-2" ) << "a1\na2\nb1" << "a" << int(QTextDocument::FindBackward) << 6 << 3 << 4; |
362 | QTest::newRow(dataTag: "bw-cross-paras-3" ) << "a1\na2\nb1" << "a" << int(QTextDocument::FindBackward) << 5 << 3 << 4; |
363 | QTest::newRow(dataTag: "bw-cross-paras-4" ) << "a1\na2\nb1" << "a" << int(QTextDocument::FindBackward) << 3 << 0 << 1; |
364 | QTest::newRow(dataTag: "bw-cross-paras-5" ) << "xa\n\nb1" << "a" << int(QTextDocument::FindBackward) << 5 << 1 << 2; |
365 | QTest::newRow(dataTag: "bw-cross-paras-6" ) << "xa\n\nb1" << "a" << int(QTextDocument::FindBackward) << 4 << 1 << 2; |
366 | QTest::newRow(dataTag: "bw-cross-paras-7" ) << "xa\n\nb1" << "a" << int(QTextDocument::FindBackward) << 3 << 1 << 2; |
367 | QTest::newRow(dataTag: "bw-cross-paras-8" ) << "xa\n\nb1" << "a" << int(QTextDocument::FindBackward) << 2 << 1 << 2; |
368 | } |
369 | |
370 | void tst_QTextDocument::find() |
371 | { |
372 | QFETCH(QString, haystack); |
373 | QFETCH(QString, needle); |
374 | QFETCH(int, flags); |
375 | QFETCH(int, from); |
376 | QFETCH(int, anchor); |
377 | QFETCH(int, position); |
378 | |
379 | cursor.insertText(text: haystack); |
380 | cursor = doc->find(subString: needle, from, options: QTextDocument::FindFlags(flags)); |
381 | |
382 | if (anchor != -1) { |
383 | QCOMPARE(cursor.anchor(), anchor); |
384 | QCOMPARE(cursor.position(), position); |
385 | } else { |
386 | QVERIFY(cursor.isNull()); |
387 | } |
388 | |
389 | //search using a regular expression |
390 | QRegExp expr(needle); |
391 | expr.setPatternSyntax(QRegExp::FixedString); |
392 | QTextDocument::FindFlags flg(flags); |
393 | expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); |
394 | cursor = doc->find(expr, from, options: flg); |
395 | |
396 | if (anchor != -1) { |
397 | QCOMPARE(cursor.anchor(), anchor); |
398 | QCOMPARE(cursor.position(), position); |
399 | } else { |
400 | QVERIFY(cursor.isNull()); |
401 | } |
402 | } |
403 | |
404 | void tst_QTextDocument::findWithRegExp_data() |
405 | { |
406 | buildRegExpData(); |
407 | } |
408 | |
409 | void tst_QTextDocument::findWithRegExp() |
410 | { |
411 | QFETCH(QString, haystack); |
412 | QFETCH(QString, needle); |
413 | QFETCH(int, flags); |
414 | QFETCH(int, from); |
415 | QFETCH(int, anchor); |
416 | QFETCH(int, position); |
417 | |
418 | cursor.insertText(text: haystack); |
419 | //search using a regular expression |
420 | QRegExp expr(needle); |
421 | QTextDocument::FindFlags flg(flags); |
422 | expr.setCaseSensitivity((flg & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive); |
423 | cursor = doc->find(expr, from, options: flg); |
424 | |
425 | if (anchor != -1) { |
426 | QCOMPARE(cursor.anchor(), anchor); |
427 | QCOMPARE(cursor.position(), position); |
428 | } else { |
429 | QVERIFY(cursor.isNull()); |
430 | } |
431 | } |
432 | |
433 | void tst_QTextDocument::findWithRegularExpression_data() |
434 | { |
435 | buildRegExpData(); |
436 | } |
437 | |
438 | void tst_QTextDocument::findWithRegularExpression() |
439 | { |
440 | QFETCH(QString, haystack); |
441 | QFETCH(QString, needle); |
442 | QFETCH(int, flags); |
443 | QFETCH(int, from); |
444 | QFETCH(int, anchor); |
445 | QFETCH(int, position); |
446 | |
447 | cursor.insertText(text: haystack); |
448 | //search using a regular expression |
449 | QRegularExpression expr(needle); |
450 | QTextDocument::FindFlags flg(flags); |
451 | cursor = doc->find(expr, from, options: flg); |
452 | |
453 | if (anchor != -1) { |
454 | QCOMPARE(cursor.anchor(), anchor); |
455 | QCOMPARE(cursor.position(), position); |
456 | } else { |
457 | QVERIFY(cursor.isNull()); |
458 | } |
459 | } |
460 | |
461 | void tst_QTextDocument::find2() |
462 | { |
463 | doc->setPlainText("aaa" ); |
464 | cursor.movePosition(op: QTextCursor::Start); |
465 | cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::KeepAnchor); |
466 | QTextCursor hit = doc->find(subString: "a" , cursor); |
467 | QCOMPARE(hit.position(), 2); |
468 | QCOMPARE(hit.anchor(), 1); |
469 | } |
470 | |
471 | void tst_QTextDocument::findMultiple() |
472 | { |
473 | const QString text("foo bar baz foo bar baz" ); |
474 | doc->setPlainText(text); |
475 | |
476 | cursor.movePosition(op: QTextCursor::Start); |
477 | cursor = doc->find(subString: "bar" , cursor); |
478 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
479 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
480 | cursor = doc->find(subString: "bar" , cursor); |
481 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
482 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
483 | |
484 | cursor.movePosition(op: QTextCursor::End); |
485 | cursor = doc->find(subString: "bar" , cursor, options: QTextDocument::FindBackward); |
486 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
487 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
488 | cursor = doc->find(subString: "bar" , cursor, options: QTextDocument::FindBackward); |
489 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
490 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
491 | |
492 | |
493 | QRegExp expr("bar" ); |
494 | expr.setPatternSyntax(QRegExp::FixedString); |
495 | |
496 | cursor.movePosition(op: QTextCursor::End); |
497 | cursor = doc->find(expr, cursor, options: QTextDocument::FindBackward); |
498 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
499 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
500 | cursor = doc->find(expr, cursor, options: QTextDocument::FindBackward); |
501 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
502 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
503 | |
504 | cursor.movePosition(op: QTextCursor::Start); |
505 | cursor = doc->find(expr, cursor); |
506 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
507 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
508 | cursor = doc->find(expr, cursor); |
509 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
510 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
511 | |
512 | QRegularExpression regularExpression("bar" ); |
513 | |
514 | cursor.movePosition(op: QTextCursor::End); |
515 | cursor = doc->find(expr: regularExpression, cursor, options: QTextDocument::FindBackward); |
516 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
517 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
518 | cursor = doc->find(expr: regularExpression, cursor, options: QTextDocument::FindBackward); |
519 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
520 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
521 | |
522 | cursor.movePosition(op: QTextCursor::Start); |
523 | cursor = doc->find(expr: regularExpression, cursor); |
524 | QCOMPARE(cursor.selectionStart(), text.indexOf("bar" )); |
525 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
526 | cursor = doc->find(expr: regularExpression, cursor); |
527 | QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar" )); |
528 | QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3); |
529 | } |
530 | |
531 | void tst_QTextDocument::basicIsModifiedChecks() |
532 | { |
533 | QSignalSpy spy(doc, SIGNAL(modificationChanged(bool))); |
534 | |
535 | QVERIFY(!doc->isModified()); |
536 | cursor.insertText(text: "Hello World" ); |
537 | QVERIFY(doc->isModified()); |
538 | QCOMPARE(spy.count(), 1); |
539 | QVERIFY(spy.takeFirst().at(0).toBool()); |
540 | |
541 | doc->undo(); |
542 | QVERIFY(!doc->isModified()); |
543 | QCOMPARE(spy.count(), 1); |
544 | QVERIFY(!spy.takeFirst().at(0).toBool()); |
545 | |
546 | doc->redo(); |
547 | QVERIFY(doc->isModified()); |
548 | QCOMPARE(spy.count(), 1); |
549 | QVERIFY(spy.takeFirst().at(0).toBool()); |
550 | } |
551 | |
552 | void tst_QTextDocument::moreIsModified() |
553 | { |
554 | QVERIFY(!doc->isModified()); |
555 | |
556 | cursor.insertText(text: "Hello" ); |
557 | QVERIFY(doc->isModified()); |
558 | |
559 | doc->undo(); |
560 | QVERIFY(!doc->isModified()); |
561 | |
562 | cursor.insertText(text: "Hello" ); |
563 | |
564 | doc->undo(); |
565 | QVERIFY(!doc->isModified()); |
566 | } |
567 | |
568 | void tst_QTextDocument::isModified2() |
569 | { |
570 | // reported on qt4-preview-feedback |
571 | QVERIFY(!doc->isModified()); |
572 | |
573 | cursor.insertText(text: "Hello" ); |
574 | QVERIFY(doc->isModified()); |
575 | |
576 | doc->setModified(false); |
577 | QVERIFY(!doc->isModified()); |
578 | |
579 | cursor.insertText(text: "Hello" ); |
580 | QVERIFY(doc->isModified()); |
581 | } |
582 | |
583 | void tst_QTextDocument::isModified3() |
584 | { |
585 | QVERIFY(!doc->isModified()); |
586 | |
587 | doc->setUndoRedoEnabled(false); |
588 | doc->setUndoRedoEnabled(true); |
589 | |
590 | cursor.insertText(text: "Hello" ); |
591 | |
592 | QVERIFY(doc->isModified()); |
593 | doc->undo(); |
594 | QVERIFY(!doc->isModified()); |
595 | } |
596 | |
597 | void tst_QTextDocument::isModified4() |
598 | { |
599 | QVERIFY(!doc->isModified()); |
600 | |
601 | cursor.insertText(text: "Hello" ); |
602 | cursor.insertText(text: "World" ); |
603 | |
604 | doc->setModified(false); |
605 | |
606 | QVERIFY(!doc->isModified()); |
607 | |
608 | cursor.insertText(text: "Again" ); |
609 | QVERIFY(doc->isModified()); |
610 | |
611 | doc->undo(); |
612 | QVERIFY(!doc->isModified()); |
613 | doc->undo(); |
614 | QVERIFY(doc->isModified()); |
615 | |
616 | doc->redo(); |
617 | QVERIFY(!doc->isModified()); |
618 | doc->redo(); |
619 | QVERIFY(doc->isModified()); |
620 | |
621 | doc->undo(); |
622 | QVERIFY(!doc->isModified()); |
623 | doc->undo(); |
624 | QVERIFY(doc->isModified()); |
625 | |
626 | //task 197769 |
627 | cursor.insertText(text: "Hello" ); |
628 | QVERIFY(doc->isModified()); |
629 | } |
630 | |
631 | void tst_QTextDocument::noundo_basicIsModifiedChecks() |
632 | { |
633 | doc->setUndoRedoEnabled(false); |
634 | QSignalSpy spy(doc, SIGNAL(modificationChanged(bool))); |
635 | |
636 | QVERIFY(!doc->isModified()); |
637 | cursor.insertText(text: "Hello World" ); |
638 | QVERIFY(doc->isModified()); |
639 | QCOMPARE(spy.count(), 1); |
640 | QVERIFY(spy.takeFirst().at(0).toBool()); |
641 | |
642 | doc->undo(); |
643 | QVERIFY(doc->isModified()); |
644 | QCOMPARE(spy.count(), 0); |
645 | |
646 | doc->redo(); |
647 | QVERIFY(doc->isModified()); |
648 | QCOMPARE(spy.count(), 0); |
649 | } |
650 | |
651 | void tst_QTextDocument::task240325() |
652 | { |
653 | doc->setHtml("<html><img width=\"100\" height=\"100\" align=\"right\"/>Foobar Foobar Foobar Foobar</html>" ); |
654 | |
655 | QImage img(1000, 7000, QImage::Format_ARGB32_Premultiplied); |
656 | QPainter p(&img); |
657 | QFontMetrics fm(p.font()); |
658 | |
659 | // Set page size to contain image and one "Foobar" |
660 | doc->setPageSize(QSize(100 + fm.horizontalAdvance("Foobar" )*2, 1000)); |
661 | |
662 | // Force layout |
663 | doc->drawContents(painter: &p); |
664 | |
665 | QCOMPARE(doc->blockCount(), 1); |
666 | for (QTextBlock block = doc->begin() ; block!=doc->end() ; block = block.next()) { |
667 | QTextLayout *layout = block.layout(); |
668 | #ifdef Q_OS_ANDROID |
669 | QEXPECT_FAIL("" , "QTBUG-69242" , Abort); |
670 | #endif |
671 | QCOMPARE(layout->lineCount(), 4); |
672 | |
673 | for (int lineIdx=0;lineIdx<layout->lineCount();++lineIdx) { |
674 | QTextLine line = layout->lineAt(i: lineIdx); |
675 | |
676 | QString text = block.text().mid(position: line.textStart(), n: line.textLength()).trimmed(); |
677 | |
678 | // Remove start token |
679 | if (lineIdx == 0) |
680 | text = text.mid(position: 1); |
681 | |
682 | QCOMPARE(text, QString::fromLatin1("Foobar" )); |
683 | } |
684 | } |
685 | } |
686 | |
687 | void tst_QTextDocument::stylesheetFont_data() |
688 | { |
689 | QTest::addColumn<QString>(name: "stylesheet" ); |
690 | QTest::addColumn<QFont>(name: "font" ); |
691 | |
692 | { |
693 | QFont font; |
694 | font.setBold(true); |
695 | font.setPixelSize(64); |
696 | |
697 | QTest::newRow(dataTag: "Regular font specification" ) |
698 | << "font-size: 64px; font-weight: bold;" |
699 | << font; |
700 | } |
701 | |
702 | |
703 | { |
704 | QFont font; |
705 | font.setBold(true); |
706 | font.setPixelSize(64); |
707 | |
708 | QTest::newRow(dataTag: "Shorthand font specification" ) |
709 | << "font: normal bold 64px Arial;" |
710 | << font; |
711 | } |
712 | |
713 | } |
714 | |
715 | void tst_QTextDocument::stylesheetFont() |
716 | { |
717 | QFETCH(QString, stylesheet); |
718 | QFETCH(QFont, font); |
719 | |
720 | QString html = QString::fromLatin1(str: "<html>" |
721 | "<body>" |
722 | "<div style=\"%1\" >" |
723 | "Foobar" |
724 | "</div>" |
725 | "</body>" |
726 | "</html>" ).arg(a: stylesheet); |
727 | |
728 | qDebug() << html; |
729 | doc->setHtml(html); |
730 | QCOMPARE(doc->blockCount(), 1); |
731 | |
732 | // First and only block |
733 | QTextBlock block = doc->firstBlock(); |
734 | |
735 | QString text = block.text(); |
736 | QCOMPARE(text, QString::fromLatin1("Foobar" )); |
737 | |
738 | QFont actualFont = block.charFormat().font(); |
739 | |
740 | QCOMPARE(actualFont.bold(), font.bold()); |
741 | QCOMPARE(actualFont.pixelSize(), font.pixelSize()); |
742 | } |
743 | |
744 | void tst_QTextDocument::preFont() |
745 | { |
746 | const QFont font = QFontDatabase::systemFont(type: QFontDatabase::FixedFont); |
747 | const QString html = QString::fromLatin1( str: "<html>" |
748 | "<body>" |
749 | "<pre>" |
750 | "Foobar" |
751 | "</pre>" |
752 | "</body>" |
753 | "</html>" ); |
754 | |
755 | doc->setHtml(html); |
756 | QCOMPARE(doc->blockCount(), 1); |
757 | |
758 | // First and only block |
759 | QTextBlock block = doc->firstBlock(); |
760 | |
761 | QString text = block.text(); |
762 | QCOMPARE(text, QString::fromLatin1("Foobar" )); |
763 | |
764 | QFont actualFont = block.charFormat().font(); |
765 | QCOMPARE(actualFont.family(), font.family()); |
766 | } |
767 | |
768 | void tst_QTextDocument::noundo_moreIsModified() |
769 | { |
770 | doc->setUndoRedoEnabled(false); |
771 | QVERIFY(!doc->isModified()); |
772 | |
773 | cursor.insertText(text: "Hello" ); |
774 | QVERIFY(doc->isModified()); |
775 | |
776 | doc->undo(); |
777 | QVERIFY(doc->isModified()); |
778 | |
779 | cursor.insertText(text: "Hello" ); |
780 | |
781 | doc->undo(); |
782 | QVERIFY(doc->isModified()); |
783 | } |
784 | |
785 | void tst_QTextDocument::noundo_isModified2() |
786 | { |
787 | // reported on qt4-preview-feedback |
788 | QVERIFY(!doc->isModified()); |
789 | |
790 | cursor.insertText(text: "Hello" ); |
791 | QVERIFY(doc->isModified()); |
792 | |
793 | doc->setModified(false); |
794 | QVERIFY(!doc->isModified()); |
795 | |
796 | cursor.insertText(text: "Hello" ); |
797 | QVERIFY(doc->isModified()); |
798 | } |
799 | |
800 | void tst_QTextDocument::noundo_isModified3() |
801 | { |
802 | doc->setUndoRedoEnabled(false); |
803 | QVERIFY(!doc->isModified()); |
804 | |
805 | cursor.insertText(text: "Hello" ); |
806 | |
807 | QVERIFY(doc->isModified()); |
808 | doc->undo(); |
809 | QVERIFY(doc->isModified()); |
810 | } |
811 | |
812 | void tst_QTextDocument::mightBeRichText_data() |
813 | { |
814 | const char [] = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" |
815 | "<!DOCTYPE html\n" |
816 | " PUBLIC " "-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n" |
817 | "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">" ; |
818 | QVERIFY(Qt::mightBeRichText(QString::fromLatin1(qtDocuHeader))); |
819 | QTest::addColumn<QString>(name: "input" ); |
820 | QTest::addColumn<bool>(name: "result" ); |
821 | |
822 | QTest::newRow(dataTag: "documentation-header" ) << QString("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" |
823 | "<!DOCTYPE html\n" |
824 | " PUBLIC " "-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n" |
825 | "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">" ) |
826 | << true; |
827 | QTest::newRow(dataTag: "br-nospace" ) << QString("Test <br/> new line" ) << true; |
828 | QTest::newRow(dataTag: "br-space" ) << QString("Test <br /> new line" ) << true; |
829 | QTest::newRow(dataTag: "br-invalidspace" ) << QString("Test <br/ > new line" ) << false; |
830 | QTest::newRow(dataTag: "invalid closing tag" ) << QString("Test <br/ line" ) << false; |
831 | } |
832 | |
833 | void tst_QTextDocument::mightBeRichText() |
834 | { |
835 | QFETCH(QString, input); |
836 | QFETCH(bool, result); |
837 | QCOMPARE(result, Qt::mightBeRichText(input)); |
838 | } |
839 | |
840 | Q_DECLARE_METATYPE(QTextDocumentFragment) |
841 | |
842 | #define CREATE_DOC_AND_CURSOR() \ |
843 | QTextDocument doc; \ |
844 | doc.setDefaultFont(defaultFont); \ |
845 | QTextCursor cursor(&doc); |
846 | |
847 | void tst_QTextDocument::toHtml_data() |
848 | { |
849 | QTest::addColumn<QTextDocumentFragment>(name: "input" ); |
850 | QTest::addColumn<QString>(name: "expectedOutput" ); |
851 | |
852 | { |
853 | CREATE_DOC_AND_CURSOR(); |
854 | |
855 | cursor.insertText(text: "Blah" ); |
856 | |
857 | QTest::newRow(dataTag: "simple" ) << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>Blah</p>" ); |
858 | } |
859 | |
860 | { |
861 | CREATE_DOC_AND_CURSOR(); |
862 | |
863 | cursor.insertText(text: "&<>" ); |
864 | |
865 | QTest::newRow(dataTag: "entities" ) << QTextDocumentFragment(&doc) << QString("<p DEFAULTBLOCKSTYLE>&<></p>" ); |
866 | } |
867 | |
868 | { |
869 | CREATE_DOC_AND_CURSOR(); |
870 | |
871 | QTextCharFormat fmt; |
872 | fmt.setFontFamily("Times" ); |
873 | cursor.insertText(text: "Blah" , format: fmt); |
874 | |
875 | QTest::newRow(dataTag: "font-family" ) << QTextDocumentFragment(&doc) |
876 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Times';\">Blah</span></p>" ); |
877 | } |
878 | |
879 | { |
880 | CREATE_DOC_AND_CURSOR(); |
881 | |
882 | QTextCharFormat fmt; |
883 | fmt.setFontFamily("Foo's Family" ); |
884 | cursor.insertText(text: "Blah" , format: fmt); |
885 | |
886 | QTest::newRow(dataTag: "font-family-with-quotes1" ) << QTextDocumentFragment(&doc) |
887 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:"Foo's Family";\">Blah</span></p>" ); |
888 | } |
889 | |
890 | { |
891 | CREATE_DOC_AND_CURSOR(); |
892 | |
893 | QTextCharFormat fmt; |
894 | fmt.setFontFamily("Foo\"s Family" ); |
895 | cursor.insertText(text: "Blah" , format: fmt); |
896 | |
897 | QTest::newRow(dataTag: "font-family-with-quotes2" ) << QTextDocumentFragment(&doc) |
898 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Foo"s Family';\">Blah</span></p>" ); |
899 | } |
900 | |
901 | { |
902 | CREATE_DOC_AND_CURSOR(); |
903 | |
904 | QTextCharFormat fmt; |
905 | fmt.setFontFamily("Times" ); |
906 | fmt.setFontFamilies(QStringList{ "Times" , "serif" }); |
907 | cursor.insertText(text: "Blah" , format: fmt); |
908 | |
909 | QTest::newRow(dataTag: "font-family-with-fallback" ) << QTextDocumentFragment(&doc) |
910 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-family:'Times','serif';\">Blah</span></p>" ); |
911 | } |
912 | |
913 | { |
914 | CREATE_DOC_AND_CURSOR(); |
915 | |
916 | QTextBlockFormat fmt; |
917 | fmt.setNonBreakableLines(true); |
918 | cursor.insertBlock(format: fmt); |
919 | cursor.insertText(text: "Blah" ); |
920 | |
921 | QTest::newRow(dataTag: "pre" ) << QTextDocumentFragment(&doc) |
922 | << |
923 | QString("EMPTYBLOCK" ) + |
924 | QString("<pre DEFAULTBLOCKSTYLE>Blah</pre>" ); |
925 | } |
926 | |
927 | { |
928 | CREATE_DOC_AND_CURSOR(); |
929 | |
930 | QTextCharFormat fmt; |
931 | fmt.setFontPointSize(40); |
932 | cursor.insertText(text: "Blah" , format: fmt); |
933 | |
934 | QTest::newRow(dataTag: "font-size" ) << QTextDocumentFragment(&doc) |
935 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:40pt;\">Blah</span></p>" ); |
936 | } |
937 | |
938 | { |
939 | CREATE_DOC_AND_CURSOR(); |
940 | |
941 | QTextCharFormat fmt; |
942 | fmt.setProperty(propertyId: QTextFormat::FontSizeIncrement, value: 2); |
943 | cursor.insertText(text: "Blah" , format: fmt); |
944 | |
945 | QTest::newRow(dataTag: "logical-font-size" ) << QTextDocumentFragment(&doc) |
946 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-size:x-large;\">Blah</span></p>" ); |
947 | } |
948 | |
949 | { |
950 | CREATE_DOC_AND_CURSOR(); |
951 | |
952 | cursor.insertText(text: "Foo" ); |
953 | |
954 | QTextCharFormat fmt; |
955 | fmt.setFontPointSize(40); |
956 | cursor.insertBlock(format: QTextBlockFormat(), charFormat: fmt); |
957 | |
958 | fmt.clearProperty(propertyId: QTextFormat::FontPointSize); |
959 | cursor.insertText(text: "Blub" , format: fmt); |
960 | |
961 | QTest::newRow(dataTag: "no-font-size" ) << QTextDocumentFragment(&doc) |
962 | << 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>" ); |
963 | } |
964 | |
965 | { |
966 | CREATE_DOC_AND_CURSOR(); |
967 | |
968 | QTextBlockFormat fmt; |
969 | fmt.setLayoutDirection(Qt::RightToLeft); |
970 | cursor.insertBlock(format: fmt); |
971 | cursor.insertText(text: "Blah" ); |
972 | |
973 | QTest::newRow(dataTag: "rtl" ) << QTextDocumentFragment(&doc) |
974 | << |
975 | QString("EMPTYBLOCK" ) + |
976 | QString("<p dir='rtl' DEFAULTBLOCKSTYLE>Blah</p>" ); |
977 | } |
978 | |
979 | { |
980 | CREATE_DOC_AND_CURSOR(); |
981 | |
982 | QTextBlockFormat fmt; |
983 | fmt.setAlignment(Qt::AlignJustify); |
984 | cursor.insertBlock(format: fmt); |
985 | cursor.insertText(text: "Blah" ); |
986 | |
987 | QTest::newRow(dataTag: "blockalign" ) << QTextDocumentFragment(&doc) |
988 | << |
989 | QString("EMPTYBLOCK" ) + |
990 | QString("<p align=\"justify\" DEFAULTBLOCKSTYLE>Blah</p>" ); |
991 | } |
992 | |
993 | { |
994 | CREATE_DOC_AND_CURSOR(); |
995 | |
996 | QTextBlockFormat fmt; |
997 | fmt.setAlignment(Qt::AlignCenter); |
998 | cursor.insertBlock(format: fmt); |
999 | cursor.insertText(text: "Blah" ); |
1000 | |
1001 | QTest::newRow(dataTag: "blockalign2" ) << QTextDocumentFragment(&doc) |
1002 | << |
1003 | QString("EMPTYBLOCK" ) + |
1004 | QString("<p align=\"center\" DEFAULTBLOCKSTYLE>Blah</p>" ); |
1005 | } |
1006 | |
1007 | { |
1008 | CREATE_DOC_AND_CURSOR(); |
1009 | |
1010 | QTextBlockFormat fmt; |
1011 | fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute); |
1012 | cursor.insertBlock(format: fmt); |
1013 | cursor.insertText(text: "Blah" ); |
1014 | |
1015 | QTest::newRow(dataTag: "blockalign3" ) << QTextDocumentFragment(&doc) |
1016 | << |
1017 | QString("EMPTYBLOCK" ) + |
1018 | QString("<p align=\"right\" DEFAULTBLOCKSTYLE>Blah</p>" ); |
1019 | } |
1020 | |
1021 | { |
1022 | CREATE_DOC_AND_CURSOR(); |
1023 | |
1024 | QTextBlockFormat fmt; |
1025 | fmt.setBackground(QColor("#0000ff" )); |
1026 | cursor.insertBlock(format: fmt); |
1027 | cursor.insertText(text: "Blah" ); |
1028 | |
1029 | QTest::newRow(dataTag: "bgcolor" ) << QTextDocumentFragment(&doc) |
1030 | << QString("EMPTYBLOCK" ) + |
1031 | QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\">Blah</p>" ); |
1032 | } |
1033 | |
1034 | { |
1035 | CREATE_DOC_AND_CURSOR(); |
1036 | |
1037 | QTextBlockFormat fmt; |
1038 | fmt.setBackground(QColor(255, 0, 0, 51)); |
1039 | cursor.insertBlock(format: fmt); |
1040 | cursor.insertText(text: "Blah" ); |
1041 | |
1042 | QTest::newRow(dataTag: "bgcolor-rgba" ) << QTextDocumentFragment(&doc) |
1043 | << QString("EMPTYBLOCK" ) + |
1044 | QString("<p OPENDEFAULTBLOCKSTYLE background-color:rgba(255,0,0,0.2);\">Blah</p>" ); |
1045 | } |
1046 | |
1047 | { |
1048 | CREATE_DOC_AND_CURSOR(); |
1049 | |
1050 | QTextBlockFormat fmt; |
1051 | fmt.setBackground(QColor(255, 0, 0, 0)); |
1052 | cursor.insertBlock(format: fmt); |
1053 | cursor.insertText(text: "Blah" ); |
1054 | |
1055 | QTest::newRow(dataTag: "bgcolor-transparent" ) << QTextDocumentFragment(&doc) |
1056 | << QString("EMPTYBLOCK" ) + |
1057 | QString("<p OPENDEFAULTBLOCKSTYLE background-color:transparent;\">Blah</p>" ); |
1058 | } |
1059 | |
1060 | { |
1061 | CREATE_DOC_AND_CURSOR(); |
1062 | |
1063 | QTextCharFormat fmt; |
1064 | fmt.setFontWeight(40); |
1065 | cursor.insertText(text: "Blah" , format: fmt); |
1066 | |
1067 | QTest::newRow(dataTag: "font-weight" ) << QTextDocumentFragment(&doc) |
1068 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-weight:320;\">Blah</span></p>" ); |
1069 | } |
1070 | |
1071 | { |
1072 | CREATE_DOC_AND_CURSOR(); |
1073 | |
1074 | QTextCharFormat fmt; |
1075 | fmt.setFontItalic(true); |
1076 | cursor.insertText(text: "Blah" , format: fmt); |
1077 | |
1078 | QTest::newRow(dataTag: "font-italic" ) << QTextDocumentFragment(&doc) |
1079 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" font-style:italic;\">Blah</span></p>" ); |
1080 | } |
1081 | |
1082 | { |
1083 | CREATE_DOC_AND_CURSOR(); |
1084 | |
1085 | QTextCharFormat fmt; |
1086 | fmt.setFontUnderline(true); |
1087 | fmt.setFontOverline(false); |
1088 | cursor.insertText(text: "Blah" , format: fmt); |
1089 | |
1090 | QTest::newRow(dataTag: "text-decoration-1" ) << QTextDocumentFragment(&doc) |
1091 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" text-decoration: underline;\">Blah</span></p>" ); |
1092 | } |
1093 | |
1094 | { |
1095 | CREATE_DOC_AND_CURSOR(); |
1096 | |
1097 | QTextCharFormat fmt; |
1098 | fmt.setForeground(QColor("#00ff00" )); |
1099 | cursor.insertText(text: "Blah" , format: fmt); |
1100 | |
1101 | QTest::newRow(dataTag: "color" ) << QTextDocumentFragment(&doc) |
1102 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:#00ff00;\">Blah</span></p>" ); |
1103 | } |
1104 | |
1105 | { |
1106 | CREATE_DOC_AND_CURSOR(); |
1107 | |
1108 | QTextCharFormat fmt; |
1109 | fmt.setForeground(QColor(0, 255, 0, 51)); |
1110 | cursor.insertText(text: "Blah" , format: fmt); |
1111 | |
1112 | QTest::newRow(dataTag: "color-rgba" ) << QTextDocumentFragment(&doc) |
1113 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:rgba(0,255,0,0.2);\">Blah</span></p>" ); |
1114 | } |
1115 | |
1116 | { |
1117 | CREATE_DOC_AND_CURSOR(); |
1118 | |
1119 | QTextCharFormat fmt; |
1120 | fmt.setForeground(QColor(0, 255, 0, 0)); |
1121 | cursor.insertText(text: "Blah" , format: fmt); |
1122 | |
1123 | QTest::newRow(dataTag: "color-transparent" ) << QTextDocumentFragment(&doc) |
1124 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" color:transparent;\">Blah</span></p>" ); |
1125 | } |
1126 | |
1127 | { |
1128 | CREATE_DOC_AND_CURSOR(); |
1129 | |
1130 | QTextCharFormat fmt; |
1131 | fmt.setBackground(QColor("#00ff00" )); |
1132 | cursor.insertText(text: "Blah" , format: fmt); |
1133 | |
1134 | QTest::newRow(dataTag: "span-bgcolor" ) << QTextDocumentFragment(&doc) |
1135 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>" ); |
1136 | } |
1137 | |
1138 | { |
1139 | CREATE_DOC_AND_CURSOR(); |
1140 | |
1141 | QTextCharFormat fmt; |
1142 | fmt.setBackground(QColor(0, 255, 0, 51)); |
1143 | cursor.insertText(text: "Blah" , format: fmt); |
1144 | |
1145 | QTest::newRow(dataTag: "span-bgcolor-rgba" ) << QTextDocumentFragment(&doc) |
1146 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:rgba(0,255,0,0.2);\">Blah</span></p>" ); |
1147 | } |
1148 | |
1149 | { |
1150 | CREATE_DOC_AND_CURSOR(); |
1151 | |
1152 | QTextCharFormat fmt; |
1153 | fmt.setBackground(QColor(0, 255, 0, 0)); |
1154 | cursor.insertText(text: "Blah" , format: fmt); |
1155 | |
1156 | QTest::newRow(dataTag: "span-bgcolor-transparent" ) << QTextDocumentFragment(&doc) |
1157 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:transparent;\">Blah</span></p>" ); |
1158 | } |
1159 | |
1160 | { |
1161 | CREATE_DOC_AND_CURSOR(); |
1162 | |
1163 | QTextCharFormat fmt; |
1164 | fmt.setVerticalAlignment(QTextCharFormat::AlignSubScript); |
1165 | cursor.insertText(text: "Blah" , format: fmt); |
1166 | |
1167 | QTest::newRow(dataTag: "valign-sub" ) << QTextDocumentFragment(&doc) |
1168 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:sub;\">Blah</span></p>" ); |
1169 | |
1170 | } |
1171 | |
1172 | { |
1173 | CREATE_DOC_AND_CURSOR(); |
1174 | |
1175 | QTextCharFormat fmt; |
1176 | fmt.setVerticalAlignment(QTextCharFormat::AlignSuperScript); |
1177 | cursor.insertText(text: "Blah" , format: fmt); |
1178 | |
1179 | QTest::newRow(dataTag: "valign-super" ) << QTextDocumentFragment(&doc) |
1180 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" vertical-align:super;\">Blah</span></p>" ); |
1181 | |
1182 | } |
1183 | |
1184 | { |
1185 | CREATE_DOC_AND_CURSOR(); |
1186 | |
1187 | QTextCharFormat fmt; |
1188 | fmt.setAnchor(true); |
1189 | fmt.setAnchorNames({"blub" }); |
1190 | cursor.insertText(text: "Blah" , format: fmt); |
1191 | |
1192 | QTest::newRow(dataTag: "named anchor" ) << QTextDocumentFragment(&doc) |
1193 | << QString("<p DEFAULTBLOCKSTYLE><a name=\"blub\"></a>Blah</p>" ); |
1194 | } |
1195 | |
1196 | { |
1197 | CREATE_DOC_AND_CURSOR(); |
1198 | |
1199 | QTextCharFormat fmt; |
1200 | fmt.setAnchor(true); |
1201 | fmt.setAnchorHref("http://www.kde.org/" ); |
1202 | cursor.insertText(text: "Blah" , format: fmt); |
1203 | |
1204 | QTest::newRow(dataTag: "href anchor" ) << QTextDocumentFragment(&doc) |
1205 | << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/\">Blah</a></p>" ); |
1206 | } |
1207 | |
1208 | { |
1209 | CREATE_DOC_AND_CURSOR(); |
1210 | |
1211 | QTextCharFormat fmt; |
1212 | fmt.setAnchor(true); |
1213 | fmt.setAnchorHref("http://www.kde.org/?a=1&b=2" ); |
1214 | cursor.insertText(text: "Blah" , format: fmt); |
1215 | |
1216 | QTest::newRow(dataTag: "href anchor with &" ) << QTextDocumentFragment(&doc) |
1217 | << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a=1&b=2\">Blah</a></p>" ); |
1218 | } |
1219 | |
1220 | { |
1221 | CREATE_DOC_AND_CURSOR(); |
1222 | |
1223 | QTextCharFormat fmt; |
1224 | fmt.setAnchor(true); |
1225 | fmt.setAnchorHref("http://www.kde.org/?a='&b=\"" ); |
1226 | cursor.insertText(text: "Blah" , format: fmt); |
1227 | |
1228 | QTest::newRow(dataTag: "href anchor with ' and \"" ) << QTextDocumentFragment(&doc) |
1229 | << QString("<p DEFAULTBLOCKSTYLE><a href=\"http://www.kde.org/?a='&b="\">Blah</a></p>" ); |
1230 | } |
1231 | |
1232 | { |
1233 | CREATE_DOC_AND_CURSOR(); |
1234 | |
1235 | cursor.insertTable(rows: 2, cols: 2); |
1236 | |
1237 | QTest::newRow(dataTag: "simpletable" ) << QTextDocumentFragment(&doc) |
1238 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1239 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1240 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1241 | "</table>" ); |
1242 | } |
1243 | |
1244 | { |
1245 | CREATE_DOC_AND_CURSOR(); |
1246 | |
1247 | QTextTable *table = cursor.insertTable(rows: 1, cols: 4); |
1248 | table->mergeCells(row: 0, col: 0, numRows: 1, numCols: 2); |
1249 | table->mergeCells(row: 0, col: 2, numRows: 1, numCols: 2); |
1250 | |
1251 | QTest::newRow(dataTag: "tablespans" ) << QTextDocumentFragment(&doc) |
1252 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1253 | "\n<tr>\n<td colspan=\"2\"></td>\n<td colspan=\"2\"></td></tr>" |
1254 | "</table>" ); |
1255 | } |
1256 | |
1257 | { |
1258 | CREATE_DOC_AND_CURSOR(); |
1259 | |
1260 | QTextTableFormat fmt; |
1261 | fmt.setBorder(1); |
1262 | fmt.setCellSpacing(3); |
1263 | fmt.setCellPadding(3); |
1264 | fmt.setBackground(QColor("#ff00ff" )); |
1265 | fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50)); |
1266 | fmt.setAlignment(Qt::AlignHCenter); |
1267 | fmt.setPosition(QTextFrameFormat::FloatRight); |
1268 | cursor.insertTable(rows: 2, cols: 2, format: fmt); |
1269 | |
1270 | QTest::newRow(dataTag: "tableattrs" ) << QTextDocumentFragment(&doc) |
1271 | << QString("<table border=\"1\" style=\" float: right;\" align=\"center\" width=\"50%\" cellspacing=\"3\" cellpadding=\"3\" bgcolor=\"#ff00ff\">" |
1272 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1273 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1274 | "</table>" ); |
1275 | } |
1276 | |
1277 | { |
1278 | CREATE_DOC_AND_CURSOR(); |
1279 | |
1280 | QTextTableFormat fmt; |
1281 | fmt.setBorder(1); |
1282 | fmt.setCellSpacing(3); |
1283 | fmt.setCellPadding(3); |
1284 | fmt.setBackground(QColor("#ff00ff" )); |
1285 | fmt.setWidth(QTextLength(QTextLength::PercentageLength, 50)); |
1286 | fmt.setAlignment(Qt::AlignHCenter); |
1287 | fmt.setPosition(QTextFrameFormat::FloatRight); |
1288 | fmt.setLeftMargin(25); |
1289 | fmt.setBottomMargin(35); |
1290 | cursor.insertTable(rows: 2, cols: 2, format: fmt); |
1291 | |
1292 | QTest::newRow(dataTag: "tableattrs2" ) << QTextDocumentFragment(&doc) |
1293 | << 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\">" |
1294 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1295 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1296 | "</table>" ); |
1297 | } |
1298 | |
1299 | { |
1300 | CREATE_DOC_AND_CURSOR(); |
1301 | |
1302 | QTextTableFormat fmt; |
1303 | fmt.setHeaderRowCount(2); |
1304 | cursor.insertTable(rows: 4, cols: 2, format: fmt); |
1305 | |
1306 | QTest::newRow(dataTag: "tableheader" ) << QTextDocumentFragment(&doc) |
1307 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1308 | "<thead>\n<tr>\n<td></td>\n<td></td></tr>" |
1309 | "\n<tr>\n<td></td>\n<td></td></tr></thead>" |
1310 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1311 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1312 | "</table>" ); |
1313 | } |
1314 | |
1315 | { |
1316 | CREATE_DOC_AND_CURSOR(); |
1317 | |
1318 | QTextTable *table = cursor.insertTable(rows: 2, cols: 2); |
1319 | QTextTable *subTable = table->cellAt(row: 0, col: 1).firstCursorPosition().insertTable(rows: 1, cols: 1); |
1320 | subTable->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "Hey" ); |
1321 | |
1322 | QTest::newRow(dataTag: "nestedtable" ) << QTextDocumentFragment(&doc) |
1323 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1324 | "\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>" |
1325 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1326 | "</table>" ); |
1327 | } |
1328 | |
1329 | { |
1330 | CREATE_DOC_AND_CURSOR(); |
1331 | |
1332 | QTextTableFormat fmt; |
1333 | QVector<QTextLength> widths; |
1334 | widths.append(t: QTextLength()); |
1335 | widths.append(t: QTextLength(QTextLength::PercentageLength, 30)); |
1336 | widths.append(t: QTextLength(QTextLength::FixedLength, 40)); |
1337 | fmt.setColumnWidthConstraints(widths); |
1338 | cursor.insertTable(rows: 1, cols: 3, format: fmt); |
1339 | |
1340 | QTest::newRow(dataTag: "colwidths" ) << QTextDocumentFragment(&doc) |
1341 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1342 | "\n<tr>\n<td></td>\n<td width=\"30%\"></td>\n<td width=\"40\"></td></tr>" |
1343 | "</table>" ); |
1344 | } |
1345 | |
1346 | // ### rowspan/colspan tests, once texttable api for that is back again |
1347 | // |
1348 | { |
1349 | CREATE_DOC_AND_CURSOR(); |
1350 | |
1351 | QTextTable *table = cursor.insertTable(rows: 1, cols: 1); |
1352 | QTextCursor cellCurs = table->cellAt(row: 0, col: 0).firstCursorPosition(); |
1353 | QTextCharFormat fmt; |
1354 | fmt.setBackground(QColor("#ffffff" )); |
1355 | cellCurs.mergeBlockCharFormat(modifier: fmt); |
1356 | |
1357 | QTest::newRow(dataTag: "cellproperties" ) << QTextDocumentFragment(&doc) |
1358 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1359 | "\n<tr>\n<td bgcolor=\"#ffffff\"></td></tr>" |
1360 | "</table>" ); |
1361 | } |
1362 | |
1363 | { |
1364 | CREATE_DOC_AND_CURSOR(); |
1365 | |
1366 | // ### fixme: use programmatic api as soon as we can create floats through it |
1367 | const char html[] = "<html><body>Blah<img src=\"image.png\" width=\"10\" height=\"20\" style=\"float: right;\" />Blubb</body></html>" ; |
1368 | |
1369 | QTest::newRow(dataTag: "image" ) << QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)) |
1370 | << 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>" ); |
1371 | } |
1372 | |
1373 | { |
1374 | CREATE_DOC_AND_CURSOR(); |
1375 | |
1376 | QTextImageFormat fmt; |
1377 | fmt.setName("foo" ); |
1378 | fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle); |
1379 | cursor.insertImage(format: fmt); |
1380 | |
1381 | QTest::newRow(dataTag: "image-align-middle" ) << QTextDocumentFragment(&doc) |
1382 | << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: middle;\" /></p>" ); |
1383 | } |
1384 | |
1385 | { |
1386 | CREATE_DOC_AND_CURSOR(); |
1387 | |
1388 | QTextImageFormat fmt; |
1389 | fmt.setName("foo" ); |
1390 | fmt.setVerticalAlignment(QTextCharFormat::AlignTop); |
1391 | cursor.insertImage(format: fmt); |
1392 | |
1393 | QTest::newRow(dataTag: "image-align-top" ) << QTextDocumentFragment(&doc) |
1394 | << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" style=\"vertical-align: top;\" /></p>" ); |
1395 | } |
1396 | |
1397 | { |
1398 | CREATE_DOC_AND_CURSOR(); |
1399 | |
1400 | QTextImageFormat fmt; |
1401 | fmt.setName("foo" ); |
1402 | cursor.insertImage(format: fmt); |
1403 | cursor.insertImage(format: fmt); |
1404 | |
1405 | QTest::newRow(dataTag: "2images" ) << QTextDocumentFragment(&doc) |
1406 | << QString("<p DEFAULTBLOCKSTYLE><img src=\"foo\" /><img src=\"foo\" /></p>" ); |
1407 | } |
1408 | |
1409 | { |
1410 | CREATE_DOC_AND_CURSOR(); |
1411 | |
1412 | QString txt = QLatin1String("Blah" ); |
1413 | txt += QChar::LineSeparator; |
1414 | txt += QLatin1String("Bar" ); |
1415 | cursor.insertText(text: txt); |
1416 | |
1417 | QTest::newRow(dataTag: "linebreaks" ) << QTextDocumentFragment(&doc) |
1418 | << QString("<p DEFAULTBLOCKSTYLE>Blah<br />Bar</p>" ); |
1419 | } |
1420 | |
1421 | { |
1422 | CREATE_DOC_AND_CURSOR(); |
1423 | |
1424 | QTextBlockFormat fmt; |
1425 | fmt.setTopMargin(10); |
1426 | fmt.setBottomMargin(20); |
1427 | fmt.setLeftMargin(30); |
1428 | fmt.setRightMargin(40); |
1429 | cursor.insertBlock(format: fmt); |
1430 | cursor.insertText(text: "Blah" ); |
1431 | |
1432 | QTest::newRow(dataTag: "blockmargins" ) << QTextDocumentFragment(&doc) |
1433 | << |
1434 | QString("EMPTYBLOCK" ) + |
1435 | QString("<p style=\" margin-top:10px; margin-bottom:20px; margin-left:30px; margin-right:40px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" ); |
1436 | } |
1437 | |
1438 | { |
1439 | CREATE_DOC_AND_CURSOR(); |
1440 | |
1441 | QTextList *list = cursor.insertList(style: QTextListFormat::ListDisc); |
1442 | cursor.insertText(text: "Blubb" ); |
1443 | cursor.insertBlock(); |
1444 | cursor.insertText(text: "Blah" ); |
1445 | QCOMPARE(list->count(), 2); |
1446 | |
1447 | QTest::newRow(dataTag: "lists" ) << QTextDocumentFragment(&doc) |
1448 | << |
1449 | QString("EMPTYBLOCK" ) + |
1450 | 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>" ); |
1451 | } |
1452 | |
1453 | { |
1454 | CREATE_DOC_AND_CURSOR(); |
1455 | |
1456 | QTextList *list = cursor.insertList(style: QTextListFormat::ListDisc); |
1457 | cursor.insertText(text: "Blubb" ); |
1458 | |
1459 | cursor.insertBlock(); |
1460 | |
1461 | QTextCharFormat blockCharFmt; |
1462 | blockCharFmt.setForeground(QColor("#0000ff" )); |
1463 | cursor.mergeBlockCharFormat(modifier: blockCharFmt); |
1464 | |
1465 | QTextCharFormat fmt; |
1466 | fmt.setForeground(QColor("#ff0000" )); |
1467 | cursor.insertText(text: "Blah" , format: fmt); |
1468 | QCOMPARE(list->count(), 2); |
1469 | |
1470 | QTest::newRow(dataTag: "charfmt-for-list-item" ) << QTextDocumentFragment(&doc) |
1471 | << |
1472 | QString("EMPTYBLOCK" ) + |
1473 | 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>" ); |
1474 | } |
1475 | |
1476 | { |
1477 | CREATE_DOC_AND_CURSOR(); |
1478 | |
1479 | QTextBlockFormat fmt; |
1480 | fmt.setIndent(3); |
1481 | fmt.setTextIndent(30); |
1482 | cursor.insertBlock(format: fmt); |
1483 | cursor.insertText(text: "Test" ); |
1484 | |
1485 | QTest::newRow(dataTag: "block-indent" ) << QTextDocumentFragment(&doc) |
1486 | << |
1487 | QString("EMPTYBLOCK" ) + |
1488 | QString("<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:3; text-indent:30px;\">Test</p>" ); |
1489 | } |
1490 | |
1491 | { |
1492 | CREATE_DOC_AND_CURSOR(); |
1493 | |
1494 | QTextListFormat fmt; |
1495 | fmt.setStyle(QTextListFormat::ListDisc); |
1496 | fmt.setIndent(4); |
1497 | cursor.insertList(format: fmt); |
1498 | cursor.insertText(text: "Blah" ); |
1499 | |
1500 | QTest::newRow(dataTag: "list-indent" ) << QTextDocumentFragment(&doc) |
1501 | << |
1502 | QString("EMPTYBLOCK" ) + |
1503 | QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 4;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>" ); |
1504 | } |
1505 | |
1506 | { |
1507 | CREATE_DOC_AND_CURSOR(); |
1508 | |
1509 | cursor.insertBlock(); |
1510 | |
1511 | |
1512 | QTest::newRow(dataTag: "emptyblock" ) << QTextDocumentFragment(&doc) |
1513 | // after insertBlock() we /do/ have two blocks in the document, so also expect |
1514 | // these in the html output |
1515 | << QString("EMPTYBLOCK" ) + QString("EMPTYBLOCK" ); |
1516 | } |
1517 | |
1518 | { |
1519 | CREATE_DOC_AND_CURSOR(); |
1520 | |
1521 | // if you press enter twice in an empty textedit and then insert 'Test' |
1522 | // you actually get three visible paragraphs, two empty leading ones and |
1523 | // a third with the actual text. the corresponding html representation |
1524 | // therefore should also contain three paragraphs. |
1525 | |
1526 | cursor.insertBlock(); |
1527 | QTextCharFormat fmt; |
1528 | fmt.setForeground(QColor("#00ff00" )); |
1529 | fmt.setProperty(propertyId: QTextFormat::FontSizeIncrement, value: 1); |
1530 | cursor.mergeBlockCharFormat(modifier: fmt); |
1531 | |
1532 | fmt.setProperty(propertyId: QTextFormat::FontSizeIncrement, value: 2); |
1533 | cursor.insertText(text: "Test" , format: fmt); |
1534 | |
1535 | QTest::newRow(dataTag: "blockcharfmt" ) << QTextDocumentFragment(&doc) |
1536 | << 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>" ); |
1537 | } |
1538 | |
1539 | { |
1540 | CREATE_DOC_AND_CURSOR(); |
1541 | |
1542 | QTextCharFormat fmt; |
1543 | fmt.setForeground(QColor("#00ff00" )); |
1544 | cursor.setBlockCharFormat(fmt); |
1545 | fmt.setForeground(QColor("#0000ff" )); |
1546 | cursor.insertText(text: "Test" , format: fmt); |
1547 | |
1548 | QTest::newRow(dataTag: "blockcharfmt2" ) << QTextDocumentFragment(&doc) |
1549 | << 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>" ); |
1550 | } |
1551 | |
1552 | { |
1553 | QTest::newRow(dataTag: "horizontal-ruler" ) << QTextDocumentFragment::fromHtml(html: "<hr />" ) |
1554 | << |
1555 | QString("EMPTYBLOCK" ) + |
1556 | QString("<hr />" ); |
1557 | } |
1558 | { |
1559 | QTest::newRow(dataTag: "horizontal-ruler-with-width" ) << QTextDocumentFragment::fromHtml(html: "<hr width=\"50%\"/>" ) |
1560 | << |
1561 | QString("EMPTYBLOCK" ) + |
1562 | QString("<hr width=\"50%\"/>" ); |
1563 | } |
1564 | { |
1565 | CREATE_DOC_AND_CURSOR(); |
1566 | |
1567 | QTextFrame *mainFrame = cursor.currentFrame(); |
1568 | |
1569 | QTextFrameFormat ffmt; |
1570 | ffmt.setBorder(1); |
1571 | ffmt.setPosition(QTextFrameFormat::FloatRight); |
1572 | ffmt.setMargin(2); |
1573 | ffmt.setWidth(100); |
1574 | ffmt.setHeight(50); |
1575 | ffmt.setBackground(QColor("#00ff00" )); |
1576 | cursor.insertFrame(format: ffmt); |
1577 | cursor.insertText(text: "Hello World" ); |
1578 | cursor = mainFrame->lastCursorPosition(); |
1579 | |
1580 | QTest::newRow(dataTag: "frame" ) << QTextDocumentFragment(&doc) |
1581 | << 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>" ); |
1582 | } |
1583 | |
1584 | { |
1585 | CREATE_DOC_AND_CURSOR(); |
1586 | |
1587 | QTextCharFormat fmt; |
1588 | fmt.setForeground(QColor("#00ff00" )); |
1589 | // fmt.setBackground(QColor("#0000ff")); |
1590 | cursor.setBlockCharFormat(fmt); |
1591 | |
1592 | fmt.setForeground(QBrush()); |
1593 | // fmt.setBackground(QBrush()); |
1594 | cursor.insertText(text: "Test" , format: fmt); |
1595 | |
1596 | // 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>"); |
1597 | QTest::newRow(dataTag: "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>" ); |
1598 | } |
1599 | |
1600 | { |
1601 | CREATE_DOC_AND_CURSOR(); |
1602 | |
1603 | QTextTable *table = cursor.insertTable(rows: 2, cols: 2); |
1604 | table->mergeCells(row: 0, col: 0, numRows: 1, numCols: 2); |
1605 | QTextTableFormat fmt = table->format(); |
1606 | QVector<QTextLength> widths; |
1607 | widths.append(t: QTextLength(QTextLength::FixedLength, 20)); |
1608 | widths.append(t: QTextLength(QTextLength::FixedLength, 40)); |
1609 | fmt.setColumnWidthConstraints(widths); |
1610 | table->setFormat(fmt); |
1611 | |
1612 | QTest::newRow(dataTag: "mergedtablecolwidths" ) << QTextDocumentFragment(&doc) |
1613 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1614 | "\n<tr>\n<td colspan=\"2\"></td></tr>" |
1615 | "\n<tr>\n<td width=\"20\"></td>\n<td width=\"40\"></td></tr>" |
1616 | "</table>" ); |
1617 | } |
1618 | |
1619 | { |
1620 | CREATE_DOC_AND_CURSOR(); |
1621 | |
1622 | QTextCharFormat fmt; |
1623 | |
1624 | cursor.insertText(text: "Blah\nGreen yellow green" ); |
1625 | cursor.setPosition(pos: 0); |
1626 | cursor.setPosition(pos: 23, mode: QTextCursor::KeepAnchor); |
1627 | fmt.setBackground(Qt::green); |
1628 | cursor.mergeCharFormat(modifier: fmt); |
1629 | cursor.clearSelection(); |
1630 | cursor.setPosition(pos: 11); |
1631 | cursor.setPosition(pos: 17, mode: QTextCursor::KeepAnchor); |
1632 | fmt.setBackground(Qt::yellow); |
1633 | cursor.mergeCharFormat(modifier: fmt); |
1634 | cursor.clearSelection(); |
1635 | |
1636 | QTest::newRow(dataTag: "multiparagraph-bgcolor" ) << QTextDocumentFragment(&doc) |
1637 | << QString("<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Blah</span></p>\n" |
1638 | "<p DEFAULTBLOCKSTYLE><span style=\" background-color:#00ff00;\">Green </span>" |
1639 | "<span style=\" background-color:#ffff00;\">yellow</span>" |
1640 | "<span style=\" background-color:#00ff00;\"> green</span></p>" ); |
1641 | } |
1642 | |
1643 | { |
1644 | CREATE_DOC_AND_CURSOR(); |
1645 | |
1646 | QTextBlockFormat fmt; |
1647 | fmt.setBackground(QColor("#0000ff" )); |
1648 | cursor.insertBlock(format: fmt); |
1649 | |
1650 | QTextCharFormat charfmt; |
1651 | charfmt.setBackground(QColor("#0000ff" )); |
1652 | cursor.insertText(text: "Blah" , format: charfmt); |
1653 | |
1654 | QTest::newRow(dataTag: "nospan-bgcolor" ) << QTextDocumentFragment(&doc) |
1655 | << QString("EMPTYBLOCK" ) + |
1656 | QString("<p OPENDEFAULTBLOCKSTYLE background-color:#0000ff;\"><span style=\" background-color:#0000ff;\">Blah</span></p>" ); |
1657 | } |
1658 | |
1659 | { |
1660 | CREATE_DOC_AND_CURSOR(); |
1661 | |
1662 | QTextTable *table = cursor.insertTable(rows: 2, cols: 2); |
1663 | QTextCharFormat fmt = table->cellAt(row: 0, col: 0).format(); |
1664 | fmt.setVerticalAlignment(QTextCharFormat::AlignMiddle); |
1665 | table->cellAt(row: 0, col: 0).setFormat(fmt); |
1666 | fmt = table->cellAt(row: 0, col: 1).format(); |
1667 | fmt.setVerticalAlignment(QTextCharFormat::AlignTop); |
1668 | table->cellAt(row: 0, col: 1).setFormat(fmt); |
1669 | fmt = table->cellAt(row: 1, col: 0).format(); |
1670 | fmt.setVerticalAlignment(QTextCharFormat::AlignBottom); |
1671 | table->cellAt(row: 1, col: 0).setFormat(fmt); |
1672 | |
1673 | table->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "Blah" ); |
1674 | |
1675 | QTest::newRow(dataTag: "table-vertical-alignment" ) << QTextDocumentFragment(&doc) |
1676 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1677 | "\n<tr>\n<td style=\" vertical-align:middle;\">\n" |
1678 | "<p DEFAULTBLOCKSTYLE>Blah</p></td>" |
1679 | "\n<td style=\" vertical-align:top;\"></td></tr>" |
1680 | "\n<tr>\n<td style=\" vertical-align:bottom;\"></td>" |
1681 | "\n<td></td></tr>" |
1682 | "</table>" ); |
1683 | } |
1684 | |
1685 | { |
1686 | CREATE_DOC_AND_CURSOR(); |
1687 | |
1688 | QTextTable *table = cursor.insertTable(rows: 2, cols: 2); |
1689 | QTextTableCellFormat fmt = table->cellAt(row: 0, col: 0).format().toTableCellFormat(); |
1690 | fmt.setLeftPadding(1); |
1691 | table->cellAt(row: 0, col: 0).setFormat(fmt); |
1692 | fmt = table->cellAt(row: 0, col: 1).format().toTableCellFormat(); |
1693 | fmt.setRightPadding(1); |
1694 | table->cellAt(row: 0, col: 1).setFormat(fmt); |
1695 | fmt = table->cellAt(row: 1, col: 0).format().toTableCellFormat(); |
1696 | fmt.setTopPadding(1); |
1697 | table->cellAt(row: 1, col: 0).setFormat(fmt); |
1698 | fmt = table->cellAt(row: 1, col: 1).format().toTableCellFormat(); |
1699 | fmt.setBottomPadding(1); |
1700 | table->cellAt(row: 1, col: 1).setFormat(fmt); |
1701 | |
1702 | table->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "Blah" ); |
1703 | |
1704 | QTest::newRow(dataTag: "table-cell-paddings" ) << QTextDocumentFragment(&doc) |
1705 | << QString("<table border=\"1\" cellspacing=\"2\">" |
1706 | "\n<tr>\n<td style=\" padding-left:1;\">\n" |
1707 | "<p DEFAULTBLOCKSTYLE>Blah</p></td>" |
1708 | "\n<td style=\" padding-right:1;\"></td></tr>" |
1709 | "\n<tr>\n<td style=\" padding-top:1;\"></td>" |
1710 | "\n<td style=\" padding-bottom:1;\"></td></tr>" |
1711 | "</table>" ); |
1712 | } |
1713 | |
1714 | { |
1715 | CREATE_DOC_AND_CURSOR(); |
1716 | |
1717 | QTextTableFormat fmt; |
1718 | fmt.setBorderBrush(QColor("#0000ff" )); |
1719 | fmt.setBorderStyle(QTextFrameFormat::BorderStyle_Solid); |
1720 | cursor.insertTable(rows: 2, cols: 2, format: fmt); |
1721 | |
1722 | QTest::newRow(dataTag: "tableborder" ) << QTextDocumentFragment(&doc) |
1723 | << QString("<table border=\"1\" style=\" border-color:#0000ff; border-style:solid;\" cellspacing=\"2\">" |
1724 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1725 | "\n<tr>\n<td></td>\n<td></td></tr>" |
1726 | "</table>" ); |
1727 | } |
1728 | |
1729 | { |
1730 | CREATE_DOC_AND_CURSOR(); |
1731 | |
1732 | cursor.insertBlock(); |
1733 | cursor.insertText(text: "Foo" ); |
1734 | |
1735 | cursor.block().setUserState(42); |
1736 | |
1737 | QTest::newRow(dataTag: "userstate" ) << QTextDocumentFragment(&doc) |
1738 | << QString("EMPTYBLOCK" ) + |
1739 | QString("<p OPENDEFAULTBLOCKSTYLE -qt-user-state:42;\">Foo</p>" ); |
1740 | } |
1741 | |
1742 | { |
1743 | CREATE_DOC_AND_CURSOR(); |
1744 | |
1745 | QTextBlockFormat blockFmt; |
1746 | blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore); |
1747 | |
1748 | cursor.insertBlock(format: blockFmt); |
1749 | cursor.insertText(text: "Foo" ); |
1750 | |
1751 | blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore | QTextFormat::PageBreak_AlwaysAfter); |
1752 | |
1753 | cursor.insertBlock(format: blockFmt); |
1754 | cursor.insertText(text: "Bar" ); |
1755 | |
1756 | QTextTableFormat tableFmt; |
1757 | tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter); |
1758 | |
1759 | cursor.insertTable(rows: 1, cols: 1, format: tableFmt); |
1760 | |
1761 | QTest::newRow(dataTag: "pagebreak" ) << QTextDocumentFragment(&doc) |
1762 | << QString("EMPTYBLOCK" ) + |
1763 | QString("<p OPENDEFAULTBLOCKSTYLE page-break-before:always;\">Foo</p>" |
1764 | "\n<p OPENDEFAULTBLOCKSTYLE page-break-before:always; page-break-after:always;\">Bar</p>" |
1765 | "\n<table border=\"1\" style=\" page-break-after:always;\" cellspacing=\"2\">\n<tr>\n<td></td></tr></table>" ); |
1766 | } |
1767 | |
1768 | { |
1769 | CREATE_DOC_AND_CURSOR(); |
1770 | |
1771 | QTextListFormat listFmt; |
1772 | listFmt.setStyle(QTextListFormat::ListDisc); |
1773 | |
1774 | cursor.insertList(format: listFmt); |
1775 | cursor.insertText(text: "Blah" ); |
1776 | |
1777 | QTest::newRow(dataTag: "list-ul-margin" ) << QTextDocumentFragment(&doc) |
1778 | << QString("EMPTYBLOCK" ) + |
1779 | QString("<ul style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;\"><li DEFAULTBLOCKSTYLE>Blah</li></ul>" ); |
1780 | } |
1781 | { |
1782 | CREATE_DOC_AND_CURSOR(); |
1783 | const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li><li>item-2.2" |
1784 | "<ul><li>item-2.2.1</li></ul></li><li>item-2.3<ul><li>item-2.3.1" |
1785 | "</li></ul></li></ul></li><li>item-3</li></ul>" ; |
1786 | cursor.insertHtml(html: listHtml); |
1787 | |
1788 | QTest::newRow(dataTag: "nested-lists-one" ) << QTextDocumentFragment(&doc) |
1789 | << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " |
1790 | "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" |
1791 | "item-1</li>\n<li DEFAULTBLOCKSTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " |
1792 | "DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2\n<ul " |
1793 | "DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>item-2.2.1</li></ul></li>\n" |
1794 | "<li DEFAULTBLOCKSTYLE>item-2.3\n<ul DEFAULTULSTYLE 3;\"><li DEFAULTBLOCKSTYLE>" |
1795 | "item-2.3.1</li></ul></li></ul></li>\n<li DEFAULTLASTLISTYLE>item-3</li></ul>" ); |
1796 | } |
1797 | { |
1798 | CREATE_DOC_AND_CURSOR(); |
1799 | const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li></ul></li></ul>" ; |
1800 | cursor.insertHtml(html: listHtml); |
1801 | |
1802 | QTest::newRow(dataTag: "nested-lists-two" ) << QTextDocumentFragment(&doc) |
1803 | << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " |
1804 | "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" |
1805 | "item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " |
1806 | "DEFAULTBLOCKSTYLE>item-2.1</li></ul></li></ul>" ); |
1807 | } |
1808 | { |
1809 | CREATE_DOC_AND_CURSOR(); |
1810 | const QString listHtml = "<ul><li>item-1</li><li>item-2<ul><li>item-2.1</li><li>item-2.2" |
1811 | "</li></ul></li></ul>" ; |
1812 | cursor.insertHtml(html: listHtml); |
1813 | |
1814 | QTest::newRow(dataTag: "nested-lists-three" ) << QTextDocumentFragment(&doc) |
1815 | << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " |
1816 | "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" |
1817 | "item-1</li>\n<li DEFAULTLASTLISTYLE>item-2\n<ul DEFAULTULSTYLE 2;\"><li " |
1818 | "DEFAULTBLOCKSTYLE>item-2.1</li>\n<li DEFAULTBLOCKSTYLE>item-2.2</li></ul>" |
1819 | "</li></ul>" ); |
1820 | } |
1821 | { |
1822 | CREATE_DOC_AND_CURSOR(); |
1823 | const QString listHtml = "<ul><li>item-1.1</li><li>item-1.2<li></ul>" |
1824 | "<ul><li>item-2.1</li></ul>" ; |
1825 | cursor.insertHtml(html: listHtml); |
1826 | |
1827 | QTest::newRow(dataTag: "not-nested-list" ) << QTextDocumentFragment(&doc) |
1828 | << QString("<ul DEFAULTULSTYLE 1;\"><li style=\" margin-top:12px; margin-bottom:0px; " |
1829 | "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">" |
1830 | "item-1.1</li>\n<li DEFAULTBLOCKSTYLE>item-1.2</li></ul>\n<ul DEFAULTULSTYLE 1;\">" |
1831 | "<li style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; " |
1832 | "margin-right:0px; -qt-block-indent:0; text-indent:0px;\">item-2.1</li></ul>" ); |
1833 | } |
1834 | } |
1835 | |
1836 | void tst_QTextDocument::toHtml() |
1837 | { |
1838 | QFETCH(QTextDocumentFragment, input); |
1839 | QFETCH(QString, expectedOutput); |
1840 | |
1841 | cursor.insertFragment(fragment: input); |
1842 | |
1843 | expectedOutput.prepend(s: htmlHead); |
1844 | |
1845 | expectedOutput.replace(before: "OPENDEFAULTBLOCKSTYLE" , after: "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;" ); |
1846 | expectedOutput.replace(before: "DEFAULTBLOCKSTYLE" , after: "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"" ); |
1847 | expectedOutput.replace(before: "EMPTYBLOCK" , after: "<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" ); |
1848 | expectedOutput.replace(before: "DEFAULTULSTYLE" , after: "style=\"margin-top: 0px; margin-bottom: 0px; " |
1849 | "margin-left: 0px; margin-right: 0px; -qt-list-indent:" ); |
1850 | expectedOutput.replace(before: "DEFAULTLASTLISTYLE" , after: "style=\" margin-top:0px; margin-bottom:12px; " |
1851 | "margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"" ); |
1852 | |
1853 | if (expectedOutput.endsWith(c: QLatin1Char('\n'))) |
1854 | expectedOutput.chop(n: 1); |
1855 | expectedOutput.append(s: htmlTail); |
1856 | |
1857 | QString output = doc->toHtml(); |
1858 | |
1859 | writeActualAndExpected(testTag: QTest::currentDataTag(), actual: output, expected: expectedOutput); |
1860 | |
1861 | QCOMPARE(output, expectedOutput); |
1862 | |
1863 | QDomDocument document; |
1864 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
1865 | QEXPECT_FAIL("charfmt-for-list-item" , |
1866 | "The attribute \"style\" is redefined in the generated HTML, which is not valid " |
1867 | "according to XML standard. The new QDomDocument implementation follows the XML " |
1868 | "standard." , Continue); |
1869 | #endif |
1870 | QVERIFY2(document.setContent(output), "Output was not valid XML" ); |
1871 | } |
1872 | |
1873 | void tst_QTextDocument::toHtml2() |
1874 | { |
1875 | QTextDocument doc; |
1876 | doc.setHtml("<p>text <img src=\"\"> text</p>" ); // 4 spaces before the second 'text' |
1877 | QTextBlock block = doc.firstBlock(); |
1878 | QTextBlock::Iterator iter = block.begin(); |
1879 | QTextFragment f = iter.fragment(); |
1880 | QVERIFY(f.isValid()); |
1881 | QCOMPARE(f.position(), 0); |
1882 | QCOMPARE(f.length(), 5); |
1883 | //qDebug() << block.text().mid(f.position(), f.length()); |
1884 | |
1885 | iter++; |
1886 | f = iter.fragment(); |
1887 | QVERIFY(f.isValid()); |
1888 | QCOMPARE(f.position(), 5); |
1889 | QCOMPARE(f.length(), 1); |
1890 | //qDebug() << block.text().mid(f.position(), f.length()); |
1891 | |
1892 | iter++; |
1893 | f = iter.fragment(); |
1894 | //qDebug() << block.text().mid(f.position(), f.length()); |
1895 | QVERIFY(f.isValid()); |
1896 | QCOMPARE(f.position(), 6); |
1897 | QCOMPARE(f.length(), 5); // 1 space should be preserved. |
1898 | QCOMPARE(block.text().mid(f.position(), f.length()), QString(" text" )); |
1899 | |
1900 | doc.setHtml("<table><tr><td> foo</td></tr></table> text" ); // 4 spaces before the second 'text' |
1901 | block = doc.firstBlock().next(); |
1902 | //qDebug() << block.text(); |
1903 | QCOMPARE(block.text(), QString("foo" )); |
1904 | |
1905 | block = block.next(); |
1906 | //qDebug() << block.text(); |
1907 | QCOMPARE(block.text(), QString("text" )); |
1908 | } |
1909 | |
1910 | void tst_QTextDocument::setFragmentMarkersInHtmlExport() |
1911 | { |
1912 | { |
1913 | CREATE_DOC_AND_CURSOR(); |
1914 | |
1915 | cursor.insertText(text: "Leadin" ); |
1916 | const int startPos = cursor.position(); |
1917 | |
1918 | cursor.insertText(text: "Test" ); |
1919 | QTextCharFormat fmt; |
1920 | fmt.setForeground(QColor("#00ff00" )); |
1921 | cursor.insertText(text: "Blah" , format: fmt); |
1922 | |
1923 | const int endPos = cursor.position(); |
1924 | cursor.insertText(text: "Leadout" , format: QTextCharFormat()); |
1925 | |
1926 | cursor.setPosition(pos: startPos); |
1927 | cursor.setPosition(pos: endPos, mode: QTextCursor::KeepAnchor); |
1928 | QTextDocumentFragment fragment(cursor); |
1929 | |
1930 | QString expected = htmlHead; |
1931 | expected.replace(rx: QRegExp("<body.*>" ), after: QString("<body>" )); |
1932 | 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; |
1933 | QCOMPARE(fragment.toHtml(), expected); |
1934 | } |
1935 | { |
1936 | CREATE_DOC_AND_CURSOR(); |
1937 | |
1938 | cursor.insertText(text: "Leadin" ); |
1939 | const int startPos = cursor.position(); |
1940 | |
1941 | cursor.insertText(text: "Test" ); |
1942 | |
1943 | const int endPos = cursor.position(); |
1944 | cursor.insertText(text: "Leadout" , format: QTextCharFormat()); |
1945 | |
1946 | cursor.setPosition(pos: startPos); |
1947 | cursor.setPosition(pos: endPos, mode: QTextCursor::KeepAnchor); |
1948 | QTextDocumentFragment fragment(cursor); |
1949 | |
1950 | QString expected = htmlHead; |
1951 | expected.replace(rx: QRegExp("<body.*>" ), after: QString("<body>" )); |
1952 | 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; |
1953 | QCOMPARE(fragment.toHtml(), expected); |
1954 | } |
1955 | } |
1956 | |
1957 | void tst_QTextDocument::toHtmlBodyBgColor() |
1958 | { |
1959 | CREATE_DOC_AND_CURSOR(); |
1960 | |
1961 | cursor.insertText(text: "Blah" ); |
1962 | |
1963 | QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); |
1964 | fmt.setBackground(QColor("#0000ff" )); |
1965 | doc.rootFrame()->setFrameFormat(fmt); |
1966 | |
1967 | QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
1968 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
1969 | "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" |
1970 | "p, li { white-space: pre-wrap; }\n" |
1971 | "</style></head>" |
1972 | "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\"" |
1973 | " bgcolor=\"#0000ff\">\n" |
1974 | "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" |
1975 | "</body></html>" ); |
1976 | |
1977 | expectedHtml = expectedHtml |
1978 | .arg(a: defaultFont.family()) |
1979 | .arg(a: cssFontSizeString(font: defaultFont)) |
1980 | .arg(a: defaultFont.weight() * 8) |
1981 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
1982 | |
1983 | QCOMPARE(doc.toHtml(), expectedHtml); |
1984 | } |
1985 | |
1986 | void tst_QTextDocument::toHtmlBodyBgColorRgba() |
1987 | { |
1988 | CREATE_DOC_AND_CURSOR(); |
1989 | |
1990 | cursor.insertText(text: "Blah" ); |
1991 | |
1992 | QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); |
1993 | fmt.setBackground(QColor(255, 0, 0, 51)); |
1994 | doc.rootFrame()->setFrameFormat(fmt); |
1995 | |
1996 | QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
1997 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
1998 | "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" |
1999 | "p, li { white-space: pre-wrap; }\n" |
2000 | "</style></head>" |
2001 | "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\"" |
2002 | " bgcolor=\"rgba(255,0,0,0.2)\">\n" |
2003 | "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" |
2004 | "</body></html>" ); |
2005 | |
2006 | expectedHtml = expectedHtml.arg(a: defaultFont.family()) |
2007 | .arg(a: cssFontSizeString(font: defaultFont)) |
2008 | .arg(a: defaultFont.weight() * 8) |
2009 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
2010 | |
2011 | QCOMPARE(doc.toHtml(), expectedHtml); |
2012 | } |
2013 | |
2014 | void tst_QTextDocument::toHtmlBodyBgColorTransparent() |
2015 | { |
2016 | CREATE_DOC_AND_CURSOR(); |
2017 | |
2018 | cursor.insertText(text: "Blah" ); |
2019 | |
2020 | QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); |
2021 | fmt.setBackground(QColor(255, 0, 0, 0)); |
2022 | doc.rootFrame()->setFrameFormat(fmt); |
2023 | |
2024 | QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
2025 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
2026 | "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" |
2027 | "p, li { white-space: pre-wrap; }\n" |
2028 | "</style></head>" |
2029 | "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\"" |
2030 | " bgcolor=\"transparent\">\n" |
2031 | "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" |
2032 | "</body></html>" ); |
2033 | |
2034 | expectedHtml = expectedHtml |
2035 | .arg(a: defaultFont.family()) |
2036 | .arg(a: cssFontSizeString(font: defaultFont)) |
2037 | .arg(a: defaultFont.weight() * 8) |
2038 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
2039 | |
2040 | QCOMPARE(doc.toHtml(), expectedHtml); |
2041 | } |
2042 | |
2043 | void tst_QTextDocument::toHtmlRootFrameProperties() |
2044 | { |
2045 | CREATE_DOC_AND_CURSOR(); |
2046 | |
2047 | QTextFrameFormat fmt = doc.rootFrame()->frameFormat(); |
2048 | fmt.setTopMargin(10); |
2049 | fmt.setLeftMargin(10); |
2050 | fmt.setBorder(2); |
2051 | doc.rootFrame()->setFrameFormat(fmt); |
2052 | |
2053 | cursor.insertText(text: "Blah" ); |
2054 | |
2055 | QString expectedOutput("<table border=\"2\" style=\"-qt-table-type: root; margin-top:10px; " |
2056 | "margin-bottom:4px; margin-left:10px; margin-right:4px;\">\n" |
2057 | "<tr>\n<td style=\"border: none;\">\n" |
2058 | "<p DEFAULTBLOCKSTYLE>Blah</p></td></tr></table>" ); |
2059 | |
2060 | expectedOutput.prepend(s: htmlHead); |
2061 | expectedOutput.replace(before: "DEFAULTBLOCKSTYLE" , after: "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"" ); |
2062 | expectedOutput.append(s: htmlTail); |
2063 | |
2064 | writeActualAndExpected(testTag: QTest::currentTestFunction(), actual: doc.toHtml(), expected: expectedOutput); |
2065 | |
2066 | QCOMPARE(doc.toHtml(), expectedOutput); |
2067 | } |
2068 | |
2069 | void tst_QTextDocument::toHtmlLineHeightProperties() |
2070 | { |
2071 | CREATE_DOC_AND_CURSOR(); |
2072 | |
2073 | QTextBlock block = doc.firstBlock(); |
2074 | QTextBlockFormat blockFormat = block.blockFormat(); |
2075 | blockFormat.setLineHeight(height: 200, heightType: QTextBlockFormat::ProportionalHeight); |
2076 | cursor.setBlockFormat(blockFormat); |
2077 | |
2078 | cursor.insertText(text: "Blah" ); |
2079 | QString expectedOutput("<p DEFAULTBLOCKSTYLE line-height:200%;\">Blah</p>" ); |
2080 | |
2081 | expectedOutput.prepend(s: htmlHead); |
2082 | expectedOutput.replace(before: "DEFAULTBLOCKSTYLE" , after: "style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;" ); |
2083 | expectedOutput.append(s: htmlTail); |
2084 | |
2085 | QCOMPARE(doc.toHtml(), expectedOutput); |
2086 | } |
2087 | |
2088 | void tst_QTextDocument::toHtmlDefaultFontSpacingProperties() |
2089 | { |
2090 | CREATE_DOC_AND_CURSOR(); |
2091 | |
2092 | cursor.insertText(text: "Blah" ); |
2093 | |
2094 | QFont fnt = doc.defaultFont(); |
2095 | fnt.setLetterSpacing(type: QFont::AbsoluteSpacing, spacing: 13); |
2096 | fnt.setWordSpacing(15); |
2097 | doc.setDefaultFont(fnt); |
2098 | |
2099 | QString expectedOutput = QString("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
2100 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
2101 | "<html><head><meta name=\"qrichtext\" content=\"1\" />" |
2102 | "<style type=\"text/css\">\n" |
2103 | "p, li { white-space: pre-wrap; }\n" |
2104 | "</style></head>" |
2105 | "<body style=\" font-family:'%1'; font-size:%2; " |
2106 | "font-weight:%3; font-style:%4; letter-spacing:13px; " |
2107 | "word-spacing:15px;\">\n" |
2108 | "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" |
2109 | "</body></html>" ); |
2110 | expectedOutput = expectedOutput.arg(a: defaultFont.family()) |
2111 | .arg(a: cssFontSizeString(font: defaultFont)) |
2112 | .arg(a: defaultFont.weight() * 8) |
2113 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
2114 | QCOMPARE(doc.toHtml(), expectedOutput); |
2115 | } |
2116 | |
2117 | void tst_QTextDocument::capitalizationHtmlInExport() |
2118 | { |
2119 | doc->setPlainText("Test" ); |
2120 | |
2121 | QRegExp re(".*span style=\"(.*)\">Test.*" ); |
2122 | QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span |
2123 | |
2124 | QTextCursor cursor(doc); |
2125 | cursor.setPosition(pos: 4, mode: QTextCursor::KeepAnchor); |
2126 | QTextCharFormat cf; |
2127 | cf.setFontCapitalization(QFont::SmallCaps); |
2128 | cursor.mergeCharFormat(modifier: cf); |
2129 | |
2130 | const QString smallcaps = doc->toHtml(); |
2131 | QVERIFY(re.exactMatch(doc->toHtml())); |
2132 | QCOMPARE(re.captureCount(), 1); |
2133 | QCOMPARE(re.cap(1).trimmed(), QString("font-variant:small-caps;" )); |
2134 | |
2135 | cf.setFontCapitalization(QFont::AllUppercase); |
2136 | cursor.mergeCharFormat(modifier: cf); |
2137 | const QString uppercase = doc->toHtml(); |
2138 | QVERIFY(re.exactMatch(doc->toHtml())); |
2139 | QCOMPARE(re.captureCount(), 1); |
2140 | QCOMPARE(re.cap(1).trimmed(), QString("text-transform:uppercase;" )); |
2141 | |
2142 | cf.setFontCapitalization(QFont::AllLowercase); |
2143 | cursor.mergeCharFormat(modifier: cf); |
2144 | const QString lowercase = doc->toHtml(); |
2145 | QVERIFY(re.exactMatch(doc->toHtml())); |
2146 | QCOMPARE(re.captureCount(), 1); |
2147 | QCOMPARE(re.cap(1).trimmed(), QString("text-transform:lowercase;" )); |
2148 | |
2149 | doc->setHtml(smallcaps); |
2150 | cursor.setPosition(pos: 1); |
2151 | QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::SmallCaps); |
2152 | doc->setHtml(uppercase); |
2153 | QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllUppercase); |
2154 | doc->setHtml(lowercase); |
2155 | QCOMPARE(cursor.charFormat().fontCapitalization(), QFont::AllLowercase); |
2156 | } |
2157 | |
2158 | void tst_QTextDocument::wordspacingHtmlExport() |
2159 | { |
2160 | doc->setPlainText("Test" ); |
2161 | |
2162 | QRegExp re(".*span style=\"(.*)\">Test.*" ); |
2163 | QVERIFY(re.exactMatch(doc->toHtml()) == false); // no span |
2164 | |
2165 | QTextCursor cursor(doc); |
2166 | cursor.setPosition(pos: 4, mode: QTextCursor::KeepAnchor); |
2167 | QTextCharFormat cf; |
2168 | cf.setFontWordSpacing(4); |
2169 | cursor.mergeCharFormat(modifier: cf); |
2170 | |
2171 | QVERIFY(re.exactMatch(doc->toHtml())); |
2172 | QCOMPARE(re.captureCount(), 1); |
2173 | QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:4px;" )); |
2174 | |
2175 | cf.setFontWordSpacing(-8.5); |
2176 | cursor.mergeCharFormat(modifier: cf); |
2177 | |
2178 | QVERIFY(re.exactMatch(doc->toHtml())); |
2179 | QCOMPARE(re.captureCount(), 1); |
2180 | QCOMPARE(re.cap(1).trimmed(), QString("word-spacing:-8.5px;" )); |
2181 | } |
2182 | |
2183 | class CursorPosSignalSpy : public QObject |
2184 | { |
2185 | Q_OBJECT |
2186 | public: |
2187 | CursorPosSignalSpy(QTextDocument *doc) |
2188 | { |
2189 | calls = 0; |
2190 | connect(sender: doc, SIGNAL(cursorPositionChanged(QTextCursor)), |
2191 | receiver: this, SLOT(cursorPositionChanged(QTextCursor))); |
2192 | } |
2193 | |
2194 | int calls; |
2195 | |
2196 | private slots: |
2197 | void cursorPositionChanged(const QTextCursor &) |
2198 | { |
2199 | ++calls; |
2200 | } |
2201 | }; |
2202 | |
2203 | void tst_QTextDocument::cursorPositionChanged() |
2204 | { |
2205 | CursorPosSignalSpy spy(doc); |
2206 | |
2207 | cursor.insertText(text: "Test" ); |
2208 | QCOMPARE(spy.calls, 1); |
2209 | |
2210 | spy.calls = 0; |
2211 | QTextCursor unrelatedCursor(doc); |
2212 | unrelatedCursor.insertText(text: "Blah" ); |
2213 | QCOMPARE(spy.calls, 2); |
2214 | |
2215 | spy.calls = 0; |
2216 | cursor.insertText(text: "Blah" ); |
2217 | QCOMPARE(spy.calls, 1); |
2218 | |
2219 | spy.calls = 0; |
2220 | cursor.movePosition(op: QTextCursor::PreviousCharacter); |
2221 | QCOMPARE(spy.calls, 0); |
2222 | } |
2223 | |
2224 | void tst_QTextDocument::cursorPositionChangedOnSetText() |
2225 | { |
2226 | CursorPosSignalSpy spy(doc); |
2227 | |
2228 | // doc has one QTextCursor stored in the |
2229 | // cursor member variable, thus the signal |
2230 | // gets emitted once. |
2231 | |
2232 | doc->setPlainText("Foo\nBar\nBaz\nBlub\nBlah" ); |
2233 | |
2234 | QCOMPARE(spy.calls, 1); |
2235 | |
2236 | spy.calls = 0; |
2237 | doc->setHtml("<p>Foo<p>Bar<p>Baz<p>Blah" ); |
2238 | |
2239 | QCOMPARE(spy.calls, 1); |
2240 | } |
2241 | |
2242 | void tst_QTextDocument::textFrameIterator() |
2243 | { |
2244 | cursor.insertTable(rows: 1, cols: 1); |
2245 | |
2246 | int blockCount = 0; |
2247 | int frameCount = 0; |
2248 | |
2249 | for (QTextFrame::Iterator frameIt = doc->rootFrame()->begin(); |
2250 | !frameIt.atEnd(); ++frameIt) { |
2251 | if (frameIt.currentFrame()) |
2252 | ++frameCount; |
2253 | else if (frameIt.currentBlock().isValid()) |
2254 | ++blockCount; |
2255 | |
2256 | } |
2257 | |
2258 | QEXPECT_FAIL("" , "This is currently worked around in the html export but needs fixing!" , Continue); |
2259 | QCOMPARE(blockCount, 0); |
2260 | QCOMPARE(frameCount, 1); |
2261 | } |
2262 | |
2263 | void tst_QTextDocument::codecForHtml() |
2264 | { |
2265 | const QByteArray ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;charset=utf-16\">" ); |
2266 | QTextCodec *c = Qt::codecForHtml(ba: header); |
2267 | QVERIFY(c); |
2268 | QCOMPARE(c->name(), QByteArray("UTF-16" )); |
2269 | } |
2270 | |
2271 | class TestSyntaxHighlighter : public QObject |
2272 | { |
2273 | Q_OBJECT |
2274 | public: |
2275 | inline TestSyntaxHighlighter(QTextDocument *doc) : QObject(doc), ok(false) {} |
2276 | |
2277 | bool ok; |
2278 | |
2279 | private slots: |
2280 | inline void markBlockDirty(int from, int charsRemoved, int charsAdded) |
2281 | { |
2282 | Q_UNUSED(charsRemoved); |
2283 | Q_UNUSED(charsAdded); |
2284 | QTextDocument *doc = static_cast<QTextDocument *>(parent()); |
2285 | QTextBlock block = doc->findBlock(pos: from); |
2286 | |
2287 | QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(object: doc->documentLayout()); |
2288 | lout->called = false; |
2289 | |
2290 | doc->markContentsDirty(from: block.position(), length: block.length()); |
2291 | |
2292 | ok = (lout->called == false); |
2293 | } |
2294 | |
2295 | inline void modifyBlockAgain(int from, int charsRemoved, int charsAdded) |
2296 | { |
2297 | Q_UNUSED(charsRemoved); |
2298 | Q_UNUSED(charsAdded); |
2299 | QTextDocument *doc = static_cast<QTextDocument *>(parent()); |
2300 | QTextBlock block = doc->findBlock(pos: from); |
2301 | QTextCursor cursor(block); |
2302 | |
2303 | QTestDocumentLayout *lout = qobject_cast<QTestDocumentLayout *>(object: doc->documentLayout()); |
2304 | lout->called = false; |
2305 | |
2306 | cursor.insertText(text: "Foo" ); |
2307 | |
2308 | ok = (lout->called == true); |
2309 | } |
2310 | }; |
2311 | |
2312 | void tst_QTextDocument::markContentsDirty() |
2313 | { |
2314 | QTestDocumentLayout *lout = new QTestDocumentLayout(doc); |
2315 | doc->setDocumentLayout(lout); |
2316 | TestSyntaxHighlighter *highlighter = new TestSyntaxHighlighter(doc); |
2317 | connect(sender: doc, SIGNAL(contentsChange(int,int,int)), |
2318 | receiver: highlighter, SLOT(markBlockDirty(int,int,int))); |
2319 | |
2320 | highlighter->ok = false; |
2321 | cursor.insertText(text: "Some dummy text blah blah" ); |
2322 | QVERIFY(highlighter->ok); |
2323 | |
2324 | disconnect(sender: doc, SIGNAL(contentsChange(int,int,int)), |
2325 | receiver: highlighter, SLOT(markBlockDirty(int,int,int))); |
2326 | connect(sender: doc, SIGNAL(contentsChange(int,int,int)), |
2327 | receiver: highlighter, SLOT(modifyBlockAgain(int,int,int))); |
2328 | highlighter->ok = false; |
2329 | cursor.insertText(text: "FooBar" ); |
2330 | QVERIFY(highlighter->ok); |
2331 | |
2332 | lout->called = false; |
2333 | |
2334 | doc->markContentsDirty(from: 1, length: 4); |
2335 | |
2336 | QVERIFY(lout->called); |
2337 | } |
2338 | |
2339 | void tst_QTextDocument::clonePreservesMetaInformation() |
2340 | { |
2341 | const QString title("Foobar" ); |
2342 | const QString url("about:blank" ); |
2343 | doc->setHtml("<html><head><title>" + title + "</title></head><body>Hrm</body></html>" ); |
2344 | doc->setMetaInformation(info: QTextDocument::DocumentUrl, url); |
2345 | QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), title); |
2346 | QCOMPARE(doc->metaInformation(QTextDocument::DocumentUrl), url); |
2347 | |
2348 | QTextDocument *clone = doc->clone(); |
2349 | QCOMPARE(clone->metaInformation(QTextDocument::DocumentTitle), title); |
2350 | QCOMPARE(clone->metaInformation(QTextDocument::DocumentUrl), url); |
2351 | delete clone; |
2352 | } |
2353 | |
2354 | void tst_QTextDocument::clonePreservesPageSize() |
2355 | { |
2356 | QSizeF sz(100., 100.); |
2357 | doc->setPageSize(sz); |
2358 | QTextDocument *clone = doc->clone(); |
2359 | QCOMPARE(clone->pageSize(), sz); |
2360 | delete clone; |
2361 | } |
2362 | |
2363 | void tst_QTextDocument::clonePreservesPageBreakPolicies() |
2364 | { |
2365 | QTextTableFormat tableFmt; |
2366 | tableFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter); |
2367 | |
2368 | QTextBlockFormat blockFmt; |
2369 | blockFmt.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysBefore); |
2370 | |
2371 | QTextCursor cursor(doc); |
2372 | |
2373 | cursor.setBlockFormat(blockFmt); |
2374 | cursor.insertText(text: "foo" ); |
2375 | cursor.insertTable(rows: 2, cols: 2, format: tableFmt); |
2376 | |
2377 | QTextDocument *clone = doc->clone(); |
2378 | QCOMPARE(clone->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore); |
2379 | QVERIFY(!clone->rootFrame()->childFrames().isEmpty()); |
2380 | QCOMPARE(clone->rootFrame()->childFrames().first()->frameFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter); |
2381 | delete clone; |
2382 | } |
2383 | |
2384 | void tst_QTextDocument::clonePreservesDefaultFont() |
2385 | { |
2386 | QFont f = doc->defaultFont(); |
2387 | QVERIFY(f.pointSize() != 100); |
2388 | f.setPointSize(100); |
2389 | doc->setDefaultFont(f); |
2390 | QTextDocument *clone = doc->clone(); |
2391 | QCOMPARE(clone->defaultFont(), f); |
2392 | delete clone; |
2393 | } |
2394 | |
2395 | void tst_QTextDocument::clonePreservesResources() |
2396 | { |
2397 | QUrl testUrl(":/foobar" ); |
2398 | QVariant testResource("hello world" ); |
2399 | |
2400 | doc->addResource(type: QTextDocument::ImageResource, name: testUrl, resource: testResource); |
2401 | QTextDocument *clone = doc->clone(); |
2402 | QVERIFY(clone->resource(QTextDocument::ImageResource, testUrl) == testResource); |
2403 | delete clone; |
2404 | } |
2405 | |
2406 | void tst_QTextDocument::clonePreservesUserStates() |
2407 | { |
2408 | QTextCursor cursor(doc); |
2409 | cursor.insertText(text: "bla bla bla" ); |
2410 | cursor.block().setUserState(1); |
2411 | cursor.insertBlock(); |
2412 | cursor.insertText(text: "foo bar" ); |
2413 | cursor.block().setUserState(2); |
2414 | cursor.insertBlock(); |
2415 | cursor.insertText(text: "no user state" ); |
2416 | |
2417 | QTextDocument *clone = doc->clone(); |
2418 | QTextBlock b1 = doc->begin(), b2 = clone->begin(); |
2419 | while (b1 != doc->end()) { |
2420 | b1 = b1.next(); |
2421 | b2 = b2.next(); |
2422 | QCOMPARE(b1.userState(), b2.userState()); |
2423 | } |
2424 | QCOMPARE(b2, clone->end()); |
2425 | delete clone; |
2426 | } |
2427 | |
2428 | void tst_QTextDocument::clonePreservesRootFrameFormat() |
2429 | { |
2430 | doc->setPlainText("Hello" ); |
2431 | QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); |
2432 | fmt.setMargin(200); |
2433 | doc->rootFrame()->setFrameFormat(fmt); |
2434 | QCOMPARE(doc->rootFrame()->frameFormat().margin(), qreal(200)); |
2435 | QTextDocument *copy = doc->clone(); |
2436 | QCOMPARE(copy->rootFrame()->frameFormat().margin(), qreal(200)); |
2437 | delete copy; |
2438 | } |
2439 | |
2440 | void tst_QTextDocument::clonePreservesIndentWidth() |
2441 | { |
2442 | doc->setIndentWidth(42); |
2443 | QTextDocument *clone = doc->clone(); |
2444 | QCOMPARE(clone->indentWidth(), qreal(42)); |
2445 | delete clone; |
2446 | } |
2447 | |
2448 | void tst_QTextDocument::clonePreservesFormatsWhenEmpty() |
2449 | { |
2450 | QTextDocument document; |
2451 | QTextCursor cursor(&document); |
2452 | |
2453 | // Change a few char format attributes |
2454 | QTextCharFormat charFormat; |
2455 | charFormat.setFontPointSize(charFormat.fontPointSize() + 1); |
2456 | charFormat.setFontWeight(charFormat.fontWeight() + 1); |
2457 | cursor.setBlockCharFormat(charFormat); |
2458 | |
2459 | // Change a few block format attributes |
2460 | QTextBlockFormat blockFormat; |
2461 | blockFormat.setAlignment(Qt::AlignRight); // The default is Qt::AlignLeft |
2462 | blockFormat.setIndent(blockFormat.indent() + 1); |
2463 | cursor.setBlockFormat(blockFormat); |
2464 | |
2465 | auto clone = document.clone(); |
2466 | QTextCursor cloneCursor(clone); |
2467 | |
2468 | QCOMPARE(cloneCursor.blockCharFormat().fontPointSize(), charFormat.fontPointSize()); |
2469 | QCOMPARE(cloneCursor.blockCharFormat().fontWeight(), charFormat.fontWeight()); |
2470 | QCOMPARE(cloneCursor.blockFormat().alignment(), blockFormat.alignment()); |
2471 | QCOMPARE(cloneCursor.blockFormat().indent(), blockFormat.indent()); |
2472 | } |
2473 | |
2474 | void tst_QTextDocument::blockCount() |
2475 | { |
2476 | QCOMPARE(doc->blockCount(), 1); |
2477 | cursor.insertBlock(); |
2478 | QCOMPARE(doc->blockCount(), 2); |
2479 | cursor.insertBlock(); |
2480 | QCOMPARE(doc->blockCount(), 3); |
2481 | cursor.insertText(text: "blah blah" ); |
2482 | QCOMPARE(doc->blockCount(), 3); |
2483 | doc->undo(); |
2484 | doc->undo(); |
2485 | QCOMPARE(doc->blockCount(), 2); |
2486 | doc->undo(); |
2487 | QCOMPARE(doc->blockCount(), 1); |
2488 | } |
2489 | |
2490 | void tst_QTextDocument::resolvedFontInEmptyFormat() |
2491 | { |
2492 | QFont font; |
2493 | font.setPointSize(42); |
2494 | doc->setDefaultFont(font); |
2495 | QTextCharFormat fmt = doc->begin().charFormat(); |
2496 | QVERIFY(fmt.properties().isEmpty()); |
2497 | QCOMPARE(fmt.font(), font); |
2498 | } |
2499 | |
2500 | void tst_QTextDocument::defaultRootFrameMargin() |
2501 | { |
2502 | QCOMPARE(doc->rootFrame()->frameFormat().margin(), 4.0); |
2503 | } |
2504 | |
2505 | class TestDocument : public QTextDocument |
2506 | { |
2507 | public: |
2508 | inline TestDocument(const QUrl &testUrl, const QString &testString) |
2509 | : url(testUrl), string(testString), resourceLoaded(false) {} |
2510 | |
2511 | bool hasResourceCached(); |
2512 | |
2513 | protected: |
2514 | virtual QVariant loadResource(int type, const QUrl &name); |
2515 | |
2516 | private: |
2517 | QUrl url; |
2518 | QString string; |
2519 | bool resourceLoaded; |
2520 | }; |
2521 | |
2522 | bool TestDocument::hasResourceCached() |
2523 | { |
2524 | resourceLoaded = false; |
2525 | resource(type: QTextDocument::ImageResource, name: url); |
2526 | return !resourceLoaded; |
2527 | } |
2528 | |
2529 | QVariant TestDocument::loadResource(int type, const QUrl &name) |
2530 | { |
2531 | if (type == QTextDocument::ImageResource |
2532 | && name == url) { |
2533 | resourceLoaded = true; |
2534 | return string; |
2535 | } |
2536 | return QTextDocument::loadResource(type, name); |
2537 | } |
2538 | |
2539 | void tst_QTextDocument::clearResources() |
2540 | { |
2541 | // regular resource for QTextDocument |
2542 | QUrl testUrl(":/foobar" ); |
2543 | QVariant testResource("hello world" ); |
2544 | |
2545 | // implicitly cached resource, initially loaded through TestDocument::loadResource() |
2546 | QUrl cacheUrl(":/blub" ); |
2547 | QString cacheResource("mah" ); |
2548 | |
2549 | TestDocument doc(cacheUrl, cacheResource); |
2550 | doc.addResource(type: QTextDocument::ImageResource, name: testUrl, resource: testResource); |
2551 | |
2552 | QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); |
2553 | |
2554 | doc.setPlainText("Hah" ); |
2555 | QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); |
2556 | |
2557 | doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>" ); |
2558 | QVERIFY(doc.resource(QTextDocument::ImageResource, testUrl) == testResource); |
2559 | QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource); |
2560 | |
2561 | doc.clear(); |
2562 | QVERIFY(!doc.resource(QTextDocument::ImageResource, testUrl).isValid()); |
2563 | QVERIFY(!doc.hasResourceCached()); |
2564 | doc.clear(); |
2565 | |
2566 | doc.setHtml("<b>Mooo</b><img src=\":/blub\"/>" ); |
2567 | QVERIFY(doc.resource(QTextDocument::ImageResource, cacheUrl) == cacheResource); |
2568 | |
2569 | doc.setPlainText("Foob" ); |
2570 | QVERIFY(!doc.hasResourceCached()); |
2571 | } |
2572 | |
2573 | void tst_QTextDocument::setPlainText() |
2574 | { |
2575 | doc->setPlainText("Hello World" ); |
2576 | QString s("" ); |
2577 | doc->setPlainText(s); |
2578 | QCOMPARE(doc->toPlainText(), s); |
2579 | } |
2580 | |
2581 | void tst_QTextDocument::toPlainText_data() |
2582 | { |
2583 | QTest::addColumn<QString>(name: "html" ); |
2584 | QTest::addColumn<QString>(name: "expectedPlainText" ); |
2585 | |
2586 | QTest::newRow(dataTag: "nbsp" ) << "Hello World" << "Hello World" ; |
2587 | QTest::newRow(dataTag: "empty_div" ) << "<div></div>hello" << "hello" ; |
2588 | QTest::newRow(dataTag: "br_and_p" ) << "<p>first<br></p><p>second<br></p>" << "first\n\nsecond\n" ; |
2589 | QTest::newRow(dataTag: "div" ) << "first<div>second<br>third</div>fourth" << "first\nsecond\nthird\nfourth" ; // <div> and </div> become newlines... |
2590 | QTest::newRow(dataTag: "br_text_end_of_div" ) << "<div><div>first<br>moretext</div>second<br></div>" << "first\nmoretext\nsecond\n" ; // ... when there is text before <div> |
2591 | QTest::newRow(dataTag: "br_end_of_div_like_gmail" ) << "<div><div><div>first<br></div>second<br></div>third<br></div>" << "first\nsecond\nthird\n" ; // ... and when there is text before </div> |
2592 | QTest::newRow(dataTag: "p_and_div" ) << "<div><div>first<p>second</p></div>third</div>" << "first\nsecond\nthird" ; |
2593 | } |
2594 | |
2595 | void tst_QTextDocument::toPlainText() |
2596 | { |
2597 | QFETCH(QString, html); |
2598 | QFETCH(QString, expectedPlainText); |
2599 | |
2600 | doc->setHtml(html); |
2601 | QCOMPARE(doc->toPlainText(), expectedPlainText); |
2602 | } |
2603 | |
2604 | void tst_QTextDocument::toRawText() |
2605 | { |
2606 | doc->setHtml(" " ); |
2607 | |
2608 | QString rawText = doc->toRawText(); |
2609 | QCOMPARE(rawText.size(), 1); |
2610 | QCOMPARE(rawText.at(0).unicode(), ushort(QChar::Nbsp)); |
2611 | } |
2612 | |
2613 | |
2614 | void tst_QTextDocument::deleteTextObjectsOnClear() |
2615 | { |
2616 | QPointer<QTextTable> table = cursor.insertTable(rows: 2, cols: 2); |
2617 | QVERIFY(!table.isNull()); |
2618 | doc->clear(); |
2619 | QVERIFY(table.isNull()); |
2620 | } |
2621 | |
2622 | void tst_QTextDocument::defaultStyleSheet() |
2623 | { |
2624 | const QColor green("green" ); |
2625 | const QString sheet("p { background-color: green; }" ); |
2626 | QVERIFY(doc->defaultStyleSheet().isEmpty()); |
2627 | doc->setDefaultStyleSheet(sheet); |
2628 | QCOMPARE(doc->defaultStyleSheet(), sheet); |
2629 | |
2630 | cursor.insertHtml(html: "<p>test" ); |
2631 | QTextBlockFormat fmt = doc->begin().blockFormat(); |
2632 | QCOMPARE(fmt.background().color(), green); |
2633 | |
2634 | doc->clear(); |
2635 | cursor.insertHtml(html: "<p>test" ); |
2636 | fmt = doc->begin().blockFormat(); |
2637 | QCOMPARE(fmt.background().color(), green); |
2638 | |
2639 | QTextDocument *clone = doc->clone(); |
2640 | QCOMPARE(clone->defaultStyleSheet(), sheet); |
2641 | cursor = QTextCursor(clone); |
2642 | cursor.insertHtml(html: "<p>test" ); |
2643 | fmt = clone->begin().blockFormat(); |
2644 | QCOMPARE(fmt.background().color(), green); |
2645 | delete clone; |
2646 | |
2647 | cursor = QTextCursor(doc); |
2648 | cursor.insertHtml(html: "<p>test" ); |
2649 | fmt = doc->begin().blockFormat(); |
2650 | QCOMPARE(fmt.background().color(), green); |
2651 | |
2652 | doc->clear(); |
2653 | cursor.insertHtml(html: "<style>p { background-color: red; }</style><p>test" ); |
2654 | fmt = doc->begin().blockFormat(); |
2655 | QCOMPARE(fmt.background().color(), QColor(Qt::red)); |
2656 | |
2657 | doc->clear(); |
2658 | doc->setDefaultStyleSheet("invalid style sheet...." ); |
2659 | cursor.insertHtml(html: "<p>test" ); |
2660 | fmt = doc->begin().blockFormat(); |
2661 | QVERIFY(fmt.background().color() != QColor("green" )); |
2662 | } |
2663 | |
2664 | void tst_QTextDocument::maximumBlockCount() |
2665 | { |
2666 | QCOMPARE(doc->maximumBlockCount(), 0); |
2667 | QVERIFY(doc->isUndoRedoEnabled()); |
2668 | |
2669 | cursor.insertBlock(); |
2670 | cursor.insertText(text: "Blah" ); |
2671 | cursor.insertBlock(); |
2672 | cursor.insertText(text: "Foo" ); |
2673 | QCOMPARE(doc->blockCount(), 3); |
2674 | QCOMPARE(doc->toPlainText(), QString("\nBlah\nFoo" )); |
2675 | |
2676 | doc->setMaximumBlockCount(1); |
2677 | QVERIFY(!doc->isUndoRedoEnabled()); |
2678 | |
2679 | QCOMPARE(doc->blockCount(), 1); |
2680 | QCOMPARE(doc->toPlainText(), QString("Foo" )); |
2681 | |
2682 | cursor.insertBlock(); |
2683 | cursor.insertText(text: "Hello" ); |
2684 | doc->setMaximumBlockCount(1); |
2685 | QCOMPARE(doc->blockCount(), 1); |
2686 | QCOMPARE(doc->toPlainText(), QString("Hello" )); |
2687 | |
2688 | doc->setMaximumBlockCount(100); |
2689 | for (int i = 0; i < 1000; ++i) { |
2690 | cursor.insertBlock(); |
2691 | cursor.insertText(text: "Blah)" ); |
2692 | QVERIFY(doc->blockCount() <= 100); |
2693 | } |
2694 | |
2695 | cursor.movePosition(op: QTextCursor::End); |
2696 | QCOMPARE(cursor.blockNumber(), 99); |
2697 | QTextCharFormat fmt; |
2698 | fmt.setFontItalic(true); |
2699 | cursor.setBlockCharFormat(fmt); |
2700 | cursor.movePosition(op: QTextCursor::Start); |
2701 | QVERIFY(!cursor.blockCharFormat().fontItalic()); |
2702 | |
2703 | doc->setMaximumBlockCount(1); |
2704 | QVERIFY(cursor.blockCharFormat().fontItalic()); |
2705 | |
2706 | cursor.insertTable(rows: 2, cols: 2); |
2707 | QCOMPARE(doc->blockCount(), 6); |
2708 | cursor.insertBlock(); |
2709 | QCOMPARE(doc->blockCount(), 1); |
2710 | } |
2711 | |
2712 | void tst_QTextDocument::adjustSize() |
2713 | { |
2714 | // avoid ugly tooltips like in task 125583 |
2715 | QString text("Test Text" ); |
2716 | doc->setPlainText(text); |
2717 | doc->rootFrame()->setFrameFormat(QTextFrameFormat()); |
2718 | doc->adjustSize(); |
2719 | QCOMPARE(doc->size().width(), doc->idealWidth()); |
2720 | } |
2721 | |
2722 | void tst_QTextDocument::initialUserData() |
2723 | { |
2724 | doc->setPlainText("Hello" ); |
2725 | QTextBlock block = doc->begin(); |
2726 | block.setUserData(new QTextBlockUserData); |
2727 | QVERIFY(block.userData()); |
2728 | doc->documentLayout(); |
2729 | QVERIFY(block.userData()); |
2730 | doc->setDocumentLayout(new QTestDocumentLayout(doc)); |
2731 | QVERIFY(!block.userData()); |
2732 | } |
2733 | |
2734 | void tst_QTextDocument::html_defaultFont() |
2735 | { |
2736 | QFont f; |
2737 | f.setItalic(true); |
2738 | f.setWeight(QFont::Bold); |
2739 | doc->setDefaultFont(f); |
2740 | doc->setPlainText("Test" ); |
2741 | |
2742 | QString bodyPart = QString::fromLatin1(str: "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:italic;\">" ) |
2743 | .arg(a: f.family()) |
2744 | .arg(a: cssFontSizeString(font: f)) |
2745 | .arg(a: f.weight() * 8); |
2746 | |
2747 | QString html = doc->toHtml(); |
2748 | if (!html.contains(s: bodyPart)) { |
2749 | qDebug() << "html:" << html; |
2750 | qDebug() << "expected body:" << bodyPart; |
2751 | QVERIFY(html.contains(bodyPart)); |
2752 | } |
2753 | |
2754 | if (html.contains(s: "span" )) |
2755 | qDebug() << "html:" << html; |
2756 | QVERIFY(!html.contains("<span style" )); |
2757 | } |
2758 | |
2759 | void tst_QTextDocument::blockCountChanged() |
2760 | { |
2761 | QSignalSpy spy(doc, SIGNAL(blockCountChanged(int))); |
2762 | |
2763 | doc->setPlainText("Foo" ); |
2764 | |
2765 | QCOMPARE(doc->blockCount(), 1); |
2766 | QCOMPARE(spy.count(), 0); |
2767 | |
2768 | spy.clear(); |
2769 | |
2770 | doc->setPlainText("Foo\nBar" ); |
2771 | QCOMPARE(doc->blockCount(), 2); |
2772 | QCOMPARE(spy.count(), 1); |
2773 | QCOMPARE(spy.at(0).value(0).toInt(), 2); |
2774 | |
2775 | spy.clear(); |
2776 | |
2777 | cursor.movePosition(op: QTextCursor::End); |
2778 | cursor.insertText(text: "Blahblah" ); |
2779 | |
2780 | QCOMPARE(spy.count(), 0); |
2781 | |
2782 | cursor.insertBlock(); |
2783 | QCOMPARE(spy.count(), 1); |
2784 | QCOMPARE(spy.at(0).value(0).toInt(), 3); |
2785 | |
2786 | spy.clear(); |
2787 | doc->undo(); |
2788 | |
2789 | QCOMPARE(spy.count(), 1); |
2790 | QCOMPARE(spy.at(0).value(0).toInt(), 2); |
2791 | } |
2792 | |
2793 | void tst_QTextDocument::nonZeroDocumentLengthOnClear() |
2794 | { |
2795 | QTestDocumentLayout *lout = new QTestDocumentLayout(doc); |
2796 | doc->setDocumentLayout(lout); |
2797 | |
2798 | doc->clear(); |
2799 | QVERIFY(lout->called); |
2800 | QVERIFY(!lout->lastDocumentLengths.contains(0)); |
2801 | } |
2802 | |
2803 | void tst_QTextDocument::setTextPreservesUndoRedoEnabled() |
2804 | { |
2805 | QVERIFY(doc->isUndoRedoEnabled()); |
2806 | |
2807 | doc->setPlainText("Test" ); |
2808 | |
2809 | QVERIFY(doc->isUndoRedoEnabled()); |
2810 | |
2811 | doc->setUndoRedoEnabled(false); |
2812 | QVERIFY(!doc->isUndoRedoEnabled()); |
2813 | doc->setPlainText("Test2" ); |
2814 | QVERIFY(!doc->isUndoRedoEnabled()); |
2815 | |
2816 | doc->setHtml("<p>hello" ); |
2817 | QVERIFY(!doc->isUndoRedoEnabled()); |
2818 | } |
2819 | |
2820 | void tst_QTextDocument::firstLast() |
2821 | { |
2822 | QCOMPARE(doc->blockCount(), 1); |
2823 | QCOMPARE(doc->firstBlock(), doc->lastBlock()); |
2824 | |
2825 | doc->setPlainText("Hello\nTest\nWorld" ); |
2826 | |
2827 | QCOMPARE(doc->blockCount(), 3); |
2828 | QVERIFY(doc->firstBlock() != doc->lastBlock()); |
2829 | |
2830 | QCOMPARE(doc->firstBlock().text(), QString("Hello" )); |
2831 | QCOMPARE(doc->lastBlock().text(), QString("World" )); |
2832 | |
2833 | // manual forward loop |
2834 | QTextBlock block = doc->firstBlock(); |
2835 | |
2836 | QVERIFY(block.isValid()); |
2837 | QCOMPARE(block.text(), QString("Hello" )); |
2838 | |
2839 | block = block.next(); |
2840 | |
2841 | QVERIFY(block.isValid()); |
2842 | QCOMPARE(block.text(), QString("Test" )); |
2843 | |
2844 | block = block.next(); |
2845 | |
2846 | QVERIFY(block.isValid()); |
2847 | QCOMPARE(block.text(), QString("World" )); |
2848 | |
2849 | block = block.next(); |
2850 | QVERIFY(!block.isValid()); |
2851 | |
2852 | // manual backward loop |
2853 | block = doc->lastBlock(); |
2854 | |
2855 | QVERIFY(block.isValid()); |
2856 | QCOMPARE(block.text(), QString("World" )); |
2857 | |
2858 | block = block.previous(); |
2859 | |
2860 | QVERIFY(block.isValid()); |
2861 | QCOMPARE(block.text(), QString("Test" )); |
2862 | |
2863 | block = block.previous(); |
2864 | |
2865 | QVERIFY(block.isValid()); |
2866 | QCOMPARE(block.text(), QString("Hello" )); |
2867 | |
2868 | block = block.previous(); |
2869 | QVERIFY(!block.isValid()); |
2870 | } |
2871 | |
2872 | const QString backgroundImage_html("<body><table><tr><td background=\"foo.png\">Blah</td></tr></table></body>" ); |
2873 | |
2874 | void tst_QTextDocument::backgroundImage_checkExpectedHtml(const QTextDocument &doc) |
2875 | { |
2876 | QString expectedHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" " |
2877 | "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" |
2878 | "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n" |
2879 | "p, li { white-space: pre-wrap; }\n" |
2880 | "</style></head>" |
2881 | "<body style=\" font-family:'%1'; font-size:%2; font-weight:%3; font-style:%4;\">\n" |
2882 | "<table border=\"0\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;\" cellspacing=\"2\" cellpadding=\"0\">" |
2883 | "\n<tr>\n<td background=\"foo.png\">" |
2884 | "\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Blah</p>" |
2885 | "</td></tr></table></body></html>" ); |
2886 | |
2887 | expectedHtml = expectedHtml |
2888 | .arg(a: defaultFont.family()) |
2889 | .arg(a: cssFontSizeString(font: defaultFont)) |
2890 | .arg(a: defaultFont.weight() * 8) |
2891 | .arg(a: (defaultFont.italic() ? "italic" : "normal" )); |
2892 | |
2893 | writeActualAndExpected(testTag: QTest::currentTestFunction(), actual: doc.toHtml(), expected: expectedHtml); |
2894 | |
2895 | QCOMPARE(doc.toHtml(), expectedHtml); |
2896 | } |
2897 | |
2898 | void tst_QTextDocument::buildRegExpData() |
2899 | { |
2900 | QTest::addColumn<QString>(name: "haystack" ); |
2901 | QTest::addColumn<QString>(name: "needle" ); |
2902 | QTest::addColumn<int>(name: "flags" ); |
2903 | QTest::addColumn<int>(name: "from" ); |
2904 | QTest::addColumn<int>(name: "anchor" ); |
2905 | QTest::addColumn<int>(name: "position" ); |
2906 | |
2907 | // match integers 0 to 99 |
2908 | QTest::newRow(dataTag: "1" ) << "23" << "^\\d\\d?$" << int(QTextDocument::FindCaseSensitively) << 0 << 0 << 2; |
2909 | // match ampersands but not & |
2910 | QTest::newRow(dataTag: "2" ) << "His & hers & theirs" << "&(?!amp;)" << int(QTextDocument::FindCaseSensitively) << 0 << 15 << 16; |
2911 | //backward search |
2912 | QTest::newRow(dataTag: "3" ) << QString::fromLatin1(str: "HelloBlahWorld Blah Hah" ) |
2913 | << "h" << int(QTextDocument::FindBackward) << 18 << 8 << 9; |
2914 | } |
2915 | |
2916 | void tst_QTextDocument::backgroundImage_toHtml() |
2917 | { |
2918 | CREATE_DOC_AND_CURSOR(); |
2919 | |
2920 | doc.setHtml(backgroundImage_html); |
2921 | backgroundImage_checkExpectedHtml(doc); |
2922 | } |
2923 | |
2924 | void tst_QTextDocument::backgroundImage_toHtml2() |
2925 | { |
2926 | CREATE_DOC_AND_CURSOR(); |
2927 | |
2928 | cursor.insertHtml(html: backgroundImage_html); |
2929 | backgroundImage_checkExpectedHtml(doc); |
2930 | } |
2931 | |
2932 | void tst_QTextDocument::backgroundImage_clone() |
2933 | { |
2934 | CREATE_DOC_AND_CURSOR(); |
2935 | |
2936 | doc.setHtml(backgroundImage_html); |
2937 | QTextDocument *clone = doc.clone(); |
2938 | backgroundImage_checkExpectedHtml(doc: *clone); |
2939 | delete clone; |
2940 | } |
2941 | |
2942 | void tst_QTextDocument::backgroundImage_copy() |
2943 | { |
2944 | CREATE_DOC_AND_CURSOR(); |
2945 | |
2946 | doc.setHtml(backgroundImage_html); |
2947 | QTextDocumentFragment fragment(&doc); |
2948 | |
2949 | { |
2950 | CREATE_DOC_AND_CURSOR(); |
2951 | |
2952 | cursor.insertFragment(fragment); |
2953 | backgroundImage_checkExpectedHtml(doc); |
2954 | } |
2955 | } |
2956 | |
2957 | void tst_QTextDocument::documentCleanup() |
2958 | { |
2959 | QTextDocument doc; |
2960 | QTextCursor cursor(&doc); |
2961 | cursor.insertText(text: "d\nfoo\nbar\n" ); |
2962 | doc.documentLayout(); // forces relayout |
2963 | |
2964 | // remove char 1 |
2965 | cursor.setPosition(pos: 0); |
2966 | QSizeF size = doc.documentLayout()->documentSize(); |
2967 | cursor.deleteChar(); |
2968 | // the size should be unchanged. |
2969 | QCOMPARE(doc.documentLayout()->documentSize(), size); |
2970 | } |
2971 | |
2972 | void tst_QTextDocument::characterAt() |
2973 | { |
2974 | QTextDocument doc; |
2975 | QTextCursor cursor(&doc); |
2976 | QString text("12345\n67890" ); |
2977 | cursor.insertText(text); |
2978 | int length = doc.characterCount(); |
2979 | QCOMPARE(length, text.length() + 1); |
2980 | QCOMPARE(doc.characterAt(length-1), QChar(QChar::ParagraphSeparator)); |
2981 | QCOMPARE(doc.characterAt(-1), QChar()); |
2982 | QCOMPARE(doc.characterAt(length), QChar()); |
2983 | QCOMPARE(doc.characterAt(length + 1), QChar()); |
2984 | for (int i = 0; i < text.length(); ++i) { |
2985 | QChar c = text.at(i); |
2986 | if (c == QLatin1Char('\n')) |
2987 | c = QChar(QChar::ParagraphSeparator); |
2988 | QCOMPARE(doc.characterAt(i), c); |
2989 | } |
2990 | } |
2991 | |
2992 | void tst_QTextDocument::revisions() |
2993 | { |
2994 | QTextDocument doc; |
2995 | QTextCursor cursor(&doc); |
2996 | QString text("Hello World" ); |
2997 | QCOMPARE(doc.firstBlock().revision(), 0); |
2998 | cursor.insertText(text); |
2999 | QCOMPARE(doc.firstBlock().revision(), 1); |
3000 | cursor.setPosition(pos: 6); |
3001 | cursor.insertBlock(); |
3002 | QCOMPARE(cursor.block().previous().revision(), 2); |
3003 | QCOMPARE(cursor.block().revision(), 2); |
3004 | cursor.insertText(text: "candle" ); |
3005 | QCOMPARE(cursor.block().revision(), 3); |
3006 | cursor.movePosition(op: QTextCursor::EndOfBlock); |
3007 | cursor.insertBlock(); // we are at the block end |
3008 | QCOMPARE(cursor.block().previous().revision(), 3); |
3009 | QCOMPARE(cursor.block().revision(), 4); |
3010 | cursor.insertText(text: "lightbulb" ); |
3011 | QCOMPARE(cursor.block().revision(), 5); |
3012 | cursor.movePosition(op: QTextCursor::StartOfBlock); |
3013 | cursor.insertBlock(); // we are the block start |
3014 | QCOMPARE(cursor.block().previous().revision(), 6); |
3015 | QCOMPARE(cursor.block().revision(), 5); |
3016 | } |
3017 | |
3018 | void tst_QTextDocument::revisionWithUndoCompressionAndUndo() |
3019 | { |
3020 | QTextDocument doc; |
3021 | QTextCursor cursor(&doc); |
3022 | cursor.insertText(text: "This is the beginning of it all." ); |
3023 | QCOMPARE(doc.firstBlock().revision(), 1); |
3024 | QCOMPARE(doc.revision(), 1); |
3025 | cursor.insertBlock(); |
3026 | QCOMPARE(doc.revision(), 2); |
3027 | cursor.insertText(text: "this" ); |
3028 | QCOMPARE(doc.revision(), 3); |
3029 | cursor.insertText(text: "is" ); |
3030 | QCOMPARE(doc.revision(), 4); |
3031 | cursor.insertText(text: "compressed" ); |
3032 | QCOMPARE(doc.revision(), 5); |
3033 | doc.undo(); |
3034 | QCOMPARE(doc.revision(), 6); |
3035 | QCOMPARE(doc.toPlainText(), QString("This is the beginning of it all.\n" )) ; |
3036 | cursor.setPosition(pos: 0); |
3037 | QCOMPARE(doc.firstBlock().revision(), 1); |
3038 | cursor.insertText(text: "Very beginnig" ); |
3039 | QCOMPARE(doc.firstBlock().revision(), 7); |
3040 | doc.undo(); |
3041 | QCOMPARE(doc.revision(), 8); |
3042 | QCOMPARE(doc.firstBlock().revision(), 1); |
3043 | |
3044 | cursor.beginEditBlock(); |
3045 | cursor.insertText(text: "Hello" ); |
3046 | cursor.insertBlock(); |
3047 | cursor.insertText(text: "world" ); |
3048 | cursor.endEditBlock(); |
3049 | QCOMPARE(doc.revision(), 9); |
3050 | doc.undo(); |
3051 | QCOMPARE(doc.revision(), 10); |
3052 | |
3053 | |
3054 | } |
3055 | |
3056 | void tst_QTextDocument::testUndoCommandAdded() |
3057 | { |
3058 | QVERIFY(doc); |
3059 | QSignalSpy spy(doc, SIGNAL(undoCommandAdded())); |
3060 | QVERIFY(spy.isValid()); |
3061 | QVERIFY(spy.isEmpty()); |
3062 | |
3063 | cursor.insertText(text: "a" ); |
3064 | QCOMPARE(spy.count(), 1); |
3065 | cursor.insertText(text: "b" ); // should be merged |
3066 | QCOMPARE(spy.count(), 1); |
3067 | cursor.insertText(text: "c" ); // should be merged |
3068 | QCOMPARE(spy.count(), 1); |
3069 | QCOMPARE(doc->toPlainText(), QString("abc" )); |
3070 | doc->undo(); |
3071 | QCOMPARE(doc->toPlainText(), QString("" )); |
3072 | |
3073 | doc->clear(); |
3074 | spy.clear(); |
3075 | cursor.insertText(text: "aaa" ); |
3076 | QCOMPARE(spy.count(), 1); |
3077 | |
3078 | spy.clear(); |
3079 | cursor.insertText(text: "aaaa\nbcd" ); |
3080 | QCOMPARE(spy.count(), 1); |
3081 | |
3082 | spy.clear(); |
3083 | cursor.beginEditBlock(); |
3084 | cursor.insertText(text: "aa" ); |
3085 | cursor.insertText(text: "bbb\n" ); |
3086 | cursor.setCharFormat(QTextCharFormat()); |
3087 | cursor.insertText(text: "\nccc" ); |
3088 | QVERIFY(spy.isEmpty()); |
3089 | cursor.endEditBlock(); |
3090 | QCOMPARE(spy.count(), 1); |
3091 | |
3092 | spy.clear(); |
3093 | cursor.insertBlock(); |
3094 | QCOMPARE(spy.count(), 1); |
3095 | |
3096 | spy.clear(); |
3097 | cursor.setPosition(pos: 5); |
3098 | QVERIFY(spy.isEmpty()); |
3099 | cursor.setCharFormat(QTextCharFormat()); |
3100 | QVERIFY(spy.isEmpty()); |
3101 | cursor.setPosition(pos: 10, mode: QTextCursor::KeepAnchor); |
3102 | QVERIFY(spy.isEmpty()); |
3103 | QTextCharFormat cf; |
3104 | cf.setFontItalic(true); |
3105 | cursor.mergeCharFormat(modifier: cf); |
3106 | QCOMPARE(spy.count(), 1); |
3107 | |
3108 | spy.clear(); |
3109 | doc->undo(); |
3110 | QCOMPARE(spy.count(), 0); |
3111 | doc->undo(); |
3112 | QCOMPARE(spy.count(), 0); |
3113 | spy.clear(); |
3114 | doc->redo(); |
3115 | QCOMPARE(spy.count(), 0); |
3116 | doc->redo(); |
3117 | QCOMPARE(spy.count(), 0); |
3118 | } |
3119 | |
3120 | void tst_QTextDocument::testUndoBlocks() |
3121 | { |
3122 | QVERIFY(doc); |
3123 | cursor.insertText(text: "Hello World" ); |
3124 | cursor.insertText(text: "period" ); |
3125 | doc->undo(); |
3126 | QCOMPARE(doc->toPlainText(), QString("" )); |
3127 | cursor.insertText(text: "Hello World" ); |
3128 | cursor.insertText(text: "One\nTwo\nThree" ); |
3129 | QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree" )); |
3130 | doc->undo(); |
3131 | QCOMPARE(doc->toPlainText(), QString("Hello World" )); |
3132 | cursor.insertText(text: "One\nTwo\nThree" ); |
3133 | cursor.insertText(text: "Trailing text" ); |
3134 | doc->undo(); |
3135 | QCOMPARE(doc->toPlainText(), QString("Hello WorldOne\nTwo\nThree" )); |
3136 | doc->undo(); |
3137 | QCOMPARE(doc->toPlainText(), QString("Hello World" )); |
3138 | doc->undo(); |
3139 | QCOMPARE(doc->toPlainText(), QString("" )); |
3140 | |
3141 | cursor.insertText(text: "town" ); |
3142 | cursor.beginEditBlock(); // Edit block 1 - Deletion/Insertion |
3143 | cursor.setPosition(pos: 0, mode: QTextCursor::KeepAnchor); |
3144 | cursor.insertText(text: "r" ); |
3145 | cursor.endEditBlock(); |
3146 | cursor.insertText(text: "est" ); // Merged into edit block 1 |
3147 | QCOMPARE(doc->toPlainText(), QString("rest" )); |
3148 | doc->undo(); |
3149 | QCOMPARE(doc->toPlainText(), QString("town" )); |
3150 | doc->undo(); |
3151 | QCOMPARE(doc->toPlainText(), QString("" )); |
3152 | |
3153 | // This case would not happen in practice. If the user typed out this text, it would all be part of one |
3154 | // edit block. This would cause the undo to clear all text. But for the purpose of testing the beginEditBlock |
3155 | // and endEditBlock calls with respect to qtextdocument this is tested. |
3156 | cursor.insertText(text: "quod" ); |
3157 | cursor.beginEditBlock(); // Edit block 1 - Insertion |
3158 | cursor.insertText(text: " erat" ); |
3159 | cursor.endEditBlock(); |
3160 | cursor.insertText(text: " demonstrandum" ); // Merged into edit block 1 |
3161 | QCOMPARE(doc->toPlainText(), QString("quod erat demonstrandum" )); |
3162 | doc->undo(); |
3163 | QCOMPARE(doc->toPlainText(), QString("quod" )); |
3164 | doc->undo(); |
3165 | QCOMPARE(doc->toPlainText(), QString("" )); |
3166 | } |
3167 | |
3168 | class Receiver : public QObject |
3169 | { |
3170 | Q_OBJECT |
3171 | public: |
3172 | QString first; |
3173 | public slots: |
3174 | void cursorPositionChanged() { |
3175 | if (first.isEmpty()) |
3176 | first = QLatin1String("cursorPositionChanged" ); |
3177 | } |
3178 | |
3179 | void contentsChange() { |
3180 | if (first.isEmpty()) |
3181 | first = QLatin1String("contentsChanged" ); |
3182 | } |
3183 | }; |
3184 | |
3185 | void tst_QTextDocument::receiveCursorPositionChangedAfterContentsChange() |
3186 | { |
3187 | QVERIFY(doc); |
3188 | doc->setDocumentLayout(new MyAbstractTextDocumentLayout(doc)); |
3189 | Receiver rec; |
3190 | connect(sender: doc, SIGNAL(cursorPositionChanged(QTextCursor)), |
3191 | receiver: &rec, SLOT(cursorPositionChanged())); |
3192 | connect(sender: doc, SIGNAL(contentsChange(int,int,int)), |
3193 | receiver: &rec, SLOT(contentsChange())); |
3194 | cursor.insertText(text: "Hello World" ); |
3195 | QCOMPARE(rec.first, QString("contentsChanged" )); |
3196 | } |
3197 | |
3198 | void tst_QTextDocument::QTBUG25778_pixelSizeFromHtml() |
3199 | { |
3200 | QTextDocument document1; |
3201 | QTextDocument document2; |
3202 | |
3203 | document1.setHtml("<span style=\"font-size: 24px\">Foobar</span>" ); |
3204 | document2.setHtml(document1.toHtml()); |
3205 | |
3206 | QTextCursor cursor(&document2); |
3207 | QCOMPARE(cursor.charFormat().font().pixelSize(), 24); |
3208 | } |
3209 | |
3210 | void tst_QTextDocument::copiedFontSize() |
3211 | { |
3212 | QTextDocument documentInput; |
3213 | QTextDocument documentOutput; |
3214 | |
3215 | QFont fontInput; |
3216 | fontInput.setPixelSize(24); |
3217 | |
3218 | QTextCursor cursorInput(&documentInput); |
3219 | QTextCharFormat formatInput = cursorInput.charFormat(); |
3220 | formatInput.setFont(fontInput); |
3221 | cursorInput.insertText(text: "Should be the same font" , format: formatInput); |
3222 | cursorInput.select(selection: QTextCursor::Document); |
3223 | |
3224 | QTextDocumentFragment fragmentInput(cursorInput); |
3225 | QString html = fragmentInput.toHtml(); |
3226 | |
3227 | QTextCursor cursorOutput(&documentOutput); |
3228 | QTextDocumentFragment fragmentOutput = QTextDocumentFragment::fromHtml(html); |
3229 | cursorOutput.insertFragment(fragment: fragmentOutput); |
3230 | |
3231 | QCOMPARE(cursorOutput.charFormat().font().pixelSize(), 24); |
3232 | } |
3233 | |
3234 | void tst_QTextDocument::htmlExportImportBlockCount() |
3235 | { |
3236 | QTextDocument document; |
3237 | { |
3238 | QTextCursor cursor(&document); |
3239 | cursor.insertText(text: "Foo" ); |
3240 | cursor.insertBlock(); |
3241 | cursor.insertBlock(); |
3242 | cursor.insertBlock(); |
3243 | cursor.insertBlock(); |
3244 | cursor.insertText(text: "Bar" ); |
3245 | } |
3246 | |
3247 | QCOMPARE(document.blockCount(), 5); |
3248 | QString html = document.toHtml(); |
3249 | |
3250 | document.clear(); |
3251 | document.setHtml(html); |
3252 | |
3253 | QCOMPARE(document.blockCount(), 5); |
3254 | } |
3255 | |
3256 | void tst_QTextDocument::QTBUG27354_spaceAndSoftSpace() |
3257 | { |
3258 | QTextDocument document; |
3259 | { |
3260 | QTextCursor cursor(&document); |
3261 | QTextBlockFormat blockFormat; |
3262 | blockFormat.setAlignment(Qt::AlignJustify); |
3263 | cursor.mergeBlockFormat(modifier: blockFormat); |
3264 | cursor.insertText(text: "ac" ); |
3265 | cursor.insertBlock(); |
3266 | cursor.insertText(text: " " ); |
3267 | cursor.insertText(text: QChar(0x2028)); |
3268 | } |
3269 | |
3270 | // Trigger justification of text |
3271 | QImage image(1000, 1000, QImage::Format_ARGB32); |
3272 | image.fill(pixel: 0); |
3273 | { |
3274 | QPainter p(&image); |
3275 | document.drawContents(painter: &p, rect: image.rect()); |
3276 | } |
3277 | { |
3278 | // If no p tag is specified it should not be inheriting it |
3279 | QTextDocument td; |
3280 | td.setHtml("<html><head><style type=\"text/css\">p { line-height: 200% }</style></head><body>Foo<ul><li>First</li></ul></body></html>" ); |
3281 | QTextBlock block = td.begin(); |
3282 | while (block.isValid()) { |
3283 | QTextBlockFormat fmt = block.blockFormat(); |
3284 | QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::SingleHeight)); |
3285 | QCOMPARE(fmt.lineHeight(), qreal(0)); |
3286 | block = block.next(); |
3287 | } |
3288 | } |
3289 | { |
3290 | QTextDocument td; |
3291 | td.setHtml("<html><head></head><body><p>Foo</p><ul><li>First</li></ul></body></html>" ); |
3292 | QList<double> originalMargins; |
3293 | QTextBlock block = td.begin(); |
3294 | while (block.isValid()) { |
3295 | originalMargins << block.blockFormat().topMargin(); |
3296 | block = block.next(); |
3297 | } |
3298 | originalMargins[0] = 85; |
3299 | td.setHtml("<html><head><style type=\"text/css\">body { margin-top: 85px; }</style></head><body><p>Foo</p><ul><li>First</li></ul></body></html>" ); |
3300 | block = td.begin(); |
3301 | int count = 0; |
3302 | while (block.isValid()) { |
3303 | QTextBlockFormat fmt = block.blockFormat(); |
3304 | QCOMPARE(fmt.topMargin(), originalMargins.at(count++)); |
3305 | block = block.next(); |
3306 | } |
3307 | } |
3308 | } |
3309 | |
3310 | class BaseDocument : public QTextDocument |
3311 | { |
3312 | public: |
3313 | QUrl loadedResource() const { return resourceUrl; } |
3314 | |
3315 | QVariant loadResource(int type, const QUrl &name) |
3316 | { |
3317 | resourceUrl = name; |
3318 | return QTextDocument::loadResource(type, name); |
3319 | } |
3320 | |
3321 | private: |
3322 | QUrl resourceUrl; |
3323 | }; |
3324 | |
3325 | void tst_QTextDocument::baseUrl_data() |
3326 | { |
3327 | QTest::addColumn<QUrl>(name: "base" ); |
3328 | QTest::addColumn<QUrl>(name: "resource" ); |
3329 | QTest::addColumn<QUrl>(name: "loaded" ); |
3330 | |
3331 | QTest::newRow(dataTag: "1" ) << QUrl() << QUrl("images/logo.png" ) << QUrl("images/logo.png" ); |
3332 | QTest::newRow(dataTag: "2" ) << QUrl("file:///path/to/content" ) << QUrl("images/logo.png" ) << QUrl("file:///path/to/images/logo.png" ); |
3333 | QTest::newRow(dataTag: "3" ) << QUrl("file:///path/to/content/" ) << QUrl("images/logo.png" ) << QUrl("file:///path/to/content/images/logo.png" ); |
3334 | QTest::newRow(dataTag: "4" ) << QUrl("file:///path/to/content/images" ) << QUrl("images/logo.png" ) << QUrl("file:///path/to/content/images/logo.png" ); |
3335 | QTest::newRow(dataTag: "5" ) << QUrl("file:///path/to/content/images/" ) << QUrl("images/logo.png" ) << QUrl("file:///path/to/content/images/images/logo.png" ); |
3336 | QTest::newRow(dataTag: "6" ) << QUrl("file:///path/to/content/images" ) << QUrl("../images/logo.png" ) << QUrl("file:///path/to/images/logo.png" ); |
3337 | QTest::newRow(dataTag: "7" ) << QUrl("file:///path/to/content/images/" ) << QUrl("../images/logo.png" ) << QUrl("file:///path/to/content/images/logo.png" ); |
3338 | QTest::newRow(dataTag: "8" ) << QUrl("file:///path/to/content/index.html" ) << QUrl("images/logo.png" ) << QUrl("file:///path/to/content/images/logo.png" ); |
3339 | } |
3340 | |
3341 | void tst_QTextDocument::baseUrl() |
3342 | { |
3343 | QFETCH(QUrl, base); |
3344 | QFETCH(QUrl, resource); |
3345 | QFETCH(QUrl, loaded); |
3346 | |
3347 | BaseDocument document; |
3348 | QVERIFY(!document.baseUrl().isValid()); |
3349 | document.setBaseUrl(base); |
3350 | QCOMPARE(document.baseUrl(), base); |
3351 | |
3352 | document.setHtml(QLatin1String("<img src='" ) + resource.toString() + QLatin1String("'/>" )); |
3353 | document.resource(type: QTextDocument::ImageResource, name: resource); |
3354 | QCOMPARE(document.loadedResource(), loaded); |
3355 | } |
3356 | |
3357 | void tst_QTextDocument::QTBUG28998_linkColor() |
3358 | { |
3359 | QPalette pal; |
3360 | pal.setColor(acr: QPalette::Link, acolor: QColor("tomato" )); |
3361 | QGuiApplication::setPalette(pal); |
3362 | |
3363 | QTextDocument doc; |
3364 | doc.setHtml("<a href=\"http://www.qt-project.org\">Qt</a>" ); |
3365 | |
3366 | QCOMPARE(doc.blockCount(), 1); |
3367 | QTextBlock block = doc.firstBlock(); |
3368 | QVERIFY(block.isValid()); |
3369 | |
3370 | QTextFragment fragment = block.begin().fragment(); |
3371 | QVERIFY(fragment.isValid()); |
3372 | |
3373 | QTextCharFormat format = fragment.charFormat(); |
3374 | QVERIFY(format.isValid()); |
3375 | QVERIFY(format.isAnchor()); |
3376 | QCOMPARE(format.anchorHref(), QStringLiteral("http://www.qt-project.org" )); |
3377 | |
3378 | QCOMPARE(format.foreground(), pal.link()); |
3379 | } |
3380 | |
3381 | class ContentsChangeHandler : public QObject |
3382 | { |
3383 | Q_OBJECT |
3384 | public: |
3385 | ContentsChangeHandler(QTextDocument *doc) |
3386 | : verticalMovementX(-1) |
3387 | , doc(doc) |
3388 | { |
3389 | connect(sender: doc, SIGNAL(contentsChange(int,int,int)), |
3390 | receiver: this, SLOT(saveModifiedText(int, int, int))); |
3391 | } |
3392 | |
3393 | private slots: |
3394 | void saveModifiedText(int from, int /*charsRemoved*/, int charsAdded) |
3395 | { |
3396 | QTextCursor tmp(doc); |
3397 | tmp.setPosition(pos: from); |
3398 | tmp.setPosition(pos: from + charsAdded, mode: QTextCursor::KeepAnchor); |
3399 | text = tmp.selectedText(); |
3400 | verticalMovementX = tmp.verticalMovementX(); |
3401 | } |
3402 | |
3403 | public: |
3404 | QString text; |
3405 | int verticalMovementX; |
3406 | private: |
3407 | QTextDocument *doc; |
3408 | }; |
3409 | |
3410 | void tst_QTextDocument::textCursorUsageWithinContentsChange() |
3411 | { |
3412 | // force creation of layout |
3413 | doc->documentLayout(); |
3414 | |
3415 | QTextCursor cursor(doc); |
3416 | cursor.insertText(text: "initial text" ); |
3417 | |
3418 | ContentsChangeHandler handler(doc); |
3419 | |
3420 | cursor.insertText(text: "new text" ); |
3421 | |
3422 | QCOMPARE(handler.text, QString("new text" )); |
3423 | QCOMPARE(handler.verticalMovementX, -1); |
3424 | } |
3425 | |
3426 | void tst_QTextDocument::cssInheritance() |
3427 | { |
3428 | { |
3429 | QTextDocument td; |
3430 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200% }</style></head><body>" |
3431 | "<p>Foo</p><p>Bar</p><p>Baz</p></body></html>" ); |
3432 | QTextBlock block = td.begin(); |
3433 | while (block.isValid()) { |
3434 | QTextBlockFormat fmt = block.blockFormat(); |
3435 | QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::ProportionalHeight)); |
3436 | QCOMPARE(fmt.lineHeight(), qreal(200)); |
3437 | block = block.next(); |
3438 | } |
3439 | } |
3440 | { |
3441 | QTextDocument td; |
3442 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200% } p { line-height: 300% }</style></head><body>" |
3443 | "<p style=\"line-height: 40px\">Foo</p><p>Bar</p><p>Baz</p></body></html>" ); |
3444 | QTextBlock block = td.begin(); |
3445 | QTextBlockFormat fmt = block.blockFormat(); |
3446 | QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::MinimumHeight)); |
3447 | QCOMPARE(fmt.lineHeight(), qreal(40)); |
3448 | block = block.next(); |
3449 | fmt = block.blockFormat(); |
3450 | QCOMPARE(fmt.lineHeightType(), int(QTextBlockFormat::ProportionalHeight)); |
3451 | QCOMPARE(fmt.lineHeight(), qreal(300)); |
3452 | } |
3453 | { |
3454 | QTextDocument td; |
3455 | td.setHtml("<html><head><style type=\"text/css\">body { font-weight: bold; background-color: #ff0000 }</style></head><body>" |
3456 | "<p>Foo</p><p>Bar</p><p>Baz</p></body></html>" ); |
3457 | QTextBlock block = td.begin(); |
3458 | while (block.isValid()) { |
3459 | QCOMPARE(block.blockFormat().background(), QBrush()); |
3460 | QVERIFY(block.charFormat().font().bold()); |
3461 | block = block.next(); |
3462 | } |
3463 | } |
3464 | { |
3465 | QTextDocument td; |
3466 | td.setHtml("<html><head><style type=\"text/css\">body { font-style: italic; font-weight: normal; }</style></head><body>" |
3467 | "<table><tr><th>Foo</th></tr><tr><td>Bar</td></tr></table></body></html>" ); |
3468 | QTextBlock block = td.begin(); |
3469 | // First is the table |
3470 | QTextCharFormat fmt = block.charFormat(); |
3471 | QVERIFY(!fmt.font().bold()); |
3472 | QVERIFY(fmt.font().italic()); |
3473 | // Then the th |
3474 | block = block.next(); |
3475 | fmt = block.charFormat(); |
3476 | QVERIFY(fmt.font().bold()); |
3477 | QVERIFY(fmt.font().italic()); |
3478 | // Then the td |
3479 | block = block.next(); |
3480 | fmt = block.charFormat(); |
3481 | QVERIFY(!fmt.font().bold()); |
3482 | QVERIFY(fmt.font().italic()); |
3483 | } |
3484 | { |
3485 | QTextDocument td; |
3486 | td.setHtml("<html><head><style type=\"text/css\">b { font-style: italic; font-weight: normal; }</style></head><body>" |
3487 | "<p>This should be <b>bold</b></p></body></html>" ); |
3488 | QTextBlock block = td.begin(); |
3489 | // First is the p |
3490 | QTextCharFormat fmt = block.charFormat(); |
3491 | QVERIFY(!fmt.font().bold()); |
3492 | QTextBlock::iterator it = block.begin(); |
3493 | // The non bold text is first |
3494 | QTextFragment currentFragment = it.fragment(); |
3495 | QVERIFY(currentFragment.isValid()); |
3496 | fmt = currentFragment.charFormat(); |
3497 | QVERIFY(!fmt.font().bold()); |
3498 | ++it; |
3499 | QVERIFY(!it.atEnd()); |
3500 | // Now check the "bold" text |
3501 | currentFragment = it.fragment(); |
3502 | QVERIFY(currentFragment.isValid()); |
3503 | fmt = currentFragment.charFormat(); |
3504 | QVERIFY(!fmt.font().bold()); |
3505 | QVERIFY(fmt.font().italic()); |
3506 | } |
3507 | { |
3508 | QTextDocument td; |
3509 | td.setHtml("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"test.css\"></head><body>" |
3510 | "<p>This should be <b>bold</b></p></body></html>" ); |
3511 | QTextBlock block = td.begin(); |
3512 | // First is the p |
3513 | QTextCharFormat fmt = block.charFormat(); |
3514 | QVERIFY(!fmt.font().bold()); |
3515 | QTextBlock::iterator it = block.begin(); |
3516 | // The non bold text is first |
3517 | QTextFragment currentFragment = it.fragment(); |
3518 | QVERIFY(currentFragment.isValid()); |
3519 | fmt = currentFragment.charFormat(); |
3520 | QVERIFY(!fmt.font().bold()); |
3521 | ++it; |
3522 | QVERIFY(!it.atEnd()); |
3523 | // Now check the bold text |
3524 | currentFragment = it.fragment(); |
3525 | QVERIFY(currentFragment.isValid()); |
3526 | fmt = currentFragment.charFormat(); |
3527 | QVERIFY(fmt.font().bold()); |
3528 | } |
3529 | } |
3530 | |
3531 | void tst_QTextDocument::lineHeightType() |
3532 | { |
3533 | { |
3534 | QTextDocument td; |
3535 | td.setHtml("<html><body>Foobar</body></html>" ); |
3536 | QTextBlock block = td.begin(); |
3537 | QTextBlockFormat format = block.blockFormat(); |
3538 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight)); |
3539 | QCOMPARE(format.lineHeight(), 0.0); |
3540 | } |
3541 | |
3542 | { |
3543 | QTextDocument td; |
3544 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 40px; }</style></head><body>Foobar</body></html>" ); |
3545 | QTextBlock block = td.begin(); |
3546 | QTextBlockFormat format = block.blockFormat(); |
3547 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight)); |
3548 | QCOMPARE(format.lineHeight(), 40.0); |
3549 | } |
3550 | |
3551 | { |
3552 | QTextDocument td; |
3553 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200%; }</style></head><body>Foobar</body></html>" ); |
3554 | QTextBlock block = td.begin(); |
3555 | QTextBlockFormat format = block.blockFormat(); |
3556 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3557 | QCOMPARE(format.lineHeight(), 200.0); |
3558 | } |
3559 | |
3560 | { |
3561 | QTextDocument td; |
3562 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 200%; -qt-line-height-type: single; }</style></head><body>Foobar</body></html>" ); |
3563 | QTextBlock block = td.begin(); |
3564 | QTextBlockFormat format = block.blockFormat(); |
3565 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::SingleHeight)); |
3566 | QCOMPARE(format.lineHeight(), 200.0); |
3567 | } |
3568 | |
3569 | { |
3570 | QTextDocument td; |
3571 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 40px; -qt-line-height-type: proportional; }</style></head><body>Foobar</body></html>" ); |
3572 | QTextBlock block = td.begin(); |
3573 | QTextBlockFormat format = block.blockFormat(); |
3574 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3575 | QCOMPARE(format.lineHeight(), 40.0); |
3576 | } |
3577 | |
3578 | { |
3579 | QTextDocument td; |
3580 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 10; -qt-line-height-type: fixed; }</style></head><body>Foobar</body></html>" ); |
3581 | QTextBlock block = td.begin(); |
3582 | QTextBlockFormat format = block.blockFormat(); |
3583 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); |
3584 | QCOMPARE(format.lineHeight(), 10.0); |
3585 | } |
3586 | |
3587 | { |
3588 | QTextDocument td; |
3589 | td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 10; -qt-line-height-type: fixed; }</style></head><body>Foobar</body></html>" ); |
3590 | QTextBlock block = td.begin(); |
3591 | QTextBlockFormat format = block.blockFormat(); |
3592 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); |
3593 | QCOMPARE(format.lineHeight(), 10.0); |
3594 | } |
3595 | |
3596 | { |
3597 | QTextDocument td; |
3598 | td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: proportional; line-height: 3; }</style></head><body>Foobar</body></html>" ); |
3599 | QTextBlock block = td.begin(); |
3600 | QTextBlockFormat format = block.blockFormat(); |
3601 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3602 | QCOMPARE(format.lineHeight(), 3.0); |
3603 | } |
3604 | |
3605 | { |
3606 | QTextDocument td; |
3607 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 2.5; -qt-line-height-type: proportional; }</style></head><body>Foobar</body></html>" ); |
3608 | QTextBlock block = td.begin(); |
3609 | QTextBlockFormat format = block.blockFormat(); |
3610 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3611 | QCOMPARE(format.lineHeight(), 2.5); |
3612 | } |
3613 | |
3614 | { |
3615 | QTextDocument td; |
3616 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 33; -qt-line-height-type: minimum; }</style></head><body>Foobar</body></html>" ); |
3617 | QTextBlock block = td.begin(); |
3618 | QTextBlockFormat format = block.blockFormat(); |
3619 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::MinimumHeight)); |
3620 | QCOMPARE(format.lineHeight(), 33.0); |
3621 | } |
3622 | |
3623 | { |
3624 | QTextDocument td; |
3625 | td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 200%; }</style></head><body>Foobar</body></html>" ); |
3626 | QTextBlock block = td.begin(); |
3627 | QTextBlockFormat format = block.blockFormat(); |
3628 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); |
3629 | QCOMPARE(format.lineHeight(), 200.0); |
3630 | } |
3631 | |
3632 | { |
3633 | QTextDocument td; |
3634 | td.setHtml("<html><head><style type=\"text/css\">body { -qt-line-height-type: fixed; line-height: 200px; }</style></head><body>Foobar</body></html>" ); |
3635 | QTextBlock block = td.begin(); |
3636 | QTextBlockFormat format = block.blockFormat(); |
3637 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::FixedHeight)); |
3638 | QCOMPARE(format.lineHeight(), 200.0); |
3639 | } |
3640 | } |
3641 | |
3642 | void tst_QTextDocument::cssLineHeightMultiplier() |
3643 | { |
3644 | { |
3645 | QTextDocument td; |
3646 | td.setHtml("<html><head><style type=\"text/css\">body { line-height: 10; }</style></head><body>Foobar</body></html>" ); |
3647 | QTextBlock block = td.begin(); |
3648 | QTextBlockFormat format = block.blockFormat(); |
3649 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3650 | QCOMPARE(format.lineHeight(), 1000.0); |
3651 | } |
3652 | |
3653 | { |
3654 | QTextDocument td; |
3655 | td.setHtml("<html><head><style type=\"text/css\">body {line-height: 1.38; }</style></head><body>Foobar</body></html>" ); |
3656 | QTextBlock block = td.begin(); |
3657 | QTextBlockFormat format = block.blockFormat(); |
3658 | QCOMPARE(int(format.lineHeightType()), int(QTextBlockFormat::ProportionalHeight)); |
3659 | QCOMPARE(format.lineHeight(), 138.0); |
3660 | } |
3661 | } |
3662 | |
3663 | void tst_QTextDocument::fontTagFace() |
3664 | { |
3665 | { |
3666 | QTextDocument td; |
3667 | td.setHtml("<html><body><font face='Times'>Foobar</font></body></html>" ); |
3668 | QTextFragment fragment = td.begin().begin().fragment(); |
3669 | QTextCharFormat format = fragment.charFormat(); |
3670 | QCOMPARE(format.fontFamily(), QLatin1String("Times" )); |
3671 | } |
3672 | |
3673 | { |
3674 | QTextDocument td; |
3675 | td.setHtml("<html><body><font face='Times, serif'>Foobar</font></body></html>" ); |
3676 | QTextFragment fragment = td.begin().begin().fragment(); |
3677 | QTextCharFormat format = fragment.charFormat(); |
3678 | QCOMPARE(format.fontFamily(), QLatin1String("Times" )); |
3679 | QStringList expectedFamilies = { QLatin1String("Times" ), QLatin1String("serif" ) }; |
3680 | QCOMPARE(format.fontFamilies().toStringList(), expectedFamilies); |
3681 | } |
3682 | } |
3683 | |
3684 | void tst_QTextDocument::mergeFontFamilies() |
3685 | { |
3686 | QTextDocument td; |
3687 | td.setHtml(QLatin1String( |
3688 | "<html><body>" |
3689 | "<span style=\" font-family:'MS Shell Dlg 2';\">Hello world</span>" |
3690 | "</body></html>" )); |
3691 | |
3692 | QTextCharFormat newFormat; |
3693 | newFormat.setFontFamily(QLatin1String("Jokerman" )); |
3694 | |
3695 | QTextCursor cursor = QTextCursor(&td); |
3696 | cursor.setPosition(pos: 0); |
3697 | cursor.setPosition(pos: QByteArray("Hello World" ).length(), mode: QTextCursor::KeepAnchor); |
3698 | cursor.mergeCharFormat(modifier: newFormat); |
3699 | |
3700 | QVERIFY(td.toHtml().contains(QLatin1String("font-family:'MS Shell Dlg 2','Jokerman';" ))); |
3701 | |
3702 | QTextCharFormat newFormatFamilies; |
3703 | newFormatFamilies.setFontFamilies({ QLatin1String("Arial" ), QLatin1String("Helvetica" ) }); |
3704 | cursor.mergeCharFormat(modifier: newFormatFamilies); |
3705 | |
3706 | QVERIFY(td.toHtml().contains(QLatin1String("font-family:'Arial','Helvetica','Jokerman'" ))); |
3707 | |
3708 | newFormatFamilies.setFontFamilies({ QLatin1String("Arial" ), QLatin1String("Jokerman" ), QLatin1String("Helvetica" ) }); |
3709 | cursor.mergeCharFormat(modifier: newFormatFamilies); |
3710 | |
3711 | QVERIFY(td.toHtml().contains(QLatin1String("font-family:'Arial','Jokerman','Helvetica'" ))); |
3712 | } |
3713 | |
3714 | void tst_QTextDocument::clearUndoRedoStacks() |
3715 | { |
3716 | QTextDocument doc; |
3717 | QTextCursor c(&doc); |
3718 | c.insertText(QStringLiteral("lorem ipsum" )); |
3719 | QVERIFY(doc.isUndoAvailable()); |
3720 | doc.clearUndoRedoStacks(historyToClear: QTextDocument::UndoStack); // Don't crash |
3721 | QVERIFY(!doc.isUndoAvailable()); |
3722 | } |
3723 | |
3724 | |
3725 | void tst_QTextDocument::contentsChangeIndices_data() |
3726 | { |
3727 | QTest::addColumn<QString>(name: "html" ); |
3728 | // adding list entries change the entire block, so change position is |
3729 | // not the same as the cursor position if this value is >= 0 |
3730 | QTest::addColumn<int>(name: "expectedBegin" ); |
3731 | |
3732 | QTest::addRow(format: "text" ) << "Test" << -1; |
3733 | QTest::addRow(format: "unnumbered list" ) << "<ul><li>Test</li></ul>" << 0; |
3734 | QTest::addRow(format: "numbered list" ) << "<ol><li>Test</li></ol>" << 0; |
3735 | QTest::addRow(format: "table" ) << "<table><tr><td>Test</td></tr></table>" << -1; |
3736 | } |
3737 | |
3738 | void tst_QTextDocument::contentsChangeIndices() |
3739 | { |
3740 | QFETCH(QString, html); |
3741 | QFETCH(int, expectedBegin); |
3742 | |
3743 | QTextDocument doc; |
3744 | QTestDocumentLayout *layout = new QTestDocumentLayout(&doc); |
3745 | doc.setDocumentLayout(layout); |
3746 | doc.setHtml(QString("<html><body>%1</body></html>" ).arg(a: html)); |
3747 | |
3748 | int documentLength = 0; |
3749 | int cursorLength = 0; |
3750 | int changeBegin = 0; |
3751 | int changeRemoved = 0; |
3752 | int changeAdded = 0; |
3753 | connect(sender: &doc, signal: &QTextDocument::contentsChange, context: this, slot: [&](int pos, int removed, int added){ |
3754 | documentLength = doc.characterCount(); |
3755 | |
3756 | QTextCursor cursor(&doc); |
3757 | cursor.movePosition(op: QTextCursor::End); |
3758 | // includes end-of-paragraph character |
3759 | cursorLength = cursor.position() + 1; |
3760 | |
3761 | changeBegin = pos; |
3762 | changeRemoved = removed; |
3763 | changeAdded = added; |
3764 | }); |
3765 | |
3766 | QTextCursor cursor(&doc); |
3767 | cursor.movePosition(op: QTextCursor::End); |
3768 | if (expectedBegin < 0) |
3769 | expectedBegin = cursor.position(); |
3770 | cursor.insertBlock(); |
3771 | |
3772 | const int changeEnd = changeBegin + changeAdded; |
3773 | |
3774 | QVERIFY(documentLength > 0); |
3775 | QCOMPARE(documentLength, cursorLength); |
3776 | QVERIFY(documentLength >= changeEnd); |
3777 | QCOMPARE(changeBegin, expectedBegin); |
3778 | QCOMPARE(changeAdded - changeRemoved, 1); |
3779 | } |
3780 | |
3781 | void tst_QTextDocument::() |
3782 | { |
3783 | QTest::addColumn<QString>(name: "html" ); |
3784 | QTest::addColumn<QStringList>(name: "expectedBlocks" ); |
3785 | |
3786 | QTest::newRow(dataTag: "commentless" ) << "<p>first</p><p>second</p><p>third</p>" |
3787 | << QStringList { "first" , "second" , "third" }; |
3788 | QTest::newRow(dataTag: "normal" ) << "<p>first</p><!--<p>second</p>--><p>third</p>" |
3789 | << QStringList { "first" , "third" }; |
3790 | QTest::newRow(dataTag: "nonClosing" ) << "<p>first</p><!--<p>second</p><p>third</p>" |
3791 | << QStringList { "first" }; |
3792 | QTest::newRow(dataTag: "immediatelyClosing" ) << "<p>first</p><!----><p>second</p><p>third</p>" |
3793 | << QStringList { "first" , "second" , "third" }; |
3794 | QTest::newRow(dataTag: "fake" ) << "<p>first</p><!-<p>second</p><p>third</p>" |
3795 | << QStringList { "first" , "second" , "third" }; |
3796 | QTest::newRow(dataTag: "endingNonExistant" ) << "<p>first</p>--><p>second</p><p>third</p>" |
3797 | << QStringList { "first" , "-->" , "second" , "third" }; |
3798 | } |
3799 | |
3800 | void tst_QTextDocument::() |
3801 | { |
3802 | QFETCH(QString, html); |
3803 | QFETCH(QStringList, expectedBlocks); |
3804 | |
3805 | QTextDocument doc; |
3806 | doc.setHtml(html); |
3807 | |
3808 | QCOMPARE(doc.blockCount(), expectedBlocks.count()); |
3809 | |
3810 | QStringList blockContent; |
3811 | auto currentBlock = doc.begin(); |
3812 | while (currentBlock != doc.end()) { |
3813 | blockContent.append(t: currentBlock.text()); |
3814 | currentBlock = currentBlock.next(); |
3815 | } |
3816 | |
3817 | QCOMPARE(blockContent, expectedBlocks); |
3818 | } |
3819 | |
3820 | QTEST_MAIN(tst_QTextDocument) |
3821 | #include "tst_qtextdocument.moc" |
3822 | |