blob: 3cc3a535eea7ec9c1c44031ba02ba8fcf1df25d6 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <private/qt_mac_p.h>
#include "qfontengine_p.h"
#include <qfile.h>
#include <qabstractfileengine.h>
#include <stdlib.h>
#include <qendian.h>
QT_BEGIN_NAMESPACE
int qt_mac_pixelsize(const QFontDef &def, int dpi); //qfont_mac.cpp
int qt_mac_pointsize(const QFontDef &def, int dpi); //qfont_mac.cpp
#ifndef QT_MAC_USE_COCOA
static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont)
{
ByteCount length = 0;
if (ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, 0, 0, &length) != noErr)
return;
QVarLengthArray<uchar> os2Table(length);
if (length < 86
|| ATSFontGetTable(atsFont, MAKE_TAG('O', 'S', '/', '2'), 0, length, os2Table.data(), &length) != noErr)
return;
// See also qfontdatabase_win.cpp, offsets taken from OS/2 table in the TrueType spec
quint32 unicodeRange[4] = {
qFromBigEndian<quint32>(os2Table.data() + 42),
qFromBigEndian<quint32>(os2Table.data() + 46),
qFromBigEndian<quint32>(os2Table.data() + 50),
qFromBigEndian<quint32>(os2Table.data() + 54)
};
quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) };
QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
#if 0
QCFString name;
ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name);
qDebug() << systems.count() << "writing systems for" << QString(name);
qDebug() << "first char" << hex << unicodeRange[0];
for (int i = 0; i < systems.count(); ++i)
qDebug() << QFontDatabase::writingSystemName(systems.at(i));
#endif
for (int i = 0; i < systems.count(); ++i)
family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
}
#endif
static void initializeDb()
{
QFontDatabasePrivate *db = privateDb();
if(!db || db->count)
return;
#if defined(QT_MAC_USE_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
if(!collection)
return;
QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
if(!fonts)
return;
QString foundry_name = "CoreText";
const int numFonts = CFArrayGetCount(fonts);
for(int i = 0; i < numFonts; ++i) {
CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i);
QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
QtFontFamily *family = db->family(family_name, true);
for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws)
family->writingSystems[ws] = QtFontFamily::Supported;
QtFontFoundry *foundry = family->foundry(foundry_name, true);
QtFontStyle::Key styleKey;
if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) {
if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) {
Q_ASSERT(CFNumberIsFloatType(weight));
double d;
if(CFNumberGetValue(weight, kCFNumberDoubleType, &d)) {
//qDebug() << "BOLD" << (QString)family_name << d;
styleKey.weight = (d > 0.0) ? QFont::Bold : QFont::Normal;
}
}
if(CFNumberRef italic = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontSlantTrait)) {
Q_ASSERT(CFNumberIsFloatType(italic));
double d;
if(CFNumberGetValue(italic, kCFNumberDoubleType, &d)) {
//qDebug() << "ITALIC" << (QString)family_name << d;
if (d > 0.0)
styleKey.style = QFont::StyleItalic;
}
}
}
QtFontStyle *style = foundry->style(styleKey, true);
style->smoothScalable = true;
if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) {
//qDebug() << "WHEE";
int pixel_size=0;
if(CFNumberIsFloatType(size)) {
double d;
CFNumberGetValue(size, kCFNumberDoubleType, &d);
pixel_size = d;
} else {
CFNumberGetValue(size, kCFNumberIntType, &pixel_size);
}
//qDebug() << "SIZE" << (QString)family_name << pixel_size;
if(pixel_size)
style->pixelSize(pixel_size, true);
} else {
//qDebug() << "WTF?";
}
}
} else
#endif
{
#ifndef QT_MAC_USE_COCOA
FMFontIterator it;
if (!FMCreateFontIterator(0, 0, kFMUseGlobalScopeOption, &it)) {
while (true) {
FMFont fmFont;
if (FMGetNextFont(&it, &fmFont) != noErr)
break;
FMFontFamily fmFamily;
FMFontStyle fmStyle;
QString familyName;
QtFontStyle::Key styleKey;
ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
if (!FMGetFontFamilyInstanceFromFont(fmFont, &fmFamily, &fmStyle)) {
{ //sanity check the font, and see if we can use it at all! --Sam
ATSUFontID fontID;
if(ATSUFONDtoFontID(fmFamily, 0, &fontID) != noErr)
continue;
}
if (fmStyle & ::italic)
styleKey.style = QFont::StyleItalic;
if (fmStyle & ::bold)
styleKey.weight = QFont::Bold;
ATSFontFamilyRef familyRef = FMGetATSFontFamilyRefFromFontFamily(fmFamily);
QCFString cfFamilyName;;
ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &cfFamilyName);
familyName = cfFamilyName;
} else {
QCFString cfFontName;
ATSFontGetName(atsFont, kATSOptionFlagsDefault, &cfFontName);
familyName = cfFontName;
quint16 macStyle = 0;
{
uchar data[4];
ByteCount len = 4;
if (ATSFontGetTable(atsFont, MAKE_TAG('h', 'e', 'a', 'd'), 44, 4, &data, &len) == noErr)
macStyle = qFromBigEndian<quint16>(data);
}
if (macStyle & 1)
styleKey.weight = QFont::Bold;
if (macStyle & 2)
styleKey.style = QFont::StyleItalic;
}
QtFontFamily *family = db->family(familyName, true);
QtFontFoundry *foundry = family->foundry(QString(), true);
QtFontStyle *style = foundry->style(styleKey, true);
style->pixelSize(0, true);
style->smoothScalable = true;
initWritingSystems(family, atsFont);
}
FMDisposeFontIterator(&it);
}
#endif
}
}
static inline void load(const QString & = QString(), int = -1)
{
initializeDb();
}
static const char *styleHint(const QFontDef &request)
{
const char *stylehint = 0;
switch (request.styleHint) {
case QFont::SansSerif:
stylehint = "Arial";
break;
case QFont::Serif:
stylehint = "Times New Roman";
break;
case QFont::TypeWriter:
stylehint = "Courier New";
break;
default:
if (request.fixedPitch)
stylehint = "Courier New";
break;
}
return stylehint;
}
void QFontDatabase::load(const QFontPrivate *d, int script)
{
// sanity checks
if(!qApp)
qWarning("QFont: Must construct a QApplication before a QFont");
Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
Q_UNUSED(script);
QFontDef req = d->request;
req.pixelSize = qt_mac_pixelsize(req, d->dpi);
// set the point size to 0 to get better caching
req.pointSize = 0;
QFontCache::Key key = QFontCache::Key(req, QUnicodeTables::Common, d->screen);
if(!(d->engineData = QFontCache::instance()->findEngineData(key))) {
d->engineData = new QFontEngineData;
QFontCache::instance()->insertEngineData(key, d->engineData);
} else {
d->engineData->ref.ref();
}
if(d->engineData->engine) // already loaded
return;
// set it to the actual pointsize, so QFontInfo will do the right thing
req.pointSize = qRound(qt_mac_pointsize(d->request, d->dpi));
QFontEngine *e = QFontCache::instance()->findEngine(key);
if(!e && qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
e = new QTestFontEngine(req.pixelSize);
e->fontDef = req;
}
if(e) {
e->ref.ref();
d->engineData->engine = e;
return; // the font info and fontdef should already be filled
}
//find the font
QStringList family_list = familyList(req);
const char *stylehint = styleHint(req);
if (stylehint)
family_list << QLatin1String(stylehint);
// add QFont::defaultFamily() to the list, for compatibility with
// previous versions
family_list << QApplication::font().defaultFamily();
ATSFontFamilyRef familyRef = 0;
ATSFontRef fontRef = 0;
QMutexLocker locker(fontDatabaseMutex());
QFontDatabasePrivate *db = privateDb();
if (!db->count)
initializeDb();
for(int i = 0; i < family_list.size(); ++i) {
for (int k = 0; k < db->count; ++k) {
if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) {
QByteArray family_name = db->families[k]->name.toUtf8();
familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
if (familyRef) {
fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault);
goto FamilyFound;
} else {
#if defined(QT_MAC_USE_COCOA)
// ATS and CT disagrees on what the family name should be,
// use CT to look up the font if ATS fails.
QCFString familyName = QString::fromAscii(family_name);
QCFType<CTFontRef> CTfontRef = CTFontCreateWithName(familyName, 12, NULL);
QCFType<CTFontDescriptorRef> fontDescriptor = CTFontCopyFontDescriptor(CTfontRef);
QCFString displayName = (CFStringRef)CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontDisplayNameAttribute);
familyRef = ATSFontFamilyFindFromName(displayName, kATSOptionFlagsDefault);
if (familyRef) {
fontRef = ATSFontFindFromName(displayName, kATSOptionFlagsDefault);
goto FamilyFound;
}
#endif
}
}
}
}
FamilyFound:
//fill in the engine's font definition
QFontDef fontDef = d->request; //copy..
if(fontDef.pointSize < 0)
fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi);
else
fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi);
#if 0
ItemCount name_count;
if(ATSUCountFontNames(fontID, &name_count) == noErr && name_count) {
ItemCount actualName_size;
if(ATSUGetIndFontName(fontID, 0, 0, 0, &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) {
QByteArray actualName(actualName_size);
if(ATSUGetIndFontName(fontID, 0, actualName_size, actualName.data(), &actualName_size, 0, 0, 0, 0) == noErr && actualName_size)
fontDef.family = QString::fromUtf8(actualName);
}
}
#else
{
QCFString actualName;
if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr)
fontDef.family = actualName;
}
#endif
#ifdef QT_MAC_USE_COCOA
QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning);
#elif 1
QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning);
#else
ATSFontFamilyRef atsFamily = familyRef;
ATSFontFamilyRef atsFontRef = fontRef;
FMFont fontID;
FMFontFamily fmFamily;
FMFontStyle fntStyle = 0;
fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily);
if (fmFamily == kInvalidFontFamily) {
// Use the ATSFont then...
fontID = FMGetFontFromATSFontRef(atsFontRef);
} else {
if (fontDef.weight >= QFont::Bold)
fntStyle |= ::bold;
if (fontDef.style != QFont::StyleNormal)
fntStyle |= ::italic;
FMFontStyle intrinsicStyle;
FMFont fnt = 0;
if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr)
fontID = FMGetATSFontRefFromFont(fnt);
}
OSStatus status;
const int maxAttributeCount = 5;
ATSUAttributeTag tags[maxAttributeCount + 1];
ByteCount sizes[maxAttributeCount + 1];
ATSUAttributeValuePtr values[maxAttributeCount + 1];
int attributeCount = 0;
Fixed size = FixRatio(fontDef.pixelSize, 1);
tags[attributeCount] = kATSUSizeTag;
sizes[attributeCount] = sizeof(size);
values[attributeCount] = &size;
++attributeCount;
tags[attributeCount] = kATSUFontTag;
sizes[attributeCount] = sizeof(fontID);
values[attributeCount] = &fontID;
++attributeCount;
CGAffineTransform transform = CGAffineTransformIdentity;
if (fontDef.stretch != 100) {
transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1);
tags[attributeCount] = kATSUFontMatrixTag;
sizes[attributeCount] = sizeof(transform);
values[attributeCount] = &transform;
++attributeCount;
}
ATSUStyle style;
status = ATSUCreateStyle(&style);
Q_ASSERT(status == noErr);
Q_ASSERT(attributeCount < maxAttributeCount + 1);
status = ATSUSetAttributes(style, attributeCount, tags, sizes, values);
Q_ASSERT(status == noErr);
QFontEngine *engine = new QFontEngineMac(style, fontID, fontDef, /*multiEngine*/ 0);
ATSUDisposeStyle(style);
#endif
d->engineData->engine = engine;
engine->ref.ref(); //a ref for the engineData->engine
QFontCache::instance()->insertEngine(key, engine);
}
static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
{
ATSFontContainerRef handle;
OSStatus e = noErr;
if(fnt->data.isEmpty()) {
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
FSRef ref;
if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr)
return;
ATSFontActivateFromFileReference(&ref, kATSFontContextLocal, kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
} else
#endif
{
#ifndef Q_WS_MAC64
extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp
FSSpec spec;
if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr)
return;
e = ATSFontActivateFromFileSpecification(&spec, kATSFontContextLocal, kATSFontFormatUnspecified,
0, kATSOptionFlagsDefault, &handle);
#endif
}
} else {
e = ATSFontActivateFromMemory((void *)fnt->data.constData(), fnt->data.size(), kATSFontContextLocal,
kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &handle);
fnt->data = QByteArray();
}
if(e != noErr)
return;
ItemCount fontCount = 0;
e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, 0, 0, &fontCount);
if(e != noErr)
return;
QVarLengthArray<ATSFontRef> containedFonts(fontCount);
e = ATSFontFindFromContainer(handle, kATSOptionFlagsDefault, fontCount, containedFonts.data(), &fontCount);
if(e != noErr)
return;
fnt->families.clear();
#if defined(QT_MAC_USE_COCOA)
// Make sure that the family name set on the font matches what
// kCTFontFamilyNameAttribute returns in initializeDb().
// So far the best solution seems find the installed font
// using CoreText and get the family name from it.
// (ATSFontFamilyGetName appears to be the correct API, but also
// returns the font display name.)
for(int i = 0; i < containedFonts.size(); ++i) {
QCFString fontPostScriptName;
ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName);
QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14);
QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute);
fnt->families.append(familyName);
}
#else
for(int i = 0; i < containedFonts.size(); ++i) {
QCFString family;
ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family);
fnt->families.append(family);
}
#endif
fnt->handle = handle;
}
bool QFontDatabase::removeApplicationFont(int handle)
{
QMutexLocker locker(fontDatabaseMutex());
QFontDatabasePrivate *db = privateDb();
if(handle < 0 || handle >= db->applicationFonts.count())
return false;
OSStatus e = ATSFontDeactivate(db->applicationFonts.at(handle).handle,
/*iRefCon=*/0, kATSOptionFlagsDefault);
if(e != noErr)
return false;
db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
db->invalidate();
return true;
}
bool QFontDatabase::removeAllApplicationFonts()
{
QMutexLocker locker(fontDatabaseMutex());
QFontDatabasePrivate *db = privateDb();
for(int i = 0; i < db->applicationFonts.count(); ++i) {
if(!removeApplicationFont(i))
return false;
}
return true;
}
bool QFontDatabase::supportsThreadedFontRendering()
{
return true;
}
QT_END_NAMESPACE