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#include <qtextdocument.h>
33#include <qabstracttextdocumentlayout.h>
34#include <qdebug.h>
35#include <qpainter.h>
36#include <qtexttable.h>
37#ifndef QT_NO_WIDGETS
38#include <qtextedit.h>
39#include <qscrollbar.h>
40#endif
41
42class tst_QTextDocumentLayout : public QObject
43{
44 Q_OBJECT
45
46private slots:
47 void init();
48 void cleanup();
49 void cleanupTestCase();
50 void defaultPageSizeHandling();
51 void idealWidth();
52 void lineSeparatorFollowingTable();
53#ifndef QT_NO_WIDGETS
54 void wrapAtWordBoundaryOrAnywhere();
55#endif
56 void inlineImage();
57 void clippedTableCell();
58 void floatingTablePageBreak();
59 void imageAtRightAlignedTab();
60 void blockVisibility();
61
62 void largeImage();
63
64private:
65 QTextDocument *doc;
66};
67
68void tst_QTextDocumentLayout::init()
69{
70 doc = new QTextDocument;
71}
72
73void tst_QTextDocumentLayout::cleanup()
74{
75 delete doc;
76 doc = 0;
77}
78
79void tst_QTextDocumentLayout::cleanupTestCase()
80{
81 if (qgetenv(varName: "QTEST_KEEP_IMAGEDATA").toInt() == 0) {
82 QFile::remove(fileName: QLatin1String("expected.png"));
83 QFile::remove(fileName: QLatin1String("img.png"));
84 }
85}
86
87void tst_QTextDocumentLayout::defaultPageSizeHandling()
88{
89 QAbstractTextDocumentLayout *layout = doc->documentLayout();
90 QVERIFY(layout);
91
92 QVERIFY(!doc->pageSize().isValid());
93 QSizeF docSize = layout->documentSize();
94 QVERIFY(docSize.width() > 0 && docSize.width() < 1000);
95 QVERIFY(docSize.height() > 0 && docSize.height() < 1000);
96
97 doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
98
99 docSize = layout->documentSize();
100 QVERIFY(docSize.isValid());
101 QVERIFY(docSize.width() != INT_MAX);
102 QVERIFY(docSize.height() != INT_MAX);
103}
104
105void tst_QTextDocumentLayout::idealWidth()
106{
107 doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
108 doc->setTextWidth(1000);
109 QCOMPARE(doc->textWidth(), qreal(1000));
110 QCOMPARE(doc->size().width(), doc->textWidth());
111 QVERIFY(doc->idealWidth() < doc->textWidth());
112 QVERIFY(doc->idealWidth() > 0);
113
114 QTextBlockFormat fmt;
115 fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
116 QTextCursor cursor(doc);
117 cursor.select(selection: QTextCursor::Document);
118 cursor.mergeBlockFormat(modifier: fmt);
119
120 QCOMPARE(doc->textWidth(), qreal(1000));
121 QCOMPARE(doc->size().width(), doc->textWidth());
122 QVERIFY(doc->idealWidth() < doc->textWidth());
123 QVERIFY(doc->idealWidth() > 0);
124}
125
126// none of the QTextLine items in the document should intersect with the margin rect
127void tst_QTextDocumentLayout::lineSeparatorFollowingTable()
128{
129 QString html_begin("<html><table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table><br>");
130 QString html_text("bla bla bla bla bla bla bla bla<br>");
131 QString html_end("<table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table></html>");
132
133 QString html = html_begin;
134
135 for (int i = 0; i < 80; ++i)
136 html += html_text;
137
138 html += html_end;
139
140 doc->setHtml(html);
141
142 QTextCursor cursor(doc);
143 cursor.movePosition(op: QTextCursor::Start);
144
145 const int margin = 87;
146 const int pageWidth = 873;
147 const int pageHeight = 1358;
148
149 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
150 fmt.setMargin(margin);
151 doc->rootFrame()->setFrameFormat(fmt);
152
153 QFont font(doc->defaultFont());
154 font.setPointSize(10);
155 doc->setDefaultFont(font);
156 doc->setPageSize(QSizeF(pageWidth, pageHeight));
157
158 QRectF marginRect(QPointF(0, pageHeight - margin), QSizeF(pageWidth, 2 * margin));
159
160 // force layouting
161 doc->pageCount();
162
163 for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
164 QTextLayout *layout = block.layout();
165 for (int i = 0; i < layout->lineCount(); ++i) {
166 QTextLine line = layout->lineAt(i);
167 QRectF rect = line.rect().translated(p: layout->position());
168 QVERIFY(!rect.intersects(marginRect));
169 }
170 }
171}
172
173#ifndef QT_NO_WIDGETS
174void tst_QTextDocumentLayout::wrapAtWordBoundaryOrAnywhere()
175{
176 //task 150562
177 QTextEdit edit;
178 edit.setText("<table><tr><td>hello hello hello"
179 "thisisabigwordthisisabigwordthisisabigwordthisisabigwordthisisabigword"
180 "hello hello hello</td></tr></table>");
181 edit.setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
182 edit.resize(w: 100, h: 100);
183 edit.show();
184 QVERIFY(!edit.horizontalScrollBar()->isVisible());
185}
186#endif
187
188void tst_QTextDocumentLayout::inlineImage()
189{
190 doc->setPageSize(QSizeF(800, 500));
191
192 QImage img(400, 400, QImage::Format_RGB32);
193 QLatin1String name("bigImage");
194
195 doc->addResource(type: QTextDocument::ImageResource, name: QUrl(name), resource: img);
196
197 QTextImageFormat imgFormat;
198 imgFormat.setName(name);
199 imgFormat.setWidth(img.width());
200
201 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
202 qreal height = doc->pageSize().height() - fmt.topMargin() - fmt.bottomMargin();
203 imgFormat.setHeight(height);
204
205 QTextCursor cursor(doc);
206 cursor.insertImage(format: imgFormat);
207
208 QCOMPARE(doc->pageCount(), 1);
209}
210
211void tst_QTextDocumentLayout::clippedTableCell()
212{
213 const char *html =
214 "<table style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""
215 "border=\"0\" margin=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td></td></tr></table>";
216
217 doc->setHtml(html);
218 doc->pageSize();
219
220 QTextCursor cursor(doc);
221 cursor.movePosition(op: QTextCursor::Right);
222
223 QTextTable *table = cursor.currentTable();
224 QVERIFY(table);
225
226 QTextCursor cellCursor = table->cellAt(row: 0, col: 0).firstCursorPosition();
227 QImage src(16, 16, QImage::Format_ARGB32_Premultiplied);
228 src.fill(pixel: 0xffff0000);
229 cellCursor.insertImage(image: src);
230
231 QTextBlock block = cellCursor.block();
232 QRectF r = doc->documentLayout()->blockBoundingRect(block);
233
234 QRectF rect(0, 0, r.left() + 1, 64);
235
236 QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
237 img.fill(pixel: 0x0);
238 QImage expected = img;
239 QPainter p(&img);
240 doc->drawContents(painter: &p, rect);
241 p.end();
242 p.begin(&expected);
243 r.setWidth(1);
244 p.fillRect(r, c: Qt::red);
245 p.end();
246
247 img.save(fileName: "img.png");
248 expected.save(fileName: "expected.png");
249 QCOMPARE(img, expected);
250}
251
252void tst_QTextDocumentLayout::floatingTablePageBreak()
253{
254 doc->clear();
255
256 QTextCursor cursor(doc);
257
258 QTextTableFormat tableFormat;
259 tableFormat.setPosition(QTextFrameFormat::FloatLeft);
260 QTextTable *table = cursor.insertTable(rows: 50, cols: 1, format: tableFormat);
261 Q_UNUSED(table);
262
263 // Make height of document 2/3 of the table, fitting the table into two pages
264 QSizeF documentSize = doc->size();
265 documentSize.rheight() *= 2.0 / 3.0;
266
267 doc->setPageSize(documentSize);
268
269 QCOMPARE(doc->pageCount(), 2);
270}
271
272void tst_QTextDocumentLayout::imageAtRightAlignedTab()
273{
274 doc->clear();
275
276 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
277 fmt.setMargin(0);
278 doc->rootFrame()->setFrameFormat(fmt);
279
280 QTextCursor cursor(doc);
281 QTextBlockFormat blockFormat;
282 QList<QTextOption::Tab> tabs;
283 QTextOption::Tab tab;
284 tab.position = 300;
285 tab.type = QTextOption::RightTab;
286 tabs.append(t: tab);
287 blockFormat.setTabPositions(tabs);
288
289 // First block: text, some of it right-aligned
290 cursor.insertBlock(format: blockFormat);
291 cursor.insertText(text: "first line\t");
292 cursor.insertText(text: "right-aligned text");
293
294 // Second block: text, then right-aligned image
295 cursor.insertBlock(format: blockFormat);
296 cursor.insertText(text: "second line\t");
297 QImage img(48, 48, QImage::Format_RGB32);
298 const QString name = QString::fromLatin1(str: "image");
299 doc->addResource(type: QTextDocument::ImageResource, name: QUrl(name), resource: img);
300 QTextImageFormat imgFormat;
301 imgFormat.setName(name);
302 cursor.insertImage(format: imgFormat);
303
304 qreal bearing = QFontMetricsF(doc->defaultFont()).rightBearing(QLatin1Char('t'));
305 QCOMPARE(doc->idealWidth(), std::max(300.0, 300.0 - bearing));
306}
307
308void tst_QTextDocumentLayout::blockVisibility()
309{
310 QTextCursor cursor(doc);
311 for (int i = 0; i < 10; ++i) {
312 if (!doc->isEmpty())
313 cursor.insertBlock();
314 cursor.insertText(text: "A");
315 }
316
317 qreal margin = doc->documentMargin();
318 QSizeF emptySize(2 * margin, 2 * margin);
319 QSizeF halfSize = doc->size();
320 halfSize.rheight() -= 2 * margin;
321 halfSize.rheight() /= 2;
322 halfSize.rheight() += 2 * margin;
323
324 for (int i = 0; i < 10; i += 2) {
325 QTextBlock block = doc->findBlockByNumber(blockNumber: i);
326 block.setVisible(false);
327 doc->markContentsDirty(from: block.position(), length: block.length());
328 }
329
330 QCOMPARE(doc->size(), halfSize);
331
332 for (int i = 1; i < 10; i += 2) {
333 QTextBlock block = doc->findBlockByNumber(blockNumber: i);
334 block.setVisible(false);
335 doc->markContentsDirty(from: block.position(), length: block.length());
336 }
337
338 QCOMPARE(doc->size(), emptySize);
339
340 for (int i = 0; i < 10; i += 2) {
341 QTextBlock block = doc->findBlockByNumber(blockNumber: i);
342 block.setVisible(true);
343 doc->markContentsDirty(from: block.position(), length: block.length());
344 }
345
346 QCOMPARE(doc->size(), halfSize);
347}
348
349void tst_QTextDocumentLayout::largeImage()
350{
351 auto img = QImage(400, 500, QImage::Format_ARGB32_Premultiplied);
352 img.fill(color: Qt::black);
353
354 {
355 QTextDocument document;
356
357 document.addResource(type: QTextDocument::ImageResource,
358 name: QUrl("data://test.png"), resource: QVariant(img));
359 document.setPageSize({500, 504});
360
361 auto html = "<img src=\"data://test.png\">";
362 document.setHtml(html);
363
364 QCOMPARE(document.pageCount(), 2);
365 }
366
367 {
368 QTextDocument document;
369
370 document.addResource(type: QTextDocument::ImageResource,
371 name: QUrl("data://test.png"), resource: QVariant(img));
372 document.setPageSize({500, 508});
373
374 auto html = "<img src=\"data://test.png\">";
375 document.setHtml(html);
376
377 QCOMPARE(document.pageCount(), 1);
378 }
379
380 {
381 QTextDocument document;
382
383 document.addResource(type: QTextDocument::ImageResource,
384 name: QUrl("data://test.png"), resource: QVariant(img));
385 document.setPageSize({585, 250});
386
387 auto html = "<img src=\"data://test.png\">";
388 document.setHtml(html);
389
390 QCOMPARE(document.pageCount(), 3);
391 }
392
393 {
394 QTextDocument document;
395
396 document.addResource(type: QTextDocument::ImageResource,
397 name: QUrl("data://test.png"), resource: QVariant(img));
398 document.setPageSize({585, 258});
399
400 auto html = "<img src=\"data://test.png\">";
401 document.setHtml(html);
402
403 QCOMPARE(document.pageCount(), 2);
404 }
405}
406
407QTEST_MAIN(tst_QTextDocumentLayout)
408#include "tst_qtextdocumentlayout.moc"
409

source code of qtbase/tests/auto/gui/text/qtextdocumentlayout/tst_qtextdocumentlayout.cpp