From 58907dfa812de8ce6c63295589f644bdf4257d15 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 30 Nov 2022 14:52:13 +0100 Subject: [PATCH] Try to match variant-selector font to preceding character MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variant-selectors are special unicode symbols which are used to modify glyph selection for the preceding character. For instance, a regular symbol could be turned into a color emoji using VS16, the emoji variation selector. In order for this to work, however, the font that handles the selector has to handle the full pair of characters, so that it can apply the correct substitution rules. One specific example of this was on macOS, where an airplane symbol + VS16 would match the symbol to the default UI font but the VS16 to the emoji font. Since there string provided for the emoji font did not have any preceding character for VS16, we just ignored it. To improve on this, we now detect variation selectors that have been matched to different font engines than the preceding character. When such a case occurs, we check if the selector font also supports the preceding character, and if it does, we keep the pair together and use the same font for both. [ChangeLog][QtGui][Text] Fix some cases where a variation-selector character would be ignored in font selection and the correct variant of a character would thus not be selected. Task-number: QTBUG-108799 Change-Id: I9f427e0520e652ee2f24a4f7dc3c1957251e06bd Reviewed-by: Tor Arne Vestbรธ --- src/gui/text/qfontengine.cpp | 26 +++++++++++++++++++ .../auto/gui/text/qglyphrun/tst_qglyphrun.cpp | 6 ++++- tests/baseline/painting/scripts/text.qps | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 61705842dbd..8b0dff31af7 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1825,6 +1825,7 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, QStringIterator it(str, str + len); int lastFallback = -1; + char32_t previousUcs4 = 0; while (it.hasNext()) { const char32_t ucs4 = it.peekNext(); @@ -1886,10 +1887,35 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, break; } } + + // For variant-selectors, they are modifiers to the previous character. If we + // end up with different font selections for the selector and the character it + // modifies, we try applying the selector font to the preceding character as well + const int variantSelectorBlock = 0xFE00; + if ((ucs4 & 0xFFF0) == variantSelectorBlock && glyph_pos > 0) { + int selectorFontEngine = glyphs->glyphs[glyph_pos] >> 24; + int precedingCharacterFontEngine = glyphs->glyphs[glyph_pos - 1] >> 24; + + if (selectorFontEngine != precedingCharacterFontEngine) { + QFontEngine *engine = m_engines.at(selectorFontEngine); + glyph_t glyph = engine->glyphIndex(previousUcs4); + if (glyph != 0) { + glyphs->glyphs[glyph_pos - 1] = glyph; + if (!(flags & GlyphIndicesOnly)) { + QGlyphLayout g = glyphs->mid(glyph_pos - 1, 1); + engine->recalcAdvances(&g, flags); + } + + // set the high byte to indicate which engine the glyph came from + glyphs->glyphs[glyph_pos - 1] |= (selectorFontEngine << 24); + } + } + } } it.advance(); ++glyph_pos; + previousUcs4 = ucs4; } *nglyphs = glyph_pos; diff --git a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp index b0d6efc065e..efd590a730a 100644 --- a/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp +++ b/tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp @@ -633,8 +633,12 @@ void tst_QGlyphRun::defaultIgnorables() bool hasFullMainFontRun = false; for (const QGlyphRun &run : runs) { + // QtsSpecialFont will be used for at least five characters: AA[...]111 + // Depending on the font selected for the 0xFE0F variant selector, the + // third 'A' may be in QtsSpecialFont or in the fallback. This is platform-specific, + // so we accept either. if (run.rawFont().familyName() == QStringLiteral("QtsSpecialTestFont") - && run.glyphIndexes().size() == 6) { + && run.glyphIndexes().size() >= 5) { hasFullMainFontRun = true; break; } diff --git a/tests/baseline/painting/scripts/text.qps b/tests/baseline/painting/scripts/text.qps index 4d81b3084c7..6bacdfd5e62 100644 --- a/tests/baseline/painting/scripts/text.qps +++ b/tests/baseline/painting/scripts/text.qps @@ -165,7 +165,7 @@ translate 0 75 save setPen black setFont "sansserif" 16 normal - drawText 0 40 "e๐Ÿ˜ƒm๐Ÿ˜‡o๐Ÿ˜j๐Ÿ˜œi๐Ÿ˜ธ!" + drawText 0 40 "e๐Ÿ˜ƒm๐Ÿ˜‡o๐Ÿ˜j๐Ÿ˜œi๐Ÿ˜ธ!โœˆ๏ธ" restore translate 0 75