From 97f73e957756753b09a778daf2ee8f0ddb97f746 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Sat, 15 Sep 2018 00:12:42 +0200 Subject: Handle fonts that have commas/quotes in the family name Since the comma character was originally used as a separator, we need to extend QFont to have setFamilies() so that we can avoid joining the family strings together. This enables us to see the family name as a single string and for multiple family names, we have families(). Subsequently, this has added functions to QTextCharFormat to account for multiple font families too. So it is now possible to set a single one directly with setFontFamily() and multiple ones with setFontFamilies(). This also bumps up the datastream version to 19 as QFont now streams the families list as well. [ChangeLog][QtGui][QFont] Add setFamilies()/families() to aid using of font families with commas and quotes in their name. [ChangeLog][Important Behavior Changes] QDataStream version bumped up to 19 to account for changes in the serialization of QFont. Fixes: QTBUG-46322 Change-Id: Iee9f715e47544a7a705c7f36401aba216a7d42b0 Reviewed-by: Lars Knoll Reviewed-by: Allan Sandfeld Jensen --- src/corelib/serialization/qdatastream.cpp | 2 +- src/corelib/serialization/qdatastream.h | 2 +- src/gui/text/qcssparser.cpp | 11 +- src/gui/text/qfont.cpp | 67 ++++++++++- src/gui/text/qfont.h | 6 +- src/gui/text/qfont_p.h | 4 + src/gui/text/qfontdatabase.cpp | 54 ++++----- src/gui/text/qfontengine.cpp | 2 +- src/gui/text/qtextformat.cpp | 23 ++++ src/gui/text/qtextformat.h | 6 + src/gui/text/qtexthtmlparser.cpp | 3 +- tests/auto/gui/text/qfont/qfont.pro | 1 + tests/auto/gui/text/qfont/testfont.qrc | 5 + tests/auto/gui/text/qfont/tst_qfont.cpp | 123 +++++++++++++++++++++ tests/auto/gui/text/qfont/weirdfont.otf | Bin 0 -> 25008 bytes tests/auto/gui/text/qfontcache/tst_qfontcache.cpp | 45 ++++++++ .../tst_qtextdocumentfragment.cpp | 51 ++++++--- 17 files changed, 356 insertions(+), 49 deletions(-) create mode 100644 tests/auto/gui/text/qfont/testfont.qrc create mode 100644 tests/auto/gui/text/qfont/weirdfont.otf diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp index 8566b4fd56..01808ebce6 100644 --- a/src/corelib/serialization/qdatastream.cpp +++ b/src/corelib/serialization/qdatastream.cpp @@ -559,7 +559,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_5_10 Same as Qt_5_6 \value Qt_5_11 Same as Qt_5_6 \value Qt_5_12 Version 18 (Qt 5.12) - \value Qt_5_13 Same as Qt_5_12 + \value Qt_5_13 Version 19 (Qt 5.13) \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index ad69621bbe..2c874700e4 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -99,7 +99,7 @@ public: Qt_5_10 = Qt_5_9, Qt_5_11 = Qt_5_10, Qt_5_12 = 18, - Qt_5_13 = Qt_5_12, + Qt_5_13 = 19, #if QT_VERSION >= 0x050e00 #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 8e317d5b97..91fa40eddf 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -1200,11 +1200,13 @@ static bool setFontWeightFromValue(const QCss::Value &value, QFont *font) static bool setFontFamilyFromValues(const QVector &values, QFont *font, int start = 0) { QString family; + QStringList families; bool shouldAddSpace = false; for (int i = start; i < values.count(); ++i) { const QCss::Value &v = values.at(i); if (v.type == Value::TermOperatorComma) { - family += QLatin1Char(','); + families << family; + family.clear(); shouldAddSpace = false; continue; } @@ -1216,9 +1218,12 @@ static bool setFontFamilyFromValues(const QVector &values, QFont *f family += str; shouldAddSpace = true; } - if (family.isEmpty()) + if (!family.isEmpty()) + families << family; + if (families.isEmpty()) return false; - font->setFamily(family); + font->setFamily(families.at(0)); + font->setFamilies(families); return true; } diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index a23ef95fde..258a9ba675 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -116,7 +116,17 @@ bool QFontDef::exactMatch(const QFontDef &other) const if (stretch != 0 && other.stretch != 0 && stretch != other.stretch) return false; + if (families.size() != other.families.size()) + return false; + QString this_family, this_foundry, other_family, other_foundry; + for (int i = 0; i < families.size(); ++i) { + QFontDatabase::parseFontName(families.at(i), this_foundry, this_family); + QFontDatabase::parseFontName(other.families.at(i), other_foundry, other_family); + if (this_family != other_family || this_foundry != other_foundry) + return false; + } + QFontDatabase::parseFontName(family, this_foundry, this_family); QFontDatabase::parseFontName(other.family, other_foundry, other_family); @@ -261,6 +271,9 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) if (! (mask & QFont::FamilyResolved)) request.family = other->request.family; + if (!(mask & QFont::FamiliesResolved)) + request.families = other->request.families; + if (! (mask & QFont::StyleNameResolved)) request.styleName = other->request.styleName; @@ -424,7 +437,8 @@ QFontEngineData::~QFontEngineData() \target fontmatching The font matching algorithm works as follows: \list 1 - \li If the specified font family exists and can be used to represent + \li The specified font families (set by setFamilies()) are searched for. + \li If not found, then if set the specified font family exists and can be used to represent the writing system in use, it will be selected. \li If not, a replacement font that supports the writing system is selected. The font matching algorithm will try to find the @@ -506,6 +520,7 @@ QFontEngineData::~QFontEngineData() individually and then considered resolved. \value FamilyResolved + \value FamiliesResolved \value SizeResolved \value StyleHintResolved \value StyleStrategyResolved @@ -1691,6 +1706,7 @@ bool QFont::operator<(const QFont &f) const if (r1.stretch != r2.stretch) return r1.stretch < r2.stretch; if (r1.styleHint != r2.styleHint) return r1.styleHint < r2.styleHint; if (r1.styleStrategy != r2.styleStrategy) return r1.styleStrategy < r2.styleStrategy; + if (r1.families != r2.families) return r1.families < r2.families; if (r1.family != r2.family) return r1.family < r2.family; if (f.d->capital != d->capital) return f.d->capital < d->capital; @@ -2192,6 +2208,48 @@ QString QFont::lastResortFont() const } #endif +/*! + \since 5.13 + + Returns the requested font family names, i.e. the names set in the last + setFamilies() call or via the constructor. Otherwise it returns an + empty list. + + \sa setFamily(), setFamilies(), family(), substitutes(), substitute() +*/ + +QStringList QFont::families() const +{ + return d->request.families; +} + +/*! + \since 5.13 + + Sets the list of family names for the font. The names are case + insensitive and may include a foundry name. The first family in + \a families will be set as the main family for the font. + + Each family name entry in \a families may optionally also include a + foundry name, e.g. "Helvetica [Cronyx]". If the family is + available from more than one foundry and the foundry isn't + specified, an arbitrary foundry is chosen. If the family isn't + available a family will be set using the \l{QFont}{font matching} + algorithm. + + \sa family(), families(), setFamily(), setStyleHint(), QFontInfo +*/ + +void QFont::setFamilies(const QStringList &families) +{ + if ((resolve_mask & QFont::FamiliesResolved) && d->request.families == families) + return; + detach(); + d->request.families = families; + resolve_mask |= QFont::FamiliesResolved; +} + + /***************************************************************************** QFont stream functions *****************************************************************************/ @@ -2256,6 +2314,8 @@ QDataStream &operator<<(QDataStream &s, const QFont &font) s << (quint8)font.d->request.hintingPreference; if (s.version() >= QDataStream::Qt_5_6) s << (quint8)font.d->capital; + if (s.version() >= QDataStream::Qt_5_13) + s << font.d->request.families; return s; } @@ -2351,6 +2411,11 @@ QDataStream &operator>>(QDataStream &s, QFont &font) s >> value; font.d->capital = QFont::Capitalization(value); } + if (s.version() >= QDataStream::Qt_5_13) { + QStringList value; + s >> value; + font.d->request.families = value; + } return s; } diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index a94586166e..1fe450e002 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -164,7 +164,8 @@ public: WordSpacingResolved = 0x4000, HintingPreferenceResolved = 0x8000, StyleNameResolved = 0x10000, - AllPropertiesResolved = 0x1ffff + FamiliesResolved = 0x20000, + AllPropertiesResolved = 0x3ffff }; QFont(); @@ -179,6 +180,9 @@ public: QString family() const; void setFamily(const QString &); + QStringList families() const; + void setFamilies(const QStringList &); + QString styleName() const; void setStyleName(const QString &); diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 350ba7ce3c..09c18de5a6 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -78,6 +78,7 @@ struct QFontDef } QString family; + QStringList families; QString styleName; QStringList fallBackFamilies; @@ -109,6 +110,7 @@ struct QFontDef && styleStrategy == other.styleStrategy && ignorePitch == other.ignorePitch && fixedPitch == other.fixedPitch && family == other.family + && families == other.families && styleName == other.styleName && hintingPreference == other.hintingPreference ; @@ -122,6 +124,7 @@ struct QFontDef if (styleHint != other.styleHint) return styleHint < other.styleHint; if (styleStrategy != other.styleStrategy) return styleStrategy < other.styleStrategy; if (family != other.family) return family < other.family; + if (families != other.families) return families < other.families; if (styleName != other.styleName) return styleName < other.styleName; if (hintingPreference != other.hintingPreference) return hintingPreference < other.hintingPreference; @@ -144,6 +147,7 @@ inline uint qHash(const QFontDef &fd, uint seed = 0) Q_DECL_NOTHROW ^ qHash(fd.ignorePitch) ^ qHash(fd.fixedPitch) ^ qHash(fd.family, seed) + ^ qHash(fd.families, seed) ^ qHash(fd.styleName) ^ qHash(fd.hintingPreference) ; diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 196eebb353..42e7871214 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -696,20 +696,20 @@ static QStringList familyList(const QFontDef &req) { // list of families to try QStringList family_list; - if (req.family.isEmpty()) - return family_list; - const auto list = req.family.splitRef(QLatin1Char(',')); - const int numFamilies = list.size(); - family_list.reserve(numFamilies); - for (int i = 0; i < numFamilies; ++i) { - QStringRef str = list.at(i).trimmed(); - if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) - || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\'')))) - str = str.mid(1, str.length() - 2); - family_list << str.toString(); + family_list << req.families; + if (!req.family.isEmpty()) { + const auto list = req.family.splitRef(QLatin1Char(',')); + const int numFamilies = list.size(); + family_list.reserve(numFamilies); + for (int i = 0; i < numFamilies; ++i) { + QStringRef str = list.at(i).trimmed(); + if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"'))) + || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\'')))) + str = str.mid(1, str.length() - 2); + family_list << str.toString(); + } } - // append the substitute list for each family in family_list for (int i = 0, size = family_list.size(); i < size; ++i) family_list += QFont::substitutes(family_list.at(i)); @@ -2688,9 +2688,8 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) } QString family_name, foundry_name; - - parseFontName(request.family, foundry_name, family_name); - + const QString requestFamily = request.families.size() > 0 ? request.families.at(0) : request.family; + parseFontName(requestFamily, foundry_name, family_name); QtFontDesc desc; QList blackListed; int index = match(multi ? QChar::Script_Common : script, request, family_name, foundry_name, &desc, blackListed); @@ -2703,8 +2702,8 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) // Don't pass empty family names to the platform font database, since it will then invoke its own matching // and we will be out of sync with the matched font. - if (fontDef.family.isEmpty()) - fontDef.family = desc.family->name; + if (fontDef.families.isEmpty() && fontDef.family.isEmpty()) + fontDef.families = QStringList(desc.family->name); engine = loadEngine(script, fontDef, desc.family, desc.foundry, desc.style, desc.size); @@ -2717,13 +2716,13 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) } if (!engine) { - if (!request.family.isEmpty()) { + if (!requestFamily.isEmpty()) { QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint); if (styleHint == QFont::AnyStyle && request.fixedPitch) styleHint = QFont::TypeWriter; QStringList fallbacks = request.fallBackFamilies - + fallbacksForFamily(request.family, + + fallbacksForFamily(requestFamily, QFont::Style(request.style), styleHint, QChar::Script(script)); @@ -2741,7 +2740,7 @@ QFontEngine *QFontDatabase::findFont(const QFontDef &request, int script) index = match(multi ? QChar::Script_Common : script, def, def.family, QLatin1String(""), &desc, blackListed); if (index >= 0) { QFontDef loadDef = def; - if (loadDef.family.isEmpty()) + if (loadDef.families.isEmpty() && loadDef.family.isEmpty()) loadDef.family = desc.family->name; engine = loadEngine(script, loadDef, desc.family, desc.foundry, desc.style, desc.size); if (engine) @@ -2782,7 +2781,10 @@ void QFontDatabase::load(const QFontPrivate *d, int script) // look for the requested font in the engine data cache // note: fallBackFamilies are not respected in the EngineData cache key; // join them with the primary selection family to avoid cache misses - req.family = fallBackFamilies.join(QLatin1Char(',')); + if (!d->request.family.isEmpty()) + req.family = fallBackFamilies.join(QLatin1Char(',')); + if (!d->request.families.isEmpty()) + req.families = fallBackFamilies; d->engineData = fontCache->findEngineData(req); if (!d->engineData) { @@ -2803,14 +2805,14 @@ void QFontDatabase::load(const QFontPrivate *d, int script) req.fallBackFamilies = fallBackFamilies; if (!req.fallBackFamilies.isEmpty()) - req.family = req.fallBackFamilies.takeFirst(); + req.families = QStringList(req.fallBackFamilies.takeFirst()); // list of families to try QStringList family_list; - if (!req.family.isEmpty()) { + if (!req.families.isEmpty()) { // Add primary selection - family_list << req.family; + family_list << req.families.at(0); // add the default family QString defaultFamily = QGuiApplication::font().family(); @@ -2824,11 +2826,11 @@ void QFontDatabase::load(const QFontPrivate *d, int script) QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); for (; !fe && it != end; ++it) { - req.family = *it; + req.families = QStringList(*it); fe = QFontDatabase::findFont(req, script); if (fe) { - if (fe->type() == QFontEngine::Box && !req.family.isEmpty()) { + if (fe->type() == QFontEngine::Box && !req.families.isEmpty()) { if (fe->ref.load() == 0) delete fe; fe = 0; diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 9b0b0ec0d5..b06b64ca63 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1842,7 +1842,7 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) { QFontDef request(fontDef); request.styleStrategy |= QFont::NoFontMerging; - request.family = fallbackFamilyAt(at - 1); + request.families = QStringList(fallbackFamilyAt(at - 1)); // At this point, the main script of the text has already been considered // when fetching the list of fallback families from the database, and the diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 4957da1908..18de8408b5 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -361,6 +361,9 @@ void QTextFormatPrivate::recalcFont() const case QTextFormat::FontFamily: f.setFamily(props.at(i).value.toString()); break; + case QTextFormat::FontFamilies: + f.setFamilies(props.at(i).value.toStringList()); + break; case QTextFormat::FontPointSize: f.setPointSizeF(props.at(i).value.toReal()); break; @@ -562,6 +565,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) Character properties \value FontFamily + \value FontFamilies \value FontPointSize \value FontPixelSize \value FontSizeAdjustment Specifies the change in size given to the fontsize already set using @@ -1390,7 +1394,23 @@ QTextCharFormat::QTextCharFormat(const QTextFormat &fmt) \sa font() */ +/*! + \fn void QTextCharFormat::setFontFamilies(const QStringList &families) + \since 5.13 + + Sets the text format's font \a families. + + \sa setFont() +*/ + +/*! + \fn QStringList QTextCharFormat::fontFamilies() const + \since 5.13 + + Returns the text format's font families. + \sa font() +*/ /*! \fn void QTextCharFormat::setFontPointSize(qreal size) @@ -1919,6 +1939,9 @@ void QTextCharFormat::setFont(const QFont &font, FontPropertiesInheritanceBehavi if (mask & QFont::FamilyResolved) setFontFamily(font.family()); + if (mask & QFont::FamiliesResolved) + setFontFamilies(font.families()); + if (mask & QFont::SizeResolved) { const qreal pointSize = font.pointSizeF(); if (pointSize > 0) { diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index a8e573d5a4..d27d2e7f17 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -188,6 +188,7 @@ public: FontStyleStrategy = 0x1FE4, FontKerning = 0x1FE5, FontHintingPreference = 0x1FE6, + FontFamilies = 0x1FE7, FontFamily = 0x2000, FontPointSize = 0x2001, FontSizeAdjustment = 0x2002, @@ -428,6 +429,11 @@ public: inline QString fontFamily() const { return stringProperty(FontFamily); } + inline void setFontFamilies(const QStringList &families) + { setProperty(FontFamilies, QVariant(families)); } + inline QVariant fontFamilies() const + { return property(FontFamilies); } + inline void setFontPointSize(qreal size) { setProperty(FontPointSize, size); } inline qreal fontPointSize() const diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 4be800b251..9d2a5d553a 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -862,7 +862,8 @@ QString QTextHtmlParser::parseWord() ++pos; while (pos < len) { QChar c = txt.at(pos++); - if (c == QLatin1Char('\'')) + // Allow for escaped single quotes as they may be part of the string + if (c == QLatin1Char('\'') && (txt.length() > 1 && txt.at(pos - 2) != QLatin1Char('\\'))) break; else word += c; diff --git a/tests/auto/gui/text/qfont/qfont.pro b/tests/auto/gui/text/qfont/qfont.pro index 048d952faf..96cd4cfdab 100644 --- a/tests/auto/gui/text/qfont/qfont.pro +++ b/tests/auto/gui/text/qfont/qfont.pro @@ -4,3 +4,4 @@ QT += testlib QT += core-private gui-private qtHaveModule(widgets): QT += widgets SOURCES += tst_qfont.cpp +RESOURCES += testfont.qrc diff --git a/tests/auto/gui/text/qfont/testfont.qrc b/tests/auto/gui/text/qfont/testfont.qrc new file mode 100644 index 0000000000..cf51e4a2b4 --- /dev/null +++ b/tests/auto/gui/text/qfont/testfont.qrc @@ -0,0 +1,5 @@ + + + weirdfont.otf + + diff --git a/tests/auto/gui/text/qfont/tst_qfont.cpp b/tests/auto/gui/text/qfont/tst_qfont.cpp index 8090f38a2c..9acf877790 100644 --- a/tests/auto/gui/text/qfont/tst_qfont.cpp +++ b/tests/auto/gui/text/qfont/tst_qfont.cpp @@ -66,6 +66,12 @@ private slots: void fromStringWithoutStyleName(); void sharing(); + void familyNameWithCommaQuote_data(); + void familyNameWithCommaQuote(); + void setFamilies_data(); + void setFamilies(); + void setFamiliesAndFamily_data(); + void setFamiliesAndFamily(); }; // Testing get/set functions @@ -116,6 +122,14 @@ void tst_QFont::exactMatch() QVERIFY(!QFont("sans-serif").exactMatch()); QVERIFY(!QFont("serif").exactMatch()); QVERIFY(!QFont("monospace").exactMatch()); + + font.setFamilies(QStringList() << "BogusFont"); + QVERIFY(!font.exactMatch()); + QVERIFY(!QFont("sans").exactMatch()); + QVERIFY(!QFont("sans-serif").exactMatch()); + QVERIFY(!QFont("serif").exactMatch()); + QVERIFY(!QFont("monospace").exactMatch()); + } void tst_QFont::italicOblique() @@ -277,6 +291,12 @@ void tst_QFont::resolve() QCOMPARE(f4.pointSize(), 45); f4 = f4.resolve(f3); QCOMPARE(f4.pointSize(), 55); + + QFont font5, font6; + const QStringList fontFamilies = { QStringLiteral("Arial") }; + font5.setFamilies(fontFamilies); + font6 = font6.resolve(font5); + QCOMPARE(font6.families(), fontFamilies); } #ifndef QT_NO_WIDGETS @@ -624,5 +644,108 @@ void tst_QFont::sharing() QVERIFY(QFontPrivate::get(f2)->engineData != QFontPrivate::get(f)->engineData); } +void tst_QFont::familyNameWithCommaQuote_data() +{ + QTest::addColumn("familyName"); + QTest::addColumn("chosenFamilyName"); + + const QString standardFont(QFont().defaultFamily()); + if (standardFont.isEmpty()) + QSKIP("No default font available on the system"); + const QString weirdFont(QLatin1String("'My, weird'' font name',")); + const QString commaSeparated(standardFont + QLatin1String(",Times New Roman")); + const QString commaSeparatedWeird(weirdFont + QLatin1String(",") + standardFont); + const QString commaSeparatedBogus(QLatin1String("BogusFont,") + standardFont); + + QTest::newRow("standard") << standardFont << standardFont; + QTest::newRow("weird") << weirdFont << weirdFont; + QTest::newRow("commaSeparated") << commaSeparated << standardFont; + QTest::newRow("commaSeparatedWeird") << commaSeparatedWeird << weirdFont; + QTest::newRow("commaSeparatedBogus") << commaSeparatedBogus << standardFont; +} + +void tst_QFont::familyNameWithCommaQuote() +{ + QFETCH(QString, familyName); + QFETCH(QString, chosenFamilyName); + + const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf"); + + QVERIFY(weirdFontId != -1); + QFont f(familyName); + QCOMPARE(f.family(), familyName); + QCOMPARE(QFontInfo(f).family(), chosenFamilyName); + + QFontDatabase::removeApplicationFont(weirdFontId); +} + +void tst_QFont::setFamilies_data() +{ + QTest::addColumn("families"); + QTest::addColumn("chosenFamilyName"); + + const QString weirdFont(QLatin1String("'My, weird'' font name',")); + const QString standardFont(QFont().defaultFamily()); + if (standardFont.isEmpty()) + QSKIP("No default font available on the system"); + + QTest::newRow("standard") << (QStringList() << standardFont) << standardFont; + QTest::newRow("weird") << (QStringList() << weirdFont) << weirdFont; + QTest::newRow("standard-weird") << (QStringList() << standardFont << weirdFont) << standardFont; + QTest::newRow("weird-standard") << (QStringList() << weirdFont << standardFont) << weirdFont; + QTest::newRow("nonexist-weird") << (QStringList() << "NonExistentFont" << weirdFont) << weirdFont; +} + +void tst_QFont::setFamilies() +{ + QFETCH(QStringList, families); + QFETCH(QString, chosenFamilyName); + + const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf"); + + QVERIFY(weirdFontId != -1); + QFont f; + f.setFamilies(families); + QCOMPARE(QFontInfo(f).family(), chosenFamilyName); + + QFontDatabase::removeApplicationFont(weirdFontId); +} + +void tst_QFont::setFamiliesAndFamily_data() +{ + QTest::addColumn("families"); + QTest::addColumn("family"); + QTest::addColumn("chosenFamilyName"); + + const QString weirdFont(QLatin1String("'My, weird'' font name',")); + const QString defaultFont(QFont().defaultFamily()); + if (defaultFont.isEmpty()) + QSKIP("No default font available on the system"); + + const QString timesFont(QLatin1String("Times")); + const QString nonExistFont(QLatin1String("NonExistentFont")); + + QTest::newRow("firstInFamilies") << (QStringList() << defaultFont << timesFont) << weirdFont << defaultFont; + QTest::newRow("secondInFamilies") << (QStringList() << nonExistFont << weirdFont) << defaultFont << weirdFont; + QTest::newRow("family") << (QStringList() << nonExistFont) << defaultFont << defaultFont; +} + +void tst_QFont::setFamiliesAndFamily() +{ + QFETCH(QStringList, families); + QFETCH(QString, family); + QFETCH(QString, chosenFamilyName); + + const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf"); + + QVERIFY(weirdFontId != -1); + QFont f; + f.setFamilies(families); + f.setFamily(family); + QCOMPARE(QFontInfo(f).family(), chosenFamilyName); + + QFontDatabase::removeApplicationFont(weirdFontId); +} + QTEST_MAIN(tst_QFont) #include "tst_qfont.moc" diff --git a/tests/auto/gui/text/qfont/weirdfont.otf b/tests/auto/gui/text/qfont/weirdfont.otf new file mode 100644 index 0000000000..b91c559f5b Binary files /dev/null and b/tests/auto/gui/text/qfont/weirdfont.otf differ diff --git a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp index fbca313ea3..785cc3fef2 100644 --- a/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp +++ b/tests/auto/gui/text/qfontcache/tst_qfontcache.cpp @@ -45,6 +45,8 @@ public: private slots: void engineData_data(); void engineData(); + void engineDataFamilies_data(); + void engineDataFamilies(); void clear(); }; @@ -109,6 +111,49 @@ void tst_QFontCache::engineData() QCOMPARE(engineData, QFontPrivate::get(f)->engineData); } +void tst_QFontCache::engineDataFamilies_data() +{ + QTest::addColumn("families"); + + const QStringList multiple = { QLatin1String("invalid"), QLatin1String("Times New Roman") }; + const QStringList multipleQuotes = { QLatin1String("'invalid'"), QLatin1String("Times New Roman") }; + const QStringList multiple2 = { QLatin1String("invalid"), QLatin1String("Times New Roman"), + QLatin1String("foobar"), QLatin1String("'baz'") }; + + QTest::newRow("unquoted-family-name") << QStringList(QLatin1String("Times New Roman")); + QTest::newRow("quoted-family-name") << QStringList(QLatin1String("Times New Roman")); + QTest::newRow("invalid") << QStringList(QLatin1String("invalid")); + QTest::newRow("multiple") << multiple; + QTest::newRow("multiple spaces quotes") << multipleQuotes; + QTest::newRow("multiple2") << multiple2; +} + +void tst_QFontCache::engineDataFamilies() +{ + QFETCH(QStringList, families); + + QFont f; + f.setFamily(QString()); // Unset the family as taken from the QGuiApplication default + f.setFamilies(families); + f.exactMatch(); // loads engine + QFontPrivate *d = QFontPrivate::get(f); + + QFontDef req = d->request; + // copy-pasted from QFontDatabase::load(), to engineer the cache key + if (req.pixelSize == -1) { + req.pixelSize = std::floor(((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100; + req.pixelSize = qRound(req.pixelSize); + } + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72.0/d->dpi; + + req.families = families; + + QFontEngineData *engineData = QFontCache::instance()->findEngineData(req); + + QCOMPARE(engineData, QFontPrivate::get(f)->engineData); +} + void tst_QFontCache::clear() { #ifdef QT_BUILD_INTERNAL diff --git a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp index 3e354b7523..08f7cf4fb2 100644 --- a/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp +++ b/tests/auto/gui/text/qtextdocumentfragment/tst_qtextdocumentfragment.cpp @@ -159,6 +159,7 @@ private slots: void nonZeroMarginOnImport(); void html_charFormatPropertiesUnset(); void html_headings(); + void html_quotedFontFamily_data(); void html_quotedFontFamily(); void html_spanBackgroundColor(); void defaultFont(); @@ -2184,23 +2185,45 @@ void tst_QTextDocumentFragment::html_headings() QCOMPARE(doc->blockCount(), 2); } -void tst_QTextDocumentFragment::html_quotedFontFamily() +void tst_QTextDocumentFragment::html_quotedFontFamily_data() { - setHtml("
Test
"); - QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); - - setHtml("
Test
"); - QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); - - setHtml("
Test
"); - QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); - - setHtml("
Test
"); - QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar")); + QTest::addColumn("html"); + QTest::addColumn("fontFamily"); + QTest::addColumn("fontFamilies"); + + const QString fooFamily = QLatin1String("Foo Bar"); + const QString weirdFamily = QLatin1String("'Weird, & font '' name',"); + + QTest::newRow("data1") << QString("
Test
") + << fooFamily << QStringList(fooFamily); + QTest::newRow("data2") << QString("
Test
") + << QString("Foo Bar") << QStringList("Foo Bar"); + QTest::newRow("data3") << QString("
Test
") + << fooFamily << QStringList(fooFamily); + QTest::newRow("data4") << QString("
Test" + "
") + << fooFamily << (QStringList() << "Foo Bar" << "serif" << "bar foo"); + QTest::newRow("data5") << QString("
Test
") + << weirdFamily << QStringList(weirdFamily); + QTest::newRow("data6") << QString("
Test
") + << weirdFamily << QStringList(weirdFamily); + QTest::newRow("data7") << QString("
Test
") + << weirdFamily + << (QStringList() << weirdFamily << "serif" << "bar foo"); +} - setHtml("
Test
"); - QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), QString("Foo Bar,serif,bar foo")); +void tst_QTextDocumentFragment::html_quotedFontFamily() +{ + QFETCH(QString, html); + QFETCH(QString, fontFamily); + QFETCH(QStringList, fontFamilies); + setHtml(html); + QCOMPARE(doc->begin().begin().fragment().charFormat().fontFamily(), fontFamily); + QCOMPARE(doc->begin().begin().fragment().charFormat().font().families(), fontFamilies); } void tst_QTextDocumentFragment::defaultFont() -- cgit v1.2.3