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 <eirik.aavitsland@qt.io>
bb10
Eskil Abrahamsen Blomfeldt 2024-04-08 14:38:32 +02:00
parent d04deafed9
commit 1593b1f6d6
16 changed files with 152 additions and 66 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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];

View File

@ -47,6 +47,7 @@ public:
NoAntialias = 0x0100,
NoSubpixelAntialias = 0x0800,
PreferNoShaping = 0x1000,
ContextFontMerging = 0x2000,
NoFontMerging = 0x8000
};
Q_ENUM(StyleStrategy)

View File

@ -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++) {

View File

@ -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;

View File

@ -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;
}
/*!

View File

@ -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)
{

View File

@ -224,6 +224,7 @@ struct QGlyphLayout
return reinterpret_cast<char *>(offsets);
}
void copy(QGlyphLayout *other);
void grow(char *address, int totalGlyphs);
};

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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);
}
}