1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qtextlayout.h"
43 #include "qtextengine_p.h"
48 #include <qvarlengtharray.h>
49 #include <qtextformat.h>
50 #include <qabstracttextdocumentlayout.h>
51 #include "qtextdocument_p.h"
52 #include "qtextformat_p.h"
53 #include "qpainterpath.h"
54 #include "qglyphrun.h"
55 #include "qglyphrun_p.h"
57 #include "qrawfont_p.h"
62 #include "qfontengine_p.h"
63 #include <private/qpainter_p.h>
67 #define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
68 #define SuppressText 0x5012
69 #define SuppressBackground 0x513
72 \class QTextLayout::FormatRange
75 \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
76 for a specified area in the text layout's content.
79 \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
83 \variable QTextLayout::FormatRange::start
84 Specifies the beginning of the format range within the text layout's text.
88 \variable QTextLayout::FormatRange::length
89 Specifies the numer of characters the format range spans.
93 \variable QTextLayout::FormatRange::format
94 Specifies the format to apply.
98 \class QTextInlineObject
101 \brief The QTextInlineObject class represents an inline object in
105 \ingroup richtext-processing
107 This class is only used if the text layout is used to lay out
108 parts of a QTextDocument.
110 The inline object has various attributes that can be set, for
111 example using, setWidth(), setAscent(), and setDescent(). The
112 rectangle it occupies is given by rect(), and its direction by
113 isRightToLeft(). Its position in the text layout is given by at(),
114 and its format is given by format().
118 \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
120 Creates a new inline object for the item at position \a i in the
125 \fn QTextInlineObject::QTextInlineObject()
131 \fn bool QTextInlineObject::isValid() const
133 Returns true if this inline object is valid; otherwise returns
138 Returns the inline object's rectangle.
140 \sa ascent(), descent(), width()
142 QRectF QTextInlineObject::rect() const
144 QScriptItem& si = eng->layoutData->items[itm];
145 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
149 Returns the inline object's width.
151 \sa ascent(), descent(), rect()
153 qreal QTextInlineObject::width() const
155 return eng->layoutData->items[itm].width.toReal();
159 Returns the inline object's ascent.
161 \sa descent(), width(), rect()
163 qreal QTextInlineObject::ascent() const
165 return eng->layoutData->items[itm].ascent.toReal();
169 Returns the inline object's descent.
171 \sa ascent(), width(), rect()
173 qreal QTextInlineObject::descent() const
175 return eng->layoutData->items[itm].descent.toReal();
179 Returns the inline object's total height. This is equal to
180 ascent() + descent() + 1.
182 \sa ascent(), descent(), width(), rect()
184 qreal QTextInlineObject::height() const
186 return eng->layoutData->items[itm].height().toReal();
190 Sets the inline object's width to \a w.
192 \sa width(), ascent(), descent(), rect()
194 void QTextInlineObject::setWidth(qreal w)
196 eng->layoutData->items[itm].width = QFixed::fromReal(w);
200 Sets the inline object's ascent to \a a.
202 \sa ascent(), setDescent(), width(), rect()
204 void QTextInlineObject::setAscent(qreal a)
206 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
210 Sets the inline object's decent to \a d.
212 \sa descent(), setAscent(), width(), rect()
214 void QTextInlineObject::setDescent(qreal d)
216 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
220 The position of the inline object within the text layout.
222 int QTextInlineObject::textPosition() const
224 return eng->layoutData->items[itm].position;
228 Returns an integer describing the format of the inline object
229 within the text layout.
231 int QTextInlineObject::formatIndex() const
233 return eng->formatIndex(&eng->layoutData->items[itm]);
237 Returns format of the inline object within the text layout.
239 QTextFormat QTextInlineObject::format() const
241 if (!eng->block.docHandle())
242 return QTextFormat();
243 return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
247 Returns if the object should be laid out right-to-left or left-to-right.
249 Qt::LayoutDirection QTextInlineObject::textDirection() const
251 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
258 \brief The QTextLayout class is used to lay out and render text.
261 \ingroup richtext-processing
263 It offers many features expected from a modern text layout
264 engine, including Unicode compliant rendering, line breaking and
265 handling of cursor positioning. It can also produce and render
266 device independent layout, something that is important for WYSIWYG
269 The class has a rather low level API and unless you intend to
270 implement your own text rendering for some specialized widget, you
271 probably won't need to use it directly.
273 QTextLayout can be used with both plain and rich text.
275 QTextLayout can be used to create a sequence of QTextLine
276 instances with given widths and can position them independently
277 on the screen. Once the layout is done, these lines can be drawn
280 The text to be laid out can be provided in the constructor or set with
283 The layout can be seen as a sequence of QTextLine objects; use createLine()
284 to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
287 Here is a code snippet that demonstrates the layout phase:
288 \snippet code/src_gui_text_qtextlayout.cpp 0
290 The text can then be rendered by calling the layout's draw() function:
291 \snippet code/src_gui_text_qtextlayout.cpp 1
293 For a given position in the text you can find a valid cursor position with
294 isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
296 The QTextLayout itself can be positioned with setPosition(); it has a
297 boundingRect(), and a minimumWidth() and a maximumWidth().
303 \enum QTextLayout::CursorMode
305 \value SkipCharacters
310 \fn QTextEngine *QTextLayout::engine() const
313 Returns the text engine used to render the text layout.
317 Constructs an empty text layout.
321 QTextLayout::QTextLayout()
322 { d = new QTextEngine(); }
325 Constructs a text layout to lay out the given \a text.
327 QTextLayout::QTextLayout(const QString& text)
329 d = new QTextEngine();
334 Constructs a text layout to lay out the given \a text with the specified
337 All the metric and layout calculations will be done in terms of
338 the paint device, \a paintdevice. If \a paintdevice is 0 the
339 calculations will be done in screen metrics.
341 QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
345 f = QFont(font, paintdevice);
346 d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f);
351 Constructs a text layout to lay out the given \a block.
353 QTextLayout::QTextLayout(const QTextBlock &block)
355 d = new QTextEngine();
360 Destructs the layout.
362 QTextLayout::~QTextLayout()
368 #ifndef QT_NO_RAWFONT
371 Sets a raw font, to be used with QTextLayout::glyphRuns.
372 Note that this only supports the needs of WebKit.
373 Use of this function with e.g. QTextLayout::draw will result
374 in undefined behaviour.
376 void QTextLayout::setRawFont(const QRawFont &rawFont)
378 d->rawFont = rawFont;
379 d->useRawFont = true;
380 d->resetFontEngineCache();
385 Sets the layout's font to the given \a font. The layout is
386 invalidated and must be laid out again.
390 void QTextLayout::setFont(const QFont &font)
393 #ifndef QT_NO_RAWFONT
394 d->useRawFont = false;
396 d->resetFontEngineCache();
400 Returns the current font that is used for the layout, or a default
405 QFont QTextLayout::font() const
411 Sets the layout's text to the given \a string. The layout is
412 invalidated and must be laid out again.
414 Notice that when using this QTextLayout as part of a QTextDocument this
415 method will have no effect.
419 void QTextLayout::setText(const QString& string)
427 Returns the layout's text.
431 QString QTextLayout::text() const
437 Sets the text option structure that controls the layout process to the
442 void QTextLayout::setTextOption(const QTextOption &option)
448 Returns the current text option used to control the layout process.
452 QTextOption QTextLayout::textOption() const
458 Sets the \a position and \a text of the area in the layout that is
459 processed before editing occurs. The layout is
460 invalidated and must be laid out again.
462 \sa preeditAreaPosition(), preeditAreaText()
464 void QTextLayout::setPreeditArea(int position, const QString &text)
466 if (text.isEmpty()) {
469 if (d->specialData->addFormats.isEmpty()) {
470 delete d->specialData;
473 d->specialData->preeditText = QString();
474 d->specialData->preeditPosition = -1;
478 d->specialData = new QTextEngine::SpecialData;
479 d->specialData->preeditPosition = position;
480 d->specialData->preeditText = text;
484 if (d->block.docHandle())
485 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
489 Returns the position of the area in the text layout that will be
490 processed before editing occurs.
492 \sa preeditAreaText()
494 int QTextLayout::preeditAreaPosition() const
496 return d->specialData ? d->specialData->preeditPosition : -1;
500 Returns the text that is inserted in the layout before editing occurs.
502 \sa preeditAreaPosition()
504 QString QTextLayout::preeditAreaText() const
506 return d->specialData ? d->specialData->preeditText : QString();
511 Sets the additional formats supported by the text layout to \a formatList.
512 The formats are applied with preedit area text in place.
514 \sa additionalFormats(), clearAdditionalFormats()
516 void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
518 if (formatList.isEmpty()) {
521 if (d->specialData->preeditText.isEmpty()) {
522 delete d->specialData;
525 d->specialData->addFormats = formatList;
526 d->specialData->addFormatIndices.clear();
529 if (!d->specialData) {
530 d->specialData = new QTextEngine::SpecialData;
531 d->specialData->preeditPosition = -1;
533 d->specialData->addFormats = formatList;
534 d->indexAdditionalFormats();
536 if (d->block.docHandle())
537 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
538 d->resetFontEngineCache();
542 Returns the list of additional formats supported by the text layout.
544 \sa setAdditionalFormats(), clearAdditionalFormats()
546 QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
548 QList<FormatRange> formats;
552 formats = d->specialData->addFormats;
554 if (d->specialData->addFormatIndices.isEmpty())
557 const QTextFormatCollection *collection = d->formats();
559 for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
560 formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
566 Clears the list of additional formats supported by the text layout.
568 \sa additionalFormats(), setAdditionalFormats()
570 void QTextLayout::clearAdditionalFormats()
572 setAdditionalFormats(QList<FormatRange>());
576 Enables caching of the complete layout information if \a enable is
577 true; otherwise disables layout caching. Usually
578 QTextLayout throws most of the layouting information away after a
579 call to endLayout() to reduce memory consumption. If you however
580 want to draw the laid out text directly afterwards enabling caching
581 might speed up drawing significantly.
585 void QTextLayout::setCacheEnabled(bool enable)
587 d->cacheGlyphs = enable;
591 Returns true if the complete layout information is cached; otherwise
594 \sa setCacheEnabled()
596 bool QTextLayout::cacheEnabled() const
598 return d->cacheGlyphs;
602 Sets the visual cursor movement style to the given \a style. If the
603 QTextLayout is backed by a document, you can ignore this and use the option
604 in QTextDocument, this option is for widgets like QLineEdit or custom
605 widgets without a QTextDocument. Default value is Qt::LogicalMoveStyle.
607 \sa cursorMoveStyle()
609 void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
611 d->visualMovement = style == Qt::VisualMoveStyle ? true : false;
615 The cursor movement style of this QTextLayout. The default is
616 Qt::LogicalMoveStyle.
618 \sa setCursorMoveStyle()
620 Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const
622 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
626 Begins the layout process.
630 void QTextLayout::beginLayout()
633 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
634 qWarning("QTextLayout::beginLayout: Called while already doing layout");
641 d->layoutData->layoutState = QTextEngine::InLayout;
645 Ends the layout process.
649 void QTextLayout::endLayout()
652 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
653 qWarning("QTextLayout::endLayout: Called without beginLayout()");
657 int l = d->lines.size();
658 if (l && d->lines.at(l-1).length < 0) {
659 QTextLine(l-1, d).setNumColumns(INT_MAX);
661 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
669 Clears the line information in the layout. After having called
670 this function, lineCount() returns 0.
672 void QTextLayout::clearLayout()
678 Returns the next valid cursor position after \a oldPos that
679 respects the given cursor \a mode.
680 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
682 \sa isValidCursorPosition(), previousCursorPosition()
684 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
686 const HB_CharAttributes *attributes = d->attributes();
687 int len = d->block.isValid() ? d->block.length() - 1
688 : d->layoutData->string.length();
689 Q_ASSERT(len <= d->layoutData->string.length());
690 if (!attributes || oldPos < 0 || oldPos >= len)
693 if (mode == SkipCharacters) {
695 while (oldPos < len && !attributes[oldPos].charStop)
698 if (oldPos < len && d->atWordSeparator(oldPos)) {
700 while (oldPos < len && d->atWordSeparator(oldPos))
703 while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
706 while (oldPos < len && d->atSpace(oldPos))
714 Returns the first valid cursor position before \a oldPos that
715 respects the given cursor \a mode.
716 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
718 \sa isValidCursorPosition(), nextCursorPosition()
720 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
722 const HB_CharAttributes *attributes = d->attributes();
723 if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
726 if (mode == SkipCharacters) {
728 while (oldPos && !attributes[oldPos].charStop)
731 while (oldPos && d->atSpace(oldPos-1))
734 if (oldPos && d->atWordSeparator(oldPos-1)) {
736 while (oldPos && d->atWordSeparator(oldPos-1))
739 while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
748 Returns the cursor position to the right of \a oldPos, next to it.
749 It's dependent on the visual position of characters, after bi-directional
752 \sa leftCursorPosition(), nextCursorPosition()
754 int QTextLayout::rightCursorPosition(int oldPos) const
756 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
757 // qDebug("%d -> %d", oldPos, newPos);
762 Returns the cursor position to the left of \a oldPos, next to it.
763 It's dependent on the visual position of characters, after bi-directional
766 \sa rightCursorPosition(), previousCursorPosition()
768 int QTextLayout::leftCursorPosition(int oldPos) const
770 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
771 // qDebug("%d -> %d", oldPos, newPos);
776 Returns true if position \a pos is a valid cursor position.
778 In a Unicode context some positions in the text are not valid
779 cursor positions, because the position is inside a Unicode
780 surrogate or a grapheme cluster.
782 A grapheme cluster is a sequence of two or more Unicode characters
783 that form one indivisible entity on the screen. For example the
784 latin character `\Auml' can be represented in Unicode by two
785 characters, `A' (0x41), and the combining diaresis (0x308). A text
786 cursor can only validly be positioned before or after these two
787 characters, never between them since that wouldn't make sense. In
788 indic languages every syllable forms a grapheme cluster.
790 bool QTextLayout::isValidCursorPosition(int pos) const
792 const HB_CharAttributes *attributes = d->attributes();
793 if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
795 return attributes[pos].charStop;
799 Returns a new text line to be laid out if there is text to be
800 inserted into the layout; otherwise returns an invalid text line.
802 The text layout creates a new line object that starts after the
803 last line in the layout, or at the beginning if the layout is empty.
804 The layout maintains an internal cursor, and each line is filled
805 with text from the cursor position onwards when the
806 QTextLine::setLineWidth() function is called.
808 Once QTextLine::setLineWidth() is called, a new line can be created and
809 filled with text. Repeating this process will lay out the whole block
810 of text contained in the QTextLayout. If there is no text left to be
811 inserted into the layout, the QTextLine returned will not be valid
812 (isValid() will return false).
814 QTextLine QTextLayout::createLine()
817 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
818 qWarning("QTextLayout::createLine: Called without layouting");
822 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
825 int l = d->lines.size();
826 if (l && d->lines.at(l-1).length < 0) {
827 QTextLine(l-1, d).setNumColumns(INT_MAX);
829 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
830 int strlen = d->layoutData->string.length();
831 if (l && from >= strlen) {
832 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
839 line.justified = false;
840 line.gridfitted = false;
842 d->lines.append(line);
843 return QTextLine(l, d);
847 Returns the number of lines in this text layout.
851 int QTextLayout::lineCount() const
853 return d->lines.size();
857 Returns the \a{i}-th line of text in this text layout.
859 \sa lineCount(), lineForTextPosition()
861 QTextLine QTextLayout::lineAt(int i) const
863 return i < lineCount() ? QTextLine(i, d) : QTextLine();
867 Returns the line that contains the cursor position specified by \a pos.
869 \sa isValidCursorPosition(), lineAt()
871 QTextLine QTextLayout::lineForTextPosition(int pos) const
873 int lineNum = d->lineNumberForTextPosition(pos);
874 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
880 The global position of the layout. This is independent of the
881 bounding rectangle and of the layout process.
885 QPointF QTextLayout::position() const
891 Moves the text layout to point \a p.
895 void QTextLayout::setPosition(const QPointF &p)
901 The smallest rectangle that contains all the lines in the layout.
903 QRectF QTextLayout::boundingRect() const
905 if (d->lines.isEmpty())
909 QFixed xmin = d->lines.at(0).x;
910 QFixed ymin = d->lines.at(0).y;
912 for (int i = 0; i < d->lines.size(); ++i) {
913 const QScriptLine &si = d->lines[i];
914 xmin = qMin(xmin, si.x);
915 ymin = qMin(ymin, si.y);
916 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
917 xmax = qMax(xmax, si.x+lineWidth);
918 // ### shouldn't the ascent be used in ymin???
919 ymax = qMax(ymax, si.y+si.height());
921 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
925 The minimum width the layout needs. This is the width of the
926 layout's smallest non-breakable substring.
928 \warning This function only returns a valid value after the layout
933 qreal QTextLayout::minimumWidth() const
935 return d->minWidth.toReal();
939 The maximum width the layout could expand to; this is essentially
940 the width of the entire text.
942 \warning This function only returns a valid value after the layout
947 qreal QTextLayout::maximumWidth() const
949 return d->maxWidth.toReal();
956 void QTextLayout::setFlags(int flags)
958 if (flags & Qt::TextJustificationForced) {
959 d->option.setAlignment(Qt::AlignJustify);
960 d->forceJustification = true;
963 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
964 d->ignoreBidi = true;
965 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
969 static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
970 QPainterPath *region, QRectF boundingRect)
972 const QScriptLine &line = eng->lines[lineNumber];
974 QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
978 const qreal selectionY = pos.y() + line.y.toReal();
979 const qreal lineHeight = line.height().toReal();
981 QFixed lastSelectionX = iterator.x;
982 QFixed lastSelectionWidth;
984 while (!iterator.atEnd()) {
987 QFixed selectionX, selectionWidth;
988 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
989 if (selectionX == lastSelectionX + lastSelectionWidth) {
990 lastSelectionWidth += selectionWidth;
994 if (lastSelectionWidth > 0)
995 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
997 lastSelectionX = selectionX;
998 lastSelectionWidth = selectionWidth;
1001 if (lastSelectionWidth > 0)
1002 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
1005 static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
1007 return clip.isValid() ? (rect & clip) : rect;
1012 Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
1013 expensive function, and should not be called in a time sensitive context.
1017 \sa draw(), QPainter::drawGlyphRun()
1019 #if !defined(QT_NO_RAWFONT)
1020 QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
1025 length = text().length();
1027 QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
1028 for (int i=0; i<d->lines.size(); ++i) {
1029 if (d->lines[i].from > from + length)
1031 else if (d->lines[i].from + d->lines[i].length >= from) {
1032 QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
1034 for (int j = 0; j < glyphRuns.size(); j++) {
1035 const QGlyphRun &glyphRun = glyphRuns.at(j);
1036 QRawFont rawFont = glyphRun.rawFont();
1038 QFontEngine *fontEngine = rawFont.d->fontEngine;
1039 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1040 QPair<QFontEngine *, int> key(fontEngine, int(flags));
1041 // merge the glyph runs using the same font
1042 if (glyphRunHash.contains(key)) {
1043 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1045 QVector<quint32> indexes = oldGlyphRun.glyphIndexes();
1046 QVector<QPointF> positions = oldGlyphRun.positions();
1048 indexes += glyphRun.glyphIndexes();
1049 positions += glyphRun.positions();
1051 oldGlyphRun.setGlyphIndexes(indexes);
1052 oldGlyphRun.setPositions(positions);
1054 glyphRunHash[key] = glyphRun;
1060 return glyphRunHash.values();
1062 #endif // QT_NO_RAWFONT
1065 Draws the whole layout on the painter \a p at the position specified by \a pos.
1066 The rendered layout includes the given \a selections and is clipped within
1067 the rectangle specified by \a clip.
1069 void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
1071 if (d->lines.isEmpty())
1077 QPointF position = pos + d->position;
1079 QFixed clipy = (INT_MIN/256);
1080 QFixed clipe = (INT_MAX/256);
1081 if (clip.isValid()) {
1082 clipy = QFixed::fromReal(clip.y() - position.y());
1083 clipe = clipy + QFixed::fromReal(clip.height());
1087 int lastLine = d->lines.size();
1088 for (int i = 0; i < d->lines.size(); ++i) {
1090 const QScriptLine &sl = d->lines[i];
1096 if ((sl.y + sl.height()) < clipy) {
1102 QPainterPath excludedRegion;
1103 QPainterPath textDoneRegion;
1104 for (int i = 0; i < selections.size(); ++i) {
1105 FormatRange selection = selections.at(i);
1106 const QBrush bg = selection.format.background();
1108 QPainterPath region;
1109 region.setFillRule(Qt::WindingFill);
1111 for (int line = firstLine; line < lastLine; ++line) {
1112 const QScriptLine &sl = d->lines[line];
1113 QTextLine tl(line, d);
1115 QRectF lineRect(tl.naturalTextRect());
1116 lineRect.translate(position);
1117 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1119 bool isLastLineInBlock = (line == d->lines.size()-1);
1120 int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
1123 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1124 continue; // no actual intersection
1126 const bool selectionStartInLine = sl.from <= selection.start;
1127 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1129 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1130 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1132 region.addRect(clipIfValid(lineRect, clip));
1135 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1136 QRectF fullLineRect(tl.rect());
1137 fullLineRect.translate(position);
1138 fullLineRect.setRight(QFIXED_MAX);
1139 if (!selectionEndInLine)
1140 region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1141 if (!selectionStartInLine)
1142 region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1143 } else if (!selectionEndInLine
1144 && isLastLineInBlock
1145 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1146 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1147 lineRect.height()/4, lineRect.height()), clip));
1152 const QPen oldPen = p->pen();
1153 const QBrush oldBrush = p->brush();
1155 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1156 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1157 p->drawPath(region);
1160 p->setBrush(oldBrush);
1165 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1166 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1168 if (hasBackground) {
1169 selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1170 // don't just clear the property, set an empty brush that overrides a potential
1171 // background brush specified in the text
1172 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1173 selection.format.clearProperty(QTextFormat::OutlinePen);
1176 selection.format.setProperty(SuppressText, !hasText);
1178 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1182 p->setClipPath(region, Qt::IntersectClip);
1184 for (int line = firstLine; line < lastLine; ++line) {
1185 QTextLine l(line, d);
1186 l.draw(p, position, &selection);
1191 textDoneRegion += region;
1194 textDoneRegion -= region;
1197 excludedRegion += region;
1200 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1201 if (!needsTextButNoBackground.isEmpty()){
1203 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1204 FormatRange selection;
1205 selection.start = 0;
1206 selection.length = INT_MAX;
1207 selection.format.setProperty(SuppressBackground, true);
1208 for (int line = firstLine; line < lastLine; ++line) {
1209 QTextLine l(line, d);
1210 l.draw(p, position, &selection);
1215 if (!excludedRegion.isEmpty()) {
1218 QRectF br = boundingRect().translated(position);
1219 br.setRight(QFIXED_MAX);
1221 br = br.intersected(clip);
1223 path -= excludedRegion;
1224 p->setClipPath(path, Qt::IntersectClip);
1227 for (int i = firstLine; i < lastLine; ++i) {
1229 l.draw(p, position);
1231 if (!excludedRegion.isEmpty())
1235 if (!d->cacheGlyphs)
1240 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
1243 Draws a text cursor with the current pen at the given \a position using the
1244 \a painter specified.
1245 The corresponding position within the text is specified by \a cursorPosition.
1247 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
1249 drawCursor(p, pos, cursorPosition, 1);
1253 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
1255 Draws a text cursor with the current pen and the specified \a width at the given \a position using the
1256 \a painter specified.
1257 The corresponding position within the text is specified by \a cursorPosition.
1259 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
1261 if (d->lines.isEmpty())
1267 QPointF position = pos + d->position;
1269 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
1270 int line = d->lineNumberForTextPosition(cursorPosition);
1273 if (line >= d->lines.size())
1276 QTextLine l(line, d);
1277 const QScriptLine &sl = d->lines[line];
1279 qreal x = position.x() + l.cursorToX(cursorPosition);
1283 if (d->visualCursorMovement()) {
1284 if (cursorPosition == sl.from + sl.length)
1286 itm = d->findItem(cursorPosition);
1288 itm = d->findItem(cursorPosition - 1);
1290 QFixed base = sl.base();
1291 QFixed descent = sl.descent;
1292 bool rightToLeft = d->isRightToLeft();
1294 const QScriptItem &si = d->layoutData->items.at(itm);
1298 descent = si.descent;
1299 rightToLeft = si.analysis.bidiLevel % 2;
1301 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1302 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1303 && (p->transform().type() > QTransform::TxTranslate);
1304 if (toggleAntialiasing)
1305 p->setRenderHint(QPainter::Antialiasing);
1306 p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
1307 if (toggleAntialiasing)
1308 p->setRenderHint(QPainter::Antialiasing, false);
1309 if (d->layoutData->hasBidi) {
1310 const int arrow_extent = 4;
1311 int sign = rightToLeft ? -1 : 1;
1312 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1313 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1322 \brief The QTextLine class represents a line of text inside a QTextLayout.
1325 \ingroup richtext-processing
1327 A text line is usually created by QTextLayout::createLine().
1329 After being created, the line can be filled using the setLineWidth()
1330 or setNumColumns() functions. A line has a number of attributes including the
1331 rectangle it occupies, rect(), its coordinates, x() and y(), its
1332 textLength(), width() and naturalTextWidth(), and its ascent() and decent()
1333 relative to the text. The position of the cursor in terms of the
1334 line is available from cursorToX() and its inverse from
1335 xToCursor(). A line can be moved with setPosition().
1339 \enum QTextLine::Edge
1346 \enum QTextLine::CursorPosition
1348 \value CursorBetweenCharacters
1349 \value CursorOnCharacter
1353 \fn QTextLine::QTextLine(int line, QTextEngine *e)
1356 Constructs a new text line using the line at position \a line in
1357 the text engine \a e.
1361 \fn QTextLine::QTextLine()
1363 Creates an invalid line.
1367 \fn bool QTextLine::isValid() const
1369 Returns true if this text line is valid; otherwise returns false.
1373 \fn int QTextLine::lineNumber() const
1375 Returns the position of the line in the text engine.
1380 Returns the line's bounding rectangle.
1382 \sa x(), y(), textLength(), width()
1384 QRectF QTextLine::rect() const
1386 const QScriptLine& sl = eng->lines[index];
1387 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1391 Returns the rectangle covered by the line.
1393 QRectF QTextLine::naturalTextRect() const
1395 const QScriptLine& sl = eng->lines[index];
1396 QFixed x = sl.x + eng->alignLine(sl);
1398 QFixed width = sl.textWidth;
1402 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1406 Returns the line's x position.
1408 \sa rect(), y(), textLength(), width()
1410 qreal QTextLine::x() const
1412 return eng->lines[index].x.toReal();
1416 Returns the line's y position.
1418 \sa x(), rect(), textLength(), width()
1420 qreal QTextLine::y() const
1422 return eng->lines[index].y.toReal();
1426 Returns the line's width as specified by the layout() function.
1428 \sa naturalTextWidth(), x(), y(), textLength(), rect()
1430 qreal QTextLine::width() const
1432 return eng->lines[index].width.toReal();
1437 Returns the line's ascent.
1439 \sa descent(), height()
1441 qreal QTextLine::ascent() const
1443 return eng->lines[index].ascent.toReal();
1447 Returns the line's descent.
1449 \sa ascent(), height()
1451 qreal QTextLine::descent() const
1453 return eng->lines[index].descent.toReal();
1457 Returns the line's height. This is equal to ascent() + descent()
1458 if leading is not included. If leading is included, this equals to
1459 ascent() + descent() + leading().
1461 \sa ascent(), descent(), leading(), setLeadingIncluded()
1463 qreal QTextLine::height() const
1465 return eng->lines[index].height().toReal();
1471 Returns the line's leading.
1473 \sa ascent(), descent(), height()
1475 qreal QTextLine::leading() const
1477 return eng->lines[index].leading.toReal();
1483 Includes positive leading into the line's height if \a included is true;
1484 otherwise does not include leading.
1486 By default, leading is not included.
1488 Note that negative leading is ignored, it must be handled
1489 in the code using the text lines by letting the lines overlap.
1491 \sa leadingIncluded()
1494 void QTextLine::setLeadingIncluded(bool included)
1496 eng->lines[index].leadingIncluded= included;
1503 Returns true if positive leading is included into the line's height;
1504 otherwise returns false.
1506 By default, leading is not included.
1508 \sa setLeadingIncluded()
1510 bool QTextLine::leadingIncluded() const
1512 return eng->lines[index].leadingIncluded;
1516 Returns the width of the line that is occupied by text. This is
1517 always \<= to width(), and is the minimum width that could be used
1518 by layout() without changing the line break position.
1520 qreal QTextLine::naturalTextWidth() const
1522 return eng->lines[index].textWidth.toReal();
1527 Returns the horizontal advance of the text. The advance of the text
1528 is the distance from its position to the next position at which
1529 text would naturally be drawn.
1531 By adding the advance to the position of the text line and using this
1532 as the position of a second text line, you will be able to position
1533 the two lines side-by-side without gaps in-between.
1535 qreal QTextLine::horizontalAdvance() const
1537 return eng->lines[index].textAdvance.toReal();
1541 Lays out the line with the given \a width. The line is filled from
1542 its starting position with as many characters as will fit into
1543 the line. In case the text cannot be split at the end of the line,
1544 it will be filled with additional characters to the next whitespace
1547 void QTextLine::setLineWidth(qreal width)
1549 QScriptLine &line = eng->lines[index];
1550 if (!eng->layoutData) {
1551 qWarning("QTextLine: Can't set a line width while not layouting.");
1555 if (width > QFIXED_MAX)
1558 line.width = QFixed::fromReal(width);
1560 && line.textWidth <= line.width
1561 && line.from + line.length == eng->layoutData->string.length())
1562 // no need to do anything if the line is already layouted and the last one. This optimization helps
1563 // when using things in a single line layout.
1568 layout_helper(INT_MAX);
1572 Lays out the line. The line is filled from its starting position
1573 with as many characters as are specified by \a numColumns. In case
1574 the text cannot be split until \a numColumns characters, the line
1575 will be filled with as many characters to the next whitespace or
1578 void QTextLine::setNumColumns(int numColumns)
1580 QScriptLine &line = eng->lines[index];
1581 line.width = QFIXED_MAX;
1584 layout_helper(numColumns);
1588 Lays out the line. The line is filled from its starting position
1589 with as many characters as are specified by \a numColumns. In case
1590 the text cannot be split until \a numColumns characters, the line
1591 will be filled with as many characters to the next whitespace or
1592 end of the text. The provided \a alignmentWidth is used as reference
1593 width for alignment.
1595 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
1597 QScriptLine &line = eng->lines[index];
1598 line.width = QFixed::fromReal(alignmentWidth);
1601 layout_helper(numColumns);
1605 #define LB_DEBUG qDebug
1607 #define LB_DEBUG if (0) qDebug
1612 struct LineBreakHelper
1615 : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
1616 manualWrap(false), whiteSpaceOrObject(true)
1621 QScriptLine tmpData;
1622 QScriptLine spaceData;
1624 QGlyphLayout glyphs;
1628 int currentPosition;
1629 glyph_t previousGlyph;
1632 QFixed softHyphenWidth;
1633 QFixed rightBearing;
1634 QFixed minimumRightBearing;
1636 QFontEngine *fontEngine;
1637 const unsigned short *logClusters;
1640 bool whiteSpaceOrObject;
1642 bool checkFullOtherwiseExtend(QScriptLine &line);
1644 QFixed calculateNewWidth(const QScriptLine &line) const {
1645 return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
1646 - qMin(rightBearing, QFixed());
1649 inline glyph_t currentGlyph() const
1651 Q_ASSERT(currentPosition > 0);
1652 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1654 return glyphs.glyphs[logClusters[currentPosition - 1]];
1657 inline void saveCurrentGlyph()
1660 if (currentPosition > 0 &&
1661 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1662 previousGlyph = currentGlyph(); // needed to calculate right bearing later
1666 inline void adjustRightBearing(glyph_t glyph)
1669 fontEngine->getGlyphBearings(glyph, 0, &rb);
1670 rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
1673 inline void adjustRightBearing()
1675 if (currentPosition <= 0)
1677 adjustRightBearing(currentGlyph());
1680 inline void adjustPreviousRightBearing()
1682 if (previousGlyph > 0)
1683 adjustRightBearing(previousGlyph);
1686 inline void resetRightBearing()
1688 rightBearing = QFixed(1); // Any positive number is defined as invalid since only
1689 // negative right bearings are interesting to us.
1693 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1695 LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1697 QFixed newWidth = calculateNewWidth(line);
1698 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1701 minw = qMax(minw, tmpData.textWidth);
1703 line.textWidth += spaceData.textWidth;
1705 line.length += spaceData.length;
1706 tmpData.textWidth = 0;
1708 spaceData.textWidth = 0;
1709 spaceData.length = 0;
1714 } // anonymous namespace
1717 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
1718 const QScriptItem ¤t, const unsigned short *logClusters,
1719 const QGlyphLayout &glyphs)
1721 int glyphPosition = logClusters[pos];
1722 do { // got to the first next cluster
1725 } while (pos < end && logClusters[pos] == glyphPosition);
1726 do { // calculate the textWidth for the rest of the current cluster.
1727 if (!glyphs.attributes[glyphPosition].dontPrint)
1728 line.textWidth += glyphs.advances_x[glyphPosition];
1730 } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
1732 Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
1739 void QTextLine::layout_helper(int maxGlyphs)
1741 QScriptLine &line = eng->lines[index];
1743 line.trailingSpaces = 0;
1745 line.hasTrailingSpaces = false;
1747 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
1748 line.setDefaultHeight(eng);
1752 Q_ASSERT(line.from < eng->layoutData->string.length());
1754 LineBreakHelper lbh;
1756 lbh.maxGlyphs = maxGlyphs;
1758 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1759 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1760 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1763 int newItem = eng->findItem(line.from);
1765 LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
1767 Qt::Alignment alignment = eng->option.alignment();
1769 const HB_CharAttributes *attributes = eng->attributes();
1772 lbh.currentPosition = line.from;
1774 lbh.logClusters = eng->layoutData->logClustersPtr;
1775 lbh.previousGlyph = 0;
1777 while (newItem < eng->layoutData->items.size()) {
1778 lbh.resetRightBearing();
1779 lbh.softHyphenWidth = 0;
1780 if (newItem != item) {
1782 const QScriptItem ¤t = eng->layoutData->items[item];
1783 if (!current.num_glyphs) {
1785 attributes = eng->attributes();
1788 lbh.logClusters = eng->layoutData->logClustersPtr;
1790 lbh.currentPosition = qMax(line.from, current.position);
1791 end = current.position + eng->length(item);
1792 lbh.glyphs = eng->shapedGlyphs(¤t);
1793 QFontEngine *fontEngine = eng->fontEngine(current);
1794 if (lbh.fontEngine != fontEngine) {
1795 lbh.fontEngine = fontEngine;
1796 lbh.minimumRightBearing = qMin(QFixed(),
1797 QFixed::fromReal(fontEngine->minRightBearing()));
1800 const QScriptItem ¤t = eng->layoutData->items[item];
1802 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1803 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1805 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1806 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1808 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1809 lbh.whiteSpaceOrObject = true;
1810 if (lbh.checkFullOtherwiseExtend(line))
1813 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1814 QFixed tabWidth = eng->calculateTabWidth(item, x);
1816 lbh.spaceData.textWidth += tabWidth;
1817 lbh.spaceData.length++;
1820 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1821 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1823 if (lbh.checkFullOtherwiseExtend(line))
1825 } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1826 lbh.whiteSpaceOrObject = true;
1827 // if the line consists only of the line separator make sure
1828 // we have a sane height
1829 if (!line.length && !lbh.tmpData.length)
1830 line.setDefaultHeight(eng);
1831 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1832 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1833 current, lbh.logClusters, lbh.glyphs);
1835 lbh.tmpData.length++;
1836 lbh.adjustPreviousRightBearing();
1838 line += lbh.tmpData;
1840 } else if (current.analysis.flags == QScriptAnalysis::Object) {
1841 lbh.whiteSpaceOrObject = true;
1842 lbh.tmpData.length++;
1844 QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
1845 if (eng->block.docHandle())
1846 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
1848 lbh.tmpData.textWidth += current.width;
1852 if (lbh.checkFullOtherwiseExtend(line))
1854 } else if (attributes[lbh.currentPosition].whiteSpace) {
1855 lbh.whiteSpaceOrObject = true;
1856 while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
1857 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1858 current, lbh.logClusters, lbh.glyphs);
1860 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
1861 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
1865 lbh.whiteSpaceOrObject = false;
1866 bool sb_or_ws = false;
1867 lbh.saveCurrentGlyph();
1869 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1870 current, lbh.logClusters, lbh.glyphs);
1872 if (lbh.currentPosition >= eng->layoutData->string.length()
1873 || attributes[lbh.currentPosition].whiteSpace
1874 || attributes[lbh.currentPosition].lineBreakType != HB_NoBreak) {
1877 } else if (breakany && attributes[lbh.currentPosition].charStop) {
1880 } while (lbh.currentPosition < end);
1881 lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
1883 if (lbh.currentPosition > 0 && lbh.currentPosition < end
1884 && attributes[lbh.currentPosition].lineBreakType == HB_SoftHyphen) {
1885 // if we are splitting up a word because of
1886 // a soft hyphen then we ...
1888 // a) have to take the width of the soft hyphen into
1889 // account to see if the first syllable(s) /and/
1890 // the soft hyphen fit into the line
1892 // b) if we are so short of available width that the
1893 // soft hyphen is the first breakable position, then
1894 // we don't want to show it. However we initially
1895 // have to take the width for it into account so that
1896 // the text document layout sees the overflow and
1897 // switch to break-anywhere mode, in which we
1898 // want the soft-hyphen to slip into the next line
1899 // and thus become invisible again.
1902 lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1904 lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1907 // The actual width of the text needs to take the right bearing into account. The
1908 // right bearing is left-ward, which means that if the rightmost pixel is to the right
1909 // of the advance of the glyph, the bearing will be negative. We flip the sign
1910 // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
1911 // We ignore the right bearing if the minimum negative bearing is too little to
1912 // expand the text beyond the edge.
1913 if (sb_or_ws|breakany) {
1914 QFixed rightBearing = lbh.rightBearing; // store previous right bearing
1915 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
1916 lbh.adjustRightBearing();
1917 if (lbh.checkFullOtherwiseExtend(line)) {
1918 // we are too wide, fix right bearing
1919 if (rightBearing <= 0)
1920 lbh.rightBearing = rightBearing; // take from cache
1922 lbh.adjustPreviousRightBearing();
1925 line.textWidth += lbh.softHyphenWidth;
1931 lbh.saveCurrentGlyph();
1933 if (lbh.currentPosition == end)
1936 LB_DEBUG("reached end of line");
1937 lbh.checkFullOtherwiseExtend(line);
1939 if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
1940 lbh.adjustRightBearing();
1941 line.textAdvance = line.textWidth;
1942 line.textWidth -= qMin(QFixed(), lbh.rightBearing);
1944 if (line.length == 0) {
1945 LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
1946 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
1947 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
1948 line += lbh.tmpData;
1951 LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
1952 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
1953 LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
1955 if (lbh.manualWrap) {
1956 eng->minWidth = qMax(eng->minWidth, line.textWidth);
1957 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
1959 eng->minWidth = qMax(eng->minWidth, lbh.minw);
1960 eng->maxWidth += line.textWidth;
1963 if (line.textWidth > 0 && item < eng->layoutData->items.size())
1964 eng->maxWidth += lbh.spaceData.textWidth;
1965 if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
1966 line.textWidth += lbh.spaceData.textWidth;
1967 if (lbh.spaceData.length) {
1968 line.trailingSpaces = lbh.spaceData.length;
1969 line.hasTrailingSpaces = true;
1972 line.justified = false;
1973 line.gridfitted = false;
1975 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
1976 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
1977 || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
1979 eng->option.setWrapMode(QTextOption::WrapAnywhere);
1982 layout_helper(lbh.maxGlyphs);
1983 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
1989 Moves the line to position \a pos.
1991 void QTextLine::setPosition(const QPointF &pos)
1993 eng->lines[index].x = QFixed::fromReal(pos.x());
1994 eng->lines[index].y = QFixed::fromReal(pos.y());
1998 Returns the line's position relative to the text layout's position.
2000 QPointF QTextLine::position() const
2002 return QPointF(eng->lines[index].x.toReal(), eng->lines[index].y.toReal());
2005 // ### DOC: I have no idea what this means/does.
2006 // You create a text layout with a string of text. Once you laid
2007 // it out, it contains a number of QTextLines. from() returns the position
2008 // inside the text string where this line starts. If you e.g. has a
2009 // text of "This is a string", laid out into two lines (the second
2010 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
2011 // layout.lineAt(1).from() == 8.
2013 Returns the start of the line from the beginning of the string
2014 passed to the QTextLayout.
2016 int QTextLine::textStart() const
2018 return eng->lines[index].from;
2022 Returns the length of the text in the line.
2024 \sa naturalTextWidth()
2026 int QTextLine::textLength() const
2028 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2029 && eng->block.isValid() && index == eng->lines.count()-1) {
2030 return eng->lines[index].length - 1;
2032 return eng->lines[index].length + eng->lines[index].trailingSpaces;
2035 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
2036 int start, int glyph_start)
2038 int ge = glyph_start + gf.glyphs.numGlyphs;
2039 int gs = glyph_start;
2040 int end = start + gf.num_chars;
2041 unsigned short *logClusters = eng->logClusters(&si);
2042 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2043 QFixed orig_width = gf.width;
2045 int *ul = eng->underlinePositions;
2047 while (*ul != -1 && *ul < start)
2049 bool rtl = si.analysis.bidiLevel % 2;
2056 if (ul && *ul != -1 && *ul < end) {
2058 gtmp = logClusters[*ul-si.position];
2061 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2062 gf.num_chars = stmp - start;
2063 gf.chars = eng->layoutData->string.unicode() + start;
2066 w += glyphs.effectiveAdvance(gs);
2074 QPainterPrivate::get(p)->drawTextItem(QPointF(x.toReal(), y.toReal()), gf, eng);
2077 if (ul && *ul != -1 && *ul < end) {
2079 gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
2081 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2082 gf.num_chars = stmp - start;
2083 gf.chars = eng->layoutData->string.unicode() + start;
2084 gf.logClusters = logClusters + start - si.position;
2087 w += glyphs.effectiveAdvance(gs);
2092 gf.underlineStyle = QTextCharFormat::SingleUnderline;
2095 QPainterPrivate::get(p)->drawTextItem(QPointF(x.toReal(), y.toReal()), gf, eng);
2098 gf.underlineStyle = QTextCharFormat::NoUnderline;
2104 gf.width = orig_width;
2108 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
2110 QBrush c = chf.foreground();
2111 if (c.style() == Qt::NoBrush) {
2112 p->setPen(defaultPen);
2115 QBrush bg = chf.background();
2116 if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
2118 if (c.style() != Qt::NoBrush) {
2119 p->setPen(QPen(c, 0));
2124 #if !defined(QT_NO_RAWFONT)
2125 static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &glyphLayout,
2126 const QPointF &pos, const QGlyphRun::GlyphRunFlags &flags,
2127 const QFixed &selectionX, const QFixed &selectionWidth)
2131 // Make a font for this particular engine
2133 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2134 fontD->fontEngine = fontEngine;
2135 fontD->thread = QThread::currentThread();
2136 fontD->fontEngine->ref.ref();
2137 QVarLengthArray<glyph_t> glyphsArray;
2138 QVarLengthArray<QFixedPoint> positionsArray;
2140 QTextItem::RenderFlags renderFlags;
2141 if (flags.testFlag(QGlyphRun::Overline))
2142 renderFlags |= QTextItem::Overline;
2143 if (flags.testFlag(QGlyphRun::Underline))
2144 renderFlags |= QTextItem::Underline;
2145 if (flags.testFlag(QGlyphRun::StrikeOut))
2146 renderFlags |= QTextItem::StrikeOut;
2147 if (flags.testFlag(QGlyphRun::RightToLeft))
2148 renderFlags |= QTextItem::RightToLeft;
2150 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2152 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2154 qreal fontHeight = font.ascent() + font.descent();
2157 QVector<quint32> glyphs;
2158 QVector<QPointF> positions;
2159 for (int i=0; i<glyphsArray.size(); ++i) {
2160 glyphs.append(glyphsArray.at(i) & 0xffffff);
2162 QPointF position = positionsArray.at(i).toPointF() + pos;
2163 positions.append(position);
2166 maxY = minY = position.y();
2168 minY = qMin(minY, position.y());
2169 maxY = qMax(maxY, position.y());
2173 qreal height = maxY + fontHeight - minY;
2175 glyphRun.setGlyphIndexes(glyphs);
2176 glyphRun.setPositions(positions);
2177 glyphRun.setFlags(flags);
2178 glyphRun.setRawFont(font);
2180 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2181 selectionWidth.toReal(), height));
2187 Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
2188 in the range defined by \a from and \a length. The \a from index is relative to the beginning
2189 of the text in the containing QTextLayout, and the range must be within the range of QTextLine
2190 as given by functions textStart() and textLength().
2192 If \a from is negative, it will default to textStart(), and if \a length is negative it will
2193 default to the return value of textLength().
2197 \sa QTextLayout::glyphRuns()
2199 QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
2201 const QScriptLine &line = eng->lines[index];
2203 if (line.length == 0)
2204 return QList<QGlyphRun>();
2210 length = textLength();
2213 return QList<QGlyphRun>();
2215 QTextLayout::FormatRange selection;
2216 selection.start = from;
2217 selection.length = length;
2219 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2220 qreal y = line.y.toReal() + line.base().toReal();
2221 QList<QGlyphRun> glyphRuns;
2222 while (!iterator.atEnd()) {
2223 QScriptItem &si = iterator.next();
2224 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2227 QPointF pos(iterator.x.toReal(), y);
2228 if (from >= 0 && length >= 0 &&
2229 (from >= si.position + eng->length(&si)
2230 || from + length <= si.position
2231 || from + length <= iterator.itemStart
2232 || from >= iterator.itemEnd)) {
2237 QGlyphRun::GlyphRunFlags flags;
2238 if (!eng->useRawFont) {
2239 font = eng->font(si);
2240 if (font.overline())
2241 flags |= QGlyphRun::Overline;
2242 if (font.underline())
2243 flags |= QGlyphRun::Underline;
2244 if (font.strikeOut())
2245 flags |= QGlyphRun::StrikeOut;
2249 if (si.analysis.bidiLevel % 2) {
2250 flags |= QGlyphRun::RightToLeft;
2254 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2255 int relativeTo = qMin(iterator.itemEnd - 1, from + length - 1) - si.position;
2257 unsigned short *logClusters = eng->logClusters(&si);
2258 int glyphsStart = logClusters[relativeFrom];
2259 int glyphsEnd = (relativeTo == eng->length(&si))
2261 : logClusters[relativeTo];
2262 // the glyph index right next to the requested range
2263 int nextGlyphIndex = relativeTo < eng->length(&si) - 1 ? logClusters[relativeTo + 1] : si.num_glyphs;
2264 if (nextGlyphIndex - 1 > glyphsEnd)
2265 glyphsEnd = nextGlyphIndex - 1;
2266 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2267 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2269 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2270 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2272 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2274 // Calculate new x position of glyph layout for a subset. This becomes somewhat complex
2275 // when we're breaking a RTL script item, since the expected position passed into
2276 // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
2277 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2278 for (int i=itemGlyphsStart; i<glyphsStart; ++i) {
2279 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2280 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2281 glyphLayout.advances_y[i].toReal());
2283 } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2284 for (int i=itemGlyphsEnd; i>glyphsEnd; --i) {
2285 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2286 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2287 glyphLayout.advances_y[i].toReal());
2291 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2295 iterator.getSelectionBounds(&x, &width);
2297 if (glyphLayout.numGlyphs > 0) {
2298 QFontEngine *mainFontEngine;
2299 #ifndef QT_NO_RAWFONT
2300 if (eng->useRawFont && eng->rawFont.isValid())
2301 mainFontEngine= eng->fontEngine(si);
2304 mainFontEngine = font.d->engineForScript(si.analysis.script);
2306 if (mainFontEngine->type() == QFontEngine::Multi) {
2307 QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
2308 int end = rtl ? glyphLayout.numGlyphs : 0;
2309 int start = rtl ? end : 0;
2310 int which = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
2311 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs);
2312 rtl ? --start : ++end) {
2313 const int e = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
2317 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2318 multiFontEngine->ensureEngineAt(which);
2320 QGlyphRun::GlyphRunFlags subFlags = flags;
2321 if (start == 0 && startsInsideLigature)
2322 subFlags |= QGlyphRun::SplitLigature;
2324 glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
2325 subLayout, pos, subFlags, x, width));
2326 for (int i = 0; i < subLayout.numGlyphs; i++) {
2327 pos += QPointF(subLayout.advances_x[i].toReal(),
2328 subLayout.advances_y[i].toReal());
2338 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2339 multiFontEngine->ensureEngineAt(which);
2341 QGlyphRun::GlyphRunFlags subFlags = flags;
2342 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2343 subFlags |= QGlyphRun::SplitLigature;
2345 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2346 subLayout, pos, subFlags, x, width);
2347 if (!glyphRun.isEmpty())
2348 glyphRuns.append(glyphRun);
2350 if (startsInsideLigature || endsInsideLigature)
2351 flags |= QGlyphRun::SplitLigature;
2352 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags, x,
2354 if (!glyphRun.isEmpty())
2355 glyphRuns.append(glyphRun);
2362 #endif // QT_NO_RAWFONT
2365 \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
2367 Draws a line on the given \a painter at the specified \a position.
2368 The \a selection is reserved for internal use.
2370 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
2372 #ifndef QT_NO_RAWFONT
2373 // Not intended to work with rawfont
2374 Q_ASSERT(!eng->useRawFont);
2376 const QScriptLine &line = eng->lines[index];
2377 QPen pen = p->pen();
2379 bool noText = (selection && selection->format.property(SuppressText).toBool());
2383 && selection->start <= line.from
2384 && selection->start + selection->length > line.from) {
2386 const qreal lineHeight = line.height().toReal();
2387 QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
2388 lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
2389 setPenAndDrawBackground(p, QPen(), selection->format, r);
2396 QTextLineItemIterator iterator(eng, index, pos, selection);
2397 QFixed lineBase = line.base();
2398 eng->clearDecorations();
2399 eng->enableDelayDecorations();
2401 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2403 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2404 while (!iterator.atEnd()) {
2405 QScriptItem &si = iterator.next();
2407 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2410 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2411 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2414 QFixed itemBaseLine = y;
2415 QFont f = eng->font(si);
2416 QTextCharFormat format;
2418 if (eng->hasFormats() || selection) {
2419 format = eng->format(&si);
2420 if (suppressColors) {
2421 format.clearForeground();
2422 format.clearBackground();
2423 format.clearProperty(QTextFormat::TextUnderlineColor);
2426 format.merge(selection->format);
2428 setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2429 iterator.itemWidth.toReal(), line.height().toReal()));
2431 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2432 if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
2433 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2434 QFixed height = fe->ascent() + fe->descent();
2435 if (valign == QTextCharFormat::AlignSubScript)
2436 itemBaseLine += height / 6;
2437 else if (valign == QTextCharFormat::AlignSuperScript)
2438 itemBaseLine -= height / 2;
2442 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2444 if (eng->hasFormats()) {
2446 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
2447 QFixed itemY = y - si.ascent;
2448 if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
2449 itemY = y - lineBase;
2452 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2454 eng->docLayout()->drawInlineObject(p, itemRect,
2455 QTextInlineObject(iterator.item, eng),
2456 si.position + eng->block.position(),
2459 QBrush bg = format.brushProperty(ObjectSelectionBrush);
2460 if (bg.style() != Qt::NoBrush) {
2461 QColor c = bg.color();
2463 p->fillRect(itemRect, c);
2466 } else { // si.isTab
2467 QFont f = eng->font(si);
2468 QTextItemInt gf(si, &f, format);
2471 gf.width = iterator.itemWidth;
2472 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2473 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2474 QChar visualTab(0x2192);
2475 int w = QFontMetrics(f).width(visualTab);
2476 qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
2478 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2479 iterator.itemWidth.toReal(), line.height().toReal()),
2483 p->drawText(QPointF(iterator.x.toReal() + x,
2484 y.toReal()), visualTab);
2494 unsigned short *logClusters = eng->logClusters(&si);
2495 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2497 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2498 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2499 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2500 gf.logClusters = logClusters + iterator.itemStart - si.position;
2501 gf.width = iterator.itemWidth;
2502 gf.justified = line.justified;
2503 gf.initWithScriptItem(si);
2505 Q_ASSERT(gf.fontEngine);
2507 if (eng->underlinePositions) {
2508 // can't have selections in this case
2509 drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
2511 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2512 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2514 path.setFillRule(Qt::WindingFill);
2516 if (gf.glyphs.numGlyphs)
2517 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2519 const QFontEngine *fe = gf.fontEngine;
2520 const qreal lw = fe->lineThickness().toReal();
2521 if (gf.flags & QTextItem::Underline) {
2522 qreal offs = fe->underlinePosition().toReal();
2523 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2525 if (gf.flags & QTextItem::Overline) {
2526 qreal offs = fe->ascent().toReal() + 1;
2527 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2529 if (gf.flags & QTextItem::StrikeOut) {
2530 qreal offs = fe->ascent().toReal() / 3;
2531 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2536 p->setRenderHint(QPainter::Antialiasing);
2537 //Currently QPen with a Qt::NoPen style still returns a default
2538 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
2539 if (p->pen().style() == Qt::NoPen)
2540 p->setBrush(Qt::NoBrush);
2542 p->setBrush(p->pen().brush());
2544 p->setPen(format.textOutline());
2549 gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
2550 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2553 if (si.analysis.flags == QScriptAnalysis::Space
2554 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2555 QBrush c = format.foreground();
2556 if (c.style() != Qt::NoBrush)
2557 p->setPen(c.color());
2558 QChar visualSpace((ushort)0xb7);
2559 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2563 eng->drawDecorations(p);
2565 if (eng->hasFormats())
2570 \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
2576 Converts the cursor position \a cursorPos to the corresponding x position
2577 inside the line, taking account of the \a edge.
2579 If \a cursorPos is not a valid cursor position, the nearest valid
2580 cursor position will be used instead, and cpos will be modified to
2581 point to this valid cursor position.
2585 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
2587 if (!eng->layoutData)
2590 const QScriptLine &line = eng->lines[index];
2591 bool lastLine = index >= eng->lines.size() - 1;
2594 x += eng->alignLine(line) - eng->leadingSpaceWidth(line);
2596 if (!index && !eng->layoutData->items.size()) {
2601 int lineEnd = line.from + line.length + line.trailingSpaces;
2602 int pos = *cursorPos;
2604 const HB_CharAttributes *attributes = eng->attributes();
2609 while (pos < lineEnd && !attributes[pos].charStop)
2611 if (pos == lineEnd) {
2612 // end of line ensure we have the last item on the line
2613 itm = eng->findItem(pos-1);
2616 itm = eng->findItem(pos);
2617 eng->shapeLine(line);
2619 const QScriptItem *si = &eng->layoutData->items[itm];
2620 if (!si->num_glyphs)
2622 pos -= si->position;
2624 QGlyphLayout glyphs = eng->shapedGlyphs(si);
2625 unsigned short *logClusters = eng->logClusters(si);
2626 Q_ASSERT(logClusters);
2628 int l = eng->length(itm);
2634 int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
2635 if (edge == Trailing && glyph_pos < si->num_glyphs) {
2636 // trailing edge is leading edge of next cluster
2638 while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2642 bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
2645 // add the items left of the cursor
2647 int firstItem = eng->findItem(line.from);
2648 int lastItem = eng->findItem(lineEnd - 1);
2649 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2651 QVarLengthArray<int> visualOrder(nItems);
2652 QVarLengthArray<uchar> levels(nItems);
2653 for (int i = 0; i < nItems; ++i)
2654 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2655 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2657 for (int i = 0; i < nItems; ++i) {
2658 int item = visualOrder[i]+firstItem;
2661 QScriptItem &si = eng->layoutData->items[item];
2665 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2669 int start = qMax(line.from, si.position);
2670 int end = qMin(lineEnd, si.position + eng->length(item));
2672 logClusters = eng->logClusters(&si);
2674 int gs = logClusters[start-si.position];
2675 int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
2677 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2680 x += glyphs.effectiveAdvance(gs);
2685 logClusters = eng->logClusters(si);
2686 glyphs = eng->shapedGlyphs(si);
2687 if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
2688 if (pos == (reverse ? 0 : l))
2691 bool rtl = eng->isRightToLeft();
2692 bool visual = eng->visualCursorMovement();
2693 int end = qMin(lineEnd, si->position + l) - si->position;
2695 int glyph_end = end == l ? si->num_glyphs : logClusters[end];
2696 int glyph_start = glyph_pos;
2697 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
2699 for (int i = glyph_end - 1; i >= glyph_start; i--)
2700 x += glyphs.effectiveAdvance(i);
2702 int start = qMax(line.from - si->position, 0);
2703 int glyph_start = logClusters[start];
2704 int glyph_end = glyph_pos;
2705 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
2707 for (int i = glyph_start; i <= glyph_end; i++)
2708 x += glyphs.effectiveAdvance(i);
2710 x += eng->offsetInLigature(si, pos, end, glyph_pos);
2713 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.width)
2715 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
2718 *cursorPos = pos + si->position;
2723 \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
2725 Converts the x-coordinate \a x, to the nearest matching cursor
2726 position, depending on the cursor position type, \a cpos.
2727 Note that result cursor position includes possible preedit area text.
2731 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
2733 QFixed x = QFixed::fromReal(_x);
2734 const QScriptLine &line = eng->lines[index];
2735 bool lastLine = index >= eng->lines.size() - 1;
2736 int lineNum = index;
2738 if (!eng->layoutData)
2741 int line_length = textLength();
2746 int firstItem = eng->findItem(line.from);
2747 int lastItem = eng->findItem(line.from + line_length - 1);
2748 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2754 x -= eng->alignLine(line);
2755 // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
2757 QVarLengthArray<int> visualOrder(nItems);
2758 QVarLengthArray<unsigned char> levels(nItems);
2759 for (int i = 0; i < nItems; ++i)
2760 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2761 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2763 bool visual = eng->visualCursorMovement();
2765 // left of first item
2766 int item = visualOrder[0]+firstItem;
2767 QScriptItem &si = eng->layoutData->items[item];
2770 int pos = si.position;
2771 if (si.analysis.bidiLevel % 2)
2772 pos += eng->length(item);
2773 pos = qMax(line.from, pos);
2774 pos = qMin(line.from + line_length, pos);
2776 } else if (x < line.textWidth
2777 || (line.justified && x < line.width)) {
2778 // has to be in one of the runs
2780 bool rtl = eng->isRightToLeft();
2782 eng->shapeLine(line);
2783 QVector<int> insertionPoints;
2785 eng->insertionPointsForLine(lineNum, insertionPoints);
2787 for (int i = 0; i < nItems; ++i) {
2788 int item = visualOrder[i]+firstItem;
2789 QScriptItem &si = eng->layoutData->items[item];
2790 int item_length = eng->length(item);
2791 // qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
2793 int start = qMax(line.from - si.position, 0);
2794 int end = qMin(line.from + line_length - si.position, item_length);
2796 unsigned short *logClusters = eng->logClusters(&si);
2798 int gs = logClusters[start];
2799 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
2800 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2802 QFixed item_width = 0;
2803 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2804 item_width = si.width;
2808 item_width += glyphs.effectiveAdvance(g);
2812 // qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
2814 if (pos + item_width < x) {
2819 // qDebug(" inside run");
2820 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2821 if (cpos == QTextLine::CursorOnCharacter)
2823 bool left_half = (x - pos) < item_width/2;
2825 if (bool(si.analysis.bidiLevel % 2) != left_half)
2827 return si.position + 1;
2832 // has to be inside run
2833 if (cpos == QTextLine::CursorOnCharacter) {
2834 if (si.analysis.bidiLevel % 2) {
2838 if (glyphs.attributes[gs].clusterStart) {
2845 pos -= glyphs.effectiveAdvance(gs);
2851 if (glyphs.attributes[gs].clusterStart) {
2857 pos += glyphs.effectiveAdvance(gs);
2862 QFixed dist = INT_MAX/256;
2863 if (si.analysis.bidiLevel % 2) {
2864 if (!visual || rtl || (lastLine && i == nItems - 1)) {
2867 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2872 pos -= glyphs.effectiveAdvance(gs);
2877 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
2882 pos += glyphs.effectiveAdvance(ge);
2887 if (!visual || !rtl || (lastLine && i == 0)) {
2889 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2894 pos += glyphs.effectiveAdvance(gs);
2898 QFixed oldPos = pos;
2900 pos += glyphs.effectiveAdvance(gs);
2901 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2911 if (qAbs(x-pos) < dist) {
2913 if (!rtl && i < nItems - 1) {
2917 if (rtl && nchars > 0)
2918 return insertionPoints[lastLine ? nchars : nchars - 1];
2920 return eng->positionInLigature(&si, end, x, pos, -1,
2921 cpos == QTextLine::CursorOnCharacter);
2924 Q_ASSERT(glyph_pos != -1);
2925 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
2926 cpos == QTextLine::CursorOnCharacter);
2929 // right of last item
2930 // qDebug() << "right of last";
2931 int item = visualOrder[nItems-1]+firstItem;
2932 QScriptItem &si = eng->layoutData->items[item];
2935 int pos = si.position;
2936 if (!(si.analysis.bidiLevel % 2))
2937 pos += eng->length(item);
2938 pos = qMax(line.from, pos);
2940 int maxPos = line.from + line_length;
2942 // except for the last line we assume that the
2943 // character between lines is a space and we want
2944 // to position the cursor to the left of that
2946 // ###### breaks with japanese for example
2947 if (this->index < eng->lines.count() - 1)
2950 pos = qMin(pos, maxPos);