5c6024986d9a3ca2dd59815a8377a62a2fbe78a4
[profile/ivi/qtbase.git] / src / platformsupport / fontdatabases / mac / qcoretextfontdatabase.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcoretextfontdatabase_p.h"
43 #include "qfontengine_coretext_p.h"
44 #include <QtCore/QSettings>
45 #import <Foundation/Foundation.h>
46
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[] = {
50     0,     // Any
51     "en",  // Latin
52     "el",  // Greek
53     "ru",  // Cyrillic
54     "hy",  // Armenian
55     "he",  // Hebrew
56     "ar",  // Arabic
57     "syr", // Syriac
58     "div", // Thaana
59     "hi",  // Devanagari
60     "bn",  // Bengali
61     "pa",  // Gurmukhi
62     "gu",  // Gujarati
63     "or",  // Oriya
64     "ta",  // Tamil
65     "te",  // Telugu
66     "kn",  // Kannada
67     "ml",  // Malayalam
68     "si",  // Sinhala
69     "th",  // Thai
70     "lo",  // Lao
71     "bo",  // Tibetan
72     "my",  // Myanmar
73     "ka",  // Georgian
74     "km",  // Khmer
75     "zh-cn", // SimplifiedChinese
76     "zh-tw", // TraditionalChinese
77     "ja",  // Japanese
78     "ko",  // Korean
79     "vi",  // Vietnamese
80     0, // Symbol
81     0, // Ogham
82     0, // Runic
83     0 // N'Ko
84 };
85 enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
86
87 inline QString qt_mac_NSStringToQString(const NSString *nsstr)
88 { return QCFString::toQString(reinterpret_cast<const CFStringRef>(nsstr)); }
89
90 int qt_antialiasing_threshold = 0;
91
92 QFont::StyleHint styleHintFromNSString(NSString *style)
93 {
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"])
101         return QFont::Serif;
102     else if ([style isEqual: @"fantasy"])
103         return QFont::Fantasy;
104     else // if ([style isEqual: @"default"])
105         return QFont::AnyStyle;
106 }
107
108 static NSInteger languageMapSort(id obj1, id obj2, void *context)
109 {
110     NSArray *map1 = (NSArray *) obj1;
111     NSArray *map2 = (NSArray *) obj2;
112     NSArray *languages = (NSArray *) context;
113
114     NSString *lang1 = [map1 objectAtIndex: 0];
115     NSString *lang2 = [map2 objectAtIndex: 0];
116
117     return [languages indexOfObject: lang1] - [languages indexOfObject: lang2];
118 }
119
120 QCoreTextFontDatabase::QCoreTextFontDatabase()
121 {
122     QSettings appleSettings(QLatin1String("apple.com"));
123     QVariant appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold"));
124     if (appleValue.isValid())
125         qt_antialiasing_threshold = appleValue.toInt();
126 }
127
128 QCoreTextFontDatabase::~QCoreTextFontDatabase()
129 {
130 }
131
132 static QString familyNameFromPostScriptName(QHash<QString, QString> &psNameToFamily,
133                                             NSString *psName)
134 {
135     QString name = qt_mac_NSStringToQString(psName);
136     if (psNameToFamily.contains(name))
137         return psNameToFamily[name];
138     else {
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);
144         if (font) {
145             QCFString family = CTFontCopyFamilyName(font);
146             psNameToFamily[name] = family;
147             return family;
148         }
149         return name;
150     }
151 }
152
153 void QCoreTextFontDatabase::populateFontDatabase()
154 {
155     QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
156     if (! collection)
157         return;
158
159     QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
160     if (! fonts)
161         return;
162
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;
174
175         if (styles) {
176             if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
177                 Q_ASSERT(CFNumberIsFloatType(weightValue));
178                 double d;
179                 if (CFNumberGetValue(weightValue, kCFNumberDoubleType, &d))
180                     weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
181             }
182             if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
183                 Q_ASSERT(CFNumberIsFloatType(italic));
184                 double d;
185                 if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
186                     if (d > 0.0)
187                         style = QFont::StyleItalic;
188                 }
189             }
190             if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) {
191                 int d;
192                 if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) {
193                     if (d & kCTFontMonoSpaceTrait)
194                         fixedPitch = true;
195                     if (d & kCTFontExpandedTrait)
196                         stretch = QFont::Expanded;
197                     else if (d & kCTFontCondensedTrait)
198                         stretch = QFont::Condensed;
199                 }
200             }
201         }
202
203         int pixelSize = 0;
204         if (QCFType<CFNumberRef> size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
205             if (CFNumberIsFloatType(size)) {
206                 double d;
207                 CFNumberGetValue(size, kCFNumberDoubleType, &d);
208                 pixelSize = d;
209             } else {
210                 CFNumberGetValue(size, kCFNumberIntType, &pixelSize);
211             }
212         }
213
214         QSupportedWritingSystems writingSystems;
215         if (QCFType<CFArrayRef> languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) {
216             for (int i = 1; i < LanguageCount; ++i) {
217                 if (!languageForWritingSystem[i])
218                     continue;
219                 QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII);
220                 if (CFArrayContainsValue(languages, CFRangeMake(0, 0), lang))
221                     writingSystems.setSupported(QFontDatabase::WritingSystem(i));
222             }
223         }
224
225         CFRetain(font);
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;
232         CFRelease(psName);
233     }
234
235     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
236     NSArray *languages = [defaults stringArrayForKey: @"AppleLanguages"];
237
238     NSAutoreleasePool *pool = [NSAutoreleasePool new];
239
240     NSDictionary *fallbackDict = [NSDictionary dictionaryWithContentsOfFile: @"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/DefaultFontFallbacks.plist"];
241
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
250                                                                     context: languages];
251                 for (NSArray *map in langs)
252                     fallbackList.append(familyNameFromPostScriptName(psNameToFamily, [map objectAtIndex: 1]));
253             }
254             else if ([item isKindOfClass: [NSString class]])
255                 fallbackList.append(familyNameFromPostScriptName(psNameToFamily, item));
256         }
257         fallbackLists[styleHint] = fallbackList;
258     }
259
260     [pool release];
261 }
262
263 void QCoreTextFontDatabase::releaseHandle(void *handle)
264 {
265     CFRelease(CTFontDescriptorRef(handle));
266 }
267
268 QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
269 {
270     Q_UNUSED(script);
271
272     CTFontDescriptorRef descriptor = (CTFontDescriptorRef) usrPtr;
273     CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, f.pointSize, NULL);
274     if (font) {
275         QFontEngine *engine = new QCoreTextFontEngine(font, f);
276         engine->fontDef = f;
277         CFRelease(font);
278         return engine;
279     }
280
281     return NULL;
282 }
283
284 QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
285 {
286     Q_UNUSED(family);
287     Q_UNUSED(style);
288     Q_UNUSED(script);
289     return fallbackLists[styleHint];
290 }
291
292 QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
293 {
294     ATSFontContainerRef fontContainer;
295     OSStatus e;
296
297     if (!fontData.isEmpty()) {
298         e = ATSFontActivateFromMemory((void *) fontData.constData(), fontData.size(),
299                                       kATSFontContextLocal, kATSFontFormatUnspecified, NULL,
300                                       kATSOptionFlagsDefault, &fontContainer);
301     } else {
302         OSErr qt_mac_create_fsref(const QString &file, FSRef *fsref);
303         FSRef ref;
304         if (qt_mac_create_fsref(fileName, &ref) != noErr)
305             return QStringList();
306         e = ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0,
307                                              kATSOptionFlagsDefault, &fontContainer);
308     }
309
310     if (e == noErr) {
311         ItemCount fontCount = 0;
312         e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, 0, 0, &fontCount);
313         if (e != noErr)
314             return QStringList();
315
316         QVarLengthArray<ATSFontRef> containedFonts(fontCount);
317         e = ATSFontFindFromContainer(fontContainer, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
318         if (e != noErr)
319             return QStringList();
320
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)));
325         }
326
327         return families;
328     }
329
330     return QStringList();
331 }
332
333 QFont QCoreTextFontDatabase::defaultFont() const
334 {
335     if (defaultFontName.isEmpty()) {
336         QCFType<CTFontRef> font = CTFontCreateUIFontForLanguage(kCTFontSystemFontType, 12.0, NULL);
337         defaultFontName = (QString) QCFString(CTFontCopyFullName(font));
338     }
339
340     return QFont(defaultFontName);
341 }
342