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.
78 \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
82 \variable QTextLayout::FormatRange::start
83 Specifies the beginning of the format range within the text layout's text.
87 \variable QTextLayout::FormatRange::length
88 Specifies the numer of characters the format range spans.
92 \variable QTextLayout::FormatRange::format
93 Specifies the format to apply.
97 \class QTextInlineObject
100 \brief The QTextInlineObject class represents an inline object in
103 \ingroup richtext-processing
105 This class is only used if the text layout is used to lay out
106 parts of a QTextDocument.
108 The inline object has various attributes that can be set, for
109 example using, setWidth(), setAscent(), and setDescent(). The
110 rectangle it occupies is given by rect(), and its direction by
111 isRightToLeft(). Its position in the text layout is given by at(),
112 and its format is given by format().
116 \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
118 Creates a new inline object for the item at position \a i in the
123 \fn QTextInlineObject::QTextInlineObject()
129 \fn bool QTextInlineObject::isValid() const
131 Returns true if this inline object is valid; otherwise returns
136 Returns the inline object's rectangle.
138 \sa ascent(), descent(), width()
140 QRectF QTextInlineObject::rect() const
142 QScriptItem& si = eng->layoutData->items[itm];
143 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
147 Returns the inline object's width.
149 \sa ascent(), descent(), rect()
151 qreal QTextInlineObject::width() const
153 return eng->layoutData->items[itm].width.toReal();
157 Returns the inline object's ascent.
159 \sa descent(), width(), rect()
161 qreal QTextInlineObject::ascent() const
163 return eng->layoutData->items[itm].ascent.toReal();
167 Returns the inline object's descent.
169 \sa ascent(), width(), rect()
171 qreal QTextInlineObject::descent() const
173 return eng->layoutData->items[itm].descent.toReal();
177 Returns the inline object's total height. This is equal to
178 ascent() + descent() + 1.
180 \sa ascent(), descent(), width(), rect()
182 qreal QTextInlineObject::height() const
184 return eng->layoutData->items[itm].height().toReal();
188 Sets the inline object's width to \a w.
190 \sa width(), ascent(), descent(), rect()
192 void QTextInlineObject::setWidth(qreal w)
194 eng->layoutData->items[itm].width = QFixed::fromReal(w);
198 Sets the inline object's ascent to \a a.
200 \sa ascent(), setDescent(), width(), rect()
202 void QTextInlineObject::setAscent(qreal a)
204 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
208 Sets the inline object's decent to \a d.
210 \sa descent(), setAscent(), width(), rect()
212 void QTextInlineObject::setDescent(qreal d)
214 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
218 The position of the inline object within the text layout.
220 int QTextInlineObject::textPosition() const
222 return eng->layoutData->items[itm].position;
226 Returns an integer describing the format of the inline object
227 within the text layout.
229 int QTextInlineObject::formatIndex() const
231 return eng->formatIndex(&eng->layoutData->items[itm]);
235 Returns format of the inline object within the text layout.
237 QTextFormat QTextInlineObject::format() const
239 if (!eng->block.docHandle())
240 return QTextFormat();
241 return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
245 Returns if the object should be laid out right-to-left or left-to-right.
247 Qt::LayoutDirection QTextInlineObject::textDirection() const
249 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
256 \brief The QTextLayout class is used to lay out and render text.
258 \ingroup richtext-processing
260 It offers many features expected from a modern text layout
261 engine, including Unicode compliant rendering, line breaking and
262 handling of cursor positioning. It can also produce and render
263 device independent layout, something that is important for WYSIWYG
266 The class has a rather low level API and unless you intend to
267 implement your own text rendering for some specialized widget, you
268 probably won't need to use it directly.
270 QTextLayout can be used with both plain and rich text.
272 QTextLayout can be used to create a sequence of QTextLine
273 instances with given widths and can position them independently
274 on the screen. Once the layout is done, these lines can be drawn
277 The text to be laid out can be provided in the constructor or set with
280 The layout can be seen as a sequence of QTextLine objects; use createLine()
281 to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
284 Here is a code snippet that demonstrates the layout phase:
285 \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
287 The text can then be rendered by calling the layout's draw() function:
288 \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
290 For a given position in the text you can find a valid cursor position with
291 isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
293 The QTextLayout itself can be positioned with setPosition(); it has a
294 boundingRect(), and a minimumWidth() and a maximumWidth().
300 \enum QTextLayout::CursorMode
302 \value SkipCharacters
307 \fn QTextEngine *QTextLayout::engine() const
310 Returns the text engine used to render the text layout.
314 Constructs an empty text layout.
318 QTextLayout::QTextLayout()
319 { d = new QTextEngine(); }
322 Constructs a text layout to lay out the given \a text.
324 QTextLayout::QTextLayout(const QString& text)
326 d = new QTextEngine();
331 Constructs a text layout to lay out the given \a text with the specified
334 All the metric and layout calculations will be done in terms of
335 the paint device, \a paintdevice. If \a paintdevice is 0 the
336 calculations will be done in screen metrics.
338 QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
342 f = QFont(font, paintdevice);
343 d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f);
348 Constructs a text layout to lay out the given \a block.
350 QTextLayout::QTextLayout(const QTextBlock &block)
352 d = new QTextEngine();
357 Destructs the layout.
359 QTextLayout::~QTextLayout()
365 #ifndef QT_NO_RAWFONT
368 Sets a raw font, to be used with QTextLayout::glyphRuns.
369 Note that this only supports the needs of WebKit.
370 Use of this function with e.g. QTextLayout::draw will result
371 in undefined behaviour.
373 void QTextLayout::setRawFont(const QRawFont &rawFont)
375 d->rawFont = rawFont;
376 d->useRawFont = true;
377 d->resetFontEngineCache();
382 Sets the layout's font to the given \a font. The layout is
383 invalidated and must be laid out again.
387 void QTextLayout::setFont(const QFont &font)
390 #ifndef QT_NO_RAWFONT
391 d->useRawFont = false;
393 d->resetFontEngineCache();
397 Returns the current font that is used for the layout, or a default
402 QFont QTextLayout::font() const
408 Sets the layout's text to the given \a string. The layout is
409 invalidated and must be laid out again.
411 Notice that when using this QTextLayout as part of a QTextDocument this
412 method will have no effect.
416 void QTextLayout::setText(const QString& string)
424 Returns the layout's text.
428 QString QTextLayout::text() const
434 Sets the text option structure that controls the layout process to the
439 void QTextLayout::setTextOption(const QTextOption &option)
445 Returns the current text option used to control the layout process.
449 QTextOption QTextLayout::textOption() const
455 Sets the \a position and \a text of the area in the layout that is
456 processed before editing occurs. The layout is
457 invalidated and must be laid out again.
459 \sa preeditAreaPosition(), preeditAreaText()
461 void QTextLayout::setPreeditArea(int position, const QString &text)
463 if (text.isEmpty()) {
466 if (d->specialData->addFormats.isEmpty()) {
467 delete d->specialData;
470 d->specialData->preeditText = QString();
471 d->specialData->preeditPosition = -1;
475 d->specialData = new QTextEngine::SpecialData;
476 d->specialData->preeditPosition = position;
477 d->specialData->preeditText = text;
481 if (d->block.docHandle())
482 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
486 Returns the position of the area in the text layout that will be
487 processed before editing occurs.
489 \sa preeditAreaText()
491 int QTextLayout::preeditAreaPosition() const
493 return d->specialData ? d->specialData->preeditPosition : -1;
497 Returns the text that is inserted in the layout before editing occurs.
499 \sa preeditAreaPosition()
501 QString QTextLayout::preeditAreaText() const
503 return d->specialData ? d->specialData->preeditText : QString();
508 Sets the additional formats supported by the text layout to \a formatList.
510 \sa additionalFormats(), clearAdditionalFormats()
512 void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
514 if (formatList.isEmpty()) {
517 if (d->specialData->preeditText.isEmpty()) {
518 delete d->specialData;
521 d->specialData->addFormats = formatList;
522 d->specialData->addFormatIndices.clear();
525 if (!d->specialData) {
526 d->specialData = new QTextEngine::SpecialData;
527 d->specialData->preeditPosition = -1;
529 d->specialData->addFormats = formatList;
530 d->indexAdditionalFormats();
532 if (d->block.docHandle())
533 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
534 d->resetFontEngineCache();
538 Returns the list of additional formats supported by the text layout.
540 \sa setAdditionalFormats(), clearAdditionalFormats()
542 QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
544 QList<FormatRange> formats;
548 formats = d->specialData->addFormats;
550 if (d->specialData->addFormatIndices.isEmpty())
553 const QTextFormatCollection *collection = d->formats();
555 for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
556 formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
562 Clears the list of additional formats supported by the text layout.
564 \sa additionalFormats(), setAdditionalFormats()
566 void QTextLayout::clearAdditionalFormats()
568 setAdditionalFormats(QList<FormatRange>());
572 Enables caching of the complete layout information if \a enable is
573 true; otherwise disables layout caching. Usually
574 QTextLayout throws most of the layouting information away after a
575 call to endLayout() to reduce memory consumption. If you however
576 want to draw the laid out text directly afterwards enabling caching
577 might speed up drawing significantly.
581 void QTextLayout::setCacheEnabled(bool enable)
583 d->cacheGlyphs = enable;
587 Returns true if the complete layout information is cached; otherwise
590 \sa setCacheEnabled()
592 bool QTextLayout::cacheEnabled() const
594 return d->cacheGlyphs;
598 Sets the visual cursor movement style to the given \a style. If the
599 QTextLayout is backed by a document, you can ignore this and use the option
600 in QTextDocument, this option is for widgets like QLineEdit or custom
601 widgets without a QTextDocument. Default value is QTextCursor::Logical.
603 \sa cursorMoveStyle()
605 void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
607 d->visualMovement = style == Qt::VisualMoveStyle ? true : false;
611 The cursor movement style of this QTextLayout. The default is
612 Qt::LogicalMoveStyle.
614 \sa setCursorMoveStyle()
616 Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const
618 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
622 Begins the layout process.
626 void QTextLayout::beginLayout()
629 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
630 qWarning("QTextLayout::beginLayout: Called while already doing layout");
637 d->layoutData->layoutState = QTextEngine::InLayout;
641 Ends the layout process.
645 void QTextLayout::endLayout()
648 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
649 qWarning("QTextLayout::endLayout: Called without beginLayout()");
653 int l = d->lines.size();
654 if (l && d->lines.at(l-1).length < 0) {
655 QTextLine(l-1, d).setNumColumns(INT_MAX);
657 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
665 Clears the line information in the layout. After having called
666 this function, lineCount() returns 0.
668 void QTextLayout::clearLayout()
674 Returns the next valid cursor position after \a oldPos that
675 respects the given cursor \a mode.
676 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
678 \sa isValidCursorPosition(), previousCursorPosition()
680 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
682 const HB_CharAttributes *attributes = d->attributes();
683 int len = d->block.isValid() ? d->block.length() - 1
684 : d->layoutData->string.length();
685 Q_ASSERT(len <= d->layoutData->string.length());
686 if (!attributes || oldPos < 0 || oldPos >= len)
689 if (mode == SkipCharacters) {
691 while (oldPos < len && !attributes[oldPos].charStop)
694 if (oldPos < len && d->atWordSeparator(oldPos)) {
696 while (oldPos < len && d->atWordSeparator(oldPos))
699 while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
702 while (oldPos < len && d->atSpace(oldPos))
710 Returns the first valid cursor position before \a oldPos that
711 respects the given cursor \a mode.
712 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
714 \sa isValidCursorPosition(), nextCursorPosition()
716 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
718 const HB_CharAttributes *attributes = d->attributes();
719 if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
722 if (mode == SkipCharacters) {
724 while (oldPos && !attributes[oldPos].charStop)
727 while (oldPos && d->atSpace(oldPos-1))
730 if (oldPos && d->atWordSeparator(oldPos-1)) {
732 while (oldPos && d->atWordSeparator(oldPos-1))
735 while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
744 Returns the cursor position to the right of \a oldPos, next to it.
745 It's dependent on the visual position of characters, after bi-directional
748 \sa leftCursorPosition(), nextCursorPosition()
750 int QTextLayout::rightCursorPosition(int oldPos) const
752 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
753 // qDebug("%d -> %d", oldPos, newPos);
758 Returns the cursor position to the left of \a oldPos, next to it.
759 It's dependent on the visual position of characters, after bi-directional
762 \sa rightCursorPosition(), previousCursorPosition()
764 int QTextLayout::leftCursorPosition(int oldPos) const
766 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
767 // qDebug("%d -> %d", oldPos, newPos);
772 Returns true if position \a pos is a valid cursor position.
774 In a Unicode context some positions in the text are not valid
775 cursor positions, because the position is inside a Unicode
776 surrogate or a grapheme cluster.
778 A grapheme cluster is a sequence of two or more Unicode characters
779 that form one indivisible entity on the screen. For example the
780 latin character `\Auml' can be represented in Unicode by two
781 characters, `A' (0x41), and the combining diaresis (0x308). A text
782 cursor can only validly be positioned before or after these two
783 characters, never between them since that wouldn't make sense. In
784 indic languages every syllable forms a grapheme cluster.
786 bool QTextLayout::isValidCursorPosition(int pos) const
788 const HB_CharAttributes *attributes = d->attributes();
789 if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
791 return attributes[pos].charStop;
795 Returns a new text line to be laid out if there is text to be
796 inserted into the layout; otherwise returns an invalid text line.
798 The text layout creates a new line object that starts after the
799 last line in the layout, or at the beginning if the layout is empty.
800 The layout maintains an internal cursor, and each line is filled
801 with text from the cursor position onwards when the
802 QTextLine::setLineWidth() function is called.
804 Once QTextLine::setLineWidth() is called, a new line can be created and
805 filled with text. Repeating this process will lay out the whole block
806 of text contained in the QTextLayout. If there is no text left to be
807 inserted into the layout, the QTextLine returned will not be valid
808 (isValid() will return false).
810 QTextLine QTextLayout::createLine()
813 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
814 qWarning("QTextLayout::createLine: Called without layouting");
818 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
821 int l = d->lines.size();
822 if (l && d->lines.at(l-1).length < 0) {
823 QTextLine(l-1, d).setNumColumns(INT_MAX);
825 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
826 int strlen = d->layoutData->string.length();
827 if (l && from >= strlen) {
828 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
835 line.justified = false;
836 line.gridfitted = false;
838 d->lines.append(line);
839 return QTextLine(l, d);
843 Returns the number of lines in this text layout.
847 int QTextLayout::lineCount() const
849 return d->lines.size();
853 Returns the \a{i}-th line of text in this text layout.
855 \sa lineCount(), lineForTextPosition()
857 QTextLine QTextLayout::lineAt(int i) const
859 return i < lineCount() ? QTextLine(i, d) : QTextLine();
863 Returns the line that contains the cursor position specified by \a pos.
865 \sa isValidCursorPosition(), lineAt()
867 QTextLine QTextLayout::lineForTextPosition(int pos) const
869 int lineNum = d->lineNumberForTextPosition(pos);
870 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
876 The global position of the layout. This is independent of the
877 bounding rectangle and of the layout process.
881 QPointF QTextLayout::position() const
887 Moves the text layout to point \a p.
891 void QTextLayout::setPosition(const QPointF &p)
897 The smallest rectangle that contains all the lines in the layout.
899 QRectF QTextLayout::boundingRect() const
901 if (d->lines.isEmpty())
905 QFixed xmin = d->lines.at(0).x;
906 QFixed ymin = d->lines.at(0).y;
908 for (int i = 0; i < d->lines.size(); ++i) {
909 const QScriptLine &si = d->lines[i];
910 xmin = qMin(xmin, si.x);
911 ymin = qMin(ymin, si.y);
912 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
913 xmax = qMax(xmax, si.x+lineWidth);
914 // ### shouldn't the ascent be used in ymin???
915 ymax = qMax(ymax, si.y+si.height());
917 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
921 The minimum width the layout needs. This is the width of the
922 layout's smallest non-breakable substring.
924 \warning This function only returns a valid value after the layout
929 qreal QTextLayout::minimumWidth() const
931 return d->minWidth.toReal();
935 The maximum width the layout could expand to; this is essentially
936 the width of the entire text.
938 \warning This function only returns a valid value after the layout
943 qreal QTextLayout::maximumWidth() const
945 return d->maxWidth.toReal();
952 void QTextLayout::setFlags(int flags)
954 if (flags & Qt::TextJustificationForced) {
955 d->option.setAlignment(Qt::AlignJustify);
956 d->forceJustification = true;
959 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
960 d->ignoreBidi = true;
961 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
965 static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
966 QPainterPath *region, QRectF boundingRect)
968 const QScriptLine &line = eng->lines[lineNumber];
970 QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
974 const qreal selectionY = pos.y() + line.y.toReal();
975 const qreal lineHeight = line.height().toReal();
977 QFixed lastSelectionX = iterator.x;
978 QFixed lastSelectionWidth;
980 while (!iterator.atEnd()) {
983 QFixed selectionX, selectionWidth;
984 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
985 if (selectionX == lastSelectionX + lastSelectionWidth) {
986 lastSelectionWidth += selectionWidth;
990 if (lastSelectionWidth > 0)
991 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
993 lastSelectionX = selectionX;
994 lastSelectionWidth = selectionWidth;
997 if (lastSelectionWidth > 0)
998 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
1001 static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
1003 return clip.isValid() ? (rect & clip) : rect;
1008 Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
1009 expensive function, and should not be called in a time sensitive context.
1013 \sa draw(), QPainter::drawGlyphRun()
1015 #if !defined(QT_NO_RAWFONT)
1016 QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
1021 length = text().length();
1023 QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
1024 for (int i=0; i<d->lines.size(); ++i) {
1025 if (d->lines[i].from > from + length)
1027 else if (d->lines[i].from + d->lines[i].length >= from) {
1028 QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
1030 for (int j = 0; j < glyphRuns.size(); j++) {
1031 const QGlyphRun &glyphRun = glyphRuns.at(j);
1032 QRawFont rawFont = glyphRun.rawFont();
1034 QFontEngine *fontEngine = rawFont.d->fontEngine;
1035 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1036 QPair<QFontEngine *, int> key(fontEngine, int(flags));
1037 // merge the glyph runs using the same font
1038 if (glyphRunHash.contains(key)) {
1039 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1041 QVector<quint32> indexes = oldGlyphRun.glyphIndexes();
1042 QVector<QPointF> positions = oldGlyphRun.positions();
1044 indexes += glyphRun.glyphIndexes();
1045 positions += glyphRun.positions();
1047 oldGlyphRun.setGlyphIndexes(indexes);
1048 oldGlyphRun.setPositions(positions);
1050 glyphRunHash[key] = glyphRun;
1056 return glyphRunHash.values();
1058 #endif // QT_NO_RAWFONT
1061 Draws the whole layout on the painter \a p at the position specified by \a pos.
1062 The rendered layout includes the given \a selections and is clipped within
1063 the rectangle specified by \a clip.
1065 void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
1067 if (d->lines.isEmpty())
1073 QPointF position = pos + d->position;
1075 QFixed clipy = (INT_MIN/256);
1076 QFixed clipe = (INT_MAX/256);
1077 if (clip.isValid()) {
1078 clipy = QFixed::fromReal(clip.y() - position.y());
1079 clipe = clipy + QFixed::fromReal(clip.height());
1083 int lastLine = d->lines.size();
1084 for (int i = 0; i < d->lines.size(); ++i) {
1086 const QScriptLine &sl = d->lines[i];
1092 if ((sl.y + sl.height()) < clipy) {
1098 QPainterPath excludedRegion;
1099 QPainterPath textDoneRegion;
1100 for (int i = 0; i < selections.size(); ++i) {
1101 FormatRange selection = selections.at(i);
1102 const QBrush bg = selection.format.background();
1104 QPainterPath region;
1105 region.setFillRule(Qt::WindingFill);
1107 for (int line = firstLine; line < lastLine; ++line) {
1108 const QScriptLine &sl = d->lines[line];
1109 QTextLine tl(line, d);
1111 QRectF lineRect(tl.naturalTextRect());
1112 lineRect.translate(position);
1113 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1115 bool isLastLineInBlock = (line == d->lines.size()-1);
1116 int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
1119 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1120 continue; // no actual intersection
1122 const bool selectionStartInLine = sl.from <= selection.start;
1123 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1125 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1126 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1128 region.addRect(clipIfValid(lineRect, clip));
1131 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1132 QRectF fullLineRect(tl.rect());
1133 fullLineRect.translate(position);
1134 fullLineRect.setRight(QFIXED_MAX);
1135 if (!selectionEndInLine)
1136 region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1137 if (!selectionStartInLine)
1138 region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1139 } else if (!selectionEndInLine
1140 && isLastLineInBlock
1141 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1142 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1143 lineRect.height()/4, lineRect.height()), clip));
1148 const QPen oldPen = p->pen();
1149 const QBrush oldBrush = p->brush();
1151 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1152 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1153 p->drawPath(region);
1156 p->setBrush(oldBrush);
1161 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1162 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1164 if (hasBackground) {
1165 selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1166 // don't just clear the property, set an empty brush that overrides a potential
1167 // background brush specified in the text
1168 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1169 selection.format.clearProperty(QTextFormat::OutlinePen);
1172 selection.format.setProperty(SuppressText, !hasText);
1174 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1178 p->setClipPath(region, Qt::IntersectClip);
1180 for (int line = firstLine; line < lastLine; ++line) {
1181 QTextLine l(line, d);
1182 l.draw(p, position, &selection);
1187 textDoneRegion += region;
1190 textDoneRegion -= region;
1193 excludedRegion += region;
1196 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1197 if (!needsTextButNoBackground.isEmpty()){
1199 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1200 FormatRange selection;
1201 selection.start = 0;
1202 selection.length = INT_MAX;
1203 selection.format.setProperty(SuppressBackground, true);
1204 for (int line = firstLine; line < lastLine; ++line) {
1205 QTextLine l(line, d);
1206 l.draw(p, position, &selection);
1211 if (!excludedRegion.isEmpty()) {
1214 QRectF br = boundingRect().translated(position);
1215 br.setRight(QFIXED_MAX);
1217 br = br.intersected(clip);
1219 path -= excludedRegion;
1220 p->setClipPath(path, Qt::IntersectClip);
1223 for (int i = firstLine; i < lastLine; ++i) {
1225 l.draw(p, position);
1227 if (!excludedRegion.isEmpty())
1231 if (!d->cacheGlyphs)
1236 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
1239 Draws a text cursor with the current pen at the given \a position using the
1240 \a painter specified.
1241 The corresponding position within the text is specified by \a cursorPosition.
1243 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
1245 drawCursor(p, pos, cursorPosition, 1);
1249 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
1251 Draws a text cursor with the current pen and the specified \a width at the given \a position using the
1252 \a painter specified.
1253 The corresponding position within the text is specified by \a cursorPosition.
1255 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
1257 if (d->lines.isEmpty())
1263 QPointF position = pos + d->position;
1265 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
1266 int line = d->lineNumberForTextPosition(cursorPosition);
1269 if (line >= d->lines.size())
1272 QTextLine l(line, d);
1273 const QScriptLine &sl = d->lines[line];
1275 qreal x = position.x() + l.cursorToX(cursorPosition);
1279 if (d->visualCursorMovement()) {
1280 if (cursorPosition == sl.from + sl.length)
1282 itm = d->findItem(cursorPosition);
1284 itm = d->findItem(cursorPosition - 1);
1286 QFixed base = sl.base();
1287 QFixed descent = sl.descent;
1288 bool rightToLeft = d->isRightToLeft();
1290 const QScriptItem &si = d->layoutData->items.at(itm);
1294 descent = si.descent;
1295 rightToLeft = si.analysis.bidiLevel % 2;
1297 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1298 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1299 && (p->transform().type() > QTransform::TxTranslate);
1300 if (toggleAntialiasing)
1301 p->setRenderHint(QPainter::Antialiasing);
1302 p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
1303 if (toggleAntialiasing)
1304 p->setRenderHint(QPainter::Antialiasing, false);
1305 if (d->layoutData->hasBidi) {
1306 const int arrow_extent = 4;
1307 int sign = rightToLeft ? -1 : 1;
1308 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1309 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1318 \brief The QTextLine class represents a line of text inside a QTextLayout.
1320 \ingroup richtext-processing
1322 A text line is usually created by QTextLayout::createLine().
1324 After being created, the line can be filled using the setLineWidth()
1325 or setNumColumns() functions. A line has a number of attributes including the
1326 rectangle it occupies, rect(), its coordinates, x() and y(), its
1327 textLength(), width() and naturalTextWidth(), and its ascent() and decent()
1328 relative to the text. The position of the cursor in terms of the
1329 line is available from cursorToX() and its inverse from
1330 xToCursor(). A line can be moved with setPosition().
1334 \enum QTextLine::Edge
1341 \enum QTextLine::CursorPosition
1343 \value CursorBetweenCharacters
1344 \value CursorOnCharacter
1348 \fn QTextLine::QTextLine(int line, QTextEngine *e)
1351 Constructs a new text line using the line at position \a line in
1352 the text engine \a e.
1356 \fn QTextLine::QTextLine()
1358 Creates an invalid line.
1362 \fn bool QTextLine::isValid() const
1364 Returns true if this text line is valid; otherwise returns false.
1368 \fn int QTextLine::lineNumber() const
1370 Returns the position of the line in the text engine.
1375 Returns the line's bounding rectangle.
1377 \sa x(), y(), textLength(), width()
1379 QRectF QTextLine::rect() const
1381 const QScriptLine& sl = eng->lines[index];
1382 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1386 Returns the rectangle covered by the line.
1388 QRectF QTextLine::naturalTextRect() const
1390 const QScriptLine& sl = eng->lines[index];
1391 QFixed x = sl.x + eng->alignLine(sl);
1393 QFixed width = sl.textWidth;
1397 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1401 Returns the line's x position.
1403 \sa rect(), y(), textLength(), width()
1405 qreal QTextLine::x() const
1407 return eng->lines[index].x.toReal();
1411 Returns the line's y position.
1413 \sa x(), rect(), textLength(), width()
1415 qreal QTextLine::y() const
1417 return eng->lines[index].y.toReal();
1421 Returns the line's width as specified by the layout() function.
1423 \sa naturalTextWidth(), x(), y(), textLength(), rect()
1425 qreal QTextLine::width() const
1427 return eng->lines[index].width.toReal();
1432 Returns the line's ascent.
1434 \sa descent(), height()
1436 qreal QTextLine::ascent() const
1438 return eng->lines[index].ascent.toReal();
1442 Returns the line's descent.
1444 \sa ascent(), height()
1446 qreal QTextLine::descent() const
1448 return eng->lines[index].descent.toReal();
1452 Returns the line's height. This is equal to ascent() + descent()
1453 if leading is not included. If leading is included, this equals to
1454 ascent() + descent() + leading().
1456 \sa ascent(), descent(), leading(), setLeadingIncluded()
1458 qreal QTextLine::height() const
1460 return eng->lines[index].height().toReal();
1466 Returns the line's leading.
1468 \sa ascent(), descent(), height()
1470 qreal QTextLine::leading() const
1472 return eng->lines[index].leading.toReal();
1478 Includes positive leading into the line's height if \a included is true;
1479 otherwise does not include leading.
1481 By default, leading is not included.
1483 Note that negative leading is ignored, it must be handled
1484 in the code using the text lines by letting the lines overlap.
1486 \sa leadingIncluded()
1489 void QTextLine::setLeadingIncluded(bool included)
1491 eng->lines[index].leadingIncluded= included;
1498 Returns true if positive leading is included into the line's height;
1499 otherwise returns false.
1501 By default, leading is not included.
1503 \sa setLeadingIncluded()
1505 bool QTextLine::leadingIncluded() const
1507 return eng->lines[index].leadingIncluded;
1511 Returns the width of the line that is occupied by text. This is
1512 always \<= to width(), and is the minimum width that could be used
1513 by layout() without changing the line break position.
1515 qreal QTextLine::naturalTextWidth() const
1517 return eng->lines[index].textWidth.toReal();
1522 Returns the horizontal advance of the text. The advance of the text
1523 is the distance from its position to the next position at which
1524 text would naturally be drawn.
1526 By adding the advance to the position of the text line and using this
1527 as the position of a second text line, you will be able to position
1528 the two lines side-by-side without gaps in-between.
1530 qreal QTextLine::horizontalAdvance() const
1532 return eng->lines[index].textAdvance.toReal();
1536 Lays out the line with the given \a width. The line is filled from
1537 its starting position with as many characters as will fit into
1538 the line. In case the text cannot be split at the end of the line,
1539 it will be filled with additional characters to the next whitespace
1542 void QTextLine::setLineWidth(qreal width)
1544 QScriptLine &line = eng->lines[index];
1545 if (!eng->layoutData) {
1546 qWarning("QTextLine: Can't set a line width while not layouting.");
1550 if (width > QFIXED_MAX)
1553 line.width = QFixed::fromReal(width);
1555 && line.textWidth <= line.width
1556 && line.from + line.length == eng->layoutData->string.length())
1557 // no need to do anything if the line is already layouted and the last one. This optimization helps
1558 // when using things in a single line layout.
1563 layout_helper(INT_MAX);
1567 Lays out the line. The line is filled from its starting position
1568 with as many characters as are specified by \a numColumns. In case
1569 the text cannot be split until \a numColumns characters, the line
1570 will be filled with as many characters to the next whitespace or
1573 void QTextLine::setNumColumns(int numColumns)
1575 QScriptLine &line = eng->lines[index];
1576 line.width = QFIXED_MAX;
1579 layout_helper(numColumns);
1583 Lays out the line. The line is filled from its starting position
1584 with as many characters as are specified by \a numColumns. In case
1585 the text cannot be split until \a numColumns characters, the line
1586 will be filled with as many characters to the next whitespace or
1587 end of the text. The provided \a alignmentWidth is used as reference
1588 width for alignment.
1590 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
1592 QScriptLine &line = eng->lines[index];
1593 line.width = QFixed::fromReal(alignmentWidth);
1596 layout_helper(numColumns);
1600 #define LB_DEBUG qDebug
1602 #define LB_DEBUG if (0) qDebug
1607 struct LineBreakHelper
1610 : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
1611 manualWrap(false), whiteSpaceOrObject(true)
1616 QScriptLine tmpData;
1617 QScriptLine spaceData;
1619 QGlyphLayout glyphs;
1623 int currentPosition;
1624 glyph_t previousGlyph;
1627 QFixed softHyphenWidth;
1628 QFixed rightBearing;
1629 QFixed minimumRightBearing;
1631 QFontEngine *fontEngine;
1632 const unsigned short *logClusters;
1635 bool whiteSpaceOrObject;
1637 bool checkFullOtherwiseExtend(QScriptLine &line);
1639 QFixed calculateNewWidth(const QScriptLine &line) const {
1640 return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
1641 - qMin(rightBearing, QFixed());
1644 inline glyph_t currentGlyph() const
1646 Q_ASSERT(currentPosition > 0);
1647 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1649 return glyphs.glyphs[logClusters[currentPosition - 1]];
1652 inline void saveCurrentGlyph()
1655 if (currentPosition > 0 &&
1656 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1657 previousGlyph = currentGlyph(); // needed to calculate right bearing later
1661 inline void adjustRightBearing(glyph_t glyph)
1664 fontEngine->getGlyphBearings(glyph, 0, &rb);
1665 rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
1668 inline void adjustRightBearing()
1670 if (currentPosition <= 0)
1672 adjustRightBearing(currentGlyph());
1675 inline void adjustPreviousRightBearing()
1677 if (previousGlyph > 0)
1678 adjustRightBearing(previousGlyph);
1681 inline void resetRightBearing()
1683 rightBearing = QFixed(1); // Any positive number is defined as invalid since only
1684 // negative right bearings are interesting to us.
1688 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1690 LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1692 QFixed newWidth = calculateNewWidth(line);
1693 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1696 minw = qMax(minw, tmpData.textWidth);
1698 line.textWidth += spaceData.textWidth;
1700 line.length += spaceData.length;
1701 tmpData.textWidth = 0;
1703 spaceData.textWidth = 0;
1704 spaceData.length = 0;
1709 } // anonymous namespace
1712 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
1713 const QScriptItem ¤t, const unsigned short *logClusters,
1714 const QGlyphLayout &glyphs)
1716 int glyphPosition = logClusters[pos];
1717 do { // got to the first next cluster
1720 } while (pos < end && logClusters[pos] == glyphPosition);
1721 do { // calculate the textWidth for the rest of the current cluster.
1722 if (!glyphs.attributes[glyphPosition].dontPrint)
1723 line.textWidth += glyphs.advances_x[glyphPosition];
1725 } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
1727 Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
1734 void QTextLine::layout_helper(int maxGlyphs)
1736 QScriptLine &line = eng->lines[index];
1738 line.trailingSpaces = 0;
1740 line.hasTrailingSpaces = false;
1742 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
1743 line.setDefaultHeight(eng);
1747 Q_ASSERT(line.from < eng->layoutData->string.length());
1749 LineBreakHelper lbh;
1751 lbh.maxGlyphs = maxGlyphs;
1753 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1754 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1755 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1758 int newItem = eng->findItem(line.from);
1760 LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
1762 Qt::Alignment alignment = eng->option.alignment();
1764 const HB_CharAttributes *attributes = eng->attributes();
1767 lbh.currentPosition = line.from;
1769 lbh.logClusters = eng->layoutData->logClustersPtr;
1770 lbh.previousGlyph = 0;
1772 while (newItem < eng->layoutData->items.size()) {
1773 lbh.resetRightBearing();
1774 lbh.softHyphenWidth = 0;
1775 if (newItem != item) {
1777 const QScriptItem ¤t = eng->layoutData->items[item];
1778 if (!current.num_glyphs) {
1780 attributes = eng->attributes();
1783 lbh.logClusters = eng->layoutData->logClustersPtr;
1785 lbh.currentPosition = qMax(line.from, current.position);
1786 end = current.position + eng->length(item);
1787 lbh.glyphs = eng->shapedGlyphs(¤t);
1788 QFontEngine *fontEngine = eng->fontEngine(current);
1789 if (lbh.fontEngine != fontEngine) {
1790 lbh.fontEngine = fontEngine;
1791 lbh.minimumRightBearing = qMin(QFixed(),
1792 QFixed::fromReal(fontEngine->minRightBearing()));
1795 const QScriptItem ¤t = eng->layoutData->items[item];
1797 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1798 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1800 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1801 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1803 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1804 lbh.whiteSpaceOrObject = true;
1805 if (lbh.checkFullOtherwiseExtend(line))
1808 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1809 QFixed tabWidth = eng->calculateTabWidth(item, x);
1811 lbh.spaceData.textWidth += tabWidth;
1812 lbh.spaceData.length++;
1815 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1816 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1818 if (lbh.checkFullOtherwiseExtend(line))
1820 } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1821 lbh.whiteSpaceOrObject = true;
1822 // if the line consists only of the line separator make sure
1823 // we have a sane height
1824 if (!line.length && !lbh.tmpData.length)
1825 line.setDefaultHeight(eng);
1826 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1827 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1828 current, lbh.logClusters, lbh.glyphs);
1830 lbh.tmpData.length++;
1831 lbh.adjustPreviousRightBearing();
1833 line += lbh.tmpData;
1835 } else if (current.analysis.flags == QScriptAnalysis::Object) {
1836 lbh.whiteSpaceOrObject = true;
1837 lbh.tmpData.length++;
1839 QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
1840 if (eng->block.docHandle())
1841 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
1843 lbh.tmpData.textWidth += current.width;
1847 if (lbh.checkFullOtherwiseExtend(line))
1849 } else if (attributes[lbh.currentPosition].whiteSpace) {
1850 lbh.whiteSpaceOrObject = true;
1851 while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
1852 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1853 current, lbh.logClusters, lbh.glyphs);
1855 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
1856 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
1860 lbh.whiteSpaceOrObject = false;
1861 bool sb_or_ws = false;
1862 lbh.saveCurrentGlyph();
1864 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1865 current, lbh.logClusters, lbh.glyphs);
1867 if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) {
1870 } else if (breakany && attributes[lbh.currentPosition].charStop) {
1873 } while (lbh.currentPosition < end);
1874 lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
1876 if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) {
1877 // if we are splitting up a word because of
1878 // a soft hyphen then we ...
1880 // a) have to take the width of the soft hyphen into
1881 // account to see if the first syllable(s) /and/
1882 // the soft hyphen fit into the line
1884 // b) if we are so short of available width that the
1885 // soft hyphen is the first breakable position, then
1886 // we don't want to show it. However we initially
1887 // have to take the width for it into account so that
1888 // the text document layout sees the overflow and
1889 // switch to break-anywhere mode, in which we
1890 // want the soft-hyphen to slip into the next line
1891 // and thus become invisible again.
1894 lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1896 lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1899 // The actual width of the text needs to take the right bearing into account. The
1900 // right bearing is left-ward, which means that if the rightmost pixel is to the right
1901 // of the advance of the glyph, the bearing will be negative. We flip the sign
1902 // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
1903 // We ignore the right bearing if the minimum negative bearing is too little to
1904 // expand the text beyond the edge.
1905 if (sb_or_ws|breakany) {
1906 QFixed rightBearing = lbh.rightBearing; // store previous right bearing
1907 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
1908 lbh.adjustRightBearing();
1909 if (lbh.checkFullOtherwiseExtend(line)) {
1910 // we are too wide, fix right bearing
1911 if (rightBearing <= 0)
1912 lbh.rightBearing = rightBearing; // take from cache
1914 lbh.adjustPreviousRightBearing();
1917 line.textWidth += lbh.softHyphenWidth;
1923 lbh.saveCurrentGlyph();
1925 if (lbh.currentPosition == end)
1928 LB_DEBUG("reached end of line");
1929 lbh.checkFullOtherwiseExtend(line);
1931 if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
1932 lbh.adjustRightBearing();
1933 line.textAdvance = line.textWidth;
1934 line.textWidth -= qMin(QFixed(), lbh.rightBearing);
1936 if (line.length == 0) {
1937 LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
1938 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
1939 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
1940 line += lbh.tmpData;
1943 LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
1944 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
1945 LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
1947 if (lbh.manualWrap) {
1948 eng->minWidth = qMax(eng->minWidth, line.textWidth);
1949 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
1951 eng->minWidth = qMax(eng->minWidth, lbh.minw);
1952 eng->maxWidth += line.textWidth;
1955 if (line.textWidth > 0 && item < eng->layoutData->items.size())
1956 eng->maxWidth += lbh.spaceData.textWidth;
1957 if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
1958 line.textWidth += lbh.spaceData.textWidth;
1959 if (lbh.spaceData.length) {
1960 line.trailingSpaces = lbh.spaceData.length;
1961 line.hasTrailingSpaces = true;
1964 line.justified = false;
1965 line.gridfitted = false;
1967 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
1968 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
1969 || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
1971 eng->option.setWrapMode(QTextOption::WrapAnywhere);
1974 layout_helper(lbh.maxGlyphs);
1975 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
1981 Moves the line to position \a pos.
1983 void QTextLine::setPosition(const QPointF &pos)
1985 eng->lines[index].x = QFixed::fromReal(pos.x());
1986 eng->lines[index].y = QFixed::fromReal(pos.y());
1990 Returns the line's position relative to the text layout's position.
1992 QPointF QTextLine::position() const
1994 return QPointF(eng->lines[index].x.toReal(), eng->lines[index].y.toReal());
1997 // ### DOC: I have no idea what this means/does.
1998 // You create a text layout with a string of text. Once you laid
1999 // it out, it contains a number of QTextLines. from() returns the position
2000 // inside the text string where this line starts. If you e.g. has a
2001 // text of "This is a string", laid out into two lines (the second
2002 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
2003 // layout.lineAt(1).from() == 8.
2005 Returns the start of the line from the beginning of the string
2006 passed to the QTextLayout.
2008 int QTextLine::textStart() const
2010 return eng->lines[index].from;
2014 Returns the length of the text in the line.
2016 \sa naturalTextWidth()
2018 int QTextLine::textLength() const
2020 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2021 && eng->block.isValid() && index == eng->lines.count()-1) {
2022 return eng->lines[index].length - 1;
2024 return eng->lines[index].length + eng->lines[index].trailingSpaces;
2027 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
2028 int start, int glyph_start)
2030 int ge = glyph_start + gf.glyphs.numGlyphs;
2031 int gs = glyph_start;
2032 int end = start + gf.num_chars;
2033 unsigned short *logClusters = eng->logClusters(&si);
2034 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2035 QFixed orig_width = gf.width;
2037 int *ul = eng->underlinePositions;
2039 while (*ul != -1 && *ul < start)
2041 bool rtl = si.analysis.bidiLevel % 2;
2048 if (ul && *ul != -1 && *ul < end) {
2050 gtmp = logClusters[*ul-si.position];
2053 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2054 gf.num_chars = stmp - start;
2055 gf.chars = eng->layoutData->string.unicode() + start;
2058 w += glyphs.effectiveAdvance(gs);
2066 QPainterPrivate::get(p)->drawTextItem(QPointF(x.toReal(), y.toReal()), gf, eng);
2069 if (ul && *ul != -1 && *ul < end) {
2071 gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
2073 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2074 gf.num_chars = stmp - start;
2075 gf.chars = eng->layoutData->string.unicode() + start;
2076 gf.logClusters = logClusters + start - si.position;
2079 w += glyphs.effectiveAdvance(gs);
2084 gf.underlineStyle = QTextCharFormat::SingleUnderline;
2087 QPainterPrivate::get(p)->drawTextItem(QPointF(x.toReal(), y.toReal()), gf, eng);
2090 gf.underlineStyle = QTextCharFormat::NoUnderline;
2096 gf.width = orig_width;
2100 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
2102 QBrush c = chf.foreground();
2103 if (c.style() == Qt::NoBrush) {
2104 p->setPen(defaultPen);
2107 QBrush bg = chf.background();
2108 if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
2110 if (c.style() != Qt::NoBrush) {
2111 p->setPen(QPen(c, 0));
2116 #if !defined(QT_NO_RAWFONT)
2117 static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &glyphLayout,
2118 const QPointF &pos, const QGlyphRun::GlyphRunFlags &flags,
2119 const QFixed &selectionX, const QFixed &selectionWidth)
2123 // Make a font for this particular engine
2125 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2126 fontD->fontEngine = fontEngine;
2127 fontD->thread = QThread::currentThread();
2128 fontD->fontEngine->ref.ref();
2129 QVarLengthArray<glyph_t> glyphsArray;
2130 QVarLengthArray<QFixedPoint> positionsArray;
2132 QTextItem::RenderFlags renderFlags;
2133 if (flags.testFlag(QGlyphRun::Overline))
2134 renderFlags |= QTextItem::Overline;
2135 if (flags.testFlag(QGlyphRun::Underline))
2136 renderFlags |= QTextItem::Underline;
2137 if (flags.testFlag(QGlyphRun::StrikeOut))
2138 renderFlags |= QTextItem::StrikeOut;
2139 if (flags.testFlag(QGlyphRun::RightToLeft))
2140 renderFlags |= QTextItem::RightToLeft;
2142 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2144 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2146 qreal fontHeight = font.ascent() + font.descent();
2149 QVector<quint32> glyphs;
2150 QVector<QPointF> positions;
2151 for (int i=0; i<glyphsArray.size(); ++i) {
2152 glyphs.append(glyphsArray.at(i) & 0xffffff);
2154 QPointF position = positionsArray.at(i).toPointF() + pos;
2155 positions.append(position);
2158 maxY = minY = position.y();
2160 minY = qMin(minY, position.y());
2161 maxY = qMax(maxY, position.y());
2165 qreal height = maxY + fontHeight - minY;
2167 glyphRun.setGlyphIndexes(glyphs);
2168 glyphRun.setPositions(positions);
2169 glyphRun.setFlags(flags);
2170 glyphRun.setRawFont(font);
2172 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY, selectionWidth.toReal(), height));
2178 Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
2179 in the range defined by \a from and \a length. The \a from index is relative to the beginning
2180 of the text in the containing QTextLayout, and the range must be within the range of QTextLine
2181 as given by functions textStart() and textLength().
2183 If \a from is negative, it will default to textStart(), and if \a length is negative it will
2184 default to the return value of textLength().
2188 \sa QTextLayout::glyphRuns()
2190 QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
2192 const QScriptLine &line = eng->lines[index];
2194 if (line.length == 0)
2195 return QList<QGlyphRun>();
2201 length = textLength();
2204 return QList<QGlyphRun>();
2206 QTextLayout::FormatRange selection;
2207 selection.start = from;
2208 selection.length = length;
2210 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2211 qreal y = line.y.toReal() + line.base().toReal();
2212 QList<QGlyphRun> glyphRuns;
2213 while (!iterator.atEnd()) {
2214 QScriptItem &si = iterator.next();
2215 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2218 QPointF pos(iterator.x.toReal(), y);
2219 if (from >= 0 && length >= 0 &&
2220 (from >= si.position + eng->length(&si)
2221 || from + length <= si.position
2222 || from + length <= iterator.itemStart
2223 || from >= iterator.itemEnd)) {
2228 QGlyphRun::GlyphRunFlags flags;
2229 if (!eng->useRawFont) {
2230 font = eng->font(si);
2231 if (font.overline())
2232 flags |= QGlyphRun::Overline;
2233 if (font.underline())
2234 flags |= QGlyphRun::Underline;
2235 if (font.strikeOut())
2236 flags |= QGlyphRun::StrikeOut;
2240 if (si.analysis.bidiLevel % 2) {
2241 flags |= QGlyphRun::RightToLeft;
2245 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2246 int relativeTo = qMin(iterator.itemEnd - 1, from + length - 1) - si.position;
2248 unsigned short *logClusters = eng->logClusters(&si);
2249 int glyphsStart = logClusters[relativeFrom];
2250 int glyphsEnd = (relativeTo == eng->length(&si))
2252 : logClusters[relativeTo];
2253 // the glyph index right next to the requested range
2254 int nextGlyphIndex = relativeTo < eng->length(&si) - 1 ? logClusters[relativeTo + 1] : si.num_glyphs;
2255 if (nextGlyphIndex - 1 > glyphsEnd)
2256 glyphsEnd = nextGlyphIndex - 1;
2257 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2258 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2260 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2261 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2263 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2265 // Calculate new x position of glyph layout for a subset. This becomes somewhat complex
2266 // when we're breaking a RTL script item, since the expected position passed into
2267 // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
2268 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2269 for (int i=itemGlyphsStart; i<glyphsStart; ++i) {
2270 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2271 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2272 glyphLayout.advances_y[i].toReal());
2274 } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2275 for (int i=itemGlyphsEnd; i>glyphsEnd; --i) {
2276 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2277 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2278 glyphLayout.advances_y[i].toReal());
2282 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2286 iterator.getSelectionBounds(&x, &width);
2288 if (glyphLayout.numGlyphs > 0) {
2289 QFontEngine *mainFontEngine = eng->fontEngine(si);
2291 if (mainFontEngine->type() == QFontEngine::Multi) {
2292 QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
2293 int end = rtl ? glyphLayout.numGlyphs : 0;
2294 int start = rtl ? end : 0;
2295 int which = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
2296 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs);
2297 rtl ? --start : ++end) {
2298 const int e = glyphLayout.glyphs[rtl ? start - 1 : end] >> 24;
2302 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2303 multiFontEngine->ensureEngineAt(which);
2305 QGlyphRun::GlyphRunFlags subFlags = flags;
2306 if (start == 0 && startsInsideLigature)
2307 subFlags |= QGlyphRun::SplitLigature;
2309 glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
2310 subLayout, pos, subFlags, x, width));
2311 for (int i = 0; i < subLayout.numGlyphs; i++) {
2312 pos += QPointF(subLayout.advances_x[i].toReal(),
2313 subLayout.advances_y[i].toReal());
2323 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2324 multiFontEngine->ensureEngineAt(which);
2326 QGlyphRun::GlyphRunFlags subFlags = flags;
2327 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2328 subFlags |= QGlyphRun::SplitLigature;
2330 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2331 subLayout, pos, subFlags, x, width);
2332 if (!glyphRun.isEmpty())
2333 glyphRuns.append(glyphRun);
2335 if (startsInsideLigature || endsInsideLigature)
2336 flags |= QGlyphRun::SplitLigature;
2337 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags, x,
2339 if (!glyphRun.isEmpty())
2340 glyphRuns.append(glyphRun);
2347 #endif // QT_NO_RAWFONT
2350 \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
2352 Draws a line on the given \a painter at the specified \a position.
2353 The \a selection is reserved for internal use.
2355 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
2357 #ifndef QT_NO_RAWFONT
2358 // Not intended to work with rawfont
2359 Q_ASSERT(!eng->useRawFont);
2361 const QScriptLine &line = eng->lines[index];
2362 QPen pen = p->pen();
2364 bool noText = (selection && selection->format.property(SuppressText).toBool());
2368 && selection->start <= line.from
2369 && selection->start + selection->length > line.from) {
2371 const qreal lineHeight = line.height().toReal();
2372 QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
2373 lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
2374 setPenAndDrawBackground(p, QPen(), selection->format, r);
2381 QTextLineItemIterator iterator(eng, index, pos, selection);
2382 QFixed lineBase = line.base();
2383 eng->clearDecorations();
2384 eng->enableDelayDecorations();
2386 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2388 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2389 while (!iterator.atEnd()) {
2390 QScriptItem &si = iterator.next();
2392 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2395 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2396 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2399 QFixed itemBaseLine = y;
2400 QFont f = eng->font(si);
2401 QTextCharFormat format;
2403 if (eng->hasFormats() || selection) {
2404 format = eng->format(&si);
2405 if (suppressColors) {
2406 format.clearForeground();
2407 format.clearBackground();
2408 format.clearProperty(QTextFormat::TextUnderlineColor);
2411 format.merge(selection->format);
2413 setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2414 iterator.itemWidth.toReal(), line.height().toReal()));
2416 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2417 if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
2418 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2419 QFixed height = fe->ascent() + fe->descent();
2420 if (valign == QTextCharFormat::AlignSubScript)
2421 itemBaseLine += height / 6;
2422 else if (valign == QTextCharFormat::AlignSuperScript)
2423 itemBaseLine -= height / 2;
2427 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2429 if (eng->hasFormats()) {
2431 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
2432 QFixed itemY = y - si.ascent;
2433 if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
2434 itemY = y - lineBase;
2437 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2439 eng->docLayout()->drawInlineObject(p, itemRect,
2440 QTextInlineObject(iterator.item, eng),
2441 si.position + eng->block.position(),
2444 QBrush bg = format.brushProperty(ObjectSelectionBrush);
2445 if (bg.style() != Qt::NoBrush) {
2446 QColor c = bg.color();
2448 p->fillRect(itemRect, c);
2451 } else { // si.isTab
2452 QFont f = eng->font(si);
2453 QTextItemInt gf(si, &f, format);
2456 gf.width = iterator.itemWidth;
2457 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2458 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2459 QChar visualTab(0x2192);
2460 int w = QFontMetrics(f).width(visualTab);
2461 qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
2463 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2464 iterator.itemWidth.toReal(), line.height().toReal()),
2468 p->drawText(QPointF(iterator.x.toReal() + x,
2469 y.toReal()), visualTab);
2479 unsigned short *logClusters = eng->logClusters(&si);
2480 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2482 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2483 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2484 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2485 gf.logClusters = logClusters + iterator.itemStart - si.position;
2486 gf.width = iterator.itemWidth;
2487 gf.justified = line.justified;
2488 gf.initWithScriptItem(si);
2490 Q_ASSERT(gf.fontEngine);
2492 if (eng->underlinePositions) {
2493 // can't have selections in this case
2494 drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
2496 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2497 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2499 path.setFillRule(Qt::WindingFill);
2501 if (gf.glyphs.numGlyphs)
2502 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2504 const QFontEngine *fe = gf.fontEngine;
2505 const qreal lw = fe->lineThickness().toReal();
2506 if (gf.flags & QTextItem::Underline) {
2507 qreal offs = fe->underlinePosition().toReal();
2508 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2510 if (gf.flags & QTextItem::Overline) {
2511 qreal offs = fe->ascent().toReal() + 1;
2512 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2514 if (gf.flags & QTextItem::StrikeOut) {
2515 qreal offs = fe->ascent().toReal() / 3;
2516 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2521 p->setRenderHint(QPainter::Antialiasing);
2522 //Currently QPen with a Qt::NoPen style still returns a default
2523 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
2524 if (p->pen().style() == Qt::NoPen)
2525 p->setBrush(Qt::NoBrush);
2527 p->setBrush(p->pen().brush());
2529 p->setPen(format.textOutline());
2534 gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
2535 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2538 if (si.analysis.flags == QScriptAnalysis::Space
2539 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2540 QBrush c = format.foreground();
2541 if (c.style() != Qt::NoBrush)
2542 p->setPen(c.color());
2543 QChar visualSpace((ushort)0xb7);
2544 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2548 eng->drawDecorations(p);
2550 if (eng->hasFormats())
2555 \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
2561 Converts the cursor position \a cursorPos to the corresponding x position
2562 inside the line, taking account of the \a edge.
2564 If \a cursorPos is not a valid cursor position, the nearest valid
2565 cursor position will be used instead, and cpos will be modified to
2566 point to this valid cursor position.
2570 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
2572 if (!eng->layoutData)
2575 const QScriptLine &line = eng->lines[index];
2576 bool lastLine = index >= eng->lines.size() - 1;
2579 x += eng->alignLine(line);
2581 if (!index && !eng->layoutData->items.size()) {
2586 int pos = *cursorPos;
2588 const HB_CharAttributes *attributes = eng->attributes();
2589 while (pos < line.from + line.length && !attributes[pos].charStop)
2591 if (pos == line.from + (int)line.length) {
2592 // end of line ensure we have the last item on the line
2593 itm = eng->findItem(pos-1);
2596 itm = eng->findItem(pos);
2597 eng->shapeLine(line);
2599 const QScriptItem *si = &eng->layoutData->items[itm];
2600 if (!si->num_glyphs)
2602 pos -= si->position;
2604 QGlyphLayout glyphs = eng->shapedGlyphs(si);
2605 unsigned short *logClusters = eng->logClusters(si);
2606 Q_ASSERT(logClusters);
2608 int l = eng->length(itm);
2614 int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
2615 if (edge == Trailing && glyph_pos < si->num_glyphs) {
2616 // trailing edge is leading edge of next cluster
2618 while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2622 bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
2624 int lineEnd = line.from + line.length;
2626 // add the items left of the cursor
2628 int firstItem = eng->findItem(line.from);
2629 int lastItem = eng->findItem(lineEnd - 1);
2630 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2632 QVarLengthArray<int> visualOrder(nItems);
2633 QVarLengthArray<uchar> levels(nItems);
2634 for (int i = 0; i < nItems; ++i)
2635 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2636 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2638 for (int i = 0; i < nItems; ++i) {
2639 int item = visualOrder[i]+firstItem;
2642 QScriptItem &si = eng->layoutData->items[item];
2646 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2650 int start = qMax(line.from, si.position);
2651 int end = qMin(lineEnd, si.position + eng->length(item));
2653 logClusters = eng->logClusters(&si);
2655 int gs = logClusters[start-si.position];
2656 int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
2658 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2661 x += glyphs.effectiveAdvance(gs);
2666 logClusters = eng->logClusters(si);
2667 glyphs = eng->shapedGlyphs(si);
2668 if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
2669 if (pos == (reverse ? 0 : l))
2672 bool rtl = eng->isRightToLeft();
2673 bool visual = eng->visualCursorMovement();
2674 int end = qMin(lineEnd, si->position + l) - si->position;
2676 int glyph_end = end == l ? si->num_glyphs : logClusters[end];
2677 int glyph_start = glyph_pos;
2678 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
2680 for (int i = glyph_end - 1; i >= glyph_start; i--)
2681 x += glyphs.effectiveAdvance(i);
2683 int start = qMax(line.from - si->position, 0);
2684 int glyph_start = logClusters[start];
2685 int glyph_end = glyph_pos;
2686 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
2688 for (int i = glyph_start; i <= glyph_end; i++)
2689 x += glyphs.effectiveAdvance(i);
2691 x += eng->offsetInLigature(si, pos, end, glyph_pos);
2694 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.width)
2697 *cursorPos = pos + si->position;
2702 \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
2704 Converts the x-coordinate \a x, to the nearest matching cursor
2705 position, depending on the cursor position type, \a cpos.
2709 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
2711 QFixed x = QFixed::fromReal(_x);
2712 const QScriptLine &line = eng->lines[index];
2713 bool lastLine = index >= eng->lines.size() - 1;
2714 int lineNum = index;
2716 if (!eng->layoutData)
2719 int line_length = textLength();
2724 int firstItem = eng->findItem(line.from);
2725 int lastItem = eng->findItem(line.from + line_length - 1);
2726 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2732 x -= eng->alignLine(line);
2733 // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
2735 QVarLengthArray<int> visualOrder(nItems);
2736 QVarLengthArray<unsigned char> levels(nItems);
2737 for (int i = 0; i < nItems; ++i)
2738 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2739 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2741 bool visual = eng->visualCursorMovement();
2743 // left of first item
2744 int item = visualOrder[0]+firstItem;
2745 QScriptItem &si = eng->layoutData->items[item];
2748 int pos = si.position;
2749 if (si.analysis.bidiLevel % 2)
2750 pos += eng->length(item);
2751 pos = qMax(line.from, pos);
2752 pos = qMin(line.from + line_length, pos);
2754 } else if (x < line.textWidth
2755 || (line.justified && x < line.width)) {
2756 // has to be in one of the runs
2758 bool rtl = eng->isRightToLeft();
2760 eng->shapeLine(line);
2761 QVector<int> insertionPoints;
2763 eng->insertionPointsForLine(lineNum, insertionPoints);
2765 for (int i = 0; i < nItems; ++i) {
2766 int item = visualOrder[i]+firstItem;
2767 QScriptItem &si = eng->layoutData->items[item];
2768 int item_length = eng->length(item);
2769 // qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
2771 int start = qMax(line.from - si.position, 0);
2772 int end = qMin(line.from + line_length - si.position, item_length);
2774 unsigned short *logClusters = eng->logClusters(&si);
2776 int gs = logClusters[start];
2777 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
2778 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2780 QFixed item_width = 0;
2781 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2782 item_width = si.width;
2786 item_width += glyphs.effectiveAdvance(g);
2790 // qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
2792 if (pos + item_width < x) {
2797 // qDebug(" inside run");
2798 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2799 if (cpos == QTextLine::CursorOnCharacter)
2801 bool left_half = (x - pos) < item_width/2;
2803 if (bool(si.analysis.bidiLevel % 2) != left_half)
2805 return si.position + 1;
2810 // has to be inside run
2811 if (cpos == QTextLine::CursorOnCharacter) {
2812 if (si.analysis.bidiLevel % 2) {
2816 if (glyphs.attributes[gs].clusterStart) {
2823 pos -= glyphs.effectiveAdvance(gs);
2829 if (glyphs.attributes[gs].clusterStart) {
2835 pos += glyphs.effectiveAdvance(gs);
2840 QFixed dist = INT_MAX/256;
2841 if (si.analysis.bidiLevel % 2) {
2842 if (!visual || rtl || (lastLine && i == nItems - 1)) {
2845 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2850 pos -= glyphs.effectiveAdvance(gs);
2855 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
2860 pos += glyphs.effectiveAdvance(ge);
2865 if (!visual || !rtl || (lastLine && i == 0)) {
2867 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2872 pos += glyphs.effectiveAdvance(gs);
2876 QFixed oldPos = pos;
2878 pos += glyphs.effectiveAdvance(gs);
2879 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2889 if (qAbs(x-pos) < dist) {
2891 if (!rtl && i < nItems - 1) {
2895 if (rtl && nchars > 0)
2896 return insertionPoints[lastLine ? nchars : nchars - 1];
2898 return eng->positionInLigature(&si, end, x, pos, -1,
2899 cpos == QTextLine::CursorOnCharacter);
2902 Q_ASSERT(glyph_pos != -1);
2903 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
2904 cpos == QTextLine::CursorOnCharacter);
2907 // right of last item
2908 // qDebug() << "right of last";
2909 int item = visualOrder[nItems-1]+firstItem;
2910 QScriptItem &si = eng->layoutData->items[item];
2913 int pos = si.position;
2914 if (!(si.analysis.bidiLevel % 2))
2915 pos += eng->length(item);
2916 pos = qMax(line.from, pos);
2918 int maxPos = line.from + line_length;
2920 // except for the last line we assume that the
2921 // character between lines is a space and we want
2922 // to position the cursor to the left of that
2924 // ###### breaks with japanese for example
2925 if (this->index < eng->lines.count() - 1)
2928 pos = qMin(pos, maxPos);