OSDN Git Service

revert some changes for the sake of compatibility (for now)
authorIvailo Monev <xakepa10@gmail.com>
Thu, 20 Jan 2022 15:30:23 +0000 (17:30 +0200)
committerIvailo Monev <xakepa10@gmail.com>
Thu, 20 Jan 2022 15:30:23 +0000 (17:30 +0200)
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
src/gui/text/qfontengine.cpp
src/gui/text/qfontengine_ft.cpp
src/gui/text/qfontengine_ft_p.h
src/gui/text/qfontengine_p.h
src/gui/text/qfontmetrics.cpp
src/gui/text/qtextengine.cpp
src/gui/text/qtextengine_p.h

index 8841db5..46cb4fb 100644 (file)
@@ -376,6 +376,15 @@ void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyp
     }
 }
 
+glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs) const
+{
+    glyph_metrics_t overall;
+    overall.width = _size*glyphs.numGlyphs;
+    overall.height = _size;
+    overall.xoff = overall.width;
+    return overall;
+}
+
 glyph_metrics_t QFontEngineBox::boundingBox(glyph_t) const
 {
     return glyph_metrics_t(0, -_size, _size, _size, _size);
index 1422656..fbf264d 100644 (file)
@@ -635,6 +635,34 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlag
     }
 }
 
+glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) const
+{
+    glyph_metrics_t overall;
+    // initialize with line height, we get the same behaviour on all platforms
+    overall.y = -ascent();
+    overall.height = ascent() + descent() + 1;
+
+    QFixed ymax = 0;
+    QFixed xmax = 0;
+    for (int i = 0; i < glyphs.numGlyphs; i++) {
+        QFontMetric* metric = getMetrics(glyphs.glyphs[i]);
+        Q_ASSERT(metric);
+
+        QFixed x = overall.xoff - (-TRUNC(metric->left));
+        QFixed y = TRUNC(metric->top);
+        overall.x = qMin(overall.x, x);
+        overall.y = qMin(overall.y, y);
+        xmax = qMax(xmax, x + TRUNC(metric->right - metric->left));
+        ymax = qMax(ymax, y + TRUNC(metric->top - metric->bottom));
+        overall.xoff += qRound(TRUNC(metric->advancex));
+
+    }
+    overall.height = qMax(overall.height, ymax - overall.y);
+    overall.width = xmax - overall.x;
+
+    return overall;
+}
+
 glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) const
 {
     glyph_metrics_t overall;
index 63e044f..44057cf 100644 (file)
@@ -130,6 +130,7 @@ public:
     virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
                       QTextEngine::ShaperFlags flags) const;
 
+    virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) const;
     virtual glyph_metrics_t boundingBox(glyph_t glyph) const;
 
     virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const;
index fb184e3..ad9688b 100644 (file)
@@ -116,6 +116,7 @@ public:
 
     virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *);
 
+    virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) const = 0;
     virtual glyph_metrics_t boundingBox(glyph_t glyph) const = 0;
 
     virtual QFixed ascent() const = 0;
@@ -183,6 +184,7 @@ public:
     virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
                                  QPainterPath *path);
 
+    virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) const;
     virtual glyph_metrics_t boundingBox(glyph_t glyph) const;
 
     virtual QFixed ascent() const;
index 2ecc495..283ab9c 100644 (file)
@@ -409,7 +409,8 @@ bool QFontMetrics::inFontUcs4(uint ucs4) const
 */
 int QFontMetrics::width(const QString &text) const
 {
-    return boundingRect(QRect(0,0,0,0), Qt::TextDontPrint, text).width();
+    QTextEngine engine(text, d.data());
+    return qRound(engine.width(0, text.size()).toReal());
 }
 
 /*!
@@ -476,10 +477,9 @@ QRect QFontMetrics::boundingRect(const QString &text) const
     if (text.isEmpty())
         return QRect();
 
-    QPainterPath textpath;
-    textpath.setFillRule(Qt::WindingFill);
-    textpath.addText(QPointF(), QFont(d.data()), text);
-    return textpath.boundingRect().toRect();
+    QTextEngine engine(text, d.data());
+    glyph_metrics_t gm = engine.boundingBox(0, text.size());
+    return QRect(qRound(gm.x.toReal()), qRound(gm.y.toReal()), qRound(gm.width.toReal()), qRound(gm.height.toReal()));
 }
 
 /*!
@@ -560,114 +560,6 @@ QSize QFontMetrics::size(int flags, const QString &text) const
     return boundingRect(QRect(0,0,0,0), flags | Qt::TextLongestVariant, text).size();
 }
 
-static inline QString qt_mnemonic_mid(const QString &text, const int pos, const bool showmnemonic)
-{
-    if (showmnemonic && pos > 0 && text.at(pos - 1) == QLatin1Char('&') && text.at(pos) != QLatin1Char('&')) {
-        return text.mid(0, pos - 1);
-    }
-    return text.mid(0, pos);
-}
-
-static inline QString qt_elided_text(const QString &text, Qt::TextElideMode mode, qreal width, int flags, const QFontPrivate* font)
-{
-    // qDebug() << Q_FUNC_INFO << text << mode << width << flags;
-
-    static const QTextEngine::ShaperFlags shaperflags = 0;
-
-    const bool showmnemonic = (flags & Qt::TextShowMnemonic);
-    int i = 0;
-    qreal xoffset = 0.0;
-    QUnicodeTables::Script inheritedscript = QUnicodeTables::Common;
-    for (i = 0; i < text.size(); i++) {
-        int nglyphs = 1;
-        QChar textchars[2] = { text.at(i), 0 };
-        uint ucs4 = textchars[0].unicode();
-        if (textchars[0].isHighSurrogate() && (i + 1) < text.size() && text.at(i + 1).isLowSurrogate()) {
-            textchars[1] = text.at(i + 1);
-            ucs4 = QChar::surrogateToUcs4(textchars[0], textchars[1]);
-            i++;
-            nglyphs = 2;
-            // qDebug() << Q_FUNC_INFO << ucs4;
-        }
-
-        QUnicodeTables::Script script = QUnicodeTables::script(ucs4);
-        if (script == QUnicodeTables::Inherited) {
-            // qDebug() << Q_FUNC_INFO << "inherited" << ucs4;
-            script = inheritedscript;
-        } else {
-            inheritedscript = script;
-        }
-
-        QFontEngine* engine = font->engineForScript(script);
-        if (Q_UNLIKELY(!engine)) {
-            qWarning("qt_elided_text: No font engine for script %d", int(script));
-            continue;
-        }
-
-        switch (QChar::category(ucs4)) {
-            case QChar::Separator_Line:
-            case QChar::Other_Control:
-            case QChar::Other_Format: {
-                // qDebug() << Q_FUNC_INFO << ucs4;
-                continue;
-            }
-            default: {
-                break;
-            }
-        }
-
-        if (showmnemonic && char(ucs4) == char('&')
-            && (i + 1) < text.size() && text.at(i + 1) != QLatin1Char('&')) {
-            continue;
-        }
-
-        QGlyphLayoutArray<2> glyphs;
-        engine->stringToCMap(textchars, nglyphs, &glyphs, &nglyphs, shaperflags);
-
-        xoffset += glyphs.advances_x[0].toReal();
-        if (nglyphs == 2) {
-            xoffset += glyphs.advances_x[1].toReal();
-        }
-        Q_ASSERT(nglyphs < 3);
-
-        if (xoffset > width) {
-            break;
-        }
-    }
-
-    int elidedlength = i;
-    // qDebug() << Q_FUNC_INFO << text << width << xoffset << elidedlength;
-
-    if (elidedlength <= 0) {
-        return QString();
-    } else if (elidedlength <= 3 && text.size() > 0) {
-        return (QString(text.at(0)) + QChar(0x2026));
-    } else if (text.size() <= elidedlength) {
-        return text;
-    }
-
-    switch (mode) {
-        case Qt::ElideLeft: {
-            return (QLatin1String("...") + qt_mnemonic_mid(text, elidedlength - 3, showmnemonic));
-        }
-        case Qt::ElideRight: {
-            return (qt_mnemonic_mid(text, elidedlength - 3, showmnemonic) + QLatin1String("..."));
-        }
-        case Qt::ElideMiddle: {
-            if (elidedlength <= 5) {
-                return text;
-            }
-            // TODO: mnemonics chopping on the left and right
-            const int midlength = (elidedlength / 2);
-            return (text.left(midlength - 2) + QLatin1String("...") + text.right(midlength - 1));
-        }
-        case Qt::ElideNone: {
-            return qt_mnemonic_mid(text, elidedlength, showmnemonic);
-        }
-    }
-    Q_UNREACHABLE();
-}
-
 /*!
     \since 4.2
 
@@ -686,7 +578,8 @@ static inline QString qt_elided_text(const QString &text, Qt::TextElideMode mode
 */
 QString QFontMetrics::elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const
 {
-    return qt_elided_text(text, mode, qreal(width), flags, d.data());
+    QTextEngine engine(text, d.data());
+    return engine.elidedText(mode, QFixed(width), flags);
 }
 
 /*!
@@ -1106,7 +999,8 @@ bool QFontMetricsF::inFontUcs4(uint ucs4) const
 */
 qreal QFontMetricsF::width(const QString &text) const
 {
-    return boundingRect(QRectF(0,0,0,0), Qt::TextDontPrint, text).width();
+    QTextEngine engine(text, d.data());
+    return engine.width(0, text.size()).toReal();
 }
 
 /*!
@@ -1173,10 +1067,12 @@ QRectF QFontMetricsF::boundingRect(const QString &text) const
     if (text.isEmpty())
         return QRectF();
 
-    QPainterPath textpath;
-    textpath.setFillRule(Qt::WindingFill);
-    textpath.addText(QPointF(), QFont(d.data()), text);
-    return textpath.boundingRect();
+    if (text.isEmpty())
+        return QRect();
+
+    QTextEngine engine(text, d.data());
+    glyph_metrics_t gm = engine.boundingBox(0, text.size());
+    return QRectF(gm.x.toReal(), gm.y.toReal(), gm.width.toReal(), gm.height.toReal());
 }
 
 /*!
@@ -1275,7 +1171,8 @@ QSizeF QFontMetricsF::size(int flags, const QString &text) const
 */
 QString QFontMetricsF::elidedText(const QString &text, Qt::TextElideMode mode, qreal width, int flags) const
 {
-    return qt_elided_text(text, mode, width, flags, d.data());
+    QTextEngine engine(text, d.data());
+    return engine.elidedText(mode, QFixed::fromReal(width), flags);
 }
 
 /*!
index d8d1606..fad4bc2 100644 (file)
@@ -480,6 +480,66 @@ QFixed QTextEngine::width(int from, int len) const
     return w;
 }
 
+glyph_metrics_t QTextEngine::boundingBox(int from,  int len) const
+{
+    itemize();
+
+    glyph_metrics_t gm;
+
+    for (int i = 0; i < layoutData->items.size(); i++) {
+        const QScriptItem *si = layoutData->items.constData() + i;
+
+        int pos = si->position;
+        int ilen = length(i);
+        if (pos > from + len)
+            break;
+        if (pos + ilen > from) {
+            if (!si->num_glyphs)
+                shape(i);
+
+            if (si->analysis.flags == QScriptAnalysis::Object) {
+                gm.width += si->width;
+                continue;
+            } else if (si->analysis.flags == QScriptAnalysis::Tab) {
+                gm.width += calculateTabWidth(i, gm.width);
+                continue;
+            }
+
+            unsigned short *logClusters = this->logClusters(si);
+            QGlyphLayout glyphs = shapedGlyphs(si);
+
+            // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
+            int charFrom = from - pos;
+            if (charFrom < 0)
+                charFrom = 0;
+            int glyphStart = logClusters[charFrom];
+            if (charFrom > 0 && logClusters[charFrom-1] == glyphStart)
+                while (charFrom < ilen && logClusters[charFrom] == glyphStart)
+                    charFrom++;
+            if (charFrom < ilen) {
+                QFontEngine *fe = fontEngine(*si);
+                glyphStart = logClusters[charFrom];
+                int charEnd = from + len - 1 - pos;
+                if (charEnd >= ilen)
+                    charEnd = ilen-1;
+                int glyphEnd = logClusters[charEnd];
+                while (charEnd < ilen && logClusters[charEnd] == glyphEnd)
+                    charEnd++;
+                glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
+                if (glyphStart <= glyphEnd ) {
+                    glyph_metrics_t m = fe->boundingBox(glyphs.mid(glyphStart, glyphEnd - glyphStart));
+                    gm.x = qMin(gm.x, m.x + gm.xoff);
+                    gm.y = qMin(gm.y, m.y);
+                    gm.width = qMax(gm.width, m.width + gm.xoff);
+                    gm.height = qMax(gm.height, m.height);
+                    gm.xoff += m.xoff;
+                }
+            }
+        }
+    }
+    return gm;
+}
+
 QFont QTextEngine::font(const QScriptItem &si) const
 {
     QFont font = fnt;
@@ -890,6 +950,187 @@ void QTextEngine::indexAdditionalFormats()
     }
 }
 
+/* These two helper functions are used to determine whether we need to insert a ZWJ character
+   between the text that gets truncated and the ellipsis. This is important to get
+   correctly shaped results for arabic text.
+*/
+static inline bool nextCharJoins(const QString &string, int pos)
+{
+    while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing)
+        ++pos;
+    if (pos == string.length())
+        return false;
+    return string.at(pos).joining() != QChar::OtherJoining;
+}
+
+static inline bool prevCharJoins(const QString &string, int pos)
+{
+    while (pos > 0 && string.at(pos - 1).category() == QChar::Mark_NonSpacing)
+        --pos;
+    if (pos == 0)
+        return false;
+    QChar::Joining joining = string.at(pos - 1).joining();
+    return (joining == QChar::Dual);
+}
+
+QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const
+{
+    // qDebug() << "elidedText; available width" << width.toReal() << "text width:" << this->width(0, layoutData->string.length()).toReal();
+
+    if (flags & Qt::TextShowMnemonic) {
+        itemize();
+        HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes());
+        if (!attributes)
+            return QString();
+        for (int i = 0; i < layoutData->items.size(); ++i) {
+            QScriptItem &si = layoutData->items[i];
+            if (!si.num_glyphs)
+                shape(i);
+
+            unsigned short *logClusters = this->logClusters(&si);
+            QGlyphLayout glyphs = shapedGlyphs(&si);
+
+            const int end = si.position + length(&si);
+            for (int i = si.position; i < end - 1; ++i) {
+                if (layoutData->string.at(i) == QLatin1Char('&')) {
+                    const int gp = logClusters[i - si.position];
+                    glyphs.attributes[gp].dontPrint = true;
+                    attributes[i + 1].charStop = false;
+                    attributes[i + 1].whiteSpace = false;
+                    attributes[i + 1].lineBreakType = HB_NoBreak;
+                    if (layoutData->string.at(i + 1) == QLatin1Char('&'))
+                        ++i;
+                }
+            }
+        }
+    }
+
+    validate();
+
+    if (mode == Qt::ElideNone
+        || this->width(0, layoutData->string.length()) <= width
+        || layoutData->string.length() <= 1)
+        return layoutData->string;
+
+    QFixed ellipsisWidth;
+    QString ellipsisText;
+    {
+        QChar ellipsisChar(0x2026);
+
+        QFontEngine *fe = fnt.d->engineForScript(QUnicodeTables::Common);
+
+        QGlyphLayoutArray<1> ellipsisGlyph;
+        {
+            if (fe->canRender(&ellipsisChar, 1)) {
+                int nGlyphs = 1;
+                fe->stringToCMap(&ellipsisChar, 1, &ellipsisGlyph, &nGlyphs, 0);
+            }
+        }
+
+        if (ellipsisGlyph.glyphs[0]) {
+            ellipsisWidth = ellipsisGlyph.advances_x[0];
+            ellipsisText = ellipsisChar;
+        } else {
+            QString dotDotDot(QLatin1String("..."));
+
+            QGlyphLayoutArray<3> glyphs;
+            int nGlyphs = 3;
+            if (!fe->stringToCMap(dotDotDot.constData(), 3, &glyphs, &nGlyphs, 0))
+                // should never happen...
+                return layoutData->string;
+            for (int i = 0; i < nGlyphs; ++i)
+                ellipsisWidth += glyphs.advances_x[i];
+            ellipsisText = dotDotDot;
+        }
+    }
+
+    const QFixed availableWidth = width - ellipsisWidth;
+    if (availableWidth < 0)
+        return QString();
+
+    const HB_CharAttributes *attributes = this->attributes();
+    if (!attributes)
+        return QString();
+
+    if (mode == Qt::ElideRight) {
+        QFixed currentWidth;
+        int pos;
+        int nextBreak = 0;
+
+        do {
+            pos = nextBreak;
+
+            ++nextBreak;
+            while (nextBreak < layoutData->string.length() && !attributes[nextBreak].charStop)
+                ++nextBreak;
+
+            currentWidth += this->width(pos, nextBreak - pos);
+        } while (nextBreak < layoutData->string.length()
+                 && currentWidth < availableWidth);
+
+        if (nextCharJoins(layoutData->string, pos))
+            ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
+
+        return layoutData->string.left(pos) + ellipsisText;
+    } else if (mode == Qt::ElideLeft) {
+        QFixed currentWidth;
+        int pos;
+        int nextBreak = layoutData->string.length();
+
+        do {
+            pos = nextBreak;
+
+            --nextBreak;
+            while (nextBreak > 0 && !attributes[nextBreak].charStop)
+                --nextBreak;
+
+            currentWidth += this->width(nextBreak, pos - nextBreak);
+        } while (nextBreak > 0
+                 && currentWidth < availableWidth);
+
+        if (prevCharJoins(layoutData->string, pos))
+            ellipsisText.append(QChar(0x200d) /* ZWJ */);
+
+        return ellipsisText + layoutData->string.mid(pos);
+    } else if (mode == Qt::ElideMiddle) {
+        QFixed leftWidth;
+        QFixed rightWidth;
+
+        int leftPos = 0;
+        int nextLeftBreak = 0;
+
+        int rightPos = layoutData->string.length();
+        int nextRightBreak = layoutData->string.length();
+
+        do {
+            leftPos = nextLeftBreak;
+            rightPos = nextRightBreak;
+
+            ++nextLeftBreak;
+            while (nextLeftBreak < layoutData->string.length() && !attributes[nextLeftBreak].charStop)
+                ++nextLeftBreak;
+
+            --nextRightBreak;
+            while (nextRightBreak > 0 && !attributes[nextRightBreak].charStop)
+                --nextRightBreak;
+
+            leftWidth += this->width(leftPos, nextLeftBreak - leftPos);
+            rightWidth += this->width(nextRightBreak, rightPos - nextRightBreak);
+        } while (nextLeftBreak < layoutData->string.length()
+                 && nextRightBreak > 0
+                 && leftWidth + rightWidth < availableWidth);
+
+        if (nextCharJoins(layoutData->string, leftPos))
+            ellipsisText.prepend(QChar(0x200d) /* ZWJ */);
+        if (prevCharJoins(layoutData->string, rightPos))
+            ellipsisText.append(QChar(0x200d) /* ZWJ */);
+
+        return layoutData->string.left(leftPos) + ellipsisText + layoutData->string.mid(rightPos);
+    }
+
+    return layoutData->string;
+}
+
 void QTextEngine::setBoundary(int strPos) const
 {
     if (strPos <= 0 || strPos >= layoutData->string.length())
index 452b906..537779d 100644 (file)
@@ -366,6 +366,7 @@ public:
     QFixed alignLine(const QScriptLine &line);
 
     QFixed width(int charFrom, int numChars) const;
+    glyph_metrics_t boundingBox(int from,  int len) const;
 
     int length(int item) const {
         int from = layoutData->items[item].position;
@@ -464,6 +465,8 @@ public:
     bool atSpace(int position) const;
     void indexAdditionalFormats();
 
+    QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const;
+
     void shapeLine(const QScriptLine &line);
 
     QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);