1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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"
64 #if !defined(QT_NO_FREETYPE)
65 # include "qfontengine_ft_p.h"
70 #define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
71 #define SuppressText 0x5012
72 #define SuppressBackground 0x513
75 \class QTextLayout::FormatRange
78 \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
79 for a specified area in the text layout's content.
81 \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
85 \variable QTextLayout::FormatRange::start
86 Specifies the beginning of the format range within the text layout's text.
90 \variable QTextLayout::FormatRange::length
91 Specifies the numer of characters the format range spans.
95 \variable QTextLayout::FormatRange::format
96 Specifies the format to apply.
100 \class QTextInlineObject
103 \brief The QTextInlineObject class represents an inline object in
106 \ingroup richtext-processing
108 This class is only used if the text layout is used to lay out
109 parts of a QTextDocument.
111 The inline object has various attributes that can be set, for
112 example using, setWidth(), setAscent(), and setDescent(). The
113 rectangle it occupies is given by rect(), and its direction by
114 isRightToLeft(). Its position in the text layout is given by at(),
115 and its format is given by format().
119 \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
121 Creates a new inline object for the item at position \a i in the
126 \fn QTextInlineObject::QTextInlineObject()
132 \fn bool QTextInlineObject::isValid() const
134 Returns true if this inline object is valid; otherwise returns
139 Returns the inline object's rectangle.
141 \sa ascent(), descent(), width()
143 QRectF QTextInlineObject::rect() const
145 QScriptItem& si = eng->layoutData->items[itm];
146 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
150 Returns the inline object's width.
152 \sa ascent(), descent(), rect()
154 qreal QTextInlineObject::width() const
156 return eng->layoutData->items[itm].width.toReal();
160 Returns the inline object's ascent.
162 \sa descent(), width(), rect()
164 qreal QTextInlineObject::ascent() const
166 return eng->layoutData->items[itm].ascent.toReal();
170 Returns the inline object's descent.
172 \sa ascent(), width(), rect()
174 qreal QTextInlineObject::descent() const
176 return eng->layoutData->items[itm].descent.toReal();
180 Returns the inline object's total height. This is equal to
181 ascent() + descent() + 1.
183 \sa ascent(), descent(), width(), rect()
185 qreal QTextInlineObject::height() const
187 return eng->layoutData->items[itm].height().toReal();
191 Sets the inline object's width to \a w.
193 \sa width(), ascent(), descent(), rect()
195 void QTextInlineObject::setWidth(qreal w)
197 eng->layoutData->items[itm].width = QFixed::fromReal(w);
201 Sets the inline object's ascent to \a a.
203 \sa ascent(), setDescent(), width(), rect()
205 void QTextInlineObject::setAscent(qreal a)
207 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
211 Sets the inline object's decent to \a d.
213 \sa descent(), setAscent(), width(), rect()
215 void QTextInlineObject::setDescent(qreal d)
217 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
221 The position of the inline object within the text layout.
223 int QTextInlineObject::textPosition() const
225 return eng->layoutData->items[itm].position;
229 Returns an integer describing the format of the inline object
230 within the text layout.
232 int QTextInlineObject::formatIndex() const
234 return eng->formatIndex(&eng->layoutData->items[itm]);
238 Returns format of the inline object within the text layout.
240 QTextFormat QTextInlineObject::format() const
242 if (!eng->block.docHandle())
243 return QTextFormat();
244 return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
248 Returns if the object should be laid out right-to-left or left-to-right.
250 Qt::LayoutDirection QTextInlineObject::textDirection() const
252 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
259 \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 doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
290 The text can then be rendered by calling the layout's draw() function:
291 \snippet doc/src/snippets/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.d.data());
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()
369 Sets the layout's font to the given \a font. The layout is
370 invalidated and must be laid out again.
374 void QTextLayout::setFont(const QFont &font)
381 Returns the current font that is used for the layout, or a default
386 QFont QTextLayout::font() const
392 Sets the layout's text to the given \a string. The layout is
393 invalidated and must be laid out again.
395 Notice that when using this QTextLayout as part of a QTextDocument this
396 method will have no effect.
400 void QTextLayout::setText(const QString& string)
408 Returns the layout's text.
412 QString QTextLayout::text() const
418 Sets the text option structure that controls the layout process to the
423 void QTextLayout::setTextOption(const QTextOption &option)
429 Returns the current text option used to control the layout process.
433 QTextOption QTextLayout::textOption() const
439 Sets the \a position and \a text of the area in the layout that is
440 processed before editing occurs.
442 \sa preeditAreaPosition(), preeditAreaText()
444 void QTextLayout::setPreeditArea(int position, const QString &text)
446 if (text.isEmpty()) {
449 if (d->specialData->addFormats.isEmpty()) {
450 delete d->specialData;
453 d->specialData->preeditText = QString();
454 d->specialData->preeditPosition = -1;
458 d->specialData = new QTextEngine::SpecialData;
459 d->specialData->preeditPosition = position;
460 d->specialData->preeditText = text;
464 if (d->block.docHandle())
465 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
469 Returns the position of the area in the text layout that will be
470 processed before editing occurs.
472 \sa preeditAreaText()
474 int QTextLayout::preeditAreaPosition() const
476 return d->specialData ? d->specialData->preeditPosition : -1;
480 Returns the text that is inserted in the layout before editing occurs.
482 \sa preeditAreaPosition()
484 QString QTextLayout::preeditAreaText() const
486 return d->specialData ? d->specialData->preeditText : QString();
491 Sets the additional formats supported by the text layout to \a formatList.
493 \sa additionalFormats(), clearAdditionalFormats()
495 void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
497 if (formatList.isEmpty()) {
500 if (d->specialData->preeditText.isEmpty()) {
501 delete d->specialData;
504 d->specialData->addFormats = formatList;
505 d->specialData->addFormatIndices.clear();
508 if (!d->specialData) {
509 d->specialData = new QTextEngine::SpecialData;
510 d->specialData->preeditPosition = -1;
512 d->specialData->addFormats = formatList;
513 d->indexAdditionalFormats();
515 if (d->block.docHandle())
516 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
521 Returns the list of additional formats supported by the text layout.
523 \sa setAdditionalFormats(), clearAdditionalFormats()
525 QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
527 QList<FormatRange> formats;
531 formats = d->specialData->addFormats;
533 if (d->specialData->addFormatIndices.isEmpty())
536 const QTextFormatCollection *collection = d->formats();
538 for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
539 formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
545 Clears the list of additional formats supported by the text layout.
547 \sa additionalFormats(), setAdditionalFormats()
549 void QTextLayout::clearAdditionalFormats()
551 setAdditionalFormats(QList<FormatRange>());
555 Enables caching of the complete layout information if \a enable is
556 true; otherwise disables layout caching. Usually
557 QTextLayout throws most of the layouting information away after a
558 call to endLayout() to reduce memory consumption. If you however
559 want to draw the laid out text directly afterwards enabling caching
560 might speed up drawing significantly.
564 void QTextLayout::setCacheEnabled(bool enable)
566 d->cacheGlyphs = enable;
570 Returns true if the complete layout information is cached; otherwise
573 \sa setCacheEnabled()
575 bool QTextLayout::cacheEnabled() const
577 return d->cacheGlyphs;
581 Sets the visual cursor movement style to the given \a style. If the
582 QTextLayout is backed by a document, you can ignore this and use the option
583 in QTextDocument, this option is for widgets like QLineEdit or custom
584 widgets without a QTextDocument. Default value is QTextCursor::Logical.
586 \sa cursorMoveStyle()
588 void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
590 d->visualMovement = style == Qt::VisualMoveStyle ? true : false;
594 The cursor movement style of this QTextLayout. The default is
595 Qt::LogicalMoveStyle.
597 \sa setCursorMoveStyle()
599 Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const
601 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
605 Begins the layout process.
609 void QTextLayout::beginLayout()
612 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
613 qWarning("QTextLayout::beginLayout: Called while already doing layout");
620 d->layoutData->layoutState = QTextEngine::InLayout;
624 Ends the layout process.
628 void QTextLayout::endLayout()
631 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
632 qWarning("QTextLayout::endLayout: Called without beginLayout()");
636 int l = d->lines.size();
637 if (l && d->lines.at(l-1).length < 0) {
638 QTextLine(l-1, d).setNumColumns(INT_MAX);
640 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
648 Clears the line information in the layout. After having called
649 this function, lineCount() returns 0.
651 void QTextLayout::clearLayout()
657 Returns the next valid cursor position after \a oldPos that
658 respects the given cursor \a mode.
659 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
661 \sa isValidCursorPosition(), previousCursorPosition()
663 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
665 const HB_CharAttributes *attributes = d->attributes();
666 int len = d->block.isValid() ? d->block.length() - 1
667 : d->layoutData->string.length();
668 Q_ASSERT(len <= d->layoutData->string.length());
669 if (!attributes || oldPos < 0 || oldPos >= len)
672 if (mode == SkipCharacters) {
674 while (oldPos < len && !attributes[oldPos].charStop)
677 if (oldPos < len && d->atWordSeparator(oldPos)) {
679 while (oldPos < len && d->atWordSeparator(oldPos))
682 while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
685 while (oldPos < len && d->atSpace(oldPos))
693 Returns the first valid cursor position before \a oldPos that
694 respects the given cursor \a mode.
695 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
697 \sa isValidCursorPosition(), nextCursorPosition()
699 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
701 const HB_CharAttributes *attributes = d->attributes();
702 if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
705 if (mode == SkipCharacters) {
707 while (oldPos && !attributes[oldPos].charStop)
710 while (oldPos && d->atSpace(oldPos-1))
713 if (oldPos && d->atWordSeparator(oldPos-1)) {
715 while (oldPos && d->atWordSeparator(oldPos-1))
718 while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
727 Returns the cursor position to the right of \a oldPos, next to it.
728 It's dependent on the visual position of characters, after bi-directional
731 \sa leftCursorPosition(), nextCursorPosition()
733 int QTextLayout::rightCursorPosition(int oldPos) const
735 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
736 // qDebug("%d -> %d", oldPos, newPos);
741 Returns the cursor position to the left of \a oldPos, next to it.
742 It's dependent on the visual position of characters, after bi-directional
745 \sa rightCursorPosition(), previousCursorPosition()
747 int QTextLayout::leftCursorPosition(int oldPos) const
749 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
750 // qDebug("%d -> %d", oldPos, newPos);
755 Returns true if position \a pos is a valid cursor position.
757 In a Unicode context some positions in the text are not valid
758 cursor positions, because the position is inside a Unicode
759 surrogate or a grapheme cluster.
761 A grapheme cluster is a sequence of two or more Unicode characters
762 that form one indivisible entity on the screen. For example the
763 latin character `\Auml' can be represented in Unicode by two
764 characters, `A' (0x41), and the combining diaresis (0x308). A text
765 cursor can only validly be positioned before or after these two
766 characters, never between them since that wouldn't make sense. In
767 indic languages every syllable forms a grapheme cluster.
769 bool QTextLayout::isValidCursorPosition(int pos) const
771 const HB_CharAttributes *attributes = d->attributes();
772 if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
774 return attributes[pos].charStop;
778 Returns a new text line to be laid out if there is text to be
779 inserted into the layout; otherwise returns an invalid text line.
781 The text layout creates a new line object that starts after the
782 last line in the layout, or at the beginning if the layout is empty.
783 The layout maintains an internal cursor, and each line is filled
784 with text from the cursor position onwards when the
785 QTextLine::setLineWidth() function is called.
787 Once QTextLine::setLineWidth() is called, a new line can be created and
788 filled with text. Repeating this process will lay out the whole block
789 of text contained in the QTextLayout. If there is no text left to be
790 inserted into the layout, the QTextLine returned will not be valid
791 (isValid() will return false).
793 QTextLine QTextLayout::createLine()
796 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
797 qWarning("QTextLayout::createLine: Called without layouting");
801 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
804 int l = d->lines.size();
805 if (l && d->lines.at(l-1).length < 0) {
806 QTextLine(l-1, d).setNumColumns(INT_MAX);
808 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
809 int strlen = d->layoutData->string.length();
810 if (l && from >= strlen) {
811 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
818 line.justified = false;
819 line.gridfitted = false;
821 d->lines.append(line);
822 return QTextLine(l, d);
826 Returns the number of lines in this text layout.
830 int QTextLayout::lineCount() const
832 return d->lines.size();
836 Returns the \a{i}-th line of text in this text layout.
838 \sa lineCount(), lineForTextPosition()
840 QTextLine QTextLayout::lineAt(int i) const
842 return QTextLine(i, d);
846 Returns the line that contains the cursor position specified by \a pos.
848 \sa isValidCursorPosition(), lineAt()
850 QTextLine QTextLayout::lineForTextPosition(int pos) const
852 int lineNum = d->lineNumberForTextPosition(pos);
853 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
859 The global position of the layout. This is independent of the
860 bounding rectangle and of the layout process.
864 QPointF QTextLayout::position() const
870 Moves the text layout to point \a p.
874 void QTextLayout::setPosition(const QPointF &p)
880 The smallest rectangle that contains all the lines in the layout.
882 QRectF QTextLayout::boundingRect() const
884 if (d->lines.isEmpty())
888 QFixed xmin = d->lines.at(0).x;
889 QFixed ymin = d->lines.at(0).y;
891 for (int i = 0; i < d->lines.size(); ++i) {
892 const QScriptLine &si = d->lines[i];
893 xmin = qMin(xmin, si.x);
894 ymin = qMin(ymin, si.y);
895 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
896 xmax = qMax(xmax, si.x+lineWidth);
897 // ### shouldn't the ascent be used in ymin???
898 ymax = qMax(ymax, si.y+si.height());
900 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
904 The minimum width the layout needs. This is the width of the
905 layout's smallest non-breakable substring.
907 \warning This function only returns a valid value after the layout
912 qreal QTextLayout::minimumWidth() const
914 return d->minWidth.toReal();
918 The maximum width the layout could expand to; this is essentially
919 the width of the entire text.
921 \warning This function only returns a valid value after the layout
926 qreal QTextLayout::maximumWidth() const
928 return d->maxWidth.toReal();
935 void QTextLayout::setFlags(int flags)
937 if (flags & Qt::TextJustificationForced) {
938 d->option.setAlignment(Qt::AlignJustify);
939 d->forceJustification = true;
942 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
943 d->ignoreBidi = true;
944 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
948 static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
949 QPainterPath *region, QRectF boundingRect)
951 const QScriptLine &line = eng->lines[lineNumber];
953 QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
957 const qreal selectionY = pos.y() + line.y.toReal();
958 const qreal lineHeight = line.height().toReal();
960 QFixed lastSelectionX = iterator.x;
961 QFixed lastSelectionWidth;
963 while (!iterator.atEnd()) {
966 QFixed selectionX, selectionWidth;
967 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
968 if (selectionX == lastSelectionX + lastSelectionWidth) {
969 lastSelectionWidth += selectionWidth;
973 if (lastSelectionWidth > 0)
974 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
976 lastSelectionX = selectionX;
977 lastSelectionWidth = selectionWidth;
980 if (lastSelectionWidth > 0)
981 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
984 static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
986 return clip.isValid() ? (rect & clip) : rect;
991 Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
992 expensive function, and should not be called in a time sensitive context.
996 \sa draw(), QPainter::drawGlyphRun()
998 #if !defined(QT_NO_RAWFONT)
999 QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
1004 length = text().length();
1006 QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
1007 for (int i=0; i<d->lines.size(); ++i) {
1008 if (d->lines[i].from > from + length)
1010 else if (d->lines[i].from + d->lines[i].length >= from) {
1011 QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
1013 for (int j = 0; j < glyphRuns.size(); j++) {
1014 const QGlyphRun &glyphRun = glyphRuns.at(j);
1015 QRawFont rawFont = glyphRun.rawFont();
1017 QFontEngine *fontEngine = rawFont.d->fontEngine;
1018 QTextItem::RenderFlags flags;
1019 if (glyphRun.underline())
1020 flags |= QTextItem::Underline;
1021 if (glyphRun.overline())
1022 flags |= QTextItem::Overline;
1023 if (glyphRun.strikeOut())
1024 flags |= QTextItem::StrikeOut;
1025 QPair<QFontEngine *, int> key(fontEngine, int(flags));
1026 // merge the glyph runs using the same font
1027 if (glyphRunHash.contains(key)) {
1028 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1030 QVector<quint32> indexes = oldGlyphRun.glyphIndexes();
1031 QVector<QPointF> positions = oldGlyphRun.positions();
1033 indexes += glyphRun.glyphIndexes();
1034 positions += glyphRun.positions();
1036 oldGlyphRun.setGlyphIndexes(indexes);
1037 oldGlyphRun.setPositions(positions);
1039 glyphRunHash[key] = glyphRun;
1045 return glyphRunHash.values();
1047 #endif // QT_NO_RAWFONT
1050 Draws the whole layout on the painter \a p at the position specified by \a pos.
1051 The rendered layout includes the given \a selections and is clipped within
1052 the rectangle specified by \a clip.
1054 void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
1056 if (d->lines.isEmpty())
1062 QPointF position = pos + d->position;
1064 QFixed clipy = (INT_MIN/256);
1065 QFixed clipe = (INT_MAX/256);
1066 if (clip.isValid()) {
1067 clipy = QFixed::fromReal(clip.y() - position.y());
1068 clipe = clipy + QFixed::fromReal(clip.height());
1072 int lastLine = d->lines.size();
1073 for (int i = 0; i < d->lines.size(); ++i) {
1075 const QScriptLine &sl = d->lines[i];
1081 if ((sl.y + sl.height()) < clipy) {
1087 QPainterPath excludedRegion;
1088 QPainterPath textDoneRegion;
1089 for (int i = 0; i < selections.size(); ++i) {
1090 FormatRange selection = selections.at(i);
1091 const QBrush bg = selection.format.background();
1093 QPainterPath region;
1094 region.setFillRule(Qt::WindingFill);
1096 for (int line = firstLine; line < lastLine; ++line) {
1097 const QScriptLine &sl = d->lines[line];
1098 QTextLine tl(line, d);
1100 QRectF lineRect(tl.naturalTextRect());
1101 lineRect.translate(position);
1102 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1104 bool isLastLineInBlock = (line == d->lines.size()-1);
1105 int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
1108 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1109 continue; // no actual intersection
1111 const bool selectionStartInLine = sl.from <= selection.start;
1112 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1114 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1115 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1117 region.addRect(clipIfValid(lineRect, clip));
1120 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1121 QRectF fullLineRect(tl.rect());
1122 fullLineRect.translate(position);
1123 fullLineRect.setRight(QFIXED_MAX);
1124 if (!selectionEndInLine)
1125 region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1126 if (!selectionStartInLine)
1127 region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1128 } else if (!selectionEndInLine
1129 && isLastLineInBlock
1130 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1131 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1132 lineRect.height()/4, lineRect.height()), clip));
1137 const QPen oldPen = p->pen();
1138 const QBrush oldBrush = p->brush();
1140 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1141 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1142 p->drawPath(region);
1145 p->setBrush(oldBrush);
1150 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1151 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1153 if (hasBackground) {
1154 selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1155 // don't just clear the property, set an empty brush that overrides a potential
1156 // background brush specified in the text
1157 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1158 selection.format.clearProperty(QTextFormat::OutlinePen);
1161 selection.format.setProperty(SuppressText, !hasText);
1163 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1167 p->setClipPath(region, Qt::IntersectClip);
1169 for (int line = firstLine; line < lastLine; ++line) {
1170 QTextLine l(line, d);
1171 l.draw(p, position, &selection);
1176 textDoneRegion += region;
1179 textDoneRegion -= region;
1182 excludedRegion += region;
1185 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1186 if (!needsTextButNoBackground.isEmpty()){
1188 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1189 FormatRange selection;
1190 selection.start = 0;
1191 selection.length = INT_MAX;
1192 selection.format.setProperty(SuppressBackground, true);
1193 for (int line = firstLine; line < lastLine; ++line) {
1194 QTextLine l(line, d);
1195 l.draw(p, position, &selection);
1200 if (!excludedRegion.isEmpty()) {
1203 QRectF br = boundingRect().translated(position);
1204 br.setRight(QFIXED_MAX);
1206 br = br.intersected(clip);
1208 path -= excludedRegion;
1209 p->setClipPath(path, Qt::IntersectClip);
1212 for (int i = firstLine; i < lastLine; ++i) {
1214 l.draw(p, position);
1216 if (!excludedRegion.isEmpty())
1220 if (!d->cacheGlyphs)
1225 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
1228 Draws a text cursor with the current pen at the given \a position using the
1229 \a painter specified.
1230 The corresponding position within the text is specified by \a cursorPosition.
1232 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
1234 drawCursor(p, pos, cursorPosition, 1);
1238 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
1240 Draws a text cursor with the current pen and the specified \a width at the given \a position using the
1241 \a painter specified.
1242 The corresponding position within the text is specified by \a cursorPosition.
1244 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
1246 if (d->lines.isEmpty())
1252 QPointF position = pos + d->position;
1254 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
1255 int line = d->lineNumberForTextPosition(cursorPosition);
1258 if (line >= d->lines.size())
1261 QTextLine l(line, d);
1262 const QScriptLine &sl = d->lines[line];
1264 qreal x = position.x() + l.cursorToX(cursorPosition);
1268 if (d->visualCursorMovement()) {
1269 if (cursorPosition == sl.from + sl.length)
1271 itm = d->findItem(cursorPosition);
1273 itm = d->findItem(cursorPosition - 1);
1275 QFixed base = sl.base();
1276 QFixed descent = sl.descent;
1277 bool rightToLeft = d->isRightToLeft();
1279 const QScriptItem &si = d->layoutData->items.at(itm);
1283 descent = si.descent;
1284 rightToLeft = si.analysis.bidiLevel % 2;
1286 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1287 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1288 && (p->transform().type() > QTransform::TxTranslate);
1289 if (toggleAntialiasing)
1290 p->setRenderHint(QPainter::Antialiasing);
1291 #if defined(QT_MAC_USE_COCOA)
1292 // Always draw the cursor aligned to pixel boundary.
1295 p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
1296 if (toggleAntialiasing)
1297 p->setRenderHint(QPainter::Antialiasing, false);
1298 if (d->layoutData->hasBidi) {
1299 const int arrow_extent = 4;
1300 int sign = rightToLeft ? -1 : 1;
1301 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1302 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1311 \brief The QTextLine class represents a line of text inside a QTextLayout.
1313 \ingroup richtext-processing
1315 A text line is usually created by QTextLayout::createLine().
1317 After being created, the line can be filled using the setLineWidth()
1318 or setNumColumns() functions. A line has a number of attributes including the
1319 rectangle it occupies, rect(), its coordinates, x() and y(), its
1320 textLength(), width() and naturalTextWidth(), and its ascent() and decent()
1321 relative to the text. The position of the cursor in terms of the
1322 line is available from cursorToX() and its inverse from
1323 xToCursor(). A line can be moved with setPosition().
1327 \enum QTextLine::Edge
1334 \enum QTextLine::CursorPosition
1336 \value CursorBetweenCharacters
1337 \value CursorOnCharacter
1341 \fn QTextLine::QTextLine(int line, QTextEngine *e)
1344 Constructs a new text line using the line at position \a line in
1345 the text engine \a e.
1349 \fn QTextLine::QTextLine()
1351 Creates an invalid line.
1355 \fn bool QTextLine::isValid() const
1357 Returns true if this text line is valid; otherwise returns false.
1361 \fn int QTextLine::lineNumber() const
1363 Returns the position of the line in the text engine.
1368 Returns the line's bounding rectangle.
1370 \sa x(), y(), textLength(), width()
1372 QRectF QTextLine::rect() const
1374 const QScriptLine& sl = eng->lines[i];
1375 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1379 Returns the rectangle covered by the line.
1381 QRectF QTextLine::naturalTextRect() const
1383 const QScriptLine& sl = eng->lines[i];
1384 QFixed x = sl.x + eng->alignLine(sl);
1386 QFixed width = sl.textWidth;
1390 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1394 Returns the line's x position.
1396 \sa rect(), y(), textLength(), width()
1398 qreal QTextLine::x() const
1400 return eng->lines[i].x.toReal();
1404 Returns the line's y position.
1406 \sa x(), rect(), textLength(), width()
1408 qreal QTextLine::y() const
1410 return eng->lines[i].y.toReal();
1414 Returns the line's width as specified by the layout() function.
1416 \sa naturalTextWidth(), x(), y(), textLength(), rect()
1418 qreal QTextLine::width() const
1420 return eng->lines[i].width.toReal();
1425 Returns the line's ascent.
1427 \sa descent(), height()
1429 qreal QTextLine::ascent() const
1431 return eng->lines[i].ascent.toReal();
1435 Returns the line's descent.
1437 \sa ascent(), height()
1439 qreal QTextLine::descent() const
1441 return eng->lines[i].descent.toReal();
1445 Returns the line's height. This is equal to ascent() + descent() + 1
1446 if leading is not included. If leading is included, this equals to
1447 ascent() + descent() + leading() + 1.
1449 \sa ascent(), descent(), leading(), setLeadingIncluded()
1451 qreal QTextLine::height() const
1453 return eng->lines[i].height().toReal();
1459 Returns the line's leading.
1461 \sa ascent(), descent(), height()
1463 qreal QTextLine::leading() const
1465 return eng->lines[i].leading.toReal();
1471 Includes positive leading into the line's height if \a included is true;
1472 otherwise does not include leading.
1474 By default, leading is not included.
1476 Note that negative leading is ignored, it must be handled
1477 in the code using the text lines by letting the lines overlap.
1479 \sa leadingIncluded()
1482 void QTextLine::setLeadingIncluded(bool included)
1484 eng->lines[i].leadingIncluded= included;
1491 Returns true if positive leading is included into the line's height;
1492 otherwise returns false.
1494 By default, leading is not included.
1496 \sa setLeadingIncluded()
1498 bool QTextLine::leadingIncluded() const
1500 return eng->lines[i].leadingIncluded;
1504 Returns the width of the line that is occupied by text. This is
1505 always \<= to width(), and is the minimum width that could be used
1506 by layout() without changing the line break position.
1508 qreal QTextLine::naturalTextWidth() const
1510 return eng->lines[i].textWidth.toReal();
1515 Returns the horizontal advance of the text. The advance of the text
1516 is the distance from its position to the next position at which
1517 text would naturally be drawn.
1519 By adding the advance to the position of the text line and using this
1520 as the position of a second text line, you will be able to position
1521 the two lines side-by-side without gaps in-between.
1523 qreal QTextLine::horizontalAdvance() const
1525 return eng->lines[i].textAdvance.toReal();
1529 Lays out the line with the given \a width. The line is filled from
1530 its starting position with as many characters as will fit into
1531 the line. In case the text cannot be split at the end of the line,
1532 it will be filled with additional characters to the next whitespace
1535 void QTextLine::setLineWidth(qreal width)
1537 QScriptLine &line = eng->lines[i];
1538 if (!eng->layoutData) {
1539 qWarning("QTextLine: Can't set a line width while not layouting.");
1543 if (width > QFIXED_MAX)
1546 line.width = QFixed::fromReal(width);
1548 && line.textWidth <= line.width
1549 && line.from + line.length == eng->layoutData->string.length())
1550 // no need to do anything if the line is already layouted and the last one. This optimization helps
1551 // when using things in a single line layout.
1556 layout_helper(INT_MAX);
1560 Lays out the line. The line is filled from its starting position
1561 with as many characters as are specified by \a numColumns. In case
1562 the text cannot be split until \a numColumns characters, the line
1563 will be filled with as many characters to the next whitespace or
1566 void QTextLine::setNumColumns(int numColumns)
1568 QScriptLine &line = eng->lines[i];
1569 line.width = QFIXED_MAX;
1572 layout_helper(numColumns);
1576 Lays out the line. The line is filled from its starting position
1577 with as many characters as are specified by \a numColumns. In case
1578 the text cannot be split until \a numColumns characters, the line
1579 will be filled with as many characters to the next whitespace or
1580 end of the text. The provided \a alignmentWidth is used as reference
1581 width for alignment.
1583 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
1585 QScriptLine &line = eng->lines[i];
1586 line.width = QFixed::fromReal(alignmentWidth);
1589 layout_helper(numColumns);
1593 #define LB_DEBUG qDebug
1595 #define LB_DEBUG if (0) qDebug
1600 struct LineBreakHelper
1603 : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
1604 manualWrap(false), whiteSpaceOrObject(true)
1609 QScriptLine tmpData;
1610 QScriptLine spaceData;
1612 QGlyphLayout glyphs;
1616 int currentPosition;
1617 glyph_t previousGlyph;
1620 QFixed softHyphenWidth;
1621 QFixed rightBearing;
1622 QFixed minimumRightBearing;
1624 QFontEngine *fontEngine;
1625 const unsigned short *logClusters;
1628 bool whiteSpaceOrObject;
1630 bool checkFullOtherwiseExtend(QScriptLine &line);
1632 QFixed calculateNewWidth(const QScriptLine &line) const {
1633 return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
1634 - qMin(rightBearing, QFixed());
1637 inline glyph_t currentGlyph() const
1639 Q_ASSERT(currentPosition > 0);
1640 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1642 return glyphs.glyphs[logClusters[currentPosition - 1]];
1645 inline void saveCurrentGlyph()
1648 if (currentPosition > 0 &&
1649 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1650 previousGlyph = currentGlyph(); // needed to calculate right bearing later
1654 inline void adjustRightBearing(glyph_t glyph)
1657 fontEngine->getGlyphBearings(glyph, 0, &rb);
1658 rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
1661 inline void adjustRightBearing()
1663 if (currentPosition <= 0)
1665 adjustRightBearing(currentGlyph());
1668 inline void adjustPreviousRightBearing()
1670 if (previousGlyph > 0)
1671 adjustRightBearing(previousGlyph);
1674 inline void resetRightBearing()
1676 rightBearing = QFixed(1); // Any positive number is defined as invalid since only
1677 // negative right bearings are interesting to us.
1681 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1683 LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1685 QFixed newWidth = calculateNewWidth(line);
1686 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1689 minw = qMax(minw, tmpData.textWidth);
1691 line.textWidth += spaceData.textWidth;
1693 line.length += spaceData.length;
1694 tmpData.textWidth = 0;
1696 spaceData.textWidth = 0;
1697 spaceData.length = 0;
1702 } // anonymous namespace
1705 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
1706 const QScriptItem ¤t, const unsigned short *logClusters,
1707 const QGlyphLayout &glyphs)
1709 int glyphPosition = logClusters[pos];
1710 do { // got to the first next cluster
1713 } while (pos < end && logClusters[pos] == glyphPosition);
1714 do { // calculate the textWidth for the rest of the current cluster.
1715 if (!glyphs.attributes[glyphPosition].dontPrint)
1716 line.textWidth += glyphs.advances_x[glyphPosition];
1718 } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
1720 Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
1727 void QTextLine::layout_helper(int maxGlyphs)
1729 QScriptLine &line = eng->lines[i];
1731 line.trailingSpaces = 0;
1733 line.hasTrailingSpaces = false;
1735 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.length()) {
1736 line.setDefaultHeight(eng);
1740 Q_ASSERT(line.from < eng->layoutData->string.length());
1742 LineBreakHelper lbh;
1744 lbh.maxGlyphs = maxGlyphs;
1746 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1747 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1748 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1751 int newItem = eng->findItem(line.from);
1753 LB_DEBUG("from: %d: item=%d, total %d, width available %f", line.from, newItem, eng->layoutData->items.size(), line.width.toReal());
1755 Qt::Alignment alignment = eng->option.alignment();
1757 const HB_CharAttributes *attributes = eng->attributes();
1760 lbh.currentPosition = line.from;
1762 lbh.logClusters = eng->layoutData->logClustersPtr;
1763 lbh.previousGlyph = 0;
1765 while (newItem < eng->layoutData->items.size()) {
1766 lbh.resetRightBearing();
1767 lbh.softHyphenWidth = 0;
1768 if (newItem != item) {
1770 const QScriptItem ¤t = eng->layoutData->items[item];
1771 if (!current.num_glyphs) {
1773 attributes = eng->attributes();
1776 lbh.logClusters = eng->layoutData->logClustersPtr;
1778 lbh.currentPosition = qMax(line.from, current.position);
1779 end = current.position + eng->length(item);
1780 lbh.glyphs = eng->shapedGlyphs(¤t);
1781 QFontEngine *fontEngine = eng->fontEngine(current);
1782 if (lbh.fontEngine != fontEngine) {
1783 lbh.fontEngine = fontEngine;
1784 lbh.minimumRightBearing = qMin(QFixed(),
1785 QFixed::fromReal(fontEngine->minRightBearing()));
1788 const QScriptItem ¤t = eng->layoutData->items[item];
1790 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1791 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1793 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1794 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1796 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1797 lbh.whiteSpaceOrObject = true;
1798 if (lbh.checkFullOtherwiseExtend(line))
1801 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1802 QFixed tabWidth = eng->calculateTabWidth(item, x);
1804 lbh.spaceData.textWidth += tabWidth;
1805 lbh.spaceData.length++;
1808 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1809 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1811 if (lbh.checkFullOtherwiseExtend(line))
1813 } else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1814 lbh.whiteSpaceOrObject = true;
1815 // if the line consists only of the line separator make sure
1816 // we have a sane height
1817 if (!line.length && !lbh.tmpData.length)
1818 line.setDefaultHeight(eng);
1819 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1820 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1821 current, lbh.logClusters, lbh.glyphs);
1823 lbh.tmpData.length++;
1824 lbh.adjustPreviousRightBearing();
1826 line += lbh.tmpData;
1828 } else if (current.analysis.flags == QScriptAnalysis::Object) {
1829 lbh.whiteSpaceOrObject = true;
1830 lbh.tmpData.length++;
1832 QTextFormat format = eng->formats()->format(eng->formatIndex(&eng->layoutData->items[item]));
1833 if (eng->block.docHandle())
1834 eng->docLayout()->positionInlineObject(QTextInlineObject(item, eng), eng->block.position() + current.position, format);
1836 lbh.tmpData.textWidth += current.width;
1840 if (lbh.checkFullOtherwiseExtend(line))
1842 } else if (attributes[lbh.currentPosition].whiteSpace) {
1843 lbh.whiteSpaceOrObject = true;
1844 while (lbh.currentPosition < end && attributes[lbh.currentPosition].whiteSpace)
1845 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1846 current, lbh.logClusters, lbh.glyphs);
1848 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width) {
1849 lbh.spaceData.textWidth = line.width; // ignore spaces that fall out of the line.
1853 lbh.whiteSpaceOrObject = false;
1854 bool sb_or_ws = false;
1855 lbh.saveCurrentGlyph();
1857 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1858 current, lbh.logClusters, lbh.glyphs);
1860 if (attributes[lbh.currentPosition].whiteSpace || attributes[lbh.currentPosition-1].lineBreakType != HB_NoBreak) {
1863 } else if (breakany && attributes[lbh.currentPosition].charStop) {
1866 } while (lbh.currentPosition < end);
1867 lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
1869 if (lbh.currentPosition && attributes[lbh.currentPosition - 1].lineBreakType == HB_SoftHyphen) {
1870 // if we are splitting up a word because of
1871 // a soft hyphen then we ...
1873 // a) have to take the width of the soft hyphen into
1874 // account to see if the first syllable(s) /and/
1875 // the soft hyphen fit into the line
1877 // b) if we are so short of available width that the
1878 // soft hyphen is the first breakable position, then
1879 // we don't want to show it. However we initially
1880 // have to take the width for it into account so that
1881 // the text document layout sees the overflow and
1882 // switch to break-anywhere mode, in which we
1883 // want the soft-hyphen to slip into the next line
1884 // and thus become invisible again.
1887 lbh.softHyphenWidth = lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1889 lbh.tmpData.textWidth += lbh.glyphs.advances_x[lbh.logClusters[lbh.currentPosition - 1]];
1892 // The actual width of the text needs to take the right bearing into account. The
1893 // right bearing is left-ward, which means that if the rightmost pixel is to the right
1894 // of the advance of the glyph, the bearing will be negative. We flip the sign
1895 // for the code to be more readable. Logic borrowed from qfontmetrics.cpp.
1896 // We ignore the right bearing if the minimum negative bearing is too little to
1897 // expand the text beyond the edge.
1898 if (sb_or_ws|breakany) {
1899 QFixed rightBearing = lbh.rightBearing; // store previous right bearing
1900 #if !defined(Q_WS_MAC)
1901 if (lbh.calculateNewWidth(line) - lbh.minimumRightBearing > line.width)
1903 lbh.adjustRightBearing();
1904 if (lbh.checkFullOtherwiseExtend(line)) {
1905 // we are too wide, fix right bearing
1906 if (rightBearing <= 0)
1907 lbh.rightBearing = rightBearing; // take from cache
1909 lbh.adjustPreviousRightBearing();
1912 line.textWidth += lbh.softHyphenWidth;
1918 lbh.saveCurrentGlyph();
1920 if (lbh.currentPosition == end)
1923 LB_DEBUG("reached end of line");
1924 lbh.checkFullOtherwiseExtend(line);
1926 if (lbh.rightBearing > 0 && !lbh.whiteSpaceOrObject) // If right bearing has not yet been adjusted
1927 lbh.adjustRightBearing();
1928 line.textAdvance = line.textWidth;
1929 line.textWidth -= qMin(QFixed(), lbh.rightBearing);
1931 if (line.length == 0) {
1932 LB_DEBUG("no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
1933 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
1934 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
1935 line += lbh.tmpData;
1938 LB_DEBUG("line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
1939 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
1940 LB_DEBUG(" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
1942 if (lbh.manualWrap) {
1943 eng->minWidth = qMax(eng->minWidth, line.textWidth);
1944 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
1946 eng->minWidth = qMax(eng->minWidth, lbh.minw);
1947 eng->maxWidth += line.textWidth;
1950 if (line.textWidth > 0 && item < eng->layoutData->items.size())
1951 eng->maxWidth += lbh.spaceData.textWidth;
1952 if (eng->option.flags() & QTextOption::IncludeTrailingSpaces)
1953 line.textWidth += lbh.spaceData.textWidth;
1954 if (lbh.spaceData.length) {
1955 line.trailingSpaces = lbh.spaceData.length;
1956 line.hasTrailingSpaces = true;
1959 line.justified = false;
1960 line.gridfitted = false;
1962 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
1963 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
1964 || (lbh.maxGlyphs == INT_MAX && line.textWidth > line.width)) {
1966 eng->option.setWrapMode(QTextOption::WrapAnywhere);
1969 layout_helper(lbh.maxGlyphs);
1970 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
1976 Moves the line to position \a pos.
1978 void QTextLine::setPosition(const QPointF &pos)
1980 eng->lines[i].x = QFixed::fromReal(pos.x());
1981 eng->lines[i].y = QFixed::fromReal(pos.y());
1985 Returns the line's position relative to the text layout's position.
1987 QPointF QTextLine::position() const
1989 return QPointF(eng->lines[i].x.toReal(), eng->lines[i].y.toReal());
1992 // ### DOC: I have no idea what this means/does.
1993 // You create a text layout with a string of text. Once you laid
1994 // it out, it contains a number of QTextLines. from() returns the position
1995 // inside the text string where this line starts. If you e.g. has a
1996 // text of "This is a string", laid out into two lines (the second
1997 // starting at the word 'a'), layout.lineAt(0).from() == 0 and
1998 // layout.lineAt(1).from() == 8.
2000 Returns the start of the line from the beginning of the string
2001 passed to the QTextLayout.
2003 int QTextLine::textStart() const
2005 return eng->lines[i].from;
2009 Returns the length of the text in the line.
2011 \sa naturalTextWidth()
2013 int QTextLine::textLength() const
2015 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2016 && eng->block.isValid() && i == eng->lines.count()-1) {
2017 return eng->lines[i].length - 1;
2019 return eng->lines[i].length + eng->lines[i].trailingSpaces;
2022 static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng,
2023 int start, int glyph_start)
2025 int ge = glyph_start + gf.glyphs.numGlyphs;
2026 int gs = glyph_start;
2027 int end = start + gf.num_chars;
2028 unsigned short *logClusters = eng->logClusters(&si);
2029 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2030 QFixed orig_width = gf.width;
2032 int *ul = eng->underlinePositions;
2034 while (*ul != -1 && *ul < start)
2036 bool rtl = si.analysis.bidiLevel % 2;
2043 if (ul && *ul != -1 && *ul < end) {
2045 gtmp = logClusters[*ul-si.position];
2048 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2049 gf.num_chars = stmp - start;
2050 gf.chars = eng->layoutData->string.unicode() + start;
2053 w += glyphs.effectiveAdvance(gs);
2061 p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
2064 if (ul && *ul != -1 && *ul < end) {
2066 gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
2068 gf.glyphs = glyphs.mid(gs, gtmp - gs);
2069 gf.num_chars = stmp - start;
2070 gf.chars = eng->layoutData->string.unicode() + start;
2071 gf.logClusters = logClusters + start - si.position;
2074 w += glyphs.effectiveAdvance(gs);
2079 gf.underlineStyle = QTextCharFormat::SingleUnderline;
2082 p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
2085 gf.underlineStyle = QTextCharFormat::NoUnderline;
2091 gf.width = orig_width;
2095 static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r)
2097 QBrush c = chf.foreground();
2098 if (c.style() == Qt::NoBrush) {
2099 p->setPen(defaultPen);
2102 QBrush bg = chf.background();
2103 if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool())
2105 if (c.style() != Qt::NoBrush) {
2106 p->setPen(QPen(c, 0));
2111 #if !defined(QT_NO_RAWFONT)
2112 static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &glyphLayout,
2113 const QPointF &pos, const QTextItem::RenderFlags &flags,
2114 const QFixed &selectionX, const QFixed &selectionWidth)
2118 // Make a font for this particular engine
2120 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2121 fontD->fontEngine = fontEngine;
2122 fontD->thread = QThread::currentThread();
2123 fontD->fontEngine->ref.ref();
2125 #if defined(Q_WS_WIN)
2126 if (fontEngine->supportsSubPixelPositions())
2127 fontD->hintingPreference = QFont::PreferVerticalHinting;
2129 fontD->hintingPreference = QFont::PreferFullHinting;
2130 #elif defined(Q_WS_MAC)
2131 fontD->hintingPreference = QFont::PreferNoHinting;
2132 #elif !defined(QT_NO_FREETYPE)
2133 if (fontEngine->type() == QFontEngine::Freetype) {
2134 QFontEngineFT *freeTypeEngine = static_cast<QFontEngineFT *>(fontEngine);
2135 switch (freeTypeEngine->defaultHintStyle()) {
2136 case QFontEngineFT::HintNone:
2137 fontD->hintingPreference = QFont::PreferNoHinting;
2139 case QFontEngineFT::HintLight:
2140 fontD->hintingPreference = QFont::PreferVerticalHinting;
2142 case QFontEngineFT::HintMedium:
2143 case QFontEngineFT::HintFull:
2144 fontD->hintingPreference = QFont::PreferFullHinting;
2150 QVarLengthArray<glyph_t> glyphsArray;
2151 QVarLengthArray<QFixedPoint> positionsArray;
2153 fontEngine->getGlyphPositions(glyphLayout, QTransform(), flags, glyphsArray,
2155 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2157 qreal fontHeight = font.ascent() + font.descent();
2160 QVector<quint32> glyphs;
2161 QVector<QPointF> positions;
2162 for (int i=0; i<glyphsArray.size(); ++i) {
2163 glyphs.append(glyphsArray.at(i) & 0xffffff);
2165 QPointF position = positionsArray.at(i).toPointF() + pos;
2166 positions.append(position);
2169 maxY = minY = position.y();
2171 minY = qMin(minY, position.y());
2172 maxY = qMax(maxY, position.y());
2176 qreal height = maxY + fontHeight - minY;
2178 glyphRun.setGlyphIndexes(glyphs);
2179 glyphRun.setPositions(positions);
2181 glyphRun.setOverline(flags.testFlag(QTextItem::Overline));
2182 glyphRun.setUnderline(flags.testFlag(QTextItem::Underline));
2183 glyphRun.setStrikeOut(flags.testFlag(QTextItem::StrikeOut));
2184 glyphRun.setRawFont(font);
2186 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY, selectionWidth.toReal(), height));
2192 Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
2193 in the range defined by \a from and \a length. The \a from index is relative to the beginning
2194 of the text in the containing QTextLayout, and the range must be within the range of QTextLine
2195 as given by functions textStart() and textLength().
2197 If \a from is negative, it will default to textStart(), and if \a length is negative it will
2198 default to the return value of textLength().
2202 \sa QTextLayout::glyphRuns()
2204 QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
2206 const QScriptLine &line = eng->lines[i];
2208 if (line.length == 0)
2209 return QList<QGlyphRun>();
2215 length = textLength();
2218 return QList<QGlyphRun>();
2220 QTextLayout::FormatRange selection;
2221 selection.start = from;
2222 selection.length = length;
2224 QTextLineItemIterator iterator(eng, i, QPointF(), &selection);
2225 qreal y = line.y.toReal() + line.base().toReal();
2226 QList<QGlyphRun> glyphRuns;
2227 while (!iterator.atEnd()) {
2228 QScriptItem &si = iterator.next();
2229 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2232 QPointF pos(iterator.x.toReal(), y);
2233 if (from >= 0 && length >= 0 &&
2234 (from >= si.position + eng->length(&si)
2235 || from + length <= si.position
2236 || from + length <= iterator.itemStart
2237 || from >= iterator.itemEnd)) {
2241 QFont font = eng->font(si);
2243 QTextItem::RenderFlags flags;
2244 if (font.overline())
2245 flags |= QTextItem::Overline;
2246 if (font.underline())
2247 flags |= QTextItem::Underline;
2248 if (font.strikeOut())
2249 flags |= QTextItem::StrikeOut;
2252 if (si.analysis.bidiLevel % 2) {
2253 flags |= QTextItem::RightToLeft;
2257 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2258 int relativeTo = qMin(iterator.itemEnd - 1, from + length - 1) - si.position;
2260 unsigned short *logClusters = eng->logClusters(&si);
2261 int glyphsStart = logClusters[relativeFrom];
2262 int glyphsEnd = (relativeTo == eng->length(&si))
2264 : logClusters[relativeTo];
2266 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2267 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2269 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2271 // Calculate new x position of glyph layout for a subset. This becomes somewhat complex
2272 // when we're breaking a RTL script item, since the expected position passed into
2273 // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
2274 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2275 for (int i=itemGlyphsStart; i<glyphsStart; ++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());
2280 } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2281 for (int i=itemGlyphsEnd; i>glyphsEnd; --i) {
2282 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2283 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2284 glyphLayout.advances_y[i].toReal());
2288 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2292 iterator.getSelectionBounds(&x, &width);
2294 if (glyphLayout.numGlyphs > 0) {
2295 QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script);
2296 if (mainFontEngine->type() == QFontEngine::Multi) {
2297 QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
2300 int which = glyphLayout.glyphs[0] >> 24;
2301 for (end = 0; end < glyphLayout.numGlyphs; ++end) {
2302 const int e = glyphLayout.glyphs[end] >> 24;
2306 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2307 multiFontEngine->ensureEngineAt(which);
2308 glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
2309 subLayout, pos, flags, x, width));
2310 for (int i = 0; i < subLayout.numGlyphs; i++) {
2311 pos += QPointF(subLayout.advances_x[i].toReal(),
2312 subLayout.advances_y[i].toReal());
2319 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2320 multiFontEngine->ensureEngineAt(which);
2321 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2322 subLayout, pos, flags, x, width);
2323 if (!glyphRun.isEmpty())
2324 glyphRuns.append(glyphRun);
2326 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags, x,
2328 if (!glyphRun.isEmpty())
2329 glyphRuns.append(glyphRun);
2336 #endif // QT_NO_RAWFONT
2339 \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
2341 Draws a line on the given \a painter at the specified \a position.
2342 The \a selection is reserved for internal use.
2344 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
2346 const QScriptLine &line = eng->lines[i];
2347 QPen pen = p->pen();
2349 bool noText = (selection && selection->format.property(SuppressText).toBool());
2353 && selection->start <= line.from
2354 && selection->start + selection->length > line.from) {
2356 const qreal lineHeight = line.height().toReal();
2357 QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
2358 lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
2359 setPenAndDrawBackground(p, QPen(), selection->format, r);
2366 QTextLineItemIterator iterator(eng, i, pos, selection);
2367 QFixed lineBase = line.base();
2369 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2371 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2372 while (!iterator.atEnd()) {
2373 QScriptItem &si = iterator.next();
2375 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2378 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2379 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2382 QFixed itemBaseLine = y;
2383 QFont f = eng->font(si);
2384 QTextCharFormat format;
2386 if (eng->hasFormats() || selection) {
2387 format = eng->format(&si);
2388 if (suppressColors) {
2389 format.clearForeground();
2390 format.clearBackground();
2391 format.clearProperty(QTextFormat::TextUnderlineColor);
2394 format.merge(selection->format);
2396 setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2397 iterator.itemWidth.toReal(), line.height().toReal()));
2399 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2400 if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
2401 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2402 QFixed height = fe->ascent() + fe->descent();
2403 if (valign == QTextCharFormat::AlignSubScript)
2404 itemBaseLine += height / 6;
2405 else if (valign == QTextCharFormat::AlignSuperScript)
2406 itemBaseLine -= height / 2;
2410 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2412 if (eng->hasFormats()) {
2414 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
2415 QFixed itemY = y - si.ascent;
2416 if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
2417 itemY = y - lineBase;
2420 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2422 eng->docLayout()->drawInlineObject(p, itemRect,
2423 QTextInlineObject(iterator.item, eng),
2424 si.position + eng->block.position(),
2427 QBrush bg = format.brushProperty(ObjectSelectionBrush);
2428 if (bg.style() != Qt::NoBrush) {
2429 QColor c = bg.color();
2431 p->fillRect(itemRect, c);
2434 } else { // si.isTab
2435 QFont f = eng->font(si);
2436 QTextItemInt gf(si, &f, format);
2439 gf.width = iterator.itemWidth;
2440 p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
2441 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2442 QChar visualTab(0x2192);
2443 int w = QFontMetrics(f).width(visualTab);
2444 qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
2446 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2447 iterator.itemWidth.toReal(), line.height().toReal()),
2451 p->drawText(QPointF(iterator.x.toReal() + x,
2452 y.toReal()), visualTab);
2462 unsigned short *logClusters = eng->logClusters(&si);
2463 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2465 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2466 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2467 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2468 gf.logClusters = logClusters + iterator.itemStart - si.position;
2469 gf.width = iterator.itemWidth;
2470 gf.justified = line.justified;
2471 gf.initWithScriptItem(si);
2473 Q_ASSERT(gf.fontEngine);
2475 if (eng->underlinePositions) {
2476 // can't have selections in this case
2477 drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
2479 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2480 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2482 path.setFillRule(Qt::WindingFill);
2484 if (gf.glyphs.numGlyphs)
2485 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2487 const QFontEngine *fe = gf.fontEngine;
2488 const qreal lw = fe->lineThickness().toReal();
2489 if (gf.flags & QTextItem::Underline) {
2490 qreal offs = fe->underlinePosition().toReal();
2491 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2493 if (gf.flags & QTextItem::Overline) {
2494 qreal offs = fe->ascent().toReal() + 1;
2495 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2497 if (gf.flags & QTextItem::StrikeOut) {
2498 qreal offs = fe->ascent().toReal() / 3;
2499 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2504 p->setRenderHint(QPainter::Antialiasing);
2505 //Currently QPen with a Qt::NoPen style still returns a default
2506 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
2507 if (p->pen().style() == Qt::NoPen)
2508 p->setBrush(Qt::NoBrush);
2510 p->setBrush(p->pen().brush());
2512 p->setPen(format.textOutline());
2517 gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
2518 p->drawTextItem(pos, gf);
2521 if (si.analysis.flags == QScriptAnalysis::Space
2522 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2523 QBrush c = format.foreground();
2524 if (c.style() != Qt::NoBrush)
2525 p->setPen(c.color());
2526 QChar visualSpace((ushort)0xb7);
2527 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2533 if (eng->hasFormats())
2538 \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
2544 Converts the cursor position \a cursorPos to the corresponding x position
2545 inside the line, taking account of the \a edge.
2547 If \a cursorPos is not a valid cursor position, the nearest valid
2548 cursor position will be used instead, and cpos will be modified to
2549 point to this valid cursor position.
2553 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
2555 if (!eng->layoutData)
2558 const QScriptLine &line = eng->lines[i];
2559 bool lastLine = i >= eng->lines.size() - 1;
2562 x += eng->alignLine(line);
2564 if (!i && !eng->layoutData->items.size()) {
2569 int pos = *cursorPos;
2571 if (pos == line.from + (int)line.length) {
2572 // end of line ensure we have the last item on the line
2573 itm = eng->findItem(pos-1);
2576 itm = eng->findItem(pos);
2577 eng->shapeLine(line);
2579 const QScriptItem *si = &eng->layoutData->items[itm];
2580 if (!si->num_glyphs)
2582 pos -= si->position;
2584 QGlyphLayout glyphs = eng->shapedGlyphs(si);
2585 unsigned short *logClusters = eng->logClusters(si);
2586 Q_ASSERT(logClusters);
2588 int l = eng->length(itm);
2594 int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
2595 if (edge == Trailing && glyph_pos < si->num_glyphs) {
2596 // trailing edge is leading edge of next cluster
2598 while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2602 bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
2604 int lineEnd = line.from + line.length;
2606 // add the items left of the cursor
2608 int firstItem = eng->findItem(line.from);
2609 int lastItem = eng->findItem(lineEnd - 1);
2610 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2612 QVarLengthArray<int> visualOrder(nItems);
2613 QVarLengthArray<uchar> levels(nItems);
2614 for (int i = 0; i < nItems; ++i)
2615 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2616 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2618 for (int i = 0; i < nItems; ++i) {
2619 int item = visualOrder[i]+firstItem;
2622 QScriptItem &si = eng->layoutData->items[item];
2626 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2630 int start = qMax(line.from, si.position);
2631 int end = qMin(lineEnd, si.position + eng->length(item));
2633 logClusters = eng->logClusters(&si);
2635 int gs = logClusters[start-si.position];
2636 int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
2638 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2641 x += glyphs.effectiveAdvance(gs);
2646 logClusters = eng->logClusters(si);
2647 glyphs = eng->shapedGlyphs(si);
2648 if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
2649 if (pos == (reverse ? 0 : l))
2652 bool rtl = eng->isRightToLeft();
2653 bool visual = eng->visualCursorMovement();
2654 int end = qMin(lineEnd, si->position + l) - si->position;
2656 int glyph_end = end == l ? si->num_glyphs : logClusters[end];
2657 int glyph_start = glyph_pos;
2658 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
2660 for (int i = glyph_end - 1; i >= glyph_start; i--)
2661 x += glyphs.effectiveAdvance(i);
2663 int start = qMax(line.from - si->position, 0);
2664 int glyph_start = logClusters[start];
2665 int glyph_end = glyph_pos;
2666 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
2668 for (int i = glyph_start; i <= glyph_end; i++)
2669 x += glyphs.effectiveAdvance(i);
2671 x += eng->offsetInLigature(si, pos, end, glyph_pos);
2674 *cursorPos = pos + si->position;
2679 \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
2681 Converts the x-coordinate \a x, to the nearest matching cursor
2682 position, depending on the cursor position type, \a cpos.
2686 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
2688 QFixed x = QFixed::fromReal(_x);
2689 const QScriptLine &line = eng->lines[i];
2690 bool lastLine = i >= eng->lines.size() - 1;
2693 if (!eng->layoutData)
2696 int line_length = textLength();
2701 int firstItem = eng->findItem(line.from);
2702 int lastItem = eng->findItem(line.from + line_length - 1);
2703 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2709 x -= eng->alignLine(line);
2710 // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
2712 QVarLengthArray<int> visualOrder(nItems);
2713 QVarLengthArray<unsigned char> levels(nItems);
2714 for (int i = 0; i < nItems; ++i)
2715 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2716 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2718 bool visual = eng->visualCursorMovement();
2720 // left of first item
2721 int item = visualOrder[0]+firstItem;
2722 QScriptItem &si = eng->layoutData->items[item];
2725 int pos = si.position;
2726 if (si.analysis.bidiLevel % 2)
2727 pos += eng->length(item);
2728 pos = qMax(line.from, pos);
2729 pos = qMin(line.from + line_length, pos);
2731 } else if (x < line.textWidth
2732 || (line.justified && x < line.width)) {
2733 // has to be in one of the runs
2735 bool rtl = eng->isRightToLeft();
2737 eng->shapeLine(line);
2738 QVector<int> insertionPoints;
2740 eng->insertionPointsForLine(lineNum, insertionPoints);
2742 for (int i = 0; i < nItems; ++i) {
2743 int item = visualOrder[i]+firstItem;
2744 QScriptItem &si = eng->layoutData->items[item];
2745 int item_length = eng->length(item);
2746 // qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
2748 int start = qMax(line.from - si.position, 0);
2749 int end = qMin(line.from + line_length - si.position, item_length);
2751 unsigned short *logClusters = eng->logClusters(&si);
2753 int gs = logClusters[start];
2754 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
2755 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2757 QFixed item_width = 0;
2758 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2759 item_width = si.width;
2763 item_width += glyphs.effectiveAdvance(g);
2767 // qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
2769 if (pos + item_width < x) {
2774 // qDebug(" inside run");
2775 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2776 if (cpos == QTextLine::CursorOnCharacter)
2778 bool left_half = (x - pos) < item_width/2;
2780 if (bool(si.analysis.bidiLevel % 2) != left_half)
2782 return si.position + 1;
2787 // has to be inside run
2788 if (cpos == QTextLine::CursorOnCharacter) {
2789 if (si.analysis.bidiLevel % 2) {
2793 if (glyphs.attributes[gs].clusterStart) {
2800 pos -= glyphs.effectiveAdvance(gs);
2806 if (glyphs.attributes[gs].clusterStart) {
2812 pos += glyphs.effectiveAdvance(gs);
2817 QFixed dist = INT_MAX/256;
2818 if (si.analysis.bidiLevel % 2) {
2819 if (!visual || rtl || (lastLine && i == nItems - 1)) {
2822 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2827 pos -= glyphs.effectiveAdvance(gs);
2832 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
2837 pos += glyphs.effectiveAdvance(ge);
2842 if (!visual || !rtl || (lastLine && i == 0)) {
2844 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2849 pos += glyphs.effectiveAdvance(gs);
2853 QFixed oldPos = pos;
2855 pos += glyphs.effectiveAdvance(gs);
2856 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2866 if (qAbs(x-pos) < dist) {
2868 if (!rtl && i < nItems - 1) {
2872 if (rtl && nchars > 0)
2873 return insertionPoints[lastLine ? nchars : nchars - 1];
2875 return eng->positionInLigature(&si, end, x, pos, -1,
2876 cpos == QTextLine::CursorOnCharacter);
2879 Q_ASSERT(glyph_pos != -1);
2880 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
2881 cpos == QTextLine::CursorOnCharacter);
2884 // right of last item
2885 // qDebug() << "right of last";
2886 int item = visualOrder[nItems-1]+firstItem;
2887 QScriptItem &si = eng->layoutData->items[item];
2890 int pos = si.position;
2891 if (!(si.analysis.bidiLevel % 2))
2892 pos += eng->length(item);
2893 pos = qMax(line.from, pos);
2895 int maxPos = line.from + line_length;
2897 // except for the last line we assume that the
2898 // character between lines is a space and we want
2899 // to position the cursor to the left of that
2901 // ###### breaks with japanese for example
2902 if (this->i < eng->lines.count() - 1)
2905 pos = qMin(pos, maxPos);