1 /*
2 * This file is part of Calligra tests
3 *
4 * Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 #include "TestDocumentLayout.h"
21
22 #include <KoParagraphStyle.h>
23 #include <KoListStyle.h>
24 #include <KoListLevelProperties.h>
25 #include <KoStyleManager.h>
26 #include <KoTextBlockData.h>
27 #include <KoTextBlockBorderData.h>
28 #include <KoTextDocument.h>
29
30 #include <QtGui>
31
32 #include <QDebug>
33
34 #define FRAME_SPACING 10.0
35
36
initTestCase()37 void TestDocumentLayout::initTestCase()
38 {
39 m_shape1 = 0;
40 m_doc = 0;
41 m_layout = 0;
42
43 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.");
44 }
45
initForNewTest(const QString & initText)46 void TestDocumentLayout::initForNewTest(const QString &initText)
47 {
48 // this leaks memory like mad, but who cares ;)
49 m_shape1 = new MockTextShape();
50 m_shape1->setSize(QSizeF(200, 1000));
51
52 // this leaks memory like mad, but who cares ;)
53 m_layout = m_shape1->layout;
54 Q_ASSERT(m_layout);
55 m_doc = m_layout->document();
56 Q_ASSERT(m_doc);
57 m_doc->setDefaultFont(QFont("Sans Serif", 12, QFont::Normal, false)); //do it manually since we do not load the appDefaultStyle
58 m_textLayout = new TextShapeLayout(m_layout);
59 m_layout->setLayout(m_textLayout);
60 m_styleManager = new KoStyleManager();
61 KoTextDocument(m_doc).setStyleManager(m_styleManager);
62
63 m_block = m_doc->begin();
64 if (initText.length() > 0) {
65 QTextCursor cursor(m_doc);
66 cursor.insertText(initText);
67 KoParagraphStyle style;
68 style.setStyleId(101); // needed to do manually since we don't use the stylemanager
69 QTextBlock b2 = m_doc->begin();
70 while (b2.isValid()) {
71 style.applyStyle(b2);
72 b2 = b2.next();
73 }
74 }
75 }
76
testLineBreaking()77 void TestDocumentLayout::testLineBreaking()
78 {
79 initForNewTest(m_loremIpsum);
80 m_layout->layout();
81 QTextLayout *blockLayout = m_block.layout();
82
83 //QCOMPARE(blockLayout->lineCount(), 16);
84 QCOMPARE(blockLayout->lineForTextPosition(1).width(), 200.0);
85 }
86
testMultiFrameLineBreaking()87 void TestDocumentLayout::testMultiFrameLineBreaking()
88 {
89 initForNewTest(m_loremIpsum);
90 m_shape1->setSize(QSizeF(200, 47)); // fits 3 lines.
91 KoShape *shape2 = new MockTextShape();
92 shape2->setSize(QSizeF(120, 1000));
93 m_layout->addShape(shape2);
94
95 m_layout->layout();
96 QTextLayout *blockLayout = m_block.layout();
97 QCOMPARE(blockLayout->lineAt(0).width(), 200.0);
98 QCOMPARE(blockLayout->lineAt(1).width(), 200.0);
99 QCOMPARE(blockLayout->lineAt(2).width(), 200.0);
100 QCOMPARE(blockLayout->lineAt(3).width(), 120.0);
101 QCOMPARE(blockLayout->lineAt(4).width(), 120.0);
102
103 QTextLine line = blockLayout->lineAt(3);
104 QVERIFY(line.y() > m_shape1->size().height()); // it has to be outside of m_shape1
105 const qreal topOfFrame2 = line.y();
106 line = blockLayout->lineAt(4);
107 //qDebug() << line.y() - topOfFrame2 - 14.4;
108 QVERIFY(qAbs(line.y() - topOfFrame2 - 14.4) < 0.125);
109 }
110
testBasicLineSpacing()111 void TestDocumentLayout::testBasicLineSpacing()
112 {
113 /// Tests incrementing Y pos based on the font size
114 initForNewTest(m_loremIpsum);
115 QTextCursor cursor(m_doc);
116 cursor.setPosition(0);
117 cursor.setPosition(m_loremIpsum.length() - 1, QTextCursor::KeepAnchor);
118 QTextCharFormat charFormat = cursor.charFormat();
119 charFormat.setFontPointSize(12);
120 cursor.mergeCharFormat(charFormat);
121 m_layout->layout();
122 QTextLayout *blockLayout = m_block.layout();
123
124 const qreal fontHeight12 = 12;
125 qreal lineSpacing12 = fontHeight12 * 1.2; // 120% is the normal lineSpacing.
126 const qreal fontHeight18 = 18;
127 qreal lineSpacing18 = fontHeight18 * 1.2; // 120% is the normal lineSpacing.
128
129 // QCOMPARE(blockLayout->lineCount(), 15);
130 QCOMPARE(blockLayout->lineForTextPosition(1).width(), 200.0);
131 QTextLine line;
132 for (int i = 0; i < 15; i++) {
133 line = blockLayout->lineAt(i);
134 QVERIFY(line.isValid());
135 // The reason for this weird check is that the values are stored internally
136 // as 26.6 fixed point integers. The entire internal text layout is
137 // actually done using fixed point arithmetic. This is due to embedded
138 // considerations, and offers general performance benefits across all
139 // platforms.
140 //qDebug() << i << qAbs(line.y() - i * lineSpacing12);
141 QVERIFY(qAbs(line.y() - i * lineSpacing12) < ROUNDING);
142 }
143
144 // make first word smaller, should have zero effect on lineSpacing.
145 cursor.setPosition(0);
146 cursor.setPosition(11, QTextCursor::KeepAnchor);
147 charFormat.setFontPointSize(10);
148 cursor.mergeCharFormat(charFormat);
149 m_layout->layout();
150 for (int i = 0; i < 15; i++) {
151 line = blockLayout->lineAt(i);
152 QVERIFY(line.isValid());
153 //qDebug() << i << qAbs(line.y() - i * lineSpacing12);
154 QVERIFY(qAbs(line.y() - i * lineSpacing12) < ROUNDING);
155 }
156
157 // make first word on second line word bigger, should move that line down a little.
158 int pos = blockLayout->lineAt(1).textStart();
159 cursor.setPosition(pos);
160 cursor.setPosition(pos + 12, QTextCursor::KeepAnchor);
161 charFormat.setFontPointSize(18);
162 cursor.mergeCharFormat(charFormat);
163 m_layout->layout();
164 line = blockLayout->lineAt(0);
165 QCOMPARE(line.y(), 0.0);
166 line = blockLayout->lineAt(1);
167 QVERIFY(qAbs(line.y() - lineSpacing12) < ROUNDING);
168
169 for (int i = 2; i < 15; i++) {
170 line = blockLayout->lineAt(i);
171 //qDebug() << "i: " << i << " gives: " << line.y() << " + " << line.ascent() << ", " << line.descent() << " = " << line.height();
172 QVERIFY(qAbs(line.y() - (lineSpacing12 + lineSpacing18 + (i - 2) * lineSpacing12)) < ROUNDING);
173 }
174 // Test widget to show what we have
175 /*
176 class Widget : public QWidget {
177 public:
178 Widget(KWTextDocumentLayout *layout) {
179 m_layout = layout;
180 }
181 void paintEvent (QPaintEvent * e) {
182 QPainter painter( this );
183 QAbstractTextDocumentLayout::PaintContext pc;
184 pc.cursorPosition = -1;
185 m_layout->draw( &painter, pc);
186 }
187 private:
188 KWTextDocumentLayout *m_layout;
189 };
190
191 QMainWindow mw;
192 mw.setCentralWidget(new Widget(m_layout));
193 mw.show();
194 m_app->exec(); */
195 }
196
testBasicLineSpacing2()197 void TestDocumentLayout::testBasicLineSpacing2()
198 {
199 initForNewTest(m_loremIpsum);
200 QTextCursor cursor(m_doc);
201 cursor.insertText("foo\n\n"); // insert empty parag;
202
203 m_layout->layout();
204 QTextBlock block = m_doc->begin().next();
205 QTextLayout *blockLayout = block.layout();
206 QVERIFY(block.isValid());
207 blockLayout = block.layout();
208 QCOMPARE(blockLayout->lineCount(), 1);
209
210 block = block.next();
211 QVERIFY(block.isValid());
212 blockLayout = block.layout();
213 //qDebug() << blockLayout->lineAt(0).y();
214 QVERIFY(qAbs(blockLayout->lineAt(0).y() - 28.8) < ROUNDING);
215 }
216
testAdvancedLineSpacing()217 void TestDocumentLayout::testAdvancedLineSpacing()
218 {
219 initForNewTest("Line1\nLine2\nLine3\nLine4\nLine5\nLine6\nLine7");
220 QTextCursor cursor(m_doc);
221
222 KoParagraphStyle style;
223 style.setLineHeightPercent(80);
224 QTextBlock block = m_doc->begin();
225 style.applyStyle(block);
226
227 // check if styles do their work ;)
228 QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 80.0);
229
230 block = block.next();
231 QVERIFY(block.isValid()); //line2
232 style.setLineHeightAbsolute(28.0); // removes the percentage
233 style.applyStyle(block);
234
235 block = block.next();
236 QVERIFY(block.isValid()); // line3
237 style.setMinimumLineHeight(40.0);
238 style.setLineHeightPercent(120);
239 style.applyStyle(block);
240
241 block = block.next();
242 QVERIFY(block.isValid()); // line4
243 style.remove(KoParagraphStyle::FixedLineHeight);
244 style.setMinimumLineHeight(5.0);
245 style.applyStyle(block);
246
247 block = block.next();
248 QVERIFY(block.isValid()); // line5
249 style.setMinimumLineHeight(0.0);
250 style.setLineSpacing(8.0);
251 style.remove(KoParagraphStyle::PercentLineHeight);
252 style.applyStyle(block);
253
254 block = block.next();
255 QVERIFY(block.isValid()); // line6
256 style.setLineSpacingFromFont(true);
257 style.setLineHeightPercent(100);
258 style.remove(KoParagraphStyle::LineSpacing);
259 style.applyStyle(block);
260
261 m_layout->layout();
262 QTextLayout *blockLayout = m_block.layout();
263 QCOMPARE(blockLayout->lineAt(0).y(), 0.0);
264 block = m_doc->begin().next(); // line2
265 QVERIFY(block.isValid());
266 blockLayout = block.layout();
267 //qDebug() << blockLayout->lineAt(0).y();
268 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (12.0 * 0.8)) < ROUNDING);
269 block = block.next(); // line3
270 QVERIFY(block.isValid());
271 blockLayout = block.layout();
272 //qDebug() << blockLayout->lineAt(0).y();
273 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.6 + 28.0)) < ROUNDING);
274 block = block.next(); // line4
275 QVERIFY(block.isValid());
276 blockLayout = block.layout();
277 //qDebug() << blockLayout->lineAt(0).y();
278 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (37.6 + 40)) < ROUNDING);
279 block = block.next(); // line5
280 QVERIFY(block.isValid());
281 blockLayout = block.layout();
282 // qDebug() << blockLayout->lineAt(0).y();
283 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (77.6 + qMax(qreal(12 * 1.2), qreal(5.0)))) < ROUNDING); // 92
284 block = block.next(); // line6
285 QVERIFY(block.isValid());
286 blockLayout = block.layout();
287 //qDebug() << blockLayout->lineAt(0).y();
288 QCOMPARE(blockLayout->lineAt(0).y(), 92.0 + 12 + 8);
289
290 qreal height = blockLayout->lineAt(0).height();
291 block = block.next(); // line 7
292 QVERIFY(block.isValid());
293 blockLayout = block.layout();
294 //qDebug() << blockLayout->lineAt(0).y();
295 QCOMPARE(blockLayout->lineAt(0).y(), 112 + height);
296 }
297
testMargins()298 void TestDocumentLayout::testMargins()
299 {
300 initForNewTest(m_loremIpsum);
301 QTextCursor cursor(m_doc);
302 QTextBlockFormat bf = cursor.blockFormat();
303 bf.setLeftMargin(10.0);
304 cursor.setBlockFormat(bf);
305 m_layout->layout();
306 QTextLayout *blockLayout = m_block.layout();
307 QCOMPARE(blockLayout->lineAt(0).x(), 10.0);
308 QCOMPARE(blockLayout->lineAt(0).width(), 190.0);
309
310 bf.setRightMargin(15.0);
311 cursor.setBlockFormat(bf);
312 m_layout->layout();
313 QCOMPARE(blockLayout->lineAt(0).x(), 10.0);
314 QCOMPARE(blockLayout->lineAt(0).width(), 175.0);
315
316 bf.setLeftMargin(0.0);
317 cursor.setBlockFormat(bf);
318 m_layout->layout();
319 QCOMPARE(blockLayout->lineAt(0).x(), 0.0);
320 QCOMPARE(blockLayout->lineAt(0).width(), 185.0); // still uses the right margin of 15
321
322 cursor.setPosition(m_loremIpsum.length());
323 cursor.insertText("\n");
324 bf.setTopMargin(12);
325 cursor.setBlockFormat(bf);
326 cursor.insertText(m_loremIpsum);// create second parag
327 m_layout->layout();
328 QCOMPARE(blockLayout->lineAt(0).x(), 0.0); // parag 1
329 QCOMPARE(blockLayout->lineAt(0).width(), 185.0);
330
331 // and test parag 2
332 QTextBlock block2 = m_doc->begin().next();
333 QTextLayout *layout = block2.layout();
334 QCOMPARE(layout->lineAt(0).x(), 0.0);
335 QCOMPARE(layout->lineAt(0).width(), 185.0);
336
337 QTextLine lastLineOfParag1 = blockLayout->lineAt(blockLayout->lineCount() - 1);
338 QTextLine firstLineOfParag2 = layout->lineAt(0);
339 const qreal FONTSIZE = 12.0;
340 const qreal BottomParag1 = lastLineOfParag1.y() + (FONTSIZE * 1.2);
341 QVERIFY(qAbs(firstLineOfParag2.y() - BottomParag1 - 12.0) < ROUNDING);
342 }
343
testMultipageMargins()344 void TestDocumentLayout::testMultipageMargins()
345 {
346 initForNewTest("123456789\nparagraph 2\nlksdjflksdjflksdjlkfjsdlkfjsdlk sldkfj lsdkjf lskdjf lsd lfsjd lfk");
347 QTextCursor cursor(m_doc);
348
349 KoParagraphStyle h1;
350 h1.setTopMargin(100.0);
351 h1.setBottomMargin(20.0);
352 m_styleManager->add(&h1);
353
354 QTextBlock block = m_doc->begin();
355 h1.applyStyle(block);
356 block = block.next();
357 h1.applyStyle(block);
358
359 m_shape1->setSize(QSizeF(200, 14.4 + 100 + 20 + 14.4 + 5)); // 5 for fun..
360 KoShape *shape2 = new MockTextShape();
361 shape2->setSize(QSizeF(120, 1000));
362 m_layout->addShape(shape2);
363
364 m_layout->layout();
365
366 /* The above has 3 parags with a very tall top margin and a bottom margin
367 * The first line is expected to be displayed at top of the page because we
368 * never show the topMargin on new pages (see also testParagOffset).
369 * The second parag will then be 100 + 20 points lower.
370 * The shape will not have enough space for the bottom margin of this second parag,
371 * but that does not move it to the second shape!
372 * The 3th parag is then displayed at the top of the second shape.
373 */
374
375 block = m_doc->begin();
376 QVERIFY(block.isValid());
377 QTextLayout *blockLayout = block.layout(); // first parag
378 QCOMPARE(blockLayout->lineAt(0).y(), 0.0);
379 block = block.next();
380 QVERIFY(block.isValid());
381 blockLayout = block.layout(); // second parag
382 //qDebug() << blockLayout->lineAt(0).y() << "=" << (12.0 * 1.2 + 100.0 + 20.0);
383 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (12.0 * 1.2 + 100.0 + 20.0)) < ROUNDING);
384 block = block.next();
385 QVERIFY(block.isValid());
386 blockLayout = block.layout(); // thirth parag
387 // the 10 in the next line is hardcoded distance between frames.
388 //qDebug() << blockLayout->lineAt(0).y() << "=" << m_shape1->size().height() + FRAME_SPACING;
389 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (m_shape1->size().height() + FRAME_SPACING)) < ROUNDING);
390
391 /* TODO
392 - top margin at new page is honoured when the style used has a
393 'page break before' set to true.
394 */
395 }
396
testTextIndent()397 void TestDocumentLayout::testTextIndent()
398 {
399 initForNewTest(m_loremIpsum);
400 QTextCursor cursor(m_doc);
401 QTextBlockFormat format = cursor.blockFormat();
402 format.setTextIndent(20);
403 cursor.setBlockFormat(format);
404
405 m_layout->layout();
406 QTextLayout *blockLayout = m_block.layout();
407
408 QCOMPARE(blockLayout->lineAt(0).x(), 20.0);
409 QCOMPARE(blockLayout->lineAt(1).x(), 0.0);
410 }
411
testBasicTextAlignments()412 void TestDocumentLayout::testBasicTextAlignments()
413 {
414 initForNewTest("Left\nCenter\nRight");
415
416 QTextCursor cursor(m_doc);
417 QTextBlockFormat format = cursor.blockFormat();
418 format.setAlignment(Qt::AlignLeft);
419 cursor.setBlockFormat(format);
420 cursor.setPosition(6);
421 format.setAlignment(Qt::AlignHCenter);
422 cursor.setBlockFormat(format);
423 cursor.setPosition(13);
424 format.setAlignment(Qt::AlignRight);
425 cursor.setBlockFormat(format);
426
427 m_layout->layout();
428 QTextLayout *blockLayout = m_block.layout();
429 QCOMPARE(blockLayout->lineAt(0).x(), 0.0);
430
431 QTextBlock block = m_doc->begin().next();
432 QVERIFY(block.isValid());
433 blockLayout = block.layout();
434
435 QRectF rect = blockLayout->lineAt(0).naturalTextRect();
436 QVERIFY(rect.x() > 60);
437 QCOMPARE(rect.x() + rect.width() + (200 - rect.right()), 200.0);
438 block = block.next();
439 QVERIFY(block.isValid());
440 blockLayout = block.layout();
441 rect = blockLayout->lineAt(0).naturalTextRect();
442 QVERIFY(rect.x() > 150);
443 QVERIFY(rect.right() >= 200.0);
444 }
445
testTextAlignments()446 void TestDocumentLayout::testTextAlignments()
447 {
448 // TODO justified & justified, last line
449 initForNewTest("Left\nRight\nﺵﻻﺆﻴﺜﺒ\nﺵﻻﺆﻴﺜﺒ\nLast Line.");
450 KoParagraphStyle start;
451 start.setAlignment(Qt::AlignLeading);
452 KoParagraphStyle end;
453 end.setAlignment(Qt::AlignTrailing);
454
455 KoParagraphStyle startRTL;
456 startRTL.setAlignment(Qt::AlignLeading);
457 startRTL.setTextProgressionDirection(KoText::RightLeftTopBottom);
458 KoParagraphStyle endRTL;
459 endRTL.setAlignment(Qt::AlignTrailing);
460 endRTL.setTextProgressionDirection(KoText::RightLeftTopBottom);
461
462 QTextBlock block = m_doc->begin();
463 start.applyStyle(block);
464 block = block.next();
465 end.applyStyle(block);
466 block = block.next();
467 startRTL.applyStyle(block);
468 block = block.next();
469 endRTL.applyStyle(block);
470 block = block.next();
471 endRTL.applyStyle(block);
472
473 m_layout->layout();
474 QTextLayout *blockLayout = m_block.layout();
475
476 // line 'Left'
477 QRectF rect = blockLayout->lineAt(0).naturalTextRect();
478 QCOMPARE(rect.x(), 0.);
479
480 // line 'Right'
481 block = m_doc->begin().next();
482 rect = block.layout()->lineAt(0).naturalTextRect();
483 QVERIFY(rect.right() - 200 <= 1);
484 QVERIFY(rect.left() > 0.);
485
486 // line with align Leading and RTL progression
487 block = block.next();
488 rect = block.layout()->lineAt(0).naturalTextRect();
489 QVERIFY(rect.right() - 200 <= 1);
490 QVERIFY(rect.left() > 0.); // expect right alignment
491
492 // line with align tailing and RTL progression
493 block = block.next();
494 rect = block.layout()->lineAt(0).naturalTextRect();
495 QCOMPARE(rect.x(), 0.); // expect left alignment
496
497 // non RTL _text_ but RTL progression as well as align trailing
498 block = block.next();
499 rect = block.layout()->lineAt(0).naturalTextRect();
500 QCOMPARE(rect.x(), 0.); // expect left alignment
501 // TODO can we check if the dot is the left most painted char?
502 }
503
testPageBreak()504 void TestDocumentLayout::testPageBreak()
505 {
506 initForNewTest("line\nParag2\nSimple Parag\nLast");
507 KoParagraphStyle style;
508 style.setBreakBefore(true);
509 style.setBreakAfter(true);
510 QTextBlock block = m_doc->begin();
511 style.applyStyle(block); // break before Line (ignored) and after, moving Parag2 to a new shape
512 block = block.next();
513 QVERIFY(block.isValid());
514 block = block.next();
515 QVERIFY(block.isValid());
516 style.setBreakBefore(false); // break after 'simple parag' moving 'Last' to a new shape
517 style.setBreakAfter(true);
518 style.applyStyle(block);
519
520 m_shape1->setSize(QSizeF(200, 40));
521 KoShape *shape2 = new MockTextShape();
522 shape2->setSize(QSizeF(200, 100));
523 m_layout->addShape(shape2);
524
525 KoShape *shape3 = new MockTextShape();
526 shape3->setSize(QSizeF(200, 100));
527 m_layout->addShape(shape3);
528
529 m_layout->layout();
530 QTextLayout *blockLayout = m_block.layout();
531
532 QCOMPARE(blockLayout->lineAt(0).y(), 0.0);
533 block = m_doc->begin().next();
534 QVERIFY(block.isValid());
535 blockLayout = block.layout(); // parag 2
536 QCOMPARE(blockLayout->lineCount(), 1);
537 QCOMPARE(blockLayout->lineAt(0).y(), 50.0);
538 block = block.next();
539 QVERIFY(block.isValid());
540 blockLayout = block.layout(); // parag 3
541 //qDebug() << qAbs(blockLayout->lineAt(0).y());
542 QVERIFY(qAbs(blockLayout->lineAt(0).y() - 64.4) < ROUNDING);
543 block = block.next();
544 QVERIFY(block.isValid());
545 blockLayout = block.layout(); // parag 4
546 QCOMPARE(blockLayout->lineCount(), 1);
547 QCOMPARE(blockLayout->lineAt(0).y(), 160.0);
548 }
549
testPageBreak2()550 void TestDocumentLayout::testPageBreak2()
551 {
552 initForNewTest("line\nParag2\nSimple Parag\nLast");
553 QTextBlock block = m_doc->begin();
554 QTextCursor cursor(block);
555 QTextBlockFormat bf = cursor.blockFormat();
556 bf.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
557 //bf.setPageBreakPolicy(PageBreak_AlwaysBefore);
558 cursor.setBlockFormat(bf);
559 block = block.next();
560 QVERIFY(block.isValid());
561 block = block.next();
562 QVERIFY(block.isValid());
563 cursor = QTextCursor(block);
564 bf.setPageBreakPolicy(QTextFormat::PageBreak_AlwaysAfter);
565 cursor.setBlockFormat(bf);
566
567 m_shape1->setSize(QSizeF(200, 40));
568 KoShape *shape2 = new MockTextShape();
569 shape2->setSize(QSizeF(200, 100));
570 m_layout->addShape(shape2);
571 KoShape *shape3 = new MockTextShape();
572 shape3->setSize(QSizeF(200, 100));
573 m_layout->addShape(shape3);
574
575 m_layout->layout();
576 QTextLayout *blockLayout = m_block.layout();
577
578 QCOMPARE(blockLayout->lineAt(0).y(), 0.0);
579 block = m_doc->begin().next();
580 QVERIFY(block.isValid());
581 blockLayout = block.layout(); // parag 2
582 QCOMPARE(blockLayout->lineCount(), 1);
583 QCOMPARE(blockLayout->lineAt(0).y(), 50.0);
584 block = block.next();
585 QVERIFY(block.isValid());
586 blockLayout = block.layout(); // parag 3
587 //qDebug() << qAbs(blockLayout->lineAt(0).y());
588 QVERIFY(qAbs(blockLayout->lineAt(0).y() - 64.4) < ROUNDING);
589 block = block.next();
590 QVERIFY(block.isValid());
591 blockLayout = block.layout(); // parag 4
592 QCOMPARE(blockLayout->lineCount(), 1);
593 QCOMPARE(blockLayout->lineAt(0).y(), 160.0);
594 }
595
testParagOffset()596 void TestDocumentLayout::testParagOffset()
597 {
598 initForNewTest("First line\nSecond line\n");
599
600 /*
601 Test for top-of-page indent.
602 When text gets moved to the next page the indent above the paragraph should be ignored
603 since the moving to the next page is already enough whitespace.
604 The user might want to have spacing at the top of the page, though, without using manually
605 typed enters.
606 There are 2 cases when such spacing is honoured.
607 1) When the paragraph has an offset defined in its style as well as a 'break before' property set.
608 2) When its in the paragraph-properties, but not in the style (i.e. a manual override)
609 */
610
611 KoParagraphStyle h1;
612 h1.setTopMargin(20);
613 h1.setBreakBefore(true);
614 m_styleManager->add(&h1);
615
616 QTextBlock block = m_doc->begin();
617 h1.applyStyle(block);
618 block = block.next();
619 h1.applyStyle(block);
620
621 m_shape1->setSize(QSizeF(200, 100));
622 KoShape *shape2 = new MockTextShape();
623 shape2->setSize(QSizeF(200, 100));
624 m_layout->addShape(shape2);
625
626 // 1)
627 m_layout->layout();
628 block = m_doc->begin();
629 QTextLayout *blockLayout = m_block.layout();
630 QCOMPARE(blockLayout->lineAt(0).y(), 20.0);
631 block = block.next();
632 QVERIFY(block.isValid());
633 blockLayout = block.layout(); // page 2
634 QCOMPARE(blockLayout->lineAt(0).y(), 130.0);
635
636 QTextCursor cursor(m_doc->begin());
637 QTextBlockFormat bf = cursor.blockFormat();
638 cursor.setPosition(0);
639 cursor.setPosition(20, QTextCursor::KeepAnchor);
640 bf.setTopMargin(30);
641 cursor.setBlockFormat(bf);
642
643 // 2)
644 m_layout->layout();
645 block = m_doc->begin();
646 blockLayout = block.layout(); // page 1
647 QCOMPARE(blockLayout->lineAt(0).y(), 30.0);
648 block = block.next();
649 QVERIFY(block.isValid());
650 blockLayout = block.layout(); // page 2
651 QCOMPARE(blockLayout->lineAt(0).y(), 140.0);
652 }
653
testParagraphBorders()654 void TestDocumentLayout::testParagraphBorders()
655 {
656 initForNewTest("Paragraph with Borders\nAnother parag\n");
657 QTextCursor cursor(m_doc->begin());
658 QTextBlockFormat bf = cursor.blockFormat();
659 bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderSolid);
660 bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderSolid);
661 bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderSolid);
662 bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderSolid);
663 bf.setProperty(KoParagraphStyle::LeftBorderWidth, 8.0);
664 bf.setProperty(KoParagraphStyle::TopBorderWidth, 9.0);
665 bf.setProperty(KoParagraphStyle::BottomBorderWidth, 10.0);
666 bf.setProperty(KoParagraphStyle::RightBorderWidth, 11.0);
667 cursor.setBlockFormat(bf);
668
669 m_layout->layout();
670 QTextBlock block = m_doc->begin();
671 QTextLayout *blockLayout = block.layout();
672 QCOMPARE(blockLayout->lineAt(0).x(), 8.0);
673 QCOMPARE(blockLayout->lineAt(0).y(), 9.0);
674 QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0);
675 block = block.next();
676 blockLayout = block.layout();
677 //qDebug() << blockLayout->lineAt(0).y();
678 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10)) < ROUNDING); // 14.4 is 12 pt font + 20% linespacing
679
680 // borders + padding create the total inset.
681 bf.setProperty(KoParagraphStyle::LeftPadding, 5.0);
682 bf.setProperty(KoParagraphStyle::RightPadding, 5.0);
683 bf.setProperty(KoParagraphStyle::TopPadding, 5.0);
684 bf.setProperty(KoParagraphStyle::BottomPadding, 5.0);
685 cursor.setBlockFormat(bf);
686
687 m_layout->layout();
688 block = m_doc->begin();
689 blockLayout = block.layout();
690 QCOMPARE(blockLayout->lineAt(0).x(), 13.0);
691 QCOMPARE(blockLayout->lineAt(0).y(), 14.0);
692 QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - 5.0 * 2);
693 block = block.next();
694 blockLayout = block.layout();
695 //qDebug() << blockLayout->lineAt(0).y();
696 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + 5.0 * 2)) < ROUNDING);
697
698 // borders are positioned outside the padding, lets check that to be the case.
699 block = m_doc->begin();
700 KoTextBlockData *data = dynamic_cast<KoTextBlockData*>(block.userData());
701 QVERIFY(data);
702 KoTextBlockBorderData *border = data->border();
703 QVERIFY(border);
704 QCOMPARE(border->hasBorders(), true);
705 QRectF borderOutline = border->rect();
706 QCOMPARE(borderOutline.top(), 0.);
707 QCOMPARE(borderOutline.left(), 0.);
708 QCOMPARE(borderOutline.right(), 200.);
709
710 // qreal borders. Specify an additional width for each side.
711 bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderDouble);
712 bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderDouble);
713 bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderDouble);
714 bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderDouble);
715 bf.setProperty(KoParagraphStyle::LeftInnerBorderWidth, 2.0);
716 bf.setProperty(KoParagraphStyle::RightInnerBorderWidth, 2.0);
717 bf.setProperty(KoParagraphStyle::BottomInnerBorderWidth, 2.0);
718 bf.setProperty(KoParagraphStyle::TopInnerBorderWidth, 2.0);
719 cursor.setBlockFormat(bf);
720
721 m_layout->layout();
722 block = m_doc->begin();
723 blockLayout = block.layout();
724 QCOMPARE(blockLayout->lineAt(0).x(), 15.0);
725 QCOMPARE(blockLayout->lineAt(0).y(), 16.0);
726 QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0) * 2);
727 block = block.next();
728 blockLayout = block.layout();
729 //qDebug() << blockLayout->lineAt(0).y();
730 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + (5.0 + 2.0) * 2)) < ROUNDING);
731
732 // and last, make the 2 qreal border have a blank space in the middle.
733 bf.setProperty(KoParagraphStyle::LeftBorderSpacing, 3.0);
734 bf.setProperty(KoParagraphStyle::RightBorderSpacing, 3.0);
735 bf.setProperty(KoParagraphStyle::BottomBorderSpacing, 3.0);
736 bf.setProperty(KoParagraphStyle::TopBorderSpacing, 3.0);
737 cursor.setBlockFormat(bf);
738
739 m_layout->layout();
740 block = m_doc->begin();
741 blockLayout = block.layout();
742 QCOMPARE(blockLayout->lineAt(0).x(), 18.0);
743 QCOMPARE(blockLayout->lineAt(0).y(), 19.0);
744 QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0 + 3.0) * 2);
745 block = block.next();
746 blockLayout = block.layout();
747 //qDebug() << blockLayout->lineAt(0).y();
748 QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + (5.0 + 2.0 + 3.0) * 2)) < ROUNDING);
749 }
750
testBorderData()751 void TestDocumentLayout::testBorderData()
752 {
753 initForNewTest("Emtpy\nParagraph with Borders\nAnother parag\n");
754
755 KoParagraphStyle style;
756 m_styleManager->add(&style);
757 style.setTopMargin(10);
758 KoListStyle listStyle;
759 KoListLevelProperties llp = listStyle.levelProperties(1);
760 llp.setStyle(KoListStyle::DecimalItem);
761 listStyle.setLevelProperties(llp);
762 style.setListStyle(&listStyle);
763 style.setLeftBorderWidth(3);
764
765 QTextBlock block = m_doc->begin().next();
766 style.applyStyle(block);
767 block = block.next();
768 style.applyStyle(block);
769
770 m_layout->layout();
771
772 block = m_doc->begin().next();
773 KoTextBlockData *data = dynamic_cast<KoTextBlockData*>(block.userData());
774 QVERIFY(data);
775 KoTextBlockBorderData *border = data->border();
776 QVERIFY(border);
777 // 1st parag is 12 * 120% = 14.4 tall.
778 // 2nd parag starts with 10 pt offset = 24.4
779 // Hight of border is 2 parags, each 14.4 for text. Plus 10 pt above the 3th parag. = 38.8pt
780
781 // The rules here are
782 // * two paragraphs share a border
783 // * The top indent (of all parags) does not have a border
784 // * The left border is left of the counter
785 QCOMPARE(border->rect(), QRectF(0, 24.4, 188, 38.8));
786
787 style.setBottomMargin(5);
788 // manually reapply and relayout to force immediate reaction.
789 block = m_doc->begin().next();
790 style.applyStyle(block);
791 block = block.next();
792 style.applyStyle(block);
793 m_layout->layout();
794
795 block = m_doc->begin().next();
796 border = data->border();
797 QVERIFY(border);
798 // The tested here is
799 // * the bottom border of the last parag is directly under the text. (so similar to rule 2)
800 // This means that the height is the prev 38.8 + the bottom of the top parag: 5pt = 43.8pt
801 QCOMPARE(border->rect(), QRectF(0, 24.4, 188, 43.8));
802
803 QCOMPARE(data->counterPosition(), QPointF(3, 24.4));
804
805 block = block.next();
806 data = dynamic_cast<KoTextBlockData*>(block.userData());
807 QCOMPARE(data->counterPosition(), QPointF(3, 53.8));
808 }
809
testEmptyParag()810 void TestDocumentLayout::testEmptyParag()
811 {
812 initForNewTest("Foo\n\nBar\n");
813 m_layout->layout();
814 QTextBlock block = m_doc->begin();
815 QTextLayout *lay = block.layout();
816 QVERIFY(lay);
817 QCOMPARE(lay->lineCount(), 1);
818 const qreal y = lay->lineAt(0).position().y();
819
820 block = block.next();
821 lay = block.layout();
822 QVERIFY(lay);
823 QCOMPARE(lay->lineCount(), 1);
824 QVERIFY(lay->lineAt(0).position().y() > y);
825 QVERIFY(qAbs(lay->lineAt(0).position().y() - 14.4) < ROUNDING);
826 }
827
testDropCaps()828 void TestDocumentLayout::testDropCaps()
829 {
830 initForNewTest(QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing elit, sed diam\nsome more text")); // some not too long text so the dropcap will be bigger than the block
831
832 KoParagraphStyle style;
833 style.setDropCaps(false);
834 style.setDropCapsLength(1);
835 style.setDropCapsLines(4);
836 style.setDropCapsDistance(9.0);
837 QTextBlock block = m_doc->begin();
838 QTextBlock secondblock = block.next();
839 style.applyStyle(block);
840
841 m_layout->layout();
842
843 // dummy version, caps is still false.
844 QTextLayout *blockLayout =block.layout();
845 QVERIFY(blockLayout->lineCount() > 2);
846 QTextLine line = blockLayout->lineAt(0);
847 QVERIFY(line.textLength() > 3);
848
849 style.setDropCaps(true);
850 style.applyStyle(block);
851 m_layout->layout();
852
853 // test that the first text line is the dropcaps and the positions are right.
854 QVERIFY(blockLayout->lineCount() > 2);
855 line = blockLayout->lineAt(0);
856 QCOMPARE(line.textLength(), 1);
857
858 QCOMPARE(line.position().x(), 0.0);
859 QVERIFY(line.position().y() <= 0.0); // can't get a tight-boundingrect here.
860
861 line = blockLayout->lineAt(1);
862 QVERIFY(line.textLength() > 2);
863 qreal heightNormalLine = line.height();
864 qreal linexpos = line.position().x();
865 QCOMPARE(line.position().y(), 0.0); // aligned top
866 QVERIFY(line.position().x() > 20.0); // can't get a tight-boundingrect here.
867
868 // Now test that a following block is moved inward by the same about since
869 // it should still be influenced by the dropcap
870 blockLayout = secondblock.layout();
871 QVERIFY(blockLayout->lineCount() == 1);
872 line = blockLayout->lineAt(0);
873 QVERIFY(line.textLength() > 3);
874 QCOMPARE(line.position().x(), linexpos);
875 QVERIFY(line.position().x() > 20.0); // can't get a tight-boundingrect here.
876
877 style.setDropCaps(false); // remove it
878 style.applyStyle(block);
879 m_layout->layout();
880 blockLayout = block.layout();
881
882 // test that the first text line is no longer dropcaps
883 QVERIFY(blockLayout->lineCount() > 2);
884 line = blockLayout->lineAt(0);
885 QVERIFY(line.textLength() > 1);
886 QCOMPARE(line.height(), heightNormalLine);
887 }
888
testNonBreakableLines()889 void TestDocumentLayout::testNonBreakableLines()
890 {
891 initForNewTest(m_loremIpsum.left(97) + '\n' + m_loremIpsum);
892 QTextBlock block = m_doc->begin().next();
893 QTextCursor cursor(block);
894 QTextBlockFormat format = cursor.blockFormat();
895 format.setNonBreakableLines(true);
896 cursor.setBlockFormat(format);
897
898 m_shape1->setSize(QSizeF(200, 100));
899 KoShape *shape2 = new MockTextShape();
900 shape2->setSize(QSizeF(120, 1000));
901 m_layout->addShape(shape2);
902
903 m_layout->layout();
904
905 block = m_doc->begin();
906 QTextLayout *l = block.layout();
907 // make sure parag1 is in shape 1.
908 for (int i = 0; i < l->lineCount(); i++)
909 QVERIFY(l->lineAt(i).y() < 100.);
910
911 block = block.next();
912 l = block.layout();
913 QCOMPARE(l->lineAt(0).y(), 110.);
914 }
915
testShapePosition()916 void TestDocumentLayout::testShapePosition()
917 {
918 initForNewTest("line\nParag2\nSimple Parag\nLast");
919
920 m_shape1->setSize(QSizeF(200, 40));
921 MockTextShape *shape2 = new MockTextShape();
922 shape2->setSize(QSizeF(200, 100));
923 m_layout->addShape(shape2);
924
925 m_layout->layout();
926
927 QCOMPARE(m_shape1->textShapeData()->position(), 0);
928 QCOMPARE(m_shape1->textShapeData()->endPosition(), 11);
929 QCOMPARE(shape2->textShapeData()->position(), 12);
930 QCOMPARE(shape2->textShapeData()->endPosition(), 30);
931 }
932
testShapePosition2()933 void TestDocumentLayout::testShapePosition2()
934 {
935 initForNewTest("Foo\n" + m_loremIpsum);
936
937 m_shape1->setSize(QSizeF(200, 40));
938 MockTextShape *shape2 = new MockTextShape();
939 shape2->setSize(QSizeF(200, 1000));
940 m_layout->addShape(shape2);
941
942 m_layout->layout();
943
944 QCOMPARE(m_shape1->textShapeData()->position(), 0);
945 int split = m_shape1->textShapeData()->endPosition();
946 // qDebug() << split;
947 QVERIFY(split > 4 && split < m_loremIpsum.length());
948 QCOMPARE(shape2->textShapeData()->position(), split + 1);
949 QCOMPARE(shape2->textShapeData()->endPosition(), m_loremIpsum.length() + 5);
950 }
951
952 QTEST_KDEMAIN(TestDocumentLayout, GUI)
953