1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qfontdatabase.h"
5#include "qfontdatabase_p.h"
6#include "qloggingcategory.h"
7#include "qalgorithms.h"
8#include "qguiapplication.h"
9#include "qvarlengtharray.h" // here or earlier - workaround for VC++6
10#include "qthread.h"
11#include "qmutex.h"
12#include "qfile.h"
13#include "qfileinfo.h"
14#include "qfontengine_p.h"
15#include <qpa/qplatformintegration.h>
16
17#include <QtGui/private/qguiapplication_p.h>
18#include <qpa/qplatformfontdatabase.h>
19#include <qpa/qplatformtheme.h>
20
21#include <QtCore/qcache.h>
22#include <QtCore/qmath.h>
23
24#include <stdlib.h>
25#include <algorithm>
26
27#include <qtgui_tracepoints_p.h>
28
29#ifdef Q_OS_WIN
30#include <QtGui/private/qwindowsfontdatabasebase_p.h>
31#endif
32
33QT_BEGIN_NAMESPACE
34
35using namespace Qt::StringLiterals;
36
37Q_LOGGING_CATEGORY(lcFontDb, "qt.text.font.db")
38Q_LOGGING_CATEGORY(lcFontMatch, "qt.text.font.match")
39
40#define SMOOTH_SCALABLE 0xffff
41
42#if defined(QT_BUILD_INTERNAL)
43bool qt_enable_test_font = false;
44
45Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value)
46{
47 qt_enable_test_font = value;
48}
49#endif
50
51Q_TRACE_POINT(qtgui, QFontDatabase_loadEngine, const QString &families, int pointSize);
52Q_TRACE_POINT(qtgui, QFontDatabasePrivate_addAppFont, const QString &fileName);
53Q_TRACE_POINT(qtgui, QFontDatabase_addApplicationFont, const QString &fileName);
54Q_TRACE_POINT(qtgui, QFontDatabase_load, const QString &family, int pointSize);
55
56static int getFontWeight(const QString &weightString)
57{
58 QString s = weightString.toLower();
59
60 // Order here is important. We want to match the common cases first, but we
61 // must also take care to acknowledge the cost of our tests.
62 //
63 // As a result, we test in two orders; the order of commonness, and the
64 // order of "expense".
65 //
66 // A simple string test is the cheapest, so let's do that first.
67 // Test in decreasing order of commonness
68 if (s == "normal"_L1 || s == "regular"_L1)
69 return QFont::Normal;
70 if (s == "bold"_L1)
71 return QFont::Bold;
72 if (s == "semibold"_L1 || s == "semi bold"_L1 || s == "demibold"_L1 || s == "demi bold"_L1)
73 return QFont::DemiBold;
74 if (s == "medium"_L1)
75 return QFont::Medium;
76 if (s == "black"_L1)
77 return QFont::Black;
78 if (s == "light"_L1)
79 return QFont::Light;
80 if (s == "thin"_L1)
81 return QFont::Thin;
82 const QStringView s2 = QStringView{s}.mid(pos: 2);
83 if (s.startsWith(s: "ex"_L1) || s.startsWith(s: "ul"_L1)) {
84 if (s2 == "tralight"_L1 || s == "tra light"_L1)
85 return QFont::ExtraLight;
86 if (s2 == "trabold"_L1 || s2 == "tra bold"_L1)
87 return QFont::ExtraBold;
88 }
89
90 // Next up, let's see if contains() matches: slightly more expensive, but
91 // still fast enough.
92 if (s.contains(s: "bold"_L1)) {
93 if (s.contains(s: "demi"_L1))
94 return QFont::DemiBold;
95 return QFont::Bold;
96 }
97 if (s.contains(s: "thin"_L1))
98 return QFont::Thin;
99 if (s.contains(s: "light"_L1))
100 return QFont::Light;
101 if (s.contains(s: "black"_L1))
102 return QFont::Black;
103
104 // Now, we perform string translations & comparisons with those.
105 // These are (very) slow compared to simple string ops, so we do these last.
106 // As using translated values for such things is not very common, this should
107 // not be too bad.
108 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Normal", disambiguation: "The Normal or Regular font weight"), cs: Qt::CaseInsensitive) == 0)
109 return QFont::Normal;
110 const QString translatedBold = QCoreApplication::translate(context: "QFontDatabase", key: "Bold").toLower();
111 if (s == translatedBold)
112 return QFont::Bold;
113 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Demi Bold"), cs: Qt::CaseInsensitive) == 0)
114 return QFont::DemiBold;
115 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Medium", disambiguation: "The Medium font weight"), cs: Qt::CaseInsensitive) == 0)
116 return QFont::Medium;
117 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Black"), cs: Qt::CaseInsensitive) == 0)
118 return QFont::Black;
119 const QString translatedLight = QCoreApplication::translate(context: "QFontDatabase", key: "Light").toLower();
120 if (s == translatedLight)
121 return QFont::Light;
122 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Thin"), cs: Qt::CaseInsensitive) == 0)
123 return QFont::Thin;
124 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Extra Light"), cs: Qt::CaseInsensitive) == 0)
125 return QFont::ExtraLight;
126 if (s.compare(s: QCoreApplication::translate(context: "QFontDatabase", key: "Extra Bold"), cs: Qt::CaseInsensitive) == 0)
127 return QFont::ExtraBold;
128
129 // And now the contains() checks for the translated strings.
130 //: The word for "Extra" as in "Extra Bold, Extra Thin" used as a pattern for string searches
131 const QString translatedExtra = QCoreApplication::translate(context: "QFontDatabase", key: "Extra").toLower();
132 if (s.contains(s: translatedBold)) {
133 //: The word for "Demi" as in "Demi Bold" used as a pattern for string searches
134 QString translatedDemi = QCoreApplication::translate(context: "QFontDatabase", key: "Demi").toLower();
135 if (s .contains(s: translatedDemi))
136 return QFont::DemiBold;
137 if (s.contains(s: translatedExtra))
138 return QFont::ExtraBold;
139 return QFont::Bold;
140 }
141
142 if (s.contains(s: translatedLight)) {
143 if (s.contains(s: translatedExtra))
144 return QFont::ExtraLight;
145 return QFont::Light;
146 }
147 return QFont::Normal;
148}
149
150
151QtFontStyle::Key::Key(const QString &styleString)
152 : style(QFont::StyleNormal), weight(QFont::Normal), stretch(0)
153{
154 weight = getFontWeight(weightString: styleString);
155
156 if (!styleString.isEmpty()) {
157 // First the straightforward no-translation checks, these are fast.
158 if (styleString.contains(s: "Italic"_L1))
159 style = QFont::StyleItalic;
160 else if (styleString.contains(s: "Oblique"_L1))
161 style = QFont::StyleOblique;
162
163 // Then the translation checks. These aren't as fast.
164 else if (styleString.contains(s: QCoreApplication::translate(context: "QFontDatabase", key: "Italic")))
165 style = QFont::StyleItalic;
166 else if (styleString.contains(s: QCoreApplication::translate(context: "QFontDatabase", key: "Oblique")))
167 style = QFont::StyleOblique;
168 }
169}
170
171QtFontSize *QtFontStyle::pixelSize(unsigned short size, bool add)
172{
173 for (int i = 0; i < count; i++) {
174 if (pixelSizes[i].pixelSize == size)
175 return pixelSizes + i;
176 }
177 if (!add)
178 return nullptr;
179
180 if (!pixelSizes) {
181 // Most style have only one font size, we avoid waisting memory
182 QtFontSize *newPixelSizes = (QtFontSize *)malloc(size: sizeof(QtFontSize));
183 Q_CHECK_PTR(newPixelSizes);
184 pixelSizes = newPixelSizes;
185 } else if (!(count % 8) || count == 1) {
186 QtFontSize *newPixelSizes = (QtFontSize *)
187 realloc(ptr: pixelSizes,
188 size: (((count+8) >> 3) << 3) * sizeof(QtFontSize));
189 Q_CHECK_PTR(newPixelSizes);
190 pixelSizes = newPixelSizes;
191 }
192 pixelSizes[count].pixelSize = size;
193 pixelSizes[count].handle = nullptr;
194 return pixelSizes + (count++);
195}
196
197QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, const QString &styleName, bool create)
198{
199 int pos = 0;
200 for (; pos < count; pos++) {
201 bool hasStyleName = !styleName.isEmpty(); // search styleName first if available
202 if (hasStyleName && !styles[pos]->styleName.isEmpty()) {
203 if (styles[pos]->styleName == styleName)
204 return styles[pos];
205 } else {
206 if (styles[pos]->key == key)
207 return styles[pos];
208 }
209 }
210 if (!create)
211 return nullptr;
212
213// qDebug("adding key (weight=%d, style=%d, oblique=%d stretch=%d) at %d", key.weight, key.style, key.oblique, key.stretch, pos);
214 if (!(count % 8)) {
215 QtFontStyle **newStyles = (QtFontStyle **)
216 realloc(ptr: styles, size: (((count+8) >> 3) << 3) * sizeof(QtFontStyle *));
217 Q_CHECK_PTR(newStyles);
218 styles = newStyles;
219 }
220
221 QtFontStyle *style = new QtFontStyle(key);
222 style->styleName = styleName;
223 styles[pos] = style;
224 count++;
225 return styles[pos];
226}
227
228QtFontFoundry *QtFontFamily::foundry(const QString &f, bool create)
229{
230 if (f.isNull() && count == 1)
231 return foundries[0];
232
233 for (int i = 0; i < count; i++) {
234 if (foundries[i]->name.compare(s: f, cs: Qt::CaseInsensitive) == 0)
235 return foundries[i];
236 }
237 if (!create)
238 return nullptr;
239
240 if (!(count % 8)) {
241 QtFontFoundry **newFoundries = (QtFontFoundry **)
242 realloc(ptr: foundries,
243 size: (((count+8) >> 3) << 3) * sizeof(QtFontFoundry *));
244 Q_CHECK_PTR(newFoundries);
245 foundries = newFoundries;
246 }
247
248 foundries[count] = new QtFontFoundry(f);
249 return foundries[count++];
250}
251
252static inline bool equalsCaseInsensitive(const QString &a, const QString &b)
253{
254 return a.size() == b.size() && a.compare(s: b, cs: Qt::CaseInsensitive) == 0;
255}
256
257bool QtFontFamily::matchesFamilyName(const QString &familyName) const
258{
259 return equalsCaseInsensitive(a: name, b: familyName) || aliases.contains(str: familyName, cs: Qt::CaseInsensitive);
260}
261
262bool QtFontFamily::ensurePopulated()
263{
264 if (populated)
265 return true;
266
267 QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamily(familyName: name);
268 return populated;
269}
270
271void QFontDatabasePrivate::clearFamilies()
272{
273 while (count--)
274 delete families[count];
275 ::free(ptr: families);
276 families = nullptr;
277 count = 0;
278
279 for (auto &font : applicationFonts)
280 font.properties.clear(); // Unpopulate
281
282 populated = false;
283 // don't clear the memory fonts!
284}
285
286void QFontDatabasePrivate::invalidate()
287{
288 qCDebug(lcFontDb) << "Invalidating font database";
289
290 QFontCache::instance()->clear();
291
292 fallbacksCache.clear();
293 clearFamilies();
294 QGuiApplicationPrivate::platformIntegration()->fontDatabase()->invalidate();
295 emit qGuiApp->fontDatabaseChanged();
296}
297
298QtFontFamily *QFontDatabasePrivate::family(const QString &f, FamilyRequestFlags flags)
299{
300 QtFontFamily *fam = nullptr;
301
302 int low = 0;
303 int high = count;
304 int pos = count / 2;
305 int res = 1;
306 if (count) {
307 while ((res = families[pos]->name.compare(s: f, cs: Qt::CaseInsensitive)) && pos != low) {
308 if (res > 0)
309 high = pos;
310 else
311 low = pos;
312 pos = (high + low) / 2;
313 }
314 if (!res)
315 fam = families[pos];
316 }
317
318 if (!fam && (flags & EnsureCreated)) {
319 if (res < 0)
320 pos++;
321
322 // qDebug() << "adding family " << f.toLatin1() << " at " << pos << " total=" << count;
323 if (!(count % 8)) {
324 QtFontFamily **newFamilies = (QtFontFamily **)
325 realloc(ptr: families,
326 size: (((count+8) >> 3) << 3) * sizeof(QtFontFamily *));
327 Q_CHECK_PTR(newFamilies);
328 families = newFamilies;
329 }
330
331 QtFontFamily *family = new QtFontFamily(f);
332 memmove(dest: families + pos + 1, src: families + pos, n: (count-pos)*sizeof(QtFontFamily *));
333 families[pos] = family;
334 count++;
335
336 fam = families[pos];
337 }
338
339 if (fam && (flags & EnsurePopulated)) {
340 if (!fam->ensurePopulated())
341 return nullptr;
342 }
343
344 return fam;
345}
346
347
348
349static const int scriptForWritingSystem[] = {
350 QChar::Script_Common, // Any
351 QChar::Script_Latin, // Latin
352 QChar::Script_Greek, // Greek
353 QChar::Script_Cyrillic, // Cyrillic
354 QChar::Script_Armenian, // Armenian
355 QChar::Script_Hebrew, // Hebrew
356 QChar::Script_Arabic, // Arabic
357 QChar::Script_Syriac, // Syriac
358 QChar::Script_Thaana, // Thaana
359 QChar::Script_Devanagari, // Devanagari
360 QChar::Script_Bengali, // Bengali
361 QChar::Script_Gurmukhi, // Gurmukhi
362 QChar::Script_Gujarati, // Gujarati
363 QChar::Script_Oriya, // Oriya
364 QChar::Script_Tamil, // Tamil
365 QChar::Script_Telugu, // Telugu
366 QChar::Script_Kannada, // Kannada
367 QChar::Script_Malayalam, // Malayalam
368 QChar::Script_Sinhala, // Sinhala
369 QChar::Script_Thai, // Thai
370 QChar::Script_Lao, // Lao
371 QChar::Script_Tibetan, // Tibetan
372 QChar::Script_Myanmar, // Myanmar
373 QChar::Script_Georgian, // Georgian
374 QChar::Script_Khmer, // Khmer
375 QChar::Script_Han, // SimplifiedChinese
376 QChar::Script_Han, // TraditionalChinese
377 QChar::Script_Han, // Japanese
378 QChar::Script_Hangul, // Korean
379 QChar::Script_Latin, // Vietnamese
380 QChar::Script_Common, // Symbol
381 QChar::Script_Ogham, // Ogham
382 QChar::Script_Runic, // Runic
383 QChar::Script_Nko // Nko
384};
385
386static_assert(sizeof(scriptForWritingSystem) / sizeof(scriptForWritingSystem[0]) == QFontDatabase::WritingSystemsCount);
387
388Q_GUI_EXPORT int qt_script_for_writing_system(QFontDatabase::WritingSystem writingSystem)
389{
390 return scriptForWritingSystem[writingSystem];
391}
392
393
394/*!
395 \internal
396
397 Tests if the given family \a family supports writing system \a writingSystem,
398 including the special case for Han script mapping to several subsequent writing systems
399*/
400static bool familySupportsWritingSystem(QtFontFamily *family, size_t writingSystem)
401{
402 Q_ASSERT(family != nullptr);
403 Q_ASSERT(writingSystem != QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount);
404
405 size_t ws = writingSystem;
406 do {
407 if ((family->writingSystems[ws] & QtFontFamily::Supported) != 0)
408 return true;
409 } while (writingSystem >= QFontDatabase::SimplifiedChinese && writingSystem <= QFontDatabase::Japanese && ++ws <= QFontDatabase::Japanese);
410
411 return false;
412}
413
414Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script)
415{
416 return QFontDatabase::WritingSystem(std::find(first: scriptForWritingSystem,
417 last: scriptForWritingSystem + QFontDatabase::WritingSystemsCount,
418 val: script) - scriptForWritingSystem);
419}
420
421/*!
422 \internal
423
424 This makes sense of the font family name:
425
426 if the family name contains a '[' and a ']', then we take the text
427 between the square brackets as the foundry, and the text before the
428 square brackets as the family (ie. "Arial [Monotype]")
429*/
430static void parseFontName(const QString &name, QString &foundry, QString &family)
431{
432 int i = name.indexOf(c: u'[');
433 int li = name.lastIndexOf(c: u']');
434 if (i >= 0 && li >= 0 && i < li) {
435 foundry = name.mid(position: i + 1, n: li - i - 1);
436 if (i > 0 && name[i - 1] == u' ')
437 i--;
438 family = name.left(n: i);
439 } else {
440 foundry.clear();
441 family = name;
442 }
443
444 // capitalize the family/foundry names
445 bool space = true;
446 QChar *s = family.data();
447 int len = family.size();
448 while(len--) {
449 if (space) *s = s->toUpper();
450 space = s->isSpace();
451 ++s;
452 }
453
454 space = true;
455 s = foundry.data();
456 len = foundry.size();
457 while(len--) {
458 if (space) *s = s->toUpper();
459 space = s->isSpace();
460 ++s;
461 }
462}
463
464
465struct QtFontDesc
466{
467 inline QtFontDesc() : family(nullptr), foundry(nullptr), style(nullptr), size(nullptr) {}
468 QtFontFamily *family;
469 QtFontFoundry *foundry;
470 QtFontStyle *style;
471 QtFontSize *size;
472};
473
474static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDef *fontDef, bool multi)
475{
476 QString family;
477 family = desc.family->name;
478 if (! desc.foundry->name.isEmpty() && desc.family->count > 1)
479 family += " ["_L1 + desc.foundry->name + u']';
480 fontDef->families = QStringList(family);
481
482 if (desc.style->smoothScalable
483 || QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable()
484 || (desc.style->bitmapScalable && (request.styleStrategy & QFont::PreferMatch))) {
485 fontDef->pixelSize = request.pixelSize;
486 } else {
487 fontDef->pixelSize = desc.size->pixelSize;
488 }
489 fontDef->pointSize = request.pointSize;
490
491 fontDef->styleHint = request.styleHint;
492 fontDef->styleStrategy = request.styleStrategy;
493
494 if (!multi)
495 fontDef->weight = desc.style->key.weight;
496 if (!multi)
497 fontDef->style = desc.style->key.style;
498 fontDef->fixedPitch = desc.family->fixedPitch;
499 fontDef->ignorePitch = false;
500}
501
502static QStringList familyList(const QFontDef &req)
503{
504 // list of families to try
505 QStringList family_list;
506
507 family_list << req.families;
508 // append the substitute list for each family in family_list
509 for (int i = 0, size = family_list.size(); i < size; ++i)
510 family_list += QFont::substitutes(family_list.at(i));
511
512 return family_list;
513}
514
515Q_GLOBAL_STATIC(QRecursiveMutex, fontDatabaseMutex)
516
517// used in qguiapplication.cpp
518void qt_cleanupFontDatabase()
519{
520 auto *db = QFontDatabasePrivate::instance();
521 db->fallbacksCache.clear();
522 db->clearFamilies();
523}
524
525// used in qfont.cpp
526QRecursiveMutex *qt_fontdatabase_mutex()
527{
528 return fontDatabaseMutex();
529}
530
531QFontDatabasePrivate *QFontDatabasePrivate::instance()
532{
533 static QFontDatabasePrivate instance;
534 return &instance;
535}
536
537void qt_registerFont(const QString &familyName, const QString &stylename,
538 const QString &foundryname, int weight,
539 QFont::Style style, int stretch, bool antialiased,
540 bool scalable, int pixelSize, bool fixedPitch,
541 const QSupportedWritingSystems &writingSystems, void *handle)
542{
543 auto *d = QFontDatabasePrivate::instance();
544 qCDebug(lcFontDb) << "Adding font: familyName" << familyName << "stylename" << stylename << "weight" << weight
545 << "style" << style << "pixelSize" << pixelSize << "antialiased" << antialiased << "fixed" << fixedPitch;
546 QtFontStyle::Key styleKey;
547 styleKey.style = style;
548 styleKey.weight = weight;
549 styleKey.stretch = stretch;
550 QtFontFamily *f = d->family(f: familyName, flags: QFontDatabasePrivate::EnsureCreated);
551 f->fixedPitch = fixedPitch;
552
553 for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
554 if (writingSystems.supported(QFontDatabase::WritingSystem(i)))
555 f->writingSystems[i] = QtFontFamily::Supported;
556 }
557
558 QtFontFoundry *foundry = f->foundry(f: foundryname, create: true);
559 QtFontStyle *fontStyle = foundry->style(key: styleKey, styleName: stylename, create: true);
560 fontStyle->smoothScalable = scalable;
561 fontStyle->antialiased = antialiased;
562 QtFontSize *size = fontStyle->pixelSize(size: pixelSize ? pixelSize : SMOOTH_SCALABLE, add: true);
563 if (size->handle) {
564 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
565 if (integration)
566 integration->fontDatabase()->releaseHandle(handle: size->handle);
567 }
568 size->handle = handle;
569 f->populated = true;
570}
571
572void qt_registerFontFamily(const QString &familyName)
573{
574 qCDebug(lcFontDb) << "Registering family" << familyName;
575
576 // Create uninitialized/unpopulated family
577 QFontDatabasePrivate::instance()->family(f: familyName, flags: QFontDatabasePrivate::EnsureCreated);
578}
579
580void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias)
581{
582 if (alias.isEmpty())
583 return;
584
585 qCDebug(lcFontDb) << "Registering alias" << alias << "to family" << familyName;
586
587 auto *d = QFontDatabasePrivate::instance();
588 QtFontFamily *f = d->family(f: familyName, flags: QFontDatabasePrivate::RequestFamily);
589 if (!f)
590 return;
591
592 if (f->aliases.contains(str: alias, cs: Qt::CaseInsensitive))
593 return;
594
595 f->aliases.push_back(t: alias);
596}
597
598QString qt_resolveFontFamilyAlias(const QString &alias)
599{
600 if (!alias.isEmpty()) {
601 const auto *d = QFontDatabasePrivate::instance();
602 for (int i = 0; i < d->count; ++i)
603 if (d->families[i]->matchesFamilyName(familyName: alias))
604 return d->families[i]->name;
605 }
606 return alias;
607}
608
609bool qt_isFontFamilyPopulated(const QString &familyName)
610{
611 auto *d = QFontDatabasePrivate::instance();
612 QtFontFamily *f = d->family(f: familyName, flags: QFontDatabasePrivate::RequestFamily);
613 return f != nullptr && f->populated;
614}
615
616/*!
617 Returns a list of alternative fonts for the specified \a family and
618 \a style and \a script using the \a styleHint given.
619
620 Default implementation returns a list of fonts for which \a style and \a script support
621 has been reported during the font database population.
622*/
623QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
624{
625 Q_UNUSED(family);
626 Q_UNUSED(styleHint);
627
628 QStringList preferredFallbacks;
629 QStringList otherFallbacks;
630
631 auto writingSystem = qt_writing_system_for_script(script);
632 if (writingSystem >= QFontDatabase::WritingSystemsCount)
633 writingSystem = QFontDatabase::Any;
634
635 auto *db = QFontDatabasePrivate::instance();
636 for (int i = 0; i < db->count; ++i) {
637 QtFontFamily *f = db->families[i];
638
639 f->ensurePopulated();
640
641 if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(family: f, writingSystem))
642 continue;
643
644 for (int j = 0; j < f->count; ++j) {
645 QtFontFoundry *foundry = f->foundries[j];
646
647 for (int k = 0; k < foundry->count; ++k) {
648 QString name = foundry->name.isEmpty()
649 ? f->name
650 : f->name + " ["_L1 + foundry->name + u']';
651 if (style == foundry->styles[k]->key.style)
652 preferredFallbacks.append(t: name);
653 else
654 otherFallbacks.append(t: name);
655 }
656 }
657 }
658
659 return preferredFallbacks + otherFallbacks;
660}
661
662static QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
663{
664 QMutexLocker locker(fontDatabaseMutex());
665 auto *db = QFontDatabasePrivate::ensureFontDatabase();
666
667 const QtFontFallbacksCacheKey cacheKey = { .family: family, .style: style, .styleHint: styleHint, .script: script };
668
669 if (const QStringList *fallbacks = db->fallbacksCache.object(key: cacheKey))
670 return *fallbacks;
671
672 // make sure that the db has all fallback families
673 QStringList retList = QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script);
674
675 QStringList::iterator i;
676 for (i = retList.begin(); i != retList.end(); ++i) {
677 bool contains = false;
678 for (int j = 0; j < db->count; j++) {
679 if (db->families[j]->matchesFamilyName(familyName: *i)) {
680 contains = true;
681 break;
682 }
683 }
684 if (!contains) {
685 i = retList.erase(pos: i);
686 --i;
687 }
688 }
689
690 db->fallbacksCache.insert(key: cacheKey, object: new QStringList(retList));
691
692 return retList;
693}
694
695QStringList qt_fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script)
696{
697 QMutexLocker locker(fontDatabaseMutex());
698 return fallbacksForFamily(family, style, styleHint, script);
699}
700
701QFontEngine *QFontDatabasePrivate::loadSingleEngine(int script,
702 const QFontDef &request,
703 QtFontFamily *family, QtFontFoundry *foundry,
704 QtFontStyle *style, QtFontSize *size)
705{
706 Q_UNUSED(foundry);
707
708 Q_ASSERT(size);
709 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
710 int pixelSize = size->pixelSize;
711 if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE)
712 || pfdb->fontsAlwaysScalable()) {
713 pixelSize = request.pixelSize;
714 }
715
716 QFontDef def = request;
717 def.pixelSize = pixelSize;
718
719 QFontCache *fontCache = QFontCache::instance();
720
721 QFontCache::Key key(def,script);
722 QFontEngine *engine = fontCache->findEngine(key);
723 if (!engine) {
724 const bool cacheForCommonScript = script != QChar::Script_Common
725 && (family->writingSystems[QFontDatabase::Latin] & QtFontFamily::Supported) != 0;
726
727 if (Q_LIKELY(cacheForCommonScript)) {
728 // fast path: check if engine was loaded for another script
729 key.script = QChar::Script_Common;
730 engine = fontCache->findEngine(key);
731 key.script = script;
732 if (engine) {
733 // Also check for OpenType tables when using complex scripts
734 if (Q_UNLIKELY(!engine->supportsScript(QChar::Script(script)))) {
735 qWarning(msg: " OpenType support missing for \"%s\", script %d",
736 qPrintable(def.families.first()), script);
737 return nullptr;
738 }
739
740 engine->isSmoothlyScalable = style->smoothScalable;
741 fontCache->insertEngine(key, engine);
742 return engine;
743 }
744 }
745
746 // To avoid synthesized stretch we need a matching stretch to be 100 after this point.
747 // If stretch didn't match exactly we need to calculate the new stretch factor.
748 // This only done if not matched by styleName.
749 if (style->key.stretch != 0 && request.stretch != 0
750 && (request.styleName.isEmpty() || request.styleName != style->styleName)) {
751 def.stretch = (request.stretch * 100 + style->key.stretch / 2) / style->key.stretch;
752 } else if (request.stretch == QFont::AnyStretch) {
753 def.stretch = 100;
754 }
755
756 engine = pfdb->fontEngine(fontDef: def, handle: size->handle);
757 if (engine) {
758 // Also check for OpenType tables when using complex scripts
759 if (!engine->supportsScript(script: QChar::Script(script))) {
760 qWarning(msg: " OpenType support missing for \"%s\", script %d",
761 +qPrintable(def.families.first()), script);
762 if (engine->ref.loadRelaxed() == 0)
763 delete engine;
764 return nullptr;
765 }
766
767 engine->isSmoothlyScalable = style->smoothScalable;
768 fontCache->insertEngine(key, engine);
769
770 if (Q_LIKELY(cacheForCommonScript && !engine->symbol)) {
771 // cache engine for Common script as well
772 key.script = QChar::Script_Common;
773 if (!fontCache->findEngine(key))
774 fontCache->insertEngine(key, engine);
775 }
776 }
777 }
778 return engine;
779}
780
781QFontEngine *QFontDatabasePrivate::loadEngine(int script, const QFontDef &request,
782 QtFontFamily *family, QtFontFoundry *foundry,
783 QtFontStyle *style, QtFontSize *size)
784{
785 QFontEngine *engine = loadSingleEngine(script, request, family, foundry, style, size);
786
787 if (engine && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) {
788 Q_TRACE(QFontDatabase_loadEngine, request.families.join(QLatin1Char(';')), request.pointSize);
789
790 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
791 QFontEngineMulti *pfMultiEngine = pfdb->fontEngineMulti(fontEngine: engine, script: QChar::Script(script));
792 if (!request.fallBackFamilies.isEmpty()) {
793 QStringList fallbacks = request.fallBackFamilies;
794
795 QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint);
796 if (styleHint == QFont::AnyStyle && request.fixedPitch)
797 styleHint = QFont::TypeWriter;
798
799 fallbacks += fallbacksForFamily(family: family->name, style: QFont::Style(style->key.style), styleHint, script: QChar::Script(script));
800
801 pfMultiEngine->setFallbackFamiliesList(fallbacks);
802 }
803 engine = pfMultiEngine;
804
805 // Cache Multi font engine as well in case we got the single
806 // font engine when we are actually looking for a Multi one
807 QFontCache::Key key(request, script, 1);
808 QFontCache::instance()->insertEngine(key, engine);
809 }
810
811 return engine;
812}
813
814QtFontStyle::~QtFontStyle()
815{
816 while (count) {
817 // bitfield count-- in while condition does not work correctly in mwccsym2
818 count--;
819 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
820 if (integration)
821 integration->fontDatabase()->releaseHandle(handle: pixelSizes[count].handle);
822 }
823
824 free(ptr: pixelSizes);
825}
826
827static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey,
828 const QString &styleName = QString())
829{
830 int best = 0;
831 int dist = 0xffff;
832
833 for ( int i = 0; i < foundry->count; i++ ) {
834 QtFontStyle *style = foundry->styles[i];
835
836 if (!styleName.isEmpty() && styleName == style->styleName) {
837 dist = 0;
838 best = i;
839 break;
840 }
841
842 int d = qAbs( t: (int(styleKey.weight) - int(style->key.weight)) / 10 );
843
844 if ( styleKey.stretch != 0 && style->key.stretch != 0 ) {
845 d += qAbs( t: styleKey.stretch - style->key.stretch );
846 }
847
848 if (styleKey.style != style->key.style) {
849 if (styleKey.style != QFont::StyleNormal && style->key.style != QFont::StyleNormal)
850 // one is italic, the other oblique
851 d += 0x0001;
852 else
853 d += 0x1000;
854 }
855
856 if ( d < dist ) {
857 best = i;
858 dist = d;
859 }
860 }
861
862 qCDebug(lcFontMatch, " best style has distance 0x%x", dist );
863 return foundry->styles[best];
864}
865
866
867unsigned int QFontDatabasePrivate::bestFoundry(int script, unsigned int score, int styleStrategy,
868 const QtFontFamily *family, const QString &foundry_name,
869 QtFontStyle::Key styleKey, int pixelSize, char pitch,
870 QtFontDesc *desc, const QString &styleName)
871{
872 Q_UNUSED(script);
873 Q_UNUSED(pitch);
874
875 desc->foundry = nullptr;
876 desc->style = nullptr;
877 desc->size = nullptr;
878
879
880 qCDebug(lcFontMatch, " REMARK: looking for best foundry for family '%s' [%d]", family->name.toLatin1().constData(), family->count);
881
882 for (int x = 0; x < family->count; ++x) {
883 QtFontFoundry *foundry = family->foundries[x];
884 if (!foundry_name.isEmpty() && foundry->name.compare(s: foundry_name, cs: Qt::CaseInsensitive) != 0)
885 continue;
886
887 qCDebug(lcFontMatch, " looking for matching style in foundry '%s' %d",
888 foundry->name.isEmpty() ? "-- none --" : foundry->name.toLatin1().constData(), foundry->count);
889
890 QtFontStyle *style = bestStyle(foundry, styleKey, styleName);
891
892 if (!style->smoothScalable && (styleStrategy & QFont::ForceOutline)) {
893 qCDebug(lcFontMatch, " ForceOutline set, but not smoothly scalable");
894 continue;
895 }
896
897 int px = -1;
898 QtFontSize *size = nullptr;
899
900 // 1. see if we have an exact matching size
901 if (!(styleStrategy & QFont::ForceOutline)) {
902 size = style->pixelSize(size: pixelSize);
903 if (size) {
904 qCDebug(lcFontMatch, " found exact size match (%d pixels)", size->pixelSize);
905 px = size->pixelSize;
906 }
907 }
908
909 // 2. see if we have a smoothly scalable font
910 if (!size && style->smoothScalable && ! (styleStrategy & QFont::PreferBitmap)) {
911 size = style->pixelSize(SMOOTH_SCALABLE);
912 if (size) {
913 qCDebug(lcFontMatch, " found smoothly scalable font (%d pixels)", pixelSize);
914 px = pixelSize;
915 }
916 }
917
918 // 3. see if we have a bitmap scalable font
919 if (!size && style->bitmapScalable && (styleStrategy & QFont::PreferMatch)) {
920 size = style->pixelSize(size: 0);
921 if (size) {
922 qCDebug(lcFontMatch, " found bitmap scalable font (%d pixels)", pixelSize);
923 px = pixelSize;
924 }
925 }
926
927
928 // 4. find closest size match
929 if (! size) {
930 unsigned int distance = ~0u;
931 for (int x = 0; x < style->count; ++x) {
932
933 unsigned int d;
934 if (style->pixelSizes[x].pixelSize < pixelSize) {
935 // penalize sizes that are smaller than the
936 // requested size, due to truncation from floating
937 // point to integer conversions
938 d = pixelSize - style->pixelSizes[x].pixelSize + 1;
939 } else {
940 d = style->pixelSizes[x].pixelSize - pixelSize;
941 }
942
943 if (d < distance) {
944 distance = d;
945 size = style->pixelSizes + x;
946 qCDebug(lcFontMatch, " best size so far: %3d (%d)", size->pixelSize, pixelSize);
947 }
948 }
949
950 if (!size) {
951 qCDebug(lcFontMatch, " no size supports the script we want");
952 continue;
953 }
954
955 if (style->bitmapScalable && ! (styleStrategy & QFont::PreferQuality) &&
956 (distance * 10 / pixelSize) >= 2) {
957 // the closest size is not close enough, go ahead and
958 // use a bitmap scaled font
959 size = style->pixelSize(size: 0);
960 px = pixelSize;
961 } else {
962 px = size->pixelSize;
963 }
964 }
965
966
967 unsigned int this_score = 0x0000;
968 enum {
969 PitchMismatch = 0x4000,
970 StyleMismatch = 0x2000,
971 BitmapScaledPenalty = 0x1000
972 };
973 if (pitch != '*') {
974 if ((pitch == 'm' && !family->fixedPitch)
975 || (pitch == 'p' && family->fixedPitch))
976 this_score += PitchMismatch;
977 }
978 if (styleKey != style->key)
979 this_score += StyleMismatch;
980 if (!style->smoothScalable && px != size->pixelSize) // bitmap scaled
981 this_score += BitmapScaledPenalty;
982 if (px != pixelSize) // close, but not exact, size match
983 this_score += qAbs(t: px - pixelSize);
984
985 if (this_score < score) {
986 qCDebug(lcFontMatch, " found a match: score %x best score so far %x",
987 this_score, score);
988
989 score = this_score;
990 desc->foundry = foundry;
991 desc->style = style;
992 desc->size = size;
993 } else {
994 qCDebug(lcFontMatch, " score %x no better than best %x", this_score, score);
995 }
996 }
997
998 return score;
999}
1000
1001static bool matchFamilyName(const QString &familyName, QtFontFamily *f)
1002{
1003 if (familyName.isEmpty())
1004 return true;
1005 return f->matchesFamilyName(familyName);
1006}
1007
1008/*!
1009 \internal
1010
1011 Tries to find the best match for a given request and family/foundry
1012*/
1013int QFontDatabasePrivate::match(int script, const QFontDef &request, const QString &family_name,
1014 const QString &foundry_name, QtFontDesc *desc, const QList<int> &blacklistedFamilies,
1015 unsigned int *resultingScore)
1016{
1017 int result = -1;
1018
1019 QtFontStyle::Key styleKey;
1020 styleKey.style = request.style;
1021 styleKey.weight = request.weight;
1022 // Prefer a stretch closest to 100.
1023 styleKey.stretch = request.stretch ? request.stretch : 100;
1024 char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p';
1025
1026
1027 qCDebug(lcFontMatch, "QFontDatabasePrivate::match\n"
1028 " request:\n"
1029 " family: %s [%s], script: %d\n"
1030 " styleName: %s\n"
1031 " weight: %d, style: %d\n"
1032 " stretch: %d\n"
1033 " pixelSize: %g\n"
1034 " pitch: %c",
1035 family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(),
1036 foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), script,
1037 request.styleName.isEmpty() ? "-- any --" : request.styleName.toLatin1().constData(),
1038 request.weight, request.style, request.stretch, request.pixelSize, pitch);
1039
1040 desc->family = nullptr;
1041 desc->foundry = nullptr;
1042 desc->style = nullptr;
1043 desc->size = nullptr;
1044
1045 unsigned int score = ~0u;
1046
1047 QMutexLocker locker(fontDatabaseMutex());
1048 QFontDatabasePrivate::ensureFontDatabase();
1049
1050 auto writingSystem = qt_writing_system_for_script(script);
1051 if (writingSystem >= QFontDatabase::WritingSystemsCount)
1052 writingSystem = QFontDatabase::Any;
1053
1054 auto *db = QFontDatabasePrivate::instance();
1055 for (int x = 0; x < db->count; ++x) {
1056 if (blacklistedFamilies.contains(t: x))
1057 continue;
1058 QtFontDesc test;
1059 test.family = db->families[x];
1060
1061 if (!matchFamilyName(familyName: family_name, f: test.family))
1062 continue;
1063 if (!test.family->ensurePopulated())
1064 continue;
1065
1066 // Check if family is supported in the script we want
1067 if (writingSystem != QFontDatabase::Any && !familySupportsWritingSystem(family: test.family, writingSystem))
1068 continue;
1069
1070 // as we know the script is supported, we can be sure
1071 // to find a matching font here.
1072 unsigned int newscore =
1073 bestFoundry(script, score, styleStrategy: request.styleStrategy,
1074 family: test.family, foundry_name, styleKey, pixelSize: request.pixelSize, pitch,
1075 desc: &test, styleName: request.styleName);
1076 if (test.foundry == nullptr && !foundry_name.isEmpty()) {
1077 // the specific foundry was not found, so look for
1078 // any foundry matching our requirements
1079 newscore = bestFoundry(script, score, styleStrategy: request.styleStrategy, family: test.family,
1080 foundry_name: QString(), styleKey, pixelSize: request.pixelSize,
1081 pitch, desc: &test, styleName: request.styleName);
1082 }
1083
1084 if (newscore < score) {
1085 result = x;
1086 score = newscore;
1087 *desc = test;
1088 }
1089 if (newscore < 10) // xlfd instead of FT... just accept it
1090 break;
1091 }
1092
1093 if (resultingScore != nullptr)
1094 *resultingScore = score;
1095
1096 return result;
1097}
1098
1099static QString styleStringHelper(int weight, QFont::Style style)
1100{
1101 QString result;
1102 if (weight > QFont::Normal) {
1103 if (weight >= QFont::Black)
1104 result = QCoreApplication::translate(context: "QFontDatabase", key: "Black");
1105 else if (weight >= QFont::ExtraBold)
1106 result = QCoreApplication::translate(context: "QFontDatabase", key: "Extra Bold");
1107 else if (weight >= QFont::Bold)
1108 result = QCoreApplication::translate(context: "QFontDatabase", key: "Bold");
1109 else if (weight >= QFont::DemiBold)
1110 result = QCoreApplication::translate(context: "QFontDatabase", key: "Demi Bold");
1111 else if (weight >= QFont::Medium)
1112 result = QCoreApplication::translate(context: "QFontDatabase", key: "Medium", disambiguation: "The Medium font weight");
1113 } else {
1114 if (weight <= QFont::Thin)
1115 result = QCoreApplication::translate(context: "QFontDatabase", key: "Thin");
1116 else if (weight <= QFont::ExtraLight)
1117 result = QCoreApplication::translate(context: "QFontDatabase", key: "Extra Light");
1118 else if (weight <= QFont::Light)
1119 result = QCoreApplication::translate(context: "QFontDatabase", key: "Light");
1120 }
1121
1122 if (style == QFont::StyleItalic)
1123 result += u' ' + QCoreApplication::translate(context: "QFontDatabase", key: "Italic");
1124 else if (style == QFont::StyleOblique)
1125 result += u' ' + QCoreApplication::translate(context: "QFontDatabase", key: "Oblique");
1126
1127 if (result.isEmpty())
1128 result = QCoreApplication::translate(context: "QFontDatabase", key: "Normal", disambiguation: "The Normal or Regular font weight");
1129
1130 return result.simplified();
1131}
1132
1133/*!
1134 Returns a string that describes the style of the \a font. For
1135 example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
1136 string may be returned.
1137*/
1138QString QFontDatabase::styleString(const QFont &font)
1139{
1140 return font.styleName().isEmpty() ? styleStringHelper(weight: font.weight(), style: font.style())
1141 : font.styleName();
1142}
1143
1144/*!
1145 Returns a string that describes the style of the \a fontInfo. For
1146 example, "Bold Italic", "Bold", "Italic" or "Normal". An empty
1147 string may be returned.
1148*/
1149QString QFontDatabase::styleString(const QFontInfo &fontInfo)
1150{
1151 return fontInfo.styleName().isEmpty() ? styleStringHelper(weight: fontInfo.weight(), style: fontInfo.style())
1152 : fontInfo.styleName();
1153}
1154
1155
1156/*!
1157 \class QFontDatabase
1158 \threadsafe
1159 \inmodule QtGui
1160
1161 \brief The QFontDatabase class provides information about the fonts available in the underlying window system.
1162
1163 \ingroup appearance
1164
1165 The most common uses of this class are to query the database for
1166 the list of font families() and for the pointSizes() and styles()
1167 that are available for each family. An alternative to pointSizes()
1168 is smoothSizes() which returns the sizes at which a given family
1169 and style will look attractive.
1170
1171 If the font family is available from two or more foundries the
1172 foundry name is included in the family name; for example:
1173 "Helvetica [Adobe]" and "Helvetica [Cronyx]". When you specify a
1174 family, you can either use the old hyphenated "foundry-family"
1175 format or the bracketed "family [foundry]" format; for example:
1176 "Cronyx-Helvetica" or "Helvetica [Cronyx]". If the family has a
1177 foundry it is always returned using the bracketed format, as is
1178 the case with the value returned by families().
1179
1180 The font() function returns a QFont given a family, style and
1181 point size.
1182
1183 A family and style combination can be checked to see if it is
1184 italic() or bold(), and to retrieve its weight(). Similarly we can
1185 call isBitmapScalable(), isSmoothlyScalable(), isScalable() and
1186 isFixedPitch().
1187
1188 Use the styleString() to obtain a text version of a style.
1189
1190 The QFontDatabase class provides some helper functions, for
1191 example, standardSizes(). You can retrieve the description of a
1192 writing system using writingSystemName(), and a sample of
1193 characters in a writing system with writingSystemSample().
1194
1195 Example:
1196
1197 \snippet qfontdatabase/qfontdatabase_snippets.cpp 0
1198
1199 This example gets the list of font families, the list of
1200 styles for each family, and the point sizes that are available for
1201 each combination of family and style, displaying this information
1202 in a tree view.
1203
1204 \sa QFont, QFontInfo, QFontMetrics
1205*/
1206
1207/*!
1208 \fn QFontDatabase::QFontDatabase()
1209 \deprecated [6.0] Call the class methods as static functions instead.
1210
1211 Creates a font database object.
1212*/
1213
1214/*!
1215 \enum QFontDatabase::WritingSystem
1216
1217 \value Any
1218 \value Latin
1219 \value Greek
1220 \value Cyrillic
1221 \value Armenian
1222 \value Hebrew
1223 \value Arabic
1224 \value Syriac
1225 \value Thaana
1226 \value Devanagari
1227 \value Bengali
1228 \value Gurmukhi
1229 \value Gujarati
1230 \value Oriya
1231 \value Tamil
1232 \value Telugu
1233 \value Kannada
1234 \value Malayalam
1235 \value Sinhala
1236 \value Thai
1237 \value Lao
1238 \value Tibetan
1239 \value Myanmar
1240 \value Georgian
1241 \value Khmer
1242 \value SimplifiedChinese
1243 \value TraditionalChinese
1244 \value Japanese
1245 \value Korean
1246 \value Vietnamese
1247 \value Symbol
1248 \value Other (the same as Symbol)
1249 \value Ogham
1250 \value Runic
1251 \value Nko
1252
1253 \omitvalue WritingSystemsCount
1254*/
1255
1256/*!
1257 \enum QFontDatabase::SystemFont
1258
1259 \value GeneralFont The default system font.
1260 \value FixedFont The fixed font that the system recommends.
1261 \value TitleFont The system standard font for titles.
1262 \value SmallestReadableFont The smallest readable system font.
1263
1264 \since 5.2
1265*/
1266
1267/*!
1268 \class QFontDatabasePrivate
1269 \internal
1270
1271 Singleton implementation of the public QFontDatabase APIs,
1272 accessed through QFontDatabasePrivate::instance().
1273
1274 The database is organized in multiple levels:
1275
1276 - QFontDatabasePrivate::families
1277 - QtFontFamily::foundries
1278 - QtFontFoundry::styles
1279 - QtFontStyle::sizes
1280 - QtFontSize::pixelSize
1281
1282 The font database is the single source of truth when doing
1283 font matching, so the database must be sufficiently filled
1284 before attempting a match.
1285
1286 The database is populated (filled) from two sources:
1287
1288 1. The system (platform's) view of the available fonts
1289
1290 Initiated via QFontDatabasePrivate::populateFontDatabase().
1291
1292 a. Can be registered lazily by family only, by calling
1293 QPlatformFontDatabase::registerFontFamily(), and later
1294 populated via QPlatformFontDatabase::populateFamily().
1295
1296 b. Or fully registered with all styles, by calling
1297 QPlatformFontDatabase::registerFont().
1298
1299 2. The fonts registered by the application via Qt APIs
1300
1301 Initiated via QFontDatabase::addApplicationFont() and
1302 QFontDatabase::addApplicationFontFromData().
1303
1304 Application fonts are always fully registered when added.
1305
1306 Fonts can be added at any time, so the database may grow even
1307 after QFontDatabasePrivate::populateFontDatabase() has been
1308 completed.
1309
1310 The database does not support granular removal of fonts,
1311 so if the system fonts change, or an application font is
1312 removed, the font database will be cleared and then filled
1313 from scratch, via QFontDatabasePrivate:invalidate() and
1314 QFontDatabasePrivate::ensureFontDatabase().
1315*/
1316
1317/*!
1318 \internal
1319
1320 Initializes the font database if necessary and returns its
1321 pointer. Mutex lock must be held when calling this function.
1322*/
1323QFontDatabasePrivate *QFontDatabasePrivate::ensureFontDatabase()
1324{
1325 auto *d = QFontDatabasePrivate::instance();
1326 if (!d->populated) {
1327 // The font database may have been partially populated, but to ensure
1328 // we can answer queries for any platform- or user-provided family we
1329 // need to fully populate it now.
1330 qCDebug(lcFontDb) << "Populating font database";
1331
1332 if (Q_UNLIKELY(qGuiApp == nullptr || QGuiApplicationPrivate::platformIntegration() == nullptr))
1333 qFatal(msg: "QFontDatabase: Must construct a QGuiApplication before accessing QFontDatabase");
1334
1335 auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1336 platformFontDatabase->populateFontDatabase();
1337
1338 for (int i = 0; i < d->applicationFonts.size(); i++) {
1339 auto *font = &d->applicationFonts[i];
1340 if (!font->isNull() && !font->isPopulated())
1341 platformFontDatabase->addApplicationFont(fontData: font->data, fileName: font->fileName, font);
1342 }
1343
1344 // Note: Both application fonts and platform fonts may be added
1345 // after this initial population, so the only thing we are tracking
1346 // is whether we've done our part in ensuring a filled font database.
1347 d->populated = true;
1348 }
1349 return d;
1350}
1351
1352/*!
1353 Returns a sorted list of the available writing systems. This is
1354 list generated from information about all installed fonts on the
1355 system.
1356
1357 \sa families()
1358*/
1359QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems()
1360{
1361 QMutexLocker locker(fontDatabaseMutex());
1362 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1363
1364 quint64 writingSystemsFound = 0;
1365 static_assert(WritingSystemsCount < 64);
1366
1367 for (int i = 0; i < d->count; ++i) {
1368 QtFontFamily *family = d->families[i];
1369 if (!family->ensurePopulated())
1370 continue;
1371
1372 if (family->count == 0)
1373 continue;
1374 for (uint x = Latin; x < uint(WritingSystemsCount); ++x) {
1375 if (family->writingSystems[x] & QtFontFamily::Supported)
1376 writingSystemsFound |= quint64(1) << x;
1377 }
1378 }
1379
1380 // mutex protection no longer needed - just working on local data now:
1381 locker.unlock();
1382
1383 QList<WritingSystem> list;
1384 list.reserve(asize: qPopulationCount(v: writingSystemsFound));
1385 for (uint x = Latin ; x < uint(WritingSystemsCount); ++x) {
1386 if (writingSystemsFound & (quint64(1) << x))
1387 list.push_back(t: WritingSystem(x));
1388 }
1389 return list;
1390}
1391
1392
1393/*!
1394 Returns a sorted list of the writing systems supported by a given
1395 font \a family.
1396
1397 \sa families()
1398*/
1399QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems(const QString &family)
1400{
1401 QString familyName, foundryName;
1402 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1403
1404 QMutexLocker locker(fontDatabaseMutex());
1405 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1406
1407 QList<WritingSystem> list;
1408 QtFontFamily *f = d->family(f: familyName);
1409 if (!f || f->count == 0)
1410 return list;
1411
1412 for (int x = Latin; x < WritingSystemsCount; ++x) {
1413 const WritingSystem writingSystem = WritingSystem(x);
1414 if (f->writingSystems[writingSystem] & QtFontFamily::Supported)
1415 list.append(t: writingSystem);
1416 }
1417 return list;
1418}
1419
1420
1421/*!
1422 Returns a sorted list of the available font families which support
1423 the \a writingSystem.
1424
1425 If a family exists in several foundries, the returned name for
1426 that font is in the form "family [foundry]". Examples: "Times
1427 [Adobe]", "Times [Cronyx]", "Palatino".
1428
1429 \sa writingSystems()
1430*/
1431QStringList QFontDatabase::families(WritingSystem writingSystem)
1432{
1433 QMutexLocker locker(fontDatabaseMutex());
1434 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1435
1436 QStringList flist;
1437 for (int i = 0; i < d->count; i++) {
1438 QtFontFamily *f = d->families[i];
1439 if (f->populated && f->count == 0)
1440 continue;
1441 if (writingSystem != Any) {
1442 if (!f->ensurePopulated())
1443 continue;
1444 if (f->writingSystems[writingSystem] != QtFontFamily::Supported)
1445 continue;
1446 }
1447 if (!f->populated || f->count == 1) {
1448 flist.append(t: f->name);
1449 } else {
1450 for (int j = 0; j < f->count; j++) {
1451 QString str = f->name;
1452 QString foundry = f->foundries[j]->name;
1453 if (!foundry.isEmpty()) {
1454 str += " ["_L1;
1455 str += foundry;
1456 str += u']';
1457 }
1458 flist.append(t: str);
1459 }
1460 }
1461 }
1462 return flist;
1463}
1464
1465/*!
1466 Returns a list of the styles available for the font family \a
1467 family. Some example styles: "Light", "Light Italic", "Bold",
1468 "Oblique", "Demi". The list may be empty.
1469
1470 \sa families()
1471*/
1472QStringList QFontDatabase::styles(const QString &family)
1473{
1474 QString familyName, foundryName;
1475 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1476
1477 QMutexLocker locker(fontDatabaseMutex());
1478 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1479
1480 QStringList l;
1481 QtFontFamily *f = d->family(f: familyName);
1482 if (!f)
1483 return l;
1484
1485 QtFontFoundry allStyles(foundryName);
1486 for (int j = 0; j < f->count; j++) {
1487 QtFontFoundry *foundry = f->foundries[j];
1488 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1489 for (int k = 0; k < foundry->count; k++) {
1490 QtFontStyle::Key ke(foundry->styles[k]->key);
1491 ke.stretch = 0;
1492 allStyles.style(key: ke, styleName: foundry->styles[k]->styleName, create: true);
1493 }
1494 }
1495 }
1496
1497 l.reserve(asize: allStyles.count);
1498 for (int i = 0; i < allStyles.count; i++) {
1499 l.append(t: allStyles.styles[i]->styleName.isEmpty() ?
1500 styleStringHelper(weight: allStyles.styles[i]->key.weight,
1501 style: (QFont::Style)allStyles.styles[i]->key.style) :
1502 allStyles.styles[i]->styleName);
1503 }
1504 return l;
1505}
1506
1507/*!
1508 Returns \c true if the font that has family \a family and style \a
1509 style is fixed pitch; otherwise returns \c false.
1510*/
1511
1512bool QFontDatabase::isFixedPitch(const QString &family,
1513 const QString &style)
1514{
1515 Q_UNUSED(style);
1516
1517 QString familyName, foundryName;
1518 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1519
1520 QMutexLocker locker(fontDatabaseMutex());
1521 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1522
1523 QtFontFamily *f = d->family(f: familyName);
1524 return (f && f->fixedPitch);
1525}
1526
1527/*!
1528 Returns \c true if the font that has family \a family and style \a
1529 style is a scalable bitmap font; otherwise returns \c false. Scaling
1530 a bitmap font usually produces an unattractive hardly readable
1531 result, because the pixels of the font are scaled. If you need to
1532 scale a bitmap font it is better to scale it to one of the fixed
1533 sizes returned by smoothSizes().
1534
1535 \sa isScalable(), isSmoothlyScalable()
1536*/
1537bool QFontDatabase::isBitmapScalable(const QString &family,
1538 const QString &style)
1539{
1540 bool bitmapScalable = false;
1541 QString familyName, foundryName;
1542 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1543
1544 QMutexLocker locker(fontDatabaseMutex());
1545 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1546
1547 QtFontFamily *f = d->family(f: familyName);
1548 if (!f) return bitmapScalable;
1549
1550 QtFontStyle::Key styleKey(style);
1551 for (int j = 0; j < f->count; j++) {
1552 QtFontFoundry *foundry = f->foundries[j];
1553 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1554 for (int k = 0; k < foundry->count; k++)
1555 if ((style.isEmpty() ||
1556 foundry->styles[k]->styleName == style ||
1557 foundry->styles[k]->key == styleKey)
1558 && foundry->styles[k]->bitmapScalable && !foundry->styles[k]->smoothScalable) {
1559 bitmapScalable = true;
1560 goto end;
1561 }
1562 }
1563 }
1564 end:
1565 return bitmapScalable;
1566}
1567
1568
1569/*!
1570 Returns \c true if the font that has family \a family and style \a
1571 style is smoothly scalable; otherwise returns \c false. If this
1572 function returns \c true, it's safe to scale this font to any size,
1573 and the result will always look attractive.
1574
1575 \sa isScalable(), isBitmapScalable()
1576*/
1577bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &style)
1578{
1579 bool smoothScalable = false;
1580 QString familyName, foundryName;
1581 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1582
1583 QMutexLocker locker(fontDatabaseMutex());
1584 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1585
1586 QtFontFamily *f = d->family(f: familyName);
1587 if (!f) {
1588 for (int i = 0; i < d->count; i++) {
1589 if (d->families[i]->matchesFamilyName(familyName)) {
1590 f = d->families[i];
1591 if (f->ensurePopulated())
1592 break;
1593 }
1594 }
1595 }
1596 if (!f) return smoothScalable;
1597
1598 const QtFontStyle::Key styleKey(style);
1599 for (int j = 0; j < f->count; j++) {
1600 QtFontFoundry *foundry = f->foundries[j];
1601 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1602 for (int k = 0; k < foundry->count; k++) {
1603 const QtFontStyle *fontStyle = foundry->styles[k];
1604 smoothScalable =
1605 fontStyle->smoothScalable
1606 && ((style.isEmpty()
1607 || fontStyle->styleName == style
1608 || fontStyle->key == styleKey)
1609 || (fontStyle->styleName.isEmpty()
1610 && style == styleStringHelper(weight: fontStyle->key.weight,
1611 style: QFont::Style(fontStyle->key.style))));
1612 if (smoothScalable)
1613 goto end;
1614 }
1615 }
1616 }
1617 end:
1618 return smoothScalable;
1619}
1620
1621/*!
1622 Returns \c true if the font that has family \a family and style \a
1623 style is scalable; otherwise returns \c false.
1624
1625 \sa isBitmapScalable(), isSmoothlyScalable()
1626*/
1627bool QFontDatabase::isScalable(const QString &family,
1628 const QString &style)
1629{
1630 QMutexLocker locker(fontDatabaseMutex());
1631 if (isSmoothlyScalable(family, style))
1632 return true;
1633 return isBitmapScalable(family, style);
1634}
1635
1636
1637/*!
1638 Returns a list of the point sizes available for the font that has
1639 family \a family and style \a styleName. The list may be empty.
1640
1641 \sa smoothSizes(), standardSizes()
1642*/
1643QList<int> QFontDatabase::pointSizes(const QString &family,
1644 const QString &styleName)
1645{
1646 if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable())
1647 return standardSizes();
1648
1649 bool smoothScalable = false;
1650 QString familyName, foundryName;
1651 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1652
1653 QMutexLocker locker(fontDatabaseMutex());
1654 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1655
1656 QList<int> sizes;
1657
1658 QtFontFamily *fam = d->family(f: familyName);
1659 if (!fam) return sizes;
1660
1661
1662 const int dpi = qt_defaultDpiY(); // embedded
1663
1664 QtFontStyle::Key styleKey(styleName);
1665 for (int j = 0; j < fam->count; j++) {
1666 QtFontFoundry *foundry = fam->foundries[j];
1667 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1668 QtFontStyle *style = foundry->style(key: styleKey, styleName);
1669 if (!style) continue;
1670
1671 if (style->smoothScalable) {
1672 smoothScalable = true;
1673 goto end;
1674 }
1675 for (int l = 0; l < style->count; l++) {
1676 const QtFontSize *size = style->pixelSizes + l;
1677
1678 if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
1679 const int pointSize = qRound(d: size->pixelSize * 72.0 / dpi);
1680 if (! sizes.contains(t: pointSize))
1681 sizes.append(t: pointSize);
1682 }
1683 }
1684 }
1685 }
1686 end:
1687 if (smoothScalable)
1688 return standardSizes();
1689
1690 std::sort(first: sizes.begin(), last: sizes.end());
1691 return sizes;
1692}
1693
1694/*!
1695 Returns a QFont object that has family \a family, style \a style
1696 and point size \a pointSize. If no matching font could be created,
1697 a QFont object that uses the application's default font is
1698 returned.
1699*/
1700QFont QFontDatabase::font(const QString &family, const QString &style,
1701 int pointSize)
1702{
1703 QString familyName, foundryName;
1704 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1705 QMutexLocker locker(fontDatabaseMutex());
1706 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1707
1708 QtFontFoundry allStyles(foundryName);
1709 QtFontFamily *f = d->family(f: familyName);
1710 if (!f) return QGuiApplication::font();
1711
1712 for (int j = 0; j < f->count; j++) {
1713 QtFontFoundry *foundry = f->foundries[j];
1714 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1715 for (int k = 0; k < foundry->count; k++)
1716 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1717 }
1718 }
1719
1720 QtFontStyle::Key styleKey(style);
1721 QtFontStyle *s = bestStyle(foundry: &allStyles, styleKey, styleName: style);
1722
1723 if (!s) // no styles found?
1724 return QGuiApplication::font();
1725
1726 QFont fnt(QStringList{family}, pointSize, s->key.weight);
1727 fnt.setStyle((QFont::Style)s->key.style);
1728 if (!s->styleName.isEmpty())
1729 fnt.setStyleName(s->styleName);
1730 return fnt;
1731}
1732
1733
1734/*!
1735 Returns the point sizes of a font that has family \a family and
1736 style \a styleName that will look attractive. The list may be empty.
1737 For non-scalable fonts and bitmap scalable fonts, this function
1738 is equivalent to pointSizes().
1739
1740 \sa pointSizes(), standardSizes()
1741*/
1742QList<int> QFontDatabase::smoothSizes(const QString &family,
1743 const QString &styleName)
1744{
1745 if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable())
1746 return standardSizes();
1747
1748 bool smoothScalable = false;
1749 QString familyName, foundryName;
1750 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1751
1752 QMutexLocker locker(fontDatabaseMutex());
1753 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1754
1755 QList<int> sizes;
1756
1757 QtFontFamily *fam = d->family(f: familyName);
1758 if (!fam)
1759 return sizes;
1760
1761 const int dpi = qt_defaultDpiY(); // embedded
1762
1763 QtFontStyle::Key styleKey(styleName);
1764 for (int j = 0; j < fam->count; j++) {
1765 QtFontFoundry *foundry = fam->foundries[j];
1766 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1767 QtFontStyle *style = foundry->style(key: styleKey, styleName);
1768 if (!style) continue;
1769
1770 if (style->smoothScalable) {
1771 smoothScalable = true;
1772 goto end;
1773 }
1774 for (int l = 0; l < style->count; l++) {
1775 const QtFontSize *size = style->pixelSizes + l;
1776
1777 if (size->pixelSize != 0 && size->pixelSize != SMOOTH_SCALABLE) {
1778 const int pointSize = qRound(d: size->pixelSize * 72.0 / dpi);
1779 if (! sizes.contains(t: pointSize))
1780 sizes.append(t: pointSize);
1781 }
1782 }
1783 }
1784 }
1785 end:
1786 if (smoothScalable)
1787 return QFontDatabase::standardSizes();
1788
1789 std::sort(first: sizes.begin(), last: sizes.end());
1790 return sizes;
1791}
1792
1793
1794/*!
1795 Returns a list of standard font sizes.
1796
1797 \sa smoothSizes(), pointSizes()
1798*/
1799QList<int> QFontDatabase::standardSizes()
1800{
1801 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->standardSizes();
1802}
1803
1804
1805/*!
1806 Returns \c true if the font that has family \a family and style \a
1807 style is italic; otherwise returns \c false.
1808
1809 \sa weight(), bold()
1810*/
1811bool QFontDatabase::italic(const QString &family, const QString &style)
1812{
1813 QString familyName, foundryName;
1814 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1815
1816 QMutexLocker locker(fontDatabaseMutex());
1817 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1818
1819 QtFontFoundry allStyles(foundryName);
1820 QtFontFamily *f = d->family(f: familyName);
1821 if (!f) return false;
1822
1823 for (int j = 0; j < f->count; j++) {
1824 QtFontFoundry *foundry = f->foundries[j];
1825 if (foundryName.isEmpty() || foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1826 for (int k = 0; k < foundry->count; k++)
1827 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1828 }
1829 }
1830
1831 QtFontStyle::Key styleKey(style);
1832 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1833 return s && s->key.style == QFont::StyleItalic;
1834}
1835
1836
1837/*!
1838 Returns \c true if the font that has family \a family and style \a
1839 style is bold; otherwise returns \c false.
1840
1841 \sa italic(), weight()
1842*/
1843bool QFontDatabase::bold(const QString &family,
1844 const QString &style)
1845{
1846 QString familyName, foundryName;
1847 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1848
1849 QMutexLocker locker(fontDatabaseMutex());
1850 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1851
1852 QtFontFoundry allStyles(foundryName);
1853 QtFontFamily *f = d->family(f: familyName);
1854 if (!f) return false;
1855
1856 for (int j = 0; j < f->count; j++) {
1857 QtFontFoundry *foundry = f->foundries[j];
1858 if (foundryName.isEmpty() ||
1859 foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1860 for (int k = 0; k < foundry->count; k++)
1861 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1862 }
1863 }
1864
1865 QtFontStyle::Key styleKey(style);
1866 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1867 return s && s->key.weight >= QFont::Bold;
1868}
1869
1870
1871/*!
1872 Returns the weight of the font that has family \a family and style
1873 \a style. If there is no such family and style combination,
1874 returns -1.
1875
1876 \sa italic(), bold()
1877*/
1878int QFontDatabase::weight(const QString &family,
1879 const QString &style)
1880{
1881 QString familyName, foundryName;
1882 parseFontName(name: family, foundry&: foundryName, family&: familyName);
1883
1884 QMutexLocker locker(fontDatabaseMutex());
1885 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1886
1887 QtFontFoundry allStyles(foundryName);
1888 QtFontFamily *f = d->family(f: familyName);
1889 if (!f) return -1;
1890
1891 for (int j = 0; j < f->count; j++) {
1892 QtFontFoundry *foundry = f->foundries[j];
1893 if (foundryName.isEmpty() ||
1894 foundry->name.compare(s: foundryName, cs: Qt::CaseInsensitive) == 0) {
1895 for (int k = 0; k < foundry->count; k++)
1896 allStyles.style(key: foundry->styles[k]->key, styleName: foundry->styles[k]->styleName, create: true);
1897 }
1898 }
1899
1900 QtFontStyle::Key styleKey(style);
1901 QtFontStyle *s = allStyles.style(key: styleKey, styleName: style);
1902 return s ? s->key.weight : -1;
1903}
1904
1905
1906/*! \internal */
1907bool QFontDatabase::hasFamily(const QString &family)
1908{
1909 QString parsedFamily, foundry;
1910 parseFontName(name: family, foundry, family&: parsedFamily);
1911 const QString familyAlias = QFontDatabasePrivate::resolveFontFamilyAlias(family: parsedFamily);
1912
1913 QMutexLocker locker(fontDatabaseMutex());
1914 QFontDatabasePrivate *d = QFontDatabasePrivate::ensureFontDatabase();
1915
1916 for (int i = 0; i < d->count; i++) {
1917 QtFontFamily *f = d->families[i];
1918 if (f->populated && f->count == 0)
1919 continue;
1920 if (familyAlias.compare(s: f->name, cs: Qt::CaseInsensitive) == 0)
1921 return true;
1922 }
1923
1924 return false;
1925}
1926
1927
1928/*!
1929 \since 5.5
1930
1931 Returns \c true if and only if the \a family font family is private.
1932
1933 This happens, for instance, on \macos and iOS, where the system UI fonts are not
1934 accessible to the user. For completeness, QFontDatabase::families() returns all
1935 font families, including the private ones. You should use this function if you
1936 are developing a font selection control in order to keep private fonts hidden.
1937
1938 \sa families()
1939*/
1940bool QFontDatabase::isPrivateFamily(const QString &family)
1941{
1942 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->isPrivateFontFamily(family);
1943}
1944
1945
1946/*!
1947 Returns the names the \a writingSystem (e.g. for displaying to the
1948 user in a dialog).
1949*/
1950QString QFontDatabase::writingSystemName(WritingSystem writingSystem)
1951{
1952 const char *name = nullptr;
1953 switch (writingSystem) {
1954 case Any:
1955 name = QT_TRANSLATE_NOOP("QFontDatabase", "Any");
1956 break;
1957 case Latin:
1958 name = QT_TRANSLATE_NOOP("QFontDatabase", "Latin");
1959 break;
1960 case Greek:
1961 name = QT_TRANSLATE_NOOP("QFontDatabase", "Greek");
1962 break;
1963 case Cyrillic:
1964 name = QT_TRANSLATE_NOOP("QFontDatabase", "Cyrillic");
1965 break;
1966 case Armenian:
1967 name = QT_TRANSLATE_NOOP("QFontDatabase", "Armenian");
1968 break;
1969 case Hebrew:
1970 name = QT_TRANSLATE_NOOP("QFontDatabase", "Hebrew");
1971 break;
1972 case Arabic:
1973 name = QT_TRANSLATE_NOOP("QFontDatabase", "Arabic");
1974 break;
1975 case Syriac:
1976 name = QT_TRANSLATE_NOOP("QFontDatabase", "Syriac");
1977 break;
1978 case Thaana:
1979 name = QT_TRANSLATE_NOOP("QFontDatabase", "Thaana");
1980 break;
1981 case Devanagari:
1982 name = QT_TRANSLATE_NOOP("QFontDatabase", "Devanagari");
1983 break;
1984 case Bengali:
1985 name = QT_TRANSLATE_NOOP("QFontDatabase", "Bengali");
1986 break;
1987 case Gurmukhi:
1988 name = QT_TRANSLATE_NOOP("QFontDatabase", "Gurmukhi");
1989 break;
1990 case Gujarati:
1991 name = QT_TRANSLATE_NOOP("QFontDatabase", "Gujarati");
1992 break;
1993 case Oriya:
1994 name = QT_TRANSLATE_NOOP("QFontDatabase", "Oriya");
1995 break;
1996 case Tamil:
1997 name = QT_TRANSLATE_NOOP("QFontDatabase", "Tamil");
1998 break;
1999 case Telugu:
2000 name = QT_TRANSLATE_NOOP("QFontDatabase", "Telugu");
2001 break;
2002 case Kannada:
2003 name = QT_TRANSLATE_NOOP("QFontDatabase", "Kannada");
2004 break;
2005 case Malayalam:
2006 name = QT_TRANSLATE_NOOP("QFontDatabase", "Malayalam");
2007 break;
2008 case Sinhala:
2009 name = QT_TRANSLATE_NOOP("QFontDatabase", "Sinhala");
2010 break;
2011 case Thai:
2012 name = QT_TRANSLATE_NOOP("QFontDatabase", "Thai");
2013 break;
2014 case Lao:
2015 name = QT_TRANSLATE_NOOP("QFontDatabase", "Lao");
2016 break;
2017 case Tibetan:
2018 name = QT_TRANSLATE_NOOP("QFontDatabase", "Tibetan");
2019 break;
2020 case Myanmar:
2021 name = QT_TRANSLATE_NOOP("QFontDatabase", "Myanmar");
2022 break;
2023 case Georgian:
2024 name = QT_TRANSLATE_NOOP("QFontDatabase", "Georgian");
2025 break;
2026 case Khmer:
2027 name = QT_TRANSLATE_NOOP("QFontDatabase", "Khmer");
2028 break;
2029 case SimplifiedChinese:
2030 name = QT_TRANSLATE_NOOP("QFontDatabase", "Simplified Chinese");
2031 break;
2032 case TraditionalChinese:
2033 name = QT_TRANSLATE_NOOP("QFontDatabase", "Traditional Chinese");
2034 break;
2035 case Japanese:
2036 name = QT_TRANSLATE_NOOP("QFontDatabase", "Japanese");
2037 break;
2038 case Korean:
2039 name = QT_TRANSLATE_NOOP("QFontDatabase", "Korean");
2040 break;
2041 case Vietnamese:
2042 name = QT_TRANSLATE_NOOP("QFontDatabase", "Vietnamese");
2043 break;
2044 case Symbol:
2045 name = QT_TRANSLATE_NOOP("QFontDatabase", "Symbol");
2046 break;
2047 case Ogham:
2048 name = QT_TRANSLATE_NOOP("QFontDatabase", "Ogham");
2049 break;
2050 case Runic:
2051 name = QT_TRANSLATE_NOOP("QFontDatabase", "Runic");
2052 break;
2053 case Nko:
2054 name = QT_TRANSLATE_NOOP("QFontDatabase", "N'Ko");
2055 break;
2056 default:
2057 Q_ASSERT_X(false, "QFontDatabase::writingSystemName", "invalid 'writingSystem' parameter");
2058 break;
2059 }
2060 return QCoreApplication::translate(context: "QFontDatabase", key: name);
2061}
2062
2063/*!
2064 Returns a string with sample characters from \a writingSystem.
2065*/
2066QString QFontDatabase::writingSystemSample(WritingSystem writingSystem)
2067{
2068 return [&]() -> QStringView {
2069 switch (writingSystem) {
2070 case QFontDatabase::Any:
2071 case QFontDatabase::Symbol:
2072 // show only ascii characters
2073 return u"AaBbzZ";
2074 case QFontDatabase::Latin:
2075 // This is cheating... we only show latin-1 characters so that we don't
2076 // end up loading lots of fonts - at least on X11...
2077 return u"Aa\x00C3\x00E1Zz";
2078 case QFontDatabase::Greek:
2079 return u"\x0393\x03B1\x03A9\x03C9";
2080 case QFontDatabase::Cyrillic:
2081 return u"\x0414\x0434\x0436\x044f";
2082 case QFontDatabase::Armenian:
2083 return u"\x053f\x054f\x056f\x057f";
2084 case QFontDatabase::Hebrew:
2085 return u"\x05D0\x05D1\x05D2\x05D3";
2086 case QFontDatabase::Arabic:
2087 return u"\x0623\x0628\x062C\x062F\x064A\x0629\x0020\x0639\x0631\x0628\x064A\x0629";
2088 case QFontDatabase::Syriac:
2089 return u"\x0715\x0725\x0716\x0726";
2090 case QFontDatabase::Thaana:
2091 return u"\x0784\x0794\x078c\x078d";
2092 case QFontDatabase::Devanagari:
2093 return u"\x0905\x0915\x0925\x0935";
2094 case QFontDatabase::Bengali:
2095 return u"\x0986\x0996\x09a6\x09b6";
2096 case QFontDatabase::Gurmukhi:
2097 return u"\x0a05\x0a15\x0a25\x0a35";
2098 case QFontDatabase::Gujarati:
2099 return u"\x0a85\x0a95\x0aa5\x0ab5";
2100 case QFontDatabase::Oriya:
2101 return u"\x0b06\x0b16\x0b2b\x0b36";
2102 case QFontDatabase::Tamil:
2103 return u"\x0b89\x0b99\x0ba9\x0bb9";
2104 case QFontDatabase::Telugu:
2105 return u"\x0c05\x0c15\x0c25\x0c35";
2106 case QFontDatabase::Kannada:
2107 return u"\x0c85\x0c95\x0ca5\x0cb5";
2108 case QFontDatabase::Malayalam:
2109 return u"\x0d05\x0d15\x0d25\x0d35";
2110 case QFontDatabase::Sinhala:
2111 return u"\x0d90\x0da0\x0db0\x0dc0";
2112 case QFontDatabase::Thai:
2113 return u"\x0e02\x0e12\x0e22\x0e32";
2114 case QFontDatabase::Lao:
2115 return u"\x0e8d\x0e9d\x0ead\x0ebd";
2116 case QFontDatabase::Tibetan:
2117 return u"\x0f00\x0f01\x0f02\x0f03";
2118 case QFontDatabase::Myanmar:
2119 return u"\x1000\x1001\x1002\x1003";
2120 case QFontDatabase::Georgian:
2121 return u"\x10a0\x10b0\x10c0\x10d0";
2122 case QFontDatabase::Khmer:
2123 return u"\x1780\x1790\x17b0\x17c0";
2124 case QFontDatabase::SimplifiedChinese:
2125 return u"\x4e2d\x6587\x8303\x4f8b";
2126 case QFontDatabase::TraditionalChinese:
2127 return u"\x4e2d\x6587\x7bc4\x4f8b";
2128 case QFontDatabase::Japanese:
2129 return u"\x30b5\x30f3\x30d7\x30eb\x3067\x3059";
2130 case QFontDatabase::Korean:
2131 return u"\xac00\xac11\xac1a\xac2f";
2132 case QFontDatabase::Vietnamese:
2133 return u"\x1ED7\x1ED9\x1ED1\x1ED3";
2134 case QFontDatabase::Ogham:
2135 return u"\x1681\x1682\x1683\x1684";
2136 case QFontDatabase::Runic:
2137 return u"\x16a0\x16a1\x16a2\x16a3";
2138 case QFontDatabase::Nko:
2139 return u"\x7ca\x7cb\x7cc\x7cd";
2140 default:
2141 return nullptr;
2142 }
2143 }().toString();
2144}
2145
2146void QFontDatabasePrivate::parseFontName(const QString &name, QString &foundry, QString &family)
2147{
2148 QT_PREPEND_NAMESPACE(parseFontName)(name, foundry, family);
2149}
2150
2151// used from qfontengine_ft.cpp
2152Q_GUI_EXPORT QByteArray qt_fontdata_from_index(int index)
2153{
2154 QMutexLocker locker(fontDatabaseMutex());
2155 return QFontDatabasePrivate::instance()->applicationFonts.value(i: index).data;
2156}
2157
2158int QFontDatabasePrivate::addAppFont(const QByteArray &fontData, const QString &fileName)
2159{
2160 QFontDatabasePrivate::ApplicationFont font;
2161 font.data = fontData;
2162 font.fileName = fileName;
2163
2164 Q_TRACE(QFontDatabasePrivate_addAppFont, fileName);
2165
2166 int i;
2167 for (i = 0; i < applicationFonts.size(); ++i)
2168 if (applicationFonts.at(i).isNull())
2169 break;
2170 if (i >= applicationFonts.size()) {
2171 applicationFonts.append(t: ApplicationFont());
2172 i = applicationFonts.size() - 1;
2173 }
2174
2175 if (font.fileName.isEmpty() && !fontData.isEmpty())
2176 font.fileName = ":qmemoryfonts/"_L1 + QString::number(i);
2177
2178 auto *platformFontDatabase = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
2179 platformFontDatabase->addApplicationFont(fontData: font.data, fileName: font.fileName, font: &font);
2180 if (font.properties.isEmpty())
2181 return -1;
2182
2183 applicationFonts[i] = font;
2184
2185 // The font cache may have cached lookups for the font that was now
2186 // loaded, so it has to be flushed.
2187 QFontCache::instance()->clear();
2188
2189 emit qApp->fontDatabaseChanged();
2190
2191 return i;
2192}
2193
2194bool QFontDatabasePrivate::isApplicationFont(const QString &fileName)
2195{
2196 for (int i = 0; i < applicationFonts.size(); ++i)
2197 if (applicationFonts.at(i).fileName == fileName)
2198 return true;
2199 return false;
2200}
2201
2202/*!
2203 \since 4.2
2204
2205 Loads the font from the file specified by \a fileName and makes it available to
2206 the application. An ID is returned that can be used to remove the font again
2207 with removeApplicationFont() or to retrieve the list of family names contained
2208 in the font.
2209
2210//! [add-application-font-doc]
2211 The function returns -1 if the font could not be loaded.
2212
2213 Currently only TrueType fonts, TrueType font collections, and OpenType fonts are
2214 supported.
2215//! [add-application-font-doc]
2216
2217 \sa addApplicationFontFromData(), applicationFontFamilies(), removeApplicationFont()
2218*/
2219int QFontDatabase::addApplicationFont(const QString &fileName)
2220{
2221 QByteArray data;
2222 if (!QFileInfo(fileName).isNativePath()) {
2223 QFile f(fileName);
2224 if (!f.open(flags: QIODevice::ReadOnly))
2225 return -1;
2226
2227 Q_TRACE(QFontDatabase_addApplicationFont, fileName);
2228
2229 data = f.readAll();
2230 }
2231 QMutexLocker locker(fontDatabaseMutex());
2232 return QFontDatabasePrivate::instance()->addAppFont(fontData: data, fileName);
2233}
2234
2235/*!
2236 \since 4.2
2237
2238 Loads the font from binary data specified by \a fontData and makes it available to
2239 the application. An ID is returned that can be used to remove the font again
2240 with removeApplicationFont() or to retrieve the list of family names contained
2241 in the font.
2242
2243 \include qfontdatabase.cpp add-application-font-doc
2244
2245 \sa addApplicationFont(), applicationFontFamilies(), removeApplicationFont()
2246*/
2247int QFontDatabase::addApplicationFontFromData(const QByteArray &fontData)
2248{
2249 QMutexLocker locker(fontDatabaseMutex());
2250 return QFontDatabasePrivate::instance()->addAppFont(fontData, fileName: QString() /* fileName */);
2251}
2252
2253/*!
2254 \since 4.2
2255
2256 Returns a list of font families for the given application font identified by
2257 \a id.
2258
2259 \sa addApplicationFont(), addApplicationFontFromData()
2260*/
2261QStringList QFontDatabase::applicationFontFamilies(int id)
2262{
2263 QMutexLocker locker(fontDatabaseMutex());
2264 auto *d = QFontDatabasePrivate::instance();
2265
2266 QStringList ret;
2267 ret.reserve(asize: d->applicationFonts.value(i: id).properties.size());
2268
2269 for (const auto &properties : d->applicationFonts.value(i: id).properties)
2270 ret.append(t: properties.familyName);
2271
2272 return ret;
2273}
2274
2275/*!
2276 \since 5.2
2277
2278 Returns the most adequate font for a given \a type case for proper integration
2279 with the system's look and feel.
2280
2281 \sa QGuiApplication::font()
2282*/
2283
2284QFont QFontDatabase::systemFont(QFontDatabase::SystemFont type)
2285{
2286 const QFont *font = nullptr;
2287 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
2288 switch (type) {
2289 case GeneralFont:
2290 font = theme->font(type: QPlatformTheme::SystemFont);
2291 break;
2292 case FixedFont:
2293 font = theme->font(type: QPlatformTheme::FixedFont);
2294 break;
2295 case TitleFont:
2296 font = theme->font(type: QPlatformTheme::TitleBarFont);
2297 break;
2298 case SmallestReadableFont:
2299 font = theme->font(type: QPlatformTheme::MiniFont);
2300 break;
2301 }
2302 }
2303
2304 if (font)
2305 return *font;
2306 else if (QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration())
2307 return integration->fontDatabase()->defaultFont();
2308 else
2309 return QFont();
2310}
2311
2312/*!
2313 \fn bool QFontDatabase::removeApplicationFont(int id)
2314 \since 4.2
2315
2316 Removes the previously loaded application font identified by \a
2317 id. Returns \c true if unloading of the font succeeded; otherwise
2318 returns \c false.
2319
2320 \sa removeAllApplicationFonts(), addApplicationFont(),
2321 addApplicationFontFromData()
2322*/
2323bool QFontDatabase::removeApplicationFont(int handle)
2324{
2325 QMutexLocker locker(fontDatabaseMutex());
2326
2327 auto *db = QFontDatabasePrivate::instance();
2328 if (handle < 0 || handle >= db->applicationFonts.size())
2329 return false;
2330
2331 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
2332
2333 db->invalidate();
2334 return true;
2335}
2336
2337/*!
2338 \fn bool QFontDatabase::removeAllApplicationFonts()
2339 \since 4.2
2340
2341 Removes all application-local fonts previously added using addApplicationFont()
2342 and addApplicationFontFromData().
2343
2344 Returns \c true if unloading of the fonts succeeded; otherwise
2345 returns \c false.
2346
2347 \sa removeApplicationFont(), addApplicationFont(), addApplicationFontFromData()
2348*/
2349bool QFontDatabase::removeAllApplicationFonts()
2350{
2351 QMutexLocker locker(fontDatabaseMutex());
2352
2353 auto *db = QFontDatabasePrivate::instance();
2354 if (!db || db->applicationFonts.isEmpty())
2355 return false;
2356
2357 db->applicationFonts.clear();
2358 db->invalidate();
2359 return true;
2360}
2361
2362/*!
2363 \internal
2364*/
2365QFontEngine *QFontDatabasePrivate::findFont(const QFontDef &req,
2366 int script,
2367 bool preferScriptOverFamily)
2368{
2369 QMutexLocker locker(fontDatabaseMutex());
2370 ensureFontDatabase();
2371
2372 QFontEngine *engine;
2373
2374#ifdef Q_OS_WIN
2375 const QFontDef request = static_cast<QWindowsFontDatabaseBase *>(
2376 QGuiApplicationPrivate::platformIntegration()->fontDatabase())
2377 ->sanitizeRequest(req);
2378#else
2379 const QFontDef &request = req;
2380#endif
2381
2382#if defined(QT_BUILD_INTERNAL)
2383 // For testing purpose only, emulates an exact-matching monospace font
2384 if (qt_enable_test_font && request.families.first() == "__Qt__Box__Engine__"_L1) {
2385 engine = new QTestFontEngine(request.pixelSize);
2386 engine->fontDef = request;
2387 return engine;
2388 }
2389#endif
2390
2391 QFontCache *fontCache = QFontCache::instance();
2392
2393 // Until we specifically asked not to, try looking for Multi font engine
2394 // first, the last '1' indicates that we want Multi font engine instead
2395 // of single ones
2396 bool multi = !(request.styleStrategy & QFont::NoFontMerging);
2397 QFontCache::Key key(request, script, multi ? 1 : 0);
2398 engine = fontCache->findEngine(key);
2399 if (engine) {
2400 qCDebug(lcFontMatch, "Cache hit level 1");
2401 return engine;
2402 }
2403
2404 if (request.pixelSize > 0xffff) {
2405 // Stop absurd requests reaching the engines; pixel size is assumed to fit ushort
2406 qCDebug(lcFontMatch, "Rejecting request for pixel size %g2, returning box engine", double(request.pixelSize));
2407 return new QFontEngineBox(32); // not request.pixelSize, to avoid overflow/DOS
2408 }
2409
2410 QString family_name, foundry_name;
2411 const QString requestFamily = request.families.at(i: 0);
2412 parseFontName(name: requestFamily, foundry&: foundry_name, family&: family_name);
2413 QtFontDesc desc;
2414 QList<int> blackListed;
2415 unsigned int score = UINT_MAX;
2416 int index = match(script: multi ? QChar::Script_Common : script, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed, resultingScore: &score);
2417 if (score > 0 && QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamilyAliases(missingFamily: family_name)) {
2418 // We populated family aliases (e.g. localized families), so try again
2419 index = match(script: multi ? QChar::Script_Common : script, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed);
2420 }
2421
2422 // If we do not find a match and NoFontMerging is set, use the requested font even if it does
2423 // not support the script.
2424 //
2425 // (we do this at the end to prefer foundries that support the script if they exist)
2426 if (index < 0 && !multi && !preferScriptOverFamily)
2427 index = match(script: QChar::Script_Common, request, family_name, foundry_name, desc: &desc, blacklistedFamilies: blackListed);
2428
2429 if (index >= 0) {
2430 QFontDef fontDef = request;
2431 // Don't pass empty family names to the platform font database, since it will then invoke its own matching
2432 // and we will be out of sync with the matched font.
2433 if (fontDef.families.isEmpty())
2434 fontDef.families = QStringList(desc.family->name);
2435
2436 engine = loadEngine(script, request: fontDef, family: desc.family, foundry: desc.foundry, style: desc.style, size: desc.size);
2437
2438 if (engine)
2439 initFontDef(desc, request, fontDef: &engine->fontDef, multi);
2440 else
2441 blackListed.append(t: index);
2442 } else {
2443 qCDebug(lcFontMatch, " NO MATCH FOUND\n");
2444 }
2445
2446 if (!engine) {
2447 if (!requestFamily.isEmpty()) {
2448 QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint);
2449 if (styleHint == QFont::AnyStyle && request.fixedPitch)
2450 styleHint = QFont::TypeWriter;
2451
2452 QStringList fallbacks = request.fallBackFamilies
2453 + fallbacksForFamily(family: requestFamily,
2454 style: QFont::Style(request.style),
2455 styleHint,
2456 script: QChar::Script(script));
2457 if (script > QChar::Script_Common)
2458 fallbacks += QString(); // Find the first font matching the specified script.
2459
2460 for (int i = 0; !engine && i < fallbacks.size(); i++) {
2461 QFontDef def = request;
2462 def.families = QStringList(fallbacks.at(i));
2463 QFontCache::Key key(def, script, multi ? 1 : 0);
2464 engine = fontCache->findEngine(key);
2465 if (!engine) {
2466 QtFontDesc desc;
2467 do {
2468 index = match(script: multi ? QChar::Script_Common : script, request: def, family_name: def.families.first(), foundry_name: ""_L1, desc: &desc, blacklistedFamilies: blackListed);
2469 if (index >= 0) {
2470 QFontDef loadDef = def;
2471 if (loadDef.families.isEmpty())
2472 loadDef.families = QStringList(desc.family->name);
2473 engine = loadEngine(script, request: loadDef, family: desc.family, foundry: desc.foundry, style: desc.style, size: desc.size);
2474 if (engine)
2475 initFontDef(desc, request: loadDef, fontDef: &engine->fontDef, multi);
2476 else
2477 blackListed.append(t: index);
2478 }
2479 } while (index >= 0 && !engine);
2480 }
2481 }
2482 }
2483
2484 if (!engine)
2485 engine = new QFontEngineBox(request.pixelSize);
2486
2487 qCDebug(lcFontMatch, "returning box engine");
2488 }
2489
2490 return engine;
2491}
2492
2493void QFontDatabasePrivate::load(const QFontPrivate *d, int script)
2494{
2495 QFontDef req = d->request;
2496
2497 if (req.pixelSize == -1) {
2498 req.pixelSize = std::floor(x: ((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100;
2499 req.pixelSize = qRound(d: req.pixelSize);
2500 }
2501 if (req.pointSize < 0)
2502 req.pointSize = req.pixelSize*72.0/d->dpi;
2503
2504 // respect the fallback families that might be passed through the request
2505 const QStringList fallBackFamilies = familyList(req);
2506
2507 if (!d->engineData) {
2508 QFontCache *fontCache = QFontCache::instance();
2509 // look for the requested font in the engine data cache
2510 // note: fallBackFamilies are not respected in the EngineData cache key;
2511 // join them with the primary selection family to avoid cache misses
2512 if (!d->request.families.isEmpty())
2513 req.families = fallBackFamilies;
2514
2515 d->engineData = fontCache->findEngineData(def: req);
2516 if (!d->engineData) {
2517 // create a new one
2518 d->engineData = new QFontEngineData;
2519 fontCache->insertEngineData(def: req, engineData: d->engineData);
2520 }
2521 d->engineData->ref.ref();
2522 }
2523
2524 // the cached engineData could have already loaded the engine we want
2525 if (d->engineData->engines[script])
2526 return;
2527
2528 QFontEngine *fe = nullptr;
2529
2530 Q_TRACE(QFontDatabase_load, req.families.join(QLatin1Char(';')), req.pointSize);
2531
2532 req.fallBackFamilies = fallBackFamilies;
2533 if (!req.fallBackFamilies.isEmpty())
2534 req.families = QStringList(req.fallBackFamilies.takeFirst());
2535
2536 // list of families to try
2537 QStringList family_list;
2538
2539 if (!req.families.isEmpty()) {
2540 // Add primary selection
2541 family_list << req.families.at(i: 0);
2542
2543 // add the default family
2544 auto families = QGuiApplication::font().families();
2545 if (!families.isEmpty()) {
2546 QString defaultFamily = families.first();
2547 if (! family_list.contains(str: defaultFamily))
2548 family_list << defaultFamily;
2549 }
2550
2551 }
2552
2553 // null family means find the first font matching the specified script
2554 family_list << QString();
2555
2556 QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd();
2557 for (; !fe && it != end; ++it) {
2558 req.families = QStringList(*it);
2559
2560 fe = QFontDatabasePrivate::findFont(req, script);
2561 if (fe) {
2562 if (fe->type() == QFontEngine::Box && !req.families.at(i: 0).isEmpty()) {
2563 if (fe->ref.loadRelaxed() == 0)
2564 delete fe;
2565 fe = nullptr;
2566 } else {
2567 if (d->dpi > 0)
2568 fe->fontDef.pointSize = qreal(double((fe->fontDef.pixelSize * 72) / d->dpi));
2569 }
2570 }
2571
2572 // No need to check requested fallback families again
2573 req.fallBackFamilies.clear();
2574 }
2575
2576 Q_ASSERT(fe);
2577 if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
2578 for (int i = 0; i < QChar::ScriptCount; ++i) {
2579 if (!d->engineData->engines[i]) {
2580 d->engineData->engines[i] = fe;
2581 fe->ref.ref();
2582 }
2583 }
2584 } else {
2585 d->engineData->engines[script] = fe;
2586 fe->ref.ref();
2587 }
2588}
2589
2590QString QFontDatabasePrivate::resolveFontFamilyAlias(const QString &family)
2591{
2592 return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->resolveFontFamilyAlias(family);
2593}
2594
2595Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script, const QStringList &families)
2596{
2597 size_t writingSystem = qt_writing_system_for_script(script);
2598 if (writingSystem == QFontDatabase::Any
2599 || writingSystem >= QFontDatabase::WritingSystemsCount) {
2600 return families;
2601 }
2602
2603 auto *db = QFontDatabasePrivate::instance();
2604 QMultiMap<uint, QString> supported;
2605 for (int i = 0; i < families.size(); ++i) {
2606 const QString &family = families.at(i);
2607
2608 QtFontFamily *testFamily = nullptr;
2609 for (int x = 0; x < db->count; ++x) {
2610 if (Q_UNLIKELY(matchFamilyName(family, db->families[x]))) {
2611 testFamily = db->families[x];
2612 if (testFamily->ensurePopulated())
2613 break;
2614 }
2615 }
2616
2617 uint order = i;
2618 if (testFamily == nullptr
2619 || !familySupportsWritingSystem(family: testFamily, writingSystem)) {
2620 order |= 1u << 31;
2621 }
2622
2623 supported.insert(key: order, value: family);
2624 }
2625
2626 return supported.values();
2627}
2628
2629QT_END_NAMESPACE
2630
2631#include "moc_qfontdatabase.cpp"
2632
2633

source code of qtbase/src/gui/text/qfontdatabase.cpp