12#include <qvarlengtharray.h>
13#include <qtextformat.h>
14#include <qabstracttextdocumentlayout.h>
27#include <private/qpainter_p.h>
31#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1
)
32#define SuppressText 0x5012
33#define SuppressBackground 0x513
36
37
38
39
40
41
42
43
44
47
48
49
52
53
54
57
58
59
62
63
64
65
68
69
70
71
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
95
96
97
98
99
100
103
104
105
106
109
110
111
112
113
116
117
118
119
120QRectF QTextInlineObject::rect()
const
122 QScriptItem& si = eng->layoutData->items[itm];
123 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
127
128
129
130
131qreal QTextInlineObject::width()
const
133 return eng->layoutData->items.at(itm).width.toReal();
137
138
139
140
141qreal QTextInlineObject::ascent()
const
143 return eng->layoutData->items.at(itm).ascent.toReal();
147
148
149
150
151qreal QTextInlineObject::descent()
const
153 return eng->layoutData->items.at(itm).descent.toReal();
157
158
159
160
161
162qreal QTextInlineObject::height()
const
164 return eng->layoutData->items.at(itm).height().toReal();
168
169
170
171
172void QTextInlineObject::setWidth(qreal w)
174 eng->layoutData->items[itm].width = QFixed::fromReal(w);
178
179
180
181
182void QTextInlineObject::setAscent(qreal a)
184 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
188
189
190
191
192void QTextInlineObject::setDescent(qreal d)
194 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
198
199
200int QTextInlineObject::textPosition()
const
202 return eng->layoutData->items[itm].position;
206
207
208
209int QTextInlineObject::formatIndex()
const
211 return eng->formatIndex(&eng->layoutData->items[itm]);
215
216
217QTextFormat QTextInlineObject::format()
const
219 return eng->format(&eng->layoutData->items[itm]);
223
224
225Qt::LayoutDirection QTextInlineObject::textDirection()
const
227 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
283
284
285
286
287
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
310
311
312
313
314
317
318
319
320
321QTextLayout::QTextLayout()
322{ d =
new QTextEngine(); }
325
326
327QTextLayout::QTextLayout(
const QString& text)
329 d =
new QTextEngine();
334
335
336
337
338
339
340
341
342
344QTextLayout::QTextLayout(
const QString &text,
const QFont &font,
const QPaintDevice *paintdevice)
346 const QFont f(paintdevice ? QFont(font, paintdevice) : font);
347 d =
new QTextEngine((text.isNull() ? (
const QString&)QString::fromLatin1(
"") : text), f);
351
352
353
354QTextLayout::QTextLayout(
const QTextBlock &block)
356 d =
new QTextEngine();
361
362
363QTextLayout::~QTextLayout()
371
372
373
374
375
376
377void QTextLayout::setRawFont(
const QRawFont &rawFont)
379 d->rawFont = rawFont;
380 d->useRawFont =
true;
381 d->resetFontEngineCache();
386
387
388
389
390
391void QTextLayout::setFont(
const QFont &font)
395 d->useRawFont =
false;
397 d->resetFontEngineCache();
401
402
403
404
405
406QFont QTextLayout::font()
const
412
413
414
415
416
417
418
419
420void QTextLayout::setText(
const QString& string)
428
429
430
431
432QString QTextLayout::text()
const
438
439
440
441
442
443void QTextLayout::setTextOption(
const QTextOption &option)
449
450
451
452
453const QTextOption &QTextLayout::textOption()
const
459
460
461
462
463
464
465void QTextLayout::setPreeditArea(
int position,
const QString &text)
467 if (d->preeditAreaPosition() == position && d->preeditAreaText() == text)
469 d->setPreeditArea(position, text);
471 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
472 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
476
477
478
479
480
481int QTextLayout::preeditAreaPosition()
const
483 return d->preeditAreaPosition();
487
488
489
490
491QString QTextLayout::preeditAreaText()
const
493 return d->preeditAreaText();
497
498
499
500
501
502
503
504void QTextLayout::setFormats(
const QList<FormatRange> &formats)
506 d->setFormats(formats);
508 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
509 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
513
514
515
516
517
518
519QList<QTextLayout::FormatRange> QTextLayout::formats()
const
525
526
527
528
529
530
531void QTextLayout::clearFormats()
533 setFormats(QList<FormatRange>());
537
538
539
540
541
542
543
544
545
546void QTextLayout::setCacheEnabled(
bool enable)
548 d->cacheGlyphs = enable;
552
553
554
555
556
557bool QTextLayout::cacheEnabled()
const
559 return d->cacheGlyphs;
563
564
565
566
567
568
569
570void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
572 d->visualMovement = style == Qt::VisualMoveStyle;
576
577
578
579
580
581Qt::CursorMoveStyle QTextLayout::cursorMoveStyle()
const
583 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
587
588
589
590
591
592
593
594void QTextLayout::beginLayout()
597 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
598 qWarning(
"QTextLayout::beginLayout: Called while already doing layout");
605 d->layoutData->layoutState = QTextEngine::InLayout;
609
610
611
612
613void QTextLayout::endLayout()
616 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
617 qWarning(
"QTextLayout::endLayout: Called without beginLayout()");
621 int l = d->lines.size();
622 if (l && d->lines.at(l-1).length < 0) {
623 QTextLine(l-1, d).setNumColumns(INT_MAX);
625 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
631
632
633
634
635
636
637
638
639void QTextLayout::clearLayout()
645
646
647
648
649
650
651int QTextLayout::nextCursorPosition(
int oldPos, CursorMode mode)
const
653 const QCharAttributes *attributes = d->attributes();
654 int len = d->block.isValid() ? d->block.length() - 1
655 : d->layoutData->string.size();
656 Q_ASSERT(len <= d->layoutData->string.size());
657 if (!attributes || oldPos < 0 || oldPos >= len)
660 if (mode == SkipCharacters) {
662 while (oldPos < len && !attributes[oldPos].graphemeBoundary)
665 if (oldPos < len && d->atWordSeparator(oldPos)) {
667 while (oldPos < len && d->atWordSeparator(oldPos))
670 while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
673 while (oldPos < len && attributes[oldPos].whiteSpace)
681
682
683
684
685
686
687int QTextLayout::previousCursorPosition(
int oldPos, CursorMode mode)
const
689 const QCharAttributes *attributes = d->attributes();
690 int len = d->block.isValid() ? d->block.length() - 1
691 : d->layoutData->string.size();
692 Q_ASSERT(len <= d->layoutData->string.size());
693 if (!attributes || oldPos <= 0 || oldPos > len)
696 if (mode == SkipCharacters) {
698 while (oldPos && !attributes[oldPos].graphemeBoundary)
701 while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
704 if (oldPos && d->atWordSeparator(oldPos-1)) {
706 while (oldPos && d->atWordSeparator(oldPos-1))
709 while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
718
719
720
721
722
723
724int QTextLayout::rightCursorPosition(
int oldPos)
const
726 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
732
733
734
735
736
737
738int QTextLayout::leftCursorPosition(
int oldPos)
const
740 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760bool QTextLayout::isValidCursorPosition(
int pos)
const
762 const QCharAttributes *attributes = d->attributes();
763 if (!attributes || pos < 0 || pos > (
int)d->layoutData->string.size())
765 return attributes[pos].graphemeBoundary;
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784QTextLine QTextLayout::createLine()
787 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
788 qWarning(
"QTextLayout::createLine: Called without layouting");
792 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
795 int l = d->lines.size();
796 if (l && d->lines.at(l-1).length < 0) {
797 QTextLine(l-1, d).setNumColumns(INT_MAX);
798 if (d->maxWidth > QFIXED_MAX / 2) {
799 qWarning(
"QTextLayout: text too long, truncated.");
803 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
804 int strlen = d->layoutData->string.size();
805 if (l && from >= strlen) {
806 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
813 line.justified =
false;
814 line.gridfitted =
false;
816 d->lines.append(line);
817 return QTextLine(l, d);
821
822
823
824
825int QTextLayout::lineCount()
const
827 return d->lines.size();
831
832
833
834
835QTextLine QTextLayout::lineAt(
int i)
const
837 return i < lineCount() ? QTextLine(i, d) : QTextLine();
841
842
843
844
845QTextLine QTextLayout::lineForTextPosition(
int pos)
const
847 int lineNum = d->lineNumberForTextPosition(pos);
848 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
852
853
854
855
856
857
858
859QPointF QTextLayout::position()
const
865
866
867
868
869void QTextLayout::setPosition(
const QPointF &p)
875
876
877QRectF QTextLayout::boundingRect()
const
879 if (d->lines.isEmpty())
883 QFixed xmin = d->lines.at(0).x;
884 QFixed ymin = d->lines.at(0).y;
886 for (
int i = 0; i < d->lines.size(); ++i) {
887 const QScriptLine &si = d->lines.at(i);
888 xmin = qMin(xmin, si.x);
889 ymin = qMin(ymin, si.y);
890 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
891 xmax = qMax(xmax, si.x+lineWidth);
893 ymax = qMax(ymax, si.y+si.height().ceil());
895 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
899
900
901
902
903
904
905
906
907qreal QTextLayout::minimumWidth()
const
909 return d->minWidth.toReal();
913
914
915
916
917
918
919
920
921qreal QTextLayout::maximumWidth()
const
923 return d->maxWidth.toReal();
928
929
930void QTextLayout::setFlags(
int flags)
932 if (flags & Qt::TextJustificationForced) {
933 d->option.setAlignment(Qt::AlignJustify);
934 d->forceJustification =
true;
937 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
938 d->ignoreBidi =
true;
939 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
944 QPainterPath *region,
const QRectF &boundingRect)
946 const QScriptLine &line = eng->lines[lineNumber];
952 const qreal selectionY = pos.y() + line.y.toReal();
953 const qreal lineHeight = line.height().toReal();
955 QFixed lastSelectionX = iterator.x;
956 QFixed lastSelectionWidth;
961 QFixed selectionX, selectionWidth;
962 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
963 if (selectionX == lastSelectionX + lastSelectionWidth) {
964 lastSelectionWidth += selectionWidth;
968 if (lastSelectionWidth > 0) {
969 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
970 region->addRect(rect.toAlignedRect());
973 lastSelectionX = selectionX;
974 lastSelectionWidth = selectionWidth;
977 if (lastSelectionWidth > 0) {
978 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
979 region->addRect(rect.toAlignedRect());
985 return clip.isValid() ? (rect & clip) : rect;
989#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009# if !defined(QT_NO_RAWFONT)
1010QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
int length)
const
1012 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033#if !defined(QT_NO_RAWFONT)
1034QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
1036 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
1041 length = text().size();
1043 QHash<std::pair<QFontEngine *,
int>, QGlyphRun> glyphRunHash;
1044 for (
int i=0; i<d->lines.size(); ++i) {
1045 if (d->lines.at(i).from > from + length)
1047 else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
1048 const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
1049 for (
const QGlyphRun &glyphRun : glyphRuns) {
1050 QRawFont rawFont = glyphRun.rawFont();
1052 QFontEngine *fontEngine = rawFont.d->fontEngine;
1053 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1054 std::pair<QFontEngine *,
int> key(fontEngine,
int(flags));
1056 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1057 if (oldGlyphRun.isEmpty()) {
1058 oldGlyphRun = glyphRun;
1060 QList<quint32> indexes = oldGlyphRun.glyphIndexes();
1061 QList<QPointF> positions = oldGlyphRun.positions();
1062 QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
1063 QRectF boundingRect = oldGlyphRun.boundingRect();
1065 indexes += glyphRun.glyphIndexes();
1066 positions += glyphRun.positions();
1067 stringIndexes += glyphRun.stringIndexes();
1068 boundingRect = boundingRect.united(glyphRun.boundingRect());
1070 oldGlyphRun.setGlyphIndexes(indexes);
1071 oldGlyphRun.setPositions(positions);
1072 oldGlyphRun.setStringIndexes(stringIndexes);
1073 oldGlyphRun.setBoundingRect(boundingRect);
1079 return glyphRunHash.values();
1084
1085
1086
1087
1088void QTextLayout::draw(QPainter *p,
const QPointF &pos,
const QList<FormatRange> &selections,
const QRectF &clip)
const
1090 if (d->lines.isEmpty())
1096 QPointF position = pos + d->position;
1098 QFixed clipy = (INT_MIN/256);
1099 QFixed clipe = (INT_MAX/256);
1100 if (clip.isValid()) {
1101 clipy = QFixed::fromReal(clip.y() - position.y());
1102 clipe = clipy + QFixed::fromReal(clip.height());
1106 int lastLine = d->lines.size();
1107 for (
int i = 0; i < d->lines.size(); ++i) {
1108 const QScriptLine &sl = d->lines.at(i);
1114 if ((sl.y + sl.height()) < clipy) {
1120 QPainterPath excludedRegion;
1121 QPainterPath textDoneRegion;
1122 for (
int i = 0; i < selections.size(); ++i) {
1123 FormatRange selection = selections.at(i);
1124 QPainterPath region;
1125 region.setFillRule(Qt::WindingFill);
1127 for (
int line = firstLine; line < lastLine; ++line) {
1128 const QScriptLine &sl = d->lines.at(line);
1129 QTextLine tl(line, d);
1131 QRectF lineRect(tl.naturalTextRect());
1132 lineRect.translate(position);
1133 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1134 lineRect.setBottom(qCeil(lineRect.bottom()));
1136 bool isLastLineInBlock = (line == d->lines.size()-1);
1137 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1140 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1143 const bool selectionStartInLine = sl.from <= selection.start;
1144 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1146 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1147 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1149 region.addRect(clipIfValid(lineRect, clip));
1152 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1153 QRectF fullLineRect(tl.rect());
1154 fullLineRect.translate(position);
1155 fullLineRect.setRight(QFIXED_MAX);
1156 fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
1158 const bool rightToLeft = d->isRightToLeft();
1160 if (!selectionEndInLine) {
1161 region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
1162 : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1164 if (!selectionStartInLine) {
1165 region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
1166 : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1168 }
else if (!selectionEndInLine
1169 && isLastLineInBlock
1170 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1171 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1172 lineRect.height()/4, lineRect.height()), clip));
1177 const QPen oldPen = p->pen();
1178 const QBrush oldBrush = p->brush();
1180 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1181 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1182 p->drawPath(region);
1185 p->setBrush(oldBrush);
1190 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1191 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1193 if (hasBackground) {
1194 selection.format.setProperty(
ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1197 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1198 selection.format.clearProperty(QTextFormat::OutlinePen);
1203 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1207 p->setClipPath(region, Qt::IntersectClip);
1209 for (
int line = firstLine; line < lastLine; ++line) {
1210 QTextLine l(line, d);
1211 l.draw_internal(p, position, &selection);
1216 textDoneRegion += region;
1219 textDoneRegion -= region;
1222 excludedRegion += region;
1225 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1226 if (!needsTextButNoBackground.isEmpty()){
1228 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1229 FormatRange selection;
1230 selection.start = 0;
1231 selection.length = INT_MAX;
1233 for (
int line = firstLine; line < lastLine; ++line) {
1234 QTextLine l(line, d);
1235 l.draw_internal(p, position, &selection);
1240 if (!excludedRegion.isEmpty()) {
1243 QRectF br = boundingRect().translated(position);
1244 br.setRight(QFIXED_MAX);
1246 br = br.intersected(clip);
1248 path -= excludedRegion;
1249 p->setClipPath(path, Qt::IntersectClip);
1252 for (
int i = firstLine; i < lastLine; ++i) {
1254 l.draw(p, position);
1256 if (!excludedRegion.isEmpty())
1260 if (!d->cacheGlyphs)
1265
1266
1267
1268
1269
1270
1271
1272void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition)
const
1274 drawCursor(p, pos, cursorPosition, 1);
1278
1279
1280
1281
1282
1283
1284void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition,
int width)
const
1286 if (d->lines.isEmpty())
1292 QPointF position = pos + d->position;
1294 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
1295 int line = d->lineNumberForTextPosition(cursorPosition);
1298 if (line >= d->lines.size())
1301 QTextLine l(line, d);
1302 const QScriptLine &sl = d->lines.at(line);
1304 qreal x = position.x() + l.cursorToX(cursorPosition);
1306 QFixed base = sl.base();
1307 QFixed descent = sl.descent;
1308 bool rightToLeft = d->isRightToLeft();
1310 const int realCursorPosition = cursorPosition;
1311 if (d->visualCursorMovement()) {
1312 if (cursorPosition == sl.from + sl.length)
1317 int itm = d->findItem(cursorPosition);
1320 const QScriptItem *si = &d->layoutData->items.at(itm);
1323 if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
1324 int neighborItem = itm;
1325 if (neighborItem > 0 && si->position == realCursorPosition)
1327 else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
1329 const bool onBoundary = neighborItem != itm
1330 && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
1331 if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
1333 si = &d->layoutData->items[itm];
1337 if (si->analysis.flags != QScriptAnalysis::Object) {
1340 if (si->descent > 0)
1341 descent = si->descent;
1343 rightToLeft = si->analysis.bidiLevel % 2;
1345 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1346 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1347 && (p->transform().type() > QTransform::TxTranslate);
1348 if (toggleAntialiasing)
1349 p->setRenderHint(QPainter::Antialiasing);
1350 QPainter::CompositionMode origCompositionMode = p->compositionMode();
1351 if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
1352 p->setCompositionMode(QPainter::RasterOp_NotDestination);
1353 const QTransform &deviceTransform = p->deviceTransform();
1354 const qreal xScale = deviceTransform.m11();
1355 if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
1356 p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
1359 const QPen origPen = p->pen();
1360 QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
1361 pen.setCosmetic(
true);
1362 const qreal center = x + qreal(width) / 2;
1364 p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
1367 p->setCompositionMode(origCompositionMode);
1368 if (toggleAntialiasing)
1369 p->setRenderHint(QPainter::Antialiasing,
false);
1370 if (d->layoutData->hasBidi) {
1371 const int arrow_extent = 4;
1372 int sign = rightToLeft ? -1 : 1;
1373 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1374 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1400
1401
1402
1403
1404
1407
1408
1409
1410
1411
1414
1415
1416
1417
1418
1419
1422
1423
1424
1425
1428
1429
1430
1431
1434
1435
1436
1437
1441
1442
1443
1444
1445QRectF QTextLine::rect()
const
1447 const QScriptLine& sl = eng->lines.at(index);
1448 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1452
1453
1454QRectF QTextLine::naturalTextRect()
const
1456 const QScriptLine& sl = eng->lines.at(index);
1457 QFixed x = sl.x + eng->alignLine(sl);
1459 QFixed width = sl.textWidth;
1463 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1467
1468
1469
1470
1471qreal QTextLine::x()
const
1473 return eng->lines.at(index).x.toReal();
1477
1478
1479
1480
1481qreal QTextLine::y()
const
1483 return eng->lines.at(index).y.toReal();
1487
1488
1489
1490
1491qreal QTextLine::width()
const
1493 return eng->lines.at(index).width.toReal();
1498
1499
1500
1501
1502qreal QTextLine::ascent()
const
1504 return eng->lines.at(index).ascent.toReal();
1508
1509
1510
1511
1512qreal QTextLine::descent()
const
1514 return eng->lines.at(index).descent.toReal();
1518
1519
1520
1521
1522
1523
1524qreal QTextLine::height()
const
1526 return eng->lines.at(index).height().ceil().toReal();
1530
1531
1532
1533
1534
1535
1536qreal QTextLine::leading()
const
1538 return eng->lines.at(index).leading.toReal();
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555void QTextLine::setLeadingIncluded(
bool included)
1557 eng->lines[index].leadingIncluded= included;
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571bool QTextLine::leadingIncluded()
const
1573 return eng->lines.at(index).leadingIncluded;
1577
1578
1579
1580
1581qreal QTextLine::naturalTextWidth()
const
1583 return eng->lines.at(index).textWidth.toReal();
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596qreal QTextLine::horizontalAdvance()
const
1598 return eng->lines.at(index).textAdvance.toReal();
1602
1603
1604
1605
1606
1607
1608void QTextLine::setLineWidth(qreal width)
1610 QScriptLine &line = eng->lines[index];
1611 if (!eng->layoutData) {
1612 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1616 line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
1618 && line.textWidth <= line.width
1619 && line.from + line.length == eng->layoutData->string.size())
1626 layout_helper(INT_MAX);
1630
1631
1632
1633
1634
1635
1636void QTextLine::setNumColumns(
int numColumns)
1638 QScriptLine &line = eng->lines[index];
1639 line.width = QFIXED_MAX;
1642 layout_helper(numColumns);
1646
1647
1648
1649
1650
1651
1652
1653void QTextLine::setNumColumns(
int numColumns, qreal alignmentWidth)
1655 QScriptLine &line = eng->lines[index];
1656 line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
1659 layout_helper(numColumns);
1663#define LB_DEBUG qDebug
1665#define LB_DEBUG if (0
) qDebug
1670 struct LineBreakHelper
1672 LineBreakHelper() =
default;
1674 QScriptLine tmpData;
1675 QScriptLine spaceData;
1681 int currentPosition = 0;
1682 glyph_t previousGlyph = 0;
1683 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1686 QFixed currentSoftHyphenWidth;
1687 QFixed commitedSoftHyphenWidth;
1688 QFixed rightBearing;
1689 QFixed minimumRightBearing;
1691 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1692 const unsigned short *logClusters =
nullptr;
1694 bool manualWrap =
false;
1695 bool whiteSpaceOrObject =
true;
1697 bool checkFullOtherwiseExtend(QScriptLine &line);
1699 QFixed calculateNewWidth(
const QScriptLine &line)
const {
1700 return line.textWidth + tmpData.textWidth + spaceData.textWidth
1701 + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
1704 inline glyph_t currentGlyph()
const
1706 Q_ASSERT(currentPosition > 0);
1707 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1709 return glyphs.glyphs[logClusters[currentPosition - 1]];
1712 inline void saveCurrentGlyph()
1715 if (currentPosition > 0 &&
1716 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1717 previousGlyph = currentGlyph();
1718 previousGlyphFontEngine = fontEngine;
1722 inline void calculateRightBearing(
QFontEngine *engine, glyph_t glyph)
1726 engine->getGlyphBearings(glyph,
nullptr, &rb);
1732 rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
1735 inline void calculateRightBearing()
1737 if (currentPosition <= 0)
1739 calculateRightBearing(fontEngine.data(), currentGlyph());
1742 inline void calculateRightBearingForPreviousGlyph()
1744 if (previousGlyph > 0)
1745 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1748 static const QFixed RightBearingNotCalculated;
1750 inline void resetRightBearing()
1752 rightBearing = RightBearingNotCalculated;
1757 inline QFixed negativeRightBearing()
const
1759 if (rightBearing == RightBearingNotCalculated)
1762 return qAbs(rightBearing);
1766Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
1768inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1770 LB_DEBUG(
"possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1772 QFixed newWidth = calculateNewWidth(line);
1773 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1776 const QFixed oldTextWidth = line.textWidth;
1778 line.textWidth += spaceData.textWidth;
1780 line.length += spaceData.length;
1781 tmpData.textWidth = 0;
1783 spaceData.textWidth = 0;
1784 spaceData.length = 0;
1786 if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
1787 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1788 currentSoftHyphenWidth = 0;
1797static inline void addNextCluster(
int &pos,
int end, QScriptLine &line,
int &glyphCount,
1798 const QScriptItem ¤t,
const unsigned short *logClusters,
1799 const QGlyphLayout &glyphs, QFixed *clusterWidth =
nullptr)
1801 int glyphPosition = logClusters[pos];
1805 }
while (pos < end && logClusters[pos] == glyphPosition);
1806 QFixed clusterWid = line.textWidth;
1809 line.textWidth += glyphs.advances[glyphPosition];
1813 Q_ASSERT((pos == end && glyphPosition == current
.num_glyphs) || logClusters[pos] == glyphPosition);
1816 *clusterWidth += (line.textWidth - clusterWid);
1822void QTextLine::layout_helper(
int maxGlyphs)
1824 QScriptLine &line = eng->lines[index];
1826 line.trailingSpaces = 0;
1828 line.hasTrailingSpaces =
false;
1830 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
1831 line.setDefaultHeight(eng);
1835 Q_ASSERT(line.from < eng->layoutData->string.size());
1837 LineBreakHelper lbh;
1839 lbh.maxGlyphs = maxGlyphs;
1841 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1842 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1843 const bool breakWordOrAny = breakany || (wrapMode == QTextOption::WrapAtWordBoundaryOrAnywhere);
1844 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1847 int newItem = eng->findItem(line.from);
1848 Q_ASSERT(newItem >= 0);
1850 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f", line.from, newItem,
int(eng->layoutData->items.size()), line.width.toReal());
1852 Qt::Alignment alignment = eng->option.alignment();
1854 const QCharAttributes *attributes = eng->attributes();
1857 lbh.currentPosition = line.from;
1859 lbh.logClusters = eng->layoutData->logClustersPtr;
1860 lbh.previousGlyph = 0;
1862 bool manuallyWrapped =
false;
1863 bool hasInlineObject =
false;
1864 bool reachedEndOfLine =
false;
1865 QFixed maxInlineObjectHeight = 0;
1867 const bool includeTrailingSpaces = eng->option.flags() & QTextOption::IncludeTrailingSpaces;
1869 while (newItem < eng->layoutData->items.size()) {
1870 lbh.resetRightBearing();
1871 if (newItem != item) {
1873 const QScriptItem ¤t = eng->layoutData->items.at(item);
1874 if (!current.num_glyphs) {
1876 attributes = eng->attributes();
1879 lbh.logClusters = eng->layoutData->logClustersPtr;
1881 lbh.currentPosition = qMax(line.from, current.position);
1882 end = current.position + eng->length(item);
1883 lbh.glyphs = eng->shapedGlyphs(¤t);
1884 QFontEngine *fontEngine = eng->fontEngine(current);
1885 if (lbh.fontEngine != fontEngine) {
1886 lbh.fontEngine = fontEngine;
1887 lbh.minimumRightBearing = qMin(QFixed(),
1888 QFixed::fromReal(fontEngine->minRightBearing()));
1891 const QScriptItem ¤t = eng->layoutData->items.at(item);
1893 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1894 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1896 if (current.analysis.flags != QScriptAnalysis::Object) {
1898 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1899 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1902 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1903 lbh.whiteSpaceOrObject =
true;
1904 if (lbh.checkFullOtherwiseExtend(line))
1907 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1908 QFixed tabWidth = eng->calculateTabWidth(item, x);
1909 attributes = eng->attributes();
1912 lbh.logClusters = eng->layoutData->logClustersPtr;
1913 lbh.glyphs = eng->shapedGlyphs(¤t);
1915 lbh.spaceData.textWidth += tabWidth;
1916 lbh.spaceData.length++;
1919 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1920 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1922 if (lbh.checkFullOtherwiseExtend(line))
1924 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1925 lbh.whiteSpaceOrObject =
true;
1928 if (!line.length && !lbh.tmpData.length)
1929 line.setDefaultHeight(eng);
1930 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1931 if (lbh.checkFullOtherwiseExtend(line))
1934 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1935 current, lbh.logClusters, lbh.glyphs);
1937 lbh.tmpData.length++;
1938 lbh.calculateRightBearingForPreviousGlyph();
1940 line += lbh.tmpData;
1941 manuallyWrapped =
true;
1943 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1944 lbh.whiteSpaceOrObject =
true;
1945 lbh.tmpData.length++;
1947 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1948 QTextInlineObject inlineObject(item, eng);
1949 QTextFormat f = inlineObject.format();
1950 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1951 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1952 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1953 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1954 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1958 lbh.tmpData.textWidth += current.width;
1962 if (lbh.checkFullOtherwiseExtend(line))
1965 hasInlineObject =
true;
1966 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1968 }
else if (attributes[lbh.currentPosition].whiteSpace
1969 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1972 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
1973 lbh.saveCurrentGlyph();
1974 lbh.whiteSpaceOrObject =
true;
1975 while (lbh.currentPosition < end
1976 && attributes[lbh.currentPosition].whiteSpace
1977 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1978 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1979 current, lbh.logClusters, lbh.glyphs);
1982 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
1985 lbh.whiteSpaceOrObject =
false;
1986 bool sb_or_ws =
false;
1990 if (lbh.currentPosition == 0
1991 || lbh.previousGlyph == 0
1992 || includeTrailingSpaces
1993 || !attributes[lbh.currentPosition - 1].whiteSpace) {
1994 lbh.saveCurrentGlyph();
1996 QFixed accumulatedTextWidth;
1998 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1999 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2006 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2007 && attributes[lbh.currentPosition].whiteSpace
2008 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2010 if (lbh.currentPosition >= eng->layoutData->string.size()
2012 || attributes[lbh.currentPosition].lineBreak
2013 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2016 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2017 if (breakWordOrAny) {
2018 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2019 accumulatedTextWidth = 0;
2024 }
while (lbh.currentPosition < end);
2025 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2027 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2028 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2029 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2046 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2049 if (sb_or_ws|breakany) {
2057 QFixed previousRightBearing = lbh.rightBearing;
2068 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2069 lbh.calculateRightBearing();
2071 if (lbh.checkFullOtherwiseExtend(line)) {
2076 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2077 lbh.rightBearing = previousRightBearing;
2079 lbh.calculateRightBearingForPreviousGlyph();
2081 line.textWidth += lbh.commitedSoftHyphenWidth;
2086 lbh.saveCurrentGlyph();
2088 if (lbh.currentPosition == end)
2092 reachedEndOfLine =
true;
2093 lbh.checkFullOtherwiseExtend(line);
2094 line.textWidth += lbh.commitedSoftHyphenWidth;
2096 line.textAdvance = line.textWidth;
2099 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2100 lbh.calculateRightBearing();
2103 const QFixed textWidthWithoutBearing = line.textWidth;
2104 line.textWidth += lbh.negativeRightBearing();
2106 if (line.length == 0) {
2107 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2108 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2109 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2110 line += lbh.tmpData;
2113 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2115 if (maxInlineObjectHeight > line.ascent + line.descent) {
2117 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2118 line.ascent += toAdd;
2119 line.descent = maxInlineObjectHeight - line.ascent;
2121 int startItem = eng->findItem(line.from);
2122 int endItem = eng->findItem(line.from + line.length);
2124 endItem = eng->layoutData->items.size();
2125 for (
int item = startItem; item < endItem; ++item) {
2126 QScriptItem ¤t = eng->layoutData->items[item];
2127 if (current.analysis.flags == QScriptAnalysis::Object) {
2128 QTextInlineObject inlineObject(item, eng);
2129 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2130 QFixed height = current.ascent + current.descent;
2132 case QTextCharFormat::AlignTop:
2133 current.ascent = line.ascent;
2134 current.descent = height - line.ascent;
2136 case QTextCharFormat::AlignMiddle:
2137 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2138 current.descent = height - line.ascent;
2140 case QTextCharFormat::AlignBottom:
2141 current.descent = line.descent;
2142 current.ascent = height - line.descent;
2147 Q_ASSERT(line.ascent >= current.ascent);
2148 Q_ASSERT(line.descent >= current.descent);
2154 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2155 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2156 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2158 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2159 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2160 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2161 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2163 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2164 layout_helper(lbh.maxGlyphs);
2165 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2170 if (lbh.manualWrap) {
2171 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2172 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2174 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2176 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2178 : textWidthWithoutBearing;
2179 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2180 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2181 if (!manuallyWrapped) {
2182 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2183 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2185 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2186 if (manuallyWrapped)
2187 eng->layoutData->currentMaxWidth = 0;
2190 line.textWidth += trailingSpace;
2191 if (lbh.spaceData.length) {
2192 line.trailingSpaces = lbh.spaceData.length;
2193 line.hasTrailingSpaces =
true;
2196 line.justified =
false;
2197 line.gridfitted =
false;
2201
2202
2203void QTextLine::setPosition(
const QPointF &pos)
2205 eng->lines[index].x = QFixed::fromReal(pos.x());
2206 eng->lines[index].y = QFixed::fromReal(pos.y());
2210
2211
2212QPointF QTextLine::position()
const
2214 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2225
2226
2227
2228int QTextLine::textStart()
const
2230 return eng->lines.at(index).from;
2234
2235
2236
2237
2238int QTextLine::textLength()
const
2240 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2241 && eng->block.isValid() && index == eng->lines.size()-1) {
2242 return eng->lines.at(index).length - 1;
2244 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2249 QBrush bg = chf.background();
2251 p->fillRect(r.toAlignedRect(), bg);
2256 QBrush c = chf.foreground();
2257 if (c.style() == Qt::NoBrush)
2258 p->setPen(defaultPen);
2260 p->setPen(QPen(c, 0));
2263#if !defined(QT_NO_RAWFONT)
2265 const QString &text,
2268 const QGlyphRun::GlyphRunFlags &flags,
2269 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2271 QFixed selectionWidth,
2274 unsigned short *logClusters,
2278 Q_ASSERT(logClusters !=
nullptr);
2284 int rangeStart = textPosition;
2285 int logClusterIndex = 0;
2286 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2291 int rangeEnd = rangeStart;
2292 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2302 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2303 fontD->setFontEngine(fontEngine);
2305 QVarLengthArray<glyph_t> glyphsArray;
2306 QVarLengthArray<QFixedPoint> positionsArray;
2308 QTextItem::RenderFlags renderFlags;
2309 if (flags.testFlag(QGlyphRun::Overline))
2310 renderFlags |= QTextItem::Overline;
2311 if (flags.testFlag(QGlyphRun::Underline))
2312 renderFlags |= QTextItem::Underline;
2313 if (flags.testFlag(QGlyphRun::StrikeOut))
2314 renderFlags |= QTextItem::StrikeOut;
2315 if (flags.testFlag(QGlyphRun::RightToLeft))
2316 renderFlags |= QTextItem::RightToLeft;
2318 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2320 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2322 qreal fontHeight = font.ascent() + font.descent();
2325 QList<quint32> glyphs;
2326 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2327 glyphs.reserve(glyphsArray.size());
2328 QList<QPointF> positions;
2329 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2330 positions.reserve(glyphsArray.size());
2331 QList<qsizetype> stringIndexes;
2332 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2333 stringIndexes.reserve(glyphsArray.size());
2335 int nextClusterIndex = 0;
2336 int currentClusterIndex = 0;
2337 for (
int i = 0; i < glyphsArray.size(); ++i) {
2338 const int glyphArrayIndex = i + glyphsStart;
2341 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2342 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2343 currentClusterIndex = nextClusterIndex;
2344 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2351 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2352 stringIndexes.append(textPosition + currentClusterIndex);
2355 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2356 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2357 glyphs.append(glyphIndex);
2360 QPointF position = positionsArray.at(i).toPointF() + pos;
2361 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2362 positions.append(position);
2365 maxY = minY = position.y();
2367 minY = qMin(minY, position.y());
2368 maxY = qMax(maxY, position.y());
2372 qreal height = maxY + fontHeight - minY;
2374 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2375 glyphRun.setGlyphIndexes(glyphs);
2376 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2377 glyphRun.setPositions(positions);
2378 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2379 glyphRun.setStringIndexes(stringIndexes);
2380 if (retrievalFlags & QTextLayout::RetrieveString)
2381 glyphRun.setSourceString(text);
2382 glyphRun.setFlags(flags);
2383 glyphRun.setRawFont(font);
2385 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2386 selectionWidth.toReal(), height));
2391# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2414 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2437 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2439 const QScriptLine &line = eng->lines.at(index);
2441 if (line.length == 0)
2442 return QList<QGlyphRun>();
2448 length = textLength();
2451 return QList<QGlyphRun>();
2453 QTextLayout::FormatRange selection;
2454 selection.start = from;
2455 selection.length = length;
2457 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2458 qreal y = line.y.toReal() + line.base().toReal();
2459 QList<QGlyphRun> glyphRuns;
2460 while (!iterator.atEnd()) {
2461 QScriptItem &si = iterator.next();
2462 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2465 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2468 QPointF pos(iterator.x.toReal(), y);
2471 QGlyphRun::GlyphRunFlags flags;
2472 if (!eng->useRawFont) {
2473 font = eng->font(si);
2474 if (font.overline())
2475 flags |= QGlyphRun::Overline;
2476 if (font.underline())
2477 flags |= QGlyphRun::Underline;
2478 if (font.strikeOut())
2479 flags |= QGlyphRun::StrikeOut;
2483 if (si.analysis.bidiLevel % 2) {
2484 flags |= QGlyphRun::RightToLeft;
2488 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2489 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2491 unsigned short *logClusters = eng->logClusters(&si);
2492 int glyphsStart = logClusters[relativeFrom];
2493 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2495 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2496 if (nextGlyphIndex - 1 > glyphsEnd)
2497 glyphsEnd = nextGlyphIndex - 1;
2498 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2499 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2501 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2502 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2504 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2509 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2510 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2511 if (!glyphLayout.attributes[i].dontPrint) {
2512 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2513 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2516 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2517 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2518 if (!glyphLayout.attributes[i].dontPrint) {
2519 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2520 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2525 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2529 iterator.getSelectionBounds(&x, &width);
2531 if (glyphLayout.numGlyphs > 0) {
2532 QFontEngine *mainFontEngine;
2533#ifndef QT_NO_RAWFONT
2534 if (eng->useRawFont && eng->rawFont.isValid())
2535 mainFontEngine= eng->fontEngine(si);
2538 mainFontEngine = font.d->engineForScript(si.analysis.script);
2540 if (mainFontEngine->type() == QFontEngine::Multi) {
2541 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2542 int start = rtl ? glyphLayout.numGlyphs : 0;
2543 int end = start - 1;
2544 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2545 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2546 rtl ? --start : ++end) {
2547 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2551 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2552 multiFontEngine->ensureEngineAt(which);
2554 QGlyphRun::GlyphRunFlags subFlags = flags;
2555 if (start == 0 && startsInsideLigature)
2556 subFlags |= QGlyphRun::SplitLigature;
2559 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2567 glyphsStart + start,
2569 logClusters + relativeFrom,
2570 relativeFrom + si.position,
2571 relativeTo - relativeFrom + 1);
2572 if (!glyphRun.isEmpty())
2573 glyphRuns.append(glyphRun);
2575 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2576 if (!subLayout.attributes[i].dontPrint) {
2577 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2578 pos.rx() += (subLayout.advances[i] + justification).toReal();
2589 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2590 multiFontEngine->ensureEngineAt(which);
2592 QGlyphRun::GlyphRunFlags subFlags = flags;
2593 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2594 subFlags |= QGlyphRun::SplitLigature;
2596 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2604 glyphsStart + start,
2606 logClusters + relativeFrom,
2607 relativeFrom + si.position,
2608 relativeTo - relativeFrom + 1);
2609 if (!glyphRun.isEmpty())
2610 glyphRuns.append(glyphRun);
2612 if (startsInsideLigature || endsInsideLigature)
2613 flags |= QGlyphRun::SplitLigature;
2614 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2624 logClusters + relativeFrom,
2625 relativeFrom + si.position,
2626 relativeTo - relativeFrom + 1);
2627 if (!glyphRun.isEmpty())
2628 glyphRuns.append(glyphRun);
2638
2639
2640
2641
2642void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2644 draw_internal(painter, position,
nullptr);
2647void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2648 const QTextLayout::FormatRange *selection)
const
2650#ifndef QT_NO_RAWFONT
2652 Q_ASSERT(!eng->useRawFont);
2654 const QScriptLine &line = eng->lines[index];
2656 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2660 && selection->start <= line.from
2661 && selection->start + selection->length > line.from) {
2663 const qreal lineHeight = line.height().toReal();
2664 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2665 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2666 drawBackground(p, selection->format, r);
2671 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2672 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2674 if (Q_LIKELY(!xlateToFixedRange))
2677 p->translate(origPos);
2680 QFixed lineBase = line.base();
2681 eng->clearDecorations();
2682 eng->enableDelayDecorations();
2684 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2686 const QTextFormatCollection *formatCollection = eng->formatCollection();
2688 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2690 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2692 format.merge(eng->format(si));
2694 if (suppressColors) {
2695 format.clearForeground();
2696 format.clearBackground();
2697 format.clearProperty(QTextFormat::TextUnderlineColor);
2700 format.merge(selection->format);
2704 QTextLineItemIterator iterator(eng, index, pos, selection);
2705 while (!iterator.atEnd()) {
2706 QScriptItem &si = iterator.next();
2708 if (eng->hasFormats() || selection || formatCollection) {
2709 QTextCharFormat format;
2710 if (formatCollection !=
nullptr)
2711 format = formatCollection->defaultTextFormat();
2712 prepareFormat(format, &si);
2713 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2714 iterator.itemWidth.toReal(), line.height().toReal()));
2719 QPen pen = p->pen();
2721 QTextLineItemIterator iterator(eng, index, pos, selection);
2722 while (!iterator.atEnd()) {
2723 QScriptItem &si = iterator.next();
2725 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2728 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2729 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2732 QFixed itemBaseLine = y;
2733 QFont f = eng->font(si);
2734 QTextCharFormat format;
2735 if (formatCollection !=
nullptr)
2736 format = formatCollection->defaultTextFormat();
2738 if (eng->hasFormats() || selection || formatCollection) {
2739 prepareFormat(format, &si);
2740 setPen(p, pen, format);
2742 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2743 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2744 if (valign == QTextCharFormat::AlignSuperScript
2745 || valign == QTextCharFormat::AlignSubScript
2746 || !qFuzzyIsNull(baseLineOffset))
2748 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2749 QFixed height = fe->ascent() + fe->descent();
2750 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2752 if (valign == QTextCharFormat::AlignSubScript)
2753 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2754 else if (valign == QTextCharFormat::AlignSuperScript)
2755 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2759 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2761 if (eng->hasFormats()) {
2763 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2764 QFixed itemY = y - si.ascent;
2765 switch (format.verticalAlignment()) {
2766 case QTextCharFormat::AlignTop:
2767 itemY = y - lineBase;
2769 case QTextCharFormat::AlignMiddle:
2770 itemY = y - lineBase + (line.height() - si.height()) / 2;
2772 case QTextCharFormat::AlignBottom:
2773 itemY = y - lineBase + line.height() - si.height();
2779 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2781 eng->docLayout()->drawInlineObject(p, itemRect,
2782 QTextInlineObject(iterator.item, eng),
2783 si.position + eng->block.position(),
2787 if (bg.style() != Qt::NoBrush) {
2788 QColor c = bg.color();
2790 p->fillRect(itemRect, c);
2794 QFont f = eng->font(si);
2795 QTextItemInt gf(si, &f, format);
2798 gf.width = iterator.itemWidth;
2799 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2800 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2801 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2802 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2803 qreal x = iterator.itemWidth.toReal() - w;
2805 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2806 iterator.itemWidth.toReal(), line.height().toReal()),
2811 p->drawText(QPointF(iterator.x.toReal() + x,
2812 y.toReal()), visualTab);
2822 unsigned short *logClusters = eng->logClusters(&si);
2823 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2825 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2826 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2827 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2828 gf.logClusters = logClusters + iterator.itemStart - si.position;
2829 gf.width = iterator.itemWidth;
2830 gf.justified = line.justified;
2831 gf.initWithScriptItem(si);
2833 Q_ASSERT(gf.fontEngine);
2835 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2836 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2838 path.setFillRule(Qt::WindingFill);
2840 if (gf.glyphs.numGlyphs)
2841 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2843 const QFontEngine *fe = gf.fontEngine;
2844 const qreal lw = fe->lineThickness().toReal();
2845 if (gf.flags & QTextItem::Underline) {
2846 qreal offs = fe->underlinePosition().toReal();
2847 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2849 if (gf.flags & QTextItem::Overline) {
2850 qreal offs = fe->ascent().toReal() + 1;
2851 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2853 if (gf.flags & QTextItem::StrikeOut) {
2854 qreal offs = fe->ascent().toReal() / 3;
2855 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2860 p->setRenderHint(QPainter::Antialiasing);
2863 if (p->pen().style() == Qt::NoPen)
2864 p->setBrush(Qt::NoBrush);
2866 p->setBrush(p->pen().brush());
2868 p->setPen(format.textOutline());
2873 gf.glyphs.numGlyphs = 0;
2874 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2877 if ((si.analysis.flags == QScriptAnalysis::Space
2878 || si.analysis.flags == QScriptAnalysis::Nbsp)
2879 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2880 QBrush c = format.foreground();
2881 if (c.style() != Qt::NoBrush)
2882 p->setPen(c.color());
2883 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2884 QFont oldFont = p->font();
2885 p->setFont(eng->font(si));
2886 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2888 p->setFont(oldFont);
2892 eng->drawDecorations(p);
2894 if (xlateToFixedRange)
2895 p->translate(-origPos);
2897 if (eng->hasFormats())
2902
2903
2904
2905
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2919 const QScriptLine &line = eng->lines[index];
2920 bool lastLine = index >= eng->lines.size() - 1;
2922 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2924 if (!eng->layoutData)
2926 if (!eng->layoutData->items.size()) {
2927 *cursorPos = line.from;
2931 int lineEnd = line.from + line.length + line.trailingSpaces;
2932 int pos = qBound(line.from, *cursorPos, lineEnd);
2933 const QCharAttributes *attributes = eng->attributes();
2935 *cursorPos = line.from;
2938 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2941 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2943 *cursorPos = line.from;
2946 eng->shapeLine(line);
2948 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2949 if (!scriptItem->num_glyphs)
2952 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2955 int neighborItem = itm;
2956 if (neighborItem > 0 && scriptItem->position == pos)
2958 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2960 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2963 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2965 scriptItem = &eng->layoutData->items[itm];
2966 if (!scriptItem->num_glyphs)
2972 const int l = eng->length(itm);
2973 pos = qBound(0, pos - scriptItem->position, l);
2975 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
2976 unsigned short *logClusters = eng->logClusters(scriptItem);
2977 Q_ASSERT(logClusters);
2979 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
2980 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
2983 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2987 bool reverse = scriptItem->analysis.bidiLevel % 2;
2992 int firstItem = eng->findItem(line.from);
2993 int lastItem = eng->findItem(lineEnd - 1, itm);
2994 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2996 QVarLengthArray<
int> visualOrder(nItems);
2997 QVarLengthArray<uchar> levels(nItems);
2998 for (
int i = 0; i < nItems; ++i)
2999 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3000 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3002 for (
int i = 0; i < nItems; ++i) {
3003 int item = visualOrder[i]+firstItem;
3006 QScriptItem &si = eng->layoutData->items[item];
3010 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3015 const int itemLength = eng->length(item);
3016 int start = qMax(line.from, si.position);
3017 int end = qMin(lineEnd, si.position + itemLength);
3019 logClusters = eng->logClusters(&si);
3021 int gs = logClusters[start-si.position];
3022 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3024 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3027 x += glyphs.effectiveAdvance(gs);
3032 logClusters = eng->logClusters(scriptItem);
3033 glyphs = eng->shapedGlyphs(scriptItem);
3034 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3035 if (pos == (reverse ? 0 : l))
3036 x += scriptItem->width;
3038 bool rtl = eng->isRightToLeft();
3039 bool visual = eng->visualCursorMovement();
3040 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3042 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3043 int glyph_start = glyph_pos;
3044 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3046 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3047 x += glyphs.effectiveAdvance(i);
3048 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3050 int start = qMax(line.from - scriptItem->position, 0);
3051 int glyph_start = logClusters[start];
3052 int glyph_end = glyph_pos;
3053 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3055 for (
int i = glyph_start; i <= glyph_end; i++)
3056 x += glyphs.effectiveAdvance(i);
3057 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3061 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3062 x = line.x + line.width;
3063 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3066 *cursorPos = pos + scriptItem->position;
3071
3072
3073
3074
3075
3076
3077
3078
3079int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3081 QFixed x = QFixed::fromReal(_x);
3082 const QScriptLine &line = eng->lines[index];
3083 bool lastLine = index >= eng->lines.size() - 1;
3084 int lineNum = index;
3086 if (!eng->layoutData)
3089 int line_length = textLength();
3094 int firstItem = eng->findItem(line.from);
3095 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3096 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3102 x -= eng->alignLine(line);
3105 QVarLengthArray<
int> visualOrder(nItems);
3106 QVarLengthArray<
unsigned char> levels(nItems);
3107 for (
int i = 0; i < nItems; ++i)
3108 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3109 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3111 bool visual = eng->visualCursorMovement();
3114 if (eng->isRightToLeft())
3115 return line.from + line_length;
3117 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3120 bool rtl = eng->isRightToLeft();
3122 eng->shapeLine(line);
3123 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3125 for (
int i = 0; i < nItems; ++i) {
3126 int item = visualOrder[i]+firstItem;
3127 QScriptItem &si = eng->layoutData->items[item];
3128 int item_length = eng->length(item);
3131 int start = qMax(line.from - si.position, 0);
3132 int end = qMin(line.from + line_length - si.position, item_length);
3134 unsigned short *logClusters = eng->logClusters(&si);
3136 int gs = logClusters[start];
3137 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3138 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3140 QFixed item_width = 0;
3141 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3142 item_width = si.width;
3146 item_width += glyphs.effectiveAdvance(g);
3152 if (pos + item_width < x) {
3158 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3159 if (cpos == QTextLine::CursorOnCharacter)
3161 bool left_half = (x - pos) < item_width/2;
3163 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3165 return si.position + 1;
3171 if (cpos == QTextLine::CursorOnCharacter) {
3172 if (si.analysis.bidiLevel % 2) {
3176 if (glyphs.attributes[gs].clusterStart) {
3182 pos -= glyphs.effectiveAdvance(gs);
3188 if (glyphs.attributes[gs].clusterStart) {
3194 pos += glyphs.effectiveAdvance(gs);
3199 QFixed dist = INT_MAX/256;
3200 if (si.analysis.bidiLevel % 2) {
3201 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3204 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3209 pos -= glyphs.effectiveAdvance(gs);
3214 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3219 pos += glyphs.effectiveAdvance(ge);
3224 if (!visual || !rtl || (lastLine && i == 0)) {
3226 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3231 pos += glyphs.effectiveAdvance(gs);
3235 QFixed oldPos = pos;
3237 pos += glyphs.effectiveAdvance(gs);
3238 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3248 if (qAbs(x-pos) < dist) {
3250 if (!rtl && i < nItems - 1) {
3254 if (rtl && nchars > 0)
3255 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3257 return eng->positionInLigature(&si, end, x, pos, -1,
3258 cpos == QTextLine::CursorOnCharacter);
3261 Q_ASSERT(glyph_pos != -1);
3262 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3263 cpos == QTextLine::CursorOnCharacter);
3267 int pos = line.from;
3268 if (!eng->isRightToLeft())
3275 if (index < eng->lines.size() - 1)
3276 pos = qMin(eng->previousLogicalPosition(pos), pos);
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection, QPainterPath *region, const QRectF &boundingRect)
static void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount, const QScriptItem ¤t, const unsigned short *logClusters, const QGlyphLayout &glyphs, QFixed *clusterWidth=nullptr)
static QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QString &text, const QGlyphLayout &glyphLayout, const QPointF &pos, const QGlyphRun::GlyphRunFlags &flags, QTextLayout::GlyphRunRetrievalFlags retrievalFlags, QFixed selectionX, QFixed selectionWidth, int glyphsStart, int glyphsEnd, unsigned short *logClusters, int textPosition, int textLength)
static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r)
#define SuppressBackground
static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf)
#define ObjectSelectionBrush
QGlyphAttributes * attributes
unsigned short num_glyphs