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#include <QtGui/QFontDatabase>
31
32#include <qrawfont.h>
33#include <private/qrawfont_p.h>
34
35class tst_QRawFont: public QObject
36{
37 Q_OBJECT
38#if !defined(QT_NO_RAWFONT)
39private slots:
40 void init();
41 void initTestCase();
42
43 void invalidRawFont();
44
45 void explicitRawFontNotLoadedInDatabase_data();
46 void explicitRawFontNotLoadedInDatabase();
47
48 void explicitRawFontNotAvailableInSystem_data();
49 void explicitRawFontNotAvailableInSystem();
50
51 void correctFontData_data();
52 void correctFontData();
53
54 void glyphIndices();
55
56 void advances_data();
57 void advances();
58
59 void textLayout();
60
61 void fontTable_data();
62 void fontTable();
63
64 void supportedWritingSystems_data();
65 void supportedWritingSystems();
66
67 void supportsCharacter_data();
68 void supportsCharacter();
69
70 void supportsUcs4Character_data();
71 void supportsUcs4Character();
72
73 void fromFont_data();
74 void fromFont();
75
76 void copyConstructor_data();
77 void copyConstructor();
78
79 void detach_data();
80 void detach();
81
82 void unsupportedWritingSystem_data();
83 void unsupportedWritingSystem();
84
85 void rawFontSetPixelSize_data();
86 void rawFontSetPixelSize();
87
88 void multipleRawFontsFromData();
89
90 void rawFontFromInvalidData();
91
92 void kernedAdvances();
93
94 void fallbackFontsOrder();
95
96 void qtbug65923_partal_clone_data();
97 void qtbug65923_partal_clone();
98
99private:
100 QString testFont;
101 QString testFontBoldItalic;
102 QString testFontOs2V1;
103#endif // QT_NO_RAWFONT
104};
105
106#if !defined(QT_NO_RAWFONT)
107Q_DECLARE_METATYPE(QFont::HintingPreference)
108Q_DECLARE_METATYPE(QFont::Style)
109Q_DECLARE_METATYPE(QFont::Weight)
110Q_DECLARE_METATYPE(QFontDatabase::WritingSystem)
111
112void tst_QRawFont::init()
113{
114}
115
116void tst_QRawFont::initTestCase()
117{
118 testFont = QFINDTESTDATA("testfont.ttf");
119 testFontBoldItalic = QFINDTESTDATA("testfont_bold_italic.ttf");
120 testFontOs2V1 = QFINDTESTDATA("testfont_os2_v1.ttf");
121 if (testFont.isEmpty() || testFontBoldItalic.isEmpty())
122 QFAIL("qrawfont unittest font files not found!");
123
124 QFontDatabase database;
125 if (database.families().count() == 0)
126 QSKIP("No fonts available!!!");
127}
128
129void tst_QRawFont::invalidRawFont()
130{
131 QRawFont font;
132 QVERIFY(!font.isValid());
133 QCOMPARE(font.pixelSize(), 0.0);
134 QVERIFY(font.familyName().isEmpty());
135 QCOMPARE(font.style(), QFont::StyleNormal);
136 QCOMPARE(font.weight(), -1);
137 QCOMPARE(font.ascent(), 0.0);
138 QCOMPARE(font.descent(), 0.0);
139 QVERIFY(font.glyphIndexesForString(QLatin1String("Test")).isEmpty());
140}
141
142void tst_QRawFont::explicitRawFontNotLoadedInDatabase_data()
143{
144 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
145
146 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
147 QTest::newRow(dataTag: "No hinting") << QFont::PreferNoHinting;
148 QTest::newRow(dataTag: "Vertical hinting") << QFont::PreferVerticalHinting;
149 QTest::newRow(dataTag: "Full hinting") << QFont::PreferFullHinting;
150}
151
152void tst_QRawFont::explicitRawFontNotLoadedInDatabase()
153{
154 QFETCH(QFont::HintingPreference, hintingPreference);
155
156 QRawFont font(testFont, 10, hintingPreference);
157 QVERIFY(font.isValid());
158
159 QVERIFY(!QFontDatabase().families().contains(font.familyName()));
160}
161
162void tst_QRawFont::explicitRawFontNotAvailableInSystem_data()
163{
164 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
165
166 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
167 QTest::newRow(dataTag: "No hinting") << QFont::PreferNoHinting;
168 QTest::newRow(dataTag: "Vertical hinting") << QFont::PreferVerticalHinting;
169 QTest::newRow(dataTag: "Full hinting") << QFont::PreferFullHinting;
170}
171
172void tst_QRawFont::explicitRawFontNotAvailableInSystem()
173{
174 QFETCH(QFont::HintingPreference, hintingPreference);
175
176 QRawFont rawfont(testFont, 10, hintingPreference);
177
178 {
179 QFont font(rawfont.familyName(), 10);
180
181 QVERIFY(!font.exactMatch());
182 QVERIFY(font.family() != QFontInfo(font).family());
183 }
184}
185
186void tst_QRawFont::correctFontData_data()
187{
188 QTest::addColumn<QString>(name: "fileName");
189 QTest::addColumn<QString>(name: "expectedFamilyName");
190 QTest::addColumn<QFont::Style>(name: "style");
191 QTest::addColumn<QFont::Weight>(name: "weight");
192 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
193 QTest::addColumn<qreal>(name: "unitsPerEm");
194 QTest::addColumn<qreal>(name: "pixelSize");
195 QTest::addColumn<int>(name: "capHeight");
196
197 int hintingPreferences[] = {
198 int(QFont::PreferDefaultHinting),
199 int(QFont::PreferNoHinting),
200 int(QFont::PreferVerticalHinting),
201 int(QFont::PreferFullHinting),
202 -1
203 };
204 int *hintingPreference = hintingPreferences;
205
206 while (*hintingPreference >= 0) {
207 QString fileName = testFont;
208 QString title = fileName
209 + QLatin1String(": hintingPreference=")
210 + QString::number(*hintingPreference);
211
212 QTest::newRow(qPrintable(title))
213 << fileName
214 << QString::fromLatin1(str: "QtBidiTestFont")
215 << QFont::StyleNormal
216 << QFont::Normal
217 << QFont::HintingPreference(*hintingPreference)
218 << qreal(1000.0)
219 << qreal(10.0)
220 << 7;
221
222 fileName = testFontBoldItalic;
223 title = fileName
224 + QLatin1String(": hintingPreference=")
225 + QString::number(*hintingPreference);
226
227 QTest::newRow(qPrintable(title))
228 << fileName
229 << QString::fromLatin1(str: "QtBidiTestFont")
230 << QFont::StyleItalic
231 << QFont::Bold
232 << QFont::HintingPreference(*hintingPreference)
233 << qreal(1000.0)
234 << qreal(10.0)
235 << 7;
236
237 fileName = testFontOs2V1;
238 title = fileName
239 + QLatin1String(": hintingPreference=")
240 + QString::number(*hintingPreference);
241
242 QTest::newRow(qPrintable(title))
243 << fileName
244 << QString::fromLatin1(str: "QtBidiTestFont")
245 << QFont::StyleNormal
246 << QFont::Normal
247 << QFont::HintingPreference(*hintingPreference)
248 << qreal(1000.0)
249 << qreal(10.0)
250 << 7;
251
252 ++hintingPreference;
253 }
254}
255
256void tst_QRawFont::correctFontData()
257{
258 QFETCH(QString, fileName);
259 QFETCH(QString, expectedFamilyName);
260 QFETCH(QFont::Style, style);
261 QFETCH(QFont::Weight, weight);
262 QFETCH(QFont::HintingPreference, hintingPreference);
263 QFETCH(qreal, unitsPerEm);
264 QFETCH(qreal, pixelSize);
265 QFETCH(int, capHeight);
266
267 QRawFont font(fileName, 10, hintingPreference);
268 QVERIFY(font.isValid());
269
270 QCOMPARE(font.familyName(), expectedFamilyName);
271 QCOMPARE(font.style(), style);
272 QCOMPARE(font.weight(), int(weight));
273 QCOMPARE(font.hintingPreference(), hintingPreference);
274 QCOMPARE(font.unitsPerEm(), unitsPerEm);
275 QCOMPARE(font.pixelSize(), pixelSize);
276
277 // Some platforms return the actual fractional height of the
278 // H character when the value is missing from the OS/2 table,
279 // so we ceil it off to match (any touched pixel counts).
280 QCOMPARE(qCeil(font.capHeight()), capHeight);
281}
282
283void tst_QRawFont::glyphIndices()
284{
285 QRawFont font(testFont, 10);
286 QVERIFY(font.isValid());
287
288 QVector<quint32> glyphIndices = font.glyphIndexesForString(text: QLatin1String("Foobar"));
289 QVector<quint32> expectedGlyphIndices;
290 expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86;
291
292 QCOMPARE(glyphIndices, expectedGlyphIndices);
293
294 glyphIndices = font.glyphIndexesForString(text: QString());
295 QVERIFY(glyphIndices.isEmpty());
296
297 QString str(QLatin1String("Foobar"));
298 int numGlyphs = str.size();
299 glyphIndices.resize(size: numGlyphs);
300
301 QVERIFY(!font.glyphIndexesForChars(str.constData(), 0, glyphIndices.data(), &numGlyphs));
302 QCOMPARE(numGlyphs, 0);
303
304 QVERIFY(!font.glyphIndexesForChars(str.constData(), str.size(), glyphIndices.data(), &numGlyphs));
305 QCOMPARE(numGlyphs, str.size());
306
307 QVERIFY(font.glyphIndexesForChars(str.constData(), str.size(), glyphIndices.data(), &numGlyphs));
308 QCOMPARE(numGlyphs, str.size());
309
310 QCOMPARE(glyphIndices, expectedGlyphIndices);
311}
312
313void tst_QRawFont::advances_data()
314{
315 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
316
317 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
318 QTest::newRow(dataTag: "No hinting") << QFont::PreferNoHinting;
319 QTest::newRow(dataTag: "Vertical hinting") << QFont::PreferVerticalHinting;
320 QTest::newRow(dataTag: "Full hinting") << QFont::PreferFullHinting;
321}
322
323void tst_QRawFont::advances()
324{
325 QFETCH(QFont::HintingPreference, hintingPreference);
326
327 QRawFont font(testFont, 10, hintingPreference);
328 QVERIFY(font.isValid());
329
330 QRawFontPrivate *font_d = QRawFontPrivate::get(font);
331 QVERIFY(font_d->fontEngine != 0);
332
333 QVector<quint32> glyphIndices;
334 glyphIndices << 44 << 83 << 83 << 70 << 69 << 86; // "Foobar"
335
336 bool supportsSubPixelPositions = font_d->fontEngine->supportsSubPixelPositions();
337 QVector<QPointF> advances = font.advancesForGlyphIndexes(glyphIndexes: glyphIndices);
338
339 bool mayDiffer = font_d->fontEngine->type() == QFontEngine::Freetype
340 && (hintingPreference == QFont::PreferFullHinting
341 || hintingPreference == QFont::PreferDefaultHinting);
342
343 for (int i = 0; i < glyphIndices.size(); ++i) {
344 if ((i == 0 || i == 5) && mayDiffer) {
345 QVERIFY2(qRound(advances.at(i).x()) == 8
346 || qRound(advances.at(i).x()) == 9,
347 qPrintable(QStringLiteral("%1 != %2 && %1 != %3")
348 .arg(qRound(advances.at(i).x()))
349 .arg(8)
350 .arg(9)));
351 } else {
352 QCOMPARE(qRound(advances.at(i).x()), 8);
353 }
354
355 if (supportsSubPixelPositions)
356 QVERIFY(advances.at(i).x() > 8.0);
357
358 QVERIFY(qFuzzyIsNull(advances.at(i).y()));
359 }
360
361 advances = font.advancesForGlyphIndexes(glyphIndexes: QVector<quint32>());
362 QVERIFY(advances.isEmpty());
363
364 int numGlyphs = glyphIndices.size();
365 advances.resize(size: numGlyphs);
366
367 QVERIFY(!font.advancesForGlyphIndexes(glyphIndices.constData(), advances.data(), 0));
368
369 QVERIFY(font.advancesForGlyphIndexes(glyphIndices.constData(), advances.data(), numGlyphs));
370
371 for (int i = 0; i < glyphIndices.size(); ++i) {
372 if ((i == 0 || i == 5) && mayDiffer) {
373 QVERIFY2(qRound(advances.at(i).x()) == 8
374 || qRound(advances.at(i).x()) == 9,
375 qPrintable(QStringLiteral("%1 != %2 && %1 != %3")
376 .arg(qRound(advances.at(i).x()))
377 .arg(8)
378 .arg(9)));
379 } else {
380 QCOMPARE(qRound(advances.at(i).x()), 8);
381 }
382
383 if (supportsSubPixelPositions)
384 QVERIFY(advances.at(i).x() > 8.0);
385
386 QVERIFY(qFuzzyIsNull(advances.at(i).y()));
387 }
388}
389
390void tst_QRawFont::textLayout()
391{
392 QFontDatabase fontDatabase;
393 int id = fontDatabase.addApplicationFont(fileName: testFont);
394 QVERIFY(id >= 0);
395
396 QString familyName = QString::fromLatin1(str: "QtBidiTestFont");
397 QFont font(familyName);
398 font.setPixelSize(18.0);
399 QCOMPARE(QFontInfo(font).family(), familyName);
400
401 QTextLayout layout(QLatin1String("Foobar"));
402 layout.setFont(font);
403 layout.setCacheEnabled(true);
404 layout.beginLayout();
405 layout.createLine();
406 layout.endLayout();
407
408 QList<QGlyphRun> glyphRuns = layout.glyphRuns();
409 QCOMPARE(glyphRuns.size(), 1);
410
411 QGlyphRun glyphs = glyphRuns.at(i: 0);
412
413 QRawFont rawFont = glyphs.rawFont();
414 QVERIFY(rawFont.isValid());
415 QCOMPARE(rawFont.familyName(), familyName);
416 QCOMPARE(rawFont.pixelSize(), 18.0);
417
418 QVector<quint32> expectedGlyphIndices;
419 expectedGlyphIndices << 44 << 83 << 83 << 70 << 69 << 86;
420
421 QCOMPARE(glyphs.glyphIndexes(), expectedGlyphIndices);
422
423 QVERIFY(fontDatabase.removeApplicationFont(id));
424}
425
426void tst_QRawFont::fontTable_data()
427{
428 QTest::addColumn<QByteArray>(name: "tagName");
429 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
430 QTest::addColumn<int>(name: "offset");
431 QTest::addColumn<quint32>(name: "expectedValue");
432
433 QTest::newRow(dataTag: "Head table, magic number, default hinting")
434 << QByteArray("head")
435 << QFont::PreferDefaultHinting
436 << 12
437 << (QSysInfo::ByteOrder == QSysInfo::BigEndian
438 ? 0x5F0F3CF5
439 : 0xF53C0F5F);
440
441 QTest::newRow(dataTag: "Head table, magic number, no hinting")
442 << QByteArray("head")
443 << QFont::PreferNoHinting
444 << 12
445 << (QSysInfo::ByteOrder == QSysInfo::BigEndian
446 ? 0x5F0F3CF5
447 : 0xF53C0F5F);
448
449 QTest::newRow(dataTag: "Head table, magic number, vertical hinting")
450 << QByteArray("head")
451 << QFont::PreferVerticalHinting
452 << 12
453 << (QSysInfo::ByteOrder == QSysInfo::BigEndian
454 ? 0x5F0F3CF5
455 : 0xF53C0F5F);
456
457 QTest::newRow(dataTag: "Head table, magic number, full hinting")
458 << QByteArray("head")
459 << QFont::PreferFullHinting
460 << 12
461 << (QSysInfo::ByteOrder == QSysInfo::BigEndian
462 ? 0x5F0F3CF5
463 : 0xF53C0F5F);
464}
465
466void tst_QRawFont::fontTable()
467{
468 QFETCH(QByteArray, tagName);
469 QFETCH(QFont::HintingPreference, hintingPreference);
470 QFETCH(int, offset);
471 QFETCH(quint32, expectedValue);
472
473 QRawFont font(testFont, 10, hintingPreference);
474 QVERIFY(font.isValid());
475
476 QByteArray table = font.fontTable(tagName);
477 QVERIFY(!table.isEmpty());
478
479 const quint32 *value = reinterpret_cast<const quint32 *>(table.constData() + offset);
480 QCOMPARE(*value, expectedValue);
481}
482
483typedef QList<QFontDatabase::WritingSystem> WritingSystemList;
484Q_DECLARE_METATYPE(WritingSystemList)
485
486void tst_QRawFont::supportedWritingSystems_data()
487{
488 QTest::addColumn<QString>(name: "fileName");
489 QTest::addColumn<WritingSystemList>(name: "writingSystems");
490 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
491
492 for (int hintingPreference=QFont::PreferDefaultHinting;
493 hintingPreference<=QFont::PreferFullHinting;
494 ++hintingPreference) {
495
496 QTest::newRow(qPrintable(QString::fromLatin1("testfont.ttf, hintingPreference=%1")
497 .arg(hintingPreference)))
498 << testFont
499 << (QList<QFontDatabase::WritingSystem>()
500 << QFontDatabase::Latin
501 << QFontDatabase::Hebrew
502 << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin
503 << QFont::HintingPreference(hintingPreference);
504
505 QTest::newRow(qPrintable(QString::fromLatin1("testfont_bold_italic.ttf, hintingPreference=%1")
506 .arg(hintingPreference)))
507 << testFontBoldItalic
508 << (QList<QFontDatabase::WritingSystem>()
509 << QFontDatabase::Latin
510 << QFontDatabase::Hebrew
511 << QFontDatabase::Devanagari
512 << QFontDatabase::Vietnamese) // Vietnamese uses same unicode bits as Latin
513 << QFont::HintingPreference(hintingPreference);
514 }
515}
516
517void tst_QRawFont::supportedWritingSystems()
518{
519 QFETCH(QString, fileName);
520 QFETCH(WritingSystemList, writingSystems);
521 QFETCH(QFont::HintingPreference, hintingPreference);
522
523 QRawFont font(fileName, 10, hintingPreference);
524 QVERIFY(font.isValid());
525
526 WritingSystemList actualWritingSystems = font.supportedWritingSystems();
527 QCOMPARE(actualWritingSystems.size(), writingSystems.size());
528
529 foreach (QFontDatabase::WritingSystem writingSystem, writingSystems)
530 QVERIFY(actualWritingSystems.contains(writingSystem));
531}
532
533void tst_QRawFont::supportsCharacter_data()
534{
535 QTest::addColumn<QString>(name: "fileName");
536 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
537 QTest::addColumn<QChar>(name: "character");
538 QTest::addColumn<bool>(name: "shouldBeSupported");
539
540 const char *fileNames[2] = {
541 "testfont.ttf",
542 "testfont_bold_italic.ttf"
543 };
544
545 for (int hintingPreference=QFont::PreferDefaultHinting;
546 hintingPreference<=QFont::PreferFullHinting;
547 ++hintingPreference) {
548
549 for (int i=0; i<2; ++i) {
550 QString fileName = QFINDTESTDATA(fileNames[i]);
551
552 // Latin text
553 for (char ch='!'; ch<='~'; ++ch) {
554 QString title = QString::fromLatin1(str: "%1, character=0x%2, hintingPreference=%3")
555 .arg(a: fileName).arg(a: QString::number(ch, base: 16)).arg(a: hintingPreference);
556
557 QTest::newRow(qPrintable(title))
558 << fileName
559 << QFont::HintingPreference(hintingPreference)
560 << QChar::fromLatin1(c: ch)
561 << true;
562 }
563
564 // Hebrew text
565 for (quint16 ch=0x05D0; ch<=0x05EA; ++ch) {
566 QString title = QString::fromLatin1(str: "%1, character=0x%2, hintingPreference=%3")
567 .arg(a: fileName).arg(a: QString::number(ch, base: 16)).arg(a: hintingPreference);
568
569 QTest::newRow(qPrintable(title))
570 << fileName
571 << QFont::HintingPreference(hintingPreference)
572 << QChar(ch)
573 << true;
574 }
575
576 QTest::newRow(qPrintable(QString::fromLatin1("Missing character, %1, hintingPreference=%2")
577 .arg(fileName).arg(hintingPreference)))
578 << fileName
579 << QFont::HintingPreference(hintingPreference)
580 << QChar(0xD8)
581 << false;
582 }
583 }
584}
585
586void tst_QRawFont::supportsCharacter()
587{
588 QFETCH(QString, fileName);
589 QFETCH(QFont::HintingPreference, hintingPreference);
590 QFETCH(QChar, character);
591 QFETCH(bool, shouldBeSupported);
592
593 QRawFont font(fileName, 10, hintingPreference);
594 QVERIFY(font.isValid());
595
596 QCOMPARE(font.supportsCharacter(character), shouldBeSupported);
597}
598
599void tst_QRawFont::supportsUcs4Character_data()
600{
601 QTest::addColumn<QString>(name: "fileName");
602 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
603 QTest::addColumn<quint32>(name: "ucs4");
604 QTest::addColumn<bool>(name: "shouldBeSupported");
605
606 // Gothic text
607 for (int hintingPreference=QFont::PreferDefaultHinting;
608 hintingPreference<=QFont::PreferFullHinting;
609 ++hintingPreference) {
610 for (quint32 ch=0x10330; ch<=0x1034A; ++ch) {
611 {
612 QString fileName = testFont;
613 QString title = QString::fromLatin1(str: "%1, character=0x%2, hintingPreference=%3")
614 .arg(a: fileName).arg(a: QString::number(ch, base: 16)).arg(a: hintingPreference);
615
616 QTest::newRow(qPrintable(title))
617 << fileName
618 << QFont::HintingPreference(hintingPreference)
619 << ch
620 << true;
621 }
622
623 {
624 QString fileName = testFontBoldItalic;
625 QString title = QString::fromLatin1(str: "%1, character=0x%2, hintingPreference=%3")
626 .arg(a: fileName).arg(a: QString::number(ch, base: 16)).arg(a: hintingPreference);
627
628 QTest::newRow(qPrintable(title))
629 << fileName
630 << QFont::HintingPreference(hintingPreference)
631 << ch
632 << false;
633 }
634 }
635 }
636}
637
638void tst_QRawFont::supportsUcs4Character()
639{
640 QFETCH(QString, fileName);
641 QFETCH(QFont::HintingPreference, hintingPreference);
642 QFETCH(quint32, ucs4);
643 QFETCH(bool, shouldBeSupported);
644
645 QRawFont font(fileName, 10, hintingPreference);
646 QVERIFY(font.isValid());
647
648 QCOMPARE(font.supportsCharacter(ucs4), shouldBeSupported);
649}
650
651void tst_QRawFont::fromFont_data()
652{
653 QTest::addColumn<QString>(name: "fileName");
654 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
655 QTest::addColumn<QString>(name: "familyName");
656 QTest::addColumn<QFontDatabase::WritingSystem>(name: "writingSystem");
657 QTest::addColumn<QFont::StyleStrategy>(name: "styleStrategy");
658
659 for (int i=QFont::PreferDefaultHinting; i<=QFont::PreferFullHinting; ++i) {
660 QString titleBase = QString::fromLatin1(str: "%2, hintingPreference=%1, writingSystem=%3")
661 .arg(a: i);
662 {
663 QString fileName = testFont;
664 QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any;
665
666 QString title = titleBase.arg(a: fileName).arg(a: writingSystem);
667 QTest::newRow(qPrintable(title))
668 << fileName
669 << QFont::HintingPreference(i)
670 << "QtBidiTestFont"
671 << writingSystem
672 << QFont::PreferDefault;
673 }
674
675 {
676 QString fileName = testFont;
677 QFontDatabase::WritingSystem writingSystem = QFontDatabase::Hebrew;
678
679 QString title = titleBase.arg(a: fileName).arg(a: writingSystem);
680 QTest::newRow(qPrintable(title))
681 << fileName
682 << QFont::HintingPreference(i)
683 << "QtBidiTestFont"
684 << writingSystem
685 << QFont::PreferDefault;
686 }
687
688 {
689 QString fileName = testFont;
690 QFontDatabase::WritingSystem writingSystem = QFontDatabase::Latin;
691
692 QString title = titleBase.arg(a: fileName).arg(a: writingSystem);
693 QTest::newRow(qPrintable(title))
694 << fileName
695 << QFont::HintingPreference(i)
696 << "QtBidiTestFont"
697 << writingSystem
698 << QFont::PreferDefault;
699 }
700 }
701
702 {
703 QString fileName = testFont;
704 QFontDatabase::WritingSystem writingSystem = QFontDatabase::Arabic;
705
706 QString title = QStringLiteral("No font merging + unsupported script");
707 QTest::newRow(qPrintable(title))
708 << fileName
709 << QFont::PreferDefaultHinting
710 << "QtBidiTestFont"
711 << writingSystem
712 << QFont::NoFontMerging;
713 }
714
715}
716
717void tst_QRawFont::fromFont()
718{
719 QFETCH(QString, fileName);
720 QFETCH(QFont::HintingPreference, hintingPreference);
721 QFETCH(QString, familyName);
722 QFETCH(QFontDatabase::WritingSystem, writingSystem);
723 QFETCH(QFont::StyleStrategy, styleStrategy);
724
725 QFontDatabase fontDatabase;
726 int id = fontDatabase.addApplicationFont(fileName);
727 QVERIFY(id >= 0);
728
729 QFont font(familyName);
730 font.setHintingPreference(hintingPreference);
731 font.setPixelSize(26.0);
732 if (styleStrategy != QFont::PreferDefault)
733 font.setStyleStrategy(styleStrategy);
734
735 QRawFont rawFont = QRawFont::fromFont(font, writingSystem);
736 QVERIFY(rawFont.isValid());
737
738 QCOMPARE(rawFont.familyName(), familyName);
739 QCOMPARE(rawFont.pixelSize(), 26.0);
740
741 QVERIFY(fontDatabase.removeApplicationFont(id));
742}
743
744void tst_QRawFont::copyConstructor_data()
745{
746 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
747
748 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
749 QTest::newRow(dataTag: "No hinting preference") << QFont::PreferNoHinting;
750 QTest::newRow(dataTag: "Vertical hinting preference") << QFont::PreferVerticalHinting;
751 QTest::newRow(dataTag: "Full hinting preference") << QFont::PreferFullHinting;
752}
753
754void tst_QRawFont::copyConstructor()
755{
756 QFETCH(QFont::HintingPreference, hintingPreference);
757
758 {
759 QString rawFontFamilyName;
760 qreal rawFontPixelSize;
761 qreal rawFontAscent;
762 qreal rawFontDescent;
763 int rawFontTableSize;
764
765 QRawFont outerRawFont;
766 {
767 QRawFont rawFont(testFont, 11, hintingPreference);
768 QVERIFY(rawFont.isValid());
769
770 rawFontFamilyName = rawFont.familyName();
771 rawFontPixelSize = rawFont.pixelSize();
772 rawFontAscent = rawFont.ascent();
773 rawFontDescent = rawFont.descent();
774 rawFontTableSize = rawFont.fontTable(tagName: "glyf").size();
775 QVERIFY(rawFontTableSize > 0);
776
777 {
778 QRawFont otherRawFont(rawFont);
779 QVERIFY(otherRawFont.isValid());
780 QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
781 QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
782 QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
783 QCOMPARE(otherRawFont.ascent(), rawFontAscent);
784 QCOMPARE(otherRawFont.descent(), rawFontDescent);
785 QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
786 }
787
788 {
789 QRawFont otherRawFont = rawFont;
790 QVERIFY(otherRawFont.isValid());
791 QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
792 QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
793 QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
794 QCOMPARE(otherRawFont.ascent(), rawFontAscent);
795 QCOMPARE(otherRawFont.descent(), rawFontDescent);
796 QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
797 }
798
799 outerRawFont = rawFont;
800 }
801
802 QVERIFY(outerRawFont.isValid());
803 QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize);
804 QCOMPARE(outerRawFont.familyName(), rawFontFamilyName);
805 QCOMPARE(outerRawFont.hintingPreference(), hintingPreference);
806 QCOMPARE(outerRawFont.ascent(), rawFontAscent);
807 QCOMPARE(outerRawFont.descent(), rawFontDescent);
808 QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize);
809 }
810}
811
812void tst_QRawFont::detach_data()
813{
814 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
815
816 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
817 QTest::newRow(dataTag: "No hinting preference") << QFont::PreferNoHinting;
818 QTest::newRow(dataTag: "Vertical hinting preference") << QFont::PreferVerticalHinting;
819 QTest::newRow(dataTag: "Full hinting preference") << QFont::PreferFullHinting;
820}
821
822void tst_QRawFont::detach()
823{
824 QFETCH(QFont::HintingPreference, hintingPreference);
825
826 {
827 QString rawFontFamilyName;
828 qreal rawFontPixelSize;
829 qreal rawFontAscent;
830 qreal rawFontDescent;
831 int rawFontTableSize;
832
833 QRawFont outerRawFont;
834 {
835 QRawFont rawFont(testFont, 11, hintingPreference);
836 QVERIFY(rawFont.isValid());
837
838 rawFontFamilyName = rawFont.familyName();
839 rawFontPixelSize = rawFont.pixelSize();
840 rawFontAscent = rawFont.ascent();
841 rawFontDescent = rawFont.descent();
842 rawFontTableSize = rawFont.fontTable(tagName: "glyf").size();
843 QVERIFY(rawFontTableSize > 0);
844
845 {
846 QRawFont otherRawFont(rawFont);
847
848 otherRawFont.loadFromFile(fileName: testFont, pixelSize: rawFontPixelSize, hintingPreference);
849
850 QVERIFY(otherRawFont.isValid());
851 QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
852 QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
853 QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
854 QCOMPARE(otherRawFont.ascent(), rawFontAscent);
855 QCOMPARE(otherRawFont.descent(), rawFontDescent);
856 QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
857 }
858
859 {
860 QRawFont otherRawFont = rawFont;
861
862 otherRawFont.loadFromFile(fileName: testFont, pixelSize: rawFontPixelSize, hintingPreference);
863
864 QVERIFY(otherRawFont.isValid());
865 QCOMPARE(otherRawFont.pixelSize(), rawFontPixelSize);
866 QCOMPARE(otherRawFont.familyName(), rawFontFamilyName);
867 QCOMPARE(otherRawFont.hintingPreference(), hintingPreference);
868 QCOMPARE(otherRawFont.ascent(), rawFontAscent);
869 QCOMPARE(otherRawFont.descent(), rawFontDescent);
870 QCOMPARE(otherRawFont.fontTable("glyf").size(), rawFontTableSize);
871 }
872
873 outerRawFont = rawFont;
874
875 rawFont.loadFromFile(fileName: testFont, pixelSize: rawFontPixelSize, hintingPreference);
876 }
877
878 QVERIFY(outerRawFont.isValid());
879 QCOMPARE(outerRawFont.pixelSize(), rawFontPixelSize);
880 QCOMPARE(outerRawFont.familyName(), rawFontFamilyName);
881 QCOMPARE(outerRawFont.hintingPreference(), hintingPreference);
882 QCOMPARE(outerRawFont.ascent(), rawFontAscent);
883 QCOMPARE(outerRawFont.descent(), rawFontDescent);
884 QCOMPARE(outerRawFont.fontTable("glyf").size(), rawFontTableSize);
885 }
886}
887
888void tst_QRawFont::unsupportedWritingSystem_data()
889{
890 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
891
892 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
893 QTest::newRow(dataTag: "No hinting preference") << QFont::PreferNoHinting;
894 QTest::newRow(dataTag: "Vertical hinting preference") << QFont::PreferVerticalHinting;
895 QTest::newRow(dataTag: "Full hinting preference") << QFont::PreferFullHinting;
896}
897
898void tst_QRawFont::unsupportedWritingSystem()
899{
900 QFETCH(QFont::HintingPreference, hintingPreference);
901
902 QFontDatabase fontDatabase;
903 int id = fontDatabase.addApplicationFont(fileName: testFont);
904
905 QFont font("QtBidiTestFont");
906 font.setHintingPreference(hintingPreference);
907 font.setPixelSize(12.0);
908
909 QRawFont rawFont = QRawFont::fromFont(font, writingSystem: QFontDatabase::Any);
910 QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont"));
911 QCOMPARE(rawFont.pixelSize(), 12.0);
912
913 rawFont = QRawFont::fromFont(font, writingSystem: QFontDatabase::Hebrew);
914 QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont"));
915 QCOMPARE(rawFont.pixelSize(), 12.0);
916
917 QString arabicText = QFontDatabase::writingSystemSample(writingSystem: QFontDatabase::Arabic).simplified().remove(c: QLatin1Char(' '));
918
919 QTextLayout layout;
920 layout.setFont(font);
921 layout.setText(arabicText);
922 layout.setCacheEnabled(true);
923 layout.beginLayout();
924 layout.createLine();
925 layout.endLayout();
926
927 QList<QGlyphRun> glyphRuns = layout.glyphRuns();
928 QCOMPARE(glyphRuns.size(), 1);
929
930 QGlyphRun glyphs = glyphRuns.at(i: 0);
931 QRawFont layoutFont = glyphs.rawFont();
932 QVERIFY(layoutFont.familyName() != QString::fromLatin1("QtBidiTestFont"));
933 QCOMPARE(layoutFont.pixelSize(), 12.0);
934
935 rawFont = QRawFont::fromFont(font, writingSystem: QFontDatabase::Arabic);
936 QCOMPARE(rawFont.familyName(), layoutFont.familyName());
937 QCOMPARE(rawFont.pixelSize(), 12.0);
938
939 fontDatabase.removeApplicationFont(id);
940}
941
942void tst_QRawFont::rawFontSetPixelSize_data()
943{
944 QTest::addColumn<QFont::HintingPreference>(name: "hintingPreference");
945
946 QTest::newRow(dataTag: "Default hinting preference") << QFont::PreferDefaultHinting;
947 QTest::newRow(dataTag: "No hinting preference") << QFont::PreferNoHinting;
948 QTest::newRow(dataTag: "Vertical hinting preference") << QFont::PreferVerticalHinting;
949 QTest::newRow(dataTag: "Full hinting preference") << QFont::PreferFullHinting;
950}
951
952void tst_QRawFont::rawFontSetPixelSize()
953{
954 QFETCH(QFont::HintingPreference, hintingPreference);
955
956 QTextLayout layout("Foobar");
957
958 QFont font = layout.font();
959 font.setHintingPreference(hintingPreference);
960 font.setPixelSize(12);
961 layout.setFont(font);
962
963 layout.setCacheEnabled(true);
964 layout.beginLayout();
965 layout.createLine();
966 layout.endLayout();
967
968 QGlyphRun glyphs = layout.glyphRuns().at(i: 0);
969 QRawFont rawFont = glyphs.rawFont();
970 QCOMPARE(rawFont.pixelSize(), 12.0);
971
972 rawFont.setPixelSize(24);
973 QCOMPARE(rawFont.pixelSize(), 24.0);
974}
975
976void tst_QRawFont::multipleRawFontsFromData()
977{
978 QFile file(testFont);
979 QRawFont testFont;
980 if (file.open(flags: QIODevice::ReadOnly)) {
981 testFont.loadFromData(fontData: file.readAll(), pixelSize: 11, hintingPreference: QFont::PreferDefaultHinting);
982 file.close();
983 }
984 file.setFileName(testFontBoldItalic);
985 QRawFont testFontBoldItalic;
986 if (file.open(flags: QIODevice::ReadOnly))
987 testFontBoldItalic.loadFromData(fontData: file.readAll(), pixelSize: 11, hintingPreference: QFont::PreferDefaultHinting);
988
989 QVERIFY(testFont.familyName() != (testFontBoldItalic.familyName())
990 || testFont.style() != (testFontBoldItalic.style()));
991}
992
993void tst_QRawFont::rawFontFromInvalidData()
994{
995 QByteArray invalidData("foobar");
996 QRawFont font;
997 font.loadFromData(fontData: invalidData, pixelSize: 10, hintingPreference: QFont::PreferDefaultHinting);
998
999 QVERIFY(!font.isValid());
1000
1001 invalidData.fill(c: char(255), size: 1024);
1002 font.loadFromData(fontData: invalidData, pixelSize: 10, hintingPreference: QFont::PreferDefaultHinting);
1003
1004 QVERIFY(!font.isValid());
1005}
1006
1007#define FUZZY_LTEQ(X, Y) (X < Y || qFuzzyCompare(X, Y))
1008
1009void tst_QRawFont::kernedAdvances()
1010{
1011 const int emSquareSize = 1000;
1012 const qreal pixelSize = 16.0;
1013 const int underScoreAW = 500;
1014 const int underscoreTwoKerning = -500;
1015 const qreal errorMargin = 1.0 / 16.0; // Fixed point error margin
1016
1017 QRawFont font(testFont, pixelSize);
1018 QVERIFY(font.isValid());
1019
1020 QVector<quint32> glyphIndexes = font.glyphIndexesForString(QStringLiteral("__"));
1021 QCOMPARE(glyphIndexes.size(), 2);
1022
1023 QVector<QPointF> advances = font.advancesForGlyphIndexes(glyphIndexes, layoutFlags: QRawFont::KernedAdvances);
1024 QCOMPARE(advances.size(), 2);
1025
1026 qreal expectedAdvanceWidth = pixelSize * underScoreAW / emSquareSize;
1027 QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin));
1028
1029 glyphIndexes = font.glyphIndexesForString(QStringLiteral("_2"));
1030 QCOMPARE(glyphIndexes.size(), 2);
1031
1032 advances = font.advancesForGlyphIndexes(glyphIndexes, layoutFlags: QRawFont::KernedAdvances);
1033 QCOMPARE(advances.size(), 2);
1034
1035 expectedAdvanceWidth = pixelSize * (underScoreAW + underscoreTwoKerning) / emSquareSize;
1036 QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin));
1037}
1038
1039void tst_QRawFont::fallbackFontsOrder()
1040{
1041 QFontDatabase fontDatabase;
1042 int id = fontDatabase.addApplicationFont(fileName: testFont);
1043
1044 QFont font("QtBidiTestFont");
1045 font.setPixelSize(12.0);
1046
1047 QString arabicText = QFontDatabase::writingSystemSample(writingSystem: QFontDatabase::Arabic);
1048
1049 // If this fails, then the writing system sample has changed and we need to create
1050 // a new text containing both a space and Arabic characters.
1051 QVERIFY(arabicText.contains(QLatin1Char(' ')));
1052
1053 QTextLayout layout;
1054 layout.setFont(font);
1055 layout.setText(arabicText);
1056 layout.setCacheEnabled(true);
1057 layout.beginLayout();
1058 layout.createLine();
1059 layout.endLayout();
1060
1061 QList<QGlyphRun> glyphRuns = layout.glyphRuns();
1062
1063#ifdef Q_OS_ANDROID
1064 QEXPECT_FAIL("", "QTBUG-69217", Continue);
1065#endif
1066 // Since QtBidiTestFont does not support Arabic nor the space, both should map to
1067 // the same font. If this fails, it is an indication that the list of fallbacks fonts
1068 // is not sorted by writing system support.
1069 QCOMPARE(glyphRuns.size(), 1);
1070
1071 fontDatabase.removeApplicationFont(id);
1072}
1073
1074void tst_QRawFont::qtbug65923_partal_clone_data()
1075{
1076 QTest::addColumn<bool>(name: "shouldClone");
1077
1078 QTest::newRow(dataTag: "Without cloning font engine") << false;
1079 QTest::newRow(dataTag: "Cloning font engine") << true;
1080}
1081
1082void tst_QRawFont::qtbug65923_partal_clone()
1083{
1084 QFile file(testFont);
1085 file.open(flags: QIODevice::ReadOnly);
1086 QByteArray fontData = file.readAll();
1087
1088 QRawFont outerFont;
1089
1090 {
1091 QRawFont innerFont(fontData, 16, QFont::PreferDefaultHinting);
1092
1093 QFETCH(bool, shouldClone);
1094 if (shouldClone) {
1095 // This will trigger QFontEngine::cloneWithSize
1096 innerFont.setPixelSize(innerFont.pixelSize() + 1);
1097 }
1098
1099 outerFont = innerFont;
1100 }
1101
1102 // This will detach if data is shared with the raw font. If the raw font has
1103 // a naked reference to the data, without informing Qt of it via the ref count
1104 // of the byte array, this will result in clearing 'live' data.
1105 fontData.fill(c: '\0');
1106
1107 QVERIFY(!outerFont.boundingRect(42).isEmpty());
1108}
1109
1110#endif // QT_NO_RAWFONT
1111
1112QTEST_MAIN(tst_QRawFont)
1113#include "tst_qrawfont.moc"
1114
1115

source code of qtbase/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp