Warning, file /office/calligra/libs/textlayout/KoTextLayoutArea.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2008,2011 Thorsten Zachmann <zachmann@kde.org>
0004  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
0005  * Copyright (C) 2008 Roopesh Chander <roop@forwardbias.in>
0006  * Copyright (C) 2007-2008 Pierre Ducroquet <pinaraf@pinaraf.info>
0007  * Copyright (C) 2009-2011 KO GmbH <cbo@kogmbh.com>
0008  * Copyright (C) 2009-2011 C. Boemann <cbo@boemann.dk>
0009  * Copyright (C) 2010 Nandita Suri <suri.nandita@gmail.com>
0010  * Copyright (C) 2010 Ajay Pundhir <ajay.pratap@iiitb.net>
0011  * Copyright (C) 2011 Lukáš Tvrdý <lukas.tvrdy@ixonos.com>
0012  * Copyright (C) 2011 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
0013  * Copyright (C) 2011 Stuart Dickson <stuart@furkinfantasic.net>
0014  *
0015  * This library is free software; you can redistribute it and/or
0016  * modify it under the terms of the GNU Library General Public
0017  * License as published by the Free Software Foundation; either
0018  * version 2 of the License, or (at your option) any later version.
0019  *
0020  * This library is distributed in the hope that it will be useful,
0021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0023  * Library General Public License for more details.
0024  *
0025  * You should have received a copy of the GNU Library General Public License
0026  * along with this library; see the file COPYING.LIB.  If not, write to
0027  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0028  * Boston, MA 02110-1301, USA.
0029  */
0030 
0031 #include "KoTextLayoutArea.h"
0032 #include "KoTextLayoutArea_p.h"
0033 
0034 #include "TableIterator.h"
0035 #include "ListItemsHelper.h"
0036 #include "RunAroundHelper.h"
0037 #include "KoTextDocumentLayout.h"
0038 #include "FrameIterator.h"
0039 #include "KoPointedAt.h"
0040 #include "KoCharAreaInfo.h"
0041 
0042 #include <KoTextDocument.h>
0043 #include <KoParagraphStyle.h>
0044 #include <KoCharacterStyle.h>
0045 #include <KoListStyle.h>
0046 #include <KoTableStyle.h>
0047 #include <KoStyleManager.h>
0048 #include <KoTextBlockData.h>
0049 #include <KoText.h>
0050 #include <KoChangeTracker.h>
0051 #include <KoChangeTrackerElement.h>
0052 #include <KoInlineNote.h>
0053 #include <KoTextSoftPageBreak.h>
0054 #include <KoInlineTextObjectManager.h>
0055 
0056 #include <TextLayoutDebug.h>
0057 
0058 #include <QTextFrame>
0059 #include <QTextTable>
0060 #include <QTextList>
0061 #include <QStyle>
0062 #include <QFontMetrics>
0063 #include <QTextFragment>
0064 #include <QTextLayout>
0065 #include <QTextCursor>
0066 
0067 #include <algorithm>
0068 
0069 extern int qt_defaultDpiY();
0070 Q_DECLARE_METATYPE(QTextDocument *)
0071 
0072 #define DropCapsAdditionalFormattingId 25602902
0073 #define PresenterFontStretch 1.2
0074 
0075 KoTextLayoutArea::KoTextLayoutArea(KoTextLayoutArea *p, KoTextDocumentLayout *documentLayout)
0076  : d (new Private)
0077 {
0078     d->parent = p;
0079     d->documentLayout = documentLayout;
0080 }
0081 
0082 KoTextLayoutArea::~KoTextLayoutArea()
0083 {
0084     qDeleteAll(d->tableAreas);
0085     qDeleteAll(d->footNoteAreas);
0086     qDeleteAll(d->preregisteredFootNoteAreas);
0087     delete d->startOfArea;
0088     delete d->endOfArea;
0089     delete d;
0090 }
0091 
0092 
0093 KoPointedAt KoTextLayoutArea::hitTest(const QPointF &p, Qt::HitTestAccuracy accuracy) const
0094 {
0095     QPointF point = p - QPointF(0, d->verticalAlignOffset);
0096 
0097     if (d->startOfArea == 0) // We have not been layouted yet
0098         return KoPointedAt();
0099 
0100     KoPointedAt pointedAt;
0101     bool basicallyFound = false;
0102 
0103     QTextFrame::iterator it = d->startOfArea->it;
0104     QTextFrame::iterator stop = d->endOfArea->it;
0105     if (!stop.atEnd()) {
0106         if(!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) {
0107             // Last thing we contain is a frame (table) or first part of a paragraph split in two
0108             // The stop point should be the object after that
0109             // However if stop is already atEnd we shouldn't increment further
0110             ++stop;
0111         }
0112     }
0113     int tableAreaIndex = 0;
0114     int tocIndex = 0;
0115     int footNoteIndex = 0;
0116     for (; it != stop && !it.atEnd(); ++it) {
0117         QTextBlock block = it.currentBlock();
0118         QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
0119         QTextFrame *subFrame = it.currentFrame();
0120 
0121         if (table) {
0122             if (tableAreaIndex >= d->tableAreas.size()) {
0123                 continue;
0124             }
0125             if (point.y() > d->tableAreas[tableAreaIndex]->top()
0126                     && point.y() < d->tableAreas[tableAreaIndex]->bottom()) {
0127                 return d->tableAreas[tableAreaIndex]->hitTest(point, accuracy);
0128             }
0129             ++tableAreaIndex;
0130             continue;
0131         } else if (subFrame) {
0132             if (it.currentFrame()->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
0133                 if (point.y() > d->endNotesArea->top()
0134                         && point.y() < d->endNotesArea->bottom()) {
0135                     pointedAt = d->endNotesArea->hitTest(point, accuracy);
0136                     return pointedAt;
0137                 }
0138             }
0139             break;
0140         } else {
0141             if (!block.isValid())
0142                 continue;
0143         }
0144         if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
0145             // check if p is over table of content
0146             if (point.y() > d->generatedDocAreas[tocIndex]->top()
0147                     && point.y() < d->generatedDocAreas[tocIndex]->bottom()) {
0148                 pointedAt = d->generatedDocAreas[tocIndex]->hitTest(point, accuracy);
0149                 pointedAt.position = block.position();
0150                 return pointedAt;
0151             }
0152             ++tocIndex;
0153             continue;
0154         }
0155         if (basicallyFound) // a subsequent table or lines have now had their chance
0156             return pointedAt;
0157 
0158         QTextLayout *layout = block.layout();
0159         QTextFrame::iterator next = it;
0160         ++next;
0161         if (next != stop && next.currentFrame() == 0 && point.y() > layout->boundingRect().bottom()) {
0162             // just skip this block.
0163             continue;
0164         }
0165 
0166         for (int i = 0; i < layout->lineCount(); i++) {
0167             QTextLine line = layout->lineAt(i);
0168             if (block == d->startOfArea->it.currentBlock() && line.textStart() < d->startOfArea->lineTextStart) {
0169                 continue; // this line is part of a previous layoutArea
0170             }
0171             if (point.y() > line.y() + line.height()) {
0172                 pointedAt.position = block.position() + line.textStart() + line.textLength();
0173                 if (block == d->endOfArea->it.currentBlock() && line.textStart() + line.textLength() >= d->endOfArea->lineTextStart) {
0174                     pointedAt.position = block.position() + line.xToCursor(point.x());
0175                     break; // this and following lines are part of a next layoutArea
0176                 }
0177                 continue;
0178             }
0179             if (accuracy == Qt::ExactHit && point.y() < line.y()) { // between lines
0180                 return KoPointedAt();
0181             }
0182             const QRectF lineRect = line.naturalTextRect();
0183             if (accuracy == Qt::ExactHit && // left or right of line
0184                     (point.x() < lineRect.left() || point.x() > lineRect.right())) {
0185                 return KoPointedAt();
0186             }
0187             if (point.x() > lineRect.x() + lineRect.width() && layout->textOption().textDirection() == Qt::RightToLeft) {
0188                 // totally right of RTL text means the position is the start of the text.
0189                 //TODO how about the other side?
0190                 pointedAt.position = block.position() + line.textStart();
0191                 return pointedAt;
0192             }
0193             if (basicallyFound && point.y() < lineRect.y()) {
0194                 // This was not same baseline so basicallyFound was correct
0195                 return pointedAt;
0196             }
0197             if (point.x() > lineRect.x() + lineRect.width()) {
0198                 // right of line
0199                 basicallyFound = true;
0200                 pointedAt.position = block.position() + line.textStart() + line.textLength();
0201                 continue; // don't break as next line may be on same baseline
0202             }
0203             pointedAt.position = block.position() + line.xToCursor(point.x());
0204             QTextCursor tmpCursor(block);
0205             tmpCursor.setPosition(block.position() + line.xToCursor(point.x(), QTextLine::CursorOnCharacter) + 1);
0206             pointedAt.fillInLinks(tmpCursor, d->documentLayout->inlineTextObjectManager(), d->documentLayout->textRangeManager());
0207             return pointedAt;
0208         }
0209     }
0210 
0211     //and finally test the footnotes
0212     point -= QPointF(0, bottom() - d->footNotesHeight);
0213     while (footNoteIndex < d->footNoteAreas.length()) {
0214         // check if p is over foot notes area
0215         if (point.y() > 0 && point.y() < d->footNoteAreas[footNoteIndex]->bottom()
0216                                                     - d->footNoteAreas[footNoteIndex]->top()) {
0217             pointedAt = d->footNoteAreas[footNoteIndex]->hitTest(point, accuracy);
0218             return pointedAt;
0219         }
0220         point -= QPointF(0, d->footNoteAreas[footNoteIndex]->bottom() - d->footNoteAreas[footNoteIndex]->top());
0221         ++footNoteIndex;
0222     }
0223     return pointedAt;
0224 }
0225 
0226 
0227 QVector<KoCharAreaInfo> KoTextLayoutArea::generateCharAreaInfos() const
0228 {
0229     QVector<KoCharAreaInfo> result;
0230     if (d->startOfArea == 0 || d->endOfArea == 0) { // We have not been completely layouted yet
0231         debugTextLayout << "called when not completely layouted yet";
0232         return result;
0233     }
0234 
0235     QTextFrame::iterator it = d->startOfArea->it;
0236     QTextFrame::iterator stop = d->endOfArea->it;
0237 
0238     int tableAreaIndex = 0;
0239     int tocIndex = 0;
0240     for (; it != stop && !it.atEnd(); ++it) {
0241        QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
0242        if (table) {
0243             if (tableAreaIndex >= d->tableAreas.size()) {
0244                 continue;
0245             }
0246             result.append(d->tableAreas[tableAreaIndex]->generateCharAreaInfos());
0247             ++tableAreaIndex;
0248             continue;
0249         }
0250 
0251         QTextFrame *subFrame = it.currentFrame();
0252         if (subFrame) {
0253             if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
0254                 result.append(d->endNotesArea->generateCharAreaInfos());
0255             }
0256             continue;
0257         }
0258 
0259         QTextBlock block = it.currentBlock();
0260         if (!block.isValid()) {
0261             continue;
0262         }
0263 
0264         if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
0265             result.append(d->generatedDocAreas[tocIndex]->generateCharAreaInfos());
0266             ++tocIndex;
0267             continue;
0268         }
0269 
0270         // TODO: also include header/paragraph numbering/bullet points
0271         QTextLayout *layout = block.layout();
0272 
0273         for (int i = 0; i < layout->lineCount(); ++i) {
0274             QTextLine line = layout->lineAt(i);
0275             if (block == d->startOfArea->it.currentBlock() && line.textStart() < d->startOfArea->lineTextStart) {
0276                 continue; // this line is part of a previous layoutArea
0277             }
0278             if (block == d->endOfArea->it.currentBlock() && line.textStart() + line.textLength() >= d->endOfArea->lineTextStart) {
0279                 break; // this and following lines are part of a next layoutArea
0280             }
0281             qreal xLeading;
0282             qreal xTrailing;
0283             for (int j = line.textStart(); j < line.textStart() + line.textLength(); ++j) {
0284                 // TODO: support RTL
0285                 xLeading = line.cursorToX(j, QTextLine::Leading);
0286                 xTrailing = line.cursorToX(j, QTextLine::Trailing);
0287                 QRectF rect(xLeading, line.y(), xTrailing-xLeading, line.height()); // TODO: at least height needs more work
0288                 result.append(KoCharAreaInfo(rect, block.text().at(j)));
0289             }
0290 
0291             // TODO: perhaps only at end of paragraph (last qtextline) add linebreak, for in-paragraph linebreak
0292             // use real whitespace(s) found in original text (or see if forced linebreak)
0293             QRectF rect(xTrailing, line.y(), 1, line.height()); // TODO: better dummy width needed, with reasoning
0294             result.append(KoCharAreaInfo(rect, QLatin1Char('\n')));
0295         }
0296     }
0297 
0298     qreal footNoteYOffset = bottom() - d->footNotesHeight;
0299     foreach(KoTextLayoutNoteArea *footerArea, d->footNoteAreas) {
0300         QVector<KoCharAreaInfo> footNoteCharAreaInfos = footerArea->generateCharAreaInfos();
0301         QMutableVectorIterator<KoCharAreaInfo> it(footNoteCharAreaInfos);
0302         while (it.hasNext()) {
0303             KoCharAreaInfo &info = it.next();
0304             info.rect.translate(0, footNoteYOffset);
0305         }
0306         result.append(footNoteCharAreaInfos);
0307         footNoteYOffset += footerArea->bottom() - footerArea->top();
0308     }
0309 
0310     return result;
0311 }
0312 
0313 
0314 QRectF KoTextLayoutArea::selectionBoundingBox(QTextCursor &cursor) const
0315 {
0316     QRectF retval(-5E6, top(), 105E6, 0);
0317 
0318     if (d->startOfArea == 0) // We have not been layouted yet
0319         return QRectF();
0320     if (d->endOfArea == 0) // no end area yet
0321         return QRectF();
0322 
0323     QTextFrame::iterator it = d->startOfArea->it;
0324     QTextFrame::iterator stop = d->endOfArea->it;
0325     if (!stop.atEnd()) {
0326         if(!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) {
0327             // Last thing we show is a frame (table) or first part of a paragraph split in two
0328             // The stop point should be the object after that
0329             // However if stop is already atEnd we shouldn't increment further
0330             ++stop;
0331         }
0332     }
0333 
0334     QTextFrame *subFrame;
0335     int footNoteIndex = 0;
0336     qreal offset = bottom() - d->footNotesHeight;
0337     while (footNoteIndex < d->footNoteAreas.length()) {
0338         subFrame = d->footNoteFrames[footNoteIndex];
0339         if (cursor.selectionStart() >= subFrame->firstPosition() && cursor.selectionEnd() <= subFrame->lastPosition()) {
0340             return d->footNoteAreas[footNoteIndex]->selectionBoundingBox(cursor).translated(0, offset) ;
0341         }
0342         offset += d->footNoteAreas[footNoteIndex]->bottom() - d->footNoteAreas[footNoteIndex]->top();
0343         ++footNoteIndex;
0344     }
0345 
0346     int tableAreaIndex = 0;
0347     int tocIndex = 0;
0348 
0349     for (; it != stop && !it.atEnd(); ++it) {
0350         QTextBlock block = it.currentBlock();
0351         QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
0352         QTextFrame *subFrame = it.currentFrame();
0353 
0354         if (table) {
0355             if (tableAreaIndex >= d->tableAreas.size()) {
0356                 continue;
0357             }
0358             if (cursor.selectionEnd() < table->firstPosition()) {
0359                 return retval.translated(0, d->verticalAlignOffset);
0360             }
0361             if (cursor.selectionStart() > table->lastPosition()) {
0362                 ++tableAreaIndex;
0363                 continue;
0364             }
0365             if (cursor.selectionStart() >= table->firstPosition() && cursor.selectionEnd() <= table->lastPosition()) {
0366                 return d->tableAreas[tableAreaIndex]->selectionBoundingBox(cursor).translated(0, d->verticalAlignOffset);
0367             }
0368             if (cursor.selectionStart() >= table->firstPosition()) {
0369                 retval = d->tableAreas[tableAreaIndex]->boundingRect();
0370             } else {
0371                 retval |= d->tableAreas[tableAreaIndex]->boundingRect();
0372             }
0373             ++tableAreaIndex;
0374             continue;
0375         } else if (subFrame) {
0376             if (it.currentFrame()->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
0377                 if (cursor.selectionEnd() < subFrame->firstPosition()) {
0378                     return retval.translated(0, d->verticalAlignOffset);
0379                 }
0380                 if (cursor.selectionStart() > subFrame->lastPosition()) {
0381                     break;
0382                 }
0383                 if (cursor.selectionStart() >= subFrame->firstPosition() && cursor.selectionEnd() <= subFrame->lastPosition()) {
0384                     return d->endNotesArea->selectionBoundingBox(cursor).translated(0, d->verticalAlignOffset);
0385                 }
0386                 break;
0387             }
0388         } else {
0389             if (!block.isValid())
0390                 continue;
0391         }
0392         if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
0393             if (cursor.selectionStart()  <= block.position()
0394                 && cursor.selectionEnd() >= block.position()) {
0395                 retval |= d->generatedDocAreas[tocIndex]->boundingRect();
0396             }
0397             ++tocIndex;
0398             continue;
0399         }
0400 
0401         if(cursor.selectionEnd() < block.position()) {
0402             return retval.translated(0, d->verticalAlignOffset);
0403         }
0404         if(cursor.selectionStart() >= block.position()
0405             && cursor.selectionStart() < block.position() + block.length()) {
0406             QTextLine line = block.layout()->lineForTextPosition(cursor.selectionStart() - block.position());
0407             if (line.isValid()) {
0408                 retval.setTop(line.y());
0409                 retval.setBottom(line.y());
0410             }
0411         }
0412         if(cursor.selectionEnd() >= block.position()
0413             && cursor.selectionEnd() < block.position() + block.length()) {
0414             QTextLine line = block.layout()->lineForTextPosition(cursor.selectionEnd() - block.position());
0415             if (line.isValid()) {
0416                 retval.setBottom(line.y() + line.height());
0417                 if (line.ascent()==0) {
0418                     // Block is empty from any visible content and has as such no height
0419                     // but in that case the block font defines line height
0420                     retval.setBottom(line.y() + 24);
0421                 }
0422 
0423                 if (cursor.selectionStart() == cursor.selectionEnd()) {
0424                     // We only have a caret so let's set the rect a bit more narrow
0425                     retval.setX(line.cursorToX(cursor.position() - block.position()));
0426                     retval.setWidth(1);
0427                 }
0428             }
0429         }
0430         // if the full paragraph is selected to add it to the rect. This makes sure we get a rect for the case
0431         // where the end of the selection lies is a different area.
0432         if (cursor.selectionEnd() >= block.position() + block.length() && cursor.selectionStart() <= block.position()) {
0433             QTextLine line = block.layout()->lineForTextPosition(block.length()-1);
0434             if (line.isValid()) {
0435                 retval.setBottom(line.y() + line.height());
0436                 if (line.ascent()==0) {
0437                     // Block is empty from any visible content and has as such no height
0438                     // but in that case the block font defines line height
0439                     retval.setBottom(line.y() + 24);
0440                 }
0441             }
0442         }
0443     }
0444     return retval.translated(0, d->verticalAlignOffset);
0445 }
0446 
0447 
0448 bool KoTextLayoutArea::isStartingAt(FrameIterator *cursor) const
0449 {
0450     if (d->startOfArea) {
0451         return *d->startOfArea == *cursor;
0452     }
0453 
0454     return false;
0455 }
0456 
0457 QTextFrame::iterator KoTextLayoutArea::startTextFrameIterator() const
0458 {
0459     return d->startOfArea->it;
0460 }
0461 
0462 QTextFrame::iterator KoTextLayoutArea::endTextFrameIterator() const
0463 {
0464     return d->endOfArea->it;
0465 }
0466 
0467 void KoTextLayoutArea::backtrackKeepWithNext(FrameIterator *cursor)
0468 {
0469     QTextFrame::iterator it = cursor->it;
0470 
0471     while (!(it == d->startOfArea->it)) {
0472         --it;
0473         QTextBlock block = it.currentBlock();
0474         QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
0475         QTextFrame *subFrame = it.currentFrame();
0476         bool keepWithNext = false;
0477         if (table) {
0478             keepWithNext = table->format().boolProperty(KoTableStyle::KeepWithNext);
0479             //setBottom(tableArea->bottom() + d->footNotesHeight);
0480         } else if (subFrame) {
0481             Q_ASSERT(false); // there should never be an aux frame before normal layouted stuff
0482         } else if (block.isValid()) {
0483             keepWithNext = block.blockFormat().boolProperty(KoParagraphStyle::KeepWithNext);
0484             //setBottom(d->blockRects.last()->bottom() + d->footNotesHeight);
0485         }
0486         if (!keepWithNext) {
0487             cursor->it = ++it;
0488             break;
0489         }
0490     }
0491 }
0492 
0493 bool KoTextLayoutArea::layout(FrameIterator *cursor)
0494 {
0495     qDeleteAll(d->tableAreas);
0496     d->tableAreas.clear();
0497     qDeleteAll(d->footNoteAreas);
0498     d->footNoteAreas.clear();
0499     qDeleteAll(d->preregisteredFootNoteAreas);
0500     d->preregisteredFootNoteAreas.clear();
0501     d->footNoteFrames.clear();
0502     d->preregisteredFootNoteFrames.clear();
0503     qDeleteAll(d->generatedDocAreas);
0504     d->generatedDocAreas.clear();
0505     d->blockRects.clear();
0506     delete d->endNotesArea;
0507     d->endNotesArea=0;
0508     if (d->copyEndOfArea && !d->copyEndOfArea->isValid()) {
0509         delete d->copyEndOfArea;
0510         d->copyEndOfArea = 0;
0511     }
0512     if (d->endOfArea && d->endOfArea->isValid()) {
0513         delete d->copyEndOfArea;
0514         d->copyEndOfArea = new FrameIterator(d->endOfArea);
0515     }
0516     delete d->startOfArea;
0517     delete d->endOfArea;
0518     d->dropCapsWidth = 0;
0519     d->dropCapsDistance = 0;
0520 
0521     d->startOfArea = new FrameIterator(cursor);
0522     d->endOfArea = 0;
0523     d->y = top();
0524     d->neededWidth = 0;
0525     setBottom(top());
0526     d->bottomSpacing = 0;
0527     d->footNoteAutoCount = 0;
0528     d->footNotesHeight = 0;
0529     d->preregisteredFootNotesHeight = 0;
0530     d->prevBorder = 0;
0531     d->prevBorderPadding = 0;
0532 
0533     if (d->footNoteCursorFromPrevious) {
0534         KoTextLayoutNoteArea *footNoteArea = new KoTextLayoutNoteArea(d->continuedNoteFromPrevious, this, d->documentLayout);
0535         d->footNoteFrames.append(d->continuedNoteFromPrevious->textFrame());
0536         footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom());
0537         footNoteArea->setAsContinuedArea(true);
0538         footNoteArea->layout(d->footNoteCursorFromPrevious);
0539         d->footNotesHeight += footNoteArea->bottom() - footNoteArea->top();
0540         d->footNoteAreas.append(footNoteArea);
0541     }
0542     while (!cursor->it.atEnd()) {
0543         QTextBlock block = cursor->it.currentBlock();
0544         QTextTable *table = qobject_cast<QTextTable*>(cursor->it.currentFrame());
0545         QTextFrame *subFrame = cursor->it.currentFrame();
0546         if (table) {
0547             QString masterPageName = table->frameFormat().property(KoTableStyle::MasterPageName).toString();
0548             bool masterPageNameChanged = !masterPageName.isEmpty();
0549             if (masterPageNameChanged) {
0550                 cursor->masterPageName = masterPageName;
0551             }
0552 
0553             if (!virginPage()) {
0554                 int breaktype = table->frameFormat().intProperty(KoTableStyle::BreakBefore);
0555                 if ((acceptsPageBreak() && (masterPageNameChanged || (breaktype == KoText::PageBreak)))
0556                     || (acceptsColumnBreak() && (breaktype == KoText::ColumnBreak))) {
0557                     d->endOfArea = new FrameIterator(cursor);
0558                     setBottom(d->y + d->footNotesHeight);
0559                     if (!d->blockRects.isEmpty()) {
0560                         d->blockRects.last().setBottom(d->y);
0561                     }
0562                     return false;
0563                 }
0564             }
0565 
0566             // Let's create KoTextLayoutTableArea and let that handle the table
0567             KoTextLayoutTableArea *tableArea = new KoTextLayoutTableArea(table, this, d->documentLayout);
0568             d->tableAreas.append(tableArea);
0569             d->y += d->bottomSpacing;
0570             if (!d->blockRects.isEmpty()) {
0571                 d->blockRects.last().setBottom(d->y);
0572             }
0573             tableArea->setVirginPage(virginPage());
0574             tableArea->setReferenceRect(left(), right(), d->y, maximumAllowedBottom());
0575             if (tableArea->layoutTable(cursor->tableIterator(table)) == false) {
0576                 d->endOfArea = new FrameIterator(cursor);
0577                 d->y = tableArea->bottom();
0578                 setBottom(d->y + d->footNotesHeight);
0579                 // Expand bounding rect so if we have content outside we show it
0580                 expandBoundingLeft(tableArea->boundingRect().left());
0581                 expandBoundingRight(tableArea->boundingRect().right());
0582 
0583                 return false;
0584             }
0585             setVirginPage(false);
0586             // Expand bounding rect so if we have content outside we show it
0587             expandBoundingLeft(tableArea->boundingRect().left());
0588             expandBoundingRight(tableArea->boundingRect().right());
0589             d->bottomSpacing = 0;
0590             d->y = tableArea->bottom();
0591             delete cursor->currentTableIterator;
0592             cursor->currentTableIterator = 0;
0593         } else if (subFrame) {
0594             if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
0595                 Q_ASSERT(d->endNotesArea == 0);
0596                 d->endNotesArea = new KoTextLayoutEndNotesArea(this, d->documentLayout);
0597                 d->y += d->bottomSpacing;
0598                 if (!d->blockRects.isEmpty()) {
0599                     d->blockRects.last().setBottom(d->y);
0600                 }
0601                 d->endNotesArea->setVirginPage(virginPage());
0602                 d->endNotesArea->setReferenceRect(left(), right(), d->y, maximumAllowedBottom());
0603                 if (d->endNotesArea->layout(cursor->subFrameIterator(subFrame)) == false) {
0604                     d->endOfArea = new FrameIterator(cursor);
0605                     d->y = d->endNotesArea->bottom();
0606                     setBottom(d->y + d->footNotesHeight);
0607                     // Expand bounding rect so if we have content outside we show it
0608                     expandBoundingLeft(d->endNotesArea->boundingRect().left());
0609                     expandBoundingRight(d->endNotesArea->boundingRect().right());
0610                     return false;
0611                 }
0612                 setVirginPage(false);
0613                 // Expand bounding rect so if we have content outside we show it
0614                 expandBoundingLeft(d->endNotesArea->boundingRect().left());
0615                 expandBoundingRight(d->endNotesArea->boundingRect().right());
0616                 d->bottomSpacing = 0;
0617                 d->y = d->endNotesArea->bottom();
0618                 delete cursor->currentSubFrameIterator;
0619                 cursor->currentSubFrameIterator = 0;
0620 
0621                 // we have layouted till the end of the document except for a blank block
0622                 // which we should ignore
0623                 ++(cursor->it);
0624                 ++(cursor->it);
0625                 break;
0626             }
0627         } else if (block.isValid()) {
0628             if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
0629                 QVariant data = block.blockFormat().property(KoParagraphStyle::GeneratedDocument);
0630                 QTextDocument *generatedDocument = data.value<QTextDocument *>();
0631 
0632                 // Let's create KoTextLayoutArea and let it handle the generated document
0633                 KoTextLayoutArea *area = new KoTextLayoutArea(this, documentLayout());
0634                 d->generatedDocAreas.append(area);
0635                 d->y += d->bottomSpacing;
0636                 if (!d->blockRects.isEmpty()) {
0637                     d->blockRects.last().setBottom(d->y);
0638                 }
0639                 area->setVirginPage(virginPage());
0640                 area->setAcceptsPageBreak(acceptsPageBreak());
0641                 area->setAcceptsColumnBreak(acceptsColumnBreak());
0642                 area->setReferenceRect(left(), right(), d->y, maximumAllowedBottom());
0643                 QTextLayout *blayout = block.layout();
0644                 blayout->beginLayout();
0645                 QTextLine line = blayout->createLine();
0646                 line.setNumColumns(0);
0647                 line.setPosition(QPointF(left(), d->y));
0648                 blayout->endLayout();
0649 
0650                 if (area->layout(cursor->subFrameIterator(generatedDocument->rootFrame())) == false) {
0651                     cursor->lineTextStart = 1; // fake we are not done
0652                     d->endOfArea = new FrameIterator(cursor);
0653                     d->y = area->bottom();
0654                     setBottom(d->y + d->footNotesHeight);
0655                     // Expand bounding rect so if we have content outside we show it
0656                     expandBoundingLeft(area->boundingRect().left());
0657                     expandBoundingRight(area->boundingRect().right());
0658                     return false;
0659                 }
0660                 setVirginPage(false);
0661                 // Expand bounding rect so if we have content outside we show it
0662                 expandBoundingLeft(area->boundingRect().left());
0663                 expandBoundingRight(area->boundingRect().right());
0664                 d->bottomSpacing = 0;
0665                 d->y = area->bottom();
0666                 delete cursor->currentSubFrameIterator;
0667                 cursor->lineTextStart = -1; // fake we are done
0668                 cursor->currentSubFrameIterator = 0;
0669             } else {
0670                 // FIXME this doesn't work for cells inside tables. We probably should make it more
0671                 // generic to handle such cases too.
0672                 QString masterPageName = block.blockFormat().property(KoParagraphStyle::MasterPageName).toString();
0673                 bool masterPageNameChanged = !masterPageName.isEmpty();
0674                 if (masterPageNameChanged) {
0675                     cursor->masterPageName = masterPageName;
0676                 }
0677 
0678                 if (!virginPage()) {
0679                     int breaktype = block.blockFormat().intProperty(KoParagraphStyle::BreakBefore);
0680                     if ((acceptsPageBreak() && (masterPageNameChanged || (breaktype == KoText::PageBreak)))
0681                         ||(acceptsColumnBreak() && (breaktype == KoText::ColumnBreak))) {
0682                         d->endOfArea = new FrameIterator(cursor);
0683                         setBottom(d->y + d->footNotesHeight);
0684                         if (!d->blockRects.isEmpty()) {
0685                             d->blockRects.last().setBottom(d->y);
0686                         }
0687                         return false;
0688                     }
0689                 }
0690 
0691                 if (layoutBlock(cursor) == false) {
0692                     if (cursor->lineTextStart == -1) {
0693                         //Nothing was added so lets backtrack keep-with-next
0694                         backtrackKeepWithNext(cursor);
0695                     }
0696                     d->endOfArea = new FrameIterator(cursor);
0697                     setBottom(d->y + d->footNotesHeight);
0698                     d->blockRects.last().setBottom(d->y);
0699                     return false;
0700                 }
0701                 d->extraTextIndent = 0;
0702 
0703                 int breaktype = block.blockFormat().intProperty(KoParagraphStyle::BreakAfter);
0704                 if ((acceptsPageBreak() && (breaktype & KoText::PageBreak))
0705                     || (acceptsColumnBreak() && (breaktype & KoText::ColumnBreak))) {
0706                     Q_ASSERT(!cursor->it.atEnd());
0707                     QTextFrame::iterator nextIt = cursor->it;
0708                     ++nextIt;
0709                     bool wasIncremented = !nextIt.currentFrame();
0710                     if (wasIncremented)
0711                         cursor->it = nextIt;
0712                     d->endOfArea = new FrameIterator(cursor);
0713                     if (!wasIncremented)
0714                         ++(cursor->it);
0715                     setBottom(d->y + d->footNotesHeight);
0716                     d->blockRects.last().setBottom(d->y);
0717                     return false;
0718                 }
0719             }
0720         }
0721         bool atEnd = cursor->it.atEnd();
0722         if (!atEnd) {
0723             ++(cursor->it);
0724         }
0725     }
0726     d->endOfArea = new FrameIterator(cursor);
0727     d->y = qMin(maximumAllowedBottom(), d->y + d->bottomSpacing);
0728     setBottom(d->y + d->footNotesHeight);
0729     if (!d->blockRects.isEmpty()) {
0730         d->blockRects.last().setBottom(d->y);
0731     }
0732     if (d->maximumAllowedWidth>0) {
0733         d->right += d->neededWidth - d->width;
0734         d->maximumAllowedWidth = 0;
0735         setVirginPage(true);
0736         KoTextLayoutArea::layout(new FrameIterator(d->startOfArea));
0737     }
0738     return true; // we have layouted till the end of the frame
0739 }
0740 
0741 
0742 QTextLine KoTextLayoutArea::Private::restartLayout(QTextBlock &block, int lineTextStartOfLastKeep)
0743 {
0744     QTextLayout *layout = block.layout();
0745     KoTextBlockData blockData(block);
0746     QPointF stashedCounterPosition = blockData.counterPosition();
0747     QVector<LineKeeper> stashedLines;
0748     QTextLine line;
0749     for(int i = 0; i < layout->lineCount(); i++) {
0750         QTextLine l = layout->lineAt(i);
0751         if (l.textStart() >= lineTextStartOfLastKeep) {
0752             break;
0753         }
0754         LineKeeper lk;
0755         lk.lineWidth = l.width();
0756         lk.columns = l.textLength();
0757         lk.position = l.position();
0758         stashedLines.append(lk);
0759     }
0760     layout->clearLayout();
0761     layout->beginLayout();
0762     line = layout->createLine();
0763 
0764     return recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
0765 }
0766 
0767 void KoTextLayoutArea::Private::stashRemainingLayout(QTextBlock &block, int lineTextStartOfFirstKeep, QVector<LineKeeper> &stashedLines, QPointF &stashedCounterPosition)
0768 {
0769     QTextLayout *layout = block.layout();
0770     KoTextBlockData blockData(block);
0771     stashedCounterPosition = blockData.counterPosition();
0772     QTextLine line;
0773     for(int i = 0; i < layout->lineCount(); i++) {
0774         QTextLine l = layout->lineAt(i);
0775         if (l.textStart() < lineTextStartOfFirstKeep) {
0776             continue;
0777         }
0778         LineKeeper lk;
0779         lk.lineWidth = l.width();
0780         lk.columns = l.textLength();
0781         lk.position = l.position();
0782         stashedLines.append(lk);
0783     }
0784 }
0785 
0786 QTextLine KoTextLayoutArea::Private::recreatePartialLayout(QTextBlock &block, const QVector<LineKeeper> &stashedLines, QPointF &stashedCounterPosition, QTextLine &line)
0787 {
0788     QTextLayout *layout = block.layout();
0789     KoTextBlockData blockData(block);
0790     documentLayout->allowPositionInlineObject(false);
0791     if (layout->lineCount() == 1) {
0792         blockData.setCounterPosition(stashedCounterPosition);
0793     }
0794     foreach(const LineKeeper &lk, stashedLines) {
0795         line.setLineWidth(lk.lineWidth);
0796         if (lk.columns != line.textLength()) {
0797             // As setNumColumns might break differently we only use it if setLineWidth doesn't give
0798             // the same textLength as we had before
0799             line.setNumColumns(lk.columns, lk.lineWidth);
0800         }
0801         line.setPosition(lk.position);
0802 
0803         line = layout->createLine();
0804         if (!line.isValid())
0805             break;
0806     }
0807     documentLayout->allowPositionInlineObject(true);
0808     return line;
0809 }
0810 
0811 static bool compareTab(const QTextOption::Tab &tab1, const QTextOption::Tab &tab2)
0812 {
0813     return tab1.position < tab2.position;
0814 }
0815 
0816 // layoutBlock() method is structured like this:
0817 //
0818 // 1) Setup various helper values
0819 //   a) related to or influenced by lists
0820 //   b) related to or influenced by dropcaps
0821 //   c) related to or influenced by margins
0822 //   d) related to or influenced by tabs
0823 //   e) related to or influenced by borders
0824 //   f) related to or influenced by list counters
0825 // 2)layout each line (possibly restarting where we stopped earlier)
0826 //   a) fit line into sub lines with as needed for text runaround
0827 //   b) break if we encounter softbreak
0828 //   c) make sure we keep above maximumAllowedBottom
0829 //   d) calls addLine()
0830 //   e) update dropcaps related variables
0831 bool KoTextLayoutArea::layoutBlock(FrameIterator *cursor)
0832 {
0833     QTextBlock block(cursor->it.currentBlock());
0834     KoTextBlockData blockData(block);
0835     KoParagraphStyle pStyle(block.blockFormat(), block.charFormat());
0836     int dropCapsAffectsNMoreLines = 0;
0837     qreal dropCapsPositionAdjust = 0.0;
0838     bool lastOfPreviousRun = (d->copyEndOfArea && d->copyEndOfArea->it.currentBlock() == block);
0839 
0840     KoText::Direction dir = pStyle.textProgressionDirection();
0841     if (dir == KoText::InheritDirection)
0842         dir = parentTextDirection();
0843     if (dir == KoText::AutoDirection)
0844         d->isRtl = block.text().isRightToLeft();
0845     else
0846         d->isRtl =  dir == KoText::RightLeftTopBottom;
0847 
0848     // initialize list item stuff for this parag.
0849     QTextList *textList = block.textList();
0850     QTextListFormat listFormat;
0851     QTextCharFormat labelFormat;
0852     if (textList) {
0853         listFormat = textList->format();
0854 
0855         if (block.text().size() == 0 || d->documentLayout->wordprocessingMode()) {
0856             labelFormat = block.charFormat();
0857         } else {
0858             labelFormat = block.begin().fragment().charFormat();
0859         }
0860 
0861         if (d->documentLayout->styleManager()) {
0862             const int id = listFormat.intProperty(KoListStyle::CharacterStyleId);
0863             KoCharacterStyle *cs = d->documentLayout->styleManager()->characterStyle(id);
0864             if (cs) {
0865                 cs->applyStyle(labelFormat);
0866                 cs->ensureMinimalProperties(labelFormat);
0867             }
0868         }
0869 
0870         // fetch the text-properties of the label
0871         if (listFormat.hasProperty(KoListStyle::CharacterProperties)) {
0872             QVariant v = listFormat.property(KoListStyle::CharacterProperties);
0873             QSharedPointer<KoCharacterStyle> textPropertiesCharStyle = v.value< QSharedPointer<KoCharacterStyle> >();
0874             if (!textPropertiesCharStyle.isNull()) {
0875                 textPropertiesCharStyle->applyStyle(labelFormat);
0876                 textPropertiesCharStyle->ensureMinimalProperties(labelFormat);
0877             }
0878         }
0879 
0880         // Calculate the correct font point size taking into account the current
0881         // block format and the relative font size percent if the size is not absolute
0882         if (listFormat.hasProperty(KoListStyle::RelativeBulletSize)) {
0883             qreal percent = listFormat.property(KoListStyle::RelativeBulletSize).toDouble();
0884             labelFormat.setFontPointSize((percent*labelFormat.fontPointSize())/100.00);
0885         }
0886 
0887         QFont font(labelFormat.font(), d->documentLayout->paintDevice());
0888 
0889         if (!blockData.hasCounterData()) {
0890             ListItemsHelper lih(textList, font);
0891             lih.recalculateBlock(block);
0892         }
0893         blockData.setLabelFormat(labelFormat);
0894     } else { // make sure it is empty
0895         blockData.clearCounter();
0896     }
0897 
0898     QTextLayout *layout = block.layout();
0899     QTextOption option = layout->textOption();
0900     option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
0901 
0902     option.setAlignment(QStyle::visualAlignment(d->isRtl ? Qt::RightToLeft : Qt::LeftToRight, pStyle.alignment()));
0903     if (d->isRtl) {
0904         option.setTextDirection(Qt::RightToLeft);
0905         // For right-to-left we need to make sure that trailing spaces are included into the QTextLine naturalTextWidth
0906         // and naturalTextRect calculation so they are proper handled in the RunAroundHelper. For left-to-right we do
0907         // not like to include trailing spaces in the calculations cause else justified text would not look proper
0908         // justified. Seems for right-to-left we have to accept that justified text will not look proper justified then.
0909         // only set it for justified text as otherwise we will cut of text at the beginning of the line
0910         if (pStyle.alignment() == Qt::AlignJustify) {
0911             option.setFlags(QTextOption::IncludeTrailingSpaces);
0912         }
0913 
0914     } else {
0915         option.setFlags(0);
0916         option.setTextDirection(Qt::LeftToRight);
0917     }
0918 
0919     option.setUseDesignMetrics(true);
0920 
0921     //==========
0922     // Drop caps
0923     //==========
0924 
0925     d->dropCapsNChars = 0;
0926     if (cursor->lineTextStart == -1) {
0927         // first remove any drop-caps related formatting that's already there in the layout.
0928         // we'll do it all afresh now.
0929         QList<QTextLayout::FormatRange> formatRanges = layout->additionalFormats();
0930         for (QList< QTextLayout::FormatRange >::Iterator iter = formatRanges.begin();
0931                 iter != formatRanges.end(); ) {
0932             if (iter->format.boolProperty(DropCapsAdditionalFormattingId)) {
0933                 iter = formatRanges.erase(iter);
0934             } else {
0935                 ++iter;
0936             }
0937         }
0938         if (formatRanges.count() != layout->additionalFormats().count())
0939             layout->setAdditionalFormats(formatRanges);
0940         bool dropCaps = pStyle.dropCaps();
0941         int dropCapsLength = pStyle.dropCapsLength();
0942         int dropCapsLines = pStyle.dropCapsLines();
0943 
0944         if (dropCaps && dropCapsLines > 1 && block.length() > 1) {
0945             QString blockText = block.text();
0946             d->dropCapsDistance = pStyle.dropCapsDistance();
0947 
0948             if (dropCapsLength == 0) { // means whole word is to be dropped
0949                 int firstNonSpace = blockText.indexOf(QRegExp("[^ ]"));
0950                 dropCapsLength = blockText.indexOf(QRegExp("\\W"), firstNonSpace);
0951             } else {
0952                 // LibreOffice skips softbreaks but not spaces. We will do the same
0953                 QTextCursor c1(block);
0954                 c1.setPosition(block.position());
0955                 c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor);
0956 
0957                 KoTextSoftPageBreak *softPageBreak = dynamic_cast<KoTextSoftPageBreak*>(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1));
0958                 if (softPageBreak) {
0959                     dropCapsLength++;
0960                 }
0961             }
0962             dropCapsLength = qMin(dropCapsLength, blockText.length() - 1);
0963 
0964             if (dropCapsLength > 0) {
0965                 // increase the size of the dropped chars
0966                 QTextCursor blockStart(block);
0967                 QTextLayout::FormatRange dropCapsFormatRange;
0968                 dropCapsFormatRange.format = blockStart.charFormat();
0969 
0970                 // find out lineHeight for this block.
0971                 QTextBlock::iterator it = block.begin();
0972                 QTextFragment lineRepresentative = it.fragment();
0973                 qreal lineHeight = pStyle.lineHeightAbsolute();
0974                 qreal dropCapsHeight = 0;
0975                 if (lineHeight == 0) {
0976                     lineHeight = lineRepresentative.charFormat().fontPointSize();
0977                     qreal linespacing = pStyle.lineSpacing();
0978                     if (linespacing == 0) { // unset
0979                         qreal percent = pStyle.lineHeightPercent();
0980                         if (percent != 0)
0981                             linespacing = lineHeight * ((percent - 100) / 100.0);
0982                         else if (linespacing == 0)
0983                             linespacing = lineHeight * 0.2; // default
0984                     }
0985                     dropCapsHeight = linespacing * (dropCapsLines-1);
0986                 }
0987                 const qreal minimum = pStyle.minimumLineHeight();
0988                 if (minimum > 0.0) {
0989                     lineHeight = qMax(lineHeight, minimum);
0990                 }
0991 
0992                 dropCapsHeight += lineHeight * dropCapsLines;
0993 
0994                 int dropCapsStyleId = pStyle.dropCapsTextStyleId();
0995                 KoCharacterStyle *dropCapsCharStyle = 0;
0996                 if (dropCapsStyleId > 0 && d->documentLayout->styleManager()) {
0997                     dropCapsCharStyle = d->documentLayout->styleManager()->characterStyle(dropCapsStyleId);
0998                     dropCapsCharStyle->applyStyle(dropCapsFormatRange.format);
0999                 }
1000 
1001                 QFont f(dropCapsFormatRange.format.font(), d->documentLayout->paintDevice());
1002                 QString dropCapsText(block.text().left(dropCapsLength));
1003                 f.setPointSizeF(dropCapsHeight);
1004                 for (int i=0; i < 5; ++i) {
1005                     QTextLayout tmplayout(dropCapsText, f);
1006                     tmplayout.setTextOption(option);
1007                     tmplayout.beginLayout();
1008                     QTextLine tmpline = tmplayout.createLine();
1009                     tmplayout.endLayout();
1010                     d->dropCapsWidth = tmpline.naturalTextWidth();
1011 
1012                     QFontMetricsF fm(f, documentLayout()->paintDevice());
1013                     QRectF rect = fm.tightBoundingRect(dropCapsText);
1014                     const qreal diff = dropCapsHeight - rect.height();
1015                     dropCapsPositionAdjust = rect.top() + fm.ascent();
1016                     if (qAbs(diff) < 0.5) // good enough
1017                         break;
1018 
1019                     const qreal adjustment = diff * (f.pointSizeF() / rect.height());
1020                     // warnTextLayout << "adjusting with" << adjustment;
1021                     f.setPointSizeF(f.pointSizeF() + adjustment);
1022                 }
1023 
1024                 dropCapsFormatRange.format.setFontPointSize(f.pointSizeF());
1025                 dropCapsFormatRange.format.setProperty(DropCapsAdditionalFormattingId,
1026                         (QVariant) true);
1027                 dropCapsFormatRange.start = 0;
1028                 dropCapsFormatRange.length = dropCapsLength;
1029                 formatRanges.append(dropCapsFormatRange);
1030                 layout->setAdditionalFormats(formatRanges);
1031 
1032                 d->dropCapsNChars = dropCapsLength;
1033                 dropCapsAffectsNMoreLines = (d->dropCapsNChars > 0) ? dropCapsLines : 0;
1034             }
1035         }
1036     }
1037 
1038     //========
1039     // Margins
1040     //========
1041     qreal startMargin = block.blockFormat().leftMargin();
1042     qreal endMargin = block.blockFormat().rightMargin();
1043     if (d->isRtl) {
1044         qSwap(startMargin, endMargin);
1045     }
1046     d->indent = textIndent(block, textList, pStyle) + d->extraTextIndent;
1047 
1048     qreal labelBoxWidth = 0;
1049     qreal labelBoxIndent = 0;
1050     if (textList) {
1051         if (listFormat.boolProperty(KoListStyle::AlignmentMode)) {
1052             // according to odf 1.2 17.20 list margin should be used when paragraph margin is
1053             // not specified by the auto style (additionally LO/OO uses 0 as condition so we do too)
1054             int id = pStyle.styleId();
1055             bool set = false;
1056             if (id && d->documentLayout->styleManager()) {
1057                 KoParagraphStyle *originalParagraphStyle = d->documentLayout->styleManager()->paragraphStyle(id);
1058                 if (originalParagraphStyle->leftMargin() != startMargin) {
1059                     set = (startMargin != 0);
1060                 }
1061             } else {
1062                 set = (startMargin != 0);
1063             }
1064             if (! set) {
1065                 startMargin = listFormat.doubleProperty(KoListStyle::Margin);
1066             }
1067 
1068             labelBoxWidth = blockData.counterWidth();
1069             Qt::Alignment align = static_cast<Qt::Alignment>(listFormat.intProperty(KoListStyle::Alignment));
1070             if (align == 0) {
1071                 align = Qt::AlignLeft;
1072             }
1073             if (align & Qt::AlignLeft) {
1074                 d->indent += labelBoxWidth;
1075             } else if (align & Qt::AlignHCenter) {
1076                 d->indent += labelBoxWidth/2;
1077             }
1078             labelBoxIndent = d->indent - labelBoxWidth;
1079         } else {
1080             labelBoxWidth = blockData.counterSpacing() + blockData.counterWidth();
1081         }
1082     }
1083 
1084     d->width = right() - left();
1085     d->width -= startMargin + endMargin;
1086     d->x = left() + (d->isRtl ? 0.0 : startMargin);
1087 
1088     d->documentLayout->clearInlineObjectRegistry(block);
1089 
1090     //========
1091     // Tabs
1092     //========
1093     const QVector<KoText::Tab> tabs = pStyle.tabPositions();
1094 
1095     // Handle tabs relative to startMargin
1096     qreal tabOffset = -d->indent;
1097     if (!d->documentLayout->relativeTabs(block)) {
1098         tabOffset -= startMargin;
1099     }
1100 
1101     // Make a list of tabs that Qt can use
1102     QList<QTextOption::Tab> qTabs;
1103     // Note: Converting to Qt tabs is needed as long as we use Qt for layout, but we
1104     // loose the possibility to do leader chars.
1105     foreach (const KoText::Tab &kTab, tabs) {
1106         qreal value = kTab.position;
1107         if (value == MaximumTabPos) { // MaximumTabPos is used in index generators
1108             // note: we subtract right margin as this is where the tab should be
1109             // note: we subtract indent so tab is not relative to it
1110             // note: we subtract left margin so tab is not relative to it
1111             // if rtl the above left/right reasons swap but formula stays the same
1112             // -tabOfset is just to cancel that we add it next
1113             // -2 is to avoid wrap at right edge to the next line
1114             value = right() - left() - startMargin - endMargin - d->indent - tabOffset - 2;
1115         }
1116 
1117         // conversion here is required because Qt thinks in device units and we don't
1118         value *= qt_defaultDpiY() / 72.0;
1119 
1120         value += tabOffset * qt_defaultDpiY() / 72.0;
1121 
1122         QTextOption::Tab tab;
1123         tab.position = value;
1124         tab.type = kTab.type;
1125         tab.delimiter = kTab.delimiter;
1126         qTabs.append(tab);
1127     }
1128 
1129     qreal presentationListTabValue(0.0); // for use in presentationListTabWorkaround
1130 
1131     // For some lists we need to add a special list tab according to odf 1.2 19.830
1132     if (textList && listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::ListTab) {
1133         qreal listTab = 0;
1134         if (listFormat.hasProperty(KoListStyle::TabStopPosition)) {
1135             listTab = listFormat.doubleProperty(KoListStyle::TabStopPosition);
1136             if (!d->documentLayout->relativeTabs(block)) {
1137                 // How list tab is defined if fixed tabs:
1138                 //        listTab
1139                 //|>-------------------------|
1140                 //           d->indent
1141                 //         |---------<|
1142                 //     LABEL                 TEXT STARTS HERE AND GOES ON
1143                 //                    TO THE NEXT LINE
1144                 //|>------------------|
1145                 //     startMargin
1146                 listTab -= startMargin;
1147             } else {
1148                 // How list tab is defined if relative tabs:
1149                 // It's relative to startMargin - list.startMargin
1150                 //              listTab
1151                 //       |>-------------------|
1152                 //             d->indent
1153                 //           |---------<|
1154                 //       LABEL                 TEXT STARTS HERE AND GOES ON
1155                 //                      TO THE NEXT LINE
1156                 //|>--------------------|
1157                 //     startMargin       |
1158                 //       |>-------------|
1159                 //          list.margin
1160                 listTab -= listFormat.doubleProperty(KoListStyle::Margin);
1161             }
1162         }
1163         // How list tab is defined now:
1164         //                    listTab
1165         //                    |>-----|
1166         //           d->indent
1167         //         |---------<|
1168         //     LABEL                 TEXT STARTS HERE AND GOES ON
1169         //                    TO THE NEXT LINE
1170         //|>------------------|
1171         //     startMargin
1172         presentationListTabValue = listTab;
1173         listTab -= d->indent;
1174 
1175         // And now listTab is like this:
1176         //         x()
1177         //         |     listTab
1178         //         |>---------------|
1179         //           d->indent
1180         //         |---------<|
1181         //     LABEL                 TEXT STARTS HERE AND GOES ON
1182         //                    TO THE NEXT LINE
1183         //|>------------------|
1184         //     startMargin
1185 
1186         // conversion here is required because Qt thinks in device units and we don't
1187         listTab *= qt_defaultDpiY() / 72.0;
1188 
1189         QTextOption::Tab tab;
1190         tab.position = listTab;
1191         tab.type = d->isRtl ? QTextOption::RightTab : QTextOption::LeftTab;
1192         qTabs.append(tab);
1193     }
1194 
1195     // We need to sort as the MaximumTabPos may be converted to a value that really
1196     // should be in the middle, and listtab needs to be sorted in too
1197     std::sort(qTabs.begin(), qTabs.end(), compareTab);
1198 
1199     // Regular interval tabs. Since Qt doesn't handle regular interval tabs offset
1200     // by a fixed number we need to create the regular tabs ourselves.
1201     qreal tabStopDistance = pStyle.tabStopDistance() * qt_defaultDpiY() / 72.0;
1202     if (tabStopDistance <= 0) {
1203         tabStopDistance = d->documentLayout->defaultTabSpacing() * qt_defaultDpiY() / 72.0;
1204     }
1205 
1206     qreal regularSpacedTabPos = -d->indent * qt_defaultDpiY() / 72.0 -0.1; // first possible position
1207     if (!qTabs.isEmpty()) {
1208         regularSpacedTabPos = qTabs.last().position;
1209     }
1210 
1211     regularSpacedTabPos -= tabOffset * qt_defaultDpiY() / 72.0;
1212     if (regularSpacedTabPos < 0) {
1213         regularSpacedTabPos = -int(-regularSpacedTabPos / tabStopDistance) * tabStopDistance;
1214     } else {
1215         regularSpacedTabPos = (int(regularSpacedTabPos / tabStopDistance) + 1) * tabStopDistance;
1216     }
1217     regularSpacedTabPos += tabOffset * qt_defaultDpiY() / 72.0;
1218 
1219     while (regularSpacedTabPos < MaximumTabPos) {
1220         QTextOption::Tab tab;
1221         tab.position = regularSpacedTabPos;
1222         qTabs.append(tab);
1223         regularSpacedTabPos += tabStopDistance;
1224     }
1225 
1226     option.setTabs(qTabs);
1227 
1228     // conversion here is required because Qt thinks in device units and we don't
1229     option.setTabStop(tabStopDistance * qt_defaultDpiY() / 72.);
1230 
1231     layout->setTextOption(option);
1232 
1233 
1234     // ==============
1235     // Possibly store the old layout of lines in case we end up splitting the paragraph at the same position
1236     // ==============
1237     QVector<LineKeeper> stashedLines;
1238     QPointF stashedCounterPosition;
1239     if (lastOfPreviousRun) {
1240         // we have been layouted before, and the block ended on the following page so better
1241         // stash the layout for later
1242         d->stashRemainingLayout(block, d->copyEndOfArea->lineTextStart, stashedLines, stashedCounterPosition);
1243     }
1244 
1245     // ==============
1246     // Setup line and possibly restart paragraph continuing from previous other area
1247     // ==============
1248     QTextLine line;
1249     if (cursor->lineTextStart == -1) {
1250         layout->beginLayout();
1251         line = layout->createLine();
1252         cursor->fragmentIterator = block.begin();
1253     } else {
1254         line = d->restartLayout(block, cursor->lineTextStart);
1255         d->indent = d->extraTextIndent;
1256     }
1257 
1258     if (block.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) {
1259         // Unnumbered list items act like "following lines" in a numbered block
1260         d->indent = 0;
1261     }
1262 
1263     // ==============
1264     // List label/counter positioning
1265     // ==============
1266     if (textList && block.layout()->lineCount() == 1
1267         && ! block.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) {
1268         // If first line in a list then set the counterposition. Following lines in the same
1269         // list-item have nothing to do with the counter.
1270         if (listFormat.boolProperty(KoListStyle::AlignmentMode) == false) {
1271             qreal minLabelWidth = listFormat.doubleProperty(KoListStyle::MinimumWidth);
1272             if (!d->isRtl) {
1273                 d->x += listFormat.doubleProperty(KoListStyle::Indent) + minLabelWidth;
1274             }
1275             d->width -= listFormat.doubleProperty(KoListStyle::Indent) + minLabelWidth;
1276             d->indent +=  labelBoxWidth - minLabelWidth;
1277             blockData.setCounterPosition(QPointF(d->x + d->indent - labelBoxWidth, d->y));
1278         } else if (labelBoxWidth > 0.0 || blockData.counterText().length() > 0) {
1279             // Alignmentmode and there is a label (double check needed to account for both
1280             // picture bullets and non width chars)
1281             blockData.setCounterPosition(QPointF(d->x + labelBoxIndent, d->y));
1282             if (listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::ListTab
1283                 && !presentationListTabWorkaround(textIndent(block, textList, pStyle), labelBoxWidth, presentationListTabValue)) {
1284                 foreach(QTextOption::Tab tab, qTabs) {
1285                     qreal position = tab.position  * 72. / qt_defaultDpiY();
1286                     if (position > 0.0) {
1287                         d->indent += position;
1288                         break;
1289                     }
1290                 }
1291 
1292                 //And finally it's like this:
1293                 //                          x()
1294                 //                    d->indent
1295                 //                    |>-----|
1296                 //     LABEL                 TEXT STARTS HERE AND GOES ON
1297                 //                    TO THE NEXT LINE
1298                 //|>------------------|
1299                 //     startMargin
1300             } else if (listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::Space) {
1301                  QFontMetrics fm(labelFormat.font(), d->documentLayout->paintDevice());
1302                  d->indent += fm.width(' ');
1303             }
1304             // default needs to be no space so presentationListTabWorkaround above makes us go here
1305         }
1306     }
1307 
1308     // Whenever we relayout the markup layout becomes invalid
1309     blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, false);
1310     blockData.setMarkupsLayoutValidity(KoTextBlockData::Grammar, false);
1311 
1312     // ==============
1313     // Now once we know the physical context we can work on the borders of the paragraph
1314     // ==============
1315     if (block.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) {
1316         if (!d->blockRects.isEmpty()) {
1317             d->blockRects.last().setBottom(d->y);
1318         }
1319         d->y += d->bottomSpacing;
1320         d->bottomSpacing = 0;
1321         d->blockRects.append(QRectF(d->x, d->y, d->width, 10.0));
1322     } else {
1323         handleBordersAndSpacing(blockData, &block);
1324     }
1325 
1326     // Expand bounding rect so if we have content outside we show it
1327     expandBoundingLeft(d->blockRects.last().x());
1328     expandBoundingRight(d->blockRects.last().right());
1329 
1330     // ==============
1331     // Create the lines of this paragraph
1332     // ==============
1333     RunAroundHelper runAroundHelper;
1334     runAroundHelper.setObstructions(documentLayout()->currentObstructions());
1335     qreal maxLineHeight = 0;
1336     qreal y_justBelowDropCaps = 0;
1337     bool anyLineAdded = false;
1338     int numBaselineShifts = 0;
1339 
1340     while (line.isValid()) {
1341         runAroundHelper.setLine(this, line);
1342         runAroundHelper.setObstructions(documentLayout()->currentObstructions());
1343         QRectF anchoringRect = d->blockRects.last();
1344         anchoringRect.setTop(d->anchoringParagraphContentTop);
1345         documentLayout()->setAnchoringParagraphContentRect(anchoringRect);
1346         anchoringRect.setLeft(left());
1347         anchoringRect.setWidth(right() - left());
1348         anchoringRect.setTop(d->anchoringParagraphTop);
1349         documentLayout()->setAnchoringParagraphRect(anchoringRect);
1350         documentLayout()->setAnchoringLayoutEnvironmentRect(layoutEnvironmentRect());
1351         runAroundHelper.fit( /* resetHorizontalPosition */ false, /* rightToLeft */ d->isRtl, QPointF(x(), d->y));
1352 
1353         documentLayout()->positionAnchorTextRanges(block.position()+line.textStart(), line.textLength(), block.document());
1354         qreal bottomOfText = line.y() + line.height();
1355 
1356         bool softBreak = false;
1357         bool moreInMiddle = d->y > maximumAllowedBottom() - 150;
1358         if (acceptsPageBreak() && !pStyle.nonBreakableLines() && moreInMiddle) {
1359             int softBreakPos = -1;
1360             QString text = block.text();
1361             int pos = text.indexOf(QChar::ObjectReplacementCharacter, line.textStart());
1362 
1363             while (pos >= 0 && pos <= line.textStart() + line.textLength()) {
1364                 QTextCursor c1(block);
1365                 c1.setPosition(block.position() + pos);
1366                 c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor);
1367 
1368                 KoTextSoftPageBreak *softPageBreak = dynamic_cast<KoTextSoftPageBreak*>(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1));
1369                 if (softPageBreak) {
1370                     softBreakPos = pos;
1371                     break;
1372                 }
1373 
1374                 pos = text.indexOf(QChar::ObjectReplacementCharacter, pos + 1);
1375             }
1376 
1377             if (softBreakPos >= 0 && softBreakPos < line.textStart() + line.textLength()) {
1378                 line.setNumColumns(softBreakPos - line.textStart() + 1, line.width());
1379                 softBreak = true;
1380                 // if the softBreakPos is at the start of the line stop here so
1381                 // we don't add a line here. That fixes the problem that e.g. the counter is before
1382                 // the page break and the text is after the page break
1383                 if (!virginPage() && softBreakPos == 0) {
1384                     d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1385                     layout->endLayout();
1386                     return false;
1387                 }
1388             }
1389         }
1390 
1391         if (documentLayout()->anchoringSoftBreak() <= block.position() + line.textStart() + line.textLength()) {
1392             //don't add an anchor that has been moved away
1393             line.setNumColumns(documentLayout()->anchoringSoftBreak() - block.position() - line.textStart(), line.width());
1394             softBreak = true;
1395             // if the softBreakPos is at the start of the block stop here so
1396             // we don't add a line here. That fixes the problem that e.g. the counter is before
1397             // the page break and the text is after the page break
1398             if (!virginPage() && documentLayout()->anchoringSoftBreak() == block.position()) {
1399                 d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1400                 layout->endLayout();
1401                 return false;
1402             }
1403         }
1404 
1405         findFootNotes(block, line, bottomOfText);
1406         if (bottomOfText > maximumAllowedBottom()) {
1407             // We can not fit line within our allowed space
1408             // in case we resume layout on next page the line is reused later
1409             // but if not then we need to make sure the line becomes invisible
1410             // we use d->maximalAllowedBottom because we want to be below
1411             // footnotes too.
1412             if (!virginPage() && pStyle.nonBreakableLines()) {
1413                 line.setPosition(QPointF(x(), d->maximalAllowedBottom));
1414                 cursor->lineTextStart = -1;
1415                 d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1416                 layout->endLayout();
1417                 clearPreregisteredFootNotes();
1418                 return false; //to indicate block was not done!
1419             }
1420             if (!virginPage() && pStyle.orphanThreshold() != 0
1421                               && pStyle.orphanThreshold() > numBaselineShifts) {
1422                 line.setPosition(QPointF(x(), d->maximalAllowedBottom));
1423                 cursor->lineTextStart = -1;
1424                 d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1425                 layout->endLayout();
1426                 clearPreregisteredFootNotes();
1427                 return false; //to indicate block was not done!
1428             }
1429             if (!virginPage() || anyLineAdded) {
1430                 line.setPosition(QPointF(x(), d->maximalAllowedBottom));
1431                 d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1432                 layout->endLayout();
1433                 clearPreregisteredFootNotes();
1434                 return false; //to indicate block was not done!
1435             }
1436         }
1437         confirmFootNotes();
1438         anyLineAdded = true;
1439         maxLineHeight = qMax(maxLineHeight, addLine(line, cursor, blockData));
1440 
1441         d->neededWidth = qMax(d->neededWidth, line.naturalTextWidth() + d->indent);
1442 
1443         if (!runAroundHelper.stayOnBaseline() && !(block.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)
1444          && block.length() <= 1)) {
1445             d->y += maxLineHeight;
1446             maxLineHeight = 0;
1447             d->indent = 0;
1448             d->extraTextIndent = 0;
1449             ++numBaselineShifts;
1450         }
1451 
1452         // drop caps
1453         if (d->dropCapsNChars > 0) { // we just laid out the dropped chars
1454             y_justBelowDropCaps = d->y; // save the y position just below the dropped characters
1455             d->y = line.y();              // keep the same y for the next line
1456             line.setPosition(line.position() - QPointF(0, dropCapsPositionAdjust));
1457             d->dropCapsNChars -= line.textLength();
1458         } else if (dropCapsAffectsNMoreLines > 0) { // we just laid out a drop-cap-affected line
1459             dropCapsAffectsNMoreLines--;
1460             if (dropCapsAffectsNMoreLines == 0) {   // no more drop-cap-affected lines
1461                 if (d->y < y_justBelowDropCaps)
1462                     d->y = y_justBelowDropCaps; // make sure d->y is below the dropped characters
1463                 y_justBelowDropCaps = 0;
1464                 d->dropCapsWidth = 0;
1465                 d->dropCapsDistance = 0;
1466             }
1467         }
1468         documentLayout()->positionAnchoredObstructions();
1469 
1470         // line fitted so try and do the next one
1471         line = layout->createLine();
1472         if (!line.isValid()) {
1473             break; // no more line means our job is done
1474         }
1475         cursor->lineTextStart = line.textStart();
1476 
1477         if (softBreak) {
1478             d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line);
1479             layout->endLayout();
1480             return false; // page-break means we need to start again on the next page
1481         }
1482     }
1483 
1484     d->bottomSpacing = pStyle.bottomMargin();
1485 
1486     layout->endLayout();
1487     setVirginPage(false);
1488     cursor->lineTextStart = -1; //set lineTextStart to -1 and returning true indicate new block
1489     block.setLineCount(layout->lineCount());
1490     return true;
1491 }
1492 
1493 bool KoTextLayoutArea::presentationListTabWorkaround(qreal indent, qreal labelBoxWidth, qreal presentationListTabValue)
1494 {
1495     if (!d->documentLayout->wordprocessingMode() && indent < 0.0) {
1496         // Impress / Powerpoint expects the label to be before the text
1497         if (indent + labelBoxWidth >= presentationListTabValue) {
1498             // but here is an unforseen overlap with normal text
1499             return true;
1500         }
1501     }
1502     return false;
1503 }
1504 
1505 qreal KoTextLayoutArea::textIndent(const QTextBlock &block, QTextList *textList, const KoParagraphStyle &pStyle) const
1506 {
1507     if (pStyle.autoTextIndent()) {
1508         // if auto-text-indent is set,
1509         // return an indent approximately 3-characters wide as per current font
1510         QTextCursor blockCursor(block);
1511         qreal guessGlyphWidth = QFontMetricsF(blockCursor.charFormat().font()).width('x');
1512         return guessGlyphWidth * 3;
1513     }
1514 
1515     qreal blockTextIndent = block.blockFormat().textIndent();
1516 
1517     if (textList && textList->format().boolProperty(KoListStyle::AlignmentMode)) {
1518         // according to odf 1.2 17.20 list text indent should be used when paragraph text indent is
1519         // not specified (additionally LO/OO uses 0 as condition so we do too)
1520         int id = pStyle.styleId();
1521         bool set = false;
1522         if (id && d->documentLayout->styleManager()) {
1523             KoParagraphStyle *originalParagraphStyle = d->documentLayout->styleManager()->paragraphStyle(id);
1524             if (originalParagraphStyle->textIndent() != blockTextIndent) {
1525                 set = (blockTextIndent != 0);
1526             }
1527         } else {
1528             set = (blockTextIndent != 0);
1529         }
1530         if (! set) {
1531             return textList->format().doubleProperty(KoListStyle::TextIndent);
1532         }
1533     }
1534     return blockTextIndent;
1535 }
1536 
1537 void KoTextLayoutArea::setExtraTextIndent(qreal extraTextIndent)
1538 {
1539     d->extraTextIndent = extraTextIndent;
1540 }
1541 
1542 qreal KoTextLayoutArea::x() const
1543 {
1544     if (d->isRtl) {
1545         return d->x;
1546     } else {
1547         if (d->dropCapsNChars > 0 || d->dropCapsWidth == 0)
1548             return d->x + d->indent ;
1549         else
1550             return d->x + d->indent + d->dropCapsWidth + d->dropCapsDistance;
1551     }
1552 }
1553 
1554 qreal KoTextLayoutArea::width() const
1555 {
1556     if (d->dropCapsNChars > 0) {
1557         return d->dropCapsWidth;
1558     }
1559     qreal width = d->width;
1560     if (d->maximumAllowedWidth > 0) {
1561         // lets use that instead but remember all the indent stuff we have calculated
1562         width = d->width - (d->right - d->left) + d->maximumAllowedWidth;
1563     }
1564     return width - d->indent - d->dropCapsWidth - d->dropCapsDistance;
1565 }
1566 
1567 void KoTextLayoutArea::setAcceptsPageBreak(bool accept)
1568 {
1569     d->acceptsPageBreak = accept;
1570 }
1571 
1572 bool KoTextLayoutArea::acceptsPageBreak() const
1573 {
1574     return d->acceptsPageBreak;
1575 }
1576 
1577 void KoTextLayoutArea::setAcceptsColumnBreak(bool accept)
1578 {
1579     d->acceptsColumnBreak = accept;
1580 }
1581 
1582 bool KoTextLayoutArea::acceptsColumnBreak() const
1583 {
1584     return d->acceptsColumnBreak;
1585 }
1586 
1587 void KoTextLayoutArea::setVirginPage(bool virgin)
1588 {
1589     d->virginPage = virgin;
1590 }
1591 
1592 bool KoTextLayoutArea::virginPage() const
1593 {
1594     return d->virginPage;
1595 }
1596 
1597 void KoTextLayoutArea::setVerticalAlignOffset(qreal offset)
1598 {
1599     d->boundingRect.setTop(d->top + qMin(qreal(0.0), offset));
1600     d->boundingRect.setBottom(d->bottom + qMax(qreal(0.0), offset));
1601     Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom(), __FUNCTION__, "Bounding-rect is not normalized");
1602     d->verticalAlignOffset = offset;
1603 }
1604 
1605 qreal KoTextLayoutArea::verticalAlignOffset() const
1606 {
1607     return d->verticalAlignOffset;
1608 }
1609 
1610 qreal KoTextLayoutArea::addLine(QTextLine &line, FrameIterator *cursor, KoTextBlockData &blockData)
1611 {
1612     QTextBlock block = cursor->it.currentBlock();
1613     QTextBlockFormat format = block.blockFormat();
1614     KoParagraphStyle style(format, block.charFormat());
1615 
1616     if (block.textList() && block.layout()->lineCount() == 1) {
1617         Qt::Alignment alignment = format.alignment();
1618         if (d->isRtl && (alignment & Qt::AlignAbsolute) == 0) {
1619             if (alignment & Qt::AlignLeft) {
1620                 alignment = Qt::AlignRight;
1621             } else if (alignment & Qt::AlignRight) {
1622                 alignment = Qt::AlignLeft;
1623             }
1624         }
1625         alignment &= Qt::AlignRight | Qt::AlignLeft | Qt::AlignHCenter;
1626 
1627         // First line, lets check where the line ended up and adjust the positioning of the counter.
1628         qreal newX;
1629         if (alignment & Qt::AlignHCenter) {
1630             const qreal padding = (line.width() - line.naturalTextWidth()) / 2;
1631             newX = blockData.counterPosition().x() + (d->isRtl ? -padding : padding);
1632         } else if (alignment & Qt::AlignRight) {
1633             const qreal padding = line.width() - line.naturalTextWidth();
1634             newX = blockData.counterPosition().x() + (d->isRtl ? -padding : padding);
1635         } else {
1636             newX = blockData.counterPosition().x();
1637         }
1638         if (d->isRtl) {
1639             newX = line.x() + line.naturalTextWidth() + line.x() + d->indent - newX;
1640         }
1641 
1642         blockData.setCounterPosition(QPointF(newX, blockData.counterPosition().y()));
1643     }
1644 
1645     qreal height = 0;
1646     qreal breakHeight = 0.0;
1647     qreal ascent = 0.0;
1648     qreal descent = 0.0;
1649     const bool useFontProperties = format.boolProperty(KoParagraphStyle::LineSpacingFromFont);
1650 
1651     if (cursor->fragmentIterator.atEnd()) {// no text in parag.
1652         qreal fontStretch = 1;
1653         QTextCharFormat charFormat = block.charFormat();
1654         if (block.blockFormat().hasProperty(KoParagraphStyle::EndCharStyle)) {
1655             QVariant v = block.blockFormat().property(KoParagraphStyle::EndCharStyle);
1656             QSharedPointer<KoCharacterStyle> endCharStyle = v.value< QSharedPointer<KoCharacterStyle> >();
1657             if (!endCharStyle.isNull()) {
1658                 endCharStyle->applyStyle(charFormat);
1659                 endCharStyle->ensureMinimalProperties(charFormat);
1660             }
1661         }
1662 
1663         if (useFontProperties) {
1664             //stretch line height to powerpoint size
1665             fontStretch = PresenterFontStretch;
1666         } else if (block.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) {
1667             // stretch line height to ms-word size
1668             fontStretch = charFormat.property(KoCharacterStyle::FontYStretch).toDouble();
1669         }
1670         height = charFormat.fontPointSize() * fontStretch;
1671     } else {
1672         qreal fontStretch = 1;
1673         QTextFragment fragment = cursor->fragmentIterator.fragment();
1674         if (useFontProperties) {
1675             //stretch line height to powerpoint size
1676             fontStretch = PresenterFontStretch;
1677         } else if (fragment.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) {
1678             // stretch line height to ms-word size
1679             fontStretch = fragment.charFormat().property(KoCharacterStyle::FontYStretch).toDouble();
1680         }
1681         // read max font height
1682         height = qMax(height, fragment.charFormat().fontPointSize() * fontStretch);
1683 
1684         KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment);
1685         ascent = qMax(ascent, pos.m_ascent);
1686         descent = qMax(descent, pos.m_descent);
1687 
1688         bool lineBreak = false;
1689         int lastCharPos = block.position() + line.textStart() + line.textLength() - 1;
1690         int blockLastCharWithoutPreedit = line.textStart() + line.textLength() - 1;
1691         if (block.layout()->preeditAreaPosition() >= block.position() + line.textStart() &&
1692                 block.layout()->preeditAreaPosition() <= lastCharPos) {
1693             blockLastCharWithoutPreedit -= block.layout()->preeditAreaText().length();
1694         }
1695         if (block.text().at(blockLastCharWithoutPreedit) == QChar(0x2028)) {
1696             // Was a line with line-break
1697             if (line.textLength() != 1) { //unless empty line we should ignore the format of it
1698                 --lastCharPos;
1699             }
1700                 lineBreak = true;
1701         }
1702         while (!(fragment.contains(lastCharPos))) {
1703             cursor->fragmentIterator++;
1704             if (cursor->fragmentIterator.atEnd()) {
1705                 break;
1706             }
1707             fragment = cursor->fragmentIterator.fragment();
1708             if (!d->documentLayout->changeTracker()
1709                 || !d->documentLayout->changeTracker()->displayChanges()
1710                 || !d->documentLayout->changeTracker()->containsInlineChanges(fragment.charFormat())
1711                 || !d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt())
1712                 || !d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt())->isEnabled()
1713                 || (d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt())->getChangeType() != KoGenChange::DeleteChange)
1714                 || d->documentLayout->changeTracker()->displayChanges()) {
1715                 qreal fontStretch = 1;
1716                 if (useFontProperties) {
1717                     //stretch line height to powerpoint size
1718                     fontStretch = PresenterFontStretch;
1719                 } else if (fragment.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) {
1720                     // stretch line height to ms-word size
1721                     fontStretch = fragment.charFormat().property(KoCharacterStyle::FontYStretch).toDouble();
1722                 }
1723                 // read max font height
1724                 height = qMax(height, fragment.charFormat().fontPointSize() * fontStretch);
1725 
1726                 KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment);
1727                 ascent = qMax(ascent, pos.m_ascent);
1728                 descent = qMax(descent, pos.m_descent);
1729             }
1730         }
1731 
1732         if (lineBreak) {
1733             // Was a line with line-break - the format of the line-break should not be
1734             // considered for the next line either. So we may have to advance the fragmentIterator.
1735             while (!cursor->fragmentIterator.atEnd() && lastCharPos > fragment.position() + fragment.length()-1) {
1736                 cursor->fragmentIterator++;
1737                 fragment = cursor->fragmentIterator.fragment();
1738             }
1739 
1740             qreal breakAscent = ascent;
1741             qreal breakDescent = descent;
1742             breakHeight = height;
1743 
1744             int firstPos = block.position() + line.textStart() + line.textLength();
1745 
1746             // Was a line with line-break - the format of the line-break should not be
1747             // considered for the next line either. So we may have to advance the fragmentIterator.
1748             while (!cursor->fragmentIterator.atEnd() && firstPos > fragment.position() + fragment.length()-1) {
1749                 cursor->fragmentIterator++;
1750                 if (!cursor->fragmentIterator.atEnd()) {
1751                     fragment = cursor->fragmentIterator.fragment();
1752 
1753                     // read max font height
1754                     breakHeight = qMax(breakHeight, fragment.charFormat().fontPointSize() * fontStretch);
1755 
1756                     KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment);
1757                     breakAscent = qMax(breakAscent, pos.m_ascent);
1758                     breakDescent = qMax(breakDescent, pos.m_descent);
1759                 }
1760             }
1761             breakHeight = qMax(breakHeight, breakAscent + breakDescent);
1762         }
1763     }
1764 
1765     height = qMax(height, ascent + descent);
1766 
1767     if (height < 0.01) {
1768         height = 12; // default size for uninitialized styles.
1769     }
1770 
1771     // Calculate adjustment to the height due to line height calculated by qt which shouldn't be
1772     // there in reality. We will just move the line
1773     qreal lineAdjust = 0.0;
1774     if (breakHeight > height) {
1775         lineAdjust = height - breakHeight;
1776     }
1777 
1778     // Adjust the line-height according to a probably defined fixed line height,
1779     // a proportional (percent) line-height and/or the line-spacing. Together
1780     // with the line-height we maybe also need to adjust the position of the
1781     // line. This is for example needed if the line needs to shrink in height
1782     // so the line-text stays on the baseline. If the line grows in height then
1783     // we don't need to do anything.
1784     if (d->dropCapsNChars <= 0) { // linespacing rules doesn't apply to drop caps
1785         qreal fixedLineHeight = format.doubleProperty(KoParagraphStyle::FixedLineHeight);
1786         if (fixedLineHeight != 0.0) {
1787             qreal prevHeight = height;
1788             height = fixedLineHeight;
1789             lineAdjust += height - prevHeight;
1790         } else {
1791             qreal lineSpacing = format.doubleProperty(KoParagraphStyle::LineSpacing);
1792             if (lineSpacing == 0.0) { // unset
1793                 qreal percent = format.doubleProperty(KoParagraphStyle::PercentLineHeight);
1794                 if (percent != 0) {
1795                     height *= percent / 100.0;
1796                 }
1797                 height *= 1.16; // default
1798             }
1799             height += lineSpacing;
1800         }
1801 
1802         qreal minimum = style.minimumLineHeight();
1803         if (minimum > 0.0) {
1804             height = qMax(height, minimum);
1805         }
1806     } else {
1807         // for drop caps we just work with a basic linespacing for the dropped characters
1808         height *= 1.16;
1809     }
1810     //rounding problems due to Qt-scribe internally using ints.
1811     //also used when line was moved down because of intersections with other shapes
1812     if (qAbs(d->y - line.y()) >= 0.126) {
1813         d->y = line.y();
1814     }
1815 
1816     if (lineAdjust) {
1817         // Adjust the position of the line itself.
1818         line.setPosition(QPointF(line.x(), line.y() + lineAdjust));
1819 
1820         // Adjust the position of the block-rect for this line which is used later
1821         // to proper clip the line while drawing. If we would not adjust it here
1822         // then we could end with text-lines being partly cutoff.
1823         if (lineAdjust < 0.0) {
1824             d->blockRects.last().moveTop(d->blockRects.last().top() + lineAdjust);
1825         }
1826 
1827         if (block.textList() && block.layout()->lineCount() == 1) {
1828             // If this is the first line in a list (aka the first line after the list-
1829             // item) then we also need to adjust the counter to match to the line again.
1830             blockData.setCounterPosition(QPointF(blockData.counterPosition().x(), blockData.counterPosition().y() + lineAdjust));
1831         }
1832     }
1833 
1834     return height;
1835 }
1836 
1837 void KoTextLayoutArea::setLayoutEnvironmentResctictions(bool isLayoutEnvironment, bool actsHorizontally)
1838 {
1839     d->isLayoutEnvironment = isLayoutEnvironment;
1840     d->actsHorizontally = actsHorizontally;
1841 }
1842 
1843 QRectF KoTextLayoutArea::layoutEnvironmentRect() const
1844 {
1845     QRectF rect(-5e10, -5e10, 10e10, 10e20); // large values that never really restrict anything
1846 
1847     if (d->parent) {
1848         rect = d->parent->layoutEnvironmentRect();
1849     }
1850 
1851     if (d->isLayoutEnvironment) {
1852         if (d->actsHorizontally) {
1853             rect.setLeft(left());
1854             rect.setRight(right());
1855         }
1856         rect.setTop(top());
1857         rect.setBottom(maximumAllowedBottom());
1858     }
1859 
1860     return rect;
1861 }
1862 
1863 QRectF KoTextLayoutArea::boundingRect() const
1864 {
1865     return d->boundingRect;
1866 }
1867 
1868 qreal KoTextLayoutArea::maximumAllowedBottom() const
1869 {
1870     return d->maximalAllowedBottom - d->footNotesHeight
1871                     - d->preregisteredFootNotesHeight;
1872 }
1873 
1874 FrameIterator *KoTextLayoutArea::footNoteCursorToNext() const
1875 {
1876     return d->footNoteCursorToNext;
1877 }
1878 
1879 KoInlineNote *KoTextLayoutArea::continuedNoteToNext() const
1880 {
1881     return d->continuedNoteToNext;
1882 }
1883 
1884 int KoTextLayoutArea::footNoteAutoCount() const
1885 {
1886     return d->footNoteAutoCount;
1887 }
1888 
1889 void KoTextLayoutArea::setFootNoteCountInDoc(int count)
1890 {
1891     d->footNoteCountInDoc = count;
1892 }
1893 
1894 void KoTextLayoutArea::setFootNoteFromPrevious(FrameIterator *footNoteCursor, KoInlineNote *note)
1895 {
1896     d->footNoteCursorFromPrevious = footNoteCursor;
1897     d->continuedNoteFromPrevious = note;
1898 }
1899 
1900 void KoTextLayoutArea::setNoWrap(qreal maximumAllowedWidth)
1901 {
1902     d->maximumAllowedWidth = maximumAllowedWidth;
1903 }
1904 
1905 KoText::Direction KoTextLayoutArea::parentTextDirection() const
1906 {
1907     Q_ASSERT(d->parent); //Root areas should overload this method
1908     return d->parent->parentTextDirection();
1909 }
1910 
1911 KoTextLayoutArea *KoTextLayoutArea::parent() const
1912 {
1913     return d->parent;
1914 }
1915 
1916 KoTextDocumentLayout *KoTextLayoutArea::documentLayout() const
1917 {
1918     return d->documentLayout;
1919 }
1920 
1921 void KoTextLayoutArea::setReferenceRect(qreal left, qreal right, qreal top, qreal maximumAllowedBottom)
1922 {
1923     d->left = left;
1924     d->right = right;
1925     d->top = top;
1926     d->boundingRect = QRectF(left, top, right - left, 0.0);
1927     Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom() && d->boundingRect.left() <= d->boundingRect.right(), __FUNCTION__, "Bounding-rect is not normalized");
1928     d->maximalAllowedBottom = maximumAllowedBottom;
1929 }
1930 
1931 QRectF KoTextLayoutArea::referenceRect() const
1932 {
1933     return QRectF(d->left, d->top, d->right - d->left, d->bottom - d->top);
1934 }
1935 
1936 qreal KoTextLayoutArea::left() const
1937 {
1938     return d->left;
1939 }
1940 
1941 qreal KoTextLayoutArea::right() const
1942 {
1943     return d->right;
1944 }
1945 
1946 qreal KoTextLayoutArea::top() const
1947 {
1948     return d->top;
1949 }
1950 
1951 qreal KoTextLayoutArea::bottom() const
1952 {
1953     return d->bottom;
1954 }
1955 
1956 void KoTextLayoutArea::setBottom(qreal bottom)
1957 {
1958     d->boundingRect.setBottom(bottom + qMax(qreal(0.0), d->verticalAlignOffset));
1959     Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom(), __FUNCTION__, "Bounding-rect is not normalized");
1960     d->bottom = bottom;
1961 }
1962 
1963 void KoTextLayoutArea::findFootNotes(const QTextBlock &block, const QTextLine &line, qreal bottomOfText)
1964 {
1965     if (d->documentLayout->inlineTextObjectManager() == 0) {
1966         return;
1967     }
1968     QString text = block.text();
1969     int pos = text.indexOf(QChar::ObjectReplacementCharacter, line.textStart());
1970 
1971     while (pos >= 0 && pos <= line.textStart() + line.textLength()) {
1972         QTextCursor c1(block);
1973         c1.setPosition(block.position() + pos);
1974         c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor);
1975 
1976         KoInlineNote *note = dynamic_cast<KoInlineNote*>(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1));
1977         if (note && note->type() == KoInlineNote::Footnote) {
1978             preregisterFootNote(note, bottomOfText);
1979         }
1980 
1981         pos = text.indexOf(QChar::ObjectReplacementCharacter, pos + 1);
1982     }
1983 }
1984 
1985 qreal KoTextLayoutArea::preregisterFootNote(KoInlineNote *note, qreal bottomOfText)
1986 {
1987     if (d->parent == 0) {
1988         // TODO to support footnotes at end of document this is
1989         // where we need to add some extra condition
1990         if (note->autoNumbering()) {
1991             KoOdfNotesConfiguration *notesConfig = d->documentLayout->styleManager()->notesConfiguration(KoOdfNotesConfiguration::Footnote);
1992             if (notesConfig->numberingScheme() == KoOdfNotesConfiguration::BeginAtDocument) {
1993                 note->setAutoNumber(d->footNoteCountInDoc + (d->footNoteAutoCount++));
1994             } else if (notesConfig->numberingScheme() == KoOdfNotesConfiguration::BeginAtPage) {
1995                 note->setAutoNumber(d->footNoteAutoCount++);
1996             }
1997         }
1998 
1999         if (maximumAllowedBottom() - bottomOfText > 0) {
2000             QTextFrame *subFrame = note->textFrame();
2001             d->footNoteCursorToNext = new FrameIterator(subFrame);
2002             KoTextLayoutNoteArea *footNoteArea = new KoTextLayoutNoteArea(note, this, d->documentLayout);
2003 
2004             d->preregisteredFootNoteFrames.append(subFrame);
2005             footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom() - bottomOfText);
2006             bool contNotNeeded = footNoteArea->layout(d->footNoteCursorToNext);
2007             if (contNotNeeded) {
2008                 delete d->footNoteCursorToNext;
2009                 d->footNoteCursorToNext = 0;
2010                 d->continuedNoteToNext = 0;
2011             } else {
2012                 d->continuedNoteToNext = note;
2013                 //layout again now it has set up a continuationObstruction
2014                 delete d->footNoteCursorToNext;
2015                 d->footNoteCursorToNext = new FrameIterator(subFrame);
2016                 footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom() - bottomOfText);
2017                 footNoteArea->layout(d->footNoteCursorToNext);
2018                 documentLayout()->setContinuationObstruction(0); // remove it again
2019             }
2020             d->preregisteredFootNotesHeight += footNoteArea->bottom() - footNoteArea->top();
2021             d->preregisteredFootNoteAreas.append(footNoteArea);
2022             return footNoteArea->bottom() - footNoteArea->top();
2023         }
2024         return 0.0;
2025     }
2026     qreal h = d->parent->preregisterFootNote(note, bottomOfText);
2027     d->preregisteredFootNotesHeight += h;
2028     return h;
2029 }
2030 
2031 void KoTextLayoutArea::confirmFootNotes()
2032 {
2033     d->footNotesHeight += d->preregisteredFootNotesHeight;
2034     d->footNoteAreas.append(d->preregisteredFootNoteAreas);
2035     d->footNoteFrames.append(d->preregisteredFootNoteFrames);
2036     d->preregisteredFootNotesHeight = 0;
2037     d->preregisteredFootNoteAreas.clear();
2038     d->preregisteredFootNoteFrames.clear();
2039     if (d->parent) {
2040         d->parent->confirmFootNotes();
2041     }
2042 }
2043 
2044 void KoTextLayoutArea::expandBoundingLeft(qreal x)
2045 {
2046     d->boundingRect.setLeft(qMin(x, d->boundingRect.x()));
2047 }
2048 
2049 void KoTextLayoutArea::expandBoundingRight(qreal x)
2050 {
2051     d->boundingRect.setRight(qMax(x, d->boundingRect.right()));
2052 }
2053 
2054 void KoTextLayoutArea::clearPreregisteredFootNotes()
2055 {
2056     d->preregisteredFootNotesHeight = 0;
2057     d->preregisteredFootNoteAreas.clear();
2058     d->preregisteredFootNoteFrames.clear();
2059     if (d->parent) {
2060         d->parent->clearPreregisteredFootNotes();
2061     }
2062 }
2063 
2064 void KoTextLayoutArea::handleBordersAndSpacing(KoTextBlockData &blockData, QTextBlock *block)
2065 {
2066     QTextBlockFormat format = block->blockFormat();
2067     KoParagraphStyle formatStyle(format, block->charFormat());
2068 
2069     // The AddParaTableSpacingAtStart config-item is used to be able to optionally prevent that
2070     // defined fo:margin-top are applied to the first paragraph. If true then the fo:margin-top
2071     // is applied to all except the first paragraph. If false fo:margin-top is applied to all
2072     // paragraphs.
2073     bool paraTableSpacingAtStart = KoTextDocument(d->documentLayout->document()).paraTableSpacingAtStart();
2074     bool paddingExpandsBorders = false;//KoTextDocument(d->documentLayout->document()).paddingExpandsBorders();
2075 
2076     qreal topMargin = 0;
2077     if (paraTableSpacingAtStart || block->previous().isValid()) {
2078         topMargin = formatStyle.topMargin();
2079     }
2080     qreal spacing = qMax(d->bottomSpacing, topMargin);
2081     qreal dx = 0.0;
2082     qreal x = d->x;
2083     qreal width = d->width;
2084     if (d->indent < 0) {
2085         x += d->indent;
2086         width -= d->indent;
2087     }
2088     if (blockData.hasCounterData() && blockData.counterPosition().x() < x) {
2089        width += x - blockData.counterPosition().x();
2090        x = blockData.counterPosition().x();
2091     }
2092 
2093     KoTextBlockBorderData border(QRectF(x, d->y, width, 1));
2094     border.setEdge(border.Left, format, KoParagraphStyle::LeftBorderStyle,
2095                    KoParagraphStyle::LeftBorderWidth, KoParagraphStyle::LeftBorderColor,
2096                    KoParagraphStyle::LeftBorderSpacing, KoParagraphStyle::LeftInnerBorderWidth);
2097     border.setEdge(border.Right, format, KoParagraphStyle::RightBorderStyle,
2098                    KoParagraphStyle::RightBorderWidth, KoParagraphStyle::RightBorderColor,
2099                    KoParagraphStyle::RightBorderSpacing, KoParagraphStyle::RightInnerBorderWidth);
2100     border.setEdge(border.Top, format, KoParagraphStyle::TopBorderStyle,
2101                    KoParagraphStyle::TopBorderWidth, KoParagraphStyle::TopBorderColor,
2102                    KoParagraphStyle::TopBorderSpacing, KoParagraphStyle::TopInnerBorderWidth);
2103     border.setEdge(border.Bottom, format, KoParagraphStyle::BottomBorderStyle,
2104                    KoParagraphStyle::BottomBorderWidth, KoParagraphStyle::BottomBorderColor,
2105                    KoParagraphStyle::BottomBorderSpacing, KoParagraphStyle::BottomInnerBorderWidth);
2106     border.setMergeWithNext(formatStyle.joinBorder());
2107 
2108     if (border.hasBorders()) {
2109         // check if we can merge with the previous parags border.
2110         if (d->prevBorder && d->prevBorder->equals(border)) {
2111             blockData.setBorder(d->prevBorder);
2112             // Merged mean we don't have inserts inbetween the blocks
2113             d->anchoringParagraphTop = d->y;
2114             if (d->bottomSpacing + topMargin) {
2115                 d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin);
2116             }
2117             if (!d->blockRects.isEmpty()) {
2118                 d->blockRects.last().setBottom(d->anchoringParagraphTop);
2119             }
2120             d->anchoringParagraphTop = d->y;
2121             d->y += spacing;
2122             d->blockRects.append(QRectF(x, d->anchoringParagraphTop, width, 1.0));
2123         } else {
2124             // can't merge; then these are our new borders.
2125             KoTextBlockBorderData *newBorder = new KoTextBlockBorderData(border);
2126             blockData.setBorder(newBorder);
2127             if (d->prevBorder) {
2128                 d->y += d->prevBorderPadding;
2129                 d->y += d->prevBorder->inset(KoTextBlockBorderData::Bottom);
2130             }
2131             if (!d->blockRects.isEmpty()) {
2132                 d->blockRects.last().setBottom(d->y);
2133             }
2134             d->anchoringParagraphTop = d->y;
2135             if (d->bottomSpacing + topMargin) {
2136                 d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin);
2137             }
2138             d->y += spacing;
2139             if (paddingExpandsBorders) {
2140                 d->blockRects.append(QRectF(x - format.doubleProperty(KoParagraphStyle::LeftPadding), d->y,
2141                     width + format.doubleProperty(KoParagraphStyle::LeftPadding) + format.doubleProperty(KoParagraphStyle::RightPadding), 1.0));
2142             } else {
2143                 d->blockRects.append(QRectF(x, d->y, width, 1.0));
2144             }
2145             d->y += newBorder->inset(KoTextBlockBorderData::Top);
2146             d->y += format.doubleProperty(KoParagraphStyle::TopPadding);
2147         }
2148 
2149         // finally, horizontal components of the borders
2150         dx = border.inset(KoTextBlockBorderData::Left);
2151         d->x += dx;
2152         d->width -= border.inset(KoTextBlockBorderData::Left);
2153         d->width -= border.inset(KoTextBlockBorderData::Right);
2154     } else { // this parag has no border.
2155         if (d->prevBorder) {
2156             d->y += d->prevBorderPadding;
2157             d->y += d->prevBorder->inset(KoTextBlockBorderData::Bottom);
2158         }
2159         blockData.setBorder(0); // remove an old one, if there was one.
2160         if (!d->blockRects.isEmpty()) {
2161             d->blockRects.last().setBottom(d->y);
2162         }
2163         d->anchoringParagraphTop = d->y;
2164         if (d->bottomSpacing + topMargin) {
2165             d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin);
2166         }
2167         d->y += spacing;
2168         d->blockRects.append(QRectF(x, d->y, width, 1.0));
2169     }
2170     if (!paddingExpandsBorders) {
2171         // add padding inside the border
2172         dx += format.doubleProperty(KoParagraphStyle::LeftPadding);
2173         d->x += format.doubleProperty(KoParagraphStyle::LeftPadding);
2174         d->width -= format.doubleProperty(KoParagraphStyle::LeftPadding);
2175         d->width -= format.doubleProperty(KoParagraphStyle::RightPadding);
2176     }
2177     if (block->layout()->lineCount() == 1 && blockData.hasCounterData()) {
2178         blockData.setCounterPosition(QPointF(blockData.counterPosition().x() + dx, d->y));
2179     }
2180     d->prevBorder = blockData.border();
2181     d->prevBorderPadding = format.doubleProperty(KoParagraphStyle::BottomPadding);
2182     d->anchoringParagraphContentTop = d->y;
2183 }