Export QTextImageHandler and add accessor for image
[profile/ivi/qtbase.git] / src / gui / text / qstatictext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qstatictext.h"
43 #include "qstatictext_p.h"
44 #include <private/qtextengine_p.h>
45 #include <private/qfontengine_p.h>
46 #include <qabstracttextdocumentlayout.h>
47
48 QT_BEGIN_NAMESPACE
49
50 /*!
51     \class QStaticText
52     \brief The QStaticText class enables optimized drawing of text when the text and its layout
53     is updated rarely.
54     \since 4.7
55
56     \ingroup multimedia
57     \ingroup text
58     \mainclass
59
60     QStaticText provides a way to cache layout data for a block of text so that it can be drawn
61     more efficiently than by using QPainter::drawText() in which the layout information is 
62     recalculated with every call. 
63
64     The class primarily provides an optimization for cases where the text, its font and the
65     transformations on the painter are static over several paint events. If the text or its layout
66     is changed for every iteration, QPainter::drawText() is the more efficient alternative, since
67     the static text's layout would have to be recalculated to take the new state into consideration.
68
69     Translating the painter will not cause the layout of the text to be recalculated, but will cause
70     a very small performance impact on drawStaticText(). Altering any other parts of the painter's
71     transformation or the painter's font will cause the layout of the static text to be
72     recalculated. This should be avoided as often as possible to maximize the performance
73     benefit of using QStaticText.
74
75     In addition, only affine transformations are supported by drawStaticText(). Calling
76     drawStaticText() on a projected painter will perform slightly worse than using the regular
77     drawText() call, so this should be avoided.
78
79     \code
80     class MyWidget: public QWidget
81     {
82     public:
83         MyWidget(QWidget *parent = 0) : QWidget(parent), m_staticText("This is static text")
84
85     protected:
86         void paintEvent(QPaintEvent *)
87         {
88             QPainter painter(this);
89             painter.drawStaticText(0, 0, m_staticText);
90         }
91
92     private:
93         QStaticText m_staticText;
94     };
95     \endcode
96
97     The QStaticText class can be used to mimic the behavior of QPainter::drawText() to a specific
98     point with no boundaries, and also when QPainter::drawText() is called with a bounding 
99     rectangle. 
100
101     If a bounding rectangle is not required, create a QStaticText object without setting a preferred
102     text width. The text will then occupy a single line.
103
104     If you set a text width on the QStaticText object, this will bound the text. The text will
105     be formatted so that no line exceeds the given width. The text width set for QStaticText will
106     not automatically be used for clipping. To achieve clipping in addition to line breaks, use
107     QPainter::setClipRect(). The position of the text is decided by the argument passed to
108     QPainter::drawStaticText() and can change from call to call with a minimal impact on
109     performance.
110
111     For extra convenience, it is possible to apply formatting to the text using the HTML subset
112     supported by QTextDocument. QStaticText will attempt to guess the format of the input text using
113     Qt::mightBeRichText(), and interpret it as rich text if this function returns true. To force
114     QStaticText to display its contents as either plain text or rich text, use the function
115     QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText.
116
117     QStaticText can only represent text, so only HTML tags which alter the layout or appearance of
118     the text will be respected. Adding an image to the input HTML, for instance, will cause the
119     image to be included as part of the layout, affecting the positions of the text glyphs, but it
120     will not be displayed. The result will be an empty area the size of the image in the output.
121     Similarly, using tables will cause the text to be laid out in table format, but the borders
122     will not be drawn.
123
124     If it's the first time the static text is drawn, or if the static text, or the painter's font
125     has been altered since the last time it was drawn, the text's layout has to be
126     recalculated. On some paint engines, changing the matrix of the painter will also cause the
127     layout to be recalculated. In particular, this will happen for any engine except for the
128     OpenGL2 paint engine. Recalculating the layout will impose an overhead on the
129     QPainter::drawStaticText() call where it occurs. To avoid this overhead in the paint event, you
130     can call prepare() ahead of time to ensure that the layout is calculated.
131
132     \sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument
133 */
134
135 /*!
136     \enum QStaticText::PerformanceHint
137
138     This enum the different performance hints that can be set on the QStaticText. These hints
139     can be used to indicate that the QStaticText should use additional caches, if possible,
140     to improve performance at the expense of memory. In particular, setting the performance hint
141     AggressiveCaching on the QStaticText will improve performance when using the OpenGL graphics
142     system or when drawing to a QOpenGLWidget.
143
144     \value ModerateCaching Do basic caching for high performance at a low memory cost.
145     \value AggressiveCaching Use additional caching when available. This may improve performance
146            at a higher memory cost.
147 */
148
149 /*!
150     Constructs an empty QStaticText
151 */
152 QStaticText::QStaticText()    
153     : data(new QStaticTextPrivate)
154 {
155 }
156
157 /*!
158     Constructs a QStaticText object with the given \a text.
159 */
160 QStaticText::QStaticText(const QString &text)
161     : data(new QStaticTextPrivate)
162 {    
163     data->text = text;
164     data->invalidate();
165 }
166
167 /*!
168     Constructs a QStaticText object which is a copy of \a other.
169 */
170 QStaticText::QStaticText(const QStaticText &other)    
171 {
172     data = other.data;
173 }
174
175 /*!
176     Destroys the QStaticText.
177 */
178 QStaticText::~QStaticText()
179 {
180     Q_ASSERT(!data || data->ref >= 1);
181 }
182
183 /*!
184     \internal
185 */
186 void QStaticText::detach()
187 {    
188     if (data->ref != 1)
189         data.detach();
190 }
191
192 /*!
193   Prepares the QStaticText object for being painted with the given \a matrix and the given \a font
194   to avoid overhead when the actual drawStaticText() call is made.
195
196   When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part
197   of the QStaticText object has changed since the last time it was drawn. It will also be
198   recalculated if the painter's font is not the same as when the QStaticText was last drawn, or,
199   on any other paint engine than the OpenGL2 engine, if the painter's matrix has been altered
200   since the static text was last drawn.
201
202   To avoid the overhead of creating the layout the first time you draw the QStaticText after
203   making changes, you can use the prepare() function and pass in the \a matrix and \a font you
204   expect to use when drawing the text.
205
206   \sa QPainter::setFont(), QPainter::setMatrix()
207 */
208 void QStaticText::prepare(const QTransform &matrix, const QFont &font)
209 {
210     data->matrix = matrix;
211     data->font = font;
212     data->init();
213 }
214
215
216 /*!
217     Assigns \a other to this QStaticText.
218 */
219 QStaticText &QStaticText::operator=(const QStaticText &other)
220 {    
221     data = other.data;
222     return *this;
223 }
224
225 /*!
226     Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths
227     are equal.
228 */
229 bool QStaticText::operator==(const QStaticText &other) const
230 {
231     return (data == other.data
232             || (data->text == other.data->text
233                 && data->font == other.data->font
234                 && data->textWidth == other.data->textWidth));
235 }
236
237 /*!
238     Compares \a other to this QStaticText. Returns true if the texts, fonts or maximum sizes
239     are different.
240 */
241 bool QStaticText::operator!=(const QStaticText &other) const
242 {
243     return !(*this == other);
244 }
245
246 /*!
247     Sets the text of the QStaticText to \a text.
248
249     \note This function will cause the layout of the text to require recalculation.
250
251     \sa text()
252 */
253 void QStaticText::setText(const QString &text)
254 {
255     detach();
256     data->text = text;
257     data->invalidate();
258 }
259
260 /*!
261    Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to
262    Qt::AutoText (the default), the format of the text will try to be determined using the
263    function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be
264    displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags
265    that alter the font of the text, its color, or its layout are supported by QStaticText.
266
267    \note This function will cause the layout of the text to require recalculation.
268
269    \sa textFormat(), setText(), text()
270 */
271 void QStaticText::setTextFormat(Qt::TextFormat textFormat)
272 {
273     detach();
274     data->textFormat = textFormat;
275     data->invalidate();
276 }
277
278 /*!
279   Returns the text format of the QStaticText.
280
281   \sa setTextFormat(), setText(), text()
282 */
283 Qt::TextFormat QStaticText::textFormat() const
284 {
285     return Qt::TextFormat(data->textFormat);
286 }
287
288 /*!
289     Returns the text of the QStaticText.
290
291     \sa setText()
292 */
293 QString QStaticText::text() const 
294 {
295     return data->text;
296 }
297
298 /*!
299   Sets the performance hint of the QStaticText according to the \a
300   performanceHint provided. The \a performanceHint is used to
301   customize how much caching is done internally to improve
302   performance.
303
304   The default is QStaticText::ModerateCaching.
305
306   \note This function will cause the layout of the text to require recalculation.
307
308   \sa performanceHint()
309 */
310 void QStaticText::setPerformanceHint(PerformanceHint performanceHint)
311 {
312     if ((performanceHint == ModerateCaching && !data->useBackendOptimizations)
313         || (performanceHint == AggressiveCaching && data->useBackendOptimizations)) {
314         return;
315     }
316     detach();
317     data->useBackendOptimizations = (performanceHint == AggressiveCaching);
318     data->invalidate();
319 }
320
321 /*!
322   Returns which performance hint is set for the QStaticText.
323
324   \sa setPerformanceHint()
325 */
326 QStaticText::PerformanceHint QStaticText::performanceHint() const
327 {
328     return data->useBackendOptimizations ? AggressiveCaching : ModerateCaching;
329 }
330
331 /*!
332    Sets the text option structure that controls the layout process to the given \a textOption.
333
334    \sa textOption()
335 */
336 void QStaticText::setTextOption(const QTextOption &textOption)
337 {
338     detach();
339     data->textOption = textOption;
340     data->invalidate();
341 }
342
343 /*!
344     Returns the current text option used to control the layout process.
345 */
346 QTextOption QStaticText::textOption() const
347 {
348     return data->textOption;
349 }
350
351 /*!
352     Sets the preferred width for this QStaticText. If the text is wider than the specified width,
353     it will be broken into multiple lines and grow vertically. If the text cannot be split into
354     multiple lines, it will be larger than the specified \a textWidth.
355
356     Setting the preferred text width to a negative number will cause the text to be unbounded.
357
358     Use size() to get the actual size of the text.
359
360     \note This function will cause the layout of the text to require recalculation.
361
362     \sa textWidth(), size()
363 */
364 void QStaticText::setTextWidth(qreal textWidth)
365 {
366     detach();
367     data->textWidth = textWidth;
368     data->invalidate();
369 }
370
371 /*!
372     Returns the preferred width for this QStaticText.
373
374     \sa setTextWidth()
375 */
376 qreal QStaticText::textWidth() const
377 {
378     return data->textWidth;
379 }
380
381 /*!
382   Returns the size of the bounding rect for this QStaticText.
383
384   \sa textWidth()
385 */
386 QSizeF QStaticText::size() const
387 {
388     if (data->needsRelayout)
389         data->init();
390     return data->actualSize;
391 }
392
393 QStaticTextPrivate::QStaticTextPrivate()
394         : textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0),
395           needsRelayout(true), useBackendOptimizations(false), textFormat(Qt::AutoText),
396           untransformedCoordinates(false)
397 {
398 }
399
400 QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other)
401     : text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix),
402       items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0), textOption(other.textOption),
403       needsRelayout(true), useBackendOptimizations(other.useBackendOptimizations),
404       textFormat(other.textFormat), untransformedCoordinates(other.untransformedCoordinates)
405 {
406 }
407
408 QStaticTextPrivate::~QStaticTextPrivate()
409 {
410     delete[] items;
411     delete[] glyphPool;
412     delete[] positionPool;
413     delete[] charPool;
414 }
415
416 QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q)
417 {
418     return q->data.data();
419 }
420
421 namespace {
422
423     class DrawTextItemRecorder: public QPaintEngine
424     {
425     public:
426         DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations)
427                 : m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations),
428                   m_untransformedCoordinates(untransformedCoordinates), m_currentColor(Qt::black)
429         {
430         }
431
432         virtual void updateState(const QPaintEngineState &newState)
433         {
434             if (newState.state() & QPaintEngine::DirtyPen
435                 && newState.pen().color() != m_currentColor) {
436                 m_dirtyPen = true;
437                 m_currentColor = newState.pen().color();
438             }
439         }
440
441         virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
442         {
443             const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
444
445             QStaticTextItem currentItem;
446             currentItem.setFontEngine(ti.fontEngine);
447             currentItem.font = ti.font();
448             currentItem.charOffset = m_chars.size();
449             currentItem.numChars = ti.num_chars;
450             currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool
451             currentItem.positionOffset = m_glyphs.size(); // Offset into position pool
452             currentItem.useBackendOptimizations = m_useBackendOptimizations;
453             if (m_dirtyPen)
454                 currentItem.color = m_currentColor;
455
456             QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform();
457             matrix.translate(position.x(), position.y());
458
459             QVarLengthArray<glyph_t> glyphs;
460             QVarLengthArray<QFixedPoint> positions;
461             ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
462
463             int size = glyphs.size();
464             Q_ASSERT(size == positions.size());
465             currentItem.numGlyphs = size;
466
467             m_glyphs.resize(m_glyphs.size() + size);
468             m_positions.resize(m_glyphs.size());
469             m_chars.resize(m_chars.size() + ti.num_chars);
470
471             glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset;
472             memcpy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * currentItem.numGlyphs);
473
474             QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset;
475             memcpy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * currentItem.numGlyphs);
476
477             QChar *charsDestination = m_chars.data() + currentItem.charOffset;
478             memcpy(charsDestination, ti.chars, sizeof(QChar) * currentItem.numChars);
479
480             m_items.append(currentItem);
481         }
482
483         virtual void drawPolygon(const QPointF *, int , PolygonDrawMode )
484         {
485             /* intentionally empty */
486         }
487
488         virtual bool begin(QPaintDevice *)  { return true; }
489         virtual bool end() { return true; }
490         virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
491         virtual Type type() const
492         {
493             return User;
494         }
495
496         QVector<QStaticTextItem> items() const
497         {
498             return m_items;
499         }
500
501         QVector<QFixedPoint> positions() const
502         {
503             return m_positions;
504         }
505
506         QVector<glyph_t> glyphs() const
507         {
508             return m_glyphs;
509         }
510
511         QVector<QChar> chars() const
512         {
513             return m_chars;
514         }
515
516     private:
517         QVector<QStaticTextItem> m_items;
518         QVector<QFixedPoint> m_positions;
519         QVector<glyph_t> m_glyphs;
520         QVector<QChar> m_chars;
521
522         bool m_dirtyPen;
523         bool m_useBackendOptimizations;
524         bool m_untransformedCoordinates;
525         QColor m_currentColor;
526     };
527
528     class DrawTextItemDevice: public QPaintDevice
529     {
530     public:
531         DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations)
532         {
533             m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates,
534                                                      useBackendOptimizations);
535         }
536
537         ~DrawTextItemDevice()
538         {
539             delete m_paintEngine;
540         }
541
542         int metric(PaintDeviceMetric m) const
543         {
544             int val;
545             switch (m) {
546             case PdmWidth:
547             case PdmHeight:
548             case PdmWidthMM:
549             case PdmHeightMM:
550                 val = 0;
551                 break;
552             case PdmDpiX:
553             case PdmPhysicalDpiX:
554                 val = qt_defaultDpiX();
555                 break;
556             case PdmDpiY:
557             case PdmPhysicalDpiY:
558                 val = qt_defaultDpiY();
559                 break;
560             case PdmNumColors:
561                 val = 16777216;
562                 break;
563             case PdmDepth:
564                 val = 24;
565                 break;
566             default:
567                 val = 0;
568                 qWarning("DrawTextItemDevice::metric: Invalid metric command");
569             }
570             return val;
571         }
572
573         virtual QPaintEngine *paintEngine() const
574         {
575             return m_paintEngine;
576         }
577
578         QVector<glyph_t> glyphs() const
579         {
580             return m_paintEngine->glyphs();
581         }
582
583         QVector<QFixedPoint> positions() const
584         {
585             return m_paintEngine->positions();
586         }
587
588         QVector<QStaticTextItem> items() const
589         {
590             return m_paintEngine->items();
591         }
592
593         QVector<QChar> chars() const
594         {
595             return m_paintEngine->chars();
596         }
597
598     private:
599         DrawTextItemRecorder *m_paintEngine;
600     };
601 }
602
603 void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p)
604 {
605     bool preferRichText = textFormat == Qt::RichText
606                           || (textFormat == Qt::AutoText && Qt::mightBeRichText(text));
607
608     if (!preferRichText) {
609         QTextLayout textLayout;
610         textLayout.setText(text);
611         textLayout.setFont(font);
612         textLayout.setTextOption(textOption);
613
614         qreal leading = QFontMetricsF(font).leading();
615         qreal height = -leading;
616
617         textLayout.beginLayout();
618         while (1) {
619             QTextLine line = textLayout.createLine();
620             if (!line.isValid())
621                 break;
622
623             if (textWidth >= 0.0)
624                 line.setLineWidth(textWidth);
625             height += leading;
626             line.setPosition(QPointF(0.0, height));
627             height += line.height();
628         }
629         textLayout.endLayout();
630
631         actualSize = textLayout.boundingRect().size();
632         textLayout.draw(p, topLeftPosition);
633     } else {
634         QTextDocument document;
635 #ifndef QT_NO_CSSPARSER
636         QColor color = p->pen().color();
637         document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }")
638                                       .arg(QString::number(color.red(), 16), 2, QLatin1Char('0'))
639                                       .arg(QString::number(color.green(), 16), 2, QLatin1Char('0'))
640                                       .arg(QString::number(color.blue(), 16), 2, QLatin1Char('0')));
641 #endif
642         document.setDefaultFont(font);
643         document.setDocumentMargin(0.0);        
644 #ifndef QT_NO_TEXTHTMLPARSER
645         document.setHtml(text);
646 #else
647         document.setPlainText(text);
648 #endif
649         if (textWidth >= 0.0)
650             document.setTextWidth(textWidth);
651         else
652             document.adjustSize();
653         document.setDefaultTextOption(textOption);
654
655         p->save();
656         p->translate(topLeftPosition);
657         QAbstractTextDocumentLayout::PaintContext ctx;
658         ctx.palette.setColor(QPalette::Text, p->pen().color());
659         document.documentLayout()->draw(p, ctx);
660         p->restore();
661
662         if (textWidth >= 0.0)
663             document.adjustSize(); // Find optimal size
664
665         actualSize = document.size();
666     }
667 }
668
669 void QStaticTextPrivate::init()
670 {
671     delete[] items;
672     delete[] glyphPool;
673     delete[] positionPool;
674     delete[] charPool;
675
676     position = QPointF(0, 0);
677
678     DrawTextItemDevice device(untransformedCoordinates, useBackendOptimizations);
679     {
680         QPainter painter(&device);
681         painter.setFont(font);
682         painter.setTransform(matrix);
683
684         paintText(QPointF(0, 0), &painter);
685     }
686
687     QVector<QStaticTextItem> deviceItems = device.items();
688     QVector<QFixedPoint> positions = device.positions();
689     QVector<glyph_t> glyphs = device.glyphs();
690     QVector<QChar> chars = device.chars();
691
692     itemCount = deviceItems.size();
693     items = new QStaticTextItem[itemCount];
694
695     glyphPool = new glyph_t[glyphs.size()];
696     memcpy(glyphPool, glyphs.constData(), glyphs.size() * sizeof(glyph_t));
697
698     positionPool = new QFixedPoint[positions.size()];
699     memcpy(positionPool, positions.constData(), positions.size() * sizeof(QFixedPoint));
700
701     charPool = new QChar[chars.size()];
702     memcpy(charPool, chars.constData(), chars.size() * sizeof(QChar));
703
704     for (int i=0; i<itemCount; ++i) {
705         items[i] = deviceItems.at(i);
706
707         items[i].glyphs = glyphPool + items[i].glyphOffset;
708         items[i].glyphPositions = positionPool + items[i].positionOffset;
709         items[i].chars = charPool + items[i].charOffset;
710     }
711
712     needsRelayout = false;
713 }
714
715 QStaticTextItem::~QStaticTextItem()
716 {
717     if (m_userData != 0 && !m_userData->ref.deref())
718         delete m_userData;
719     m_fontEngine->ref.deref();
720 }
721
722 void QStaticTextItem::setFontEngine(QFontEngine *fe)
723 {
724     if (m_fontEngine != 0)
725         m_fontEngine->ref.deref();
726     m_fontEngine = fe;
727     if (m_fontEngine != 0)
728         m_fontEngine->ref.ref();
729 }
730
731 QT_END_NAMESPACE