From 97391be5ebddc62545ddb88f92fc2045bfa10711 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Sat, 4 Jun 2011 14:47:22 +0200 Subject: Allow selecting fonts with irregular style names Fonts like "Helvetica Neue UltraLight" or "Skia Regular Black Condensed" can't be selected in Qt because either they don't report correct numeric values for weight/stretch/etc. or these values are not mapped from QFont enums in a linear way. Thus we provide a shortcut to select these fonts with PostScript name or full name without resorting to family name matching in QFontDatabase (these fonts are not registered in font database anyway). After this, we can simply use: QFont font("Helvetica Neue"); font.setStyleName("UltraLight"); to select these fonts. QCoreTextFontEngineMulti matched like this can be created directly from the CTFontRef instance instead of creating from the font name, making this process faster. The commit also cleaned up the font loading process in Mac font database a bit, moving the code for family matching into a separate function. Add QFontInfo::styleName() and QRawFont::styleName() to access the resolved style name for a font. Task-number: QTBUG-19366 Change-Id: Iad07768c02ed06cc8d6b7395dec554384f410506 Reviewed-on: http://codereview.qt.nokia.com/333 Reviewed-by: Qt Sanity Bot Reviewed-by: Jiang Jiang --- src/gui/text/qfont.cpp | 50 ++++++++++++ src/gui/text/qfont.h | 6 +- src/gui/text/qfont_p.h | 1 + src/gui/text/qfontdatabase_mac.cpp | 142 ++++++++++++++++++++-------------- src/gui/text/qfontdatabase_x11.cpp | 58 ++++++++------ src/gui/text/qfontengine_coretext.mm | 22 +++--- src/gui/text/qfontengine_coretext_p.h | 4 +- src/gui/text/qfontengine_ft.cpp | 2 + src/gui/text/qfontinfo.h | 1 + src/gui/text/qrawfont.cpp | 13 ++++ src/gui/text/qrawfont.h | 1 + 11 files changed, 204 insertions(+), 96 deletions(-) (limited to 'src/gui') diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 90dd0293fc..d4c81b90db 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -348,6 +348,9 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) if (! (mask & QFont::FamilyResolved)) request.family = other->request.family; + if (! (mask & QFont::StyleNameResolved)) + request.styleName = other->request.styleName; + if (! (mask & QFont::SizeResolved)) { request.pointSize = other->request.pointSize; request.pixelSize = other->request.pixelSize; @@ -896,6 +899,38 @@ void QFont::setFamily(const QString &family) resolve_mask |= QFont::FamilyResolved; } +/*! + \since 4.8 + + Returns the requested font style name, it will be used to match the + font with irregular styles (that can't be normalized in other style + properties). It depends on system font support, thus only works for + Mac OS X and X11 so far. On Windows irregular styles will be added + as separate font families so there is no need for this. + + \sa setFamily() setStyle() +*/ +QString QFont::styleName() const +{ + return d->request.styleName; +} + +/*! + \since 4.8 + + Sets the style name of the font. When set, other style properties + like \a style() and \a weight() will be ignored for font matching. + + \sa styleName() +*/ +void QFont::setStyleName(const QString &styleName) +{ + detach(); + + d->request.styleName = styleName; + resolve_mask |= QFont::StyleNameResolved; +} + /*! Returns the point size of the font. Returns -1 if the font size was specified in pixels. @@ -2508,6 +2543,21 @@ QString QFontInfo::family() const return engine->fontDef.family; } +/*! + \since 4.8 + + Returns the style name of the matched window system font on + system that supports it. + + \sa QFont::styleName() +*/ +QString QFontInfo::styleName() const +{ + QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); + Q_ASSERT(engine != 0); + return engine->fontDef.styleName; +} + /*! Returns the point size of the matched window system font. diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 7636ecf910..14f290ceb3 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -156,7 +156,8 @@ public: LetterSpacingResolved = 0x2000, WordSpacingResolved = 0x4000, HintingPreferenceResolved = 0x8000, - AllPropertiesResolved = 0xffff + StyleNameResolved = 0x10000, + AllPropertiesResolved = 0x1ffff }; QFont(); @@ -168,6 +169,9 @@ public: QString family() const; void setFamily(const QString &); + QString styleName() const; + void setStyleName(const QString &); + int pointSize() const; void setPointSize(int); qreal pointSizeF() const; diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index d36518ee4b..8eeae6ffc1 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -80,6 +80,7 @@ struct QFontDef } QString family; + QString styleName; #ifdef Q_WS_X11 QString addStyle; diff --git a/src/gui/text/qfontdatabase_mac.cpp b/src/gui/text/qfontdatabase_mac.cpp index 6fdaf06c8b..724dbf6c4a 100644 --- a/src/gui/text/qfontdatabase_mac.cpp +++ b/src/gui/text/qfontdatabase_mac.cpp @@ -249,6 +249,63 @@ static inline float weightToFloat(unsigned int weight) return (weight - 50) / 100.0; } +static QFontEngine *loadFromDatabase(const QFontDef &req, const QFontPrivate *d) +{ +#if defined(QT_MAC_USE_COCOA) + QCFString fontName = NULL; +#else + ATSFontFamilyRef familyRef = 0; + ATSFontRef fontRef = 0; +#endif + + 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(); + + 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(); +#if defined(QT_MAC_USE_COCOA) + QCFType ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); + if (ctFont) { + fontName = CTFontCopyFullName(ctFont); + goto found; + } +#else + familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + if (familyRef) { + fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + goto found; + } +#endif + } + } + } +found: +#ifdef QT_MAC_USE_COCOA + if (fontName) + return new QCoreTextFontEngineMulti(fontName, req, d->kerning); +#else + if (familyRef) { + QCFString actualName; + if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) + req.family = actualName; + return new QFontEngineMacMulti(familyRef, req, fontDef, d->kerning); + } +#endif + return NULL; +} + void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks @@ -289,69 +346,38 @@ void QFontDatabase::load(const QFontPrivate *d, int script) 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(); - + QFontEngine *engine = NULL; #if defined(QT_MAC_USE_COCOA) - QCFString fontName = NULL, familyName = NULL; -#else - ATSFontFamilyRef familyRef = 0; - ATSFontRef fontRef = 0; -#endif - - 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(); -#if defined(QT_MAC_USE_COCOA) - QCFType ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); - if (ctFont) { - fontName = CTFontCopyFullName(ctFont); - familyName = CTFontCopyFamilyName(ctFont); - goto FamilyFound; - } -#else - familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - if (familyRef) { - fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - goto FamilyFound; - } -#endif + // Shortcut to get the font directly without going through the font database + if (!req.family.isEmpty() && !req.styleName.isEmpty()) { + QCFString expectedFamily = QCFString(req.family); + QCFString expectedStyle = QCFString(req.styleName); + + QCFType attributes = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, expectedFamily); + CFDictionaryAddValue(attributes, kCTFontStyleNameAttribute, expectedStyle); + + QCFType descriptor = CTFontDescriptorCreateWithAttributes(attributes); + CGAffineTransform transform = qt_transform_from_fontdef(req); + QCFType ctFont = CTFontCreateWithFontDescriptor(descriptor, req.pixelSize, &transform); + if (ctFont) { + QCFString familyName = CTFontCopyFamilyName(ctFont); + // Only accept the font if the family name is exactly the same as we specified + if (CFEqual(expectedFamily, familyName)) { + engine = new QCoreTextFontEngineMulti(ctFont, req, d->kerning); } } } -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); - -#ifdef QT_MAC_USE_COCOA - fontDef.family = familyName; - QFontEngine *engine = new QCoreTextFontEngineMulti(fontName, fontDef, d->kerning); -#else - QCFString actualName; - if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) - fontDef.family = actualName; - QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); #endif - d->engineData->engine = engine; - engine->ref.ref(); //a ref for the engineData->engine - QFontCache::instance()->insertEngine(key, engine); + if (!engine) + engine = loadFromDatabase(req, d); + + if (engine) { + d->engineData->engine = engine; + engine->ref.ref(); + QFontCache::instance()->insertEngine(key, engine); + } } static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index a5fdcb5216..754334c0c4 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -1452,6 +1452,35 @@ static const char *styleHint(const QFontDef &request) void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontDef &request) { + double size_value = qMax(qreal(1.), request.pixelSize); + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); + + if (X11->display && QX11Info::appDepth(screen) <= 8) { + FcPatternDel(pattern, FC_ANTIALIAS); + // can't do antialiasing on 8bpp + FcPatternAddBool(pattern, FC_ANTIALIAS, false); + } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { + FcPatternDel(pattern, FC_ANTIALIAS); + FcPatternAddBool(pattern, FC_ANTIALIAS, + !(request.styleStrategy & QFont::NoAntialias)); + } + + if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { + Q_ASSERT(script < QUnicodeTables::ScriptCount); + FcLangSet *ls = FcLangSetCreate(); + FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); + FcPatternDel(pattern, FC_LANG); + FcPatternAddLangSet(pattern, FC_LANG, ls); + FcLangSetDestroy(ls); + } + + if (!request.styleName.isEmpty()) { + QByteArray cs = request.styleName.toUtf8(); + FcPatternAddString(pattern, FC_STYLE, (const FcChar8 *) cs.constData()); + return; + } + int weight_value = FC_WEIGHT_BLACK; if (request.weight == 0) weight_value = FC_WEIGHT_MEDIUM; @@ -1474,34 +1503,11 @@ void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontD FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, slant_value); - double size_value = qMax(qreal(1.), request.pixelSize); - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); - int stretch = request.stretch; if (!stretch) stretch = 100; FcPatternDel(pattern, FC_WIDTH); FcPatternAddInteger(pattern, FC_WIDTH, stretch); - - if (X11->display && QX11Info::appDepth(screen) <= 8) { - FcPatternDel(pattern, FC_ANTIALIAS); - // can't do antialiasing on 8bpp - FcPatternAddBool(pattern, FC_ANTIALIAS, false); - } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { - FcPatternDel(pattern, FC_ANTIALIAS); - FcPatternAddBool(pattern, FC_ANTIALIAS, - !(request.styleStrategy & QFont::NoAntialias)); - } - - if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { - Q_ASSERT(script < QUnicodeTables::ScriptCount); - FcLangSet *ls = FcLangSetCreate(); - FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); - FcPatternDel(pattern, FC_LANG); - FcPatternAddLangSet(pattern, FC_LANG, ls); - FcLangSetDestroy(ls); - } } static bool preferScalable(const QFontDef &request) @@ -2049,7 +2055,8 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) return; FcPatternDel(pattern, FC_FILE); - FcPatternAddString(pattern, FC_FILE, (const FcChar8 *)fnt->fileName.toUtf8().constData()); + QByteArray cs = fnt->fileName.toUtf8(); + FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData()); FcChar8 *fam = 0, *familylang = 0; int i, n = 0; @@ -2135,7 +2142,8 @@ QString QFontDatabase::resolveFontFamilyAlias(const QString &family) if (!pattern) return family; - FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) family.toUtf8().data()); + QByteArray cs = family.toUtf8(); + FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData()); FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); diff --git a/src/gui/text/qfontengine_coretext.mm b/src/gui/text/qfontengine_coretext.mm index eedcc69a81..a68a1551d9 100644 --- a/src/gui/text/qfontengine_coretext.mm +++ b/src/gui/text/qfontengine_coretext.mm @@ -116,17 +116,11 @@ QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const init(kerning); } -QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CGFontRef cgFontRef, const QFontDef &fontDef, bool kerning) +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning) : QFontEngineMulti(0) { this->fontDef = fontDef; - - transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - } - - ctfont = CTFontCreateWithGraphicsFont(cgFontRef, fontDef.pixelSize, &transform, NULL); + ctfont = (CTFontRef) CFRetain(ctFontRef); init(kerning); } @@ -149,6 +143,9 @@ void QCoreTextFontEngineMulti::init(bool kerning) } QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef); + fontDef.family = fe->fontDef.family; + fontDef.styleName = fe->fontDef.styleName; + transform = fe->transform; fe->ref.ref(); engines.append(fe); } @@ -405,7 +402,7 @@ void QCoreTextFontEngineMulti::loadEngine(int) extern int qt_antialiasing_threshold; // from qapplication.cpp -static inline CGAffineTransform transformFromFontDef(const QFontDef &fontDef) +CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef) { CGAffineTransform transform = CGAffineTransformIdentity; if (fontDef.stretch != 100) @@ -416,7 +413,7 @@ static inline CGAffineTransform transformFromFontDef(const QFontDef &fontDef) QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def) { fontDef = def; - transform = transformFromFontDef(fontDef); + transform = qt_transform_from_fontdef(fontDef); ctfont = font; CFRetain(ctfont); cgFont = CTFontCopyGraphicsFont(font, NULL); @@ -426,7 +423,7 @@ QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def) QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def) { fontDef = def; - transform = transformFromFontDef(fontDef); + transform = qt_transform_from_fontdef(fontDef); cgFont = font; // Keep reference count balanced CFRetain(cgFont); @@ -464,6 +461,9 @@ void QCoreTextFontEngine::init() QCFString family = CTFontCopyFamilyName(ctfont); fontDef.family = family; + QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute); + fontDef.styleName = styleName; + synthesisFlags = 0; CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont); if (traits & kCTFontItalicTrait) diff --git a/src/gui/text/qfontengine_coretext_p.h b/src/gui/text/qfontengine_coretext_p.h index 98d3b50c66..3ca8a0ad60 100644 --- a/src/gui/text/qfontengine_coretext_p.h +++ b/src/gui/text/qfontengine_coretext_p.h @@ -113,7 +113,7 @@ class QCoreTextFontEngineMulti : public QFontEngineMulti { public: QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning); - QCoreTextFontEngineMulti(CGFontRef cgFontRef, const QFontDef &fontDef, bool kerning); + QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning); ~QCoreTextFontEngineMulti(); virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, @@ -141,6 +141,8 @@ private: friend class QFontDialogPrivate; }; +CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); + #endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) #endif // QFONTENGINE_CORETEXT_P_H diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 39abbd6f58..9a5d9d6f2e 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -758,6 +758,8 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, } #endif + fontDef.styleName = QString::fromUtf8(face->style_name); + unlockFace(); fsType = freetype->fsType(); diff --git a/src/gui/text/qfontinfo.h b/src/gui/text/qfontinfo.h index 1238cba4da..37a724ec7c 100644 --- a/src/gui/text/qfontinfo.h +++ b/src/gui/text/qfontinfo.h @@ -61,6 +61,7 @@ public: QFontInfo &operator=(const QFontInfo &); QString family() const; + QString styleName() const; int pixelSize() const; int pointSize() const; qreal pointSizeF() const; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 481180ec78..71762df09f 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -403,6 +403,19 @@ QString QRawFont::familyName() const return d->fontEngine->fontDef.family; } +/*! + Returns the style name of this QRawFont. + + \sa QFont::styleName() +*/ +QString QRawFont::styleName() const +{ + if (!isValid()) + return QString(); + + return d->fontEngine->fontDef.styleName; +} + /*! Returns the style of this QRawFont. diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h index 3857da35e0..aca33af127 100644 --- a/src/gui/text/qrawfont.h +++ b/src/gui/text/qrawfont.h @@ -84,6 +84,7 @@ public: bool operator==(const QRawFont &other) const; QString familyName() const; + QString styleName() const; QFont::Style style() const; int weight() const; -- cgit v1.2.3