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 | |
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 = nullptr) : 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 | */ |
156 | QStaticText::QStaticText() |
157 | : data(new QStaticTextPrivate) |
158 | { |
159 | } |
160 | |
161 | /*! |
162 | Constructs a QStaticText object with the given \a text. |
163 | */ |
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 | */ |
174 | QStaticText::QStaticText(const QStaticText &other) |
175 | { |
176 | data = other.data; |
177 | } |
178 | |
179 | /*! |
180 | Destroys the QStaticText. |
181 | */ |
182 | QStaticText::~QStaticText() |
183 | { |
184 | Q_ASSERT(!data || data->ref.loadRelaxed() >= 1); |
185 | } |
186 | |
187 | /*! |
188 | \internal |
189 | */ |
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::setWorldTransform() |
211 | */ |
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 | */ |
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 | */ |
241 | 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 | */ |
253 | 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 | */ |
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 | */ |
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 | */ |
295 | 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 | */ |
305 | 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 | */ |
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 | */ |
338 | 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 | */ |
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 | */ |
358 | 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 | */ |
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 | */ |
388 | 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 | */ |
398 | QSizeF QStaticText::size() const |
399 | { |
400 | if (data->needsRelayout) |
401 | data->init(); |
402 | return data->actualSize; |
403 | } |
404 | |
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 | |
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 | |
420 | QStaticTextPrivate::~QStaticTextPrivate() |
421 | { |
422 | delete[] items; |
423 | delete[] glyphPool; |
424 | delete[] positionPool; |
425 | } |
426 | |
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: |
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 | |
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 | |
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 | |
488 | virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) override |
489 | { |
490 | /* intentionally empty */ |
491 | } |
492 | |
493 | virtual bool begin(QPaintDevice *) override { return true; } |
494 | virtual bool end() override { return true; } |
495 | virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) override {} |
496 | virtual Type type() const override |
497 | { |
498 | return User; |
499 | } |
500 | |
501 | QList<QStaticTextItem> items() const |
502 | { |
503 | return m_items; |
504 | } |
505 | |
506 | QList<QFixedPoint> positions() const |
507 | { |
508 | return m_positions; |
509 | } |
510 | |
511 | QList<glyph_t> glyphs() const |
512 | { |
513 | return m_glyphs; |
514 | } |
515 | |
516 | private: |
517 | QList<QStaticTextItem> m_items; |
518 | QList<QFixedPoint> m_positions; |
519 | QList<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: |
530 | DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) |
531 | { |
532 | m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, |
533 | useBackendOptimizations); |
534 | } |
535 | |
536 | ~DrawTextItemDevice() |
537 | { |
538 | delete m_paintEngine; |
539 | } |
540 | |
541 | 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 | |
578 | virtual QPaintEngine *paintEngine() const override |
579 | { |
580 | return m_paintEngine; |
581 | } |
582 | |
583 | QList<glyph_t> glyphs() const |
584 | { |
585 | return m_paintEngine->glyphs(); |
586 | } |
587 | |
588 | QList<QFixedPoint> positions() const |
589 | { |
590 | return m_paintEngine->positions(); |
591 | } |
592 | |
593 | QList<QStaticTextItem> items() const |
594 | { |
595 | return m_paintEngine->items(); |
596 | } |
597 | |
598 | private: |
599 | DrawTextItemRecorder *m_paintEngine; |
600 | }; |
601 | } |
602 | |
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 | |
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 | QList<QStaticTextItem> deviceItems = device.items(); |
688 | QList<QFixedPoint> positions = device.positions(); |
689 | QList<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 | |