Warning, file /office/calligra/libs/textlayout/KoStyleThumbnailer.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, 2009 Thomas Zander <zander@kde.org> 0003 * Copyright (C) 2007 Sebastian Sauer <mail@dipe.org> 0004 * Copyright (C) 2007 Pierre Ducroquet <pinaraf@gmail.com> 0005 * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in> 0006 * Copyright (C) 2009,2011 KO GmbH <cbo@kogmbh.com> 0007 * Copyright (C) 2011-2012 Pierre Stirnweiss <pstirnweiss@googlemail.com> 0008 * Copyright (C) 2012 Gopalakrishna Bhat A <gopalakbhat@gmail.com> 0009 * 0010 * This library is free software; you can redistribute it and/or 0011 * modify it under the terms of the GNU Library General Public 0012 * License as published by the Free Software Foundation; either 0013 * version 2 of the License, or (at your option) any later version. 0014 * 0015 * This library is distributed in the hope that it will be useful, 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0018 * Library General Public License for more details. 0019 * 0020 * You should have received a copy of the GNU Library General Public License 0021 * along with this library; see the file COPYING.LIB. If not, write to 0022 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0023 * Boston, MA 02110-1301, USA. 0024 */ 0025 0026 #include "KoStyleThumbnailer.h" 0027 0028 #include "KoParagraphStyle.h" 0029 #include "KoCharacterStyle.h" 0030 #include "KoTextDocumentLayout.h" 0031 #include "KoTextLayoutRootArea.h" 0032 #include "FrameIterator.h" 0033 0034 #include <stdint.h> 0035 0036 #include <klocalizedstring.h> 0037 0038 #include <QCache> 0039 #include <QFont> 0040 #include <QImage> 0041 #include <QPainter> 0042 #include <QRect> 0043 #include <QTextBlock> 0044 #include <QTextCursor> 0045 #include <QTextLength> 0046 0047 #include <TextLayoutDebug.h> 0048 0049 extern int qt_defaultDpiX(); 0050 extern int qt_defaultDpiY(); 0051 0052 class Q_DECL_HIDDEN KoStyleThumbnailer::Private 0053 { 0054 public: 0055 Private() : 0056 thumbnailHelperDocument(new QTextDocument), 0057 documentLayout(new KoTextDocumentLayout(thumbnailHelperDocument)), 0058 defaultSize(QSize(250, 48)) 0059 { 0060 thumbnailHelperDocument->setDocumentLayout(documentLayout); 0061 } 0062 0063 ~Private() 0064 { 0065 delete documentLayout; 0066 delete thumbnailHelperDocument; 0067 } 0068 0069 QTextDocument *thumbnailHelperDocument; 0070 KoTextDocumentLayout *documentLayout; 0071 QCache<QString, QImage> thumbnailCache; // cache of QImage representations of the styles 0072 QSize defaultSize; 0073 QString thumbnailText; 0074 }; 0075 0076 KoStyleThumbnailer::KoStyleThumbnailer() 0077 : d(new Private()) 0078 { 0079 } 0080 0081 KoStyleThumbnailer::~KoStyleThumbnailer() 0082 { 0083 delete d; 0084 } 0085 0086 QImage KoStyleThumbnailer::thumbnail(KoParagraphStyle *style, const QSize &_size, bool recreateThumbnail, KoStyleThumbnailerFlags flags) 0087 { 0088 if ((flags & UseStyleNameText) && (!style || style->name().isNull())) { 0089 return QImage(); 0090 } else if ((! (flags & UseStyleNameText)) && d->thumbnailText.isEmpty()) { 0091 return QImage(); 0092 } 0093 0094 const QSize &size = (!_size.isValid() || _size.isNull()) ? d->defaultSize : _size; 0095 0096 QString imageKey = "p_" + QString::number(reinterpret_cast<uintptr_t>(style)) + "_" + QString::number(size.width()) + "_" + QString::number(size.height()); 0097 0098 if (!recreateThumbnail && d->thumbnailCache.object(imageKey)) { 0099 return QImage(*(d->thumbnailCache.object(imageKey))); 0100 } 0101 0102 QImage *im = new QImage(size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); 0103 im->fill(QColor(Qt::transparent).rgba()); 0104 0105 KoParagraphStyle *clone = style->clone(); 0106 //TODO: make the following real options 0107 //we ignore these properties when the thumbnail would not be sufficient to preview properly the whole paragraph with margins. 0108 clone->setMargin(QTextLength(QTextLength::FixedLength, 0)); 0109 clone->setPadding(0); 0110 // 0111 QTextCursor cursor(d->thumbnailHelperDocument); 0112 cursor.select(QTextCursor::Document); 0113 cursor.setBlockFormat(QTextBlockFormat()); 0114 cursor.setBlockCharFormat(QTextCharFormat()); 0115 cursor.setCharFormat(QTextCharFormat()); 0116 QTextBlock block = cursor.block(); 0117 clone->applyStyle(block, true); 0118 0119 QTextCharFormat format; 0120 // Default to black as text color, to match what KoTextLayoutArea::paint(...) 0121 // does, setting solid black if no brush is set. Otherwise the UI text color 0122 // would be used, which might be too bright with dark UI color schemes 0123 format.setForeground(QColor(Qt::black)); 0124 clone->KoCharacterStyle::applyStyle(format); 0125 if (flags & UseStyleNameText) { 0126 cursor.insertText(clone->name(), format); 0127 } else { 0128 cursor.insertText(d->thumbnailText, format); 0129 } 0130 layoutThumbnail(size, im, flags); 0131 // Make a copy of the image before inserting in the cache 0132 QImage res = QImage(*im); 0133 // Because on inserting, QCache can decide to delete the object immediately 0134 d->thumbnailCache.insert(imageKey, im); 0135 0136 delete clone; 0137 return res; 0138 } 0139 0140 QImage KoStyleThumbnailer::thumbnail(KoCharacterStyle *characterStyle, KoParagraphStyle *paragraphStyle, const QSize &_size, bool recreateThumbnail, KoStyleThumbnailerFlags flags) 0141 { 0142 if ((flags & UseStyleNameText) && (!characterStyle || characterStyle->name().isNull())) { 0143 return QImage(); 0144 } else if ((! (flags & UseStyleNameText)) && d->thumbnailText.isEmpty()) { 0145 return QImage(); 0146 } 0147 else if (characterStyle == 0) { 0148 return QImage(); 0149 } 0150 0151 const QSize &size = (!_size.isValid() || _size.isNull()) ? d->defaultSize : _size; 0152 0153 QString imageKey = "c_" + QString::number(reinterpret_cast<uintptr_t>(characterStyle)) + "_" 0154 + "p_" + QString::number(reinterpret_cast<uintptr_t>(paragraphStyle)) + "_" 0155 + QString::number(size.width()) + "_" + QString::number(size.height()); 0156 0157 if (!recreateThumbnail && d->thumbnailCache.object(imageKey)) { 0158 return QImage(*(d->thumbnailCache.object(imageKey))); 0159 } 0160 0161 QImage *im = new QImage(size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); 0162 im->fill(QColor(Qt::transparent).rgba()); 0163 0164 QTextCursor cursor(d->thumbnailHelperDocument); 0165 QTextCharFormat format; 0166 // Default to black as text color, to match what KoTextLayoutArea::paint(...) 0167 // does, setting solid black if no brush is set. Otherwise the UI text color 0168 // would be used, which might be too bright with dark UI color schemes 0169 format.setForeground(QColor(Qt::black)); 0170 KoCharacterStyle *characterStyleClone = characterStyle->clone(); 0171 characterStyleClone->applyStyle(format); 0172 cursor.select(QTextCursor::Document); 0173 cursor.setBlockFormat(QTextBlockFormat()); 0174 cursor.setBlockCharFormat(QTextCharFormat()); 0175 cursor.setCharFormat(QTextCharFormat()); 0176 0177 if (paragraphStyle) { 0178 KoParagraphStyle *paragraphStyleClone = paragraphStyle->clone(); 0179 // paragraphStyleClone->KoCharacterStyle::applyStyle(format); 0180 QTextBlock block = cursor.block(); 0181 paragraphStyleClone->applyStyle(block, true); 0182 delete paragraphStyleClone; 0183 paragraphStyleClone = 0; 0184 } 0185 0186 if (flags & UseStyleNameText) { 0187 cursor.insertText(characterStyleClone->name(), format); 0188 } else { 0189 cursor.insertText(d->thumbnailText, format); 0190 } 0191 0192 layoutThumbnail(size, im, flags); 0193 QImage res = QImage(*im); 0194 d->thumbnailCache.insert(imageKey, im); 0195 delete characterStyleClone; 0196 return res; 0197 } 0198 0199 void KoStyleThumbnailer::setThumbnailSize(const QSize &size) 0200 { 0201 d->defaultSize = size; 0202 } 0203 0204 void KoStyleThumbnailer::layoutThumbnail(const QSize &size, QImage *im, KoStyleThumbnailerFlags flags) 0205 { 0206 QPainter p(im); 0207 d->documentLayout->removeRootArea(); 0208 KoTextLayoutRootArea rootArea(d->documentLayout); 0209 rootArea.setReferenceRect(0, size.width() * 72.0 / qt_defaultDpiX(), 0, 1E6); 0210 rootArea.setNoWrap(1E6); 0211 0212 FrameIterator frameCursor(d->thumbnailHelperDocument->rootFrame()); 0213 rootArea.layoutRoot(&frameCursor); 0214 0215 QSizeF documentSize = rootArea.boundingRect().size(); 0216 documentSize.setWidth(documentSize.width() * qt_defaultDpiX() / 72.0); 0217 documentSize.setHeight(documentSize.height() * qt_defaultDpiY() / 72.0); 0218 if (documentSize.width() > size.width() || documentSize.height() > size.height()) { 0219 //calculate the space needed for the font size indicator (should the preview be too big with the style's font size 0220 QTextCursor cursor(d->thumbnailHelperDocument); 0221 cursor.select(QTextCursor::Document); 0222 QString sizeHint = "\t" + QString::number(cursor.charFormat().fontPointSize()) + "pt"; 0223 p.save(); 0224 QFont sizeHintFont = p.font(); 0225 sizeHintFont.setPointSize(8); 0226 p.setFont(sizeHintFont); 0227 QRectF sizeHintRect(p.boundingRect(0, 0, 1, 1, Qt::AlignCenter, sizeHint)); 0228 p.restore(); 0229 qreal width = qMax<qreal>(0., size.width()-sizeHintRect.width()); 0230 0231 QTextCharFormat fmt = cursor.charFormat(); 0232 if (flags & ScaleThumbnailFont) { 0233 //calculate the font reduction factor so that the text + the sizeHint fits 0234 qreal reductionFactor = qMin(width/documentSize.width(), size.height()/documentSize.height()); 0235 0236 fmt.setFontPointSize((int)(fmt.fontPointSize()*reductionFactor)); 0237 } 0238 0239 cursor.mergeCharFormat(fmt); 0240 0241 frameCursor = FrameIterator(d->thumbnailHelperDocument->rootFrame()); 0242 rootArea.setReferenceRect(0, width * 72.0 / qt_defaultDpiX(), 0, 1E6); 0243 rootArea.setNoWrap(1E6); 0244 rootArea.layoutRoot(&frameCursor); 0245 documentSize = rootArea.boundingRect().size(); 0246 documentSize.setWidth(documentSize.width() * qt_defaultDpiX() / 72.0); 0247 documentSize.setHeight(documentSize.height() * qt_defaultDpiY() / 72.0); 0248 //center the preview in the pixmap 0249 qreal yOffset = (size.height()-documentSize.height())/2; 0250 p.save(); 0251 if ((flags & CenterAlignThumbnail) && yOffset) { 0252 p.translate(0, yOffset); 0253 } 0254 0255 p.scale(qt_defaultDpiX() / 72.0, qt_defaultDpiY() / 72.0); 0256 0257 KoTextDocumentLayout::PaintContext pc; 0258 rootArea.paint(&p, pc); 0259 0260 p.restore(); 0261 0262 p.setFont(sizeHintFont); 0263 p.drawText(QRectF(size.width()-sizeHintRect.width(), 0, sizeHintRect.width(), 0264 size.height() /*because we want to be vertically centered in the pixmap, like the style name*/),Qt::AlignCenter, sizeHint); 0265 } 0266 else { 0267 //center the preview in the pixmap 0268 qreal yOffset = (size.height()-documentSize.height())/2; 0269 if ((flags & CenterAlignThumbnail) && yOffset) { 0270 p.translate(0, yOffset); 0271 } 0272 0273 p.scale(qt_defaultDpiX() / 72.0, qt_defaultDpiY() / 72.0); 0274 0275 KoTextDocumentLayout::PaintContext pc; 0276 rootArea.paint(&p, pc); 0277 } 0278 } 0279 0280 void KoStyleThumbnailer::removeFromCache(KoParagraphStyle *style) 0281 { 0282 QString imageKey = "p_" + QString::number(reinterpret_cast<uintptr_t>(style)) + "_"; 0283 removeFromCache(imageKey); 0284 } 0285 0286 void KoStyleThumbnailer::removeFromCache(KoCharacterStyle *style) 0287 { 0288 QString imageKey = "c_" + QString::number(reinterpret_cast<uintptr_t>(style)) + "_"; 0289 removeFromCache(imageKey); 0290 } 0291 0292 void KoStyleThumbnailer::setText(const QString &text) 0293 { 0294 d->thumbnailText = text; 0295 } 0296 0297 void KoStyleThumbnailer::removeFromCache(const QString &expr) 0298 { 0299 QList<QString> keys = d->thumbnailCache.keys(); 0300 foreach (const QString &key, keys) { 0301 if (key.contains(expr)) { 0302 d->thumbnailCache.remove(key); 0303 } 0304 } 0305 }