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"
46 #include <qapplication.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 "qstyleoption.h"
54 #include "qpainterpath.h"
55 #include "qglyphrun.h"
56 #include "qglyphrun_p.h"
58 #include "qrawfont_p.h"
63 #include "qfontengine_p.h"
65 #if !defined(QT_NO_FREETYPE)
66 # include "qfontengine_ft_p.h"
71 #define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
72 #define SuppressText 0x5012
73 #define SuppressBackground 0x513
76 \class QTextLayout::FormatRange
79 \brief The QTextLayout::FormatRange structure is used to apply extra formatting information
80 for a specified area in the text layout's content.
82 \sa QTextLayout::setAdditionalFormats(), QTextLayout::draw()
86 \variable QTextLayout::FormatRange::start
87 Specifies the beginning of the format range within the text layout's text.
91 \variable QTextLayout::FormatRange::length
92 Specifies the numer of characters the format range spans.
96 \variable QTextLayout::FormatRange::format
97 Specifies the format to apply.
101 \class QTextInlineObject
104 \brief The QTextInlineObject class represents an inline object in
107 \ingroup richtext-processing
109 This class is only used if the text layout is used to lay out
110 parts of a QTextDocument.
112 The inline object has various attributes that can be set, for
113 example using, setWidth(), setAscent(), and setDescent(). The
114 rectangle it occupies is given by rect(), and its direction by
115 isRightToLeft(). Its position in the text layout is given by at(),
116 and its format is given by format().
120 \fn QTextInlineObject::QTextInlineObject(int i, QTextEngine *e)
122 Creates a new inline object for the item at position \a i in the
127 \fn QTextInlineObject::QTextInlineObject()
133 \fn bool QTextInlineObject::isValid() const
135 Returns true if this inline object is valid; otherwise returns
140 Returns the inline object's rectangle.
142 \sa ascent(), descent(), width()
144 QRectF QTextInlineObject::rect() const
146 QScriptItem& si = eng->layoutData->items[itm];
147 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
151 Returns the inline object's width.
153 \sa ascent(), descent(), rect()
155 qreal QTextInlineObject::width() const
157 return eng->layoutData->items[itm].width.toReal();
161 Returns the inline object's ascent.
163 \sa descent(), width(), rect()
165 qreal QTextInlineObject::ascent() const
167 return eng->layoutData->items[itm].ascent.toReal();
171 Returns the inline object's descent.
173 \sa ascent(), width(), rect()
175 qreal QTextInlineObject::descent() const
177 return eng->layoutData->items[itm].descent.toReal();
181 Returns the inline object's total height. This is equal to
182 ascent() + descent() + 1.
184 \sa ascent(), descent(), width(), rect()
186 qreal QTextInlineObject::height() const
188 return eng->layoutData->items[itm].height().toReal();
192 Sets the inline object's width to \a w.
194 \sa width(), ascent(), descent(), rect()
196 void QTextInlineObject::setWidth(qreal w)
198 eng->layoutData->items[itm].width = QFixed::fromReal(w);
202 Sets the inline object's ascent to \a a.
204 \sa ascent(), setDescent(), width(), rect()
206 void QTextInlineObject::setAscent(qreal a)
208 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
212 Sets the inline object's decent to \a d.
214 \sa descent(), setAscent(), width(), rect()
216 void QTextInlineObject::setDescent(qreal d)
218 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
222 The position of the inline object within the text layout.
224 int QTextInlineObject::textPosition() const
226 return eng->layoutData->items[itm].position;
230 Returns an integer describing the format of the inline object
231 within the text layout.
233 int QTextInlineObject::formatIndex() const
235 return eng->formatIndex(&eng->layoutData->items[itm]);
239 Returns format of the inline object within the text layout.
241 QTextFormat QTextInlineObject::format() const
243 if (!eng->block.docHandle())
244 return QTextFormat();
245 return eng->formats()->format(eng->formatIndex(&eng->layoutData->items[itm]));
249 Returns if the object should be laid out right-to-left or left-to-right.
251 Qt::LayoutDirection QTextInlineObject::textDirection() const
253 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
260 \brief The QTextLayout class is used to lay out and render text.
262 \ingroup richtext-processing
264 It offers many features expected from a modern text layout
265 engine, including Unicode compliant rendering, line breaking and
266 handling of cursor positioning. It can also produce and render
267 device independent layout, something that is important for WYSIWYG
270 The class has a rather low level API and unless you intend to
271 implement your own text rendering for some specialized widget, you
272 probably won't need to use it directly.
274 QTextLayout can be used with both plain and rich text.
276 QTextLayout can be used to create a sequence of QTextLine
277 instances with given widths and can position them independently
278 on the screen. Once the layout is done, these lines can be drawn
281 The text to be laid out can be provided in the constructor or set with
284 The layout can be seen as a sequence of QTextLine objects; use createLine()
285 to create a QTextLine instance, and lineAt() or lineForTextPosition() to retrieve
288 Here is a code snippet that demonstrates the layout phase:
289 \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 0
291 The text can then be rendered by calling the layout's draw() function:
292 \snippet doc/src/snippets/code/src_gui_text_qtextlayout.cpp 1
294 For a given position in the text you can find a valid cursor position with
295 isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
297 The QTextLayout itself can be positioned with setPosition(); it has a
298 boundingRect(), and a minimumWidth() and a maximumWidth().
304 \enum QTextLayout::CursorMode
306 \value SkipCharacters
311 \fn QTextEngine *QTextLayout::engine() const
314 Returns the text engine used to render the text layout.
318 Constructs an empty text layout.
322 QTextLayout::QTextLayout()
323 { d = new QTextEngine(); }
326 Constructs a text layout to lay out the given \a text.
328 QTextLayout::QTextLayout(const QString& text)
330 d = new QTextEngine();
335 Constructs a text layout to lay out the given \a text with the specified
338 All the metric and layout calculations will be done in terms of
339 the paint device, \a paintdevice. If \a paintdevice is 0 the
340 calculations will be done in screen metrics.
342 QTextLayout::QTextLayout(const QString& text, const QFont &font, QPaintDevice *paintdevice)
346 f = QFont(font, paintdevice);
347 d = new QTextEngine((text.isNull() ? (const QString&)QString::fromLatin1("") : text), f.d.data());
352 Constructs a text layout to lay out the given \a block.
354 QTextLayout::QTextLayout(const QTextBlock &block)
356 d = new QTextEngine();
361 Destructs the layout.
363 QTextLayout::~QTextLayout()
370 Sets the layout's font to the given \a font. The layout is
371 invalidated and must be laid out again.
375 void QTextLayout::setFont(const QFont &font)
382 Returns the current font that is used for the layout, or a default
387 QFont QTextLayout::font() const
393 Sets the layout's text to the given \a string. The layout is
394 invalidated and must be laid out again.
396 Notice that when using this QTextLayout as part of a QTextDocument this
397 method will have no effect.
401 void QTextLayout::setText(const QString& string)
409 Returns the layout's text.
413 QString QTextLayout::text() const
419 Sets the text option structure that controls the layout process to the
424 void QTextLayout::setTextOption(const QTextOption &option)
430 Returns the current text option used to control the layout process.
434 QTextOption QTextLayout::textOption() const
440 Sets the \a position and \a text of the area in the layout that is
441 processed before editing occurs.
443 \sa preeditAreaPosition(), preeditAreaText()
445 void QTextLayout::setPreeditArea(int position, const QString &text)
447 if (text.isEmpty()) {
450 if (d->specialData->addFormats.isEmpty()) {
451 delete d->specialData;
454 d->specialData->preeditText = QString();
455 d->specialData->preeditPosition = -1;
459 d->specialData = new QTextEngine::SpecialData;
460 d->specialData->preeditPosition = position;
461 d->specialData->preeditText = text;
465 if (d->block.docHandle())
466 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
470 Returns the position of the area in the text layout that will be
471 processed before editing occurs.
473 \sa preeditAreaText()
475 int QTextLayout::preeditAreaPosition() const
477 return d->specialData ? d->specialData->preeditPosition : -1;
481 Returns the text that is inserted in the layout before editing occurs.
483 \sa preeditAreaPosition()
485 QString QTextLayout::preeditAreaText() const
487 return d->specialData ? d->specialData->preeditText : QString();
492 Sets the additional formats supported by the text layout to \a formatList.
494 \sa additionalFormats(), clearAdditionalFormats()
496 void QTextLayout::setAdditionalFormats(const QList<FormatRange> &formatList)
498 if (formatList.isEmpty()) {
501 if (d->specialData->preeditText.isEmpty()) {
502 delete d->specialData;
505 d->specialData->addFormats = formatList;
506 d->specialData->addFormatIndices.clear();
509 if (!d->specialData) {
510 d->specialData = new QTextEngine::SpecialData;
511 d->specialData->preeditPosition = -1;
513 d->specialData->addFormats = formatList;
514 d->indexAdditionalFormats();
516 if (d->block.docHandle())
517 d->block.docHandle()->documentChange(d->block.position(), d->block.length());
522 Returns the list of additional formats supported by the text layout.
524 \sa setAdditionalFormats(), clearAdditionalFormats()
526 QList<QTextLayout::FormatRange> QTextLayout::additionalFormats() const
528 QList<FormatRange> formats;
532 formats = d->specialData->addFormats;
534 if (d->specialData->addFormatIndices.isEmpty())
537 const QTextFormatCollection *collection = d->formats();
539 for (int i = 0; i < d->specialData->addFormatIndices.count(); ++i)
540 formats[i].format = collection->charFormat(d->specialData->addFormatIndices.at(i));
546 Clears the list of additional formats supported by the text layout.
548 \sa additionalFormats(), setAdditionalFormats()
550 void QTextLayout::clearAdditionalFormats()
552 setAdditionalFormats(QList<FormatRange>());
556 Enables caching of the complete layout information if \a enable is
557 true; otherwise disables layout caching. Usually
558 QTextLayout throws most of the layouting information away after a
559 call to endLayout() to reduce memory consumption. If you however
560 want to draw the laid out text directly afterwards enabling caching
561 might speed up drawing significantly.
565 void QTextLayout::setCacheEnabled(bool enable)
567 d->cacheGlyphs = enable;
571 Returns true if the complete layout information is cached; otherwise
574 \sa setCacheEnabled()
576 bool QTextLayout::cacheEnabled() const
578 return d->cacheGlyphs;
582 Sets the visual cursor movement style to the given \a style. If the
583 QTextLayout is backed by a document, you can ignore this and use the option
584 in QTextDocument, this option is for widgets like QLineEdit or custom
585 widgets without a QTextDocument. Default value is QTextCursor::Logical.
587 \sa cursorMoveStyle()
589 void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
591 d->visualMovement = style == Qt::VisualMoveStyle ? true : false;
595 The cursor movement style of this QTextLayout. The default is
596 Qt::LogicalMoveStyle.
598 \sa setCursorMoveStyle()
600 Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const
602 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
606 Begins the layout process.
610 void QTextLayout::beginLayout()
613 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
614 qWarning("QTextLayout::beginLayout: Called while already doing layout");
621 d->layoutData->layoutState = QTextEngine::InLayout;
625 Ends the layout process.
629 void QTextLayout::endLayout()
632 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
633 qWarning("QTextLayout::endLayout: Called without beginLayout()");
637 int l = d->lines.size();
638 if (l && d->lines.at(l-1).length < 0) {
639 QTextLine(l-1, d).setNumColumns(INT_MAX);
641 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
649 Clears the line information in the layout. After having called
650 this function, lineCount() returns 0.
652 void QTextLayout::clearLayout()
658 Returns the next valid cursor position after \a oldPos that
659 respects the given cursor \a mode.
660 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
662 \sa isValidCursorPosition(), previousCursorPosition()
664 int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
666 const HB_CharAttributes *attributes = d->attributes();
667 int len = d->block.isValid() ? d->block.length() - 1
668 : d->layoutData->string.length();
669 Q_ASSERT(len <= d->layoutData->string.length());
670 if (!attributes || oldPos < 0 || oldPos >= len)
673 if (mode == SkipCharacters) {
675 while (oldPos < len && !attributes[oldPos].charStop)
678 if (oldPos < len && d->atWordSeparator(oldPos)) {
680 while (oldPos < len && d->atWordSeparator(oldPos))
683 while (oldPos < len && !d->atSpace(oldPos) && !d->atWordSeparator(oldPos))
686 while (oldPos < len && d->atSpace(oldPos))
694 Returns the first valid cursor position before \a oldPos that
695 respects the given cursor \a mode.
696 Returns value of \a oldPos, if \a oldPos is not a valid cursor position.
698 \sa isValidCursorPosition(), nextCursorPosition()
700 int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
702 const HB_CharAttributes *attributes = d->attributes();
703 if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length())
706 if (mode == SkipCharacters) {
708 while (oldPos && !attributes[oldPos].charStop)
711 while (oldPos && d->atSpace(oldPos-1))
714 if (oldPos && d->atWordSeparator(oldPos-1)) {
716 while (oldPos && d->atWordSeparator(oldPos-1))
719 while (oldPos && !d->atSpace(oldPos-1) && !d->atWordSeparator(oldPos-1))
728 Returns the cursor position to the right of \a oldPos, next to it.
729 It's dependent on the visual position of characters, after bi-directional
732 \sa leftCursorPosition(), nextCursorPosition()
734 int QTextLayout::rightCursorPosition(int oldPos) const
736 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
737 // qDebug("%d -> %d", oldPos, newPos);
742 Returns the cursor position to the left of \a oldPos, next to it.
743 It's dependent on the visual position of characters, after bi-directional
746 \sa rightCursorPosition(), previousCursorPosition()
748 int QTextLayout::leftCursorPosition(int oldPos) const
750 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
751 // qDebug("%d -> %d", oldPos, newPos);
756 Returns true if position \a pos is a valid cursor position.
758 In a Unicode context some positions in the text are not valid
759 cursor positions, because the position is inside a Unicode
760 surrogate or a grapheme cluster.
762 A grapheme cluster is a sequence of two or more Unicode characters
763 that form one indivisible entity on the screen. For example the
764 latin character `\Auml' can be represented in Unicode by two
765 characters, `A' (0x41), and the combining diaresis (0x308). A text
766 cursor can only validly be positioned before or after these two
767 characters, never between them since that wouldn't make sense. In
768 indic languages every syllable forms a grapheme cluster.
770 bool QTextLayout::isValidCursorPosition(int pos) const
772 const HB_CharAttributes *attributes = d->attributes();
773 if (!attributes || pos < 0 || pos > (int)d->layoutData->string.length())
775 return attributes[pos].charStop;
779 Returns a new text line to be laid out if there is text to be
780 inserted into the layout; otherwise returns an invalid text line.
782 The text layout creates a new line object that starts after the
783 last line in the layout, or at the beginning if the layout is empty.
784 The layout maintains an internal cursor, and each line is filled
785 with text from the cursor position onwards when the
786 QTextLine::setLineWidth() function is called.
788 Once QTextLine::setLineWidth() is called, a new line can be created and
789 filled with text. Repeating this process will lay out the whole block
790 of text contained in the QTextLayout. If there is no text left to be
791 inserted into the layout, the QTextLine returned will not be valid
792 (isValid() will return false).
794 QTextLine QTextLayout::createLine()
797 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
798 qWarning("QTextLayout::createLine: Called without layouting");
802 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
805 int l = d->lines.size();
806 if (l && d->lines.at(l-1).length < 0) {
807 QTextLine(l-1, d).setNumColumns(INT_MAX);
809 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length : 0;
810 int strlen = d->layoutData->string.length();
811 if (l && from >= strlen) {
812 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
819 line.justified = false;
820 line.gridfitted = false;
822 d->lines.append(line);
823 return QTextLine(l, d);
827 Returns the number of lines in this text layout.
831 int QTextLayout::lineCount() const
833 return d->lines.size();
837 Returns the \a{i}-th line of text in this text layout.
839 \sa lineCount(), lineForTextPosition()
841 QTextLine QTextLayout::lineAt(int i) const
843 return QTextLine(i, d);
847 Returns the line that contains the cursor position specified by \a pos.
849 \sa isValidCursorPosition(), lineAt()
851 QTextLine QTextLayout::lineForTextPosition(int pos) const
853 int lineNum = d->lineNumberForTextPosition(pos);
854 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
860 The global position of the layout. This is independent of the
861 bounding rectangle and of the layout process.
865 QPointF QTextLayout::position() const
871 Moves the text layout to point \a p.
875 void QTextLayout::setPosition(const QPointF &p)
881 The smallest rectangle that contains all the lines in the layout.
883 QRectF QTextLayout::boundingRect() const
885 if (d->lines.isEmpty())
889 QFixed xmin = d->lines.at(0).x;
890 QFixed ymin = d->lines.at(0).y;
892 for (int i = 0; i < d->lines.size(); ++i) {
893 const QScriptLine &si = d->lines[i];
894 xmin = qMin(xmin, si.x);
895 ymin = qMin(ymin, si.y);
896 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
897 xmax = qMax(xmax, si.x+lineWidth);
898 // ### shouldn't the ascent be used in ymin???
899 ymax = qMax(ymax, si.y+si.height());
901 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
905 The minimum width the layout needs. This is the width of the
906 layout's smallest non-breakable substring.
908 \warning This function only returns a valid value after the layout
913 qreal QTextLayout::minimumWidth() const
915 return d->minWidth.toReal();
919 The maximum width the layout could expand to; this is essentially
920 the width of the entire text.
922 \warning This function only returns a valid value after the layout
927 qreal QTextLayout::maximumWidth() const
929 return d->maxWidth.toReal();
936 void QTextLayout::setFlags(int flags)
938 if (flags & Qt::TextJustificationForced) {
939 d->option.setAlignment(Qt::AlignJustify);
940 d->forceJustification = true;
943 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
944 d->ignoreBidi = true;
945 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
949 static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
950 QPainterPath *region, QRectF boundingRect)
952 const QScriptLine &line = eng->lines[lineNumber];
954 QTextLineItemIterator iterator(eng, lineNumber, pos, selection);
958 const qreal selectionY = pos.y() + line.y.toReal();
959 const qreal lineHeight = line.height().toReal();
961 QFixed lastSelectionX = iterator.x;
962 QFixed lastSelectionWidth;
964 while (!iterator.atEnd()) {
967 QFixed selectionX, selectionWidth;
968 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
969 if (selectionX == lastSelectionX + lastSelectionWidth) {
970 lastSelectionWidth += selectionWidth;
974 if (lastSelectionWidth > 0)
975 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
977 lastSelectionX = selectionX;
978 lastSelectionWidth = selectionWidth;
981 if (lastSelectionWidth > 0)
982 region->addRect(boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight));
985 static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
987 return clip.isValid() ? (rect & clip) : rect;
992 Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an
993 expensive function, and should not be called in a time sensitive context.
997 \sa draw(), QPainter::drawGlyphRun()
999 #if !defined(QT_NO_RAWFONT)
1000 QList<QGlyphRun> QTextLayout::glyphRuns(int from, int length) const
1005 length = text().length();
1007 QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphRunHash;
1008 for (int i=0; i<d->lines.size(); ++i) {
1009 if (d->lines[i].from > from + length)
1011 else if (d->lines[i].from + d->lines[i].length >= from) {
1012 QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length);
1014 for (int j = 0; j < glyphRuns.size(); j++) {
1015 const QGlyphRun &glyphRun = glyphRuns.at(j);
1016 QRawFont rawFont = glyphRun.rawFont();
1018 QFontEngine *fontEngine = rawFont.d->fontEngine;
1019 QTextItem::RenderFlags flags;
1020 if (glyphRun.underline())
1021 flags |= QTextItem::Underline;
1022 if (glyphRun.overline())
1023 flags |= QTextItem::Overline;
1024 if (glyphRun.strikeOut())
1025 flags |= QTextItem::StrikeOut;
1026 QPair<QFontEngine *, int> key(fontEngine, int(flags));
1027 // merge the glyph runs using the same font
1028 if (glyphRunHash.contains(key)) {
1029 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1031 QVector<quint32> indexes = oldGlyphRun.glyphIndexes();
1032 QVector<QPointF> positions = oldGlyphRun.positions();
1034 indexes += glyphRun.glyphIndexes();
1035 positions += glyphRun.positions();
1037 oldGlyphRun.setGlyphIndexes(indexes);
1038 oldGlyphRun.setPositions(positions);
1040 glyphRunHash[key] = glyphRun;
1046 return glyphRunHash.values();
1048 #endif // QT_NO_RAWFONT
1051 Draws the whole layout on the painter \a p at the position specified by \a pos.
1052 The rendered layout includes the given \a selections and is clipped within
1053 the rectangle specified by \a clip.
1055 void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections, const QRectF &clip) const
1057 if (d->lines.isEmpty())
1063 QPointF position = pos + d->position;
1065 QFixed clipy = (INT_MIN/256);
1066 QFixed clipe = (INT_MAX/256);
1067 if (clip.isValid()) {
1068 clipy = QFixed::fromReal(clip.y() - position.y());
1069 clipe = clipy + QFixed::fromReal(clip.height());
1073 int lastLine = d->lines.size();
1074 for (int i = 0; i < d->lines.size(); ++i) {
1076 const QScriptLine &sl = d->lines[i];
1082 if ((sl.y + sl.height()) < clipy) {
1088 QPainterPath excludedRegion;
1089 QPainterPath textDoneRegion;
1090 for (int i = 0; i < selections.size(); ++i) {
1091 FormatRange selection = selections.at(i);
1092 const QBrush bg = selection.format.background();
1094 QPainterPath region;
1095 region.setFillRule(Qt::WindingFill);
1097 for (int line = firstLine; line < lastLine; ++line) {
1098 const QScriptLine &sl = d->lines[line];
1099 QTextLine tl(line, d);
1101 QRectF lineRect(tl.naturalTextRect());
1102 lineRect.translate(position);
1103 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1105 bool isLastLineInBlock = (line == d->lines.size()-1);
1106 int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
1109 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1110 continue; // no actual intersection
1112 const bool selectionStartInLine = sl.from <= selection.start;
1113 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1115 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1116 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1118 region.addRect(clipIfValid(lineRect, clip));
1121 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1122 QRectF fullLineRect(tl.rect());
1123 fullLineRect.translate(position);
1124 fullLineRect.setRight(QFIXED_MAX);
1125 if (!selectionEndInLine)
1126 region.addRect(clipIfValid(QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1127 if (!selectionStartInLine)
1128 region.addRect(clipIfValid(QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1129 } else if (!selectionEndInLine
1130 && isLastLineInBlock
1131 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1132 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1133 lineRect.height()/4, lineRect.height()), clip));
1138 const QPen oldPen = p->pen();
1139 const QBrush oldBrush = p->brush();
1141 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1142 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1143 p->drawPath(region);
1146 p->setBrush(oldBrush);
1151 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1152 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1154 if (hasBackground) {
1155 selection.format.setProperty(ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1156 // don't just clear the property, set an empty brush that overrides a potential
1157 // background brush specified in the text
1158 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1159 selection.format.clearProperty(QTextFormat::OutlinePen);
1162 selection.format.setProperty(SuppressText, !hasText);
1164 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1168 p->setClipPath(region, Qt::IntersectClip);
1170 for (int line = firstLine; line < lastLine; ++line) {
1171 QTextLine l(line, d);
1172 l.draw(p, position, &selection);
1177 textDoneRegion += region;
1180 textDoneRegion -= region;
1183 excludedRegion += region;
1186 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1187 if (!needsTextButNoBackground.isEmpty()){
1189 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1190 FormatRange selection;
1191 selection.start = 0;
1192 selection.length = INT_MAX;
1193 selection.format.setProperty(SuppressBackground, true);
1194 for (int line = firstLine; line < lastLine; ++line) {
1195 QTextLine l(line, d);
1196 l.draw(p, position, &selection);
1201 if (!excludedRegion.isEmpty()) {
1204 QRectF br = boundingRect().translated(position);
1205 br.setRight(QFIXED_MAX);
1207 br = br.intersected(clip);
1209 path -= excludedRegion;
1210 p->setClipPath(path, Qt::IntersectClip);
1213 for (int i = firstLine; i < lastLine; ++i) {
1215 l.draw(p, position);
1217 if (!excludedRegion.isEmpty())
1221 if (!d->cacheGlyphs)
1226 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const
1229 Draws a text cursor with the current pen at the given \a position using the
1230 \a painter specified.
1231 The corresponding position within the text is specified by \a cursorPosition.
1233 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
1235 drawCursor(p, pos, cursorPosition, 1);
1239 \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const
1241 Draws a text cursor with the current pen and the specified \a width at the given \a position using the
1242 \a painter specified.
1243 The corresponding position within the text is specified by \a cursorPosition.
1245 void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const
1247 if (d->lines.isEmpty())
1253 QPointF position = pos + d->position;
1255 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
1256 int line = d->lineNumberForTextPosition(cursorPosition);
1259 if (line >= d->lines.size())
1262 QTextLine l(line, d);
1263 const QScriptLine &sl = d->lines[line];
1265 qreal x = position.x() + l.cursorToX(cursorPosition);
1269 if (d->visualCursorMovement()) {
1270 if (cursorPosition == sl.from + sl.length)
1272 itm = d->findItem(cursorPosition);
1274 itm = d->findItem(cursorPosition - 1);
1276 QFixed base = sl.base();
1277 QFixed descent = sl.descent;
1278 bool rightToLeft = d->isRightToLeft();
1280 const QScriptItem &si = d->layoutData->items.at(itm);
1284 descent = si.descent;
1285 rightToLeft = si.analysis.bidiLevel % 2;
1287 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1288 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1289 && (p->transform().type() > QTransform::TxTranslate);
1290 if (toggleAntialiasing)
1291 p->setRenderHint(QPainter::Antialiasing);
1292 #if defined(QT_MAC_USE_COCOA)
1293 // Always draw the cursor aligned to pixel boundary.
1296 p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
1297 if (toggleAntialiasing)
1298 p->setRenderHint(QPainter::Antialiasing, false);
1299 if (d->layoutData->hasBidi) {
1300 const int arrow_extent = 4;
1301 int sign = rightToLeft ? -1 : 1;
1302 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1303 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1312 \brief The QTextLine class represents a line of text inside a QTextLayout.
1314 \ingroup richtext-processing
1316 A text line is usually created by QTextLayout::createLine().
1318 After being created, the line can be filled using the setLineWidth()
1319 or setNumColumns() functions. A line has a number of attributes including the
1320 rectangle it occupies, rect(), its coordinates, x() and y(), its
1321 textLength(), width() and naturalTextWidth(), and its ascent() and decent()
1322 relative to the text. The position of the cursor in terms of the
1323 line is available from cursorToX() and its inverse from
1324 xToCursor(). A line can be moved with setPosition().
1328 \enum QTextLine::Edge
1335 \enum QTextLine::CursorPosition
1337 \value CursorBetweenCharacters
1338 \value CursorOnCharacter
1342 \fn QTextLine::QTextLine(int line, QTextEngine *e)
1345 Constructs a new text line using the line at position \a line in
1346 the text engine \a e.
1350 \fn QTextLine::QTextLine()
1352 Creates an invalid line.
1356 \fn bool QTextLine::isValid() const
1358 Returns true if this text line is valid; otherwise returns false.
1362 \fn int QTextLine::lineNumber() const
1364 Returns the position of the line in the text engine.
1369 Returns the line's bounding rectangle.
1371 \sa x(), y(), textLength(), width()
1373 QRectF QTextLine::rect() const
1375 const QScriptLine& sl = eng->lines[i];
1376 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1380 Returns the rectangle covered by the line.
1382 QRectF QTextLine::naturalTextRect() const
1384 const QScriptLine& sl = eng->lines[i];
1385 QFixed x = sl.x + eng->alignLine(sl);
1387 QFixed width = sl.textWidth;
1391 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1395 Returns the line's x position.
1397 \sa rect(), y(), textLength(), width()
1399 qreal QTextLine::x() const
1401 return eng->lines[i].x.toReal();
1405 Returns the line's y position.
1407 \sa x(), rect(), textLength(), width()
1409 qreal QTextLine::y() const
1411 return eng->lines[i].y.toReal();
1415 Returns the line's width as specified by the layout() function.
1417 \sa naturalTextWidth(), x(), y(), textLength(), rect()
1419 qreal QTextLine::width() const
1421 return eng->lines[i].width.toReal();
1426 Returns the line's ascent.
1428 \sa descent(), height()
1430 qreal QTextLine::ascent() const
1432 return eng->lines[i].ascent.toReal();
1436 Returns the line's descent.
1438 \sa ascent(), height()
1440 qreal QTextLine::descent() const
1442 return eng->lines[i].descent.toReal();
1446 Returns the line's height. This is equal to ascent() + descent() + 1
1447 if leading is not included. If leading is included, this equals to
1448 ascent() + descent() + leading() + 1.
1450 \sa ascent(), descent(), leading(), setLeadingIncluded()
1452 qreal QTextLine::height() const
1454 return eng->lines[i].height().toReal();
1460 Returns the line's leading.
1462 \sa ascent(), descent(), height()
1464 qreal QTextLine::leading() const
1466 return eng->lines[i].leading.toReal();
1472 Includes positive leading into the line's height if \a included is true;
1473 otherwise does not include leading.
1475 By default, leading is not included.
1477 Note that negative leading is ignored, it must be handled
1478 in the code using the text lines by letting the lines overlap.
1480 \sa leadingIncluded()
1483 void QTextLine::setLeadingIncluded(bool included)
1485 eng->lines[i].leadingIncluded= included;
1492 Returns true if positive leading is included into the line's height;
1493 otherwise returns false.
1495 By default, leading is not included.
1497 \sa setLeadingIncluded()
1499 bool QTextLine::leadingIncluded() const
1501 return eng->lines[i].leadingIncluded;
1505 Returns the width of the line that is occupied by text. This is
1506 always \<= to width(), and is the minimum width that could be used
1507 by layout() without changing the line break position.
1509 qreal QTextLine::naturalTextWidth() const
1511 return eng->lines[i].textWidth.toReal();
1516 Returns the horizontal advance of the text. The advance of the text
1517 is the distance from its position to the next position at which
1518 text would naturally be drawn.
1520 By adding the advance to the position of the text line and using this
1521 as the position of a second text line, you will be able to position
1522 the two lines side-by-side without gaps in-between.
1524 qreal QTextLine::horizontalAdvance() const
1526 return eng->lines[i].textAdvance.toReal();
1530 Lays out the line with the given \a width. The line is filled from
1531 its starting position with as many characters as will fit into
1532 the line. In case the text cannot be split at the end of the line,
1533 it will be filled with additional characters to the next whitespace
1536 void QTextLine::setLineWidth(qreal width)
1538 QScriptLine &line = eng->lines[i];
1539 if (!eng->layoutData) {
1540 qWarning("QTextLine: Can't set a line width while not layouting.");
1544 if (width > QFIXED_MAX)
1547 line.width = QFixed::fromReal(width);
1549 && line.textWidth <= line.width
1550 && line.from + line.length == eng->layoutData->string.length())
1551 // no need to do anything if the line is already layouted and the last one. This optimization helps
1552 // when using things in a single line layout.
1557 layout_helper(INT_MAX);
1561 Lays out the line. The line is filled from its starting position
1562 with as many characters as are specified by \a numColumns. In case
1563 the text cannot be split until \a numColumns characters, the line
1564 will be filled with as many characters to the next whitespace or
1567 void QTextLine::setNumColumns(int numColumns)
1569 QScriptLine &line = eng->lines[i];
1570 line.width = QFIXED_MAX;
1573 layout_helper(numColumns);
1577 Lays out the line. The line is filled from its starting position
1578 with as many characters as are specified by \a numColumns. In case
1579 the text cannot be split until \a numColumns characters, the line
1580 will be filled with as many characters to the next whitespace or
1581 end of the text. The provided \a alignmentWidth is used as reference
1582 width for alignment.
1584 void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth)
1586 QScriptLine &line = eng->lines[i];
1587 line.width = QFixed::fromReal(alignmentWidth);
1590 layout_helper(numColumns);
1594 #define LB_DEBUG qDebug
1596 #define LB_DEBUG if (0) qDebug
1601 struct LineBreakHelper
1604 : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(0), logClusters(0),
1605 manualWrap(false), whiteSpaceOrObject(true)
1610 QScriptLine tmpData;
1611 QScriptLine spaceData;
1613 QGlyphLayout glyphs;
1617 int currentPosition;
1618 glyph_t previousGlyph;
1621 QFixed softHyphenWidth;
1622 QFixed rightBearing;
1623 QFixed minimumRightBearing;
1625 QFontEngine *fontEngine;
1626 const unsigned short *logClusters;
1629 bool whiteSpaceOrObject;
1631 bool checkFullOtherwiseExtend(QScriptLine &line);
1633 QFixed calculateNewWidth(const QScriptLine &line) const {
1634 return line.textWidth + tmpData.textWidth + spaceData.textWidth + softHyphenWidth
1635 - qMin(rightBearing, QFixed());
1638 inline glyph_t currentGlyph() const
1640 Q_ASSERT(currentPosition > 0);
1641 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1643 return glyphs.glyphs[logClusters[currentPosition - 1]];
1646 inline void saveCurrentGlyph()
1649 if (currentPosition > 0 &&
1650 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1651 previousGlyph = currentGlyph(); // needed to calculate right bearing later
1655 inline void adjustRightBearing(glyph_t glyph)
1658 fontEngine->getGlyphBearings(glyph, 0, &rb);
1659 rightBearing = qMin(QFixed(), QFixed::fromReal(rb));
1662 inline void adjustRightBearing()
1664 if (currentPosition <= 0)
1666 adjustRightBearing(currentGlyph());
1669 inline void adjustPreviousRightBearing()
1671 if (previousGlyph > 0)
1672 adjustRightBearing(previousGlyph);
1675 inline void resetRightBearing()
1677 rightBearing = QFixed(1); // Any positive number is defined as invalid since only
1678 // negative right bearings are interesting to us.
1682 inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1684 LB_DEBUG("possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1686 QFixed newWidth = calculateNewWidth(line);
1687 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1690 minw = qMax(minw, tmpData.textWidth);
1692 line.textWidth += spaceData.textWidth;
1694 line.length += spaceData.length;
1695 tmpData.textWidth = 0;
1697 spaceData.textWidth = 0;
1698 spaceData.length = 0;
1703 } // anonymous namespace
1706 static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount,
1707 const QScriptItem ¤t, const unsigned short *logClusters,
1708 const QGlyphLayout &glyphs)
1710 int glyphPosition = logClusters[pos];
1711 do { // got to the first next cluster
1714 } while (pos < end && logClusters[pos] == glyphPosition);
1715 do { // calculate the textWidth for the rest of the current cluster.
1716 if (!glyphs.attributes[glyphPosition].dontPrint)
1717 line.textWidth += glyphs.advances_x[glyphPosition];
1719 } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
1721 Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
1728 void QTextLine::layout_helper(int maxGlyphs)
1730 QScriptLine &line = eng->lines[i];
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.length += 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;
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 static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QGlyphLayout &glyphLayout,
2112 const QPointF &pos, const QTextItem::RenderFlags &flags)
2116 // Make a font for this particular engine
2118 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2119 fontD->fontEngine = fontEngine;
2120 fontD->fontEngine->ref.ref();
2122 #if defined(Q_WS_WIN)
2123 if (fontEngine->supportsSubPixelPositions())
2124 fontD->hintingPreference = QFont::PreferVerticalHinting;
2126 fontD->hintingPreference = QFont::PreferFullHinting;
2127 #elif defined(Q_WS_MAC)
2128 fontD->hintingPreference = QFont::PreferNoHinting;
2129 #elif !defined(QT_NO_FREETYPE)
2130 if (fontEngine->type() == QFontEngine::Freetype) {
2131 QFontEngineFT *freeTypeEngine = static_cast<QFontEngineFT *>(fontEngine);
2132 switch (freeTypeEngine->defaultHintStyle()) {
2133 case QFontEngineFT::HintNone:
2134 fontD->hintingPreference = QFont::PreferNoHinting;
2136 case QFontEngineFT::HintLight:
2137 fontD->hintingPreference = QFont::PreferVerticalHinting;
2139 case QFontEngineFT::HintMedium:
2140 case QFontEngineFT::HintFull:
2141 fontD->hintingPreference = QFont::PreferFullHinting;
2147 QVarLengthArray<glyph_t> glyphsArray;
2148 QVarLengthArray<QFixedPoint> positionsArray;
2150 fontEngine->getGlyphPositions(glyphLayout, QTransform(), flags, glyphsArray,
2152 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2154 QVector<quint32> glyphs;
2155 QVector<QPointF> positions;
2156 for (int i=0; i<glyphsArray.size(); ++i) {
2157 glyphs.append(glyphsArray.at(i) & 0xffffff);
2158 positions.append(positionsArray.at(i).toPointF() + pos);
2161 glyphRun.setGlyphIndexes(glyphs);
2162 glyphRun.setPositions(positions);
2164 glyphRun.setOverline(flags.testFlag(QTextItem::Overline));
2165 glyphRun.setUnderline(flags.testFlag(QTextItem::Underline));
2166 glyphRun.setStrikeOut(flags.testFlag(QTextItem::StrikeOut));
2167 glyphRun.setRawFont(font);
2173 Returns the glyph indexes and positions for all glyphs in this QTextLine for characters
2174 in the range defined by \a from and \a length. The \a from index is relative to the beginning
2175 of the text in the containing QTextLayout, and the range must be within the range of QTextLine
2176 as given by functions textStart() and textLength().
2178 If \a from is negative, it will default to textStart(), and if \a length is negative it will
2179 default to the return value of textLength().
2183 \sa QTextLayout::glyphRuns()
2185 #if !defined(QT_NO_RAWFONT)
2186 QList<QGlyphRun> QTextLine::glyphRuns(int from, int length) const
2188 const QScriptLine &line = eng->lines[i];
2190 if (line.length == 0)
2191 return QList<QGlyphRun>();
2197 length = textLength();
2199 QTextLineItemIterator iterator(eng, i);
2200 qreal y = line.y.toReal() + line.base().toReal();
2201 QList<QGlyphRun> glyphRuns;
2202 while (!iterator.atEnd()) {
2203 QScriptItem &si = iterator.next();
2204 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2207 QPointF pos(iterator.x.toReal(), y);
2208 if (from >= 0 && length >= 0 &&
2209 (from >= si.position + eng->length(&si) || from + length <= si.position)) {
2213 QFont font = eng->font(si);
2215 QTextItem::RenderFlags flags;
2216 if (font.overline())
2217 flags |= QTextItem::Overline;
2218 if (font.underline())
2219 flags |= QTextItem::Underline;
2220 if (font.strikeOut())
2221 flags |= QTextItem::StrikeOut;
2224 if (si.analysis.bidiLevel % 2) {
2225 flags |= QTextItem::RightToLeft;
2229 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2230 int relativeTo = qMin(iterator.itemEnd - 1, from + length - 1) - si.position;
2232 unsigned short *logClusters = eng->logClusters(&si);
2233 int glyphsStart = logClusters[relativeFrom];
2234 int glyphsEnd = (relativeTo == eng->length(&si))
2236 : logClusters[relativeTo];
2238 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2240 // Calculate new x position of glyph layout for a subset. This becomes somewhat complex
2241 // when we're breaking a RTL script item, since the expected position passed into
2242 // getGlyphPositions() is the left-most edge of the left-most glyph in an RTL run.
2243 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2244 for (int i=0; i<glyphsStart; ++i) {
2245 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2246 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2247 glyphLayout.advances_y[i].toReal());
2249 } else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2250 for (int i=glyphLayout.numGlyphs - 1; i>glyphsEnd; --i) {
2251 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2252 pos += QPointF((glyphLayout.advances_x[i] + justification).toReal(),
2253 glyphLayout.advances_y[i].toReal());
2257 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2259 if (glyphLayout.numGlyphs > 0) {
2260 QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script);
2261 if (mainFontEngine->type() == QFontEngine::Multi) {
2262 QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine);
2265 int which = glyphLayout.glyphs[0] >> 24;
2266 for (end = 0; end < glyphLayout.numGlyphs; ++end) {
2267 const int e = glyphLayout.glyphs[end] >> 24;
2271 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2272 glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
2273 subLayout, pos, flags));
2274 for (int i = 0; i < subLayout.numGlyphs; i++) {
2275 pos += QPointF(subLayout.advances_x[i].toReal(),
2276 subLayout.advances_y[i].toReal());
2283 QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
2284 glyphRuns.append(glyphRunWithInfo(multiFontEngine->engine(which),
2285 subLayout, pos, flags));
2287 glyphRuns.append(glyphRunWithInfo(mainFontEngine, glyphLayout, pos, flags));
2294 #endif // QT_NO_RAWFONT
2297 \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const
2299 Draws a line on the given \a painter at the specified \a position.
2300 The \a selection is reserved for internal use.
2302 void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatRange *selection) const
2304 const QScriptLine &line = eng->lines[i];
2305 QPen pen = p->pen();
2307 bool noText = (selection && selection->format.property(SuppressText).toBool());
2311 && selection->start <= line.from
2312 && selection->start + selection->length > line.from) {
2314 const qreal lineHeight = line.height().toReal();
2315 QRectF r(pos.x() + line.x.toReal(), pos.y() + line.y.toReal(),
2316 lineHeight / 2, QFontMetrics(eng->font()).width(QLatin1Char(' ')));
2317 setPenAndDrawBackground(p, QPen(), selection->format, r);
2324 QTextLineItemIterator iterator(eng, i, pos, selection);
2325 QFixed lineBase = line.base();
2327 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2329 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2330 while (!iterator.atEnd()) {
2331 QScriptItem &si = iterator.next();
2333 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2336 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2337 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2340 QFixed itemBaseLine = y;
2341 QFont f = eng->font(si);
2342 QTextCharFormat format;
2344 if (eng->hasFormats() || selection) {
2345 format = eng->format(&si);
2346 if (suppressColors) {
2347 format.clearForeground();
2348 format.clearBackground();
2349 format.clearProperty(QTextFormat::TextUnderlineColor);
2352 format.merge(selection->format);
2354 setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2355 iterator.itemWidth.toReal(), line.height().toReal()));
2357 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2358 if (valign == QTextCharFormat::AlignSuperScript || valign == QTextCharFormat::AlignSubScript) {
2359 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2360 QFixed height = fe->ascent() + fe->descent();
2361 if (valign == QTextCharFormat::AlignSubScript)
2362 itemBaseLine += height / 6;
2363 else if (valign == QTextCharFormat::AlignSuperScript)
2364 itemBaseLine -= height / 2;
2368 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2370 if (eng->hasFormats()) {
2372 if (si.analysis.flags == QScriptAnalysis::Object && eng->block.docHandle()) {
2373 QFixed itemY = y - si.ascent;
2374 if (format.verticalAlignment() == QTextCharFormat::AlignTop) {
2375 itemY = y - lineBase;
2378 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2380 eng->docLayout()->drawInlineObject(p, itemRect,
2381 QTextInlineObject(iterator.item, eng),
2382 si.position + eng->block.position(),
2385 QBrush bg = format.brushProperty(ObjectSelectionBrush);
2386 if (bg.style() != Qt::NoBrush) {
2387 QColor c = bg.color();
2389 p->fillRect(itemRect, c);
2392 } else { // si.isTab
2393 QFont f = eng->font(si);
2394 QTextItemInt gf(si, &f, format);
2397 gf.width = iterator.itemWidth;
2398 p->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf);
2399 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2400 QChar visualTab(0x2192);
2401 int w = QFontMetrics(f).width(visualTab);
2402 qreal x = iterator.itemWidth.toReal() - w; // Right-aligned
2404 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2405 iterator.itemWidth.toReal(), line.height().toReal()),
2409 p->drawText(QPointF(iterator.x.toReal() + x,
2410 y.toReal()), visualTab);
2420 unsigned short *logClusters = eng->logClusters(&si);
2421 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2423 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2424 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2425 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2426 gf.logClusters = logClusters + iterator.itemStart - si.position;
2427 gf.width = iterator.itemWidth;
2428 gf.justified = line.justified;
2429 gf.initWithScriptItem(si);
2431 Q_ASSERT(gf.fontEngine);
2433 if (eng->underlinePositions) {
2434 // can't have selections in this case
2435 drawMenuText(p, iterator.x, itemBaseLine, si, gf, eng, iterator.itemStart, iterator.glyphsStart);
2437 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2438 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2440 path.setFillRule(Qt::WindingFill);
2442 if (gf.glyphs.numGlyphs)
2443 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2445 const QFontEngine *fe = gf.fontEngine;
2446 const qreal lw = fe->lineThickness().toReal();
2447 if (gf.flags & QTextItem::Underline) {
2448 qreal offs = fe->underlinePosition().toReal();
2449 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2451 if (gf.flags & QTextItem::Overline) {
2452 qreal offs = fe->ascent().toReal() + 1;
2453 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2455 if (gf.flags & QTextItem::StrikeOut) {
2456 qreal offs = fe->ascent().toReal() / 3;
2457 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2462 p->setRenderHint(QPainter::Antialiasing);
2463 //Currently QPen with a Qt::NoPen style still returns a default
2464 //QBrush which != Qt::NoBrush so we need this specialcase to reset it
2465 if (p->pen().style() == Qt::NoPen)
2466 p->setBrush(Qt::NoBrush);
2468 p->setBrush(p->pen().brush());
2470 p->setPen(format.textOutline());
2475 gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be
2476 p->drawTextItem(pos, gf);
2479 if (si.analysis.flags == QScriptAnalysis::Space
2480 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2481 QBrush c = format.foreground();
2482 if (c.style() != Qt::NoBrush)
2483 p->setPen(c.color());
2484 QChar visualSpace((ushort)0xb7);
2485 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2491 if (eng->hasFormats())
2496 \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const
2502 Converts the cursor position \a cursorPos to the corresponding x position
2503 inside the line, taking account of the \a edge.
2505 If \a cursorPos is not a valid cursor position, the nearest valid
2506 cursor position will be used instead, and cpos will be modified to
2507 point to this valid cursor position.
2511 qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
2513 if (!eng->layoutData)
2516 const QScriptLine &line = eng->lines[i];
2517 bool lastLine = i >= eng->lines.size() - 1;
2520 x += eng->alignLine(line);
2522 if (!i && !eng->layoutData->items.size()) {
2527 int pos = *cursorPos;
2529 if (pos == line.from + (int)line.length) {
2530 // end of line ensure we have the last item on the line
2531 itm = eng->findItem(pos-1);
2534 itm = eng->findItem(pos);
2535 eng->shapeLine(line);
2537 const QScriptItem *si = &eng->layoutData->items[itm];
2538 if (!si->num_glyphs)
2540 pos -= si->position;
2542 QGlyphLayout glyphs = eng->shapedGlyphs(si);
2543 unsigned short *logClusters = eng->logClusters(si);
2544 Q_ASSERT(logClusters);
2546 int l = eng->length(itm);
2552 int glyph_pos = pos == l ? si->num_glyphs : logClusters[pos];
2553 if (edge == Trailing && glyph_pos < si->num_glyphs) {
2554 // trailing edge is leading edge of next cluster
2556 while (glyph_pos < si->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2560 bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
2562 int lineEnd = line.from + line.length;
2564 // add the items left of the cursor
2566 int firstItem = eng->findItem(line.from);
2567 int lastItem = eng->findItem(lineEnd - 1);
2568 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2570 QVarLengthArray<int> visualOrder(nItems);
2571 QVarLengthArray<uchar> levels(nItems);
2572 for (int i = 0; i < nItems; ++i)
2573 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2574 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2576 for (int i = 0; i < nItems; ++i) {
2577 int item = visualOrder[i]+firstItem;
2580 QScriptItem &si = eng->layoutData->items[item];
2584 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2588 int start = qMax(line.from, si.position);
2589 int end = qMin(lineEnd, si.position + eng->length(item));
2591 logClusters = eng->logClusters(&si);
2593 int gs = logClusters[start-si.position];
2594 int ge = (end == si.position + eng->length(item)) ? si.num_glyphs-1 : logClusters[end-si.position-1];
2596 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2599 x += glyphs.effectiveAdvance(gs);
2604 logClusters = eng->logClusters(si);
2605 glyphs = eng->shapedGlyphs(si);
2606 if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
2607 if (pos == (reverse ? 0 : l))
2610 bool rtl = eng->isRightToLeft();
2611 bool visual = eng->visualCursorMovement();
2612 int end = qMin(lineEnd, si->position + l) - si->position;
2614 int glyph_end = end == l ? si->num_glyphs : logClusters[end];
2615 int glyph_start = glyph_pos;
2616 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
2618 for (int i = glyph_end - 1; i >= glyph_start; i--)
2619 x += glyphs.effectiveAdvance(i);
2621 int start = qMax(line.from - si->position, 0);
2622 int glyph_start = logClusters[start];
2623 int glyph_end = glyph_pos;
2624 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
2626 for (int i = glyph_start; i <= glyph_end; i++)
2627 x += glyphs.effectiveAdvance(i);
2629 x += eng->offsetInLigature(si, pos, end, glyph_pos);
2632 *cursorPos = pos + si->position;
2637 \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const
2639 Converts the x-coordinate \a x, to the nearest matching cursor
2640 position, depending on the cursor position type, \a cpos.
2644 int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
2646 QFixed x = QFixed::fromReal(_x);
2647 const QScriptLine &line = eng->lines[i];
2648 bool lastLine = i >= eng->lines.size() - 1;
2651 if (!eng->layoutData)
2654 int line_length = textLength();
2659 int firstItem = eng->findItem(line.from);
2660 int lastItem = eng->findItem(line.from + line_length - 1);
2661 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2667 x -= eng->alignLine(line);
2668 // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
2670 QVarLengthArray<int> visualOrder(nItems);
2671 QVarLengthArray<unsigned char> levels(nItems);
2672 for (int i = 0; i < nItems; ++i)
2673 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2674 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
2676 bool visual = eng->visualCursorMovement();
2678 // left of first item
2679 int item = visualOrder[0]+firstItem;
2680 QScriptItem &si = eng->layoutData->items[item];
2683 int pos = si.position;
2684 if (si.analysis.bidiLevel % 2)
2685 pos += eng->length(item);
2686 pos = qMax(line.from, pos);
2687 pos = qMin(line.from + line_length, pos);
2689 } else if (x < line.textWidth
2690 || (line.justified && x < line.width)) {
2691 // has to be in one of the runs
2693 bool rtl = eng->isRightToLeft();
2695 eng->shapeLine(line);
2696 QVector<int> insertionPoints;
2698 eng->insertionPointsForLine(lineNum, insertionPoints);
2700 for (int i = 0; i < nItems; ++i) {
2701 int item = visualOrder[i]+firstItem;
2702 QScriptItem &si = eng->layoutData->items[item];
2703 int item_length = eng->length(item);
2704 // qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
2706 int start = qMax(line.from - si.position, 0);
2707 int end = qMin(line.from + line_length - si.position, item_length);
2709 unsigned short *logClusters = eng->logClusters(&si);
2711 int gs = logClusters[start];
2712 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
2713 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2715 QFixed item_width = 0;
2716 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2717 item_width = si.width;
2721 item_width += glyphs.effectiveAdvance(g);
2725 // qDebug(" start=%d, end=%d, gs=%d, ge=%d item_width=%f", start, end, gs, ge, item_width.toReal());
2727 if (pos + item_width < x) {
2732 // qDebug(" inside run");
2733 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2734 if (cpos == QTextLine::CursorOnCharacter)
2736 bool left_half = (x - pos) < item_width/2;
2738 if (bool(si.analysis.bidiLevel % 2) != left_half)
2740 return si.position + 1;
2745 // has to be inside run
2746 if (cpos == QTextLine::CursorOnCharacter) {
2747 if (si.analysis.bidiLevel % 2) {
2751 if (glyphs.attributes[gs].clusterStart) {
2758 pos -= glyphs.effectiveAdvance(gs);
2764 if (glyphs.attributes[gs].clusterStart) {
2770 pos += glyphs.effectiveAdvance(gs);
2775 QFixed dist = INT_MAX/256;
2776 if (si.analysis.bidiLevel % 2) {
2777 if (!visual || rtl || (lastLine && i == nItems - 1)) {
2780 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2785 pos -= glyphs.effectiveAdvance(gs);
2790 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
2795 pos += glyphs.effectiveAdvance(ge);
2800 if (!visual || !rtl || (lastLine && i == 0)) {
2802 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2807 pos += glyphs.effectiveAdvance(gs);
2811 QFixed oldPos = pos;
2813 pos += glyphs.effectiveAdvance(gs);
2814 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
2824 if (qAbs(x-pos) < dist) {
2826 if (!rtl && i < nItems - 1) {
2830 if (rtl && nchars > 0)
2831 return insertionPoints[lastLine ? nchars : nchars - 1];
2833 return eng->positionInLigature(&si, end, x, pos, -1,
2834 cpos == QTextLine::CursorOnCharacter);
2837 Q_ASSERT(glyph_pos != -1);
2838 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
2839 cpos == QTextLine::CursorOnCharacter);
2842 // right of last item
2843 // qDebug() << "right of last";
2844 int item = visualOrder[nItems-1]+firstItem;
2845 QScriptItem &si = eng->layoutData->items[item];
2848 int pos = si.position;
2849 if (!(si.analysis.bidiLevel % 2))
2850 pos += eng->length(item);
2851 pos = qMax(line.from, pos);
2853 int maxPos = line.from + line_length;
2855 // except for the last line we assume that the
2856 // character between lines is a space and we want
2857 // to position the cursor to the left of that
2859 // ###### breaks with japanese for example
2860 if (this->i < eng->lines.count() - 1)
2863 pos = qMin(pos, maxPos);