// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include class tst_QRawFont: public QObject { Q_OBJECT #if !defined(QT_NO_RAWFONT) private slots: void init(); void initTestCase(); void invalidRawFont(); void explicitRawFontNotLoadedInDatabase_data(); void explicitRawFontNotLoadedInDatabase(); void explicitRawFontNotAvailableInSystem_data(); void explicitRawFontNotAvailableInSystem(); void correctFontData_data(); void correctFontData(); void glyphIndices(); void advances_data(); void advances(); void textLayout(); void fontTable_data(); void fontTable(); void supportedWritingSystems_data(); void supportedWritingSystems(); void supportsCharacter_data(); void supportsCharacter(); void supportsUcs4Character_data(); void supportsUcs4Character(); void fromFont_data(); void fromFont(); void copyConstructor_data(); void copyConstructor(); void detach_data(); void detach(); void unsupportedWritingSystem_data(); void unsupportedWritingSystem(); void rawFontSetPixelSize_data(); void rawFontSetPixelSize(); void multipleRawFontsFromData(); void rawFontFromInvalidData(); void kernedAdvances(); void fallbackFontsOrder(); void qtbug65923_partal_clone_data(); void qtbug65923_partal_clone(); private: QString testFont; QString testFontBoldItalic; QString testFontOs2V1; #endif // QT_NO_RAWFONT }; #if !defined(QT_NO_RAWFONT) Q_DECLARE_METATYPE(QFont::HintingPreference) Q_DECLARE_METATYPE(QFont::Style) Q_DECLARE_METATYPE(QFont::Weight) Q_DECLARE_METATYPE(QFontDatabase::WritingSystem) void tst_QRawFont::init() { } void tst_QRawFont::initTestCase() { testFont = QFINDTESTDATA("testfont.ttf"); testFontBoldItalic = QFINDTESTDATA("testfont_bold_italic.ttf"); testFontOs2V1 = QFINDTESTDATA("testfont_os2_v1.ttf"); if (testFont.isEmpty() || testFontBoldItalic.isEmpty()) QFAIL("qrawfont unittest font files not found!"); if (QFontDatabase::families().size() == 0) QSKIP("No fonts available!!!"); } void tst_QRawFont::invalidRawFont() { QRawFont font; QVERIFY(!font.isValid()); QCOMPARE(font.pixelSize(), 0.0); QVERIFY(font.familyName().isEmpty()); QCOMPARE(font.style(), QFont::StyleNormal); QCOMPARE(font.weight(), -1); QCOMPARE(font.ascent(), 0.0); QCOMPARE(font.descent(), 0.0); QVERIFY(font.glyphIndexesForString(QLatin1String("Test")).isEmpty()); } void tst_QRawFont::explicitRawFontNotLoadedInDatabase_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting") << QFont::PreferFullHinting; } void tst_QRawFont::explicitRawFontNotLoadedInDatabase() { QFETCH(QFont::HintingPreference, hintingPreference); QRawFont font(testFont, 10, hintingPreference); QVERIFY(font.isValid()); QVERIFY(!QFontDatabase::families().contains(font.familyName())); } void tst_QRawFont::explicitRawFontNotAvailableInSystem_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting") << QFont::PreferFullHinting; } void tst_QRawFont::explicitRawFontNotAvailableInSystem() { QFETCH(QFont::HintingPreference, hintingPreference); QRawFont rawfont(testFont, 10, hintingPreference); { QFont font(rawfont.familyName(), 10); QVERIFY(!font.exactMatch()); QVERIFY(font.family() != QFontInfo(font).family()); } } void tst_QRawFont::correctFontData_data() { QTest::addColumn("fileName"); QTest::addColumn("expectedFamilyName"); QTest::addColumn("style"); QTest::addColumn("weight"); QTest::addColumn("hintingPreference"); QTest::addColumn("unitsPerEm"); QTest::addColumn("pixelSize"); QTest::addColumn("capHeight"); int hintingPreferences[] = { int(QFont::PreferDefaultHinting), int(QFont::PreferNoHinting), int(QFont::PreferVerticalHinting), int(QFont::PreferFullHinting), -1 }; int *hintingPreference = hintingPreferences; while (*hintingPreference >= 0) { QString fileName = testFont; QString title = fileName + QLatin1String(": hintingPreference=") + QString::number(*hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QString::fromLatin1("QtBidiTestFont") << QFont::StyleNormal << QFont::Normal << QFont::HintingPreference(*hintingPreference) << qreal(1000.0) << qreal(10.0) << 7; fileName = testFontBoldItalic; title = fileName + QLatin1String(": hintingPreference=") + QString::number(*hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QString::fromLatin1("QtBidiTestFont") << QFont::StyleItalic << QFont::Bold << QFont::HintingPreference(*hintingPreference) << qreal(1000.0) << qreal(10.0) << 7; fileName = testFontOs2V1; title = fileName + QLatin1String(": hintingPreference=") + QString::number(*hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QString::fromLatin1("QtBidiTestFont") << QFont::StyleNormal << QFont::Normal << QFont::HintingPreference(*hintingPreference) << qreal(1000.0) << qreal(10.0) << 7; ++hintingPreference; } } void tst_QRawFont::correctFontData() { QFETCH(QString, fileName); QFETCH(QString, expectedFamilyName); QFETCH(QFont::Style, style); QFETCH(QFont::Weight, weight); QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(qreal, unitsPerEm); QFETCH(qreal, pixelSize); QFETCH(int, capHeight); QRawFont font(fileName, 10, hintingPreference); QVERIFY(font.isValid()); QCOMPARE(font.familyName(), expectedFamilyName); QCOMPARE(font.style(), style); QCOMPARE(font.weight(), int(weight)); QCOMPARE(font.hintingPreference(), hintingPreference); QCOMPARE(font.unitsPerEm(), unitsPerEm); QCOMPARE(font.pixelSize(), pixelSize); // Some platforms return the actual fractional height of the // H character when the value is missing from the OS/2 table, // so we ceil it off to match (any touched pixel counts). QCOMPARE(qCeil(font.capHeight()), capHeight); } void tst_QRawFont::glyphIndices() { QRawFont font(testFont, 10); QVERIFY(font.isValid()); QList glyphIndices = font.glyphIndexesForString(QLatin1String("Foobar")); QList expectedGlyphIndices; expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86; QCOMPARE(glyphIndices, expectedGlyphIndices); glyphIndices = font.glyphIndexesForString(QString()); QVERIFY(glyphIndices.isEmpty()); QString str(QLatin1String("Foobar")); int numGlyphs = str.size(); glyphIndices.resize(numGlyphs); QVERIFY(!font.glyphIndexesForChars(str.constData(), 0, glyphIndices.data(), &numGlyphs)); QCOMPARE(numGlyphs, 0); QVERIFY(!font.glyphIndexesForChars(str.constData(), str.size(), glyphIndices.data(), &numGlyphs)); QCOMPARE(numGlyphs, str.size()); QVERIFY(font.glyphIndexesForChars(str.constData(), str.size(), glyphIndices.data(), &numGlyphs)); QCOMPARE(numGlyphs, str.size()); QCOMPARE(glyphIndices, expectedGlyphIndices); } void tst_QRawFont::advances_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting") << QFont::PreferFullHinting; } void tst_QRawFont::advances() { QFETCH(QFont::HintingPreference, hintingPreference); QRawFont font(testFont, 10, hintingPreference); QVERIFY(font.isValid()); QRawFontPrivate *font_d = QRawFontPrivate::get(font); QVERIFY(font_d->fontEngine != 0); QList glyphIndices; glyphIndices << 44 << 83 << 83 << 70 << 69 << 86; // "Foobar" bool supportsSubPixelPositions = font_d->fontEngine->supportsSubPixelPositions(); QList advances = font.advancesForGlyphIndexes(glyphIndices); bool mayDiffer = font_d->fontEngine->type() == QFontEngine::Freetype && (hintingPreference == QFont::PreferFullHinting || hintingPreference == QFont::PreferDefaultHinting); for (int i = 0; i < glyphIndices.size(); ++i) { if ((i == 0 || i == 5) && mayDiffer) { QVERIFY2(qRound(advances.at(i).x()) == 8 || qRound(advances.at(i).x()) == 9, qPrintable(QStringLiteral("%1 != %2 && %1 != %3") .arg(qRound(advances.at(i).x())) .arg(8) .arg(9))); } else { QCOMPARE(qRound(advances.at(i).x()), 8); } if (supportsSubPixelPositions) QVERIFY(advances.at(i).x() > 8.0); QVERIFY(qFuzzyIsNull(advances.at(i).y())); } advances = font.advancesForGlyphIndexes(QList()); QVERIFY(advances.isEmpty()); int numGlyphs = glyphIndices.size(); advances.resize(numGlyphs); QVERIFY(!font.advancesForGlyphIndexes(glyphIndices.constData(), advances.data(), 0)); QVERIFY(font.advancesForGlyphIndexes(glyphIndices.constData(), advances.data(), numGlyphs)); for (int i = 0; i < glyphIndices.size(); ++i) { if ((i == 0 || i == 5) && mayDiffer) { QVERIFY2(qRound(advances.at(i).x()) == 8 || qRound(advances.at(i).x()) == 9, qPrintable(QStringLiteral("%1 != %2 && %1 != %3") .arg(qRound(advances.at(i).x())) .arg(8) .arg(9))); } else { QCOMPARE(qRound(advances.at(i).x()), 8); } if (supportsSubPixelPositions) QVERIFY(advances.at(i).x() > 8.0); QVERIFY(qFuzzyIsNull(advances.at(i).y())); } } void tst_QRawFont::textLayout() { int id = QFontDatabase::addApplicationFont(testFont); QVERIFY(id >= 0); QString familyName = QString::fromLatin1("QtBidiTestFont"); QFont font(familyName); font.setPixelSize(18.0); QCOMPARE(QFontInfo(font).family(), familyName); QTextLayout layout(QLatin1String("Foobar")); layout.setFont(font); layout.setCacheEnabled(true); layout.beginLayout(); layout.createLine(); layout.endLayout(); QList glyphRuns = layout.glyphRuns(); QCOMPARE(glyphRuns.size(), 1); QGlyphRun glyphs = glyphRuns.at(0); QRawFont rawFont = glyphs.rawFont(); QVERIFY(rawFont.isValid()); QCOMPARE(rawFont.familyName(), familyName); QCOMPARE(rawFont.pixelSize(), 18.0); QList expectedGlyphIndices; expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86; QCOMPARE(glyphs.glyphIndexes(), expectedGlyphIndices); QVERIFY(QFontDatabase::removeApplicationFont(id)); } void tst_QRawFont::fontTable_data() { QTest::addColumn("tagName"); QTest::addColumn("hintingPreference"); QTest::addColumn("offset"); QTest::addColumn("expectedValue"); QTest::newRow("Head table, magic number, default hinting") << QByteArray("head") << QFont::PreferDefaultHinting << 12 << (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0x5F0F3CF5 : 0xF53C0F5F); QTest::newRow("Head table, magic number, no hinting") << QByteArray("head") << QFont::PreferNoHinting << 12 << (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0x5F0F3CF5 : 0xF53C0F5F); QTest::newRow("Head table, magic number, vertical hinting") << QByteArray("head") << QFont::PreferVerticalHinting << 12 << (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0x5F0F3CF5 : 0xF53C0F5F); QTest::newRow("Head table, magic number, full hinting") << QByteArray("head") << QFont::PreferFullHinting << 12 << (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0x5F0F3CF5 : 0xF53C0F5F); } void tst_QRawFont::fontTable() { QFETCH(QByteArray, tagName); QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(int, offset); QFETCH(quint32, expectedValue); QRawFont font(testFont, 10, hintingPreference); QVERIFY(font.isValid()); QByteArray table = font.fontTable(tagName); QVERIFY(!table.isEmpty()); const quint32 *value = reinterpret_cast(table.constData() + offset); QCOMPARE(*value, expectedValue); } typedef QList WritingSystemList; Q_DECLARE_METATYPE(WritingSystemList) void tst_QRawFont::supportedWritingSystems_data() { QTest::addColumn("fileName"); QTest::addColumn("writingSystems"); QTest::addColumn("hintingPreference"); for (int hintingPreference=QFont::PreferDefaultHinting; hintingPreference<=QFont::PreferFullHinting; ++hintingPreference) { QTest::newRow(qPrintable(QString::fromLatin1("testfont.ttf, hintingPreference=%1") .arg(hintingPreference))) << testFont << (QList() << QFontDatabase::Latin << QFontDatabase::Hebrew << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin << QFont::HintingPreference(hintingPreference); QTest::newRow(qPrintable(QString::fromLatin1("testfont_bold_italic.ttf, hintingPreference=%1") .arg(hintingPreference))) << testFontBoldItalic << (QList() << QFontDatabase::Latin << QFontDatabase::Hebrew << QFontDatabase::Devanagari << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin << QFont::HintingPreference(hintingPreference); } } void tst_QRawFont::supportedWritingSystems() { QFETCH(QString, fileName); QFETCH(WritingSystemList, writingSystems); QFETCH(QFont::HintingPreference, hintingPreference); QRawFont font(fileName, 10, hintingPreference); QVERIFY(font.isValid()); WritingSystemList actualWritingSystems = font.supportedWritingSystems(); QCOMPARE(actualWritingSystems.size(), writingSystems.size()); foreach (QFontDatabase::WritingSystem writingSystem, writingSystems) QVERIFY(actualWritingSystems.contains(writingSystem)); } void tst_QRawFont::supportsCharacter_data() { QTest::addColumn("fileName"); QTest::addColumn("hintingPreference"); QTest::addColumn("character"); QTest::addColumn("shouldBeSupported"); const char *fileNames[2] = { "testfont.ttf", "testfont_bold_italic.ttf" }; for (int hintingPreference=QFont::PreferDefaultHinting; hintingPreference<=QFont::PreferFullHinting; ++hintingPreference) { for (int i=0; i<2; ++i) { QString fileName = QFINDTESTDATA(fileNames[i]); // Latin text for (char ch='!'; ch<='~'; ++ch) { QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3") .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(hintingPreference) << QChar::fromLatin1(ch) << true; } // Hebrew text for (quint16 ch=0x05D0; ch<=0x05EA; ++ch) { QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3") .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(hintingPreference) << QChar(ch) << true; } QTest::newRow(qPrintable(QString::fromLatin1("Missing character, %1, hintingPreference=%2") .arg(fileName).arg(hintingPreference))) << fileName << QFont::HintingPreference(hintingPreference) << QChar(0xD8) << false; } } } void tst_QRawFont::supportsCharacter() { QFETCH(QString, fileName); QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(QChar, character); QFETCH(bool, shouldBeSupported); QRawFont font(fileName, 10, hintingPreference); QVERIFY(font.isValid()); QCOMPARE(font.supportsCharacter(character), shouldBeSupported); } void tst_QRawFont::supportsUcs4Character_data() { QTest::addColumn("fileName"); QTest::addColumn("hintingPreference"); QTest::addColumn("ucs4"); QTest::addColumn("shouldBeSupported"); // Gothic text for (int hintingPreference=QFont::PreferDefaultHinting; hintingPreference<=QFont::PreferFullHinting; ++hintingPreference) { for (quint32 ch=0x10330; ch<=0x1034A; ++ch) { { QString fileName = testFont; QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3") .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(hintingPreference) << ch << true; } { QString fileName = testFontBoldItalic; QString title = QString::fromLatin1("%1, character=0x%2, hintingPreference=%3") .arg(fileName).arg(QString::number(ch, 16)).arg(hintingPreference); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(hintingPreference) << ch << false; } } } } void tst_QRawFont::supportsUcs4Character() { QFETCH(QString, fileName); QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(quint32, ucs4); QFETCH(bool, shouldBeSupported); QRawFont font(fileName, 10, hintingPreference); QVERIFY(font.isValid()); QCOMPARE(font.supportsCharacter(ucs4), shouldBeSupported); } void tst_QRawFont::fromFont_data() { QTest::addColumn("fileName"); QTest::addColumn("hintingPreference"); QTest::addColumn("familyName"); QTest::addColumn("writingSystem"); QTest::addColumn("styleStrategy"); for (int i=QFont::PreferDefaultHinting; i<=QFont::PreferFullHinting; ++i) { QString titleBase = QString::fromLatin1("%2, hintingPreference=%1, writingSystem=%3") .arg(i); { QString fileName = testFont; QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any; QString title = titleBase.arg(fileName).arg(writingSystem); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" << writingSystem << QFont::PreferDefault; } { QString fileName = testFont; QFontDatabase::WritingSystem writingSystem = QFontDatabase::Hebrew; QString title = titleBase.arg(fileName).arg(writingSystem); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" << writingSystem << QFont::PreferDefault; } { QString fileName = testFont; QFontDatabase::WritingSystem writingSystem = QFontDatabase::Latin; QString title = titleBase.arg(fileName).arg(writingSystem); QTest::newRow(qPrintable(title)) << fileName << QFont::HintingPreference(i) << "QtBidiTestFont" << writingSystem << QFont::PreferDefault; } } { QString fileName = testFont; QFontDatabase::WritingSystem writingSystem = QFontDatabase::Arabic; QString title = QStringLiteral("No font merging + unsupported script"); QTest::newRow(qPrintable(title)) << fileName << QFont::PreferDefaultHinting << "QtBidiTestFont" << writingSystem << QFont::NoFontMerging; } } void tst_QRawFont::fromFont() { QFETCH(QString, fileName); QFETCH(QFont::HintingPreference, hintingPreference); QFETCH(QString, familyName); QFETCH(QFontDatabase::WritingSystem, writingSystem); QFETCH(QFont::StyleStrategy, styleStrategy); int id = QFontDatabase::addApplicationFont(fileName); QVERIFY(id >= 0); QFont font(familyName); font.setHintingPreference(hintingPreference); font.setPixelSize(26.0); if (styleStrategy != QFont::PreferDefault) font.setStyleStrategy(styleStrategy); QRawFont rawFont = QRawFont::fromFont(font, writingSystem); QVERIFY(rawFont.isValid()); QCOMPARE(rawFont.familyName(), familyName); QCOMPARE(rawFont.pixelSize(), 26.0); QVERIFY(QFontDatabase::removeApplicationFont(id)); } void tst_QRawFont::copyConstructor_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting preference") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting preference") << QFont::PreferFullHinting; } void tst_QRawFont::copyConstructor() { QFETCH(QFont::HintingPreference, hintingPreference); { QString rawFontFamilyName; qreal rawFontPixelSize; qreal rawFontAscent; qreal rawFontDescent; int rawFontTableSize; QRawFont outerRawFont; { QRawFont rawFont(testFont, 11, hintingPreference); QVERIFY(rawFont.isValid()); rawFontFamilyName = rawFont.familyName(); rawFontPixelSize = rawFont.pixelSize(); rawFontAscent = rawFont.ascent(); rawFontDescent = rawFont.descent(); rawFontTableSize = rawFont.fontTable("glyf").size(); QVERIFY(rawFontTableSize > 0); { QRawFont otherRawFont(rawFont); QVERIFY(otherRawFont.isValid()); QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(otherRawFont.familyName(), rawFontFamilyName); QCOMPARE(otherRawFont.hintingPreference(), hintingPreference); QCOMPARE(otherRawFont.ascent(), rawFontAscent); QCOMPARE(otherRawFont.descent(), rawFontDescent); QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize); } { QRawFont otherRawFont = rawFont; QVERIFY(otherRawFont.isValid()); QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(otherRawFont.familyName(), rawFontFamilyName); QCOMPARE(otherRawFont.hintingPreference(), hintingPreference); QCOMPARE(otherRawFont.ascent(), rawFontAscent); QCOMPARE(otherRawFont.descent(), rawFontDescent); QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize); } outerRawFont = rawFont; } QVERIFY(outerRawFont.isValid()); QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(outerRawFont.familyName(), rawFontFamilyName); QCOMPARE(outerRawFont.hintingPreference(), hintingPreference); QCOMPARE(outerRawFont.ascent(), rawFontAscent); QCOMPARE(outerRawFont.descent(), rawFontDescent); QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize); } } void tst_QRawFont::detach_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting preference") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting preference") << QFont::PreferFullHinting; } void tst_QRawFont::detach() { QFETCH(QFont::HintingPreference, hintingPreference); { QString rawFontFamilyName; qreal rawFontPixelSize; qreal rawFontAscent; qreal rawFontDescent; int rawFontTableSize; QRawFont outerRawFont; { QRawFont rawFont(testFont, 11, hintingPreference); QVERIFY(rawFont.isValid()); rawFontFamilyName = rawFont.familyName(); rawFontPixelSize = rawFont.pixelSize(); rawFontAscent = rawFont.ascent(); rawFontDescent = rawFont.descent(); rawFontTableSize = rawFont.fontTable("glyf").size(); QVERIFY(rawFontTableSize > 0); { QRawFont otherRawFont(rawFont); otherRawFont.loadFromFile(testFont, rawFontPixelSize, hintingPreference); QVERIFY(otherRawFont.isValid()); QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(otherRawFont.familyName(), rawFontFamilyName); QCOMPARE(otherRawFont.hintingPreference(), hintingPreference); QCOMPARE(otherRawFont.ascent(), rawFontAscent); QCOMPARE(otherRawFont.descent(), rawFontDescent); QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize); } { QRawFont otherRawFont = rawFont; otherRawFont.loadFromFile(testFont, rawFontPixelSize, hintingPreference); QVERIFY(otherRawFont.isValid()); QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(otherRawFont.familyName(), rawFontFamilyName); QCOMPARE(otherRawFont.hintingPreference(), hintingPreference); QCOMPARE(otherRawFont.ascent(), rawFontAscent); QCOMPARE(otherRawFont.descent(), rawFontDescent); QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize); } outerRawFont = rawFont; rawFont.loadFromFile(testFont, rawFontPixelSize, hintingPreference); } QVERIFY(outerRawFont.isValid()); QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize); QCOMPARE(outerRawFont.familyName(), rawFontFamilyName); QCOMPARE(outerRawFont.hintingPreference(), hintingPreference); QCOMPARE(outerRawFont.ascent(), rawFontAscent); QCOMPARE(outerRawFont.descent(), rawFontDescent); QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize); } } void tst_QRawFont::unsupportedWritingSystem_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting preference") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting preference") << QFont::PreferFullHinting; } void tst_QRawFont::unsupportedWritingSystem() { QFETCH(QFont::HintingPreference, hintingPreference); int id = QFontDatabase::addApplicationFont(testFont); QFont font("QtBidiTestFont"); font.setHintingPreference(hintingPreference); font.setPixelSize(12.0); QRawFont rawFont = QRawFont::fromFont(font, QFontDatabase::Any); QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont")); QCOMPARE(rawFont.pixelSize(), 12.0); rawFont = QRawFont::fromFont(font, QFontDatabase::Hebrew); QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont")); QCOMPARE(rawFont.pixelSize(), 12.0); QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic).simplified().remove(QLatin1Char(' ')); QTextLayout layout; layout.setFont(font); layout.setText(arabicText); layout.setCacheEnabled(true); layout.beginLayout(); layout.createLine(); layout.endLayout(); QList glyphRuns = layout.glyphRuns(); QCOMPARE(glyphRuns.size(), 1); QGlyphRun glyphs = glyphRuns.at(0); QRawFont layoutFont = glyphs.rawFont(); QVERIFY(layoutFont.familyName() != QString::fromLatin1("QtBidiTestFont")); QCOMPARE(layoutFont.pixelSize(), 12.0); rawFont = QRawFont::fromFont(font, QFontDatabase::Arabic); QCOMPARE(rawFont.familyName(), layoutFont.familyName()); QCOMPARE(rawFont.pixelSize(), 12.0); QFontDatabase::removeApplicationFont(id); } void tst_QRawFont::rawFontSetPixelSize_data() { QTest::addColumn("hintingPreference"); QTest::newRow("Default hinting preference") << QFont::PreferDefaultHinting; QTest::newRow("No hinting preference") << QFont::PreferNoHinting; QTest::newRow("Vertical hinting preference") << QFont::PreferVerticalHinting; QTest::newRow("Full hinting preference") << QFont::PreferFullHinting; } void tst_QRawFont::rawFontSetPixelSize() { QFETCH(QFont::HintingPreference, hintingPreference); QTextLayout layout("Foobar"); QFont font = layout.font(); font.setHintingPreference(hintingPreference); font.setPixelSize(12); layout.setFont(font); layout.setCacheEnabled(true); layout.beginLayout(); layout.createLine(); layout.endLayout(); QGlyphRun glyphs = layout.glyphRuns().at(0); QRawFont rawFont = glyphs.rawFont(); QCOMPARE(rawFont.pixelSize(), 12.0); rawFont.setPixelSize(24); QCOMPARE(rawFont.pixelSize(), 24.0); } void tst_QRawFont::multipleRawFontsFromData() { QFile file(testFont); QRawFont testFont; if (file.open(QIODevice::ReadOnly)) { testFont.loadFromData(file.readAll(), 11, QFont::PreferDefaultHinting); file.close(); } file.setFileName(testFontBoldItalic); QRawFont testFontBoldItalic; if (file.open(QIODevice::ReadOnly)) testFontBoldItalic.loadFromData(file.readAll(), 11, QFont::PreferDefaultHinting); QVERIFY(testFont.familyName() != (testFontBoldItalic.familyName()) || testFont.style() != (testFontBoldItalic.style())); } void tst_QRawFont::rawFontFromInvalidData() { QByteArray invalidData("foobar"); QRawFont font; font.loadFromData(invalidData, 10, QFont::PreferDefaultHinting); QVERIFY(!font.isValid()); invalidData.fill(char(255), 1024); font.loadFromData(invalidData, 10, QFont::PreferDefaultHinting); QVERIFY(!font.isValid()); } #define FUZZY_LTEQ(X, Y) (X < Y || qFuzzyCompare(X, Y)) void tst_QRawFont::kernedAdvances() { const int emSquareSize = 1000; const qreal pixelSize = 16.0; const int underScoreAW = 500; const int underscoreTwoKerning = -500; const qreal errorMargin = 1.0 / 16.0; // Fixed point error margin QRawFont font(testFont, pixelSize); QVERIFY(font.isValid()); QList glyphIndexes = font.glyphIndexesForString(QStringLiteral("__")); QCOMPARE(glyphIndexes.size(), 2); QList advances = font.advancesForGlyphIndexes(glyphIndexes, QRawFont::KernedAdvances); QCOMPARE(advances.size(), 2); qreal expectedAdvanceWidth = pixelSize * underScoreAW / emSquareSize; QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin)); glyphIndexes = font.glyphIndexesForString(QStringLiteral("_2")); QCOMPARE(glyphIndexes.size(), 2); advances = font.advancesForGlyphIndexes(glyphIndexes, QRawFont::KernedAdvances); QCOMPARE(advances.size(), 2); expectedAdvanceWidth = pixelSize * (underScoreAW + underscoreTwoKerning) / emSquareSize; QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin)); } void tst_QRawFont::fallbackFontsOrder() { int id = QFontDatabase::addApplicationFont(testFont); QFont font("QtBidiTestFont"); font.setPixelSize(12.0); QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic); // If this fails, then the writing system sample has changed and we need to create // a new text containing both a space and Arabic characters. QVERIFY(arabicText.contains(QLatin1Char(' '))); QTextLayout layout; layout.setFont(font); layout.setText(arabicText); layout.setCacheEnabled(true); layout.beginLayout(); layout.createLine(); layout.endLayout(); QList glyphRuns = layout.glyphRuns(); #ifdef Q_OS_ANDROID QEXPECT_FAIL("", "QTBUG-69217", Continue); #endif // Since QtBidiTestFont does not support Arabic nor the space, both should map to // the same font. If this fails, it is an indication that the list of fallbacks fonts // is not sorted by writing system support. QCOMPARE(glyphRuns.size(), 1); QFontDatabase::removeApplicationFont(id); } void tst_QRawFont::qtbug65923_partal_clone_data() { QTest::addColumn("shouldClone"); QTest::newRow("Without cloning font engine") << false; QTest::newRow("Cloning font engine") << true; } void tst_QRawFont::qtbug65923_partal_clone() { QFile file(testFont); file.open(QIODevice::ReadOnly); QByteArray fontData = file.readAll(); QRawFont outerFont; { QRawFont innerFont(fontData, 16, QFont::PreferDefaultHinting); QFETCH(bool, shouldClone); if (shouldClone) { // This will trigger QFontEngine::cloneWithSize innerFont.setPixelSize(innerFont.pixelSize() + 1); } outerFont = innerFont; } // This will detach if data is shared with the raw font. If the raw font has // a naked reference to the data, without informing Qt of it via the ref count // of the byte array, this will result in clearing 'live' data. fontData.fill('\0'); QVERIFY(!outerFont.boundingRect(42).isEmpty()); } #endif // QT_NO_RAWFONT QTEST_MAIN(tst_QRawFont) #include "tst_qrawfont.moc"