13#include <qvarlengtharray.h>
14#include <qtextformat.h>
15#include <qabstracttextdocumentlayout.h>
28#include <private/qpainter_p.h>
32#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1
)
33#define SuppressText 0x5012
34#define SuppressBackground 0x513
37
38
39
40
41
42
43
44
45
48
49
50
53
54
55
58
59
60
63
64
65
66
69
70
71
72
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
96
97
98
99
100
101
104
105
106
107
110
111
112
113
114
117
118
119
120
121QRectF QTextInlineObject::rect()
const
123 QScriptItem& si = eng->layoutData->items[itm];
124 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
128
129
130
131
132qreal QTextInlineObject::width()
const
134 return eng->layoutData->items.at(itm).width.toReal();
138
139
140
141
142qreal QTextInlineObject::ascent()
const
144 return eng->layoutData->items.at(itm).ascent.toReal();
148
149
150
151
152qreal QTextInlineObject::descent()
const
154 return eng->layoutData->items.at(itm).descent.toReal();
158
159
160
161
162
163qreal QTextInlineObject::height()
const
165 return eng->layoutData->items.at(itm).height().toReal();
169
170
171
172
173void QTextInlineObject::setWidth(qreal w)
175 eng->layoutData->items[itm].width = QFixed::fromReal(w);
179
180
181
182
183void QTextInlineObject::setAscent(qreal a)
185 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
189
190
191
192
193void QTextInlineObject::setDescent(qreal d)
195 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
199
200
201int QTextInlineObject::textPosition()
const
203 return eng->layoutData->items[itm].position;
207
208
209
210int QTextInlineObject::formatIndex()
const
212 return eng->formatIndex(&eng->layoutData->items[itm]);
216
217
218QTextFormat QTextInlineObject::format()
const
220 return eng->format(&eng->layoutData->items[itm]);
224
225
226Qt::LayoutDirection QTextInlineObject::textDirection()
const
228 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
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
281
284
285
286
287
288
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
311
312
313
314
315
318
319
320
321
322QTextLayout::QTextLayout()
323{ d =
new QTextEngine(); }
326
327
328QTextLayout::QTextLayout(
const QString& text)
330 d =
new QTextEngine();
335
336
337
338
339
340
341
342
343
345QTextLayout::QTextLayout(
const QString &text,
const QFont &font,
const QPaintDevice *paintdevice)
347 const QFont f(paintdevice ? QFont(font, paintdevice) : font);
348 d =
new QTextEngine((text.isNull() ? (
const QString&)QString::fromLatin1(
"") : text), f);
352
353
354
355QTextLayout::QTextLayout(
const QTextBlock &block)
357 d =
new QTextEngine();
362
363
364QTextLayout::~QTextLayout()
372
373
374
375
376
377
378void QTextLayout::setRawFont(
const QRawFont &rawFont)
380 d->rawFont = rawFont;
381 d->useRawFont =
true;
382 d->resetFontEngineCache();
387
388
389
390
391
392void QTextLayout::setFont(
const QFont &font)
396 d->useRawFont =
false;
398 d->resetFontEngineCache();
402
403
404
405
406
407QFont QTextLayout::font()
const
413
414
415
416
417
418
419
420
421void QTextLayout::setText(
const QString& string)
429
430
431
432
433QString QTextLayout::text()
const
439
440
441
442
443
444void QTextLayout::setTextOption(
const QTextOption &option)
450
451
452
453
454const QTextOption &QTextLayout::textOption()
const
460
461
462
463
464
465
466void QTextLayout::setPreeditArea(
int position,
const QString &text)
468 if (d->preeditAreaPosition() == position && d->preeditAreaText() == text)
470 d->setPreeditArea(position, text);
472 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
473 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
477
478
479
480
481
482int QTextLayout::preeditAreaPosition()
const
484 return d->preeditAreaPosition();
488
489
490
491
492QString QTextLayout::preeditAreaText()
const
494 return d->preeditAreaText();
498
499
500
501
502
503
504
505void QTextLayout::setFormats(
const QList<FormatRange> &formats)
507 d->setFormats(formats);
509 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
510 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
514
515
516
517
518
519
520QList<QTextLayout::FormatRange> QTextLayout::formats()
const
526
527
528
529
530
531
532void QTextLayout::clearFormats()
534 setFormats(QList<FormatRange>());
538
539
540
541
542
543
544
545
546
547void QTextLayout::setCacheEnabled(
bool enable)
549 d->cacheGlyphs = enable;
553
554
555
556
557
558bool QTextLayout::cacheEnabled()
const
560 return d->cacheGlyphs;
564
565
566
567
568
569
570
571void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
573 d->visualMovement = style == Qt::VisualMoveStyle;
577
578
579
580
581
582Qt::CursorMoveStyle QTextLayout::cursorMoveStyle()
const
584 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
588
589
590
591
592
593
594
595void QTextLayout::beginLayout()
598 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
599 qWarning(
"QTextLayout::beginLayout: Called while already doing layout");
606 d->layoutData->layoutState = QTextEngine::InLayout;
610
611
612
613
614void QTextLayout::endLayout()
617 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
618 qWarning(
"QTextLayout::endLayout: Called without beginLayout()");
622 int l = d->lines.size();
623 if (l && d->lines.at(l-1).length < 0) {
624 QTextLine(l-1, d).setNumColumns(INT_MAX);
626 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
632
633
634
635
636
637
638
639
640void QTextLayout::clearLayout()
646
647
648
649
650
651
652int QTextLayout::nextCursorPosition(
int oldPos, CursorMode mode)
const
654 const QCharAttributes *attributes = d->attributes();
655 int len = d->block.isValid() ? d->block.length() - 1
656 : d->layoutData->string.size();
657 Q_ASSERT(len <= d->layoutData->string.size());
658 if (!attributes || oldPos < 0 || oldPos >= len)
661 if (mode == SkipCharacters) {
663 while (oldPos < len && !attributes[oldPos].graphemeBoundary)
666 if (oldPos < len && d->atWordSeparator(oldPos)) {
668 while (oldPos < len && d->atWordSeparator(oldPos))
671 while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
674 while (oldPos < len && attributes[oldPos].whiteSpace)
682
683
684
685
686
687
688int QTextLayout::previousCursorPosition(
int oldPos, CursorMode mode)
const
690 const QCharAttributes *attributes = d->attributes();
691 int len = d->block.isValid() ? d->block.length() - 1
692 : d->layoutData->string.size();
693 Q_ASSERT(len <= d->layoutData->string.size());
694 if (!attributes || oldPos <= 0 || oldPos > len)
697 if (mode == SkipCharacters) {
699 while (oldPos && !attributes[oldPos].graphemeBoundary)
702 while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
705 if (oldPos && d->atWordSeparator(oldPos-1)) {
707 while (oldPos && d->atWordSeparator(oldPos-1))
710 while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
719
720
721
722
723
724
725int QTextLayout::rightCursorPosition(
int oldPos)
const
727 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
733
734
735
736
737
738
739int QTextLayout::leftCursorPosition(
int oldPos)
const
741 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761bool QTextLayout::isValidCursorPosition(
int pos)
const
763 const QCharAttributes *attributes = d->attributes();
764 if (!attributes || pos < 0 || pos > (
int)d->layoutData->string.size())
766 return attributes[pos].graphemeBoundary;
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785QTextLine QTextLayout::createLine()
788 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
789 qWarning(
"QTextLayout::createLine: Called without layouting");
793 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
796 int l = d->lines.size();
797 if (l && d->lines.at(l-1).length < 0) {
798 QTextLine(l-1, d).setNumColumns(INT_MAX);
799 if (d->maxWidth > QFIXED_MAX / 2) {
800 qWarning(
"QTextLayout: text too long, truncated.");
804 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
805 int strlen = d->layoutData->string.size();
806 if (l && from >= strlen) {
807 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
814 line.justified =
false;
815 line.gridfitted =
false;
817 d->lines.append(line);
818 return QTextLine(l, d);
822
823
824
825
826int QTextLayout::lineCount()
const
828 return d->lines.size();
832
833
834
835
836QTextLine QTextLayout::lineAt(
int i)
const
838 return i < lineCount() ? QTextLine(i, d) : QTextLine();
842
843
844
845
846QTextLine QTextLayout::lineForTextPosition(
int pos)
const
848 int lineNum = d->lineNumberForTextPosition(pos);
849 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
853
854
855
856
857
858
859
860QPointF QTextLayout::position()
const
866
867
868
869
870void QTextLayout::setPosition(
const QPointF &p)
876
877
878QRectF QTextLayout::boundingRect()
const
880 if (d->lines.isEmpty())
884 QFixed xmin = d->lines.at(0).x;
885 QFixed ymin = d->lines.at(0).y;
887 for (
int i = 0; i < d->lines.size(); ++i) {
888 const QScriptLine &si = d->lines.at(i);
889 xmin = qMin(xmin, si.x);
890 ymin = qMin(ymin, si.y);
891 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
892 xmax = qMax(xmax, si.x+lineWidth);
894 ymax = qMax(ymax, si.y+si.height().ceil());
896 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
900
901
902
903
904
905
906
907
908qreal QTextLayout::minimumWidth()
const
910 return d->minWidth.toReal();
914
915
916
917
918
919
920
921
922qreal QTextLayout::maximumWidth()
const
924 return d->maxWidth.toReal();
929
930
931void QTextLayout::setFlags(
int flags)
933 if (flags & Qt::TextJustificationForced) {
934 d->option.setAlignment(Qt::AlignJustify);
935 d->forceJustification =
true;
938 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
939 d->ignoreBidi =
true;
940 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
945 QPainterPath *region,
const QRectF &boundingRect)
947 const QScriptLine &line = eng->lines[lineNumber];
953 const qreal selectionY = pos.y() + line.y.toReal();
954 const qreal lineHeight = line.height().toReal();
956 QFixed lastSelectionX = iterator.x;
957 QFixed lastSelectionWidth;
962 QFixed selectionX, selectionWidth;
963 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
964 if (selectionX == lastSelectionX + lastSelectionWidth) {
965 lastSelectionWidth += selectionWidth;
969 if (lastSelectionWidth > 0) {
970 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
971 region->addRect(rect.toAlignedRect());
974 lastSelectionX = selectionX;
975 lastSelectionWidth = selectionWidth;
978 if (lastSelectionWidth > 0) {
979 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
980 region->addRect(rect.toAlignedRect());
986 return clip.isValid() ? (rect & clip) : rect;
990#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010# if !defined(QT_NO_RAWFONT)
1011QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
int length)
const
1013 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034#if !defined(QT_NO_RAWFONT)
1035QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
1037 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
1042 length = text().size();
1044 QHash<std::pair<QFontEngine *,
int>, QGlyphRun> glyphRunHash;
1045 for (
int i=0; i<d->lines.size(); ++i) {
1046 if (d->lines.at(i).from > from + length)
1048 else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
1049 const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
1050 for (
const QGlyphRun &glyphRun : glyphRuns) {
1051 QRawFont rawFont = glyphRun.rawFont();
1053 QFontEngine *fontEngine = rawFont.d->fontEngine;
1054 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1055 std::pair<QFontEngine *,
int> key(fontEngine,
int(flags));
1057 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1058 if (oldGlyphRun.isEmpty()) {
1059 oldGlyphRun = glyphRun;
1061 QList<quint32> indexes = oldGlyphRun.glyphIndexes();
1062 QList<QPointF> positions = oldGlyphRun.positions();
1063 QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
1064 QRectF boundingRect = oldGlyphRun.boundingRect();
1066 indexes += glyphRun.glyphIndexes();
1067 positions += glyphRun.positions();
1068 stringIndexes += glyphRun.stringIndexes();
1069 boundingRect = boundingRect.united(glyphRun.boundingRect());
1071 oldGlyphRun.setGlyphIndexes(indexes);
1072 oldGlyphRun.setPositions(positions);
1073 oldGlyphRun.setStringIndexes(stringIndexes);
1074 oldGlyphRun.setBoundingRect(boundingRect);
1080 return glyphRunHash.values();
1085
1086
1087
1088
1089void QTextLayout::draw(QPainter *p,
const QPointF &pos,
const QList<FormatRange> &selections,
const QRectF &clip)
const
1091 if (d->lines.isEmpty())
1097 QPointF position = pos + d->position;
1099 QFixed clipy = (INT_MIN/256);
1100 QFixed clipe = (INT_MAX/256);
1101 if (clip.isValid()) {
1102 clipy = QFixed::fromReal(clip.y() - position.y());
1103 clipe = clipy + QFixed::fromReal(clip.height());
1107 int lastLine = d->lines.size();
1108 for (
int i = 0; i < d->lines.size(); ++i) {
1109 const QScriptLine &sl = d->lines.at(i);
1115 if ((sl.y + sl.height()) < clipy) {
1121 QPainterPath excludedRegion;
1122 QPainterPath textDoneRegion;
1123 for (
int i = 0; i < selections.size(); ++i) {
1124 FormatRange selection = selections.at(i);
1127 const QPen pen = selection.format.penProperty(QTextFormat::OutlinePen);
1128 const bool hasOutline = (pen.style() != Qt::NoPen && pen.widthF() > 0);
1129 const QRectF selectionClip = hasOutline ? QRectF() : clip;
1130 QPainterPath region;
1131 region.setFillRule(Qt::WindingFill);
1133 for (
int line = firstLine; line < lastLine; ++line) {
1134 const QScriptLine &sl = d->lines.at(line);
1135 QTextLine tl(line, d);
1137 QRectF lineRect(tl.naturalTextRect());
1138 lineRect.translate(position);
1139 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1140 lineRect.setBottom(qCeil(lineRect.bottom()));
1142 bool isLastLineInBlock = (line == d->lines.size()-1);
1143 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1146 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1149 const bool selectionStartInLine = sl.from <= selection.start;
1150 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1152 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1153 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, selectionClip));
1155 region.addRect(clipIfValid(lineRect, selectionClip));
1158 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1159 QRectF fullLineRect(tl.rect());
1160 fullLineRect.translate(position);
1161 fullLineRect.setRight(QFIXED_MAX);
1162 fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
1164 const bool rightToLeft = d->isRightToLeft();
1166 if (!selectionEndInLine) {
1167 region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
1168 : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), selectionClip));
1170 if (!selectionStartInLine) {
1171 region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
1172 : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), selectionClip));
1174 }
else if (!selectionEndInLine
1175 && isLastLineInBlock
1176 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1177 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1178 lineRect.height()/4, lineRect.height()), selectionClip));
1183 const QPen oldPen = p->pen();
1184 const QBrush oldBrush = p->brush();
1188 p->setClipRect(clip, Qt::IntersectClip);
1191 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1192 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1193 p->drawPath(region);
1196 p->setBrush(oldBrush);
1204 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1205 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1207 if (hasBackground) {
1208 selection.format.setProperty(
ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1211 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1212 selection.format.clearProperty(QTextFormat::OutlinePen);
1217 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1221 p->setClipPath(region, Qt::IntersectClip);
1223 for (
int line = firstLine; line < lastLine; ++line) {
1224 QTextLine l(line, d);
1225 l.draw_internal(p, position, &selection);
1230 textDoneRegion += region;
1233 textDoneRegion -= region;
1236 excludedRegion += region;
1239 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1240 if (!needsTextButNoBackground.isEmpty()){
1242 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1243 FormatRange selection;
1244 selection.start = 0;
1245 selection.length = INT_MAX;
1247 for (
int line = firstLine; line < lastLine; ++line) {
1248 QTextLine l(line, d);
1249 l.draw_internal(p, position, &selection);
1254 if (!excludedRegion.isEmpty()) {
1257 QRectF br = boundingRect().translated(position);
1258 br.setRight(QFIXED_MAX);
1260 br = br.intersected(clip);
1262 path -= excludedRegion;
1263 p->setClipPath(path, Qt::IntersectClip);
1266 for (
int i = firstLine; i < lastLine; ++i) {
1268 l.draw(p, position);
1270 if (!excludedRegion.isEmpty())
1274 if (!d->cacheGlyphs)
1279
1280
1281
1282
1283
1284
1285
1286void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition)
const
1288 drawCursor(p, pos, cursorPosition, 1);
1292
1293
1294
1295
1296
1297
1298void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition,
int width)
const
1300 if (d->lines.isEmpty())
1306 QPointF position = pos + d->position;
1308 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
1309 int line = d->lineNumberForTextPosition(cursorPosition);
1312 if (line >= d->lines.size())
1315 QTextLine l(line, d);
1316 const QScriptLine &sl = d->lines.at(line);
1318 qreal x = position.x() + l.cursorToX(cursorPosition);
1320 QFixed base = sl.base();
1321 QFixed descent = sl.descent;
1322 bool rightToLeft = d->isRightToLeft();
1324 const int realCursorPosition = cursorPosition;
1325 if (d->visualCursorMovement()) {
1326 if (cursorPosition == sl.from + sl.length)
1331 int itm = d->findItem(cursorPosition);
1334 const QScriptItem *si = &d->layoutData->items.at(itm);
1337 if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
1338 int neighborItem = itm;
1339 if (neighborItem > 0 && si->position == realCursorPosition)
1341 else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
1343 const bool onBoundary = neighborItem != itm
1344 && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
1345 if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
1347 si = &d->layoutData->items[itm];
1351 if (si->analysis.flags != QScriptAnalysis::Object) {
1354 if (si->descent > 0)
1355 descent = si->descent;
1357 rightToLeft = si->analysis.bidiLevel % 2;
1359 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1360 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1361 && (p->transform().type() > QTransform::TxTranslate);
1362 if (toggleAntialiasing)
1363 p->setRenderHint(QPainter::Antialiasing);
1364 QPainter::CompositionMode origCompositionMode = p->compositionMode();
1370 QPaintDevice *targetDevice = p->paintEngine()->paintDevice();
1371 const bool targetHasAlphaChannel = targetDevice
1372 && targetDevice->devType() == QInternal::Image
1373 &&
static_cast<QImage *>(targetDevice)->hasAlphaChannel();
1374 if (!targetHasAlphaChannel && p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
1375 p->setCompositionMode(QPainter::RasterOp_NotDestination);
1376 const QTransform &deviceTransform = p->deviceTransform();
1377 const qreal xScale = deviceTransform.m11();
1378 if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
1379 p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
1382 const QPen origPen = p->pen();
1383 QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
1384 pen.setCosmetic(
true);
1385 const qreal center = x + qreal(width) / 2;
1387 p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
1390 p->setCompositionMode(origCompositionMode);
1391 if (toggleAntialiasing)
1392 p->setRenderHint(QPainter::Antialiasing,
false);
1393 if (d->layoutData->hasBidi) {
1394 const int arrow_extent = 4;
1395 int sign = rightToLeft ? -1 : 1;
1396 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1397 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1423
1424
1425
1426
1427
1430
1431
1432
1433
1434
1437
1438
1439
1440
1441
1442
1445
1446
1447
1448
1451
1452
1453
1454
1457
1458
1459
1460
1464
1465
1466
1467
1468QRectF QTextLine::rect()
const
1470 const QScriptLine& sl = eng->lines.at(index);
1471 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1475
1476
1477QRectF QTextLine::naturalTextRect()
const
1479 const QScriptLine& sl = eng->lines.at(index);
1480 QFixed x = sl.x + eng->alignLine(sl);
1482 QFixed width = sl.textWidth;
1486 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1490
1491
1492
1493
1494qreal QTextLine::x()
const
1496 return eng->lines.at(index).x.toReal();
1500
1501
1502
1503
1504qreal QTextLine::y()
const
1506 return eng->lines.at(index).y.toReal();
1510
1511
1512
1513
1514qreal QTextLine::width()
const
1516 return eng->lines.at(index).width.toReal();
1521
1522
1523
1524
1525qreal QTextLine::ascent()
const
1527 return eng->lines.at(index).ascent.toReal();
1531
1532
1533
1534
1535qreal QTextLine::descent()
const
1537 return eng->lines.at(index).descent.toReal();
1541
1542
1543
1544
1545
1546
1547qreal QTextLine::height()
const
1549 return eng->lines.at(index).height().ceil().toReal();
1553
1554
1555
1556
1557
1558
1559qreal QTextLine::leading()
const
1561 return eng->lines.at(index).leading.toReal();
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578void QTextLine::setLeadingIncluded(
bool included)
1580 eng->lines[index].leadingIncluded= included;
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594bool QTextLine::leadingIncluded()
const
1596 return eng->lines.at(index).leadingIncluded;
1600
1601
1602
1603
1604qreal QTextLine::naturalTextWidth()
const
1606 return eng->lines.at(index).textWidth.toReal();
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619qreal QTextLine::horizontalAdvance()
const
1621 return eng->lines.at(index).textAdvance.toReal();
1625
1626
1627
1628
1629
1630
1631void QTextLine::setLineWidth(qreal width)
1633 QScriptLine &line = eng->lines[index];
1634 if (!eng->layoutData) {
1635 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1639 line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
1641 && line.textWidth <= line.width
1642 && line.from + line.length == eng->layoutData->string.size())
1649 layout_helper(INT_MAX);
1653
1654
1655
1656
1657
1658
1659void QTextLine::setNumColumns(
int numColumns)
1661 QScriptLine &line = eng->lines[index];
1662 line.width = QFIXED_MAX;
1665 layout_helper(numColumns);
1669
1670
1671
1672
1673
1674
1675
1676void QTextLine::setNumColumns(
int numColumns, qreal alignmentWidth)
1678 QScriptLine &line = eng->lines[index];
1679 line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
1682 layout_helper(numColumns);
1686#define LB_DEBUG qDebug
1688#define LB_DEBUG if (0
) qDebug
1693 struct LineBreakHelper
1695 LineBreakHelper() =
default;
1697 QScriptLine tmpData;
1698 QScriptLine spaceData;
1704 int currentPosition = 0;
1705 glyph_t previousGlyph = 0;
1706 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1709 QFixed currentSoftHyphenWidth;
1710 QFixed commitedSoftHyphenWidth;
1711 QFixed rightBearing;
1712 QFixed minimumRightBearing;
1714 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1715 const unsigned short *logClusters =
nullptr;
1717 bool manualWrap =
false;
1718 bool whiteSpaceOrObject =
true;
1720 bool checkFullOtherwiseExtend(QScriptLine &line);
1722 QFixed calculateNewWidth(
const QScriptLine &line)
const {
1723 return line.textWidth + tmpData.textWidth + spaceData.textWidth
1724 + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
1727 inline glyph_t currentGlyph()
const
1729 Q_ASSERT(currentPosition > 0);
1730 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1732 return glyphs.glyphs[logClusters[currentPosition - 1]];
1735 inline void saveCurrentGlyph()
1738 if (currentPosition > 0 &&
1739 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1740 previousGlyph = currentGlyph();
1741 previousGlyphFontEngine = fontEngine;
1745 inline void calculateRightBearing(
QFontEngine *engine, glyph_t glyph)
1749 engine->getGlyphBearings(glyph,
nullptr, &rb);
1755 rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
1758 inline void calculateRightBearing()
1760 if (currentPosition <= 0)
1762 calculateRightBearing(fontEngine.data(), currentGlyph());
1765 inline void calculateRightBearingForPreviousGlyph()
1767 if (previousGlyph > 0)
1768 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1771 static const QFixed RightBearingNotCalculated;
1773 inline void resetRightBearing()
1775 rightBearing = RightBearingNotCalculated;
1780 inline QFixed negativeRightBearing()
const
1782 if (rightBearing == RightBearingNotCalculated)
1785 return qAbs(rightBearing);
1789Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
1791inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1793 LB_DEBUG(
"possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1795 QFixed newWidth = calculateNewWidth(line);
1796 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1799 const QFixed oldTextWidth = line.textWidth;
1801 line.textWidth += spaceData.textWidth;
1803 line.length += spaceData.length;
1804 tmpData.textWidth = 0;
1806 spaceData.textWidth = 0;
1807 spaceData.length = 0;
1809 if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
1810 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1811 currentSoftHyphenWidth = 0;
1820static inline void addNextCluster(
int &pos,
int end, QScriptLine &line,
int &glyphCount,
1821 const QScriptItem ¤t,
const unsigned short *logClusters,
1822 const QGlyphLayout &glyphs, QFixed *clusterWidth =
nullptr)
1824 int glyphPosition = logClusters[pos];
1828 }
while (pos < end && logClusters[pos] == glyphPosition);
1829 QFixed clusterWid = line.textWidth;
1832 line.textWidth += glyphs.advances[glyphPosition];
1836 Q_ASSERT((pos == end && glyphPosition == current
.num_glyphs) || logClusters[pos] == glyphPosition);
1839 *clusterWidth += (line.textWidth - clusterWid);
1845void QTextLine::layout_helper(
int maxGlyphs)
1847 QScriptLine &line = eng->lines[index];
1849 line.trailingSpaces = 0;
1851 line.hasTrailingSpaces =
false;
1853 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
1854 line.setDefaultHeight(eng);
1858 Q_ASSERT(line.from < eng->layoutData->string.size());
1860 LineBreakHelper lbh;
1862 lbh.maxGlyphs = maxGlyphs;
1864 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1865 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1866 const bool breakWordOrAny = breakany || (wrapMode == QTextOption::WrapAtWordBoundaryOrAnywhere);
1867 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1870 int newItem = eng->findItem(line.from);
1871 Q_ASSERT(newItem >= 0);
1873 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f", line.from, newItem,
int(eng->layoutData->items.size()), line.width.toReal());
1875 Qt::Alignment alignment = eng->option.alignment();
1877 const QCharAttributes *attributes = eng->attributes();
1880 lbh.currentPosition = line.from;
1882 lbh.logClusters = eng->layoutData->logClustersPtr;
1883 lbh.previousGlyph = 0;
1885 bool manuallyWrapped =
false;
1886 bool hasInlineObject =
false;
1887 bool reachedEndOfLine =
false;
1888 QFixed maxInlineObjectHeight = 0;
1890 const bool includeTrailingSpaces = eng->option.flags() & QTextOption::IncludeTrailingSpaces;
1892 while (newItem < eng->layoutData->items.size()) {
1893 lbh.resetRightBearing();
1894 if (newItem != item) {
1896 const QScriptItem ¤t = eng->layoutData->items.at(item);
1897 if (!current.num_glyphs) {
1899 attributes = eng->attributes();
1902 lbh.logClusters = eng->layoutData->logClustersPtr;
1904 lbh.currentPosition = qMax(line.from, current.position);
1905 end = current.position + eng->length(item);
1906 lbh.glyphs = eng->shapedGlyphs(¤t);
1907 QFontEngine *fontEngine = eng->fontEngine(current);
1908 if (lbh.fontEngine != fontEngine) {
1909 lbh.fontEngine = fontEngine;
1910 lbh.minimumRightBearing = qMin(QFixed(),
1911 QFixed::fromReal(fontEngine->minRightBearing()));
1914 const QScriptItem ¤t = eng->layoutData->items.at(item);
1916 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1917 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1919 if (current.analysis.flags != QScriptAnalysis::Object
1920 || QTextDocumentPrivate::get(eng->block) ==
nullptr) {
1925 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1926 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1929 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1930 lbh.whiteSpaceOrObject =
true;
1931 if (lbh.checkFullOtherwiseExtend(line))
1934 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1935 QFixed tabWidth = eng->calculateTabWidth(item, x);
1936 attributes = eng->attributes();
1939 lbh.logClusters = eng->layoutData->logClustersPtr;
1940 lbh.glyphs = eng->shapedGlyphs(¤t);
1942 lbh.spaceData.textWidth += tabWidth;
1943 lbh.spaceData.length++;
1946 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1947 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1949 if (lbh.checkFullOtherwiseExtend(line))
1951 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1952 lbh.whiteSpaceOrObject =
true;
1955 if (!line.length && !lbh.tmpData.length)
1956 line.setDefaultHeight(eng);
1957 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1958 if (lbh.checkFullOtherwiseExtend(line))
1961 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1962 current, lbh.logClusters, lbh.glyphs);
1964 lbh.tmpData.length++;
1965 lbh.calculateRightBearingForPreviousGlyph();
1967 line += lbh.tmpData;
1968 manuallyWrapped =
true;
1970 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1971 lbh.whiteSpaceOrObject =
true;
1972 lbh.tmpData.length++;
1974 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1975 QTextInlineObject inlineObject(item, eng);
1976 QTextFormat f = inlineObject.format();
1977 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1978 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1979 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1980 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1981 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1985 lbh.tmpData.textWidth += current.width;
1989 if (lbh.checkFullOtherwiseExtend(line))
1992 hasInlineObject =
true;
1993 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1995 }
else if (attributes[lbh.currentPosition].whiteSpace
1996 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1999 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
2000 lbh.saveCurrentGlyph();
2001 lbh.whiteSpaceOrObject =
true;
2002 while (lbh.currentPosition < end
2003 && attributes[lbh.currentPosition].whiteSpace
2004 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
2005 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
2006 current, lbh.logClusters, lbh.glyphs);
2009 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
2012 lbh.whiteSpaceOrObject =
false;
2013 bool sb_or_ws =
false;
2017 if (lbh.currentPosition == 0
2018 || lbh.previousGlyph == 0
2019 || includeTrailingSpaces
2020 || !attributes[lbh.currentPosition - 1].whiteSpace) {
2021 lbh.saveCurrentGlyph();
2023 QFixed accumulatedTextWidth;
2025 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
2026 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2033 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2034 && attributes[lbh.currentPosition].whiteSpace
2035 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2037 if (lbh.currentPosition >= eng->layoutData->string.size()
2039 || attributes[lbh.currentPosition].lineBreak
2040 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2043 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2044 if (breakWordOrAny) {
2045 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2046 accumulatedTextWidth = 0;
2051 }
while (lbh.currentPosition < end);
2052 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2054 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2055 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2056 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2073 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2076 if (sb_or_ws|breakany) {
2084 QFixed previousRightBearing = lbh.rightBearing;
2095 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2096 lbh.calculateRightBearing();
2098 if (lbh.checkFullOtherwiseExtend(line)) {
2103 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2104 lbh.rightBearing = previousRightBearing;
2106 lbh.calculateRightBearingForPreviousGlyph();
2108 line.textWidth += lbh.commitedSoftHyphenWidth;
2113 lbh.saveCurrentGlyph();
2115 if (lbh.currentPosition == end)
2119 reachedEndOfLine =
true;
2120 lbh.checkFullOtherwiseExtend(line);
2121 line.textWidth += lbh.commitedSoftHyphenWidth;
2123 line.textAdvance = line.textWidth;
2126 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2127 lbh.calculateRightBearing();
2130 const QFixed textWidthWithoutBearing = line.textWidth;
2131 line.textWidth += lbh.negativeRightBearing();
2133 if (line.length == 0) {
2134 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2135 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2136 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2137 line += lbh.tmpData;
2140 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2142 if (maxInlineObjectHeight > line.ascent + line.descent) {
2144 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2145 line.ascent += toAdd;
2146 line.descent = maxInlineObjectHeight - line.ascent;
2148 int startItem = eng->findItem(line.from);
2149 int endItem = eng->findItem(line.from + line.length);
2151 endItem = eng->layoutData->items.size();
2152 for (
int item = startItem; item < endItem; ++item) {
2153 QScriptItem ¤t = eng->layoutData->items[item];
2154 if (current.analysis.flags == QScriptAnalysis::Object) {
2155 QTextInlineObject inlineObject(item, eng);
2156 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2157 QFixed height = current.ascent + current.descent;
2159 case QTextCharFormat::AlignTop:
2160 current.ascent = line.ascent;
2161 current.descent = height - line.ascent;
2163 case QTextCharFormat::AlignMiddle:
2164 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2165 current.descent = height - line.ascent;
2167 case QTextCharFormat::AlignBottom:
2168 current.descent = line.descent;
2169 current.ascent = height - line.descent;
2174 Q_ASSERT(line.ascent >= current.ascent);
2175 Q_ASSERT(line.descent >= current.descent);
2181 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2182 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2183 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2185 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2186 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2187 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2188 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2190 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2191 layout_helper(lbh.maxGlyphs);
2192 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2197 if (lbh.manualWrap) {
2198 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2199 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2201 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2203 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2205 : textWidthWithoutBearing;
2206 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2207 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2208 if (!manuallyWrapped) {
2209 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2210 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2212 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2213 if (manuallyWrapped)
2214 eng->layoutData->currentMaxWidth = 0;
2217 line.textWidth += trailingSpace;
2218 if (lbh.spaceData.length) {
2219 line.trailingSpaces = lbh.spaceData.length;
2220 line.hasTrailingSpaces =
true;
2223 line.justified =
false;
2224 line.gridfitted =
false;
2228
2229
2230void QTextLine::setPosition(
const QPointF &pos)
2232 eng->lines[index].x = QFixed::fromReal(pos.x());
2233 eng->lines[index].y = QFixed::fromReal(pos.y());
2237
2238
2239QPointF QTextLine::position()
const
2241 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2252
2253
2254
2255int QTextLine::textStart()
const
2257 return eng->lines.at(index).from;
2261
2262
2263
2264
2265int QTextLine::textLength()
const
2267 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2268 && eng->block.isValid() && index == eng->lines.size()-1) {
2269 return eng->lines.at(index).length - 1;
2271 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2276 QBrush bg = chf.background();
2278 p->fillRect(r.toAlignedRect(), bg);
2283 QBrush c = chf.foreground();
2284 if (c.style() == Qt::NoBrush)
2285 p->setPen(defaultPen);
2287 p->setPen(QPen(c, 0));
2290#if !defined(QT_NO_RAWFONT)
2292 const QString &text,
2295 const QGlyphRun::GlyphRunFlags &flags,
2296 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2298 QFixed selectionWidth,
2301 unsigned short *logClusters,
2305 Q_ASSERT(logClusters !=
nullptr);
2311 int rangeStart = textPosition;
2312 int logClusterIndex = 0;
2313 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2318 int rangeEnd = rangeStart;
2319 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2329 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2330 fontD->setFontEngine(fontEngine);
2332 QVarLengthArray<glyph_t> glyphsArray;
2333 QVarLengthArray<QFixedPoint> positionsArray;
2335 QTextItem::RenderFlags renderFlags;
2336 if (flags.testFlag(QGlyphRun::Overline))
2337 renderFlags |= QTextItem::Overline;
2338 if (flags.testFlag(QGlyphRun::Underline))
2339 renderFlags |= QTextItem::Underline;
2340 if (flags.testFlag(QGlyphRun::StrikeOut))
2341 renderFlags |= QTextItem::StrikeOut;
2342 if (flags.testFlag(QGlyphRun::RightToLeft))
2343 renderFlags |= QTextItem::RightToLeft;
2345 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2347 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2349 qreal fontHeight = font.ascent() + font.descent();
2352 QList<quint32> glyphs;
2353 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2354 glyphs.reserve(glyphsArray.size());
2355 QList<QPointF> positions;
2356 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2357 positions.reserve(glyphsArray.size());
2358 QList<qsizetype> stringIndexes;
2359 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2360 stringIndexes.reserve(glyphsArray.size());
2362 int nextClusterIndex = 0;
2363 int currentClusterIndex = 0;
2364 for (
int i = 0; i < glyphsArray.size(); ++i) {
2365 const int glyphArrayIndex = i + glyphsStart;
2368 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2369 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2370 currentClusterIndex = nextClusterIndex;
2371 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2378 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2379 stringIndexes.append(textPosition + currentClusterIndex);
2382 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2383 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2384 glyphs.append(glyphIndex);
2387 QPointF position = positionsArray.at(i).toPointF() + pos;
2388 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2389 positions.append(position);
2392 maxY = minY = position.y();
2394 minY = qMin(minY, position.y());
2395 maxY = qMax(maxY, position.y());
2399 qreal height = maxY + fontHeight - minY;
2401 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2402 glyphRun.setGlyphIndexes(glyphs);
2403 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2404 glyphRun.setPositions(positions);
2405 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2406 glyphRun.setStringIndexes(stringIndexes);
2407 if (retrievalFlags & QTextLayout::RetrieveString)
2408 glyphRun.setSourceString(text);
2409 glyphRun.setFlags(flags);
2410 glyphRun.setRawFont(font);
2412 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2413 selectionWidth.toReal(), height));
2418# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2441 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2464 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2466 const QScriptLine &line = eng->lines.at(index);
2468 if (line.length == 0)
2469 return QList<QGlyphRun>();
2475 length = textLength();
2478 return QList<QGlyphRun>();
2480 QTextLayout::FormatRange selection;
2481 selection.start = from;
2482 selection.length = length;
2484 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2485 qreal y = line.y.toReal() + line.base().toReal();
2486 QList<QGlyphRun> glyphRuns;
2487 while (!iterator.atEnd()) {
2488 QScriptItem &si = iterator.next();
2489 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2492 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2495 QPointF pos(iterator.x.toReal(), y);
2498 QGlyphRun::GlyphRunFlags flags;
2499 if (!eng->useRawFont) {
2500 font = eng->font(si);
2501 if (font.overline())
2502 flags |= QGlyphRun::Overline;
2503 if (font.underline())
2504 flags |= QGlyphRun::Underline;
2505 if (font.strikeOut())
2506 flags |= QGlyphRun::StrikeOut;
2510 if (si.analysis.bidiLevel % 2) {
2511 flags |= QGlyphRun::RightToLeft;
2515 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2516 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2518 unsigned short *logClusters = eng->logClusters(&si);
2519 int glyphsStart = logClusters[relativeFrom];
2520 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2522 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2523 if (nextGlyphIndex - 1 > glyphsEnd)
2524 glyphsEnd = nextGlyphIndex - 1;
2525 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2526 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2528 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2529 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2531 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2536 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2537 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2538 if (!glyphLayout.attributes[i].dontPrint) {
2539 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2540 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2543 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2544 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2545 if (!glyphLayout.attributes[i].dontPrint) {
2546 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2547 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2552 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2556 iterator.getSelectionBounds(&x, &width);
2558 if (glyphLayout.numGlyphs > 0) {
2559 QFontEngine *mainFontEngine;
2560#ifndef QT_NO_RAWFONT
2561 if (eng->useRawFont && eng->rawFont.isValid())
2562 mainFontEngine= eng->fontEngine(si);
2565 mainFontEngine = font.d->engineForScript(si.analysis.script);
2567 if (mainFontEngine->type() == QFontEngine::Multi) {
2568 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2569 int start = rtl ? glyphLayout.numGlyphs : 0;
2570 int end = start - 1;
2571 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2572 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2573 rtl ? --start : ++end) {
2574 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2578 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2579 multiFontEngine->ensureEngineAt(which);
2581 QGlyphRun::GlyphRunFlags subFlags = flags;
2582 if (start == 0 && startsInsideLigature)
2583 subFlags |= QGlyphRun::SplitLigature;
2586 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2594 glyphsStart + start,
2596 logClusters + relativeFrom,
2597 relativeFrom + si.position,
2598 relativeTo - relativeFrom + 1);
2599 if (!glyphRun.isEmpty())
2600 glyphRuns.append(glyphRun);
2602 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2603 if (!subLayout.attributes[i].dontPrint) {
2604 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2605 pos.rx() += (subLayout.advances[i] + justification).toReal();
2616 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2617 multiFontEngine->ensureEngineAt(which);
2619 QGlyphRun::GlyphRunFlags subFlags = flags;
2620 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2621 subFlags |= QGlyphRun::SplitLigature;
2623 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2631 glyphsStart + start,
2633 logClusters + relativeFrom,
2634 relativeFrom + si.position,
2635 relativeTo - relativeFrom + 1);
2636 if (!glyphRun.isEmpty())
2637 glyphRuns.append(glyphRun);
2639 if (startsInsideLigature || endsInsideLigature)
2640 flags |= QGlyphRun::SplitLigature;
2641 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2651 logClusters + relativeFrom,
2652 relativeFrom + si.position,
2653 relativeTo - relativeFrom + 1);
2654 if (!glyphRun.isEmpty())
2655 glyphRuns.append(glyphRun);
2665
2666
2667
2668
2669void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2671 draw_internal(painter, position,
nullptr);
2674void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2675 const QTextLayout::FormatRange *selection)
const
2677#ifndef QT_NO_RAWFONT
2679 Q_ASSERT(!eng->useRawFont);
2681 const QScriptLine &line = eng->lines[index];
2683 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2687 && selection->start <= line.from
2688 && selection->start + selection->length > line.from) {
2690 const qreal lineHeight = line.height().toReal();
2691 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2692 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2693 drawBackground(p, selection->format, r);
2698 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2699 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2701 if (Q_LIKELY(!xlateToFixedRange))
2704 p->translate(origPos);
2707 QFixed lineBase = line.base();
2708 eng->clearDecorations();
2709 eng->enableDelayDecorations();
2711 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2713 const QTextFormatCollection *formatCollection = eng->formatCollection();
2715 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2717 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2719 format.merge(eng->format(si));
2721 if (suppressColors) {
2722 format.clearForeground();
2723 format.clearBackground();
2724 format.clearProperty(QTextFormat::TextUnderlineColor);
2727 format.merge(selection->format);
2731 QTextLineItemIterator iterator(eng, index, pos, selection);
2732 while (!iterator.atEnd()) {
2733 QScriptItem &si = iterator.next();
2735 if (eng->hasFormats() || selection || formatCollection) {
2736 QTextCharFormat format;
2737 if (formatCollection !=
nullptr)
2738 format = formatCollection->defaultTextFormat();
2739 prepareFormat(format, &si);
2740 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2741 iterator.itemWidth.toReal(), line.height().toReal()));
2746 QPen pen = p->pen();
2748 QTextLineItemIterator iterator(eng, index, pos, selection);
2749 while (!iterator.atEnd()) {
2750 QScriptItem &si = iterator.next();
2752 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2755 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2756 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2759 QFixed itemBaseLine = y;
2760 QFont f = eng->font(si);
2761 QTextCharFormat format;
2762 if (formatCollection !=
nullptr)
2763 format = formatCollection->defaultTextFormat();
2765 if (eng->hasFormats() || selection || formatCollection) {
2766 prepareFormat(format, &si);
2767 setPen(p, pen, format);
2769 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2770 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2771 if (valign == QTextCharFormat::AlignSuperScript
2772 || valign == QTextCharFormat::AlignSubScript
2773 || !qFuzzyIsNull(baseLineOffset))
2775 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2776 QFixed height = fe->ascent() + fe->descent();
2777 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2779 if (valign == QTextCharFormat::AlignSubScript)
2780 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2781 else if (valign == QTextCharFormat::AlignSuperScript)
2782 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2786 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2788 if (eng->hasFormats()) {
2790 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2791 QFixed itemY = y - si.ascent;
2792 switch (format.verticalAlignment()) {
2793 case QTextCharFormat::AlignTop:
2794 itemY = y - lineBase;
2796 case QTextCharFormat::AlignMiddle:
2797 itemY = y - lineBase + (line.height() - si.height()) / 2;
2799 case QTextCharFormat::AlignBottom:
2800 itemY = y - lineBase + line.height() - si.height();
2806 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2808 eng->docLayout()->drawInlineObject(p, itemRect,
2809 QTextInlineObject(iterator.item, eng),
2810 si.position + eng->block.position(),
2814 if (bg.style() != Qt::NoBrush) {
2815 QColor c = bg.color();
2817 p->fillRect(itemRect, c);
2821 QFont f = eng->font(si);
2822 QTextItemInt gf(si, &f, format);
2825 gf.width = iterator.itemWidth;
2826 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2827 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2828 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2829 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2830 qreal x = iterator.itemWidth.toReal() - w;
2832 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2833 iterator.itemWidth.toReal(), line.height().toReal()),
2838 p->drawText(QPointF(iterator.x.toReal() + x,
2839 y.toReal()), visualTab);
2849 unsigned short *logClusters = eng->logClusters(&si);
2850 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2852 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2853 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2854 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2855 gf.logClusters = logClusters + iterator.itemStart - si.position;
2856 gf.width = iterator.itemWidth;
2857 gf.justified = line.justified;
2858 gf.initWithScriptItem(si);
2860 Q_ASSERT(gf.fontEngine);
2862 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2863 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2865 path.setFillRule(Qt::WindingFill);
2867 if (gf.glyphs.numGlyphs)
2868 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2870 const QFontEngine *fe = gf.fontEngine;
2871 const qreal lw = fe->lineThickness().toReal();
2872 if (gf.flags & QTextItem::Underline) {
2873 qreal offs = fe->underlinePosition().toReal();
2874 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2876 if (gf.flags & QTextItem::Overline) {
2877 qreal offs = fe->ascent().toReal() + 1;
2878 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2880 if (gf.flags & QTextItem::StrikeOut) {
2881 qreal offs = fe->ascent().toReal() / 3;
2882 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2887 p->setRenderHint(QPainter::Antialiasing);
2890 if (p->pen().style() == Qt::NoPen)
2891 p->setBrush(Qt::NoBrush);
2893 p->setBrush(p->pen().brush());
2895 p->setPen(format.textOutline());
2900 gf.glyphs.numGlyphs = 0;
2901 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2904 if ((si.analysis.flags == QScriptAnalysis::Space
2905 || si.analysis.flags == QScriptAnalysis::Nbsp)
2906 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2907 QBrush c = format.foreground();
2908 if (c.style() != Qt::NoBrush)
2909 p->setPen(c.color());
2910 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2911 QFont oldFont = p->font();
2912 p->setFont(eng->font(si));
2913 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2915 p->setFont(oldFont);
2919 eng->drawDecorations(p);
2921 if (xlateToFixedRange)
2922 p->translate(-origPos);
2924 if (eng->hasFormats())
2929
2930
2931
2932
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2946 const QScriptLine &line = eng->lines[index];
2947 bool lastLine = index >= eng->lines.size() - 1;
2949 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2951 if (!eng->layoutData)
2953 if (!eng->layoutData->items.size()) {
2954 *cursorPos = line.from;
2958 int lineEnd = line.from + line.length + line.trailingSpaces;
2959 int pos = qBound(line.from, *cursorPos, lineEnd);
2960 const QCharAttributes *attributes = eng->attributes();
2962 *cursorPos = line.from;
2965 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2968 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2970 *cursorPos = line.from;
2973 eng->shapeLine(line);
2975 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2976 if (!scriptItem->num_glyphs)
2979 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2982 int neighborItem = itm;
2983 if (neighborItem > 0 && scriptItem->position == pos)
2985 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2987 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2990 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2992 scriptItem = &eng->layoutData->items[itm];
2993 if (!scriptItem->num_glyphs)
2999 const int l = eng->length(itm);
3000 pos = qBound(0, pos - scriptItem->position, l);
3002 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
3003 unsigned short *logClusters = eng->logClusters(scriptItem);
3004 Q_ASSERT(logClusters);
3006 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
3007 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
3010 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
3014 bool reverse = scriptItem->analysis.bidiLevel % 2;
3019 int firstItem = eng->findItem(line.from);
3020 int lastItem = eng->findItem(lineEnd - 1, itm);
3021 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3023 QVarLengthArray<
int> visualOrder(nItems);
3024 QVarLengthArray<uchar> levels(nItems);
3025 for (
int i = 0; i < nItems; ++i)
3026 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3027 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3029 for (
int i = 0; i < nItems; ++i) {
3030 int item = visualOrder[i]+firstItem;
3033 QScriptItem &si = eng->layoutData->items[item];
3037 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3042 const int itemLength = eng->length(item);
3043 int start = qMax(line.from, si.position);
3044 int end = qMin(lineEnd, si.position + itemLength);
3046 logClusters = eng->logClusters(&si);
3048 int gs = logClusters[start-si.position];
3049 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3051 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3054 x += glyphs.effectiveAdvance(gs);
3059 logClusters = eng->logClusters(scriptItem);
3060 glyphs = eng->shapedGlyphs(scriptItem);
3061 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3062 if (pos == (reverse ? 0 : l))
3063 x += scriptItem->width;
3065 bool rtl = eng->isRightToLeft();
3066 bool visual = eng->visualCursorMovement();
3067 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3069 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3070 int glyph_start = glyph_pos;
3071 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3073 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3074 x += glyphs.effectiveAdvance(i);
3075 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3077 int start = qMax(line.from - scriptItem->position, 0);
3078 int glyph_start = logClusters[start];
3079 int glyph_end = glyph_pos;
3080 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3082 for (
int i = glyph_start; i <= glyph_end; i++)
3083 x += glyphs.effectiveAdvance(i);
3084 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3088 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3089 x = line.x + line.width;
3090 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3093 *cursorPos = pos + scriptItem->position;
3098
3099
3100
3101
3102
3103
3104
3105
3106int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3108 QFixed x = QFixed::fromReal(_x);
3109 const QScriptLine &line = eng->lines[index];
3110 bool lastLine = index >= eng->lines.size() - 1;
3111 int lineNum = index;
3113 if (!eng->layoutData)
3116 int line_length = textLength();
3121 int firstItem = eng->findItem(line.from);
3122 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3123 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3129 x -= eng->alignLine(line);
3132 QVarLengthArray<
int> visualOrder(nItems);
3133 QVarLengthArray<
unsigned char> levels(nItems);
3134 for (
int i = 0; i < nItems; ++i)
3135 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3136 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3138 bool visual = eng->visualCursorMovement();
3141 if (eng->isRightToLeft())
3142 return line.from + line_length;
3144 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3147 bool rtl = eng->isRightToLeft();
3149 eng->shapeLine(line);
3150 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3152 for (
int i = 0; i < nItems; ++i) {
3153 int item = visualOrder[i]+firstItem;
3154 QScriptItem &si = eng->layoutData->items[item];
3155 int item_length = eng->length(item);
3158 int start = qMax(line.from - si.position, 0);
3159 int end = qMin(line.from + line_length - si.position, item_length);
3161 unsigned short *logClusters = eng->logClusters(&si);
3163 int gs = logClusters[start];
3164 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3165 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3167 QFixed item_width = 0;
3168 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3169 item_width = si.width;
3173 item_width += glyphs.effectiveAdvance(g);
3179 if (pos + item_width < x) {
3185 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3186 if (cpos == QTextLine::CursorOnCharacter)
3188 bool left_half = (x - pos) < item_width/2;
3190 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3192 return si.position + 1;
3198 if (cpos == QTextLine::CursorOnCharacter) {
3199 if (si.analysis.bidiLevel % 2) {
3203 if (glyphs.attributes[gs].clusterStart) {
3209 pos -= glyphs.effectiveAdvance(gs);
3215 if (glyphs.attributes[gs].clusterStart) {
3221 pos += glyphs.effectiveAdvance(gs);
3226 QFixed dist = INT_MAX/256;
3227 if (si.analysis.bidiLevel % 2) {
3228 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3231 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3236 pos -= glyphs.effectiveAdvance(gs);
3241 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3246 pos += glyphs.effectiveAdvance(ge);
3251 if (!visual || !rtl || (lastLine && i == 0)) {
3253 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3258 pos += glyphs.effectiveAdvance(gs);
3262 QFixed oldPos = pos;
3264 pos += glyphs.effectiveAdvance(gs);
3265 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3275 if (qAbs(x-pos) < dist) {
3277 if (!rtl && i < nItems - 1) {
3281 if (rtl && nchars > 0)
3282 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3284 return eng->positionInLigature(&si, end, x, pos, -1,
3285 cpos == QTextLine::CursorOnCharacter);
3288 Q_ASSERT(glyph_pos != -1);
3289 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3290 cpos == QTextLine::CursorOnCharacter);
3294 int pos = line.from;
3295 if (!eng->isRightToLeft())
3302 if (index < eng->lines.size() - 1)
3303 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