OSDN Git Service

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