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

Last change on this file since 352 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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