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);
1126 const QPen pen = selection.format.penProperty(QTextFormat::OutlinePen);
1127 const bool hasOutline = (pen.style() != Qt::NoPen && pen.widthF() > 0);
1128 const QRectF selectionClip = hasOutline ? QRectF() : clip;
1129 QPainterPath region;
1130 region.setFillRule(Qt::WindingFill);
1132 for (
int line = firstLine; line < lastLine; ++line) {
1133 const QScriptLine &sl = d->lines.at(line);
1134 QTextLine tl(line, d);
1136 QRectF lineRect(tl.naturalTextRect());
1137 lineRect.translate(position);
1138 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1139 lineRect.setBottom(qCeil(lineRect.bottom()));
1141 bool isLastLineInBlock = (line == d->lines.size()-1);
1142 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1145 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1148 const bool selectionStartInLine = sl.from <= selection.start;
1149 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1151 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1152 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, selectionClip));
1154 region.addRect(clipIfValid(lineRect, selectionClip));
1157 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1158 QRectF fullLineRect(tl.rect());
1159 fullLineRect.translate(position);
1160 fullLineRect.setRight(QFIXED_MAX);
1161 fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
1163 const bool rightToLeft = d->isRightToLeft();
1165 if (!selectionEndInLine) {
1166 region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
1167 : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), selectionClip));
1169 if (!selectionStartInLine) {
1170 region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
1171 : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), selectionClip));
1173 }
else if (!selectionEndInLine
1174 && isLastLineInBlock
1175 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1176 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1177 lineRect.height()/4, lineRect.height()), selectionClip));
1182 const QPen oldPen = p->pen();
1183 const QBrush oldBrush = p->brush();
1187 p->setClipRect(clip, Qt::IntersectClip);
1190 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1191 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1192 p->drawPath(region);
1195 p->setBrush(oldBrush);
1203 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1204 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1206 if (hasBackground) {
1207 selection.format.setProperty(
ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1210 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1211 selection.format.clearProperty(QTextFormat::OutlinePen);
1216 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1220 p->setClipPath(region, Qt::IntersectClip);
1222 for (
int line = firstLine; line < lastLine; ++line) {
1223 QTextLine l(line, d);
1224 l.draw_internal(p, position, &selection);
1229 textDoneRegion += region;
1232 textDoneRegion -= region;
1235 excludedRegion += region;
1238 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1239 if (!needsTextButNoBackground.isEmpty()){
1241 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1242 FormatRange selection;
1243 selection.start = 0;
1244 selection.length = INT_MAX;
1246 for (
int line = firstLine; line < lastLine; ++line) {
1247 QTextLine l(line, d);
1248 l.draw_internal(p, position, &selection);
1253 if (!excludedRegion.isEmpty()) {
1256 QRectF br = boundingRect().translated(position);
1257 br.setRight(QFIXED_MAX);
1259 br = br.intersected(clip);
1261 path -= excludedRegion;
1262 p->setClipPath(path, Qt::IntersectClip);
1265 for (
int i = firstLine; i < lastLine; ++i) {
1267 l.draw(p, position);
1269 if (!excludedRegion.isEmpty())
1273 if (!d->cacheGlyphs)
1278
1279
1280
1281
1282
1283
1284
1285void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition)
const
1287 drawCursor(p, pos, cursorPosition, 1);
1291
1292
1293
1294
1295
1296
1297void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition,
int width)
const
1299 if (d->lines.isEmpty())
1305 QPointF position = pos + d->position;
1307 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
1308 int line = d->lineNumberForTextPosition(cursorPosition);
1311 if (line >= d->lines.size())
1314 QTextLine l(line, d);
1315 const QScriptLine &sl = d->lines.at(line);
1317 qreal x = position.x() + l.cursorToX(cursorPosition);
1319 QFixed base = sl.base();
1320 QFixed descent = sl.descent;
1321 bool rightToLeft = d->isRightToLeft();
1323 const int realCursorPosition = cursorPosition;
1324 if (d->visualCursorMovement()) {
1325 if (cursorPosition == sl.from + sl.length)
1330 int itm = d->findItem(cursorPosition);
1333 const QScriptItem *si = &d->layoutData->items.at(itm);
1336 if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
1337 int neighborItem = itm;
1338 if (neighborItem > 0 && si->position == realCursorPosition)
1340 else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
1342 const bool onBoundary = neighborItem != itm
1343 && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
1344 if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
1346 si = &d->layoutData->items[itm];
1350 if (si->analysis.flags != QScriptAnalysis::Object) {
1353 if (si->descent > 0)
1354 descent = si->descent;
1356 rightToLeft = si->analysis.bidiLevel % 2;
1358 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1359 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1360 && (p->transform().type() > QTransform::TxTranslate);
1361 if (toggleAntialiasing)
1362 p->setRenderHint(QPainter::Antialiasing);
1363 QPainter::CompositionMode origCompositionMode = p->compositionMode();
1364 if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
1365 p->setCompositionMode(QPainter::RasterOp_NotDestination);
1366 const QTransform &deviceTransform = p->deviceTransform();
1367 const qreal xScale = deviceTransform.m11();
1368 if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
1369 p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
1372 const QPen origPen = p->pen();
1373 QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
1374 pen.setCosmetic(
true);
1375 const qreal center = x + qreal(width) / 2;
1377 p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
1380 p->setCompositionMode(origCompositionMode);
1381 if (toggleAntialiasing)
1382 p->setRenderHint(QPainter::Antialiasing,
false);
1383 if (d->layoutData->hasBidi) {
1384 const int arrow_extent = 4;
1385 int sign = rightToLeft ? -1 : 1;
1386 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1387 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1413
1414
1415
1416
1417
1420
1421
1422
1423
1424
1427
1428
1429
1430
1431
1432
1435
1436
1437
1438
1441
1442
1443
1444
1447
1448
1449
1450
1454
1455
1456
1457
1458QRectF QTextLine::rect()
const
1460 const QScriptLine& sl = eng->lines.at(index);
1461 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1465
1466
1467QRectF QTextLine::naturalTextRect()
const
1469 const QScriptLine& sl = eng->lines.at(index);
1470 QFixed x = sl.x + eng->alignLine(sl);
1472 QFixed width = sl.textWidth;
1476 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1480
1481
1482
1483
1484qreal QTextLine::x()
const
1486 return eng->lines.at(index).x.toReal();
1490
1491
1492
1493
1494qreal QTextLine::y()
const
1496 return eng->lines.at(index).y.toReal();
1500
1501
1502
1503
1504qreal QTextLine::width()
const
1506 return eng->lines.at(index).width.toReal();
1511
1512
1513
1514
1515qreal QTextLine::ascent()
const
1517 return eng->lines.at(index).ascent.toReal();
1521
1522
1523
1524
1525qreal QTextLine::descent()
const
1527 return eng->lines.at(index).descent.toReal();
1531
1532
1533
1534
1535
1536
1537qreal QTextLine::height()
const
1539 return eng->lines.at(index).height().ceil().toReal();
1543
1544
1545
1546
1547
1548
1549qreal QTextLine::leading()
const
1551 return eng->lines.at(index).leading.toReal();
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568void QTextLine::setLeadingIncluded(
bool included)
1570 eng->lines[index].leadingIncluded= included;
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584bool QTextLine::leadingIncluded()
const
1586 return eng->lines.at(index).leadingIncluded;
1590
1591
1592
1593
1594qreal QTextLine::naturalTextWidth()
const
1596 return eng->lines.at(index).textWidth.toReal();
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609qreal QTextLine::horizontalAdvance()
const
1611 return eng->lines.at(index).textAdvance.toReal();
1615
1616
1617
1618
1619
1620
1621void QTextLine::setLineWidth(qreal width)
1623 QScriptLine &line = eng->lines[index];
1624 if (!eng->layoutData) {
1625 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1629 line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
1631 && line.textWidth <= line.width
1632 && line.from + line.length == eng->layoutData->string.size())
1639 layout_helper(INT_MAX);
1643
1644
1645
1646
1647
1648
1649void QTextLine::setNumColumns(
int numColumns)
1651 QScriptLine &line = eng->lines[index];
1652 line.width = QFIXED_MAX;
1655 layout_helper(numColumns);
1659
1660
1661
1662
1663
1664
1665
1666void QTextLine::setNumColumns(
int numColumns, qreal alignmentWidth)
1668 QScriptLine &line = eng->lines[index];
1669 line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
1672 layout_helper(numColumns);
1676#define LB_DEBUG qDebug
1678#define LB_DEBUG if (0
) qDebug
1683 struct LineBreakHelper
1685 LineBreakHelper() =
default;
1687 QScriptLine tmpData;
1688 QScriptLine spaceData;
1694 int currentPosition = 0;
1695 glyph_t previousGlyph = 0;
1696 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1699 QFixed currentSoftHyphenWidth;
1700 QFixed commitedSoftHyphenWidth;
1701 QFixed rightBearing;
1702 QFixed minimumRightBearing;
1704 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1705 const unsigned short *logClusters =
nullptr;
1707 bool manualWrap =
false;
1708 bool whiteSpaceOrObject =
true;
1710 bool checkFullOtherwiseExtend(QScriptLine &line);
1712 QFixed calculateNewWidth(
const QScriptLine &line)
const {
1713 return line.textWidth + tmpData.textWidth + spaceData.textWidth
1714 + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
1717 inline glyph_t currentGlyph()
const
1719 Q_ASSERT(currentPosition > 0);
1720 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1722 return glyphs.glyphs[logClusters[currentPosition - 1]];
1725 inline void saveCurrentGlyph()
1728 if (currentPosition > 0 &&
1729 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1730 previousGlyph = currentGlyph();
1731 previousGlyphFontEngine = fontEngine;
1735 inline void calculateRightBearing(
QFontEngine *engine, glyph_t glyph)
1739 engine->getGlyphBearings(glyph,
nullptr, &rb);
1745 rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
1748 inline void calculateRightBearing()
1750 if (currentPosition <= 0)
1752 calculateRightBearing(fontEngine.data(), currentGlyph());
1755 inline void calculateRightBearingForPreviousGlyph()
1757 if (previousGlyph > 0)
1758 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1761 static const QFixed RightBearingNotCalculated;
1763 inline void resetRightBearing()
1765 rightBearing = RightBearingNotCalculated;
1770 inline QFixed negativeRightBearing()
const
1772 if (rightBearing == RightBearingNotCalculated)
1775 return qAbs(rightBearing);
1779Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
1781inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1783 LB_DEBUG(
"possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1785 QFixed newWidth = calculateNewWidth(line);
1786 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1789 const QFixed oldTextWidth = line.textWidth;
1791 line.textWidth += spaceData.textWidth;
1793 line.length += spaceData.length;
1794 tmpData.textWidth = 0;
1796 spaceData.textWidth = 0;
1797 spaceData.length = 0;
1799 if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
1800 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1801 currentSoftHyphenWidth = 0;
1810static inline void addNextCluster(
int &pos,
int end, QScriptLine &line,
int &glyphCount,
1811 const QScriptItem ¤t,
const unsigned short *logClusters,
1812 const QGlyphLayout &glyphs, QFixed *clusterWidth =
nullptr)
1814 int glyphPosition = logClusters[pos];
1818 }
while (pos < end && logClusters[pos] == glyphPosition);
1819 QFixed clusterWid = line.textWidth;
1822 line.textWidth += glyphs.advances[glyphPosition];
1826 Q_ASSERT((pos == end && glyphPosition == current
.num_glyphs) || logClusters[pos] == glyphPosition);
1829 *clusterWidth += (line.textWidth - clusterWid);
1835void QTextLine::layout_helper(
int maxGlyphs)
1837 QScriptLine &line = eng->lines[index];
1839 line.trailingSpaces = 0;
1841 line.hasTrailingSpaces =
false;
1843 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
1844 line.setDefaultHeight(eng);
1848 Q_ASSERT(line.from < eng->layoutData->string.size());
1850 LineBreakHelper lbh;
1852 lbh.maxGlyphs = maxGlyphs;
1854 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1855 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1856 const bool breakWordOrAny = breakany || (wrapMode == QTextOption::WrapAtWordBoundaryOrAnywhere);
1857 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1860 int newItem = eng->findItem(line.from);
1861 Q_ASSERT(newItem >= 0);
1863 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f", line.from, newItem,
int(eng->layoutData->items.size()), line.width.toReal());
1865 Qt::Alignment alignment = eng->option.alignment();
1867 const QCharAttributes *attributes = eng->attributes();
1870 lbh.currentPosition = line.from;
1872 lbh.logClusters = eng->layoutData->logClustersPtr;
1873 lbh.previousGlyph = 0;
1875 bool manuallyWrapped =
false;
1876 bool hasInlineObject =
false;
1877 bool reachedEndOfLine =
false;
1878 QFixed maxInlineObjectHeight = 0;
1880 const bool includeTrailingSpaces = eng->option.flags() & QTextOption::IncludeTrailingSpaces;
1882 while (newItem < eng->layoutData->items.size()) {
1883 lbh.resetRightBearing();
1884 if (newItem != item) {
1886 const QScriptItem ¤t = eng->layoutData->items.at(item);
1887 if (!current.num_glyphs) {
1889 attributes = eng->attributes();
1892 lbh.logClusters = eng->layoutData->logClustersPtr;
1894 lbh.currentPosition = qMax(line.from, current.position);
1895 end = current.position + eng->length(item);
1896 lbh.glyphs = eng->shapedGlyphs(¤t);
1897 QFontEngine *fontEngine = eng->fontEngine(current);
1898 if (lbh.fontEngine != fontEngine) {
1899 lbh.fontEngine = fontEngine;
1900 lbh.minimumRightBearing = qMin(QFixed(),
1901 QFixed::fromReal(fontEngine->minRightBearing()));
1904 const QScriptItem ¤t = eng->layoutData->items.at(item);
1906 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1907 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1909 if (current.analysis.flags != QScriptAnalysis::Object) {
1911 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1912 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1915 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1916 lbh.whiteSpaceOrObject =
true;
1917 if (lbh.checkFullOtherwiseExtend(line))
1920 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1921 QFixed tabWidth = eng->calculateTabWidth(item, x);
1922 attributes = eng->attributes();
1925 lbh.logClusters = eng->layoutData->logClustersPtr;
1926 lbh.glyphs = eng->shapedGlyphs(¤t);
1928 lbh.spaceData.textWidth += tabWidth;
1929 lbh.spaceData.length++;
1932 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1933 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1935 if (lbh.checkFullOtherwiseExtend(line))
1937 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1938 lbh.whiteSpaceOrObject =
true;
1941 if (!line.length && !lbh.tmpData.length)
1942 line.setDefaultHeight(eng);
1943 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1944 if (lbh.checkFullOtherwiseExtend(line))
1947 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1948 current, lbh.logClusters, lbh.glyphs);
1950 lbh.tmpData.length++;
1951 lbh.calculateRightBearingForPreviousGlyph();
1953 line += lbh.tmpData;
1954 manuallyWrapped =
true;
1956 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1957 lbh.whiteSpaceOrObject =
true;
1958 lbh.tmpData.length++;
1960 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1961 QTextInlineObject inlineObject(item, eng);
1962 QTextFormat f = inlineObject.format();
1963 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1964 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1965 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1966 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1967 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1971 lbh.tmpData.textWidth += current.width;
1975 if (lbh.checkFullOtherwiseExtend(line))
1978 hasInlineObject =
true;
1979 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1981 }
else if (attributes[lbh.currentPosition].whiteSpace
1982 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1985 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
1986 lbh.saveCurrentGlyph();
1987 lbh.whiteSpaceOrObject =
true;
1988 while (lbh.currentPosition < end
1989 && attributes[lbh.currentPosition].whiteSpace
1990 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1991 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1992 current, lbh.logClusters, lbh.glyphs);
1995 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
1998 lbh.whiteSpaceOrObject =
false;
1999 bool sb_or_ws =
false;
2003 if (lbh.currentPosition == 0
2004 || lbh.previousGlyph == 0
2005 || includeTrailingSpaces
2006 || !attributes[lbh.currentPosition - 1].whiteSpace) {
2007 lbh.saveCurrentGlyph();
2009 QFixed accumulatedTextWidth;
2011 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
2012 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2019 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2020 && attributes[lbh.currentPosition].whiteSpace
2021 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2023 if (lbh.currentPosition >= eng->layoutData->string.size()
2025 || attributes[lbh.currentPosition].lineBreak
2026 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2029 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2030 if (breakWordOrAny) {
2031 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2032 accumulatedTextWidth = 0;
2037 }
while (lbh.currentPosition < end);
2038 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2040 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2041 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2042 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2059 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2062 if (sb_or_ws|breakany) {
2070 QFixed previousRightBearing = lbh.rightBearing;
2081 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2082 lbh.calculateRightBearing();
2084 if (lbh.checkFullOtherwiseExtend(line)) {
2089 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2090 lbh.rightBearing = previousRightBearing;
2092 lbh.calculateRightBearingForPreviousGlyph();
2094 line.textWidth += lbh.commitedSoftHyphenWidth;
2099 lbh.saveCurrentGlyph();
2101 if (lbh.currentPosition == end)
2105 reachedEndOfLine =
true;
2106 lbh.checkFullOtherwiseExtend(line);
2107 line.textWidth += lbh.commitedSoftHyphenWidth;
2109 line.textAdvance = line.textWidth;
2112 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2113 lbh.calculateRightBearing();
2116 const QFixed textWidthWithoutBearing = line.textWidth;
2117 line.textWidth += lbh.negativeRightBearing();
2119 if (line.length == 0) {
2120 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2121 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2122 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2123 line += lbh.tmpData;
2126 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2128 if (maxInlineObjectHeight > line.ascent + line.descent) {
2130 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2131 line.ascent += toAdd;
2132 line.descent = maxInlineObjectHeight - line.ascent;
2134 int startItem = eng->findItem(line.from);
2135 int endItem = eng->findItem(line.from + line.length);
2137 endItem = eng->layoutData->items.size();
2138 for (
int item = startItem; item < endItem; ++item) {
2139 QScriptItem ¤t = eng->layoutData->items[item];
2140 if (current.analysis.flags == QScriptAnalysis::Object) {
2141 QTextInlineObject inlineObject(item, eng);
2142 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2143 QFixed height = current.ascent + current.descent;
2145 case QTextCharFormat::AlignTop:
2146 current.ascent = line.ascent;
2147 current.descent = height - line.ascent;
2149 case QTextCharFormat::AlignMiddle:
2150 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2151 current.descent = height - line.ascent;
2153 case QTextCharFormat::AlignBottom:
2154 current.descent = line.descent;
2155 current.ascent = height - line.descent;
2160 Q_ASSERT(line.ascent >= current.ascent);
2161 Q_ASSERT(line.descent >= current.descent);
2167 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2168 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2169 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2171 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2172 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2173 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2174 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2176 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2177 layout_helper(lbh.maxGlyphs);
2178 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2183 if (lbh.manualWrap) {
2184 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2185 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2187 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2189 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2191 : textWidthWithoutBearing;
2192 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2193 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2194 if (!manuallyWrapped) {
2195 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2196 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2198 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2199 if (manuallyWrapped)
2200 eng->layoutData->currentMaxWidth = 0;
2203 line.textWidth += trailingSpace;
2204 if (lbh.spaceData.length) {
2205 line.trailingSpaces = lbh.spaceData.length;
2206 line.hasTrailingSpaces =
true;
2209 line.justified =
false;
2210 line.gridfitted =
false;
2214
2215
2216void QTextLine::setPosition(
const QPointF &pos)
2218 eng->lines[index].x = QFixed::fromReal(pos.x());
2219 eng->lines[index].y = QFixed::fromReal(pos.y());
2223
2224
2225QPointF QTextLine::position()
const
2227 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2238
2239
2240
2241int QTextLine::textStart()
const
2243 return eng->lines.at(index).from;
2247
2248
2249
2250
2251int QTextLine::textLength()
const
2253 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2254 && eng->block.isValid() && index == eng->lines.size()-1) {
2255 return eng->lines.at(index).length - 1;
2257 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2262 QBrush bg = chf.background();
2264 p->fillRect(r.toAlignedRect(), bg);
2269 QBrush c = chf.foreground();
2270 if (c.style() == Qt::NoBrush)
2271 p->setPen(defaultPen);
2273 p->setPen(QPen(c, 0));
2276#if !defined(QT_NO_RAWFONT)
2278 const QString &text,
2281 const QGlyphRun::GlyphRunFlags &flags,
2282 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2284 QFixed selectionWidth,
2287 unsigned short *logClusters,
2291 Q_ASSERT(logClusters !=
nullptr);
2297 int rangeStart = textPosition;
2298 int logClusterIndex = 0;
2299 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2304 int rangeEnd = rangeStart;
2305 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2315 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2316 fontD->setFontEngine(fontEngine);
2318 QVarLengthArray<glyph_t> glyphsArray;
2319 QVarLengthArray<QFixedPoint> positionsArray;
2321 QTextItem::RenderFlags renderFlags;
2322 if (flags.testFlag(QGlyphRun::Overline))
2323 renderFlags |= QTextItem::Overline;
2324 if (flags.testFlag(QGlyphRun::Underline))
2325 renderFlags |= QTextItem::Underline;
2326 if (flags.testFlag(QGlyphRun::StrikeOut))
2327 renderFlags |= QTextItem::StrikeOut;
2328 if (flags.testFlag(QGlyphRun::RightToLeft))
2329 renderFlags |= QTextItem::RightToLeft;
2331 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2333 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2335 qreal fontHeight = font.ascent() + font.descent();
2338 QList<quint32> glyphs;
2339 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2340 glyphs.reserve(glyphsArray.size());
2341 QList<QPointF> positions;
2342 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2343 positions.reserve(glyphsArray.size());
2344 QList<qsizetype> stringIndexes;
2345 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2346 stringIndexes.reserve(glyphsArray.size());
2348 int nextClusterIndex = 0;
2349 int currentClusterIndex = 0;
2350 for (
int i = 0; i < glyphsArray.size(); ++i) {
2351 const int glyphArrayIndex = i + glyphsStart;
2354 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2355 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2356 currentClusterIndex = nextClusterIndex;
2357 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2364 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2365 stringIndexes.append(textPosition + currentClusterIndex);
2368 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2369 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2370 glyphs.append(glyphIndex);
2373 QPointF position = positionsArray.at(i).toPointF() + pos;
2374 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2375 positions.append(position);
2378 maxY = minY = position.y();
2380 minY = qMin(minY, position.y());
2381 maxY = qMax(maxY, position.y());
2385 qreal height = maxY + fontHeight - minY;
2387 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2388 glyphRun.setGlyphIndexes(glyphs);
2389 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2390 glyphRun.setPositions(positions);
2391 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2392 glyphRun.setStringIndexes(stringIndexes);
2393 if (retrievalFlags & QTextLayout::RetrieveString)
2394 glyphRun.setSourceString(text);
2395 glyphRun.setFlags(flags);
2396 glyphRun.setRawFont(font);
2398 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2399 selectionWidth.toReal(), height));
2404# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2427 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2450 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2452 const QScriptLine &line = eng->lines.at(index);
2454 if (line.length == 0)
2455 return QList<QGlyphRun>();
2461 length = textLength();
2464 return QList<QGlyphRun>();
2466 QTextLayout::FormatRange selection;
2467 selection.start = from;
2468 selection.length = length;
2470 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2471 qreal y = line.y.toReal() + line.base().toReal();
2472 QList<QGlyphRun> glyphRuns;
2473 while (!iterator.atEnd()) {
2474 QScriptItem &si = iterator.next();
2475 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2478 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2481 QPointF pos(iterator.x.toReal(), y);
2484 QGlyphRun::GlyphRunFlags flags;
2485 if (!eng->useRawFont) {
2486 font = eng->font(si);
2487 if (font.overline())
2488 flags |= QGlyphRun::Overline;
2489 if (font.underline())
2490 flags |= QGlyphRun::Underline;
2491 if (font.strikeOut())
2492 flags |= QGlyphRun::StrikeOut;
2496 if (si.analysis.bidiLevel % 2) {
2497 flags |= QGlyphRun::RightToLeft;
2501 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2502 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2504 unsigned short *logClusters = eng->logClusters(&si);
2505 int glyphsStart = logClusters[relativeFrom];
2506 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2508 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2509 if (nextGlyphIndex - 1 > glyphsEnd)
2510 glyphsEnd = nextGlyphIndex - 1;
2511 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2512 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2514 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2515 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2517 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2522 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2523 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2524 if (!glyphLayout.attributes[i].dontPrint) {
2525 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2526 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2529 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2530 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2531 if (!glyphLayout.attributes[i].dontPrint) {
2532 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2533 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2538 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2542 iterator.getSelectionBounds(&x, &width);
2544 if (glyphLayout.numGlyphs > 0) {
2545 QFontEngine *mainFontEngine;
2546#ifndef QT_NO_RAWFONT
2547 if (eng->useRawFont && eng->rawFont.isValid())
2548 mainFontEngine= eng->fontEngine(si);
2551 mainFontEngine = font.d->engineForScript(si.analysis.script);
2553 if (mainFontEngine->type() == QFontEngine::Multi) {
2554 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2555 int start = rtl ? glyphLayout.numGlyphs : 0;
2556 int end = start - 1;
2557 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2558 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2559 rtl ? --start : ++end) {
2560 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2564 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2565 multiFontEngine->ensureEngineAt(which);
2567 QGlyphRun::GlyphRunFlags subFlags = flags;
2568 if (start == 0 && startsInsideLigature)
2569 subFlags |= QGlyphRun::SplitLigature;
2572 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2580 glyphsStart + start,
2582 logClusters + relativeFrom,
2583 relativeFrom + si.position,
2584 relativeTo - relativeFrom + 1);
2585 if (!glyphRun.isEmpty())
2586 glyphRuns.append(glyphRun);
2588 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2589 if (!subLayout.attributes[i].dontPrint) {
2590 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2591 pos.rx() += (subLayout.advances[i] + justification).toReal();
2602 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2603 multiFontEngine->ensureEngineAt(which);
2605 QGlyphRun::GlyphRunFlags subFlags = flags;
2606 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2607 subFlags |= QGlyphRun::SplitLigature;
2609 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2617 glyphsStart + start,
2619 logClusters + relativeFrom,
2620 relativeFrom + si.position,
2621 relativeTo - relativeFrom + 1);
2622 if (!glyphRun.isEmpty())
2623 glyphRuns.append(glyphRun);
2625 if (startsInsideLigature || endsInsideLigature)
2626 flags |= QGlyphRun::SplitLigature;
2627 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2637 logClusters + relativeFrom,
2638 relativeFrom + si.position,
2639 relativeTo - relativeFrom + 1);
2640 if (!glyphRun.isEmpty())
2641 glyphRuns.append(glyphRun);
2651
2652
2653
2654
2655void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2657 draw_internal(painter, position,
nullptr);
2660void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2661 const QTextLayout::FormatRange *selection)
const
2663#ifndef QT_NO_RAWFONT
2665 Q_ASSERT(!eng->useRawFont);
2667 const QScriptLine &line = eng->lines[index];
2669 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2673 && selection->start <= line.from
2674 && selection->start + selection->length > line.from) {
2676 const qreal lineHeight = line.height().toReal();
2677 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2678 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2679 drawBackground(p, selection->format, r);
2684 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2685 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2687 if (Q_LIKELY(!xlateToFixedRange))
2690 p->translate(origPos);
2693 QFixed lineBase = line.base();
2694 eng->clearDecorations();
2695 eng->enableDelayDecorations();
2697 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2699 const QTextFormatCollection *formatCollection = eng->formatCollection();
2701 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2703 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2705 format.merge(eng->format(si));
2707 if (suppressColors) {
2708 format.clearForeground();
2709 format.clearBackground();
2710 format.clearProperty(QTextFormat::TextUnderlineColor);
2713 format.merge(selection->format);
2717 QTextLineItemIterator iterator(eng, index, pos, selection);
2718 while (!iterator.atEnd()) {
2719 QScriptItem &si = iterator.next();
2721 if (eng->hasFormats() || selection || formatCollection) {
2722 QTextCharFormat format;
2723 if (formatCollection !=
nullptr)
2724 format = formatCollection->defaultTextFormat();
2725 prepareFormat(format, &si);
2726 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2727 iterator.itemWidth.toReal(), line.height().toReal()));
2732 QPen pen = p->pen();
2734 QTextLineItemIterator iterator(eng, index, pos, selection);
2735 while (!iterator.atEnd()) {
2736 QScriptItem &si = iterator.next();
2738 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2741 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2742 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2745 QFixed itemBaseLine = y;
2746 QFont f = eng->font(si);
2747 QTextCharFormat format;
2748 if (formatCollection !=
nullptr)
2749 format = formatCollection->defaultTextFormat();
2751 if (eng->hasFormats() || selection || formatCollection) {
2752 prepareFormat(format, &si);
2753 setPen(p, pen, format);
2755 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2756 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2757 if (valign == QTextCharFormat::AlignSuperScript
2758 || valign == QTextCharFormat::AlignSubScript
2759 || !qFuzzyIsNull(baseLineOffset))
2761 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2762 QFixed height = fe->ascent() + fe->descent();
2763 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2765 if (valign == QTextCharFormat::AlignSubScript)
2766 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2767 else if (valign == QTextCharFormat::AlignSuperScript)
2768 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2772 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2774 if (eng->hasFormats()) {
2776 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2777 QFixed itemY = y - si.ascent;
2778 switch (format.verticalAlignment()) {
2779 case QTextCharFormat::AlignTop:
2780 itemY = y - lineBase;
2782 case QTextCharFormat::AlignMiddle:
2783 itemY = y - lineBase + (line.height() - si.height()) / 2;
2785 case QTextCharFormat::AlignBottom:
2786 itemY = y - lineBase + line.height() - si.height();
2792 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2794 eng->docLayout()->drawInlineObject(p, itemRect,
2795 QTextInlineObject(iterator.item, eng),
2796 si.position + eng->block.position(),
2800 if (bg.style() != Qt::NoBrush) {
2801 QColor c = bg.color();
2803 p->fillRect(itemRect, c);
2807 QFont f = eng->font(si);
2808 QTextItemInt gf(si, &f, format);
2811 gf.width = iterator.itemWidth;
2812 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2813 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2814 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2815 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2816 qreal x = iterator.itemWidth.toReal() - w;
2818 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2819 iterator.itemWidth.toReal(), line.height().toReal()),
2824 p->drawText(QPointF(iterator.x.toReal() + x,
2825 y.toReal()), visualTab);
2835 unsigned short *logClusters = eng->logClusters(&si);
2836 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2838 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2839 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2840 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2841 gf.logClusters = logClusters + iterator.itemStart - si.position;
2842 gf.width = iterator.itemWidth;
2843 gf.justified = line.justified;
2844 gf.initWithScriptItem(si);
2846 Q_ASSERT(gf.fontEngine);
2848 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2849 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2851 path.setFillRule(Qt::WindingFill);
2853 if (gf.glyphs.numGlyphs)
2854 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2856 const QFontEngine *fe = gf.fontEngine;
2857 const qreal lw = fe->lineThickness().toReal();
2858 if (gf.flags & QTextItem::Underline) {
2859 qreal offs = fe->underlinePosition().toReal();
2860 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2862 if (gf.flags & QTextItem::Overline) {
2863 qreal offs = fe->ascent().toReal() + 1;
2864 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2866 if (gf.flags & QTextItem::StrikeOut) {
2867 qreal offs = fe->ascent().toReal() / 3;
2868 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2873 p->setRenderHint(QPainter::Antialiasing);
2876 if (p->pen().style() == Qt::NoPen)
2877 p->setBrush(Qt::NoBrush);
2879 p->setBrush(p->pen().brush());
2881 p->setPen(format.textOutline());
2886 gf.glyphs.numGlyphs = 0;
2887 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2890 if ((si.analysis.flags == QScriptAnalysis::Space
2891 || si.analysis.flags == QScriptAnalysis::Nbsp)
2892 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2893 QBrush c = format.foreground();
2894 if (c.style() != Qt::NoBrush)
2895 p->setPen(c.color());
2896 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2897 QFont oldFont = p->font();
2898 p->setFont(eng->font(si));
2899 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2901 p->setFont(oldFont);
2905 eng->drawDecorations(p);
2907 if (xlateToFixedRange)
2908 p->translate(-origPos);
2910 if (eng->hasFormats())
2915
2916
2917
2918
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2932 const QScriptLine &line = eng->lines[index];
2933 bool lastLine = index >= eng->lines.size() - 1;
2935 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2937 if (!eng->layoutData)
2939 if (!eng->layoutData->items.size()) {
2940 *cursorPos = line.from;
2944 int lineEnd = line.from + line.length + line.trailingSpaces;
2945 int pos = qBound(line.from, *cursorPos, lineEnd);
2946 const QCharAttributes *attributes = eng->attributes();
2948 *cursorPos = line.from;
2951 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2954 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2956 *cursorPos = line.from;
2959 eng->shapeLine(line);
2961 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2962 if (!scriptItem->num_glyphs)
2965 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2968 int neighborItem = itm;
2969 if (neighborItem > 0 && scriptItem->position == pos)
2971 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2973 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2976 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2978 scriptItem = &eng->layoutData->items[itm];
2979 if (!scriptItem->num_glyphs)
2985 const int l = eng->length(itm);
2986 pos = qBound(0, pos - scriptItem->position, l);
2988 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
2989 unsigned short *logClusters = eng->logClusters(scriptItem);
2990 Q_ASSERT(logClusters);
2992 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
2993 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
2996 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
3000 bool reverse = scriptItem->analysis.bidiLevel % 2;
3005 int firstItem = eng->findItem(line.from);
3006 int lastItem = eng->findItem(lineEnd - 1, itm);
3007 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3009 QVarLengthArray<
int> visualOrder(nItems);
3010 QVarLengthArray<uchar> levels(nItems);
3011 for (
int i = 0; i < nItems; ++i)
3012 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3013 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3015 for (
int i = 0; i < nItems; ++i) {
3016 int item = visualOrder[i]+firstItem;
3019 QScriptItem &si = eng->layoutData->items[item];
3023 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3028 const int itemLength = eng->length(item);
3029 int start = qMax(line.from, si.position);
3030 int end = qMin(lineEnd, si.position + itemLength);
3032 logClusters = eng->logClusters(&si);
3034 int gs = logClusters[start-si.position];
3035 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3037 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3040 x += glyphs.effectiveAdvance(gs);
3045 logClusters = eng->logClusters(scriptItem);
3046 glyphs = eng->shapedGlyphs(scriptItem);
3047 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3048 if (pos == (reverse ? 0 : l))
3049 x += scriptItem->width;
3051 bool rtl = eng->isRightToLeft();
3052 bool visual = eng->visualCursorMovement();
3053 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3055 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3056 int glyph_start = glyph_pos;
3057 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3059 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3060 x += glyphs.effectiveAdvance(i);
3061 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3063 int start = qMax(line.from - scriptItem->position, 0);
3064 int glyph_start = logClusters[start];
3065 int glyph_end = glyph_pos;
3066 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3068 for (
int i = glyph_start; i <= glyph_end; i++)
3069 x += glyphs.effectiveAdvance(i);
3070 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3074 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3075 x = line.x + line.width;
3076 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3079 *cursorPos = pos + scriptItem->position;
3084
3085
3086
3087
3088
3089
3090
3091
3092int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3094 QFixed x = QFixed::fromReal(_x);
3095 const QScriptLine &line = eng->lines[index];
3096 bool lastLine = index >= eng->lines.size() - 1;
3097 int lineNum = index;
3099 if (!eng->layoutData)
3102 int line_length = textLength();
3107 int firstItem = eng->findItem(line.from);
3108 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3109 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3115 x -= eng->alignLine(line);
3118 QVarLengthArray<
int> visualOrder(nItems);
3119 QVarLengthArray<
unsigned char> levels(nItems);
3120 for (
int i = 0; i < nItems; ++i)
3121 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3122 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3124 bool visual = eng->visualCursorMovement();
3127 if (eng->isRightToLeft())
3128 return line.from + line_length;
3130 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3133 bool rtl = eng->isRightToLeft();
3135 eng->shapeLine(line);
3136 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3138 for (
int i = 0; i < nItems; ++i) {
3139 int item = visualOrder[i]+firstItem;
3140 QScriptItem &si = eng->layoutData->items[item];
3141 int item_length = eng->length(item);
3144 int start = qMax(line.from - si.position, 0);
3145 int end = qMin(line.from + line_length - si.position, item_length);
3147 unsigned short *logClusters = eng->logClusters(&si);
3149 int gs = logClusters[start];
3150 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3151 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3153 QFixed item_width = 0;
3154 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3155 item_width = si.width;
3159 item_width += glyphs.effectiveAdvance(g);
3165 if (pos + item_width < x) {
3171 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3172 if (cpos == QTextLine::CursorOnCharacter)
3174 bool left_half = (x - pos) < item_width/2;
3176 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3178 return si.position + 1;
3184 if (cpos == QTextLine::CursorOnCharacter) {
3185 if (si.analysis.bidiLevel % 2) {
3189 if (glyphs.attributes[gs].clusterStart) {
3195 pos -= glyphs.effectiveAdvance(gs);
3201 if (glyphs.attributes[gs].clusterStart) {
3207 pos += glyphs.effectiveAdvance(gs);
3212 QFixed dist = INT_MAX/256;
3213 if (si.analysis.bidiLevel % 2) {
3214 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3217 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3222 pos -= glyphs.effectiveAdvance(gs);
3227 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3232 pos += glyphs.effectiveAdvance(ge);
3237 if (!visual || !rtl || (lastLine && i == 0)) {
3239 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3244 pos += glyphs.effectiveAdvance(gs);
3248 QFixed oldPos = pos;
3250 pos += glyphs.effectiveAdvance(gs);
3251 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3261 if (qAbs(x-pos) < dist) {
3263 if (!rtl && i < nItems - 1) {
3267 if (rtl && nchars > 0)
3268 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3270 return eng->positionInLigature(&si, end, x, pos, -1,
3271 cpos == QTextLine::CursorOnCharacter);
3274 Q_ASSERT(glyph_pos != -1);
3275 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3276 cpos == QTextLine::CursorOnCharacter);
3280 int pos = line.from;
3281 if (!eng->isRightToLeft())
3288 if (index < eng->lines.size() - 1)
3289 pos = qMin(eng->previousLogicalPosition(pos), pos);
Combined button and popup list for selecting options.
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