diff options
author | Eskil Abrahamsen Blomfeldt <[email protected]> | 2024-04-08 14:38:32 +0200 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <[email protected]> | 2024-04-26 12:30:53 +0200 |
commit | 1593b1f6d610ce9bdb9dff4104f7f447e03a9585 (patch) | |
tree | 6e93417d16dfaff4031fb9f51c371e5ea0e481eb | |
parent | d04deafed932d955e2e94324ec5b1a261fdb3332 (diff) |
Introduce optional smarter font merging with ContextFontMerging
This introduces an optional, slightly more expensive approach
to font merging which takes the full string into account,
instead of just going character by character.
This addresses the issue that you may sometimes get multiple fonts
to cover one string of text in a single language. With Chinese,
this is especially an issue because many fonts will only support
parts of the very large character set.
The new algorithm detects if the string was incompletely covered
by the font and tries the fallback fonts in order to find the best
match. This is obviously more expensive, especially if no perfect
match is found and we have to check all the fallbacks in the list,
but it is opt-in and only enabled if the ContextFontMerging flag
is set.
Task-number: QTBUG-121131
Change-Id: I8c7874d0918640bd83418e3c4726c89f43a220a3
Reviewed-by: Eirik Aavitsland <[email protected]>
-rw-r--r-- | src/gui/text/coretext/qfontengine_coretext.mm | 30 | ||||
-rw-r--r-- | src/gui/text/coretext/qfontengine_coretext_p.h | 2 | ||||
-rw-r--r-- | src/gui/text/freetype/qfontengine_ft.cpp | 11 | ||||
-rw-r--r-- | src/gui/text/freetype/qfontengine_ft_p.h | 2 | ||||
-rw-r--r-- | src/gui/text/qfont.cpp | 10 | ||||
-rw-r--r-- | src/gui/text/qfont.h | 1 | ||||
-rw-r--r-- | src/gui/text/qfontengine.cpp | 79 | ||||
-rw-r--r-- | src/gui/text/qfontengine_p.h | 17 | ||||
-rw-r--r-- | src/gui/text/qrawfont.cpp | 4 | ||||
-rw-r--r-- | src/gui/text/qtextengine.cpp | 17 | ||||
-rw-r--r-- | src/gui/text/qtextengine_p.h | 1 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontengine.cpp | 16 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontengine_p.h | 4 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontenginedirectwrite.cpp | 16 | ||||
-rw-r--r-- | src/gui/text/windows/qwindowsfontenginedirectwrite_p.h | 4 | ||||
-rw-r--r-- | tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp | 4 |
16 files changed, 152 insertions, 66 deletions
diff --git a/src/gui/text/coretext/qfontengine_coretext.mm b/src/gui/text/coretext/qfontengine_coretext.mm index b6fef2fecbd..1050c03d75b 100644 --- a/src/gui/text/coretext/qfontengine_coretext.mm +++ b/src/gui/text/coretext/qfontengine_coretext.mm @@ -13,6 +13,7 @@ #include <private/qcoregraphics_p.h> #include <private/qimage_p.h> #include <private/qguiapplication_p.h> +#include <private/qstringiterator_p.h> #include <qpa/qplatformtheme.h> #include <cmath> @@ -274,29 +275,30 @@ glyph_t QCoreTextFontEngine::glyphIndex(uint ucs4) const return glyphIndices[0]; } -bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, - int *nglyphs, QFontEngine::ShaperFlags flags) const +int QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } QVarLengthArray<CGGlyph> cgGlyphs(len); CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); int glyph_pos = 0; - for (int i = 0; i < len; ++i) { - glyphs->glyphs[glyph_pos] = cgGlyphs[i]; - if (glyph_pos < i) - cgGlyphs[glyph_pos] = cgGlyphs[i]; - glyph_pos++; - - // If it's a non-BMP char, skip the lower part of surrogate pair and go - // directly to the next char without increasing glyph_pos - if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) - ++i; + int mappedGlyphs = 0; + QStringIterator it(str, str + len); + while (it.hasNext()) { + qsizetype idx = it.index(); + char32_t ucs4 = it.next(); + glyphs->glyphs[glyph_pos] = cgGlyphs[idx]; + if (glyph_pos < idx) + cgGlyphs[glyph_pos] = cgGlyphs[idx]; + if (glyphs->glyphs[glyph_pos] != 0 || isIgnorableChar(ucs4)) + mappedGlyphs++; + glyph_pos++; } *nglyphs = glyph_pos; @@ -305,7 +307,7 @@ bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout * if (!(flags & GlyphIndicesOnly)) loadAdvancesForGlyphs(cgGlyphs, glyphs); - return true; + return mappedGlyphs; } glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) diff --git a/src/gui/text/coretext/qfontengine_coretext_p.h b/src/gui/text/coretext/qfontengine_coretext_p.h index af87f5f6f3a..2f388c32bcf 100644 --- a/src/gui/text/coretext/qfontengine_coretext_p.h +++ b/src/gui/text/coretext/qfontengine_coretext_p.h @@ -38,7 +38,7 @@ public: ~QCoreTextFontEngine(); glyph_t glyphIndex(uint ucs4) const override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; void recalcAdvances(QGlyphLayout *, ShaperFlags) const override; glyph_metrics_t boundingBox(glyph_t glyph) override; diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 72d2c72fe31..d3791f1f6e7 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -1678,15 +1678,16 @@ glyph_t QFontEngineFT::glyphIndex(uint ucs4) const return glyph; } -bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, +int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } + int mappedGlyphs = 0; int glyph_pos = 0; if (freetype->symbol_map) { FT_Face face = freetype->face; @@ -1719,6 +1720,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (uc < QFreetypeFace::cmapCacheSize) freetype->cmapCache[uc] = glyph; } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } else { @@ -1740,6 +1743,8 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs freetype->cmapCache[uc] = glyph; } } + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + mappedGlyphs++; ++glyph_pos; } } @@ -1750,7 +1755,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, flags); - return true; + return mappedGlyphs; } bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h index c8e5e20d371..bdd45498272 100644 --- a/src/gui/text/freetype/qfontengine_ft_p.h +++ b/src/gui/text/freetype/qfontengine_ft_p.h @@ -186,7 +186,7 @@ private: void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override; glyph_metrics_t boundingBox(glyph_t glyph) override; diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 7fd590c3558..f3b861957ea 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -1462,6 +1462,14 @@ QFont::StyleHint QFont::styleHint() const \value NoAntialias don't antialias the fonts. \value NoSubpixelAntialias avoid subpixel antialiasing on the fonts if possible. \value PreferAntialias antialias if possible. + \value [since 6.8] ContextFontMerging If the selected font does not contain a certain character, + then Qt automatically chooses a similar-looking fallback font that contains the + character. By default this is done on a character-by-character basis. This means that in + certain uncommon cases, multiple fonts may be used to represent one string of text even + if it's in the same script. Setting \c ContextFontMerging will try finding the fallback + font that matches the largest subset of the input string instead. This will be more + expensive for strings where missing glyphs occur, but may give more consistent results. + If \c NoFontMerging is set, then \c ContextFontMerging will have no effect. \value NoFontMerging If the font selected for a certain writing system does not contain a character requested to draw, then Qt automatically chooses a similar looking font that contains the character. The NoFontMerging flag disables this feature. @@ -3266,7 +3274,7 @@ bool QFontInfo::fixedPitch() const QChar ch[2] = { u'i', u'm' }; QGlyphLayoutArray<2> g; int l = 2; - if (!engine->stringToCMap(ch, 2, &g, &l, {})) + if (engine->stringToCMap(ch, 2, &g, &l, {}) < 0) Q_UNREACHABLE(); Q_ASSERT(l == 2); engine->fontDef.fixedPitch = g.advances[0] == g.advances[1]; diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index ace07780b5b..66a5f7c155a 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -47,6 +47,7 @@ public: NoAntialias = 0x0100, NoSubpixelAntialias = 0x0800, PreferNoShaping = 0x1000, + ContextFontMerging = 0x2000, NoFontMerging = 0x8000 }; Q_ENUM(StyleStrategy) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index dff400c18bc..4e78aaac2e4 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -13,6 +13,7 @@ #include "qpainter.h" #include "qpainterpath.h" #include "qvarlengtharray.h" +#include "qtextengine_p.h" #include <qmath.h> #include <qendian.h> #include <private/qstringiterator_p.h> @@ -1542,12 +1543,12 @@ glyph_t QFontEngineBox::glyphIndex(uint ucs4) const return 1; } -bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const +int QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } int ucs4Length = 0; @@ -1563,7 +1564,7 @@ bool QFontEngineBox::stringToCMap(const QChar *str, int len, QGlyphLayout *glyph if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, flags); - return true; + return *nglyphs; } void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const @@ -1793,11 +1794,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const { glyph_t glyph = engine(0)->glyphIndex(ucs4); - if (glyph == 0 - && ucs4 != QChar::LineSeparator - && ucs4 != QChar::LineFeed - && ucs4 != QChar::CarriageReturn - && ucs4 != QChar::ParagraphSeparator) { + if (glyph == 0 && !isIgnorableChar(ucs4)) { if (!m_fallbackFamiliesQueried) const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried(); for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) { @@ -1824,13 +1821,55 @@ glyph_t QFontEngineMulti::glyphIndex(uint ucs4) const return glyph; } -bool QFontEngineMulti::stringToCMap(const QChar *str, int len, - QGlyphLayout *glyphs, int *nglyphs, - QFontEngine::ShaperFlags flags) const +int QFontEngineMulti::stringToCMap(const QChar *str, int len, + QGlyphLayout *glyphs, int *nglyphs, + QFontEngine::ShaperFlags flags) const { - if (!engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags)) - return false; + const int originalNumGlyphs = glyphs->numGlyphs; + int mappedGlyphCount = engine(0)->stringToCMap(str, len, glyphs, nglyphs, flags); + if (mappedGlyphCount < 0) + return -1; + + // If ContextFontMerging is set and the match for the string was incomplete, we try all + // fallbacks on the full string until we find the best match. + bool contextFontMerging = mappedGlyphCount < *nglyphs && (fontDef.styleStrategy & QFont::ContextFontMerging); + if (contextFontMerging) { + QVarLengthGlyphLayoutArray tempLayout(len); + if (!m_fallbackFamiliesQueried) + const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried(); + int maxGlyphCount = 0; + uchar engineIndex = 0; + for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) { + int numGlyphs = len; + const_cast<QFontEngineMulti *>(this)->ensureEngineAt(x); + maxGlyphCount = engine(x)->stringToCMap(str, len, &tempLayout, &numGlyphs, flags); + + // If we found a better match, we copy data into the main QGlyphLayout + if (maxGlyphCount > mappedGlyphCount) { + *nglyphs = numGlyphs; + glyphs->numGlyphs = originalNumGlyphs; + glyphs->copy(&tempLayout); + engineIndex = x; + if (maxGlyphCount == numGlyphs) + break; + } + } + + if (engineIndex > 0) { + for (int y = 0; y < glyphs->numGlyphs; ++y) { + if (glyphs->glyphs[y] != 0) + glyphs->glyphs[y] |= (engineIndex << 24); + } + } else { + contextFontMerging = false; + } + + mappedGlyphCount = maxGlyphCount; + } + + // Fill in missing glyphs by going through string one character at the time and finding + // the first viable fallback. int glyph_pos = 0; QStringIterator it(str, str + len); @@ -1861,15 +1900,10 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, lastFallback = -1; } - if (glyphs->glyphs[glyph_pos] == 0 - && ucs4 != QChar::LineSeparator - && ucs4 != QChar::LineFeed - && ucs4 != QChar::CarriageReturn - && ucs4 != QChar::ParagraphSeparator - && QChar::category(ucs4) != QChar::Other_Control) { + if (glyphs->glyphs[glyph_pos] == 0 && !isIgnorableChar(ucs4)) { if (!m_fallbackFamiliesQueried) const_cast<QFontEngineMulti *>(this)->ensureFallbackFamiliesQueried(); - for (int x = 1, n = qMin(m_engines.size(), 256); x < n; ++x) { + for (int x = contextFontMerging ? 0 : 1, n = qMin(m_engines.size(), 256); x < n; ++x) { QFontEngine *engine = m_engines.at(x); if (!engine) { if (!shouldLoadFontEngineForCharacter(x, ucs4)) @@ -1946,8 +1980,7 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, *nglyphs = glyph_pos; glyphs->numGlyphs = glyph_pos; - - return true; + return mappedGlyphCount; } bool QFontEngineMulti::shouldLoadFontEngineForCharacter(int at, uint ucs4) const @@ -2239,7 +2272,7 @@ bool QFontEngineMulti::canRender(const QChar *string, int len) const QGlyphLayout g; g.numGlyphs = nglyphs; g.glyphs = glyphs.data(); - if (!stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly)) + if (stringToCMap(string, len, &g, &nglyphs, GlyphIndicesOnly) < 0) Q_UNREACHABLE(); for (int i = 0; i < nglyphs; i++) { diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index af44eeab3f7..a0e08013545 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -76,7 +76,8 @@ public: enum ShaperFlag { DesignMetrics = 0x0002, - GlyphIndicesOnly = 0x0004 + GlyphIndicesOnly = 0x0004, + FullStringFallback = 0x008 }; Q_DECLARE_FLAGS(ShaperFlags, ShaperFlag) @@ -148,12 +149,20 @@ public: } bool isColorFont() const { return glyphFormat == Format_ARGB; } + static bool isIgnorableChar(char32_t ucs4) + { + return ucs4 == QChar::LineSeparator + || ucs4 == QChar::LineFeed + || ucs4 == QChar::CarriageReturn + || ucs4 == QChar::ParagraphSeparator + || QChar::category(ucs4) == QChar::Other_Control; + } virtual QFixed emSquareSize() const { return ascent(); } /* returns 0 as glyph index for non existent glyphs */ virtual glyph_t glyphIndex(uint ucs4) const = 0; - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0; + virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const = 0; virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const {} virtual void doKerning(QGlyphLayout *, ShaperFlags) const; @@ -393,7 +402,7 @@ public: ~QFontEngineBox(); virtual glyph_t glyphIndex(uint ucs4) const override; - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; virtual void recalcAdvances(QGlyphLayout *, ShaperFlags) const override; void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si); @@ -431,7 +440,7 @@ public: ~QFontEngineMulti(); virtual glyph_t glyphIndex(uint ucs4) const override; - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + virtual int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override; virtual glyph_metrics_t boundingBox(glyph_t glyph) override; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index c5a92f95cb5..54676b3560b 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -498,7 +498,7 @@ QList<quint32> QRawFont::glyphIndexesForString(const QString &text) const QGlyphLayout glyphs; glyphs.numGlyphs = numGlyphs; glyphs.glyphs = glyphIndexes.data(); - if (!d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly)) + if (d->fontEngine->stringToCMap(text.data(), text.size(), &glyphs, &numGlyphs, QFontEngine::GlyphIndicesOnly) < 0) Q_UNREACHABLE(); glyphIndexes.resize(numGlyphs); @@ -531,7 +531,7 @@ bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *g QGlyphLayout glyphs; glyphs.numGlyphs = *numGlyphs; glyphs.glyphs = glyphIndexes; - return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly); + return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QFontEngine::GlyphIndicesOnly) >= 0; } /*! diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index cb945b73ce7..a18157ab9ba 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1445,7 +1445,7 @@ void QTextEngine::shapeText(int item) const shapingEnabled ? QFontEngine::GlyphIndicesOnly : QFontEngine::ShaperFlag(0); - if (!fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags)) + if (fontEngine->stringToCMap(reinterpret_cast<const QChar *>(string), itemLength, &initialGlyphs, &nGlyphs, shaperFlags) < 0) Q_UNREACHABLE(); } @@ -2741,6 +2741,21 @@ bool QTextEngine::LayoutData::reallocate(int totalGlyphs) return true; } +void QGlyphLayout::copy(QGlyphLayout *oldLayout) +{ + Q_ASSERT(offsets != oldLayout->offsets); + + int n = std::min(numGlyphs, oldLayout->numGlyphs); + + memcpy(offsets, oldLayout->offsets, n * sizeof(QFixedPoint)); + memcpy(attributes, oldLayout->attributes, n * sizeof(QGlyphAttributes)); + memcpy(justifications, oldLayout->justifications, n * sizeof(QGlyphJustification)); + memcpy(advances, oldLayout->advances, n * sizeof(QFixed)); + memcpy(glyphs, oldLayout->glyphs, n * sizeof(glyph_t)); + + numGlyphs = n; +} + // grow to the new size, copying the existing data to the new layout void QGlyphLayout::grow(char *address, int totalGlyphs) { diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index a829265a229..c01d3a07113 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -224,6 +224,7 @@ struct QGlyphLayout return reinterpret_cast<char *>(offsets); } + void copy(QGlyphLayout *other); void grow(char *address, int totalGlyphs); }; diff --git a/src/gui/text/windows/qwindowsfontengine.cpp b/src/gui/text/windows/qwindowsfontengine.cpp index fe078973696..5de80dc8a32 100644 --- a/src/gui/text/windows/qwindowsfontengine.cpp +++ b/src/gui/text/windows/qwindowsfontengine.cpp @@ -132,8 +132,9 @@ void QWindowsFontEngine::getCMap() } } -int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const +int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const { + *mappedGlyphs = 0; int glyph_pos = 0; { if (symbol) { @@ -143,6 +144,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); if (!glyphs->glyphs[glyph_pos] && uc < 0x100) glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000); + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + (*mappedGlyphs)++; ++glyph_pos; } } else if (ttf) { @@ -150,6 +153,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa while (it.hasNext()) { const uint uc = it.next(); glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + (*mappedGlyphs)++; ++glyph_pos; } } else { @@ -160,6 +165,8 @@ int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLa glyphs->glyphs[glyph_pos] = uc; else glyphs->glyphs[glyph_pos] = 0; + if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc)) + (*mappedGlyphs)++; ++glyph_pos; } } @@ -259,7 +266,7 @@ HGDIOBJ QWindowsFontEngine::selectDesignFont() const return SelectObject(m_fontEngineData->hdc, designFont); } -bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const +int QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { @@ -268,12 +275,13 @@ bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *g } glyphs->numGlyphs = *nglyphs; - *nglyphs = getGlyphIndexes(str, len, glyphs); + int mappedGlyphs; + *nglyphs = getGlyphIndexes(str, len, glyphs, &mappedGlyphs); if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, flags); - return true; + return mappedGlyphs; } inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) diff --git a/src/gui/text/windows/qwindowsfontengine_p.h b/src/gui/text/windows/qwindowsfontengine_p.h index afe8ee4ca58..07f4db3c4ab 100644 --- a/src/gui/text/windows/qwindowsfontengine_p.h +++ b/src/gui/text/windows/qwindowsfontengine_p.h @@ -48,7 +48,7 @@ public: QFixed emSquareSize() const override; glyph_t glyphIndex(uint ucs4) const override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override; void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const override; void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override; @@ -88,7 +88,7 @@ public: bool hasUnreliableGlyphOutline() const override; - int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs) const; + int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, int *mappedGlyphs) const; void getCMap(); bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const; diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp index 2070deb2966..47b8a7ee3c2 100644 --- a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp @@ -435,13 +435,13 @@ glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const return glyphIndex; } -bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, - int *nglyphs, QFontEngine::ShaperFlags flags) const +int QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QFontEngine::ShaperFlags flags) const { Q_ASSERT(glyphs->numGlyphs >= *nglyphs); if (*nglyphs < len) { *nglyphs = len; - return false; + return -1; } QVarLengthArray<UINT32> codePoints(len); @@ -455,11 +455,15 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly glyphIndices.data()); if (FAILED(hr)) { qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__); - return false; + return -1; } - for (int i = 0; i < actualLength; ++i) + int mappedGlyphs = 0; + for (int i = 0; i < actualLength; ++i) { glyphs->glyphs[i] = glyphIndices.at(i); + if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i))) + mappedGlyphs++; + } *nglyphs = actualLength; glyphs->numGlyphs = actualLength; @@ -467,7 +471,7 @@ bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGly if (!(flags & GlyphIndicesOnly)) recalcAdvances(glyphs, {}); - return true; + return mappedGlyphs; } QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h index 44e466789c8..d7c9a792677 100644 --- a/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h +++ b/src/gui/text/windows/qwindowsfontenginedirectwrite_p.h @@ -53,8 +53,8 @@ public: QFixed emSquareSize() const override; glyph_t glyphIndex(uint ucs4) const override; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, - ShaperFlags flags) const override; + int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + ShaperFlags flags) const override; void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags) const override; void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, diff --git a/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp index 678eb0393f5..9471c1d93f8 100644 --- a/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp +++ b/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp @@ -263,7 +263,7 @@ void tst_QFontMetrics::inFontUcs4() glyphs.glyphs[0] = 0; QVERIFY(engine->stringToCMap(string.constData(), string.size(), &glyphs, &glyphs.numGlyphs, - QFontEngine::GlyphIndicesOnly)); + QFontEngine::GlyphIndicesOnly) > 0); QCOMPARE(glyphs.numGlyphs, 1); QCOMPARE(glyphs.glyphs[0], uint(1)); } @@ -275,7 +275,7 @@ void tst_QFontMetrics::inFontUcs4() glyphs.glyphs[0] = 0; QVERIFY(engine->stringToCMap(string.constData(), string.size(), &glyphs, &glyphs.numGlyphs, - QFontEngine::GlyphIndicesOnly)); + QFontEngine::GlyphIndicesOnly) >= 0); QVERIFY(glyphs.glyphs[0] != 1); } } |