UIKit: Improve handling of private system fonts / fallback fonts

Commit 2bc7a40048 taught the CoreText font database to populate the
families lazily, and in the process added a guard to ensure that we
didn't populate internal fonts (prefixed with a '.'), as these fonts
would then show up in font selection dialogs.

Commit 909d3f5c7 then added support for private fonts, by making it
possible to filter out any private fonts from font selection daialogs.
But the guard was not removed, so we were still not populating these
fonts. This guard has been removed, and the filtering function has
been updated to include the conditions of the guard.

Next, commit e5e93345c5 used [UIFont fontNamesForFamilyName:] to verify
that each family that we registered with the font database would also
have matching fonts when finally populated. This is not the right approach,
as [UIFont fontNamesForFamilyName:] does not handle internal fonts.

Instead we trust what CTFontDescriptorCreateMatchingFontDescriptors()
gives us, but make sure to register the resulting font descriptors
with the original/originating font family, instead of the one we pull
out of the font descriptor.

Finally, as of iOS 10, we can use CTFontManagerCopyAvailableFontFamilyNames
instead of [UIFont familyNames], which gives us all of the internal font
families like on macOS, instead of just the user-visible families. For
earlier iOS versions we manually add '.PhoneFallback', as we know it
will be available even if not listed in [UIFont familyNames].

The end result is that we register and populate families like '.PhoneFallback',
which is critical to supporting more esoteric writing systems.

The check in tst_QFont that styles for a given family is not empty has
been removed, as we can't guarantee that on all platforms, which is
also documented for QFontDatabase::styles().

Task-number: QTBUG-45746
Task-number: QTBUG-50624
Change-Id: I04674dcb2bb36b4cdf5646d540c35727ff3daaad
Reviewed-by: Jake Petroules <jake.petroules@qt.io>
This commit is contained in:
Tor Arne Vestbø 2016-12-02 12:44:16 +01:00
parent 2488f34ecf
commit 50cb2687b2
3 changed files with 19 additions and 21 deletions

View File

@ -189,11 +189,16 @@ QCoreTextFontDatabase::~QCoreTextFontDatabase()
static CFArrayRef availableFamilyNames()
{
#if defined(Q_OS_OSX)
return CTFontManagerCopyAvailableFontFamilyNames();
#elif defined(QT_PLATFORM_UIKIT)
return (CFArrayRef) [[UIFont familyNames] retain];
#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(1060, 100000, 100000, 30000)
if (&CTFontManagerCopyAvailableFontFamilyNames)
return CTFontManagerCopyAvailableFontFamilyNames();
#endif
#if defined(QT_PLATFORM_UIKIT)
CFMutableArrayRef familyNames = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, (CFArrayRef)[UIFont familyNames]);
CFArrayAppendValue(familyNames, CFSTR(".PhoneFallback"));
return familyNames;
#endif
Q_UNREACHABLE();
}
void QCoreTextFontDatabase::populateFontDatabase()
@ -207,17 +212,6 @@ void QCoreTextFontDatabase::populateFontDatabase()
for (int i = 0; i < numberOfFamilies; ++i) {
CFStringRef familyNameRef = (CFStringRef) CFArrayGetValueAtIndex(familyNames, i);
QString familyName = QString::fromCFString(familyNameRef);
// Don't populate internal fonts
if (familyName.startsWith(QLatin1Char('.')) || familyName == QLatin1String("LastResort"))
continue;
#if defined(Q_OS_IOS) || defined(Q_OS_TVOS)
// Skip font families with no corresponding fonts
if (![UIFont fontNamesForFamilyName:(NSString*)familyNameRef].count)
continue;
#endif
QPlatformFontDatabase::registerFontFamily(familyName);
#if defined(Q_OS_OSX)
@ -250,7 +244,7 @@ void QCoreTextFontDatabase::populateFamily(const QString &familyName)
const int numFonts = CFArrayGetCount(matchingFonts);
for (int i = 0; i < numFonts; ++i)
populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)));
populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)), familyName);
}
struct FontDescription {
@ -352,13 +346,18 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
}
}
void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font)
void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName)
{
FontDescription fd;
getFontDescription(font, &fd);
// Note: The familyName we are registering, and the family name of the font descriptor, may not
// match, as CTFontDescriptorCreateMatchingFontDescriptors will return descriptors for replacement
// fonts if a font family does not have any fonts available on the system.
QString family = !familyName.isNull() ? familyName : static_cast<QString>(fd.familyName);
CFRetain(font);
QPlatformFontDatabase::registerFont(fd.familyName, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch,
QPlatformFontDatabase::registerFont(family, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch,
true /* antialiased */, true /* scalable */,
fd.pixelSize, fd.fixedPitch, fd.writingSystems, (void *) font);
}
@ -699,7 +698,7 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const
{
if (family.startsWith(QLatin1Char('.')))
if (family.startsWith(QLatin1Char('.')) || family == QLatin1String("LastResort"))
return true;
return QPlatformFontDatabase::isPrivateFontFamily(family);

View File

@ -92,7 +92,7 @@ public:
const QHash<QPlatformTheme::Font, QFont *> &themeFonts() const;
private:
void populateFromDescriptor(CTFontDescriptorRef font);
void populateFromDescriptor(CTFontDescriptorRef font, const QString &familyName = QString());
#ifndef QT_NO_FREETYPE
bool m_useFreeType;

View File

@ -130,7 +130,6 @@ void tst_QFont::italicOblique()
QString family = *f_it;
QStringList styles = fdb.styles(family);
QVERIFY(!styles.isEmpty());
QStringList::ConstIterator s_it, s_end = styles.end();
for (s_it = styles.begin(); s_it != s_end; ++s_it) {
QString style = *s_it;