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 <qt_sanity_bot@ovi.com>
Reviewed-by: Jiang Jiang <jiang.jiang@nokia.com>
bb10
Jiang Jiang 2011-06-04 14:47:22 +02:00 committed by Qt Continuous Integration System
parent b97215b04c
commit 97391be5eb
12 changed files with 216 additions and 95 deletions

View File

@ -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.

View File

@ -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;

View File

@ -80,6 +80,7 @@ struct QFontDef
}
QString family;
QString styleName;
#ifdef Q_WS_X11
QString addStyle;

View File

@ -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<CTFontRef> 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
// 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);
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<CTFontRef> 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
QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, expectedFamily);
CFDictionaryAddValue(attributes, kCTFontStyleNameAttribute, expectedStyle);
QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithAttributes(attributes);
CGAffineTransform transform = qt_transform_from_fontdef(req);
QCFType<CTFontRef> 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)

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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();

View File

@ -61,6 +61,7 @@ public:
QFontInfo &operator=(const QFontInfo &);
QString family() const;
QString styleName() const;
int pixelSize() const;
int pointSize() const;
qreal pointSizeF() const;

View File

@ -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.

View File

@ -84,6 +84,7 @@ public:
bool operator==(const QRawFont &other) const;
QString familyName() const;
QString styleName() const;
QFont::Style style() const;
int weight() const;

View File

@ -77,6 +77,7 @@ private slots:
void insertAndRemoveSubstitutions();
void serializeSpacing();
void lastResortFont();
void styleName();
};
// Testing get/set functions
@ -612,5 +613,17 @@ void tst_QFont::lastResortFont()
QVERIFY(!font.lastResortFont().isEmpty());
}
void tst_QFont::styleName()
{
#if !defined(Q_WS_MAC)
QSKIP("Only tested on Mac", SkipAll);
#else
QFont font("Helvetica Neue");
font.setStyleName("UltraLight");
QCOMPARE(QFontInfo(font).styleName(), QString("UltraLight"));
#endif
}
QTEST_MAIN(tst_QFont)
#include "tst_qfont.moc"