From 3745b0ca126f46eadfeeb7e3d1d53360dbf800cd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 17 Jul 2013 14:53:27 +0200 Subject: Fix application font population on OS X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes two issues that prevented the application font related tests of tst_QFontDatabase from passing: * The code for creating the font descriptor after the registration of an app font with file name using CTFontDescriptorCreateWithAttributes must create a dictionary with kCTFontURLAttribute as key and the CFURLRef pointing to the on-disk file as value. Unfortunately the code mixed up keys and values in the dictionary. * Registration of app fonts within QFontDatabase itself on Windows and Fontconfig platforms works by QFontDatabase calling addApplicationFont on the platform db after calling populateFontDatabase(). It assumes that addApplicationFont on the platform db is capable of registering the font right away. This part was also missing from the Mac implementation and this patch implements it by moving the common registration code from a CTFontDescriptorRef out into a separate method, called from populateFontDatabase() as well as addApplicationFont(). Task-number: QTBUG-23062 Change-Id: Ide5e6bf277d99f3cab50ee0d4631cc3fba6d0d45 Reviewed-by: Tor Arne Vestbø --- .../fontdatabases/mac/qcoretextfontdatabase.mm | 173 +++++++++++++-------- .../fontdatabases/mac/qcoretextfontdatabase_p.h | 20 +++ .../gui/text/qfontdatabase/tst_qfontdatabase.cpp | 6 - 3 files changed, 127 insertions(+), 72 deletions(-) diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 1e15a9e62c..5139f11d23 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -176,6 +176,10 @@ QCoreTextFontDatabase::~QCoreTextFontDatabase() void QCoreTextFontDatabase::populateFontDatabase() { + // The caller (QFontDB) expects the db to be populate only with system fonts, so we need + // to make sure that any previously registered app fonts become invisible. + removeApplicationFonts(); + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; QCFType collection = CTFontCollectionCreateFromAvailableFonts(0); @@ -186,82 +190,87 @@ void QCoreTextFontDatabase::populateFontDatabase() if (! fonts) return; - QString foundryName = QLatin1String("CoreText"); const int numFonts = CFArrayGetCount(fonts); for (int i = 0; i < numFonts; ++i) { CTFontDescriptorRef font = (CTFontDescriptorRef) CFArrayGetValueAtIndex(fonts, i); - QCFString familyName = (CFStringRef) CTFontDescriptorCopyLocalizedAttribute(font, kCTFontFamilyNameAttribute, NULL); - QCFString styleName = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontStyleNameAttribute, NULL); - QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); - QFont::Weight weight = QFont::Normal; - QFont::Style style = QFont::StyleNormal; - QFont::Stretch stretch = QFont::Unstretched; - bool fixedPitch = false; - - if (styles) { - if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { - Q_ASSERT(CFNumberIsFloatType(weightValue)); - double d; - if (CFNumberGetValue(weightValue, kCFNumberDoubleType, &d)) - weight = (d > 0.0) ? QFont::Bold : QFont::Normal; - } - if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { - Q_ASSERT(CFNumberIsFloatType(italic)); - double d; - if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { - if (d > 0.0) - style = QFont::StyleItalic; - } - } - if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { - int d; - if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { - if (d & kCTFontMonoSpaceTrait) - fixedPitch = true; - if (d & kCTFontExpandedTrait) - stretch = QFont::Expanded; - else if (d & kCTFontCondensedTrait) - stretch = QFont::Condensed; - } - } - } + populateFromDescriptor(font); + } - int pixelSize = 0; - if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { - if (CFNumberIsFloatType(size)) { - double d; - CFNumberGetValue(size, kCFNumberDoubleType, &d); - pixelSize = d; - } else { - CFNumberGetValue(size, kCFNumberIntType, &pixelSize); + [pool release]; +} + +void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) +{ + QString foundryName = QStringLiteral("CoreText"); + QCFString familyName = (CFStringRef) CTFontDescriptorCopyLocalizedAttribute(font, kCTFontFamilyNameAttribute, NULL); + QCFString styleName = (CFStringRef)CTFontDescriptorCopyLocalizedAttribute(font, kCTFontStyleNameAttribute, NULL); + QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); + QFont::Weight weight = QFont::Normal; + QFont::Style style = QFont::StyleNormal; + QFont::Stretch stretch = QFont::Unstretched; + bool fixedPitch = false; + + if (styles) { + if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { + Q_ASSERT(CFNumberIsFloatType(weightValue)); + double d; + if (CFNumberGetValue(weightValue, kCFNumberDoubleType, &d)) + weight = (d > 0.0) ? QFont::Bold : QFont::Normal; + } + if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { + Q_ASSERT(CFNumberIsFloatType(italic)); + double d; + if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { + if (d > 0.0) + style = QFont::StyleItalic; } } - - QSupportedWritingSystems writingSystems; - if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { - CFIndex length = CFArrayGetCount(languages); - for (int i = 1; i < LanguageCount; ++i) { - if (!languageForWritingSystem[i]) - continue; - QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); - if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) - writingSystems.setSupported(QFontDatabase::WritingSystem(i)); + if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { + int d; + if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { + if (d & kCTFontMonoSpaceTrait) + fixedPitch = true; + if (d & kCTFontExpandedTrait) + stretch = QFont::Expanded; + else if (d & kCTFontCondensedTrait) + stretch = QFont::Condensed; } } + } - CFRetain(font); - QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch, - true /* antialiased */, true /* scalable */, - pixelSize, fixedPitch, writingSystems, (void *) font); + int pixelSize = 0; + if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { + if (CFNumberIsFloatType(size)) { + double d; + CFNumberGetValue(size, kCFNumberDoubleType, &d); + pixelSize = d; + } else { + CFNumberGetValue(size, kCFNumberIntType, &pixelSize); + } + } - // 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); + QSupportedWritingSystems writingSystems; + if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { + CFIndex length = CFArrayGetCount(languages); + for (int i = 1; i < LanguageCount; ++i) { + if (!languageForWritingSystem[i]) + continue; + QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); + if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) + writingSystems.setSupported(QFontDatabase::WritingSystem(i)); + } } - [pool release]; + CFRetain(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) @@ -473,6 +482,7 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData bool success = CTFontManagerRegisterGraphicsFont(cgFont, &error); if (success) { font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL); + m_applicationGraphicsFonts.append(QCFType::constructFromGet(cgFont)); } else { NSLog(@"Unable to register font: %@", error); CFRelease(error); @@ -484,12 +494,13 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData QCFType fontURL = CFURLCreateWithFileSystemPath(NULL, QCFString(fileName), kCFURLPOSIXPathStyle, false); bool success = CTFontManagerRegisterFontsForURL(fontURL, kCTFontManagerScopeProcess, &error); if (success) { - const void *keys[] = { fontURL }; - const void *values[] = { kCTFontURLAttribute }; + const void *keys[] = { kCTFontURLAttribute }; + const void *values[] = { fontURL }; QCFType attributes = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); QCFType descriptor = CTFontDescriptorCreateWithAttributes(attributes); font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL); + m_applicationURLFonts.append(QCFType::constructFromGet(fontURL)); } else { NSLog(@"Unable to register font: %@", error); CFRelease(error); @@ -499,6 +510,10 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData if (font) { QStringList families; families.append(QCFString(CTFontCopyFamilyName(font))); + + QCFType descriptor = CTFontCopyFontDescriptor(font); + populateFromDescriptor(descriptor); + CFRelease(font); return families; } @@ -534,9 +549,12 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData QStringList families; for (int i = 0; i < containedFonts.size(); ++i) { QCFType font = CTFontCreateWithPlatformFont(containedFonts[i], 12.0, NULL, NULL); + QCFType descriptor = CTFontCopyFontDescriptor(font); + populateFromDescriptor(descriptor); families.append(QCFString(CTFontCopyFamilyName(font))); } + m_applicationFonts.append(fontContainer); return families; } #endif @@ -569,5 +587,28 @@ QList QCoreTextFontDatabase::standardSizes() const return ret; } +void QCoreTextFontDatabase::removeApplicationFonts() +{ +#ifdef Q_OS_MACX +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + CFErrorRef error; + for (int i = 0; i < m_applicationGraphicsFonts.count(); ++i) + CTFontManagerUnregisterGraphicsFont(m_applicationGraphicsFonts[i], &error); + m_applicationGraphicsFonts.clear(); + + for (int i = 0; i < m_applicationURLFonts.count(); ++i) + CTFontManagerUnregisterFontsForURL(m_applicationURLFonts[i], kCTFontManagerScopeProcess, &error); + m_applicationURLFonts.clear(); + } +#else + for (int i = 0; i < m_applicationFonts.count(); ++i) + ATSFontDeactivate(m_applicationFonts[i], 0, kATSOptionFlagsDoNotNotify); + m_applicationFonts.clear(); + ATSFontNotify(kATSFontNotifyActionFontsChanged, 0); +#endif +#endif +} + QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h index 8536ad9123..c3bb4d428a 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h @@ -43,6 +43,14 @@ #define QCORETEXTFONTDATABASE_H #include +#include + +#ifndef Q_OS_IOS +#include +#else +#include +#include +#endif QT_BEGIN_NAMESPACE @@ -63,9 +71,21 @@ public: QList standardSizes() const; private: + void populateFromDescriptor(CTFontDescriptorRef font); + mutable QString defaultFontName; mutable QHash psNameToFamily; mutable QHash familyNameToPsName; + + void removeApplicationFonts(); +#ifdef Q_OS_MACX +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + QVector > m_applicationGraphicsFonts; + QVector > m_applicationURLFonts; +#else + QVector m_applicationFonts; +#endif +#endif }; QT_END_NAMESPACE diff --git a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp index 63da383596..104d3465f2 100644 --- a/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp +++ b/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp @@ -253,11 +253,8 @@ void tst_QFontDatabase::addAppFont() return; #endif QCOMPARE(fontDbChangedSpy.count(), 1); -// addApplicationFont is supported on Mac, don't skip the test if it breaks. -#ifndef Q_OS_MAC if (id == -1) QSKIP("Skip the test since app fonts are not supported on this system"); -#endif const QStringList addedFamilies = QFontDatabase::applicationFontFamilies(id); QVERIFY(!addedFamilies.isEmpty()); @@ -272,9 +269,6 @@ void tst_QFontDatabase::addAppFont() QVERIFY(QFontDatabase::removeApplicationFont(id)); QCOMPARE(fontDbChangedSpy.count(), 2); -#ifdef Q_OS_MAC - QEXPECT_FAIL("font file", "QTBUG-23062", Continue); -#endif QCOMPARE(db.families(), oldFamilies); } -- cgit v1.2.3