2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
|
|
** Contact: http://www.qt.io/licensing/
|
|
|
|
**
|
|
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qstatictext.h"
|
|
|
|
#include "qstatictext_p.h"
|
|
|
|
#include <qtextengine_p.h>
|
|
|
|
#include <qfontengine_p.h>
|
|
|
|
#include <qfontmetrics.h>
|
|
|
|
#include <qabstracttextdocumentlayout.h>
|
2019-05-07 05:16:11 +00:00
|
|
|
#include <qapplication.h>
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class QStaticText
|
|
|
|
\brief The QStaticText class enables optimized drawing of text when the text and its layout
|
|
|
|
is updated rarely.
|
|
|
|
\since 4.7
|
|
|
|
|
|
|
|
\ingroup multimedia
|
|
|
|
\ingroup text
|
|
|
|
\mainclass
|
|
|
|
|
|
|
|
QStaticText provides a way to cache layout data for a block of text so that it can be drawn
|
|
|
|
more efficiently than by using QPainter::drawText() in which the layout information is
|
|
|
|
recalculated with every call.
|
|
|
|
|
|
|
|
The class primarily provides an optimization for cases where the text, its font and the
|
|
|
|
transformations on the painter are static over several paint events. If the text or its layout
|
|
|
|
is changed for every iteration, QPainter::drawText() is the more efficient alternative, since
|
|
|
|
the static text's layout would have to be recalculated to take the new state into consideration.
|
|
|
|
|
|
|
|
Translating the painter will not cause the layout of the text to be recalculated, but will cause
|
|
|
|
a very small performance impact on drawStaticText(). Altering any other parts of the painter's
|
|
|
|
transformation or the painter's font will cause the layout of the static text to be
|
|
|
|
recalculated. This should be avoided as often as possible to maximize the performance
|
|
|
|
benefit of using QStaticText.
|
|
|
|
|
|
|
|
In addition, only affine transformations are supported by drawStaticText(). Calling
|
|
|
|
drawStaticText() on a projected painter will perform slightly worse than using the regular
|
|
|
|
drawText() call, so this should be avoided.
|
|
|
|
|
|
|
|
\code
|
|
|
|
class MyWidget: public QWidget
|
|
|
|
{
|
|
|
|
public:
|
2016-11-04 04:13:46 +00:00
|
|
|
MyWidget(QWidget *parent = Q_NULLPTR) : QWidget(parent), m_staticText("This is static text")
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
protected:
|
|
|
|
void paintEvent(QPaintEvent *)
|
|
|
|
{
|
|
|
|
QPainter painter(this);
|
|
|
|
painter.drawStaticText(0, 0, m_staticText);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QStaticText m_staticText;
|
|
|
|
};
|
|
|
|
\endcode
|
|
|
|
|
|
|
|
The QStaticText class can be used to mimic the behavior of QPainter::drawText() to a specific
|
|
|
|
point with no boundaries, and also when QPainter::drawText() is called with a bounding
|
|
|
|
rectangle.
|
|
|
|
|
|
|
|
If a bounding rectangle is not required, create a QStaticText object without setting a preferred
|
|
|
|
text width. The text will then occupy a single line.
|
|
|
|
|
|
|
|
If you set a text width on the QStaticText object, this will bound the text. The text will
|
|
|
|
be formatted so that no line exceeds the given width. The text width set for QStaticText will
|
|
|
|
not automatically be used for clipping. To achieve clipping in addition to line breaks, use
|
|
|
|
QPainter::setClipRect(). The position of the text is decided by the argument passed to
|
|
|
|
QPainter::drawStaticText() and can change from call to call with a minimal impact on
|
|
|
|
performance.
|
|
|
|
|
|
|
|
For extra convenience, it is possible to apply formatting to the text using the HTML subset
|
|
|
|
supported by QTextDocument. QStaticText will attempt to guess the format of the input text using
|
|
|
|
Qt::mightBeRichText(), and interpret it as rich text if this function returns true. To force
|
|
|
|
QStaticText to display its contents as either plain text or rich text, use the function
|
|
|
|
QStaticText::setTextFormat() and pass in, respectively, Qt::PlainText and Qt::RichText.
|
|
|
|
|
|
|
|
QStaticText can only represent text, so only HTML tags which alter the layout or appearance of
|
|
|
|
the text will be respected. Adding an image to the input HTML, for instance, will cause the
|
|
|
|
image to be included as part of the layout, affecting the positions of the text glyphs, but it
|
|
|
|
will not be displayed. The result will be an empty area the size of the image in the output.
|
|
|
|
Similarly, using tables will cause the text to be laid out in table format, but the borders
|
|
|
|
will not be drawn.
|
|
|
|
|
|
|
|
If it's the first time the static text is drawn, or if the static text, or the painter's font
|
|
|
|
has been altered since the last time it was drawn, the text's layout has to be
|
|
|
|
recalculated. On some paint engines, changing the matrix of the painter will also cause the
|
2016-11-16 13:53:20 +00:00
|
|
|
layout to be recalculated. In particular, this will happen for any paint engine. Recalculating
|
|
|
|
the layout will impose an overhead on the QPainter::drawStaticText() call where it occurs. To
|
|
|
|
avoid this overhead in the paint event, you can call prepare() ahead of time to ensure that
|
|
|
|
the layout is calculated.
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
\sa QPainter::drawText(), QPainter::drawStaticText(), QTextLayout, QTextDocument
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Constructs an empty QStaticText
|
|
|
|
*/
|
2017-04-26 13:10:59 +00:00
|
|
|
QStaticText::QStaticText()
|
2015-12-10 05:06:13 +02:00
|
|
|
: data(new QStaticTextPrivate)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Constructs a QStaticText object with the given \a text.
|
|
|
|
*/
|
|
|
|
QStaticText::QStaticText(const QString &text)
|
|
|
|
: data(new QStaticTextPrivate)
|
2017-04-26 13:10:59 +00:00
|
|
|
{
|
2015-12-10 05:06:13 +02:00
|
|
|
data->text = text;
|
|
|
|
data->invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Constructs a QStaticText object which is a copy of \a other.
|
|
|
|
*/
|
|
|
|
QStaticText::QStaticText(const QStaticText &other)
|
|
|
|
{
|
|
|
|
data = other.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Destroys the QStaticText.
|
|
|
|
*/
|
|
|
|
QStaticText::~QStaticText()
|
|
|
|
{
|
|
|
|
Q_ASSERT(!data || data->ref >= 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
*/
|
|
|
|
void QStaticText::detach()
|
|
|
|
{
|
|
|
|
if (data->ref != 1)
|
|
|
|
data.detach();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Prepares the QStaticText object for being painted with the given \a matrix and the given \a font
|
|
|
|
to avoid overhead when the actual drawStaticText() call is made.
|
|
|
|
|
|
|
|
When drawStaticText() is called, the layout of the QStaticText will be recalculated if any part
|
|
|
|
of the QStaticText object has changed since the last time it was drawn. It will also be
|
|
|
|
recalculated if the painter's font is not the same as when the QStaticText was last drawn, or,
|
2016-11-16 13:53:20 +00:00
|
|
|
on any other paint engine, if the painter's matrix has been altered since the static text was
|
|
|
|
last drawn.
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
To avoid the overhead of creating the layout the first time you draw the QStaticText after
|
|
|
|
making changes, you can use the prepare() function and pass in the \a matrix and \a font you
|
|
|
|
expect to use when drawing the text.
|
|
|
|
|
|
|
|
\sa QPainter::setFont(), QPainter::setMatrix()
|
|
|
|
*/
|
|
|
|
void QStaticText::prepare(const QTransform &matrix, const QFont &font)
|
|
|
|
{
|
|
|
|
data->matrix = matrix;
|
|
|
|
data->font = font;
|
|
|
|
data->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Assigns \a other to this QStaticText.
|
|
|
|
*/
|
|
|
|
QStaticText &QStaticText::operator=(const QStaticText &other)
|
|
|
|
{
|
|
|
|
data = other.data;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Compares \a other to this QStaticText. Returns true if the texts, fonts and text widths
|
|
|
|
are equal.
|
|
|
|
*/
|
|
|
|
bool QStaticText::operator==(const QStaticText &other) const
|
|
|
|
{
|
|
|
|
return (data == other.data
|
|
|
|
|| (data->text == other.data->text
|
|
|
|
&& data->font == other.data->font
|
|
|
|
&& data->textWidth == other.data->textWidth));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Compares \a other to this QStaticText. Returns true if the texts, fonts or maximum sizes
|
|
|
|
are different.
|
|
|
|
*/
|
|
|
|
bool QStaticText::operator!=(const QStaticText &other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Sets the text of the QStaticText to \a text.
|
|
|
|
|
|
|
|
\note This function will cause the layout of the text to require recalculation.
|
|
|
|
|
|
|
|
\sa text()
|
|
|
|
*/
|
|
|
|
void QStaticText::setText(const QString &text)
|
|
|
|
{
|
|
|
|
detach();
|
|
|
|
data->text = text;
|
|
|
|
data->invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Sets the text format of the QStaticText to \a textFormat. If \a textFormat is set to
|
|
|
|
Qt::AutoText (the default), the format of the text will try to be determined using the
|
|
|
|
function Qt::mightBeRichText(). If the text format is Qt::PlainText, then the text will be
|
|
|
|
displayed as is, whereas it will be interpreted as HTML if the format is Qt::RichText. HTML tags
|
|
|
|
that alter the font of the text, its color, or its layout are supported by QStaticText.
|
|
|
|
|
|
|
|
\note This function will cause the layout of the text to require recalculation.
|
|
|
|
|
|
|
|
\sa textFormat(), setText(), text()
|
|
|
|
*/
|
|
|
|
void QStaticText::setTextFormat(Qt::TextFormat textFormat)
|
|
|
|
{
|
|
|
|
detach();
|
|
|
|
data->textFormat = textFormat;
|
|
|
|
data->invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the text format of the QStaticText.
|
|
|
|
|
|
|
|
\sa setTextFormat(), setText(), text()
|
|
|
|
*/
|
|
|
|
Qt::TextFormat QStaticText::textFormat() const
|
|
|
|
{
|
2019-05-07 23:00:31 +00:00
|
|
|
return data->textFormat;
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the text of the QStaticText.
|
|
|
|
|
|
|
|
\sa setText()
|
|
|
|
*/
|
|
|
|
QString QStaticText::text() const
|
|
|
|
{
|
|
|
|
return data->text;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Sets the text option structure that controls the layout process to the given \a textOption.
|
|
|
|
|
|
|
|
\sa textOption()
|
|
|
|
*/
|
|
|
|
void QStaticText::setTextOption(const QTextOption &textOption)
|
|
|
|
{
|
|
|
|
detach();
|
|
|
|
data->textOption = textOption;
|
|
|
|
data->invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the current text option used to control the layout process.
|
|
|
|
*/
|
|
|
|
QTextOption QStaticText::textOption() const
|
|
|
|
{
|
|
|
|
return data->textOption;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Sets the preferred width for this QStaticText. If the text is wider than the specified width,
|
|
|
|
it will be broken into multiple lines and grow vertically. If the text cannot be split into
|
|
|
|
multiple lines, it will be larger than the specified \a textWidth.
|
|
|
|
|
|
|
|
Setting the preferred text width to a negative number will cause the text to be unbounded.
|
|
|
|
|
|
|
|
Use size() to get the actual size of the text.
|
|
|
|
|
|
|
|
\note This function will cause the layout of the text to require recalculation.
|
|
|
|
|
|
|
|
\sa textWidth(), size()
|
|
|
|
*/
|
|
|
|
void QStaticText::setTextWidth(qreal textWidth)
|
|
|
|
{
|
|
|
|
detach();
|
|
|
|
data->textWidth = textWidth;
|
|
|
|
data->invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the preferred width for this QStaticText.
|
|
|
|
|
|
|
|
\sa setTextWidth()
|
|
|
|
*/
|
|
|
|
qreal QStaticText::textWidth() const
|
|
|
|
{
|
|
|
|
return data->textWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the size of the bounding rect for this QStaticText.
|
|
|
|
|
|
|
|
\sa textWidth()
|
|
|
|
*/
|
|
|
|
QSizeF QStaticText::size() const
|
|
|
|
{
|
|
|
|
if (data->needsRelayout)
|
|
|
|
data->init();
|
|
|
|
return data->actualSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStaticTextPrivate::QStaticTextPrivate()
|
|
|
|
: textWidth(-1.0), items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0),
|
2017-04-26 13:10:59 +00:00
|
|
|
needsRelayout(true), textFormat(Qt::AutoText),
|
2015-12-10 05:06:13 +02:00
|
|
|
untransformedCoordinates(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other)
|
|
|
|
: text(other.text), font(other.font), textWidth(other.textWidth), matrix(other.matrix),
|
|
|
|
items(0), itemCount(0), glyphPool(0), positionPool(0), charPool(0), textOption(other.textOption),
|
2017-04-26 13:10:59 +00:00
|
|
|
needsRelayout(true), textFormat(other.textFormat),
|
|
|
|
untransformedCoordinates(other.untransformedCoordinates)
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QStaticTextPrivate::~QStaticTextPrivate()
|
|
|
|
{
|
|
|
|
delete[] items;
|
|
|
|
delete[] glyphPool;
|
|
|
|
delete[] positionPool;
|
|
|
|
delete[] charPool;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q)
|
|
|
|
{
|
|
|
|
return q->data.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class DrawTextItemRecorder: public QPaintEngine
|
|
|
|
{
|
|
|
|
public:
|
2017-04-26 13:10:59 +00:00
|
|
|
DrawTextItemRecorder(bool untransformedCoordinates)
|
|
|
|
: m_dirtyPen(false), m_untransformedCoordinates(untransformedCoordinates),
|
|
|
|
m_currentColor(Qt::black)
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void updateState(const QPaintEngineState &newState)
|
|
|
|
{
|
|
|
|
if (newState.state() & QPaintEngine::DirtyPen
|
|
|
|
&& newState.pen().color() != m_currentColor) {
|
|
|
|
m_dirtyPen = true;
|
|
|
|
m_currentColor = newState.pen().color();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void drawTextItem(const QPointF &position, const QTextItem &textItem)
|
|
|
|
{
|
|
|
|
const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem);
|
|
|
|
|
|
|
|
QStaticTextItem currentItem;
|
|
|
|
currentItem.setFontEngine(ti.fontEngine);
|
|
|
|
currentItem.font = ti.font();
|
|
|
|
currentItem.charOffset = m_chars.size();
|
|
|
|
currentItem.numChars = ti.num_chars;
|
|
|
|
currentItem.glyphOffset = m_glyphs.size(); // Store offset into glyph pool
|
|
|
|
currentItem.positionOffset = m_glyphs.size(); // Offset into position pool
|
|
|
|
if (m_dirtyPen)
|
|
|
|
currentItem.color = m_currentColor;
|
|
|
|
|
|
|
|
QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform();
|
|
|
|
matrix.translate(position.x(), position.y());
|
|
|
|
|
|
|
|
QVarLengthArray<glyph_t> glyphs;
|
|
|
|
QVarLengthArray<QFixedPoint> positions;
|
|
|
|
ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions);
|
|
|
|
|
|
|
|
int size = glyphs.size();
|
|
|
|
Q_ASSERT(size == positions.size());
|
|
|
|
currentItem.numGlyphs = size;
|
|
|
|
|
|
|
|
m_glyphs.resize(m_glyphs.size() + size);
|
|
|
|
m_positions.resize(m_glyphs.size());
|
|
|
|
m_chars.resize(m_chars.size() + ti.num_chars);
|
|
|
|
|
|
|
|
glyph_t *glyphsDestination = m_glyphs.data() + currentItem.glyphOffset;
|
|
|
|
memcpy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * currentItem.numGlyphs);
|
|
|
|
|
|
|
|
QFixedPoint *positionsDestination = m_positions.data() + currentItem.positionOffset;
|
|
|
|
memcpy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * currentItem.numGlyphs);
|
|
|
|
|
|
|
|
QChar *charsDestination = m_chars.data() + currentItem.charOffset;
|
|
|
|
memcpy(charsDestination, ti.chars, sizeof(QChar) * currentItem.numChars);
|
|
|
|
|
|
|
|
m_items.append(currentItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void drawPolygon(const QPointF *, int , PolygonDrawMode )
|
|
|
|
{
|
|
|
|
/* intentionally empty */
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool begin(QPaintDevice *) { return true; }
|
|
|
|
virtual bool end() { return true; }
|
|
|
|
virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {}
|
|
|
|
virtual Type type() const
|
|
|
|
{
|
|
|
|
return User;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QStaticTextItem> items() const
|
|
|
|
{
|
|
|
|
return m_items;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QFixedPoint> positions() const
|
|
|
|
{
|
|
|
|
return m_positions;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<glyph_t> glyphs() const
|
|
|
|
{
|
|
|
|
return m_glyphs;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QChar> chars() const
|
|
|
|
{
|
|
|
|
return m_chars;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QVector<QStaticTextItem> m_items;
|
|
|
|
QVector<QFixedPoint> m_positions;
|
|
|
|
QVector<glyph_t> m_glyphs;
|
|
|
|
QVector<QChar> m_chars;
|
|
|
|
|
|
|
|
bool m_dirtyPen;
|
|
|
|
bool m_untransformedCoordinates;
|
|
|
|
QColor m_currentColor;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DrawTextItemDevice: public QPaintDevice
|
|
|
|
{
|
|
|
|
public:
|
2017-04-26 13:10:59 +00:00
|
|
|
DrawTextItemDevice(bool untransformedCoordinates)
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
2017-04-26 13:10:59 +00:00
|
|
|
m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates);
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
~DrawTextItemDevice()
|
|
|
|
{
|
|
|
|
delete m_paintEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
int metric(PaintDeviceMetric m) const
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
switch (m) {
|
|
|
|
case PdmWidth:
|
|
|
|
case PdmHeight:
|
|
|
|
case PdmWidthMM:
|
|
|
|
case PdmHeightMM:
|
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
case PdmDpiX:
|
|
|
|
case PdmPhysicalDpiX:
|
|
|
|
val = qt_defaultDpiX();
|
|
|
|
break;
|
|
|
|
case PdmDpiY:
|
|
|
|
case PdmPhysicalDpiY:
|
|
|
|
val = qt_defaultDpiY();
|
|
|
|
break;
|
|
|
|
case PdmNumColors:
|
|
|
|
val = 16777216;
|
|
|
|
break;
|
|
|
|
case PdmDepth:
|
|
|
|
val = 24;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val = 0;
|
|
|
|
qWarning("DrawTextItemDevice::metric: Invalid metric command");
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual QPaintEngine *paintEngine() const
|
|
|
|
{
|
|
|
|
return m_paintEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<glyph_t> glyphs() const
|
|
|
|
{
|
|
|
|
return m_paintEngine->glyphs();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QFixedPoint> positions() const
|
|
|
|
{
|
|
|
|
return m_paintEngine->positions();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QStaticTextItem> items() const
|
|
|
|
{
|
|
|
|
return m_paintEngine->items();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QChar> chars() const
|
|
|
|
{
|
|
|
|
return m_paintEngine->chars();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DrawTextItemRecorder *m_paintEngine;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void QStaticTextPrivate::paintText(const QPointF &topLeftPosition, QPainter *p)
|
|
|
|
{
|
|
|
|
bool preferRichText = textFormat == Qt::RichText
|
|
|
|
|| (textFormat == Qt::AutoText && Qt::mightBeRichText(text));
|
|
|
|
|
|
|
|
if (!preferRichText) {
|
|
|
|
QTextLayout textLayout;
|
|
|
|
textLayout.setText(text);
|
|
|
|
textLayout.setFont(font);
|
|
|
|
textLayout.setTextOption(textOption);
|
|
|
|
|
|
|
|
qreal leading = QFontMetricsF(font).leading();
|
|
|
|
qreal height = -leading;
|
|
|
|
|
|
|
|
textLayout.beginLayout();
|
|
|
|
while (1) {
|
|
|
|
QTextLine line = textLayout.createLine();
|
|
|
|
if (!line.isValid())
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (textWidth >= 0.0)
|
|
|
|
line.setLineWidth(textWidth);
|
|
|
|
height += leading;
|
|
|
|
line.setPosition(QPointF(0.0, height));
|
|
|
|
height += line.height();
|
|
|
|
}
|
|
|
|
textLayout.endLayout();
|
|
|
|
|
|
|
|
actualSize = textLayout.boundingRect().size();
|
|
|
|
textLayout.draw(p, topLeftPosition);
|
|
|
|
} else {
|
|
|
|
QTextDocument document;
|
|
|
|
#ifndef QT_NO_CSSPARSER
|
|
|
|
QColor color = p->pen().color();
|
|
|
|
document.setDefaultStyleSheet(QString::fromLatin1("body { color: #%1%2%3 }")
|
|
|
|
.arg(QString::number(color.red(), 16), 2, QLatin1Char('0'))
|
|
|
|
.arg(QString::number(color.green(), 16), 2, QLatin1Char('0'))
|
|
|
|
.arg(QString::number(color.blue(), 16), 2, QLatin1Char('0')));
|
|
|
|
#endif
|
|
|
|
document.setDefaultFont(font);
|
|
|
|
document.setDocumentMargin(0.0);
|
|
|
|
#ifndef QT_NO_TEXTHTMLPARSER
|
|
|
|
document.setHtml(text);
|
|
|
|
#else
|
|
|
|
document.setPlainText(text);
|
|
|
|
#endif
|
|
|
|
if (textWidth >= 0.0)
|
|
|
|
document.setTextWidth(textWidth);
|
|
|
|
else
|
|
|
|
document.adjustSize();
|
|
|
|
document.setDefaultTextOption(textOption);
|
|
|
|
|
|
|
|
p->save();
|
|
|
|
p->translate(topLeftPosition);
|
|
|
|
QAbstractTextDocumentLayout::PaintContext ctx;
|
|
|
|
ctx.palette.setColor(QPalette::Text, p->pen().color());
|
|
|
|
document.documentLayout()->draw(p, ctx);
|
|
|
|
p->restore();
|
|
|
|
|
|
|
|
if (textWidth >= 0.0)
|
|
|
|
document.adjustSize(); // Find optimal size
|
|
|
|
|
|
|
|
actualSize = document.size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QStaticTextPrivate::init()
|
|
|
|
{
|
|
|
|
delete[] items;
|
|
|
|
delete[] glyphPool;
|
|
|
|
delete[] positionPool;
|
|
|
|
delete[] charPool;
|
|
|
|
|
|
|
|
position = QPointF(0, 0);
|
|
|
|
|
2017-04-26 13:10:59 +00:00
|
|
|
DrawTextItemDevice device(untransformedCoordinates);
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
QPainter painter(&device);
|
|
|
|
painter.setFont(font);
|
|
|
|
painter.setTransform(matrix);
|
|
|
|
|
|
|
|
paintText(QPointF(0, 0), &painter);
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<QStaticTextItem> deviceItems = device.items();
|
|
|
|
QVector<QFixedPoint> positions = device.positions();
|
|
|
|
QVector<glyph_t> glyphs = device.glyphs();
|
|
|
|
QVector<QChar> chars = device.chars();
|
|
|
|
|
|
|
|
itemCount = deviceItems.size();
|
|
|
|
items = new QStaticTextItem[itemCount];
|
|
|
|
|
|
|
|
glyphPool = new glyph_t[glyphs.size()];
|
|
|
|
memcpy(glyphPool, glyphs.constData(), glyphs.size() * sizeof(glyph_t));
|
|
|
|
|
|
|
|
positionPool = new QFixedPoint[positions.size()];
|
|
|
|
memcpy(positionPool, positions.constData(), positions.size() * sizeof(QFixedPoint));
|
|
|
|
|
|
|
|
charPool = new QChar[chars.size()];
|
|
|
|
memcpy(charPool, chars.constData(), chars.size() * sizeof(QChar));
|
|
|
|
|
|
|
|
for (int i=0; i<itemCount; ++i) {
|
|
|
|
items[i] = deviceItems.at(i);
|
|
|
|
|
|
|
|
items[i].glyphs = glyphPool + items[i].glyphOffset;
|
|
|
|
items[i].glyphPositions = positionPool + items[i].positionOffset;
|
|
|
|
items[i].chars = charPool + items[i].charOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
needsRelayout = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStaticTextItem::~QStaticTextItem()
|
|
|
|
{
|
|
|
|
if (!m_fontEngine->ref.deref())
|
|
|
|
delete m_fontEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QStaticTextItem::setFontEngine(QFontEngine *fe)
|
|
|
|
{
|
|
|
|
if (m_fontEngine != 0 && !m_fontEngine->ref.deref())
|
|
|
|
delete m_fontEngine;
|
|
|
|
|
|
|
|
m_fontEngine = fe;
|
|
|
|
if (m_fontEngine != 0)
|
|
|
|
m_fontEngine->ref.ref();
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|