diff options
Diffstat (limited to 'src/gui/text/qtextlayout.cpp')
-rw-r--r-- | src/gui/text/qtextlayout.cpp | 414 |
1 files changed, 215 insertions, 199 deletions
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index b8edea11d5..f0c7dd24e5 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -1043,12 +1043,9 @@ QList<QGlyphRun> QTextLayout::glyphRuns(int from, for (int i=0; i<d->lines.size(); ++i) { if (d->lines.at(i).from > from + length) break; - else if (d->lines.at(i).from + d->lines[i].length >= from) { - QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags); - - for (int j = 0; j < glyphRuns.size(); j++) { - const QGlyphRun &glyphRun = glyphRuns.at(j); - + else if (d->lines.at(i).from + d->lines.at(i).length >= from) { + const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags); + for (const QGlyphRun &glyphRun : glyphRuns) { QRawFont rawFont = glyphRun.rawFont(); QFontEngine *fontEngine = rawFont.d->fontEngine; @@ -1107,7 +1104,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QList<FormatRange> int firstLine = 0; int lastLine = d->lines.size(); for (int i = 0; i < d->lines.size(); ++i) { - QTextLine l(i, d); const QScriptLine &sl = d->lines.at(i); if (sl.y > clipe) { @@ -1656,7 +1652,7 @@ void QTextLine::setNumColumns(int numColumns) void QTextLine::setNumColumns(int numColumns, qreal alignmentWidth) { QScriptLine &line = eng->lines[index]; - line.width = QFixed::fromReal(alignmentWidth); + line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX))); line.length = 0; line.textWidth = 0; layout_helper(numColumns); @@ -1672,23 +1668,18 @@ namespace { struct LineBreakHelper { - LineBreakHelper() - : glyphCount(0), maxGlyphs(0), currentPosition(0), fontEngine(nullptr), logClusters(nullptr), - manualWrap(false), whiteSpaceOrObject(true) - { - } - + LineBreakHelper() = default; QScriptLine tmpData; QScriptLine spaceData; QGlyphLayout glyphs; - int glyphCount; - int maxGlyphs; - int currentPosition; - glyph_t previousGlyph; - QFontEngine *previousGlyphFontEngine; + int glyphCount = 0; + int maxGlyphs = 0; + int currentPosition = 0; + glyph_t previousGlyph = 0; + QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine; QFixed minw; QFixed currentSoftHyphenWidth; @@ -1696,11 +1687,11 @@ namespace { QFixed rightBearing; QFixed minimumRightBearing; - QFontEngine *fontEngine; - const unsigned short *logClusters; + QExplicitlySharedDataPointer<QFontEngine> fontEngine; + const unsigned short *logClusters = nullptr; - bool manualWrap; - bool whiteSpaceOrObject; + bool manualWrap = false; + bool whiteSpaceOrObject = true; bool checkFullOtherwiseExtend(QScriptLine &line); @@ -1744,13 +1735,13 @@ namespace { { if (currentPosition <= 0) return; - calculateRightBearing(fontEngine, currentGlyph()); + calculateRightBearing(fontEngine.data(), currentGlyph()); } inline void calculateRightBearingForPreviousGlyph() { if (previousGlyph > 0) - calculateRightBearing(previousGlyphFontEngine, previousGlyph); + calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph); } static const QFixed RightBearingNotCalculated; @@ -2164,9 +2155,12 @@ found: eng->maxWidth = qMax(eng->maxWidth, line.textWidth); } else { eng->minWidth = qMax(eng->minWidth, lbh.minw); - eng->layoutData->currentMaxWidth += line.textWidth; - if (!manuallyWrapped) - eng->layoutData->currentMaxWidth += lbh.spaceData.textWidth; + if (qAddOverflow(eng->layoutData->currentMaxWidth, line.textWidth, &eng->layoutData->currentMaxWidth)) + eng->layoutData->currentMaxWidth = QFIXED_MAX; + if (!manuallyWrapped) { + if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth)) + eng->layoutData->currentMaxWidth = QFIXED_MAX; + } eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth); if (manuallyWrapped) eng->layoutData->currentMaxWidth = 0; @@ -2229,20 +2223,20 @@ int QTextLine::textLength() const return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces; } -static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf, const QRectF &r) +static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r) { - QBrush c = chf.foreground(); - if (c.style() == Qt::NoBrush) { - p->setPen(defaultPen); - } - QBrush bg = chf.background(); if (bg.style() != Qt::NoBrush && !chf.property(SuppressBackground).toBool()) p->fillRect(r.toAlignedRect(), bg); - if (c.style() != Qt::NoBrush) { - p->setPen(QPen(c, 0)); - } +} +static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf) +{ + QBrush c = chf.foreground(); + if (c.style() == Qt::NoBrush) + p->setPen(defaultPen); + else + p->setPen(QPen(c, 0)); } #if !defined(QT_NO_RAWFONT) @@ -2637,7 +2631,6 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, Q_ASSERT(!eng->useRawFont); #endif const QScriptLine &line = eng->lines[index]; - QPen pen = p->pen(); bool noText = (selection && selection->format.property(SuppressText).toBool()); @@ -2649,8 +2642,7 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, const qreal lineHeight = line.height().toReal(); QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(), lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' ')); - setPenAndDrawBackground(p, QPen(), selection->format, r); - p->setPen(pen); + drawBackground(p, selection->format, r); } return; } @@ -2663,7 +2655,7 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, else p->translate(origPos); - QTextLineItemIterator iterator(eng, index, pos, selection); + QFixed lineBase = line.base(); eng->clearDecorations(); eng->enableDelayDecorations(); @@ -2673,183 +2665,207 @@ void QTextLine::draw_internal(QPainter *p, const QPointF &origPos, const QTextFormatCollection *formatCollection = eng->formatCollection(); bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors); - while (!iterator.atEnd()) { - QScriptItem &si = iterator.next(); - - if (selection && selection->start >= 0 && iterator.isOutsideSelection()) - continue; - if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator - && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) - continue; - - QFixed itemBaseLine = y; - QFont f = eng->font(si); - QTextCharFormat format; - if (formatCollection != nullptr) - format = formatCollection->defaultTextFormat(); + auto prepareFormat = [suppressColors, selection, this](QTextCharFormat &format, + QScriptItem *si) { + format.merge(eng->format(si)); - if (eng->hasFormats() || selection || formatCollection) { - format.merge(eng->format(&si)); + if (suppressColors) { + format.clearForeground(); + format.clearBackground(); + format.clearProperty(QTextFormat::TextUnderlineColor); + } + if (selection) + format.merge(selection->format); + }; - if (suppressColors) { - format.clearForeground(); - format.clearBackground(); - format.clearProperty(QTextFormat::TextUnderlineColor); - } - if (selection) - format.merge(selection->format); - - setPenAndDrawBackground(p, pen, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), - iterator.itemWidth.toReal(), line.height().toReal())); - - const qreal baseLineOffset = format.baselineOffset() / 100.0; - QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); - if (valign == QTextCharFormat::AlignSuperScript - || valign == QTextCharFormat::AlignSubScript - || !qFuzzyIsNull(baseLineOffset)) - { - QFontEngine *fe = f.d->engineForScript(si.analysis.script); - QFixed height = fe->ascent() + fe->descent(); - itemBaseLine -= height * QFixed::fromReal(baseLineOffset); - - if (valign == QTextCharFormat::AlignSubScript) - itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0); - else if (valign == QTextCharFormat::AlignSuperScript) - itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0); + { + QTextLineItemIterator iterator(eng, index, pos, selection); + while (!iterator.atEnd()) { + QScriptItem &si = iterator.next(); + + if (eng->hasFormats() || selection || formatCollection) { + QTextCharFormat format; + if (formatCollection != nullptr) + format = formatCollection->defaultTextFormat(); + prepareFormat(format, &si); + drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(), + iterator.itemWidth.toReal(), line.height().toReal())); } } + } - if (si.analysis.flags >= QScriptAnalysis::TabOrObject) { + QPen pen = p->pen(); + { + QTextLineItemIterator iterator(eng, index, pos, selection); + while (!iterator.atEnd()) { + QScriptItem &si = iterator.next(); - if (eng->hasFormats()) { - p->save(); - if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) { - QFixed itemY = y - si.ascent; - switch (format.verticalAlignment()) { - case QTextCharFormat::AlignTop: - itemY = y - lineBase; - break; - case QTextCharFormat::AlignMiddle: - itemY = y - lineBase + (line.height() - si.height()) / 2; - break; - case QTextCharFormat::AlignBottom: - itemY = y - lineBase + line.height() - si.height(); - break; - default: - break; - } + if (selection && selection->start >= 0 && iterator.isOutsideSelection()) + continue; - QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); - - eng->docLayout()->drawInlineObject(p, itemRect, - QTextInlineObject(iterator.item, eng), - si.position + eng->block.position(), - format); - if (selection) { - QBrush bg = format.brushProperty(ObjectSelectionBrush); - if (bg.style() != Qt::NoBrush) { - QColor c = bg.color(); - c.setAlpha(128); - p->fillRect(itemRect, c); - } - } - } else { // si.isTab - QFont f = eng->font(si); - QTextItemInt gf(si, &f, format); - gf.chars = nullptr; - gf.num_chars = 0; - gf.width = iterator.itemWidth; - QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng); - if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) { - const QChar visualTab = QChar(QChar::VisualTabCharacter); - int w = QFontMetrics(f).horizontalAdvance(visualTab); - qreal x = iterator.itemWidth.toReal() - w; // Right-aligned - if (x < 0) - p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(), - iterator.itemWidth.toReal(), line.height().toReal()), - Qt::IntersectClip); - else - x /= 2; // Centered - p->setFont(f); - p->drawText(QPointF(iterator.x.toReal() + x, - y.toReal()), visualTab); - } + if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator + && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) + continue; + QFixed itemBaseLine = y; + QFont f = eng->font(si); + QTextCharFormat format; + if (formatCollection != nullptr) + format = formatCollection->defaultTextFormat(); + + if (eng->hasFormats() || selection || formatCollection) { + prepareFormat(format, &si); + setPen(p, pen, format); + + const qreal baseLineOffset = format.baselineOffset() / 100.0; + QTextCharFormat::VerticalAlignment valign = format.verticalAlignment(); + if (valign == QTextCharFormat::AlignSuperScript + || valign == QTextCharFormat::AlignSubScript + || !qFuzzyIsNull(baseLineOffset)) + { + QFontEngine *fe = f.d->engineForScript(si.analysis.script); + QFixed height = fe->ascent() + fe->descent(); + itemBaseLine -= height * QFixed::fromReal(baseLineOffset); + + if (valign == QTextCharFormat::AlignSubScript) + itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0); + else if (valign == QTextCharFormat::AlignSuperScript) + itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0); } - p->restore(); } - continue; - } + if (si.analysis.flags >= QScriptAnalysis::TabOrObject) { - unsigned short *logClusters = eng->logClusters(&si); - QGlyphLayout glyphs = eng->shapedGlyphs(&si); + if (eng->hasFormats()) { + p->save(); + if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) { + QFixed itemY = y - si.ascent; + switch (format.verticalAlignment()) { + case QTextCharFormat::AlignTop: + itemY = y - lineBase; + break; + case QTextCharFormat::AlignMiddle: + itemY = y - lineBase + (line.height() - si.height()) / 2; + break; + case QTextCharFormat::AlignBottom: + itemY = y - lineBase + line.height() - si.height(); + break; + default: + break; + } - QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart), - &f, eng->layoutData->string.unicode() + iterator.itemStart, - iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format); - gf.logClusters = logClusters + iterator.itemStart - si.position; - gf.width = iterator.itemWidth; - gf.justified = line.justified; - gf.initWithScriptItem(si); - - Q_ASSERT(gf.fontEngine); - - QPointF pos(iterator.x.toReal(), itemBaseLine.toReal()); - if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) { - QPainterPath path; - path.setFillRule(Qt::WindingFill); - - if (gf.glyphs.numGlyphs) - gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags); - if (gf.flags) { - const QFontEngine *fe = gf.fontEngine; - const qreal lw = fe->lineThickness().toReal(); - if (gf.flags & QTextItem::Underline) { - qreal offs = fe->underlinePosition().toReal(); - path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw); - } - if (gf.flags & QTextItem::Overline) { - qreal offs = fe->ascent().toReal() + 1; - path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); - } - if (gf.flags & QTextItem::StrikeOut) { - qreal offs = fe->ascent().toReal() / 3; - path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal()); + + eng->docLayout()->drawInlineObject(p, itemRect, + QTextInlineObject(iterator.item, eng), + si.position + eng->block.position(), + format); + if (selection) { + QBrush bg = format.brushProperty(ObjectSelectionBrush); + if (bg.style() != Qt::NoBrush) { + QColor c = bg.color(); + c.setAlpha(128); + p->fillRect(itemRect, c); + } + } + } else { // si.isTab + QFont f = eng->font(si); + QTextItemInt gf(si, &f, format); + gf.chars = nullptr; + gf.num_chars = 0; + gf.width = iterator.itemWidth; + QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng); + if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) { + const QChar visualTab = QChar(QChar::VisualTabCharacter); + int w = QFontMetrics(f).horizontalAdvance(visualTab); + qreal x = iterator.itemWidth.toReal() - w; // Right-aligned + if (x < 0) + p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(), + iterator.itemWidth.toReal(), line.height().toReal()), + Qt::IntersectClip); + else + x /= 2; // Centered + p->setFont(f); + p->drawText(QPointF(iterator.x.toReal() + x, + y.toReal()), visualTab); + } + + } + p->restore(); } + + continue; } - p->save(); - p->setRenderHint(QPainter::Antialiasing); - //Currently QPen with a Qt::NoPen style still returns a default - //QBrush which != Qt::NoBrush so we need this specialcase to reset it - if (p->pen().style() == Qt::NoPen) - p->setBrush(Qt::NoBrush); - else - p->setBrush(p->pen().brush()); + unsigned short *logClusters = eng->logClusters(&si); + QGlyphLayout glyphs = eng->shapedGlyphs(&si); - p->setPen(format.textOutline()); - p->drawPath(path); - p->restore(); - } else { - if (noText) - gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be - QPainterPrivate::get(p)->drawTextItem(pos, gf, eng); - } + QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart), + &f, eng->layoutData->string.unicode() + iterator.itemStart, + iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format); + gf.logClusters = logClusters + iterator.itemStart - si.position; + gf.width = iterator.itemWidth; + gf.justified = line.justified; + gf.initWithScriptItem(si); + + Q_ASSERT(gf.fontEngine); + + QPointF pos(iterator.x.toReal(), itemBaseLine.toReal()); + if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) { + QPainterPath path; + path.setFillRule(Qt::WindingFill); + + if (gf.glyphs.numGlyphs) + gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags); + if (gf.flags) { + const QFontEngine *fe = gf.fontEngine; + const qreal lw = fe->lineThickness().toReal(); + if (gf.flags & QTextItem::Underline) { + qreal offs = fe->underlinePosition().toReal(); + path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw); + } + if (gf.flags & QTextItem::Overline) { + qreal offs = fe->ascent().toReal() + 1; + path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + } + if (gf.flags & QTextItem::StrikeOut) { + qreal offs = fe->ascent().toReal() / 3; + path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw); + } + } - if ((si.analysis.flags == QScriptAnalysis::Space - || si.analysis.flags == QScriptAnalysis::Nbsp) - && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) { - QBrush c = format.foreground(); - if (c.style() != Qt::NoBrush) - p->setPen(c.color()); - const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0'; - QFont oldFont = p->font(); - p->setFont(eng->font(si)); - p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace); - p->setPen(pen); - p->setFont(oldFont); + p->save(); + p->setRenderHint(QPainter::Antialiasing); + //Currently QPen with a Qt::NoPen style still returns a default + //QBrush which != Qt::NoBrush so we need this specialcase to reset it + if (p->pen().style() == Qt::NoPen) + p->setBrush(Qt::NoBrush); + else + p->setBrush(p->pen().brush()); + + p->setPen(format.textOutline()); + p->drawPath(path); + p->restore(); + } else { + if (noText) + gf.glyphs.numGlyphs = 0; // slightly less elegant than it should be + QPainterPrivate::get(p)->drawTextItem(pos, gf, eng); + } + + if ((si.analysis.flags == QScriptAnalysis::Space + || si.analysis.flags == QScriptAnalysis::Nbsp) + && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) { + QBrush c = format.foreground(); + if (c.style() != Qt::NoBrush) + p->setPen(c.color()); + const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0'; + QFont oldFont = p->font(); + p->setFont(eng->font(si)); + p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace); + p->setPen(pen); + p->setFont(oldFont); + } } } eng->drawDecorations(p); |