26#include <private/qpainter_p.h>
30#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1)
31#define SuppressText 0x5012
32#define SuppressBackground 0x513
597 qWarning(
"QTextLayout::beginLayout: Called while already doing layout");
616 qWarning(
"QTextLayout::endLayout: Called without beginLayout()");
655 Q_ASSERT(len <= d->layoutData->string.size());
656 if (!attributes || oldPos < 0 || oldPos >=
len)
661 while (oldPos <
len && !attributes[oldPos].graphemeBoundary)
664 if (oldPos < len && d->atWordSeparator(oldPos)) {
666 while (oldPos < len && d->atWordSeparator(oldPos))
672 while (oldPos <
len && attributes[oldPos].whiteSpace)
691 Q_ASSERT(len <= d->layoutData->string.size());
692 if (!attributes || oldPos <= 0 || oldPos >
len)
697 while (oldPos && !attributes[oldPos].graphemeBoundary)
700 while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
708 while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->
atWordSeparator(oldPos-1))
787 qWarning(
"QTextLayout::createLine: Called without layouting");
798 qWarning(
"QTextLayout: text too long, truncated.");
804 if (l && from >= strlen) {
812 line.justified =
false;
813 line.gridfitted =
false;
887 xmin =
qMin(xmin, si.
x);
888 ymin =
qMin(ymin, si.
y);
890 xmax =
qMax(xmax, si.
x+lineWidth);
894 return QRectF(xmin.
toReal(), ymin.
toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
952 const qreal lineHeight =
line.height().toReal();
954 QFixed lastSelectionX = iterator.x;
955 QFixed lastSelectionWidth;
957 while (!iterator.atEnd()) {
960 QFixed selectionX, selectionWidth;
961 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
962 if (selectionX == lastSelectionX + lastSelectionWidth) {
963 lastSelectionWidth += selectionWidth;
967 if (lastSelectionWidth > 0) {
972 lastSelectionX = selectionX;
973 lastSelectionWidth = selectionWidth;
976 if (lastSelectionWidth > 0) {
988#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1008# if !defined(QT_NO_RAWFONT)
1032#if !defined(QT_NO_RAWFONT)
1035 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
1042 QHash<QPair<QFontEngine *, int>,
QGlyphRun> glyphRunHash;
1049 QRawFont rawFont = glyphRun.rawFont();
1052 QGlyphRun::GlyphRunFlags
flags = glyphRun.flags();
1053 QPair<QFontEngine *, int>
key(fontEngine,
int(
flags));
1056 if (oldGlyphRun.isEmpty()) {
1057 oldGlyphRun = glyphRun;
1060 QList<QPointF>
positions = oldGlyphRun.positions();
1061 QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
1064 indexes += glyphRun.glyphIndexes();
1066 stringIndexes += glyphRun.stringIndexes();
1069 oldGlyphRun.setGlyphIndexes(indexes);
1071 oldGlyphRun.setStringIndexes(stringIndexes);
1078 return glyphRunHash.values();
1097 QFixed clipy = (INT_MIN/256);
1098 QFixed clipe = (INT_MAX/256);
1113 if ((sl.y + sl.height()) < clipy) {
1121 for (
int i = 0;
i < selections.size(); ++
i) {
1133 lineRect.setBottom(
qCeil(lineRect.bottom()));
1136 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1142 const bool selectionStartInLine = sl.from <=
selection.start;
1145 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1155 fullLineRect.setBottom(
qCeil(fullLineRect.bottom()));
1159 if (!selectionEndInLine) {
1161 :
QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1163 if (!selectionStartInLine) {
1165 :
QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1167 }
else if (!selectionEndInLine
1168 && isLastLineInBlock
1171 lineRect.height()/4, lineRect.height()), clip));
1176 const QPen oldPen =
p->pen();
1177 const QBrush oldBrush =
p->brush();
1181 p->drawPath(region);
1184 p->setBrush(oldBrush);
1192 if (hasBackground) {
1202 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1215 textDoneRegion += region;
1218 textDoneRegion -= region;
1221 excludedRegion += region;
1224 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1225 if (!needsTextButNoBackground.isEmpty()){
1239 if (!excludedRegion.isEmpty()) {
1247 path -= excludedRegion;
1251 for (
int i = firstLine;
i < lastLine; ++
i) {
1255 if (!excludedRegion.isEmpty())
1306 QFixed descent = sl.descent;
1309 const int realCursorPosition = cursorPosition;
1311 if (cursorPosition == sl.from + sl.length)
1316 int itm = d->
findItem(cursorPosition);
1323 int neighborItem = itm;
1324 if (neighborItem > 0 && si->
position == realCursorPosition)
1326 else if (neighborItem < d->layoutData->items.size() - 1 && si->
position + si->
num_glyphs == realCursorPosition)
1328 const bool onBoundary = neighborItem != itm
1347 if (toggleAntialiasing)
1358 const QPen origPen =
p->pen();
1366 p->setCompositionMode(origCompositionMode);
1367 if (toggleAntialiasing)
1370 const int arrow_extent = 4;
1371 int sign = rightToLeft ? -1 : 1;
1372 p->drawLine(
QLineF(
x,
y,
x + (
sign * arrow_extent/2),
y + arrow_extent/2));
1373 p->drawLine(
QLineF(
x,
y+arrow_extent,
x + (
sign * arrow_extent/2),
y + arrow_extent/2));
1447 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1462 return QRectF(
x.toReal(), sl.y.toReal(),
width.toReal(), sl.height().toReal());
1611 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1625 layout_helper(INT_MAX);
1641 layout_helper(numColumns);
1658 layout_helper(numColumns);
1662#define LB_DEBUG qDebug
1664#define LB_DEBUG if (0) qDebug
1669 struct LineBreakHelper
1671 LineBreakHelper() =
default;
1680 int currentPosition = 0;
1682 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1685 QFixed currentSoftHyphenWidth;
1686 QFixed commitedSoftHyphenWidth;
1688 QFixed minimumRightBearing;
1690 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1691 const unsigned short *logClusters =
nullptr;
1693 bool manualWrap =
false;
1694 bool whiteSpaceOrObject =
true;
1700 + (
line.textWidth > 0 ? currentSoftHyphenWidth :
QFixed()) + negativeRightBearing();
1703 inline glyph_t currentGlyph()
const
1708 return glyphs.
glyphs[logClusters[currentPosition - 1]];
1711 inline void saveCurrentGlyph()
1714 if (currentPosition > 0 &&
1715 logClusters[currentPosition - 1] < glyphs.
numGlyphs) {
1716 previousGlyph = currentGlyph();
1717 previousGlyphFontEngine = fontEngine;
1725 engine->getGlyphBearings(glyph,
nullptr, &rb);
1734 inline void calculateRightBearing()
1736 if (currentPosition <= 0)
1738 calculateRightBearing(fontEngine.data(), currentGlyph());
1741 inline void calculateRightBearingForPreviousGlyph()
1743 if (previousGlyph > 0)
1744 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1747 static const QFixed RightBearingNotCalculated;
1749 inline void resetRightBearing()
1751 rightBearing = RightBearingNotCalculated;
1756 inline QFixed negativeRightBearing()
const
1758 if (rightBearing == RightBearingNotCalculated)
1761 return qAbs(rightBearing);
1765Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated =
QFixed(1);
1767inline bool LineBreakHelper::checkFullOtherwiseExtend(
QScriptLine &
line)
1772 if (
line.
length && !manualWrap && (newWidth >
line.width || glyphCount > maxGlyphs))
1785 if (oldTextWidth !=
line.textWidth || currentSoftHyphenWidth > 0) {
1786 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1787 currentSoftHyphenWidth = 0;
1797 const QScriptItem ¤t,
const unsigned short *logClusters,
1800 int glyphPosition = logClusters[
pos];
1804 }
while (
pos <
end && logClusters[
pos] == glyphPosition);
1815 *clusterWidth += (
line.textWidth - clusterWid);
1821void QTextLine::layout_helper(
int maxGlyphs)
1825 line.trailingSpaces = 0;
1827 line.hasTrailingSpaces =
false;
1829 if (!eng->layoutData->items.size() ||
line.
from >= eng->layoutData->string.size()) {
1830 line.setDefaultHeight(eng);
1836 LineBreakHelper lbh;
1838 lbh.maxGlyphs = maxGlyphs;
1846 int newItem = eng->findItem(
line.
from);
1849 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f",
line.
from, newItem,
int(eng->layoutData->items.size()),
line.width.toReal());
1851 Qt::Alignment
alignment = eng->option.alignment();
1858 lbh.logClusters = eng->layoutData->logClustersPtr;
1859 lbh.previousGlyph = 0;
1861 bool manuallyWrapped =
false;
1862 bool hasInlineObject =
false;
1863 QFixed maxInlineObjectHeight = 0;
1865 while (newItem < eng->layoutData->items.size()) {
1866 lbh.resetRightBearing();
1867 if (newItem !=
item) {
1872 attributes = eng->attributes();
1875 lbh.logClusters = eng->layoutData->logClustersPtr;
1879 lbh.glyphs = eng->shapedGlyphs(¤t);
1880 QFontEngine *fontEngine = eng->fontEngine(current);
1881 if (lbh.fontEngine != fontEngine) {
1882 lbh.fontEngine = fontEngine;
1889 lbh.tmpData.
leading =
qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1894 lbh.tmpData.ascent =
qMax(lbh.tmpData.ascent, current.
ascent);
1895 lbh.tmpData.descent =
qMax(lbh.tmpData.descent, current.
descent);
1899 lbh.whiteSpaceOrObject =
true;
1900 if (lbh.checkFullOtherwiseExtend(
line))
1903 QFixed x =
line.x +
line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1904 QFixed tabWidth = eng->calculateTabWidth(
item,
x);
1905 attributes = eng->attributes();
1908 lbh.logClusters = eng->layoutData->logClustersPtr;
1909 lbh.glyphs = eng->shapedGlyphs(¤t);
1911 lbh.spaceData.textWidth += tabWidth;
1912 lbh.spaceData.length++;
1915 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1916 lbh.glyphCount +=
qRound(tabWidth / averageCharWidth);
1918 if (lbh.checkFullOtherwiseExtend(
line))
1921 lbh.whiteSpaceOrObject =
true;
1925 line.setDefaultHeight(eng);
1927 if (lbh.checkFullOtherwiseExtend(
line))
1931 current, lbh.logClusters, lbh.glyphs);
1933 lbh.tmpData.length++;
1934 lbh.calculateRightBearingForPreviousGlyph();
1936 line += lbh.tmpData;
1937 manuallyWrapped =
true;
1940 lbh.whiteSpaceOrObject =
true;
1946 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.
position,
f);
1949 lbh.tmpData.ascent =
qMax(lbh.tmpData.ascent, current.
ascent);
1950 lbh.tmpData.descent =
qMax(lbh.tmpData.descent, current.
descent);
1954 hasInlineObject =
true;
1955 maxInlineObjectHeight =
qMax(maxInlineObjectHeight, current.
ascent + current.
descent);
1957 lbh.tmpData.textWidth += current.
width;
1961 if (lbh.checkFullOtherwiseExtend(
line))
1963 }
else if (attributes[lbh.currentPosition].whiteSpace
1964 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1965 lbh.whiteSpaceOrObject =
true;
1966 while (lbh.currentPosition <
end
1967 && attributes[lbh.currentPosition].whiteSpace
1968 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1970 current, lbh.logClusters, lbh.glyphs);
1973 if (!lbh.manualWrap && lbh.spaceData.textWidth >
line.width)
1976 lbh.whiteSpaceOrObject =
false;
1977 bool sb_or_ws =
false;
1978 lbh.saveCurrentGlyph();
1979 QFixed accumulatedTextWidth;
1982 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
1989 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
1990 && attributes[lbh.currentPosition].
whiteSpace
1991 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
1993 if (lbh.currentPosition >= eng->layoutData->string.size()
1995 || attributes[lbh.currentPosition].lineBreak
1999 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2000 if (breakWordOrAny) {
2001 lbh.minw =
qMax(accumulatedTextWidth, lbh.minw);
2002 accumulatedTextWidth = 0;
2007 }
while (lbh.currentPosition <
end);
2008 lbh.minw =
qMax(accumulatedTextWidth, lbh.minw);
2010 if (lbh.currentPosition > 0 && lbh.currentPosition <=
end
2011 && (lbh.currentPosition ==
end || attributes[lbh.currentPosition].lineBreak)
2012 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2029 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2032 if (sb_or_ws|breakany) {
2040 QFixed previousRightBearing = lbh.rightBearing;
2051 if ((lbh.calculateNewWidth(
line) +
qAbs(lbh.minimumRightBearing)) >
line.width)
2052 lbh.calculateRightBearing();
2054 if (lbh.checkFullOtherwiseExtend(
line)) {
2059 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2060 lbh.rightBearing = previousRightBearing;
2062 lbh.calculateRightBearingForPreviousGlyph();
2064 line.textWidth += lbh.commitedSoftHyphenWidth;
2069 lbh.saveCurrentGlyph();
2071 if (lbh.currentPosition ==
end)
2075 lbh.checkFullOtherwiseExtend(
line);
2076 line.textWidth += lbh.commitedSoftHyphenWidth;
2081 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2082 lbh.calculateRightBearing();
2085 line.textWidth += lbh.negativeRightBearing();
2088 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2089 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2090 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2091 line += lbh.tmpData;
2096 if (maxInlineObjectHeight >
line.ascent +
line.descent) {
2098 QFixed toAdd = (maxInlineObjectHeight -
line.ascent -
line.descent)/2;
2099 line.ascent += toAdd;
2100 line.descent = maxInlineObjectHeight -
line.ascent;
2102 int startItem = eng->findItem(
line.
from);
2105 endItem = eng->layoutData->items.
size();
2135 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)",
line.
length,
line.ascent.toReal(),
2136 line.descent.toReal(),
line.textWidth.toReal(), lbh.spaceData.width.toReal());
2140 ? lbh.spaceData.textWidth
2143 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2144 || (lbh.maxGlyphs == INT_MAX &&
line.textWidth > (
line.width - trailingSpace))) {
2147 layout_helper(lbh.maxGlyphs);
2153 if (lbh.manualWrap) {
2154 eng->minWidth =
qMax(eng->minWidth,
line.textWidth);
2155 eng->maxWidth =
qMax(eng->maxWidth,
line.textWidth);
2157 eng->minWidth =
qMax(eng->minWidth, lbh.minw);
2158 if (
qAddOverflow(eng->layoutData->currentMaxWidth,
line.textWidth, &eng->layoutData->currentMaxWidth))
2159 eng->layoutData->currentMaxWidth =
QFIXED_MAX;
2160 if (!manuallyWrapped) {
2161 if (
qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2162 eng->layoutData->currentMaxWidth =
QFIXED_MAX;
2164 eng->maxWidth =
qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2165 if (manuallyWrapped)
2166 eng->layoutData->currentMaxWidth = 0;
2169 line.textWidth += trailingSpace;
2170 if (lbh.spaceData.length) {
2172 line.hasTrailingSpaces =
true;
2175 line.justified =
false;
2176 line.gridfitted =
false;
2193 return QPointF(eng->lines.at(
index).x.toReal(), eng->lines.at(
index).y.toReal());
2209 return eng->lines.at(
index).from;
2220 && eng->block.isValid() &&
index == eng->lines.size()-1) {
2221 return eng->lines.at(
index).length - 1;
2223 return eng->lines.at(
index).length + eng->lines.at(
index).trailingSpaces;
2228 QBrush bg = chf.background();
2230 p->fillRect(
r.toAlignedRect(), bg);
2237 p->setPen(defaultPen);
2242#if !defined(QT_NO_RAWFONT)
2247 const QGlyphRun::GlyphRunFlags &
flags,
2248 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2253 unsigned short *logClusters,
2263 int rangeStart = textPosition;
2264 int logClusterIndex = 0;
2265 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2270 int rangeEnd = rangeStart;
2271 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2276 d->textRangeStart = rangeStart;
2277 d->textRangeEnd = rangeEnd;
2282 fontD->setFontEngine(fontEngine);
2284 QVarLengthArray<glyph_t> glyphsArray;
2285 QVarLengthArray<QFixedPoint> positionsArray;
2287 QTextItem::RenderFlags renderFlags;
2299 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2304 QList<quint32> glyphs;
2306 glyphs.reserve(glyphsArray.size());
2310 QList<qsizetype> stringIndexes;
2312 stringIndexes.reserve(glyphsArray.size());
2314 int nextClusterIndex = 0;
2315 int currentClusterIndex = 0;
2316 for (
int i = 0;
i < glyphsArray.size(); ++
i) {
2317 const int glyphArrayIndex =
i + glyphsStart;
2321 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2322 currentClusterIndex = nextClusterIndex;
2323 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2330 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2331 stringIndexes.append(textPosition + currentClusterIndex);
2335 glyph_t glyphIndex = glyphsArray.at(
i) & 0xffffff;
2336 glyphs.append(glyphIndex);
2365 selectionWidth.toReal(),
height));
2370# if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
2416 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2421 return QList<QGlyphRun>();
2430 return QList<QGlyphRun>();
2438 QList<QGlyphRun> glyphRuns;
2439 while (!iterator.atEnd()) {
2444 if (from >= 0 &&
length >= 0 && (from >= iterator.itemEnd || from +
length <= iterator.itemStart))
2450 QGlyphRun::GlyphRunFlags
flags;
2451 if (!eng->useRawFont) {
2452 font = eng->font(si);
2467 int relativeFrom =
qMax(iterator.itemStart, from) - si.
position;
2470 unsigned short *logClusters = eng->logClusters(&si);
2471 int glyphsStart = logClusters[relativeFrom];
2472 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.
num_glyphs - 1 : logClusters[relativeTo];
2474 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.
num_glyphs;
2475 if (nextGlyphIndex - 1 > glyphsEnd)
2476 glyphsEnd = nextGlyphIndex - 1;
2477 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2478 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2480 int itemGlyphsStart = logClusters[iterator.itemStart - si.
position];
2481 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.
position];
2488 if (relativeFrom != (iterator.itemStart - si.
position) && !rtl) {
2489 for (
int i = itemGlyphsStart;
i < glyphsStart; ++
i) {
2492 pos.rx() += (glyphLayout.
advances[
i] + justification).toReal();
2495 }
else if (relativeTo != (iterator.itemEnd - si.
position - 1) && rtl) {
2496 for (
int i = itemGlyphsEnd;
i > glyphsEnd; --
i) {
2499 pos.rx() += (glyphLayout.
advances[
i] + justification).toReal();
2504 glyphLayout = glyphLayout.
mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2508 iterator.getSelectionBounds(&
x, &
width);
2512#ifndef QT_NO_RAWFONT
2513 if (eng->useRawFont && eng->rawFont.isValid())
2514 mainFontEngine= eng->fontEngine(si);
2526 const int e = glyphLayout.
glyphs[rtl ?
start - 1 :
end + 1] >> 24;
2531 multiFontEngine->ensureEngineAt(which);
2533 QGlyphRun::GlyphRunFlags subFlags =
flags;
2534 if (
start == 0 && startsInsideLigature)
2546 glyphsStart +
start,
2548 logClusters + relativeFrom,
2550 relativeTo - relativeFrom + 1);
2552 glyphRuns.append(glyphRun);
2557 pos.rx() += (subLayout.
advances[
i] + justification).toReal();
2569 multiFontEngine->ensureEngineAt(which);
2571 QGlyphRun::GlyphRunFlags subFlags =
flags;
2572 if ((
start == 0 && startsInsideLigature) || endsInsideLigature)
2583 glyphsStart +
start,
2585 logClusters + relativeFrom,
2587 relativeTo - relativeFrom + 1);
2589 glyphRuns.append(glyphRun);
2591 if (startsInsideLigature || endsInsideLigature)
2603 logClusters + relativeFrom,
2605 relativeTo - relativeFrom + 1);
2607 glyphRuns.append(glyphRun);
2629#ifndef QT_NO_RAWFONT
2642 const qreal lineHeight =
line.height().toReal();
2643 QRectF r(origPos.x() +
line.x.toReal(), origPos.y() +
line.y.toReal(),
2651 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2656 p->translate(origPos);
2660 eng->clearDecorations();
2661 eng->enableDelayDecorations();
2671 format.merge(eng->format(si));
2673 if (suppressColors) {
2674 format.clearForeground();
2675 format.clearBackground();
2687 if (eng->hasFormats() ||
selection || formatCollection) {
2689 if (formatCollection !=
nullptr)
2691 prepareFormat(
format, &si);
2698 QPen pen =
p->pen();
2714 if (formatCollection !=
nullptr)
2717 if (eng->hasFormats() ||
selection || formatCollection) {
2718 prepareFormat(
format, &si);
2740 if (eng->hasFormats()) {
2744 switch (
format.verticalAlignment()) {
2746 itemY =
y - lineBase;
2749 itemY =
y - lineBase + (
line.height() - si.
height()) / 2;
2752 itemY =
y - lineBase +
line.height() - si.
height();
2760 eng->docLayout()->drawInlineObject(
p, itemRect,
2762 si.
position + eng->block.position(),
2769 p->fillRect(itemRect,
c);
2780 const QChar visualTab =
QChar(QChar::VisualTabCharacter);
2791 y.toReal()), visualTab);
2801 unsigned short *logClusters = eng->logClusters(&si);
2805 &
f, eng->layoutData->string.unicode() +
iterator.itemStart,
2809 gf.justified =
line.justified;
2810 gf.initWithScriptItem(si);
2819 if (gf.glyphs.numGlyphs)
2820 gf.fontEngine->addOutlineToPath(
pos.x(),
pos.y(), gf.glyphs, &
path, gf.flags);
2825 qreal offs = fe->underlinePosition().toReal();
2826 path.addRect(
pos.x(),
pos.y() + offs, gf.width.toReal(), lw);
2829 qreal offs = fe->ascent().toReal() + 1;
2830 path.addRect(
pos.x(),
pos.y() - offs, gf.width.toReal(), lw);
2833 qreal offs = fe->ascent().toReal() / 3;
2834 path.addRect(
pos.x(),
pos.y() - offs, gf.width.toReal(), lw);
2845 p->setBrush(
p->pen().brush());
2847 p->setPen(
format.textOutline());
2852 gf.glyphs.numGlyphs = 0;
2861 p->setPen(
c.color());
2863 QFont oldFont =
p->font();
2864 p->setFont(eng->font(si));
2865 p->drawText(
QPointF(
iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2867 p->setFont(oldFont);
2871 eng->drawDecorations(
p);
2873 if (xlateToFixedRange)
2874 p->translate(-origPos);
2876 if (eng->hasFormats())
2899 bool lastLine =
index >= eng->lines.
size() - 1;
2903 if (!eng->layoutData)
2905 if (!eng->layoutData->items.size()) {
2917 while (
pos < lineEnd && !attributes[
pos].graphemeBoundary)
2920 int itm =
pos == lineEnd ? eng->findItem(
pos-1) : eng->findItem(
pos);
2925 eng->shapeLine(
line);
2927 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2928 if (!scriptItem->num_glyphs)
2931 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2934 int neighborItem = itm;
2935 if (neighborItem > 0 && scriptItem->position ==
pos)
2937 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs ==
pos)
2939 const bool onBoundary = neighborItem != itm && scriptItem->
analysis.
bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2942 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2944 scriptItem = &eng->layoutData->items[itm];
2945 if (!scriptItem->num_glyphs)
2951 const int l = eng->length(itm);
2955 unsigned short *logClusters = eng->logClusters(scriptItem);
2958 int glyph_pos =
pos == l ? scriptItem->num_glyphs : logClusters[
pos];
2959 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
2966 bool reverse = scriptItem->analysis.bidiLevel % 2;
2971 int firstItem = eng->findItem(
line.
from);
2972 int lastItem = eng->findItem(lineEnd - 1, itm);
2973 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2975 QVarLengthArray<int> visualOrder(nItems);
2976 QVarLengthArray<uchar>
levels(nItems);
2977 for (
int i = 0;
i < nItems; ++
i)
2978 levels[
i] = eng->layoutData->items[
i+firstItem].analysis.bidiLevel;
2981 for (
int i = 0;
i < nItems; ++
i) {
2982 int item = visualOrder[
i]+firstItem;
2994 const int itemLength = eng->length(
item);
2998 logClusters = eng->logClusters(&si);
3011 logClusters = eng->logClusters(scriptItem);
3012 glyphs = eng->shapedGlyphs(scriptItem);
3014 if (
pos == (reverse ? 0 : l))
3015 x += scriptItem->width;
3017 bool rtl = eng->isRightToLeft();
3018 bool visual = eng->visualCursorMovement();
3019 int end =
qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3021 int glyph_end =
end == l ? scriptItem->num_glyphs : logClusters[
end];
3022 int glyph_start = glyph_pos;
3023 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3025 for (
int i = glyph_end - 1;
i >= glyph_start;
i--)
3027 x -= eng->offsetInLigature(scriptItem,
pos,
end, glyph_pos);
3030 int glyph_start = logClusters[
start];
3031 int glyph_end = glyph_pos;
3032 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3034 for (
int i = glyph_start;
i <= glyph_end;
i++)
3036 x += eng->offsetInLigature(scriptItem,
pos,
end, glyph_pos);
3045 *cursorPos =
pos + scriptItem->position;
3062 bool lastLine =
index >= eng->lines.
size() - 1;
3063 int lineNum =
index;
3065 if (!eng->layoutData)
3068 int line_length = textLength();
3073 int firstItem = eng->findItem(
line.
from);
3074 int lastItem = eng->findItem(
line.
from + line_length - 1, firstItem);
3075 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3081 x -= eng->alignLine(
line);
3084 QVarLengthArray<int> visualOrder(nItems);
3085 QVarLengthArray<unsigned char>
levels(nItems);
3086 for (
int i = 0;
i < nItems; ++
i)
3087 levels[
i] = eng->layoutData->items[
i+firstItem].analysis.bidiLevel;
3090 bool visual = eng->visualCursorMovement();
3093 if (eng->isRightToLeft())
3096 }
else if (
x <
line.textWidth || (
line.justified &&
x <
line.width)) {
3099 bool rtl = eng->isRightToLeft();
3101 eng->shapeLine(
line);
3102 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<int>();
3104 for (
int i = 0;
i < nItems; ++
i) {
3105 int item = visualOrder[
i]+firstItem;
3107 int item_length = eng->length(
item);
3113 unsigned short *logClusters = eng->logClusters(&si);
3115 int gs = logClusters[
start];
3121 item_width = si.
width;
3131 if (
pos + item_width <
x) {
3140 bool left_half = (
x -
pos) < item_width/2;
3180 if (!visual || rtl || (lastLine &&
i == nItems - 1)) {
3203 if (!visual || !rtl || (lastLine &&
i == 0)) {
3229 if (!rtl &&
i < nItems - 1) {
3233 if (rtl && nchars > 0)
3234 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3236 return eng->positionInLigature(&si,
end,
x,
pos, -1,
3241 return eng->positionInLigature(&si,
end,
x, edge, glyph_pos,
3247 if (!eng->isRightToLeft())
3254 if (index < eng->lines.size() - 1)
const QColor & color() const
Returns the brush color.
Qt::BrushStyle style() const
Returns the brush style.
The QColor class provides colors based on RGB, HSV or CMYK values.
void setAlpha(int alpha)
Sets the alpha of this color to alpha.
virtual qreal minRightBearing() const
void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, QVarLengthArray< glyph_t > &glyphs_out, QVarLengthArray< QFixedPoint > &positions)
virtual QFixed lineThickness() const
\reentrant \inmodule QtGui
int horizontalAdvance(const QString &, int len=-1) const
Returns the horizontal advance in pixels of the first len characters of text.
QFontEngine * engineForScript(int script) const
bool strikeOut() const
Returns true if strikeout has been set; otherwise returns false.
bool underline() const
Returns true if underline has been set; otherwise returns false.
bool overline() const
Returns true if overline has been set; otherwise returns false.
static QGlyphRunPrivate * get(const QGlyphRun &glyphRun)
The QGlyphRun class provides direct access to the internal glyphs in a font.
void setSourceString(const QString &sourceString)
void setStringIndexes(const QList< qsizetype > &stringIndexes)
void setPositions(const QList< QPointF > &positions)
Sets the positions of the edge of the baseline for each glyph in this set of glyph indexes to positio...
void setRawFont(const QRawFont &rawFont)
Sets the font in which to look up the glyph indexes to the rawFont specified.
QList< quint32 > glyphIndexes() const
Returns the glyph indexes for this QGlyphRun object.
void setBoundingRect(const QRectF &boundingRect)
Sets the bounding rect of the glyphs in this QGlyphRun to be boundingRect.
void setFlags(GlyphRunFlags flags)
Sets the flags of this QGlyphRun to flags.
void setGlyphIndexes(const QList< quint32 > &glyphIndexes)
Set the glyph indexes for this QGlyphRun object to glyphIndexes.
bool isEmpty() const
Returns true if the QGlyphRun does not contain any glyphs.
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
qsizetype size() const noexcept
bool isEmpty() const noexcept
qsizetype length() const noexcept
const_reference at(qsizetype i) const noexcept
void append(parameter_type t)
void addRect(const QRectF &rect)
Adds the given rectangle to this path as a closed subpath.
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule of the painter path to the given fillRule.
static QPainterPrivate * get(QPainter *painter)
The QPainter class performs low-level painting on widgets and other paint devices.
CompositionMode
Defines the modes supported for digital image compositing.
@ RasterOp_NotDestination
void setCosmetic(bool cosmetic)
Sets this pen to cosmetic or non-cosmetic, depending on the value of cosmetic.
\inmodule QtCore\reentrant
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
static QRawFontPrivate * get(const QRawFont &font)
The QRawFont class provides access to a single physical instance of a font.
\inmodule QtCore\reentrant
constexpr void setRight(qreal pos) noexcept
Sets the right edge of the rectangle to the given finite x coordinate.
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
constexpr qreal height() const noexcept
Returns the height of the rectangle.
constexpr QRectF translated(qreal dx, qreal dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
QRectF intersected(const QRectF &other) const noexcept
QRectF united(const QRectF &other) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isNull() const
Returns true if this string is null; otherwise returns false.
qsizetype size() const noexcept
Returns the number of characters in this string.
static QString static QString qsizetype from
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
qsizetype length() const noexcept
Returns the number of characters in this string.
int length() const
Returns the length of the block in characters.
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
int position() const
Returns the index of the block's first character within the document.
VerticalAlignment
This enum describes the ways that adjacent characters can be vertically aligned.
VerticalAlignment verticalAlignment() const
Returns the vertical alignment used for characters with this format.
static const QTextDocumentPrivate * get(const QTextDocument *document)
QList< QTextLayout::FormatRange > formats() const
void setPreeditArea(int position, const QString &text)
int lineNumberForTextPosition(int pos)
bool isRightToLeft() const
int preeditAreaPosition() const
int findItem(int strPos, int firstItem=0) const
void resetFontEngineCache()
const QCharAttributes * attributes() const
QFixed leadingSpaceWidth(const QScriptLine &line)
QFixed alignLine(const QScriptLine &line)
QString preeditAreaText() const
int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op)
int formatIndex(const QScriptItem *si) const
bool visualCursorMovement() const
bool atWordSeparator(int position) const
void setFormats(const QList< QTextLayout::FormatRange > &formats)
QTextCharFormat format(const QScriptItem *si) const
static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder)
QFont font(const QScriptItem &si) const
QTextCharFormat defaultTextFormat() const
QTextCharFormat toCharFormat() const
Returns this format as a character format.
QTextFormat format() const
Returns format of the inline object within the text layout.
void setDescent(qreal d)
Sets the inline object's descent to d.
void setAscent(qreal a)
Sets the inline object's ascent to a.
qreal ascent() const
Returns the inline object's ascent.
qreal width() const
Returns the inline object's width.
qreal height() const
Returns the inline object's total height.
int formatIndex() const
Returns an integer describing the format of the inline object within the text layout.
QRectF rect() const
Returns the inline object's rectangle.
void setWidth(qreal w)
Sets the inline object's width to w.
Qt::LayoutDirection textDirection() const
Returns if the object should be laid out right-to-left or left-to-right.
qreal descent() const
Returns the inline object's descent.
int textPosition() const
The position of the inline object within the text layout.
const QTextOption & textOption() const
Returns the current text option used to control the layout process.
int rightCursorPosition(int oldPos) const
Returns the cursor position to the right of oldPos, next to it.
QTextLine lineForTextPosition(int pos) const
Returns the line that contains the cursor position specified by pos.
void setFont(const QFont &f)
Sets the layout's font to the given font.
QTextLine createLine()
Returns a new text line to be laid out if there is text to be inserted into the layout; otherwise ret...
void beginLayout()
Begins the layout process.
qreal minimumWidth() const
The minimum width the layout needs.
void setPosition(const QPointF &p)
Moves the text layout to point p.
void setCacheEnabled(bool enable)
Enables caching of the complete layout information if enable is true; otherwise disables layout cachi...
void setFormats(const QList< FormatRange > &overrides)
void setText(const QString &string)
Sets the layout's text to the given string.
QList< QGlyphRun > glyphRuns(int from=-1, int length=-1, GlyphRunRetrievalFlags flags=DefaultRetrievalFlags) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QList< FormatRange > formats() const
QString text() const
Returns the layout's text.
QTextLayout()
Constructs an empty text layout.
void setRawFont(const QRawFont &rawFont)
int previousCursorPosition(int oldPos, CursorMode mode=SkipCharacters) const
Returns the first valid cursor position before oldPos that respects the given cursor mode.
int lineCount() const
Returns the number of lines in this text layout.
int preeditAreaPosition() const
Returns the position of the area in the text layout that will be processed before editing occurs.
qreal maximumWidth() const
The maximum width the layout could expand to; this is essentially the width of the entire text.
QTextLine lineAt(int i) const
Returns the {i}-th line of text in this text layout.
int nextCursorPosition(int oldPos, CursorMode mode=SkipCharacters) const
Returns the next valid cursor position after oldPos that respects the given cursor mode.
void setTextOption(const QTextOption &option)
Sets the text option structure that controls the layout process to the given option.
bool isValidCursorPosition(int pos) const
Returns true if position pos is a valid cursor position.
bool cacheEnabled() const
Returns true if the complete layout information is cached; otherwise returns false.
void draw(QPainter *p, const QPointF &pos, const QList< FormatRange > &selections=QList< FormatRange >(), const QRectF &clip=QRectF()) const
Draws the whole layout on the painter p at the position specified by pos.
Qt::CursorMoveStyle cursorMoveStyle() const
The cursor movement style of this QTextLayout.
QFont font() const
Returns the current font that is used for the layout, or a default font if none is set.
~QTextLayout()
Destructs the layout.
void endLayout()
Ends the layout process.
QString preeditAreaText() const
Returns the text that is inserted in the layout before editing occurs.
void setPreeditArea(int position, const QString &text)
Sets the position and text of the area in the layout that is processed before editing occurs.
void drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setCursorMoveStyle(Qt::CursorMoveStyle style)
Sets the visual cursor movement style to the given style.
QRectF boundingRect() const
The smallest rectangle that contains all the lines in the layout.
CursorMode
\value SkipCharacters \value SkipWords
int leftCursorPosition(int oldPos) const
Returns the cursor position to the left of oldPos, next to it.
qreal descent() const
Returns the line's descent.
int textStart() const
Returns the start of the line from the beginning of the string passed to the QTextLayout.
void setNumColumns(int columns)
Lays out the line.
QList< QGlyphRun > glyphRuns(int from=-1, int length=-1, QTextLayout::GlyphRunRetrievalFlags flags=QTextLayout::DefaultRetrievalFlags) const
Returns the glyph indexes and positions for all glyphs in this QTextLine for characters in the range ...
QRectF naturalTextRect() const
Returns the rectangle covered by the line.
QRectF rect() const
Returns the line's bounding rectangle.
qreal height() const
Returns the line's height.
void draw(QPainter *painter, const QPointF &position) const
Draws a line on the given painter at the specified position.
bool leadingIncluded() const
qreal cursorToX(int *cursorPos, Edge edge=Leading) const
Converts the cursor position cursorPos to the corresponding x position inside the line,...
qreal naturalTextWidth() const
Returns the width of the line that is occupied by text.
void setLeadingIncluded(bool included)
qreal y() const
Returns the line's y position.
qreal width() const
Returns the line's width as specified by the layout() function.
void setPosition(const QPointF &pos)
Moves the line to position pos.
Edge
\value Leading \value Trailing
CursorPosition
\value CursorBetweenCharacters \value CursorOnCharacter
void setLineWidth(qreal width)
Lays out the line with the given width.
qreal ascent() const
Returns the line's ascent.
int xToCursor(qreal x, CursorPosition=CursorBetweenCharacters) const
Converts the x-coordinate x, to the nearest matching cursor position, depending on the cursor positio...
qreal horizontalAdvance() const
qreal x() const
Returns the line's x position.
int textLength() const
Returns the length of the text in the line.
QPointF position() const
Returns the line's position relative to the text layout's position.
void setTextDirection(Qt::LayoutDirection aDirection)
Sets the direction of the text layout defined by the option to the given direction.
void setAlignment(Qt::Alignment alignment)
Sets the option's text alignment to the specified alignment.
Flags flags() const
Returns the flags associated with the option.
@ ShowLineAndParagraphSeparators
WrapMode
This enum describes how text is wrapped in a document.
@ WrapAtWordBoundaryOrAnywhere
typename C::iterator iterator
Combined button and popup list for selecting options.
@ TextJustificationForced
static const QCssKnownValue positions[NumKnownPositionModes - 1]
bool qFuzzyIsNull(qfloat16 f) noexcept
int qRound(qfloat16 d) noexcept
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qBound(const T &min, const T &val, const T &max)
constexpr const T & qMax(const T &a, const T &b)
constexpr T qAbs(const T &t)
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLfloat GLfloat GLfloat GLfloat GLfloat maxY
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum format
GLsizei const GLchar *const * path
GLsizei const GLchar *const * string
[0]
static const QRectF boundingRect(const QPointF *points, int pointCount)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
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
std::uniform_real_distribution dist(1, 2.5)
[2]
rect deviceTransform(view->viewportTransform()).map(QPointF(0
QItemSelection * selection
[0]
static constexpr QFixed fromReal(qreal r)
static constexpr QFixed fromFixed(int fixed)
constexpr QFixed ceil() const
constexpr qreal toReal() const
QGlyphJustification * justifications
QFixed effectiveAdvance(int item) const
QGlyphAttributes * attributes
QGlyphLayout mid(int position, int n=-1) const
@ LineOrParagraphSeparator
constexpr QFixed height() const noexcept
unsigned short num_glyphs
unsigned short trailingSpaces