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
1910 || QTextDocumentPrivate::get(eng->block) ==
nullptr) {
1915 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1916 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1919 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1920 lbh.whiteSpaceOrObject =
true;
1921 if (lbh.checkFullOtherwiseExtend(line))
1924 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1925 QFixed tabWidth = eng->calculateTabWidth(item, x);
1926 attributes = eng->attributes();
1929 lbh.logClusters = eng->layoutData->logClustersPtr;
1930 lbh.glyphs = eng->shapedGlyphs(¤t);
1932 lbh.spaceData.textWidth += tabWidth;
1933 lbh.spaceData.length++;
1936 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1937 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1939 if (lbh.checkFullOtherwiseExtend(line))
1941 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1942 lbh.whiteSpaceOrObject =
true;
1945 if (!line.length && !lbh.tmpData.length)
1946 line.setDefaultHeight(eng);
1947 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1948 if (lbh.checkFullOtherwiseExtend(line))
1951 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1952 current, lbh.logClusters, lbh.glyphs);
1954 lbh.tmpData.length++;
1955 lbh.calculateRightBearingForPreviousGlyph();
1957 line += lbh.tmpData;
1958 manuallyWrapped =
true;
1960 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1961 lbh.whiteSpaceOrObject =
true;
1962 lbh.tmpData.length++;
1964 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1965 QTextInlineObject inlineObject(item, eng);
1966 QTextFormat f = inlineObject.format();
1967 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1968 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1969 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1970 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1971 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1975 lbh.tmpData.textWidth += current.width;
1979 if (lbh.checkFullOtherwiseExtend(line))
1982 hasInlineObject =
true;
1983 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1985 }
else if (attributes[lbh.currentPosition].whiteSpace
1986 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1989 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
1990 lbh.saveCurrentGlyph();
1991 lbh.whiteSpaceOrObject =
true;
1992 while (lbh.currentPosition < end
1993 && attributes[lbh.currentPosition].whiteSpace
1994 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1995 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1996 current, lbh.logClusters, lbh.glyphs);
1999 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
2002 lbh.whiteSpaceOrObject =
false;
2003 bool sb_or_ws =
false;
2007 if (lbh.currentPosition == 0
2008 || lbh.previousGlyph == 0
2009 || includeTrailingSpaces
2010 || !attributes[lbh.currentPosition - 1].whiteSpace) {
2011 lbh.saveCurrentGlyph();
2013 QFixed accumulatedTextWidth;
2015 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
2016 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2023 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2024 && attributes[lbh.currentPosition].whiteSpace
2025 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2027 if (lbh.currentPosition >= eng->layoutData->string.size()
2029 || attributes[lbh.currentPosition].lineBreak
2030 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2033 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2034 if (breakWordOrAny) {
2035 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2036 accumulatedTextWidth = 0;
2041 }
while (lbh.currentPosition < end);
2042 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2044 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2045 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2046 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2063 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2066 if (sb_or_ws|breakany) {
2074 QFixed previousRightBearing = lbh.rightBearing;
2085 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2086 lbh.calculateRightBearing();
2088 if (lbh.checkFullOtherwiseExtend(line)) {
2093 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2094 lbh.rightBearing = previousRightBearing;
2096 lbh.calculateRightBearingForPreviousGlyph();
2098 line.textWidth += lbh.commitedSoftHyphenWidth;
2103 lbh.saveCurrentGlyph();
2105 if (lbh.currentPosition == end)
2109 reachedEndOfLine =
true;
2110 lbh.checkFullOtherwiseExtend(line);
2111 line.textWidth += lbh.commitedSoftHyphenWidth;
2113 line.textAdvance = line.textWidth;
2116 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2117 lbh.calculateRightBearing();
2120 const QFixed textWidthWithoutBearing = line.textWidth;
2121 line.textWidth += lbh.negativeRightBearing();
2123 if (line.length == 0) {
2124 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2125 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2126 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2127 line += lbh.tmpData;
2130 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2132 if (maxInlineObjectHeight > line.ascent + line.descent) {
2134 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2135 line.ascent += toAdd;
2136 line.descent = maxInlineObjectHeight - line.ascent;
2138 int startItem = eng->findItem(line.from);
2139 int endItem = eng->findItem(line.from + line.length);
2141 endItem = eng->layoutData->items.size();
2142 for (
int item = startItem; item < endItem; ++item) {
2143 QScriptItem ¤t = eng->layoutData->items[item];
2144 if (current.analysis.flags == QScriptAnalysis::Object) {
2145 QTextInlineObject inlineObject(item, eng);
2146 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2147 QFixed height = current.ascent + current.descent;
2149 case QTextCharFormat::AlignTop:
2150 current.ascent = line.ascent;
2151 current.descent = height - line.ascent;
2153 case QTextCharFormat::AlignMiddle:
2154 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2155 current.descent = height - line.ascent;
2157 case QTextCharFormat::AlignBottom:
2158 current.descent = line.descent;
2159 current.ascent = height - line.descent;
2164 Q_ASSERT(line.ascent >= current.ascent);
2165 Q_ASSERT(line.descent >= current.descent);
2171 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2172 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2173 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2175 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2176 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2177 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2178 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2180 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2181 layout_helper(lbh.maxGlyphs);
2182 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2187 if (lbh.manualWrap) {
2188 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2189 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2191 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2193 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2195 : textWidthWithoutBearing;
2196 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2197 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2198 if (!manuallyWrapped) {
2199 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2200 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2202 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2203 if (manuallyWrapped)
2204 eng->layoutData->currentMaxWidth = 0;
2207 line.textWidth += trailingSpace;
2208 if (lbh.spaceData.length) {
2209 line.trailingSpaces = lbh.spaceData.length;
2210 line.hasTrailingSpaces =
true;
2213 line.justified =
false;
2214 line.gridfitted =
false;
2218
2219
2220void QTextLine::setPosition(
const QPointF &pos)
2222 eng->lines[index].x = QFixed::fromReal(pos.x());
2223 eng->lines[index].y = QFixed::fromReal(pos.y());
2227
2228
2229QPointF QTextLine::position()
const
2231 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2242
2243
2244
2245int QTextLine::textStart()
const
2247 return eng->lines.at(index).from;
2251
2252
2253
2254
2255int QTextLine::textLength()
const
2257 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2258 && eng->block.isValid() && index == eng->lines.size()-1) {
2259 return eng->lines.at(index).length - 1;
2261 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2266 QBrush bg = chf.background();
2268 p->fillRect(r.toAlignedRect(), bg);
2273 QBrush c = chf.foreground();
2274 if (c.style() == Qt::NoBrush)
2275 p->setPen(defaultPen);
2277 p->setPen(QPen(c, 0));
2280#if !defined(QT_NO_RAWFONT)
2282 const QString &text,
2285 const QGlyphRun::GlyphRunFlags &flags,
2286 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2288 QFixed selectionWidth,
2291 unsigned short *logClusters,
2295 Q_ASSERT(logClusters !=
nullptr);
2301 int rangeStart = textPosition;
2302 int logClusterIndex = 0;
2303 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2308 int rangeEnd = rangeStart;
2309 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2319 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2320 fontD->setFontEngine(fontEngine);
2322 QVarLengthArray<glyph_t> glyphsArray;
2323 QVarLengthArray<QFixedPoint> positionsArray;
2325 QTextItem::RenderFlags renderFlags;
2326 if (flags.testFlag(QGlyphRun::Overline))
2327 renderFlags |= QTextItem::Overline;
2328 if (flags.testFlag(QGlyphRun::Underline))
2329 renderFlags |= QTextItem::Underline;
2330 if (flags.testFlag(QGlyphRun::StrikeOut))
2331 renderFlags |= QTextItem::StrikeOut;
2332 if (flags.testFlag(QGlyphRun::RightToLeft))
2333 renderFlags |= QTextItem::RightToLeft;
2335 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2337 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2339 qreal fontHeight = font.ascent() + font.descent();
2342 QList<quint32> glyphs;
2343 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2344 glyphs.reserve(glyphsArray.size());
2345 QList<QPointF> positions;
2346 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2347 positions.reserve(glyphsArray.size());
2348 QList<qsizetype> stringIndexes;
2349 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2350 stringIndexes.reserve(glyphsArray.size());
2352 int nextClusterIndex = 0;
2353 int currentClusterIndex = 0;
2354 for (
int i = 0; i < glyphsArray.size(); ++i) {
2355 const int glyphArrayIndex = i + glyphsStart;
2358 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2359 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2360 currentClusterIndex = nextClusterIndex;
2361 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2368 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2369 stringIndexes.append(textPosition + currentClusterIndex);
2372 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2373 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2374 glyphs.append(glyphIndex);
2377 QPointF position = positionsArray.at(i).toPointF() + pos;
2378 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2379 positions.append(position);
2382 maxY = minY = position.y();
2384 minY = qMin(minY, position.y());
2385 maxY = qMax(maxY, position.y());
2389 qreal height = maxY + fontHeight - minY;
2391 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2392 glyphRun.setGlyphIndexes(glyphs);
2393 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2394 glyphRun.setPositions(positions);
2395 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2396 glyphRun.setStringIndexes(stringIndexes);
2397 if (retrievalFlags & QTextLayout::RetrieveString)
2398 glyphRun.setSourceString(text);
2399 glyphRun.setFlags(flags);
2400 glyphRun.setRawFont(font);
2402 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2403 selectionWidth.toReal(), height));
2408# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2431 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2454 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2456 const QScriptLine &line = eng->lines.at(index);
2458 if (line.length == 0)
2459 return QList<QGlyphRun>();
2465 length = textLength();
2468 return QList<QGlyphRun>();
2470 QTextLayout::FormatRange selection;
2471 selection.start = from;
2472 selection.length = length;
2474 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2475 qreal y = line.y.toReal() + line.base().toReal();
2476 QList<QGlyphRun> glyphRuns;
2477 while (!iterator.atEnd()) {
2478 QScriptItem &si = iterator.next();
2479 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2482 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2485 QPointF pos(iterator.x.toReal(), y);
2488 QGlyphRun::GlyphRunFlags flags;
2489 if (!eng->useRawFont) {
2490 font = eng->font(si);
2491 if (font.overline())
2492 flags |= QGlyphRun::Overline;
2493 if (font.underline())
2494 flags |= QGlyphRun::Underline;
2495 if (font.strikeOut())
2496 flags |= QGlyphRun::StrikeOut;
2500 if (si.analysis.bidiLevel % 2) {
2501 flags |= QGlyphRun::RightToLeft;
2505 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2506 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2508 unsigned short *logClusters = eng->logClusters(&si);
2509 int glyphsStart = logClusters[relativeFrom];
2510 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2512 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2513 if (nextGlyphIndex - 1 > glyphsEnd)
2514 glyphsEnd = nextGlyphIndex - 1;
2515 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2516 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2518 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2519 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2521 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2526 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2527 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2528 if (!glyphLayout.attributes[i].dontPrint) {
2529 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2530 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2533 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2534 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2535 if (!glyphLayout.attributes[i].dontPrint) {
2536 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2537 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2542 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2546 iterator.getSelectionBounds(&x, &width);
2548 if (glyphLayout.numGlyphs > 0) {
2549 QFontEngine *mainFontEngine;
2550#ifndef QT_NO_RAWFONT
2551 if (eng->useRawFont && eng->rawFont.isValid())
2552 mainFontEngine= eng->fontEngine(si);
2555 mainFontEngine = font.d->engineForScript(si.analysis.script);
2557 if (mainFontEngine->type() == QFontEngine::Multi) {
2558 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2559 int start = rtl ? glyphLayout.numGlyphs : 0;
2560 int end = start - 1;
2561 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2562 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2563 rtl ? --start : ++end) {
2564 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2568 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2569 multiFontEngine->ensureEngineAt(which);
2571 QGlyphRun::GlyphRunFlags subFlags = flags;
2572 if (start == 0 && startsInsideLigature)
2573 subFlags |= QGlyphRun::SplitLigature;
2576 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2584 glyphsStart + start,
2586 logClusters + relativeFrom,
2587 relativeFrom + si.position,
2588 relativeTo - relativeFrom + 1);
2589 if (!glyphRun.isEmpty())
2590 glyphRuns.append(glyphRun);
2592 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2593 if (!subLayout.attributes[i].dontPrint) {
2594 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2595 pos.rx() += (subLayout.advances[i] + justification).toReal();
2606 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2607 multiFontEngine->ensureEngineAt(which);
2609 QGlyphRun::GlyphRunFlags subFlags = flags;
2610 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2611 subFlags |= QGlyphRun::SplitLigature;
2613 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2621 glyphsStart + start,
2623 logClusters + relativeFrom,
2624 relativeFrom + si.position,
2625 relativeTo - relativeFrom + 1);
2626 if (!glyphRun.isEmpty())
2627 glyphRuns.append(glyphRun);
2629 if (startsInsideLigature || endsInsideLigature)
2630 flags |= QGlyphRun::SplitLigature;
2631 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2641 logClusters + relativeFrom,
2642 relativeFrom + si.position,
2643 relativeTo - relativeFrom + 1);
2644 if (!glyphRun.isEmpty())
2645 glyphRuns.append(glyphRun);
2655
2656
2657
2658
2659void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2661 draw_internal(painter, position,
nullptr);
2664void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2665 const QTextLayout::FormatRange *selection)
const
2667#ifndef QT_NO_RAWFONT
2669 Q_ASSERT(!eng->useRawFont);
2671 const QScriptLine &line = eng->lines[index];
2673 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2677 && selection->start <= line.from
2678 && selection->start + selection->length > line.from) {
2680 const qreal lineHeight = line.height().toReal();
2681 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2682 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2683 drawBackground(p, selection->format, r);
2688 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2689 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2691 if (Q_LIKELY(!xlateToFixedRange))
2694 p->translate(origPos);
2697 QFixed lineBase = line.base();
2698 eng->clearDecorations();
2699 eng->enableDelayDecorations();
2701 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2703 const QTextFormatCollection *formatCollection = eng->formatCollection();
2705 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2707 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2709 format.merge(eng->format(si));
2711 if (suppressColors) {
2712 format.clearForeground();
2713 format.clearBackground();
2714 format.clearProperty(QTextFormat::TextUnderlineColor);
2717 format.merge(selection->format);
2721 QTextLineItemIterator iterator(eng, index, pos, selection);
2722 while (!iterator.atEnd()) {
2723 QScriptItem &si = iterator.next();
2725 if (eng->hasFormats() || selection || formatCollection) {
2726 QTextCharFormat format;
2727 if (formatCollection !=
nullptr)
2728 format = formatCollection->defaultTextFormat();
2729 prepareFormat(format, &si);
2730 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2731 iterator.itemWidth.toReal(), line.height().toReal()));
2736 QPen pen = p->pen();
2738 QTextLineItemIterator iterator(eng, index, pos, selection);
2739 while (!iterator.atEnd()) {
2740 QScriptItem &si = iterator.next();
2742 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2745 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2746 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2749 QFixed itemBaseLine = y;
2750 QFont f = eng->font(si);
2751 QTextCharFormat format;
2752 if (formatCollection !=
nullptr)
2753 format = formatCollection->defaultTextFormat();
2755 if (eng->hasFormats() || selection || formatCollection) {
2756 prepareFormat(format, &si);
2757 setPen(p, pen, format);
2759 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2760 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2761 if (valign == QTextCharFormat::AlignSuperScript
2762 || valign == QTextCharFormat::AlignSubScript
2763 || !qFuzzyIsNull(baseLineOffset))
2765 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2766 QFixed height = fe->ascent() + fe->descent();
2767 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2769 if (valign == QTextCharFormat::AlignSubScript)
2770 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2771 else if (valign == QTextCharFormat::AlignSuperScript)
2772 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2776 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2778 if (eng->hasFormats()) {
2780 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2781 QFixed itemY = y - si.ascent;
2782 switch (format.verticalAlignment()) {
2783 case QTextCharFormat::AlignTop:
2784 itemY = y - lineBase;
2786 case QTextCharFormat::AlignMiddle:
2787 itemY = y - lineBase + (line.height() - si.height()) / 2;
2789 case QTextCharFormat::AlignBottom:
2790 itemY = y - lineBase + line.height() - si.height();
2796 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2798 eng->docLayout()->drawInlineObject(p, itemRect,
2799 QTextInlineObject(iterator.item, eng),
2800 si.position + eng->block.position(),
2804 if (bg.style() != Qt::NoBrush) {
2805 QColor c = bg.color();
2807 p->fillRect(itemRect, c);
2811 QFont f = eng->font(si);
2812 QTextItemInt gf(si, &f, format);
2815 gf.width = iterator.itemWidth;
2816 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2817 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2818 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2819 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2820 qreal x = iterator.itemWidth.toReal() - w;
2822 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2823 iterator.itemWidth.toReal(), line.height().toReal()),
2828 p->drawText(QPointF(iterator.x.toReal() + x,
2829 y.toReal()), visualTab);
2839 unsigned short *logClusters = eng->logClusters(&si);
2840 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2842 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2843 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2844 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2845 gf.logClusters = logClusters + iterator.itemStart - si.position;
2846 gf.width = iterator.itemWidth;
2847 gf.justified = line.justified;
2848 gf.initWithScriptItem(si);
2850 Q_ASSERT(gf.fontEngine);
2852 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2853 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2855 path.setFillRule(Qt::WindingFill);
2857 if (gf.glyphs.numGlyphs)
2858 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2860 const QFontEngine *fe = gf.fontEngine;
2861 const qreal lw = fe->lineThickness().toReal();
2862 if (gf.flags & QTextItem::Underline) {
2863 qreal offs = fe->underlinePosition().toReal();
2864 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2866 if (gf.flags & QTextItem::Overline) {
2867 qreal offs = fe->ascent().toReal() + 1;
2868 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2870 if (gf.flags & QTextItem::StrikeOut) {
2871 qreal offs = fe->ascent().toReal() / 3;
2872 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2877 p->setRenderHint(QPainter::Antialiasing);
2880 if (p->pen().style() == Qt::NoPen)
2881 p->setBrush(Qt::NoBrush);
2883 p->setBrush(p->pen().brush());
2885 p->setPen(format.textOutline());
2890 gf.glyphs.numGlyphs = 0;
2891 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2894 if ((si.analysis.flags == QScriptAnalysis::Space
2895 || si.analysis.flags == QScriptAnalysis::Nbsp)
2896 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2897 QBrush c = format.foreground();
2898 if (c.style() != Qt::NoBrush)
2899 p->setPen(c.color());
2900 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2901 QFont oldFont = p->font();
2902 p->setFont(eng->font(si));
2903 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2905 p->setFont(oldFont);
2909 eng->drawDecorations(p);
2911 if (xlateToFixedRange)
2912 p->translate(-origPos);
2914 if (eng->hasFormats())
2919
2920
2921
2922
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2936 const QScriptLine &line = eng->lines[index];
2937 bool lastLine = index >= eng->lines.size() - 1;
2939 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2941 if (!eng->layoutData)
2943 if (!eng->layoutData->items.size()) {
2944 *cursorPos = line.from;
2948 int lineEnd = line.from + line.length + line.trailingSpaces;
2949 int pos = qBound(line.from, *cursorPos, lineEnd);
2950 const QCharAttributes *attributes = eng->attributes();
2952 *cursorPos = line.from;
2955 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2958 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2960 *cursorPos = line.from;
2963 eng->shapeLine(line);
2965 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2966 if (!scriptItem->num_glyphs)
2969 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2972 int neighborItem = itm;
2973 if (neighborItem > 0 && scriptItem->position == pos)
2975 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2977 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2980 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2982 scriptItem = &eng->layoutData->items[itm];
2983 if (!scriptItem->num_glyphs)
2989 const int l = eng->length(itm);
2990 pos = qBound(0, pos - scriptItem->position, l);
2992 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
2993 unsigned short *logClusters = eng->logClusters(scriptItem);
2994 Q_ASSERT(logClusters);
2996 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
2997 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
3000 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
3004 bool reverse = scriptItem->analysis.bidiLevel % 2;
3009 int firstItem = eng->findItem(line.from);
3010 int lastItem = eng->findItem(lineEnd - 1, itm);
3011 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3013 QVarLengthArray<
int> visualOrder(nItems);
3014 QVarLengthArray<uchar> levels(nItems);
3015 for (
int i = 0; i < nItems; ++i)
3016 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3017 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3019 for (
int i = 0; i < nItems; ++i) {
3020 int item = visualOrder[i]+firstItem;
3023 QScriptItem &si = eng->layoutData->items[item];
3027 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3032 const int itemLength = eng->length(item);
3033 int start = qMax(line.from, si.position);
3034 int end = qMin(lineEnd, si.position + itemLength);
3036 logClusters = eng->logClusters(&si);
3038 int gs = logClusters[start-si.position];
3039 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3041 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3044 x += glyphs.effectiveAdvance(gs);
3049 logClusters = eng->logClusters(scriptItem);
3050 glyphs = eng->shapedGlyphs(scriptItem);
3051 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3052 if (pos == (reverse ? 0 : l))
3053 x += scriptItem->width;
3055 bool rtl = eng->isRightToLeft();
3056 bool visual = eng->visualCursorMovement();
3057 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3059 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3060 int glyph_start = glyph_pos;
3061 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3063 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3064 x += glyphs.effectiveAdvance(i);
3065 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3067 int start = qMax(line.from - scriptItem->position, 0);
3068 int glyph_start = logClusters[start];
3069 int glyph_end = glyph_pos;
3070 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3072 for (
int i = glyph_start; i <= glyph_end; i++)
3073 x += glyphs.effectiveAdvance(i);
3074 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3078 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3079 x = line.x + line.width;
3080 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3083 *cursorPos = pos + scriptItem->position;
3088
3089
3090
3091
3092
3093
3094
3095
3096int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3098 QFixed x = QFixed::fromReal(_x);
3099 const QScriptLine &line = eng->lines[index];
3100 bool lastLine = index >= eng->lines.size() - 1;
3101 int lineNum = index;
3103 if (!eng->layoutData)
3106 int line_length = textLength();
3111 int firstItem = eng->findItem(line.from);
3112 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3113 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3119 x -= eng->alignLine(line);
3122 QVarLengthArray<
int> visualOrder(nItems);
3123 QVarLengthArray<
unsigned char> levels(nItems);
3124 for (
int i = 0; i < nItems; ++i)
3125 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3126 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3128 bool visual = eng->visualCursorMovement();
3131 if (eng->isRightToLeft())
3132 return line.from + line_length;
3134 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3137 bool rtl = eng->isRightToLeft();
3139 eng->shapeLine(line);
3140 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3142 for (
int i = 0; i < nItems; ++i) {
3143 int item = visualOrder[i]+firstItem;
3144 QScriptItem &si = eng->layoutData->items[item];
3145 int item_length = eng->length(item);
3148 int start = qMax(line.from - si.position, 0);
3149 int end = qMin(line.from + line_length - si.position, item_length);
3151 unsigned short *logClusters = eng->logClusters(&si);
3153 int gs = logClusters[start];
3154 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3155 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3157 QFixed item_width = 0;
3158 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3159 item_width = si.width;
3163 item_width += glyphs.effectiveAdvance(g);
3169 if (pos + item_width < x) {
3175 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3176 if (cpos == QTextLine::CursorOnCharacter)
3178 bool left_half = (x - pos) < item_width/2;
3180 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3182 return si.position + 1;
3188 if (cpos == QTextLine::CursorOnCharacter) {
3189 if (si.analysis.bidiLevel % 2) {
3193 if (glyphs.attributes[gs].clusterStart) {
3199 pos -= glyphs.effectiveAdvance(gs);
3205 if (glyphs.attributes[gs].clusterStart) {
3211 pos += glyphs.effectiveAdvance(gs);
3216 QFixed dist = INT_MAX/256;
3217 if (si.analysis.bidiLevel % 2) {
3218 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3221 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3226 pos -= glyphs.effectiveAdvance(gs);
3231 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3236 pos += glyphs.effectiveAdvance(ge);
3241 if (!visual || !rtl || (lastLine && i == 0)) {
3243 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3248 pos += glyphs.effectiveAdvance(gs);
3252 QFixed oldPos = pos;
3254 pos += glyphs.effectiveAdvance(gs);
3255 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3265 if (qAbs(x-pos) < dist) {
3267 if (!rtl && i < nItems - 1) {
3271 if (rtl && nchars > 0)
3272 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3274 return eng->positionInLigature(&si, end, x, pos, -1,
3275 cpos == QTextLine::CursorOnCharacter);
3278 Q_ASSERT(glyph_pos != -1);
3279 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3280 cpos == QTextLine::CursorOnCharacter);
3284 int pos = line.from;
3285 if (!eng->isRightToLeft())
3292 if (index < eng->lines.size() - 1)
3293 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