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 <qtextdocumentfragment.h>
35#include <qtexttable.h>
36#include <qtextlist.h>
37#include <qregularexpression.h>
38#include <qdebug.h>
39#include <private/qtextdocument_p.h>
40
41
42#include <qtextcursor.h>
43
44QT_FORWARD_DECLARE_CLASS(QTextDocument)
45
46class tst_QTextDocumentFragment : public QObject
47{
48 Q_OBJECT
49
50public:
51 tst_QTextDocumentFragment();
52 ~tst_QTextDocumentFragment();
53
54public slots:
55 void init();
56 void cleanup();
57private slots:
58 void listCopying();
59 void listZeroCopying();
60 void listCopying2();
61 void tableCopying();
62 void tableCopyingWithColSpans();
63 void tableColSpanAndWidth();
64 void tableImport();
65 void tableImport2();
66 void tableImport3();
67 void tableImport4();
68 void tableImport5();
69 void textCopy();
70 void copyWholeDocument();
71 void title();
72 void html_listIndents1();
73 void html_listIndents2();
74 void html_listIndents3();
75 void html_listIndents4();
76 void html_listIndents5();
77 void html_listIndents6();
78 void html_listIndents7();
79 void blockCharFormat();
80 void blockCharFormatCopied();
81 void initialBlock();
82 void clone();
83 void dontRemoveInitialBlockIfItHoldsObjectIndexedCharFormat();
84 void dosLineFeed();
85 void unorderedListEnumeration();
86 void resetHasBlockAfterClosedBlockTags();
87 void ignoreStyleTags();
88 void hrefAnchor();
89 void namedAnchorFragments();
90 void namedAnchorFragments2();
91 void namedAnchorFragments3();
92 void dontInheritAlignmentInTables();
93 void cellBlockCount();
94 void cellBlockCount2();
95 void emptyTable();
96 void emptyTable2();
97 void emptyTable3();
98 void doubleRowClose();
99 void mayNotHaveChildren();
100 void inheritAlignment();
101 void dontEmitEmptyNodeWhenEmptyTagIsFollowedByCloseTag();
102 void toPlainText();
103 void copyTableRow();
104 void copyTableColumn();
105 void copySubTable();
106 void html_textDecoration();
107 void html_infiniteLoop();
108 void html_blockIndent();
109 void html_listIndent();
110 void html_whitespace();
111 void html_whitespace_data();
112 void html_qt3Whitespace();
113 void html_qt3WhitespaceWithFragments();
114 void html_qt3WhitespaceAfterTags();
115 void html_listStart1();
116 void html_listStart2();
117 void html_cssMargin();
118 void html_hexEntities();
119 void html_decEntities();
120 void html_thCentered();
121 void orderedListNumbering();
122 void html_blockAfterList();
123 void html_subAndSuperScript();
124 void html_cssColors();
125 void obeyFragmentMarkersInImport();
126 void whitespaceWithFragmentMarkers();
127 void html_emptyParapgraphs1();
128 void html_emptyParapgraphs2();
129 void html_emptyParagraphs3();
130 void html_emptyParagraphs4();
131 void html_font();
132 void html_fontSize();
133 void html_fontSizeAdjustment();
134 void html_cssFontSize();
135 void html_cssShorthandFont();
136 void html_bodyBgColor();
137 void html_qtBgColor();
138 void html_blockLevelDiv();
139 void html_spanNesting();
140 void html_nestedLists();
141 void noSpecialCharactersInPlainText();
142 void html_doNotInheritBackground();
143 void html_inheritBackgroundToInlineElements();
144 void html_doNotInheritBackgroundFromBlockElements();
145 void html_nobr();
146 void fromPlainText();
147 void fromPlainText2();
148 void html_closingImageTag();
149 void html_emptyDocument();
150 void html_closingTag();
151 void html_anchorAroundImage();
152 void html_floatBorder();
153 void html_frameImport();
154 void html_frameImport2();
155 void html_dontAddMarginsAcrossTableCells();
156 void html_dontMergeCenterBlocks();
157 void html_tableCellBgColor();
158 void html_tableCellBgColor2();
159 void html_cellSkip();
160 void nonZeroMarginOnImport();
161 void html_charFormatPropertiesUnset();
162 void html_headings();
163 void html_quotedFontFamily_data();
164 void html_quotedFontFamily();
165 void html_spanBackgroundColor();
166 void defaultFont();
167 void html_brokenTitle_data();
168 void html_brokenTitle();
169 void html_blockVsInline();
170 void html_tbody();
171 void html_nestedTables();
172 void html_rowSpans();
173 void html_rowSpans2();
174 void html_implicitParagraphs();
175 void html_missingCloseTag();
176 void html_anchorColor();
177 void html_lastParagraphClosing();
178 void html_tableHeaderBodyFootParent();
179 void html_columnWidths();
180 void html_bodyBackground();
181 void html_tableCellBackground();
182 void css_bodyBackground();
183 void css_tableCellBackground();
184 void css_tableCellBorder();
185 void css_tableCellBorderWidthOneValue();
186 void css_tableCellBorderWidthTwoValues();
187 void css_tableCellBorderShorthand();
188 void css_tableCellAllBordersShorthand();
189 void css_tableCellOverrideOneBorder();
190 void css_tableBorderCollapse();
191 void css_fontWeight();
192 void css_float();
193 void css_textIndent();
194 void css_inline();
195 void css_external();
196 void css_import();
197 void css_selectors_data();
198 void css_selectors();
199 void css_nodeNameCaseInsensitivity();
200 void css_textUnderlineStyle_data();
201 void css_textUnderlineStyle();
202 void css_textUnderlineStyleAndDecoration();
203 void css_listStyleType();
204 void css_linkPseudo();
205 void css_pageBreaks();
206 void css_cellPaddings();
207 void css_whiteSpace_data();
208 void css_whiteSpace();
209 void universalSelectors_data();
210 void universalSelectors();
211 void screenMedia();
212 void htmlResourceLoading();
213 void someCaseInsensitiveAttributeValues();
214 void backgroundImage();
215 void dontMergePreAndNonPre();
216 void leftMarginInsideHtml();
217 void html_margins();
218 void newlineInsidePreShouldBecomeNewParagraph();
219 void invalidColspan();
220 void html_brokenTableWithJustTr();
221 void html_brokenTableWithJustTd();
222 void html_preNewlineHandling_data();
223 void html_preNewlineHandling();
224 void html_br();
225 void html_dl();
226 void html_tableStrangeNewline();
227 void html_tableStrangeNewline2();
228 void html_tableStrangeNewline3();
229 void html_caption();
230 void html_windowsEntities();
231 void html_eatenText();
232 void html_hr();
233 void html_hrMargins();
234 void html_blockQuoteMargins();
235 void html_definitionListMargins();
236 void html_listMargins();
237 void html_titleAttribute();
238 void html_compressDivs();
239 void completeToPlainText();
240 void copyContents();
241 void html_textAfterHr();
242 void blockTagClosing();
243 void isEmpty();
244 void html_alignmentInheritance();
245 void html_ignoreEmptyDivs();
246 void html_dontInheritAlignmentForFloatingImages();
247 void html_verticalImageAlignment();
248 void html_verticalCellAlignment();
249 void html_borderColor();
250 void html_borderStyle();
251 void html_borderWidth();
252 void html_userState();
253 void html_rootFrameProperties();
254 void html_alignmentPropertySet();
255 void html_appendList();
256 void html_appendList2();
257 void html_qt3RichtextWhitespaceMode();
258 void html_brAfterHr();
259 void html_unclosedHead();
260 void html_entities();
261 void html_entities_data();
262 void html_ignore_script();
263 void html_directionWithHtml();
264 void html_directionWithRichText();
265 void html_metaInBody();
266 void html_importImageWithoutAspectRatio();
267 void html_fromFirefox();
268 void html_emptyInlineInsideBlock();
269 void css_fontAndWordSpacing();
270
271private:
272 inline void setHtml(const QString &html)
273 // don't take the shortcut in QTextDocument::setHtml
274 { doc->clear(); QTextCursor(doc).insertFragment(fragment: QTextDocumentFragment::fromHtml(html)); }
275
276 inline void appendHtml(const QString &html)
277 {
278 QTextCursor cursor(doc);
279 cursor.movePosition(op: QTextCursor::End);
280 cursor.insertHtml(html);
281 }
282
283 QTextDocument *doc;
284 QTextCursor cursor;
285};
286
287tst_QTextDocumentFragment::tst_QTextDocumentFragment()
288{
289 QImage img(16, 16, QImage::Format_ARGB32_Premultiplied);
290 img.save(fileName: "foo.png");
291}
292
293tst_QTextDocumentFragment::~tst_QTextDocumentFragment()
294{
295 QFile::remove(fileName: QLatin1String("foo.png"));
296}
297
298void tst_QTextDocumentFragment::init()
299{
300 doc = new QTextDocument;
301 cursor = QTextCursor(doc);
302}
303
304void tst_QTextDocumentFragment::cleanup()
305{
306 cursor = QTextCursor();
307 delete doc;
308 doc = 0;
309}
310
311void tst_QTextDocumentFragment::listCopying()
312{
313 cursor.insertList(style: QTextListFormat::ListDecimal);
314
315 QTextFormat originalBlockFormat = cursor.blockFormat();
316 QVERIFY(originalBlockFormat.objectIndex() != -1);
317 int originalListItemIdx = cursor.blockFormat().objectIndex();
318
319 cursor.insertText(text: "Hello World");
320
321 QTextDocumentFragment fragment(doc);
322
323 cursor.insertFragment(fragment);
324
325 QVERIFY(cursor.currentList());
326 QVERIFY(cursor.blockFormat() != originalBlockFormat);
327 QVERIFY(cursor.blockFormat().objectIndex() != originalListItemIdx);
328}
329
330void tst_QTextDocumentFragment::listZeroCopying()
331{
332 // same testcase as above but using the zero-copying
333
334 cursor.insertList(style: QTextListFormat::ListDecimal);
335
336 QTextFormat originalBlockFormat = cursor.blockFormat();
337 int originalListItemIdx = cursor.blockFormat().objectIndex();
338
339 cursor.insertText(text: "Hello World");
340
341 QTextDocumentFragment fragment(doc);
342 cursor.insertFragment(fragment);
343
344 QVERIFY(cursor.currentList());
345 QVERIFY(cursor.blockFormat() != originalBlockFormat);
346 QVERIFY(cursor.blockFormat().objectIndex() != originalListItemIdx);
347}
348
349void tst_QTextDocumentFragment::listCopying2()
350{
351 cursor.insertList(style: QTextListFormat::ListDecimal);
352 cursor.insertText(text: "Hello World");
353
354 cursor.insertList(style: QTextListFormat::ListDisc);
355 cursor.insertText(text: "Hello World");
356
357 QTextDocumentFragment fragment(doc);
358
359 cursor.insertFragment(fragment);
360
361 cursor.movePosition(op: QTextCursor::Start);
362 int listItemCount = 0;
363 do {
364 if (cursor.currentList())
365 listItemCount++;
366 } while (cursor.movePosition(op: QTextCursor::NextBlock));
367
368 QCOMPARE(listItemCount, 4);
369
370 // we call this here because it used to cause a failing assertion in the
371 // list manager.
372 doc->undo();
373}
374
375void tst_QTextDocumentFragment::tableCopying()
376{
377 // this tests both, the fragment to use the direction insertion instead of using the
378 // cursor, which might adjuts its position when inserting a table step by step, as well
379 // as the pasiveness of the tablemanager.
380 QTextDocumentFragment fragment;
381 {
382 QTextDocument doc;
383 QTextCursor cursor(&doc);
384
385 QTextTableFormat fmt;
386 QTextTable *table = cursor.insertTable(rows: 2, cols: 2, format: fmt);
387
388 table->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "First Cell");
389 table->cellAt(row: 0, col: 1).firstCursorPosition().insertText(text: "Second Cell");
390 table->cellAt(row: 1, col: 0).firstCursorPosition().insertText(text: "Third Cell");
391 table->cellAt(row: 1, col: 1).firstCursorPosition().insertText(text: "Fourth Cell");
392
393 fragment = QTextDocumentFragment(&doc);
394 }
395 {
396 QTextDocument doc;
397 QTextCursor cursor(&doc);
398
399 cursor.insertText(text: "FooBar");
400 cursor.insertBlock();
401 cursor.movePosition(op: QTextCursor::Left);
402
403 cursor.insertFragment(fragment);
404 cursor.movePosition(op: QTextCursor::Start);
405 cursor.movePosition(op: QTextCursor::NextBlock);
406
407 QTextTable *table = cursor.currentTable();
408 QVERIFY(table);
409 QCOMPARE(table->rows(), 2);
410 QCOMPARE(table->columns(), 2);
411 }
412}
413
414void tst_QTextDocumentFragment::tableCopyingWithColSpans()
415{
416 const char html[] = ""
417"<table border>"
418" <tr>"
419" <td>First Cell"
420" <td>Second Cell"
421" </tr>"
422" <tr>"
423" <td colspan=\"2\">Third Cell"
424" </tr>"
425" <tr>"
426" <td>Fourth Cell"
427" <td>Fifth Cell"
428" </tr>"
429"</table>";
430 setHtml(html);
431
432 cursor.movePosition(op: QTextCursor::Start);
433 cursor.movePosition(op: QTextCursor::NextBlock);
434 QTextTable *table = cursor.currentTable();
435 QVERIFY(table);
436 QVERIFY(table->columns() == 2 && table->rows() == 3);
437
438 cursor = table->cellAt(row: 2, col: 0).lastCursorPosition();
439 cursor.setPosition(pos: table->cellAt(row: 0, col: 0).firstPosition(), mode: QTextCursor::KeepAnchor);
440 QVERIFY(cursor.hasComplexSelection());
441
442 int firstRow = 0, numRows = 0, firstCol = 0, numCols = 0;
443 cursor.selectedTableCells(firstRow: &firstRow, numRows: &numRows, firstColumn: &firstCol, numColumns: &numCols);
444 QCOMPARE(firstRow, 0);
445 QCOMPARE(numRows, 3);
446 QCOMPARE(firstCol, 0);
447 QCOMPARE(numCols, 1);
448
449 QTextDocumentFragment frag = cursor.selection();
450 cleanup();
451 init();
452 cursor.insertFragment(fragment: frag);
453
454 cursor.movePosition(op: QTextCursor::Start);
455 cursor.movePosition(op: QTextCursor::NextBlock);
456 table = cursor.currentTable();
457 QVERIFY(table);
458 QVERIFY(table->columns() == 1 && table->rows() == 3);
459}
460
461void tst_QTextDocumentFragment::tableColSpanAndWidth()
462{
463 const char html[] = ""
464"<table border=\"0\">"
465" <tr>"
466" <td colspan=\"4\" width=\"400\">First Cell</td>"
467" </tr>"
468"</table>";
469 setHtml(html);
470
471 cursor.movePosition(op: QTextCursor::Start);
472 cursor.movePosition(op: QTextCursor::NextBlock);
473 QTextTable *table = cursor.currentTable();
474 QVERIFY(table);
475 QVERIFY(table->columns() == 4 && table->rows() == 1);
476 // make sure its approx 400 and not a multiple due to the colspan
477 QVERIFY(doc->size().width()> 398.);
478 QVERIFY(doc->size().width() < 420.);
479}
480
481void tst_QTextDocumentFragment::tableImport()
482{
483 // used to cause a failing assertion, as HTMLImporter::closeTag was
484 // called twice with the last node.
485 QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: "<table><tr><td>Hey</td><td>Blah"));
486 QVERIFY(!fragment.isEmpty());
487}
488
489void tst_QTextDocumentFragment::tableImport2()
490{
491 {
492 const char html[] = ""
493 "<table>"
494 "<tr><td>First Cell</td><td>Second Cell</td></tr>"
495 "<tr><td>Third Cell</td><td>Fourth Cell</td></tr>"
496 "</table>";
497
498 QTextDocument doc;
499 QTextCursor cursor(&doc);
500 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QByteArray::fromRawData(html, size: sizeof(html) / sizeof(html[0]))));
501
502 cursor.movePosition(op: QTextCursor::Start);
503 cursor.movePosition(op: QTextCursor::NextBlock);
504 QTextTable *table = cursor.currentTable();
505 QVERIFY(table);
506 QCOMPARE(table->columns(), 2);
507 QCOMPARE(table->rows(), 2);
508 }
509 {
510 const char html[] = ""
511 "<table>"
512 "<tr><td>First Cell</td><td>Second Cell</td></tr>"
513 "<tr><td>Third Cell</td><td>"
514 " <table>"
515 " <tr><td>First Nested Cell</td><td>Second Nested Cell</td></tr>"
516 " <tr><td>Third Nested Cell</td><td>Fourth Nested Cell</td></tr>"
517 " <tr><td>Fifth Nested Cell</td><td>Sixth Nested Cell</td></tr>"
518 " </table></td></tr>"
519 "</table>";
520
521 QTextDocument doc;
522 QTextCursor cursor(&doc);
523 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QByteArray::fromRawData(html, size: sizeof(html) / sizeof(html[0]))));
524
525 cursor.movePosition(op: QTextCursor::Start);
526 cursor.movePosition(op: QTextCursor::NextBlock);
527 QTextTable *table = cursor.currentTable();
528 QVERIFY(table);
529 QCOMPARE(table->columns(), 2);
530 QCOMPARE(table->rows(), 2);
531
532 /*
533 QTextCursor fourthCell = table->cellAt(1, 1).firstCursorPosition();
534 fourthCell.movePosition(QTextCursor::NextBlock);
535 table = fourthCell.currentTable();
536 QVERIFY(table);
537 QVERIFY(table != cursor.currentTable());
538 QCOMPARE(table->columns(), 2);
539 QCOMPARE(table->rows(), 3);
540 */
541 }
542 {
543 const char buggyHtml[] = ""
544 "<table>"
545 "<tr><td>First Cell<td>Second Cell"
546 "<tr><td>Third Cell<td>Fourth Cell"
547 "</table>";
548
549 QTextDocument doc;
550 QTextCursor cursor(&doc);
551 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QByteArray::fromRawData(buggyHtml, size: sizeof(buggyHtml) / sizeof(buggyHtml[0]))));
552
553 cursor.movePosition(op: QTextCursor::Start);
554 cursor.movePosition(op: QTextCursor::NextBlock);
555 QTextTable *table = cursor.currentTable();
556 QVERIFY(table);
557 QCOMPARE(table->columns(), 2);
558 QCOMPARE(table->rows(), 2);
559 }
560 {
561 const char buggyHtml[] = ""
562 "<table>"
563 "<tr><th>First Cell<th>Second Cell"
564 "<tr><td>Third Cell<td>Fourth Cell"
565 "</table>";
566
567 QTextDocument doc;
568 QTextCursor cursor(&doc);
569 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QByteArray::fromRawData(buggyHtml, size: sizeof(buggyHtml) / sizeof(buggyHtml[0]))));
570
571 cursor.movePosition(op: QTextCursor::Start);
572 cursor.movePosition(op: QTextCursor::NextBlock);
573 QTextTable *table = cursor.currentTable();
574 QVERIFY(table);
575 QCOMPARE(table->columns(), 2);
576 QCOMPARE(table->rows(), 2);
577 }
578
579}
580
581void tst_QTextDocumentFragment::tableImport3()
582{
583 // ### would be better to have tree tests for QTextHtmlParser
584 // make sure the p is a child of the td. If not the following td
585 // ends up outside the table, causing an assertion
586 const char html[] = "<table><tr><td><p></p></td><td></td></tr></table>";
587 QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html));
588 QVERIFY(!fragment.isEmpty());
589}
590
591void tst_QTextDocumentFragment::tableImport4()
592{
593 const char html[] = "<table>"
594 "<tr><td>blah</td></tr>"
595 "<tr><td>blah</td><td>blah</td></tr>"
596 "</table>";
597 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
598 cursor.movePosition(op: QTextCursor::Start);
599 cursor.movePosition(op: QTextCursor::NextBlock);
600 QVERIFY(cursor.currentTable());
601 QCOMPARE(cursor.currentTable()->columns(), 2);
602}
603
604void tst_QTextDocumentFragment::tableImport5()
605{
606 const char html[] = "<table>"
607 "<tr>"
608 " <td>Foo</td>"
609 " <td>Bar</td>"
610 " <td>Bleh</td>"
611 " <td>Blub</td>"
612 "</tr>"
613 "<tr>"
614 " <td>Ahh</td>"
615 " <td colspan=5>Gah</td>"
616 "</tr>"
617 "</table>";
618
619 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
620 cursor.movePosition(op: QTextCursor::Start);
621 cursor.movePosition(op: QTextCursor::NextBlock);
622 QVERIFY(cursor.currentTable());
623 QCOMPARE(cursor.currentTable()->rows(), 2);
624 QCOMPARE(cursor.currentTable()->columns(), 6);
625}
626
627void tst_QTextDocumentFragment::textCopy()
628{
629 /* this test used to cause failing assertions in QTextDocumentFragment */
630 /* copy&paste 'lo\bwor' */
631 cursor.insertText(text: "Hello");
632 cursor.insertBlock();
633 cursor.insertText(text: "World");
634
635 cursor.movePosition(op: QTextCursor::Start);
636 cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::MoveAnchor, n: 3);
637 cursor.movePosition(op: QTextCursor::NextBlock, QTextCursor::KeepAnchor);
638 cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::KeepAnchor, n: 3);
639
640 QTextDocumentFragment fragment(cursor);
641 QVERIFY(!fragment.isEmpty());
642 cursor.insertFragment(fragment);
643}
644
645void tst_QTextDocumentFragment::copyWholeDocument()
646{
647 // used to cause the famous currentBlock.position() == pos + 1 failing assertion
648 cursor.insertText(text: "\nHey\nBlah\n");
649 cursor.movePosition(op: QTextCursor::Start);
650 cursor.movePosition(op: QTextCursor::End, QTextCursor::KeepAnchor);
651
652 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
653 fmt.setBackground(Qt::blue);
654 doc->rootFrame()->setFrameFormat(fmt);
655
656 QTextDocumentFragment fragment(cursor);
657 QVERIFY(true); // good if we reach this point :)
658
659 cleanup();
660 init();
661
662 fmt.setBackground(Qt::red);
663 doc->rootFrame()->setFrameFormat(fmt);
664
665 cursor.insertFragment(fragment);
666
667 QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::red);
668}
669
670void tst_QTextDocumentFragment::title()
671{
672 doc->setHtml(QString::fromLatin1(str: "<html><head><title>Test</title></head><body>Blah</body></html>"));
673 QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), QString::fromLatin1("Test"));
674}
675
676void tst_QTextDocumentFragment::html_listIndents1()
677{
678 const char html[] = "<ul><li>Hey</li><li>Hah</li></ul>";
679 setHtml(QString::fromLatin1(str: html));
680 cursor.movePosition(op: QTextCursor::Start);
681 cursor.movePosition(op: QTextCursor::NextBlock);
682 QTextList *list = cursor.currentList();
683 QVERIFY(list);
684 QCOMPARE(list->format().indent(), 1);
685 QCOMPARE(cursor.block().blockFormat().indent(), 0);
686}
687
688void tst_QTextDocumentFragment::html_listIndents2()
689{
690 const char html[] = "<ul><li>Hey<p>Hah</ul>";
691 setHtml(QString::fromLatin1(str: html));
692 cursor.movePosition(op: QTextCursor::Start);
693 QTextList *list = cursor.currentList();
694 QVERIFY(list);
695 QCOMPARE(list->format().indent(), 1);
696 QCOMPARE(cursor.block().blockFormat().indent(), 0);
697
698 cursor.movePosition(op: QTextCursor::NextBlock);
699 QCOMPARE(cursor.block().blockFormat().indent(), 1);
700}
701
702void tst_QTextDocumentFragment::html_listIndents3()
703{
704 const char html[] = "<ul><li><p>Hah</ul>";
705 setHtml(QString::fromLatin1(str: html));
706 cursor.movePosition(op: QTextCursor::Start);
707 QTextList *list = cursor.currentList();
708 QVERIFY(list);
709 QCOMPARE(list->format().indent(), 1);
710 QCOMPARE(cursor.block().blockFormat().indent(), 0);
711}
712
713void tst_QTextDocumentFragment::html_listIndents4()
714{
715 const char html[] = "<ul><li>Foo</ul><p>This should not have the same indent as Foo";
716 setHtml(QString::fromLatin1(str: html));
717 cursor.movePosition(op: QTextCursor::Start);
718 QTextList *list = cursor.currentList();
719 QVERIFY(list);
720 QCOMPARE(list->format().indent(), 1);
721
722 cursor.movePosition(op: QTextCursor::NextBlock);
723 QVERIFY(!cursor.currentList());
724 QCOMPARE(cursor.blockFormat().indent(), 0);
725}
726
727void tst_QTextDocumentFragment::html_listIndents5()
728{
729 const char html[] = "<ul><li>Foo<p><li>Bar</li></ul>";
730 setHtml(QString::fromLatin1(str: html));
731 cursor.movePosition(op: QTextCursor::Start);
732 QTextList *list = cursor.currentList();
733 QVERIFY(list);
734 QCOMPARE(list->format().indent(), 1);
735
736 cursor.movePosition(op: QTextCursor::NextBlock);
737 QCOMPARE(cursor.currentList(), list);
738 QCOMPARE(cursor.blockFormat().indent(), 0);
739}
740
741void tst_QTextDocumentFragment::html_listIndents6()
742{
743 const char html[] = "<ul><li>Outer List<div class=\"testclass\"><ul><li>Nested Item 1</li></ul></div></li></ul>";
744 setHtml(QString::fromLatin1(str: html));
745 cursor.movePosition(op: QTextCursor::Start);
746 QTextList *list = cursor.currentList();
747 QVERIFY(list);
748 QCOMPARE(list->format().indent(), 1);
749
750 cursor.movePosition(op: QTextCursor::NextBlock);
751 QVERIFY(cursor.currentList() != list);
752 list = cursor.currentList();
753 QVERIFY(list);
754 QCOMPARE(list->format().indent(), 2);
755
756 QCOMPARE(cursor.blockFormat().indent(), 0);
757}
758
759void tst_QTextDocumentFragment::html_listIndents7()
760{
761 const char html[] = "<ul><li style=\"-qt-block-indent:1;\">Hey</ul>";
762 setHtml(QString::fromLatin1(str: html));
763 cursor.movePosition(op: QTextCursor::Start);
764 cursor.movePosition(op: QTextCursor::NextBlock);
765 QTextList *list = cursor.currentList();
766 QVERIFY(list);
767 QCOMPARE(list->format().indent(), 1);
768 QCOMPARE(cursor.block().blockFormat().indent(), 1);
769}
770
771void tst_QTextDocumentFragment::blockCharFormat()
772{
773 const char html[] = "<p style=\"font-style:italic\"><span style=\"font-style:normal\">Test</span></p>";
774 setHtml(QString::fromLatin1(str: html));
775 QVERIFY(doc->begin().charFormat().fontItalic());
776}
777
778void tst_QTextDocumentFragment::blockCharFormatCopied()
779{
780 QTextCharFormat fmt;
781 fmt.setForeground(Qt::green);
782 cursor.setBlockCharFormat(fmt);
783 cursor.insertText(text: "Test", format: QTextCharFormat());
784 QTextDocumentFragment frag(doc);
785 cleanup();
786 init();
787 cursor.insertFragment(fragment: frag);
788 QCOMPARE(cursor.blockCharFormat(), fmt);
789}
790
791void tst_QTextDocumentFragment::initialBlock()
792{
793 const char html[] = "<p>Test</p>";
794 setHtml(QString::fromLatin1(str: html));
795 QCOMPARE(doc->blockCount(), 1);
796}
797
798void tst_QTextDocumentFragment::clone()
799{
800 QTextBlockFormat mod;
801 mod.setAlignment(Qt::AlignCenter);
802 cursor.mergeBlockFormat(modifier: mod);
803 cursor.insertText(text: "Blah");
804 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignCenter);
805 QTextDocumentFragment frag(doc);
806 cleanup();
807 init();
808 cursor.insertFragment(fragment: frag);
809 cursor.movePosition(op: QTextCursor::Start);
810 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignCenter);
811}
812
813void tst_QTextDocumentFragment::dontRemoveInitialBlockIfItHoldsObjectIndexedCharFormat()
814{
815 const char html[] = "<table><tr><td>cell one<td>cell two</tr><tr><td>cell three<td>cell four</tr></table>";
816 QCOMPARE(doc->begin().charFormat().objectIndex(), -1);
817 setHtml(QString::fromLatin1(str: html));
818 int cnt = 0;
819
820 int objectIndexOfLast = -1;
821 for (QTextBlock blk = doc->begin(); blk.isValid(); blk = blk.next()) {
822 ++cnt;
823 objectIndexOfLast = blk.charFormat().objectIndex();
824 }
825 // beginning of frame for first cell
826 // + beginning of frame for second cell
827 // + beginning of frame for third cell
828 // + beginning of frame for fourth cell
829 // + end of frame
830 // + initial block
831 // ==> 6
832 QCOMPARE(cnt, 6);
833 QVERIFY(objectIndexOfLast != -1);
834 QVERIFY(doc->begin().next().charFormat().objectIndex() != -1);
835}
836
837void tst_QTextDocumentFragment::dosLineFeed()
838{
839 const char html[] = "<pre>Test\r\n</pre>Bar";
840 setHtml(QString::fromLatin1(str: html));
841 QVERIFY(!doc->toPlainText().contains('\r'));
842 QCOMPARE(doc->toPlainText(), QString("Test\nBar"));
843}
844
845void tst_QTextDocumentFragment::unorderedListEnumeration()
846{
847 const char html[] = "<ul><ul><ul><li>Blah</li></ul></ul>";
848 setHtml(QString::fromLatin1(str: html));
849 cursor.movePosition(op: QTextCursor::End);
850 QVERIFY(cursor.currentList());
851 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListDisc);
852
853 const char html2[] = "<ul><ul><ul type=circle><li>Blah</li></ul></ul>";
854 setHtml(QString::fromLatin1(str: html2));
855 cursor.movePosition(op: QTextCursor::End);
856 QVERIFY(cursor.currentList());
857 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListCircle);
858
859}
860
861void tst_QTextDocumentFragment::resetHasBlockAfterClosedBlockTags()
862{
863 // when closing tags we have to make sure hasBlock in import() gets reset
864 const char html[] = "<body><table><tr><td><td><p></table><p></body>";
865 setHtml(QString::fromLatin1(str: html));
866 QVERIFY(!doc->isEmpty());
867}
868
869void tst_QTextDocumentFragment::ignoreStyleTags()
870{
871 const char html[] = "<body><style>Blah</style>Hello</body>";
872 setHtml(QString::fromLatin1(str: html));
873 QCOMPARE(doc->toPlainText(), QString("Hello"));
874}
875
876void tst_QTextDocumentFragment::hrefAnchor()
877{
878 {
879 const char html[] = "<a href=\"test\">blah</a>";
880 setHtml(QString::fromLatin1(str: html));
881 QVERIFY(doc->begin().begin().fragment().charFormat().isAnchor());
882 QCOMPARE(doc->begin().begin().fragment().charFormat().anchorHref(), QString::fromLatin1("test"));
883 QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline());
884 }
885
886 {
887 // only hyperlinks should have special formatting
888 const char html[] = "<a>blah</a>";
889 setHtml(QString::fromLatin1(str: html));
890 QVERIFY(doc->begin().begin().fragment().charFormat().isAnchor());
891 QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline());
892 }
893}
894
895void tst_QTextDocumentFragment::namedAnchorFragments()
896{
897 // named anchors should be 'invisible', but the fragment right after it should
898 // hold the attribute
899 const char html[] = "a<a name=\"test\" />blah";
900 setHtml(QString::fromLatin1(str: html));
901
902 QTextBlock firstBlock = doc->begin();
903 QVERIFY(firstBlock.isValid());
904
905 QTextBlock::Iterator it = firstBlock.begin();
906 QVERIFY(!it.atEnd());
907
908 // the 'a'
909 QVERIFY(it.fragment().isValid());
910 QCOMPARE(it.fragment().text(), QString::fromLatin1("a"));
911 QVERIFY(!it.fragment().charFormat().isAnchor());
912
913 // the 'b' of 'blah' as separate fragment with the anchor attribute
914 ++it;
915 QVERIFY(it.fragment().isValid());
916 QCOMPARE(it.fragment().text(), QString::fromLatin1("b"));
917 QVERIFY(it.fragment().charFormat().isAnchor());
918
919 // the 'lah' of 'blah' as remainder
920 ++it;
921 QVERIFY(it.fragment().isValid());
922 QVERIFY(it.fragment().text().startsWith("lah"));
923 QVERIFY(!it.fragment().charFormat().isAnchor());
924}
925
926void tst_QTextDocumentFragment::namedAnchorFragments2()
927{
928 const char html[] = "<p> <a name=\"foo\"> Hello";
929 setHtml(QString::fromLatin1(str: html));
930
931 QCOMPARE(doc->toPlainText(), QString("Hello"));
932
933 QTextBlock::Iterator it = doc->begin().begin();
934 QVERIFY(!it.atEnd());
935
936 QCOMPARE(it.fragment().text(), QString::fromLatin1("H"));
937 QVERIFY(it.fragment().charFormat().isAnchor());
938
939 ++it;
940
941 QCOMPARE(it.fragment().text(), QString::fromLatin1("ello"));
942 QVERIFY(!it.fragment().charFormat().isAnchor());
943}
944
945void tst_QTextDocumentFragment::namedAnchorFragments3()
946{
947 setHtml("<a name=\"target\" /><a name=\"target2\"/><span>Text</span>");
948
949 QCOMPARE(doc->toPlainText(), QString("Text"));
950
951 QTextBlock::Iterator it = doc->begin().begin();
952 QVERIFY(!it.atEnd());
953
954 QCOMPARE(it.fragment().text(), QString::fromLatin1("T"));
955 QVERIFY(it.fragment().charFormat().isAnchor());
956 QCOMPARE(it.fragment().charFormat().anchorNames().constFirst(), QLatin1String("target"));
957 QStringList targets; targets << "target" << "target2";
958 QCOMPARE(it.fragment().charFormat().anchorNames(), targets);
959
960 ++it;
961
962 QCOMPARE(it.fragment().text(), QString::fromLatin1("ext"));
963 QVERIFY(!it.fragment().charFormat().isAnchor());
964}
965
966void tst_QTextDocumentFragment::dontInheritAlignmentInTables()
967{
968 const char html[] = "<table align=center><tr><td>Hey</td></tr></table>";
969 setHtml(QString::fromLatin1(str: html));
970
971 cursor.movePosition(op: QTextCursor::Start);
972 cursor.movePosition(op: QTextCursor::NextBlock);
973 QVERIFY(cursor.currentTable());
974 QVERIFY(cursor.currentTable()->cellAt(0, 0).isValid());
975 QVERIFY(cursor.currentTable()->cellAt(0, 0).firstCursorPosition().block().next().blockFormat().alignment() != Qt::AlignHCenter);
976}
977
978void tst_QTextDocumentFragment::cellBlockCount()
979{
980 const char html[] = "<table><tr><td>Hey</td></tr></table>";
981 setHtml(QString::fromLatin1(str: html));
982
983 cursor.movePosition(op: QTextCursor::Start);
984 cursor.movePosition(op: QTextCursor::NextBlock);
985 QVERIFY(cursor.currentTable());
986
987 QTextTableCell cell = cursor.currentTable()->cellAt(row: 0, col: 0);
988 QVERIFY(cell.isValid());
989
990 int blockCount = 0;
991 for (QTextFrame::iterator it = cell.begin(); !it.atEnd(); ++it) {
992 QVERIFY(!it.currentFrame());
993 QVERIFY(it.currentBlock().isValid());
994 ++blockCount;
995 }
996 QCOMPARE(blockCount, 1);
997}
998
999void tst_QTextDocumentFragment::cellBlockCount2()
1000{
1001 const char html[] = "<table><tr><td><p>Hey</p></td></tr></table>";
1002 setHtml(QString::fromLatin1(str: html));
1003
1004 cursor.movePosition(op: QTextCursor::Start);
1005 cursor.movePosition(op: QTextCursor::NextBlock);
1006 QVERIFY(cursor.currentTable());
1007
1008 QTextTableCell cell = cursor.currentTable()->cellAt(row: 0, col: 0);
1009 QVERIFY(cell.isValid());
1010
1011 int blockCount = 0;
1012 for (QTextFrame::iterator it = cell.begin(); !it.atEnd(); ++it) {
1013 QVERIFY(!it.currentFrame());
1014 QVERIFY(it.currentBlock().isValid());
1015 ++blockCount;
1016 }
1017 QCOMPARE(blockCount, 1);
1018}
1019
1020void tst_QTextDocumentFragment::emptyTable()
1021{
1022 const char html[] = "<table></table>";
1023 setHtml(QString::fromLatin1(str: html));
1024 QVERIFY(true); // don't crash with a failing assertion
1025}
1026
1027void tst_QTextDocumentFragment::emptyTable2()
1028{
1029 const char html[] = "<table></td></tr></table><p>blah</p>";
1030 setHtml(QString::fromLatin1(str: html));
1031 QVERIFY(true); // don't crash with a failing assertion
1032}
1033
1034void tst_QTextDocumentFragment::emptyTable3()
1035{
1036 const char html[] = "<table><tr><td><table></table></td><td>Foobar</td></tr></table>";
1037 setHtml(QString::fromLatin1(str: html));
1038
1039 cursor.movePosition(op: QTextCursor::Start);
1040 cursor.movePosition(op: QTextCursor::NextBlock);
1041 QTextTable *table = cursor.currentTable();
1042 QVERIFY(table);
1043 QCOMPARE(table->rows(), 1);
1044 QCOMPARE(table->columns(), 2);
1045 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1046 QVERIFY(cell.isValid());
1047 QCOMPARE(cell.firstPosition(), cell.lastPosition());
1048 cell = table->cellAt(row: 0, col: 1);
1049 QTextCursor cursor = cell.firstCursorPosition();
1050 cursor.setPosition(pos: cell.lastPosition(), mode: QTextCursor::KeepAnchor);
1051 QCOMPARE(cursor.selectedText(), QString("Foobar"));
1052}
1053
1054void tst_QTextDocumentFragment::doubleRowClose()
1055{
1056 const char html[] = "<table><tr><td>Blah</td></tr></tr><tr><td>Hm</td></tr></table>";
1057 setHtml(QString::fromLatin1(str: html));
1058 QVERIFY(true); // don't crash with a failing assertion
1059}
1060
1061void tst_QTextDocumentFragment::mayNotHaveChildren()
1062{
1063 // make sure the Hey does not end up as tag text for the img tag
1064 const char html[] = "<img />Hey";
1065 setHtml(QString::fromLatin1(str: html));
1066 QCOMPARE(doc->toPlainText().mid(1), QString::fromLatin1("Hey"));
1067}
1068
1069void tst_QTextDocumentFragment::inheritAlignment()
1070{
1071 // make sure attributes from the body tag get inherited
1072 const char html[] = "<body align=right><p>Hey";
1073 setHtml(QString::fromLatin1(str: html));
1074 // html alignment is absolute
1075 QCOMPARE(doc->begin().blockFormat().alignment(), Qt::Alignment(Qt::AlignRight|Qt::AlignAbsolute));
1076}
1077
1078void tst_QTextDocumentFragment::dontEmitEmptyNodeWhenEmptyTagIsFollowedByCloseTag()
1079{
1080 // make sure the Hey does not end up as tag text for the img tag
1081 const char html[] = "<body align=right><p align=left>Blah<img></img><p>Hey";
1082 setHtml(QString::fromLatin1(str: html));
1083 QCOMPARE(doc->begin().blockFormat().alignment(), Qt::Alignment(Qt::AlignLeft|Qt::AlignAbsolute));
1084 QCOMPARE(doc->begin().next().blockFormat().alignment(), Qt::Alignment(Qt::AlignRight|Qt::AlignAbsolute));
1085}
1086
1087void tst_QTextDocumentFragment::toPlainText()
1088{
1089 QString input = "Hello\nWorld";
1090 input += QChar::ParagraphSeparator;
1091 input += "Blah";
1092 doc->setPlainText(input);
1093 QCOMPARE(doc->blockCount(), 3);
1094}
1095
1096void tst_QTextDocumentFragment::copyTableRow()
1097{
1098 QTextDocumentFragment frag;
1099 {
1100 QTextTable *table = cursor.insertTable(rows: 2, cols: 2);
1101 table->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "Blah");
1102 table->cellAt(row: 0, col: 1).firstCursorPosition().insertText(text: "Foo");
1103 table->cellAt(row: 1, col: 0).firstCursorPosition().insertText(text: "Bar");
1104 table->cellAt(row: 1, col: 1).firstCursorPosition().insertText(text: "Hah");
1105
1106 // select second row
1107 cursor = table->cellAt(row: 1, col: 1).firstCursorPosition();
1108 cursor.movePosition(op: QTextCursor::PreviousBlock, QTextCursor::KeepAnchor);
1109
1110 QCOMPARE(table->cellAt(cursor.position()).row(), 1);
1111 QCOMPARE(table->cellAt(cursor.position()).column(), 0);
1112 QCOMPARE(table->cellAt(cursor.anchor()).row(), 1);
1113 QCOMPARE(table->cellAt(cursor.anchor()).column(), 1);
1114
1115 frag = QTextDocumentFragment(cursor);
1116 }
1117 {
1118 QTextDocument doc2;
1119 cursor = QTextCursor(&doc2);
1120 cursor.insertFragment(fragment: frag);
1121
1122 cursor.movePosition(op: QTextCursor::Start);
1123 cursor.movePosition(op: QTextCursor::NextBlock);
1124 QTextTable *table = cursor.currentTable();
1125
1126 QVERIFY(table);
1127 QCOMPARE(table->columns(), 2);
1128 QCOMPARE(table->rows(), 1);
1129
1130 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Bar"));
1131 QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Hah"));
1132 }
1133}
1134
1135void tst_QTextDocumentFragment::copyTableColumn()
1136{
1137 QTextDocumentFragment frag;
1138 {
1139 QTextTable *table = cursor.insertTable(rows: 2, cols: 2);
1140 table->cellAt(row: 0, col: 0).firstCursorPosition().insertText(text: "Blah");
1141 table->cellAt(row: 0, col: 1).firstCursorPosition().insertText(text: "Foo");
1142 table->cellAt(row: 1, col: 0).firstCursorPosition().insertText(text: "Bar");
1143 table->cellAt(row: 1, col: 1).firstCursorPosition().insertText(text: "Hah");
1144
1145 // select second column
1146 cursor = table->cellAt(row: 0, col: 1).firstCursorPosition();
1147 cursor.movePosition(op: QTextCursor::Down, QTextCursor::KeepAnchor);
1148
1149 QCOMPARE(table->cellAt(cursor.anchor()).row(), 0);
1150 QCOMPARE(table->cellAt(cursor.anchor()).column(), 1);
1151 QCOMPARE(table->cellAt(cursor.position()).row(), 1);
1152 QCOMPARE(table->cellAt(cursor.position()).column(), 1);
1153
1154 frag = QTextDocumentFragment(cursor);
1155 }
1156 {
1157 QTextDocument doc2;
1158 cursor = QTextCursor(&doc2);
1159 cursor.insertFragment(fragment: frag);
1160
1161 cursor.movePosition(op: QTextCursor::Start);
1162 cursor.movePosition(op: QTextCursor::NextBlock);
1163 QTextTable *table = cursor.currentTable();
1164
1165 QVERIFY(table);
1166 QCOMPARE(table->columns(), 1);
1167 QCOMPARE(table->rows(), 2);
1168
1169 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Foo"));
1170 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Hah"));
1171 }
1172}
1173
1174void tst_QTextDocumentFragment::copySubTable()
1175{
1176 QTextDocumentFragment frag;
1177 {
1178 QTextTableFormat fmt;
1179 QVector<QTextLength> constraints;
1180 constraints << QTextLength(QTextLength::PercentageLength, 16);
1181 constraints << QTextLength(QTextLength::PercentageLength, 28);
1182 constraints << QTextLength(QTextLength::PercentageLength, 28);
1183 constraints << QTextLength(QTextLength::PercentageLength, 28);
1184 fmt.setColumnWidthConstraints(constraints);
1185
1186 QTextTable *table = cursor.insertTable(rows: 4, cols: 4, format: fmt);
1187 for (int row = 0; row < 4; ++row) {
1188 const QString rowS = QString::number(row) + QLatin1Char('/');
1189 for (int col = 0; col < 4; ++col)
1190 table->cellAt(row, col).firstCursorPosition().insertText(text: rowS + QString::number(col));
1191 }
1192
1193 QCOMPARE(table->format().columnWidthConstraints().count(), table->columns());
1194
1195 // select 2x2 subtable
1196 cursor = table->cellAt(row: 1, col: 1).firstCursorPosition();
1197 cursor.movePosition(op: QTextCursor::Down, QTextCursor::KeepAnchor);
1198 cursor.movePosition(op: QTextCursor::Right, QTextCursor::KeepAnchor);
1199
1200 QCOMPARE(table->cellAt(cursor.anchor()).row(), 1);
1201 QCOMPARE(table->cellAt(cursor.anchor()).column(), 1);
1202 QCOMPARE(table->cellAt(cursor.position()).row(), 2);
1203 QCOMPARE(table->cellAt(cursor.position()).column(), 2);
1204
1205 frag = QTextDocumentFragment(cursor);
1206 }
1207 {
1208 QTextDocument doc2;
1209 cursor = QTextCursor(&doc2);
1210 cursor.insertFragment(fragment: frag);
1211
1212 cursor.movePosition(op: QTextCursor::Start);
1213 cursor.movePosition(op: QTextCursor::NextBlock);
1214 QTextTable *table = cursor.currentTable();
1215
1216 QVERIFY(table);
1217 QVERIFY(table->format().columnWidthConstraints().isEmpty());
1218 QCOMPARE(table->columns(), 2);
1219 QCOMPARE(table->rows(), 2);
1220
1221 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("1/1"));
1222 QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("1/2"));
1223 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("2/1"));
1224 QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("2/2"));
1225 }
1226}
1227
1228void tst_QTextDocumentFragment::html_textDecoration()
1229{
1230 const char html[] = "<span style='text-decoration: overline line-through underline'>Blah</span>";
1231 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QByteArray::fromRawData(html, size: sizeof(html) / sizeof(html[0]))));
1232
1233 cursor.movePosition(op: QTextCursor::Start);
1234 cursor.movePosition(op: QTextCursor::NextCharacter);
1235 QVERIFY(cursor.charFormat().fontUnderline());
1236 QVERIFY(cursor.charFormat().fontOverline());
1237 QVERIFY(cursor.charFormat().fontStrikeOut());
1238}
1239
1240void tst_QTextDocumentFragment::html_infiniteLoop()
1241{
1242 {
1243 // used to cause an infinite loop due to the lack of a space after the
1244 // tag name
1245 const char html[] = "<ahref=\"argl\">Link</a>";
1246 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1247 QVERIFY(true);
1248 }
1249
1250 {
1251 const char html[] = "<a href=\"\"a<";
1252 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1253 QVERIFY(true);
1254 }
1255}
1256
1257void tst_QTextDocumentFragment::html_blockIndent()
1258{
1259 const char html[] = "<p style=\"-qt-block-indent:3;\">Test</p>";
1260 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1261 QCOMPARE(cursor.blockFormat().indent(), 3);
1262}
1263
1264void tst_QTextDocumentFragment::html_listIndent()
1265{
1266 const char html[] = "<ul style=\"-qt-list-indent:4;\"><li>Blah</ul>";
1267 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1268 QVERIFY(cursor.currentList());
1269 QCOMPARE(cursor.currentList()->format().indent(), 4);
1270}
1271
1272void tst_QTextDocumentFragment::html_whitespace_data()
1273{
1274 QTest::addColumn<QString>(name: "html");
1275 QTest::addColumn<QString>(name: "expectedPlainText");
1276
1277 QTest::newRow(dataTag: "1") << QString("<span>This is some test</span><span> with spaces between words</span>")
1278 << QString("This is some test with spaces between words");
1279
1280 QTest::newRow(dataTag: "2") << QString("<span> </span><span>nowhitespacehereplease</span>")
1281 << QString::fromLatin1(str: "nowhitespacehereplease");
1282
1283 QTest::newRow(dataTag: "3") << QString("<span style=\"white-space: pre;\"> white space \n\n here </span>")
1284 << QString::fromLatin1(str: " white space \n\n here ");
1285
1286 QTest::newRow(dataTag: "4") << QString("<span style=\"white-space: pre-wrap;\"> white space \n\n here </span>")
1287 << QString::fromLatin1(str: " white space \n\n here ");
1288
1289 QTest::newRow(dataTag: "5") << QString("<a href=\"One.html\">One</a> <a href=\"Two.html\">Two</a> <b>Three</b>\n"
1290 "<b>Four</b>")
1291 << QString::fromLatin1(str: "One Two Three Four");
1292
1293 QTest::newRow(dataTag: "6") << QString("<p>Testing: <b><i><u>BoldItalic</u></i></b> <i>Italic</i></p>")
1294 << QString("Testing: BoldItalic Italic");
1295
1296 QTest::newRow(dataTag: "7") << QString("<table><tr><td>Blah</td></tr></table> <table border><tr><td>Foo</td></tr></table>")
1297 << QString("\nBlah\n\nFoo\n");
1298
1299 QTest::newRow(dataTag: "8") << QString("<table><tr><td><i>Blah</i></td></tr></table> <i>Blub</i>")
1300 << QString("\nBlah\nBlub");
1301
1302 QTest::newRow(dataTag: "9") << QString("<span style=\"white-space: nowrap;\"> white space \n\n here </span>")
1303 << QString::fromLatin1(str: "white space here ");
1304
1305 QTest::newRow(dataTag: "10") << QString("<span style=\"white-space: pre-line;\"> white space \n\n here </span>")
1306 << QString::fromLatin1(str: "white space\n\nhere ");
1307
1308 QTest::newRow(dataTag: "task116492") << QString("<p>a<font=\"Times\"> b </font>c</p>")
1309 << QString("a b c");
1310
1311 QTest::newRow(dataTag: "task121653") << QString("abc<b> def</b>")
1312 << QString("abc def");
1313
1314 QTest::newRow(dataTag: "task122650") << QString("<p>Foo</p> Bar")
1315 << QString("Foo\nBar");
1316
1317 QTest::newRow(dataTag: "task122650-2") << QString("<p>Foo</p> <p> Bar")
1318 << QString("Foo \nBar");
1319
1320 QTest::newRow(dataTag: "task122650-3") << QString("<html>Before<pre>\nTest</pre>")
1321 << QString("Before\nTest");
1322
1323 QTest::newRow(dataTag: "br-with-whitespace") << QString("Foo<br>\nBlah")
1324 << QString("Foo\nBlah");
1325
1326 QTest::newRow(dataTag: "collapse-p-with-newline") << QString("Foo<p>\n<p>\n<p>\n<p>\n<p>\n<p>\nBar")
1327 << QString("Foo\nBar");
1328
1329 QTest::newRow(dataTag: "table") << QString("<table><tr><td>Blah</td></tr></table>\nTest")
1330 << QString("\nBlah\nTest");
1331
1332 QTest::newRow(dataTag: "table2") << QString("<table><tr><td><pre>\nTest\n</pre></td>\n </tr></table>")
1333 << QString("\nTest\n");
1334
1335 QTest::newRow(dataTag: "table3") << QString("<table><tr><td><pre>\nTest\n</pre> \n \n </td></tr></table>")
1336 << QString("\nTest \n");
1337}
1338
1339void tst_QTextDocumentFragment::html_whitespace()
1340{
1341 QFETCH(QString, html);
1342 QFETCH(QString, expectedPlainText);
1343
1344 setHtml(html);
1345
1346 QCOMPARE(doc->toPlainText(), expectedPlainText);
1347}
1348
1349void tst_QTextDocumentFragment::html_qt3Whitespace()
1350{
1351 QString text = "This text has some whitespace"
1352 "\n and \nnewlines that \n should be ignored\n\n";
1353 const QString html = QString("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>")
1354 + text
1355 + QString("</body></html>");
1356
1357 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1358
1359 text.remove(c: QChar::fromLatin1(c: '\n'));
1360
1361 QCOMPARE(doc->toPlainText(), text);
1362}
1363
1364void tst_QTextDocumentFragment::html_qt3WhitespaceWithFragments()
1365{
1366 QString text = "This text has some whitespace"
1367 "\n and \nnewlines that \n should be ignored\n\n";
1368 const QString html = QString("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>"
1369 "blah blah<!--StartFragment--><span>")
1370 + text
1371 + QString("</span><!--EndFragment--></body></html>");
1372
1373 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1374
1375 text.remove(c: QChar::fromLatin1(c: '\n'));
1376
1377 QCOMPARE(doc->toPlainText(), text);
1378}
1379
1380void tst_QTextDocumentFragment::html_qt3WhitespaceAfterTags()
1381{
1382 QString text = " This text has some whitespace ";
1383 const QString html = QString("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body><span>")
1384 + text
1385 + QString("</span></body></html>");
1386
1387 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1388
1389 QCOMPARE(doc->toPlainText(), text);
1390}
1391
1392void tst_QTextDocumentFragment::html_listStart1()
1393{
1394 // don't create a block for the <ul> element, even if there's some whitespace between
1395 // it and the <li>
1396 const QString html = QStringLiteral("<ul> <li>list item</li><ul>");
1397 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1398
1399 QCOMPARE(doc->blockCount(), 1);
1400}
1401
1402void tst_QTextDocumentFragment::html_listStart2()
1403{
1404 // unlike with html_listStart1 we want a block showing the 'buggy' text here
1405 const QString html = QStringLiteral("<ul>buggy, but text should appear<li>list item</li><ul>");
1406 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1407
1408 QCOMPARE(doc->blockCount(), 2);
1409}
1410
1411void tst_QTextDocumentFragment::html_cssMargin()
1412{
1413 const char html[] = "<p style=\"margin-top: 1px; margin-bottom: 2px; margin-left: 3px; margin-right: 4px\">Test</p>";
1414 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1415 const QTextBlockFormat fmt = cursor.blockFormat();
1416 QCOMPARE(fmt.topMargin(), qreal(1));
1417 QCOMPARE(fmt.bottomMargin(), qreal(2));
1418 QCOMPARE(fmt.leftMargin(), qreal(3));
1419 QCOMPARE(fmt.rightMargin(), qreal(4));
1420}
1421
1422void tst_QTextDocumentFragment::html_hexEntities()
1423{
1424 const char html[] = "&#x00040;";
1425 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1426 QCOMPARE(doc->begin().begin().fragment().text(), QString("@"));
1427}
1428
1429void tst_QTextDocumentFragment::html_decEntities()
1430{
1431 const char html[] = "&#64;";
1432 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1433 QCOMPARE(doc->begin().begin().fragment().text(), QString("@"));
1434}
1435
1436void tst_QTextDocumentFragment::html_thCentered()
1437{
1438 const char html[] = "<table><tr><th>This should be centered</th></tr></table>";
1439 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1440
1441 cursor.movePosition(op: QTextCursor::PreviousBlock);
1442 QTextTable *table = cursor.currentTable();
1443 QVERIFY(table);
1444
1445 QVERIFY(table->cellAt(0, 0).begin().currentBlock().blockFormat().alignment() == Qt::AlignCenter);
1446}
1447
1448void tst_QTextDocumentFragment::orderedListNumbering()
1449{
1450 // Supporter issue 45941 - make sure _two_ separate lists
1451 // are imported, which have their own numbering
1452 const char html[] = "<html><body>"
1453 "<ol><li>elem 1</li></ol>"
1454 "<ol><li>elem 1</li></ol>"
1455 "</body></html>";
1456
1457 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1458
1459 int numberOfLists = 0;
1460
1461 cursor.movePosition(op: QTextCursor::Start);
1462 QTextList *lastList = 0;
1463 do {
1464 QTextList *list = cursor.currentList();
1465 if (list && list != lastList) {
1466 lastList = list;
1467 ++numberOfLists;
1468 }
1469 } while (cursor.movePosition(op: QTextCursor::NextBlock));
1470
1471 QCOMPARE(numberOfLists, 2);
1472}
1473
1474void tst_QTextDocumentFragment::html_blockAfterList()
1475{
1476 const char html[] = "<ul><li>Foo</ul>This should be a separate paragraph and not be indented at the same level as Foo";
1477 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html));
1478
1479 cursor.movePosition(op: QTextCursor::Start);
1480
1481 QVERIFY(cursor.currentList());
1482 QCOMPARE(cursor.currentList()->format().indent(), 1);
1483
1484 QVERIFY(cursor.movePosition(QTextCursor::NextBlock));
1485 QVERIFY(!cursor.currentList());
1486 QCOMPARE(cursor.blockFormat().indent(), 0);
1487}
1488
1489void tst_QTextDocumentFragment::html_subAndSuperScript()
1490{
1491 const char subHtml[] = "<sub>Subby</sub>";
1492 const char superHtml[] = "<sup>Super</sup>";
1493 const char subHtmlCss[] = "<span style=\"vertical-align: sub\">Subby</span>";
1494 const char superHtmlCss[] = "<span style=\"vertical-align: super\">Super</span>";
1495 const char alignmentInherited[] = "<sub><font face=\"Verdana\">Subby</font></sub>";
1496
1497 setHtml(subHtml);
1498 QCOMPARE(cursor.charFormat().verticalAlignment(), QTextCharFormat::AlignSubScript);
1499
1500 setHtml(subHtmlCss);
1501 QCOMPARE(cursor.charFormat().verticalAlignment(), QTextCharFormat::AlignSubScript);
1502
1503 setHtml(superHtml);
1504 QCOMPARE(cursor.charFormat().verticalAlignment(), QTextCharFormat::AlignSuperScript);
1505
1506 setHtml(superHtmlCss);
1507 QCOMPARE(cursor.charFormat().verticalAlignment(), QTextCharFormat::AlignSuperScript);
1508
1509 setHtml(alignmentInherited);
1510 QCOMPARE(cursor.charFormat().verticalAlignment(), QTextCharFormat::AlignSubScript);
1511}
1512
1513void tst_QTextDocumentFragment::html_cssColors()
1514{
1515 const char color[] = "<span style=\"color:red\"><span style=\"color:blue\">Blue</span></span>";
1516 setHtml(color);
1517 QVERIFY(cursor.charFormat().foreground().color() == Qt::blue);
1518
1519 const char rgbColor[] = "<span style=\"color:red\"><span style=\"color:rgb(0, 0, 255)\">Blue</span></span>";
1520 setHtml(rgbColor);
1521 QVERIFY(cursor.charFormat().foreground().color() == Qt::blue);
1522}
1523
1524void tst_QTextDocumentFragment::obeyFragmentMarkersInImport()
1525{
1526 const char html[] = "This leading text should not appear<!--StartFragment--><span>Text</span><!--EndFragment-->This text at the end should not appear";
1527 setHtml(html);
1528
1529 QCOMPARE(doc->toPlainText(), QString("Text"));
1530}
1531
1532void tst_QTextDocumentFragment::whitespaceWithFragmentMarkers()
1533{
1534 QString text(" text with leading and trailing whitespace ");
1535 const char html[] = "This leading text should not appear<!--StartFragment-->%1<!--EndFragment-->This text at the end should not appear";
1536 setHtml(QString::fromLatin1(str: html).arg(a: text));
1537
1538 QString expected("text with leading and trailing whitespace ");
1539 QCOMPARE(doc->toPlainText(), expected);
1540}
1541
1542void tst_QTextDocumentFragment::html_emptyParapgraphs1()
1543{
1544 const char html[] = "<p style=\"-qt-paragraph-type:empty;\">&nbsp;</p><p>Two paragraphs</p>";
1545 setHtml(html);
1546
1547 QCOMPARE(doc->blockCount(), 2);
1548 QVERIFY(doc->begin().text().isEmpty());
1549 QCOMPARE(doc->begin().next().text(), QString("Two paragraphs"));
1550}
1551
1552void tst_QTextDocumentFragment::html_emptyParapgraphs2()
1553{
1554 const char html[] = "<p style=\"margin-left:80px\"></p><p>One paragraph</p>";
1555 setHtml(html);
1556
1557 QCOMPARE(doc->blockCount(), 1);
1558 QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0));
1559
1560 const char html2[] = "<p style=\"margin-left:80px\"></p>One paragraph";
1561 setHtml(html2);
1562 QCOMPARE(doc->blockCount(), 1);
1563 QCOMPARE(cursor.blockFormat().leftMargin(), qreal(0));
1564
1565 const char html3[] = "<p style=\"margin-left:80px\">Foo</p><p></p>Two paragraphs";
1566 setHtml(html3);
1567 QCOMPARE(doc->blockCount(), 2);
1568 cursor = QTextCursor(doc);
1569 QCOMPARE(cursor.blockFormat().leftMargin(), qreal(80));
1570 QCOMPARE(cursor.block().next().blockFormat().leftMargin(), qreal(0));
1571}
1572
1573void tst_QTextDocumentFragment::html_emptyParagraphs3()
1574{
1575 const char html[] = "<ul><p>Foo</p><p></p></ul><h4>Bar</h4>";
1576
1577 setHtml(html);
1578
1579 QCOMPARE(doc->blockCount(), 2);
1580
1581 cursor = QTextCursor(doc);
1582 QCOMPARE(cursor.block().next().blockFormat().indent(), 0);
1583}
1584
1585void tst_QTextDocumentFragment::html_emptyParagraphs4()
1586{
1587 const char html[] = "<p>foo</p><p style=\"page-break-before: always\"></p><p>bar</p>";
1588 setHtml(html);
1589
1590 QTextBlock block = doc->begin();
1591 QVERIFY(block.isValid());
1592 QCOMPARE(block.text(), QString("foo"));
1593 block = block.next();
1594 QVERIFY(block.isValid());
1595 QTextBlockFormat bf = block.blockFormat();
1596 QVERIFY(bf.hasProperty(QTextFormat::PageBreakPolicy));
1597 QCOMPARE(bf.pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore);
1598 QCOMPARE(block.text(), QString("bar"));
1599
1600 const char html2[] = "<p>foo</p><p style=\"page-break-after: always\"></p><p>bar</p>";
1601 setHtml(html2);
1602
1603 block = doc->begin();
1604 QVERIFY(block.isValid());
1605 QCOMPARE(block.text(), QString("foo"));
1606 block = block.next();
1607 QVERIFY(block.isValid());
1608 bf = block.blockFormat();
1609 QVERIFY(bf.hasProperty(QTextFormat::PageBreakPolicy));
1610 QCOMPARE(bf.pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore); // after the empty line means it should appear for 'bar'
1611 QCOMPARE(block.text(), QString("bar"));
1612}
1613
1614void tst_QTextDocumentFragment::html_font()
1615{
1616 const char html[] = "<font color=\"blue\"><p>Hah</p></font>";
1617 setHtml(html);
1618
1619 QVERIFY(cursor.charFormat().foreground().color() == Qt::blue);
1620 QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue);
1621}
1622
1623void tst_QTextDocumentFragment::html_fontSize()
1624{
1625 const char html[] = "<font size=\"2\">Hah</font>";
1626 setHtml(html);
1627
1628 QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), -1);
1629}
1630
1631void tst_QTextDocumentFragment::html_fontSizeAdjustment()
1632{
1633 const char html[] = "<font size=\"7\"><b>Hah</b></font>";
1634 setHtml(html);
1635
1636 QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), 4);
1637 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
1638}
1639
1640void tst_QTextDocumentFragment::html_cssFontSize()
1641{
1642 const char html[] = "<span style=\"font-size: 50pt\">Foo</span>";
1643 setHtml(html);
1644
1645 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50);
1646
1647 const char html2[] = "<span style=\"font-size: 50px\">Foo</span>";
1648 setHtml(html2);
1649
1650 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50);
1651
1652 const char html3[] = "<span style=\"font-size: large\">Foo</span>";
1653 setHtml(html3);
1654
1655 QCOMPARE(cursor.charFormat().property(QTextFormat::FontSizeAdjustment).toInt(), 1);
1656}
1657
1658void tst_QTextDocumentFragment::html_cssShorthandFont()
1659{
1660 {
1661 const char html[] = "<span style=\"font: 50px sans-serif\">Foo</span>";
1662 setHtml(html);
1663 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPixelSize).toInt(), 50);
1664 QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif"));
1665 }
1666 {
1667 const char html[] = "<span style=\"font: 50pt sans-serif\">Foo</span>";
1668 setHtml(html);
1669 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 50);
1670 QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("sans-serif"));
1671 }
1672 {
1673 const char html[] = "<span style='font:7.0pt \"Times New Roman\"'>Foo</span>";
1674 setHtml(html);
1675 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7);
1676 QCOMPARE(cursor.charFormat().property(QTextFormat::FontFamily).toString(), QString("Times New Roman"));
1677 }
1678 {
1679 const char html[] = "<span style='font:bold 7.0pt'>Foo</span>";
1680 setHtml(html);
1681 QCOMPARE(cursor.charFormat().property(QTextFormat::FontWeight).toInt(), int(QFont::Bold));
1682 QCOMPARE(cursor.charFormat().property(QTextFormat::FontPointSize).toInt(), 7);
1683 }
1684 {
1685 const char html[] = "<span style='font:bold italic 7.0pt'>Foo</span>";
1686 setHtml(html);
1687 QCOMPARE(cursor.charFormat().property(QTextFormat::FontWeight).toInt(), int(QFont::Bold));
1688 QCOMPARE(cursor.charFormat().property(QTextFormat::FontItalic).toBool(), true);
1689 }
1690}
1691
1692void tst_QTextDocumentFragment::html_bodyBgColor()
1693{
1694 const char html[] = "<body bgcolor=\"blue\">Foo</body>";
1695 doc->setHtml(html);
1696
1697 QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue);
1698}
1699
1700void tst_QTextDocumentFragment::html_qtBgColor()
1701{
1702 const char html[] = "<qt bgcolor=\"blue\">Foo</qt>";
1703 doc->setHtml(html);
1704
1705 QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue);
1706}
1707
1708void tst_QTextDocumentFragment::html_bodyBackground()
1709{
1710 const char html[] = "<body background=\"foo.png\">Foo</body>";
1711 doc->setHtml(html);
1712
1713#ifdef Q_OS_WINRT
1714 QEXPECT_FAIL("", "Fails on winrt. Investigate - QTBUG-68297", Continue);
1715#endif
1716 QCOMPARE(doc->rootFrame()->frameFormat().background().style(), Qt::TexturePattern);
1717}
1718
1719void tst_QTextDocumentFragment::html_tableCellBackground()
1720{
1721 const char html[] = "<body><table><tr><td background=\"foo.png\">Foo</td></tr></table></body>";
1722 doc->setHtml(html);
1723
1724 cursor.movePosition(op: QTextCursor::Start);
1725 cursor.movePosition(op: QTextCursor::NextBlock);
1726 QTextTable *table = cursor.currentTable();
1727 QVERIFY(table);
1728
1729 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1730#ifdef Q_OS_WINRT
1731 QEXPECT_FAIL("", "Fails on winrt. Investigate - QTBUG-68297", Continue);
1732#endif
1733 QCOMPARE(cell.format().background().style(), Qt::TexturePattern);
1734}
1735
1736void tst_QTextDocumentFragment::css_bodyBackground()
1737{
1738 const char html[] = "<body style=\"background-image:url('foo.png')\">Foo</body>";
1739 doc->setHtml(html);
1740
1741#ifdef Q_OS_WINRT
1742 QEXPECT_FAIL("", "Fails on winrt. Investigate - QTBUG-68297", Continue);
1743#endif
1744 QCOMPARE(doc->rootFrame()->frameFormat().background().style(), Qt::TexturePattern);
1745}
1746
1747void tst_QTextDocumentFragment::css_tableCellBackground()
1748{
1749 const char html[] = "<body><table><tr><td style=\"background-image:url('foo.png')\">Foo</td></tr></table></body>";
1750 doc->setHtml(html);
1751
1752 cursor.movePosition(op: QTextCursor::Start);
1753 cursor.movePosition(op: QTextCursor::NextBlock);
1754 QTextTable *table = cursor.currentTable();
1755 QVERIFY(table);
1756
1757 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1758#ifdef Q_OS_WINRT
1759 QEXPECT_FAIL("", "Fails on winrt. Investigate - QTBUG-68297", Continue);
1760#endif
1761 QCOMPARE(cell.format().background().style(), Qt::TexturePattern);
1762}
1763
1764void tst_QTextDocumentFragment::css_tableCellBorder()
1765{
1766 const char html[] = "<body><table><tr><td style=\"border-width:8px;border-color:green;border-style:groove;border-left-style:dashed;border-left-color:red;border-left-width:4px\">Foo</td></tr></table></body>";
1767 doc->setHtml(html);
1768
1769 cursor.movePosition(op: QTextCursor::Start);
1770 cursor.movePosition(op: QTextCursor::NextBlock);
1771 QTextTable *table = cursor.currentTable();
1772 QVERIFY(table);
1773
1774 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1775 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1776 QCOMPARE(cellFormat.leftBorder(), qreal(4));
1777 QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("red")));
1778 QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1779
1780 QCOMPARE(cellFormat.rightBorder(), qreal(8));
1781 QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green")));
1782 QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Groove);
1783
1784 QCOMPARE(cellFormat.bottomBorder(), qreal(8));
1785 QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green")));
1786 QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Groove);
1787
1788 QCOMPARE(cellFormat.topBorder(), qreal(8));
1789 QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green")));
1790 QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Groove);
1791}
1792
1793void tst_QTextDocumentFragment::css_tableCellBorderWidthOneValue() // QTBUG-80496
1794{
1795 const char html[] = "<head><style type=\"text/css\"> body, td { border-width: 2px; }</style></head> <body> <table> <tr> <td></td> </tr> </table> </body> </html>";
1796 doc->setHtml(html);
1797
1798 cursor.movePosition(op: QTextCursor::Start);
1799 cursor.movePosition(op: QTextCursor::NextBlock);
1800 QTextTable *table = cursor.currentTable();
1801 QVERIFY(table);
1802
1803 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1804 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1805 QCOMPARE(cellFormat.leftBorder(), qreal(2));
1806 QCOMPARE(cellFormat.rightBorder(), qreal(2));
1807 QCOMPARE(cellFormat.bottomBorder(), qreal(2));
1808 QCOMPARE(cellFormat.topBorder(), qreal(2));
1809}
1810
1811void tst_QTextDocumentFragment::css_tableCellBorderWidthTwoValues() // QTBUG-80496
1812{
1813 const char html[] = "<head><style type=\"text/css\"> body, td { border-width: 2px 3px; }</style></head> <body> <table> <tr> <td></td> </tr> </table> </body> </html>";
1814 doc->setHtml(html);
1815
1816 cursor.movePosition(op: QTextCursor::Start);
1817 cursor.movePosition(op: QTextCursor::NextBlock);
1818 QTextTable *table = cursor.currentTable();
1819 QVERIFY(table);
1820
1821 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1822 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1823 QCOMPARE(cellFormat.leftBorder(), qreal(3));
1824 QCOMPARE(cellFormat.rightBorder(), qreal(3));
1825 QCOMPARE(cellFormat.bottomBorder(), qreal(2));
1826 QCOMPARE(cellFormat.topBorder(), qreal(2));
1827}
1828
1829void tst_QTextDocumentFragment::css_tableCellBorderShorthand()
1830{
1831 const char html[] = "<body><table><tr><td style=\"border-left:1px solid green;border-right:2px dashed red;border-bottom:3px dotted yellow;border-top:4px dot-dash blue\">Foo</td></tr></table></body>";
1832 doc->setHtml(html);
1833
1834 cursor.movePosition(op: QTextCursor::Start);
1835 cursor.movePosition(op: QTextCursor::NextBlock);
1836 QTextTable *table = cursor.currentTable();
1837 QVERIFY(table);
1838
1839 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1840 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1841 QCOMPARE(cellFormat.leftBorder(), qreal(1));
1842 QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("green")));
1843 QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Solid);
1844
1845 QCOMPARE(cellFormat.rightBorder(), qreal(2));
1846 QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("red")));
1847 QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1848
1849 QCOMPARE(cellFormat.bottomBorder(), qreal(3));
1850 QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("yellow")));
1851 QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dotted);
1852
1853 QCOMPARE(cellFormat.topBorder(), qreal(4));
1854 QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("blue")));
1855 QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_DotDash);
1856}
1857
1858void tst_QTextDocumentFragment::css_tableCellAllBordersShorthand()
1859{
1860 const char html[] = "<body><table><tr><td style=\"border:2px dashed green\">Foo</td></tr></table></body>";
1861 doc->setHtml(html);
1862
1863 cursor.movePosition(op: QTextCursor::Start);
1864 cursor.movePosition(op: QTextCursor::NextBlock);
1865 QTextTable *table = cursor.currentTable();
1866 QVERIFY(table);
1867
1868 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1869 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1870 QCOMPARE(cellFormat.leftBorder(), qreal(2));
1871 QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("green")));
1872 QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1873
1874 QCOMPARE(cellFormat.rightBorder(), qreal(2));
1875 QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green")));
1876 QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1877
1878 QCOMPARE(cellFormat.bottomBorder(), qreal(2));
1879 QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green")));
1880 QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1881
1882 QCOMPARE(cellFormat.topBorder(), qreal(2));
1883 QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green")));
1884 QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1885}
1886
1887void tst_QTextDocumentFragment::css_tableCellOverrideOneBorder()
1888{
1889 const char html[] = "<body><table><tr><td style=\"border:2px dashed green;border-left:4px solid red\">Foo</td></tr></table></body>";
1890 doc->setHtml(html);
1891
1892 cursor.movePosition(op: QTextCursor::Start);
1893 cursor.movePosition(op: QTextCursor::NextBlock);
1894 QTextTable *table = cursor.currentTable();
1895 QVERIFY(table);
1896
1897 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1898 QTextTableCellFormat cellFormat = cell.format().toTableCellFormat();
1899 QCOMPARE(cellFormat.leftBorder(), qreal(4));
1900 QCOMPARE(cellFormat.leftBorderBrush(), QBrush(QColor("red")));
1901 QCOMPARE(cellFormat.leftBorderStyle(), QTextFrameFormat::BorderStyle_Solid);
1902
1903 QCOMPARE(cellFormat.rightBorder(), qreal(2));
1904 QCOMPARE(cellFormat.rightBorderBrush(), QBrush(QColor("green")));
1905 QCOMPARE(cellFormat.rightBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1906
1907 QCOMPARE(cellFormat.bottomBorder(), qreal(2));
1908 QCOMPARE(cellFormat.bottomBorderBrush(), QBrush(QColor("green")));
1909 QCOMPARE(cellFormat.bottomBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1910
1911 QCOMPARE(cellFormat.topBorder(), qreal(2));
1912 QCOMPARE(cellFormat.topBorderBrush(), QBrush(QColor("green")));
1913 QCOMPARE(cellFormat.topBorderStyle(), QTextFrameFormat::BorderStyle_Dashed);
1914}
1915
1916void tst_QTextDocumentFragment::css_tableBorderCollapse()
1917{
1918 const char html[] = "<body><table style=\"border-collapse:collapse\"><tr><td>Foo</td></tr></table></body>";
1919 doc->setHtml(html);
1920
1921 cursor.movePosition(op: QTextCursor::Start);
1922 cursor.movePosition(op: QTextCursor::NextBlock);
1923 QTextTable *table = cursor.currentTable();
1924 QVERIFY(table);
1925
1926 QCOMPARE(table->format().borderCollapse(), true);
1927}
1928
1929void tst_QTextDocumentFragment::css_cellPaddings()
1930{
1931 const char html[] = "<body><table><tr><td style=\"padding-left:1\">Foo</td>"
1932 "<td style=\"padding-right:1\"></td><td style=\"padding-top:10\"></td>"
1933 "<td style=\"padding-bottom:5\"></td><td style=\"padding:15\"></td></tr></table></body>";
1934 doc->setHtml(html);
1935
1936 cursor.movePosition(op: QTextCursor::Start);
1937 cursor.movePosition(op: QTextCursor::NextBlock);
1938 QTextTable *table = cursor.currentTable();
1939 QVERIFY(table);
1940
1941 QTextTableCell cell = table->cellAt(row: 0, col: 0);
1942 QCOMPARE(cell.format().toTableCellFormat().leftPadding(), qreal(1));
1943 cell = table->cellAt(row: 0, col: 1);
1944 QCOMPARE(cell.format().toTableCellFormat().rightPadding(), qreal(1));
1945 cell = table->cellAt(row: 0, col: 2);
1946 QCOMPARE(cell.format().toTableCellFormat().topPadding(), qreal(10));
1947 cell = table->cellAt(row: 0, col: 3);
1948 QCOMPARE(cell.format().toTableCellFormat().bottomPadding(), qreal(5));
1949 cell = table->cellAt(row: 0, col: 4);
1950 QCOMPARE(cell.format().toTableCellFormat().leftPadding(), qreal(15));
1951 QCOMPARE(cell.format().toTableCellFormat().rightPadding(), qreal(15));
1952 QCOMPARE(cell.format().toTableCellFormat().topPadding(), qreal(15));
1953 QCOMPARE(cell.format().toTableCellFormat().bottomPadding(), qreal(15));
1954}
1955
1956void tst_QTextDocumentFragment::css_whiteSpace_data()
1957{
1958 QTest::addColumn<QString>(name: "htmlText");
1959 QTest::addColumn<bool>(name: "nowrap");
1960
1961 QTest::newRow(dataTag: "default") << QString("<p>Normal Text</p>") << false;
1962 QTest::newRow(dataTag: "white-space:nowrap") << QString("<p style=white-space:nowrap>Normal Text</p>") << true;
1963 QTest::newRow(dataTag: "white-space:pre") << QString("<p style=white-space:pre>Normal Text</p>") << true;
1964}
1965
1966void tst_QTextDocumentFragment::css_whiteSpace()
1967{
1968 QFETCH(QString, htmlText);
1969 QFETCH(bool, nowrap);
1970
1971 doc->setHtml(htmlText);
1972 QCOMPARE(doc->blockCount(), 1);
1973 QCOMPARE(doc->begin().blockFormat().nonBreakableLines(), nowrap);
1974}
1975
1976void tst_QTextDocumentFragment::html_blockLevelDiv()
1977{
1978 const char html[] = "<div align=right><b>Hello World";
1979 setHtml(html);
1980
1981 QCOMPARE(doc->begin().blockFormat().alignment(), Qt::AlignRight|Qt::AlignAbsolute);
1982 QCOMPARE(doc->begin().next(), doc->end());
1983}
1984
1985void tst_QTextDocumentFragment::html_spanNesting()
1986{
1987 const char html[] = "<span style=\"color:black\">a<span style=\"color:red\">b<span style=\"color:black\">c</span></span>d</span>";
1988 setHtml(html);
1989
1990 cursor.movePosition(op: QTextCursor::Start);
1991 cursor.movePosition(op: QTextCursor::NextCharacter);
1992 QVERIFY(cursor.charFormat().foreground() == Qt::black);
1993 cursor.movePosition(op: QTextCursor::NextCharacter);
1994 QVERIFY(cursor.charFormat().foreground() == Qt::red);
1995 cursor.movePosition(op: QTextCursor::NextCharacter);
1996 QVERIFY(cursor.charFormat().foreground() == Qt::black);
1997 cursor.movePosition(op: QTextCursor::NextCharacter);
1998 QVERIFY(cursor.charFormat().foreground() == Qt::black);
1999}
2000
2001void tst_QTextDocumentFragment::html_nestedLists()
2002{
2003 const char html[] = "<p><ul><li>Foo<ul><li>In nested list</li></ul></li><li>Last item</li></ul></p>";
2004 setHtml(html);
2005
2006 cursor.movePosition(op: QTextCursor::Start);
2007 QTextList *firstList = cursor.currentList();
2008 QVERIFY(firstList);
2009 QCOMPARE(firstList->format().indent(), 1);
2010
2011 cursor.movePosition(op: QTextCursor::NextBlock);
2012 QTextList *secondList = cursor.currentList();
2013 QVERIFY(secondList);
2014 QVERIFY(secondList != firstList);
2015 QCOMPARE(cursor.currentList()->format().indent(), 2);
2016
2017 cursor.movePosition(op: QTextCursor::NextBlock);
2018 QTextList *thirdList = cursor.currentList();
2019 QVERIFY(thirdList);
2020 QCOMPARE(thirdList, firstList);
2021}
2022
2023void tst_QTextDocumentFragment::noSpecialCharactersInPlainText()
2024{
2025 cursor.insertTable(rows: 2, cols: 2);
2026 cursor.insertBlock();
2027 cursor.insertText(text: QString(QChar::LineSeparator));
2028 cursor.insertText(text: QString(QChar::Nbsp));
2029
2030 QString plain = doc->toPlainText();
2031 QVERIFY(!plain.contains(QChar::ParagraphSeparator));
2032 QVERIFY(!plain.contains(QChar::Nbsp));
2033 QVERIFY(!plain.contains(QTextBeginningOfFrame));
2034 QVERIFY(!plain.contains(QTextEndOfFrame));
2035 QVERIFY(!plain.contains(QChar::LineSeparator));
2036
2037 plain = QTextDocumentFragment(doc).toPlainText();
2038 QVERIFY(!plain.contains(QChar::ParagraphSeparator));
2039 QVERIFY(!plain.contains(QChar::Nbsp));
2040 QVERIFY(!plain.contains(QTextBeginningOfFrame));
2041 QVERIFY(!plain.contains(QTextEndOfFrame));
2042 QVERIFY(!plain.contains(QChar::LineSeparator));
2043}
2044
2045void tst_QTextDocumentFragment::html_doNotInheritBackground()
2046{
2047 const char html[] = "<html><body bgcolor=\"blue\"><p>Blah</p></body></html>";
2048 doc->setHtml(html);
2049
2050 for (QTextBlock block = doc->begin();
2051 block.isValid(); block = block.next()) {
2052 QVERIFY(!block.blockFormat().hasProperty(QTextFormat::BackgroundBrush));
2053 }
2054
2055 QVERIFY(doc->rootFrame()->frameFormat().hasProperty(QTextFormat::BackgroundBrush));
2056 QVERIFY(doc->rootFrame()->frameFormat().background().color() == Qt::blue);
2057}
2058
2059void tst_QTextDocumentFragment::html_inheritBackgroundToInlineElements()
2060{
2061 const char html[] = "<span style=\"background: blue\">Foo<span>Bar</span></span>";
2062 doc->setHtml(html);
2063
2064 int fragmentCount = 0;
2065
2066 QTextBlock block = doc->begin();
2067 for (QTextBlock::Iterator it = block.begin();
2068 !it.atEnd(); ++it, ++fragmentCount) {
2069
2070 const QTextFragment fragment = it.fragment();
2071 if (fragmentCount == 0) {
2072 QCOMPARE(fragment.text(), QString("FooBar"));
2073 QVERIFY(fragment.charFormat().background().color() == Qt::blue);
2074 }
2075 }
2076
2077 QCOMPARE(fragmentCount, 1);
2078}
2079
2080void tst_QTextDocumentFragment::html_doNotInheritBackgroundFromBlockElements()
2081{
2082 const char html[] = "<p style=\"background: blue\"><span>Foo</span></span>";
2083 doc->setHtml(html);
2084
2085 int fragmentCount = 0;
2086
2087 QTextBlock block = doc->begin();
2088 for (QTextBlock::Iterator it = block.begin();
2089 !it.atEnd(); ++it, ++fragmentCount) {
2090
2091 const QTextFragment fragment = it.fragment();
2092 if (fragmentCount == 0) {
2093 QCOMPARE(fragment.text(), QString("Foo"));
2094 QVERIFY(!fragment.charFormat().hasProperty(QTextFormat::BackgroundBrush));
2095 }
2096 }
2097
2098 QCOMPARE(fragmentCount, 1);
2099}
2100void tst_QTextDocumentFragment::html_nobr()
2101{
2102 const QString input = "Blah Foo Bar";
2103 const QString html = QString::fromLatin1(str: "<html><body><p><nobr>") + input + QString::fromLatin1(str: "</nobr></p></body></html>");
2104 setHtml(html);
2105
2106 QString text = doc->begin().begin().fragment().text();
2107 QString expectedText = input;
2108 expectedText.replace(re: QRegularExpression("\\s+"), after: QString(QChar::Nbsp));
2109 QCOMPARE(text, expectedText);
2110}
2111
2112void tst_QTextDocumentFragment::fromPlainText()
2113{
2114 QString plainText;
2115 plainText = "Hello\nWorld\r\nBlub";
2116 plainText += QChar::ParagraphSeparator;
2117 // TextEdit on OS 10 gives us OS 9 style linefeeds
2118 // when copy & pasteing multi-line plaintext.
2119 plainText += "OS9IsOldSchool\r";
2120 plainText += "Last Parag";
2121
2122 doc->setPlainText(plainText);
2123
2124 int blockCount = 0;
2125 for (QTextBlock block = doc->begin(); block.isValid(); block = block.next()) {
2126 QVERIFY(!block.text().contains(QLatin1Char('\n')));
2127 QVERIFY(!block.text().contains(QLatin1Char('\r')));
2128 QVERIFY(!block.text().contains(QChar::ParagraphSeparator));
2129
2130 if (blockCount == 0)
2131 QCOMPARE(block.text(), QString("Hello"));
2132 else if (blockCount == 1)
2133 QCOMPARE(block.text(), QString("World"));
2134 else if (blockCount == 2)
2135 QCOMPARE(block.text(), QString("Blub"));
2136 else if (blockCount == 3)
2137 QCOMPARE(block.text(), QString("OS9IsOldSchool"));
2138 else if (blockCount == 4)
2139 QCOMPARE(block.text(), QString("Last Parag"));
2140
2141
2142 ++blockCount;
2143 }
2144
2145 QCOMPARE(blockCount, 5);
2146}
2147
2148void tst_QTextDocumentFragment::fromPlainText2()
2149{
2150 doc->setPlainText("Hello World");
2151 QCOMPARE(QTextDocumentFragment(doc).toPlainText(), doc->toPlainText());
2152}
2153
2154void tst_QTextDocumentFragment::html_closingImageTag()
2155{
2156 const char html[] = "<span style=\"font-size: 10pt\"><span style=\"font-size: 40pt\">Blah<img src=\"blah\"></img>Foo</span></span>";
2157 setHtml(html);
2158
2159 int fragmentCount = 0;
2160
2161 QTextBlock block = doc->begin();
2162 for (QTextBlock::Iterator it = block.begin();
2163 !it.atEnd(); ++it, ++fragmentCount) {
2164
2165 const QTextFragment fragment = it.fragment();
2166 if (fragmentCount == 0) {
2167 QCOMPARE(fragment.text(), QString("Blah"));
2168 QCOMPARE(fragment.charFormat().fontPointSize(), qreal(40));
2169 } else if (fragmentCount == 1) {
2170 QCOMPARE(fragment.text(), QString(QChar::ObjectReplacementCharacter));
2171 } else if (fragmentCount == 2) {
2172 QCOMPARE(fragment.text(), QString("Foo"));
2173 QCOMPARE(fragment.charFormat().fontPointSize(), qreal(40));
2174 }
2175 }
2176
2177 QCOMPARE(fragmentCount, 3);
2178}
2179
2180void tst_QTextDocumentFragment::html_emptyDocument()
2181{
2182 const char html[] = "<html><body><p style=\"-qt-paragraph-type:empty;\"></p></body></html>";
2183 setHtml(html);
2184 QCOMPARE(doc->blockCount(), 1);
2185}
2186
2187void tst_QTextDocumentFragment::html_closingTag()
2188{
2189 const char html[] = "<i />text";
2190 setHtml(html);
2191
2192 QVERIFY(!cursor.charFormat().fontItalic());
2193}
2194
2195void tst_QTextDocumentFragment::html_anchorAroundImage()
2196{
2197 const char html[] = "<a href=\"http://www.troll.no\"><img src=test.png></a>";
2198 setHtml(html);
2199
2200 cursor.movePosition(op: QTextCursor::Start);
2201 cursor.movePosition(op: QTextCursor::NextCharacter);
2202 QTextImageFormat fmt = cursor.charFormat().toImageFormat();
2203 QCOMPARE(fmt.name(), QString("test.png"));
2204 QVERIFY(fmt.isAnchor());
2205 QCOMPARE(fmt.anchorHref(), QString("http://www.troll.no"));
2206}
2207
2208void tst_QTextDocumentFragment::html_floatBorder()
2209{
2210 const char html[] = "<table border=1.2><tr><td>Foo";
2211 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
2212 cursor.movePosition(op: QTextCursor::Start);
2213 cursor.movePosition(op: QTextCursor::NextBlock);
2214 QVERIFY(cursor.currentTable());
2215 QCOMPARE(cursor.currentTable()->format().border(), qreal(1.2));
2216}
2217
2218void tst_QTextDocumentFragment::html_frameImport()
2219{
2220 QTextFrameFormat ffmt;
2221 ffmt.setBorder(1);
2222 ffmt.setPosition(QTextFrameFormat::FloatRight);
2223 ffmt.setMargin(2);
2224 ffmt.setWidth(100);
2225 ffmt.setHeight(50);
2226 ffmt.setBackground(QColor("#00ff00"));
2227 cursor.insertFrame(format: ffmt);
2228 cursor.insertText(text: "Hello World");
2229
2230 QTextDocumentFragment frag(doc);
2231 cleanup();
2232 init();
2233 frag = QTextDocumentFragment::fromHtml(html: frag.toHtml());
2234 cursor.insertFragment(fragment: frag);
2235
2236 QList<QTextFrame *> childFrames = doc->rootFrame()->childFrames();
2237 QCOMPARE(childFrames.count(), 1);
2238 QTextFrame *frame = childFrames.first();
2239 QCOMPARE(frame->frameFormat().margin(), ffmt.margin());
2240 QCOMPARE(frame->frameFormat().border(), ffmt.border());
2241}
2242
2243void tst_QTextDocumentFragment::html_frameImport2()
2244{
2245 QTextFrameFormat ffmt;
2246 ffmt.setBorder(1);
2247 ffmt.setPosition(QTextFrameFormat::FloatRight);
2248 ffmt.setLeftMargin(200);
2249 ffmt.setTopMargin(100);
2250 ffmt.setBottomMargin(50);
2251 ffmt.setRightMargin(250);
2252 ffmt.setWidth(100);
2253 ffmt.setHeight(50);
2254 ffmt.setBackground(QColor("#00ff00"));
2255 cursor.insertFrame(format: ffmt);
2256 cursor.insertText(text: "Hello World");
2257
2258 QTextDocumentFragment frag(doc);
2259 cleanup();
2260 init();
2261 frag = QTextDocumentFragment::fromHtml(html: frag.toHtml());
2262 cursor.insertFragment(fragment: frag);
2263
2264 QList<QTextFrame *> childFrames = doc->rootFrame()->childFrames();
2265 QCOMPARE(childFrames.count(), 1);
2266 QTextFrame *frame = childFrames.first();
2267 QCOMPARE(frame->frameFormat().topMargin(), ffmt.topMargin());
2268 QCOMPARE(frame->frameFormat().bottomMargin(), ffmt.bottomMargin());
2269 QCOMPARE(frame->frameFormat().leftMargin(), ffmt.leftMargin());
2270 QCOMPARE(frame->frameFormat().rightMargin(), ffmt.rightMargin());
2271 QCOMPARE(frame->frameFormat().border(), ffmt.border());
2272}
2273
2274void tst_QTextDocumentFragment::html_dontAddMarginsAcrossTableCells()
2275{
2276 const char html[] = "<table style=\"margin-left: 100px;\"><tr><td><p style=\"margin-left:50px;\">Foo</p></td></tr></table>";
2277 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
2278
2279 QList<QTextFrame *> childFrames = doc->rootFrame()->childFrames();
2280 QCOMPARE(childFrames.count(), 1);
2281 QTextFrame *frame = childFrames.first();
2282 cursor = frame->firstCursorPosition();
2283 QCOMPARE(cursor.blockFormat().leftMargin(), qreal(50.0));
2284}
2285
2286void tst_QTextDocumentFragment::html_dontMergeCenterBlocks()
2287{
2288 const char html[] = "<center>This should be centered</center>And this should not be centered anymore";
2289 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
2290
2291 QCOMPARE(doc->blockCount(), 2);
2292 QTextBlock blk = doc->begin();
2293 QCOMPARE(blk.blockFormat().alignment(), Qt::AlignCenter);
2294 blk = blk.next();
2295 QVERIFY(blk.blockFormat().alignment() != Qt::AlignCenter);
2296}
2297
2298void tst_QTextDocumentFragment::html_tableCellBgColor()
2299{
2300 const char html[] = "<table><tr><td bgcolor=\"blue\">Test<p>Second Parag</p></td></tr></table>";
2301 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
2302
2303 cursor.movePosition(op: QTextCursor::Start);
2304 cursor.movePosition(op: QTextCursor::NextBlock);
2305 QTextTable *table = cursor.currentTable();
2306 QVERIFY(table);
2307
2308 QTextTableCell cell = table->cellAt(row: 0, col: 0);
2309 QVERIFY(cell.format().background().color() == Qt::blue);
2310}
2311
2312void tst_QTextDocumentFragment::html_tableCellBgColor2()
2313{
2314 const char html[] = "<table><tr><td bgcolor=\"blue\"><table><tr><td>Blah</td></tr></table></td></tr></table>";
2315 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
2316
2317 cursor.movePosition(op: QTextCursor::Start);
2318 cursor.movePosition(op: QTextCursor::NextBlock);
2319 QTextTable *table = cursor.currentTable();
2320 QVERIFY(table);
2321
2322 QTextTableCell cell = table->cellAt(row: 0, col: 0);
2323 QVERIFY(cell.format().background().color() == Qt::blue);
2324
2325 QTextFrame::Iterator it = cell.begin();
2326 QVERIFY(!it.atEnd());
2327 QVERIFY(!it.currentFrame());
2328 QVERIFY(it.currentBlock().isValid());
2329
2330 ++it;
2331 QVERIFY(!it.atEnd());
2332 QVERIFY(it.currentFrame() != 0);
2333 QVERIFY(!it.currentBlock().isValid());
2334
2335 ++it;
2336 QVERIFY(!it.atEnd());
2337 QVERIFY(!it.currentFrame());
2338 QVERIFY(it.currentBlock().isValid());
2339 QCOMPARE(it.currentBlock().blockFormat().background(), QBrush(Qt::NoBrush));
2340
2341 ++it;
2342 QVERIFY(it.atEnd());
2343}
2344
2345void tst_QTextDocumentFragment::html_cellSkip()
2346{
2347 const char html[] = ""
2348"<table border>"
2349" <tr>"
2350" <td>First Cell</td>"
2351" </tr>"
2352" <tr>"
2353" <td>Second Cell</td>"
2354" <td>Third Cell</td>"
2355" </tr>"
2356"</table>";
2357
2358 setHtml(html);
2359 cursor.movePosition(op: QTextCursor::Start);
2360 cursor.movePosition(op: QTextCursor::NextBlock);
2361 QTextTable *table = cursor.currentTable();
2362 QVERIFY(table);
2363 QVERIFY(table->columns() == 2 && table->rows() == 2);
2364
2365 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
2366 QVERIFY(table->cellAt(0, 1).firstCursorPosition().block().text().isEmpty());
2367 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell"));
2368 QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("Third Cell"));
2369}
2370
2371void tst_QTextDocumentFragment::nonZeroMarginOnImport()
2372{
2373 // specify bgcolor so that the html import creates a root frame format
2374 setHtml("<html><body bgcolor=\"#00ff00\"><b>Hello World</b></body></html>");
2375 QVERIFY(doc->rootFrame()->frameFormat().margin() > 0.0);
2376}
2377
2378void tst_QTextDocumentFragment::html_charFormatPropertiesUnset()
2379{
2380 setHtml("Hello World");
2381 QVERIFY(doc->begin().begin().fragment().charFormat().properties().isEmpty());
2382}
2383
2384void tst_QTextDocumentFragment::html_headings()
2385{
2386 setHtml("<h1>foo</h1>bar");
2387 QCOMPARE(doc->blockCount(), 2);
2388}
2389
2390void tst_QTextDocumentFragment::html_quotedFontFamily_data()
2391{
2392 QTest::addColumn<QString>(name: "html");
2393 QTest::addColumn<QString>(name: "fontFamily");
2394 QTest::addColumn<QStringList>(name: "fontFamilies");
2395
2396 const QString fooFamily = QLatin1String("Foo Bar");
2397 const QString weirdFamily = QLatin1String("'Weird, & font '' name',");
2398
2399 QTest::newRow(dataTag: "data1") << QString("<div style=\"font-family: 'Foo Bar';\">Test</div>")
2400 << fooFamily << QStringList(fooFamily);
2401 QTest::newRow(dataTag: "data2") << QString("<div style='font-family: \"Foo Bar\";'>Test</div>")
2402 << QString("Foo Bar") << QStringList("Foo Bar");
2403 QTest::newRow(dataTag: "data3") << QString("<div style='font-family: Foo\n Bar;'>Test</div>")
2404 << fooFamily << QStringList(fooFamily);
2405 QTest::newRow(dataTag: "data4") << QString("<div style='font-family: Foo\n Bar, serif, \"bar foo\";'>Test"
2406 "</div>")
2407 << fooFamily << (QStringList() << "Foo Bar" << "serif" << "bar foo");
2408 QTest::newRow(dataTag: "data5") << QString("<div style='font-family: \"\\'Weird, & font \\'\\' name\\',"
2409 "\";'>Test</div>")
2410 << weirdFamily << QStringList(weirdFamily);
2411 QTest::newRow(dataTag: "data6") << QString("<div style='font-family: \"\\'Weird, & font \\'\\' name\\',"
2412 "\";'>Test</div>")
2413 << weirdFamily << QStringList(weirdFamily);
2414 QTest::newRow(dataTag: "data7") << QString("<div style='font-family: \"\\'Weird, & font \\'\\' name\\',\", "
2415 "serif, \"bar foo\";'>Test</div>")
2416 << weirdFamily
2417 << (QStringList() << weirdFamily << "serif" << "bar foo");
2418}
2419
2420void tst_QTextDocumentFragment::html_quotedFontFamily()
2421{
2422 QFETCH(QString, html);
2423 QFETCH(QString, fontFamily);
2424 QFETCH(QStringList, fontFamilies);
2425
2426 setHtml(html);
2427 QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), fontFamily);
2428 QCOMPARE(doc->begin().begin().fragment().charFormat().font().families(), fontFamilies);
2429}
2430
2431void tst_QTextDocumentFragment::defaultFont()
2432{
2433 QFont f;
2434 f.setFamily("Courier New");
2435 f.setBold(true);
2436 f.setItalic(true);
2437 f.setStrikeOut(true); // set here but deliberately ignored for the html export
2438 f.setPointSize(100);
2439 doc->setDefaultFont(f);
2440 doc->setPlainText("Hello World");
2441 const QString html = doc->toHtml();
2442 QLatin1String str("<body style=\" font-family:'Courier New'; font-size:100pt; font-weight:600; font-style:italic;\">");
2443 QVERIFY(html.contains(str));
2444}
2445
2446void tst_QTextDocumentFragment::html_spanBackgroundColor()
2447{
2448 setHtml("<span style=\"background-color: blue\">Foo</span>");
2449 QVERIFY(doc->begin().begin().fragment().charFormat().background().color() == QColor(Qt::blue));
2450}
2451
2452void tst_QTextDocumentFragment::html_brokenTitle_data()
2453{
2454 QTest::addColumn<QString>(name: "html");
2455 QTest::addColumn<QString>(name: "expectedBody");
2456 QTest::addColumn<QString>(name: "expectedTitle");
2457
2458 QTest::newRow(dataTag: "brokentitle") << QString("<html><head><title>Foo<b>bar</b></title></head><body>Blah</body></html>")
2459 << QString("Blah") << QString("Foo");
2460 QTest::newRow(dataTag: "brokentitle2") << QString("<html><head><title>Foo<font color=red>i</font>t<font color=red>i</font>Blub</title></head><body>Blah</body></html>")
2461 << QString("Blah") << QString("Foo");
2462 QTest::newRow(dataTag: "entities") << QString("<html><head><title>Foo&lt;bar</title></head><body>Blah</body></html>")
2463 << QString("Blah") << QString("Foo<bar");
2464 QTest::newRow(dataTag: "unclosedtitle") << QString("<html><head><title>Foo</head><body>Blah</body></html>")
2465 << QString("Blah") << QString("Foo");
2466}
2467
2468void tst_QTextDocumentFragment::html_brokenTitle()
2469{
2470 QFETCH(QString, html);
2471 QFETCH(QString, expectedBody);
2472 QFETCH(QString, expectedTitle);
2473 doc->setHtml(html);
2474 QCOMPARE(doc->toPlainText(), expectedBody);
2475 QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), expectedTitle);
2476}
2477
2478void tst_QTextDocumentFragment::html_blockVsInline()
2479{
2480 {
2481 setHtml("<html><body><div><b>Foo<div>Bar");
2482 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2483 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2484 }
2485 {
2486 setHtml("<html><body><p><b>Foo<p>Bar");
2487 QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold);
2488 QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold);
2489 }
2490 {
2491 setHtml("<html><body><b><center>Foo</center></b>");
2492 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2493 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2494 }
2495 {
2496 setHtml("<html><body><b><p>Foo");
2497 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2498 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2499 }
2500 {
2501 setHtml("<html><body><b><p>Foo<p>Bar");
2502 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2503 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2504 }
2505 {
2506 setHtml("<div><b>Foo<div>Bar");
2507 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2508 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2509 }
2510 {
2511 setHtml("<p><b>Foo<p>Bar");
2512 QVERIFY(cursor.charFormat().fontWeight() != QFont::Bold);
2513 QVERIFY(cursor.blockCharFormat().fontWeight() != QFont::Bold);
2514 }
2515 {
2516 setHtml("<b><center>Foo</center></b>");
2517 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2518 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2519 }
2520 {
2521 setHtml("<b><p>Foo");
2522 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2523 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2524 }
2525 {
2526 setHtml("<b><p>Foo<p>Bar");
2527 QCOMPARE(cursor.charFormat().fontWeight(), int(QFont::Bold));
2528 QCOMPARE(cursor.blockCharFormat().fontWeight(), int(QFont::Bold));
2529 }
2530}
2531
2532void tst_QTextDocumentFragment::html_tbody()
2533{
2534 setHtml("<table><thead><tr><td>First Cell</td></tr></thead><tbody><tr><td>Second Cell</td></tr></tbody></table>");
2535 cursor.movePosition(op: QTextCursor::Start);
2536 cursor.movePosition(op: QTextCursor::NextBlock);
2537 QTextTable *table = cursor.currentTable();
2538 QVERIFY(table);
2539 QCOMPARE(table->columns(), 1);
2540 QCOMPARE(table->rows(), 2);
2541 QCOMPARE(table->format().headerRowCount(), 1);
2542 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
2543 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell"));
2544}
2545
2546void tst_QTextDocumentFragment::html_nestedTables()
2547{
2548 setHtml("<table>"
2549 " <tr><td>"
2550 ""
2551 " <table>"
2552 " <tr><td>Hello</td></tr>"
2553 " </table>"
2554 ""
2555 " <table>"
2556 " <tr><td>World</td></tr>"
2557 " </table>"
2558 ""
2559 " </td></tr>"
2560 "</table>"
2561 );
2562
2563 cursor.movePosition(op: QTextCursor::Start);
2564 cursor.movePosition(op: QTextCursor::NextBlock);
2565 QTextTable *table = cursor.currentTable();
2566 QVERIFY(table);
2567 QCOMPARE(table->rows(), 1);
2568 QCOMPARE(table->columns(), 1);
2569
2570 cursor = table->cellAt(row: 0, col: 0).firstCursorPosition();
2571 cursor.movePosition(op: QTextCursor::NextBlock);
2572
2573 QTextTable *firstNestedTable = cursor.currentTable();
2574 QVERIFY(firstNestedTable);
2575 QCOMPARE(firstNestedTable->parentFrame(), table);
2576 QCOMPARE(firstNestedTable->rows(), 1);
2577 QCOMPARE(firstNestedTable->columns(), 1);
2578 QCOMPARE(firstNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hello"));
2579
2580 while (cursor.currentTable() == firstNestedTable
2581 && cursor.movePosition(op: QTextCursor::NextBlock))
2582 ;
2583
2584 QVERIFY(!cursor.isNull());
2585 QCOMPARE(cursor.currentTable(), table);
2586
2587 cursor.movePosition(op: QTextCursor::NextBlock);
2588
2589 QTextTable *secondNestedTable = cursor.currentTable();
2590 QVERIFY(secondNestedTable);
2591 QCOMPARE(secondNestedTable->parentFrame(), table);
2592 QCOMPARE(secondNestedTable->rows(), 1);
2593 QCOMPARE(secondNestedTable->columns(), 1);
2594 QCOMPARE(secondNestedTable->cellAt(0, 0).firstCursorPosition().block().text(), QString("World"));
2595}
2596
2597void tst_QTextDocumentFragment::html_rowSpans()
2598{
2599 setHtml(""
2600 "<table border=\"1\" width=\"100%\">"
2601 " <tr>"
2602 " <td rowspan=\"2\">blah</td>"
2603 " <td rowspan=\"2\">foo</td>"
2604 " </tr>"
2605 " <tr></tr>"
2606 " <tr>"
2607 " <td rowspan=\"2\">blubb</td>"
2608 " <td rowspan=\"2\">baz</td>"
2609 " </tr>"
2610 " <tr></tr>"
2611 "</table>");
2612
2613 cursor.movePosition(op: QTextCursor::Start);
2614 cursor.movePosition(op: QTextCursor::NextBlock);
2615 QTextTable *table = cursor.currentTable();
2616 QVERIFY(table);
2617 QCOMPARE(table->rows(), 4);
2618 QCOMPARE(table->columns(), 2);
2619
2620 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("blah"));
2621 QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("foo"));
2622
2623 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("blah"));
2624 QCOMPARE(table->cellAt(1, 1).firstCursorPosition().block().text(), QString("foo"));
2625
2626 QCOMPARE(table->cellAt(2, 0).firstCursorPosition().block().text(), QString("blubb"));
2627 QCOMPARE(table->cellAt(2, 1).firstCursorPosition().block().text(), QString("baz"));
2628
2629 QCOMPARE(table->cellAt(3, 0).firstCursorPosition().block().text(), QString("blubb"));
2630 QCOMPARE(table->cellAt(3, 1).firstCursorPosition().block().text(), QString("baz"));
2631}
2632
2633void tst_QTextDocumentFragment::html_rowSpans2()
2634{
2635 setHtml(""
2636 "<html><body>"
2637 "<table border=\"1\">"
2638 "<tr>"
2639 "<td>Row 1 col 1</td>"
2640 "</tr>"
2641 "<tr>"
2642 "<td rowspan=\"3\">Row 2 col 1, rowspan 3</td>"
2643 "<td>Row 2 col 2</td>"
2644 "</tr>"
2645 "<tr>"
2646 "<td rowspan=\"2\">Row 3 col 2, rowspan 2</td>"
2647 "</tr>"
2648 "<tr>"
2649 "</tr>"
2650 "</table>"
2651 "</body></html>");
2652
2653 cursor.movePosition(op: QTextCursor::Start);
2654 cursor.movePosition(op: QTextCursor::NextBlock);
2655 QTextTable *table = cursor.currentTable();
2656 QVERIFY(table);
2657 QCOMPARE(table->rows(), 4);
2658 QCOMPARE(table->columns(), 2);
2659 QCOMPARE(table->cellAt(0, 1).rowSpan(), 1);
2660 QCOMPARE(table->cellAt(1, 0).rowSpan(), 3);
2661 QCOMPARE(table->cellAt(2, 1).rowSpan(), 2);
2662}
2663
2664void tst_QTextDocumentFragment::html_implicitParagraphs()
2665{
2666 setHtml("<p>foo</p>bar");
2667 QCOMPARE(doc->blockCount(), 2);
2668}
2669
2670void tst_QTextDocumentFragment::html_missingCloseTag()
2671{
2672 setHtml("<font color=\"red\"><span style=\"color:blue\">blue</span></span>&nbsp;red</font>");
2673 cursor.movePosition(op: QTextCursor::Start);
2674 cursor.movePosition(op: QTextCursor::NextCharacter);
2675 QVERIFY(cursor.charFormat().foreground().color() == Qt::blue);
2676 cursor.movePosition(op: QTextCursor::NextWord);
2677 cursor.movePosition(op: QTextCursor::NextCharacter);
2678 QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
2679}
2680
2681void tst_QTextDocumentFragment::html_anchorColor()
2682{
2683 setHtml("<span style=\"color: red;\">Red</span>");
2684 cursor.movePosition(op: QTextCursor::Start);
2685 cursor.movePosition(op: QTextCursor::NextCharacter);
2686 QVERIFY(cursor.charFormat().foreground().color() == Qt::red);
2687
2688 setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\">Blue</a></span>");
2689 cursor.movePosition(op: QTextCursor::Start);
2690 cursor.movePosition(op: QTextCursor::NextCharacter);
2691 QCOMPARE(cursor.charFormat().foreground().color(), QGuiApplication::palette().link().color());
2692
2693 setHtml("<span style=\"color: red;\"><a href=\"http://www.kde.org/\" style=\"color: yellow;\">Green</a></span>");
2694 cursor.movePosition(op: QTextCursor::Start);
2695 cursor.movePosition(op: QTextCursor::NextCharacter);
2696 QVERIFY(cursor.charFormat().foreground().color() == Qt::yellow);
2697}
2698
2699void tst_QTextDocumentFragment::html_lastParagraphClosing()
2700{
2701 setHtml("<p>Foo<b>Bar</b>Baz");
2702 QCOMPARE(doc->blockCount(), 1);
2703}
2704
2705void tst_QTextDocumentFragment::html_tableHeaderBodyFootParent()
2706{
2707 // don't get confused by strange tags, keep tbody/thead/tfoot children of
2708 // the table tag
2709 setHtml("<table><col><col><col><tbody><tr><td>Hey</td></tr></tbody></table>");
2710
2711 cursor.movePosition(op: QTextCursor::Start);
2712 cursor.movePosition(op: QTextCursor::NextBlock);
2713 QTextTable *table = cursor.currentTable();
2714 QVERIFY(table);
2715 QCOMPARE(table->columns(), 1);
2716 QCOMPARE(table->rows(), 1);
2717 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
2718
2719 setHtml("<table><col><col><col><thead><tr><td>Hey</td></tr></thead></table>");
2720
2721 cursor.movePosition(op: QTextCursor::Start);
2722 cursor.movePosition(op: QTextCursor::NextBlock);
2723 table = cursor.currentTable();
2724 QVERIFY(table);
2725 QCOMPARE(table->columns(), 1);
2726 QCOMPARE(table->rows(), 1);
2727 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
2728
2729 setHtml("<table><col><col><col><tfoot><tr><td>Hey</td></tr></tfoot></table>");
2730
2731 cursor.movePosition(op: QTextCursor::Start);
2732 cursor.movePosition(op: QTextCursor::NextBlock);
2733 table = cursor.currentTable();
2734 QVERIFY(table);
2735 QCOMPARE(table->columns(), 1);
2736 QCOMPARE(table->rows(), 1);
2737 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("Hey"));
2738}
2739
2740void tst_QTextDocumentFragment::html_columnWidths()
2741{
2742 setHtml("<table>"
2743 " <tr>"
2744 " <td colspan=\"2\">Foo</td>"
2745 " </tr>"
2746 " <tr>"
2747 " <td>Bar</td>"
2748 " <td width=\"1%\">Baz</td>"
2749 " </tr>"
2750 "</table>");
2751
2752 cursor.movePosition(op: QTextCursor::Start);
2753 cursor.movePosition(op: QTextCursor::NextBlock);
2754 QTextTable *table = cursor.currentTable();
2755 QVERIFY(table);
2756 QCOMPARE(table->columns(), 2);
2757 QCOMPARE(table->rows(), 2);
2758 QTextTableFormat fmt = table->format();
2759
2760 const QVector<QTextLength> columnWidths = fmt.columnWidthConstraints();
2761 QCOMPARE(columnWidths.count(), 2);
2762 QCOMPARE(columnWidths.at(0).type(), QTextLength::VariableLength);
2763 QCOMPARE(columnWidths.at(1).type(), QTextLength::PercentageLength);
2764 QCOMPARE(columnWidths.at(1).rawValue(), qreal(1));
2765}
2766
2767void tst_QTextDocumentFragment::css_fontWeight()
2768{
2769 setHtml("<p style=\"font-weight:bold\">blah</p>");
2770 QCOMPARE(doc->begin().charFormat().fontWeight(), int(QFont::Bold));
2771 setHtml("<p style=\"font-weight:600\">blah</p>");
2772 QCOMPARE(doc->begin().charFormat().fontWeight(), int(QFont::Bold));
2773
2774}
2775
2776void tst_QTextDocumentFragment::css_float()
2777{
2778 setHtml("<img src=\"foo\" style=\"float: right\">");
2779 QTextCharFormat fmt = doc->begin().begin().fragment().charFormat();
2780 QVERIFY(fmt.isImageFormat());
2781 QTextObject *o = doc->objectForFormat(fmt);
2782 QVERIFY(o);
2783 QTextFormat f = o->format();
2784 QVERIFY(f.isFrameFormat());
2785 QCOMPARE(f.toFrameFormat().position(), QTextFrameFormat::FloatRight);
2786
2787 setHtml("<img src=\"foo\" align=right>");
2788 fmt = doc->begin().begin().fragment().charFormat();
2789 QVERIFY(fmt.isImageFormat());
2790 o = doc->objectForFormat(fmt);
2791 QVERIFY(o);
2792 f = o->format();
2793 QVERIFY(f.isFrameFormat());
2794 QCOMPARE(f.toFrameFormat().position(), QTextFrameFormat::FloatRight);
2795
2796 setHtml("<img src=\"foo\" align=left>");
2797 fmt = doc->begin().begin().fragment().charFormat();
2798 QVERIFY(fmt.isImageFormat());
2799 o = doc->objectForFormat(fmt);
2800 QVERIFY(o);
2801 f = o->format();
2802 QVERIFY(f.isFrameFormat());
2803 QCOMPARE(f.toFrameFormat().position(), QTextFrameFormat::FloatLeft);
2804}
2805
2806void tst_QTextDocumentFragment::css_textIndent()
2807{
2808 setHtml("<p style=\"text-indent: 42px\">foo</p>");
2809 QTextBlockFormat fmt = doc->begin().blockFormat();
2810 QCOMPARE(fmt.textIndent(), qreal(42));
2811}
2812
2813void tst_QTextDocumentFragment::css_inline()
2814{
2815 setHtml(""
2816 "<style>"
2817 " p { background-color: green;}"
2818 "</style>"
2819 "<p>test</p>"
2820 );
2821 QTextBlockFormat fmt = doc->begin().blockFormat();
2822 QCOMPARE(fmt.background().color(), QColor("green"));
2823}
2824
2825void tst_QTextDocumentFragment::css_external()
2826{
2827 doc->addResource(type: QTextDocument::StyleSheetResource, name: QUrl("test.css"), resource: QString("p { background-color: green; }"));
2828 doc->setHtml(""
2829 "<link href=\"test.css\" type=\"text/css\" />"
2830 "<p>test</p>"
2831 );
2832 QTextBlockFormat fmt = doc->begin().blockFormat();
2833 QCOMPARE(fmt.background().color(), QColor("green"));
2834}
2835
2836void tst_QTextDocumentFragment::css_import()
2837{
2838 const QColor green("green");
2839 doc->addResource(type: QTextDocument::StyleSheetResource, name: QUrl("test.css"), resource: QString("@import \"other.css\";"));
2840 doc->addResource(type: QTextDocument::StyleSheetResource, name: QUrl("other.css"), resource: QString("@import url(\"other2.css\");"));
2841 doc->addResource(type: QTextDocument::StyleSheetResource, name: QUrl("other2.css"), resource: QString("p { background-color: green; }"));
2842 doc->setHtml(""
2843 "<link href=\"test.css\" type=\"text/css\" />"
2844 "<p>test</p>"
2845 );
2846 QTextBlockFormat fmt = doc->begin().blockFormat();
2847 QCOMPARE(fmt.background().color(), green);
2848
2849 doc->setHtml(""
2850 "<style>@import \"test.css\" screen;</style>"
2851 "<p>test</p>"
2852 );
2853 fmt = doc->begin().blockFormat();
2854 QCOMPARE(fmt.background().color(), green);
2855}
2856
2857void tst_QTextDocumentFragment::css_selectors_data()
2858{
2859 QTest::addColumn<bool>(name: "match");
2860 QTest::addColumn<QString>(name: "selector");
2861 QTest::addColumn<QString>(name: "attributes");
2862
2863 QTest::newRow(dataTag: "plain") << true << QString() << QString();
2864
2865 QTest::newRow(dataTag: "class") << true << QString(".foo") << QString("class=foo");
2866 QTest::newRow(dataTag: "notclass") << false << QString(".foo") << QString("class=bar");
2867
2868 QTest::newRow(dataTag: "attrset") << true << QString("[justset]") << QString("justset");
2869 QTest::newRow(dataTag: "notattrset") << false << QString("[justset]") << QString("otherattribute");
2870
2871 QTest::newRow(dataTag: "attrmatch") << true << QString("[foo=bar]") << QString("foo=bar");
2872 QTest::newRow(dataTag: "noattrmatch") << false << QString("[foo=bar]") << QString("foo=xyz");
2873
2874 QTest::newRow(dataTag: "contains") << true << QString("[foo~=bar]") << QString("foo=\"baz bleh bar\"");
2875 QTest::newRow(dataTag: "notcontains") << false << QString("[foo~=bar]") << QString("foo=\"test\"");
2876
2877 QTest::newRow(dataTag: "beingswith") << true << QString("[foo|=bar]") << QString("foo=\"bar-bleh\"");
2878 QTest::newRow(dataTag: "notbeingswith") << false << QString("[foo|=bar]") << QString("foo=\"bleh-bar\"");
2879
2880 QTest::newRow(dataTag: "attr2") << true << QString("[bar=foo]") << QString("bleh=bar bar=foo");
2881}
2882
2883void tst_QTextDocumentFragment::css_selectors()
2884{
2885 QFETCH(bool, match);
2886 QFETCH(QString, selector);
2887 QFETCH(QString, attributes);
2888
2889 QString html = QString(""
2890 "<style>"
2891 " p { background-color: green }"
2892 " p%1 { background-color: red }"
2893 "</style>"
2894 "<p %2>test</p>"
2895 ).arg(a: selector).arg(a: attributes);
2896 setHtml(html);
2897
2898 QTextBlockFormat fmt = doc->begin().blockFormat();
2899 if (match)
2900 QCOMPARE(fmt.background().color(), QColor("red"));
2901 else
2902 QCOMPARE(fmt.background().color(), QColor("green"));
2903}
2904
2905void tst_QTextDocumentFragment::css_nodeNameCaseInsensitivity()
2906{
2907 setHtml("<style>"
2908 "P { background-color: green }"
2909 "</style>"
2910 "<p>test</p>");
2911 QTextBlockFormat fmt = doc->begin().blockFormat();
2912 QCOMPARE(fmt.background().color(), QColor("green"));
2913}
2914
2915void tst_QTextDocumentFragment::css_textUnderlineStyle_data()
2916{
2917 QTest::addColumn<QString>(name: "styleName");
2918 QTest::addColumn<int>(name: "expectedStyle");
2919
2920 QTest::newRow(dataTag: "none") << QString("none") << int(QTextCharFormat::NoUnderline);
2921 QTest::newRow(dataTag: "solid") << QString("solid") << int(QTextCharFormat::SingleUnderline);
2922 QTest::newRow(dataTag: "dash") << QString("dashed") << int(QTextCharFormat::DashUnderline);
2923 QTest::newRow(dataTag: "dot") << QString("dotted") << int(QTextCharFormat::DotLine);
2924 QTest::newRow(dataTag: "dashdot") << QString("dot-dash") << int(QTextCharFormat::DashDotLine);
2925 QTest::newRow(dataTag: "dashdotdot") << QString("dot-dot-dash") << int(QTextCharFormat::DashDotDotLine);
2926 QTest::newRow(dataTag: "wave") << QString("wave") << int(QTextCharFormat::WaveUnderline);
2927}
2928
2929void tst_QTextDocumentFragment::css_textUnderlineStyle()
2930{
2931 QFETCH(QString, styleName);
2932 QFETCH(int, expectedStyle);
2933
2934 QString html = QString::fromLatin1(str: "<span style=\"text-underline-style: %1\">Blah</span>").arg(a: styleName);
2935 doc->setHtml(html);
2936
2937 QTextFragment fragment = doc->begin().begin().fragment();
2938 QVERIFY(fragment.isValid());
2939 QCOMPARE(int(fragment.charFormat().underlineStyle()), expectedStyle);
2940}
2941
2942void tst_QTextDocumentFragment::css_textUnderlineStyleAndDecoration()
2943{
2944 doc->setHtml("<span style=\"text-decoration: overline; text-underline-style: solid\">Test</span>");
2945
2946 QTextFragment fragment = doc->begin().begin().fragment();
2947 QVERIFY(fragment.isValid());
2948 QCOMPARE(fragment.charFormat().underlineStyle(), QTextCharFormat::SingleUnderline);
2949 QVERIFY(fragment.charFormat().fontOverline());
2950
2951 doc->setHtml("<span style=\"text-underline-style: solid; text-decoration: overline\">Test</span>");
2952
2953 fragment = doc->begin().begin().fragment();
2954 QVERIFY(fragment.isValid());
2955 QCOMPARE(fragment.charFormat().underlineStyle(), QTextCharFormat::SingleUnderline);
2956 QVERIFY(fragment.charFormat().fontOverline());
2957}
2958
2959void tst_QTextDocumentFragment::css_listStyleType()
2960{
2961 doc->setHtml("<ol style=\"list-style-type: disc\"><li>Blah</li></ol>");
2962 cursor.movePosition(op: QTextCursor::End);
2963 QVERIFY(cursor.currentList());
2964 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListDisc);
2965
2966 doc->setHtml("<ul style=\"list-style-type: square\"><li>Blah</li></ul>");
2967 cursor.movePosition(op: QTextCursor::End);
2968 QVERIFY(cursor.currentList());
2969 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListSquare);
2970
2971 doc->setHtml("<ul style=\"list-style-type: circle\"><li>Blah</li></ul>");
2972 cursor.movePosition(op: QTextCursor::End);
2973 QVERIFY(cursor.currentList());
2974 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListCircle);
2975
2976 doc->setHtml("<ul style=\"list-style-type: decimal\"><li>Blah</li></ul>");
2977 cursor.movePosition(op: QTextCursor::End);
2978 QVERIFY(cursor.currentList());
2979 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListDecimal);
2980
2981 doc->setHtml("<ul style=\"list-style-type: lower-alpha\"><li>Blah</li></ul>");
2982 cursor.movePosition(op: QTextCursor::End);
2983 QVERIFY(cursor.currentList());
2984 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListLowerAlpha);
2985
2986 doc->setHtml("<ul style=\"list-style-type: upper-alpha\"><li>Blah</li></ul>");
2987 cursor.movePosition(op: QTextCursor::End);
2988 QVERIFY(cursor.currentList());
2989 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListUpperAlpha);
2990
2991 doc->setHtml("<ul style=\"list-style-type: upper-roman\"><li>Blah</li></ul>");
2992 cursor.movePosition(op: QTextCursor::End);
2993 QVERIFY(cursor.currentList());
2994 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListUpperRoman);
2995
2996 doc->setHtml("<ul style=\"list-style-type: lower-roman\"><li>Blah</li></ul>");
2997 cursor.movePosition(op: QTextCursor::End);
2998 QVERIFY(cursor.currentList());
2999 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListLowerRoman);
3000
3001 // ignore the unsupported list-style-position inside the list-style shorthand property
3002 doc->setHtml("<ul style=\"list-style: outside decimal\"><li>Blah</li></ul>");
3003 cursor.movePosition(op: QTextCursor::End);
3004 QVERIFY(cursor.currentList());
3005 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListDecimal);
3006}
3007
3008void tst_QTextDocumentFragment::css_linkPseudo()
3009{
3010 doc->setHtml("<a href=\"foobar\">Blah</a>");
3011 QVERIFY(doc->begin().begin().fragment().charFormat().fontUnderline());
3012
3013 doc->setHtml("<style>a { text-decoration: none; }</style><a href=\"foobar\">Blah</a>");
3014 QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline());
3015
3016 doc->setHtml("<style>a:link { text-decoration: none; }</style><a href=\"foobar\">Blah</a>");
3017 QVERIFY(!doc->begin().begin().fragment().charFormat().fontUnderline());
3018}
3019
3020void tst_QTextDocumentFragment::css_pageBreaks()
3021{
3022 doc->setHtml("<p>Foo</p>");
3023 QCOMPARE(doc->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_Auto);
3024
3025 doc->setHtml("<p style=\" page-break-before:always;\">Foo</p>");
3026 QCOMPARE(doc->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysBefore);
3027
3028 doc->setHtml("<p style=\" page-break-after:always;\">Foo</p>");
3029 QCOMPARE(doc->begin().blockFormat().pageBreakPolicy(), QTextFormat::PageBreak_AlwaysAfter);
3030
3031 doc->setHtml("<p style=\" page-break-before:always; page-break-after:always;\">Foo</p>");
3032 QVERIFY(doc->begin().blockFormat().pageBreakPolicy() == (QTextFormat::PageBreak_AlwaysAfter | QTextFormat::PageBreak_AlwaysBefore));
3033}
3034
3035void tst_QTextDocumentFragment::universalSelectors_data()
3036{
3037 QTest::addColumn<bool>(name: "match");
3038 QTest::addColumn<QString>(name: "selector");
3039 QTest::addColumn<QString>(name: "attributes");
3040
3041 QTest::newRow(dataTag: "1") << true << QString("*") << QString();
3042 QTest::newRow(dataTag: "2") << false << QString() << QString(); // invalid totally empty selector
3043
3044 QTest::newRow(dataTag: "3") << false << QString("*[foo=bar]") << QString("foo=bleh");
3045 QTest::newRow(dataTag: "4") << true << QString("*[foo=bar]") << QString("foo=bar");
3046
3047 QTest::newRow(dataTag: "5") << false << QString("[foo=bar]") << QString("foo=bleh");
3048 QTest::newRow(dataTag: "6") << true << QString("[foo=bar]") << QString("foo=bar");
3049
3050 QTest::newRow(dataTag: "7") << true << QString(".charfmt1") << QString("class=charfmt1");
3051}
3052
3053void tst_QTextDocumentFragment::universalSelectors()
3054{
3055 QFETCH(bool, match);
3056 QFETCH(QString, selector);
3057 QFETCH(QString, attributes);
3058
3059 QString html = QString(""
3060 "<style>"
3061 "%1 { background-color: green }"
3062 "</style>"
3063 "<p %2>test</p>"
3064 ).arg(a: selector).arg(a: attributes);
3065
3066 setHtml(html);
3067
3068 QTextBlockFormat fmt = doc->begin().blockFormat();
3069 if (match)
3070 QCOMPARE(fmt.background().color(), QColor("green"));
3071 else
3072 QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
3073}
3074
3075void tst_QTextDocumentFragment::screenMedia()
3076{
3077 const QColor green("green");
3078 setHtml("<style>"
3079 "@media screen {"
3080 "p { background-color: green }"
3081 "}"
3082 "</style>"
3083 "<p>test</p>"
3084 "");
3085 QTextBlockFormat fmt = doc->begin().blockFormat();
3086 QCOMPARE(fmt.background().color(), green);
3087
3088 setHtml("<style>"
3089 "@media foobar {"
3090 "p { background-color: green }"
3091 "}"
3092 "</style>"
3093 "<p>test</p>"
3094 "");
3095 fmt = doc->begin().blockFormat();
3096 QVERIFY(fmt.background().color() != green);
3097
3098 setHtml("<style>"
3099 "@media sCrEeN {"
3100 "p { background-color: green }"
3101 "}"
3102 "</style>"
3103 "<p>test</p>"
3104 "");
3105 fmt = doc->begin().blockFormat();
3106 QCOMPARE(fmt.background().color(), green);
3107}
3108
3109void tst_QTextDocumentFragment::htmlResourceLoading()
3110{
3111 const QString html("<link href=\"test.css\" type=\"text/css\" />"
3112 "<p>test</p>");
3113
3114 QTextDocument tmp;
3115 tmp.addResource(type: QTextDocument::StyleSheetResource, name: QUrl("test.css"), resource: QString("p { background-color: green; }"));
3116 QTextDocumentFragment frag = QTextDocumentFragment::fromHtml(html, resourceProvider: &tmp);
3117 doc->clear();
3118 QTextCursor(doc).insertFragment(fragment: frag);
3119 QTextBlockFormat fmt = doc->begin().blockFormat();
3120 QCOMPARE(fmt.background().color(), QColor("green"));
3121}
3122
3123void tst_QTextDocumentFragment::someCaseInsensitiveAttributeValues()
3124{
3125 const char html1[] = "<ul type=sQUarE><li>Blah</li></ul>";
3126 setHtml(QString::fromLatin1(str: html1));
3127 cursor.movePosition(op: QTextCursor::End);
3128 QVERIFY(cursor.currentList());
3129 QCOMPARE(cursor.currentList()->format().style(), QTextListFormat::ListSquare);
3130
3131 const char html2[] = "<div align=ceNTeR><b>Hello World";
3132 setHtml(html2);
3133
3134 QCOMPARE(doc->begin().blockFormat().alignment(), Qt::AlignHCenter);
3135
3136 const char html3[] = "<p dir=rTL><b>Hello World";
3137 setHtml(html3);
3138
3139 QCOMPARE(doc->begin().blockFormat().layoutDirection(), Qt::RightToLeft);
3140}
3141
3142class TestDocument : public QTextDocument
3143{
3144public:
3145 inline TestDocument() {}
3146
3147 QPixmap testPixmap;
3148
3149 virtual QVariant loadResource(int type, const QUrl &name) {
3150 if (name.toString() == QLatin1String("testPixmap")) {
3151 return testPixmap;
3152 }
3153 return QTextDocument::loadResource(type, name);
3154 }
3155};
3156
3157void tst_QTextDocumentFragment::backgroundImage()
3158{
3159 TestDocument doc;
3160 doc.testPixmap = QPixmap(100, 100);
3161 doc.testPixmap.fill(fillColor: Qt::blue);
3162 doc.setHtml("<p style=\"background-image: url(testPixmap)\">Hello</p>");
3163 QBrush bg = doc.begin().blockFormat().background();
3164 QCOMPARE(bg.style(), Qt::TexturePattern);
3165 QCOMPARE(bg.texture().cacheKey(), doc.testPixmap.cacheKey());
3166}
3167
3168void tst_QTextDocumentFragment::dontMergePreAndNonPre()
3169{
3170 doc->setHtml("<pre>Pre text</pre>Text that should be wrapped");
3171 QCOMPARE(doc->blockCount(), 2);
3172 QCOMPARE(doc->begin().text(), QString("Pre text"));
3173 QCOMPARE(doc->begin().next().text(), QString("Text that should be wrapped"));
3174}
3175
3176void tst_QTextDocumentFragment::leftMarginInsideHtml()
3177{
3178 doc->setHtml("<html><dl><dd>Blah");
3179 QCOMPARE(doc->blockCount(), 1);
3180 QVERIFY(doc->begin().blockFormat().leftMargin() > 0);
3181}
3182
3183void tst_QTextDocumentFragment::html_margins()
3184{
3185 doc->setHtml("<p style=\"margin-left: 42px\">Test");
3186 QCOMPARE(doc->blockCount(), 1);
3187 QCOMPARE(doc->begin().blockFormat().topMargin(), 12.);
3188 QCOMPARE(doc->begin().blockFormat().bottomMargin(), 12.);
3189 QCOMPARE(doc->begin().blockFormat().leftMargin(), 42.);
3190}
3191
3192void tst_QTextDocumentFragment::newlineInsidePreShouldBecomeNewParagraph()
3193{
3194 // rationale: we used to map newlines inside <pre> to QChar::LineSeparator, but
3195 // if you display a lot of text inside pre it all ended up inside one single paragraph,
3196 // which doesn't scale very well with our text engine. Paragraphs spanning thousands of
3197 // lines are not a common use-case otherwise.
3198
3199 doc->setHtml("<pre>Foo\nBar</pre>");
3200 QCOMPARE(doc->blockCount(), 2);
3201 QTextBlock block = doc->begin();
3202 QCOMPARE(block.blockFormat().topMargin(), qreal(12));
3203 QVERIFY(qIsNull(block.blockFormat().bottomMargin()));
3204
3205 block = block.next();
3206
3207 QVERIFY(qIsNull(block.blockFormat().topMargin()));
3208 QCOMPARE(block.blockFormat().bottomMargin(), qreal(12));
3209
3210 doc->setHtml("<pre style=\"margin-top: 32px; margin-bottom: 45px; margin-left: 50px\">Foo\nBar</pre>");
3211 QCOMPARE(doc->blockCount(), 2);
3212 block = doc->begin();
3213 QCOMPARE(block.blockFormat().topMargin(), qreal(32));
3214 QVERIFY(qIsNull(block.blockFormat().bottomMargin()));
3215 QCOMPARE(block.blockFormat().leftMargin(), qreal(50));
3216
3217 block = block.next();
3218
3219 QVERIFY(qIsNull(block.blockFormat().topMargin()));
3220 QCOMPARE(block.blockFormat().bottomMargin(), qreal(45));
3221 QCOMPARE(block.blockFormat().leftMargin(), qreal(50));
3222
3223}
3224
3225void tst_QTextDocumentFragment::invalidColspan()
3226{
3227 doc->setHtml("<table><tr rowspan=-1><td colspan=-1>Blah</td></tr></table>");
3228
3229 cursor.movePosition(op: QTextCursor::Start);
3230 cursor.movePosition(op: QTextCursor::NextBlock);
3231 QTextTable *table = cursor.currentTable();
3232 QVERIFY(table);
3233 QCOMPARE(table->columns(), 1);
3234 QCOMPARE(table->rows(), 1);
3235}
3236
3237void tst_QTextDocumentFragment::html_brokenTableWithJustTr()
3238{
3239 doc->setHtml("<tr><td>First Cell</td><tr><td>Second Cell");
3240 cursor.movePosition(op: QTextCursor::Start);
3241 cursor.movePosition(op: QTextCursor::NextBlock);
3242 QTextTable *table = cursor.currentTable();
3243 QVERIFY(table);
3244 QCOMPARE(table->rows(), 2);
3245 QCOMPARE(table->columns(), 1);
3246 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
3247 QCOMPARE(table->cellAt(1, 0).firstCursorPosition().block().text(), QString("Second Cell"));
3248
3249 doc->setHtml(""
3250 "<col width=286 style='mso-width-source:userset;mso-width-alt:10459;width:215pt'>"
3251 "<col width=64 span=3 style='width:48pt'>"
3252 "<tr height=17 style='height:12.75pt'>"
3253 "<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>"
3254 "<td width=64 style='width:48pt'>1b</td>"
3255 "<td width=64 style='width:48pt'>1c</td>"
3256 "<td width=64 style='width:48pt'>1d</td>"
3257 "</tr>"
3258 "<tr height=17 style='height:12.75pt'>"
3259 "<td height=17 style='height:12.75pt'>|2a</td>"
3260 "<td>2b</td>"
3261 "<td>2c</td>"
3262 "<td>2d</td>"
3263 "</tr>");
3264 cursor.movePosition(op: QTextCursor::Start);
3265 cursor.movePosition(op: QTextCursor::NextBlock);
3266 table = cursor.currentTable();
3267 QVERIFY(table);
3268 QCOMPARE(table->rows(), 2);
3269 QCOMPARE(table->columns(), 4);
3270}
3271
3272void tst_QTextDocumentFragment::html_brokenTableWithJustTd()
3273{
3274 doc->setHtml("<td>First Cell</td><td>Second Cell");
3275 cursor.movePosition(op: QTextCursor::Start);
3276 cursor.movePosition(op: QTextCursor::NextBlock);
3277 QTextTable *table = cursor.currentTable();
3278 QVERIFY(table);
3279 QCOMPARE(table->rows(), 1);
3280 QCOMPARE(table->columns(), 2);
3281 QCOMPARE(table->cellAt(0, 0).firstCursorPosition().block().text(), QString("First Cell"));
3282 QCOMPARE(table->cellAt(0, 1).firstCursorPosition().block().text(), QString("Second Cell"));
3283
3284 doc->setHtml("<td height=17 width=286 style='height:12.75pt;width:215pt'>1a</td>"
3285 "<td width=64 style='width:48pt'>1b</td>"
3286 "<td width=64 style='width:48pt'>1c</td>"
3287 "<td width=64 style='width:48pt'>1d</td>");
3288 cursor.movePosition(op: QTextCursor::Start);
3289 cursor.movePosition(op: QTextCursor::NextBlock);
3290 table = cursor.currentTable();
3291 QVERIFY(table);
3292 QCOMPARE(table->rows(), 1);
3293 QCOMPARE(table->columns(), 4);
3294}
3295
3296void tst_QTextDocumentFragment::html_preNewlineHandling_data()
3297{
3298 QTest::addColumn<QString>(name: "html");
3299 QTest::addColumn<QString>(name: "expectedPlainText");
3300
3301 QTest::newRow(dataTag: "pre1") << QString("Foo<pre>Bar")
3302 << QString("Foo\nBar");
3303 QTest::newRow(dataTag: "pre2") << QString("Foo<pre>\nBar")
3304 << QString("Foo\nBar");
3305 QTest::newRow(dataTag: "pre3") << QString("Foo<pre>\n\nBar")
3306 << QString("Foo\n\nBar");
3307 QTest::newRow(dataTag: "pre4") << QString("<html>Foo<pre>\nBar")
3308 << QString("Foo\nBar");
3309 QTest::newRow(dataTag: "pre5") << QString("<pre>Foo\n</pre>\nBar")
3310 << QString("Foo\nBar");
3311 QTest::newRow(dataTag: "pre6") << QString("<pre>Foo<b>Bar</b>Blah\n</pre>\nMooh")
3312 << QString("FooBarBlah\nMooh");
3313 QTest::newRow(dataTag: "pre7") << QString("<pre>\nPara1\n</pre>\n<pre>\nPara2\n</pre>")
3314 << QString("Para1\nPara2");
3315}
3316
3317void tst_QTextDocumentFragment::html_preNewlineHandling()
3318{
3319 QFETCH(QString, html);
3320
3321 doc->setHtml(html);
3322 QTEST(doc->toPlainText(), "expectedPlainText");
3323}
3324
3325void tst_QTextDocumentFragment::html_br()
3326{
3327 doc->setHtml("Foo<br><br><br>Blah");
3328 QCOMPARE(doc->toPlainText(), QString("Foo\n\n\nBlah"));
3329}
3330
3331void tst_QTextDocumentFragment::html_dl()
3332{
3333 doc->setHtml("<dl><dt>term<dd>data</dl>Text afterwards");
3334 QCOMPARE(doc->toPlainText(), QString("term\ndata\nText afterwards"));
3335}
3336
3337void tst_QTextDocumentFragment::html_tableStrangeNewline()
3338{
3339 doc->setHtml("<table><tr><td>Foo</td></tr>\n</table>");
3340 cursor.movePosition(op: QTextCursor::Start);
3341 cursor.movePosition(op: QTextCursor::NextBlock);
3342 QTextTable *table = cursor.currentTable();
3343 QVERIFY(table);
3344 QCOMPARE(table->rows(), 1);
3345 QCOMPARE(table->columns(), 1);
3346 const QTextTableCell cell = table->cellAt(row: 0, col: 0);
3347 QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
3348 QCOMPARE(cell.firstCursorPosition().block(), cell.lastCursorPosition().block());
3349}
3350
3351void tst_QTextDocumentFragment::html_tableStrangeNewline2()
3352{
3353 doc->setHtml("<table><tr><td>Foo</td></tr><tr>\n<td/></tr></table>");
3354 cursor.movePosition(op: QTextCursor::Start);
3355 cursor.movePosition(op: QTextCursor::NextBlock);
3356 QTextTable *table = cursor.currentTable();
3357 QVERIFY(table);
3358 QCOMPARE(table->rows(), 2);
3359 QCOMPARE(table->columns(), 1);
3360 const QTextTableCell cell = table->cellAt(row: 0, col: 0);
3361 QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
3362 QCOMPARE(cell.firstCursorPosition().block(), cell.lastCursorPosition().block());
3363}
3364
3365void tst_QTextDocumentFragment::html_tableStrangeNewline3()
3366{
3367 doc->setHtml("<table border>"
3368 "<tr>"
3369 "<td>"
3370 "<ul>"
3371 "<li>Meh</li>"
3372 "</ul>"
3373 "</td>"
3374 "<td>\n"
3375 "<ul>"
3376 "<li>Foo</li>"
3377 "</ul>"
3378 "</td>"
3379 "</tr>"
3380 "</table>");
3381
3382 cursor.movePosition(op: QTextCursor::Start);
3383 cursor.movePosition(op: QTextCursor::NextBlock);
3384 QTextTable *table = cursor.currentTable();
3385 QVERIFY(table);
3386 QCOMPARE(table->rows(), 1);
3387 QCOMPARE(table->columns(), 2);
3388
3389 QTextTableCell cell = table->cellAt(row: 0, col: 0);
3390 QCOMPARE(cell.firstCursorPosition().block().text(), QString("Meh"));
3391 QCOMPARE(cell.firstCursorPosition().block(), cell.lastCursorPosition().block());
3392
3393 cell = table->cellAt(row: 0, col: 1);
3394 QCOMPARE(cell.firstCursorPosition().block().text(), QString("Foo"));
3395 QCOMPARE(cell.firstCursorPosition().block(), cell.lastCursorPosition().block());
3396}
3397
3398void tst_QTextDocumentFragment::html_caption()
3399{
3400 doc->setHtml("<table border align=center>"
3401 "<caption>This <b> is a</b> Caption!</caption>"
3402 "<tr><td>Blah</td></tr>"
3403 "</table>");
3404
3405 cursor.movePosition(op: QTextCursor::Start);
3406 cursor.movePosition(op: QTextCursor::NextBlock);
3407
3408 QCOMPARE(cursor.block().text(), QString("This is a Caption!"));
3409 QCOMPARE(cursor.blockFormat().alignment(), Qt::AlignHCenter);
3410
3411 cursor.movePosition(op: QTextCursor::NextBlock);
3412 QTextTable *table = cursor.currentTable();
3413 QVERIFY(table);
3414 QCOMPARE(table->rows(), 1);
3415 QCOMPARE(table->columns(), 1);
3416
3417 QTextTableCell cell = table->cellAt(row: 0, col: 0);
3418 QCOMPARE(cell.firstCursorPosition().block().text(), QString("Blah"));
3419}
3420
3421static const uint windowsLatin1ExtendedCharacters[0xA0 - 0x80] = {
3422 0x20ac, // 0x80
3423 0x0081, // 0x81 direct mapping
3424 0x201a, // 0x82
3425 0x0192, // 0x83
3426 0x201e, // 0x84
3427 0x2026, // 0x85
3428 0x2020, // 0x86
3429 0x2021, // 0x87
3430 0x02C6, // 0x88
3431 0x2030, // 0x89
3432 0x0160, // 0x8A
3433 0x2039, // 0x8B
3434 0x0152, // 0x8C
3435 0x008D, // 0x8D direct mapping
3436 0x017D, // 0x8E
3437 0x008F, // 0x8F directmapping
3438 0x0090, // 0x90 directmapping
3439 0x2018, // 0x91
3440 0x2019, // 0x92
3441 0x201C, // 0x93
3442 0X201D, // 0x94
3443 0x2022, // 0x95
3444 0x2013, // 0x96
3445 0x2014, // 0x97
3446 0x02DC, // 0x98
3447 0x2122, // 0x99
3448 0x0161, // 0x9A
3449 0x203A, // 0x9B
3450 0x0153, // 0x9C
3451 0x009D, // 0x9D direct mapping
3452 0x017E, // 0x9E
3453 0x0178 // 0x9F
3454};
3455
3456void tst_QTextDocumentFragment::html_windowsEntities()
3457{
3458 for (uint i = 0; i < sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]); ++i) {
3459 QString html = QString::number(i + 0x80);
3460 html.prepend(s: "<p>&#");
3461 html.append(s: ";");
3462 doc->setHtml(html);
3463 QCOMPARE(doc->toPlainText(), QString(QChar(windowsLatin1ExtendedCharacters[i])));
3464 }
3465}
3466
3467void tst_QTextDocumentFragment::html_eatenText()
3468{
3469 doc->setHtml("<h1>Test1</h1>\nTest2<h1>Test3</h1>");
3470 cursor.movePosition(op: QTextCursor::Start);
3471 QCOMPARE(cursor.block().text(), QString("Test1"));
3472 cursor.movePosition(op: QTextCursor::NextBlock);
3473 QCOMPARE(cursor.block().text(), QString("Test2"));
3474 cursor.movePosition(op: QTextCursor::NextBlock);
3475 QCOMPARE(cursor.block().text(), QString("Test3"));
3476}
3477
3478void tst_QTextDocumentFragment::html_hr()
3479{
3480 doc->setHtml("<hr />");
3481 QCOMPARE(doc->blockCount(), 1);
3482 QVERIFY(doc->begin().blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
3483}
3484
3485void tst_QTextDocumentFragment::html_hrMargins()
3486{
3487 doc->setHtml("<p>Test<hr/>Blah");
3488 QCOMPARE(doc->blockCount(), 3);
3489
3490 cursor.movePosition(op: QTextCursor::Start);
3491 QTextBlock block = cursor.block();
3492 QCOMPARE(block.text(), QString("Test"));
3493 QVERIFY(block.blockFormat().bottomMargin() <= qreal(12.));
3494 QTextBlock first = block;
3495
3496 cursor.movePosition(op: QTextCursor::NextBlock);
3497 block = cursor.block();
3498 QVERIFY(qMax(first.blockFormat().bottomMargin(), block.blockFormat().topMargin()) > 0);
3499
3500 cursor.movePosition(op: QTextCursor::NextBlock);
3501 block = cursor.block();
3502
3503 QCOMPARE(block.text(), QString("Blah"));
3504}
3505
3506void tst_QTextDocumentFragment::html_blockQuoteMargins()
3507{
3508 doc->setHtml("<blockquote>Bar</blockquote>");
3509 QCOMPARE(doc->blockCount(), 1);
3510 cursor.movePosition(op: QTextCursor::Start);
3511 QTextBlock block = cursor.block();
3512 QCOMPARE(block.text(), QString("Bar"));
3513 QCOMPARE(block.blockFormat().leftMargin(), qreal(40.));
3514 QCOMPARE(block.blockFormat().rightMargin(), qreal(40.));
3515 QCOMPARE(block.blockFormat().topMargin(), qreal(12.));
3516 QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.));
3517}
3518
3519void tst_QTextDocumentFragment::html_definitionListMargins()
3520{
3521 doc->setHtml("Foo<dl><dt>tag<dd>data</dl>Bar");
3522 QCOMPARE(doc->blockCount(), 4);
3523
3524 cursor.movePosition(op: QTextCursor::Start);
3525 QTextBlock block = cursor.block();
3526 QCOMPARE(block.text(), QString("Foo"));
3527
3528 block = block.next();
3529 QCOMPARE(block.text(), QString("tag"));
3530 QCOMPARE(block.blockFormat().topMargin(), qreal(8.));
3531
3532 block = block.next();
3533 QCOMPARE(block.text(), QString("data"));
3534 QCOMPARE(block.blockFormat().bottomMargin(), qreal(8.));
3535
3536 block = block.next();
3537 QCOMPARE(block.text(), QString("Bar"));
3538}
3539
3540void tst_QTextDocumentFragment::html_listMargins()
3541{
3542 doc->setHtml("Foo<ol><li>First<li>Second</ol>Bar");
3543 QCOMPARE(doc->blockCount(), 4);
3544
3545 cursor.movePosition(op: QTextCursor::Start);
3546 QTextBlock block = cursor.block();
3547 QCOMPARE(block.text(), QString("Foo"));
3548
3549 block = block.next();
3550 QCOMPARE(block.text(), QString("First"));
3551 QCOMPARE(block.blockFormat().topMargin(), qreal(12.));
3552
3553 block = block.next();
3554 QCOMPARE(block.text(), QString("Second"));
3555 QCOMPARE(block.blockFormat().bottomMargin(), qreal(12.));
3556
3557 block = block.next();
3558 QCOMPARE(block.text(), QString("Bar"));
3559}
3560
3561void tst_QTextDocumentFragment::html_titleAttribute()
3562{
3563 doc->setHtml("<span title=\"this is my title\">Test</span>");
3564 cursor.movePosition(op: QTextCursor::Start);
3565 cursor.movePosition(op: QTextCursor::NextCharacter);
3566 QCOMPARE(cursor.charFormat().toolTip(), QString("this is my title"));
3567}
3568
3569void tst_QTextDocumentFragment::html_compressDivs()
3570{
3571 doc->setHtml("<p/><div/><div/><div/><div/>Test");
3572 QCOMPARE(doc->blockCount(), 1);
3573 QCOMPARE(doc->begin().text(), QString("Test"));
3574}
3575
3576void tst_QTextDocumentFragment::completeToPlainText()
3577{
3578 doc->setPlainText("Hello\nWorld");
3579 QCOMPARE(doc->toPlainText(), QString("Hello\nWorld"));
3580 QTextDocumentFragment fragment(doc);
3581 QCOMPARE(fragment.toPlainText(), QString("Hello\nWorld"));
3582}
3583
3584void tst_QTextDocumentFragment::copyContents()
3585{
3586 doc->setPlainText("Hello");
3587 QFont f;
3588 doc->setDefaultFont(f);
3589 QTextFragment fragment = doc->begin().begin().fragment();
3590 QCOMPARE(fragment.text(), QString("Hello"));
3591 QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize());
3592
3593 QTextDocumentFragment frag(doc);
3594 doc->clear();
3595 f.setPointSize(48);
3596 doc->setDefaultFont(f);
3597 QTextCursor(doc).insertFragment(fragment: QTextDocumentFragment::fromHtml(html: frag.toHtml()));
3598 fragment = doc->begin().begin().fragment();
3599 QCOMPARE(fragment.text(), QString("Hello"));
3600 QCOMPARE(fragment.charFormat().font().pointSize(), f.pointSize());
3601}
3602
3603void tst_QTextDocumentFragment::html_textAfterHr()
3604{
3605 doc->setHtml("<hr><nobr><b>After the centered text</b></nobr>");
3606 QCOMPARE(doc->blockCount(), 2);
3607 QTextBlock block = doc->begin();
3608 QVERIFY(block.text().isEmpty());
3609 QVERIFY(block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
3610 block = block.next();
3611
3612 QString txt("After the centered text");
3613 txt.replace(before: QLatin1Char(' '), after: QChar::Nbsp);
3614 QCOMPARE(block.text(), txt);
3615 QVERIFY(!block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth));
3616}
3617
3618void tst_QTextDocumentFragment::blockTagClosing()
3619{
3620 doc->setHtml("<p>foo<p>bar<span>baz</span>");
3621 QCOMPARE(doc->blockCount(), 2);
3622 QTextBlock block = doc->begin();
3623 QCOMPARE(block.text(), QString("foo"));
3624 block = block.next();
3625 QCOMPARE(block.text(), QString("barbaz"));
3626}
3627
3628void tst_QTextDocumentFragment::isEmpty()
3629{
3630 QTextDocumentFragment frag;
3631 QVERIFY(frag.isEmpty());
3632 frag = QTextDocumentFragment::fromHtml(html: "test");
3633 QVERIFY(!frag.isEmpty());
3634 frag = QTextDocumentFragment::fromHtml(html: "<hr />");
3635 QVERIFY(!frag.isEmpty());
3636}
3637
3638void tst_QTextDocumentFragment::html_alignmentInheritance()
3639{
3640 doc->setHtml("<center>Centered text<hr></center><b>After the centered text</b>");
3641 QCOMPARE(doc->blockCount(), 3);
3642 QTextBlock block = doc->begin();
3643 QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter);
3644 block = block.next();
3645 QVERIFY(block.blockFormat().alignment() & Qt::AlignHCenter);
3646 block = block.next();
3647 QVERIFY(!(block.blockFormat().alignment() & Qt::AlignHCenter));
3648}
3649
3650void tst_QTextDocumentFragment::html_ignoreEmptyDivs()
3651{
3652 doc->setHtml("<p><div/><b>Foo</b>");
3653 QCOMPARE(doc->blockCount(), 1);
3654 QCOMPARE(doc->begin().text(), QString("Foo"));
3655}
3656
3657void tst_QTextDocumentFragment::html_dontInheritAlignmentForFloatingImages()
3658{
3659 doc->setHtml("<p align=right><img align=unknownignored src=\"foo\" /></p>");
3660 QTextCharFormat fmt = doc->begin().begin().fragment().charFormat();
3661 QVERIFY(fmt.isImageFormat());
3662 QTextObject *o = doc->objectForFormat(fmt);
3663 QVERIFY(o);
3664 QTextFormat f = o->format();
3665 QVERIFY(f.isFrameFormat());
3666 QCOMPARE(f.toFrameFormat().position(), QTextFrameFormat::InFlow);
3667}
3668
3669void tst_QTextDocumentFragment::html_verticalImageAlignment()
3670{
3671 doc->setHtml("<img src=\"foo\"/>");
3672 cursor.movePosition(op: QTextCursor::Start);
3673 cursor.movePosition(op: QTextCursor::NextCharacter);
3674 QVERIFY(cursor.charFormat().isImageFormat());
3675 QTextImageFormat fmt = cursor.charFormat().toImageFormat();
3676 QCOMPARE(fmt.verticalAlignment(), QTextCharFormat::AlignNormal);
3677
3678 doc->setHtml("<img src=\"foo\" align=middle />");
3679 cursor.movePosition(op: QTextCursor::Start);
3680 cursor.movePosition(op: QTextCursor::NextCharacter);
3681 QVERIFY(cursor.charFormat().isImageFormat());
3682 fmt = cursor.charFormat().toImageFormat();
3683 QCOMPARE(fmt.verticalAlignment(), QTextCharFormat::AlignMiddle);
3684
3685 doc->setHtml("<img src=\"foo\" style=\"vertical-align: middle\" />");
3686 cursor.movePosition(op: QTextCursor::Start);
3687 cursor.movePosition(op: QTextCursor::NextCharacter);
3688 QVERIFY(cursor.charFormat().isImageFormat());
3689 fmt = cursor.charFormat().toImageFormat();
3690 QCOMPARE(fmt.verticalAlignment(), QTextCharFormat::AlignMiddle);
3691
3692 doc->setHtml("<img src=\"foo\" align=top />");
3693 cursor.movePosition(op: QTextCursor::Start);
3694 cursor.movePosition(op: QTextCursor::NextCharacter);
3695 QVERIFY(cursor.charFormat().isImageFormat());
3696 fmt = cursor.charFormat().toImageFormat();
3697 QCOMPARE(fmt.verticalAlignment(), QTextCharFormat::AlignTop);
3698
3699 doc->setHtml("<img src=\"foo\" style=\"vertical-align: top\" />");
3700 cursor.movePosition(op: QTextCursor::Start);
3701 cursor.movePosition(op: QTextCursor::NextCharacter);
3702 QVERIFY(cursor.charFormat().isImageFormat());
3703 fmt = cursor.charFormat().toImageFormat();
3704 QCOMPARE(fmt.verticalAlignment(), QTextCharFormat::AlignTop);
3705}
3706
3707void tst_QTextDocumentFragment::html_verticalCellAlignment()
3708{
3709 const char *alt[] =
3710 {
3711 // vertical-align property
3712 "<table>"
3713 "<tr>"
3714 "<td style=\"vertical-align: middle\"></td>"
3715 "<td style=\"vertical-align: top\"></td>"
3716 "<td style=\"vertical-align: bottom\"></td>"
3717 "</tr>"
3718 "</table>",
3719 // valign property
3720 "<table>"
3721 "<tr>"
3722 "<td valign=\"middle\"></td>"
3723 "<td valign=\"top\"></td>"
3724 "<td valign=\"bottom\"></td>"
3725 "</tr>"
3726 "</table>",
3727 // test td override of tr property
3728 "<table>"
3729 "<tr valign=\"bottom\">"
3730 "<td valign=\"middle\"></td>"
3731 "<td valign=\"top\"></td>"
3732 "<td></td>"
3733 "</tr>"
3734 "</table>"
3735 };
3736
3737 const int numTestCases = sizeof(alt) / sizeof(*alt);
3738 for (int i = 0; i < numTestCases; ++i) {
3739 doc->setHtml(alt[i]);
3740
3741 QTextTable *table = qobject_cast<QTextTable *>(object: doc->rootFrame()->childFrames().at(i: 0));
3742 QVERIFY(table);
3743
3744 QCOMPARE(table->cellAt(0, 0).format().verticalAlignment(), QTextCharFormat::AlignMiddle);
3745 QCOMPARE(table->cellAt(0, 1).format().verticalAlignment(), QTextCharFormat::AlignTop);
3746 QCOMPARE(table->cellAt(0, 2).format().verticalAlignment(), QTextCharFormat::AlignBottom);
3747 }
3748}
3749
3750void tst_QTextDocumentFragment::html_borderColor()
3751{
3752 const char html[] = "<table border=1 style=\"border-color:#0000ff;\"><tr><td>Foo</td></tr></table>";
3753 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
3754 cursor.movePosition(op: QTextCursor::Start);
3755 cursor.movePosition(op: QTextCursor::NextBlock);
3756 QVERIFY(cursor.currentTable());
3757 QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Outset);
3758 QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(QColor("#0000ff")));
3759}
3760
3761void tst_QTextDocumentFragment::html_borderStyle()
3762{
3763 const char html[] = "<table border=1 style=\"border-style:solid;\"><tr><td>Foo</td></tr></table>";
3764 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
3765 cursor.movePosition(op: QTextCursor::Start);
3766 cursor.movePosition(op: QTextCursor::NextBlock);
3767 QVERIFY(cursor.currentTable());
3768 QCOMPARE(cursor.currentTable()->format().borderStyle(), QTextFrameFormat::BorderStyle_Solid);
3769 QCOMPARE(cursor.currentTable()->format().borderBrush(), QBrush(Qt::darkGray));
3770}
3771
3772void tst_QTextDocumentFragment::html_borderWidth()
3773{
3774 const char *html[2] = { "<table style=\"border-width:2;\"><tr><td>Foo</td></tr></table>",
3775 "<table style=\"border-width:2px;\"><tr><td>Foo</td></tr></table>" };
3776
3777 for (int i = 0; i < 2; ++i) {
3778 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html[i])));
3779 cursor.movePosition(op: QTextCursor::Start);
3780 cursor.movePosition(op: QTextCursor::NextBlock);
3781 QVERIFY(cursor.currentTable());
3782 QCOMPARE(cursor.currentTable()->format().border(), qreal(2));
3783 }
3784}
3785
3786void tst_QTextDocumentFragment::html_userState()
3787{
3788 const char html[] = "<p style=\"-qt-user-state:42;\">A</p><p style=\"-qt-user-state:0;\">B</p><p>C</p>";
3789 cursor.insertFragment(fragment: QTextDocumentFragment::fromHtml(html: QString::fromLatin1(str: html)));
3790 QTextBlock block = doc->begin();
3791 QCOMPARE(block.userState(), 42);
3792 QCOMPARE(block.next().userState(), 0);
3793 QCOMPARE(block.next().next().userState(), -1);
3794}
3795
3796void tst_QTextDocumentFragment::html_rootFrameProperties()
3797{
3798 const char html[] = "<table border=1 style=\"-qt-table-type:root; margin-top:10px;\"><tr><td>Foo</tr></td>";
3799 doc->setHtml(html);
3800
3801 QCOMPARE(doc->rootFrame()->childFrames().size(), 0);
3802
3803 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
3804 QCOMPARE(fmt.topMargin(), qreal(10));
3805 QCOMPARE(fmt.bottomMargin(), qreal(0));
3806 QCOMPARE(fmt.leftMargin(), qreal(0));
3807 QCOMPARE(fmt.rightMargin(), qreal(0));
3808 QCOMPARE(fmt.border(), qreal(1));
3809
3810 QString normalFrameHtml = QLatin1String(html);
3811 normalFrameHtml.replace(before: QLatin1String("root"), after: QLatin1String("frame"));
3812
3813 doc->setHtml(normalFrameHtml);
3814 QCOMPARE(doc->rootFrame()->childFrames().size(), 1);
3815}
3816
3817void tst_QTextDocumentFragment::html_appendList()
3818{
3819 appendHtml(html: "<p>foo</p>");
3820 appendHtml(html: "<ul><li>Line 1</li><li>Line 2</li></ul>");
3821
3822 QCOMPARE(doc->blockCount(), 3);
3823 QVERIFY(doc->begin().next().textList() != 0);
3824}
3825
3826void tst_QTextDocumentFragment::html_appendList2()
3827{
3828 appendHtml(html: "1");
3829 appendHtml(html: "<ul><li><img src=\"/foo/bar\" /></li></ul>");
3830
3831 QCOMPARE(doc->blockCount(), 2);
3832 QVERIFY(doc->begin().next().textList() != 0);
3833}
3834
3835void tst_QTextDocumentFragment::html_alignmentPropertySet()
3836{
3837 const char html[] = "<p>Test</p>";
3838 setHtml(QString::fromLatin1(str: html));
3839 QVERIFY(!doc->begin().blockFormat().hasProperty(QTextFormat::BlockAlignment));
3840}
3841
3842void tst_QTextDocumentFragment::html_qt3RichtextWhitespaceMode()
3843{
3844 setHtml(QString::fromLatin1(str: "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><p> line with whitespace</p><p> another line with whitespace</p></body></html>"));
3845 QCOMPARE(doc->blockCount(), 2);
3846
3847 QTextBlock block = doc->begin();
3848 QVERIFY(block.text().startsWith(" "));
3849
3850 block = block.next();
3851 QVERIFY(block.text().startsWith(" "));
3852}
3853
3854void tst_QTextDocumentFragment::html_brAfterHr()
3855{
3856 setHtml(QString::fromLatin1(str: "Text A<br><hr><br>Text B<hr>"));
3857
3858 QCOMPARE(doc->blockCount(), 4);
3859
3860 QTextBlock block = doc->begin();
3861 QCOMPARE(block.text(), QString("Text A") + QChar(QChar::LineSeparator));
3862
3863 block = block.next();
3864 QVERIFY(block.text().isEmpty());
3865
3866 block = block.next();
3867 QCOMPARE(block.text(), QChar(QChar::LineSeparator) + QString("Text B"));
3868
3869 block = block.next();
3870 QVERIFY(block.text().isEmpty());
3871}
3872
3873void tst_QTextDocumentFragment::html_unclosedHead()
3874{
3875 doc->setHtml(QString::fromLatin1(str: "<html><head><title>Test</title><body>Blah</body></html>"));
3876 QCOMPARE(doc->metaInformation(QTextDocument::DocumentTitle), QString::fromLatin1("Test"));
3877 QCOMPARE(doc->toPlainText(), QString::fromLatin1("Blah"));
3878}
3879
3880// duplicated from qtexthtmlparser.cpp
3881#define MAX_ENTITY 258
3882static const struct { const char *name; quint16 code; } entities[MAX_ENTITY]= {
3883 { .name: "AElig", .code: 0x00c6 },
3884 { .name: "Aacute", .code: 0x00c1 },
3885 { .name: "Acirc", .code: 0x00c2 },
3886 { .name: "Agrave", .code: 0x00c0 },
3887 { .name: "Alpha", .code: 0x0391 },
3888 { .name: "AMP", .code: 38 },
3889 { .name: "Aring", .code: 0x00c5 },
3890 { .name: "Atilde", .code: 0x00c3 },
3891 { .name: "Auml", .code: 0x00c4 },
3892 { .name: "Beta", .code: 0x0392 },
3893 { .name: "Ccedil", .code: 0x00c7 },
3894 { .name: "Chi", .code: 0x03a7 },
3895 { .name: "Dagger", .code: 0x2021 },
3896 { .name: "Delta", .code: 0x0394 },
3897 { .name: "ETH", .code: 0x00d0 },
3898 { .name: "Eacute", .code: 0x00c9 },
3899 { .name: "Ecirc", .code: 0x00ca },
3900 { .name: "Egrave", .code: 0x00c8 },
3901 { .name: "Epsilon", .code: 0x0395 },
3902 { .name: "Eta", .code: 0x0397 },
3903 { .name: "Euml", .code: 0x00cb },
3904 { .name: "Gamma", .code: 0x0393 },
3905 { .name: "GT", .code: 62 },
3906 { .name: "Iacute", .code: 0x00cd },
3907 { .name: "Icirc", .code: 0x00ce },
3908 { .name: "Igrave", .code: 0x00cc },
3909 { .name: "Iota", .code: 0x0399 },
3910 { .name: "Iuml", .code: 0x00cf },
3911 { .name: "Kappa", .code: 0x039a },
3912 { .name: "Lambda", .code: 0x039b },
3913 { .name: "LT", .code: 60 },
3914 { .name: "Mu", .code: 0x039c },
3915 { .name: "Ntilde", .code: 0x00d1 },
3916 { .name: "Nu", .code: 0x039d },
3917 { .name: "OElig", .code: 0x0152 },
3918 { .name: "Oacute", .code: 0x00d3 },
3919 { .name: "Ocirc", .code: 0x00d4 },
3920 { .name: "Ograve", .code: 0x00d2 },
3921 { .name: "Omega", .code: 0x03a9 },
3922 { .name: "Omicron", .code: 0x039f },
3923 { .name: "Oslash", .code: 0x00d8 },
3924 { .name: "Otilde", .code: 0x00d5 },
3925 { .name: "Ouml", .code: 0x00d6 },
3926 { .name: "Phi", .code: 0x03a6 },
3927 { .name: "Pi", .code: 0x03a0 },
3928 { .name: "Prime", .code: 0x2033 },
3929 { .name: "Psi", .code: 0x03a8 },
3930 { .name: "QUOT", .code: 34 },
3931 { .name: "Rho", .code: 0x03a1 },
3932 { .name: "Scaron", .code: 0x0160 },
3933 { .name: "Sigma", .code: 0x03a3 },
3934 { .name: "THORN", .code: 0x00de },
3935 { .name: "Tau", .code: 0x03a4 },
3936 { .name: "Theta", .code: 0x0398 },
3937 { .name: "Uacute", .code: 0x00da },
3938 { .name: "Ucirc", .code: 0x00db },
3939 { .name: "Ugrave", .code: 0x00d9 },
3940 { .name: "Upsilon", .code: 0x03a5 },
3941 { .name: "Uuml", .code: 0x00dc },
3942 { .name: "Xi", .code: 0x039e },
3943 { .name: "Yacute", .code: 0x00dd },
3944 { .name: "Yuml", .code: 0x0178 },
3945 { .name: "Zeta", .code: 0x0396 },
3946 { .name: "aacute", .code: 0x00e1 },
3947 { .name: "acirc", .code: 0x00e2 },
3948 { .name: "acute", .code: 0x00b4 },
3949 { .name: "aelig", .code: 0x00e6 },
3950 { .name: "agrave", .code: 0x00e0 },
3951 { .name: "alefsym", .code: 0x2135 },
3952 { .name: "alpha", .code: 0x03b1 },
3953 { .name: "amp", .code: 38 },
3954 { .name: "and", .code: 0x22a5 },
3955 { .name: "ang", .code: 0x2220 },
3956 { .name: "apos", .code: 0x0027 },
3957 { .name: "aring", .code: 0x00e5 },
3958 { .name: "asymp", .code: 0x2248 },
3959 { .name: "atilde", .code: 0x00e3 },
3960 { .name: "auml", .code: 0x00e4 },
3961 { .name: "bdquo", .code: 0x201e },
3962 { .name: "beta", .code: 0x03b2 },
3963 { .name: "brvbar", .code: 0x00a6 },
3964 { .name: "bull", .code: 0x2022 },
3965 { .name: "cap", .code: 0x2229 },
3966 { .name: "ccedil", .code: 0x00e7 },
3967 { .name: "cedil", .code: 0x00b8 },
3968 { .name: "cent", .code: 0x00a2 },
3969 { .name: "chi", .code: 0x03c7 },
3970 { .name: "circ", .code: 0x02c6 },
3971 { .name: "clubs", .code: 0x2663 },
3972 { .name: "cong", .code: 0x2245 },
3973 { .name: "copy", .code: 0x00a9 },
3974 { .name: "crarr", .code: 0x21b5 },
3975 { .name: "cup", .code: 0x222a },
3976 { .name: "curren", .code: 0x00a4 },
3977 { .name: "dArr", .code: 0x21d3 },
3978 { .name: "dagger", .code: 0x2020 },
3979 { .name: "darr", .code: 0x2193 },
3980 { .name: "deg", .code: 0x00b0 },
3981 { .name: "delta", .code: 0x03b4 },
3982 { .name: "diams", .code: 0x2666 },
3983 { .name: "divide", .code: 0x00f7 },
3984 { .name: "eacute", .code: 0x00e9 },
3985 { .name: "ecirc", .code: 0x00ea },
3986 { .name: "egrave", .code: 0x00e8 },
3987 { .name: "empty", .code: 0x2205 },
3988 { .name: "emsp", .code: 0x2003 },
3989 { .name: "ensp", .code: 0x2002 },
3990 { .name: "epsilon", .code: 0x03b5 },
3991 { .name: "equiv", .code: 0x2261 },
3992 { .name: "eta", .code: 0x03b7 },
3993 { .name: "eth", .code: 0x00f0 },
3994 { .name: "euml", .code: 0x00eb },
3995 { .name: "euro", .code: 0x20ac },
3996 { .name: "exist", .code: 0x2203 },
3997 { .name: "fnof", .code: 0x0192 },
3998 { .name: "forall", .code: 0x2200 },
3999 { .name: "frac12", .code: 0x00bd },
4000 { .name: "frac14", .code: 0x00bc },
4001 { .name: "frac34", .code: 0x00be },
4002 { .name: "frasl", .code: 0x2044 },
4003 { .name: "gamma", .code: 0x03b3 },
4004 { .name: "ge", .code: 0x2265 },
4005 { .name: "gt", .code: 62 },
4006 { .name: "hArr", .code: 0x21d4 },
4007 { .name: "harr", .code: 0x2194 },
4008 { .name: "hearts", .code: 0x2665 },
4009 { .name: "hellip", .code: 0x2026 },
4010 { .name: "iacute", .code: 0x00ed },
4011 { .name: "icirc", .code: 0x00ee },
4012 { .name: "iexcl", .code: 0x00a1 },
4013 { .name: "igrave", .code: 0x00ec },
4014 { .name: "image", .code: 0x2111 },
4015 { .name: "infin", .code: 0x221e },
4016 { .name: "int", .code: 0x222b },
4017 { .name: "iota", .code: 0x03b9 },
4018 { .name: "iquest", .code: 0x00bf },
4019 { .name: "isin", .code: 0x2208 },
4020 { .name: "iuml", .code: 0x00ef },
4021 { .name: "kappa", .code: 0x03ba },
4022 { .name: "lArr", .code: 0x21d0 },
4023 { .name: "lambda", .code: 0x03bb },
4024 { .name: "lang", .code: 0x2329 },
4025 { .name: "laquo", .code: 0x00ab },
4026 { .name: "larr", .code: 0x2190 },
4027 { .name: "lceil", .code: 0x2308 },
4028 { .name: "ldquo", .code: 0x201c },
4029 { .name: "le", .code: 0x2264 },
4030 { .name: "lfloor", .code: 0x230a },
4031 { .name: "lowast", .code: 0x2217 },
4032 { .name: "loz", .code: 0x25ca },
4033 { .name: "lrm", .code: 0x200e },
4034 { .name: "lsaquo", .code: 0x2039 },
4035 { .name: "lsquo", .code: 0x2018 },
4036 { .name: "lt", .code: 60 },
4037 { .name: "macr", .code: 0x00af },
4038 { .name: "mdash", .code: 0x2014 },
4039 { .name: "micro", .code: 0x00b5 },
4040 { .name: "middot", .code: 0x00b7 },
4041 { .name: "minus", .code: 0x2212 },
4042 { .name: "mu", .code: 0x03bc },
4043 { .name: "nabla", .code: 0x2207 },
4044 { .name: "nbsp", .code: 0x00a0 },
4045 { .name: "ndash", .code: 0x2013 },
4046 { .name: "ne", .code: 0x2260 },
4047 { .name: "ni", .code: 0x220b },
4048 { .name: "not", .code: 0x00ac },
4049 { .name: "notin", .code: 0x2209 },
4050 { .name: "nsub", .code: 0x2284 },
4051 { .name: "ntilde", .code: 0x00f1 },
4052 { .name: "nu", .code: 0x03bd },
4053 { .name: "oacute", .code: 0x00f3 },
4054 { .name: "ocirc", .code: 0x00f4 },
4055 { .name: "oelig", .code: 0x0153 },
4056 { .name: "ograve", .code: 0x00f2 },
4057 { .name: "oline", .code: 0x203e },
4058 { .name: "omega", .code: 0x03c9 },
4059 { .name: "omicron", .code: 0x03bf },
4060 { .name: "oplus", .code: 0x2295 },
4061 { .name: "or", .code: 0x22a6 },
4062 { .name: "ordf", .code: 0x00aa },
4063 { .name: "ordm", .code: 0x00ba },
4064 { .name: "oslash", .code: 0x00f8 },
4065 { .name: "otilde", .code: 0x00f5 },
4066 { .name: "otimes", .code: 0x2297 },
4067 { .name: "ouml", .code: 0x00f6 },
4068 { .name: "para", .code: 0x00b6 },
4069 { .name: "part", .code: 0x2202 },
4070 { .name: "percnt", .code: 0x0025 },
4071 { .name: "permil", .code: 0x2030 },
4072 { .name: "perp", .code: 0x22a5 },
4073 { .name: "phi", .code: 0x03c6 },
4074 { .name: "pi", .code: 0x03c0 },
4075 { .name: "piv", .code: 0x03d6 },
4076 { .name: "plusmn", .code: 0x00b1 },
4077 { .name: "pound", .code: 0x00a3 },
4078 { .name: "prime", .code: 0x2032 },
4079 { .name: "prod", .code: 0x220f },
4080 { .name: "prop", .code: 0x221d },
4081 { .name: "psi", .code: 0x03c8 },
4082 { .name: "quot", .code: 34 },
4083 { .name: "rArr", .code: 0x21d2 },
4084 { .name: "radic", .code: 0x221a },
4085 { .name: "rang", .code: 0x232a },
4086 { .name: "raquo", .code: 0x00bb },
4087 { .name: "rarr", .code: 0x2192 },
4088 { .name: "rceil", .code: 0x2309 },
4089 { .name: "rdquo", .code: 0x201d },
4090 { .name: "real", .code: 0x211c },
4091 { .name: "reg", .code: 0x00ae },
4092 { .name: "rfloor", .code: 0x230b },
4093 { .name: "rho", .code: 0x03c1 },
4094 { .name: "rlm", .code: 0x200f },
4095 { .name: "rsaquo", .code: 0x203a },
4096 { .name: "rsquo", .code: 0x2019 },
4097 { .name: "sbquo", .code: 0x201a },
4098 { .name: "scaron", .code: 0x0161 },
4099 { .name: "sdot", .code: 0x22c5 },
4100 { .name: "sect", .code: 0x00a7 },
4101 { .name: "shy", .code: 0x00ad },
4102 { .name: "sigma", .code: 0x03c3 },
4103 { .name: "sigmaf", .code: 0x03c2 },
4104 { .name: "sim", .code: 0x223c },
4105 { .name: "spades", .code: 0x2660 },
4106 { .name: "sub", .code: 0x2282 },
4107 { .name: "sube", .code: 0x2286 },
4108 { .name: "sum", .code: 0x2211 },
4109 { .name: "sup1", .code: 0x00b9 },
4110 { .name: "sup2", .code: 0x00b2 },
4111 { .name: "sup3", .code: 0x00b3 },
4112 { .name: "sup", .code: 0x2283 },
4113 { .name: "supe", .code: 0x2287 },
4114 { .name: "szlig", .code: 0x00df },
4115 { .name: "tau", .code: 0x03c4 },
4116 { .name: "there4", .code: 0x2234 },
4117 { .name: "theta", .code: 0x03b8 },
4118 { .name: "thetasym", .code: 0x03d1 },
4119 { .name: "thinsp", .code: 0x2009 },
4120 { .name: "thorn", .code: 0x00fe },
4121 { .name: "tilde", .code: 0x02dc },
4122 { .name: "times", .code: 0x00d7 },
4123 { .name: "trade", .code: 0x2122 },
4124 { .name: "uArr", .code: 0x21d1 },
4125 { .name: "uacute", .code: 0x00fa },
4126 { .name: "uarr", .code: 0x2191 },
4127 { .name: "ucirc", .code: 0x00fb },
4128 { .name: "ugrave", .code: 0x00f9 },
4129 { .name: "uml", .code: 0x00a8 },
4130 { .name: "upsih", .code: 0x03d2 },
4131 { .name: "upsilon", .code: 0x03c5 },
4132 { .name: "uuml", .code: 0x00fc },
4133 { .name: "weierp", .code: 0x2118 },
4134 { .name: "xi", .code: 0x03be },
4135 { .name: "yacute", .code: 0x00fd },
4136 { .name: "yen", .code: 0x00a5 },
4137 { .name: "yuml", .code: 0x00ff },
4138 { .name: "zeta", .code: 0x03b6 },
4139 { .name: "zwj", .code: 0x200d },
4140 { .name: "zwnj", .code: 0x200c }
4141};
4142
4143void tst_QTextDocumentFragment::html_entities_data()
4144{
4145 QTest::addColumn<QString>(name: "html");
4146 QTest::addColumn<quint16>(name: "code");
4147
4148 for (int i = 0; i < MAX_ENTITY; ++i) {
4149 QTest::newRow(dataTag: entities[i].name) << QString("<pre>&") + QString::fromLatin1(str: entities[i].name) + QString(";</pre>")
4150 << entities[i].code;
4151 }
4152}
4153
4154void tst_QTextDocumentFragment::html_entities()
4155{
4156 QFETCH(QString, html);
4157 QFETCH(quint16, code);
4158
4159 setHtml(html);
4160 QCOMPARE(doc->blockCount(), 1);
4161 QString txt = doc->begin().text();
4162 QCOMPARE(txt.length(), 1);
4163 QCOMPARE(txt.at(0).unicode(), code);
4164}
4165
4166void tst_QTextDocumentFragment::html_ignore_script()
4167{
4168 doc->setHtml(QString::fromLatin1(str: "<html><script>Test</script><body>Blah</body></html>"));
4169 QCOMPARE(doc->toPlainText(), QString("Blah"));
4170}
4171
4172void tst_QTextDocumentFragment::html_directionWithHtml()
4173{
4174 doc->setHtml(QString::fromLatin1(str: "<html><body><p>Test<p dir=rtl>RTL<p dir=ltr>LTR"));
4175 QCOMPARE(doc->blockCount(), 3);
4176
4177 QTextBlock block = doc->firstBlock();
4178 QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4179 QVERIFY(block.blockFormat().layoutDirection() == Qt::LeftToRight); // HTML default
4180
4181 block = block.next();
4182 QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4183 QCOMPARE(block.blockFormat().layoutDirection(), Qt::RightToLeft);
4184
4185 block = block.next();
4186 QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4187 QCOMPARE(block.blockFormat().layoutDirection(), Qt::LeftToRight);
4188}
4189
4190void tst_QTextDocumentFragment::html_directionWithRichText()
4191{
4192 doc->setHtml(QString::fromLatin1(str: "<p>Test<p dir=rtl>RTL<p dir=ltr>LTR"));
4193 QCOMPARE(doc->blockCount(), 3);
4194
4195 QTextBlock block = doc->firstBlock();
4196 QVERIFY(!block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4197
4198 block = block.next();
4199 QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4200 QCOMPARE(block.blockFormat().layoutDirection(), Qt::RightToLeft);
4201
4202 block = block.next();
4203 QVERIFY(block.blockFormat().hasProperty(QTextFormat::LayoutDirection));
4204 QCOMPARE(block.blockFormat().layoutDirection(), Qt::LeftToRight);
4205}
4206
4207void tst_QTextDocumentFragment::html_metaInBody()
4208{
4209 setHtml("<body>Hello<meta>World</body>");
4210 QCOMPARE(doc->toPlainText(), QString("HelloWorld"));
4211}
4212
4213void tst_QTextDocumentFragment::html_importImageWithoutAspectRatio()
4214{
4215 doc->setHtml("<img src=\"foo\" width=\"100%\" height=\"43\">");
4216 cursor.movePosition(op: QTextCursor::Start);
4217 cursor.movePosition(op: QTextCursor::NextCharacter);
4218 QVERIFY(cursor.charFormat().isImageFormat());
4219 QTextImageFormat fmt = cursor.charFormat().toImageFormat();
4220 // qDebug() << fmt.width() << fmt.height();
4221 QVERIFY (fmt.hasProperty(QTextFormat::ImageWidth));
4222 QCOMPARE (fmt.height(), 43.);
4223
4224 doc->setHtml("<img src=\"foo\" height=\"43\">");
4225 cursor.movePosition(op: QTextCursor::Start);
4226 cursor.movePosition(op: QTextCursor::NextCharacter);
4227 QVERIFY(cursor.charFormat().isImageFormat());
4228 fmt = cursor.charFormat().toImageFormat();
4229 QVERIFY (! fmt.hasProperty(QTextFormat::ImageWidth));
4230 QCOMPARE (fmt.height(), 43.);
4231
4232 doc->setHtml("<img src=\"foo\" width=\"200\">");
4233 cursor.movePosition(op: QTextCursor::Start);
4234 cursor.movePosition(op: QTextCursor::NextCharacter);
4235 QVERIFY(cursor.charFormat().isImageFormat());
4236 fmt = cursor.charFormat().toImageFormat();
4237 QVERIFY (! fmt.hasProperty(QTextFormat::ImageHeight));
4238 QCOMPARE (fmt.width(), 200.);
4239}
4240
4241void tst_QTextDocumentFragment::html_fromFirefox()
4242{
4243 // if you have a html loaded in firefox like <html>Test\nText</html> then selecting all and copying will
4244 // result in the following text on the clipboard (for text/html)
4245 doc->setHtml(QString::fromLatin1(str: "<!--StartFragment-->Test\nText\n\n<!--EndFragment-->"));
4246 QCOMPARE(doc->toPlainText(), QString::fromLatin1("Test Text "));
4247}
4248
4249void tst_QTextDocumentFragment::html_emptyInlineInsideBlock()
4250{
4251 doc->setHtml(QString::fromLatin1(str: "<!--StartFragment--><blockquote><span/>Foobar</blockquote><!--EndFragment-->"));
4252 QVERIFY(doc->firstBlock().blockFormat().leftMargin() > 0);
4253}
4254
4255void tst_QTextDocumentFragment::css_fontAndWordSpacing()
4256{
4257 {
4258 const char html[] = "<body style=\"letter-spacing:13px; word-spacing:15px;\">Foo</span>";
4259 doc->setHtml(html);
4260 cursor.movePosition(op: QTextCursor::Start);
4261 cursor.movePosition(op: QTextCursor::NextCharacter);
4262 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 13);
4263 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(),
4264 (uint)(QFont::AbsoluteSpacing));
4265 QCOMPARE(cursor.charFormat().property(QTextFormat::FontWordSpacing).toInt(), 15);
4266 }
4267 {
4268 const char html[] = "<body style=\"letter-spacing:1em; word-spacing:0px;\">Foo</span>";
4269 doc->setHtml(html);
4270 cursor.movePosition(op: QTextCursor::Start);
4271 cursor.movePosition(op: QTextCursor::NextCharacter);
4272 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 200);
4273 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(),
4274 (uint)(QFont::PercentageSpacing));
4275 QCOMPARE(cursor.charFormat().property(QTextFormat::FontWordSpacing).toInt(), 0);
4276 }
4277 {
4278 const char html[] = "<body style=\"letter-spacing:0em;\">Foo</span>";
4279 doc->setHtml(html);
4280 cursor.movePosition(op: QTextCursor::Start);
4281 cursor.movePosition(op: QTextCursor::NextCharacter);
4282 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 100);
4283 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(),
4284 (uint)(QFont::PercentageSpacing));
4285 }
4286 {
4287 const char html[] = "<body style=\"letter-spacing:-0.5em;\">Foo</span>";
4288 doc->setHtml(html);
4289 cursor.movePosition(op: QTextCursor::Start);
4290 cursor.movePosition(op: QTextCursor::NextCharacter);
4291 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacing).toInt(), 50);
4292 QCOMPARE(cursor.charFormat().property(QTextFormat::FontLetterSpacingType).toUInt(),
4293 (uint)(QFont::PercentageSpacing));
4294 }
4295}
4296
4297QTEST_MAIN(tst_QTextDocumentFragment)
4298#include "tst_qtextdocumentfragment.moc"
4299

source code of qtbase/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp