Fix uses of qRound on non-floating-point types.
[profile/ivi/qtbase.git] / src / gui / text / qfontdatabase_mac.cpp
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 QtGui module 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 <private/qt_mac_p.h>
43 #include "qfontengine_p.h"
44 #include <qfile.h>
45 #include <qabstractfileengine.h>
46 #include <stdlib.h>
47 #include <qendian.h>
48 #include <private/qfontengine_coretext_p.h>
49 #include <private/qfontengine_mac_p.h>
50
51 QT_BEGIN_NAMESPACE
52
53 int qt_mac_pixelsize(const QFontDef &def, int dpi); //qfont_mac.cpp
54 int qt_mac_pointsize(const QFontDef &def, int dpi); //qfont_mac.cpp
55
56 #ifndef QT_MAC_USE_COCOA
57 static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont)
58 {
59     ByteCount length = 0;
60     if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, 0, 0, &length) != noErr)
61         return;
62     QVarLengthArray<uchar> os2Table(length);
63     if (length < 86
64         || ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, length, os2Table.data(), &length) != noErr)
65         return;
66
67     // See also qfontdatabase_win.cpp, offsets taken from OS/2 table in the TrueType spec
68     quint32 unicodeRange[4] = {
69         qFromBigEndian<quint32>(os2Table.data() + 42),
70         qFromBigEndian<quint32>(os2Table.data() + 46),
71         qFromBigEndian<quint32>(os2Table.data() + 50),
72         qFromBigEndian<quint32>(os2Table.data() + 54)
73     };
74     quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) };
75     QList<QFontDatabase::WritingSystem> systems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
76 #if 0
77     QCFString name;
78     ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name);
79     qDebug() << systems.count() << "writing systems for" << QString(name);
80 qDebug() << "first char" << hex << unicodeRange[0];
81     for (int i = 0; i < systems.count(); ++i)
82         qDebug() << QFontDatabase::writingSystemName(systems.at(i));
83 #endif
84     for (int i = 0; i < systems.count(); ++i)
85         family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
86 }
87 #endif
88
89 static void initializeDb()
90 {
91     QFontDatabasePrivate *db = privateDb();
92     if(!db || db->count)
93         return;
94
95 #if defined(QT_MAC_USE_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
96 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
97     QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
98     if(!collection)
99         return;
100     QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
101     if(!fonts)
102         return;
103     QString foundry_name = "CoreText";
104     const int numFonts = CFArrayGetCount(fonts);
105     for(int i = 0; i < numFonts; ++i) {
106         CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
107
108         QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
109         QCFString style_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute);
110         QtFontFamily *family = db->family(family_name, true);
111         for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws)
112             family->writingSystems[ws] = QtFontFamily::Supported;
113         QtFontFoundry *foundry = family->foundry(foundry_name, true);
114
115         QtFontStyle::Key styleKey;
116         QString styleName = style_name;
117         if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) {
118             if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
119                 Q_ASSERT(CFNumberIsFloatType(weight));
120                 double d;
121                 if(CFNumberGetValue(weight, kCFNumberDoubleType, &d)) {
122                     //qDebug() << "BOLD" << (QString)family_name << d;
123                     styleKey.weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
124                 }
125             }
126             if(CFNumberRef italic = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
127                 Q_ASSERT(CFNumberIsFloatType(italic));
128                 double d;
129                 if(CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
130                     //qDebug() << "ITALIC" << (QString)family_name << d;
131                     if (d > 0.0)
132                         styleKey.style = QFont::StyleItalic;
133                 }
134             }
135         }
136
137         QtFontStyle *style = foundry->style(styleKey, styleName, true);
138         style->smoothScalable = true;
139         if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
140             //qDebug() << "WHEE";
141             int pixel_size=0;
142             if(CFNumberIsFloatType(size)) {
143                 double d;
144                 CFNumberGetValue(size, kCFNumberDoubleType, &d);
145                 pixel_size = d;
146             } else {
147                 CFNumberGetValue(size, kCFNumberIntType, &pixel_size);
148             }
149             //qDebug() << "SIZE" << (QString)family_name << pixel_size;
150             if(pixel_size)
151                 style->pixelSize(pixel_size, true);
152         } else {
153             //qDebug() << "WTF?";
154         }
155     }
156 } else 
157 #endif
158     {
159 #ifndef QT_MAC_USE_COCOA
160         FMFontIterator it;
161         if (!FMCreateFontIterator(0, 0, kFMUseGlobalScopeOption, &it)) {
162             while (true) {
163                 FMFont fmFont;
164                 if (FMGetNextFont(&it, &fmFont) != noErr)
165                     break;
166
167                 FMFontFamily fmFamily;
168                 FMFontStyle fmStyle;
169                 QString familyName;
170
171                 QtFontStyle::Key styleKey;
172
173                 ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
174
175                 if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
176                     { //sanity check the font, and see if we can use it at all! --Sam
177                         ATSUFontID fontID;
178                         if(ATSUFONDtoFontID(fmFamily, 0, &fontID) != noErr)
179                             continue;
180                     }
181
182                     if (fmStyle & ::italic)
183                         styleKey.style = QFont::StyleItalic;
184                     if (fmStyle & ::bold)
185                         styleKey.weight = QFont::Bold;
186
187                     ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
188                     QCFString cfFamilyName;;
189                     ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
190                     familyName = cfFamilyName;
191                 } else {
192                     QCFString cfFontName;
193                     ATSFontGetName(atsFont, kATSOptionFlagsDefault, &cfFontName);
194                     familyName = cfFontName;
195                     quint16 macStyle = 0;
196                     {
197                         uchar data[4];
198                         ByteCount len = 4;
199                         if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
200                             macStyle = qFromBigEndian<quint16>(data);
201                     }
202                     if (macStyle & 1)
203                         styleKey.weight = QFont::Bold;
204                     if (macStyle & 2)
205                         styleKey.style = QFont::StyleItalic;
206                 }
207
208                 QtFontFamily *family = db->family(familyName, true);
209                 QtFontFoundry *foundry = family->foundry(QString(), true);
210                 QtFontStyle *style = foundry->style(styleKey, QString(), true);
211                 style->pixelSize(0, true);
212                 style->smoothScalable = true;
213
214                 initWritingSystems(family, atsFont);
215             }
216             FMDisposeFontIterator(&it);
217         }
218 #endif
219     }
220
221 }
222
223 static inline void load(const QString & = QString(), int = -1)
224 {
225     initializeDb();
226 }
227
228 static const char *styleHint(const QFontDef &request)
229 {
230     const char *stylehint = 0;
231     switch (request.styleHint) {
232     case QFont::SansSerif:
233         stylehint = "Arial";
234         break;
235     case QFont::Serif:
236         stylehint = "Times New Roman";
237         break;
238     case QFont::TypeWriter:
239         stylehint = "Courier New";
240         break;
241     default:
242         if (request.fixedPitch)
243             stylehint = "Courier New";
244         break;
245     }
246     return stylehint;
247 }
248
249 static inline float weightToFloat(unsigned int weight)
250 {
251     return (weight - 50) / 100.0;
252 }
253
254 static QFontEngine *loadFromDatabase(const QFontDef &req, const QFontPrivate *d)
255 {
256 #if defined(QT_MAC_USE_COCOA)
257     QCFString fontName = NULL;
258 #else
259     ATSFontFamilyRef familyRef = 0;
260     ATSFontRef fontRef = 0;
261 #endif
262
263     QStringList family_list = familyList(req);
264
265     const char *stylehint = styleHint(req);
266     if (stylehint)
267         family_list << QLatin1String(stylehint);
268
269     // add QFont::defaultFamily() to the list, for compatibility with previous versions
270     family_list << QApplication::font().defaultFamily();
271
272     QMutexLocker locker(fontDatabaseMutex());
273     QFontDatabasePrivate *db = privateDb();
274     if (!db->count)
275         initializeDb();
276     for (int i = 0; i < family_list.size(); ++i) {
277         for (int k = 0; k < db->count; ++k) {
278             if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
279                 QByteArray family_name = db->families[k]->name.toUtf8();
280 #if defined(QT_MAC_USE_COCOA)
281                 QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL);
282                 if (ctFont) {
283                     fontName = CTFontCopyFullName(ctFont);
284                     goto found;
285                 }
286 #else
287                 familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
288                 if (familyRef) {
289                     fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
290                     goto found;
291                 }
292 #endif
293             }
294         }
295     }
296 found:
297 #ifdef QT_MAC_USE_COCOA
298     if (fontName)
299         return new QCoreTextFontEngineMulti(fontName, req, d->kerning);
300 #else
301     if (familyRef) {
302         QCFString actualName;
303         if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
304             req.family = actualName;
305         return new QFontEngineMacMulti(familyRef, req, fontDef, d->kerning);
306     }
307 #endif
308     return NULL;
309 }
310
311 void QFontDatabase::load(const QFontPrivate *d, int script)
312 {
313     // sanity checks
314     if(!qApp)
315         qWarning("QFont: Must construct a QApplication before a QFont");
316
317     Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
318     Q_UNUSED(script);
319
320     QFontDef req = d->request;
321     req.pixelSize = qt_mac_pixelsize(req, d->dpi);
322
323     // set the point size to 0 to get better caching
324     req.pointSize = 0;
325     QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen);
326
327     if(!(d->engineData = QFontCache::instance()->findEngineData(key))) {
328         d->engineData = new QFontEngineData;
329         QFontCache::instance()->insertEngineData(key, d->engineData);
330     } else {
331         d->engineData->ref.ref();
332     }
333     if(d->engineData->engine) // already loaded
334         return;
335
336     // set it to the actual pointsize, so QFontInfo will do the right thing
337     req.pointSize = qRound(qt_mac_pointsize(d->request, d->dpi));
338
339     QFontEngine *e = QFontCache::instance()->findEngine(key);
340     if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
341         e = new QTestFontEngine(req.pixelSize);
342         e->fontDef = req;
343     }
344
345     if(e) {
346         e->ref.ref();
347         d->engineData->engine = e;
348         return; // the font info and fontdef should already be filled
349     }
350
351     QFontEngine *engine = NULL;
352 #if defined(QT_MAC_USE_COCOA)
353     // Shortcut to get the font directly without going through the font database
354     if (!req.family.isEmpty() && !req.styleName.isEmpty()) {
355         QCFString expectedFamily = QCFString(req.family);
356         QCFString expectedStyle = QCFString(req.styleName);
357
358         QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(NULL, 0,
359             &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
360         CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, expectedFamily);
361         CFDictionaryAddValue(attributes, kCTFontStyleNameAttribute, expectedStyle);
362
363         QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithAttributes(attributes);
364         CGAffineTransform transform = qt_transform_from_fontdef(req);
365         QCFType<CTFontRef> ctFont = CTFontCreateWithFontDescriptor(descriptor, req.pixelSize, &transform);
366         if (ctFont) {
367             QCFString familyName = CTFontCopyFamilyName(ctFont);
368             // Only accept the font if the family name is exactly the same as we specified
369             if (CFEqual(expectedFamily, familyName)) {
370                 engine = new QCoreTextFontEngineMulti(ctFont, req, d->kerning);
371             }
372         }
373     }
374 #endif
375     if (!engine)
376         engine = loadFromDatabase(req, d);
377
378     if (engine) {
379         d->engineData->engine = engine;
380         engine->ref.ref();
381         QFontCache::instance()->insertEngine(key, engine);
382     }
383 }
384
385 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
386 {
387     ATSFontContainerRef handle;
388     OSStatus e  = noErr;
389
390     if(fnt->data.isEmpty()) {
391 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
392         if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
393                 extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
394                 FSRef ref;
395                 if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr)
396                     return;
397
398                 ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
399         } else 
400 #endif
401         {
402 #ifndef Q_WS_MAC64
403                 extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp
404                 FSSpec spec;
405                 if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr)
406                     return;
407
408                 e = ATSFontActivateFromFileSpecification(&spec, kATSFontContextLocal, kATSFontFormatUnspecified,
409                                                    0, kATSOptionFlagsDefault, &handle);
410 #endif
411         }
412     } else {
413         e = ATSFontActivateFromMemory((void *)fnt->data.constData(), fnt->data.size(), kATSFontContextLocal,
414                                            kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
415
416         fnt->data = QByteArray();
417     }
418
419     if(e != noErr)
420         return;
421
422     ItemCount fontCount = 0;
423     e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, 0, 0, &fontCount);
424     if(e != noErr)
425         return;
426
427     QVarLengthArray<ATSFontRef> containedFonts(fontCount);
428     e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
429     if(e != noErr)
430         return;
431
432     fnt->families.clear();
433 #if defined(QT_MAC_USE_COCOA)
434     // Make sure that the family name set on the font matches what
435     // kCTFontFamilyNameAttribute returns in initializeDb().
436     // So far the best solution seems find the installed font
437     // using CoreText and get the family name from it.
438     // (ATSFontFamilyGetName appears to be the correct API, but also
439     // returns the font display name.)
440     for(int i = 0; i < containedFonts.size(); ++i) {
441         QCFString fontPostScriptName;
442         ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName);
443         QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14);
444         QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
445         fnt->families.append(familyName);
446     }
447 #else
448     for(int i = 0; i < containedFonts.size(); ++i) {
449         QCFString family;
450         ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family);
451         fnt->families.append(family);
452     }
453 #endif
454
455     fnt->handle = handle;
456 }
457
458 bool QFontDatabase::removeApplicationFont(int handle)
459 {
460     QMutexLocker locker(fontDatabaseMutex());
461
462     QFontDatabasePrivate *db = privateDb();
463     if(handle < 0 || handle >= db->applicationFonts.count())
464         return false;
465
466     OSStatus e = ATSFontDeactivate(db->applicationFonts.at(handle).handle,
467                                    /*iRefCon=*/0, kATSOptionFlagsDefault);
468     if(e != noErr)
469         return false;
470
471     db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
472
473     db->invalidate();
474     return true;
475 }
476
477 bool QFontDatabase::removeAllApplicationFonts()
478 {
479     QMutexLocker locker(fontDatabaseMutex());
480
481     QFontDatabasePrivate *db = privateDb();
482     for(int i = 0; i < db->applicationFonts.count(); ++i) {
483         if(!removeApplicationFont(i))
484             return false;
485     }
486     return true;
487 }
488
489 bool QFontDatabase::supportsThreadedFontRendering()
490 {
491     return true;
492 }
493
494 QT_END_NAMESPACE