4#include <QtGui/private/qtguiglobal_p.h>
13#include <QtCore/private/qunicodetables_p.h>
42 ~Itemizer() =
default;
48 generateScriptItemsSmallCaps(
reinterpret_cast<const ushort *
>(m_string.unicode()),
start,
length);
60 enum { MaxItemLength = 4096 };
65 if (m_items.isEmpty())
81 if (m_analysis[
i].bidiLevel == m_analysis[
start].bidiLevel
83 && (m_analysis[
i].script == m_analysis[
start].script || m_string[
i] == u
'.')
85 &&
i -
start < MaxItemLength)
93 void generateScriptItemsCapitalize(
int start,
int length)
100 m_string.constData(), m_string.size(),
103 m_splitter->setPosition(
start);
109 m_splitter->toNextBoundary();
113 bool atWordStart =
false;
115 if (
i == m_splitter->position()) {
121 m_splitter->toNextBoundary();
124 if (m_analysis[
i] == itemAnalysis
127 &&
i -
start < MaxItemLength)
132 itemAnalysis = m_analysis[
start];
144 bool lower = (QChar::category(uc[
start]) == QChar::Letter_Lowercase);
148 bool l = (QChar::category(uc[
i]) == QChar::Letter_Lowercase);
149 if ((m_analysis[
i] == m_analysis[
start])
152 &&
i -
start < MaxItemLength)
169 std::unique_ptr<QTextBoundaryFinder> m_splitter;
181enum { BidiDebugEnabled =
false };
182#define BIDI_DEBUG if (1) ; else qDebug
184enum { BidiDebugEnabled =
true };
185static const char *directions[] = {
186 "DirL",
"DirR",
"DirEN",
"DirES",
"DirET",
"DirAN",
"DirCS",
"DirB",
"DirS",
"DirWS",
"DirON",
187 "DirLRE",
"DirLRO",
"DirAL",
"DirRLE",
"DirRLO",
"DirPDF",
"DirNSM",
"DirBN",
188 "DirLRI",
"DirRLI",
"DirFSI",
"DirPDI"
190#define BIDI_DEBUG qDebug
192 return (
d << directions[
dir]);
196struct QBidiAlgorithm {
197 template<
typename T>
using Vector = QVarLengthArray<T, 64>;
203 baseLevel(baseDirectionIsRtl ? 1 : 0)
213 void initScriptAnalysisAndIsolatePairs(Vector<IsolatePair> &isolatePairs)
215 int isolateStack[128];
216 int isolateLevel = 0;
221 if (QChar::isHighSurrogate(uc) &&
i <
length - 1 && text[
i + 1].isLowSurrogate()) {
223 analysis[
i].bidiDirection = QChar::DirNSM;
224 uc = QChar::surrogateToUcs4(
ushort(uc), text[
i].unicode());
227 analysis[
pos].bidiDirection = QChar::Direction(
p->direction);
228 switch (QChar::Direction(
p->direction)) {
245 if (isolateLevel < 128) {
246 isolateStack[isolateLevel] = isolatePairs.size();
253 if (isolateLevel > 0) {
255 if (isolateLevel < 128)
256 isolatePairs[isolateStack[isolateLevel]].end =
pos;
265 if (uc == QChar::ParagraphSeparator) {
267 while (isolateLevel > 0) {
269 if (isolateLevel < 128)
270 isolatePairs[isolateStack[isolateLevel]].end =
pos;
280 struct DirectionalRun {
289 void generateDirectionalRuns(
const Vector<IsolatePair> &isolatePairs, Vector<DirectionalRun> &runs)
291 struct DirectionalStack {
292 enum { MaxDepth = 125 };
297 int runBeforeIsolate;
316 int overflowIsolateCount = 0;
317 int overflowEmbeddingCount = 0;
318 int validIsolateCount = 0;
321 bool override =
false;
322 stack.push({
level,
false,
false, -1 });
326 int continuationFrom = -1;
327 int lastRunWithContent = -1;
328 bool runHasContent =
false;
330 auto appendRun = [&](
int runEnd) {
331 if (runEnd < runStart)
333 bool isContinuation =
false;
334 if (continuationFrom != -1) {
335 runs[continuationFrom].continuation = runs.size();
336 isContinuation =
true;
337 }
else if (lastRunWithContent != -1 &&
level == runs.at(lastRunWithContent).level) {
338 runs[lastRunWithContent].continuation = runs.size();
339 isContinuation =
true;
342 lastRunWithContent = runs.size();
343 BIDI_DEBUG() <<
" appending run start/end" << runStart << runEnd <<
"level" <<
level;
344 runs.append({ runStart, runEnd, -1,
level, isContinuation, runHasContent });
345 runHasContent =
false;
346 runStart = runEnd + 1;
347 continuationFrom = -1;
350 int isolatePairPosition = 0;
353 QChar::Direction
dir = analysis[
i].bidiDirection;
356 auto doEmbed = [&](
bool isRtl,
bool isOverride,
bool isIsolate) {
359 analysis[
i].bidiDirection = (
level & 1) ? QChar::DirR :
QChar::DirL;
360 runHasContent =
true;
361 lastRunWithContent = -1;
362 ++isolatePairPosition;
364 int runBeforeIsolate = runs.size();
365 ushort newLevel = isRtl ? ((stack.top().level + 1) | 1) : ((stack.
top().
level + 2) & ~1);
366 if (newLevel <= DirectionalStack::MaxDepth && !overflowEmbeddingCount && !overflowIsolateCount) {
370 runBeforeIsolate = -1;
371 appendRun(isIsolate ?
i :
i - 1);
372 BIDI_DEBUG() <<
"pushing new item on stack: level" << (int)newLevel <<
"isOverride" << isOverride <<
"isIsolate" << isIsolate << runBeforeIsolate;
373 stack.push({ newLevel, isOverride, isIsolate, runBeforeIsolate });
374 override = isOverride;
378 ++overflowIsolateCount;
379 else if (!overflowIsolateCount)
380 ++overflowEmbeddingCount;
384 analysis[
i].bidiDirection = (
level & 1) ? QChar::DirR :
QChar::DirL;
386 analysis[
i].bidiDirection = QChar::DirBN;
392 doEmbed(
false,
false,
false);
395 doEmbed(
true,
false,
false);
398 doEmbed(
false,
true,
false);
401 doEmbed(
true,
true,
false);
404 doEmbed(
false,
false,
true);
407 doEmbed(
true,
false,
true);
409 case QChar::DirFSI: {
411 if (isolatePairPosition < isolatePairs.size()) {
412 const auto &pair = isolatePairs.at(isolatePairPosition);
416 doEmbed(isRtl,
false,
true);
422 analysis[
i].bidiDirection = (
level & 1) ? QChar::DirR :
QChar::DirL;
424 analysis[
i].bidiDirection = QChar::DirBN;
425 if (overflowIsolateCount) {
427 }
else if (overflowEmbeddingCount) {
428 --overflowEmbeddingCount;
429 }
else if (!stack.top().isIsolate && stack.depth() >= 2) {
432 override = stack.top().isOverride;
433 level = stack.top().level;
434 BIDI_DEBUG() <<
"popped PDF from stack, level now" << (int)stack.top().level;
438 runHasContent =
true;
439 if (overflowIsolateCount) {
440 --overflowIsolateCount;
441 }
else if (validIsolateCount == 0) {
445 overflowEmbeddingCount = 0;
446 while (!stack.top().isIsolate)
448 continuationFrom = stack.top().runBeforeIsolate;
449 BIDI_DEBUG() <<
"popped PDI from stack, level now" << (int)stack.top().level <<
"continuation from" << continuationFrom;
451 override = stack.top().isOverride;
452 level = stack.top().level;
453 lastRunWithContent = -1;
457 analysis[
i].bidiDirection = (
level & 1) ? QChar::DirR :
QChar::DirL;
461 if (text[
i].unicode() == QChar::ParagraphSeparator) {
463 while (stack.counter > 1) {
466 const auto &
t = stack.top();
468 runs[
t.runBeforeIsolate].continuation = -2;
472 continuationFrom = -1;
473 lastRunWithContent = -1;
474 validIsolateCount = 0;
475 overflowIsolateCount = 0;
476 overflowEmbeddingCount = 0;
481 runHasContent =
true;
485 analysis[
i].bidiDirection = (
level & 1) ? QChar::DirR :
QChar::DirL;
490 while (stack.counter > 1) {
493 const auto &
t = stack.top();
495 runs[
t.runBeforeIsolate].continuation = -2;
501 void resolveExplicitLevels(Vector<DirectionalRun> &runs)
503 Vector<IsolatePair> isolatePairs;
505 initScriptAnalysisAndIsolatePairs(isolatePairs);
506 generateDirectionalRuns(isolatePairs, runs);
509 struct IsolatedRunSequenceIterator {
514 Position() =
default;
515 Position(
int current,
int pos) : current(current),
pos(
pos) {}
517 bool isValid()
const {
return pos != -1; }
520 IsolatedRunSequenceIterator(
const Vector<DirectionalRun> &runs,
int i)
524 pos = runs.at(current).start;
527 bool atEnd()
const {
return pos < 0; }
530 if (pos > runs.at(current).end) {
531 current = runs.at(current).continuation;
533 pos = runs.at(current).start;
543 return Position(current, pos);
549 const Vector<DirectionalRun> &runs;
555 void resolveW1W2W3(
const Vector<DirectionalRun> &runs,
int i, QChar::Direction sos)
557 QChar::Direction last = sos;
558 QChar::Direction lastStrong = sos;
559 IsolatedRunSequenceIterator
it(runs,
i);
560 while (!
it.atEnd()) {
564 QChar::Direction current = analysis[
pos].bidiDirection;
565 if (current == QChar::DirNSM) {
567 analysis[
pos].bidiDirection = current;
568 }
else if (current >= QChar::DirLRI) {
570 }
else if (current == QChar::DirBN) {
584 if (current == QChar::DirEN && lastStrong == QChar::DirAL) {
585 current = QChar::DirAN;
586 analysis[
pos].bidiDirection = current;
590 if (current == QChar::DirL || current == QChar::DirR) {
591 lastStrong = current;
592 }
else if (current == QChar::DirAL) {
594 lastStrong = current;
595 analysis[
pos].bidiDirection = QChar::DirR;
603 void resolveW4(
const Vector<DirectionalRun> &runs,
int i, QChar::Direction sos)
606 QChar::Direction secondLast = sos;
608 IsolatedRunSequenceIterator
it(runs,
i);
610 QChar::Direction last = analysis[lastPos].bidiDirection;
614 while (!
it.atEnd()) {
616 QChar::Direction current = analysis[
pos].bidiDirection;
617 if (current == QChar::DirBN) {
622 if (last == QChar::DirES && current == QChar::DirEN && secondLast == QChar::DirEN) {
624 analysis[lastPos].bidiDirection = last;
625 }
else if (last == QChar::DirCS) {
626 if (current == QChar::DirEN && secondLast == QChar::DirEN) {
628 analysis[lastPos].bidiDirection = last;
629 }
else if (current == QChar::DirAN && secondLast == QChar::DirAN) {
631 analysis[lastPos].bidiDirection = last;
641 void resolveW5(
const Vector<DirectionalRun> &runs,
int i)
644 IsolatedRunSequenceIterator::Position lastETPosition;
646 IsolatedRunSequenceIterator
it(runs,
i);
648 QChar::Direction last = analysis[lastPos].bidiDirection;
649 if (last == QChar::DirET || last == QChar::DirBN)
650 lastETPosition =
it.position();
653 while (!
it.atEnd()) {
655 QChar::Direction current = analysis[
pos].bidiDirection;
656 if (current == QChar::DirBN) {
660 if (current == QChar::DirET) {
661 if (last == QChar::DirEN) {
662 current = QChar::DirEN;
663 analysis[
pos].bidiDirection = current;
664 }
else if (!lastETPosition.isValid()) {
665 lastETPosition =
it.position();
667 }
else if (lastETPosition.isValid()) {
668 if (current == QChar::DirEN) {
669 it.setPosition(lastETPosition);
672 analysis[
pos].bidiDirection = QChar::DirEN;
676 lastETPosition.
clear();
684 void resolveW6W7(
const Vector<DirectionalRun> &runs,
int i, QChar::Direction sos)
686 QChar::Direction lastStrong = sos;
687 IsolatedRunSequenceIterator
it(runs,
i);
688 while (!
it.atEnd()) {
692 QChar::Direction current = analysis[
pos].bidiDirection;
693 if (current == QChar::DirBN) {
697 if (current == QChar::DirET || current == QChar::DirES || current == QChar::DirCS) {
698 analysis[
pos].bidiDirection = QChar::DirON;
702 else if (current == QChar::DirL || current == QChar::DirR) {
703 lastStrong = current;
704 }
else if (current == QChar::DirEN && lastStrong == QChar::DirL) {
705 analysis[
pos].bidiDirection = lastStrong;
715 bool isValid()
const {
return second > 0; }
717 QChar::Direction containedDirection(
const QScriptAnalysis *analysis, QChar::Direction embeddingDir)
const {
718 int isolateCounter = 0;
719 QChar::Direction containedDir = QChar::DirON;
720 for (
int i =
first + 1;
i < second; ++
i) {
722 if (isolateCounter) {
723 if (
dir == QChar::DirPDI)
727 if (
dir == QChar::DirL) {
729 if (embeddingDir ==
dir)
731 }
else if (
dir == QChar::DirR ||
dir == QChar::DirAN ||
dir == QChar::DirEN) {
732 containedDir = QChar::DirR;
733 if (embeddingDir == QChar::DirR)
735 }
else if (
dir == QChar::DirLRI ||
dir == QChar::DirRLI ||
dir == QChar::DirFSI)
738 BIDI_DEBUG() <<
" contained dir for backet pair" <<
first <<
"/" << second <<
"is" << containedDir;
744 struct BracketStack {
748 uint pairedBracked = 0;
753 if (position < MaxDepth)
761 if (stack[
p].pairedBracked == unicode ||
763 (stack[
p].pairedBracked == 0x3009 && unicode == 0x232a) ||
764 (stack[
p].pairedBracked == 0x232a && unicode == 0x3009)) {
766 return stack[
p].position;
773 enum { MaxDepth = 63 };
774 Item stack[MaxDepth];
777 bool overflowed()
const {
return position > MaxDepth; }
780 void resolveN0(
const Vector<DirectionalRun> &runs,
int i, QChar::Direction sos)
784 Vector<BracketPair> bracketPairs;
786 BracketStack bracketStack;
787 IsolatedRunSequenceIterator
it(runs,
i);
788 while (!
it.atEnd()) {
791 if (
dir == QChar::DirON) {
795 if (
p->category == QChar::Punctuation_Open) {
798 bracketStack.push(closingBracked, bracketPairs.size());
799 if (bracketStack.overflowed()) {
800 bracketPairs.clear();
803 bracketPairs.append({
pos, -1 });
804 }
else if (
p->category == QChar::Punctuation_Close) {
805 int pairPos = bracketStack.match(text[
pos].unicode());
807 bracketPairs[pairPos].second =
pos;
815 if (BidiDebugEnabled && bracketPairs.size()) {
817 for (
int i = 0;
i < bracketPairs.size(); ++
i)
818 BIDI_DEBUG() <<
" " << bracketPairs.at(
i).first << bracketPairs.at(
i).second;
821 QChar::Direction lastStrong = sos;
822 IsolatedRunSequenceIterator
it(runs,
i);
823 QChar::Direction embeddingDir = (
level & 1) ? QChar::DirR :
QChar::DirL;
824 for (
int i = 0;
i < bracketPairs.size(); ++
i) {
825 const auto &pair = bracketPairs.at(
i);
828 QChar::Direction containedDir = pair.containedDirection(analysis, embeddingDir);
829 if (containedDir == QChar::DirON) {
830 BIDI_DEBUG() <<
" 3: resolve bracket pair" <<
i <<
"to DirON";
832 }
else if (containedDir == embeddingDir) {
835 BIDI_DEBUG() <<
" 1: resolve bracket pair" <<
i <<
"to" << embeddingDir;
838 while (
it.pos < pair.first) {
840 switch (analysis[
pos].bidiDirection) {
844 lastStrong = QChar::DirR;
847 lastStrong = QChar::DirL;
856 BIDI_DEBUG() <<
" 2: resolve bracket pair" <<
i <<
"to" << lastStrong;
858 for (
int i = pair.second + 1;
i <
length; ++
i) {
860 analysis[
i].bidiDirection = analysis[pair.second].bidiDirection;
867 void resolveN1N2(
const Vector<DirectionalRun> &runs,
int i, QChar::Direction sos, QChar::Direction eos)
870 QChar::Direction lastStrong = sos;
871 IsolatedRunSequenceIterator::Position niPos;
872 IsolatedRunSequenceIterator
it(runs,
i);
878 QChar::Direction currentStrong = current;
882 currentStrong = QChar::DirR;
886 if (niPos.isValid()) {
887 QChar::Direction
dir = currentStrong;
888 if (lastStrong != currentStrong)
889 dir = (runs.at(
i).level) & 1 ? QChar::DirR :
QChar::DirL;
890 it.setPosition(niPos);
892 if (analysis[*
it].bidiDirection != QChar::DirBN)
898 lastStrong = currentStrong;
910 if (!niPos.isValid())
911 niPos =
it.position();
924 void resolveImplicitLevelsForIsolatedRun(
const Vector<DirectionalRun> &runs,
int i)
927 int level = runs.at(
i).level;
929 while (before >= 0 && !runs.at(before).hasContent)
931 int level_before = (before >= 0) ? runs.at(before).level : baseLevel;
933 while (runs.at(after).continuation >= 0)
934 after = runs.at(after).continuation;
935 if (runs.at(after).continuation == -2) {
939 while (after < runs.size() && !runs.at(after).hasContent)
942 int level_after = (after == runs.size()) ? baseLevel : runs.
at(after).
level;
943 QChar::Direction sos = (
qMax(level_before,
level) & 1) ? QChar::DirR :
QChar::DirL;
944 QChar::Direction eos = (
qMax(level_after,
level) & 1) ? QChar::DirR :
QChar::DirL;
946 if (BidiDebugEnabled) {
947 BIDI_DEBUG() <<
"Isolated run starting at" <<
i <<
"sos/eos" << sos << eos;
948 BIDI_DEBUG() <<
"before implicit level processing:";
949 IsolatedRunSequenceIterator
it(runs,
i);
950 while (!
it.atEnd()) {
956 resolveW1W2W3(runs,
i, sos);
957 resolveW4(runs,
i, sos);
960 if (BidiDebugEnabled) {
962 IsolatedRunSequenceIterator
it(runs,
i);
963 while (!
it.atEnd()) {
969 resolveW6W7(runs,
i, sos);
974 resolveN0(runs,
i, sos);
975 resolveN1N2(runs,
i, sos, eos);
981 IsolatedRunSequenceIterator
it(runs,
i);
982 while (!
it.atEnd()) {
1008 void resolveImplicitLevels(
const Vector<DirectionalRun> &runs)
1010 for (
int i = 0;
i < runs.size(); ++
i) {
1011 if (runs.at(
i).isContinuation)
1014 resolveImplicitLevelsForIsolatedRun(runs,
i);
1018 bool checkForBidi()
const
1023 if (text[
i].unicode() >= 0x590) {
1025 case QChar::DirR:
case QChar::DirAN:
1026 case QChar::DirLRE:
case QChar::DirLRO:
case QChar::DirAL:
1027 case QChar::DirRLE:
case QChar::DirRLO:
case QChar::DirPDF:
1028 case QChar::DirLRI:
case QChar::DirRLI:
case QChar::DirFSI:
case QChar::DirPDI:
1042 bool hasBidi = checkForBidi();
1047 if (BidiDebugEnabled) {
1054 Vector<DirectionalRun> runs;
1055 resolveExplicitLevels(runs);
1057 if (BidiDebugEnabled) {
1058 BIDI_DEBUG() <<
"resolved explicit levels, nruns" << runs.size();
1059 for (
int i = 0;
i < runs.size(); ++
i)
1060 BIDI_DEBUG() <<
" " <<
i <<
"start/end" << runs.at(
i).start << runs.at(
i).end <<
"level" << (int)runs.at(
i).level <<
"continuation" << runs.at(
i).continuation;
1066 resolveImplicitLevels(runs);
1071 bool resetLevel =
true;
1074 BIDI_DEBUG() <<
"resetting pos" <<
i <<
"to baselevel";
1078 BIDI_DEBUG() <<
"resetting pos" <<
i <<
"to baselevel (maybereset flag)";
1088 int lastLevel = baseLevel;
1097 if (lastBNPos >= 0) {
1098 if (l < lastLevel) {
1099 while (lastBNPos <
i) {
1109 if (lastBNPos >= 0 && baseLevel < lastLevel) {
1110 while (lastBNPos <
length) {
1111 analysis[lastBNPos].
bidiLevel = baseLevel;
1116 if (BidiDebugEnabled) {
1141 while (
i < numItems) {
1155 if (!(levelLow%2)) levelLow++;
1157 BIDI_DEBUG() <<
"reorderLine: lineLow = " << (
uint)levelLow <<
", lineHigh = " << (
uint)levelHigh;
1159 int count = numItems - 1;
1160 for (
i = 0;
i < numItems;
i++)
1163 while(levelHigh >= levelLow) {
1174 int tmp = visualOrder[
start+
j];
1176 visualOrder[
end-
j] = tmp;
1204#if QT_CONFIG(harfbuzz)
1213 while (str_pos <
length) {
1214 int glyph_pos = log_clusters[str_pos];
1216 Q_ASSERT(glyph_pos <
g.numGlyphs &&
g.attributes[glyph_pos].clusterStart);
1218 uint ucs4 =
string[str_pos];
1219 if (QChar::isHighSurrogate(ucs4) && str_pos + 1 <
length) {
1220 ushort low =
string[str_pos + 1];
1221 if (QChar::isLowSurrogate(low)) {
1223 ucs4 = QChar::surrogateToUcs4(ucs4, low);
1230 }
while (str_pos <
length && log_clusters[str_pos] == glyph_pos);
1233 }
while (glyph_pos <
g.numGlyphs && !
g.attributes[glyph_pos].clusterStart);
1237 if (
Q_LIKELY(QChar::isLetterOrNumber(ucs4)))
1239 else if (
Q_LIKELY(QChar::isSpace(ucs4)))
1240 g.attributes[glyph_pos].justification = spaceAs;
1248 for (
int glyph_pos = 0; glyph_pos <
g.numGlyphs; ++glyph_pos)
1254 case QChar::Script_Arabic:
1255 case QChar::Script_Syriac:
1256 case QChar::Script_Nko:
1257 case QChar::Script_Mandaic:
1258 case QChar::Script_Mongolian:
1259 case QChar::Script_PhagsPa:
1260 case QChar::Script_Manichaean:
1261 case QChar::Script_PsalterPahlavi:
1266 case QChar::Script_Tibetan:
1267 case QChar::Script_Hiragana:
1268 case QChar::Script_Katakana:
1269 case QChar::Script_Bopomofo:
1270 case QChar::Script_Han:
1280 qt_getDefaultJustificationOpportunities(
string,
length,
g, log_clusters, spaceAs);
1321 case QChar::LineFeed:
1323 case QChar::CarriageReturn:
1324 case QChar::LineSeparator:
1325 case QChar::ParagraphSeparator:
1328 case QChar::SoftHyphen:
1329 if (!fontEngine->
symbol) {
1334 const uint engineIndex = glyphs->
glyphs[glyphPosition] & 0xff000000;
1340 glyphs->
glyphs[glyphPosition] = glyph;
1342 glyphs->
glyphs[glyphPosition] |= engineIndex;
1354void QTextEngine::shapeText(
int item)
const
1370 casedString.
resize(itemLength);
1371 ushort *uc =
reinterpret_cast<ushort *
>(casedString.data());
1372 for (
int i = 0;
i < itemLength; ++
i) {
1373 uint ucs4 =
string[
i];
1374 if (QChar::isHighSurrogate(ucs4) &&
i + 1 < itemLength) {
1375 uint low =
string[
i + 1];
1376 if (QChar::isLowSurrogate(low)) {
1380 ucs4 = QChar::surrogateToUcs4(ucs4, low);
1382 :
QChar::toUpper(ucs4);
1383 uc[
i] = QChar::lowSurrogate(ucs4);
1387 :
QChar::toUpper(ucs4);
1390 string =
reinterpret_cast<const ushort *
>(casedString.constData());
1394 Q_UNREACHABLE_RETURN();
1399#if QT_CONFIG(harfbuzz)
1400 bool kerningEnabled;
1402 bool letterSpacingIsAbsolute;
1403 bool shapingEnabled =
false;
1404 QHash<QFont::Tag, quint32> features;
1405 QFixed letterSpacing, wordSpacing;
1406#ifndef QT_NO_RAWFONT
1410# if QT_CONFIG(harfbuzz)
1417 letterSpacingIsAbsolute =
true;
1423#if QT_CONFIG(harfbuzz)
1433 if (letterSpacingIsAbsolute && letterSpacing.
value())
1439 QVarLengthArray<uint, 24> itemBoundaries;
1442 int nGlyphs = initialGlyphs.numGlyphs;
1446 QFontEngine::ShaperFlags shaperFlags =
1455 uint lastEngine = ~0u;
1456 for (
int i = 0, glyph_pos = 0;
i < itemLength; ++
i, ++glyph_pos) {
1457 const uint engineIdx = initialGlyphs.glyphs[glyph_pos] >> 24;
1458 if (lastEngine != engineIdx) {
1459 itemBoundaries.push_back(
i);
1460 itemBoundaries.push_back(glyph_pos);
1461 itemBoundaries.push_back(engineIdx);
1463 if (engineIdx != 0) {
1470 lastEngine = engineIdx;
1473 if (QChar::isHighSurrogate(
string[
i]) &&
i + 1 < itemLength && QChar::isLowSurrogate(
string[
i + 1]))
1477 itemBoundaries.push_back(0);
1478 itemBoundaries.push_back(0);
1479 itemBoundaries.push_back(0);
1482#if QT_CONFIG(harfbuzz)
1498 for (
int i = 0;
i < itemLength; ++
i, ++glyph_pos) {
1499 log_clusters[
i] = glyph_pos;
1500 initialGlyphs.attributes[glyph_pos].clusterStart =
true;
1501 if (QChar::isHighSurrogate(
string[
i])
1502 &&
i + 1 < itemLength
1503 && QChar::isLowSurrogate(
string[
i + 1])) {
1504 initialGlyphs.attributes[glyph_pos].dontPrint = !QChar::isPrint(QChar::surrogateToUcs4(
string[
i],
string[
i + 1]));
1506 log_clusters[
i] = glyph_pos;
1509 initialGlyphs.attributes[glyph_pos].dontPrint = !QChar::isPrint(
string[
i]);
1512 if (
Q_UNLIKELY(!initialGlyphs.attributes[glyph_pos].dontPrint)) {
1515 const uint engineIdx = initialGlyphs.glyphs[glyph_pos] >> 24;
1528 qWarning() <<
"Unable to allocate space for place-holder glyph";
1537 g.attributes[0].clusterStart =
true;
1540 for (
int i = 0;
i < itemLength; ++
i)
1541 log_clusters[
i] = 0;
1550#if QT_CONFIG(harfbuzz)
1551 qt_getJustificationOpportunities(
string, itemLength, si, glyphs,
logClusters(&si));
1554 if (letterSpacing != 0) {
1557 if (letterSpacingIsAbsolute)
1558 glyphs.
advances[
i - 1] += letterSpacing;
1561 advance += (letterSpacing - 100) * advance / 100;
1565 if (letterSpacingIsAbsolute)
1569 advance += (letterSpacing - 100) * advance / 100;
1572 if (wordSpacing != 0) {
1589#if QT_CONFIG(harfbuzz)
1597int QTextEngine::shapeTextWithHarfbuzzNG(
const QScriptItem &si,
1601 QSpan<uint> itemBoundaries,
1602 bool kerningEnabled,
1603 bool hasLetterSpacing,
1604 const QHash<QFont::Tag, quint32> &fontFeatures)
const
1606 uint glyphs_shaped = 0;
1608 hb_buffer_t *
buffer = hb_buffer_create();
1610 hb_buffer_pre_allocate(
buffer, itemLength);
1612 hb_buffer_destroy(
buffer);
1616 hb_segment_properties_t
props = HB_SEGMENT_PROPERTIES_DEFAULT;
1621 props.language = hb_language_get_default();
1623 for (
qsizetype k = 0; k < itemBoundaries.size(); k += 3) {
1624 const uint item_pos = itemBoundaries[k];
1625 const uint item_length = (k + 4 < itemBoundaries.size() ? itemBoundaries[k + 3] : itemLength) - item_pos;
1626 const uint engineIdx = itemBoundaries[k + 2];
1633 hb_buffer_clear_contents(
buffer);
1634 hb_buffer_add_utf16(
buffer,
reinterpret_cast<const uint16_t *
>(
string) + item_pos, item_length, 0, item_length);
1638 uint buffer_flags = HB_BUFFER_FLAG_DEFAULT;
1642 buffer_flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES;
1643 hb_buffer_set_flags(
buffer, hb_buffer_flags_t(buffer_flags));
1654 bool scriptRequiresOpenType = ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala)
1655 || script == QChar::Script_Khmer || script == QChar::Script_Nko);
1657 bool dontLigate = hasLetterSpacing && !scriptRequiresOpenType;
1659 QHash<QFont::Tag, quint32> features;
1660 features.insert(
QFont::Tag(
"kern"), !!kerningEnabled);
1667 features.insert(fontFeatures);
1669 QVarLengthArray<hb_feature_t, 16> featureArray;
1670 for (
auto it = features.constBegin();
it != features.constEnd(); ++
it) {
1671 featureArray.append({
it.key().value(),
1673 HB_FEATURE_GLOBAL_START,
1674 HB_FEATURE_GLOBAL_END });
1678 static const char *shaper_list[] = {
1685 bool shapedOk = hb_shape_full(hb_font,
1687 featureArray.constData(),
1691 hb_buffer_destroy(
buffer);
1696 hb_buffer_reverse(
buffer);
1699 uint num_glyphs = hb_buffer_get_length(
buffer);
1700 const bool has_glyphs = num_glyphs > 0;
1707 hb_buffer_destroy(
buffer);
1715 hb_glyph_info_t *infos = hb_buffer_get_glyph_infos(
buffer,
nullptr);
1716 hb_glyph_position_t *
positions = hb_buffer_get_glyph_positions(
buffer,
nullptr);
1718 uint last_cluster = ~0u;
1719 uint last_glyph_pos = glyphs_shaped;
1721 g.glyphs[
i] = infos->codepoint;
1727 uint cluster = infos->cluster;
1728 if (
Q_LIKELY(last_cluster != cluster)) {
1729 g.attributes[
i].clusterStart =
true;
1733 while (last_cluster++ < cluster && str_pos < item_length)
1734 log_clusters[str_pos++] = last_glyph_pos;
1735 last_glyph_pos =
i + glyphs_shaped;
1736 last_cluster = cluster;
1741 while (str_pos < item_length)
1742 log_clusters[str_pos++] = last_glyph_pos;
1748 g.attributes[0].clusterStart =
true;
1749 g.attributes[0].dontPrint =
true;
1750 log_clusters[0] = glyphs_shaped;
1755 g.glyphs[
i] |= (engineIdx << 24);
1758 if (!actualFontEngine->supportsHorizontalSubPixelPositions()) {
1759 for (
uint i = 0;
i < num_glyphs; ++
i) {
1761 g.offsets[
i].x =
g.offsets[
i].x.round();
1765 glyphs_shaped += num_glyphs;
1768 hb_buffer_destroy(
buffer);
1770 return glyphs_shaped;
1788 e->specialData =
nullptr;
1790#ifndef QT_NO_RAWFONT
1833 scriptItems.
data(), scriptItems.size(),
1863 fontEngine(li, &li.ascent, &li.descent, &li.leading);
1876 if (fontEngine && !fontEngine->
ref.
deref())
1918 if (specialData && specialData->preeditPosition != -1)
1936 QVarLengthArray<QScriptAnalysis, 4096> scriptAnalysis(
length);
1945 for (
int i = 0;
i < scriptItems.size(); ++
i) {
1946 const auto &
item = scriptItems.at(
i);
1947 int end =
i < scriptItems.size() - 1 ? scriptItems.at(
i + 1).position :
length;
1949 analysis[
j].script =
item.script;
1957 case QChar::ObjectReplacementCharacter:
1960 if (doc_p !=
nullptr
1961 && doc_p->layout() !=
nullptr
1970 case QChar::LineSeparator:
1978 *
const_cast<ushort*
>(uc) = 0x21B5;
1981 case QChar::Tabulation:
2007 SpecialData *
s = specialData;
2013 int preeditPosition =
s ?
s->preeditPosition : INT_MAX;
2014 int prevPosition = 0;
2021 preeditPosition = INT_MAX;
2029 for (
const auto &
range : std::as_const(
s->formats)) {
2033 if (
range.start > prevPosition)
2034 itemizer.generate(prevPosition,
range.start - prevPosition, capitalization);
2035 int newStart = std::max(prevPosition,
range.start);
2037 itemizer.generate(newStart, newEnd - newStart,
range.format.fontCapitalization());
2038 prevPosition = newEnd;
2042 itemizer.generate(prevPosition,
position - prevPosition, capitalization);
2055#ifndef QT_NO_RAWFONT
2058 for (
int i = 0;
i < specialData->formats.
size(); ++
i) {
2073 addRequiredBoundaries();
2079 switch (
option.textDirection()) {
2102 int left = firstItem + 1;
2108 else if (strPos < layoutData->
items.
at(middle).position)
2118template<
typename InnerFunc>
2121 for (
int i = 0;
i < textEngine->layoutData->items.size();
i++) {
2122 const QScriptItem *si = textEngine->layoutData->items.constData() +
i;
2124 int ilen = textEngine->length(
i);
2128 if (
pos + ilen > from) {
2130 textEngine->shape(
i);
2140 unsigned short *logClusters = textEngine->logClusters(si);
2147 int charFrom = from -
pos;
2150 int glyphStart = logClusters[charFrom];
2151 if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
2152 while (charFrom < ilen && logClusters[charFrom] == glyphStart)
2154 if (charFrom < ilen) {
2155 glyphStart = logClusters[charFrom];
2156 int charEnd = from +
len - 1 -
pos;
2157 if (charEnd >= ilen)
2159 int glyphEnd = logClusters[charEnd];
2160 while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
2162 glyphEnd = (charEnd == ilen) ? si->
num_glyphs : logClusters[charEnd];
2165 innerFunc(glyphStart, glyphEnd, si);
2178 textIterator(
this, from,
len,
w, [
this, &
w](
int glyphStart,
int glyphEnd,
const QScriptItem *si) {
2180 for (
int j = glyphStart;
j < glyphEnd;
j++)
2193 textIterator(
this, from,
len, gm.width, [
this, &gm](
int glyphStart,
int glyphEnd,
const QScriptItem *si) {
2194 if (glyphStart <= glyphEnd) {
2195 QGlyphLayout glyphs = this->shapedGlyphs(si);
2196 QFontEngine *fe = this->fontEngine(*si);
2197 glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
2198 gm.x = qMin(gm.x, m.x + gm.xoff);
2199 gm.y = qMin(gm.y, m.y + gm.yoff);
2200 gm.width = qMax(gm.width, m.width + gm.xoff);
2201 gm.height = qMax(gm.height, m.height + gm.yoff);
2216 textIterator(
this, from,
len, gm.width, [
this, &gm](
int glyphStart,
int glyphEnd,
const QScriptItem *si) {
2217 if (glyphStart <= glyphEnd) {
2218 QGlyphLayout glyphs = this->shapedGlyphs(si);
2219 QFontEngine *fe = fontEngine(*si);
2220 glyph_metrics_t m = fe->tightBoundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
2221 gm.x = qMin(gm.x, m.x + gm.xoff);
2222 gm.y = qMin(gm.y, m.y + gm.yoff);
2223 gm.width = qMax(gm.width, m.width + gm.xoff);
2224 gm.height = qMax(gm.height, m.height + gm.yoff);
2240 if (document_d !=
nullptr && document_d->layout() !=
nullptr) {
2242 QPaintDevice *pdev = document_d->layout()->paintDevice();
2263QTextEngine::FontEngineCache::FontEngineCache()
2277#ifndef QT_NO_RAWFONT
2279 if (feCache.prevFontEngine && feCache.prevFontEngine->
type() ==
QFontEngine::Multi && feCache.prevScript == script) {
2280 engine = feCache.prevFontEngine;
2283 feCache.prevFontEngine =
engine;
2284 feCache.prevScript = script;
2286 if (feCache.prevScaledFontEngine) {
2288 feCache.prevScaledFontEngine =
nullptr;
2292 if (feCache.prevScaledFontEngine) {
2293 scaledEngine = feCache.prevScaledFontEngine;
2302 scEngine->ref.ref();
2304 scaledEngine->ref.ref();
2305 feCache.prevScaledFontEngine = scaledEngine;
2307 if (!scEngine->ref.deref())
2317 if (feCache.prevFontEngine && feCache.prevPosition == si.
position && feCache.prevLength ==
length(&si) && feCache.prevScript == script) {
2318 engine = feCache.prevFontEngine;
2319 scaledEngine = feCache.prevScaledFontEngine;
2347 if (feCache.prevFontEngine)
2349 feCache.prevFontEngine =
engine;
2351 if (feCache.prevScaledFontEngine)
2353 feCache.prevScaledFontEngine = scaledEngine;
2355 feCache.prevScript = script;
2356 feCache.prevPosition = si.
position;
2357 feCache.prevLength =
length(&si);
2360 if (feCache.prevFontEngine && feCache.prevScript == script && feCache.prevPosition == -1) {
2361 engine = feCache.prevFontEngine;
2366 if (feCache.prevFontEngine)
2368 feCache.prevFontEngine =
engine;
2370 feCache.prevScript = script;
2371 feCache.prevPosition = -1;
2372 feCache.prevLength = -1;
2373 feCache.prevScaledFontEngine =
nullptr;
2387 *ascent =
engine->ascent();
2388 *descent =
engine->descent();
2389 *leading =
engine->leading();
2393 return scaledEngine;
2408 point->
glyph = glyph;
2411 const char32_t ch = U
'\x640';
2413 glyph_t kashidaGlyph = fe->glyphIndex(
ch);
2414 if (kashidaGlyph != 0) {
2417 g.glyphs = &kashidaGlyph;
2419 fe->recalcAdvances(&
g, { });
2434 if (
line.gridfitted &&
line.justified)
2437 if (!
line.gridfitted) {
2464 while (line_length &&
a[line_length-1].whiteSpace)
2469 if (line_length <= 0)
2474 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2476 QVarLengthArray<QJustificationPoint> justificationPoints;
2479 QFixed minKashida = 0x100000;
2484 for (
int i = 0;
i < nItems; ++
i) {
2490 for (
int i = 0;
i < nItems; ++
i) {
2494 int kashida_pos = -1;
2501 int gs = log_clusters[
start];
2508 for (
int i = gs;
i < ge; ++
i) {
2510 g.justifications[
i].nKashidas = 0;
2511 g.justifications[
i].space_18d6 = 0;
2513 justificationPoints.resize(nPoints+3);
2514 int justification =
g.attributes[
i].justification;
2516 switch(justification) {
2521 if (kashida_pos >= 0) {
2523 set(&justificationPoints[nPoints], kashida_type,
g.mid(kashida_pos),
fontEngine(si));
2524 if (justificationPoints[nPoints].kashidaWidth > 0) {
2525 minKashida =
qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
2526 maxJustify =
qMax(maxJustify, justificationPoints[nPoints].
type);
2534 set(&justificationPoints[nPoints++], justification,
g.mid(
i),
fontEngine(si));
2535 maxJustify =
qMax(maxJustify, justification);
2544 if (justification >= kashida_type) {
2546 kashida_type = justification;
2550 if (kashida_pos >= 0) {
2551 set(&justificationPoints[nPoints], kashida_type,
g.mid(kashida_pos),
fontEngine(si));
2552 if (justificationPoints[nPoints].kashidaWidth > 0) {
2553 minKashida =
qMin(minKashida, justificationPoints[nPoints].kashidaWidth);
2554 maxJustify =
qMax(maxJustify, justificationPoints[nPoints].
type);
2573 while (need >= minKashida) {
2575 for (
int i = 0; need >= minKashida &&
i < nPoints; ++
i) {
2576 if (justificationPoints[
i].
type ==
type && justificationPoints[
i].kashidaWidth <= need) {
2577 justificationPoints[
i].glyph.justifications->nKashidas++;
2579 justificationPoints[
i].glyph.justifications->space_18d6 += justificationPoints[
i].kashidaWidth.value();
2580 need -= justificationPoints[
i].kashidaWidth;
2592 for (
int type = maxJustify; need != 0 &&
type > 0; --
type) {
2594 for (
int i = 0;
i < nPoints; ++
i) {
2595 if (justificationPoints[
i].
type ==
type)
2604 for (
int i = 0;
i < nPoints; ++
i) {
2605 if (justificationPoints[
i].
type ==
type) {
2608 justificationPoints[
i].glyph.justifications[0].space_18d6 =
add.value();
2631 e =
f.d->engineForScript(QChar::Script_Common);
2639 leading =
qMax(leading + ascent, other_leading + other_ascent) -
qMax(ascent, other_ascent);
2640 ascent =
qMax(ascent, other_ascent);
2641 descent =
qMax(descent, other_descent);
2648 memory_on_stack =
false;
2652 haveCharAttributes =
false;
2653 logClustersPtr =
nullptr;
2654 available_glyphs = 0;
2655 currentMaxWidth = 0;
2663 constexpr qsizetype voidSize =
sizeof(
void*);
2665 qsizetype space_logClusters =
sizeof(
unsigned short) *
string.
size() / voidSize + 1;
2680 void *
m =
memory + space_charAttributes + space_logClusters;
2683 memset(
memory, 0, space_charAttributes*
sizeof(
void *));
2694 if (!memory_on_stack)
2701 Q_ASSERT(totalGlyphs >= glyphLayout.numGlyphs);
2702 if (memory_on_stack && available_glyphs >= totalGlyphs) {
2703 glyphLayout.grow(glyphLayout.data(), totalGlyphs);
2708 const qsizetype space_logClusters = (
sizeof(
unsigned short) *
string.
size() /
sizeof(
void*) + 1);
2711 const qsizetype newAllocated = space_charAttributes + space_glyphs + space_logClusters;
2715 if (
size_t(space_charAttributes) > INT_MAX || size_t(space_logClusters) > INT_MAX || totalGlyphs < 0
2716 || size_t(space_glyphs) > INT_MAX || size_t(newAllocated) > INT_MAX || newAllocated < allocated) {
2721 void **newMem = (
void **)::realloc(memory_on_stack ?
nullptr :
memory, newAllocated*
sizeof(
void *));
2726 if (memory_on_stack)
2727 memcpy(newMem,
memory, allocated*
sizeof(
void *));
2729 memory_on_stack =
false;
2732 m += space_charAttributes;
2733 logClustersPtr = (
unsigned short *)
m;
2734 m += space_logClusters;
2736 const qsizetype space_preGlyphLayout = space_charAttributes + space_logClusters;
2737 if (allocated < space_preGlyphLayout)
2738 memset(
memory + allocated, 0, (space_preGlyphLayout - allocated)*
sizeof(
void *));
2740 glyphLayout.grow(
reinterpret_cast<char *
>(
m), totalGlyphs);
2742 allocated = newAllocated;
2750 int n = std::min(
numGlyphs, oldLayout->numGlyphs);
2755 memcpy(advances, oldLayout->advances,
n *
sizeof(
QFixed));
2771 memmove(newLayout.advances, oldLayout.advances,
numGlyphs *
sizeof(
QFixed));
2795 specialData->resolvedFormats.
clear();
2804 if (specialData && !specialData->resolvedFormats.
isEmpty()) {
2807 return collection->indexForFormat(specialData->resolvedFormats.
at(si - &
layoutData->
items.
at(0)));
2814 if (specialData && si->
position >= specialData->preeditPosition) {
2815 if (si->
position < specialData->preeditPosition + specialData->preeditText.
size())
2818 pos -= specialData->preeditText.
size();
2821 return it.value()->format;
2832void QTextEngine::addRequiredBoundaries()
const
2835 for (
int i = 0;
i < specialData->formats.
size(); ++
i) {
2837 setBoundary(
r.start);
2838 setBoundary(
r.start +
r.length);
2847 switch (
c.unicode()) {
2891 if (specialData->formats.
isEmpty()) {
2893 specialData =
nullptr;
2895 specialData->preeditText =
QString();
2896 specialData->preeditPosition = -1;
2900 specialData =
new SpecialData;
2901 specialData->preeditPosition =
position;
2902 specialData->preeditText = preeditText;
2913 if (specialData->preeditText.
isEmpty()) {
2915 specialData =
nullptr;
2917 specialData->formats.
clear();
2921 specialData =
new SpecialData;
2922 specialData->preeditPosition = -1;
2924 specialData->formats =
formats;
2931void QTextEngine::indexFormats()
2937 collection = specialData->formatCollection.
data();
2941 for (
int i = 0;
i < specialData->formats.
size(); ++
i) {
2943 format = collection->charFormat(collection->indexForFormat(
format));
2957 QChar::JoiningType joining =
string.at(
pos).joiningType();
2958 return joining != QChar::Joining_None && joining != QChar::Joining_Transparent;
2967 QChar::JoiningType joining =
string.at(
pos - 1).joiningType();
2968 return joining == QChar::Joining_Dual || joining == QChar::Joining_Causing;
2973 return (
c >= 0x202a &&
c <= 0x202e)
2974 || (
c >= 0x200e &&
c <= 0x200f)
2975 || (
c >= 0x2066 &&
c <= 0x2069);
2987 for (
int i=subStringFrom;
i<midStart; ++
i) {
2988 char16_t c =
string.
at(
i).unicode();
2994 for (
int i=midStart + midLength;
i<subStringTo; ++
i) {
2995 char16_t c =
string.
at(
i).unicode();
3000 return prefix + ellidePrefix +
QStringView{
string}.
mid(midStart, midLength) + ellideSuffix + suffix;
3052 constexpr char16_t ellipsisChar = u
'\x2026';
3061 engine = multiEngine->engine(0);
3072 engine->recalcAdvances(&glyphs, { });
3074 ellipsisText = ellipsisChar;
3076 glyph =
engine->glyphIndex(
'.');
3078 engine->recalcAdvances(&glyphs, { });
3084 glyph =
engine->glyphIndex(ellipsisChar);
3085 engine->recalcAdvances(&glyphs, { });
3086 ellipsisText = ellipsisChar;
3091 const QFixed availableWidth =
width - ellipsisWidth;
3092 if (availableWidth < 0)
3099 constexpr char16_t ZWJ = u
'\x200d';
3104 int nextBreak = from;
3110 while (nextBreak < layoutData->
string.
size() && !
attributes[nextBreak].graphemeBoundary)
3113 currentWidth += this->
width(pos, nextBreak -
pos);
3114 }
while (nextBreak < to
3115 && currentWidth < availableWidth);
3118 ellipsisText.prepend(ZWJ);
3133 while (nextBreak > 0 && !
attributes[nextBreak].graphemeBoundary)
3136 currentWidth += this->
width(nextBreak,
pos - nextBreak);
3137 }
while (nextBreak > from
3138 && currentWidth < availableWidth);
3141 ellipsisText.append(ZWJ);
3152 int nextLeftBreak = from;
3155 int nextRightBreak = to;
3158 leftPos = nextLeftBreak;
3159 rightPos = nextRightBreak;
3162 while (nextLeftBreak < layoutData->
string.
size() && !
attributes[nextLeftBreak].graphemeBoundary)
3166 while (nextRightBreak > from && !
attributes[nextRightBreak].graphemeBoundary)
3169 leftWidth += this->
width(leftPos, nextLeftBreak - leftPos);
3170 rightWidth += this->
width(nextRightBreak, rightPos - nextRightBreak);
3171 }
while (nextLeftBreak < to
3172 && nextRightBreak > from
3173 && leftWidth + rightWidth < availableWidth);
3176 ellipsisText.prepend(ZWJ);
3178 ellipsisText.append(ZWJ);
3186void QTextEngine::setBoundary(
int strPos)
const
3193 if (newItem.position != strPos) {
3212 QList<QTextOption::Tab> tabArray =
option.tabs();
3213 if (!tabArray.isEmpty()) {
3218 const auto cbegin = tabArray.cbegin();
3219 const auto cend = tabArray.cend();
3220 const auto cit = std::find_if(cbegin, cend, isLeftOrRightTab);
3222 const int index = std::distance(cbegin, cit);
3224 const auto end = tabArray.end();
3245 tabSectionEnd =
item.position;
3268 for (
int i=0;
i <
end;
i++)
3274 switch (tabSpec.type) {
3296 QFixed nextTabPos = ((
x / tab).truncate() + 1) * tab;
3297 QFixed tabWidth = nextTabPos -
x;
3303class FormatRangeComparatorByStart {
3304 const QList<QTextLayout::FormatRange> &
list;
3306 FormatRangeComparatorByStart(
const QList<QTextLayout::FormatRange> &
list) :
list(
list) { }
3307 bool operator()(
int a,
int b) {
3311class FormatRangeComparatorByEnd {
3312 const QList<QTextLayout::FormatRange> &
list;
3314 FormatRangeComparatorByEnd(
const QList<QTextLayout::FormatRange> &
list) :
list(
list) { }
3315 bool operator()(
int a,
int b) {
3321void QTextEngine::resolveFormats()
const
3323 if (!specialData || specialData->formats.
isEmpty())
3331 QVarLengthArray<int, 64> formatsSortedByStart;
3332 formatsSortedByStart.reserve(specialData->formats.
size());
3333 for (
int i = 0;
i < specialData->formats.
size(); ++
i) {
3334 if (specialData->formats.
at(
i).
length >= 0)
3335 formatsSortedByStart.append(
i);
3337 QVarLengthArray<int, 64> formatsSortedByEnd = formatsSortedByStart;
3338 std::sort(formatsSortedByStart.begin(), formatsSortedByStart.end(),
3339 FormatRangeComparatorByStart(specialData->formats));
3340 std::sort(formatsSortedByEnd.begin(), formatsSortedByEnd.end(),
3341 FormatRangeComparatorByEnd(specialData->formats));
3343 QVarLengthArray<int, 16> currentFormats;
3344 const int *startIt = formatsSortedByStart.constBegin();
3345 const int *endIt = formatsSortedByEnd.constBegin();
3351 while (startIt != formatsSortedByStart.constEnd() &&
3353 currentFormats.insert(std::upper_bound(currentFormats.begin(), currentFormats.end(), *startIt),
3357 while (endIt != formatsSortedByEnd.constEnd() &&
3358 specialData->formats.
at(*endIt).
start + specialData->formats.
at(*endIt).
length <
end) {
3359 int *currentFormatIterator = std::lower_bound(currentFormats.begin(), currentFormats.end(), *endIt);
3360 if (*endIt < *currentFormatIterator)
3361 currentFormatIterator = currentFormats.end();
3362 currentFormats.remove(currentFormatIterator - currentFormats.begin());
3372 if (!currentFormats.isEmpty()) {
3373 for (
int cur : currentFormats) {
3378 format = collection->charFormat(collection->indexForFormat(
format));
3382 specialData->resolvedFormats = resolvedFormats;
3387 if (!
line.hasTrailingSpaces
3401 int align =
option.alignment();
3417 int offsetInCluster = 0;
3418 for (
int i =
pos - 1;
i >= 0;
i--) {
3427 if (offsetInCluster > 0) {
3428 int clusterLength = 0;
3429 for (
int i =
pos - offsetInCluster;
i < max;
i++) {
3436 return glyphs.
advances[glyph_pos] * offsetInCluster / clusterLength;
3443int QTextEngine::getClusterLength(
unsigned short *
logClusters,
3445 int from,
int to,
int glyph_pos,
int *
start)
3447 int clusterLength = 0;
3448 for (
int i = from;
i < to;
i++) {
3454 else if (clusterLength)
3457 return clusterLength;
3462 bool cursorOnCharacter)
3465 int clusterStart = -1;
3466 int clusterLength = 0;
3475 if (glyph_pos == -1)
3479 for (
i = 0;
i <
end;
i++)
3486 if (glyph_pos == -1 &&
end > 0)
3497 if (clusterLength) {
3501 QFixed perItemWidth = glyphWidth / clusterLength;
3502 if (perItemWidth <= 0)
3503 return si->
position + clusterStart;
3504 QFixed left =
x > edge ? edge : edge - glyphWidth;
3505 int n = ((
x -
left) / perItemWidth).
floor().toInt();
3507 int closestItem =
dist > (perItemWidth / 2) ?
n + 1 :
n;
3508 if (cursorOnCharacter && closestItem > 0)
3510 int pos = clusterStart + closestItem;
3525 if (!
attrs || oldPos <= 0 || oldPos >
len)
3529 while (oldPos && !
attrs[oldPos].graphemeBoundary)
3540 if (!
attrs || oldPos < 0 || oldPos >=
len)
3544 while (oldPos <
len && !
attrs[oldPos].graphemeBoundary)
3567 std::vector<int> insertionPoints;
3568 insertionPoints.reserve(
size_t(iterator.line.length));
3570 bool lastLine = lineNum >=
lines.
size() - 1;
3572 while (!iterator.atEnd()) {
3575 int end = iterator.itemEnd;
3576 if (lastLine && iterator.item == iterator.lastItem)
3579 for (
int i =
end - 1;
i >= iterator.itemStart; --
i)
3580 insertionPoints.push_back(
i);
3582 for (
int i = iterator.itemStart;
i <
end; ++
i)
3583 insertionPoints.push_back(
i);
3586 return insertionPoints;
3589int QTextEngine::endOfLine(
int lineNum)
3592 if (insertionPoints.size() > 0)
3593 return insertionPoints.back();
3597int QTextEngine::beginningOfLine(
int lineNum)
3600 if (insertionPoints.size() > 0)
3601 return insertionPoints.front();
3619 for (
size_t i = 0, max = insertionPoints.size();
i < max; ++
i)
3620 if (
pos == insertionPoints[
i]) {
3623 return insertionPoints[
i + 1];
3626 return insertionPoints[
i - 1];
3629 if (moveRight ^ alignRight) {
3631 return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
3635 return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
3672 if (decorationList.isEmpty())
3675 for (
const ItemDecoration &
decoration : decorationList) {
3710 ItemDecorationList::iterator
it =
start;
3717 underlinePos =
qMax(underlinePos,
it->y);
3718 penWidth =
qMax(penWidth,
it->pen.widthF());
3722 underlinePos =
start->y;
3723 penWidth =
start->pen.widthF();
3725 lastLineEnd =
it->x2;
3733 ItemDecorationList::iterator
end,
3737 it->y = underlinePos;
3738 it->pen.setWidthF(penWidth);
3744 _layoutData(
string, _memory, MemSize)
3753 fontEngine(
font->
d->engineForScript(si.analysis.script))
3762 num_chars(numChars),
3784 ||
f->d->underline) {
3810 ti.logClusters += (ti.chars -
chars);
3813 int char_start = ti.chars -
chars;
3814 while (char_start + ti.num_chars <
num_chars && ti.logClusters[ti.num_chars] - logClusterOffset <
end)
3859 line(eng->lines[_lineNum]),
3863 firstItem(eng->findItem(
line.from)),
3864 lastItem(eng->findItem(lineEnd - 1, firstItem)),
3865 nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
3868 visualOrder(nItems),
3928 *selectionX = *selectionWidth = 0;
3949 int start_glyph = logClusters[from];
3956 for (
int g = end_glyph - 1;
g >= start_glyph; --
g)
3961 for (
int g = start_glyph;
g < end_glyph; ++
g)
3970 *selectionX =
x + soff + leftOffsetInLigature;
3971 *selectionWidth = swidth - leftOffsetInLigature;
static QAbstractTextDocumentLayoutPrivate * get(QAbstractTextDocumentLayout *layout)
virtual void resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
Sets the size of the inline object item corresponding to the text format.
void ensureEngineAt(int at)
static QFontEngine * createMultiFontEngine(QFontEngine *fe, int script)
virtual QFixed descent() const
virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const =0
virtual QFixed ascent() const
virtual QFontEngine * cloneWithSize(qreal) const
static bool scriptRequiresOpenType(QChar::Script script)
virtual glyph_t glyphIndex(uint ucs4) const =0
virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const
virtual QFixed leading() const
bool letterSpacingIsAbsolute
QFontPrivate * smallCapsFontPrivate() const
QFontEngine * engineForScript(int script) const
QFont smallCapsFont() const
QHash< QFont::Tag, quint32 > features
StyleStrategy styleStrategy() const
Returns the StyleStrategy.
void setPointSize(int)
Sets the point size to pointSize.
int pixelSize() const
Returns the pixel size of the font if it was set with setPixelSize().
qreal letterSpacing() const
QFont resolve(const QFont &) const
Returns a new QFont that has attributes copied from other that have not been previously set on this f...
Capitalization capitalization() const
qreal wordSpacing() const
int pointSize() const
Returns the point size of the font.
bool kerning() const
Returns true if kerning should be used when drawing text with this font.
void setPixelSize(int)
Sets the font size to pixelSize pixels, with a maxiumum size of an unsigned 16-bit integer.
virtual int type() const
Returns the type of an item as an int.
GraphicsItemFlags flags() const
Returns this item's flags.
static QInputMethod * inputMethod()
returns the input method.
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
constexpr qreal x1() const
Returns the x-coordinate of the line's start point.
constexpr qreal dx() const
Returns the horizontal component of the line's vector.
constexpr qreal dy() const
Returns the vertical component of the line's vector.
constexpr qreal y1() const
Returns the y-coordinate of the line's start point.
qsizetype size() const noexcept
bool isEmpty() const noexcept
iterator insert(qsizetype i, parameter_type t)
const_reference at(qsizetype i) const noexcept
The QPainter class performs low-level painting on widgets and other paint devices.
const QPen & pen() const
Returns the painter's current pen.
void setPen(const QColor &color)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void drawLine(const QLineF &line)
Draws a line defined by line.
\inmodule QtCore\reentrant
constexpr int x() const noexcept
Returns the x coordinate of this point.
qreal pixelSize() const
Returns the pixel size set for this QRawFont.
bool isValid() const
Returns true if the QRawFont is valid and false otherwise.
\inmodule QtCore\reentrant
constexpr int width() const noexcept
Returns the width of the rectangle.
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
iterator find(const T &value)
QStackTextEngine(const QString &string, const QFont &f)
bool isRightToLeft() const noexcept
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
QString mid(qsizetype position, qsizetype n=-1) const &
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
const QChar * constData() const
Returns a pointer to the data stored in the QString.
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.
QString & insert(qsizetype i, QChar c)
QChar * data()
Returns a pointer to the data stored in the QString.
bool isRightToLeft() const
Returns true if the string is read right to left.
const QChar * unicode() const
Returns a Unicode representation of the string.
qsizetype length() const noexcept
Returns the number of characters in this string.
void resize(qsizetype size)
Sets the size of the string to size characters.
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.
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
int position() const
Returns the index of the block's first character within the document.
QString text() const
Returns the block's contents as plain text.
QTextCharFormat charFormat() const
Returns the QTextCharFormat that describes the block's character format.
VerticalAlignment
This enum describes the ways that adjacent characters can be vertically aligned.
UnderlineStyle underlineStyle() const
bool fontStrikeOut() const
Returns true if the text format's font is struck out (has a horizontal line drawn through it); otherw...
QFont::Capitalization fontCapitalization() const
bool fontOverline() const
Returns true if the text format's font is overlined; otherwise returns false.
QFont font() const
Returns the font for this character format.
MoveOperation
\value NoMove Keep the cursor where it is
static const QTextDocumentPrivate * get(const QTextDocument *document)
FragmentMap::ConstIterator FragmentIterator
QList< ItemDecoration > ItemDecorationList
glyph_metrics_t boundingBox(int from, int len) const
QList< QTextLayout::FormatRange > formats() const
void setPreeditArea(int position, const QString &text)
int lineNumberForTextPosition(int pos)
ItemDecorationList strikeOutList
void justify(const QScriptLine &si)
unsigned short * logClusters(const QScriptItem *si) const
ItemDecorationList underlineList
bool isRightToLeft() const
void shapeLine(const QScriptLine &line)
QGlyphLayout shapedGlyphs(const QScriptItem *si) const
int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter)
int findItem(int strPos, int firstItem=0) const
void shape(int item) const
void resetFontEngineCache()
const QCharAttributes * attributes() const
void addOverline(QPainter *painter, const QLineF &line)
QFixed leadingSpaceWidth(const QScriptLine &line)
QFixed alignLine(const QScriptLine &line)
bool ensureSpace(int nGlyphs) const
int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op)
int formatIndex(const QScriptItem *si) const
QFontEngine * fontEngine(const QScriptItem &si, QFixed *ascent=nullptr, QFixed *descent=nullptr, QFixed *leading=nullptr) const
void drawDecorations(QPainter *painter)
QFixed calculateTabWidth(int index, QFixed x) const
returns the width of tab at index (in the tabs array) with the tab-start at position x
bool atWordSeparator(int position) const
void addStrikeOut(QPainter *painter, const QLineF &line)
void setFormats(const QList< QTextLayout::FormatRange > &formats)
QGlyphLayout availableGlyphs(const QScriptItem *si) const
void addUnderline(QPainter *painter, const QLineF &line)
QTextCharFormat format(const QScriptItem *si) const
std::vector< int > insertionPointsForLine(int lineNum)
int nextLogicalPosition(int oldPos) const
QFixed width(int charFrom, int numChars) const
static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder)
glyph_metrics_t tightBoundingBox(int from, int len) const
int previousLogicalPosition(int oldPos) const
QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos)
int length(int item) const
QTextFormatCollection * formatCollection() const
QAbstractTextDocumentLayout * docLayout() const
ItemDecorationList overlineList
QString elidedText(Qt::TextElideMode mode, QFixed width, int flags=0, int from=0, int count=-1) const
QFont defaultFont() const
QTextCharFormat charFormat(int index) const
bool boolProperty(int propertyId) const
Returns the value of the property specified by propertyId.
bool hasProperty(int propertyId) const
Returns true if the text format has a property with the given propertyId; otherwise returns false.
void initWithScriptItem(const QScriptItem &si)
const QTextCharFormat charFormat
QTextItemInt midItem(QFontEngine *fontEngine, int firstGlyphIndex, int numGlyphs) const
const unsigned short * logClusters
QTextCharFormat::UnderlineStyle underlineStyle
@ ShowLineAndParagraphSeparators
const QLoggingCategory & category()
[1]
QSet< QString >::iterator it
QHighDpiScaling::Point position(T, QHighDpiScaling::Point::Kind)
Combined button and popup list for selecting options.
Q_DECL_CONST_FUNCTION Q_CORE_EXPORT const Properties *QT_FASTCALL properties(char32_t ucs4) noexcept
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
#define QT_WARNING_DISABLE_GCC(text)
constexpr bool operator!=(const timespec &t1, const timespec &t2)
constexpr timespec operator*(const timespec &t1, int mul)
static const QCssKnownValue positions[NumKnownPositionModes - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
static struct AttrInfo attrs[]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Q_GUI_EXPORT int qt_defaultDpiY()
hb_unicode_funcs_t * hb_qt_get_unicode_funcs()
hb_script_t hb_qt_script_to_script(QChar::Script script)
hb_font_t * hb_qt_font_get_for_engine(QFontEngine *fe)
void hb_qt_font_set_use_design_metrics(hb_font_t *font, uint value)
struct hb_font_t hb_font_t
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
n void setPosition(void) \n\
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum GLuint GLint level
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLenum const void GLbitfield GLsizei numGlyphs
GLenum GLuint GLsizei const GLenum * props
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
GLsizei GLenum GLsizei GLsizei GLuint memory
GLenum const void GLbitfield GLuint firstGlyphIndex
GLfloat GLfloat GLfloat GLfloat h
GLuint GLsizei const GLuint const GLintptr * offsets
GLuint GLuint64EXT address
GLsizei const GLchar *const * string
[0]
static void add(QPainterPath &path, const QWingedEdge &list, int edge, QPathEdge::Traversal traversal)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define QStringLiteral(str)
#define QT_BEGIN_INCLUDE_NAMESPACE
#define QT_END_INCLUDE_NAMESPACE
QTransform qt_true_matrix(qreal w, qreal h, const QTransform &x)
@ Justification_Arabic_Waw
@ Justification_Arabic_Kashida
@ Justification_Arabic_Alef
@ Justification_Arabic_HahDal
@ Justification_Character
@ Justification_Arabic_Space
@ Justification_Arabic_Seen
@ Justification_Arabic_BaRa
@ Justification_Arabic_Normal
@ Justification_Prohibited
static bool prevCharJoins(const QString &string, int pos)
static QString stringMidRetainingBidiCC(const QString &string, const QString &ellidePrefix, const QString &ellideSuffix, int subStringFrom, int subStringTo, int midStart, int midLength)
static void applyVisibilityRules(ushort ucs, QGlyphLayout *glyphs, uint glyphPosition, QFontEngine *fontEngine)
static bool nextCharJoins(const QString &string, int pos)
static QT_BEGIN_NAMESPACE const float smallCapsFraction
static constexpr bool isRetainableControlCode(char16_t c) noexcept
static void releaseCachedFontEngine(QFontEngine *fontEngine)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
QFuture< QSet< QChar > > set
[10]
std::uniform_real_distribution dist(1, 2.5)
[2]
QItemSelection * selection
[0]
static constexpr QFixed fromReal(qreal r)
constexpr QFixed floor() const
static constexpr QFixed fromFixed(int fixed)
constexpr int value() const
constexpr QFixed round() const
constexpr qreal toReal() const
The QFont::Tag type provides access to advanced font features.
void grow(char *address, int totalGlyphs)
QFixed effectiveAdvance(int item) const
void copy(QGlyphLayout *other)
void clear(int first=0, int last=-1)
static constexpr qsizetype SpaceNeeded
QGlyphAttributes * attributes
QGlyphLayout mid(int position, int n=-1) const
\inmodule QtCore \reentrant
@ BidiResetToParagraphLevel
@ BidiMaybeResetToParagraphLevel
QChar::Direction bidiDirection
@ LineOrParagraphSeparator
unsigned short num_glyphs
void setDefaultHeight(QTextEngine *eng)
bool reallocate(int totalGlyphs)
unsigned short * logClustersPtr
qsizetype available_glyphs
bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos=QPointF(), const QTextLayout::FormatRange *_selection=nullptr)
const QTextLayout::FormatRange * selection
QVarLengthArray< int > visualOrder
glyph_metrics_t transformed(const QTransform &xform) const