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
parent
d04deafed9
commit
1593b1f6d6
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
NoAntialias = 0x0100,
|
||||
NoSubpixelAntialias = 0x0800,
|
||||
PreferNoShaping = 0x1000,
|
||||
ContextFontMerging = 0x2000,
|
||||
NoFontMerging = 0x8000
|
||||
};
|
||||
Q_ENUM(StyleStrategy)
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ struct QGlyphLayout
|
|||
return reinterpret_cast<char *>(offsets);
|
||||
}
|
||||
|
||||
void copy(QGlyphLayout *other);
|
||||
void grow(char *address, int totalGlyphs);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue