1 /*
2  *  This file is part of Calligra tests
3  *
4  *  Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
5  *  Copyright (C) 2011 C. Boemann <cbo@boemann.dk>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 #include "TestBlockLayout.h"
22 #include "MockRootAreaProvider.h"
23 
24 #include <KoParagraphStyle.h>
25 #include <KoCharacterStyle.h>
26 #include <KoListStyle.h>
27 #include <KoListLevelProperties.h>
28 #include <KoStyleManager.h>
29 #include <KoTextBlockData.h>
30 #include <KoTextBlockBorderData.h>
31 #include <KoTextDocument.h>
32 #include <KoInlineTextObjectManager.h>
33 
34 #include <QSharedPointer>
35 #include <QVariant>
36 #include <QTest>
37 
38 #include <TextLayoutDebug.h>
39 
40 #define FRAME_SPACING 10.0
41 #define LINESPACING_FACTOR 1.16
42 
initTestCase()43 void TestBlockLayout::initTestCase()
44 {
45     m_doc = 0;
46     m_layout = 0;
47 
48     m_loremIpsum = QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing 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.");
49 }
50 
setupTest(const QString & initText)51 void TestBlockLayout::setupTest(const QString &initText)
52 {
53     m_doc = new QTextDocument;
54     Q_ASSERT(m_doc);
55 
56     MockRootAreaProvider *provider = new MockRootAreaProvider();
57     Q_ASSERT(provider);
58     KoTextDocument(m_doc).setInlineTextObjectManager(new KoInlineTextObjectManager);
59 
60     m_doc->setDefaultFont(QFont("Sans Serif", 12.0, QFont::Normal, false)); //do it manually since we do not load the appDefaultStyle
61 
62     m_styleManager = new KoStyleManager(0);
63     KoTextDocument(m_doc).setStyleManager(m_styleManager);
64 
65     m_layout = new KoTextDocumentLayout(m_doc, provider);
66     Q_ASSERT(m_layout);
67     m_doc->setDocumentLayout(m_layout);
68 
69     //m_area = provider->provide(m_layout);
70 
71     m_block = m_doc->begin();
72     if (initText.length() > 0) {
73         QTextCursor cursor(m_doc);
74         cursor.insertText(initText);
75         KoParagraphStyle style;
76         style.setFontPointSize(12.0);
77         style.setStyleId(101); // needed to do manually since we don't use the stylemanager
78         QTextBlock b2 = m_doc->begin();
79         while (b2.isValid()) {
80             style.applyStyle(b2);
81             b2 = b2.next();
82         }
83     }
84 }
85 
86 
testLineBreaking()87 void TestBlockLayout::testLineBreaking()
88 {
89     setupTest(m_loremIpsum);
90     m_layout->layout();
91     QTextLayout *blockLayout = m_block.layout();
92 
93     //QCOMPARE(blockLayout->lineCount(), 16);
94     QCOMPARE(blockLayout->lineForTextPosition(1).width(), 200.0);
95 }
96 
97 
testBasicLineSpacing()98 void TestBlockLayout::testBasicLineSpacing()
99 {
100     /// Tests incrementing Y pos based on the font size
101     setupTest(m_loremIpsum);
102     QTextCursor cursor(m_doc);
103     cursor.setPosition(0);
104     cursor.setPosition(m_loremIpsum.length() - 1, QTextCursor::KeepAnchor);
105     QTextCharFormat charFormat = cursor.charFormat();
106     charFormat.setFontPointSize(12.0);
107     cursor.mergeCharFormat(charFormat);
108     m_layout->layout();
109     QTextLayout *blockLayout = m_block.layout();
110 
111     const qreal fontHeight12 = 12.0;
112     qreal lineSpacing12 = fontHeight12 * LINESPACING_FACTOR;
113     const qreal fontHeight18 = 18.0;
114     qreal lineSpacing18 = fontHeight18 * LINESPACING_FACTOR;
115 
116     // QCOMPARE(blockLayout->lineCount(), 15);
117     QTextLine line;
118     for (int i = 0; i < 15; i++) {
119         line = blockLayout->lineAt(i);
120         QVERIFY(line.isValid());
121         // The reason for this weird check is that the values are stored internally
122         // as 26.6 fixed point integers. The entire internal text layout is
123         // actually done using fixed point arithmetic. This is due to embedded
124         // considerations, and offers general performance benefits across all
125         // platforms.
126         //qDebug() << i << qAbs(line.y() - i * lineSpacing12);
127         QVERIFY(qAbs(line.y() - (i * lineSpacing12  + 100.0)) < ROUNDING);
128     }
129 
130     // make first word smaller, should have zero effect on lineSpacing.
131     cursor.setPosition(0);
132     cursor.setPosition(11, QTextCursor::KeepAnchor);
133     charFormat.setFontPointSize(10);
134     cursor.mergeCharFormat(charFormat);
135     m_layout->layout();
136     for (int i = 0; i < 15; i++) {
137         line = blockLayout->lineAt(i);
138         QVERIFY(line.isValid());
139         //qDebug() << i << qAbs(line.y() - i * lineSpacing12);
140         QVERIFY(qAbs(line.y() - (i * lineSpacing12 + 100.0)) < ROUNDING);
141     }
142 
143     // make first word on second line word bigger, should move that line down a little.
144     int pos = blockLayout->lineAt(1).textStart();
145     cursor.setPosition(pos);
146     cursor.setPosition(pos + 12, QTextCursor::KeepAnchor);
147     charFormat.setFontPointSize(18);
148     cursor.mergeCharFormat(charFormat);
149     m_layout->layout();
150     line = blockLayout->lineAt(0);
151     QCOMPARE(line.y(), 0.0 + 100.0);
152     line = blockLayout->lineAt(1);
153     QVERIFY(qAbs(line.y() - (lineSpacing12 + 100.0)) < ROUNDING);
154 
155     for (int i = 2; i < 15; i++) {
156         line = blockLayout->lineAt(i);
157 //qDebug() << "i: " << i << " gives: " << line.y() << (lineSpacing12 + lineSpacing18 + (i - 2) * lineSpacing12);
158         QVERIFY(qAbs(line.y() - (lineSpacing12 + lineSpacing18 + (i - 2) * lineSpacing12 + 100.0)) < ROUNDING);
159     }
160 }
161 
testBasicLineSpacing2()162 void TestBlockLayout::testBasicLineSpacing2()
163 {
164     setupTest(m_loremIpsum);
165     QTextCursor cursor(m_doc);
166     cursor.insertText("foo\n\n"); // insert empty parag;
167 
168     m_layout->layout();
169     QTextBlock block = m_doc->begin().next();
170     QTextLayout *blockLayout = block.layout();
171     QVERIFY(block.isValid());
172     blockLayout = block.layout();
173     QCOMPARE(blockLayout->lineCount(), 1);
174 
175     block = block.next();
176     QVERIFY(block.isValid());
177     blockLayout = block.layout();
178     //qDebug() << blockLayout->lineAt(0).y();
179     QVERIFY(qAbs(blockLayout->lineAt(0).y() - (2*12.0*LINESPACING_FACTOR + 100.0)) < ROUNDING);
180 }
181 
testFixedLineSpacing()182 void TestBlockLayout::testFixedLineSpacing()
183 {
184     setupTest(QString("Line1")+QChar(0x2028)+"Line2"+QChar(0x2028)+"Line3");
185 
186     KoParagraphStyle style;
187     style.setFontPointSize(12.0);
188     style.setLineHeightAbsolute(28.0);
189     QTextBlock block = m_doc->begin();
190     style.applyStyle(block);
191 
192     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 28.0);
193 
194     m_layout->layout();
195     QTextLayout *blockLayout = block.layout();
196 
197     // lines with fontsize less than the fixed height are bottom aligned, resulting in
198     // positive y for first line
199     QCOMPARE(blockLayout->lineAt(0).y(), 28.0-12.0 + 100.0);
200     QCOMPARE(blockLayout->lineAt(1).y(), 28.0 + 28.0-12.0 + 100.0);
201     QCOMPARE(blockLayout->lineAt(2).y(), 56.0 + 28.0-12.0 + 100.0);
202 
203     style.setLineHeightAbsolute(8.0);
204     style.applyStyle(block);
205 
206     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 8.0);
207 
208     m_layout->layout();
209     blockLayout = block.layout();
210 
211     // lines with fontsize more than the fixed height are bottom aligned, resulting in
212     //negative y for first line
213     QCOMPARE(blockLayout->lineAt(0).y(), 8.0-12.0 + 100.0);
214     QCOMPARE(blockLayout->lineAt(1).y(), 8.0-12.0 + 8.0 + 100.0);
215     QCOMPARE(blockLayout->lineAt(2).y(), 8.0-12.0 + 8.0 + 8.0 + 100.0);
216 }
217 
testPercentageLineSpacing()218 void TestBlockLayout::testPercentageLineSpacing()
219 {
220     setupTest(QString("Line1")+QChar(0x2028)+"Line2"+QChar(0x2028)+"Line3");
221 
222     KoParagraphStyle style;
223     style.setFontPointSize(12.0);
224     style.setLineHeightPercent(150); // NOTE: This is *PercentLineHeight*, so operates on font size * linespacing
225     QTextBlock block = m_doc->begin();
226     style.applyStyle(block);
227 
228     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 150.0);
229 
230     m_layout->layout();
231     QTextLayout *blockLayout = block.layout();
232 
233     const qreal origlineheight = 12.0 * LINESPACING_FACTOR;
234     qreal newlineheight = origlineheight * 1.5;
235 
236     QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0);
237     QVERIFY(qAbs(blockLayout->lineAt(1).y() - (0.0 + newlineheight + 100.0)) < ROUNDING);
238     QVERIFY(qAbs(blockLayout->lineAt(2).y() - (0.0 + newlineheight + newlineheight + 100.0)) < ROUNDING);
239 
240     style.setLineHeightPercent(50);
241     style.applyStyle(block);
242 
243     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 50.0);
244 
245     m_layout->layout();
246     blockLayout = block.layout();
247 
248     newlineheight = origlineheight * 0.5;
249 
250     QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0);
251     QVERIFY(qAbs(blockLayout->lineAt(1).y() - (0.0 + newlineheight + 100.0)) < ROUNDING);
252     QVERIFY(qAbs(blockLayout->lineAt(2).y() - (0.0 + newlineheight + newlineheight + 100.0)) < ROUNDING);
253 }
254 
testAdvancedLineSpacing()255 void TestBlockLayout::testAdvancedLineSpacing()
256 {
257     setupTest("Line1\nLine2\nLine3\nLine4\nLine5\nLine6\nLine7");
258 
259     KoParagraphStyle style;
260     style.setFontPointSize(12.0);
261     style.setLineHeightPercent(80);
262     QTextBlock block = m_doc->begin();
263     style.applyStyle(block); //line1
264 
265     // check if styles do their work ;)
266     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 80.0);
267 
268     block = block.next();
269     QVERIFY(block.isValid()); //line2
270     style.setLineHeightAbsolute(28.0); // removes the percentage
271     style.applyStyle(block);
272     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 0.0);
273     QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 28.0);
274 
275     block = block.next();
276     QVERIFY(block.isValid()); // line3
277     style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 40.0));
278     style.setLineHeightPercent(120);
279     style.applyStyle(block);
280 
281     block = block.next();
282     QVERIFY(block.isValid()); // line4
283     style.remove(KoParagraphStyle::FixedLineHeight);
284     style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 5.0));
285     style.applyStyle(block);
286 
287     block = block.next();
288     QVERIFY(block.isValid()); // line5
289     style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 0.0));
290     style.setLineSpacing(8.0);
291     style.remove(KoParagraphStyle::PercentLineHeight);
292     style.applyStyle(block);
293     block = block.next();
294     QVERIFY(block.isValid()); // line6
295     style.setLineSpacingFromFont(true);
296     style.setLineHeightPercent(100);
297     style.remove(KoParagraphStyle::LineSpacing);
298     style.applyStyle(block);
299 
300     const qreal lineheight = 12.0 * LINESPACING_FACTOR;
301 
302     m_layout->layout();
303 
304     block = m_doc->begin(); // line1
305     for (int i = 1; i < 7; ++i) {
306         qInfo()<<i<<block.layout()->lineAt(0).y();
307         block = block.next();
308     }
309 
310     const qreal line1_y = 0.0  + 100.0;
311     const qreal line1_height = 0.8 * lineheight;
312     const qreal line2_y = line1_y + line1_height + 28.0 - lineheight; // bottom aligned
313     const qreal line2_height = 28.0;
314     const qreal line3_y = line1_y + line1_height +  line2_height;
315     const qreal line3_height = 1.2 * lineheight; // percentage overrides minimum so percentage value is the right to test against
316     const qreal line4_y = line1_y + line1_height + line2_height + line3_height;
317     const qreal line4_height = lineheight;
318     const qreal line5_y = line1_y + line1_height + line2_height + line3_height + line4_height;
319     const qreal line5_height = lineheight + 8.0 /*spacing*/; // minimum of 5 is irrelevant (Note: percentage of 1.2 has been removed)
320     const qreal line6_y = line1_y + line1_height + line2_height + line3_height + line4_height + line5_height;
321     const qreal line6_height = lineheight;
322     const qreal line7_y = line1_y + line1_height + line2_height + line3_height + line4_height + line5_height + line6_height;
323 
324     block = m_block; // line1
325     QVERIFY(block.isValid());
326     QTextLayout *blockLayout = block.layout();
327     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line1_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line1_y).toLatin1());
328 
329     block = block.next(); // line2 with fixed we are bottom aligned so offset by 28.0-lineheight
330     QVERIFY(block.isValid());
331     blockLayout = block.layout();
332     //qDebug() << blockLayout->lineAt(0).y();
333     QEXPECT_FAIL("", "FIXME: Should not this calculate with lineheight and not fontsize?", Continue);
334     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line2_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line2_y).toLatin1());
335 
336     block = block.next(); // line3
337     QVERIFY(block.isValid());
338     blockLayout = block.layout();
339     //qDebug() << blockLayout->lineAt(0).y();
340     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line3_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line3_y).toLatin1());
341 
342     block = block.next(); // line4
343     QVERIFY(block.isValid());
344     blockLayout = block.layout();
345     //qDebug() << blockLayout->lineAt(0).y();
346     // percentage overrides minimum so percentage value is the right to test against
347     //QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 40.0 + 100.0)) < ROUNDING);
348     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line4_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line4_y).toLatin1());
349 
350     block = block.next(); // line5
351     QVERIFY(block.isValid());
352     blockLayout = block.layout();
353     //qDebug() << blockLayout->lineAt(0).y();
354     // minimum of 5 is irrelevant and percentage of 1.2 was still there
355     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line5_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line5_y).toLatin1());
356 
357     block = block.next(); // line6
358     QVERIFY(block.isValid());
359     blockLayout = block.layout();
360     //qDebug() << blockLayout->lineAt(0).y();
361     QEXPECT_FAIL("", "FIXME: Should not this calculate with lineheight and not fontsize?", Continue);
362     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line6_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line6_y).toLatin1());
363 
364     block = block.next(); // line 7
365     QVERIFY(block.isValid());
366     blockLayout = block.layout();
367     //qDebug() << blockLayout->lineAt(0).y();
368     QEXPECT_FAIL("", "FIXME: setLineSpacingFromFont(true) has been set on line 6, so what will the linespacing be then?", Continue);
369     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - line7_y) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(line7_y).toLatin1());
370 }
371 
testEmptyLineHeights()372 void TestBlockLayout::testEmptyLineHeights()
373 {
374     // 1) a blank line is affected by the line break after
375     //  1b) a line with contents is not affected by the linebreak
376     // 2) a final line if blank can have it's height specified by a special textstyle
377     //    If the special style is empty the par style is used for the line
378 
379     setupTest(QString("")+QChar(0x2028)+QChar(0x2028)+"\nNextBlock");
380     QTextCursor cursor(m_doc);
381 
382     QTextCharFormat bigCharFormat;
383     bigCharFormat.setFontPointSize(20.0);
384     QTextCharFormat smallCharFormat;
385     smallCharFormat.setFontPointSize(8.0);
386 
387     KoParagraphStyle style;
388     style.setFontPointSize(12.0);
389     style.setLineHeightPercent(100);
390 
391     QTextBlock block = m_doc->begin();
392     style.applyStyle(block);
393 
394     // apply formats
395     cursor.setPosition(0);
396     cursor.setPosition(1, QTextCursor::KeepAnchor);
397     cursor.mergeCharFormat(bigCharFormat);
398     cursor.setPosition(1);
399     cursor.setPosition(2, QTextCursor::KeepAnchor);
400     cursor.mergeCharFormat(smallCharFormat);
401 
402 
403     m_layout->layout();
404     QTextLayout *blockLayout = block.layout();
405     QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0);
406 
407     qreal exp = (20.0 * LINESPACING_FACTOR) + 100.0;
408     QVERIFY2(qAbs(blockLayout->lineAt(1).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(1).y()).arg(exp).toLatin1());
409 
410     exp = (20.0 * LINESPACING_FACTOR) + (8.0 * LINESPACING_FACTOR) + 100.0;
411     QVERIFY2(qAbs(blockLayout->lineAt(2).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(2).y()).arg(exp).toLatin1());
412     block = block.next();
413     QVERIFY(block.isValid());
414     blockLayout = block.layout();
415     exp = (20.0 * LINESPACING_FACTOR) + (8.0 * LINESPACING_FACTOR) + (12.0 * LINESPACING_FACTOR) + 100.0;
416     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(exp).toLatin1());
417 
418     // Now do the test again but with last line having bigger font
419     block = m_doc->begin();
420     QTextBlockFormat blockFormat = block.blockFormat();
421     KoCharacterStyle charStyle;
422     charStyle.setFontPointSize(20.0);
423     blockFormat.setProperty(KoParagraphStyle::EndCharStyle, QVariant::fromValue< QSharedPointer<KoCharacterStyle> >(QSharedPointer<KoCharacterStyle>(&charStyle)));
424     cursor.setBlockFormat(blockFormat);
425 
426     m_layout->layout();
427     blockLayout = block.layout();
428     QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0);
429     exp = (20.0 * LINESPACING_FACTOR) + 100.0;
430     QVERIFY2(qAbs(blockLayout->lineAt(1).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(1).y()).arg(exp).toLatin1());
431     exp = (20.0 * LINESPACING_FACTOR) + (8.0 * LINESPACING_FACTOR) + 100.0;
432     QVERIFY2(qAbs(blockLayout->lineAt(2).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(2).y()).arg(exp).toLatin1());
433     block = block.next();
434     QVERIFY(block.isValid());
435     blockLayout = block.layout();
436     exp = (20.0 + 8.0 + 20.0) * LINESPACING_FACTOR + 100.0;
437     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(exp).toLatin1());
438 
439     // Now do the test again but with last line having a small font
440     block = m_doc->begin();
441     KoCharacterStyle charStyle2;
442     charStyle2.setFontPointSize(6.0);
443     blockFormat.setProperty(KoParagraphStyle::EndCharStyle, QVariant::fromValue< QSharedPointer<KoCharacterStyle> >(QSharedPointer<KoCharacterStyle>(&charStyle2)));
444     cursor.setBlockFormat(blockFormat);
445 
446     m_layout->layout();
447     blockLayout = block.layout();
448     QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0);
449     exp = (20.0 * LINESPACING_FACTOR) + 100.0;
450     QVERIFY2(qAbs(blockLayout->lineAt(1).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(1).y()).arg(exp).toLatin1());
451     exp = (20.0 * LINESPACING_FACTOR) + (8.0 * LINESPACING_FACTOR) + 100.0;
452     QVERIFY2(qAbs(blockLayout->lineAt(2).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(2).y()).arg(exp).toLatin1());
453     block = block.next();
454     QVERIFY(block.isValid());
455     blockLayout = block.layout();
456     exp = (20.0 * LINESPACING_FACTOR) + (8.0 * LINESPACING_FACTOR) + (6.0 * LINESPACING_FACTOR) + 100.0;
457     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(exp).toLatin1());
458 }
459 
460 
461 // Test that spacing between blocks are the max of bottomMargin and topMargin
462 // of the top and bottom block respectively
463 // If the block doesn't connect to another block (top and bottom of pages or
464 // table cells, if blocks are intersected with say a table. Then it's
465 // just the plain margin
466 // For completeness sake we test with 3 blocks just to make sure it works
testBlockSpacing()467 void TestBlockLayout::testBlockSpacing()
468 {
469     setupTest(m_loremIpsum);
470     QTextCursor cursor(m_doc);
471     QTextCursor cursor1(m_doc);
472 
473     // create second parag
474     cursor.setPosition(m_loremIpsum.length());
475     cursor.insertText("\n");
476     cursor.insertText(m_loremIpsum);
477 
478     // create third parag
479     cursor.insertText("\n");
480     cursor.insertText(m_loremIpsum);
481     m_layout->layout();
482 
483     QTextBlock block2 = m_doc->begin().next();
484     QTextBlock block3 = m_doc->begin().next().next();
485 
486     QTextCursor cursor2(block2);
487     QTextCursor cursor3(block3);
488 
489     // and test spacing between blocks
490     QTextBlockFormat bf1 = cursor1.blockFormat();
491     QTextLayout *block1Layout = m_block.layout();
492     QTextBlockFormat bf2 = cursor2.blockFormat();
493     QTextLayout *block2Layout = block2.layout();
494     QTextBlockFormat bf3 = cursor3.blockFormat();
495     QTextLayout *block3Layout = block3.layout();
496     int lastLineNum = block1Layout->lineCount() - 1;
497     const qreal lineSpacing = 12.0 * LINESPACING_FACTOR;
498     KoTextDocument(m_doc).setParaTableSpacingAtStart(false);
499     bool paraTableSpacingAtStart = KoTextDocument(m_doc).paraTableSpacingAtStart();
500 
501     qreal spaces[3] = {0.0, 3.0, 6.0};
502     for (int t1 = 0; t1 < 3; ++t1) {
503         for (int t2 = 0; t2 < 3; ++t2) {
504             for (int t3 = 0; t3 < 3; ++t3) {
505                 for (int b1 = 0; b1 < 3; ++b1) {
506                     bf1.setTopMargin(spaces[t1]);
507                     bf1.setBottomMargin(spaces[b1]);
508                     cursor1.setBlockFormat(bf1);
509                     for (int b2 = 0; b2 < 3; ++b2) {
510                         bf2.setTopMargin(spaces[t2]);
511                         bf2.setBottomMargin(spaces[b2]);
512                         cursor2.setBlockFormat(bf2);
513                         for (int b3 = 0; b3 < 3; ++b3) {
514                             bf3.setTopMargin(spaces[t3]);
515                             bf3.setBottomMargin(spaces[b3]);
516                             cursor3.setBlockFormat(bf3);
517                             m_layout->layout();
518 
519                             // Now lets do the actual testing
520                             //Above first block is just plain
521                             if (paraTableSpacingAtStart) {
522                                 QVERIFY(qAbs(block1Layout->lineAt(0).y() - spaces[t1]) < ROUNDING);
523                             } else {
524                                 QVERIFY(qAbs(block1Layout->lineAt(0).y() - (0.0 + 100.0)) < ROUNDING);
525                             }
526 
527                             // Between 1st and 2nd block is max of spaces
528                             QVERIFY(qAbs((block2Layout->lineAt(0).y() - block1Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b1], spaces[t2])) < ROUNDING);
529 
530 
531                             // Between 2nd and 3rd block is max of spaces
532                             QVERIFY(qAbs((block3Layout->lineAt(0).y() - block2Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b2], spaces[t3])) < ROUNDING);
533 
534                             //Below 3rd block is just plain
535                             //QVERIFY(qAbs(bottom()-block3Layout->lineAt(lastLineNum).y() - lineSpacing - spaces[t1]) < ROUNDING);
536                         }
537                     }
538                 }
539             }
540         }
541     }
542 
543     KoTextDocument(m_doc).setParaTableSpacingAtStart(true);
544     paraTableSpacingAtStart = KoTextDocument(m_doc).paraTableSpacingAtStart();
545 
546     for (int t1 = 0; t1 < 3; ++t1) {
547         for (int t2 = 0; t2 < 3; ++t2) {
548             for (int t3 = 0; t3 < 3; ++t3) {
549                 for (int b1 = 0; b1 < 3; ++b1) {
550                     bf1.setTopMargin(spaces[t1]);
551                     bf1.setBottomMargin(spaces[b1]);
552                     cursor1.setBlockFormat(bf1);
553                     for (int b2 = 0; b2 < 3; ++b2) {
554                         bf2.setTopMargin(spaces[t2]);
555                         bf2.setBottomMargin(spaces[b2]);
556                         cursor2.setBlockFormat(bf2);
557                         for (int b3 = 0; b3 < 3; ++b3) {
558                             bf3.setTopMargin(spaces[t3]);
559                             bf3.setBottomMargin(spaces[b3]);
560                             cursor3.setBlockFormat(bf3);
561                             m_layout->layout();
562 
563                             // Now lets do the actual testing
564                             //Above first block is just plain
565                             if (paraTableSpacingAtStart) {
566                                 QVERIFY(qAbs(block1Layout->lineAt(0).y() - (spaces[t1] + 100.0)) < ROUNDING);
567                             } else {
568                                 QVERIFY(qAbs(block1Layout->lineAt(0).y() - (0.0 + 100.0)) < ROUNDING);
569                             }
570 
571                             // Between 1st and 2nd block is max of spaces
572                             QVERIFY(qAbs((block2Layout->lineAt(0).y() - block1Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b1], spaces[t2])) < ROUNDING);
573 
574 
575                             // Between 2nd and 3rd block is max of spaces
576                             QVERIFY(qAbs((block3Layout->lineAt(0).y() - block2Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b2], spaces[t3])) < ROUNDING);
577 
578                             //Below 3rd block is just plain
579                             //QVERIFY(qAbs(bottom()-block3Layout->lineAt(lastLineNum).y() - lineSpacing - spaces[t1]) < ROUNDING);
580                         }
581                     }
582                 }
583             }
584         }
585     }
586 }
587 
testLeftRightMargins()588 void TestBlockLayout::testLeftRightMargins()
589 {
590     setupTest(m_loremIpsum);
591     QTextCursor cursor(m_doc);
592     QTextBlockFormat bf = cursor.blockFormat();
593     bf.setLeftMargin(10.0);
594     cursor.setBlockFormat(bf);
595     m_layout->layout();
596     QTextLayout *blockLayout = m_block.layout();
597     QCOMPARE(blockLayout->lineAt(0).x(), 10.0 + 100.0);
598     QCOMPARE(blockLayout->lineAt(0).width(), 190.0);
599 
600     bf.setRightMargin(15.0);
601     cursor.setBlockFormat(bf);
602     m_layout->layout();
603     QCOMPARE(blockLayout->lineAt(0).x(), 10.0 + 100.0);
604     QCOMPARE(blockLayout->lineAt(0).width(), 175.0);
605 
606     bf.setLeftMargin(0.0);
607     cursor.setBlockFormat(bf);
608     m_layout->layout();
609     QCOMPARE(blockLayout->lineAt(0).x(), 0.0 + 100.0);
610     QCOMPARE(blockLayout->lineAt(0).width(), 185.0); // still uses the right margin of 15
611 
612     // create second parag
613     cursor.setPosition(m_loremIpsum.length());
614     cursor.insertText("\n");
615     bf.setTopMargin(12);
616     cursor.setBlockFormat(bf);
617     cursor.insertText(m_loremIpsum);
618     m_layout->layout();
619     QCOMPARE(blockLayout->lineAt(0).x(), 0.0 + 100.0); // parag 1
620     QCOMPARE(blockLayout->lineAt(0).width(), 185.0);
621 
622     // and test parag 2
623     QTextBlock block2 = m_doc->begin().next();
624     QTextLayout *block2Layout = block2.layout();
625     QCOMPARE(block2Layout->lineAt(0).x(), 0.0 + 100.0);
626     QCOMPARE(block2Layout->lineAt(0).width(), 185.0);
627 }
628 
testTextIndent()629 void TestBlockLayout::testTextIndent()
630 {
631     setupTest(m_loremIpsum);
632     QTextCursor cursor(m_doc);
633     QTextBlockFormat bf = cursor.blockFormat();
634     bf.setTextIndent(20);
635     cursor.setBlockFormat(bf);
636 
637     m_layout->layout();
638     QTextLayout *blockLayout = m_block.layout();
639 
640     QCOMPARE(blockLayout->lineAt(0).x(), 20.0 + 100.0);
641     QCOMPARE(blockLayout->lineAt(0).width(), 180.0);
642     QCOMPARE(blockLayout->lineAt(1).x(), 0.0 + 100.0);
643     QCOMPARE(blockLayout->lineAt(1).width(), 200.0);
644 
645     // Add som left margin to check for no correlation
646     bf.setLeftMargin(15.0);
647     cursor.setBlockFormat(bf);
648     m_layout->layout();
649     QCOMPARE(blockLayout->lineAt(0).x(), 35.0 + 100.0);
650     QCOMPARE(blockLayout->lineAt(0).width(), 165.0);
651     QCOMPARE(blockLayout->lineAt(1).x(), 15.0 + 100.0);
652     QCOMPARE(blockLayout->lineAt(1).width(), 185.0);
653 
654     // create second parag and see it works too
655     cursor.setPosition(m_loremIpsum.length());
656     cursor.insertText("\n");
657     bf.setTopMargin(12);
658     cursor.setBlockFormat(bf);
659     cursor.insertText(m_loremIpsum);
660     m_layout->layout();
661     QTextBlock block2 = m_doc->begin().next();
662     QTextLayout *block2Layout = block2.layout();
663     QCOMPARE(block2Layout->lineAt(0).x(), 35.0 + 100.0);
664     QCOMPARE(block2Layout->lineAt(0).width(), 165.0);
665     QCOMPARE(block2Layout->lineAt(1).x(), 15.0 + 100.0);
666     QCOMPARE(block2Layout->lineAt(1).width(), 185.0);
667 }
668 
testTabs_data()669 void TestBlockLayout::testTabs_data()
670 {
671     static const struct TestCaseData {
672         bool relativeTabs;
673         qreal leftMargin;
674         qreal textIndent;
675         qreal rightMargin;
676         qreal expected; // expected value of pos=2 of each line
677     } testcaseDataList[] = {
678         { true, 0, 0, 0, 50},
679         { true, 0, 0, 5, 50},
680         { true, 0, 10, 0, 50},
681         { true, 0, 10, 5, 50},
682         { true, 0, -10, 0, 0},
683         { true, 0, -10, 5, 0},
684         { true, 20, 0, 0, 70},
685         { true, 20, 0, 5, 70},
686         { true, 20, 10, 0, 70},
687         { true, 20, 10, 5, 70},
688         { true, 20, -10, 0, 20},
689         { true, 20, -10, 5, 20},
690         { true, -20, 0, 0+20, 30}, //+20 to avoid extra tab fitting in
691         { true, -20, 0, 5+20, 30}, //+20 to avoid extra tab fitting in
692         { true, -20, 10, 0+20, 30}, //+20 to avoid extra tab fitting in
693         { true, -20, 10, 5+20, 30}, //+20 to avoid extra tab fitting in
694         { true, -20, -10, 0+20, -20}, //+20 to avoid extra tab fitting in
695         { true, -20, -10, 5+20, -20}, //+20 to avoid extra tab fitting in
696 
697         { false, 0, 0, 0, 50},
698         { false, 0, 0, 5, 50},
699         { false, 0, 10, 0, 50},
700         { false, 0, 10, 5, 50},
701         { false, 0, -10, 0, 0},
702         { false, 0, -10, 5, 0},
703         { false, 20, 0, 0, 50},
704         { false, 20, 0, 5, 50},
705         { false, 20, 10, 0, 50},
706         { false, 20, 10, 5, 50},
707         { false, 20, -10, 0, 50},
708         { false, 20, -10, 5, 50},
709         { false, -20, 0, 0+70, 0}, //+70 to avoid extra tab fitting in
710         { false, -20, 0, 5+70, 0}, //+70 to avoid extra tab fitting in
711         { false, -20, 10, 0+70, 0}, //+70 to avoid extra tab fitting in
712         { false, -20, 10, 5+70, 0}, //+70 to avoid extra tab fitting in
713         { false, -20, -10, 0+70, 0}, //+70 to avoid extra tab fitting in
714         { false, -20, -10, 5+70, 0}, //+70 to avoid extra tab fitting in
715     };
716     static const int testcasesCount = sizeof(testcaseDataList)/sizeof(testcaseDataList[0]);
717 
718     QTest::addColumn<bool>("relativeTabs");
719     QTest::addColumn<qreal>("leftMargin");
720     QTest::addColumn<qreal>("textIndent");
721     QTest::addColumn<qreal>("rightMargin");
722     QTest::addColumn<qreal>("expected");
723 
724     for (int i = 0; i < testcasesCount; ++i) {
725         const TestCaseData &testcaseData = testcaseDataList[i];
726 
727         QTest::newRow(QString::number(i).toLatin1())
728             << testcaseData.relativeTabs
729             << testcaseData.leftMargin
730             << testcaseData.textIndent
731             << testcaseData.rightMargin
732             << testcaseData.expected;
733     }
734 }
735 
testTabs()736 void TestBlockLayout::testTabs()
737 {
738     QFETCH(bool, relativeTabs);
739     QFETCH(qreal, leftMargin);
740     QFETCH(qreal, textIndent);
741     QFETCH(qreal, rightMargin);
742     QFETCH(qreal, expected); // expected value of pos=2 of each line
743 
744     setupTest("x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\te");
745     QTextCursor cursor(m_doc);
746     QTextBlockFormat bf = cursor.blockFormat();
747     cursor.setBlockFormat(bf);
748 
749     m_layout->layout();
750     QTextLayout *blockLayout = m_block.layout();
751 
752     const qreal tabSpacing = 50.0; // in pt
753     m_layout->setTabSpacing(tabSpacing);
754 
755     KoTextDocument(m_doc).setRelativeTabs(relativeTabs);
756     bf.setLeftMargin(leftMargin);
757     bf.setTextIndent(textIndent);
758     bf.setRightMargin(rightMargin);
759     cursor.setBlockFormat(bf);
760     m_layout->layout();
761 
762     for (int pos=0; pos<4; pos++) {
763         if (pos==0)
764             QCOMPARE(blockLayout->lineAt(0).cursorToX(pos*2), leftMargin + textIndent);
765         else {
766             warnTextLayout << blockLayout->lineAt(0).cursorToX(pos*2) << expected+(pos-1)*tabSpacing;
767             QVERIFY(qAbs(blockLayout->lineAt(0).cursorToX(pos*2) - (expected+(pos-1)*tabSpacing)) < 1.0);
768         }
769     }
770     if (textIndent == 0.0) { // excluding known fails
771         for (int pos=0; pos<4; pos++) {
772             // pos==0 is known to fail see https://bugs.kde.org/show_bug.cgi?id=239819
773             if (pos!=0)
774                 QVERIFY(qAbs(blockLayout->lineAt(1).cursorToX(pos*2+8)- (expected+(pos-1)*tabSpacing)) < 1.0);
775         }
776         for (int pos=0; pos<4; pos++) {
777             // pos==0 is known to fail see https://bugs.kde.org/show_bug.cgi?id=239819
778             if (pos!=0)
779                 QVERIFY(qAbs(blockLayout->lineAt(2).cursorToX(pos*2+16)- (expected+(pos-1)*tabSpacing)) < 1.0);
780         }
781     }
782 }
783 
testBasicTextAlignments()784 void TestBlockLayout::testBasicTextAlignments()
785 {
786     setupTest("Left\nCenter\nRight");
787 
788     QTextCursor cursor(m_doc);
789     QTextBlockFormat format = cursor.blockFormat();
790     format.setAlignment(Qt::AlignLeft);
791     cursor.setBlockFormat(format);
792     cursor.setPosition(6);
793     format.setAlignment(Qt::AlignHCenter);
794     cursor.setBlockFormat(format);
795     cursor.setPosition(13);
796     format.setAlignment(Qt::AlignRight);
797     cursor.setBlockFormat(format);
798 
799     m_layout->layout();
800     QTextLayout *blockLayout = m_block.layout();
801     QCOMPARE(blockLayout->lineAt(0).x(), 100.0);
802 
803     QTextBlock block = m_doc->begin().next();
804     QVERIFY(block.isValid());
805     blockLayout = block.layout();
806 
807     QRectF rect = blockLayout->lineAt(0).naturalTextRect();
808     QVERIFY(rect.x() > 60);
809     QCOMPARE(rect.x() + rect.width() + (200 - rect.right()), 200.0);
810     block = block.next();
811     QVERIFY(block.isValid());
812     blockLayout = block.layout();
813     rect = blockLayout->lineAt(0).naturalTextRect();
814     QVERIFY(rect.x() > 150);
815     QVERIFY(rect.right() >= 200.0 + 100.0);
816 }
817 
testTextAlignments()818 void TestBlockLayout::testTextAlignments()
819 {
820     // TODO justified & justified, last line
821     setupTest("Left\nRight\nﺵﻻﺆﻴﺜﺒ\nﺵﻻﺆﻴﺜﺒ\nLast Line.");
822     KoParagraphStyle start;
823     start.setFontPointSize(12.0);
824     start.setAlignment(Qt::AlignLeading);
825     KoParagraphStyle end;
826     end.setFontPointSize(12.0);
827     end.setAlignment(Qt::AlignTrailing);
828 
829     KoParagraphStyle startRTL;
830     startRTL.setFontPointSize(12.0);
831     startRTL.setAlignment(Qt::AlignLeading);
832     startRTL.setTextProgressionDirection(KoText::RightLeftTopBottom);
833     KoParagraphStyle endRTL;
834     endRTL.setAlignment(Qt::AlignTrailing);
835     endRTL.setTextProgressionDirection(KoText::RightLeftTopBottom);
836     endRTL.setFontPointSize(12.0);
837 
838     QTextBlock block = m_doc->begin();
839     start.applyStyle(block);
840     block = block.next();
841     end.applyStyle(block);
842     block = block.next();
843     startRTL.applyStyle(block);
844     block = block.next();
845     endRTL.applyStyle(block);
846     block = block.next();
847     endRTL.applyStyle(block);
848 
849     m_layout->layout();
850     QTextLayout *blockLayout = m_block.layout();
851 
852     // line 'Left'
853     QRectF rect = blockLayout->lineAt(0).naturalTextRect();
854     QCOMPARE(rect.x(), 100.0);
855 
856     // line 'Right'
857     block = m_doc->begin().next();
858     rect = block.layout()->lineAt(0).naturalTextRect();
859     QVERIFY(rect.right() - 200 <= (1 + 100.0));
860     QVERIFY(rect.left() > 100.0);
861 
862     // line with align Leading and RTL progression
863     block = block.next();
864     rect = block.layout()->lineAt(0).naturalTextRect();
865     QVERIFY(rect.right() - 200 <= (1 + 100.0));
866     QVERIFY(rect.left() > 100.0); // expect right alignment
867 
868     // line with align tailing and RTL progression
869     block = block.next();
870     rect = block.layout()->lineAt(0).naturalTextRect();
871     QCOMPARE(rect.x(), 100.0); // expect left alignment
872 
873     // non RTL _text_ but RTL progression as well as align trailing
874     block = block.next();
875     rect = block.layout()->lineAt(0).naturalTextRect();
876     QCOMPARE(rect.x(), 100.0); // expect left alignment
877     // TODO can we check if the dot is the left most painted char?
878 }
879 
testParagraphBorders()880 void TestBlockLayout::testParagraphBorders()
881 {
882     setupTest("Paragraph with Borders\nAnother parag\n");
883     QTextCursor cursor(m_doc->begin());
884     QTextBlockFormat bf = cursor.blockFormat();
885     bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderSolid);
886     bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderSolid);
887     bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderSolid);
888     bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderSolid);
889     bf.setProperty(KoParagraphStyle::LeftBorderWidth, 8.0);
890     bf.setProperty(KoParagraphStyle::TopBorderWidth, 9.0);
891     bf.setProperty(KoParagraphStyle::BottomBorderWidth, 10.0);
892     bf.setProperty(KoParagraphStyle::RightBorderWidth, 11.0);
893     cursor.setBlockFormat(bf);
894 
895     m_layout->layout();
896     QTextBlock block = m_doc->begin();
897     QTextLayout *blockLayout = block.layout();
898     QCOMPARE(blockLayout->lineAt(0).x(), 8.0 + 100.0);
899     QCOMPARE(blockLayout->lineAt(0).y(), 9.0 + 100.0);
900     QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0);
901     block = block.next();
902     blockLayout = block.layout();
903     //warnTextLayout << "blockLayout->lineAt(0).y() "<<blockLayout->lineAt(0).y();
904     qreal exp = 9.0 + (12.0 * LINESPACING_FACTOR) + 10.0 + 100.0;
905     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(exp).toLatin1());
906 
907     // borders + padding create the total inset.
908     bf.setProperty(KoParagraphStyle::LeftPadding, 5.0);
909     bf.setProperty(KoParagraphStyle::RightPadding, 5.0);
910     bf.setProperty(KoParagraphStyle::TopPadding, 5.0);
911     bf.setProperty(KoParagraphStyle::BottomPadding, 5.0);
912     cursor.setBlockFormat(bf);
913 
914     m_layout->layout();
915     block = m_doc->begin();
916     blockLayout = block.layout();
917     QCOMPARE(blockLayout->lineAt(0).x(), 13.0 + 100.0);
918     QCOMPARE(blockLayout->lineAt(0).y(), 14.0 + 100.0);
919     QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - 5.0 * 2);
920     block = block.next();
921     blockLayout = block.layout();
922     //qDebug() << blockLayout->lineAt(0).y() << (9.0 + 14.4 + 10 + 5.0 * 2);
923     exp = 9.0 + (12.0 * LINESPACING_FACTOR) + 10 + 5.0 * 2 + 100.0;
924     QVERIFY2(qAbs(blockLayout->lineAt(0).y() - exp) < ROUNDING, QString("Actual: %1 Expected: %2").arg(blockLayout->lineAt(0).y()).arg(exp).toLatin1());
925 
926     // borders are positioned outside the padding, lets check that to be the case.
927     block = m_doc->begin();
928     KoTextBlockData data(block);
929     KoTextBlockBorderData *border = data.border();
930     QVERIFY(border);
931     QCOMPARE(border->hasBorders(), true);
932     /*
933     QRectF borderOutline = border->rect();
934     QCOMPARE(borderOutline.top(), 0.);
935     QCOMPARE(borderOutline.left(), 0.);
936     QCOMPARE(borderOutline.right(), 200.);
937     */
938 
939     // qreal borders.  Specify an additional width for each side.
940     bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderDouble);
941     bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderDouble);
942     bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderDouble);
943     bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderDouble);
944     bf.setProperty(KoParagraphStyle::LeftInnerBorderWidth, 2.0);
945     bf.setProperty(KoParagraphStyle::RightInnerBorderWidth, 2.0);
946     bf.setProperty(KoParagraphStyle::BottomInnerBorderWidth, 2.0);
947     bf.setProperty(KoParagraphStyle::TopInnerBorderWidth, 2.0);
948     cursor.setBlockFormat(bf);
949 
950     m_layout->layout();
951     block = m_doc->begin();
952     blockLayout = block.layout();
953     QCOMPARE(blockLayout->lineAt(0).x(), 15.0 + 100.0);
954     QCOMPARE(blockLayout->lineAt(0).y(), 16.0 + 100.0);
955     QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0) * 2);
956     block = block.next();
957     blockLayout = block.layout();
958     //qDebug() << blockLayout->lineAt(0).y();
959     QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + (12.0  * LINESPACING_FACTOR) + 10 + (5.0 + 2.0) * 2 + 100.0)) < ROUNDING);
960 
961     // and last, make the 2 qreal border have a blank space in the middle.
962     bf.setProperty(KoParagraphStyle::LeftBorderSpacing, 3.0);
963     bf.setProperty(KoParagraphStyle::RightBorderSpacing, 3.0);
964     bf.setProperty(KoParagraphStyle::BottomBorderSpacing, 3.0);
965     bf.setProperty(KoParagraphStyle::TopBorderSpacing, 3.0);
966     cursor.setBlockFormat(bf);
967 
968     m_layout->layout();
969     block = m_doc->begin();
970     blockLayout = block.layout();
971     QCOMPARE(blockLayout->lineAt(0).x(), 18.0 + 100.0);
972     QCOMPARE(blockLayout->lineAt(0).y(), 19.0 + 100.0);
973     QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0 + 3.0) * 2);
974     block = block.next();
975     blockLayout = block.layout();
976     //qDebug() << blockLayout->lineAt(0).y();
977     QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + (12.0  * LINESPACING_FACTOR) + 10 + (5.0 + 2.0 + 3.0) * 2 + 100.0)) < ROUNDING);
978 }
979 
testParagraphMargins()980 void TestBlockLayout::testParagraphMargins()
981 {
982     setupTest("Emtpy\nParagraph\nAnother parag\n");
983 
984     KoParagraphStyle style;
985     style.setFontPointSize(12.0);
986     m_styleManager->add(&style);
987     style.setTopMargin(QTextLength(QTextLength::FixedLength, 10));
988     KoListStyle listStyle;
989     KoListLevelProperties llp = listStyle.levelProperties(1);
990     llp.setLabelType(KoListStyle::NumberLabelType);
991     llp.setNumberFormat(KoOdfNumberDefinition::Numeric);
992     listStyle.setLevelProperties(llp);
993     style.setListStyle(&listStyle);
994     style.setLeftBorderWidth(3);
995 
996     QTextBlock block = m_doc->begin().next();
997     style.applyStyle(block);
998     block = block.next();
999     style.applyStyle(block);
1000 
1001     m_layout->layout();
1002 
1003     block = m_doc->begin().next();
1004     KoTextBlockData data(block);
1005     KoTextBlockBorderData *border = data.border();
1006     QVERIFY(border);
1007     QCOMPARE(data.counterPosition(), QPointF(3 + 100.0, (12.0 * LINESPACING_FACTOR) + 10.0 + 100.0));
1008 
1009     block = block.next();
1010     KoTextBlockData data2(block);
1011     QCOMPARE(data2.counterPosition(), QPointF(3 + 100.0, (12.0 * LINESPACING_FACTOR + 10) * 2 + 100.0));
1012 
1013 
1014     style.setBottomMargin(QTextLength(QTextLength::FixedLength, 5)); //bottom spacing
1015     // manually reapply and relayout to force immediate reaction.
1016     block = m_doc->begin().next();
1017     style.applyStyle(block);
1018     block = block.next();
1019     style.applyStyle(block);
1020     m_layout->layout();
1021 
1022     block = m_doc->begin().next();
1023     border = data2.border();
1024     QVERIFY(border);
1025 
1026     KoTextBlockData data3(block);
1027     QCOMPARE(data3.counterPosition(), QPointF(3 + 100.0, (12.0  * LINESPACING_FACTOR) + 10.0 + 100.0));
1028 
1029     block = block.next();
1030     KoTextBlockData data4(block);
1031     QCOMPARE(data4.counterPosition(), QPointF(3 + 100.0, (12.0  * LINESPACING_FACTOR + 10.0) * 2 + 100.0)); // same y as before as we take max spacing
1032 }
1033 
testEmptyParag()1034 void TestBlockLayout::testEmptyParag()
1035 {
1036     setupTest("Foo\n\nBar\n");
1037     m_layout->layout();
1038     QTextBlock block = m_doc->begin();
1039     QTextLayout *lay = block.layout();
1040     QVERIFY(lay);
1041     QCOMPARE(lay->lineCount(), 1);
1042     const qreal y = lay->lineAt(0).position().y();
1043 
1044     block = block.next();
1045     lay = block.layout();
1046     QVERIFY(lay);
1047     QCOMPARE(lay->lineCount(), 1);
1048     QVERIFY(lay->lineAt(0).position().y() > y);
1049     qInfo()<<lay->lineAt(0).position().y();
1050     QVERIFY(qAbs(lay->lineAt(0).position().y() - (12.0*LINESPACING_FACTOR + 100.0)) < ROUNDING);
1051 }
1052 
testDropCapsLongText()1053 void TestBlockLayout::testDropCapsLongText()
1054 {
1055     // This text should be so long that it is split into more lines
1056     // than the number of dropcap lines.
1057     // This is the normal use case, with a shorter line dropcaps doesn't work well
1058     setupTest(m_loremIpsum);
1059 
1060     KoParagraphStyle style;
1061     style.setFontPointSize(12.0);
1062     style.setDropCaps(false);
1063     style.setDropCapsLength(1);
1064     style.setDropCapsLines(4);
1065     style.setDropCapsDistance(9.0);
1066     QTextBlock block = m_doc->begin();
1067     style.applyStyle(block);
1068 
1069     qInfo()<<"Font:"<<style.font();
1070     qInfo()<<"Dropcaps off";
1071 
1072     m_layout->layout();
1073 
1074     // dummy version, caps is still false.
1075     QTextLayout *blockLayout = block.layout();
1076     QVERIFY(blockLayout->lineCount() > 4);
1077     QTextLine line = blockLayout->lineAt(0);
1078     QVERIFY(line.textLength() > 1);
1079 
1080     qInfo()<<"Dropcaps on";
1081     style.setDropCaps(true);
1082     style.applyStyle(block);
1083     m_layout->layout();
1084 
1085     // test that the first text line is the dropcaps and the positions are right.
1086     QVERIFY(blockLayout->lineCount() > 4);
1087     line = blockLayout->lineAt(0);
1088     QCOMPARE(line.textLength(), 1);
1089 
1090     QCOMPARE(line.x(), 100.0);
1091     QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here.
1092 
1093     qInfo()<<"dropcaps:"<<line.rect();
1094     qreal dropCapsRight = line.rect().right();
1095     qreal dropCapsBottom = 100.0 - line.y() + line.height();
1096 
1097     line = blockLayout->lineAt(1);
1098     QVERIFY(line.textLength() > 1);
1099     qreal heightNormalLine = line.height();
1100     QCOMPARE(line.y(), 100.0); // aligned top
1101     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1102 
1103     line = blockLayout->lineAt(1);
1104     QVERIFY(line.textLength() > 2);
1105     QCOMPARE(line.y(), 100.0); // aligned top
1106     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1107 
1108     line = blockLayout->lineAt(3);
1109     QVERIFY(line.textLength() > 0);
1110     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1111 
1112     line = blockLayout->lineAt(4);
1113     QVERIFY(line.textLength() > 0);
1114     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1115 
1116     // This should be below the dropcap and thus not indented
1117     line = blockLayout->lineAt(5);
1118     QVERIFY(line.textLength() > 0);
1119     QCOMPARE(line.x(), 100.0); // aligned left
1120     qInfo()<<"dropCapsBottom"<<dropCapsBottom<<line.y();
1121     QVERIFY(line.y() >= dropCapsBottom);
1122 
1123     qInfo()<<"Dropcaps off";
1124     style.setDropCaps(false); // remove it
1125     style.applyStyle(block);
1126     m_layout->layout();
1127     blockLayout = block.layout();
1128 
1129     // test that the first text line is no longer dropcaps
1130     QVERIFY(blockLayout->lineCount() > 1);
1131     line = blockLayout->lineAt(0);
1132     QVERIFY(line.textLength() > 1);
1133     QCOMPARE(line.height(), heightNormalLine);
1134 }
1135 
testDropCapsShortText()1136 void TestBlockLayout::testDropCapsShortText()
1137 {
1138     setupTest(QString("Lorem ipsum")); // short enough to only get one line
1139 
1140     KoParagraphStyle style;
1141     style.setFontPointSize(12.0);
1142     style.setDropCaps(false);
1143     style.setDropCapsLength(1);
1144     style.setDropCapsLines(4);
1145     style.setDropCapsDistance(9.0);
1146     QTextBlock block = m_doc->begin();
1147     style.applyStyle(block);
1148 
1149     qInfo()<<"Font:"<<style.font()<<"Text"<<block.text();
1150     qInfo()<<"Dropcaps off";
1151 
1152     m_layout->layout();
1153 
1154     // dummy version, caps is still false.
1155     QTextLayout *blockLayout = block.layout();
1156     int lineCount = blockLayout->lineCount();
1157 
1158     QVERIFY(lineCount > 0);
1159     QTextLine line = blockLayout->lineAt(0);
1160     QVERIFY(line.textLength() > 1);
1161 
1162     qInfo()<<"Dropcaps on";
1163     style.setDropCaps(true);
1164     style.applyStyle(block);
1165     m_layout->layout();
1166 
1167     // test that the first text line is the dropcaps and the positions are right.
1168     QCOMPARE(blockLayout->lineCount(), lineCount + 1);
1169     line = blockLayout->lineAt(0);
1170     QCOMPARE(line.textLength(), 1);
1171 
1172     QCOMPARE(line.x(), 100.0);
1173     QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here.
1174 
1175     qreal dropCapsRight = line.rect().right();
1176 
1177     line = blockLayout->lineAt(1);
1178     QVERIFY(line.textLength() > 1);
1179     qreal heightNormalLine = line.height();
1180     QCOMPARE(line.y(), 100.0); // aligned top
1181     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1182 
1183     qInfo()<<"Dropcaps off";
1184     style.setDropCaps(false); // remove it
1185     style.applyStyle(block);
1186     m_layout->layout();
1187     blockLayout = block.layout();
1188 
1189     // test that the first text line is no longer dropcaps
1190     QCOMPARE(blockLayout->lineCount(), lineCount);
1191     line = blockLayout->lineAt(0);
1192     QVERIFY(line.textLength() > 1);
1193     QCOMPARE(line.height(), heightNormalLine);
1194 }
1195 
testDropCapsWithNewline()1196 void TestBlockLayout::testDropCapsWithNewline()
1197 {
1198     // Some not too long text so the dropcap will be bigger than the block.
1199     // The text after newline will be in a separate block, but
1200     // shall also be indented by the dropcap.
1201     // Note that we cannot be certain of the number of lines we will get
1202     // as it depends on the actual available font.
1203     setupTest(QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing elit, sed diam\nsome more text"));
1204 
1205     KoParagraphStyle style;
1206     style.setFontPointSize(12.0);
1207     style.setDropCaps(false);
1208     style.setDropCapsLength(1);
1209     style.setDropCapsLines(4);
1210     style.setDropCapsDistance(9.0);
1211     QTextBlock block = m_doc->begin();
1212     QTextBlock secondblock = block.next(); // "some more text"
1213     style.applyStyle(block);
1214 
1215     qInfo()<<"Font:"<<style.font()<<"Text"<<block.text();
1216     qInfo()<<"Dropcaps off";
1217 
1218     m_layout->layout();
1219 
1220     // dummy version, caps is still false.
1221     QTextLayout *blockLayout = block.layout();
1222     int lineCount = blockLayout->lineCount();
1223     // lineCount must be >= 1 and <= 3 for this test to work
1224     if (lineCount == 0 || lineCount >= 3) {
1225         qWarning()<<"The text was not layouted in the required number of lines, so this test will fail";
1226     }
1227     QVERIFY(blockLayout->lineCount() >= 1);
1228     QVERIFY(blockLayout->lineCount() <= 3);
1229     QTextLine line = blockLayout->lineAt(0);
1230     QVERIFY(line.textLength() > 1);
1231 
1232     qInfo()<<"Dropcaps on";
1233     style.setDropCaps(true);
1234     style.applyStyle(block);
1235     m_layout->layout();
1236 
1237     // test that the first text line is the dropcaps and the positions are right.
1238     QVERIFY(blockLayout->lineCount() > 1);
1239     line = blockLayout->lineAt(0);
1240     QCOMPARE(line.textLength(), 1);
1241 
1242     QCOMPARE(line.x(), 100.0);
1243     QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here.
1244 
1245     qreal dropCapsRight = line.rect().right();
1246 
1247     line = blockLayout->lineAt(1);
1248     QVERIFY(line.textLength() > 2);
1249     qreal heightNormalLine = line.height();
1250     QCOMPARE(line.y(), 100.0); // aligned top
1251     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1252 
1253     // Now test that a following block is indented by the same amount
1254     // since it also should be influenced by the dropcap
1255     blockLayout = secondblock.layout();
1256     QVERIFY(blockLayout->lineCount() >= 1);
1257     line = blockLayout->lineAt(0);
1258     QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance());
1259 
1260     qInfo()<<"Dropcaps off";
1261     style.setDropCaps(false); // remove it
1262     style.applyStyle(block);
1263     m_layout->layout();
1264     blockLayout = block.layout();
1265 
1266     // test that the first text line is no longer dropcaps
1267     QCOMPARE(blockLayout->lineCount(), lineCount);
1268     line = blockLayout->lineAt(0);
1269     QVERIFY(line.textLength() > 1);
1270     QCOMPARE(line.height(), heightNormalLine);
1271 }
1272 
1273 QTEST_MAIN(TestBlockLayout)
1274