summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/painting/qpainter.cpp52
-rw-r--r--src/gui/painting/qpainter.h3
-rw-r--r--src/gui/painting/qpainter_p.h3
-rw-r--r--src/gui/text/qfont.h1
-rw-r--r--src/gui/text/qfontengine.cpp18
-rw-r--r--src/gui/text/qfontengine_ft.cpp9
-rw-r--r--src/gui/text/qfontengine_mac.mm27
-rw-r--r--src/gui/text/qfontengine_p.h9
-rw-r--r--src/gui/text/qglyphs.cpp199
-rw-r--r--src/gui/text/qglyphs.h96
-rw-r--r--src/gui/text/qglyphs_p.h84
-rw-r--r--src/gui/text/qtextlayout.cpp136
-rw-r--r--src/gui/text/qtextlayout.h5
-rw-r--r--src/gui/text/text.pri7
-rw-r--r--tests/auto/qglyphs/qglyphs.pro5
-rw-r--r--tests/auto/qglyphs/test.ttfbin0 -> 3712 bytes
-rw-r--r--tests/auto/qglyphs/tst_qglyphs.cpp431
17 files changed, 1069 insertions, 16 deletions
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index dd3584af3c..cb56ac8828 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -61,6 +61,8 @@
#include "qstyle.h"
#include "qthread.h"
#include "qvarlengtharray.h"
+#include "qstatictext.h"
+#include "qglyphs.h"
#include <private/qfontengine_p.h>
#include <private/qpaintengine_p.h>
@@ -70,8 +72,8 @@
#include <private/qwidget_p.h>
#include <private/qpaintengine_raster_p.h>
#include <private/qmath_p.h>
-#include <qstatictext.h>
#include <private/qstatictext_p.h>
+#include <private/qglyphs_p.h>
QT_BEGIN_NAMESPACE
@@ -5704,16 +5706,47 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR
d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags);
}
+/*!
+ Draws the glyphs represented by \a glyphs at \a position. The \a position gives the
+ edge of the baseline for the string of glyphs. The glyphs will be retrieved from the font
+ selected on \a glyphs and at offsets given by the positions in \a glyphs.
+
+ \since 4.8
+
+ \sa QGlyphs::setFont(), QGlyphs::setPositions(), QGlyphs::setGlyphIndexes()
+*/
+void QPainter::drawGlyphs(const QPointF &position, const QGlyphs &glyphs)
+{
+ Q_D(QPainter);
+
+ QFont oldFont = d->state->font;
+ d->state->font = glyphs.font();
+
+ QVector<quint32> glyphIndexes = glyphs.glyphIndexes();
+ QVector<QPointF> glyphPositions = glyphs.positions();
+
+ int count = qMin(glyphIndexes.size(), glyphPositions.size());
+ QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count);
+ for (int i=0; i<count; ++i)
+ fixedPointPositions[i] = QFixedPoint::fromPointF(position + glyphPositions.at(i));
+
+ d->drawGlyphs(glyphIndexes.data(), fixedPointPositions.data(), count);
+
+ d->state->font = oldFont;
+}
void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, const QPointF *positionArray,
int glyphCount)
-{
+{
+ QVarLengthArray<QFixedPoint, 128> positions(glyphCount);
+ for (int i=0; i<glyphCount; ++i)
+ positions[i] = QFixedPoint::fromPointF(positionArray[i]);
+
QPainterPrivate *painter_d = QPainterPrivate::get(painter);
- painter_d->drawGlyphs(glyphArray, positionArray, glyphCount);
+ painter_d->drawGlyphs(const_cast<quint32 *>(glyphArray), positions.data(), glyphCount);
}
-void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray,
- int glyphCount)
+void QPainterPrivate::drawGlyphs(quint32 *glyphArray, QFixedPoint *positions, int glyphCount)
{
updateState(state);
@@ -5729,11 +5762,6 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit
fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
}
- QVarLengthArray<QFixedPoint, 128> positions;
- for (int i=0; i<glyphCount; ++i) {
- QFixedPoint fp = QFixedPoint::fromPointF(positionArray[i]);
- positions.append(fp);
- }
if (extended != 0) {
QStaticTextItem staticTextItem;
@@ -5742,7 +5770,7 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit
staticTextItem.fontEngine = fontEngine;
staticTextItem.numGlyphs = glyphCount;
staticTextItem.glyphs = reinterpret_cast<glyph_t *>(const_cast<glyph_t *>(glyphArray));
- staticTextItem.glyphPositions = positions.data();
+ staticTextItem.glyphPositions = positions;
extended->drawStaticTextItem(&staticTextItem);
} else {
@@ -5759,7 +5787,7 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit
textItem.glyphs.numGlyphs = glyphCount;
textItem.glyphs.glyphs = reinterpret_cast<HB_Glyph *>(const_cast<quint32 *>(glyphArray));
- textItem.glyphs.offsets = positions.data();
+ textItem.glyphs.offsets = positions;
textItem.glyphs.advances_x = advances.data();
textItem.glyphs.advances_y = advances.data();
textItem.glyphs.justifications = glyphJustifications.data();
diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h
index edfb67ecb1..85751a9f2d 100644
--- a/src/gui/painting/qpainter.h
+++ b/src/gui/painting/qpainter.h
@@ -79,6 +79,7 @@ class QTextItem;
class QMatrix;
class QTransform;
class QStaticText;
+class QGlyphs;
class QPainterPrivateDeleter;
@@ -396,6 +397,8 @@ public:
void setLayoutDirection(Qt::LayoutDirection direction);
Qt::LayoutDirection layoutDirection() const;
+ void drawGlyphs(const QPointF &position, const QGlyphs &glyphs);
+
void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText);
inline void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText);
inline void drawStaticText(int left, int top, const QStaticText &staticText);
diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h
index 9362dbe80c..a61f5cae58 100644
--- a/src/gui/painting/qpainter_p.h
+++ b/src/gui/painting/qpainter_p.h
@@ -70,6 +70,7 @@ QT_BEGIN_NAMESPACE
class QPaintEngine;
class QEmulationPaintEngine;
class QPaintEngineEx;
+struct QFixedPoint;
struct QTLWExtra;
@@ -228,7 +229,7 @@ public:
void draw_helper(const QPainterPath &path, DrawOperation operation = StrokeAndFillDraw);
void drawStretchedGradient(const QPainterPath &path, DrawOperation operation);
void drawOpaqueBackground(const QPainterPath &path, DrawOperation operation);
- void drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray, int glyphCount);
+ void drawGlyphs(quint32 *glyphArray, QFixedPoint *positionArray, int glyphCount);
void updateMatrix();
void updateInvMatrix();
diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h
index 6f624246b9..b0e405edd0 100644
--- a/src/gui/text/qfont.h
+++ b/src/gui/text/qfont.h
@@ -312,6 +312,7 @@ private:
friend class QPainterReplayer;
friend class QPaintBufferEngine;
friend class QCommandLinkButtonPrivate;
+ friend class QFontEngine;
#ifndef QT_NO_DATASTREAM
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QFont &);
diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp
index 194c5f3dd4..2d95baef1f 100644
--- a/src/gui/text/qfontengine.cpp
+++ b/src/gui/text/qfontengine.cpp
@@ -242,6 +242,24 @@ glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix
return metrics;
}
+QFont QFontEngine::createExplicitFont() const
+{
+ return createExplicitFontWithName(fontDef.family);
+}
+
+QFont QFontEngine::createExplicitFontWithName(const QString &familyName) const
+{
+ QFont font(familyName);
+ font.setStyleStrategy(QFont::NoFontMerging);
+ font.setWeight(fontDef.weight);
+ font.setItalic(fontDef.style == QFont::StyleItalic);
+ if (fontDef.pointSize < 0)
+ font.setPixelSize(fontDef.pixelSize);
+ else
+ font.setPointSizeF(fontDef.pointSize);
+ return font;
+}
+
QFixed QFontEngine::xHeight() const
{
QGlyphLayoutArray<8> glyphs;
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index 9056012b6c..737689365f 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -756,9 +756,18 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph
return g;
int load_flags = FT_LOAD_DEFAULT | default_load_flags;
+ int load_target = default_hint_style == HintLight
+ ? FT_LOAD_TARGET_LIGHT
+ : FT_LOAD_TARGET_NORMAL;
+
if (set->outline_drawing)
load_flags = FT_LOAD_NO_BITMAP;
+ if (default_hint_style == HintNone)
+ load_flags |= FT_LOAD_NO_HINTING;
+ else
+ load_flags |= load_target;
+
// apply our matrix to this, but note that the metrics will not be affected by this.
FT_Face face = lockFace();
FT_Matrix matrix = this->matrix;
diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm
index 3be6d284c1..c9b5655889 100644
--- a/src/gui/text/qfontengine_mac.mm
+++ b/src/gui/text/qfontengine_mac.mm
@@ -607,6 +607,12 @@ void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *position
}
}
+QFont QCoreTextFontEngine::createExplicitFont() const
+{
+ QString familyName = QCFString::toQString(CTFontCopyFamilyName(ctfont));
+ return createExplicitFontWithName(familyName);
+}
+
QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa)
{
const glyph_metrics_t br = boundingBox(glyph);
@@ -1542,6 +1548,27 @@ static void addGlyphsToPathHelper(ATSUStyle style, glyph_t *glyphs, QFixedPoint
DisposeATSCubicClosePathUPP(closePath);
}
+QFont QFontEngineMac::createExplicitFont() const
+{
+ FMFont fmFont = FMGetFontFromATSFontRef(fontID);
+
+ FMFontFamily fmFamily;
+ FMFontStyle fmStyle;
+ QString familyName;
+ if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
+ ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
+ QCFString cfFamilyName;;
+ ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
+ familyName = cfFamilyName;
+ } else {
+ QCFString cfFontName;
+ ATSFontGetName(fontID, kATSOptionFlagsDefault, &cfFontName);
+ familyName = cfFontName;
+ }
+
+ return createExplicitFontWithName(familyName);
+}
+
void QFontEngineMac::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, QPainterPath *path,
QTextItem::RenderFlags)
{
diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h
index 922acfb34c..d29ef456ae 100644
--- a/src/gui/text/qfontengine_p.h
+++ b/src/gui/text/qfontengine_p.h
@@ -173,6 +173,10 @@ public:
#endif
virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
QPainterPath *path, QTextItem::RenderFlags flags);
+
+ /* Creates a QFont object to represent this particular QFontEngine */
+ virtual QFont createExplicitFont() const;
+
void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags,
QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions);
@@ -252,6 +256,7 @@ public:
int glyphFormat;
protected:
+ QFont createExplicitFontWithName(const QString &familyName) const;
static const QVector<QRgb> &grayPalette();
private:
@@ -454,7 +459,7 @@ public:
virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t);
virtual qreal minRightBearing() const;
virtual qreal minLeftBearing() const;
-
+ virtual QFont createExplicitFont() const;
private:
QImage imageForGlyph(glyph_t glyph, int margin, bool colorful);
@@ -520,6 +525,8 @@ public:
virtual qreal maxCharWidth() const;
virtual QFixed averageCharWidth() const;
+ virtual QFont createExplicitFont() const;
+
virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
QPainterPath *path, QTextItem::RenderFlags);
diff --git a/src/gui/text/qglyphs.cpp b/src/gui/text/qglyphs.cpp
new file mode 100644
index 0000000000..847646e4ba
--- /dev/null
+++ b/src/gui/text/qglyphs.cpp
@@ -0,0 +1,199 @@
+#include "qglyphs.h"
+#include "qglyphs_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QGlyphs
+ \brief the QGlyphs class provides direct access to the internal glyphs in a font
+ \since 4.8
+
+ \ingroup text
+ \mainclass
+
+ When Qt displays a string of text encoded in Unicode, it will first convert the Unicode points
+ into a list of glyph indexes and a list of positions based on one or more fonts. The Unicode
+ representation of the text and the QFont object will in this case serve as a convenient
+ abstraction that hides the details of what actually takes place when displaying the text
+ on-screen. For instance, by the time the text actually reaches the screen, it may be represented
+ by a set of fonts in addition to the one specified by the user, e.g. in case the originally
+ selected font did not support all the writing systems contained in the text.
+
+ Under certain circumstances, it can be useful as an application developer to have more low-level
+ control over which glyphs in a specific font are drawn to the screen. This could for instance
+ be the case in applications that use an external font engine and text shaper together with Qt.
+ QGlyphs provides an interface to the raw data needed to get text on the screen. It
+ contains a list of glyph indexes, a position for each glyph and a font.
+
+ It is the user's responsibility to ensure that the selected font actually contains the
+ provided glyph indexes.
+
+ QTextLayout::glyphs() can be used to convert unicode encoded text into a list of QGlyphs
+ objects, and QPainter::drawGlyphs() can be used to draw the glyphs.
+*/
+
+
+/*!
+ Constructs an empty QGlyphs object.
+*/
+QGlyphs::QGlyphs() : d(new QGlyphsPrivate)
+{
+}
+
+/*!
+ Constructs a QGlyphs object which is a copy of \a other.
+*/
+QGlyphs::QGlyphs(const QGlyphs &other)
+{
+ d = other.d;
+}
+
+/*!
+ Destroys the QGlyphs.
+*/
+QGlyphs::~QGlyphs()
+{
+ // Required for QExplicitlySharedDataPointer
+}
+
+/*!
+ \internal
+*/
+void QGlyphs::detach()
+{
+ if (d->ref != 1)
+ d.detach();
+}
+
+/*!
+ Assigns \a other to this QGlyphs object.
+*/
+QGlyphs &QGlyphs::operator=(const QGlyphs &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Compares \a other to this QGlyphs object. Returns true if the list of glyph indexes,
+ the list of positions and the font are all equal, otherwise returns false.
+*/
+bool QGlyphs::operator==(const QGlyphs &other) const
+{
+ return ((d == other.d)
+ || (d->glyphIndexes == other.d->glyphIndexes
+ && d->glyphPositions == other.d->glyphPositions
+ && d->font == other.d->font));
+}
+
+/*!
+ Compares \a other to this QGlyphs object. Returns true if any of the list of glyph
+ indexes, the list of positions or the font are different, otherwise returns false.
+*/
+bool QGlyphs::operator!=(const QGlyphs &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ \internal
+
+ Adds together the lists of glyph indexes and positions in \a other and this QGlyphs
+ object and returns the result. The font in the returned QGlyphs will be the same as in
+ this QGlyphs object.
+*/
+QGlyphs QGlyphs::operator+(const QGlyphs &other) const
+{
+ QGlyphs ret(*this);
+ ret += other;
+ return ret;
+}
+
+/*!
+ \internal
+
+ Appends the glyph indexes and positions in \a other to this QGlyphs object and returns
+ a reference to the current object.
+*/
+QGlyphs &QGlyphs::operator+=(const QGlyphs &other)
+{
+ detach();
+
+ d->glyphIndexes += other.d->glyphIndexes;
+ d->glyphPositions += other.d->glyphPositions;
+
+ return *this;
+}
+
+/*!
+ Returns the font selected for this QGlyphs object.
+
+ \sa setFont()
+*/
+QFont QGlyphs::font() const
+{
+ return d->font;
+}
+
+/*!
+ Sets the font in which to look up the glyph indexes to \a font. This must be an explicitly
+ resolvable font which defines glyphs for the specified glyph indexes.
+
+ \sa font(), setGlyphIndexes()
+*/
+void QGlyphs::setFont(const QFont &font)
+{
+ detach();
+ d->font = font;
+}
+
+/*!
+ Returns the glyph indexes for this QGlyphs object.
+
+ \sa setGlyphIndexes(), setPositions()
+*/
+QVector<quint32> QGlyphs::glyphIndexes() const
+{
+ return d->glyphIndexes;
+}
+
+/*!
+ Set the glyph indexes for this QGlyphs object to \a glyphIndexes. The glyph indexes must
+ be valid for the selected font.
+*/
+void QGlyphs::setGlyphIndexes(const QVector<quint32> &glyphIndexes)
+{
+ detach();
+ d->glyphIndexes = glyphIndexes;
+}
+
+/*!
+ Returns the position of the edge of the baseline for each glyph in this set of glyph indexes.
+*/
+QVector<QPointF> QGlyphs::positions() const
+{
+ return d->glyphPositions;
+}
+
+/*!
+ Sets the positions of the edge of the baseline for each glyph in this set of glyph indexes to
+ \a positions.
+*/
+void QGlyphs::setPositions(const QVector<QPointF> &positions)
+{
+ detach();
+ d->glyphPositions = positions;
+}
+
+/*!
+ Clears all data in the QGlyphs object.
+*/
+void QGlyphs::clear()
+{
+ detach();
+ d->glyphPositions = QVector<QPointF>();
+ d->glyphIndexes = QVector<quint32>();
+ d->font = QFont();
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/text/qglyphs.h b/src/gui/text/qglyphs.h
new file mode 100644
index 0000000000..282ecb47c5
--- /dev/null
+++ b/src/gui/text/qglyphs.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLYPHS_H
+#define QGLYPHS_H
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qpoint.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Gui)
+
+class QGlyphsPrivate;
+class Q_GUI_EXPORT QGlyphs
+{
+public:
+ QGlyphs();
+ QGlyphs(const QGlyphs &other);
+ ~QGlyphs();
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QVector<quint32> glyphIndexes() const;
+ void setGlyphIndexes(const QVector<quint32> &glyphIndexes);
+
+ QVector<QPointF> positions() const;
+ void setPositions(const QVector<QPointF> &positions);
+
+ void clear();
+
+ QGlyphs &operator=(const QGlyphs &other);
+ bool operator==(const QGlyphs &other) const;
+ bool operator!=(const QGlyphs &other) const;
+
+private:
+ friend class QGlyphsPrivate;
+ friend class QTextLine;
+
+ QGlyphs operator+(const QGlyphs &other) const;
+ QGlyphs &operator+=(const QGlyphs &other);
+
+ void detach();
+ QExplicitlySharedDataPointer<QGlyphsPrivate> d;
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif // QGLYPHS_H
diff --git a/src/gui/text/qglyphs_p.h b/src/gui/text/qglyphs_p.h
new file mode 100644
index 0000000000..c39f5d0e56
--- /dev/null
+++ b/src/gui/text/qglyphs_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGLYPHS_P_H
+#define QGLYPHS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of internal files. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qfont.h>
+#include "qglyphs.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QGlyphsPrivate: public QSharedData
+{
+public:
+ QGlyphsPrivate()
+ {
+ }
+
+ QGlyphsPrivate(const QGlyphsPrivate &other)
+ : QSharedData(other), glyphIndexes(other.glyphIndexes), glyphPositions(other.glyphPositions), font(other.font)
+ {
+ }
+
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> glyphPositions;
+ QFont font;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGLYPHS_P_H
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 412c909639..b581969f97 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -52,6 +52,8 @@
#include "qtextformat_p.h"
#include "qstyleoption.h"
#include "qpainterpath.h"
+#include "qglyphs.h"
+#include "qglyphs_p.h"
#include <limits.h>
#include <qdebug.h>
@@ -894,6 +896,7 @@ qreal QTextLayout::maximumWidth() const
return d->maxWidth.toReal();
}
+
/*!
\internal
*/
@@ -1106,6 +1109,24 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
return clip.isValid() ? (rect & clip) : rect;
}
+
+/*!
+ Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
+ expensive function, and should not be called in a time sensitive context.
+
+ \since 4.8
+
+ \sa draw(), QPainter::drawGlyphs()
+*/
+QList<QGlyphs> QTextLayout::glyphs() const
+{
+ QList<QGlyphs> glyphs;
+ for (int i=0; i<d->lines.size(); ++i)
+ glyphs += QTextLine(i, d).glyphs();
+
+ return glyphs;
+}
+
/*!
Draws the whole layout on the painter \a p at the position specified by
\a pos.
@@ -2135,6 +2156,121 @@ static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const Q
}
+namespace {
+ struct GlyphInfo
+ {
+ GlyphInfo(const QGlyphLayout &layout, const QPointF &position)
+ : glyphLayout(layout), itemPosition(position)
+ {
+ }
+
+ QGlyphLayout glyphLayout;
+ QPointF itemPosition;
+ };
+}
+
+/*!
+ \internal
+
+ Returns the glyph indexes and positions for all glyphs in this QTextLine.
+
+ \since 4.8
+
+ \sa QTextLayout::glyphs()
+*/
+QList<QGlyphs> QTextLine::glyphs() const
+{
+ const QScriptLine &line = eng->lines[i];
+
+ if (line.length == 0)
+ return QList<QGlyphs>();
+
+ QHash<QFontEngine *, GlyphInfo> glyphLayoutHash;
+
+ QTextLineItemIterator iterator(eng, i);
+ qreal y = line.y.toReal() + line.base().toReal();
+ while (!iterator.atEnd()) {
+ QScriptItem &si = iterator.next();
+ QPointF pos(iterator.x.toReal(), y);
+
+ QFont font = eng->font(si);
+ QGlyphLayout glyphLayout = eng->shapedGlyphs(&si).mid(iterator.glyphsStart,
+ iterator.glyphsEnd - iterator.glyphsStart);
+
+ if (glyphLayout.numGlyphs > 0) {
+ QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script);
+ if (mainFontEngine->type() == QFontEngine::Multi) {
+ QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
+ int start = 0;
+ int end;
+ int which = glyphLayout.glyphs[0] >> 24;
+ for (end = 0; end < glyphLayout.numGlyphs; ++end) {
+ const int e = glyphLayout.glyphs[end] >> 24;
+ if (e == which)
+ continue;
+
+ QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
+ glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
+ GlyphInfo(subLayout, pos));
+
+ start = end;
+ which = e;
+ }
+
+ QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
+ glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
+ GlyphInfo(subLayout, pos));
+
+ } else {
+ glyphLayoutHash.insertMulti(mainFontEngine,
+ GlyphInfo(glyphLayout, pos));
+ }
+ }
+ }
+
+ QHash<QFontEngine *, QGlyphs> glyphsHash;
+
+ QList<QFontEngine *> keys = glyphLayoutHash.uniqueKeys();
+ for (int i=0; i<keys.size(); ++i) {
+ QFontEngine *fontEngine = keys.at(i);
+
+ // Make a font for this particular engine
+ QFont font = fontEngine->createExplicitFont();
+
+ QList<GlyphInfo> glyphLayouts = glyphLayoutHash.values(fontEngine);
+ for (int j=0; j<glyphLayouts.size(); ++j) {
+ const QPointF &pos = glyphLayouts.at(j).itemPosition;
+ const QGlyphLayout &glyphLayout = glyphLayouts.at(j).glyphLayout;
+
+ QVarLengthArray<glyph_t> glyphsArray;
+ QVarLengthArray<QFixedPoint> positionsArray;
+
+ fontEngine->getGlyphPositions(glyphLayout, QTransform(), 0, glyphsArray, positionsArray);
+ Q_ASSERT(glyphsArray.size() == positionsArray.size());
+
+ QVector<quint32> glyphs;
+ QVector<QPointF> positions;
+ for (int i=0; i<glyphsArray.size(); ++i) {
+ glyphs.append(glyphsArray.at(i) & 0xffffff);
+ positions.append(positionsArray.at(i).toPointF() + pos);
+ }
+
+ QGlyphs glyphIndexes;
+ glyphIndexes.setGlyphIndexes(glyphs);
+ glyphIndexes.setPositions(positions);
+
+ if (!glyphsHash.contains(fontEngine))
+ glyphsHash.insert(fontEngine, QGlyphs());
+
+ QGlyphs &target = glyphsHash[fontEngine];
+ target += glyphIndexes;
+ target.setFont(font);
+ }
+ }
+
+ return glyphsHash.values();
+}
+
/*!
\fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
index 8c93ed65b6..f6aaf22d0f 100644
--- a/src/gui/text/qtextlayout.h
+++ b/src/gui/text/qtextlayout.h
@@ -49,6 +49,7 @@
#include <QtCore/qobject.h>
#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
+#include <QtGui/qglyphs.h>
QT_BEGIN_HEADER
@@ -166,6 +167,8 @@ public:
qreal minimumWidth() const;
qreal maximumWidth() const;
+ QList<QGlyphs> glyphs() const;
+
QTextEngine *engine() const { return d; }
void setFlags(int flags);
private:
@@ -236,6 +239,8 @@ public:
private:
QTextLine(int line, QTextEngine *e) : i(line), eng(e) {}
void layout_helper(int numGlyphs);
+ QList<QGlyphs> glyphs() const;
+
friend class QTextLayout;
int i;
QTextEngine *eng;
diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri
index 9ec314299c..4e1e9fa029 100644
--- a/src/gui/text/text.pri
+++ b/src/gui/text/text.pri
@@ -39,7 +39,9 @@ HEADERS += \
text/qzipwriter_p.h \
text/qtextodfwriter_p.h \
text/qstatictext_p.h \
- text/qstatictext.h
+ text/qstatictext.h \
+ text/qglyphs.h \
+ text/qglyphs_p.h
SOURCES += \
text/qfont.cpp \
@@ -69,7 +71,8 @@ SOURCES += \
text/qcssparser.cpp \
text/qzip.cpp \
text/qtextodfwriter.cpp \
- text/qstatictext.cpp
+ text/qstatictext.cpp \
+ text/qglyphs.cpp
win32 {
SOURCES += \
diff --git a/tests/auto/qglyphs/qglyphs.pro b/tests/auto/qglyphs/qglyphs.pro
new file mode 100644
index 0000000000..70920f08a3
--- /dev/null
+++ b/tests/auto/qglyphs/qglyphs.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+QT = core gui
+
+SOURCES += \
+ tst_qglyphs.cpp
diff --git a/tests/auto/qglyphs/test.ttf b/tests/auto/qglyphs/test.ttf
new file mode 100644
index 0000000000..9043a576ef
--- /dev/null
+++ b/tests/auto/qglyphs/test.ttf
Binary files differ
diff --git a/tests/auto/qglyphs/tst_qglyphs.cpp b/tests/auto/qglyphs/tst_qglyphs.cpp
new file mode 100644
index 0000000000..c906f10049
--- /dev/null
+++ b/tests/auto/qglyphs/tst_qglyphs.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <qglyphs.h>
+#include <qpainter.h>
+#include <qtextlayout.h>
+#include <qfontdatabase.h>
+
+//#define DEBUG_SAVE_IMAGE
+
+class tst_QGlyphs: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void constructionAndDestruction();
+ void copyConstructor();
+ void assignment();
+ void equalsOperator_data();
+ void equalsOperator();
+ void textLayoutGlyphIndexes();
+ void drawExistingGlyphs();
+ void drawNonExistentGlyphs();
+ void drawMultiScriptText1();
+ void drawMultiScriptText2();
+ void detach();
+
+private:
+ int m_testFontId;
+ QFont m_testFont;
+};
+
+Q_DECLARE_METATYPE(QGlyphs);
+
+void tst_QGlyphs::initTestCase()
+{
+ m_testFontId = QFontDatabase::addApplicationFont("test.ttf");
+ QVERIFY(m_testFontId >= 0);
+
+ m_testFont = QFont("QtsSpecialTestFont");
+
+ QCOMPARE(QFontInfo(m_testFont).family(), QString::fromLatin1("QtsSpecialTestFont"));
+}
+
+void tst_QGlyphs::cleanupTestCase()
+{
+ QFontDatabase::removeApplicationFont(m_testFontId);
+}
+
+void tst_QGlyphs::constructionAndDestruction()
+{
+ QGlyphs glyphIndexes;
+}
+
+static QGlyphs make_dummy_indexes()
+{
+ QGlyphs glyphs;
+
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+ QFont font;
+ font.setPointSize(18);
+
+ glyphIndexes.append(1);
+ glyphIndexes.append(2);
+ glyphIndexes.append(3);
+
+ positions.append(QPointF(1, 2));
+ positions.append(QPointF(3, 4));
+ positions.append(QPointF(5, 6));
+
+ glyphs.setFont(font);
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+
+ return glyphs;
+}
+
+static QGlyphs make_dummy_indexes2()
+{
+ QGlyphs glyphs;
+
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+ QFont font;
+ font.setPointSize(26);
+
+ glyphIndexes.append(4);
+ glyphIndexes.append(5);
+ glyphIndexes.append(6);
+
+ positions.append(QPointF(7, 8));
+ positions.append(QPointF(9, 10));
+ positions.append(QPointF(11, 12));
+
+ glyphs.setFont(font);
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+
+ return glyphs;
+}
+
+
+void tst_QGlyphs::copyConstructor()
+{
+ QGlyphs glyphs;
+
+ {
+ QVector<quint32> glyphIndexes;
+ QVector<QPointF> positions;
+ QFont font;
+ font.setPointSize(18);
+
+ glyphIndexes.append(1);
+ glyphIndexes.append(2);
+ glyphIndexes.append(3);
+
+ positions.append(QPointF(1, 2));
+ positions.append(QPointF(3, 4));
+ positions.append(QPointF(5, 6));
+
+ glyphs.setFont(font);
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(positions);
+ }
+
+ QGlyphs otherGlyphs(glyphs);
+ QCOMPARE(otherGlyphs.font(), glyphs.font());
+ QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
+ QCOMPARE(glyphs.positions(), otherGlyphs.positions());
+}
+
+void tst_QGlyphs::assignment()
+{
+ QGlyphs glyphs(make_dummy_indexes());
+
+ QGlyphs otherGlyphs = glyphs;
+ QCOMPARE(otherGlyphs.font(), glyphs.font());
+ QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
+ QCOMPARE(glyphs.positions(), otherGlyphs.positions());
+}
+
+void tst_QGlyphs::equalsOperator_data()
+{
+ QTest::addColumn<QGlyphs>("one");
+ QTest::addColumn<QGlyphs>("two");
+ QTest::addColumn<bool>("equals");
+
+ QGlyphs one(make_dummy_indexes());
+ QGlyphs two(make_dummy_indexes());
+
+ QTest::newRow("Identical") << one << two << true;
+
+ {
+ QGlyphs busted(two);
+
+ QVector<QPointF> positions = busted.positions();
+ positions[2] += QPointF(1, 1);
+ busted.setPositions(positions);
+
+ QTest::newRow("Different positions") << one << busted << false;
+ }
+
+ {
+ QGlyphs busted(two);
+ QFont font = busted.font();
+ font.setPointSize(font.pointSize() * 2);
+ busted.setFont(font);
+
+ QTest::newRow("Different fonts") << one << busted << false;
+ }
+
+ {
+ QGlyphs busted(two);
+
+ QVector<quint32> glyphIndexes = busted.glyphIndexes();
+ glyphIndexes[2] += 1;
+ busted.setGlyphIndexes(glyphIndexes);
+
+ QTest::newRow("Different glyph indexes") << one << busted << false;
+ }
+
+}
+
+void tst_QGlyphs::equalsOperator()
+{
+ QFETCH(QGlyphs, one);
+ QFETCH(QGlyphs, two);
+ QFETCH(bool, equals);
+
+ QCOMPARE(one == two, equals);
+ QCOMPARE(one != two, !equals);
+}
+
+
+void tst_QGlyphs::textLayoutGlyphIndexes()
+{
+ QString s;
+ s.append(QLatin1Char('A'));
+ s.append(QChar(0xe000));
+
+ QTextLayout layout(s);
+ layout.setFont(m_testFont);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ QList<QGlyphs> listOfGlyphs = layout.glyphs();
+ QCOMPARE(listOfGlyphs.size(), 1);
+
+ QGlyphs glyphs = listOfGlyphs.at(0);
+
+ QCOMPARE(glyphs.glyphIndexes().size(), 2);
+ QCOMPARE(glyphs.glyphIndexes().at(0), quint32(2));
+ QCOMPARE(glyphs.glyphIndexes().at(1), quint32(1));
+}
+
+void tst_QGlyphs::drawExistingGlyphs()
+{
+ QImage textLayoutDraw(1000, 1000, QImage::Format_ARGB32);
+ QImage drawGlyphs(1000, 1000, QImage::Format_ARGB32);
+
+ textLayoutDraw.fill(0xffffffff);
+ drawGlyphs.fill(0xffffffff);
+
+ QString s;
+ s.append(QLatin1Char('A'));
+ s.append(QChar(0xe000));
+
+ QTextLayout layout(s);
+ layout.setFont(m_testFont);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphs glyphs = layout.glyphs().size() > 0
+ ? layout.glyphs().at(0)
+ : QGlyphs();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawExistingGlyphs_textLayoutDraw.png");
+ drawGlyphs.save("drawExistingGlyphs_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphs::drawNonExistentGlyphs()
+{
+ QVector<quint32> glyphIndexes;
+ glyphIndexes.append(3);
+
+ QVector<QPointF> glyphPositions;
+ glyphPositions.append(QPointF(0, 0));
+
+ QGlyphs glyphs;
+ glyphs.setGlyphIndexes(glyphIndexes);
+ glyphs.setPositions(glyphPositions);
+ glyphs.setFont(m_testFont);
+
+ QImage image(1000, 1000, QImage::Format_ARGB32);
+ image.fill(0);
+
+ QImage imageBefore = image;
+ {
+ QPainter p(&image);
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ image.save("drawNonExistentGlyphs.png");
+#endif
+
+ QCOMPARE(image, imageBefore); // Should be unchanged
+}
+
+void tst_QGlyphs::drawMultiScriptText1()
+{
+ QString text;
+ text += QChar(0x3131); // Hangul, Kiyeok
+
+ QTextLayout textLayout(text);
+ textLayout.beginLayout();
+ textLayout.createLine();
+ textLayout.endLayout();
+
+ QImage textLayoutDraw(1000, 1000, QImage::Format_ARGB32);
+ textLayoutDraw.fill(0xffffffff);
+
+ QImage drawGlyphs(1000, 1000, QImage::Format_ARGB32);
+ drawGlyphs.fill(0xffffffff);
+
+ QList<QGlyphs> glyphsList = textLayout.glyphs();
+ QCOMPARE(glyphsList.size(), 1);
+
+ {
+ QPainter p(&textLayoutDraw);
+ textLayout.draw(&p, QPointF(50, 50));
+ }
+
+ {
+ QPainter p(&drawGlyphs);
+ foreach (QGlyphs glyphs, glyphsList)
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawMultiScriptText1_textLayoutDraw.png");
+ drawGlyphs.save("drawMultiScriptText1_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(drawGlyphs, textLayoutDraw);
+}
+
+
+void tst_QGlyphs::drawMultiScriptText2()
+{
+#if defined(Q_WS_MAC)
+ QSKIP("Unstable because of QTBUG-11145", SkipAll);
+#endif
+
+ QString text;
+ text += QChar(0x0621); // Arabic, Hamza
+ text += QChar(0x3131); // Hangul, Kiyeok
+
+ QTextLayout textLayout(text);
+ textLayout.beginLayout();
+ textLayout.createLine();
+ textLayout.endLayout();
+
+ QImage textLayoutDraw(1000, 1000, QImage::Format_ARGB32);
+ textLayoutDraw.fill(0xffffffff);
+
+ QImage drawGlyphs(1000, 1000, QImage::Format_ARGB32);
+ drawGlyphs.fill(0xffffffff);
+
+ QList<QGlyphs> glyphsList = textLayout.glyphs();
+ QCOMPARE(glyphsList.size(), 2);
+
+ {
+ QPainter p(&textLayoutDraw);
+ textLayout.draw(&p, QPointF(50, 50));
+ }
+
+ {
+ QPainter p(&drawGlyphs);
+ foreach (QGlyphs glyphs, glyphsList)
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawMultiScriptText2_textLayoutDraw.png");
+ drawGlyphs.save("drawMultiScriptText2_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(drawGlyphs, textLayoutDraw);
+}
+
+void tst_QGlyphs::detach()
+{
+ QGlyphs glyphs;
+
+ glyphs.setGlyphIndexes(QVector<quint32>() << 1 << 2 << 3);
+
+ QGlyphs otherGlyphs;
+ otherGlyphs = glyphs;
+
+ QCOMPARE(otherGlyphs.glyphIndexes(), glyphs.glyphIndexes());
+
+ otherGlyphs.setGlyphIndexes(QVector<quint32>() << 4 << 5 << 6);
+
+ QCOMPARE(otherGlyphs.glyphIndexes(), QVector<quint32>() << 4 << 5 << 6);
+ QCOMPARE(glyphs.glyphIndexes(), QVector<quint32>() << 1 << 2 << 3);
+}
+
+QTEST_MAIN(tst_QGlyphs)
+#include "tst_qglyphs.moc"
+