1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include <qfontdatabase.h>
32#include <qfontinfo.h>
33#include <qfontmetrics.h>
34#include <qtextlayout.h>
35#include <private/qrawfont_p.h>
36#include <private/qfont_p.h>
37#include <private/qfontengine_p.h>
38#include <qpa/qplatformfontdatabase.h>
39
40Q_LOGGING_CATEGORY(lcTests, "qt.text.tests")
41
42class tst_QFontDatabase : public QObject
43{
44Q_OBJECT
45
46public:
47 tst_QFontDatabase();
48
49private slots:
50 void initTestCase();
51 void styles_data();
52 void styles();
53
54 void fixedPitch_data();
55 void fixedPitch();
56 void systemFixedFont();
57
58#ifdef Q_OS_MAC
59 void trickyFonts_data();
60 void trickyFonts();
61#endif
62
63 void widthTwoTimes_data();
64 void widthTwoTimes();
65
66 void addAppFont_data();
67 void addAppFont();
68
69 void addTwoAppFontsFromFamily();
70
71 void aliases();
72 void fallbackFonts();
73
74 void condensedFontWidth();
75 void condensedFontWidthNoFontMerging();
76 void condensedFontMatching();
77
78 void rasterFonts();
79 void smoothFonts();
80
81 void registerOpenTypePreferredNamesSystem();
82 void registerOpenTypePreferredNamesApplication();
83
84private:
85 QString m_ledFont;
86 QString m_testFont;
87 QString m_testFontCondensed;
88 QString m_testFontItalic;
89};
90
91tst_QFontDatabase::tst_QFontDatabase()
92{
93}
94
95void tst_QFontDatabase::initTestCase()
96{
97 m_ledFont = QFINDTESTDATA("LED_REAL.TTF");
98 m_testFont = QFINDTESTDATA("testfont.ttf");
99 m_testFontCondensed = QFINDTESTDATA("testfont_condensed.ttf");
100 m_testFontItalic = QFINDTESTDATA("testfont_italic.ttf");
101 QVERIFY(!m_ledFont.isEmpty());
102 QVERIFY(!m_testFont.isEmpty());
103 QVERIFY(!m_testFontCondensed.isEmpty());
104 QVERIFY(!m_testFontItalic.isEmpty());
105}
106
107void tst_QFontDatabase::styles_data()
108{
109 QTest::addColumn<QString>(name: "font");
110
111 QTest::newRow( dataTag: "data0" ) << QString( "Times New Roman" );
112}
113
114void tst_QFontDatabase::styles()
115{
116 QFETCH( QString, font );
117
118 QFontDatabase fdb;
119 QStringList styles = fdb.styles( family: font );
120 QStringList::Iterator it = styles.begin();
121 while ( it != styles.end() ) {
122 QString style = *it;
123 QString trimmed = style.trimmed();
124 ++it;
125
126 QCOMPARE(style, trimmed);
127 }
128}
129
130void tst_QFontDatabase::fixedPitch_data()
131{
132 QTest::addColumn<QString>(name: "font");
133 QTest::addColumn<bool>(name: "fixedPitch");
134
135 QTest::newRow( dataTag: "Times New Roman" ) << QString( "Times New Roman" ) << false;
136 QTest::newRow( dataTag: "Arial" ) << QString( "Arial" ) << false;
137 QTest::newRow( dataTag: "Andale Mono" ) << QString( "Andale Mono" ) << true;
138 QTest::newRow( dataTag: "Courier" ) << QString( "Courier" ) << true;
139 QTest::newRow( dataTag: "Courier New" ) << QString( "Courier New" ) << true;
140#ifndef Q_OS_MAC
141 QTest::newRow( dataTag: "Script" ) << QString( "Script" ) << false;
142 QTest::newRow( dataTag: "Lucida Console" ) << QString( "Lucida Console" ) << true;
143 QTest::newRow( dataTag: "DejaVu Sans" ) << QString( "DejaVu Sans" ) << false;
144 QTest::newRow( dataTag: "DejaVu Sans Mono" ) << QString( "DejaVu Sans Mono" ) << true;
145#else
146 QTest::newRow( "Menlo" ) << QString( "Menlo" ) << true;
147 QTest::newRow( "Monaco" ) << QString( "Monaco" ) << true;
148#endif
149}
150
151void tst_QFontDatabase::fixedPitch()
152{
153 QFETCH(QString, font);
154 QFETCH(bool, fixedPitch);
155
156 QFontDatabase fdb;
157 if (!fdb.families().contains(str: font))
158 QSKIP("Font not installed");
159
160 QCOMPARE(fdb.isFixedPitch(font), fixedPitch);
161
162 QFont qfont(font);
163 QFontInfo fi(qfont);
164 QCOMPARE(fi.fixedPitch(), fixedPitch);
165}
166
167void tst_QFontDatabase::systemFixedFont() // QTBUG-54623
168{
169 QFont font = QFontDatabase::systemFont(type: QFontDatabase::FixedFont);
170 QFontInfo fontInfo(font);
171 bool fdbSaysFixed = QFontDatabase().isFixedPitch(family: fontInfo.family(), style: fontInfo.styleName());
172 qCDebug(lcTests) << "system fixed font is" << font << "really fixed?" << fdbSaysFixed << fontInfo.fixedPitch();
173 QVERIFY(fdbSaysFixed);
174 QVERIFY(fontInfo.fixedPitch());
175}
176
177#ifdef Q_OS_MAC
178void tst_QFontDatabase::trickyFonts_data()
179{
180 QTest::addColumn<QString>("font");
181
182 QTest::newRow( "Geeza Pro" ) << QString( "Geeza Pro" );
183}
184
185void tst_QFontDatabase::trickyFonts()
186{
187 QFETCH(QString, font);
188
189 QFontDatabase fdb;
190 if (!fdb.families().contains(font))
191 QSKIP( "Font not installed");
192
193 QFont qfont(font);
194 QFontInfo fi(qfont);
195 QCOMPARE(fi.family(), font);
196}
197#endif
198
199void tst_QFontDatabase::widthTwoTimes_data()
200{
201 QTest::addColumn<QString>(name: "font");
202 QTest::addColumn<int>(name: "pixelSize");
203 QTest::addColumn<QString>(name: "text");
204
205 QTest::newRow(dataTag: "Arial") << QString("Arial") << 1000 << QString("Some text");
206}
207
208void tst_QFontDatabase::widthTwoTimes()
209{
210 QFETCH(QString, font);
211 QFETCH(int, pixelSize);
212 QFETCH(QString, text);
213
214 QFont f;
215 f.setFamily(font);
216 f.setPixelSize(pixelSize);
217
218 QFontMetrics fm(f);
219 int w1 = fm.horizontalAdvance(text, len: 0);
220 int w2 = fm.horizontalAdvance(text, len: 0);
221
222 QCOMPARE(w1, w2);
223}
224
225void tst_QFontDatabase::addAppFont_data()
226{
227 QTest::addColumn<bool>(name: "useMemoryFont");
228 QTest::newRow(dataTag: "font file") << false;
229 QTest::newRow(dataTag: "memory font") << true;
230}
231
232void tst_QFontDatabase::addAppFont()
233{
234 QFETCH(bool, useMemoryFont);
235 QSignalSpy fontDbChangedSpy(QGuiApplication::instance(), SIGNAL(fontDatabaseChanged()));
236
237 QFontDatabase db;
238
239 const QStringList oldFamilies = db.families();
240 QVERIFY(!oldFamilies.isEmpty());
241
242 fontDbChangedSpy.clear();
243
244 int id;
245 if (useMemoryFont) {
246 QFile fontfile(m_ledFont);
247 fontfile.open(flags: QIODevice::ReadOnly);
248 QByteArray fontdata = fontfile.readAll();
249 QVERIFY(!fontdata.isEmpty());
250 id = QFontDatabase::addApplicationFontFromData(fontData: fontdata);
251 } else {
252 id = QFontDatabase::addApplicationFont(fileName: m_ledFont);
253 }
254#if defined(Q_OS_HPUX) && defined(QT_NO_FONTCONFIG)
255 // Documentation says that X11 systems that don't have fontconfig
256 // don't support application fonts.
257 QCOMPARE(id, -1);
258 return;
259#endif
260 QCOMPARE(fontDbChangedSpy.count(), 1);
261 if (id == -1)
262 QSKIP("Skip the test since app fonts are not supported on this system");
263
264 const QStringList addedFamilies = QFontDatabase::applicationFontFamilies(id);
265 QVERIFY(!addedFamilies.isEmpty());
266
267 const QStringList newFamilies = db.families();
268 QVERIFY(!newFamilies.isEmpty());
269 QVERIFY(newFamilies.count() >= oldFamilies.count());
270
271 for (int i = 0; i < addedFamilies.count(); ++i) {
272 QString family = addedFamilies.at(i);
273 QVERIFY(newFamilies.contains(family));
274 QFont qfont(family);
275 QFontInfo fi(qfont);
276 QCOMPARE(fi.family(), family);
277 }
278
279 QVERIFY(QFontDatabase::removeApplicationFont(id));
280 QCOMPARE(fontDbChangedSpy.count(), 2);
281
282 QVERIFY(db.families().count() <= oldFamilies.count());
283}
284
285void tst_QFontDatabase::addTwoAppFontsFromFamily()
286{
287 int regularId = QFontDatabase::addApplicationFont(fileName: m_testFont);
288 if (regularId == -1)
289 QSKIP("Skip the test since app fonts are not supported on this system");
290
291 int italicId = QFontDatabase::addApplicationFont(fileName: m_testFontItalic);
292 QVERIFY(italicId != -1);
293
294 QVERIFY(!QFontDatabase::applicationFontFamilies(regularId).isEmpty());
295 QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty());
296
297 QString regularFontName = QFontDatabase::applicationFontFamilies(id: regularId).first();
298 QString italicFontName = QFontDatabase::applicationFontFamilies(id: italicId).first();
299 QCOMPARE(regularFontName, italicFontName);
300
301 QFont italicFont = QFontDatabase().font(family: italicFontName,
302 style: QString::fromLatin1(str: "Italic"), pointSize: 14);
303 QVERIFY(italicFont.italic());
304
305 QFontDatabase::removeApplicationFont(id: regularId);
306 QFontDatabase::removeApplicationFont(id: italicId);
307}
308
309void tst_QFontDatabase::aliases()
310{
311 QFontDatabase db;
312 const QStringList families = db.families();
313 QVERIFY(!families.isEmpty());
314 QString firstFont;
315 for (int i = 0; i < families.size(); ++i) {
316 if (!families.at(i).contains(c: '[')) {
317 firstFont = families.at(i);
318 break;
319 }
320 }
321
322 if (firstFont.isEmpty())
323 QSKIP("Skipped because there are no unambiguous font families on the system.");
324
325 QVERIFY(db.hasFamily(firstFont));
326 const QString alias = QStringLiteral("AliasToFirstFont") + firstFont;
327 QVERIFY(!db.hasFamily(alias));
328 QPlatformFontDatabase::registerAliasToFontFamily(familyName: firstFont, alias);
329 QVERIFY(db.hasFamily(alias));
330}
331
332void tst_QFontDatabase::fallbackFonts()
333{
334 QTextLayout layout;
335 QString s;
336 s.append(c: QChar(0x31));
337 s.append(c: QChar(0x05D0));
338 layout.setText(s);
339 layout.beginLayout();
340 layout.createLine();
341 layout.endLayout();
342
343 QList<QGlyphRun> runs = layout.glyphRuns(from: 0, length: 1);
344 foreach (QGlyphRun run, runs) {
345 QRawFont rawFont = run.rawFont();
346 QVERIFY(rawFont.isValid());
347
348 QCOMPARE(run.glyphIndexes().size(), 1);
349 QVERIFY(run.glyphIndexes().at(0) != 0);
350 }
351}
352
353static QString testString()
354{
355 return QStringLiteral("foo bar");
356}
357
358void tst_QFontDatabase::condensedFontWidthNoFontMerging()
359{
360 int regularFontId = QFontDatabase::addApplicationFont(fileName: m_testFont);
361 int condensedFontId = QFontDatabase::addApplicationFont(fileName: m_testFontCondensed);
362
363 QVERIFY(!QFontDatabase::applicationFontFamilies(regularFontId).isEmpty());
364 QVERIFY(!QFontDatabase::applicationFontFamilies(condensedFontId).isEmpty());
365
366 QString regularFontName = QFontDatabase::applicationFontFamilies(id: regularFontId).first();
367 QString condensedFontName = QFontDatabase::applicationFontFamilies(id: condensedFontId).first();
368
369 QFont condensedFont1(condensedFontName);
370 if (regularFontName == condensedFontName)
371 condensedFont1.setStyleName(QStringLiteral("Condensed"));
372 condensedFont1.setStyleStrategy(QFont::PreferMatch);
373
374 QFont condensedFont2 = condensedFont1;
375 condensedFont2.setStyleStrategy(QFont::StyleStrategy(QFont::NoFontMerging | QFont::PreferMatch));
376
377 QCOMPARE(QFontMetricsF(condensedFont2).horizontalAdvance(QStringLiteral("foobar")),
378 QFontMetricsF(condensedFont1).horizontalAdvance(QStringLiteral("foobar")));
379 }
380
381void tst_QFontDatabase::condensedFontWidth()
382{
383 QFontDatabase db;
384 QFontDatabase::addApplicationFont(fileName: m_testFont);
385 QFontDatabase::addApplicationFont(fileName: m_testFontCondensed);
386
387 QVERIFY(db.hasFamily("QtBidiTestFont"));
388 if (!db.hasFamily(family: "QtBidiTestFontCondensed"))
389 QSKIP("This platform doesn't support font sub-family names (QTBUG-55625)");
390
391 // Test we really get a condensed font, and a not renormalized one (QTBUG-48043):
392 QFont testFont("QtBidiTestFont");
393 QFont testFontCondensed("QtBidiTestFontCondensed");
394 QFontMetrics fmTF(testFont);
395 QFontMetrics fmTFC(testFontCondensed);
396 QVERIFY(fmTF.horizontalAdvance(testString()) > fmTFC.horizontalAdvance(testString()));
397
398}
399
400void tst_QFontDatabase::condensedFontMatching()
401{
402 QFontDatabase db;
403 QFontDatabase::removeAllApplicationFonts();
404 QFontDatabase::addApplicationFont(fileName: m_testFontCondensed);
405 if (!db.hasFamily(family: "QtBidiTestFont"))
406 QSKIP("This platform doesn't support preferred font family names (QTBUG-53478)");
407 QFontDatabase::addApplicationFont(fileName: m_testFont);
408
409 // Test we correctly get the condensed font using different font matching methods:
410 QFont tfcByStretch("QtBidiTestFont");
411 tfcByStretch.setStretch(QFont::Condensed);
412 QFont tfcByStyleName("QtBidiTestFont");
413 tfcByStyleName.setStyleName("Condensed");
414
415#ifdef Q_OS_WIN
416 QFont f;
417 f.setStyleStrategy(QFont::NoFontMerging);
418 QFontPrivate *font_d = QFontPrivate::get(f);
419 if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype)
420 QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue);
421#endif
422
423#ifdef Q_OS_ANDROID
424 QEXPECT_FAIL("", "QTBUG-69216", Continue);
425#endif
426 QCOMPARE(QFontMetrics(tfcByStretch).horizontalAdvance(testString()),
427 QFontMetrics(tfcByStyleName).horizontalAdvance(testString()));
428
429 if (!db.hasFamily(family: "QtBidiTestFontCondensed"))
430 QSKIP("This platform doesn't support font sub-family names (QTBUG-55625)");
431
432 QFont tfcBySubfamilyName("QtBidiTestFontCondensed");
433 QCOMPARE(QFontMetrics(tfcByStyleName).horizontalAdvance(testString()),
434 QFontMetrics(tfcBySubfamilyName).horizontalAdvance(testString()));
435}
436
437void tst_QFontDatabase::rasterFonts()
438{
439 QFont font(QLatin1String("Fixedsys"), 1000);
440 QFontInfo fontInfo(font);
441
442 if (fontInfo.family() != font.family())
443 QSKIP("Fixedsys font not available.");
444
445 QVERIFY(!QFontDatabase().isSmoothlyScalable(font.family()));
446 QVERIFY(fontInfo.pointSize() != font.pointSize());
447}
448
449void tst_QFontDatabase::smoothFonts()
450{
451 QFont font(QLatin1String("Arial"), 1000);
452 QFontInfo fontInfo(font);
453
454 if (fontInfo.family() != font.family())
455 QSKIP("Arial font not available.");
456
457 // Smooth and bitmap scaling are mutually exclusive
458 QVERIFY(QFontDatabase().isSmoothlyScalable(font.family()));
459 QVERIFY(!QFontDatabase().isBitmapScalable(font.family()));
460}
461
462void tst_QFontDatabase::registerOpenTypePreferredNamesSystem()
463{
464 QFontDatabase db;
465 // This font family was picked because it was the only one I had installed which showcased the
466 // problem
467 if (!db.hasFamily(family: QString::fromLatin1(str: "Source Code Pro ExtraLight")))
468 QSKIP("Source Code Pro ExtraLight is not installed");
469
470 QStringList styles = db.styles(family: QString::fromLatin1(str: "Source Code Pro"));
471 QVERIFY(styles.contains(QLatin1String("ExtraLight")));
472}
473
474void tst_QFontDatabase::registerOpenTypePreferredNamesApplication()
475{
476 QFontDatabase db;
477
478 int id = QFontDatabase::addApplicationFont(fileName: QString::fromLatin1(str: ":/testfont_open.otf"));
479 if (id == -1)
480 QSKIP("Skip the test since app fonts are not supported on this system");
481
482 QStringList styles = db.styles(family: QString::fromLatin1(str: "QtBidiTestFont"));
483 QVERIFY(styles.contains(QLatin1String("Open")));
484
485 QFontDatabase::removeApplicationFont(id);
486}
487
488QTEST_MAIN(tst_QFontDatabase)
489#include "tst_qfontdatabase.moc"
490

source code of qtbase/tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp