Support application fallbacks for common script
While common script typically adapts the surrounding script, it is possible for strings of only punctuation (for instance) to remain undetermined. In addition, Qt considers Latin and Common the same in font matching, so in order to get merging for latin, we need to conflate them in the application fallback API as well. This also adds a couple of missing clears of caches (clearing the font cache itself when adding a new fallback, since the fallbacks are kept as part of the cached font engine + clearing the fallback cache when adding a new application font, since the new application font may be a fallback candidate). Note: This also adds some missing removeApplicationFont() calls from other tests, since these were causing issues with the new test. Task-number: QTBUG-124914 Change-Id: Idbfa0f6b492a9194eca67b57101e674f7b8a4ec4 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>bb10
parent
daf494c585
commit
99e94f1035
|
|
@ -670,7 +670,7 @@ static QStringList fallbacksForFamily(const QString &family, QFont::Style style,
|
|||
return *fallbacks;
|
||||
|
||||
// make sure that the db has all fallback families
|
||||
QStringList userFallbacks = db->applicationFallbackFontFamilies.value(script == QChar::Script_Common ? QChar::Script_Latin : script);
|
||||
QStringList userFallbacks = db->applicationFallbackFontFamilies.value(script == QChar::Script_Latin ? QChar::Script_Common : script);
|
||||
QStringList retList = userFallbacks + QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
|
||||
|
||||
QStringList::iterator i;
|
||||
|
|
@ -2187,6 +2187,8 @@ int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &
|
|||
// loaded, so it has to be flushed.
|
||||
QFontCache::instance()->clear();
|
||||
|
||||
fallbacksCache.clear();
|
||||
|
||||
emit qApp->fontDatabaseChanged();
|
||||
|
||||
return i;
|
||||
|
|
@ -2386,23 +2388,32 @@ bool QFontDatabase::removeAllApplicationFonts()
|
|||
be prioritized in reverse order, so that the last family added will be checked first and so
|
||||
on.
|
||||
|
||||
\note Qt's font matching algorithm considers \c{QChar::Script_Common} (undetermined script)
|
||||
and \c{QChar::Script_Latin} the same. Adding a fallback for either of these will also apply
|
||||
to the other.
|
||||
|
||||
\sa setApplicationFallbackFontFamilies(), removeApplicationFallbackFontFamily(), applicationFallbackFontFamilies()
|
||||
*/
|
||||
void QFontDatabase::addApplicationFallbackFontFamily(QChar::Script script, const QString &familyName)
|
||||
{
|
||||
QMutexLocker locker(fontDatabaseMutex());
|
||||
|
||||
if (script < QChar::Script_Latin) {
|
||||
if (script < QChar::Script_Common) {
|
||||
qCWarning(lcFontDb) << "Invalid script passed to addApplicationFallbackFontFamily:" << script;
|
||||
return;
|
||||
}
|
||||
|
||||
if (script == QChar::Script_Latin)
|
||||
script = QChar::Script_Common;
|
||||
|
||||
auto *db = QFontDatabasePrivate::instance();
|
||||
auto it = db->applicationFallbackFontFamilies.find(script);
|
||||
if (it == db->applicationFallbackFontFamilies.end())
|
||||
it = db->applicationFallbackFontFamilies.insert(script, QStringList{});
|
||||
|
||||
it->prepend(familyName);
|
||||
|
||||
QFontCache::instance()->clear();
|
||||
db->fallbacksCache.clear();
|
||||
}
|
||||
|
||||
|
|
@ -2420,6 +2431,14 @@ bool QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script script, co
|
|||
{
|
||||
QMutexLocker locker(fontDatabaseMutex());
|
||||
|
||||
if (script < QChar::Script_Common) {
|
||||
qCWarning(lcFontDb) << "Invalid script passed to removeApplicationFallbackFontFamily:" << script;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (script == QChar::Script_Latin)
|
||||
script = QChar::Script_Common;
|
||||
|
||||
auto *db = QFontDatabasePrivate::instance();
|
||||
auto it = db->applicationFallbackFontFamilies.find(script);
|
||||
if (it != db->applicationFallbackFontFamilies.end()) {
|
||||
|
|
@ -2452,11 +2471,14 @@ void QFontDatabase::setApplicationFallbackFontFamilies(QChar::Script script, con
|
|||
{
|
||||
QMutexLocker locker(fontDatabaseMutex());
|
||||
|
||||
if (script < QChar::Script_Latin) {
|
||||
if (script < QChar::Script_Common) {
|
||||
qCWarning(lcFontDb) << "Invalid script passed to setApplicationFallbackFontFamilies:" << script;
|
||||
return;
|
||||
}
|
||||
|
||||
if (script == QChar::Script_Latin)
|
||||
script = QChar::Script_Common;
|
||||
|
||||
auto *db = QFontDatabasePrivate::instance();
|
||||
db->applicationFallbackFontFamilies[script] = familyNames;
|
||||
|
||||
|
|
@ -2476,6 +2498,9 @@ QStringList QFontDatabase::applicationFallbackFontFamilies(QChar::Script script)
|
|||
{
|
||||
QMutexLocker locker(fontDatabaseMutex());
|
||||
|
||||
if (script == QChar::Script_Latin)
|
||||
script = QChar::Script_Common;
|
||||
|
||||
auto *db = QFontDatabasePrivate::instance();
|
||||
return db->applicationFallbackFontFamilies.value(script);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ set(testdata_resource_files
|
|||
"../../../shared/resources/testfont_open.otf"
|
||||
"../../../shared/resources/testfont_variable.ttf"
|
||||
"LED_REAL.TTF"
|
||||
"QtTestLimitedFont-Regular.ttf"
|
||||
"QtTestFallbackFont-Regular.ttf"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qfontdatabase "testdata"
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -78,6 +78,8 @@ private:
|
|||
QString m_testFontCondensed;
|
||||
QString m_testFontItalic;
|
||||
QString m_testFontVariable;
|
||||
QString m_limitedFont;
|
||||
QString m_fallbackFont;
|
||||
};
|
||||
|
||||
tst_QFontDatabase::tst_QFontDatabase()
|
||||
|
|
@ -91,11 +93,15 @@ void tst_QFontDatabase::initTestCase()
|
|||
m_testFontCondensed = QFINDTESTDATA("testfont_condensed.ttf");
|
||||
m_testFontItalic = QFINDTESTDATA("testfont_italic.ttf");
|
||||
m_testFontVariable = QFINDTESTDATA("testfont_variable.ttf");
|
||||
m_limitedFont = QFINDTESTDATA("QtTestLimitedFont-Regular.ttf");
|
||||
m_fallbackFont = QFINDTESTDATA("QtTestFallbackFont-Regular.ttf");
|
||||
QVERIFY(!m_ledFont.isEmpty());
|
||||
QVERIFY(!m_testFont.isEmpty());
|
||||
QVERIFY(!m_testFontCondensed.isEmpty());
|
||||
QVERIFY(!m_testFontItalic.isEmpty());
|
||||
QVERIFY(!m_testFontVariable.isEmpty());
|
||||
QVERIFY(!m_limitedFont.isEmpty());
|
||||
QVERIFY(!m_fallbackFont.isEmpty());
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::styles_data()
|
||||
|
|
@ -391,8 +397,14 @@ void tst_QFontDatabase::condensedFontWidthNoFontMerging()
|
|||
|
||||
void tst_QFontDatabase::condensedFontWidth()
|
||||
{
|
||||
QFontDatabase::addApplicationFont(m_testFont);
|
||||
QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
int testFontId = QFontDatabase::addApplicationFont(m_testFont);
|
||||
int testFontCondensedId = QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
auto cleanup = qScopeGuard([&testFontId, &testFontCondensedId] {
|
||||
if (testFontId >= 0)
|
||||
QFontDatabase::removeApplicationFont(testFontId);
|
||||
if (testFontCondensedId >= 0)
|
||||
QFontDatabase::removeApplicationFont(testFontCondensedId);
|
||||
});
|
||||
|
||||
QVERIFY(QFontDatabase::hasFamily("QtBidiTestFont"));
|
||||
if (!QFontDatabase::hasFamily("QtBidiTestFontCondensed"))
|
||||
|
|
@ -410,10 +422,16 @@ void tst_QFontDatabase::condensedFontWidth()
|
|||
void tst_QFontDatabase::condensedFontMatching()
|
||||
{
|
||||
QFontDatabase::removeAllApplicationFonts();
|
||||
QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
int testFontCondensedId = QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
if (!QFontDatabase::hasFamily("QtBidiTestFont"))
|
||||
QSKIP("This platform doesn't support preferred font family names (QTBUG-53478)");
|
||||
QFontDatabase::addApplicationFont(m_testFont);
|
||||
int testFontId = QFontDatabase::addApplicationFont(m_testFont);
|
||||
auto cleanup = qScopeGuard([&testFontId, &testFontCondensedId] {
|
||||
if (testFontId >= 0)
|
||||
QFontDatabase::removeApplicationFont(testFontId);
|
||||
if (testFontCondensedId >= 0)
|
||||
QFontDatabase::removeApplicationFont(testFontCondensedId);
|
||||
});
|
||||
|
||||
// Test we correctly get the condensed font using different font matching methods:
|
||||
QFont tfcByStretch("QtBidiTestFont");
|
||||
|
|
@ -561,11 +579,17 @@ void tst_QFontDatabase::addApplicationFontFallback()
|
|||
{
|
||||
int ledId = -1;
|
||||
int id = -1;
|
||||
auto cleanup = qScopeGuard([&id, &ledId] {
|
||||
int limitedId = -1;
|
||||
int fallbackId = -1;
|
||||
auto cleanup = qScopeGuard([&id, &ledId, &limitedId, &fallbackId] {
|
||||
if (id >= 0)
|
||||
QFontDatabase::removeApplicationFont(id);
|
||||
if (ledId >= 0)
|
||||
QFontDatabase::removeApplicationFont(ledId);
|
||||
if (limitedId >= 0)
|
||||
QFontDatabase::removeApplicationFont(limitedId);
|
||||
if (fallbackId >= 0)
|
||||
QFontDatabase::removeApplicationFont(fallbackId);
|
||||
});
|
||||
|
||||
const QChar hebrewChar(0x05D0); // Hebrew 'aleph'
|
||||
|
|
@ -633,6 +657,107 @@ void tst_QFontDatabase::addApplicationFontFallback()
|
|||
QCOMPARE(hebrewFontNow, defaultHebrewFont);
|
||||
}
|
||||
|
||||
limitedId = QFontDatabase::addApplicationFont(m_limitedFont);
|
||||
QVERIFY(limitedId >= 0);
|
||||
|
||||
fallbackId = QFontDatabase::addApplicationFont(m_fallbackFont);
|
||||
QVERIFY(fallbackId >= 0);
|
||||
|
||||
QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Common, u"QtTestFallbackFont"_s);
|
||||
|
||||
// The fallback for Common will be used also for Latin, because Latin and Common are
|
||||
// considered the same script by the font matching engine.
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(u"A'B,"_s);
|
||||
layout.setFont(QFont(u"QtTestLimitedFont"_s));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QVERIFY(glyphRuns.size() > 1);
|
||||
for (int i = 0; i < glyphRuns.size(); ++i) {
|
||||
QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s
|
||||
|| glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s);
|
||||
}
|
||||
}
|
||||
|
||||
// When the text only consists of common script characters, the fallback font will also be used.
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(u"',"_s);
|
||||
layout.setFont(QFont(u"QtTestLimitedFont"_s));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 2);
|
||||
for (int i = 0; i < glyphRuns.size(); ++i) {
|
||||
QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s
|
||||
|| glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s);
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Common, u"QtTestFallbackFont"_s));
|
||||
QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Latin, u"QtTestFallbackFont"_s);
|
||||
|
||||
// Latin fallback works just the same as Common fallback
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(u"A'B,"_s);
|
||||
layout.setFont(QFont(u"QtTestLimitedFont"_s));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 2);
|
||||
for (int i = 0; i < glyphRuns.size(); ++i) {
|
||||
QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s
|
||||
|| glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s);
|
||||
}
|
||||
}
|
||||
|
||||
// When the common character is placed next to a Cyrillic characters, it gets adapted to this,
|
||||
// so the fallback font will not be selected, even if it supports the character in question
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(u"A'Б,"_s);
|
||||
layout.setFont(QFont(u"QtTestLimitedFont"_s));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 2);
|
||||
for (int i = 0; i < glyphRuns.size(); ++i) {
|
||||
QVERIFY(glyphRuns.at(i).rawFont().familyName() != u"QtTestFallbackFont"_s);
|
||||
}
|
||||
}
|
||||
|
||||
QFontDatabase::addApplicationFallbackFontFamily(QChar::Script_Cyrillic, u"QtTestFallbackFont"_s);
|
||||
|
||||
// When we set the fallback font for Cyrillic as well, it gets selected
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(u"A'Б,"_s);
|
||||
layout.setFont(QFont(u"QtTestLimitedFont"_s));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 2);
|
||||
for (int i = 0; i < glyphRuns.size(); ++i) {
|
||||
QVERIFY(glyphRuns.at(i).rawFont().familyName() == u"QtTestFallbackFont"_s
|
||||
|| glyphRuns.at(i).rawFont().familyName() == u"QtTestLimitedFont"_s);
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Cyrillic, u"QtTestFallbackFont"_s));
|
||||
QVERIFY(QFontDatabase::removeApplicationFallbackFontFamily(QChar::Script_Latin, u"QtTestFallbackFont"_s));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QFontDatabase)
|
||||
|
|
|
|||
Loading…
Reference in New Issue