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