From 3e5719ae7b8f4ff2b1aff6d9134386bcc64e5b4b Mon Sep 17 00:00:00 2001 From: Konstantin Ritt Date: Wed, 27 Jan 2016 04:35:05 +0400 Subject: QFont: Fix possible cache misses due to misprepared cache key Parse the requested family before we're looking/saving into the cache, thus hitting the cached EngineData for: * quoted family names (eg. QFont("'Arial'")) * non-simplified family names (eg. QFont(" Arial ")) * substituted family names (\sa QFont::insertSubstitution()) * explicit fallback list, where possible (eg. QFont("Tahoma, Arial")) This also improves the cache hitting for the font engines in some cases. Change-Id: I18cdc3e8d669cccec961f84e9b27329402e2b7ed Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/gui/text/qfontdatabase.cpp | 24 +++++------ tests/auto/gui/text/qfontcache/tst_qfontcache.cpp | 49 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 333f1d2bb0..d606681e52 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -2732,9 +2732,16 @@ void QFontDatabase::load(const QFontPrivate *d, int script) if (req.stretch == 0) req.stretch = 100; + // respect the fallback families that might be passed through the request + const QStringList fallBackFamilies = familyList(req); + if (!d->engineData) { QFontCache *fontCache = QFontCache::instance(); // look for the requested font in the engine data cache + // note: fallBackFamilies are not respected in the EngineData cache key; + // join them with the primary selection family to avoid cache misses + req.family = fallBackFamilies.join(QLatin1Char(',')); + d->engineData = fontCache->findEngineData(req); if (!d->engineData) { // create a new one @@ -2748,25 +2755,18 @@ void QFontDatabase::load(const QFontPrivate *d, int script) if (d->engineData->engines[script]) return; - // Until we specifically asked not to, try looking for Multi font engine - // first, the last '1' indicates that we want Multi font engine instead - // of single ones - bool multi = !(req.styleStrategy & QFont::NoFontMerging); - QFontCache::Key key(req, script, multi ? 1 : 0); + QFontEngine *fe = Q_NULLPTR; - QFontEngine *fe = QFontCache::instance()->findEngine(key); + req.fallBackFamilies = fallBackFamilies; + if (!req.fallBackFamilies.isEmpty()) + req.family = req.fallBackFamilies.takeFirst(); // list of families to try QStringList family_list; if (!req.family.isEmpty()) { - QStringList familiesForRequest = familyList(req); - // Add primary selection - family_list << familiesForRequest.takeFirst(); - - // Fallbacks requested in font request - req.fallBackFamilies = familiesForRequest; + family_list << req.family; // add the default family QString defaultFamily = QGuiApplication::font().family(); diff --git a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp index c92b7eed58..4d5ddfd523 100644 --- a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp +++ b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp @@ -48,6 +48,9 @@ public: virtual ~tst_QFontCache(); private slots: + void engineData_data(); + void engineData(); + void clear(); }; @@ -69,6 +72,52 @@ tst_QFontCache::~tst_QFontCache() { } +void tst_QFontCache::engineData_data() +{ + QTest::addColumn("family"); + QTest::addColumn("cacheKey"); + + QTest::newRow("unquoted-family-name") << QString("Times New Roman") << QString("Times New Roman"); + QTest::newRow("quoted-family-name") << QString("'Times New Roman'") << QString("Times New Roman"); + QTest::newRow("invalid") << QString("invalid") << QString("invalid"); + QTest::newRow("multiple") << QString("invalid, Times New Roman") << QString("invalid,Times New Roman"); + QTest::newRow("multiple spaces") << QString("invalid, Times New Roman ") << QString("invalid,Times New Roman"); + QTest::newRow("multiple spaces quotes") << QString("'invalid', Times New Roman ") << QString("invalid,Times New Roman"); + QTest::newRow("multiple2") << QString("invalid, Times New Roman , foobar, 'baz'") << QString("invalid,Times New Roman,foobar,baz"); + QTest::newRow("invalid spaces") << QString("invalid spaces, Times New Roman ") << QString("invalid spaces,Times New Roman"); + QTest::newRow("invalid spaces quotes") << QString("'invalid spaces', 'Times New Roman' ") << QString("invalid spaces,Times New Roman"); +} + +void tst_QFontCache::engineData() +{ + QFETCH(QString, family); + QFETCH(QString, cacheKey); + + QFont f(family); + f.exactMatch(); // loads engine + + QFontPrivate *d = QFontPrivate::get(f); + + QFontDef req = d->request; + // copy-pasted from QFontDatabase::load(), to engineer the cache key + if (req.pixelSize == -1) { + req.pixelSize = std::floor(((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100; + req.pixelSize = qRound(req.pixelSize); + } + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72.0/d->dpi; + if (req.weight == 0) + req.weight = QFont::Normal; + if (req.stretch == 0) + req.stretch = 100; + + req.family = cacheKey; + + QFontEngineData *engineData = QFontCache::instance()->findEngineData(req); + + QCOMPARE(engineData, QFontPrivate::get(f)->engineData); +} + void tst_QFontCache::clear() { #ifdef QT_BUILD_INTERNAL -- cgit v1.2.3