Warning, file /office/calligra/libs/textlayout/KoTextLayoutArea_paint.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 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-2012 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  * Copyright (C) 2014 Denis Kuplyakov <dener.kup@gmail.com>
0015  *
0016  * This library is free software; you can redistribute it and/or
0017  * modify it under the terms of the GNU Library General Public
0018  * License as published by the Free Software Foundation; either
0019  * version 2 of the License, or (at your option) any later version.
0020  *
0021  * This library is distributed in the hope that it will be useful,
0022  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0023  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0024  * Library General Public License for more details.
0025  *
0026  * You should have received a copy of the GNU Library General Public License
0027  * along with this library; see the file COPYING.LIB.  If not, write to
0028  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0029  * Boston, MA 02110-1301, USA.
0030  */
0031 
0032 #include "KoTextLayoutArea.h"
0033 
0034 #include "KoTextLayoutEndNotesArea.h"
0035 #include "KoTextLayoutTableArea.h"
0036 #include "KoTextLayoutNoteArea.h"
0037 #include "TableIterator.h"
0038 #include "ListItemsHelper.h"
0039 #include "RunAroundHelper.h"
0040 #include "KoTextDocumentLayout.h"
0041 #include "FrameIterator.h"
0042 
0043 #include <KoParagraphStyle.h>
0044 #include <KoCharacterStyle.h>
0045 #include <KoListStyle.h>
0046 #include <KoStyleManager.h>
0047 #include <KoTextBlockData.h>
0048 #include <KoTextBlockBorderData.h>
0049 #include <KoTextBlockPaintStrategyBase.h>
0050 #include <KoText.h>
0051 #include <KoChangeTracker.h>
0052 #include <KoChangeTrackerElement.h>
0053 #include <KoImageData.h>
0054 
0055 #include <TextLayoutDebug.h>
0056 #include <KoSection.h>
0057 #include <KoSectionEnd.h>
0058 #include <KoSectionUtils.h>
0059 
0060 #include <QPainter>
0061 #include <QTextTable>
0062 #include <QTextList>
0063 #include <QFontMetrics>
0064 #include <QTextFragment>
0065 #include <QTextLayout>
0066 #include <QTextCursor>
0067 #include <QTime>
0068 
0069 extern int qt_defaultDpiY();
0070 Q_DECLARE_METATYPE(QTextDocument *)
0071 
0072 #define DropCapsAdditionalFormattingId 25602902
0073 
0074 #include "KoTextLayoutArea_p.h"
0075 
0076 void KoTextLayoutArea::paint(QPainter *painter, const KoTextDocumentLayout::PaintContext &context)
0077 {
0078     if (d->startOfArea == 0 || d->endOfArea == 0) // We have not been layouted yet
0079         return;
0080 
0081     /*
0082     struct Timer {
0083         QTime d->time;
0084         Timer() { d->time.start(); }
0085         ~Timer() { warnTextLayout << "elapsed=" << d->time.elapsed(); }
0086     };
0087     Timer timer;
0088     */
0089 
0090     painter->save();
0091     painter->translate(0, d->verticalAlignOffset);
0092 
0093     painter->setPen(context.textContext.palette.color(QPalette::Text)); // for text that has no color.
0094     const QRegion clipRegion = painter->clipRegion(); // fetch after painter->translate so the clipRegion is correct
0095     KoTextBlockBorderData *lastBorder = 0;
0096     QRectF lastBorderRect;
0097 
0098     QTextFrame::iterator it = d->startOfArea->it;
0099     QTextFrame::iterator stop = d->endOfArea->it;
0100     if (!stop.atEnd()) {
0101         if (!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) {
0102             // Last thing we show is a frame (table) or first part of a paragraph split in two
0103             // The stop point should be the object after that
0104             // However if stop is already atEnd we shouldn't increment further
0105             ++stop;
0106         }
0107     }
0108 
0109     int tableAreaIndex = 0;
0110     int blockIndex = 0;
0111     int tocIndex = 0;
0112     for (; it != stop && !it.atEnd(); ++it) {
0113         QTextBlock block = it.currentBlock();
0114         QTextTable *table = qobject_cast<QTextTable*>(it.currentFrame());
0115         QTextFrame *subFrame = it.currentFrame();
0116 
0117         if (!block.isValid()) {
0118             if (lastBorder) { // draw previous block's border
0119                 lastBorder->paint(*painter, lastBorderRect);
0120                 lastBorder = 0;
0121             }
0122         }
0123 
0124         if (table) {
0125             if (tableAreaIndex >= d->tableAreas.size()) {
0126                 continue;
0127             }
0128             d->tableAreas[tableAreaIndex]->paint(painter, context);
0129             ++tableAreaIndex;
0130             continue;
0131         } else if (subFrame) {
0132             if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) {
0133                 d->endNotesArea->paint(painter, context);
0134             }
0135             continue;
0136         } else {
0137             if (!block.isValid()) {
0138                 continue;
0139             }
0140         }
0141 
0142         if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) {
0143             // Possibly paint the selection of the entire Table of Contents
0144             // but since it's a secondary document we need to create a fake selection
0145             QVariant data = block.blockFormat().property(KoParagraphStyle::GeneratedDocument);
0146             QTextDocument *generatedDocument = data.value<QTextDocument *>();
0147 
0148             KoTextDocumentLayout::PaintContext tocContext = context;
0149             tocContext.textContext.selections = QVector<QAbstractTextDocumentLayout::Selection>();
0150 
0151             bool pure = true;
0152             foreach(const QAbstractTextDocumentLayout::Selection &selection, context.textContext.selections) {
0153                 if (selection.cursor.selectionStart()  <= block.position()
0154                     && selection.cursor.selectionEnd() >= block.position()) {
0155                     painter->fillRect(d->generatedDocAreas[tocIndex]->boundingRect(), selection.format.background());
0156                     if (pure) {
0157                         tocContext.textContext.selections.append(QAbstractTextDocumentLayout::Selection());
0158                         tocContext.textContext.selections[0].cursor = QTextCursor(generatedDocument);
0159                         tocContext.textContext.selections[0].cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
0160                         tocContext.textContext.selections[0].format = selection.format;
0161                         pure = false;
0162                     }
0163                 }
0164             }
0165             d->generatedDocAreas[tocIndex]->paint(painter, tocContext);
0166             ++tocIndex;
0167             continue;
0168         }
0169 
0170         QTextLayout *layout = block.layout();
0171         KoTextBlockBorderData *border = 0;
0172 
0173         if (blockIndex >= d->blockRects.count())
0174             break;
0175         QRectF br = d->blockRects[blockIndex];
0176         ++blockIndex;
0177 
0178         if (!painter->hasClipping() || clipRegion.intersects(br.toRect())) {
0179             KoTextBlockData blockData(block);
0180             border = blockData.border();
0181             KoTextBlockPaintStrategyBase *paintStrategy = blockData.paintStrategy();
0182 
0183             KoTextBlockPaintStrategyBase dummyPaintStrategy;
0184             if (paintStrategy == 0) {
0185                 paintStrategy = &dummyPaintStrategy;
0186             }
0187             if (!paintStrategy->isVisible()) {
0188                 if (lastBorder) { // draw previous block's border
0189                     lastBorder->paint(*painter, lastBorderRect);
0190                     lastBorder = 0;
0191                 }
0192                 continue; // this paragraph shouldn't be shown so just skip it
0193             }
0194 
0195             // Check and update border drawing code
0196             if (lastBorder == 0) {
0197                 lastBorderRect = br;
0198             } else if (lastBorder != border
0199                         || lastBorderRect.width() != br.width()
0200                         || lastBorderRect.x() != br.x()) {
0201                 lastBorder->paint(*painter, lastBorderRect);
0202                 lastBorderRect = br;
0203             } else {
0204                 lastBorderRect = lastBorderRect.united(br);
0205             }
0206             lastBorder = border;
0207 
0208             painter->save();
0209 
0210             QBrush bg = paintStrategy->background(block.blockFormat().background());
0211             if (bg != Qt::NoBrush ) {
0212                 painter->fillRect(br, bg);
0213             } else {
0214                 bg = context.background;
0215             }
0216 
0217             paintStrategy->applyStrategy(painter);
0218             painter->save();
0219             drawListItem(painter, block);
0220             painter->restore();
0221 
0222             QVector<QTextLayout::FormatRange> selections;
0223             if (context.showSelections) {
0224                 foreach(const QAbstractTextDocumentLayout::Selection & selection, context.textContext.selections) {
0225                     QTextCursor cursor = selection.cursor;
0226                     int begin = cursor.position();
0227                     int end = cursor.anchor();
0228                     if (begin > end)
0229                         qSwap(begin, end);
0230 
0231                     if (end < block.position() || begin > block.position() + block.length())
0232                         continue; // selection does not intersect this block.
0233                     if (selection.cursor.hasComplexSelection()) {
0234                         continue; // selections of several table cells are covered by the within drawBorders above.
0235                     }
0236                     if (d->documentLayout->changeTracker()
0237                         && !d->documentLayout->changeTracker()->displayChanges()
0238                         && d->documentLayout->changeTracker()->containsInlineChanges(selection.format)
0239                         && d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->isEnabled()
0240                         && d->documentLayout->changeTracker()->elementById(selection.format.property(KoCharacterStyle::ChangeTrackerId).toInt())->getChangeType() == KoGenChange::DeleteChange) {
0241                         continue; // Deletions should not be shown.
0242                     }
0243                     QTextLayout::FormatRange fr;
0244                     fr.start = begin - block.position();
0245                     fr.length = end - begin;
0246                     fr.format = selection.format;
0247                     selections.append(fr);
0248                 }
0249             }
0250             // this is a workaround to fix text getting cut of when format ranges are used. There
0251             // is a bug in Qt that can hit when text lines overlap each other. In case a format range
0252             // is used for formatting it can clip the lines above/below as Qt creates a clip rect for
0253             // the places it already painted for the format range which results in clippling. So use
0254             // the format range always to paint the text.
0255             QVector<QTextLayout::FormatRange> workaroundFormatRanges;
0256             for (QTextBlock::iterator it = block.begin(); !(it.atEnd()); ++it) {
0257                 QTextFragment currentFragment = it.fragment();
0258                 if (currentFragment.isValid()) {
0259                     bool formatChanged = false;
0260 
0261                     QTextCharFormat format = currentFragment.charFormat();
0262                     int changeId = format.intProperty(KoCharacterStyle::ChangeTrackerId);
0263                     if (changeId && d->documentLayout->changeTracker() && d->documentLayout->changeTracker()->displayChanges()) {
0264                         KoChangeTrackerElement *changeElement = d->documentLayout->changeTracker()->elementById(changeId);
0265                         switch(changeElement->getChangeType()) {
0266                             case (KoGenChange::InsertChange):
0267                             format.setBackground(QBrush(d->documentLayout->changeTracker()->getInsertionBgColor()));
0268                             break;
0269                             case (KoGenChange::FormatChange):
0270                             format.setBackground(QBrush(d->documentLayout->changeTracker()->getFormatChangeBgColor()));
0271                             break;
0272                             case (KoGenChange::DeleteChange):
0273                             format.setBackground(QBrush(d->documentLayout->changeTracker()->getDeletionBgColor()));
0274                             break;
0275                             case (KoGenChange::UNKNOWN):
0276                             break;
0277                         }
0278                         formatChanged = true;
0279                     }
0280 
0281                     if (format.isAnchor()) {
0282                         if (!format.hasProperty(KoCharacterStyle::UnderlineStyle))
0283                             format.setFontUnderline(true);
0284                         if (!format.hasProperty(QTextFormat::ForegroundBrush))
0285                             format.setForeground(Qt::blue);
0286                         formatChanged = true;
0287                     }
0288 
0289                     if (format.boolProperty(KoCharacterStyle::UseWindowFontColor)) {
0290                         QBrush backbrush = bg;
0291                         if (format.background() != Qt::NoBrush) {
0292                             backbrush = format.background();
0293                         }
0294 
0295                         QBrush frontBrush;
0296                         frontBrush.setStyle(Qt::SolidPattern);
0297                         // use the same luma calculation and threshold as msoffice
0298                         // see http://social.msdn.microsoft.com/Forums/en-US/os_binaryfile/thread/a02a9a24-efb6-4ba0-a187-0e3d2704882b
0299                         int luma = ((5036060/2) * backbrush.color().red()
0300                                     + (9886846/2) * backbrush.color().green()
0301                                     + (1920103/2) * backbrush.color().blue()) >> 23;
0302                         if (luma > 60) {
0303                             frontBrush.setColor(QColor(Qt::black));
0304                         } else {
0305                             frontBrush.setColor(QColor(Qt::white));
0306                         }
0307                         format.setForeground(frontBrush);
0308 
0309                         formatChanged = true;
0310                     }
0311                     if (formatChanged) {
0312                         QTextLayout::FormatRange fr;
0313                         fr.start = currentFragment.position() - block.position();
0314                         fr.length = currentFragment.length();
0315                         if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) {
0316                             if (format.background().style() == Qt::NoBrush) {
0317                                 format.setBackground(QBrush(QColor(0, 0, 0, 0)));
0318                             }
0319                             if (format.foreground().style() == Qt::NoBrush) {
0320                                 format.setForeground(QBrush(QColor(0, 0, 0)));
0321                             }
0322                         }
0323                         fr.format = format;
0324                         // the prepend is done so the selections are at the end.
0325                         selections.prepend(fr);
0326                     }
0327                     else {
0328                         if (!format.hasProperty(KoCharacterStyle::InlineInstanceId)) {
0329                             QTextLayout::FormatRange fr;
0330                             fr.start = currentFragment.position() - block.position();
0331                             fr.length = currentFragment.length();
0332                             QTextCharFormat f;
0333                             if (format.background().style() == Qt::NoBrush) {
0334                                 f.setBackground(QBrush(QColor(0, 0, 0, 0)));
0335                             }
0336                             else {
0337                                 f.setBackground(format.background());
0338                             }
0339                             if (format.foreground().style() == Qt::NoBrush) {
0340                                 f.setForeground(QBrush(QColor(0, 0, 0)));
0341                             }
0342                             else {
0343                                 f.setForeground(format.foreground());
0344                             }
0345                             fr.format = f;
0346                             workaroundFormatRanges.append(fr);
0347                         }
0348                     }
0349                 }
0350             }
0351 
0352             if (!selections.isEmpty()) {
0353                 selections = workaroundFormatRanges + selections;
0354             }
0355 
0356             //We set clip because layout-draw doesn't clip text to it correctly after all
0357             //and adjust to make sure we don't clip edges of glyphs. The clipping is
0358             //important for paragraph split across two pages.
0359             //20pt enlargement seems safe as pages is split by 50pt and this helps unwanted
0360             //glyph cutting
0361             painter->setClipRect(br.adjusted(-20,-20,20,20), Qt::IntersectClip);
0362 
0363             layout->draw(painter, QPointF(0, 0), selections);
0364 
0365             if (context.showSectionBounds) {
0366                 decorateParagraphSections(painter, block);
0367             }
0368             decorateParagraph(painter, block, context.showFormattingCharacters, context.showSpellChecking);
0369 
0370             painter->restore();
0371         } else {
0372             if (lastBorder) {
0373                 lastBorder->paint(*painter, lastBorderRect);
0374                 lastBorder = 0;
0375             }
0376         }
0377     }
0378     if (lastBorder) {
0379         lastBorder->paint(*painter, lastBorderRect);
0380     }
0381 
0382     painter->translate(0, -d->verticalAlignOffset);
0383     painter->translate(0, bottom() - d->footNotesHeight);
0384     foreach(KoTextLayoutNoteArea *footerArea, d->footNoteAreas) {
0385         footerArea->paint(painter, context);
0386         painter->translate(0, footerArea->bottom() - footerArea->top());
0387     }
0388     painter->restore();
0389 }
0390 
0391 void KoTextLayoutArea::drawListItem(QPainter *painter, QTextBlock &block)
0392 {
0393     KoTextBlockData blockData(block);
0394 
0395     QTextList *list = block.textList();
0396     if (list && blockData.hasCounterData()) {
0397         QTextListFormat listFormat = list->format();
0398         if (! blockData.counterText().isEmpty()) {
0399             QFont font(blockData.labelFormat().font(), d->documentLayout->paintDevice());
0400 
0401             KoListStyle::LabelType labelType = static_cast<KoListStyle::LabelType>(listFormat.style());
0402             QString result = blockData.counterText();
0403 
0404             QTextLayout layout(result, font, d->documentLayout->paintDevice());
0405 
0406             QList<QTextLayout::FormatRange> layouts;
0407             QTextLayout::FormatRange format;
0408             format.start = 0;
0409             format.length = blockData.counterText().length();
0410             format.format = blockData.labelFormat();
0411 
0412             layouts.append(format);
0413             layout.setAdditionalFormats(layouts);
0414 
0415             Qt::Alignment alignment = static_cast<Qt::Alignment>(listFormat.intProperty(KoListStyle::Alignment));
0416 
0417             if (alignment == 0) {
0418                 alignment = Qt::AlignLeft | Qt::AlignAbsolute;
0419             }
0420             if (d->isRtl && (alignment & Qt::AlignAbsolute) == 0) {
0421                 if (alignment & Qt::AlignLeft) {
0422                     alignment = Qt::AlignRight;
0423                 } else if (alignment & Qt::AlignRight) {
0424                     alignment = Qt::AlignLeft;
0425                 }
0426             }
0427             alignment |= Qt::AlignAbsolute;
0428 
0429             QTextOption option(alignment);
0430             option.setTextDirection(block.layout()->textOption().textDirection());
0431 /*
0432             if (option.textDirection() == Qt::RightToLeft || blockData.counterText().isRightToLeft()) {
0433                 option.setAlignment(Qt::AlignRight);
0434             }
0435 */
0436             layout.setTextOption(option);
0437 
0438             layout.beginLayout();
0439 
0440             QTextLine line = layout.createLine();
0441             line.setLineWidth(blockData.counterWidth());
0442             layout.endLayout();
0443 
0444             QPointF counterPosition = blockData.counterPosition();
0445 
0446             if (block.layout()->lineCount() > 0) {
0447                 // if there is text, then baseline align the counter.
0448                 QTextLine firstParagLine = block.layout()->lineAt(0);
0449                 if (KoListStyle::isNumberingStyle(labelType)) {
0450                     //if numbered list baseline align
0451                     counterPosition += QPointF(0, firstParagLine.ascent() - layout.lineAt(0).ascent());
0452                 } else {
0453                      //for unnumbered list center align
0454                     counterPosition += QPointF(0, (firstParagLine.height() - layout.lineAt(0).height())/2.0);
0455                 }
0456             }
0457             layout.draw(painter, counterPosition);
0458 
0459             //decorate the list label iff it is a numbered list
0460             if (KoListStyle::isNumberingStyle(labelType)) {
0461                 painter->save();
0462                 decorateListLabel(painter, blockData, layout.lineAt(0), block);
0463                 painter->restore();
0464             }
0465         }
0466 
0467         KoListStyle::LabelType labelType = static_cast<KoListStyle::LabelType>(listFormat.style());
0468         if (labelType == KoListStyle::ImageLabelType) {
0469             QFontMetricsF fm(blockData.labelFormat().font(), d->documentLayout->paintDevice());
0470             qreal x = qMax(qreal(1), blockData.counterPosition().x());
0471             qreal width = qMax(listFormat.doubleProperty(KoListStyle::Width), (qreal)1.0);
0472             qreal height = qMax(listFormat.doubleProperty(KoListStyle::Height), (qreal)1.0);
0473             qreal y = blockData.counterPosition().y() + fm.ascent() - fm.xHeight()/2 - height/2; // centered
0474             KoImageData *idata = listFormat.property(KoListStyle::BulletImage).value<KoImageData *>();
0475             if (idata) {
0476                 painter->drawPixmap(x, y, width, height, idata->pixmap());
0477             }
0478         }
0479     }
0480 }
0481 
0482 void KoTextLayoutArea::decorateListLabel(QPainter *painter, const KoTextBlockData &blockData, const QTextLine &listLabelLine, const QTextBlock &listItem)
0483 {
0484     const QTextCharFormat listLabelCharFormat = blockData.labelFormat();
0485     painter->setFont(listLabelCharFormat.font());
0486 
0487     int startOfFragmentInBlock = 0;
0488     Q_ASSERT_X(listLabelLine.isValid(), __FUNCTION__, QString("Invalid list label").toLocal8Bit());
0489     if (!listLabelLine.isValid()) {
0490         return;
0491     }
0492 
0493     int fragmentToLineOffset = 0;
0494 
0495     qreal x1 = blockData.counterPosition().x();
0496     qreal x2 = listItem.layout()->lineAt(0).x();
0497 
0498     if (x2 != x1) {
0499         drawStrikeOuts(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0500         drawOverlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0501         drawUnderlines(painter, listLabelCharFormat, blockData.counterText(), listItem.layout()->lineAt(0), x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0502     }
0503 }
0504 
0505 /**
0506  * Draw a line. Typically meant to underline text or similar.
0507  * @param painter the painter to paint on.
0508  * @param color the pen color to for the decoration line
0509  * @param type The type
0510  * @param style the type of line to draw.
0511  * @param width The thickness of the line, in pixels (the painter will be prescaled to points coordinate system).
0512  * @param x1 we are always drawing horizontal lines, this is the start point.
0513  * @param x2 we are always drawing horizontal lines, this is the end point.
0514  * @param y the y-offset to paint on.
0515  */
0516 static void drawDecorationLine(QPainter *painter, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, qreal width, const qreal x1, const qreal x2, const qreal y)
0517 {
0518     QPen penBackup = painter->pen();
0519     QPen pen = painter->pen();
0520     pen.setColor(color);
0521     pen.setWidthF(width);
0522     if (style == KoCharacterStyle::WaveLine) {
0523         // Ok, try the waves :)
0524         pen.setStyle(Qt::SolidLine);
0525         painter->setPen(pen);
0526         qreal x = x1;
0527         const qreal halfWaveWidth = 0.5 * width;
0528         const qreal halfWaveLength = 2 * width;
0529         const int startAngle = 0 * 16;
0530         const int middleAngle = 180 * 16;
0531         const int endAngle = 180 * 16;
0532         while (x < x2) {
0533             QRectF rectangle1(x, y, halfWaveLength, 2*halfWaveWidth);
0534             if (type == KoCharacterStyle::DoubleLine) {
0535                 painter->translate(0, -pen.width());
0536                 painter->drawArc(rectangle1, startAngle, middleAngle);
0537                 painter->translate(0, 2*pen.width());
0538                 painter->drawArc(rectangle1, startAngle, middleAngle);
0539                 painter->translate(0, -pen.width());
0540             } else {
0541                 painter->drawArc(rectangle1, startAngle, middleAngle);
0542             }
0543             if (x + halfWaveLength > x2)
0544                 break;
0545             QRectF rectangle2(x + halfWaveLength, y, halfWaveLength, 2*halfWaveWidth);
0546             if (type == KoCharacterStyle::DoubleLine) {
0547                 painter->translate(0, -pen.width());
0548                 painter->drawArc(rectangle2, middleAngle, endAngle);
0549                 painter->translate(0, 2*pen.width());
0550                 painter->drawArc(rectangle2, middleAngle, endAngle);
0551                 painter->translate(0, -pen.width());
0552             } else {
0553                 painter->drawArc(rectangle2, middleAngle, endAngle);
0554             }
0555             x = x + 2 * halfWaveLength;
0556         }
0557     } else {
0558         if (style == KoCharacterStyle::LongDashLine) {
0559             QVector<qreal> dashes;
0560             dashes << 12 << 2;
0561             pen.setDashPattern(dashes);
0562         } else {
0563             pen.setStyle((Qt::PenStyle)style);
0564         }
0565         painter->setPen(pen);
0566         if (type == KoCharacterStyle::DoubleLine) {
0567             painter->translate(0, -pen.width());
0568             painter->drawLine(QPointF(x1, y), QPointF(x2, y));
0569             painter->translate(0, 2*pen.width());
0570             painter->drawLine(QPointF(x1, y), QPointF(x2, y));
0571             painter->translate(0, -pen.width());
0572         } else {
0573             painter->drawLine(QPointF(x1, y), QPointF(x2, y));
0574         }
0575     }
0576     painter->setPen(penBackup);
0577 }
0578 
0579 static void drawDecorationText(QPainter *painter, const QTextLine &line, const QColor &color, const QString& decorText, qreal x1, qreal x2)
0580 {
0581     qreal y = line.position().y();
0582     QPen oldPen = painter->pen();
0583     painter->setPen(QPen(color));
0584     do {
0585         QRectF br;
0586         painter->drawText(QRectF(QPointF(x1, y), QPointF(x2, y + line.height())), Qt::AlignLeft | Qt::AlignVCenter, decorText, &br);
0587         x1 = br.right();
0588     } while (x1 <= x2);
0589     painter->setPen(oldPen);
0590 }
0591 
0592 static void drawDecorationWords(QPainter *painter, const QTextLine &line, const QString &text, const QColor &color, KoCharacterStyle::LineType type, KoCharacterStyle::LineStyle style, const QString& decorText, qreal width, const qreal y, const int fragmentToLineOffset, const int startOfFragmentInBlock)
0593 {
0594     qreal wordBeginX = -1;
0595     int j = line.textStart()+fragmentToLineOffset;
0596     while (j < line.textLength() + line.textStart() && j-startOfFragmentInBlock<text.size()) {
0597         if (text[j-startOfFragmentInBlock].isSpace()) {
0598             if (wordBeginX != -1) {
0599                 if (decorText.isEmpty())
0600                     drawDecorationLine(painter, color, type, style, width, wordBeginX, line.cursorToX(j), y);
0601                 else
0602                     drawDecorationText(painter, line, color, decorText, wordBeginX, line.cursorToX(j));
0603             }
0604             wordBeginX = -1;
0605         } else if (wordBeginX == -1) {
0606             wordBeginX = line.cursorToX(j);
0607         }
0608     ++j;
0609     }
0610     if (wordBeginX != -1) {
0611         if (decorText.isEmpty())
0612             drawDecorationLine(painter, color, type, style, width, wordBeginX, line.cursorToX(j), y);
0613         else
0614             drawDecorationText(painter, line, color, decorText, wordBeginX, line.cursorToX(j));
0615     }
0616 }
0617 
0618 static qreal computeWidth(KoCharacterStyle::LineWeight weight, qreal width, const QFont& font)
0619 {
0620     switch (weight) {
0621     case KoCharacterStyle::AutoLineWeight:
0622     case KoCharacterStyle::NormalLineWeight:
0623     case KoCharacterStyle::MediumLineWeight:
0624     case KoCharacterStyle::DashLineWeight:
0625         return QFontMetricsF(font).lineWidth();
0626     case KoCharacterStyle::BoldLineWeight:
0627     case KoCharacterStyle::ThickLineWeight:
0628         return QFontMetricsF(font).lineWidth() * 1.5;
0629     case KoCharacterStyle::ThinLineWeight:
0630         return QFontMetricsF(font).lineWidth() * 0.7;
0631     case KoCharacterStyle::PercentLineWeight:
0632         return QFontInfo(font).pointSizeF() * width / 100;
0633     case KoCharacterStyle::LengthLineWeight:
0634         return width;
0635     }
0636     Q_ASSERT(0); // illegal weight passed
0637     return 0;
0638 }
0639 
0640 void KoTextLayoutArea::decorateParagraphSections(QPainter *painter, QTextBlock &block)
0641 {
0642     QTextLayout *layout = block.layout();
0643     QTextBlockFormat bf = block.blockFormat();
0644 
0645     QPen penBackup = painter->pen();
0646     QPen pen = painter->pen();
0647 
0648     pen.setWidth(1);
0649     pen.setColor(Qt::gray);
0650     painter->setPen(pen);
0651 
0652     qreal xl = layout->boundingRect().left();
0653     qreal xr = qMax(layout->boundingRect().right(), layout->boundingRect().left() + width());
0654     qreal yu = layout->boundingRect().top();
0655     qreal yd = layout->boundingRect().bottom();
0656 
0657     qreal bracketSize = painter->fontMetrics().height() / 2;
0658 
0659     const qreal levelShift = 3;
0660 
0661     QList<KoSection *> openList = KoSectionUtils::sectionStartings(bf);
0662     for (int i = 0; i < openList.size(); i++) {
0663         int sectionLevel = openList[i]->level();
0664         if (i == 0) {
0665             painter->drawLine(xl + sectionLevel * levelShift, yu,
0666                               xr - sectionLevel * levelShift, yu);
0667         }
0668         painter->drawLine(xl + sectionLevel * levelShift, yu,
0669                           xl + sectionLevel * levelShift, yu + bracketSize);
0670 
0671         painter->drawLine(xr - sectionLevel * levelShift, yu,
0672                           xr - sectionLevel * levelShift, yu + bracketSize);
0673     }
0674 
0675     QList<KoSectionEnd *> closeList = KoSectionUtils::sectionEndings(bf);
0676     for (int i = 0; i < closeList.size(); i++) {
0677         int sectionLevel = closeList[i]->correspondingSection()->level();
0678         if (i == closeList.count() - 1) {
0679             painter->drawLine(xl + sectionLevel * levelShift, yd,
0680                               xr - sectionLevel * levelShift, yd);
0681         }
0682         painter->drawLine(xl + sectionLevel * levelShift, yd,
0683                           xl + sectionLevel * levelShift, yd - bracketSize);
0684 
0685         painter->drawLine(xr - sectionLevel * levelShift, yd,
0686                           xr - sectionLevel * levelShift, yd - bracketSize);
0687     }
0688 
0689     painter->setPen(penBackup);
0690 }
0691 
0692 void KoTextLayoutArea::decorateParagraph(QPainter *painter, QTextBlock &block, bool showFormattingCharacters, bool showSpellChecking)
0693 {
0694     QTextLayout *layout = block.layout();
0695 
0696     QTextBlockFormat bf = block.blockFormat();
0697     QVariantList tabList = bf.property(KoParagraphStyle::TabPositions).toList();
0698     QFont oldFont = painter->font();
0699 
0700     QTextBlock::iterator it;
0701     int startOfBlock = -1;
0702     int currentTabStop = 0;
0703 
0704 //    qDebug() << "\n-------------------"
0705 //             << "\nGoing to decorate block\n"
0706 //             << block.text()
0707 //             << "\n-------------------";
0708 
0709     // loop over text fragments in this paragraph and draw the underline and line through.
0710     for (it = block.begin(); !it.atEnd(); ++it) {
0711         QTextFragment currentFragment = it.fragment();
0712         if (currentFragment.isValid()) {
0713 
0714 //            qDebug() << "\tGoing to layout fragment:" << currentFragment.text();
0715 
0716             QTextCharFormat fmt = currentFragment.charFormat();
0717             painter->setFont(fmt.font());
0718 
0719             // a block doesn't have a real start position, so use our own counter. Initialize
0720             // it with the position of the first text fragment in the block.
0721             if (startOfBlock == -1) {
0722                 startOfBlock = currentFragment.position(); // start of this block w.r.t. the document
0723             }
0724 
0725             // the start of our fragment in the block is the absolute position of the fragment
0726             // in the document minus the start of the block in the document.
0727             int startOfFragmentInBlock = currentFragment.position() - startOfBlock;
0728 
0729             // a fragment can span multiple lines, but we paint the decorations per line.
0730             int firstLine = layout->lineForTextPosition(currentFragment.position() - startOfBlock).lineNumber();
0731             int lastLine = layout->lineForTextPosition(currentFragment.position() + currentFragment.length()
0732                     - startOfBlock).lineNumber();
0733 
0734 //            qDebug() << "\tfirst line:" << firstLine << "last line:" << lastLine;
0735 
0736             for (int i = firstLine ; i <= lastLine ; ++i) {
0737                 QTextLine line = layout->lineAt(i);
0738 //                qDebug() << "\n\t\tcurrent line:" << i
0739 //                         << "\n\t\tline length:" << line.textLength() << "width:"<< line.width() << "natural width" << line.naturalTextWidth()
0740 //                         << "\n\t\tvalid:" << layout->isValidCursorPosition(currentFragment.position() - startOfBlock)
0741 //                         << "\n\t\tcurrentFragment.position:"  << currentFragment.position()
0742 //                         << "\n\t\tstartOfBlock:" << startOfBlock
0743 //                         << "\n\t\tstartOfFragmentInBlock:" << startOfFragmentInBlock;
0744 
0745                 if (layout->isValidCursorPosition(currentFragment.position() - startOfBlock)) {
0746 
0747                     // the start position for painting the decoration is the position of the fragment
0748                     // inside, but after the first line, the decoration always starts at the beginning
0749                     // of the line.  See bug: 264471
0750                     int p1 = startOfFragmentInBlock;
0751                     if (i > firstLine) {
0752                         p1 = line.textStart();
0753                     }
0754 
0755 //                    qDebug() << "\n\t\tblock.text.length:" << block.text().length() << "p1" << p1;
0756 
0757                     if (block.text().length() > p1 && block.text().at(p1) != QChar::ObjectReplacementCharacter) {
0758                         Q_ASSERT_X(line.isValid(), __FUNCTION__, QString("Invalid line=%1 first=%2 last=%3").arg(i).arg(firstLine).arg(lastLine).toLocal8Bit()); // see bug 278682
0759                         if (!line.isValid())
0760                             continue;
0761 
0762                         // end position: note that x2 can be smaller than x1 when we are handling RTL
0763                         int p2 = startOfFragmentInBlock + currentFragment.length();
0764                         int lineEndWithoutPreedit = line.textStart() + line.textLength();
0765                         if (block.layout()->preeditAreaPosition() >= block.position() + line.textStart() &&
0766                                 block.layout()->preeditAreaPosition() <= block.position() + line.textStart() + line.textLength()) {
0767                             lineEndWithoutPreedit -= block.layout()->preeditAreaText().length();
0768                         }
0769                         while (lineEndWithoutPreedit > line.textStart() && block.text().at(lineEndWithoutPreedit - 1) == ' ') {
0770                             --lineEndWithoutPreedit;
0771                         }
0772                         if (lineEndWithoutPreedit < p2) { //line caps
0773                             p2 = lineEndWithoutPreedit;
0774                         }
0775                         int fragmentToLineOffset = qMax(startOfFragmentInBlock - line.textStart(), 0);
0776 
0777                         qreal x1 = line.cursorToX(p1);
0778                         qreal x2 = line.cursorToX(p2);
0779 
0780                         //qDebug() << "\n\t\t\tp1:" << p1 << "x1:" << x1
0781                           //       << "\n\t\t\tp2:" << p2 << "x2:" << x2
0782                             //     << "\n\t\t\tlineEndWithoutPreedit" << lineEndWithoutPreedit;
0783 
0784                         if (x1 != x2) {
0785                             drawStrikeOuts(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0786                             drawOverlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0787                             drawUnderlines(painter, fmt, currentFragment.text(), line, x1, x2, startOfFragmentInBlock, fragmentToLineOffset);
0788                         }
0789                         decorateTabsAndFormatting(painter, currentFragment, line, startOfFragmentInBlock, tabList, currentTabStop, showFormattingCharacters);
0790 
0791                         // underline preedit strings
0792                         if (layout->preeditAreaPosition() > -1) {
0793                             int start = block.layout()->preeditAreaPosition();
0794                             int end = block.layout()->preeditAreaPosition() + block.layout()->preeditAreaText().length();
0795 
0796                             QTextCharFormat underline;
0797                             underline.setFontUnderline(true);
0798                             underline.setUnderlineStyle(QTextCharFormat::DashUnderline);
0799 
0800                             //qDebug() << "underline style" << underline.underlineStyle();
0801                             //qDebug() << "line start: " << block.position() << line.textStart();
0802                             qreal z1 = 0;
0803                             qreal z2 = 0;
0804 
0805                             // preedit start in this line, end in this line
0806                             if ( start >= block.position() + line.textStart() &&
0807                                  end <= block.position() + line.textStart() + line.textLength() ) {
0808                                 z1 = line.cursorToX(start);
0809                                 z2 = line.cursorToX(end);
0810                             }
0811                             // preedit start in this line, end after this line
0812                             if ( start >= block.position() + line.textStart() &&
0813                                  end > block.position() + line.textStart() + line.textLength() ) {
0814                                 z1 = line.cursorToX(start);
0815                                 z2 = line.cursorToX(block.position() + line.textStart() + line.textLength());
0816                             }
0817                             // preedit start before this line, end in this line
0818                             if ( start < block.position() + line.textStart() &&
0819                                  end <= block.position() + line.textStart() + line.textLength() ) {
0820                                 z1 = line.cursorToX(block.position() + line.textStart());
0821                                 z2 = line.cursorToX(end);
0822                             }
0823                             // preedit start before this line, end after this line
0824                             if ( start < block.position() + line.textStart() &&
0825                                  end > block.position() + line.textStart() + line.textLength() ) {
0826                                 z1 = line.cursorToX(block.position() + line.textStart());
0827                                 z2 = line.cursorToX(block.position() + line.textStart() + line.textLength());
0828                             }
0829                             if (z2 > z1) {
0830                                 //qDebug() << "z1: " << z1 << "z2: " << z2;
0831                                 KoCharacterStyle::LineStyle fontUnderLineStyle = KoCharacterStyle::DashLine;
0832                                 KoCharacterStyle::LineType fontUnderLineType = KoCharacterStyle::SingleLine;
0833                                 QTextCharFormat::VerticalAlignment valign = fmt.verticalAlignment();
0834 
0835                                 QFont font(fmt.font());
0836                                 if (valign == QTextCharFormat::AlignSubScript
0837                                         || valign == QTextCharFormat::AlignSuperScript)
0838                                     font.setPointSize(font.pointSize() * 2 / 3);
0839                                 QFontMetricsF metrics(font, d->documentLayout->paintDevice());
0840 
0841                                 qreal y = line.position().y();
0842                                 if (valign == QTextCharFormat::AlignSubScript)
0843                                     y += line.height() - metrics.descent() + metrics.underlinePos();
0844                                 else if (valign == QTextCharFormat::AlignSuperScript)
0845                                     y += metrics.ascent() + metrics.underlinePos();
0846                                 else
0847                                     y += line.ascent() + metrics.underlinePos();
0848 
0849                                 QColor color = fmt.foreground().color();
0850                                 qreal width = computeWidth( // line thickness
0851                                                             (KoCharacterStyle::LineWeight) underline.intProperty(KoCharacterStyle::UnderlineWeight),
0852                                                             underline.doubleProperty(KoCharacterStyle::UnderlineWidth),
0853                                                             font);
0854                                 if (valign == QTextCharFormat::AlignSubScript
0855                                         || valign == QTextCharFormat::AlignSuperScript) // adjust size.
0856                                     width = width * 2 / 3;
0857 
0858                                 drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, z1, z2, y);
0859                             }
0860                         }
0861                     }
0862                 }
0863             }
0864         }
0865     }
0866 
0867     if (showFormattingCharacters) {
0868         QTextLine line = layout->lineForTextPosition(block.length()-1);
0869         qreal y = line.position().y() + line.ascent();
0870         qreal x = line.cursorToX(block.length()-1);
0871         painter->drawText(QPointF(x, y), QChar((ushort)0x00B6));
0872     }
0873 
0874     if (showSpellChecking) {
0875         // Finally let's paint our own spelling markings
0876         // TODO Should we make this optional at this point (right on/off handled by the plugin)
0877         // also we might want to provide alternative ways of drawing it
0878         KoTextBlockData blockData(block);
0879         QPen penBackup = painter->pen();
0880         QPen pen;
0881         pen.setColor(QColor(Qt::red));
0882         pen.setWidthF(1.5);
0883         QVector<qreal> pattern;
0884         pattern << 1 << 2;
0885         pen.setDashPattern(pattern);
0886         painter->setPen(pen);
0887 
0888         QVector<KoTextBlockData::MarkupRange>::Iterator markIt = blockData.markupsBegin(KoTextBlockData::Misspell);
0889         QVector<KoTextBlockData::MarkupRange>::Iterator markEnd = blockData.markupsEnd(KoTextBlockData::Misspell);
0890         for (int i = 0 ; i < layout->lineCount(); ++i) {
0891             if (markIt == markEnd) {
0892                 break;
0893             }
0894             QTextLine line = layout->lineAt(i);
0895             // the y position is placed half way between baseline and descent of the line
0896             // this is fast and sufficient
0897             qreal y = line.position().y() + line.ascent() + 0.5 * line.descent();
0898 
0899             // first handle all those marking ranges that end on this line
0900             while (markIt != markEnd && markIt->lastChar < line.textStart() + line.textLength()
0901                 && line.textStart() + line.textLength() <= block.length()) {
0902                 if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) {
0903                     if (markIt->firstChar > line.textStart()) {
0904                         markIt->startX = line.cursorToX(markIt->firstChar);
0905                     }
0906                     markIt->endX = line.cursorToX(qMin(markIt->lastChar, block.length()));
0907                 }
0908                 qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0);
0909 
0910                 painter->drawLine(QPointF(x1, y), QPointF(markIt->endX, y));
0911 
0912                 ++markIt;
0913             }
0914 
0915             // there may be a markup range on this line that extends to the next line
0916             if (markIt != markEnd && markIt->firstChar < line.textStart() + line.textLength()
0917                 && line.textStart() + line.textLength() <= block.length()) {
0918                 if (!blockData.isMarkupsLayoutValid(KoTextBlockData::Misspell)) {
0919                     if (markIt->firstChar > line.textStart()) {
0920                         markIt->startX = line.cursorToX(markIt->firstChar);
0921                     }
0922                 }
0923                 qreal x1 = (markIt->firstChar > line.textStart()) ? markIt->startX : line.cursorToX(0);
0924 
0925                 painter->drawLine(QPointF(x1, y), QPointF(line.cursorToX(line.textStart() + line.textLength()), y));
0926 
0927                 // since it extends to next line we don't increment the iterator
0928             }
0929         }
0930         blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, true);
0931 
0932         painter->setPen(penBackup);
0933     }
0934     painter->setFont(oldFont);
0935 }
0936 
0937 void KoTextLayoutArea::drawStrikeOuts(QPainter *painter, const QTextCharFormat &currentCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
0938 {
0939     KoCharacterStyle::LineStyle strikeOutStyle = (KoCharacterStyle::LineStyle)
0940             currentCharFormat.intProperty(KoCharacterStyle::StrikeOutStyle);
0941     KoCharacterStyle::LineType strikeOutType = (KoCharacterStyle::LineType)
0942             currentCharFormat.intProperty(KoCharacterStyle::StrikeOutType);
0943     if ((strikeOutStyle != KoCharacterStyle::NoLineStyle) &&
0944             (strikeOutType != KoCharacterStyle::NoLineType)) {
0945         QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
0946 
0947         QFont font(currentCharFormat.font());
0948         if (valign == QTextCharFormat::AlignSubScript
0949                 || valign == QTextCharFormat::AlignSuperScript)
0950             font.setPointSize(qRound(font.pointSize() * 2 / 3.));
0951         QFontMetricsF metrics(font, d->documentLayout->paintDevice());
0952 
0953         qreal y = line.position().y();
0954         if (valign == QTextCharFormat::AlignSubScript)
0955             y += line.height() - metrics.descent() - metrics.strikeOutPos();
0956         else if (valign == QTextCharFormat::AlignSuperScript)
0957             y += metrics.ascent() - metrics.strikeOutPos();
0958         else
0959             y += line.ascent() - metrics.strikeOutPos();
0960 
0961         QColor color = currentCharFormat.colorProperty(KoCharacterStyle::StrikeOutColor);
0962         if (!color.isValid())
0963             color = currentCharFormat.foreground().color();
0964         KoCharacterStyle::LineMode strikeOutMode =
0965             (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutMode);
0966 
0967         QString strikeOutText = currentCharFormat.stringProperty(KoCharacterStyle::StrikeOutText);
0968         qreal width = 0; // line thickness
0969         if (strikeOutText.isEmpty()) {
0970             width = computeWidth(
0971                         (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::StrikeOutWeight),
0972                         currentCharFormat.doubleProperty(KoCharacterStyle::StrikeOutWidth),
0973                         font);
0974         }
0975         if (valign == QTextCharFormat::AlignSubScript
0976                 || valign == QTextCharFormat::AlignSuperScript) // adjust size.
0977             width = width * 2 / 3;
0978 
0979         if (strikeOutMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
0980             drawDecorationWords(painter, line, text, color, strikeOutType,
0981                     strikeOutStyle, strikeOutText, width, y, fragmentToLineOffset,
0982                     startOfFragmentInBlock);
0983         } else {
0984             if (strikeOutText.isEmpty())
0985                 drawDecorationLine(painter, color, strikeOutType, strikeOutStyle, width, x1, x2, y);
0986             else
0987                 drawDecorationText(painter, line, color, strikeOutText, x1, x2);
0988         }
0989     }
0990 }
0991 
0992 void KoTextLayoutArea::drawOverlines(QPainter *painter, const QTextCharFormat &currentCharFormat, const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
0993 {
0994     KoCharacterStyle::LineStyle fontOverLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::OverlineStyle);
0995     KoCharacterStyle::LineType fontOverLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::OverlineType);
0996     if ((fontOverLineStyle != KoCharacterStyle::NoLineStyle) &&
0997             (fontOverLineType != KoCharacterStyle::NoLineType)) {
0998         QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
0999 
1000         QFont font(currentCharFormat.font());
1001         if (valign == QTextCharFormat::AlignSubScript
1002                 || valign == QTextCharFormat::AlignSuperScript)
1003             font.setPointSize(font.pointSize() * 2 / 3);
1004         QFontMetricsF metrics(font, d->documentLayout->paintDevice());
1005 
1006         qreal y = line.position().y();
1007         if (valign == QTextCharFormat::AlignSubScript)
1008             y += line.height() - metrics.descent() - metrics.overlinePos();
1009         else if (valign == QTextCharFormat::AlignSuperScript)
1010             y += metrics.ascent() - metrics.overlinePos();
1011         else
1012             y += line.ascent() - metrics.overlinePos();
1013 
1014         QColor color = currentCharFormat.colorProperty(KoCharacterStyle::OverlineColor);
1015         if (!color.isValid())
1016             color = currentCharFormat.foreground().color();
1017         KoCharacterStyle::LineMode overlineMode =
1018             (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::OverlineMode);
1019         qreal width = computeWidth( // line thickness
1020                           (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::OverlineWeight),
1021                           currentCharFormat.doubleProperty(KoCharacterStyle::OverlineWidth),
1022                           font);
1023         if (valign == QTextCharFormat::AlignSubScript
1024                 || valign == QTextCharFormat::AlignSuperScript) // adjust size.
1025             width = width * 2 / 3;
1026 
1027         if (overlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
1028             drawDecorationWords(painter, line, text, color, fontOverLineType,
1029                     fontOverLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock);
1030         } else {
1031             drawDecorationLine(painter, color, fontOverLineType, fontOverLineStyle, width, x1, x2, y);
1032         }
1033     }
1034 }
1035 
1036 void KoTextLayoutArea::drawUnderlines(QPainter *painter, const QTextCharFormat &currentCharFormat,const QString &text, const QTextLine &line, qreal x1, qreal x2, const int startOfFragmentInBlock, const int fragmentToLineOffset) const
1037 {
1038     KoCharacterStyle::LineStyle fontUnderLineStyle = (KoCharacterStyle::LineStyle) currentCharFormat.intProperty(KoCharacterStyle::UnderlineStyle);
1039     KoCharacterStyle::LineType fontUnderLineType = (KoCharacterStyle::LineType) currentCharFormat.intProperty(KoCharacterStyle::UnderlineType);
1040     if ((fontUnderLineStyle != KoCharacterStyle::NoLineStyle) &&
1041             (fontUnderLineType != KoCharacterStyle::NoLineType)) {
1042         QTextCharFormat::VerticalAlignment valign = currentCharFormat.verticalAlignment();
1043 
1044         QFont font(currentCharFormat.font());
1045         if (valign == QTextCharFormat::AlignSubScript
1046                 || valign == QTextCharFormat::AlignSuperScript)
1047             font.setPointSize(font.pointSize() * 2 / 3);
1048         QFontMetricsF metrics(font, d->documentLayout->paintDevice());
1049 
1050         qreal y = line.position().y();
1051         if (valign == QTextCharFormat::AlignSubScript)
1052             y += line.height() - metrics.descent() + metrics.underlinePos();
1053         else if (valign == QTextCharFormat::AlignSuperScript)
1054             y += metrics.ascent() + metrics.underlinePos();
1055         else
1056             y += line.ascent() + metrics.underlinePos();
1057 
1058         QColor color = currentCharFormat.underlineColor();
1059         if (!color.isValid())
1060             color = currentCharFormat.foreground().color();
1061         KoCharacterStyle::LineMode underlineMode =
1062             (KoCharacterStyle::LineMode) currentCharFormat.intProperty(KoCharacterStyle::UnderlineMode);
1063         qreal width = computeWidth( // line thickness
1064                           (KoCharacterStyle::LineWeight) currentCharFormat.intProperty(KoCharacterStyle::UnderlineWeight),
1065                           currentCharFormat.doubleProperty(KoCharacterStyle::UnderlineWidth),
1066                           font);
1067         if (valign == QTextCharFormat::AlignSubScript
1068                 || valign == QTextCharFormat::AlignSuperScript) // adjust size.
1069             width = width * 2 / 3;
1070 
1071         if (underlineMode == KoCharacterStyle::SkipWhiteSpaceLineMode) {
1072             drawDecorationWords(painter, line, text, color, fontUnderLineType,
1073                     fontUnderLineStyle, QString(), width, y, fragmentToLineOffset, startOfFragmentInBlock);
1074         } else {
1075             drawDecorationLine(painter, color, fontUnderLineType, fontUnderLineStyle, width, x1, x2, y);
1076         }
1077     }
1078 }
1079 
1080 // Decorate any tabs ('\t's) in 'currentFragment' and laid out in 'line'.
1081 int KoTextLayoutArea::decorateTabsAndFormatting(QPainter *painter, const QTextFragment& currentFragment, const QTextLine &line, const int startOfFragmentInBlock, const QVariantList& tabList, int currentTabStop, bool showFormattingCharacters)
1082 {
1083     // If a line in the layout represent multiple text fragments, this function will
1084     // be called multiple times on the same line, with different fragments.
1085     // Likewise, if a fragment spans two lines, then this function will be called twice
1086     // on the same fragment, once for each line.
1087 
1088     QString fragText = currentFragment.text();
1089 
1090     QFontMetricsF fm(currentFragment.charFormat().font(), d->documentLayout->paintDevice());
1091     qreal tabStyleLineMargin = fm.averageCharWidth() / 4; // leave some margin for the tab decoration line
1092 
1093     // currentFragment.position() : start of this fragment w.r.t. the document
1094     // startOfFragmentInBlock : start of this fragment w.r.t. the block
1095     // line.textStart() : start of this line w.r.t. the block
1096 
1097     int searchForCharFrom; // search for \t from this point onwards in fragText
1098     int searchForCharTill; // search for \t till this point in fragText
1099 
1100     if (line.textStart() >= startOfFragmentInBlock) { // fragment starts at or before the start of line
1101         // we are concerned with only that part of the fragment displayed in this line
1102         searchForCharFrom = line.textStart() - startOfFragmentInBlock;
1103         // It's a new line. So we should look at the first tab-stop properties for the next \t.
1104         currentTabStop = 0;
1105     } else { // fragment starts in the middle of the line
1106         searchForCharFrom = 0;
1107     }
1108     if (line.textStart() + line.textLength() > startOfFragmentInBlock + currentFragment.length()) {
1109         // fragment ends before the end of line. need to see only till the end of the fragment.
1110         searchForCharTill = currentFragment.length();
1111     } else {
1112         // line ends before the fragment ends. need to see only till the end of this line.
1113         // but then, we need to convert the end of line to an index into fragText
1114         searchForCharTill = line.textLength() + line.textStart() - startOfFragmentInBlock;
1115     }
1116     for (int i = searchForCharFrom ; i < searchForCharTill; i++) {
1117         if (currentTabStop >= tabList.size() && !showFormattingCharacters) // no more decorations
1118             break;
1119 
1120         if (fragText[i] == '\t') {
1121             qreal x1(0.0);
1122             qreal x2(0.0);
1123 
1124             if (showFormattingCharacters) {
1125                 x1 = line.cursorToX(startOfFragmentInBlock + i);
1126                 x2 = line.cursorToX(startOfFragmentInBlock + i + 1);
1127                 qreal y = line.position().y() + line.ascent() - fm.xHeight()/2.0;
1128                 qreal arrowDim = fm.xHeight()/2.0;
1129                 QPen penBackup = painter->pen();
1130                 QPen pen = painter->pen();
1131                 pen.setWidthF(fm.ascent()/10.0);
1132                 pen.setStyle(Qt::SolidLine);
1133                 painter->setPen(pen);
1134                 painter->drawLine(QPointF(x1, y), QPointF(x2, y));
1135                 painter->drawLine(QPointF(x2 - arrowDim, y - arrowDim), QPointF(x2, y));
1136                 painter->drawLine(QPointF(x2 - arrowDim, y + arrowDim), QPointF(x2, y));
1137                 painter->setPen(penBackup);
1138             }
1139             if (currentTabStop < tabList.size()) { // still tabsstops worth examining
1140                 if (!showFormattingCharacters) {
1141                     // only then was it  not calculated
1142                     x1 = line.cursorToX(startOfFragmentInBlock + i);
1143                 }
1144                 // find a tab-stop decoration for this tab position
1145                 // for eg., if there's a tab-stop at 1in, but the text before \t already spans 1.2in,
1146                 // we should look at the next tab-stop
1147                 KoText::Tab tab;
1148                 do {
1149                     tab = qvariant_cast<KoText::Tab>(tabList[currentTabStop]);
1150                     currentTabStop++;
1151                     // comparing with x1 should work for all of left/right/center/char tabs
1152                 } while (tab.position <= x1 && currentTabStop < tabList.size());
1153 
1154                 if (tab.position > x1) {
1155                     if (!showFormattingCharacters) {
1156                         // only then was it not calculated
1157                         x2 = line.cursorToX(startOfFragmentInBlock + i + 1);
1158                     }
1159                     qreal tabStyleLeftLineMargin = tabStyleLineMargin;
1160                     qreal tabStyleRightLineMargin = tabStyleLineMargin;
1161                     // no margin if its adjacent char is also a tab
1162                     if (i > searchForCharFrom && fragText[i-1] == '\t')
1163                         tabStyleLeftLineMargin = 0;
1164                     if (i < (searchForCharTill - 1) && fragText[i+1] == '\t')
1165                         tabStyleRightLineMargin = 0;
1166 
1167                     qreal y = line.position().y() + line.ascent() - 1;
1168                     x1 += tabStyleLeftLineMargin;
1169                     x2 -= tabStyleRightLineMargin;
1170                     QColor tabDecorColor = currentFragment.charFormat().foreground().color();
1171                     if (tab.leaderColor.isValid())
1172                         tabDecorColor = tab.leaderColor;
1173                     qreal width = computeWidth(tab.leaderWeight, tab.leaderWidth, painter->font());
1174                     if (x1 < x2) {
1175                         if (tab.leaderText.isEmpty()) {
1176                             drawDecorationLine(painter, tabDecorColor, tab.leaderType, tab.leaderStyle, width, x1, x2, y);
1177                         } else {
1178                             drawDecorationText(painter, line, tabDecorColor, tab.leaderText, x1, x2);
1179                         }
1180                     }
1181                 }
1182             }
1183         } else if (showFormattingCharacters) {
1184             if (fragText[i] == ' ' || fragText[i] == QChar::Nbsp) {
1185                 qreal x = line.cursorToX(startOfFragmentInBlock + i);
1186                 qreal y = line.position().y() + line.ascent();
1187 
1188                 painter->drawText(QPointF(x, y), QChar((ushort)0xb7));
1189             } else if (fragText[i] == QChar::LineSeparator){
1190                 qreal x = line.cursorToX(startOfFragmentInBlock + i);
1191                 qreal y = line.position().y() + line.ascent();
1192 
1193                 painter->drawText(QPointF(x, y), QChar((ushort)0x21B5));
1194             }
1195         }
1196     }
1197     return currentTabStop;
1198 }