Remove need to populate font database on OS X to get fallback families

Populating the whole database takes a while, and it's very easy to trigger
the fallback family code path through eg. matching or getting the default
font family from QFont.

Instead of relying on populate to resolve family name to a PostScript name
(which was required when using CTFontCreateWithName), we instead use the
CTFontDescriptorCreateWithAttributes() function to create a descriptor
based on the family name, and then use CTFontCreateWithFontDescriptor().

The other way around, we use CTFontDescriptorCreateWithNameAndSize, and
then pull out the family name from the descriptor. The need for creating
a CTFont for private fonts (eg '.Apple Symbols Fallback') does not seem
necessary anymore, as tested on 10.7-10.9.

The disadvantage of creating font descriptors instead of re-using the
data computed by population is that we're doing the same work twice,
but the end result is making the font database more lazy, and the 80%
use-case is assumed to be that you're only interested in the fallback
of a small number of fonts, which means you don't want to populate all
of the fonts in the system (taking about 1100ms on the test system).
Looking up the fallback of a single family or style now takes about
15-25ms.

Task-number: QTBUG-37165
Change-Id: I6b904dbe796a3f236919d778d0168fdef9a20e69
Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
Tor Arne Vestbø 2014-03-06 14:58:18 +01:00 committed by The Qt Project
parent 800232e1d3
commit c466f2ed46
2 changed files with 14 additions and 45 deletions

View File

@ -199,12 +199,6 @@ void QCoreTextFontDatabase::populateFontDatabase()
[pool release];
}
void QCoreTextFontDatabase::invalidate()
{
psNameToFamily.clear();
familyNameToPsName.clear();
}
void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font)
{
QString foundryName = QStringLiteral("CoreText");
@ -271,12 +265,6 @@ void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font)
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch,
true /* antialiased */, true /* scalable */,
pixelSize, fixedPitch, writingSystems, (void *) font);
// We need to map back and forth between PostScript-names and family-names for fallback list construction
CFStringRef psName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontNameAttribute);
psNameToFamily[QCFString::toQString((NSString *) psName)] = familyName;
familyNameToPsName[familyName] = QCFString::toQString((NSString *) psName);
CFRelease(psName);
}
void QCoreTextFontDatabase::releaseHandle(void *handle)
@ -356,25 +344,15 @@ QFont::StyleHint styleHintFromNSString(NSString *style)
return QFont::AnyStyle;
}
static QString familyNameFromPostScriptName(QHash<QString, QString> &psNameToFamily,
NSString *psName)
static QString familyNameFromPostScriptName(NSString *psName)
{
QString name = QCFString::toQString(psName);
if (psNameToFamily.contains(name))
return psNameToFamily[name];
else {
// Some of the font name in DefaultFontFallbacks.plist are hidden fonts like AquaHiraKaku,
// their family name begins with a dot, like ".AquaHiraKaku" or ".Apple Symbols Fallback",
// the only way (I've found) to get it are actually creating a CTFont with them. We only
// need to do it once though.
QCFType<CTFontRef> font = CTFontCreateWithName((CFStringRef) psName, 12.0, NULL);
if (font) {
QCFString family = CTFontCopyFamilyName(font);
psNameToFamily[name] = family;
return family;
}
return name;
}
QCFType<CTFontDescriptorRef> fontDescriptor = (CTFontDescriptorRef) CTFontDescriptorCreateWithNameAndSize((CFStringRef)psName, 12.0);
QCFString familyName = (CFStringRef) CTFontDescriptorCopyLocalizedAttribute(fontDescriptor, kCTFontFamilyNameAttribute, 0);
QString name = QCFString::toQString(familyName);
if (name.isEmpty())
qWarning() << "QCoreTextFontDatabase: Failed to resolve family name for PostScript name " << QCFString::toQString((CFStringRef)psName);
return name;
}
QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
@ -395,12 +373,10 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
if (fallbackLists.contains(family))
return fallbackLists.value(family);
if (familyNameToPsName.isEmpty())
const_cast<QCoreTextFontDatabase*>(this)->populateFontDatabase();
if (familyNameToPsName.contains(family)) {
QCFType<CTFontRef> font = CTFontCreateWithName(QCFString(familyNameToPsName[family]), 12.0, NULL);
if (font) {
QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(family));
if (QCFType<CTFontDescriptorRef> fontDescriptor = CTFontDescriptorCreateWithAttributes(attributes)) {
if (QCFType<CTFontRef> font = CTFontCreateWithFontDescriptor(fontDescriptor, 12.0, 0)) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"];
@ -432,10 +408,6 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
static bool didPopulateStyleFallbacks = false;
if (!didPopulateStyleFallbacks) {
#if defined(Q_OS_MACX)
// Ensure we have the psNameToFamily mapping set up
if (psNameToFamily.isEmpty())
const_cast<QCoreTextFontDatabase*>(this)->populateFontDatabase();
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"];
@ -451,10 +423,10 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
NSArray *langs = [(NSArray *) item sortedArrayUsingFunction: languageMapSort
context: languages];
for (NSArray *map in langs)
fallbackList.append(familyNameFromPostScriptName(psNameToFamily, [map objectAtIndex: 1]));
fallbackList.append(familyNameFromPostScriptName([map objectAtIndex: 1]));
}
else if ([item isKindOfClass: [NSString class]])
fallbackList.append(familyNameFromPostScriptName(psNameToFamily, item));
fallbackList.append(familyNameFromPostScriptName(item));
}
if (QCoreTextFontEngine::supportsColorGlyphs())

View File

@ -72,7 +72,6 @@ public:
QCoreTextFontDatabase();
~QCoreTextFontDatabase();
void populateFontDatabase();
void invalidate() Q_DECL_OVERRIDE;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
@ -86,8 +85,6 @@ private:
void populateFromDescriptor(CTFontDescriptorRef font);
mutable QString defaultFontName;
mutable QHash<QString, QString> psNameToFamily;
mutable QHash<QString, QString> familyNameToPsName;
void removeApplicationFonts();