CoreText: Warn the user when family alias lookup slows down the application

If the user specifies a font family in their application that doesn't
exist in the system, or one that uses the localized family name, we
will end up resolving the family alias for all fonts in the system,
which typically adds 600-800ms of startup time. Let the user know
when this happens.

Change-Id: Id8d6f55028e37f681ec4a686df25d33240b5a30f
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Tor Arne Vestbø 2019-10-03 16:07:37 +02:00
parent 6906b0647a
commit c962c77044
4 changed files with 48 additions and 8 deletions

View File

@ -2684,7 +2684,7 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script)
QtFontDesc desc; QtFontDesc desc;
QList<int> blackListed; QList<int> blackListed;
int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
if (index < 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases()) { if (index < 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(family_name)) {
// We populated familiy aliases (e.g. localized families), so try again // We populated familiy aliases (e.g. localized families), so try again
index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed);
} }

View File

@ -104,7 +104,7 @@ class Q_GUI_EXPORT QPlatformFontDatabase
public: public:
virtual ~QPlatformFontDatabase(); virtual ~QPlatformFontDatabase();
virtual void populateFontDatabase(); virtual void populateFontDatabase();
virtual bool populateFamilyAliases() { return false; } virtual bool populateFamilyAliases(const QString &missingFamily) { Q_UNUSED(missingFamily); return false; }
virtual void populateFamily(const QString &familyName); virtual void populateFamily(const QString &familyName);
virtual void invalidate(); virtual void invalidate();

View File

@ -48,6 +48,8 @@
#import <UIKit/UIFont.h> #import <UIKit/UIFont.h>
#endif #endif
#include <QtCore/qelapsedtimer.h>
#include "qcoretextfontdatabase_p.h" #include "qcoretextfontdatabase_p.h"
#include "qfontengine_coretext_p.h" #include "qfontengine_coretext_p.h"
#if QT_CONFIG(settings) #if QT_CONFIG(settings)
@ -113,39 +115,77 @@ QCoreTextFontDatabase::~QCoreTextFontDatabase()
void QCoreTextFontDatabase::populateFontDatabase() void QCoreTextFontDatabase::populateFontDatabase()
{ {
qCDebug(lcQpaFonts) << "Populating font database...";
QElapsedTimer elapsed;
if (lcQpaFonts().isDebugEnabled())
elapsed.start();
QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames();
for (NSString *familyName in familyNames.as<const NSArray *>()) for (NSString *familyName in familyNames.as<const NSArray *>())
QPlatformFontDatabase::registerFontFamily(QString::fromNSString(familyName)); QPlatformFontDatabase::registerFontFamily(QString::fromNSString(familyName));
qCDebug(lcQpaFonts) << "Populating available families took" << elapsed.restart() << "ms";
// Force creating the theme fonts to get the descriptors in m_systemFontDescriptors // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors
if (m_themeFonts.isEmpty()) if (m_themeFonts.isEmpty())
(void)themeFonts(); (void)themeFonts();
qCDebug(lcQpaFonts) << "Resolving theme fonts took" << elapsed.restart() << "ms";
Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors) Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors)
populateFromDescriptor(fontDesc); populateFromDescriptor(fontDesc);
qCDebug(lcQpaFonts) << "Populating system descriptors took" << elapsed.restart() << "ms";
Q_ASSERT(!m_hasPopulatedAliases); Q_ASSERT(!m_hasPopulatedAliases);
} }
bool QCoreTextFontDatabase::populateFamilyAliases() bool QCoreTextFontDatabase::populateFamilyAliases(const QString &missingFamily)
{ {
#if defined(Q_OS_MACOS) #if defined(Q_OS_MACOS)
if (m_hasPopulatedAliases) if (m_hasPopulatedAliases)
return false; return false;
// There's no API to go from a localized family name to its non-localized
// name, so we have to resort to enumerating all the available fonts and
// doing a reverse lookup.
qCDebug(lcQpaFonts) << "Populating family aliases...";
QElapsedTimer elapsed;
elapsed.start();
QString nonLocalizedMatch;
QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames(); QCFType<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames();
NSFontManager *fontManager = NSFontManager.sharedFontManager;
for (NSString *familyName in familyNames.as<const NSArray *>()) { for (NSString *familyName in familyNames.as<const NSArray *>()) {
NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSString *localizedFamilyName = [fontManager localizedNameForFamily:familyName face:nil]; NSString *localizedFamilyName = [fontManager localizedNameForFamily:familyName face:nil];
if (![localizedFamilyName isEqual:familyName]) { if (![localizedFamilyName isEqual:familyName]) {
QPlatformFontDatabase::registerAliasToFontFamily( QString nonLocalizedFamily = QString::fromNSString(familyName);
QString::fromNSString(familyName), QString localizedFamily = QString::fromNSString(localizedFamilyName);
QString::fromNSString(localizedFamilyName)); QPlatformFontDatabase::registerAliasToFontFamily(nonLocalizedFamily, localizedFamily);
if (localizedFamily == missingFamily)
nonLocalizedMatch = nonLocalizedFamily;
} }
} }
m_hasPopulatedAliases = true; m_hasPopulatedAliases = true;
if (lcQpaFonts().isWarningEnabled()) {
QString warningMessage;
QDebug msg(&warningMessage);
msg << "Populating font family aliases took" << elapsed.restart() << "ms.";
if (!nonLocalizedMatch.isNull())
msg << "Replace uses of" << missingFamily << "with its non-localized name" << nonLocalizedMatch;
else
msg << "Replace uses of missing font family" << missingFamily << "with one that exists";
msg << "to avoid this cost.";
qCWarning(lcQpaFonts) << qPrintable(warningMessage);
}
return true; return true;
#else #else
Q_UNUSED(missingFamily);
return false; return false;
#endif #endif
} }

View File

@ -71,7 +71,7 @@ public:
QCoreTextFontDatabase(); QCoreTextFontDatabase();
~QCoreTextFontDatabase(); ~QCoreTextFontDatabase();
void populateFontDatabase() override; void populateFontDatabase() override;
bool populateFamilyAliases() override; bool populateFamilyAliases(const QString &missingFamily) override;
void populateFamily(const QString &familyName) override; void populateFamily(const QString &familyName) override;
void invalidate() override; void invalidate() override;