qt5base-lts/tests/auto/gui/text/qfont/tst_qfont.cpp
Volker Hilsheimer bde443801f QFont: add a tag type for features and other advanced properties
Instead of overloads and generic string literals with runtime checks,
use a dedicated Tag type for specifying the font feature. The Tag type
can only be instantiated with a string literal of exactly 4 characters
(plus the terminating null)l; longer or shorter literals result in a
compile time error.

Constructing a Tag from any other string type is possible through the
named fromString constructor, in which case we can only check the length
and warn at runtime.

The type's API is almost completely constexpr so that we can use it to
calculate e.g. enum values.

Task-number: QTBUG-117046
Change-Id: I31038c7c6fd2b843a105b032f021e506b0b60822
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
2023-10-20 21:36:52 +02:00

904 lines
30 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#include <QTest>
#include <QBuffer>
#include <QtEndian>
#if QT_CONFIG(process)
#include <QProcess>
#endif
#include <qfont.h>
#include <private/qfont_p.h>
#include <qfontdatabase.h>
#include <qfontinfo.h>
#include <qstringlist.h>
#include <qguiapplication.h>
#ifndef QT_NO_WIDGETS
#include <qwidget.h>
#endif
#include <qlist.h>
#include <QtTest/private/qemulationdetector_p.h>
using namespace Qt::StringLiterals;
class tst_QFont : public QObject
{
Q_OBJECT
private slots:
void getSetCheck();
void exactMatch();
void compare();
void resolve();
#ifndef QT_NO_WIDGETS
void resetFont();
#endif
void isCopyOf();
void italicOblique();
void insertAndRemoveSubstitutions();
void serialize_data();
void serialize();
void deserializeQt515();
void styleName();
void defaultFamily_data();
void defaultFamily();
void toAndFromString();
void fromStringWithoutStyleName();
void fromDegenerateString_data();
void fromDegenerateString();
void sharing();
void familyNameWithCommaQuote_data();
void familyNameWithCommaQuote();
void setFamilies_data();
void setFamilies();
void setFamiliesAndFamily_data();
void setFamiliesAndFamily();
void featureAccessors();
};
// Testing get/set functions
void tst_QFont::getSetCheck()
{
QFont obj1;
// Style QFont::style()
// void QFont::setStyle(Style)
obj1.setStyle(QFont::Style(QFont::StyleNormal));
QCOMPARE(QFont::Style(QFont::StyleNormal), obj1.style());
obj1.setStyle(QFont::Style(QFont::StyleItalic));
QCOMPARE(QFont::Style(QFont::StyleItalic), obj1.style());
obj1.setStyle(QFont::Style(QFont::StyleOblique));
QCOMPARE(QFont::Style(QFont::StyleOblique), obj1.style());
// StyleStrategy QFont::styleStrategy()
// void QFont::setStyleStrategy(StyleStrategy)
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault));
QCOMPARE(QFont::StyleStrategy(QFont::PreferDefault), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferBitmap));
QCOMPARE(QFont::StyleStrategy(QFont::PreferBitmap), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDevice));
QCOMPARE(QFont::StyleStrategy(QFont::PreferDevice), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferOutline));
QCOMPARE(QFont::StyleStrategy(QFont::PreferOutline), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::ForceOutline));
QCOMPARE(QFont::StyleStrategy(QFont::ForceOutline), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferMatch));
QCOMPARE(QFont::StyleStrategy(QFont::PreferMatch), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferQuality));
QCOMPARE(QFont::StyleStrategy(QFont::PreferQuality), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferAntialias));
QCOMPARE(QFont::StyleStrategy(QFont::PreferAntialias), obj1.styleStrategy());
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::NoAntialias));
QCOMPARE(QFont::StyleStrategy(QFont::NoAntialias), obj1.styleStrategy());
}
void tst_QFont::exactMatch()
{
QFont font;
// Check if a non-existing font hasn't an exact match
font = QFont( "BogusFont", 33 );
QVERIFY( !font.exactMatch() );
QVERIFY(!QFont("sans").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());
// Confirm that exactMatch is true for a valid font
const QString family = QFontDatabase::families().first();
const QString style = QFontDatabase::styles(family).first();
const int pointSize = QFontDatabase::pointSizes(family, style).first();
font = QFontDatabase::font(family, style, pointSize);
QVERIFY(font.exactMatch());
if (QFontDatabase::families().contains("Arial")) {
font = QFont("Arial");
QVERIFY(font.exactMatch());
font = QFont(QString());
font.setFamilies({"Arial"});
QVERIFY(font.exactMatch());
}
}
void tst_QFont::italicOblique()
{
QStringList families = QFontDatabase::families();
if (families.isEmpty())
return;
QStringList::ConstIterator f_it, f_end = families.end();
for (f_it = families.begin(); f_it != f_end; ++f_it) {
QString family = *f_it;
QStringList styles = QFontDatabase::styles(family);
QStringList::ConstIterator s_it, s_end = styles.end();
for (s_it = styles.begin(); s_it != s_end; ++s_it) {
QString style = *s_it;
if (QFontDatabase::isSmoothlyScalable(family, style)) {
if (style.contains("Oblique")) {
style.replace("Oblique", "Italic");
} else if (style.contains("Italic")) {
style.replace("Italic", "Oblique");
} else {
continue;
}
QFont f = QFontDatabase::font(family, style, 12);
QVERIFY2(f.italic(), qPrintable(QString::asprintf("Failed for font \"%ls\"", qUtf16Printable(f.family()))));
}
}
}
}
void tst_QFont::compare()
{
QFont font;
{
QFont font2 = font;
font2.setPointSize(24);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
}
{
QFont font2 = font;
font2.setPixelSize(24);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
}
font.setPointSize(12);
font.setItalic(false);
font.setWeight(QFont::Normal);
font.setUnderline(false);
font.setStrikeOut(false);
font.setOverline(false);
{
QFont font2 = font;
font2.setPointSize(24);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
}
{
QFont font2 = font;
font2.setPixelSize(24);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
}
{
QFont font2 = font;
font2.setItalic(true);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font2.setItalic(false);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
font2.setWeight(QFont::Bold);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font2.setWeight(QFont::Normal);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
font.setUnderline(true);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font.setUnderline(false);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
font.setStrikeOut(true);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font.setStrikeOut(false);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
font.setOverline(true);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font.setOverline(false);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
font.setCapitalization(QFont::SmallCaps);
QVERIFY(font != font2);
QCOMPARE(font < font2,!(font2 < font));
font.setCapitalization(QFont::MixedCase);
QCOMPARE(font, font2);
QVERIFY(!(font < font2));
}
}
void tst_QFont::resolve()
{
QFont font;
font.setPointSize(font.pointSize() * 2);
font.setItalic(false);
font.setWeight(QFont::Normal);
font.setUnderline(false);
font.setStrikeOut(false);
font.setOverline(false);
font.setStretch(QFont::Unstretched);
QFont font1;
font1.setWeight(QFont::Bold);
QFont font2 = font1.resolve(font);
QCOMPARE(font2.weight(), font1.weight());
QCOMPARE(font2.pointSize(), font.pointSize());
QCOMPARE(font2.italic(), font.italic());
QCOMPARE(font2.underline(), font.underline());
QCOMPARE(font2.overline(), font.overline());
QCOMPARE(font2.strikeOut(), font.strikeOut());
QCOMPARE(font2.stretch(), font.stretch());
QFont font3;
font3.setStretch(QFont::UltraCondensed);
QFont font4 = font3.resolve(font1).resolve(font);
QCOMPARE(font4.stretch(), font3.stretch());
QCOMPARE(font4.weight(), font.weight());
QCOMPARE(font4.pointSize(), font.pointSize());
QCOMPARE(font4.italic(), font.italic());
QCOMPARE(font4.underline(), font.underline());
QCOMPARE(font4.overline(), font.overline());
QCOMPARE(font4.strikeOut(), font.strikeOut());
QFont f1,f2,f3;
f2.setPointSize(45);
f3.setPointSize(55);
QFont f4 = f1.resolve(f2);
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);
QFont font7, font8;
// This will call setFamilies() directly now
font7.setFamily(QLatin1String("Helvetica"));
font8.setFamilies(fontFamilies);
font7 = font7.resolve(font8);
QCOMPARE(font7.families(), QStringList({"Helvetica"}));
QCOMPARE(font7.family(), "Helvetica");
}
#ifndef QT_NO_WIDGETS
void tst_QFont::resetFont()
{
QWidget parent;
QWidget firstChild(&parent);
QFont parentFont = parent.font();
parentFont.setPointSize(parentFont.pointSize() + 2);
parent.setFont(parentFont);
QFont childFont = firstChild.font();
childFont.setBold(!childFont.bold());
firstChild.setFont(childFont);
QWidget secondChild(&parent);
secondChild.setFont(childFont);
QVERIFY(parentFont.resolveMask() != 0);
QVERIFY(childFont.resolveMask() != 0);
QVERIFY(childFont != parentFont);
// reset font on both children
firstChild.setFont(QFont());
secondChild.setFont(QFont());
QCOMPARE(firstChild.font().resolveMask(), QFont::SizeResolved);
QCOMPARE(secondChild.font().resolveMask(), QFont::SizeResolved);
QCOMPARE(firstChild.font().pointSize(), parent.font().pointSize());
QCOMPARE(secondChild.font().pointSize(), parent.font().pointSize());
QVERIFY(parent.font().resolveMask() != 0);
}
#endif
void tst_QFont::isCopyOf()
{
QFont font;
QVERIFY(font.isCopyOf(QGuiApplication::font()));
QFont font2("bogusfont", 23);
QVERIFY(! font2.isCopyOf(QGuiApplication::font()));
QFont font3 = font;
QVERIFY(font3.isCopyOf(font));
font3.setPointSize(256);
QVERIFY(!font3.isCopyOf(font));
font3.setPointSize(font.pointSize());
QVERIFY(!font3.isCopyOf(font));
}
void tst_QFont::insertAndRemoveSubstitutions()
{
QFont::removeSubstitutions("BogusFontFamily");
// make sure it is empty before we start
QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
// inserting Foo
QFont::insertSubstitution("BogusFontFamily", "Foo");
QCOMPARE(QFont::substitutes("BogusFontFamily").size(), 1);
QCOMPARE(QFont::substitutes("bogusfontfamily").size(), 1);
// inserting Bar and Baz
QStringList moreFonts;
moreFonts << "Bar" << "Baz";
QFont::insertSubstitutions("BogusFontFamily", moreFonts);
QCOMPARE(QFont::substitutes("BogusFontFamily").size(), 3);
QCOMPARE(QFont::substitutes("bogusfontfamily").size(), 3);
QFont::removeSubstitutions("BogusFontFamily");
// make sure it is empty again
QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
}
Q_DECLARE_METATYPE(QDataStream::Version)
void tst_QFont::serialize_data()
{
QTest::addColumn<QFont>("font");
// The version in which the tested feature was added.
QTest::addColumn<QDataStream::Version>("minimumStreamVersion");
QFont basicFont;
// Versions <= Qt 2.1 had broken point size serialization,
// so we set an integer point size.
basicFont.setPointSize(9);
// Versions <= Qt 5.4 didn't serialize styleName, so clear it
basicFont.setStyleName(QString());
QFont font = basicFont;
QTest::newRow("defaultConstructed") << font << QDataStream::Qt_1_0;
font.setLetterSpacing(QFont::AbsoluteSpacing, 105);
QTest::newRow("letterSpacing=105") << font << QDataStream::Qt_4_5;
font = basicFont;
font.setWordSpacing(50.0);
QTest::newRow("wordSpacing") << font << QDataStream::Qt_4_5;
font = basicFont;
font.setPointSize(20);
QTest::newRow("pointSize") << font << QDataStream::Qt_1_0;
font = basicFont;
font.setPixelSize(32);
QTest::newRow("pixelSize") << font << QDataStream::Qt_3_0;
font = basicFont;
font.setStyleHint(QFont::Monospace);
QTest::newRow("styleHint") << font << QDataStream::Qt_1_0;
font = basicFont;
font.setStretch(4000);
QTest::newRow("stretch") << font << QDataStream::Qt_4_3;
font = basicFont;
font.setWeight(QFont::Light);
QTest::newRow("weight") << font << QDataStream::Qt_1_0;
font = basicFont;
font.setUnderline(true);
QTest::newRow("underline") << font << QDataStream::Qt_1_0;
font = basicFont;
font.setStrikeOut(true);
QTest::newRow("strikeOut") << font << QDataStream::Qt_1_0;
font = basicFont;
font.setFixedPitch(true);
// This fails for versions less than this, as ignorePitch is set to false
// whenever setFixedPitch() is called, but ignorePitch is considered an
// extended bit, which were apparently not available until 4.4.
QTest::newRow("fixedPitch") << font << QDataStream::Qt_4_4;
font = basicFont;
font.setLetterSpacing(QFont::AbsoluteSpacing, 10);
// Fails for 4.4 because letterSpacing wasn't read until 4.5.
QTest::newRow("letterSpacing=10") << font << QDataStream::Qt_4_5;
font = basicFont;
font.setKerning(false);
QTest::newRow("kerning") << font << QDataStream::Qt_4_0;
font = basicFont;
font.setStyleStrategy(QFont::NoFontMerging);
// This wasn't read properly until 5.4.
QTest::newRow("styleStrategy") << font << QDataStream::Qt_5_4;
font = basicFont;
font.setHintingPreference(QFont::PreferFullHinting);
// This wasn't read until 5.4.
QTest::newRow("hintingPreference") << font << QDataStream::Qt_5_4;
font = basicFont;
font.setStyleName("Regular Black Condensed");
// This wasn't read until 5.4.
QTest::newRow("styleName") << font << QDataStream::Qt_5_4;
font = basicFont;
font.setCapitalization(QFont::AllUppercase);
// This wasn't read until 5.6.
QTest::newRow("capitalization") << font << QDataStream::Qt_5_6;
}
void tst_QFont::serialize()
{
QFETCH(QFont, font);
QFETCH(QDataStream::Version, minimumStreamVersion);
QDataStream stream;
const int thisVersion = stream.version();
for (int version = minimumStreamVersion; version <= thisVersion; ++version) {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
stream.setDevice(&buffer);
stream.setVersion(version);
stream << font;
buffer.close();
buffer.open(QIODevice::ReadOnly);
QFont readFont;
stream >> readFont;
QVERIFY2(readFont == font, qPrintable(QString::fromLatin1("Fonts do not compare equal for QDataStream version ") +
QString::fromLatin1("%1:\nactual: %2\nexpected: %3").arg(version).arg(readFont.toString()).arg(font.toString())));
}
}
void tst_QFont::deserializeQt515()
{
QFile file;
file.setFileName(QFINDTESTDATA("datastream.515"));
QVERIFY(file.open(QIODevice::ReadOnly));
QFont font;
{
QDataStream stream(&file);
stream.setVersion(QDataStream::Qt_5_15);
stream >> font;
}
QCOMPARE(font.family(), QStringLiteral("FirstFamily"));
QCOMPARE(font.families().size(), 3);
QCOMPARE(font.families().at(0), QStringLiteral("FirstFamily"));
QCOMPARE(font.families().at(1), QStringLiteral("OtherFamily1"));
QCOMPARE(font.families().at(2), QStringLiteral("OtherFamily2"));
QCOMPARE(font.pointSize(), 12);
QVERIFY(file.reset());
QByteArray fileContent = file.readAll();
QByteArray serializedContent;
{
QBuffer buffer(&serializedContent);
QVERIFY(buffer.open(QIODevice::WriteOnly));
QDataStream stream(&buffer);
stream.setVersion(QDataStream::Qt_5_15);
stream << font;
}
QCOMPARE(serializedContent, fileContent);
file.close();
}
void tst_QFont::styleName()
{
#if !defined(Q_OS_MAC)
QSKIP("Only tested on Mac");
#else
QFont font("Helvetica Neue");
font.setStyleName("UltraLight");
QCOMPARE(QFontInfo(font).styleName(), QString("UltraLight"));
#endif
}
QString getPlatformGenericFont(const char* genericName)
{
#if defined(Q_OS_UNIX) && !defined(QT_NO_FONTCONFIG) && QT_CONFIG(process)
QProcess p;
p.start(QLatin1String("fc-match"), (QStringList() << "-f%{family}" << genericName));
if (!p.waitForStarted())
qWarning("fc-match cannot be started: %s", qPrintable(p.errorString()));
if (p.waitForFinished())
return QString::fromLatin1(p.readAllStandardOutput());
#endif
return QLatin1String(genericName);
}
static inline QByteArray msgNotAcceptableFont(const QString &defaultFamily, const QStringList &acceptableFamilies)
{
QString res = QString::fromLatin1("Font family '%1' is not one of the following acceptable results: ").arg(defaultFamily);
Q_FOREACH (const QString &family, acceptableFamilies)
res += QLatin1String("\n ") + family;
return res.toLocal8Bit();
}
Q_DECLARE_METATYPE(QFont::StyleHint)
void tst_QFont::defaultFamily_data()
{
QTest::addColumn<QFont::StyleHint>("styleHint");
QTest::addColumn<QStringList>("acceptableFamilies");
QTest::newRow("serif") << QFont::Serif << (QStringList() << "Times New Roman" << "Times" << "Droid Serif" << getPlatformGenericFont("serif").split(","));
QTest::newRow("monospace") << QFont::Monospace << (QStringList() << "Courier New" << "Monaco" << "Menlo" << "Droid Sans Mono" << getPlatformGenericFont("monospace").split(","));
QTest::newRow("cursive") << QFont::Cursive << (QStringList() << "Comic Sans MS" << "Apple Chancery" << "Roboto" << "Droid Sans" << getPlatformGenericFont("cursive").split(","));
QTest::newRow("fantasy") << QFont::Fantasy << (QStringList() << "Impact" << "Zapfino" << "Roboto" << "Droid Sans" << getPlatformGenericFont("fantasy").split(","));
QTest::newRow("sans-serif") << QFont::SansSerif << (QStringList() << "Arial" << "Lucida Grande" << "Helvetica" << "Roboto" << "Droid Sans" << "Segoe UI" << getPlatformGenericFont("sans-serif").split(","));
}
void tst_QFont::defaultFamily()
{
QFETCH(QFont::StyleHint, styleHint);
QFETCH(QStringList, acceptableFamilies);
QFont f;
f.setStyleHint(styleHint);
const QString familyForHint(f.defaultFamily());
// it should at least return a family that is available.
QVERIFY(QFontDatabase::hasFamily(familyForHint));
bool isAcceptable = false;
Q_FOREACH (const QString& family, acceptableFamilies) {
if (!familyForHint.compare(family, Qt::CaseInsensitive)) {
isAcceptable = true;
break;
}
}
#if defined(Q_OS_UNIX) && defined(QT_NO_FONTCONFIG)
QSKIP("This platform does not support checking for default font acceptability");
#endif
#ifdef Q_PROCESSOR_ARM_32
if (QTestPrivate::isRunningArmOnX86())
QEXPECT_FAIL("", "Fails on ARMv7 QEMU (QTQAINFRA-4127)", Continue);
#endif
#ifdef Q_OS_ANDROID
QEXPECT_FAIL("serif", "QTBUG-69215", Continue);
#endif
QVERIFY2(isAcceptable, msgNotAcceptableFont(familyForHint, acceptableFamilies));
}
void tst_QFont::toAndFromString()
{
QFont defaultFont = QGuiApplication::font();
QString family = defaultFont.family();
const QStringList stylesList = QFontDatabase::styles(family);
if (stylesList.size() == 0)
QSKIP("Default font doesn't have any styles");
for (const QString &style : stylesList) {
QFont result;
QFont initial = QFontDatabase::font(family, style, defaultFont.pointSize());
result.fromString(initial.toString());
QCOMPARE(result, initial);
}
// Since Qt 6.0 it was changed to include more information in the description, so
// this checks for compatibility
const QString fontStringFrom515(QLatin1String("Times New Roman,18,-1,5,75,1,0,0,1,0,Regular"));
QFont fontFrom515("Times New Roman", 18);
fontFrom515.setBold(true);
fontFrom515.setItalic(true);
fontFrom515.setFixedPitch(true);
fontFrom515.setStyleName("Regular");
QFont from515String;
from515String.fromString(fontStringFrom515);
QCOMPARE(from515String, fontFrom515);
const QString fontStringFrom60(
QLatin1String("Times New Roman,18,-1,5,700,1,0,0,1,0,1,0,150.5,2.5,50,2,Regular"));
QFont fontFrom60 = fontFrom515;
fontFrom60.setStyleStrategy(QFont::PreferBitmap);
fontFrom60.setCapitalization(QFont::AllUppercase);
fontFrom60.setLetterSpacing(QFont::PercentageSpacing, 150.5);
fontFrom60.setWordSpacing(2.5);
fontFrom60.setStretch(50);
QFont from60String;
from60String.fromString(fontStringFrom60);
QCOMPARE(fontFrom60.toString(), fontStringFrom60);
QCOMPARE(from60String, fontFrom60);
}
void tst_QFont::fromStringWithoutStyleName()
{
QFont font1;
font1.fromString("Noto Sans,12,-1,5,50,0,0,0,0,0,Regular");
QFont font2 = font1;
const QString str = "Times,16,-1,5,400,0,0,0,0,0,0,0,0,0,0,1";
font2.fromString(str);
QCOMPARE(font2.toString(), str);
const QString fontStringFrom60(
QLatin1String("Times New Roman,18,-1,5,700,1,0,0,1,0,1,0,150.5,2.5,50,2"));
QFont font3;
font3.fromString("Noto Sans,12,-1,5,50,0,0,0,0,0,Regular");
QFont font4 = font3;
font4.fromString(fontStringFrom60);
QCOMPARE(font4.toString(), fontStringFrom60);
}
void tst_QFont::fromDegenerateString_data()
{
QTest::addColumn<QString>("string");
QTest::newRow("empty") << QString();
QTest::newRow("justAComma") << ",";
QTest::newRow("commasAndSpaces") << " , , ";
QTest::newRow("spaces") << " ";
QTest::newRow("spacesTabsAndNewlines") << " \t \n";
}
void tst_QFont::fromDegenerateString()
{
QFETCH(QString, string);
QFont f;
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Invalid description.*"));
QCOMPARE(f.fromString(string), false);
QCOMPARE(f, QFont());
}
void tst_QFont::sharing()
{
// QFontCache references the engineData
int refs_by_cache = 1;
QFont f;
f.setStyleHint(QFont::Serif);
f.exactMatch(); // loads engine
QCOMPARE(QFontPrivate::get(f)->ref.loadRelaxed(), 1);
QVERIFY(QFontPrivate::get(f)->engineData);
QCOMPARE(QFontPrivate::get(f)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
QFont f2(f);
QCOMPARE(QFontPrivate::get(f2), QFontPrivate::get(f));
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 2);
QVERIFY(QFontPrivate::get(f2)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
f2.setKerning(!f.kerning());
QVERIFY(QFontPrivate::get(f2) != QFontPrivate::get(f));
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 1);
QVERIFY(QFontPrivate::get(f2)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 2 + refs_by_cache);
f2 = f;
QCOMPARE(QFontPrivate::get(f2), QFontPrivate::get(f));
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 2);
QVERIFY(QFontPrivate::get(f2)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
if (f.pointSize() > 0)
f2.setPointSize(f.pointSize() * 2 / 3);
else
f2.setPixelSize(f.pixelSize() * 2 / 3);
QVERIFY(QFontPrivate::get(f2) != QFontPrivate::get(f));
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 1);
QVERIFY(!QFontPrivate::get(f2)->engineData);
QVERIFY(QFontPrivate::get(f2)->engineData != QFontPrivate::get(f)->engineData);
}
void tst_QFont::familyNameWithCommaQuote_data()
{
QTest::addColumn<QString>("enteredFamilyName");
QTest::addColumn<QString>("familyName");
QTest::addColumn<QString>("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 bogusFont(QLatin1String("BogusFont"));
const QString commaSeparated(standardFont + QLatin1String(",Times New Roman"));
const QString commaSeparatedWeird(weirdFont + QLatin1String(",") + standardFont);
const QString commaSeparatedBogus(bogusFont + QLatin1String(",") + standardFont);
QTest::newRow("standard") << standardFont << standardFont << standardFont;
QTest::newRow("weird") << weirdFont << QString("'My") << standardFont;
QTest::newRow("commaSeparated") << commaSeparated << standardFont << standardFont;
QTest::newRow("commaSeparatedWeird") << commaSeparatedWeird << QString("'My") << standardFont;
QTest::newRow("commaSeparatedBogus") << commaSeparatedBogus << bogusFont << 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<QStringList>("families");
QTest::addColumn<QString>("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("emptyFamily") << (QStringList()) << QString();
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);
if (!chosenFamilyName.isEmpty()) // Only check when it is not empty
QCOMPARE(QFontInfo(f).family(), chosenFamilyName);
QFontDatabase::removeApplicationFont(weirdFontId);
}
void tst_QFont::setFamiliesAndFamily_data()
{
QTest::addColumn<QStringList>("families");
QTest::addColumn<QString>("family");
QTest::addColumn<QString>("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("emptyFamily") << (QStringList()) << QString() << QString();
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.setFamily(family);
f.setFamilies(families);
if (!family.isEmpty()) // Only check when it is not empty
QCOMPARE(QFontInfo(f).family(), chosenFamilyName);
QFontDatabase::removeApplicationFont(weirdFontId);
}
void tst_QFont::featureAccessors()
{
const QFont::Tag abcdTag("abcd");
QCOMPARE(abcdTag.toString(), "abcd");
QVERIFY(abcdTag.isValid());
QFont font;
QVERIFY(font.featureTags().isEmpty());
font.setFeature("abcd", 0xc0ffee);
QVERIFY(font.isFeatureSet(abcdTag));
QVERIFY(!font.isFeatureSet("bcde"));
QCOMPARE(font.featureTags().size(), 1);
QCOMPARE(font.featureTags().first(), abcdTag);
QCOMPARE(font.featureTags().first(), "abcd");
QCOMPARE(font.featureValue(abcdTag), 0xc0ffeeU);
QCOMPARE(font.featureValue("bcde"), 0U);
font.setFeature(abcdTag, 0xf00d);
QCOMPARE(font.featureTags().size(), 1);
QCOMPARE(font.featureValue(abcdTag), 0xf00dU);
QFont::Tag invalidTag;
QVERIFY(!invalidTag.isValid());
font.setFeature(invalidTag, 0xcaca0);
QVERIFY(!font.isFeatureSet(invalidTag));
QCOMPARE(font.featureTags().size(), 1);
QFont font2 = font;
font.unsetFeature("abcd");
QVERIFY(!font.isFeatureSet("abcd"));
QVERIFY(font.featureTags().isEmpty());
QVERIFY(font2.isFeatureSet("abcd"));
font2.clearFeatures();
QVERIFY(font.featureTags().isEmpty());
// various constructor compile tests
QFont::Tag tag;
tag = QFont::Tag("1234");
QVERIFY(QFont::Tag::fromString(QByteArray("abcd")));
QVERIFY(QFont::Tag::fromString(u"frac"_s));
// named constructors with invalid input
QTest::ignoreMessage(QtWarningMsg, "The tag name must be exactly 4 characters long!");
QVERIFY(!QFont::Tag::fromString(u"fraction"_s));
QVERIFY(!QFont::Tag::fromValue(0));
QVERIFY(QFont::Tag::fromValue(abcdTag.value()));
enum Features {
Frac = QFont::Tag("frac").value()
};
}
QTEST_MAIN(tst_QFont)
#include "tst_qfont.moc"