1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include <QDebug>
43 #include <QTextDocument>
44 #include <QTextDocumentWriter>
45 #include <QTextLayout>
46 #include <QTextCursor>
47 #include <private/qtextcontrol_p.h>
48 #include <qmath.h>
49 #include <QFile>
50 #include <QPainter>
51 #include <QBuffer>
52 #include <qtest.h>
53 
54 #ifdef Q_OS_SYMBIAN
55 // In Symbian OS test data is located in applications private dir
56 // Application private dir is default serach path for files, so SRCDIR can be set to empty
57 #define SRCDIR ""
58 #endif
59 
60 Q_DECLARE_METATYPE(QTextDocument*)
61 
62 class tst_QText: public QObject
63 {
64     Q_OBJECT
65 public:
tst_QText()66     tst_QText() {
67         m_lorem = QString::fromLatin1("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.");
68         m_shortLorem = QString::fromLatin1("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
69     }
70 
71 private slots:
72     void loadHtml_data();
73     void loadHtml();
74 
75     void shaping_data();
76     void shaping();
77 
78     void odfWriting_empty();
79     void odfWriting_text();
80     void odfWriting_images();
81 
82     void constructControl();
83     void constructDocument();
84 
85     void newLineReplacement();
86     void formatManipulation();
87     void fontResolution();
88 
89     void layout_data();
90     void layout();
91     void formattedLayout();
92     void paintLayoutToPixmap();
93     void paintLayoutToPixmap_painterFill();
94 
95     void document();
96     void paintDocToPixmap();
97     void paintDocToPixmap_painterFill();
98 
99     void control();
100     void paintControlToPixmap();
101     void paintControlToPixmap_painterFill();
102 
103 private:
104     QSize setupTextLayout(QTextLayout *layout, bool wrap = true, int wrapWidth = 100);
105 
106     QString m_lorem;
107     QString m_shortLorem;
108 };
109 
loadHtml_data()110 void tst_QText::loadHtml_data()
111 {
112     QTest::addColumn<QString>("source");
113     QTest::newRow("empty") << QString();
114     QTest::newRow("simple") << QString::fromLatin1("<html><b>Foo</b></html>");
115     QTest::newRow("simple2") << QString::fromLatin1("<b>Foo</b>");
116 
117     QString parag = QString::fromLatin1("<p>%1</p>").arg(m_lorem);
118     QString header = QString::fromLatin1("<html><head><title>test</title></head><body>");
119     QTest::newRow("long") << QString::fromLatin1("<html><head><title>test</title></head><body>") + parag + parag + parag
120         + parag + parag + parag + parag + parag + parag + parag + parag + parag + parag + parag + parag + parag + parag
121         + QString::fromLatin1("</html>");
122     QTest::newRow("table") <<  header + QLatin1String("<table border=\"1\"1><tr><td>xx</td></tr><tr><td colspan=\"2\">")
123         + parag + QLatin1String("</td></tr></table></html");
124     QTest::newRow("crappy") <<  header + QLatin1String("<table border=\"1\"1><tr><td>xx</td></tr><tr><td colspan=\"2\">")
125         + parag;
126 }
127 
loadHtml()128 void tst_QText::loadHtml()
129 {
130     QFETCH(QString, source);
131     QTextDocument doc;
132     QBENCHMARK {
133         doc.setHtml(source);
134     }
135 }
136 
shaping_data()137 void tst_QText::shaping_data()
138 {
139     QTest::addColumn<QString>("parag");
140     QTest::newRow("empty") << QString();
141     QTest::newRow("lorem") << m_lorem;
142     QTest::newRow("short") << QString::fromLatin1("Lorem ipsum dolor sit amet");
143 
144 #if !defined(Q_OS_SYMBIAN)
145     QFile file(QString::fromLatin1(SRCDIR) + QLatin1String("/bidi.txt"));
146 #else
147     QFile file( SRCDIR "bidi.txt" );
148 #endif
149     QVERIFY(file.open(QFile::ReadOnly));
150     QByteArray data = file.readAll();
151     QVERIFY(data.count() > 1000);
152     QStringList list = QString::fromUtf8(data.data()).split(QLatin1Char('\n'), QString::SkipEmptyParts);
153     QVERIFY(list.count() %2 == 0); // even amount as we have title and then content.
154     for (int i=0; i < list.count(); i+=2) {
155         QTest::newRow(list.at(i).toLatin1()) << list.at(i+1);
156     }
157 }
158 
shaping()159 void tst_QText::shaping()
160 {
161     QFETCH(QString, parag);
162 
163     QTextLayout lay(parag);
164     lay.setCacheEnabled(false);
165 
166     // do one run to make sure any fonts are loaded.
167     lay.beginLayout();
168     lay.createLine();
169     lay.endLayout();
170 
171     QBENCHMARK {
172         lay.beginLayout();
173         lay.createLine();
174         lay.endLayout();
175     }
176 }
177 
odfWriting_empty()178 void tst_QText::odfWriting_empty()
179 {
180     QVERIFY(QTextDocumentWriter::supportedDocumentFormats().contains("ODF")); // odf compiled in
181     QTextDocument *doc = new QTextDocument();
182     // write it
183     QBENCHMARK {
184         QBuffer buffer;
185         buffer.open(QIODevice::WriteOnly);
186         QTextDocumentWriter writer(&buffer, "ODF");
187         writer.write(doc);
188     }
189     delete doc;
190 }
191 
odfWriting_text()192 void tst_QText::odfWriting_text()
193 {
194     QTextDocument *doc = new QTextDocument();
195     QTextCursor cursor(doc);
196     QTextBlockFormat bf;
197     bf.setIndent(2);
198     cursor.insertBlock(bf);
199     cursor.insertText(m_lorem);
200     bf.setTopMargin(10);
201     cursor.insertBlock(bf);
202     cursor.insertText(m_lorem);
203     bf.setRightMargin(30);
204     cursor.insertBlock(bf);
205     cursor.insertText(m_lorem);
206 
207     // write it
208     QBENCHMARK {
209         QBuffer buffer;
210         buffer.open(QIODevice::WriteOnly);
211         QTextDocumentWriter writer(&buffer, "ODF");
212         writer.write(doc);
213     }
214     delete doc;
215 }
216 
odfWriting_images()217 void tst_QText::odfWriting_images()
218 {
219     QTextDocument *doc = new QTextDocument();
220     QTextCursor cursor(doc);
221     cursor.insertText(m_lorem);
222     QImage image(400, 200, QImage::Format_ARGB32_Premultiplied);
223     cursor.insertImage(image);
224     cursor.insertText(m_lorem);
225 
226     // write it
227     QBENCHMARK {
228         QBuffer buffer;
229         buffer.open(QIODevice::WriteOnly);
230         QTextDocumentWriter writer(&buffer, "ODF");
231         writer.write(doc);
232     }
233     delete doc;
234 }
235 
setupTextLayout(QTextLayout * layout,bool wrap,int wrapWidth)236 QSize tst_QText::setupTextLayout(QTextLayout *layout, bool wrap, int wrapWidth)
237 {
238     layout->setCacheEnabled(true);
239 
240     int height = 0;
241     qreal widthUsed = 0;
242     qreal lineWidth = 0;
243 
244     //set manual width
245     if (wrap)
246         lineWidth = wrapWidth;
247 
248     layout->beginLayout();
249     while (1) {
250         QTextLine line = layout->createLine();
251         if (!line.isValid())
252             break;
253 
254         if (wrap)
255             line.setLineWidth(lineWidth);
256     }
257     layout->endLayout();
258 
259     for (int i = 0; i < layout->lineCount(); ++i) {
260         QTextLine line = layout->lineAt(i);
261         widthUsed = qMax(widthUsed, line.naturalTextWidth());
262         line.setPosition(QPointF(0, height));
263         height += int(line.height());
264     }
265     return QSize(qCeil(widthUsed), height);
266 }
267 
constructControl()268 void tst_QText::constructControl()
269 {
270     QTextControl *control = new QTextControl;
271     delete control;
272 
273     QBENCHMARK {
274         QTextControl *control = new QTextControl;
275         delete control;
276     }
277 }
278 
constructDocument()279 void tst_QText::constructDocument()
280 {
281     QTextDocument *doc = new QTextDocument;
282     delete doc;
283 
284     QBENCHMARK {
285         QTextDocument *doc = new QTextDocument;
286         delete doc;
287     }
288 }
289 
290 //this step is needed before giving the string to a QTextLayout
newLineReplacement()291 void tst_QText::newLineReplacement()
292 {
293     QString text = QString::fromLatin1("H\ne\nl\nl\no\n\nW\no\nr\nl\nd");
294 
295     QBENCHMARK {
296         QString tmp = text;
297         tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
298     }
299 }
300 
formatManipulation()301 void tst_QText::formatManipulation()
302 {
303     QFont font;
304 
305     QBENCHMARK {
306         QTextCharFormat format;
307         format.setFont(font);
308     }
309 }
310 
fontResolution()311 void tst_QText::fontResolution()
312 {
313     QFont font;
314     QFont font2;
315     font.setFamily("DejaVu");
316     font2.setBold(true);
317 
318     QBENCHMARK {
319         QFont res = font.resolve(font2);
320     }
321 }
322 
layout_data()323 void tst_QText::layout_data()
324 {
325     QTest::addColumn<bool>("wrap");
326     QTest::newRow("wrap") << true;
327     QTest::newRow("nowrap") << false;
328 }
329 
layout()330 void tst_QText::layout()
331 {
332     QFETCH(bool,wrap);
333     QTextLayout layout(m_shortLorem);
334     setupTextLayout(&layout, wrap);
335 
336     QBENCHMARK {
337         QTextLayout layout(m_shortLorem);
338         setupTextLayout(&layout, wrap);
339     }
340 }
341 
342 //### requires tst_QText to be a friend of QTextLayout
343 /*void tst_QText::stackTextLayout()
344 {
345     QStackTextEngine engine(m_shortLorem, qApp->font());
346     QTextLayout layout(&engine);
347     setupTextLayout(&layout);
348 
349     QBENCHMARK {
350         QStackTextEngine engine(m_shortLorem, qApp->font());
351         QTextLayout layout(&engine);
352         setupTextLayout(&layout);
353     }
354 }*/
355 
formattedLayout()356 void tst_QText::formattedLayout()
357 {
358     //set up formatting
359     QList<QTextLayout::FormatRange> ranges;
360     {
361         QTextCharFormat format;
362         format.setForeground(QColor("steelblue"));
363 
364         QTextLayout::FormatRange formatRange;
365         formatRange.format = format;
366         formatRange.start = 0;
367         formatRange.length = 50;
368 
369         ranges.append(formatRange);
370     }
371 
372     QTextLayout layout(m_shortLorem);
373     layout.setAdditionalFormats(ranges);
374     setupTextLayout(&layout);
375 
376     QBENCHMARK {
377         QTextLayout layout(m_shortLorem);
378         layout.setAdditionalFormats(ranges);
379         setupTextLayout(&layout);
380     }
381 }
382 
paintLayoutToPixmap()383 void tst_QText::paintLayoutToPixmap()
384 {
385     QTextLayout layout(m_shortLorem);
386     QSize size = setupTextLayout(&layout);
387 
388     QBENCHMARK {
389         QPixmap img(size);
390         img.fill(Qt::transparent);
391         QPainter p(&img);
392         layout.draw(&p, QPointF(0, 0));
393     }
394 }
395 
paintLayoutToPixmap_painterFill()396 void tst_QText::paintLayoutToPixmap_painterFill()
397 {
398     QTextLayout layout(m_shortLorem);
399     QSize size = setupTextLayout(&layout);
400 
401     QBENCHMARK {
402         QPixmap img(size);
403         QPainter p(&img);
404         p.setCompositionMode(QPainter::CompositionMode_Source);
405         p.fillRect(0, 0, img.width(), img.height(), Qt::transparent);
406         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
407         layout.draw(&p, QPointF(0, 0));
408     }
409 }
410 
document()411 void tst_QText::document()
412 {
413     QTextDocument *doc = new QTextDocument;
414 
415     QBENCHMARK {
416         QTextDocument *doc = new QTextDocument;
417         doc->setHtml(m_shortLorem);
418     }
419 }
420 
paintDocToPixmap()421 void tst_QText::paintDocToPixmap()
422 {
423     QTextDocument *doc = new QTextDocument;
424     doc->setHtml(m_shortLorem);
425     doc->setTextWidth(300);
426     QSize size = doc->size().toSize();
427 
428     QBENCHMARK {
429         QPixmap img(size);
430         img.fill(Qt::transparent);
431         QPainter p(&img);
432         doc->drawContents(&p);
433     }
434 }
435 
paintDocToPixmap_painterFill()436 void tst_QText::paintDocToPixmap_painterFill()
437 {
438     QTextDocument *doc = new QTextDocument;
439     doc->setHtml(m_shortLorem);
440     doc->setTextWidth(300);
441     QSize size = doc->size().toSize();
442 
443     QBENCHMARK {
444         QPixmap img(size);
445         QPainter p(&img);
446         p.setCompositionMode(QPainter::CompositionMode_Source);
447         p.fillRect(0, 0, img.width(), img.height(), Qt::transparent);
448         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
449         doc->drawContents(&p);
450     }
451 }
452 
control()453 void tst_QText::control()
454 {
455     QTextControl *control = new QTextControl(m_shortLorem);
456 
457     QBENCHMARK {
458         QTextControl *control = new QTextControl;
459         QTextDocument *doc = control->document();
460         doc->setHtml(m_shortLorem);
461     }
462 }
463 
paintControlToPixmap()464 void tst_QText::paintControlToPixmap()
465 {
466     QTextControl *control = new QTextControl;
467     QTextDocument *doc = control->document();
468     doc->setHtml(m_shortLorem);
469     doc->setTextWidth(300);
470     QSize size = doc->size().toSize();
471 
472     QBENCHMARK {
473         QPixmap img(size);
474         img.fill(Qt::transparent);
475         QPainter p(&img);
476         control->drawContents(&p, QRectF(QPointF(0, 0), QSizeF(size)));
477     }
478 }
479 
paintControlToPixmap_painterFill()480 void tst_QText::paintControlToPixmap_painterFill()
481 {
482     QTextControl *control = new QTextControl;
483     QTextDocument *doc = control->document();
484     doc->setHtml(m_shortLorem);
485     doc->setTextWidth(300);
486     QSize size = doc->size().toSize();
487 
488     QBENCHMARK {
489         QPixmap img(size);
490         QPainter p(&img);
491         p.setCompositionMode(QPainter::CompositionMode_Source);
492         p.fillRect(0, 0, img.width(), img.height(), Qt::transparent);
493         p.setCompositionMode(QPainter::CompositionMode_SourceOver);
494         control->drawContents(&p, QRectF(QPointF(0, 0), QSizeF(size)));
495     }
496 }
497 
498 QTEST_MAIN(tst_QText)
499 
500 #include "main.moc"
501