1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the plugins of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qcoretextfontdatabase_p.h"
43 #include "qfontengine_coretext_p.h"
44 #include <QtCore/QSettings>
45 #import <Foundation/Foundation.h>
47 // this could become a list of all languages used for each writing
48 // system, instead of using the single most common language.
49 static const char *languageForWritingSystem[] = {
75 "zh-cn", // SimplifiedChinese
76 "zh-tw", // TraditionalChinese
85 enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
87 inline QString qt_mac_NSStringToQString(const NSString *nsstr)
88 { return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
90 int qt_antialiasing_threshold = 0;
92 QFont::StyleHint styleHintFromNSString(NSString *style)
94 if ([style isEqual: @"sans-serif"])
95 return QFont::SansSerif;
96 else if ([style isEqual: @"monospace"])
97 return QFont::Monospace;
98 else if ([style isEqual: @"cursive"])
99 return QFont::Cursive;
100 else if ([style isEqual: @"serif"])
102 else if ([style isEqual: @"fantasy"])
103 return QFont::Fantasy;
104 else // if ([style isEqual: @"default"])
105 return QFont::AnyStyle;
108 static NSInteger languageMapSort(id obj1, id obj2, void *context)
110 NSArray *map1 = (NSArray *) obj1;
111 NSArray *map2 = (NSArray *) obj2;
112 NSArray *languages = (NSArray *) context;
114 NSString *lang1 = [map1 objectAtIndex: 0];
115 NSString *lang2 = [map2 objectAtIndex: 0];
117 return [languages indexOfObject: lang1] - [languages indexOfObject: lang2];
120 QCoreTextFontDatabase::QCoreTextFontDatabase()
122 QSettings appleSettings(QLatin1String("apple.com"));
123 QVariant appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold"));
124 if (appleValue.isValid())
125 qt_antialiasing_threshold = appleValue.toInt();
128 QCoreTextFontDatabase::~QCoreTextFontDatabase()
132 static QString familyNameFromPostScriptName(QHash<QString, QString> &psNameToFamily,
135 QString name = qt_mac_NSStringToQString(psName);
136 if (psNameToFamily.contains(name))
137 return psNameToFamily[name];
139 // Some of the font name in DefaultFontFallbacks.plist are hidden fonts like AquaHiraKaku,
140 // their family name begins with a dot, like ".AquaHiraKaku" or ".Apple Symbols Fallback",
141 // the only way (I've found) to get it are actually creating a CTFont with them. We only
142 // need to do it once though.
143 QCFType<CTFontRef> font = CTFontCreateWithName((CFStringRef) psName, 12.0, NULL);
145 QCFString family = CTFontCopyFamilyName(font);
146 psNameToFamily[name] = family;
153 void QCoreTextFontDatabase::populateFontDatabase()
155 QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
159 QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
163 QString foundryName = QLatin1String("CoreText");
164 const int numFonts = CFArrayGetCount(fonts);
165 QHash<QString, QString> psNameToFamily;
166 for (int i = 0; i < numFonts; ++i) {
167 CTFontDescriptorRef font = (CTFontDescriptorRef) CFArrayGetValueAtIndex(fonts, i);
168 QCFString familyName = (CFStringRef) CTFontDescriptorCopyLocalizedAttribute(font, kCTFontFamilyNameAttribute, NULL);
169 QCFType<CFDictionaryRef> styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
170 QFont::Weight weight = QFont::Normal;
171 QFont::Style style = QFont::StyleNormal;
172 QFont::Stretch stretch = QFont::Unstretched;
173 bool fixedPitch = false;
176 if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
177 Q_ASSERT(CFNumberIsFloatType(weightValue));
179 if (CFNumberGetValue(weightValue, kCFNumberDoubleType, &d))
180 weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
182 if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
183 Q_ASSERT(CFNumberIsFloatType(italic));
185 if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
187 style = QFont::StyleItalic;
190 if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) {
192 if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) {
193 if (d & kCTFontMonoSpaceTrait)
195 if (d & kCTFontExpandedTrait)
196 stretch = QFont::Expanded;
197 else if (d & kCTFontCondensedTrait)
198 stretch = QFont::Condensed;
204 if (QCFType<CFNumberRef> size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
205 if (CFNumberIsFloatType(size)) {
207 CFNumberGetValue(size, kCFNumberDoubleType, &d);
210 CFNumberGetValue(size, kCFNumberIntType, &pixelSize);
214 QSupportedWritingSystems writingSystems;
215 if (QCFType<CFArrayRef> languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
216 for (int i = 1; i < LanguageCount; ++i) {
217 if (!languageForWritingSystem[i])
219 QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII);
220 if (CFArrayContainsValue(languages, CFRangeMake(0, 0), lang))
221 writingSystems.setSupported(QFontDatabase::WritingSystem(i));
226 QPlatformFontDatabase::registerFont(familyName, foundryName, weight, style, stretch,
227 true /* antialiased */, true /* scalable */,
228 pixelSize, fixedPitch, writingSystems, (void *) font);
229 CFStringRef psName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontNameAttribute);
230 // we need PostScript Name to family name mapping for fallback list construction
231 psNameToFamily[qt_mac_NSStringToQString((NSString *) psName)] = familyName;
235 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
236 NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"];
238 NSAutoreleasePool *pool = [NSAutoreleasePool new];
240 NSDictionary *fallbackDict = [NSDictionary dictionaryWithContentsOfFile: @"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist"];
242 for (NSString *style in [fallbackDict allKeys]) {
243 NSArray *list = [fallbackDict valueForKey: style];
244 QFont::StyleHint styleHint = styleHintFromNSString(style);
245 QStringList fallbackList;
246 for (id item in list) {
247 // sort the array based on system language preferences
248 if ([item isKindOfClass: [NSArray class]]) {
249 NSArray *langs = [(NSArray *) item sortedArrayUsingFunction: languageMapSort
251 for (NSArray *map in langs)
252 fallbackList.append(familyNameFromPostScriptName(psNameToFamily, [map objectAtIndex: 1]));
254 else if ([item isKindOfClass: [NSString class]])
255 fallbackList.append(familyNameFromPostScriptName(psNameToFamily, item));
257 fallbackLists[styleHint] = fallbackList;
263 void QCoreTextFontDatabase::releaseHandle(void *handle)
265 CFRelease(CTFontDescriptorRef(handle));
268 QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
272 CTFontDescriptorRef descriptor = (CTFontDescriptorRef) usrPtr;
273 CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, f.pointSize, NULL);
275 QFontEngine *engine = new QCoreTextFontEngine(font, f);
284 QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
289 return fallbackLists[styleHint];
292 QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
294 ATSFontContainerRef fontContainer;
297 if (!fontData.isEmpty()) {
298 e = ATSFontActivateFromMemory((void *) fontData.constData(), fontData.size(),
299 kATSFontContextLocal, kATSFontFormatUnspecified, NULL,
300 kATSOptionFlagsDefault, &fontContainer);
302 OSErr qt_mac_create_fsref(const QString &file, FSRef *fsref);
304 if (qt_mac_create_fsref(fileName, &ref) != noErr)
305 return QStringList();
306 e = ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0,
307 kATSOptionFlagsDefault, &fontContainer);
311 ItemCount fontCount = 0;
312 e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, 0, 0, &fontCount);
314 return QStringList();
316 QVarLengthArray<ATSFontRef> containedFonts(fontCount);
317 e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
319 return QStringList();
321 QStringList families;
322 for (int i = 0; i < containedFonts.size(); ++i) {
323 QCFType<CTFontRef> font = CTFontCreateWithPlatformFont(containedFonts[i], 12.0, NULL, NULL);
324 families.append(QCFString(CTFontCopyFamilyName(font)));
330 return QStringList();
333 QFont QCoreTextFontDatabase::defaultFont() const
335 if (defaultFontName.isEmpty()) {
336 QCFType<CTFontRef> font = CTFontCreateUIFontForLanguage(kCTFontSystemFontType, 12.0, NULL);
337 defaultFontName = (QString) QCFString(CTFontCopyFullName(font));
340 return QFont(defaultFontName);