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