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