source: trunk/src/gui/text/qfontdatabase_mac.cpp@ 890

Last change on this file since 890 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 19.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
49QT_BEGIN_NAMESPACE
50
51int qt_mac_pixelsize(const QFontDef &def, int dpi); //qfont_mac.cpp
52int qt_mac_pointsize(const QFontDef &def, int dpi); //qfont_mac.cpp
53
54#ifndef QT_MAC_USE_COCOA
55static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont)
56{
57 ByteCount length = 0;
58 if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, 0, 0, &length) != noErr)
59 return;
60 QVarLengthArray<uchar> os2Table(length);
61 if (length < 86
62 || ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, length, os2Table.data(), &length) != noErr)
63 return;
64
65 // See also qfontdatabase_win.cpp, offsets taken from OS/2 table in the TrueType spec
66 quint32 unicodeRange[4] = {
67 qFromBigEndian<quint32>(os2Table.data() + 42),
68 qFromBigEndian<quint32>(os2Table.data() + 46),
69 qFromBigEndian<quint32>(os2Table.data() + 50),
70 qFromBigEndian<quint32>(os2Table.data() + 54)
71 };
72 quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) };
73 QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
74#if 0
75 QCFString name;
76 ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name);
77 qDebug() << systems.count() << "writing systems for" << QString(name);
78qDebug() << "first char" << hex << unicodeRange[0];
79 for (int i = 0; i < systems.count(); ++i)
80 qDebug() << QFontDatabase::writingSystemName(systems.at(i));
81#endif
82 for (int i = 0; i < systems.count(); ++i)
83 family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
84}
85#endif
86
87static void initializeDb()
88{
89 QFontDatabasePrivate *db = privateDb();
90 if(!db || db->count)
91 return;
92
93#if defined(QT_MAC_USE_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
94if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
95 QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
96 if(!collection)
97 return;
98 QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
99 if(!fonts)
100 return;
101 QString foundry_name = "CoreText";
102 const int numFonts = CFArrayGetCount(fonts);
103 for(int i = 0; i < numFonts; ++i) {
104 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
105
106 QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
107 QtFontFamily *family = db->family(family_name, true);
108 for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws)
109 family->writingSystems[ws] = QtFontFamily::Supported;
110 QtFontFoundry *foundry = family->foundry(foundry_name, true);
111
112 QtFontStyle::Key styleKey;
113 if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) {
114 if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
115 Q_ASSERT(CFNumberIsFloatType(weight));
116 double d;
117 if(CFNumberGetValue(weight, kCFNumberDoubleType, &d)) {
118 //qDebug() << "BOLD" << (QString)family_name << d;
119 styleKey.weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
120 }
121 }
122 if(CFNumberRef italic = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
123 Q_ASSERT(CFNumberIsFloatType(italic));
124 double d;
125 if(CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
126 //qDebug() << "ITALIC" << (QString)family_name << d;
127 if (d > 0.0)
128 styleKey.style = QFont::StyleItalic;
129 }
130 }
131 }
132
133 QtFontStyle *style = foundry->style(styleKey, true);
134 style->smoothScalable = true;
135 if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
136 //qDebug() << "WHEE";
137 int pixel_size=0;
138 if(CFNumberIsFloatType(size)) {
139 double d;
140 CFNumberGetValue(size, kCFNumberDoubleType, &d);
141 pixel_size = d;
142 } else {
143 CFNumberGetValue(size, kCFNumberIntType, &pixel_size);
144 }
145 //qDebug() << "SIZE" << (QString)family_name << pixel_size;
146 if(pixel_size)
147 style->pixelSize(pixel_size, true);
148 } else {
149 //qDebug() << "WTF?";
150 }
151 }
152} else
153#endif
154 {
155#ifndef QT_MAC_USE_COCOA
156 FMFontIterator it;
157 if (!FMCreateFontIterator(0, 0, kFMUseGlobalScopeOption, &it)) {
158 while (true) {
159 FMFont fmFont;
160 if (FMGetNextFont(&it, &fmFont) != noErr)
161 break;
162
163 FMFontFamily fmFamily;
164 FMFontStyle fmStyle;
165 QString familyName;
166
167 QtFontStyle::Key styleKey;
168
169 ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
170
171 if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
172 { //sanity check the font, and see if we can use it at all! --Sam
173 ATSUFontID fontID;
174 if(ATSUFONDtoFontID(fmFamily, 0, &fontID) != noErr)
175 continue;
176 }
177
178 if (fmStyle & ::italic)
179 styleKey.style = QFont::StyleItalic;
180 if (fmStyle & ::bold)
181 styleKey.weight = QFont::Bold;
182
183 ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
184 QCFString cfFamilyName;;
185 ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
186 familyName = cfFamilyName;
187 } else {
188 QCFString cfFontName;
189 ATSFontGetName(atsFont, kATSOptionFlagsDefault, &cfFontName);
190 familyName = cfFontName;
191 quint16 macStyle = 0;
192 {
193 uchar data[4];
194 ByteCount len = 4;
195 if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
196 macStyle = qFromBigEndian<quint16>(data);
197 }
198 if (macStyle & 1)
199 styleKey.weight = QFont::Bold;
200 if (macStyle & 2)
201 styleKey.style = QFont::StyleItalic;
202 }
203
204 QtFontFamily *family = db->family(familyName, true);
205 QtFontFoundry *foundry = family->foundry(QString(), true);
206 QtFontStyle *style = foundry->style(styleKey, true);
207 style->pixelSize(0, true);
208 style->smoothScalable = true;
209
210 initWritingSystems(family, atsFont);
211 }
212 FMDisposeFontIterator(&it);
213 }
214#endif
215 }
216
217}
218
219static inline void load(const QString & = QString(), int = -1)
220{
221 initializeDb();
222}
223
224static const char *styleHint(const QFontDef &request)
225{
226 const char *stylehint = 0;
227 switch (request.styleHint) {
228 case QFont::SansSerif:
229 stylehint = "Arial";
230 break;
231 case QFont::Serif:
232 stylehint = "Times New Roman";
233 break;
234 case QFont::TypeWriter:
235 stylehint = "Courier New";
236 break;
237 default:
238 if (request.fixedPitch)
239 stylehint = "Courier New";
240 break;
241 }
242 return stylehint;
243}
244
245void QFontDatabase::load(const QFontPrivate *d, int script)
246{
247 // sanity checks
248 if(!qApp)
249 qWarning("QFont: Must construct a QApplication before a QFont");
250
251 Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
252 Q_UNUSED(script);
253
254 QFontDef req = d->request;
255 req.pixelSize = qt_mac_pixelsize(req, d->dpi);
256
257 // set the point size to 0 to get better caching
258 req.pointSize = 0;
259 QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen);
260
261 if(!(d->engineData = QFontCache::instance()->findEngineData(key))) {
262 d->engineData = new QFontEngineData;
263 QFontCache::instance()->insertEngineData(key, d->engineData);
264 } else {
265 d->engineData->ref.ref();
266 }
267 if(d->engineData->engine) // already loaded
268 return;
269
270 // set it to the actual pointsize, so QFontInfo will do the right thing
271 req.pointSize = qRound(qt_mac_pointsize(d->request, d->dpi));
272
273 QFontEngine *e = QFontCache::instance()->findEngine(key);
274 if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
275 e = new QTestFontEngine(req.pixelSize);
276 e->fontDef = req;
277 }
278
279 if(e) {
280 e->ref.ref();
281 d->engineData->engine = e;
282 return; // the font info and fontdef should already be filled
283 }
284
285 //find the font
286 QStringList family_list = familyList(req);
287
288 const char *stylehint = styleHint(req);
289 if (stylehint)
290 family_list << QLatin1String(stylehint);
291
292 // add QFont::defaultFamily() to the list, for compatibility with
293 // previous versions
294 family_list << QApplication::font().defaultFamily();
295
296 ATSFontFamilyRef familyRef = 0;
297 ATSFontRef fontRef = 0;
298
299 QMutexLocker locker(fontDatabaseMutex());
300 QFontDatabasePrivate *db = privateDb();
301 if (!db->count)
302 initializeDb();
303 for(int i = 0; i < family_list.size(); ++i) {
304 for (int k = 0; k < db->count; ++k) {
305 if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
306 QByteArray family_name = db->families[k]->name.toUtf8();
307 familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
308 if (familyRef) {
309 fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
310 goto FamilyFound;
311 } else {
312#if defined(QT_MAC_USE_COCOA)
313 // ATS and CT disagrees on what the family name should be,
314 // use CT to look up the font if ATS fails.
315 QCFString familyName = QString::fromAscii(family_name);
316 QCFType<CTFontRef> CTfontRef = CTFontCreateWithName(familyName, 12, NULL);
317 QCFType<CTFontDescriptorRef> fontDescriptor = CTFontCopyFontDescriptor(CTfontRef);
318 QCFString displayName = (CFStringRef)CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontDisplayNameAttribute);
319
320 familyRef = ATSFontFamilyFindFromName(displayName, kATSOptionFlagsDefault);
321 if (familyRef) {
322 fontRef = ATSFontFindFromName(displayName, kATSOptionFlagsDefault);
323 goto FamilyFound;
324 }
325#endif
326 }
327 }
328 }
329 }
330FamilyFound:
331 //fill in the engine's font definition
332 QFontDef fontDef = d->request; //copy..
333 if(fontDef.pointSize < 0)
334 fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi);
335 else
336 fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi);
337#if 0
338 ItemCount name_count;
339 if(ATSUCountFontNames(fontID, &name_count) == noErr && name_count) {
340 ItemCount actualName_size;
341 if(ATSUGetIndFontName(fontID, 0, 0, 0, &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) {
342 QByteArray actualName(actualName_size);
343 if(ATSUGetIndFontName(fontID, 0, actualName_size, actualName.data(), &actualName_size, 0, 0, 0, 0) == noErr && actualName_size)
344 fontDef.family = QString::fromUtf8(actualName);
345 }
346 }
347#else
348 {
349 QCFString actualName;
350 if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
351 fontDef.family = actualName;
352 }
353#endif
354
355#ifdef QT_MAC_USE_COCOA
356 QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning);
357#elif 1
358 QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning);
359#else
360 ATSFontFamilyRef atsFamily = familyRef;
361 ATSFontFamilyRef atsFontRef = fontRef;
362
363 FMFont fontID;
364 FMFontFamily fmFamily;
365 FMFontStyle fntStyle = 0;
366 fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
367 if (fmFamily == kInvalidFontFamily) {
368 // Use the ATSFont then...
369 fontID = FMGetFontFromATSFontRef(atsFontRef);
370 } else {
371 if (fontDef.weight >= QFont::Bold)
372 fntStyle |= ::bold;
373 if (fontDef.style != QFont::StyleNormal)
374 fntStyle |= ::italic;
375
376 FMFontStyle intrinsicStyle;
377 FMFont fnt = 0;
378 if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
379 fontID = FMGetATSFontRefFromFont(fnt);
380 }
381
382 OSStatus status;
383
384 const int maxAttributeCount = 5;
385 ATSUAttributeTag tags[maxAttributeCount + 1];
386 ByteCount sizes[maxAttributeCount + 1];
387 ATSUAttributeValuePtr values[maxAttributeCount + 1];
388 int attributeCount = 0;
389
390 Fixed size = FixRatio(fontDef.pixelSize, 1);
391 tags[attributeCount] = kATSUSizeTag;
392 sizes[attributeCount] = sizeof(size);
393 values[attributeCount] = &size;
394 ++attributeCount;
395
396 tags[attributeCount] = kATSUFontTag;
397 sizes[attributeCount] = sizeof(fontID);
398 values[attributeCount] = &fontID;
399 ++attributeCount;
400
401 CGAffineTransform transform = CGAffineTransformIdentity;
402 if (fontDef.stretch != 100) {
403 transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
404 tags[attributeCount] = kATSUFontMatrixTag;
405 sizes[attributeCount] = sizeof(transform);
406 values[attributeCount] = &transform;
407 ++attributeCount;
408 }
409
410 ATSUStyle style;
411 status = ATSUCreateStyle(&style);
412 Q_ASSERT(status == noErr);
413
414 Q_ASSERT(attributeCount < maxAttributeCount + 1);
415 status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
416 Q_ASSERT(status == noErr);
417
418 QFontEngine *engine = new QFontEngineMac(style, fontID, fontDef, /*multiEngine*/ 0);
419 ATSUDisposeStyle(style);
420#endif
421 d->engineData->engine = engine;
422 engine->ref.ref(); //a ref for the engineData->engine
423 QFontCache::instance()->insertEngine(key, engine);
424}
425
426static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
427{
428 ATSFontContainerRef handle;
429 OSStatus e = noErr;
430
431 if(fnt->data.isEmpty()) {
432#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
433 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
434 extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
435 FSRef ref;
436 if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr)
437 return;
438
439 ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
440 } else
441#endif
442 {
443#ifndef Q_WS_MAC64
444 extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp
445 FSSpec spec;
446 if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr)
447 return;
448
449 e = ATSFontActivateFromFileSpecification(&spec, kATSFontContextLocal, kATSFontFormatUnspecified,
450 0, kATSOptionFlagsDefault, &handle);
451#endif
452 }
453 } else {
454 e = ATSFontActivateFromMemory((void *)fnt->data.constData(), fnt->data.size(), kATSFontContextLocal,
455 kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
456
457 fnt->data = QByteArray();
458 }
459
460 if(e != noErr)
461 return;
462
463 ItemCount fontCount = 0;
464 e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, 0, 0, &fontCount);
465 if(e != noErr)
466 return;
467
468 QVarLengthArray<ATSFontRef> containedFonts(fontCount);
469 e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
470 if(e != noErr)
471 return;
472
473 fnt->families.clear();
474#if defined(QT_MAC_USE_COCOA)
475 // Make sure that the family name set on the font matches what
476 // kCTFontFamilyNameAttribute returns in initializeDb().
477 // So far the best solution seems find the installed font
478 // using CoreText and get the family name from it.
479 // (ATSFontFamilyGetName appears to be the correct API, but also
480 // returns the font display name.)
481 for(int i = 0; i < containedFonts.size(); ++i) {
482 QCFString fontPostScriptName;
483 ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName);
484 QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14);
485 QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
486 fnt->families.append(familyName);
487 }
488#else
489 for(int i = 0; i < containedFonts.size(); ++i) {
490 QCFString family;
491 ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family);
492 fnt->families.append(family);
493 }
494#endif
495
496 fnt->handle = handle;
497}
498
499bool QFontDatabase::removeApplicationFont(int handle)
500{
501 QMutexLocker locker(fontDatabaseMutex());
502
503 QFontDatabasePrivate *db = privateDb();
504 if(handle < 0 || handle >= db->applicationFonts.count())
505 return false;
506
507 OSStatus e = ATSFontDeactivate(db->applicationFonts.at(handle).handle,
508 /*iRefCon=*/0, kATSOptionFlagsDefault);
509 if(e != noErr)
510 return false;
511
512 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
513
514 db->invalidate();
515 return true;
516}
517
518bool QFontDatabase::removeAllApplicationFonts()
519{
520 QMutexLocker locker(fontDatabaseMutex());
521
522 QFontDatabasePrivate *db = privateDb();
523 for(int i = 0; i < db->applicationFonts.count(); ++i) {
524 if(!removeApplicationFont(i))
525 return false;
526 }
527 return true;
528}
529
530bool QFontDatabase::supportsThreadedFontRendering()
531{
532 return true;
533}
534
535QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.