OSX: Add initial FreeType support
This permits text rendering consistent with other FreeType enabled platforms, like Windows and Linux. Task-number: QTBUG-42839 Change-Id: I8c99bcaa3fb07c16e935a0c3705af467bc3da584 Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
This commit is contained in:
parent
5551295028
commit
bf013c9e74
@ -1120,6 +1120,15 @@ QT_BEGIN_INCLUDE_NAMESPACE
|
||||
|
||||
QT_END_INCLUDE_NAMESPACE
|
||||
|
||||
#if defined(Q_OS_OSX) && !defined(QT_NO_FREETYPE)
|
||||
static const char *s_shapersForOsxFreeType[] =
|
||||
{
|
||||
"ot",
|
||||
"fallback",
|
||||
Q_NULLPTR
|
||||
};
|
||||
#endif
|
||||
|
||||
int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *string, int itemLength, QFontEngine *fontEngine, const QVector<uint> &itemBoundaries, bool kerningEnabled) const
|
||||
{
|
||||
uint glyphs_shaped = 0;
|
||||
@ -1172,7 +1181,15 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si, const ushort *st
|
||||
};
|
||||
const int num_features = 1;
|
||||
|
||||
bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, 0);
|
||||
const char *const *shaper_list = Q_NULLPTR;
|
||||
#if defined(Q_OS_OSX) && !defined(QT_NO_FREETYPE)
|
||||
// What's behind QFontEngine::FaceData::user_data isn't compatible between CoreText and
|
||||
// FreeType font engines - specifically functions in hb-coretext.cc would run into undefined
|
||||
// behavior with data from the FreeType engine. The OpenType shaper works with that engine.
|
||||
if (actualFontEngine->type() == QFontEngine::Freetype)
|
||||
shaper_list = s_shapersForOsxFreeType;
|
||||
#endif
|
||||
bool shapedOk = hb_shape_full(hb_font, buffer, features, num_features, shaper_list);
|
||||
if (Q_UNLIKELY(!shapedOk)) {
|
||||
hb_buffer_destroy(buffer);
|
||||
return 0;
|
||||
|
@ -1,6 +1,13 @@
|
||||
HEADERS += $$PWD/qcoretextfontdatabase_p.h $$PWD/qfontengine_coretext_p.h
|
||||
OBJECTIVE_SOURCES += $$PWD/qfontengine_coretext.mm $$PWD/qcoretextfontdatabase.mm
|
||||
|
||||
contains(QT_CONFIG, freetype) {
|
||||
include($$QT_SOURCE_TREE/src/3rdparty/freetype_dependency.pri)
|
||||
HEADERS += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft_p.h
|
||||
SOURCES += $$QT_SOURCE_TREE/src/gui/text/qfontengine_ft.cpp
|
||||
CONFIG += opentype
|
||||
}
|
||||
|
||||
ios: \
|
||||
# On iOS CoreText and CoreGraphics are stand-alone frameworks
|
||||
LIBS_PRIVATE += -framework CoreText -framework CoreGraphics
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include "qglobal.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <IOKit/graphics/IOGraphicsLib.h>
|
||||
@ -45,6 +47,9 @@
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtCore/QtEndian>
|
||||
#ifndef QT_NO_FREETYPE
|
||||
#include <QtGui/private/qfontengine_ft_p.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -102,8 +107,12 @@ static NSInteger languageMapSort(id obj1, id obj2, void *context)
|
||||
}
|
||||
#endif
|
||||
|
||||
QCoreTextFontDatabase::QCoreTextFontDatabase()
|
||||
QCoreTextFontDatabase::QCoreTextFontDatabase(bool useFreeType)
|
||||
#ifndef QT_NO_FREETYPE
|
||||
: m_useFreeType(useFreeType)
|
||||
#endif
|
||||
{
|
||||
Q_UNUSED(useFreeType)
|
||||
#ifdef Q_OS_MACX
|
||||
QSettings appleSettings(QLatin1String("apple.com"));
|
||||
QVariant appleValue = appleSettings.value(QLatin1String("AppleAntiAliasingThreshold"));
|
||||
@ -348,10 +357,48 @@ void QCoreTextFontDatabase::releaseHandle(void *handle)
|
||||
CFRelease(CTFontDescriptorRef(handle));
|
||||
}
|
||||
|
||||
#ifndef QT_NO_FREETYPE
|
||||
static QByteArray filenameForCFUrl(CFURLRef url)
|
||||
{
|
||||
// The on-stack buffer prevents that a QByteArray allocated for the worst case (MAXPATHLEN)
|
||||
// stays around for the lifetime of the font. Additionally, it helps to move the char
|
||||
// signedness cast to an acceptable place.
|
||||
uchar buffer[MAXPATHLEN];
|
||||
QByteArray filename;
|
||||
|
||||
if (!CFURLGetFileSystemRepresentation(url, true, buffer, sizeof(buffer))) {
|
||||
qWarning("QCoreTextFontDatabase::filenameForCFUrl: could not resolve file for URL %s",
|
||||
qPrintable(QString::fromCFString(CFURLGetString(url))));
|
||||
} else {
|
||||
QCFType<CFStringRef> scheme = CFURLCopyScheme(url);
|
||||
if (QString::fromCFString(scheme) == QLatin1String("qrc"))
|
||||
filename = ":";
|
||||
|
||||
filename += reinterpret_cast<char *>(buffer);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef);
|
||||
|
||||
QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr)
|
||||
{
|
||||
CTFontDescriptorRef descriptor = static_cast<CTFontDescriptorRef>(usrPtr);
|
||||
|
||||
#ifndef QT_NO_FREETYPE
|
||||
if (m_useFreeType) {
|
||||
QCFType<CFURLRef> url(static_cast<CFURLRef>(CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute)));
|
||||
|
||||
QByteArray filename;
|
||||
if (url)
|
||||
filename = filenameForCFUrl(url);
|
||||
|
||||
return freeTypeFontEngine(f, filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
qreal scaledPointSize = f.pixelSize;
|
||||
|
||||
// When 96 DPI is forced, the Mac plugin will use DPI 72 for some
|
||||
@ -363,7 +410,6 @@ QFontEngine *QCoreTextFontDatabase::fontEngine(const QFontDef &f, void *usrPtr)
|
||||
if (QGuiApplication::testAttribute(Qt::AA_Use96Dpi))
|
||||
scaledPointSize = f.pointSize;
|
||||
|
||||
CTFontDescriptorRef descriptor = (CTFontDescriptorRef) usrPtr;
|
||||
CGAffineTransform matrix = qt_transform_from_fontdef(f);
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, scaledPointSize, &matrix);
|
||||
if (font) {
|
||||
@ -385,6 +431,29 @@ static void releaseFontData(void* info, const void* data, size_t size)
|
||||
|
||||
QFontEngine *QCoreTextFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
|
||||
{
|
||||
#ifndef QT_NO_FREETYPE
|
||||
if (m_useFreeType) {
|
||||
QByteArray *fontDataCopy = new QByteArray(fontData);
|
||||
QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(fontDataCopy,
|
||||
fontDataCopy->constData(), fontDataCopy->size(), releaseFontData);
|
||||
QCFType<CGFontRef> cgFont(CGFontCreateWithDataProvider(dataProvider));
|
||||
|
||||
if (!cgFont) {
|
||||
qWarning("QCoreTextFontDatabase::fontEngine: CGFontCreateWithDataProvider failed");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QFontDef fontDef;
|
||||
fontDef.pixelSize = pixelSize;
|
||||
fontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi();
|
||||
fontDef.hintingPreference = hintingPreference;
|
||||
CGAffineTransform transform = qt_transform_from_fontdef(fontDef);
|
||||
QCFType<CTFontRef> ctFont(CTFontCreateWithGraphicsFont(cgFont, fontDef.pixelSize, &transform, Q_NULLPTR));
|
||||
QCFType<CFURLRef> url(static_cast<CFURLRef>(CTFontCopyAttribute(ctFont, kCTFontURLAttribute)));
|
||||
return freeTypeFontEngine(fontDef, filenameForCFUrl(url), fontData);
|
||||
}
|
||||
#endif
|
||||
|
||||
Q_UNUSED(hintingPreference);
|
||||
|
||||
QByteArray* fontDataCopy = new QByteArray(fontData);
|
||||
@ -556,10 +625,36 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo
|
||||
}
|
||||
|
||||
#if HAVE_CORETEXT
|
||||
static CFArrayRef createDescriptorArrayForFont(CTFontRef font)
|
||||
static CFArrayRef createDescriptorArrayForFont(CTFontRef font, const QString &fileName = QString())
|
||||
{
|
||||
CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
||||
CFArrayAppendValue(array, QCFType<CTFontDescriptorRef>(CTFontCopyFontDescriptor(font)));
|
||||
QCFType<CTFontDescriptorRef> descriptor = CTFontCopyFontDescriptor(font);
|
||||
|
||||
Q_UNUSED(fileName)
|
||||
#ifndef QT_NO_FREETYPE
|
||||
// The physical font source URL (usually a local file or Qt resource) is only required for
|
||||
// FreeType, when using non-system fonts, and needs some hackery to attach in a format
|
||||
// agreeable to OSX.
|
||||
if (!fileName.isEmpty()) {
|
||||
QCFType<CFURLRef> fontURL;
|
||||
|
||||
if (fileName.startsWith(QLatin1String(":/"))) {
|
||||
// QUrl::fromLocalFile() doesn't accept qrc pseudo-paths like ":/fonts/myfont.ttf".
|
||||
// Therefore construct from QString with the qrc:// scheme -> "qrc:///fonts/myfont.ttf".
|
||||
fontURL = QUrl(QStringLiteral("qrc://") + fileName.mid(1)).toCFURL();
|
||||
} else if (!fileName.isEmpty()) {
|
||||
// At this point we hope that filename is in a format that QUrl can handle.
|
||||
fontURL = QUrl::fromLocalFile(fileName).toCFURL();
|
||||
}
|
||||
|
||||
QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 1,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryAddValue(attributes, kCTFontURLAttribute, fontURL);
|
||||
descriptor = CTFontDescriptorCreateCopyWithAttributes(descriptor, attributes);
|
||||
}
|
||||
#endif
|
||||
|
||||
CFArrayAppendValue(array, descriptor);
|
||||
return array;
|
||||
}
|
||||
#endif
|
||||
@ -580,7 +675,11 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData
|
||||
if (cgFont) {
|
||||
if (CTFontManagerRegisterGraphicsFont(cgFont, &error)) {
|
||||
QCFType<CTFontRef> font = CTFontCreateWithGraphicsFont(cgFont, 0.0, NULL, NULL);
|
||||
fonts = createDescriptorArrayForFont(font);
|
||||
fonts = createDescriptorArrayForFont(font
|
||||
#ifndef QT_NO_FREETYPE
|
||||
, m_useFreeType ? fileName : QString()
|
||||
#endif
|
||||
);
|
||||
m_applicationFonts.append(QVariant::fromValue(QCFType<CGFontRef>::constructFromGet(cgFont)));
|
||||
}
|
||||
}
|
||||
@ -880,5 +979,25 @@ void QCoreTextFontDatabase::removeApplicationFonts()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef QT_NO_FREETYPE
|
||||
QFontEngine *QCoreTextFontDatabase::freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename,
|
||||
const QByteArray &fontData)
|
||||
{
|
||||
QFontEngine::FaceId faceId;
|
||||
faceId.filename = filename;
|
||||
const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
|
||||
const QFontEngineFT::GlyphFormat format = antialias ? QFontEngineFT::Format_A8
|
||||
: QFontEngineFT::Format_Mono;
|
||||
|
||||
QScopedPointer<QFontEngineFT> engine(new QFontEngineFT(fontDef));
|
||||
if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
|
||||
qWarning() << "QCoreTextFontDatabase::freeTypefontEngine Failed to create engine";
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
return engine.take();
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE
|
||||
class QCoreTextFontDatabase : public QPlatformFontDatabase
|
||||
{
|
||||
public:
|
||||
QCoreTextFontDatabase();
|
||||
QCoreTextFontDatabase(bool useFreeType = false);
|
||||
~QCoreTextFontDatabase();
|
||||
void populateFontDatabase() Q_DECL_OVERRIDE;
|
||||
void populateFamily(const QString &familyName) Q_DECL_OVERRIDE;
|
||||
@ -95,6 +95,11 @@ public:
|
||||
private:
|
||||
void populateFromDescriptor(CTFontDescriptorRef font);
|
||||
|
||||
#ifndef QT_NO_FREETYPE
|
||||
bool m_useFreeType;
|
||||
QFontEngine *freeTypeFontEngine(const QFontDef &fontDef, const QByteArray &filename,
|
||||
const QByteArray &fontData = QByteArray());
|
||||
#endif
|
||||
mutable QString defaultFontName;
|
||||
|
||||
void removeApplicationFonts();
|
||||
|
@ -50,11 +50,9 @@ public:
|
||||
|
||||
QPlatformIntegration * QCocoaIntegrationPlugin::create(const QString& system, const QStringList& paramList)
|
||||
{
|
||||
Q_UNUSED(paramList);
|
||||
|
||||
QMacAutoReleasePool pool;
|
||||
if (system.compare(QLatin1String("cocoa"), Qt::CaseInsensitive) == 0)
|
||||
return new QCocoaIntegration;
|
||||
return new QCocoaIntegration(paramList);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -97,10 +97,16 @@ public:
|
||||
class QCocoaIntegration : public QPlatformIntegration
|
||||
{
|
||||
public:
|
||||
QCocoaIntegration();
|
||||
enum Option {
|
||||
UseFreeTypeFontEngine = 0x1
|
||||
};
|
||||
Q_DECLARE_FLAGS(Options, Option)
|
||||
|
||||
QCocoaIntegration(const QStringList ¶mList);
|
||||
~QCocoaIntegration();
|
||||
|
||||
static QCocoaIntegration *instance();
|
||||
Options options() const;
|
||||
|
||||
bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE;
|
||||
QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE;
|
||||
@ -141,6 +147,7 @@ public:
|
||||
void setApplicationIcon(const QIcon &icon) const Q_DECL_OVERRIDE;
|
||||
private:
|
||||
static QCocoaIntegration *mInstance;
|
||||
Options mOptions;
|
||||
|
||||
QScopedPointer<QCoreTextFontDatabase> mFontDb;
|
||||
|
||||
@ -160,6 +167,8 @@ private:
|
||||
QList<QCocoaWindow *> m_popupWindowStack;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QCocoaIntegration::Options)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
@ -244,10 +244,25 @@ QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height
|
||||
return windowPixmap;
|
||||
}
|
||||
|
||||
static QCocoaIntegration::Options parseOptions(const QStringList ¶mList)
|
||||
{
|
||||
QCocoaIntegration::Options options;
|
||||
foreach (const QString ¶m, paramList) {
|
||||
#ifndef QT_NO_FREETYPE
|
||||
if (param == QLatin1String("fontengine=freetype"))
|
||||
options |= QCocoaIntegration::UseFreeTypeFontEngine;
|
||||
else
|
||||
#endif
|
||||
qWarning() << "Unknown option" << param;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
QCocoaIntegration *QCocoaIntegration::mInstance = 0;
|
||||
|
||||
QCocoaIntegration::QCocoaIntegration()
|
||||
: mFontDb(new QCoreTextFontDatabase())
|
||||
QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList)
|
||||
: mOptions(parseOptions(paramList))
|
||||
, mFontDb(new QCoreTextFontDatabase(mOptions.testFlag(UseFreeTypeFontEngine)))
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
, mAccessibility(new QCocoaAccessibility)
|
||||
#endif
|
||||
@ -345,6 +360,11 @@ QCocoaIntegration *QCocoaIntegration::instance()
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
QCocoaIntegration::Options QCocoaIntegration::options() const
|
||||
{
|
||||
return mOptions;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief Synchronizes the screen list, adds new screens, removes deleted ones
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user