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 ¤tCharFormat, 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 ¤tCharFormat, 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 ¤tCharFormat,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 }